]> git.saurik.com Git - wxWidgets.git/blob - src/msw/app.cpp
attempt to fix another occurence of WS_EX_CONTROLPARENT-related infinite loop
[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 #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_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 #include "wx/msw/wrapcctl.h"
89
90 #if (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
91 !defined(__CYGWIN__) && !defined(__DIGITALMARS__) && !defined(__WXWINCE__) && \
92 (!defined(_MSC_VER) || (_MSC_VER > 1100))
93 #include <shlwapi.h>
94 #endif
95
96 // ---------------------------------------------------------------------------
97 // global variables
98 // ---------------------------------------------------------------------------
99
100 extern wxList WXDLLEXPORT wxPendingDelete;
101
102 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
103 extern void wxSetKeyboardHook(bool doIt);
104 #endif
105
106 MSG s_currentMsg;
107
108 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
109 // with NR suffix - wxWindow::MSWCreate() supposes this
110 const wxChar *wxCanvasClassName = wxT("wxWindowClass");
111 const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
112 const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
113 const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
114 const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
115 const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
116
117 HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
118
119 // ----------------------------------------------------------------------------
120 // private functions
121 // ----------------------------------------------------------------------------
122
123 LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
124
125 // ===========================================================================
126 // wxGUIAppTraits implementation
127 // ===========================================================================
128
129 // private class which we use to pass parameters from BeforeChildWaitLoop() to
130 // AfterChildWaitLoop()
131 struct 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
143 void *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(),
171 wxID_ANY,
172 wxEmptyString,
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
182 void wxGUIAppTraits::AlwaysYield()
183 {
184 wxYield();
185 }
186
187 void 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
201 bool wxGUIAppTraits::DoMessageFromThreadWait()
202 {
203 return !wxTheApp || wxTheApp->DoMessage();
204 }
205
206 wxToolkitInfo& wxGUIAppTraits::GetToolkitInfo()
207 {
208 static wxToolkitInfo info;
209 wxToolkitInfo& baseInfo = wxAppTraits::GetToolkitInfo();
210 info.versionMajor = baseInfo.versionMajor;
211 info.versionMinor = baseInfo.versionMinor;
212 info.os = baseInfo.os;
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
219 return info;
220 }
221
222 // ===========================================================================
223 // wxApp implementation
224 // ===========================================================================
225
226 int wxApp::m_nCmdShow = SW_SHOWNORMAL;
227
228 // ---------------------------------------------------------------------------
229 // wxWin macros
230 // ---------------------------------------------------------------------------
231
232 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
233
234 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
235 EVT_IDLE(wxApp::OnIdle)
236 EVT_END_SESSION(wxApp::OnEndSession)
237 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
238 END_EVENT_TABLE()
239
240 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
241 // fails
242 class wxCallBaseCleanup
243 {
244 public:
245 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
246 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
247
248 void Dismiss() { m_app = NULL; }
249
250 private:
251 wxApp *m_app;
252 };
253
254 //// Initialize
255 bool wxApp::Initialize(int& argc, wxChar **argv)
256 {
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
263 // the first thing to do is to check if we're trying to run an Unicode
264 // program under Win9x w/o MSLU emulation layer - if so, abort right now
265 // as it has no chance to work
266 #if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
267 if ( wxGetOsVersion() != wxWINDOWS_NT && wxGetOsVersion() != wxWINDOWS_CE )
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,
275 _T("This program uses Unicode and requires Windows NT/2000/XP/CE.\nProgram aborted."),
276 _T("wxWindows Fatal Error"),
277 MB_ICONERROR | MB_OK
278 );
279
280 return FALSE;
281 }
282 #endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
283
284 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
285 InitCommonControls();
286 #endif // __WIN95__
287
288 #if wxUSE_OLE || wxUSE_DRAG_AND_DROP
289
290 #ifdef __WIN16__
291 // for OLE, enlarge message queue to be as large as possible
292 int iMsg = 96;
293 while (!SetMessageQueue(iMsg) && (iMsg -= 8))
294 ;
295 #endif // Win16
296
297 #if wxUSE_OLE
298 // we need to initialize OLE library
299 #ifdef __WXWINCE__
300 if ( FAILED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
301 wxLogError(_("Cannot initialize OLE"));
302 #else
303 if ( FAILED(::OleInitialize(NULL)) )
304 wxLogError(_("Cannot initialize OLE"));
305 #endif
306 #endif
307
308 #endif // wxUSE_OLE
309
310 #if wxUSE_CTL3D
311 if (!Ctl3dRegister(wxhInstance))
312 wxLogError(wxT("Cannot register CTL3D"));
313
314 Ctl3dAutoSubclass(wxhInstance);
315 #endif // wxUSE_CTL3D
316
317 RegisterWindowClasses();
318
319 #if defined(__WXMICROWIN__) && !defined(__WXWINCE__)
320 // Create the brush for disabling bitmap buttons
321
322 LOGBRUSH lb;
323 lb.lbStyle = BS_PATTERN;
324 lb.lbColor = 0;
325 lb.lbHatch = (int)LoadBitmap( wxhInstance, wxT("wxDISABLE_BUTTON_BITMAP") );
326 if ( lb.lbHatch )
327 {
328 wxDisableButtonBrush = ::CreateBrushIndirect( & lb );
329 ::DeleteObject( (HGDIOBJ)lb.lbHatch );
330 }
331 //else: wxWindows resources are probably not linked in
332 #endif
333
334 #if wxUSE_PENWINDOWS
335 wxRegisterPenWin();
336 #endif
337
338 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
339
340 // This is to foil optimizations in Visual C++ that throw out dummy.obj.
341 // PLEASE DO NOT ALTER THIS.
342 #if defined(__VISUALC__) && defined(__WIN16__) && !defined(WXMAKINGDLL)
343 extern char wxDummyChar;
344 if (wxDummyChar) wxDummyChar++;
345 #endif
346
347 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
348 wxSetKeyboardHook(TRUE);
349 #endif
350
351 callBaseCleanup.Dismiss();
352
353 return true;
354 }
355
356 // ---------------------------------------------------------------------------
357 // RegisterWindowClasses
358 // ---------------------------------------------------------------------------
359
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.
363 bool wxApp::RegisterWindowClasses()
364 {
365 WNDCLASS wndclass;
366 wxZeroMemory(wndclass);
367
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
373 // the fields which are common to all classes
374 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
375 wndclass.hInstance = wxhInstance;
376 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
377
378 // Register the frame window class.
379 wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
380 wndclass.lpszClassName = wxCanvasClassName;
381 wndclass.style = styleNormal;
382
383 if ( !RegisterClass(&wndclass) )
384 {
385 wxLogLastError(wxT("RegisterClass(frame)"));
386 }
387
388 // "no redraw" frame
389 wndclass.lpszClassName = wxCanvasClassNameNR;
390 wndclass.style = styleNoRedraw;
391
392 if ( !RegisterClass(&wndclass) )
393 {
394 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
395 }
396
397 // Register the MDI frame window class.
398 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
399 wndclass.lpszClassName = wxMDIFrameClassName;
400 wndclass.style = styleNormal;
401
402 if ( !RegisterClass(&wndclass) )
403 {
404 wxLogLastError(wxT("RegisterClass(MDI parent)"));
405 }
406
407 // "no redraw" MDI frame
408 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
409 wndclass.style = styleNoRedraw;
410
411 if ( !RegisterClass(&wndclass) )
412 {
413 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
414 }
415
416 // Register the MDI child frame window class.
417 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
418 wndclass.lpszClassName = wxMDIChildFrameClassName;
419 wndclass.style = styleNormal;
420
421 if ( !RegisterClass(&wndclass) )
422 {
423 wxLogLastError(wxT("RegisterClass(MDI child)"));
424 }
425
426 // "no redraw" MDI child frame
427 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
428 wndclass.style = styleNoRedraw;
429
430 if ( !RegisterClass(&wndclass) )
431 {
432 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
433 }
434
435 return TRUE;
436 }
437
438 // ---------------------------------------------------------------------------
439 // UnregisterWindowClasses
440 // ---------------------------------------------------------------------------
441
442 bool wxApp::UnregisterWindowClasses()
443 {
444 bool retval = TRUE;
445
446 #ifndef __WXMICROWIN__
447 // MDI frame window class.
448 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
449 {
450 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
451
452 retval = FALSE;
453 }
454
455 // "no redraw" MDI frame
456 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
457 {
458 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
459
460 retval = FALSE;
461 }
462
463 // MDI child frame window class.
464 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
465 {
466 wxLogLastError(wxT("UnregisterClass(MDI child)"));
467
468 retval = FALSE;
469 }
470
471 // "no redraw" MDI child frame
472 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
473 {
474 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
475
476 retval = FALSE;
477 }
478
479 // canvas class name
480 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
481 {
482 wxLogLastError(wxT("UnregisterClass(canvas)"));
483
484 retval = FALSE;
485 }
486
487 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
488 {
489 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
490
491 retval = FALSE;
492 }
493 #endif // __WXMICROWIN__
494
495 return retval;
496 }
497
498 void wxApp::CleanUp()
499 {
500 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
501 wxSetKeyboardHook(FALSE);
502 #endif
503
504 #if wxUSE_PENWINDOWS
505 wxCleanUpPenWin();
506 #endif
507
508 if ( wxDisableButtonBrush )
509 ::DeleteObject( wxDisableButtonBrush );
510
511 #if wxUSE_OLE
512 #ifdef __WXWINCE__
513 ::CoUninitialize();
514 #else
515 ::OleUninitialize();
516 #endif
517 #endif
518
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();
524
525 #if wxUSE_CTL3D
526 Ctl3dUnregister(wxhInstance);
527 #endif
528
529 delete wxWinHandleHash;
530 wxWinHandleHash = NULL;
531
532 wxAppBase::CleanUp();
533 }
534
535 // ----------------------------------------------------------------------------
536 // wxApp ctor/dtor
537 // ----------------------------------------------------------------------------
538
539 wxApp::wxApp()
540 {
541 m_printMode = wxPRINT_WINDOWS;
542 }
543
544 wxApp::~wxApp()
545 {
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 )
550 {
551 // m_argv elements were allocated by wxStrdup()
552 free(argv[--argc]);
553 }
554
555 // but m_argv itself -- using new[]
556 delete [] argv;
557 }
558
559 bool wxApp::Initialized()
560 {
561 #ifndef _WINDLL
562 if (GetTopWindow())
563 return TRUE;
564 else
565 return FALSE;
566 #else // Assume initialized if DLL (no way of telling)
567 return TRUE;
568 #endif
569 }
570
571 /*
572 * Get and process a message, returning FALSE if WM_QUIT
573 * received (and also set the flag telling the app to exit the main loop)
574 *
575 */
576 bool wxApp::DoMessage()
577 {
578 BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
579 if ( rc == 0 )
580 {
581 // got WM_QUIT
582 m_keepGoing = FALSE;
583
584 return FALSE;
585 }
586 else if ( rc == -1 )
587 {
588 // should never happen, but let's test for it nevertheless
589 wxLogLastError(wxT("GetMessage"));
590 }
591 else
592 {
593 #if wxUSE_THREADS
594 wxASSERT_MSG( wxThread::IsMain(),
595 wxT("only the main thread can process Windows messages") );
596
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
607 // leave out WM_COMMAND messages: too dangerous, sometimes
608 // the message will be processed twice
609 if ( !wxIsWaitingForThread() ||
610 s_currentMsg.message != WM_COMMAND )
611 {
612 s_aSavedMessages.Add(s_currentMsg);
613 }
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
628 size_t count = s_aSavedMessages.GetCount();
629 for ( size_t n = 0; n < count; n++ )
630 {
631 MSG& msg = s_aSavedMessages[n];
632
633 DoMessage((WXMSG *)&msg);
634 }
635
636 s_aSavedMessages.Empty();
637 }
638 }
639 #endif // wxUSE_THREADS
640
641 // Process the message
642 DoMessage((WXMSG *)&s_currentMsg);
643 }
644
645 return TRUE;
646 }
647
648 void wxApp::DoMessage(WXMSG *pMsg)
649 {
650 if ( !ProcessMessage(pMsg) )
651 {
652 ::TranslateMessage((MSG *)pMsg);
653 ::DispatchMessage((MSG *)pMsg);
654 }
655 }
656
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
671 int wxApp::MainLoop()
672 {
673 m_keepGoing = TRUE;
674
675 while ( m_keepGoing )
676 {
677 #if wxUSE_THREADS
678 wxMutexGuiLeaveOrEnter();
679 #endif // wxUSE_THREADS
680
681 while ( !Pending() && ProcessIdle() )
682 ;
683
684 // a message came or no more idle processing to do
685 DoMessage();
686 }
687
688 return s_currentMsg.wParam;
689 }
690
691 void wxApp::ExitMainLoop()
692 {
693 // this will set m_keepGoing to FALSE a bit later
694 ::PostQuitMessage(0);
695 }
696
697 bool wxApp::Pending()
698 {
699 return ::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) != 0;
700 }
701
702 void wxApp::Dispatch()
703 {
704 DoMessage();
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 */
712
713 bool wxApp::ProcessMessage(WXMSG *wxmsg)
714 {
715 MSG *msg = (MSG *)wxmsg;
716 HWND hwnd = msg->hwnd;
717 wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd);
718
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 {
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
727 while ( hwnd && ::GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
728 {
729 hwnd = ::GetParent(hwnd);
730 }
731
732 return hwnd && ::IsDialogMessage(hwnd, msg) != 0;
733 }
734
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
738 if ( (msg->message == WM_MOUSEMOVE) )
739 {
740 wxToolTip *tt = wndThis->GetToolTip();
741 if ( tt )
742 {
743 tt->RelayEvent(wxmsg);
744 }
745 }
746 #endif // wxUSE_TOOLTIPS
747
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
756 // try translations first: the accelerators override everything
757 wxWindow *wnd;
758
759 for ( wnd = wndThis; wnd; wnd = wnd->GetParent() )
760 {
761 if ( wnd->MSWTranslateMessage(wxmsg))
762 return TRUE;
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() )
768 break;
769 }
770
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() )
775 {
776 if ( wnd->MSWProcessMessage(wxmsg) )
777 return TRUE;
778 }
779
780 // no special preprocessing for this message, dispatch it normally
781 return FALSE;
782 }
783
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.
790 bool wxIsInOnIdleFlag = FALSE;
791
792 void wxApp::OnIdle(wxIdleEvent& event)
793 {
794 // Avoid recursion (via ProcessEvent default case)
795 if ( wxIsInOnIdleFlag )
796 return;
797
798 wxIsInOnIdleFlag = TRUE;
799
800 wxAppBase::OnIdle(event);
801
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.
806 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
807 wxDC::ClearCache();
808 #endif // wxUSE_DC_CACHEING
809
810 wxIsInOnIdleFlag = FALSE;
811 }
812
813 void 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
830 void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
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.
838 void wxApp::OnQueryEndSession(wxCloseEvent& event)
839 {
840 if (GetTopWindow())
841 {
842 if (!GetTopWindow()->Close(!event.CanVeto()))
843 event.Veto(TRUE);
844 }
845 }
846
847 /* static */
848 int wxApp::GetComCtl32Version()
849 {
850 //FIX ME FOR DIGITALMARS!!
851 #if defined(__WXMICROWIN__) || defined(__WXWINCE__) || defined(__DIGITALMARS__)
852 return 0;
853 #else
854 // cache the result
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
858 static int s_verComCtl32 = -1;
859
860 if ( s_verComCtl32 == -1 )
861 {
862 // initally assume no comctl32.dll at all
863 s_verComCtl32 = 0;
864
865 // we're prepared to handle the errors
866 wxLogNull noLog;
867
868 // do we have it?
869 wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM);
870
871 // if so, then we can check for the version
872 if ( dllComCtl32.IsLoaded() )
873 {
874 #ifdef DLLVER_PLATFORM_WINDOWS
875 // try to use DllGetVersion() if available in _headers_
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) )
884 {
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 }
896 #endif
897
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;
908 }
909 else // 4.70+
910 {
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 )
915 {
916 // not found, must be 4.70
917 s_verComCtl32 = 470;
918 }
919 else
920 {
921 // found, must be 4.71 or later
922 s_verComCtl32 = 471;
923 }
924 }
925 }
926 }
927 }
928
929 return s_verComCtl32;
930 #endif // Microwin/!Microwin
931 }
932
933 // Yield to incoming messages
934
935 bool wxApp::Yield(bool onlyIfNeeded)
936 {
937 // MT-FIXME
938 static bool s_inYield = FALSE;
939
940 #if wxUSE_LOG
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();
944 #endif // wxUSE_LOG
945
946 if ( s_inYield )
947 {
948 if ( !onlyIfNeeded )
949 {
950 wxFAIL_MSG( wxT("wxYield called recursively" ) );
951 }
952
953 return FALSE;
954 }
955
956 s_inYield = TRUE;
957
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
960 MSG msg;
961 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
962 msg.message != WM_QUIT )
963 {
964 #if wxUSE_THREADS
965 wxMutexGuiLeaveOrEnter();
966 #endif // wxUSE_THREADS
967
968 if ( !wxTheApp->DoMessage() )
969 break;
970 }
971
972 // if there are pending events, we must process them.
973 ProcessPendingEvents();
974
975 #if wxUSE_LOG
976 // let the logs be flashed again
977 wxLog::Resume();
978 #endif // wxUSE_LOG
979
980 s_inYield = FALSE;
981
982 return TRUE;
983 }
984