Use new wxAppTraits::SafeMessageBox() in wxSafeShowMessage()

This allows to show message boxes in ports other than wxMSW too by doing
it only when it is safe, i.e. when the GUI is initialized, while still
keeping the old code directly using the native MessageBox() function for
MSW for maximal robustness.
This commit is contained in:
Vadim Zeitlin
2021-03-07 20:50:30 +01:00
parent fdd4ff8bf9
commit e00d5e131b
7 changed files with 107 additions and 28 deletions

View File

@@ -88,6 +88,16 @@ public:
// return true to suppress subsequent asserts, false to continue as before
virtual bool ShowAssertDialog(const wxString& msg) = 0;
// show the message safely to the user, i.e. show it in a message box if
// possible (even in a console application!) or return false if we can't do
// it (e.g. GUI is not initialized at all)
//
// note that this function can be called even when wxApp doesn't exist, as
// it's supposed to be always safe to call -- hence the name
//
// return true if the message box was shown, false if nothing was done
virtual bool SafeMessageBox(const wxString& text, const wxString& title) = 0;
// return true if fprintf(stderr) goes somewhere, false otherwise
virtual bool HasStderr() = 0;
@@ -208,6 +218,8 @@ public:
virtual bool ShowAssertDialog(const wxString& msg) wxOVERRIDE;
virtual bool HasStderr() wxOVERRIDE;
virtual bool SafeMessageBox(const wxString& text,
const wxString& title) wxOVERRIDE;
// the GetToolkitVersion for console application is always the same
wxPortId GetToolkitVersion(int *verMaj = NULL,
@@ -248,6 +260,13 @@ public:
virtual bool ShowAssertDialog(const wxString& msg) wxOVERRIDE;
virtual bool HasStderr() wxOVERRIDE;
// Win32 has its own implementation using native message box directly in
// the base class, don't override it.
#ifndef __WIN32__
virtual bool SafeMessageBox(const wxString& text,
const wxString& title) wxOVERRIDE;
#endif // !__WIN32__
virtual bool IsUsingUniversalWidgets() const wxOVERRIDE
{
#ifdef __WXUNIVERSAL__

View File

@@ -61,6 +61,10 @@ public:
// return the main application window or 0 if none
virtual WXHWND GetMainHWND() const = 0;
// implement this base class function for both console and GUI applications
virtual bool SafeMessageBox(const wxString& text,
const wxString& title) wxOVERRIDE;
protected:
#if wxUSE_THREADS
// implementation of WaitForThread() for the console applications which is

View File

@@ -133,5 +133,28 @@ public:
Returns @true to suppress subsequent asserts, @false to continue as before.
*/
virtual bool ShowAssertDialog(const wxString& msg) = 0;
/**
Shows a message box with the given text and title if possible.
In some ports, e.g. wxMSW, a message box will always be shown, while in
the other ones it will be only done if the GUI is available (e.g. X11
display was successfully opened for X11-based ports) and the function
simply returns @false without doing anything otherwise.
This function is safe in the sense that it can always be called, even
before creating wxApp, similarly to wxSafeShowMessage() which is
implemented by calling this function and then logging the message to
standard error stream if it returned @false.
@since 3.1.5
@param text
The text to show to the user.
@param title
The title of the message box shown to the user.
@return @true if the message box was shown or @false otherwise.
*/
virtual bool SafeMessageBox(const wxString& text, const wxString& title) = 0;
};

View File

@@ -944,6 +944,14 @@ bool wxConsoleAppTraitsBase::HasStderr()
return true;
}
bool wxConsoleAppTraitsBase::SafeMessageBox(const wxString& WXUNUSED(text),
const wxString& WXUNUSED(title))
{
// console applications don't show message boxes by default, although this
// can be done in platform-specific cases
return false;
}
// ----------------------------------------------------------------------------
// wxAppTraits
// ----------------------------------------------------------------------------

View File

@@ -25,6 +25,7 @@
#include "wx/window.h"
#include "wx/bitmap.h"
#include "wx/log.h"
#include "wx/module.h"
#include "wx/msgdlg.h"
#include "wx/confbase.h"
#include "wx/utils.h"
@@ -584,3 +585,21 @@ bool wxGUIAppTraitsBase::HasStderr()
#endif
}
#ifndef __WIN32__
bool wxGUIAppTraitsBase::SafeMessageBox(const wxString& text,
const wxString& title)
{
// The modules are initialized only after a successful call to
// wxApp::Initialize() in wxEntryStart, so it can be used as a proxy for
// GUI availability (note that the mere existence of wxTheApp is not enough
// for this).
if ( !wxModule::AreInitialized() )
return false;
wxMessageBox(text, title, wxOK | wxICON_ERROR);
return true;
}
#endif // !__WIN32__

View File

@@ -182,34 +182,11 @@ private:
void wxSafeShowMessage(const wxString& title, const wxString& text)
{
#ifdef __WINDOWS__
const wxAppTraits* const traits = wxApp::GetTraitsIfExists();
const HWND hwndParent = traits ? traits->GetMainHWND() : NULL;
int flags = MB_OK | MB_ICONSTOP;
// Using MB_TASKMODAL with valid parent doesn't work well because it
// prevents the typical behaviour of modal message boxes, e.g. the message
// box doesn't come up to front when the parent is clicked. But if we don't
// have any parent anyhow, we can just as well use it, as we don't lose
// anything and it has a useful side effect of disabling any existing TLWs
// if there are any.
//
// Note that we also might have chosen to always use MB_TASKMODAL and NULL
// parent. This would have the advantage of always disabling all the window
// which, but at the cost of the behaviour mentioned above and other
// related problems, e.g. showing ugly default icon in Alt-Tab list and an
// extra taskbar button for the message box, so we don't do this, although
// perhaps we still should, at least in case when there is more than one
// TLW (but we can't check for this easily as this is non-GUI code and
// wxTopLevelWindows is not accessible from it).
if ( !hwndParent )
flags |= MB_TASKMODAL;
::MessageBox(hwndParent, text.t_str(), title.t_str(), flags);
#else
wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str());
fflush(stderr);
#endif
if ( !wxApp::GetValidTraits().SafeMessageBox(text, title) )
{
wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str());
fflush(stderr);
}
}
// ----------------------------------------------------------------------------

View File

@@ -39,6 +39,35 @@
// wxAppTraits implementation
// ============================================================================
bool wxAppTraits::SafeMessageBox(const wxString& text,
const wxString& title)
{
const HWND hwndParent = GetMainHWND();
int flags = MB_OK | MB_ICONSTOP;
// Using MB_TASKMODAL with valid parent doesn't work well because it
// prevents the typical behaviour of modal message boxes, e.g. the message
// box doesn't come up to front when the parent is clicked. But if we don't
// have any parent anyhow, we can just as well use it, as we don't lose
// anything and it has a useful side effect of disabling any existing TLWs
// if there are any.
//
// Note that we also might have chosen to always use MB_TASKMODAL and NULL
// parent. This would have the advantage of always disabling all the window
// which, but at the cost of the behaviour mentioned above and other
// related problems, e.g. showing ugly default icon in Alt-Tab list and an
// extra taskbar button for the message box, so we don't do this, although
// perhaps we still should, at least in case when there is more than one
// TLW (but we can't check for this easily as this is non-GUI code and
// wxTopLevelWindows is not accessible from it).
if ( !hwndParent )
flags |= MB_TASKMODAL;
::MessageBox(hwndParent, text.t_str(), title.t_str(), flags);
return true;
}
#if wxUSE_THREADS
WXDWORD wxAppTraits::DoSimpleWaitForThread(WXHANDLE hThread)
{