From 1cbee0b42c9755a98104e3b1d8fd1133483ea54f Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
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.50.0

