From 1cbee0b42c9755a98104e3b1d8fd1133483ea54f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 15 Aug 2002 20:42:07 +0000 Subject: [PATCH] the app doesn't exit any more if a dialog is shown (and destroyed) while the flow of control is still in OnInit() git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@16523 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/latex/wx/app.tex | 8 +++++++- docs/latex/wx/tapp.tex | 27 +++++++++++++++++++++------ include/wx/app.h | 26 ++++++++++++++++++-------- include/wx/toplevel.h | 13 +++++++++---- src/common/appcmn.cpp | 28 +++++++++++++++++++++++++++- src/common/toplvcmn.cpp | 27 ++++++++++++++++++++++++++- src/gtk/toplevel.cpp | 29 +++++++++-------------------- src/gtk1/toplevel.cpp | 29 +++++++++-------------------- src/msw/app.cpp | 20 +++----------------- src/msw/toplevel.cpp | 18 ++++++------------ 10 files changed, 135 insertions(+), 90 deletions(-) diff --git a/docs/latex/wx/app.tex b/docs/latex/wx/app.tex index f082563254..2a73956ac3 100644 --- a/docs/latex/wx/app.tex +++ b/docs/latex/wx/app.tex @@ -137,7 +137,8 @@ otherwise. \wxheading{See also} -\helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete} +\helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete},\\ +\helpref{wxApp shutdown overview}{wxappshutdownoverview} \membersection{wxApp::GetTopWindow}\label{wxappgettopwindow} @@ -550,6 +551,11 @@ top-level frame is deleted. \docparam{flag}{If TRUE (the default), the application will exit when the top-level frame is deleted. If FALSE, the application will continue to run.} +\wxheading{See also} + +\helpref{wxApp::GetExitOnFrameDelete}{wxappgetexitonframedelete},\\ +\helpref{wxApp shutdown overview}{wxappshutdownoverview} + \membersection{wxApp::SetTopWindow}\label{wxappsettopwindow} \func{void}{SetTopWindow}{\param{wxWindow* }{window}} diff --git a/docs/latex/wx/tapp.tex b/docs/latex/wx/tapp.tex index 563b3d085b..9deac1d1ba 100644 --- a/docs/latex/wx/tapp.tex +++ b/docs/latex/wx/tapp.tex @@ -22,7 +22,8 @@ is to explicitly delete child frames in the top-level frame's \helpref{wxCloseEv handler. In emergencies the \helpref{wxExit}{wxexit} function can be called to kill the -application. +application however normally the applications shuts down automatically, +\helpref{see below}{wxappshutdownoverview}. An example of defining an application follows: @@ -55,11 +56,25 @@ construction time. You can also use DECLARE\_APP(appClass) in a header file to declare the wxGetApp function which returns a reference to the application object. -\subsection{Application shutdown} - -\helpref{OnExit}{wxapponexit} is called when the application exits but {\it before} -wxWindows cleans its internal structures. Your should delete all wxWindows object that -your created by the time OnExit finishes. In particular, do {\bf not} destroy them +\subsection{Application shutdown}\label{wxappshutdownoverview} + +The application normally shuts down when the last of its top level windows is +closed. This is normally the expected behaviour and means that it is enough to +call \helpref{Close()}{wxwindowclose} in response to the {\tt "Exit"} menu +command if your program has a single top level window. If this behaviour is not +desirable \helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete} can +be called to change it. Note that starting from wxWindows 2.3.3 such logic +doesn't apply for the windows shown before the program enters the main loop: in +other words, you can safely show a dialog from +\helpref{wxApp::OnInit}{wxapponinit} and not be afraid that your application +terminates when this dialog -- which is the last top level window for the +moment -- is closed. + + +Another aspect of the application shutdown is the \helpref{OnExit}{wxapponexit} +which is called when the application exits but {\it before} wxWindows cleans up +its internal structures. Your should delete all wxWindows object that your +created by the time OnExit finishes. In particular, do {\bf not} destroy them from application class' destructor! For example, this code may crash: diff --git a/include/wx/app.h b/include/wx/app.h index 8324651d60..8b17fc42aa 100644 --- a/include/wx/app.h +++ b/include/wx/app.h @@ -128,7 +128,7 @@ public: // // Override: rarely in GUI applications, always in console ones. #if wxUSE_GUI - virtual int OnRun() { return MainLoop(); }; + virtual int OnRun(); #else // !GUI virtual int OnRun() = 0; #endif // wxUSE_GUI @@ -236,11 +236,13 @@ public: // control the exit behaviour: by default, the program will exit the // main loop (and so, usually, terminate) when the last top-level - // program window is deleted. Beware that if you disabel this (with - // SetExitOnFrameDelete(FALSE)), you'll have to call ExitMainLoop() - // explicitly from somewhere. - void SetExitOnFrameDelete(bool flag) { m_exitOnFrameDelete = flag; } - bool GetExitOnFrameDelete() const { return m_exitOnFrameDelete; } + // program window is deleted. Beware that if you disable this behaviour + // (with SetExitOnFrameDelete(FALSE)), you'll have to call + // ExitMainLoop() explicitly from somewhere. + void SetExitOnFrameDelete(bool flag) + { m_exitOnFrameDelete = flag ? Yes : No; } + bool GetExitOnFrameDelete() const + { return m_exitOnFrameDelete == Yes; } #endif // wxUSE_GUI @@ -392,8 +394,16 @@ protected: // the main top level window - may be NULL wxWindow *m_topWindow; - // if TRUE, exit the main loop when the last top level window is deleted - bool m_exitOnFrameDelete; + // if Yes, exit the main loop when the last top level window is deleted, if + // No don't do it and if Later -- only do it once we reach our OnRun() + // + // the explanation for using this strange scheme is given in appcmn.cpp + enum + { + Later = -1, + No, + Yes + } m_exitOnFrameDelete; // TRUE if the apps whats to use the best visual on systems where // more than one are available (Sun, SGI, XFree86 4.0 ?) diff --git a/include/wx/toplevel.h b/include/wx/toplevel.h index ce058932b0..84ac5bffeb 100644 --- a/include/wx/toplevel.h +++ b/include/wx/toplevel.h @@ -135,16 +135,21 @@ protected: virtual void DoClientToScreen(int *x, int *y) const; virtual void DoScreenToClient(int *x, int *y) const; + // test whether this window makes part of the frame + // (menubar, toolbar and statusbar are excluded from automatic layout) + virtual bool IsOneOfBars(const wxWindow *WXUNUSED(win)) const + { return FALSE; } + + // check if we should exit the program after deleting another top level + // window (this is used in common dtor and wxMSW code) + static bool IsLastBeforeExit(); + // send the iconize event, return TRUE if processed bool SendIconizeEvent(bool iconized = TRUE); // the frame icon wxIconBundle m_icons; - // test whether this window makes part of the frame - // (menubar, toolbar and statusbar are excluded from automatic layout) - virtual bool IsOneOfBars(const wxWindow *WXUNUSED(win)) const { return FALSE; } - DECLARE_EVENT_TABLE() }; diff --git a/src/common/appcmn.cpp b/src/common/appcmn.cpp index 2398dad313..19ddb1965c 100644 --- a/src/common/appcmn.cpp +++ b/src/common/appcmn.cpp @@ -84,8 +84,21 @@ wxAppBase::wxAppBase() #if wxUSE_GUI m_topWindow = (wxWindow *)NULL; m_useBestVisual = FALSE; - m_exitOnFrameDelete = TRUE; m_isActive = TRUE; + + // We don't want to exit the app if the user code shows a dialog from its + // OnInit() -- but this is what would happen if we set m_exitOnFrameDelete + // to Yes initially as this dialog would be the last top level window. + // OTOH, if we set it to No initially we'll have to overwrite it with Yes + // when we enter our OnRun() because we do want the default behaviour from + // then on. But this would be a problem if the user code calls + // SetExitOnFrameDelete(FALSE) from OnInit(). + // + // So we use the special "Later" value which is such that + // GetExitOnFrameDelete() returns FALSE for it but which we know we can + // safely (i.e. without losing the effect of the users SetExitOnFrameDelete + // call) overwrite in OnRun() + m_exitOnFrameDelete = Later; #endif // wxUSE_GUI #ifdef __WXDEBUG__ @@ -113,6 +126,19 @@ bool wxAppBase::OnInitGui() } #endif // wxUSE_GUI +int wxAppBase::OnRun() +{ + // see the comment in ctor: if the initial value hasn't been changed, use + // the default Yes from now on + if ( m_exitOnFrameDelete == Later ) + { + m_exitOnFrameDelete = Yes; + } + //else: it has been changed, assume the user knows what he is doing + + return MainLoop(); +} + int wxAppBase::OnExit() { #if wxUSE_CONFIG diff --git a/src/common/toplvcmn.cpp b/src/common/toplvcmn.cpp index da1e1cb9e0..fd659ac2b6 100644 --- a/src/common/toplvcmn.cpp +++ b/src/common/toplvcmn.cpp @@ -60,7 +60,19 @@ wxTopLevelWindowBase::wxTopLevelWindowBase() wxTopLevelWindowBase::~wxTopLevelWindowBase() { - // this destructor is required for Darwin + // don't let wxTheApp keep any stale pointers to us + if ( wxTheApp && wxTheApp->GetTopWindow() == this ) + wxTheApp->SetTopWindow(NULL); + + bool shouldExit = IsLastBeforeExit(); + + wxTopLevelWindows.DeleteObject(this); + + if ( shouldExit ) + { + // then do it + wxTheApp->ExitMainLoop(); + } } bool wxTopLevelWindowBase::Destroy() @@ -73,6 +85,19 @@ bool wxTopLevelWindowBase::Destroy() return TRUE; } +/* static */ +bool wxTopLevelWindowBase::IsLastBeforeExit() +{ + // we exit the application if there are no more top level windows left + // normally but wxApp can prevent this from happening + return (wxTopLevelWindows.GetCount() == 1) && + wxTheApp && wxTheApp->GetExitOnFrameDelete(); +} + +// ---------------------------------------------------------------------------- +// wxTopLevelWindow geometry +// ---------------------------------------------------------------------------- + wxSize wxTopLevelWindowBase::GetMaxSize() const { wxSize size( GetMaxWidth(), GetMaxHeight() ); diff --git a/src/gtk/toplevel.cpp b/src/gtk/toplevel.cpp index f2005c8c5d..73fc746c0b 100644 --- a/src/gtk/toplevel.cpp +++ b/src/gtk/toplevel.cpp @@ -483,7 +483,7 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK() wxASSERT_MSG( FALSE, _T("Window still grabbed")); RemoveGrab(); } - + m_isBeingDeleted = TRUE; // it may also be GtkScrolledWindow in the case of an MDI child @@ -491,17 +491,6 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK() { gtk_window_set_focus( GTK_WINDOW(m_widget), NULL ); } - - wxTopLevelWindows.DeleteObject( this ); - - if (wxTheApp->GetTopWindow() == this) - wxTheApp->SetTopWindow( (wxWindow*) NULL ); - - if ((wxTopLevelWindows.Number() == 0) && - (wxTheApp->GetExitOnFrameDelete())) - { - wxTheApp->ExitMainLoop(); - } } @@ -522,11 +511,11 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer) XEvent xev; GdkWindowPrivate *priv; gint prev_error; - + prev_error = gdk_error_warnings; gdk_error_warnings = 0; priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window); - + if (GTK_WIDGET_MAPPED(window)) { xev.type = ClientMessage; @@ -536,14 +525,14 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer) xev.xclient.format = 32; xev.xclient.data.l[0] = (long)layer; xev.xclient.data.l[1] = gdk_time_get(); - + XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False, SubstructureNotifyMask, (XEvent*) &xev); } else { long data[1]; - + data[0] = layer; XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); @@ -574,14 +563,14 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style ) int screen_width,screen_height; wxDisplaySize( &screen_width, &screen_height ); - + gint client_x, client_y, root_x, root_y; gint width, height; gdk_window_get_origin (m_widget->window, &root_x, &root_y); gdk_window_get_geometry (m_widget->window, &client_x, &client_y, &width, &height, NULL); - + wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK ); gdk_window_move_resize (m_widget->window, -client_x, -client_y, @@ -590,7 +579,7 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style ) else { wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL ); - + SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height ); } @@ -778,7 +767,7 @@ void wxTopLevelWindowGTK::GtkOnSize( int WXUNUSED(x), int WXUNUSED(y), // Gtk-WARNING **: gtk_widget_size_allocate(): // attempt to allocate widget with width 65535 and height 600 // but I don't have time to track them all now.. - // + // // Really we need to encapulate all this height/width business and // stop any old method from ripping at the members directly and // scattering -1's without regard for who might resolve them later. diff --git a/src/gtk1/toplevel.cpp b/src/gtk1/toplevel.cpp index f2005c8c5d..73fc746c0b 100644 --- a/src/gtk1/toplevel.cpp +++ b/src/gtk1/toplevel.cpp @@ -483,7 +483,7 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK() wxASSERT_MSG( FALSE, _T("Window still grabbed")); RemoveGrab(); } - + m_isBeingDeleted = TRUE; // it may also be GtkScrolledWindow in the case of an MDI child @@ -491,17 +491,6 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK() { gtk_window_set_focus( GTK_WINDOW(m_widget), NULL ); } - - wxTopLevelWindows.DeleteObject( this ); - - if (wxTheApp->GetTopWindow() == this) - wxTheApp->SetTopWindow( (wxWindow*) NULL ); - - if ((wxTopLevelWindows.Number() == 0) && - (wxTheApp->GetExitOnFrameDelete())) - { - wxTheApp->ExitMainLoop(); - } } @@ -522,11 +511,11 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer) XEvent xev; GdkWindowPrivate *priv; gint prev_error; - + prev_error = gdk_error_warnings; gdk_error_warnings = 0; priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window); - + if (GTK_WIDGET_MAPPED(window)) { xev.type = ClientMessage; @@ -536,14 +525,14 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer) xev.xclient.format = 32; xev.xclient.data.l[0] = (long)layer; xev.xclient.data.l[1] = gdk_time_get(); - + XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False, SubstructureNotifyMask, (XEvent*) &xev); } else { long data[1]; - + data[0] = layer; XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); @@ -574,14 +563,14 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style ) int screen_width,screen_height; wxDisplaySize( &screen_width, &screen_height ); - + gint client_x, client_y, root_x, root_y; gint width, height; gdk_window_get_origin (m_widget->window, &root_x, &root_y); gdk_window_get_geometry (m_widget->window, &client_x, &client_y, &width, &height, NULL); - + wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK ); gdk_window_move_resize (m_widget->window, -client_x, -client_y, @@ -590,7 +579,7 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style ) else { wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL ); - + SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height ); } @@ -778,7 +767,7 @@ void wxTopLevelWindowGTK::GtkOnSize( int WXUNUSED(x), int WXUNUSED(y), // Gtk-WARNING **: gtk_widget_size_allocate(): // attempt to allocate widget with width 65535 and height 600 // but I don't have time to track them all now.. - // + // // Really we need to encapulate all this height/width business and // stop any old method from ripping at the members directly and // scattering -1's without regard for who might resolve them later. diff --git a/src/msw/app.cpp b/src/msw/app.cpp index c4808f1b35..a6ba1ee020 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -701,22 +701,8 @@ int wxEntry(WXHINSTANCE hInstance, wxLog::SetTimestamp(NULL); #endif - int retValue = 0; - - // it is common to create a modal dialog in OnInit() (to ask/notify the - // user about something) but it wouldn't work if we don't change the - // "exit on delete last frame" flag here as when this dialog is - // deleted, the app would terminate (it was the last top level window - // as the main frame wasn't created yet!), so disable this behaviour - // temproarily - bool exitOnLastFrameDelete = wxTheApp->GetExitOnFrameDelete(); - wxTheApp->SetExitOnFrameDelete(FALSE); - // init the app - retValue = wxEntryInitGui() && wxTheApp->OnInit() ? 0 : -1; - - // restore the old flag value - wxTheApp->SetExitOnFrameDelete(exitOnLastFrameDelete); + int retValue = wxEntryInitGui() && wxTheApp->OnInit() ? 0 : -1; if ( retValue == 0 ) { @@ -986,8 +972,8 @@ bool wxApp::ProcessIdle() void wxApp::ExitMainLoop() { - // VZ: why not ::PostQuitMessage()? - m_keepGoing = FALSE; + // this will set m_keepGoing to FALSE a bit later + ::PostQuitMessage(0); } bool wxApp::Pending() diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 9ce65687c6..1e6fc3de12 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -454,12 +454,12 @@ wxTopLevelWindowMSW::~wxTopLevelWindowMSW() if ( this == ms_hiddenParent ) { // stop [infinite] recursion which would otherwise happen when we do - // "delete ms_hiddenParent" below + // "delete ms_hiddenParent" below -- and we're not interested in doing + // anything of the rest below for that window because the rest of + // wxWindows doesn't even know about it return; } - wxTopLevelWindows.DeleteObject(this); - if ( wxModelessWindows.Find(this) ) wxModelessWindows.DeleteObject(this); @@ -476,21 +476,15 @@ wxTopLevelWindowMSW::~wxTopLevelWindowMSW() } } - // If this is the last top-level window, exit. - if ( wxTheApp && (wxTopLevelWindows.Number() == 0) ) + // if this is the last top-level window, we're going to exit and we should + // delete ms_hiddenParent now to avoid leaking it + if ( IsLastBeforeExit() ) { if ( ms_hiddenParent ) { delete ms_hiddenParent; ms_hiddenParent = NULL; } - - wxTheApp->SetTopWindow(NULL); - - if ( wxTheApp->GetExitOnFrameDelete() ) - { - ::PostQuitMessage(0); - } } } -- 2.47.2