From 69e48f036bb6e12882e1db2fc5c4496dad6e97e6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 7 Mar 2021 15:25:27 +0100 Subject: [PATCH 1/6] Add wxApp::GetMainTopWindow() wrapper This trivial function just allows to avoid checking if wxTheApp is not null before calling GetTopWindow() on it. Replace the existing "wxTheApp && wxTheApp->GetTopWindow()" calls with wxApp::GetMainTopWindow(). No real changes. --- include/wx/app.h | 4 ++++ interface/wx/app.h | 12 ++++++++++++ src/common/appcmn.cpp | 8 ++++++++ src/common/dlgcmn.cpp | 4 ++-- src/common/iconbndl.cpp | 2 +- src/common/sizer.cpp | 2 +- src/generic/aboutdlgg.cpp | 4 ++-- src/html/m_image.cpp | 4 ++-- src/msw/artmsw.cpp | 2 +- src/msw/cursor.cpp | 4 ++-- src/msw/gdiimage.cpp | 4 ++-- src/msw/helpchm.cpp | 4 ++-- src/msw/msgdlg.cpp | 2 +- src/msw/settings.cpp | 4 ++-- src/msw/window.cpp | 16 +++++++++------- 15 files changed, 51 insertions(+), 25 deletions(-) diff --git a/include/wx/app.h b/include/wx/app.h index 33810ef719..45e35ef7d3 100644 --- a/include/wx/app.h +++ b/include/wx/app.h @@ -622,6 +622,10 @@ public: // there are none, will return NULL) virtual wxWindow *GetTopWindow() const; + // convenient helper which is safe to use even if there is no wxApp at + // all, it will just return NULL in this case + static wxWindow *GetMainTopWindow(); + // 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 disable this behaviour diff --git a/interface/wx/app.h b/interface/wx/app.h index 073148dcd5..de56c7e987 100644 --- a/interface/wx/app.h +++ b/interface/wx/app.h @@ -859,6 +859,18 @@ public: */ bool GetUseBestVisual() const; + /** + Returns a pointer to the top application window if any. + + This function is safe to call even before creating, or after + destroying, the application object, as it simply returns @NULL if it + doesn't exist. Otherwise it's equivalent to calling + @c wxTheApp->GetTopWindow(). + + @since 3.1.5 + */ + static wxWindow* GetMainTopWindow(); + /** Returns a pointer to the top window. diff --git a/src/common/appcmn.cpp b/src/common/appcmn.cpp index 3ab92d0dd0..d257599f42 100644 --- a/src/common/appcmn.cpp +++ b/src/common/appcmn.cpp @@ -189,6 +189,14 @@ wxWindow* wxAppBase::GetTopWindow() const return window; } +/* static */ +wxWindow* wxAppBase::GetMainTopWindow() +{ + const wxAppBase* const app = static_cast(GetInstance()); + + return app ? app->GetTopWindow() : NULL; +} + wxVideoMode wxAppBase::GetDisplayMode() const { return wxVideoMode(); diff --git a/src/common/dlgcmn.cpp b/src/common/dlgcmn.cpp index 7c5b6da04e..dfc23046ca 100644 --- a/src/common/dlgcmn.cpp +++ b/src/common/dlgcmn.cpp @@ -183,8 +183,8 @@ wxDialogBase::GetParentForModalDialog(wxWindow *parent, long style) const wxGetTopLevelParent(wxGetActiveWindow())); // and finally the application main window - if ( !parent && wxTheApp ) - parent = CheckIfCanBeUsedAsParent(wxTheApp->GetTopWindow()); + if ( !parent ) + parent = CheckIfCanBeUsedAsParent(wxApp::GetMainTopWindow()); return parent; } diff --git a/src/common/iconbndl.cpp b/src/common/iconbndl.cpp index eadaad6988..077f09fd56 100644 --- a/src/common/iconbndl.cpp +++ b/src/common/iconbndl.cpp @@ -264,7 +264,7 @@ wxIcon wxIconBundle::GetIcon(const wxSize& size, int flags) const sysY = 0; if ( flags & FALLBACK_SYSTEM ) { - wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + wxWindow* win = wxApp::GetMainTopWindow(); sysX = wxSystemSettings::GetMetric(wxSYS_ICON_X, win); sysY = wxSystemSettings::GetMetric(wxSYS_ICON_Y, win); } diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index b469097468..110a5d69c3 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -104,7 +104,7 @@ float wxSizerFlags::DoGetDefaultBorderInPx() // We also have to use the DPI for the monitor showing the top window here // as we don't have any associated window -- but, again, without changes // in the API, there is nothing we can do about this. - const wxWindow* const win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* const win = wxApp::GetMainTopWindow(); static wxPrivate::DpiDependentValue s_defaultBorderInPx; if ( s_defaultBorderInPx.HasChanged(win) ) { diff --git a/src/generic/aboutdlgg.cpp b/src/generic/aboutdlgg.cpp index bb824780f5..a0ecb03030 100644 --- a/src/generic/aboutdlgg.cpp +++ b/src/generic/aboutdlgg.cpp @@ -81,10 +81,10 @@ wxString wxAboutDialogInfo::GetDescriptionAndCredits() const wxIcon wxAboutDialogInfo::GetIcon() const { wxIcon icon = m_icon; - if ( !icon.IsOk() && wxTheApp ) + if ( !icon.IsOk() ) { const wxTopLevelWindow * const - tlw = wxDynamicCast(wxTheApp->GetTopWindow(), wxTopLevelWindow); + tlw = wxDynamicCast(wxApp::GetMainTopWindow(), wxTopLevelWindow); if ( tlw ) icon = tlw->GetIcon(); } diff --git a/src/html/m_image.cpp b/src/html/m_image.cpp index cf1836a86c..4123efcffb 100644 --- a/src/html/m_image.cpp +++ b/src/html/m_image.cpp @@ -691,8 +691,8 @@ TAG_HANDLER_BEGIN(IMG, "IMG,MAP,AREA") #if defined(__WXOSX_COCOA__) // Try to find a 2x resolution image with @2x appended before the file extension. wxWindow* win = m_WParser->GetWindowInterface() ? m_WParser->GetWindowInterface()->GetHTMLWindow() : NULL; - if (!win && wxTheApp) - win = wxTheApp->GetTopWindow(); + if (!win) + win = wxApp::GetMainTopWindow(); if (win && win->GetContentScaleFactor() > 1.0) { if (tmp.Find('.') != wxNOT_FOUND) diff --git a/src/msw/artmsw.cpp b/src/msw/artmsw.cpp index 2a3cfe1657..4e08e4abd1 100644 --- a/src/msw/artmsw.cpp +++ b/src/msw/artmsw.cpp @@ -326,7 +326,7 @@ wxBitmap wxWindowsArtProvider::CreateBitmap(const wxArtID& id, /*static*/ wxSize wxArtProvider::GetNativeSizeHint(const wxArtClient& client) { - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); if ( client == wxART_TOOLBAR ) { return wxWindow::FromDIP(wxSize(24, 24), win); diff --git a/src/msw/cursor.cpp b/src/msw/cursor.cpp index 9f3920fe05..e37727a59d 100644 --- a/src/msw/cursor.cpp +++ b/src/msw/cursor.cpp @@ -109,13 +109,13 @@ public: wxCoord wxCursorRefData::GetStandardWidth() { - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); return wxSystemSettings::GetMetric(wxSYS_CURSOR_X, win); } wxCoord wxCursorRefData::GetStandardHeight() { - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); return wxSystemSettings::GetMetric(wxSYS_CURSOR_Y, win); } diff --git a/src/msw/gdiimage.cpp b/src/msw/gdiimage.cpp index db16d2405b..8bb2cf6ecf 100644 --- a/src/msw/gdiimage.cpp +++ b/src/msw/gdiimage.cpp @@ -471,7 +471,7 @@ bool wxICOFileHandler::LoadIcon(wxIcon *icon, else #endif // were we asked for a large icon? - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); if ( desiredWidth == wxGetSystemMetrics(SM_CXICON, win) && desiredHeight == wxGetSystemMetrics(SM_CYICON, win) ) { @@ -666,7 +666,7 @@ wxSize wxGetHiconSize(HICON hicon) if ( !size.x ) { // use default icon size on this hardware - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); size.x = wxGetSystemMetrics(SM_CXICON, win); size.y = wxGetSystemMetrics(SM_CYICON, win); } diff --git a/src/msw/helpchm.cpp b/src/msw/helpchm.cpp index c8207268a4..b998d061a4 100644 --- a/src/msw/helpchm.cpp +++ b/src/msw/helpchm.cpp @@ -69,8 +69,8 @@ HTMLHELP GetHtmlHelpFunction() // fall back to the top level app window and then the desktop if it's NULL static HWND GetSuitableHWND(wxWindow *win) { - if ( !win && wxTheApp ) - win = wxTheApp->GetTopWindow(); + if ( !win ) + win = wxApp::GetMainTopWindow(); return win ? GetHwndOf(win) : ::GetDesktopWindow(); } diff --git a/src/msw/msgdlg.cpp b/src/msw/msgdlg.cpp index 58dd7a3c5e..36c543f27e 100644 --- a/src/msw/msgdlg.cpp +++ b/src/msw/msgdlg.cpp @@ -393,7 +393,7 @@ void wxMessageDialog::AdjustButtonLabels() /* static */ wxFont wxMessageDialog::GetMessageFont() { - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); const wxNativeFontInfo info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont, win); diff --git a/src/msw/settings.cpp b/src/msw/settings.cpp index 42152ffb36..f4b33df25a 100644 --- a/src/msw/settings.cpp +++ b/src/msw/settings.cpp @@ -179,7 +179,7 @@ wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) // for most (simple) controls, e.g. buttons and such but other // controls may prefer to use lfStatusFont or lfCaptionFont if it // is more appropriate for them - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); const wxNativeFontInfo info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont, win); @@ -337,7 +337,7 @@ extern wxFont wxGetCCDefaultFont() // font which is also used for the icon titles and not the stock default // GUI font LOGFONT lf; - const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + const wxWindow* win = wxApp::GetMainTopWindow(); if ( wxSystemParametersInfo ( SPI_GETICONTITLELOGFONT, diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 8eb40c461a..2c5e6b8051 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -4236,7 +4236,7 @@ bool wxWindowMSW::HandleEndSession(bool endSession, long logOff) return false; // only send once - if ( (this != wxTheApp->GetTopWindow()) ) + if ( this != wxApp::GetMainTopWindow() ) return false; wxCloseEvent event(wxEVT_END_SESSION, wxID_ANY); @@ -4773,10 +4773,11 @@ static wxSize GetWindowDPI(HWND hwnd) } /*extern*/ -int wxGetSystemMetrics(int nIndex, const wxWindow* win) +int wxGetSystemMetrics(int nIndex, const wxWindow* window) { #if wxUSE_DYNLIB_CLASS - const wxWindow* window = (!win && wxTheApp) ? wxTheApp->GetTopWindow() : win; + if ( !window ) + window = wxApp::GetMainTopWindow(); if ( window ) { @@ -4798,14 +4799,14 @@ int wxGetSystemMetrics(int nIndex, const wxWindow* win) } } #else - wxUnusedVar(win); + wxUnusedVar(window); #endif // wxUSE_DYNLIB_CLASS return ::GetSystemMetrics(nIndex); } /*extern*/ -bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, const wxWindow* win) +bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, const wxWindow* window) { // Note that we can't use SystemParametersInfoForDpi() in non-Unicode build // because it always works with wide strings and we'd have to check for all @@ -4813,7 +4814,8 @@ bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWi // for them, and convert the returned value to ANSI after the call. Instead // of doing all this, just don't use it at all in the deprecated ANSI build. #if wxUSE_DYNLIB_CLASS && wxUSE_UNICODE - const wxWindow* window = (!win && wxTheApp) ? wxTheApp->GetTopWindow() : win; + if ( !window ) + window = wxApp::GetMainTopWindow(); if ( window ) { @@ -4838,7 +4840,7 @@ bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWi } } #else - wxUnusedVar(win); + wxUnusedVar(window); #endif // wxUSE_DYNLIB_CLASS return ::SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni) == TRUE; From 9073221584f3fed50a1786db3313e4eabc1823d7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 7 Mar 2021 15:43:55 +0100 Subject: [PATCH 2/6] Use wxApp::GetTraitsIfExists() wrappers when applicable This is simpler and more clear than testing wxTheApp explicitly. No real changes. --- src/common/appbase.cpp | 2 +- src/common/cmdline.cpp | 3 +-- src/common/config.cpp | 2 +- src/common/fmapbase.cpp | 2 +- src/common/log.cpp | 2 +- src/common/msgout.cpp | 2 +- src/common/platinfo.cpp | 2 +- src/common/rendcmn.cpp | 3 +-- src/common/stdpbase.cpp | 2 +- src/common/timercmn.cpp | 2 +- src/msw/utilsexc.cpp | 2 +- 11 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/common/appbase.cpp b/src/common/appbase.cpp index 7d1467f2c2..af819469b2 100644 --- a/src/common/appbase.cpp +++ b/src/common/appbase.cpp @@ -358,7 +358,7 @@ wxAppTraits *wxAppConsoleBase::GetTraitsIfExists() wxAppTraits& wxAppConsoleBase::GetValidTraits() { static wxConsoleAppTraits s_traitsConsole; - wxAppTraits* const traits = (wxTheApp ? wxTheApp->GetTraits() : NULL); + wxAppTraits* const traits = GetTraitsIfExists(); return *(traits ? traits : &s_traitsConsole); } diff --git a/src/common/cmdline.cpp b/src/common/cmdline.cpp index eb2b806830..531f7a772f 100644 --- a/src/common/cmdline.cpp +++ b/src/common/cmdline.cpp @@ -1430,9 +1430,8 @@ wxString wxCmdLineParser::GetUsageString() const count = namesOptions.size(); // get option names & descriptions for standard options, if any: - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; wxString stdDesc; - if ( traits ) + if ( wxAppTraits *traits = wxApp::GetTraitsIfExists() ) stdDesc = traits->GetStandardCmdLineOptions(namesOptions, descOptions); // now construct the detailed help message diff --git a/src/common/config.cpp b/src/common/config.cpp index 6cbded738d..f2ea75e3be 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -98,7 +98,7 @@ wxConfigBase *wxConfigBase::Set(wxConfigBase *pConfig) wxConfigBase *wxConfigBase::Create() { if ( ms_bAutoCreate && ms_pConfig == NULL ) { - wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits * const traits = wxApp::GetTraitsIfExists(); wxCHECK_MSG( traits, NULL, wxT("create wxApp before calling this") ); ms_pConfig = traits->CreateConfig(); diff --git a/src/common/fmapbase.cpp b/src/common/fmapbase.cpp index 32b1d3bf49..21704ea4d4 100644 --- a/src/common/fmapbase.cpp +++ b/src/common/fmapbase.cpp @@ -413,7 +413,7 @@ wxFontMapperBase *wxFontMapperBase::Get() { if ( !sm_instance ) { - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits *traits = wxApp::GetTraitsIfExists(); if ( traits ) { sm_instance = traits->CreateFontMapper(); diff --git a/src/common/log.cpp b/src/common/log.cpp index 85e8b4fd61..3bf1c33d40 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -892,7 +892,7 @@ void wxLogStderr::DoLogText(const wxString& msg) // simply lost if ( m_fp == stderr ) { - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits *traits = wxApp::GetTraitsIfExists(); if ( traits && !traits->HasStderr() ) { wxMessageOutputDebug().Output(msg + wxS('\n')); diff --git a/src/common/msgout.cpp b/src/common/msgout.cpp index bae7b781b3..0511224168 100644 --- a/src/common/msgout.cpp +++ b/src/common/msgout.cpp @@ -105,7 +105,7 @@ void wxMessageOutputBest::Output(const wxString& str) { #ifdef __WINDOWS__ // decide whether to use console output or not - wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits * const traits = wxApp::GetTraitsIfExists(); const bool hasStderr = traits ? traits->CanUseStderr() : false; if ( !(m_flags & wxMSGOUT_PREFER_MSGBOX) ) diff --git a/src/common/platinfo.cpp b/src/common/platinfo.cpp index f06e0706e7..b19f6f9e09 100644 --- a/src/common/platinfo.cpp +++ b/src/common/platinfo.cpp @@ -174,7 +174,7 @@ void wxPlatformInfo::InitForCurrentPlatform() m_initializedForCurrentPlatform = true; // autodetect all informations - const wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + const wxAppTraits * const traits = wxApp::GetTraitsIfExists(); if ( !traits ) { wxFAIL_MSG( wxT("failed to initialize wxPlatformInfo") ); diff --git a/src/common/rendcmn.cpp b/src/common/rendcmn.cpp index 0f02a2740a..d17940b427 100644 --- a/src/common/rendcmn.cpp +++ b/src/common/rendcmn.cpp @@ -65,8 +65,7 @@ private: void DoInit() { - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; - if ( traits ) + if ( wxAppTraits *traits = wxApp::GetTraitsIfExists() ) { // ask the traits object to create a renderer for us reset(traits->CreateRenderer()); diff --git a/src/common/stdpbase.cpp b/src/common/stdpbase.cpp index 59897b33af..5f5c12a0c8 100644 --- a/src/common/stdpbase.cpp +++ b/src/common/stdpbase.cpp @@ -55,7 +55,7 @@ static wxStandardPathsDefault gs_stdPaths; /* static */ wxStandardPaths& wxStandardPathsBase::Get() { - wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits * const traits = wxApp::GetTraitsIfExists(); wxCHECK_MSG( traits, gs_stdPaths, wxT("create wxApp before calling this") ); return traits->GetStandardPaths(); diff --git a/src/common/timercmn.cpp b/src/common/timercmn.cpp index 0f8f1f41f4..943ef10ec7 100644 --- a/src/common/timercmn.cpp +++ b/src/common/timercmn.cpp @@ -54,7 +54,7 @@ wxTimer::~wxTimer() void wxTimer::Init() { - wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits * const traits = wxApp::GetTraitsIfExists(); m_impl = traits ? traits->CreateTimerImpl(this) : NULL; if ( !m_impl ) { diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp index 0ebdd59d24..e23b22fc26 100644 --- a/src/msw/utilsexc.cpp +++ b/src/msw/utilsexc.cpp @@ -1018,7 +1018,7 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler, return pi.dwProcessId; } - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + wxAppTraits *traits = wxApp::GetTraitsIfExists(); wxCHECK_MSG( traits, -1, wxT("no wxAppTraits in wxExecute()?") ); void *cookie = NULL; From 39883a270a0f1e7b093183e3dfbe613250cb2205 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 7 Mar 2021 15:37:35 +0100 Subject: [PATCH 3/6] Disable main window when showing msgbox in wxSafeShowMessage() Pass correct parent HWND to ::MessageBox() in order to disable the window while the message box is shown, as this function is supposed to be similar to modal wxMessageBox() and it was unexpected that the application could be reentered via the event handlers from inside it. This required adding wxAppTraits::GetMainHWND() in order to only use the HWND in GUI applications from the function defined in non-GUI code. --- include/wx/msw/apptbase.h | 3 +++ include/wx/msw/apptrait.h | 4 ++++ src/common/log.cpp | 4 +++- src/msw/app.cpp | 6 ++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/wx/msw/apptbase.h b/include/wx/msw/apptbase.h index 2442bf3b61..c6d6ce4b45 100644 --- a/include/wx/msw/apptbase.h +++ b/include/wx/msw/apptbase.h @@ -58,6 +58,9 @@ public: // write text to the console, return true if ok or false on error virtual bool WriteToStderr(const wxString& text) = 0; + // return the main application window or 0 if none + virtual WXHWND GetMainHWND() const = 0; + protected: #if wxUSE_THREADS // implementation of WaitForThread() for the console applications which is diff --git a/include/wx/msw/apptrait.h b/include/wx/msw/apptrait.h index 4d6c29abc4..30a61d4f31 100644 --- a/include/wx/msw/apptrait.h +++ b/include/wx/msw/apptrait.h @@ -30,6 +30,7 @@ public: #endif // wxUSE_THREADS virtual bool CanUseStderr() wxOVERRIDE { return true; } virtual bool WriteToStderr(const wxString& text) wxOVERRIDE; + virtual WXHWND GetMainHWND() const wxOVERRIDE { return NULL; } }; #if wxUSE_GUI @@ -55,6 +56,7 @@ public: virtual bool CanUseStderr() wxOVERRIDE; virtual bool WriteToStderr(const wxString& text) wxOVERRIDE; + virtual WXHWND GetMainHWND() const wxOVERRIDE; }; #elif defined(__WXGTK__) @@ -85,6 +87,7 @@ public: virtual bool CanUseStderr() { return false; } virtual bool WriteToStderr(const wxString& WXUNUSED(text)) { return false; } + virtual WXHWND GetMainHWND() const wxOVERRIDE { return NULL; } }; #elif defined(__WXQT__) @@ -110,6 +113,7 @@ public: virtual bool CanUseStderr() { return false; } virtual bool WriteToStderr(const wxString&) { return false; } + virtual WXHWND GetMainHWND() const wxOVERRIDE { return NULL; } }; #endif diff --git a/src/common/log.cpp b/src/common/log.cpp index 3bf1c33d40..8122b7d9e2 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -183,7 +183,9 @@ private: void wxSafeShowMessage(const wxString& title, const wxString& text) { #ifdef __WINDOWS__ - ::MessageBox(NULL, text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP); + const wxAppTraits* const traits = wxApp::GetTraitsIfExists(); + ::MessageBox(traits ? traits->GetMainHWND() : NULL, + text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP); #else wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str()); fflush(stderr); diff --git a/src/msw/app.cpp b/src/msw/app.cpp index 17adabf9a8..d8b19f0295 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -603,6 +603,12 @@ bool wxGUIAppTraits::WriteToStderr(const wxString& WXUNUSED(text)) #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS +WXHWND wxGUIAppTraits::GetMainHWND() const +{ + const wxWindow* const w = wxApp::GetMainTopWindow(); + return w ? w->GetHWND() : NULL; +} + // =========================================================================== // wxApp implementation // =========================================================================== From fdd4ff8bf9e8fbd8e1ea3b6852e0e87e24bd68cc Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 7 Mar 2021 19:34:18 +0100 Subject: [PATCH 4/6] Make wxSafeShowMessage() msgbox app-modal in some cases Although we don't want to use MB_TASKMODAL unconditionally, because it results in non-optimal UI behaviour when there is a parent window, there is no reason not to use it when we don't have any parent anyhow, so do this, at least. --- src/common/log.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/common/log.cpp b/src/common/log.cpp index 8122b7d9e2..463deb04f7 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -184,8 +184,28 @@ void wxSafeShowMessage(const wxString& title, const wxString& text) { #ifdef __WINDOWS__ const wxAppTraits* const traits = wxApp::GetTraitsIfExists(); - ::MessageBox(traits ? traits->GetMainHWND() : NULL, - text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP); + 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); From e00d5e131bd3cc490fe9f472124f93d092b1eada Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 7 Mar 2021 20:50:30 +0100 Subject: [PATCH 5/6] 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. --- include/wx/apptrait.h | 19 +++++++++++++++++++ include/wx/msw/apptbase.h | 4 ++++ interface/wx/apptrait.h | 23 +++++++++++++++++++++++ src/common/appbase.cpp | 8 ++++++++ src/common/appcmn.cpp | 19 +++++++++++++++++++ src/common/log.cpp | 33 +++++---------------------------- src/msw/basemsw.cpp | 29 +++++++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 28 deletions(-) diff --git a/include/wx/apptrait.h b/include/wx/apptrait.h index f93b90a621..c7d99aae0f 100644 --- a/include/wx/apptrait.h +++ b/include/wx/apptrait.h @@ -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__ diff --git a/include/wx/msw/apptbase.h b/include/wx/msw/apptbase.h index c6d6ce4b45..2dea2f5eed 100644 --- a/include/wx/msw/apptbase.h +++ b/include/wx/msw/apptbase.h @@ -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 diff --git a/interface/wx/apptrait.h b/interface/wx/apptrait.h index 9eff45d838..776c18e0ee 100644 --- a/interface/wx/apptrait.h +++ b/interface/wx/apptrait.h @@ -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; }; diff --git a/src/common/appbase.cpp b/src/common/appbase.cpp index af819469b2..c613393b90 100644 --- a/src/common/appbase.cpp +++ b/src/common/appbase.cpp @@ -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 // ---------------------------------------------------------------------------- diff --git a/src/common/appcmn.cpp b/src/common/appcmn.cpp index d257599f42..404429d4ff 100644 --- a/src/common/appcmn.cpp +++ b/src/common/appcmn.cpp @@ -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__ diff --git a/src/common/log.cpp b/src/common/log.cpp index 463deb04f7..c5ab359fd0 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -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); + } } // ---------------------------------------------------------------------------- diff --git a/src/msw/basemsw.cpp b/src/msw/basemsw.cpp index ce270b73bc..7623bab46a 100644 --- a/src/msw/basemsw.cpp +++ b/src/msw/basemsw.cpp @@ -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) { From 77593c5996b3514ac807581a987183278e189737 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 14 Mar 2021 13:18:36 +0100 Subject: [PATCH 6/6] Add return value to wxSafeShowMessage() This allows the caller to log the message to the console in addition to showing the message box, for example. Previously, this would be impossible to do without getting the duplicates if the message box was not shown, but now it is. --- include/wx/log.h | 2 +- interface/wx/log.h | 7 ++++++- src/common/log.cpp | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/wx/log.h b/include/wx/log.h index f3552e9724..b6d3ecc209 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -1459,7 +1459,7 @@ inline void wxLogNop() { } // wxLogFatalError helper: show the (fatal) error to the user in a safe way, // i.e. without using wxMessageBox() for example because it could crash -void WXDLLIMPEXP_BASE +bool WXDLLIMPEXP_BASE wxSafeShowMessage(const wxString& title, const wxString& text); // ---------------------------------------------------------------------------- diff --git a/interface/wx/log.h b/interface/wx/log.h index f875709dba..62f46a2e5c 100644 --- a/interface/wx/log.h +++ b/interface/wx/log.h @@ -1268,12 +1268,17 @@ public: message string. @param text The text to show to the user. + @return + @true If a message box was actually shown or @false if the message was + logged to the console because there is no safe to show it currently + (the return value is only available since wxWidgets 3.1.5, the function + doesn't return anything in the previous versions). @see wxLogFatalError() @header{wx/log.h} */ -void wxSafeShowMessage(const wxString& title, const wxString& text); +bool wxSafeShowMessage(const wxString& title, const wxString& text); /** Returns the error code from the last system call. This function uses diff --git a/src/common/log.cpp b/src/common/log.cpp index c5ab359fd0..ee11051613 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -180,13 +180,17 @@ private: // helper global functions // ---------------------------------------------------------------------------- -void wxSafeShowMessage(const wxString& title, const wxString& text) +bool wxSafeShowMessage(const wxString& title, const wxString& text) { if ( !wxApp::GetValidTraits().SafeMessageBox(text, title) ) { wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str()); fflush(stderr); + return false; } + + // Message box actually shown. + return true; } // ----------------------------------------------------------------------------