1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/appcmn.cpp
3 // Purpose:     wxAppBase methods common to all platforms
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     18.10.99
7 // Copyright:   (c) Vadim Zeitlin
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ---------------------------------------------------------------------------
16 // headers
17 // ---------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 
23 #ifndef WX_PRECOMP
24     #include "wx/app.h"
25     #include "wx/window.h"
26     #include "wx/bitmap.h"
27     #include "wx/log.h"
28     #include "wx/module.h"
29     #include "wx/msgdlg.h"
30     #include "wx/confbase.h"
31     #include "wx/utils.h"
32     #include "wx/wxcrtvararg.h"
33 #endif
34 
35 #include "wx/apptrait.h"
36 #include "wx/cmdline.h"
37 #include "wx/msgout.h"
38 #include "wx/richmsgdlg.h"
39 #include "wx/thread.h"
40 #include "wx/vidmode.h"
41 #include "wx/evtloop.h"
42 
43 #if wxUSE_FONTMAP
44     #include "wx/fontmap.h"
45 #endif // wxUSE_FONTMAP
46 
47 // DLL options compatibility check:
48 #include "wx/build.h"
49 WX_CHECK_BUILD_OPTIONS("wxCore")
50 
51 // ============================================================================
52 // wxAppBase implementation
53 // ============================================================================
54 
55 // ----------------------------------------------------------------------------
56 // initialization
57 // ----------------------------------------------------------------------------
58 
wxAppBase()59 wxAppBase::wxAppBase()
60 {
61     m_topWindow = NULL;
62 
63     m_useBestVisual = false;
64     m_forceTrueColour = false;
65 
66     m_isActive = true;
67 
68     // We don't want to exit the app if the user code shows a dialog from its
69     // OnInit() -- but this is what would happen if we set m_exitOnFrameDelete
70     // to Yes initially as this dialog would be the last top level window.
71     // OTOH, if we set it to No initially we'll have to overwrite it with Yes
72     // when we enter our OnRun() because we do want the default behaviour from
73     // then on. But this would be a problem if the user code calls
74     // SetExitOnFrameDelete(false) from OnInit().
75     //
76     // So we use the special "Later" value which is such that
77     // GetExitOnFrameDelete() returns false for it but which we know we can
78     // safely (i.e. without losing the effect of the users SetExitOnFrameDelete
79     // call) overwrite in OnRun()
80     m_exitOnFrameDelete = Later;
81 }
82 
Initialize(int & argcOrig,wxChar ** argvOrig)83 bool wxAppBase::Initialize(int& argcOrig, wxChar **argvOrig)
84 {
85 #ifdef __DARWIN__
86     // Mac OS X passes a process serial number command line argument when
87     // the application is launched from the Finder. This argument must be
88     // removed from the command line arguments before being handled by the
89     // application (otherwise applications would need to handle it)
90     //
91     // Notice that this has to be done for all ports that can be used under OS
92     // X (e.g. wxGTK) and not just wxOSX itself, hence this code is here and
93     // not in a port-specific file.
94     if ( argcOrig > 1 )
95     {
96         static const wxChar *ARG_PSN = wxT("-psn_");
97         if ( wxStrncmp(argvOrig[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
98         {
99             // remove this argument
100             --argcOrig;
101             memmove(argvOrig + 1, argvOrig + 2, argcOrig * sizeof(wxChar*));
102         }
103     }
104 #endif // __DARWIN__
105 
106     if ( !wxAppConsole::Initialize(argcOrig, argvOrig) )
107         return false;
108 
109     wxInitializeStockLists();
110 
111     wxBitmap::InitStandardHandlers();
112 
113     // for compatibility call the old initialization function too
114     if ( !OnInitGui() )
115         return false;
116 
117     return true;
118 }
119 
120 // ----------------------------------------------------------------------------
121 // cleanup
122 // ----------------------------------------------------------------------------
123 
~wxAppBase()124 wxAppBase::~wxAppBase()
125 {
126     // this destructor is required for Darwin
127 }
128 
DeleteAllTLWs()129 void wxAppBase::DeleteAllTLWs()
130 {
131     // TLWs remove themselves from wxTopLevelWindows when destroyed, so iterate
132     // until none are left.
133     while ( !wxTopLevelWindows.empty() )
134     {
135         // do not use Destroy() here as it only puts the TLW in pending list
136         // but we want to delete them now
137         delete wxTopLevelWindows.GetFirst()->GetData();
138     }
139 }
140 
CleanUp()141 void wxAppBase::CleanUp()
142 {
143     // Clean up any still pending objects. Normally there shouldn't any as we
144     // already do this in OnExit(), but this could happen if the user code has
145     // somehow managed to create more of them since then or just forgot to call
146     // the base class OnExit().
147     DeletePendingObjects();
148 
149     // and any remaining TLWs
150     DeleteAllTLWs();
151 
152     // undo everything we did in Initialize() above
153     wxBitmap::CleanUpHandlers();
154 
155     wxStockGDI::DeleteAll();
156 
157     wxDeleteStockLists();
158 
159     wxDELETE(wxTheColourDatabase);
160 
161     wxAppConsole::CleanUp();
162 }
163 
164 // ----------------------------------------------------------------------------
165 // various accessors
166 // ----------------------------------------------------------------------------
167 
GetTopWindow() const168 wxWindow* wxAppBase::GetTopWindow() const
169 {
170     wxWindow* window = m_topWindow;
171 
172     // If there is no top window or it is about to be destroyed,
173     // we need to search for the first TLW which is not pending delete
174     if ( !window || wxPendingDelete.Member(window) )
175     {
176         window = NULL;
177         wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
178         while ( node )
179         {
180             wxWindow* win = node->GetData();
181             if ( !wxPendingDelete.Member(win) )
182             {
183                 window = win;
184                 break;
185             }
186             node = node->GetNext();
187         }
188     }
189 
190     return window;
191 }
192 
193 /* static */
GetMainTopWindow()194 wxWindow* wxAppBase::GetMainTopWindow()
195 {
196     const wxAppBase* const app = static_cast<wxAppBase*>(GetInstance());
197 
198     return app ? app->GetTopWindow() : NULL;
199 }
200 
GetDisplayMode() const201 wxVideoMode wxAppBase::GetDisplayMode() const
202 {
203     return wxVideoMode();
204 }
205 
GetLayoutDirection() const206 wxLayoutDirection wxAppBase::GetLayoutDirection() const
207 {
208 #if wxUSE_INTL
209     const wxLocale *const locale = wxGetLocale();
210     if ( locale )
211     {
212         const wxLanguageInfo *const
213             info = wxLocale::GetLanguageInfo(locale->GetLanguage());
214 
215         if ( info )
216             return info->LayoutDirection;
217     }
218 #endif // wxUSE_INTL
219 
220     // we don't know
221     return wxLayout_Default;
222 }
223 
224 #if wxUSE_CMDLINE_PARSER
225 
226 // ----------------------------------------------------------------------------
227 // GUI-specific command line options handling
228 // ----------------------------------------------------------------------------
229 
230 #ifdef __WXUNIVERSAL__
231 #define OPTION_THEME   "theme"
232 #endif
233 #if defined(__WXDFB__)
234 #define OPTION_MODE    "mode"
235 #endif
236 
OnInitCmdLine(wxCmdLineParser & parser)237 void wxAppBase::OnInitCmdLine(wxCmdLineParser& parser)
238 {
239     // first add the standard non GUI options
240     wxAppConsole::OnInitCmdLine(parser);
241 
242     // the standard command line options
243     static const wxCmdLineEntryDesc cmdLineGUIDesc[] =
244     {
245 #ifdef __WXUNIVERSAL__
246         {
247             wxCMD_LINE_OPTION,
248             NULL,
249             OPTION_THEME,
250             gettext_noop("specify the theme to use"),
251             wxCMD_LINE_VAL_STRING,
252             0x0
253         },
254 #endif // __WXUNIVERSAL__
255 
256 #if defined(__WXDFB__)
257         // VS: this is not specific to wxDFB, all fullscreen (framebuffer) ports
258         //     should provide this option. That's why it is in common/appcmn.cpp
259         //     and not dfb/app.cpp
260         {
261             wxCMD_LINE_OPTION,
262             NULL,
263             OPTION_MODE,
264             gettext_noop("specify display mode to use (e.g. 640x480-16)"),
265             wxCMD_LINE_VAL_STRING,
266             0x0
267         },
268 #endif // __WXDFB__
269 
270         // terminator
271         wxCMD_LINE_DESC_END
272     };
273 
274     parser.SetDesc(cmdLineGUIDesc);
275 }
276 
OnCmdLineParsed(wxCmdLineParser & parser)277 bool wxAppBase::OnCmdLineParsed(wxCmdLineParser& parser)
278 {
279 #ifdef __WXUNIVERSAL__
280     wxString themeName;
281     if ( parser.Found(OPTION_THEME, &themeName) )
282     {
283         wxTheme *theme = wxTheme::Create(themeName);
284         if ( !theme )
285         {
286             wxLogError(_("Unsupported theme '%s'."), themeName.c_str());
287             return false;
288         }
289 
290         // Delete the defaultly created theme and set the new theme.
291         delete wxTheme::Get();
292         wxTheme::Set(theme);
293     }
294 #endif // __WXUNIVERSAL__
295 
296 #if defined(__WXDFB__)
297     wxString modeDesc;
298     if ( parser.Found(OPTION_MODE, &modeDesc) )
299     {
300         unsigned w, h, bpp;
301         if ( wxSscanf(modeDesc.c_str(), wxT("%ux%u-%u"), &w, &h, &bpp) != 3 )
302         {
303             wxLogError(_("Invalid display mode specification '%s'."), modeDesc.c_str());
304             return false;
305         }
306 
307         if ( !SetDisplayMode(wxVideoMode(w, h, bpp)) )
308             return false;
309     }
310 #endif // __WXDFB__
311 
312     return wxAppConsole::OnCmdLineParsed(parser);
313 }
314 
315 #endif // wxUSE_CMDLINE_PARSER
316 
317 // ----------------------------------------------------------------------------
318 // OnXXX() hooks
319 // ----------------------------------------------------------------------------
320 
OnInitGui()321 bool wxAppBase::OnInitGui()
322 {
323 #ifdef __WXUNIVERSAL__
324     if ( !wxTheme::Get() && !wxTheme::CreateDefault() )
325         return false;
326 #endif // __WXUNIVERSAL__
327 
328     return true;
329 }
330 
OnRun()331 int wxAppBase::OnRun()
332 {
333     // see the comment in ctor: if the initial value hasn't been changed, use
334     // the default Yes from now on
335     if ( m_exitOnFrameDelete == Later )
336     {
337         m_exitOnFrameDelete = Yes;
338     }
339     //else: it has been changed, assume the user knows what he is doing
340 
341     return wxAppConsole::OnRun();
342 }
343 
OnExit()344 int wxAppBase::OnExit()
345 {
346 #ifdef __WXUNIVERSAL__
347     delete wxTheme::Set(NULL);
348 #endif // __WXUNIVERSAL__
349 
350     return wxAppConsole::OnExit();
351 }
352 
CreateTraits()353 wxAppTraits *wxAppBase::CreateTraits()
354 {
355     return new wxGUIAppTraits;
356 }
357 
358 // ----------------------------------------------------------------------------
359 // misc
360 // ----------------------------------------------------------------------------
361 
SetActive(bool active,wxWindow * WXUNUSED (lastFocus))362 void wxAppBase::SetActive(bool active, wxWindow * WXUNUSED(lastFocus))
363 {
364     if ( active == m_isActive )
365         return;
366 
367     m_isActive = active;
368 
369     wxActivateEvent event(wxEVT_ACTIVATE_APP, active);
370     event.SetEventObject(this);
371 
372     (void)ProcessEvent(event);
373 }
374 
SafeYield(wxWindow * win,bool onlyIfNeeded)375 bool wxAppBase::SafeYield(wxWindow *win, bool onlyIfNeeded)
376 {
377     wxWindowDisabler wd(win);
378 
379     wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
380 
381     return loop && loop->Yield(onlyIfNeeded);
382 }
383 
SafeYieldFor(wxWindow * win,long eventsToProcess)384 bool wxAppBase::SafeYieldFor(wxWindow *win, long eventsToProcess)
385 {
386     wxWindowDisabler wd(win);
387 
388     wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
389 
390     return loop && loop->YieldFor(eventsToProcess);
391 }
392 
393 
394 // ----------------------------------------------------------------------------
395 // idle handling
396 // ----------------------------------------------------------------------------
397 
398 // Returns true if more time is needed.
ProcessIdle()399 bool wxAppBase::ProcessIdle()
400 {
401     // call the base class version first to send the idle event to wxTheApp
402     // itself
403     bool needMore = wxAppConsoleBase::ProcessIdle();
404     wxIdleEvent event;
405     wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
406     while (node)
407     {
408         wxWindow* win = node->GetData();
409 
410         // Don't send idle events to the windows that are about to be destroyed
411         // anyhow, this is wasteful and unexpected.
412         if ( !wxPendingDelete.Member(win) && win->SendIdleEvents(event) )
413             needMore = true;
414         node = node->GetNext();
415     }
416 
417     wxUpdateUIEvent::ResetUpdateTime();
418 
419     return needMore;
420 }
421 
422 // ----------------------------------------------------------------------------
423 // wxGUIAppTraitsBase
424 // ----------------------------------------------------------------------------
425 
426 #if wxUSE_LOG
427 
CreateLogTarget()428 wxLog *wxGUIAppTraitsBase::CreateLogTarget()
429 {
430 #if wxUSE_LOGGUI
431 #ifndef __WXOSX_IPHONE__
432     return new wxLogGui;
433 #else
434     return new wxLogStderr;
435 #endif
436 #else
437     // we must have something!
438     return new wxLogStderr;
439 #endif
440 }
441 
442 #endif // wxUSE_LOG
443 
CreateMessageOutput()444 wxMessageOutput *wxGUIAppTraitsBase::CreateMessageOutput()
445 {
446     // The standard way of printing help on command line arguments (app --help)
447     // is (according to common practice):
448     //     - console apps: to stderr (on any platform)
449     //     - GUI apps: stderr on Unix platforms (!)
450     //                 stderr if available and message box otherwise on others
451     //                 (currently stderr only Windows if app running from console)
452 #ifdef __UNIX__
453     return new wxMessageOutputStderr;
454 #else // !__UNIX__
455     // wxMessageOutputMessageBox doesn't work under Motif
456     #ifdef __WXMOTIF__
457         return new wxMessageOutputLog;
458     #elif wxUSE_MSGDLG
459         return new wxMessageOutputBest(wxMSGOUT_PREFER_STDERR);
460     #else
461         return new wxMessageOutputStderr;
462     #endif
463 #endif // __UNIX__/!__UNIX__
464 }
465 
466 #if wxUSE_FONTMAP
467 
CreateFontMapper()468 wxFontMapper *wxGUIAppTraitsBase::CreateFontMapper()
469 {
470     return new wxFontMapper;
471 }
472 
473 #endif // wxUSE_FONTMAP
474 
CreateRenderer()475 wxRendererNative *wxGUIAppTraitsBase::CreateRenderer()
476 {
477     // use the default native renderer by default
478     return NULL;
479 }
480 
ShowAssertDialog(const wxString & msg)481 bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
482 {
483 #if wxDEBUG_LEVEL
484     // If possible, show the assert using a dialog allowing to hide the stack
485     // trace by default to avoid frightening people unnecessarily.
486     //
487     // Otherwise, show the assert using a basic message box, but under MSW
488     // we prefer to use the base class version using ::MessageBox() even if
489     // wxMessageBox() is available because it has less chances to double
490     // fault our app than our wxMessageBox()
491     //
492     // Notice that under DFB the message dialog is not always functional right
493     // now and, finally, we can't use wxMessageBox() if it wasn't compiled in.
494 #if wxUSE_RICHMSGDLG || \
495     (wxUSE_MSGDLG && !defined(__WXMSW__) && !defined(__WXDFB__))
496 
497     // we can't (safely) show the GUI dialog from another thread, only do it
498     // for the asserts in the main thread
499     if ( wxIsMainThread() )
500     {
501         // Note that this and the other messages here are intentionally not
502         // translated -- they are for developpers only.
503         static const wxStringCharType* caption = wxS("wxWidgets Debug Alert");
504 
505         wxString msgDlg = wxS("A debugging check in this application ")
506                           wxS("has failed.\n\n") + msg;
507 
508         // "No" button means to continue execution, so it should be the default
509         // action as leaving the "Yes" button the default one would mean that
510         // accidentally pressing Space or Enter would trap and kill the program.
511         const int flags = wxYES_NO | wxNO_DEFAULT | wxICON_STOP;
512 
513 #if wxUSE_STACKWALKER
514         const wxString stackTrace = GetAssertStackTrace();
515 #endif // wxUSE_STACKWALKER
516 
517 #if wxUSE_RICHMSGDLG
518         wxRichMessageDialog dlg(NULL, msgDlg, caption, flags);
519 
520         dlg.SetYesNoLabels("Stop", "Continue");
521 
522         dlg.ShowCheckBox("Don't show this dialog again");
523 
524 #if wxUSE_STACKWALKER
525         if ( !stackTrace.empty() )
526             dlg.ShowDetailedText(stackTrace);
527 #endif // wxUSE_STACKWALKER
528 #else // !wxUSE_RICHMSGDLG
529 #if wxUSE_STACKWALKER
530         if ( !stackTrace.empty() )
531             msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
532 #endif // wxUSE_STACKWALKER
533 
534         msgDlg += wxT("\nDo you want to stop the program?\n")
535                   wxT("You can also choose [Cancel] to suppress ")
536                   wxT("further warnings.");
537 
538         wxMessageDialog dlg(NULL, msg, caption, flags);
539 #endif // wxUSE_RICHMSGDLG/!wxUSE_RICHMSGDLG
540 
541         switch ( dlg.ShowModal() )
542         {
543             case wxID_YES:
544                 // See the comment about using the same variable in
545                 // DoShowAssertDialog().
546                 wxTrapInAssert = true;
547                 break;
548 
549             case wxID_CANCEL:
550                 // This button is used with the plain message dialog only to
551                 // indicate that no more assert dialogs should be shown, as
552                 // there is no other way to do it with it.
553                 return true;
554 
555             case wxID_NO:
556 #if wxUSE_RICHMSGDLG
557                 if ( dlg.IsCheckBoxChecked() )
558                 {
559                     // With this dialog, the checkbox is used to indicate that
560                     // the subsequent asserts should be skipped.
561                     return true;
562                 }
563 #endif // wxUSE_RICHMSGDLG
564 
565                 // Nothing to do otherwise.
566                 break;
567         }
568 
569         return false;
570     }
571 #endif // wxUSE_RICHMSGDLG || wxUSE_MSGDLG
572 #endif // wxDEBUG_LEVEL
573 
574     return wxAppTraitsBase::ShowAssertDialog(msg);
575 }
576 
HasStderr()577 bool wxGUIAppTraitsBase::HasStderr()
578 {
579     // we consider that under Unix stderr always goes somewhere, even if the
580     // user doesn't always see it under GUI desktops
581 #ifdef __UNIX__
582     return true;
583 #else
584     return false;
585 #endif
586 }
587 
588 #ifndef __WIN32__
589 
SafeMessageBox(const wxString & text,const wxString & title)590 bool wxGUIAppTraitsBase::SafeMessageBox(const wxString& text,
591                                         const wxString& title)
592 {
593     // The modules are initialized only after a successful call to
594     // wxApp::Initialize() in wxEntryStart, so it can be used as a proxy for
595     // GUI availability (note that the mere existence of wxTheApp is not enough
596     // for this).
597     if ( !wxModule::AreInitialized() )
598         return false;
599 
600     wxMessageBox(text, title, wxOK | wxICON_ERROR);
601 
602     return true;
603 }
604 
605 #endif // !__WIN32__
606