\wxheading{See also}
-\helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete}
+\helpref{wxApp::SetExitOnFrameDelete}{wxappsetexitonframedelete},\\
+\helpref{wxApp shutdown overview}{wxappshutdownoverview}
\membersection{wxApp::GetTopWindow}\label{wxappgettopwindow}
\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}}
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:
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:
//
// 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
// 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
// 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 ?)
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()
};
#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__
}
#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
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()
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() );
wxASSERT_MSG( FALSE, _T("Window still grabbed"));
RemoveGrab();
}
-
+
m_isBeingDeleted = TRUE;
// it may also be GtkScrolledWindow in the case of an MDI child
{
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();
- }
}
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;
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);
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,
else
{
wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL );
-
+
SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height );
}
// 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.
wxASSERT_MSG( FALSE, _T("Window still grabbed"));
RemoveGrab();
}
-
+
m_isBeingDeleted = TRUE;
// it may also be GtkScrolledWindow in the case of an MDI child
{
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();
- }
}
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;
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);
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,
else
{
wx_win_hints_set_layer( m_widget, WIN_LAYER_NORMAL );
-
+
SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height );
}
// 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.
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 )
{
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()
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);
}
}
- // 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);
- }
}
}