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