]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/app.cpp
toplevel event handler exposed
[wxWidgets.git] / src / msw / app.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: app.cpp
3// Purpose: wxApp
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "app.h"
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#if defined(__BORLANDC__)
28 #pragma hdrstop
29#endif
30
31#ifndef WX_PRECOMP
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"
46 #include "wx/wxchar.h"
47 #include "wx/icon.h"
48 #include "wx/log.h"
49#endif
50
51#include "wx/apptrait.h"
52#include "wx/filename.h"
53#include "wx/module.h"
54#include "wx/dynlib.h"
55
56#include "wx/msw/private.h"
57
58#if wxUSE_TOOLTIPS
59 #include "wx/tooltip.h"
60#endif // wxUSE_TOOLTIPS
61
62// OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
63// compilers don't support it (missing headers, libs, ...)
64#if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
65 #undef wxUSE_OLE
66
67 #define wxUSE_OLE 0
68#endif // broken compilers
69
70#if wxUSE_OLE
71 #include <ole2.h>
72#endif
73
74#include <string.h>
75#include <ctype.h>
76
77#include "wx/msw/wrapcctl.h"
78
79#if (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
80 !defined(__CYGWIN__) && !defined(__DIGITALMARS__) && !defined(__WXWINCE__) && \
81 (!defined(_MSC_VER) || (_MSC_VER > 1100))
82 #include <shlwapi.h>
83#endif
84
85// ---------------------------------------------------------------------------
86// global variables
87// ---------------------------------------------------------------------------
88
89extern wxList WXDLLEXPORT wxPendingDelete;
90
91#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
92extern void wxSetKeyboardHook(bool doIt);
93#endif
94
95// NB: all "NoRedraw" classes must have the same names as the "normal" classes
96// with NR suffix - wxWindow::MSWCreate() supposes this
97const wxChar *wxCanvasClassName = wxT("wxWindowClass");
98const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
99const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
100const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
101const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
102const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
103
104HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
105
106// ----------------------------------------------------------------------------
107// private functions
108// ----------------------------------------------------------------------------
109
110LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
111
112// ===========================================================================
113// wxGUIAppTraits implementation
114// ===========================================================================
115
116// private class which we use to pass parameters from BeforeChildWaitLoop() to
117// AfterChildWaitLoop()
118struct ChildWaitLoopData
119{
120 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
121 {
122 wd = wd_;
123 winActive = winActive_;
124 }
125
126 wxWindowDisabler *wd;
127 wxWindow *winActive;
128};
129
130void *wxGUIAppTraits::BeforeChildWaitLoop()
131{
132 /*
133 We use a dirty hack here to disable all application windows (which we
134 must do because otherwise the calls to wxYield() could lead to some very
135 unexpected reentrancies in the users code) but to avoid losing
136 focus/activation entirely when the child process terminates which would
137 happen if we simply disabled everything using wxWindowDisabler. Indeed,
138 remember that Windows will never activate a disabled window and when the
139 last childs window is closed and Windows looks for a window to activate
140 all our windows are still disabled. There is no way to enable them in
141 time because we don't know when the childs windows are going to be
142 closed, so the solution we use here is to keep one special tiny frame
143 enabled all the time. Then when the child terminates it will get
144 activated and when we close it below -- after reenabling all the other
145 windows! -- the previously active window becomes activated again and
146 everything is ok.
147 */
148 wxBeginBusyCursor();
149
150 // first disable all existing windows
151 wxWindowDisabler *wd = new wxWindowDisabler;
152
153 // then create an "invisible" frame: it has minimal size, is positioned
154 // (hopefully) outside the screen and doesn't appear on the taskbar
155 wxWindow *winActive = new wxFrame
156 (
157 wxTheApp->GetTopWindow(),
158 wxID_ANY,
159 wxEmptyString,
160 wxPoint(32600, 32600),
161 wxSize(1, 1),
162 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
163 );
164 winActive->Show();
165
166 return new ChildWaitLoopData(wd, winActive);
167}
168
169void wxGUIAppTraits::AlwaysYield()
170{
171 wxYield();
172}
173
174void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
175{
176 wxEndBusyCursor();
177
178 const ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
179
180 delete data->wd;
181
182 // finally delete the dummy frame and, as wd has been already destroyed and
183 // the other windows reenabled, the activation is going to return to the
184 // window which had had it before
185 data->winActive->Destroy();
186}
187
188bool wxGUIAppTraits::DoMessageFromThreadWait()
189{
190 // we should return false only if the app should exit, i.e. only if
191 // Dispatch() determines that the main event loop should terminate
192 return !wxTheApp || wxTheApp->Dispatch();
193}
194
195wxToolkitInfo& wxGUIAppTraits::GetToolkitInfo()
196{
197 static wxToolkitInfo info;
198 wxToolkitInfo& baseInfo = wxAppTraits::GetToolkitInfo();
199 info.versionMajor = baseInfo.versionMajor;
200 info.versionMinor = baseInfo.versionMinor;
201 info.os = baseInfo.os;
202 info.shortName = _T("msw");
203 info.name = _T("wxMSW");
204#ifdef __WXUNIVERSAL__
205 info.shortName << _T("univ");
206 info.name << _T("/wxUniversal");
207#endif
208 return info;
209}
210
211// ===========================================================================
212// wxApp implementation
213// ===========================================================================
214
215int wxApp::m_nCmdShow = SW_SHOWNORMAL;
216
217// ---------------------------------------------------------------------------
218// wxWin macros
219// ---------------------------------------------------------------------------
220
221IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
222
223BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
224 EVT_IDLE(wxApp::OnIdle)
225 EVT_END_SESSION(wxApp::OnEndSession)
226 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
227END_EVENT_TABLE()
228
229// class to ensure that wxAppBase::CleanUp() is called if our Initialize()
230// fails
231class wxCallBaseCleanup
232{
233public:
234 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
235 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
236
237 void Dismiss() { m_app = NULL; }
238
239private:
240 wxApp *m_app;
241};
242
243//// Initialize
244bool wxApp::Initialize(int& argc, wxChar **argv)
245{
246 if ( !wxAppBase::Initialize(argc, argv) )
247 return false;
248
249 // ensure that base cleanup is done if we return too early
250 wxCallBaseCleanup callBaseCleanup(this);
251
252 // the first thing to do is to check if we're trying to run an Unicode
253 // program under Win9x w/o MSLU emulation layer - if so, abort right now
254 // as it has no chance to work
255#if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
256 if ( wxGetOsVersion() != wxWINDOWS_NT && wxGetOsVersion() != wxWINDOWS_CE )
257 {
258 // note that we can use MessageBoxW() as it's implemented even under
259 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
260 // used by wxLocale are not
261 ::MessageBox
262 (
263 NULL,
264 _T("This program uses Unicode and requires Windows NT/2000/XP/CE.\nProgram aborted."),
265 _T("wxWindows Fatal Error"),
266 MB_ICONERROR | MB_OK
267 );
268
269 return FALSE;
270 }
271#endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
272
273#if defined(__WIN95__) && !defined(__WXMICROWIN__)
274 InitCommonControls();
275#endif // __WIN95__
276
277#if wxUSE_OLE || wxUSE_DRAG_AND_DROP
278
279#if wxUSE_OLE
280 // we need to initialize OLE library
281#ifdef __WXWINCE__
282 if ( FAILED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
283 wxLogError(_("Cannot initialize OLE"));
284#else
285 if ( FAILED(::OleInitialize(NULL)) )
286 wxLogError(_("Cannot initialize OLE"));
287#endif
288#endif
289
290#endif // wxUSE_OLE
291
292#if wxUSE_CTL3D
293 if (!Ctl3dRegister(wxhInstance))
294 wxLogError(wxT("Cannot register CTL3D"));
295
296 Ctl3dAutoSubclass(wxhInstance);
297#endif // wxUSE_CTL3D
298
299 RegisterWindowClasses();
300
301#if defined(__WXMICROWIN__) && !defined(__WXWINCE__)
302 // Create the brush for disabling bitmap buttons
303
304 LOGBRUSH lb;
305 lb.lbStyle = BS_PATTERN;
306 lb.lbColor = 0;
307 lb.lbHatch = (int)LoadBitmap( wxhInstance, wxT("wxDISABLE_BUTTON_BITMAP") );
308 if ( lb.lbHatch )
309 {
310 wxDisableButtonBrush = ::CreateBrushIndirect( & lb );
311 ::DeleteObject( (HGDIOBJ)lb.lbHatch );
312 }
313 //else: wxWindows resources are probably not linked in
314#endif
315
316#if wxUSE_PENWINDOWS
317 wxRegisterPenWin();
318#endif
319
320 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
321
322#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
323 wxSetKeyboardHook(TRUE);
324#endif
325
326 callBaseCleanup.Dismiss();
327
328 return true;
329}
330
331// ---------------------------------------------------------------------------
332// RegisterWindowClasses
333// ---------------------------------------------------------------------------
334
335// TODO we should only register classes really used by the app. For this it
336// would be enough to just delay the class registration until an attempt
337// to create a window of this class is made.
338bool wxApp::RegisterWindowClasses()
339{
340 WNDCLASS wndclass;
341 wxZeroMemory(wndclass);
342
343 // for each class we register one with CS_(V|H)REDRAW style and one
344 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
345 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
346 static const long styleNoRedraw = CS_DBLCLKS;
347
348 // the fields which are common to all classes
349 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
350 wndclass.hInstance = wxhInstance;
351 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
352
353 // Register the frame window class.
354 wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
355 wndclass.lpszClassName = wxCanvasClassName;
356 wndclass.style = styleNormal;
357
358 if ( !RegisterClass(&wndclass) )
359 {
360 wxLogLastError(wxT("RegisterClass(frame)"));
361 }
362
363 // "no redraw" frame
364 wndclass.lpszClassName = wxCanvasClassNameNR;
365 wndclass.style = styleNoRedraw;
366
367 if ( !RegisterClass(&wndclass) )
368 {
369 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
370 }
371
372 // Register the MDI frame window class.
373 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
374 wndclass.lpszClassName = wxMDIFrameClassName;
375 wndclass.style = styleNormal;
376
377 if ( !RegisterClass(&wndclass) )
378 {
379 wxLogLastError(wxT("RegisterClass(MDI parent)"));
380 }
381
382 // "no redraw" MDI frame
383 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
384 wndclass.style = styleNoRedraw;
385
386 if ( !RegisterClass(&wndclass) )
387 {
388 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
389 }
390
391 // Register the MDI child frame window class.
392 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
393 wndclass.lpszClassName = wxMDIChildFrameClassName;
394 wndclass.style = styleNormal;
395
396 if ( !RegisterClass(&wndclass) )
397 {
398 wxLogLastError(wxT("RegisterClass(MDI child)"));
399 }
400
401 // "no redraw" MDI child frame
402 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
403 wndclass.style = styleNoRedraw;
404
405 if ( !RegisterClass(&wndclass) )
406 {
407 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
408 }
409
410 return TRUE;
411}
412
413// ---------------------------------------------------------------------------
414// UnregisterWindowClasses
415// ---------------------------------------------------------------------------
416
417bool wxApp::UnregisterWindowClasses()
418{
419 bool retval = TRUE;
420
421#ifndef __WXMICROWIN__
422 // MDI frame window class.
423 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
424 {
425 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
426
427 retval = FALSE;
428 }
429
430 // "no redraw" MDI frame
431 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
432 {
433 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
434
435 retval = FALSE;
436 }
437
438 // MDI child frame window class.
439 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
440 {
441 wxLogLastError(wxT("UnregisterClass(MDI child)"));
442
443 retval = FALSE;
444 }
445
446 // "no redraw" MDI child frame
447 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
448 {
449 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
450
451 retval = FALSE;
452 }
453
454 // canvas class name
455 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
456 {
457 wxLogLastError(wxT("UnregisterClass(canvas)"));
458
459 retval = FALSE;
460 }
461
462 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
463 {
464 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
465
466 retval = FALSE;
467 }
468#endif // __WXMICROWIN__
469
470 return retval;
471}
472
473void wxApp::CleanUp()
474{
475 // all objects pending for deletion must be deleted first, otherwise we
476 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
477 // call wouldn't succeed as long as any windows still exist), so call the
478 // base class method first and only then do our clean up
479 wxAppBase::CleanUp();
480
481#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
482 wxSetKeyboardHook(FALSE);
483#endif
484
485#if wxUSE_PENWINDOWS
486 wxCleanUpPenWin();
487#endif
488
489 if ( wxDisableButtonBrush )
490 ::DeleteObject( wxDisableButtonBrush );
491
492#if wxUSE_OLE
493#ifdef __WXWINCE__
494 ::CoUninitialize();
495#else
496 ::OleUninitialize();
497#endif
498#endif
499
500 // for an EXE the classes are unregistered when it terminates but DLL may
501 // be loaded several times (load/unload/load) into the same process in
502 // which case the registration will fail after the first time if we don't
503 // unregister the classes now
504 UnregisterWindowClasses();
505
506#if wxUSE_CTL3D
507 Ctl3dUnregister(wxhInstance);
508#endif
509
510 delete wxWinHandleHash;
511 wxWinHandleHash = NULL;
512}
513
514// ----------------------------------------------------------------------------
515// wxApp ctor/dtor
516// ----------------------------------------------------------------------------
517
518wxApp::wxApp()
519{
520 m_printMode = wxPRINT_WINDOWS;
521}
522
523wxApp::~wxApp()
524{
525 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
526 // don't come from main(), so we have to free them
527
528 while ( argc )
529 {
530 // m_argv elements were allocated by wxStrdup()
531 free(argv[--argc]);
532 }
533
534 // but m_argv itself -- using new[]
535 delete [] argv;
536}
537
538void wxApp::OnIdle(wxIdleEvent& event)
539{
540 wxAppBase::OnIdle(event);
541
542#if wxUSE_DC_CACHEING
543 // automated DC cache management: clear the cached DCs and bitmap
544 // if it's likely that the app has finished with them, that is, we
545 // get an idle event and we're not dragging anything.
546 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
547 wxDC::ClearCache();
548#endif // wxUSE_DC_CACHEING
549}
550
551void wxApp::WakeUpIdle()
552{
553 // Send the top window a dummy message so idle handler processing will
554 // start up again. Doing it this way ensures that the idle handler
555 // wakes up in the right thread (see also wxWakeUpMainThread() which does
556 // the same for the main app thread only)
557 wxWindow *topWindow = wxTheApp->GetTopWindow();
558 if ( topWindow )
559 {
560 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
561 {
562 // should never happen
563 wxLogLastError(wxT("PostMessage(WM_NULL)"));
564 }
565 }
566}
567
568void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
569{
570 if (GetTopWindow())
571 GetTopWindow()->Close(TRUE);
572}
573
574// Default behaviour: close the application with prompts. The
575// user can veto the close, and therefore the end session.
576void wxApp::OnQueryEndSession(wxCloseEvent& event)
577{
578 if (GetTopWindow())
579 {
580 if (!GetTopWindow()->Close(!event.CanVeto()))
581 event.Veto(TRUE);
582 }
583}
584
585/* static */
586int wxApp::GetComCtl32Version()
587{
588//FIX ME FOR DIGITALMARS!!
589#if defined(__WXMICROWIN__) || defined(__WXWINCE__) || defined(__DIGITALMARS__)
590 return 0;
591#else
592 // cache the result
593 //
594 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
595 // but as its value should be the same both times it doesn't matter
596 static int s_verComCtl32 = -1;
597
598 if ( s_verComCtl32 == -1 )
599 {
600 // initally assume no comctl32.dll at all
601 s_verComCtl32 = 0;
602
603 // we're prepared to handle the errors
604 wxLogNull noLog;
605
606 // do we have it?
607 wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM);
608
609 // if so, then we can check for the version
610 if ( dllComCtl32.IsLoaded() )
611 {
612#ifdef DLLVER_PLATFORM_WINDOWS
613 // try to use DllGetVersion() if available in _headers_
614 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC, DllGetVersion, dllComCtl32 );
615 if ( pfnDllGetVersion )
616 {
617 DLLVERSIONINFO dvi;
618 dvi.cbSize = sizeof(dvi);
619
620 HRESULT hr = (*pfnDllGetVersion)(&dvi);
621 if ( FAILED(hr) )
622 {
623 wxLogApiError(_T("DllGetVersion"), hr);
624 }
625 else
626 {
627 // this is incompatible with _WIN32_IE values, but
628 // compatible with the other values returned by
629 // GetComCtl32Version()
630 s_verComCtl32 = 100*dvi.dwMajorVersion +
631 dvi.dwMinorVersion;
632 }
633 }
634#endif
635
636 // if DllGetVersion() is unavailable either during compile or
637 // run-time, try to guess the version otherwise
638 if ( !s_verComCtl32 )
639 {
640 // InitCommonControlsEx is unique to 4.70 and later
641 void *pfn = dllComCtl32.GetSymbol(_T("InitCommonControlsEx"));
642 if ( !pfn )
643 {
644 // not found, must be 4.00
645 s_verComCtl32 = 400;
646 }
647 else // 4.70+
648 {
649 // many symbols appeared in comctl32 4.71, could use any of
650 // them except may be DllInstall()
651 pfn = dllComCtl32.GetSymbol(_T("InitializeFlatSB"));
652 if ( !pfn )
653 {
654 // not found, must be 4.70
655 s_verComCtl32 = 470;
656 }
657 else
658 {
659 // found, must be 4.71 or later
660 s_verComCtl32 = 471;
661 }
662 }
663 }
664 }
665 }
666
667 return s_verComCtl32;
668#endif // Microwin/!Microwin
669}
670
671// Yield to incoming messages
672
673bool wxApp::Yield(bool onlyIfNeeded)
674{
675 // MT-FIXME
676 static bool s_inYield = FALSE;
677
678#if wxUSE_LOG
679 // disable log flushing from here because a call to wxYield() shouldn't
680 // normally result in message boxes popping up &c
681 wxLog::Suspend();
682#endif // wxUSE_LOG
683
684 if ( s_inYield )
685 {
686 if ( !onlyIfNeeded )
687 {
688 wxFAIL_MSG( wxT("wxYield called recursively" ) );
689 }
690
691 return FALSE;
692 }
693
694 s_inYield = TRUE;
695
696 // we don't want to process WM_QUIT from here - it should be processed in
697 // the main event loop in order to stop it
698 MSG msg;
699 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
700 msg.message != WM_QUIT )
701 {
702#if wxUSE_THREADS
703 wxMutexGuiLeaveOrEnter();
704#endif // wxUSE_THREADS
705
706 if ( !wxTheApp->Dispatch() )
707 break;
708 }
709
710 // if there are pending events, we must process them.
711 ProcessPendingEvents();
712
713#if wxUSE_LOG
714 // let the logs be flashed again
715 wxLog::Resume();
716#endif // wxUSE_LOG
717
718 s_inYield = FALSE;
719
720 return TRUE;
721}
722