1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/appcmn.cpp
3 // Purpose: wxAppConsole and wxAppBase methods common to all platforms
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 18.10.99
7 // RCS-ID: $Id: appcmn.cpp 47229 2007-07-08 05:31:32Z PC $
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #if defined(__BORLANDC__)
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/window.h"
30 #include "wx/bitmap.h"
31 #include "wx/log.h"
32 #include "wx/msgdlg.h"
33 #include "wx/confbase.h"
34 #include "wx/utils.h"
35 #endif
36
37 #include "wx/apptrait.h"
38 #include "wx/cmdline.h"
39 #include "wx/evtloop.h"
40 #include "wx/msgout.h"
41 #include "wx/thread.h"
42 #include "wx/vidmode.h"
43 #include "wx/ptr_scpd.h"
44
45 #ifdef __WXDEBUG__
46 #if wxUSE_STACKWALKER
47 #include "wx/stackwalk.h"
48 #endif // wxUSE_STACKWALKER
49 #endif // __WXDEBUG__
50
51 #if defined(__WXMSW__)
52 #include "wx/msw/private.h" // includes windows.h for LOGFONT
53 #endif
54
55 #if defined(__WXMAC__)
56 #include "wx/mac/private.h"
57 #endif
58
59 #if wxUSE_FONTMAP
60 #include "wx/fontmap.h"
61 #endif // wxUSE_FONTMAP
62
63 // DLL options compatibility check:
64 #include "wx/build.h"
65 WX_CHECK_BUILD_OPTIONS("wxCore")
66
67 WXDLLIMPEXP_DATA_CORE(wxList) wxPendingDelete;
68
69 // ----------------------------------------------------------------------------
70 // wxEventLoopPtr
71 // ----------------------------------------------------------------------------
72
73 // this defines wxEventLoopPtr
wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoop)74 wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoop)
75
76 // ============================================================================
77 // wxAppBase implementation
78 // ============================================================================
79
80 // ----------------------------------------------------------------------------
81 // initialization
82 // ----------------------------------------------------------------------------
83
84 wxAppBase::wxAppBase()
85 {
86 m_topWindow = (wxWindow *)NULL;
87
88 m_useBestVisual = false;
89 m_forceTrueColour = false;
90
91 m_isActive = true;
92
93 m_mainLoop = NULL;
94
95 // We don't want to exit the app if the user code shows a dialog from its
96 // OnInit() -- but this is what would happen if we set m_exitOnFrameDelete
97 // to Yes initially as this dialog would be the last top level window.
98 // OTOH, if we set it to No initially we'll have to overwrite it with Yes
99 // when we enter our OnRun() because we do want the default behaviour from
100 // then on. But this would be a problem if the user code calls
101 // SetExitOnFrameDelete(false) from OnInit().
102 //
103 // So we use the special "Later" value which is such that
104 // GetExitOnFrameDelete() returns false for it but which we know we can
105 // safely (i.e. without losing the effect of the users SetExitOnFrameDelete
106 // call) overwrite in OnRun()
107 m_exitOnFrameDelete = Later;
108 }
109
Initialize(int & argcOrig,wxChar ** argvOrig)110 bool wxAppBase::Initialize(int& argcOrig, wxChar **argvOrig)
111 {
112 if ( !wxAppConsole::Initialize(argcOrig, argvOrig) )
113 return false;
114
115 #if wxUSE_THREADS
116 wxPendingEventsLocker = new wxCriticalSection;
117 #endif
118
119 wxInitializeStockLists();
120
121 wxBitmap::InitStandardHandlers();
122
123 return true;
124 }
125
126 // ----------------------------------------------------------------------------
127 // cleanup
128 // ----------------------------------------------------------------------------
129
~wxAppBase()130 wxAppBase::~wxAppBase()
131 {
132 // this destructor is required for Darwin
133 }
134
CleanUp()135 void wxAppBase::CleanUp()
136 {
137 // clean up all the pending objects
138 DeletePendingObjects();
139
140 // and any remaining TLWs (they remove themselves from wxTopLevelWindows
141 // when destroyed, so iterate until none are left)
142 while ( !wxTopLevelWindows.empty() )
143 {
144 // do not use Destroy() here as it only puts the TLW in pending list
145 // but we want to delete them now
146 delete wxTopLevelWindows.GetFirst()->GetData();
147 }
148
149 // undo everything we did in Initialize() above
150 wxBitmap::CleanUpHandlers();
151
152 wxStockGDI::DeleteAll();
153
154 wxDeleteStockLists();
155
156 delete wxTheColourDatabase;
157 wxTheColourDatabase = NULL;
158
159 delete wxPendingEvents;
160 wxPendingEvents = NULL;
161
162 #if wxUSE_THREADS
163 delete wxPendingEventsLocker;
164 wxPendingEventsLocker = NULL;
165
166 #if wxUSE_VALIDATORS
167 // If we don't do the following, we get an apparent memory leak.
168 ((wxEvtHandler&) wxDefaultValidator).ClearEventLocker();
169 #endif // wxUSE_VALIDATORS
170 #endif // wxUSE_THREADS
171 }
172
173 // ----------------------------------------------------------------------------
174 // various accessors
175 // ----------------------------------------------------------------------------
176
GetTopWindow() const177 wxWindow* wxAppBase::GetTopWindow() const
178 {
179 wxWindow* window = m_topWindow;
180 if (window == NULL && wxTopLevelWindows.GetCount() > 0)
181 window = wxTopLevelWindows.GetFirst()->GetData();
182 return window;
183 }
184
GetDisplayMode() const185 wxVideoMode wxAppBase::GetDisplayMode() const
186 {
187 return wxVideoMode();
188 }
189
GetLayoutDirection() const190 wxLayoutDirection wxAppBase::GetLayoutDirection() const
191 {
192 #if wxUSE_INTL
193 const wxLocale *const locale = wxGetLocale();
194 if ( locale )
195 {
196 const wxLanguageInfo *const
197 info = wxLocale::GetLanguageInfo(locale->GetLanguage());
198
199 if ( info )
200 return info->LayoutDirection;
201 }
202 #endif // wxUSE_INTL
203
204 // we don't know
205 return wxLayout_Default;
206 }
207
208 #if wxUSE_CMDLINE_PARSER
209
210 // ----------------------------------------------------------------------------
211 // GUI-specific command line options handling
212 // ----------------------------------------------------------------------------
213
214 #define OPTION_THEME _T("theme")
215 #define OPTION_MODE _T("mode")
216
OnInitCmdLine(wxCmdLineParser & parser)217 void wxAppBase::OnInitCmdLine(wxCmdLineParser& parser)
218 {
219 // first add the standard non GUI options
220 wxAppConsole::OnInitCmdLine(parser);
221
222 // the standard command line options
223 static const wxCmdLineEntryDesc cmdLineGUIDesc[] =
224 {
225 #ifdef __WXUNIVERSAL__
226 {
227 wxCMD_LINE_OPTION,
228 wxEmptyString,
229 OPTION_THEME,
230 gettext_noop("specify the theme to use"),
231 wxCMD_LINE_VAL_STRING,
232 0x0
233 },
234 #endif // __WXUNIVERSAL__
235
236 #if defined(__WXMGL__)
237 // VS: this is not specific to wxMGL, all fullscreen (framebuffer) ports
238 // should provide this option. That's why it is in common/appcmn.cpp
239 // and not mgl/app.cpp
240 {
241 wxCMD_LINE_OPTION,
242 wxEmptyString,
243 OPTION_MODE,
244 gettext_noop("specify display mode to use (e.g. 640x480-16)"),
245 wxCMD_LINE_VAL_STRING,
246 0x0
247 },
248 #endif // __WXMGL__
249
250 // terminator
251 {
252 wxCMD_LINE_NONE,
253 wxEmptyString,
254 wxEmptyString,
255 wxEmptyString,
256 wxCMD_LINE_VAL_NONE,
257 0x0
258 }
259 };
260
261 parser.SetDesc(cmdLineGUIDesc);
262 }
263
OnCmdLineParsed(wxCmdLineParser & parser)264 bool wxAppBase::OnCmdLineParsed(wxCmdLineParser& parser)
265 {
266 #ifdef __WXUNIVERSAL__
267 wxString themeName;
268 if ( parser.Found(OPTION_THEME, &themeName) )
269 {
270 wxTheme *theme = wxTheme::Create(themeName);
271 if ( !theme )
272 {
273 wxLogError(_("Unsupported theme '%s'."), themeName.c_str());
274 return false;
275 }
276
277 // Delete the defaultly created theme and set the new theme.
278 delete wxTheme::Get();
279 wxTheme::Set(theme);
280 }
281 #endif // __WXUNIVERSAL__
282
283 #if defined(__WXMGL__)
284 wxString modeDesc;
285 if ( parser.Found(OPTION_MODE, &modeDesc) )
286 {
287 unsigned w, h, bpp;
288 if ( wxSscanf(modeDesc.c_str(), _T("%ux%u-%u"), &w, &h, &bpp) != 3 )
289 {
290 wxLogError(_("Invalid display mode specification '%s'."), modeDesc.c_str());
291 return false;
292 }
293
294 if ( !SetDisplayMode(wxVideoMode(w, h, bpp)) )
295 return false;
296 }
297 #endif // __WXMGL__
298
299 return wxAppConsole::OnCmdLineParsed(parser);
300 }
301
302 #endif // wxUSE_CMDLINE_PARSER
303
304 // ----------------------------------------------------------------------------
305 // main event loop implementation
306 // ----------------------------------------------------------------------------
307
MainLoop()308 int wxAppBase::MainLoop()
309 {
310 wxEventLoopTiedPtr mainLoop(&m_mainLoop, new wxEventLoop);
311
312 return m_mainLoop->Run();
313 }
314
ExitMainLoop()315 void wxAppBase::ExitMainLoop()
316 {
317 // we should exit from the main event loop, not just any currently active
318 // (e.g. modal dialog) event loop
319 if ( m_mainLoop && m_mainLoop->IsRunning() )
320 {
321 m_mainLoop->Exit(0);
322 }
323 }
324
Pending()325 bool wxAppBase::Pending()
326 {
327 // use the currently active message loop here, not m_mainLoop, because if
328 // we're showing a modal dialog (with its own event loop) currently the
329 // main event loop is not running anyhow
330 wxEventLoop * const loop = wxEventLoop::GetActive();
331
332 return loop && loop->Pending();
333 }
334
Dispatch()335 bool wxAppBase::Dispatch()
336 {
337 // see comment in Pending()
338 wxEventLoop * const loop = wxEventLoop::GetActive();
339
340 return loop && loop->Dispatch();
341 }
342
343 // ----------------------------------------------------------------------------
344 // OnXXX() hooks
345 // ----------------------------------------------------------------------------
346
OnInitGui()347 bool wxAppBase::OnInitGui()
348 {
349 #ifdef __WXUNIVERSAL__
350 if ( !wxTheme::Get() && !wxTheme::CreateDefault() )
351 return false;
352 #endif // __WXUNIVERSAL__
353
354 return true;
355 }
356
OnRun()357 int wxAppBase::OnRun()
358 {
359 // see the comment in ctor: if the initial value hasn't been changed, use
360 // the default Yes from now on
361 if ( m_exitOnFrameDelete == Later )
362 {
363 m_exitOnFrameDelete = Yes;
364 }
365 //else: it has been changed, assume the user knows what he is doing
366
367 return MainLoop();
368 }
369
OnExit()370 int wxAppBase::OnExit()
371 {
372 #ifdef __WXUNIVERSAL__
373 delete wxTheme::Set(NULL);
374 #endif // __WXUNIVERSAL__
375
376 return wxAppConsole::OnExit();
377 }
378
Exit()379 void wxAppBase::Exit()
380 {
381 ExitMainLoop();
382 }
383
CreateTraits()384 wxAppTraits *wxAppBase::CreateTraits()
385 {
386 return new wxGUIAppTraits;
387 }
388
389 // ----------------------------------------------------------------------------
390 // misc
391 // ----------------------------------------------------------------------------
392
SetActive(bool active,wxWindow * WXUNUSED (lastFocus))393 void wxAppBase::SetActive(bool active, wxWindow * WXUNUSED(lastFocus))
394 {
395 if ( active == m_isActive )
396 return;
397
398 m_isActive = active;
399
400 wxActivateEvent event(wxEVT_ACTIVATE_APP, active);
401 event.SetEventObject(this);
402
403 (void)ProcessEvent(event);
404 }
405
406 // ----------------------------------------------------------------------------
407 // idle handling
408 // ----------------------------------------------------------------------------
409
DeletePendingObjects()410 void wxAppBase::DeletePendingObjects()
411 {
412 wxList::compatibility_iterator node = wxPendingDelete.GetFirst();
413 while (node)
414 {
415 wxObject *obj = node->GetData();
416
417 // remove it from the list first so that if we get back here somehow
418 // during the object deletion (e.g. wxYield called from its dtor) we
419 // wouldn't try to delete it the second time
420 if ( wxPendingDelete.Member(obj) )
421 wxPendingDelete.Erase(node);
422
423 delete obj;
424
425 // Deleting one object may have deleted other pending
426 // objects, so start from beginning of list again.
427 node = wxPendingDelete.GetFirst();
428 }
429 }
430
431 // Returns true if more time is needed.
ProcessIdle()432 bool wxAppBase::ProcessIdle()
433 {
434 // process pending wx events before sending idle events
435 ProcessPendingEvents();
436
437 wxIdleEvent event;
438 bool needMore = false;
439 wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
440 while (node)
441 {
442 wxWindow* win = node->GetData();
443 if (SendIdleEvents(win, event))
444 needMore = true;
445 node = node->GetNext();
446 }
447
448 event.SetEventObject(this);
449 (void) ProcessEvent(event);
450 if (event.MoreRequested())
451 needMore = true;
452
453 // 'Garbage' collection of windows deleted with Close().
454 DeletePendingObjects();
455
456 #if wxUSE_LOG
457 // flush the logged messages if any
458 wxLog::FlushActive();
459 #endif
460
461 wxUpdateUIEvent::ResetUpdateTime();
462
463 return needMore;
464 }
465
466 // Send idle event to window and all subwindows
SendIdleEvents(wxWindow * win,wxIdleEvent & event)467 bool wxAppBase::SendIdleEvents(wxWindow* win, wxIdleEvent& event)
468 {
469 bool needMore = false;
470
471 win->OnInternalIdle();
472
473 if (wxIdleEvent::CanSend(win))
474 {
475 event.SetEventObject(win);
476 win->GetEventHandler()->ProcessEvent(event);
477
478 if (event.MoreRequested())
479 needMore = true;
480 }
481 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
482 while ( node )
483 {
484 wxWindow *child = node->GetData();
485 if (SendIdleEvents(child, event))
486 needMore = true;
487
488 node = node->GetNext();
489 }
490
491 return needMore;
492 }
493
OnIdle(wxIdleEvent & WXUNUSED (event))494 void wxAppBase::OnIdle(wxIdleEvent& WXUNUSED(event))
495 {
496 }
497
498 // ----------------------------------------------------------------------------
499 // exceptions support
500 // ----------------------------------------------------------------------------
501
502 #if wxUSE_EXCEPTIONS
503
OnExceptionInMainLoop()504 bool wxAppBase::OnExceptionInMainLoop()
505 {
506 throw;
507
508 // some compilers are too stupid to know that we never return after throw
509 #if defined(__DMC__) || (defined(_MSC_VER) && _MSC_VER < 1200)
510 return false;
511 #endif
512 }
513
514 #endif // wxUSE_EXCEPTIONS
515
516 // ----------------------------------------------------------------------------
517 // wxGUIAppTraitsBase
518 // ----------------------------------------------------------------------------
519
520 #if wxUSE_LOG
521
CreateLogTarget()522 wxLog *wxGUIAppTraitsBase::CreateLogTarget()
523 {
524 #if wxUSE_LOGGUI
525 return new wxLogGui;
526 #else
527 // we must have something!
528 return new wxLogStderr;
529 #endif
530 }
531
532 #endif // wxUSE_LOG
533
CreateMessageOutput()534 wxMessageOutput *wxGUIAppTraitsBase::CreateMessageOutput()
535 {
536 // The standard way of printing help on command line arguments (app --help)
537 // is (according to common practice):
538 // - console apps: to stderr (on any platform)
539 // - GUI apps: stderr on Unix platforms (!)
540 // message box under Windows and others
541 #ifdef __UNIX__
542 return new wxMessageOutputStderr;
543 #else // !__UNIX__
544 // wxMessageOutputMessageBox doesn't work under Motif
545 #ifdef __WXMOTIF__
546 return new wxMessageOutputLog;
547 #else
548 return new wxMessageOutputMessageBox;
549 #endif
550 #endif // __UNIX__/!__UNIX__
551 }
552
553 #if wxUSE_FONTMAP
554
CreateFontMapper()555 wxFontMapper *wxGUIAppTraitsBase::CreateFontMapper()
556 {
557 return new wxFontMapper;
558 }
559
560 #endif // wxUSE_FONTMAP
561
CreateRenderer()562 wxRendererNative *wxGUIAppTraitsBase::CreateRenderer()
563 {
564 // use the default native renderer by default
565 return NULL;
566 }
567
568 #ifdef __WXDEBUG__
569
ShowAssertDialog(const wxString & msg)570 bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
571 {
572 #if defined(__WXMSW__) || !wxUSE_MSGDLG
573 // under MSW we prefer to use the base class version using ::MessageBox()
574 // even if wxMessageBox() is available because it has less chances to
575 // double fault our app than our wxMessageBox()
576 return wxAppTraitsBase::ShowAssertDialog(msg);
577 #else // wxUSE_MSGDLG
578 wxString msgDlg = msg;
579
580 #if wxUSE_STACKWALKER
581 // on Unix stack frame generation may take some time, depending on the
582 // size of the executable mainly... warn the user that we are working
583 wxFprintf(stderr, wxT("[Debug] Generating a stack trace... please wait"));
584 fflush(stderr);
585
586 const wxString stackTrace = GetAssertStackTrace();
587 if ( !stackTrace.empty() )
588 msgDlg << _T("\n\nCall stack:\n") << stackTrace;
589 #endif // wxUSE_STACKWALKER
590
591 // this message is intentionally not translated -- it is for
592 // developpers only
593 msgDlg += wxT("\nDo you want to stop the program?\n")
594 wxT("You can also choose [Cancel] to suppress ")
595 wxT("further warnings.");
596
597 #ifdef __WXMAC__
598 // in order to avoid reentrancy problems, use the lowest alert API available
599 CFOptionFlags exitButton;
600 wxMacCFStringHolder cfText(msgDlg);
601 OSStatus err = CFUserNotificationDisplayAlert(
602 0, kAlertStopAlert, NULL, NULL, NULL, CFSTR("wxWidgets Debug Alert"), cfText,
603 CFSTR("Yes"), CFSTR("No"), CFSTR("Cancel"), &exitButton );
604 if ( err == noErr )
605 {
606 switch( exitButton )
607 {
608 case 0 : // yes
609 wxTrap();
610 break;
611 case 2 : // cancel
612 // no more asserts
613 return true;
614 case 1 : // no -> nothing to do
615 break ;
616 }
617 }
618 #else
619 switch ( wxMessageBox(msgDlg, wxT("wxWidgets Debug Alert"),
620 wxYES_NO | wxCANCEL | wxICON_STOP ) )
621 {
622 case wxYES:
623 wxTrap();
624 break;
625
626 case wxCANCEL:
627 // no more asserts
628 return true;
629
630 //case wxNO: nothing to do
631 }
632 #endif
633 return false;
634 #endif // !wxUSE_MSGDLG/wxUSE_MSGDLG
635 }
636
637 #endif // __WXDEBUG__
638
HasStderr()639 bool wxGUIAppTraitsBase::HasStderr()
640 {
641 // we consider that under Unix stderr always goes somewhere, even if the
642 // user doesn't always see it under GUI desktops
643 #ifdef __UNIX__
644 return true;
645 #else
646 return false;
647 #endif
648 }
649
ScheduleForDestroy(wxObject * object)650 void wxGUIAppTraitsBase::ScheduleForDestroy(wxObject *object)
651 {
652 if ( !wxPendingDelete.Member(object) )
653 wxPendingDelete.Append(object);
654 }
655
RemoveFromPendingDelete(wxObject * object)656 void wxGUIAppTraitsBase::RemoveFromPendingDelete(wxObject *object)
657 {
658 wxPendingDelete.DeleteObject(object);
659 }
660
661 #if wxUSE_SOCKETS
662
663 #if defined(__WINDOWS__)
664 #include "wx/msw/gsockmsw.h"
665 #elif defined(__UNIX__) || defined(__DARWIN__) || defined(__OS2__)
666 #include "wx/unix/gsockunx.h"
667 #elif defined(__WXMAC__)
668 #include <MacHeaders.c>
669 #define OTUNIXERRORS 1
670 #include <OpenTransport.h>
671 #include <OpenTransportProviders.h>
672 #include <OpenTptInternet.h>
673
674 #include "wx/mac/gsockmac.h"
675 #else
676 #error "Must include correct GSocket header here"
677 #endif
678
GetSocketGUIFunctionsTable()679 GSocketGUIFunctionsTable* wxGUIAppTraitsBase::GetSocketGUIFunctionsTable()
680 {
681 #if defined(__WXMAC__) && !defined(__DARWIN__)
682 // NB: wxMac CFM does not have any GUI-specific functions in gsocket.c and
683 // so it doesn't need this table at all
684 return NULL;
685 #else // !__WXMAC__ || __DARWIN__
686 static GSocketGUIFunctionsTableConcrete table;
687 return &table;
688 #endif // !__WXMAC__ || __DARWIN__
689 }
690
691 #endif
692