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
This commit is contained in:
Vadim Zeitlin 2002-08-15 20:42:07 +00:00
parent 62b77e42be
commit 1cbee0b42c
10 changed files with 134 additions and 89 deletions

View File

@ -137,7 +137,8 @@ otherwise.
\wxheading{See also} \wxheading{See also}
\helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete} \helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete},\\
\helpref{wxApp shutdown overview}{wxappshutdownoverview}
\membersection{wxApp::GetTopWindow}\label{wxappgettopwindow} \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 \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.} 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} \membersection{wxApp::SetTopWindow}\label{wxappsettopwindow}
\func{void}{SetTopWindow}{\param{wxWindow* }{window}} \func{void}{SetTopWindow}{\param{wxWindow* }{window}}

View File

@ -22,7 +22,8 @@ is to explicitly delete child frames in the top-level frame's \helpref{wxCloseEv
handler. handler.
In emergencies the \helpref{wxExit}{wxexit} function can be called to kill the 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: 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 You can also use DECLARE\_APP(appClass) in a header file to declare the wxGetApp function which returns
a reference to the application object. a reference to the application object.
\subsection{Application shutdown} \subsection{Application shutdown}\label{wxappshutdownoverview}
\helpref{OnExit}{wxapponexit} is called when the application exits but {\it before} The application normally shuts down when the last of its top level windows is
wxWindows cleans its internal structures. Your should delete all wxWindows object that closed. This is normally the expected behaviour and means that it is enough to
your created by the time OnExit finishes. In particular, do {\bf not} destroy them 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! from application class' destructor!
For example, this code may crash: For example, this code may crash:

View File

@ -128,7 +128,7 @@ public:
// //
// Override: rarely in GUI applications, always in console ones. // Override: rarely in GUI applications, always in console ones.
#if wxUSE_GUI #if wxUSE_GUI
virtual int OnRun() { return MainLoop(); }; virtual int OnRun();
#else // !GUI #else // !GUI
virtual int OnRun() = 0; virtual int OnRun() = 0;
#endif // wxUSE_GUI #endif // wxUSE_GUI
@ -236,11 +236,13 @@ public:
// control the exit behaviour: by default, the program will exit the // control the exit behaviour: by default, the program will exit the
// main loop (and so, usually, terminate) when the last top-level // main loop (and so, usually, terminate) when the last top-level
// program window is deleted. Beware that if you disabel this (with // program window is deleted. Beware that if you disable this behaviour
// SetExitOnFrameDelete(FALSE)), you'll have to call ExitMainLoop() // (with SetExitOnFrameDelete(FALSE)), you'll have to call
// explicitly from somewhere. // ExitMainLoop() explicitly from somewhere.
void SetExitOnFrameDelete(bool flag) { m_exitOnFrameDelete = flag; } void SetExitOnFrameDelete(bool flag)
bool GetExitOnFrameDelete() const { return m_exitOnFrameDelete; } { m_exitOnFrameDelete = flag ? Yes : No; }
bool GetExitOnFrameDelete() const
{ return m_exitOnFrameDelete == Yes; }
#endif // wxUSE_GUI #endif // wxUSE_GUI
@ -392,8 +394,16 @@ protected:
// the main top level window - may be NULL // the main top level window - may be NULL
wxWindow *m_topWindow; wxWindow *m_topWindow;
// if TRUE, exit the main loop when the last top level window is deleted // if Yes, exit the main loop when the last top level window is deleted, if
bool m_exitOnFrameDelete; // 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 // TRUE if the apps whats to use the best visual on systems where
// more than one are available (Sun, SGI, XFree86 4.0 ?) // more than one are available (Sun, SGI, XFree86 4.0 ?)

View File

@ -135,16 +135,21 @@ protected:
virtual void DoClientToScreen(int *x, int *y) const; virtual void DoClientToScreen(int *x, int *y) const;
virtual void DoScreenToClient(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 // send the iconize event, return TRUE if processed
bool SendIconizeEvent(bool iconized = TRUE); bool SendIconizeEvent(bool iconized = TRUE);
// the frame icon // the frame icon
wxIconBundle m_icons; 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() DECLARE_EVENT_TABLE()
}; };

View File

@ -84,8 +84,21 @@ wxAppBase::wxAppBase()
#if wxUSE_GUI #if wxUSE_GUI
m_topWindow = (wxWindow *)NULL; m_topWindow = (wxWindow *)NULL;
m_useBestVisual = FALSE; m_useBestVisual = FALSE;
m_exitOnFrameDelete = TRUE;
m_isActive = 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 #endif // wxUSE_GUI
#ifdef __WXDEBUG__ #ifdef __WXDEBUG__
@ -113,6 +126,19 @@ bool wxAppBase::OnInitGui()
} }
#endif // wxUSE_GUI #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() int wxAppBase::OnExit()
{ {
#if wxUSE_CONFIG #if wxUSE_CONFIG

View File

@ -60,7 +60,19 @@ wxTopLevelWindowBase::wxTopLevelWindowBase()
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() bool wxTopLevelWindowBase::Destroy()
@ -73,6 +85,19 @@ bool wxTopLevelWindowBase::Destroy()
return TRUE; 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 wxTopLevelWindowBase::GetMaxSize() const
{ {
wxSize size( GetMaxWidth(), GetMaxHeight() ); wxSize size( GetMaxWidth(), GetMaxHeight() );

View File

@ -483,7 +483,7 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
wxASSERT_MSG( FALSE, _T("Window still grabbed")); wxASSERT_MSG( FALSE, _T("Window still grabbed"));
RemoveGrab(); RemoveGrab();
} }
m_isBeingDeleted = TRUE; m_isBeingDeleted = TRUE;
// it may also be GtkScrolledWindow in the case of an MDI child // 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 ); 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; XEvent xev;
GdkWindowPrivate *priv; GdkWindowPrivate *priv;
gint prev_error; gint prev_error;
prev_error = gdk_error_warnings; prev_error = gdk_error_warnings;
gdk_error_warnings = 0; gdk_error_warnings = 0;
priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window); priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window);
if (GTK_WIDGET_MAPPED(window)) if (GTK_WIDGET_MAPPED(window))
{ {
xev.type = ClientMessage; xev.type = ClientMessage;
@ -536,14 +525,14 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer)
xev.xclient.format = 32; xev.xclient.format = 32;
xev.xclient.data.l[0] = (long)layer; xev.xclient.data.l[0] = (long)layer;
xev.xclient.data.l[1] = gdk_time_get(); xev.xclient.data.l[1] = gdk_time_get();
XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False, XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
SubstructureNotifyMask, (XEvent*) &xev); SubstructureNotifyMask, (XEvent*) &xev);
} }
else else
{ {
long data[1]; long data[1];
data[0] = layer; data[0] = layer;
XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER, XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
@ -574,14 +563,14 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style )
int screen_width,screen_height; int screen_width,screen_height;
wxDisplaySize( &screen_width, &screen_height ); wxDisplaySize( &screen_width, &screen_height );
gint client_x, client_y, root_x, root_y; gint client_x, client_y, root_x, root_y;
gint width, height; gint width, height;
gdk_window_get_origin (m_widget->window, &root_x, &root_y); gdk_window_get_origin (m_widget->window, &root_x, &root_y);
gdk_window_get_geometry (m_widget->window, &client_x, &client_y, gdk_window_get_geometry (m_widget->window, &client_x, &client_y,
&width, &height, NULL); &width, &height, NULL);
wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK ); wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK );
gdk_window_move_resize (m_widget->window, -client_x, -client_y, gdk_window_move_resize (m_widget->window, -client_x, -client_y,
@ -590,7 +579,7 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style )
else else
{ {
wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL ); wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL );
SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height ); 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(): // Gtk-WARNING **: gtk_widget_size_allocate():
// attempt to allocate widget with width 65535 and height 600 // attempt to allocate widget with width 65535 and height 600
// but I don't have time to track them all now.. // but I don't have time to track them all now..
// //
// Really we need to encapulate all this height/width business and // Really we need to encapulate all this height/width business and
// stop any old method from ripping at the members directly and // stop any old method from ripping at the members directly and
// scattering -1's without regard for who might resolve them later. // scattering -1's without regard for who might resolve them later.

View File

@ -483,7 +483,7 @@ wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
wxASSERT_MSG( FALSE, _T("Window still grabbed")); wxASSERT_MSG( FALSE, _T("Window still grabbed"));
RemoveGrab(); RemoveGrab();
} }
m_isBeingDeleted = TRUE; m_isBeingDeleted = TRUE;
// it may also be GtkScrolledWindow in the case of an MDI child // 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 ); 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; XEvent xev;
GdkWindowPrivate *priv; GdkWindowPrivate *priv;
gint prev_error; gint prev_error;
prev_error = gdk_error_warnings; prev_error = gdk_error_warnings;
gdk_error_warnings = 0; gdk_error_warnings = 0;
priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window); priv = (GdkWindowPrivate*)(GTK_WIDGET(window)->window);
if (GTK_WIDGET_MAPPED(window)) if (GTK_WIDGET_MAPPED(window))
{ {
xev.type = ClientMessage; xev.type = ClientMessage;
@ -536,14 +525,14 @@ static void wx_win_hints_set_layer(GtkWidget *window, int layer)
xev.xclient.format = 32; xev.xclient.format = 32;
xev.xclient.data.l[0] = (long)layer; xev.xclient.data.l[0] = (long)layer;
xev.xclient.data.l[1] = gdk_time_get(); xev.xclient.data.l[1] = gdk_time_get();
XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False, XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
SubstructureNotifyMask, (XEvent*) &xev); SubstructureNotifyMask, (XEvent*) &xev);
} }
else else
{ {
long data[1]; long data[1];
data[0] = layer; data[0] = layer;
XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER, XChangeProperty(GDK_DISPLAY(), priv->xwindow, gs_XA_WIN_LAYER,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
@ -574,14 +563,14 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style )
int screen_width,screen_height; int screen_width,screen_height;
wxDisplaySize( &screen_width, &screen_height ); wxDisplaySize( &screen_width, &screen_height );
gint client_x, client_y, root_x, root_y; gint client_x, client_y, root_x, root_y;
gint width, height; gint width, height;
gdk_window_get_origin (m_widget->window, &root_x, &root_y); gdk_window_get_origin (m_widget->window, &root_x, &root_y);
gdk_window_get_geometry (m_widget->window, &client_x, &client_y, gdk_window_get_geometry (m_widget->window, &client_x, &client_y,
&width, &height, NULL); &width, &height, NULL);
wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK ); wx_win_hints_set_layer( m_widget, WIN_LAYER_ABOVE_DOCK );
gdk_window_move_resize (m_widget->window, -client_x, -client_y, gdk_window_move_resize (m_widget->window, -client_x, -client_y,
@ -590,7 +579,7 @@ bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long style )
else else
{ {
wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL ); wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL );
SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height ); 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(): // Gtk-WARNING **: gtk_widget_size_allocate():
// attempt to allocate widget with width 65535 and height 600 // attempt to allocate widget with width 65535 and height 600
// but I don't have time to track them all now.. // but I don't have time to track them all now..
// //
// Really we need to encapulate all this height/width business and // Really we need to encapulate all this height/width business and
// stop any old method from ripping at the members directly and // stop any old method from ripping at the members directly and
// scattering -1's without regard for who might resolve them later. // scattering -1's without regard for who might resolve them later.

View File

@ -701,22 +701,8 @@ int wxEntry(WXHINSTANCE hInstance,
wxLog::SetTimestamp(NULL); wxLog::SetTimestamp(NULL);
#endif #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 // init the app
retValue = wxEntryInitGui() && wxTheApp->OnInit() ? 0 : -1; int retValue = wxEntryInitGui() && wxTheApp->OnInit() ? 0 : -1;
// restore the old flag value
wxTheApp->SetExitOnFrameDelete(exitOnLastFrameDelete);
if ( retValue == 0 ) if ( retValue == 0 )
{ {
@ -986,8 +972,8 @@ bool wxApp::ProcessIdle()
void wxApp::ExitMainLoop() void wxApp::ExitMainLoop()
{ {
// VZ: why not ::PostQuitMessage()? // this will set m_keepGoing to FALSE a bit later
m_keepGoing = FALSE; ::PostQuitMessage(0);
} }
bool wxApp::Pending() bool wxApp::Pending()

View File

@ -454,12 +454,12 @@ wxTopLevelWindowMSW::~wxTopLevelWindowMSW()
if ( this == ms_hiddenParent ) if ( this == ms_hiddenParent )
{ {
// stop [infinite] recursion which would otherwise happen when we do // 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; return;
} }
wxTopLevelWindows.DeleteObject(this);
if ( wxModelessWindows.Find(this) ) if ( wxModelessWindows.Find(this) )
wxModelessWindows.DeleteObject(this); wxModelessWindows.DeleteObject(this);
@ -476,21 +476,15 @@ wxTopLevelWindowMSW::~wxTopLevelWindowMSW()
} }
} }
// If this is the last top-level window, exit. // if this is the last top-level window, we're going to exit and we should
if ( wxTheApp && (wxTopLevelWindows.Number() == 0) ) // delete ms_hiddenParent now to avoid leaking it
if ( IsLastBeforeExit() )
{ {
if ( ms_hiddenParent ) if ( ms_hiddenParent )
{ {
delete ms_hiddenParent; delete ms_hiddenParent;
ms_hiddenParent = NULL; ms_hiddenParent = NULL;
} }
wxTheApp->SetTopWindow(NULL);
if ( wxTheApp->GetExitOnFrameDelete() )
{
::PostQuitMessage(0);
}
} }
} }