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