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