]> git.saurik.com Git - wxWidgets.git/blame - src/msw/app.cpp
implementation changes to closer follow MSW, crashes fixed
[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
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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"
9fc6c21c 54#include "wx/dynlib.h"
4bf78aae 55
4286a5b5
RR
56#include "wx/msw/private.h"
57
4bf78aae 58#if wxUSE_THREADS
bee503b0
VZ
59 #include "wx/thread.h"
60
61 // define the array of MSG strutures
62 WX_DECLARE_OBJARRAY(MSG, wxMsgArray);
63
64 #include "wx/arrimpl.cpp"
65
66 WX_DEFINE_OBJARRAY(wxMsgArray);
67#endif // wxUSE_THREADS
2bda0e17 68
8614c467
VZ
69#if wxUSE_TOOLTIPS
70 #include "wx/tooltip.h"
71#endif // wxUSE_TOOLTIPS
72
c42404a5
VZ
73// OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
74// compilers don't support it (missing headers, libs, ...)
2bdf7154 75#if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
e5c0b16a
VZ
76 #undef wxUSE_OLE
77
78 #define wxUSE_OLE 0
79#endif // broken compilers
80
81#if wxUSE_OLE
6e0d9d43 82 #include <ole2.h>
d05237ea 83#endif
ce3ed50d 84
2bda0e17 85#include <string.h>
a5e0e655 86#include <ctype.h>
2bda0e17 87
0d236fd0 88#include "wx/msw/wrapcctl.h"
bdc72a22 89
9fc6c21c 90#if (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
50165591
CE
91 !defined(__CYGWIN__) && !defined(__DIGITALMARS__) && !defined(__WXWINCE__) && \
92 (!defined(_MSC_VER) || (_MSC_VER > 1100))
036bc7d9
VZ
93 #include <shlwapi.h>
94#endif
95
e5c0b16a
VZ
96// ---------------------------------------------------------------------------
97// global variables
98// ---------------------------------------------------------------------------
99
cde9f08e 100extern wxList WXDLLEXPORT wxPendingDelete;
4676948b
JS
101
102#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
2bda0e17 103extern void wxSetKeyboardHook(bool doIt);
04ef50df 104#endif
2bda0e17 105
42e69d6b 106MSG s_currentMsg;
2bda0e17 107
193fe989
VZ
108// NB: all "NoRedraw" classes must have the same names as the "normal" classes
109// with NR suffix - wxWindow::MSWCreate() supposes this
03baf031
VZ
110const wxChar *wxCanvasClassName = wxT("wxWindowClass");
111const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
2ffa221c
VZ
112const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
113const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
114const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
115const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
2bda0e17 116
57c208c5 117HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
2bda0e17 118
94826170
VZ
119// ----------------------------------------------------------------------------
120// private functions
121// ----------------------------------------------------------------------------
2bda0e17 122
94826170 123LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
3b415ba4 124
e5c0b16a 125// ===========================================================================
e2478fde
VZ
126// wxGUIAppTraits implementation
127// ===========================================================================
128
129// private class which we use to pass parameters from BeforeChildWaitLoop() to
130// AfterChildWaitLoop()
131struct ChildWaitLoopData
132{
133 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
134 {
135 wd = wd_;
136 winActive = winActive_;
137 }
138
139 wxWindowDisabler *wd;
140 wxWindow *winActive;
141};
142
143void *wxGUIAppTraits::BeforeChildWaitLoop()
144{
145 /*
146 We use a dirty hack here to disable all application windows (which we
147 must do because otherwise the calls to wxYield() could lead to some very
148 unexpected reentrancies in the users code) but to avoid losing
149 focus/activation entirely when the child process terminates which would
150 happen if we simply disabled everything using wxWindowDisabler. Indeed,
151 remember that Windows will never activate a disabled window and when the
152 last childs window is closed and Windows looks for a window to activate
153 all our windows are still disabled. There is no way to enable them in
154 time because we don't know when the childs windows are going to be
155 closed, so the solution we use here is to keep one special tiny frame
156 enabled all the time. Then when the child terminates it will get
157 activated and when we close it below -- after reenabling all the other
158 windows! -- the previously active window becomes activated again and
159 everything is ok.
160 */
161 wxBeginBusyCursor();
162
163 // first disable all existing windows
164 wxWindowDisabler *wd = new wxWindowDisabler;
165
166 // then create an "invisible" frame: it has minimal size, is positioned
167 // (hopefully) outside the screen and doesn't appear on the taskbar
168 wxWindow *winActive = new wxFrame
169 (
170 wxTheApp->GetTopWindow(),
fda7962d
JS
171 wxID_ANY,
172 wxEmptyString,
e2478fde
VZ
173 wxPoint(32600, 32600),
174 wxSize(1, 1),
175 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
176 );
177 winActive->Show();
178
179 return new ChildWaitLoopData(wd, winActive);
180}
181
182void wxGUIAppTraits::AlwaysYield()
183{
184 wxYield();
185}
186
187void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
188{
189 wxEndBusyCursor();
190
191 const ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
192
193 delete data->wd;
194
195 // finally delete the dummy frame and, as wd has been already destroyed and
196 // the other windows reenabled, the activation is going to return to the
197 // window which had had it before
198 data->winActive->Destroy();
199}
200
201bool wxGUIAppTraits::DoMessageFromThreadWait()
202{
203 return !wxTheApp || wxTheApp->DoMessage();
204}
205
324899f6 206wxToolkitInfo& wxGUIAppTraits::GetToolkitInfo()
a8eaaeb2
VS
207{
208 static wxToolkitInfo info;
324899f6
VS
209 wxToolkitInfo& baseInfo = wxAppTraits::GetToolkitInfo();
210 info.versionMajor = baseInfo.versionMajor;
211 info.versionMinor = baseInfo.versionMinor;
212 info.os = baseInfo.os;
a8eaaeb2
VS
213 info.shortName = _T("msw");
214 info.name = _T("wxMSW");
215#ifdef __WXUNIVERSAL__
216 info.shortName << _T("univ");
217 info.name << _T("/wxUniversal");
218#endif
324899f6 219 return info;
a8eaaeb2
VS
220}
221
e2478fde
VZ
222// ===========================================================================
223// wxApp implementation
e5c0b16a 224// ===========================================================================
589f0e3e 225
94826170
VZ
226int wxApp::m_nCmdShow = SW_SHOWNORMAL;
227
e5c0b16a 228// ---------------------------------------------------------------------------
e2478fde 229// wxWin macros
e5c0b16a
VZ
230// ---------------------------------------------------------------------------
231
f6bcfd97 232IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
e5c0b16a 233
f6bcfd97
BP
234BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
235 EVT_IDLE(wxApp::OnIdle)
236 EVT_END_SESSION(wxApp::OnEndSession)
237 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
238END_EVENT_TABLE()
e5c0b16a 239
94826170
VZ
240// class to ensure that wxAppBase::CleanUp() is called if our Initialize()
241// fails
242class wxCallBaseCleanup
243{
244public:
245 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
246 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
247
248 void Dismiss() { m_app = NULL; }
249
250private:
251 wxApp *m_app;
252};
253
e5c0b16a 254//// Initialize
05e2b077 255bool wxApp::Initialize(int& argc, wxChar **argv)
2bda0e17 256{
94826170
VZ
257 if ( !wxAppBase::Initialize(argc, argv) )
258 return false;
259
260 // ensure that base cleanup is done if we return too early
261 wxCallBaseCleanup callBaseCleanup(this);
262
f6bcfd97 263 // the first thing to do is to check if we're trying to run an Unicode
7c9955d1 264 // program under Win9x w/o MSLU emulation layer - if so, abort right now
dfc40ef3 265 // as it has no chance to work
eb5e4d9a 266#if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f07dc2e2 267 if ( wxGetOsVersion() != wxWINDOWS_NT && wxGetOsVersion() != wxWINDOWS_CE )
f6bcfd97
BP
268 {
269 // note that we can use MessageBoxW() as it's implemented even under
270 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
271 // used by wxLocale are not
272 ::MessageBox
273 (
274 NULL,
f07dc2e2 275 _T("This program uses Unicode and requires Windows NT/2000/XP/CE.\nProgram aborted."),
f6bcfd97
BP
276 _T("wxWindows Fatal Error"),
277 MB_ICONERROR | MB_OK
278 );
279
280 return FALSE;
281 }
eb5e4d9a 282#endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
f6bcfd97 283
04ef50df 284#if defined(__WIN95__) && !defined(__WXMICROWIN__)
a5e0e655 285 InitCommonControls();
e5c0b16a 286#endif // __WIN95__
2bda0e17 287
8cb172b4 288#if wxUSE_OLE || wxUSE_DRAG_AND_DROP
c49245f8
VZ
289
290#ifdef __WIN16__
e5c0b16a 291 // for OLE, enlarge message queue to be as large as possible
aa0b7e1e 292 int iMsg = 96;
c49245f8
VZ
293 while (!SetMessageQueue(iMsg) && (iMsg -= 8))
294 ;
295#endif // Win16
8cb172b4 296
abad5367 297#if wxUSE_OLE
a5e0e655 298 // we need to initialize OLE library
f07dc2e2
JS
299#ifdef __WXWINCE__
300 if ( FAILED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
301 wxLogError(_("Cannot initialize OLE"));
302#else
a5e0e655 303 if ( FAILED(::OleInitialize(NULL)) )
e5c0b16a 304 wxLogError(_("Cannot initialize OLE"));
abad5367 305#endif
f07dc2e2 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
4676948b 319#if defined(__WXMICROWIN__) && !defined(__WXWINCE__)
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
4676948b 347#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
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{
4676948b 500#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
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
f07dc2e2
JS
512#ifdef __WXWINCE__
513 ::CoUninitialize();
514#else
e5c0b16a 515 ::OleUninitialize();
f07dc2e2 516#endif
5de5db0e 517#endif
2bda0e17 518
9787a4b6
VZ
519 // for an EXE the classes are unregistered when it terminates but DLL may
520 // be loaded several times (load/unload/load) into the same process in
521 // which case the registration will fail after the first time if we don't
522 // unregister the classes now
523 UnregisterWindowClasses();
9787a4b6 524
1f112209 525#if wxUSE_CTL3D
e5c0b16a 526 Ctl3dUnregister(wxhInstance);
2bda0e17
KB
527#endif
528
1bffa913 529 delete wxWinHandleHash;
94826170 530 wxWinHandleHash = NULL;
f8a3e080 531
94826170 532 wxAppBase::CleanUp();
2bda0e17
KB
533}
534
94826170
VZ
535// ----------------------------------------------------------------------------
536// wxApp ctor/dtor
537// ----------------------------------------------------------------------------
589f0e3e 538
bb6290e3 539wxApp::wxApp()
2bda0e17 540{
e5c0b16a 541 m_printMode = wxPRINT_WINDOWS;
2bda0e17
KB
542}
543
589f0e3e
JS
544wxApp::~wxApp()
545{
94826170
VZ
546 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
547 // don't come from main(), so we have to free them
548
549 while ( argc )
e5c0b16a 550 {
94826170
VZ
551 // m_argv elements were allocated by wxStrdup()
552 free(argv[--argc]);
e5c0b16a 553 }
94826170
VZ
554
555 // but m_argv itself -- using new[]
556 delete [] argv;
589f0e3e
JS
557}
558
bb6290e3 559bool wxApp::Initialized()
2bda0e17
KB
560{
561#ifndef _WINDLL
e5c0b16a
VZ
562 if (GetTopWindow())
563 return TRUE;
564 else
565 return FALSE;
3b415ba4 566#else // Assume initialized if DLL (no way of telling)
e5c0b16a 567 return TRUE;
2bda0e17
KB
568#endif
569}
570
571/*
572 * Get and process a message, returning FALSE if WM_QUIT
bee503b0 573 * received (and also set the flag telling the app to exit the main loop)
2bda0e17
KB
574 *
575 */
bb6290e3 576bool wxApp::DoMessage()
2bda0e17 577{
bee503b0
VZ
578 BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
579 if ( rc == 0 )
580 {
581 // got WM_QUIT
582 m_keepGoing = FALSE;
4a9968f9 583
bee503b0
VZ
584 return FALSE;
585 }
586 else if ( rc == -1 )
587 {
588 // should never happen, but let's test for it nevertheless
f6bcfd97 589 wxLogLastError(wxT("GetMessage"));
bee503b0
VZ
590 }
591 else
592 {
593#if wxUSE_THREADS
594 wxASSERT_MSG( wxThread::IsMain(),
223d09f6 595 wxT("only the main thread can process Windows messages") );
d50b2a58 596
bee503b0
VZ
597 static bool s_hadGuiLock = TRUE;
598 static wxMsgArray s_aSavedMessages;
599
600 // if a secondary thread owns is doing GUI calls, save all messages for
601 // later processing - we can't process them right now because it will
602 // lead to recursive library calls (and we're not reentrant)
603 if ( !wxGuiOwnedByMainThread() )
604 {
605 s_hadGuiLock = FALSE;
606
4a9968f9
VZ
607 // leave out WM_COMMAND messages: too dangerous, sometimes
608 // the message will be processed twice
609 if ( !wxIsWaitingForThread() ||
e5c0b16a 610 s_currentMsg.message != WM_COMMAND )
4a9968f9
VZ
611 {
612 s_aSavedMessages.Add(s_currentMsg);
613 }
bee503b0
VZ
614
615 return TRUE;
616 }
617 else
618 {
619 // have we just regained the GUI lock? if so, post all of the saved
620 // messages
621 //
622 // FIXME of course, it's not _exactly_ the same as processing the
623 // messages normally - expect some things to break...
624 if ( !s_hadGuiLock )
625 {
626 s_hadGuiLock = TRUE;
627
0a95f6d0 628 size_t count = s_aSavedMessages.GetCount();
bee503b0
VZ
629 for ( size_t n = 0; n < count; n++ )
630 {
631 MSG& msg = s_aSavedMessages[n];
632
0a95f6d0 633 DoMessage((WXMSG *)&msg);
bee503b0
VZ
634 }
635
636 s_aSavedMessages.Empty();
637 }
638 }
639#endif // wxUSE_THREADS
640
641 // Process the message
ed45e263 642 DoMessage((WXMSG *)&s_currentMsg);
bee503b0
VZ
643 }
644
645 return TRUE;
2bda0e17
KB
646}
647
ed45e263
VZ
648void wxApp::DoMessage(WXMSG *pMsg)
649{
650 if ( !ProcessMessage(pMsg) )
651 {
652 ::TranslateMessage((MSG *)pMsg);
653 ::DispatchMessage((MSG *)pMsg);
654 }
655}
656
2bda0e17
KB
657/*
658 * Keep trying to process messages until WM_QUIT
659 * received.
660 *
661 * If there are messages to be processed, they will all be
662 * processed and OnIdle will not be called.
663 * When there are no more messages, OnIdle is called.
664 * If OnIdle requests more time,
665 * it will be repeatedly called so long as there are no pending messages.
666 * A 'feature' of this is that once OnIdle has decided that no more processing
667 * is required, then it won't get processing time until further messages
668 * are processed (it'll sit in DoMessage).
669 */
670
bb6290e3 671int wxApp::MainLoop()
2bda0e17 672{
e5c0b16a 673 m_keepGoing = TRUE;
bee503b0 674
e5c0b16a
VZ
675 while ( m_keepGoing )
676 {
677#if wxUSE_THREADS
bee503b0 678 wxMutexGuiLeaveOrEnter();
e5c0b16a 679#endif // wxUSE_THREADS
bee503b0 680
b6c588e1
VZ
681 while ( !Pending() && ProcessIdle() )
682 ;
7214297d 683
b6c588e1 684 // a message came or no more idle processing to do
e5c0b16a
VZ
685 DoMessage();
686 }
2bda0e17 687
e5c0b16a 688 return s_currentMsg.wParam;
2bda0e17
KB
689}
690
bb6290e3 691void wxApp::ExitMainLoop()
2bda0e17 692{
1cbee0b4
VZ
693 // this will set m_keepGoing to FALSE a bit later
694 ::PostQuitMessage(0);
2bda0e17
KB
695}
696
bb6290e3 697bool wxApp::Pending()
2bda0e17 698{
b6c588e1 699 return ::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) != 0;
2bda0e17
KB
700}
701
bb6290e3 702void wxApp::Dispatch()
2bda0e17 703{
bee503b0 704 DoMessage();
2bda0e17
KB
705}
706
707/*
708 * Give all windows a chance to preprocess
709 * the message. Some may have accelerator tables, or have
710 * MDI complications.
711 */
2a47d3c1 712
d3f0a137 713bool wxApp::ProcessMessage(WXMSG *wxmsg)
2bda0e17 714{
d3f0a137 715 MSG *msg = (MSG *)wxmsg;
2a3caeb5
VZ
716 HWND hwnd = msg->hwnd;
717 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
2bda0e17 718
761989ff
VZ
719 // this may happen if the event occured in a standard modeless dialog (the
720 // only example of which I know of is the find/replace dialog) - then call
721 // IsDialogMessage() to make TAB navigation in it work
722 if ( !wndThis )
723 {
2a3caeb5
VZ
724 // we need to find the dialog containing this control as
725 // IsDialogMessage() just eats all the messages (i.e. returns TRUE for
726 // them) if we call it for the control itself
de7f0860 727 while ( hwnd && ::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
2a3caeb5
VZ
728 {
729 hwnd = ::GetParent(hwnd);
730 }
731
de7f0860 732 return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
761989ff
VZ
733 }
734
8614c467
VZ
735#if wxUSE_TOOLTIPS
736 // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to
737 // popup the tooltip bubbles
761989ff 738 if ( (msg->message == WM_MOUSEMOVE) )
834362a2 739 {
8614c467
VZ
740 wxToolTip *tt = wndThis->GetToolTip();
741 if ( tt )
742 {
743 tt->RelayEvent(wxmsg);
744 }
834362a2 745 }
8614c467 746#endif // wxUSE_TOOLTIPS
834362a2 747
a37d422a
VZ
748 // allow the window to prevent certain messages from being
749 // translated/processed (this is currently used by wxTextCtrl to always
750 // grab Ctrl-C/V/X, even if they are also accelerators in some parent)
751 if ( !wndThis->MSWShouldPreProcessMessage(wxmsg) )
752 {
753 return FALSE;
754 }
755
2a3caeb5 756 // try translations first: the accelerators override everything
8614c467 757 wxWindow *wnd;
42bcb12b 758
d3f0a137 759 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
2bda0e17 760 {
2a3caeb5 761 if ( wnd->MSWTranslateMessage(wxmsg))
d3f0a137 762 return TRUE;
3bdc265d
VZ
763
764 // stop at first top level window, i.e. don't try to process the key
765 // strokes originating in a dialog using the accelerators of the parent
766 // frame - this doesn't make much sense
767 if ( wnd->IsTopLevel() )
2a3caeb5
VZ
768 break;
769 }
770
a37d422a
VZ
771 // now try the other hooks (kbd navigation is handled here): we start from
772 // wndThis->GetParent() because wndThis->MSWProcessMessage() was already
773 // called above
774 for ( wnd = wndThis->GetParent(); wnd; wnd = wnd->GetParent() )
2a3caeb5
VZ
775 {
776 if ( wnd->MSWProcessMessage(wxmsg) )
777 return TRUE;
57a7b7c1 778 }
d3f0a137 779
a37d422a 780 // no special preprocessing for this message, dispatch it normally
e5c0b16a 781 return FALSE;
2bda0e17
KB
782}
783
2b5f62a0
VZ
784// this is a temporary hack and will be replaced by using wxEventLoop in the
785// future
786//
787// it is needed to allow other event loops (currently only one: the modal
788// dialog one) to reset the OnIdle() semaphore because otherwise OnIdle()
789// wouldn't do anything while a modal dialog shown from OnIdle() call is shown.
790bool wxIsInOnIdleFlag = FALSE;
791
2bda0e17
KB
792void wxApp::OnIdle(wxIdleEvent& event)
793{
3222fde2 794 // Avoid recursion (via ProcessEvent default case)
2b5f62a0 795 if ( wxIsInOnIdleFlag )
3222fde2 796 return;
2bda0e17 797
2b5f62a0 798 wxIsInOnIdleFlag = TRUE;
955a9197
JS
799
800 wxAppBase::OnIdle(event);
c54f78a2 801
aef94d68
JS
802#if wxUSE_DC_CACHEING
803 // automated DC cache management: clear the cached DCs and bitmap
804 // if it's likely that the app has finished with them, that is, we
805 // get an idle event and we're not dragging anything.
4624defa 806 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
aef94d68
JS
807 wxDC::ClearCache();
808#endif // wxUSE_DC_CACHEING
809
2b5f62a0 810 wxIsInOnIdleFlag = FALSE;
2bda0e17
KB
811}
812
e2478fde
VZ
813void wxApp::WakeUpIdle()
814{
815 // Send the top window a dummy message so idle handler processing will
816 // start up again. Doing it this way ensures that the idle handler
817 // wakes up in the right thread (see also wxWakeUpMainThread() which does
818 // the same for the main app thread only)
819 wxWindow *topWindow = wxTheApp->GetTopWindow();
820 if ( topWindow )
821 {
822 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
823 {
824 // should never happen
825 wxLogLastError(wxT("PostMessage(WM_NULL)"));
826 }
827 }
828}
829
57c208c5 830void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
387a3b02
JS
831{
832 if (GetTopWindow())
833 GetTopWindow()->Close(TRUE);
834}
835
836// Default behaviour: close the application with prompts. The
837// user can veto the close, and therefore the end session.
838void wxApp::OnQueryEndSession(wxCloseEvent& event)
839{
840 if (GetTopWindow())
841 {
842 if (!GetTopWindow()->Close(!event.CanVeto()))
843 event.Veto(TRUE);
844 }
845}
846
6d167489
VZ
847/* static */
848int wxApp::GetComCtl32Version()
849{
50165591
CE
850//FIX ME FOR DIGITALMARS!!
851#if defined(__WXMICROWIN__) || defined(__WXWINCE__) || defined(__DIGITALMARS__)
04ef50df
JS
852 return 0;
853#else
6d167489 854 // cache the result
9fc6c21c
VZ
855 //
856 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
857 // but as its value should be the same both times it doesn't matter
bdc72a22
VZ
858 static int s_verComCtl32 = -1;
859
6d167489
VZ
860 if ( s_verComCtl32 == -1 )
861 {
bdc72a22 862 // initally assume no comctl32.dll at all
6d167489
VZ
863 s_verComCtl32 = 0;
864
9fc6c21c
VZ
865 // we're prepared to handle the errors
866 wxLogNull noLog;
867
bdc72a22 868 // do we have it?
9fc6c21c 869 wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM);
6d167489
VZ
870
871 // if so, then we can check for the version
9fc6c21c 872 if ( dllComCtl32.IsLoaded() )
bb6290e3 873 {
548bf4b7 874#ifdef DLLVER_PLATFORM_WINDOWS
bdc72a22 875 // try to use DllGetVersion() if available in _headers_
9fc6c21c
VZ
876 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC, DllGetVersion, dllComCtl32 );
877 if ( pfnDllGetVersion )
878 {
879 DLLVERSIONINFO dvi;
880 dvi.cbSize = sizeof(dvi);
881
882 HRESULT hr = (*pfnDllGetVersion)(&dvi);
883 if ( FAILED(hr) )
6d167489 884 {
9fc6c21c
VZ
885 wxLogApiError(_T("DllGetVersion"), hr);
886 }
887 else
888 {
889 // this is incompatible with _WIN32_IE values, but
890 // compatible with the other values returned by
891 // GetComCtl32Version()
892 s_verComCtl32 = 100*dvi.dwMajorVersion +
893 dvi.dwMinorVersion;
894 }
895 }
548bf4b7 896#endif
bdc72a22 897
9fc6c21c
VZ
898 // if DllGetVersion() is unavailable either during compile or
899 // run-time, try to guess the version otherwise
900 if ( !s_verComCtl32 )
901 {
902 // InitCommonControlsEx is unique to 4.70 and later
903 void *pfn = dllComCtl32.GetSymbol(_T("InitCommonControlsEx"));
904 if ( !pfn )
905 {
906 // not found, must be 4.00
907 s_verComCtl32 = 400;
6d167489 908 }
9fc6c21c 909 else // 4.70+
bdc72a22 910 {
9fc6c21c
VZ
911 // many symbols appeared in comctl32 4.71, could use any of
912 // them except may be DllInstall()
913 pfn = dllComCtl32.GetSymbol(_T("InitializeFlatSB"));
914 if ( !pfn )
bdc72a22 915 {
9fc6c21c
VZ
916 // not found, must be 4.70
917 s_verComCtl32 = 470;
bdc72a22
VZ
918 }
919 else
920 {
9fc6c21c
VZ
921 // found, must be 4.71 or later
922 s_verComCtl32 = 471;
bdc72a22 923 }
6d167489 924 }
9fc6c21c 925 }
ef094fa0 926 }
bb6290e3 927 }
6d167489
VZ
928
929 return s_verComCtl32;
9fc6c21c 930#endif // Microwin/!Microwin
bb6290e3
JS
931}
932
2bda0e17 933// Yield to incoming messages
cb2713bf 934
8461e4c2 935bool wxApp::Yield(bool onlyIfNeeded)
2bda0e17 936{
8461e4c2
VZ
937 // MT-FIXME
938 static bool s_inYield = FALSE;
939
e30285ab 940#if wxUSE_LOG
2ed3265e
VZ
941 // disable log flushing from here because a call to wxYield() shouldn't
942 // normally result in message boxes popping up &c
943 wxLog::Suspend();
e30285ab 944#endif // wxUSE_LOG
2ed3265e 945
8461e4c2
VZ
946 if ( s_inYield )
947 {
948 if ( !onlyIfNeeded )
949 {
950 wxFAIL_MSG( wxT("wxYield called recursively" ) );
951 }
33ac7e6f 952
8461e4c2
VZ
953 return FALSE;
954 }
955
956 s_inYield = TRUE;
cb2713bf 957
8e193f38
VZ
958 // we don't want to process WM_QUIT from here - it should be processed in
959 // the main event loop in order to stop it
e5c0b16a 960 MSG msg;
8e193f38
VZ
961 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
962 msg.message != WM_QUIT )
e5c0b16a 963 {
5b615ed8
VZ
964#if wxUSE_THREADS
965 wxMutexGuiLeaveOrEnter();
966#endif // wxUSE_THREADS
967
e5c0b16a
VZ
968 if ( !wxTheApp->DoMessage() )
969 break;
970 }
8e193f38 971
8461e4c2
VZ
972 // if there are pending events, we must process them.
973 ProcessPendingEvents();
e5c0b16a 974
e30285ab 975#if wxUSE_LOG
2ed3265e
VZ
976 // let the logs be flashed again
977 wxLog::Resume();
e30285ab 978#endif // wxUSE_LOG
2ed3265e 979
8461e4c2 980 s_inYield = FALSE;
cb2713bf 981
e5c0b16a 982 return TRUE;
2bda0e17 983}
094637f6 984