diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj index c3a5881e2d..aefa145428 100644 --- a/common/build/Utilities/utilities.vcproj +++ b/common/build/Utilities/utilities.vcproj @@ -203,6 +203,10 @@ RelativePath="..\..\src\Utilities\Console.cpp" > + + @@ -429,6 +433,10 @@ RelativePath="..\..\include\Utilities\EventSource.h" > + + diff --git a/common/include/Utilities/EventSource.h b/common/include/Utilities/EventSource.h index 938902bee8..1b7024d51a 100644 --- a/common/include/Utilities/EventSource.h +++ b/common/include/Utilities/EventSource.h @@ -78,7 +78,7 @@ public: protected: typedef typename ListenerList::const_iterator ConstIterator; - ListenerList m_listeners; + ListenerList m_listeners; // This is a cached copy of the listener list used to handle standard dispatching, which // allows for self-modification of the EventSource's listener list by the listeners. @@ -112,10 +112,6 @@ public: return m_listeners.begin(); } - // Checks for duplicates before adding the event. - virtual inline void Add( const ListenerType& listener ); - virtual inline void RemoveObject( const void* object ); - void Add( void* objhandle, typename ListenerType::FuncType* fnptr ) { Add( ListenerType( objhandle, fnptr ) ); @@ -126,43 +122,17 @@ public: Remove( ListenerType( objhandle, fnptr ) ); } - void Dispatch( EvtType& evt ) - { - if( !m_cache_valid ) - { - m_cache_copy = m_listeners; - m_cache_valid = true; - } - - _DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evt ); - } + // Checks for duplicates before adding the event. + virtual void Add( const ListenerType& listener ); + virtual void RemoveObject( const void* object ); + void Dispatch( EvtType& evt ); protected: - __forceinline void _DispatchRaw( ConstIterator iter, const ConstIterator& iend, EvtType& evt ) - { - while( iter != iend ) - { - try - { - iter->OnEvent( iter->object, evt ); - } - catch( Exception::RuntimeError& ex ) - { - Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() ); - } - catch( Exception::BaseException& ex ) - { - if( IsDevBuild ) throw; - Console.Error( L"Ignoring non-runtime BaseException thrown from event listener: " + ex.FormatDiagnosticMessage() ); - } - ++iter; - } - } - + inline void _DispatchRaw( ConstIterator iter, const ConstIterator& iend, EvtType& evt ); }; // -------------------------------------------------------------------------------------- -// EventListenerBinding< typename EvtType ? +// EventListenerBinding< typename EvtType > // -------------------------------------------------------------------------------------- // Encapsulated event listener binding, provides the "benefits" of object unwinding. // @@ -215,44 +185,9 @@ typedef EventSource CmdEvt_Source; typedef EventListener CmdEvt_Listener; typedef EventListenerBinding CmdEvt_ListenerBinding; - -// Checks for duplicates before adding the event. -template< typename EvtType > -void EventSource::Add( const ListenerType& listener ) -{ - if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return; - - Handle iter = m_listeners.begin(); - while( iter != m_listeners.end() ) - { - if( *iter == listener ) return; - ++iter; - } - AddFast( listener ); -} - -template< typename EvtType > -class PredicatesAreTheThingsOfNightmares -{ - typedef EventListener< EvtType > ListenerType; - -protected: - const void* const m_object_match; - -public: - PredicatesAreTheThingsOfNightmares( const void* objmatch ) : m_object_match( objmatch ) { } - - bool operator()( const ListenerType& src ) const - { - return src.object == m_object_match; - } -}; - -// removes all listeners which reference the given object. Use for assuring object deletion. -template< typename EvtType > -void EventSource::RemoveObject( const void* object ) -{ - m_cache_valid = false; - m_listeners.remove_if( PredicatesAreTheThingsOfNightmares( object ) ); -} - +#define EventSource_ImplementType( tname ) \ + template void EventSource::Add(const EventSource::ListenerType &listener); \ + template void EventSource::RemoveObject(const void* object); \ + template void EventSource::Add(const EventSource::ListenerType &listener); \ + template void EventSource::Dispatch(tname& evt); \ + template void EventSource::_DispatchRaw( EventSource::ConstIterator iter, const EventSource::ConstIterator& iend, tname& evt ); diff --git a/common/include/Utilities/EventSource.inl b/common/include/Utilities/EventSource.inl new file mode 100644 index 0000000000..755c6ec2c6 --- /dev/null +++ b/common/include/Utilities/EventSource.inl @@ -0,0 +1,90 @@ +/* PCSX2 - PS2 Emulator for PCs +* Copyright (C) 2002-2009 PCSX2 Dev Team +* +* PCSX2 is free software: you can redistribute it and/or modify it under the terms +* of the GNU Lesser General Public License as published by the Free Software Found- +* ation, either version 3 of the License, or (at your option) any later version. +* +* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with PCSX2. +* If not, see . +*/ + +#pragma once + +// Checks for duplicates before adding the event. +template< typename EvtType > +void EventSource::Add( const ListenerType& listener ) +{ + if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return; + + Handle iter = m_listeners.begin(); + while( iter != m_listeners.end() ) + { + if( *iter == listener ) return; + ++iter; + } + AddFast( listener ); +} + +template< typename EvtType > +class PredicatesAreTheThingsOfNightmares +{ + typedef EventListener< EvtType > ListenerType; + +protected: + const void* const m_object_match; + +public: + PredicatesAreTheThingsOfNightmares( const void* objmatch ) : m_object_match( objmatch ) { } + + bool operator()( const ListenerType& src ) const + { + return src.object == m_object_match; + } +}; + +// removes all listeners which reference the given object. Use for assuring object deletion. +template< typename EvtType > +void EventSource::RemoveObject( const void* object ) +{ + m_cache_valid = false; + m_listeners.remove_if( PredicatesAreTheThingsOfNightmares( object ) ); +} + +template< typename EvtType > +__forceinline void EventSource::_DispatchRaw( ConstIterator iter, const ConstIterator& iend, EvtType& evt ) +{ + while( iter != iend ) + { + try + { + iter->OnEvent( iter->object, evt ); + } + catch( Exception::RuntimeError& ex ) + { + Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() ); + } + catch( Exception::BaseException& ex ) + { + if( IsDevBuild ) throw; + Console.Error( L"Ignoring non-runtime BaseException thrown from event listener: " + ex.FormatDiagnosticMessage() ); + } + ++iter; + } +} + +template< typename EvtType > +void EventSource::Dispatch( EvtType& evt ) +{ + if( !m_cache_valid ) + { + m_cache_copy = m_listeners; + m_cache_valid = true; + } + + _DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evt ); +} diff --git a/common/src/Utilities/EventSource.cpp b/common/src/Utilities/EventSource.cpp new file mode 100644 index 0000000000..a128352cd8 --- /dev/null +++ b/common/src/Utilities/EventSource.cpp @@ -0,0 +1,23 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "EventSource.h" +#include "EventSource.inl" + +#include + +EventSource_ImplementType( wxCommandEvent ); +EventSource_ImplementType( int ); diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 141623f60f..0a2cb7da43 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -28,6 +28,9 @@ #include "CDVD/CDVD.h" #include "System/PageFaultSource.h" +#include "Utilities/EventSource.inl" +EventSource_ImplementType( PageFaultInfo ); + SrcType_PageFault Source_PageFault; #if _MSC_VER diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index f50a86e168..02aa72a5f7 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -531,6 +531,11 @@ DECLARE_APP(Pcsx2App) #define sMainFrame \ if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_) +// Use this within the scope of a wxWindow (wxDialog or wxFrame). If the window has a valid menu +// bar, the command will run, otherwise it will be silently ignored. :) +#define sMenuBar \ + if( wxMenuBar* __menubar_ = GetMenuBar() ) (*__menubar_) + // -------------------------------------------------------------------------------------- // External App-related Globals and Shortcuts diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp index 97cb3c6bb5..cb2343a820 100644 --- a/pcsx2/gui/AppConfig.cpp +++ b/pcsx2/gui/AppConfig.cpp @@ -520,7 +520,7 @@ void RelocateLogfile() if( (emuLog != NULL) && (emuLogName != newlogname) ) { - Console.Status( "\nRelocating Logfile...\n\tFrom: %s\n\tTo : %s\n", emuLogName.c_str(), newlogname.c_str() ); + Console.Status( wxsFormat(L"\nRelocating Logfile...\n\tFrom: %s\n\tTo : %s\n", emuLogName.c_str(), newlogname.c_str()) ); wxGetApp().DisableDiskLogging(); fclose( emuLog ); diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 944cef4239..16a1f8c20b 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -447,6 +447,6 @@ struct CrtDebugBreak } }; -//CrtDebugBreak breakAt( 8890 ); +//CrtDebugBreak breakAt( 7586 ); #endif \ No newline at end of file diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index cf8f91e80f..6d076a065e 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -39,6 +39,10 @@ DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); DEFINE_EVENT_TYPE( pxEVT_CoreThreadStatus ); DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished ); +#include "Utilities/EventSource.inl" +EventSource_ImplementType( IniInterface ); +EventSource_ImplementType( AppEventType ); + bool UseAdminMode = false; wxDirName SettingsFolder; bool UseDefaultSettingsFolder = true; diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp index f6827bc2d8..d329529f9f 100644 --- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp +++ b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp @@ -168,8 +168,6 @@ void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt ) evt.Veto(); return; } - m_panel_PluginSel.ReloadSettings(); - m_panel_BiosSel.ReloadSettings(); } } } diff --git a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp b/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp index 2d5a4dc978..f3d329ec4f 100644 --- a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp +++ b/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp @@ -55,7 +55,7 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) : void Dialogs::ImportSettingsDialog::OnImport_Click( wxCommandEvent& /* evt */ ) { - AppConfig_OnChangedSettingsFolder( false ); // ... and import existing settings + AppConfig_OnChangedSettingsFolder( false ); // ... and import existing settings g_Conf->Folders.Bios.Mkdir(); EndModal( wxID_OK ); } diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index 5dcc68303d..7210a11cb1 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -70,8 +70,7 @@ void MainEmuFrame::UpdateIsoSrcSelection() jNO_DEFAULT } - - GetMenuBar()->Check( cdsrc, true ); + sMenuBar.Check( cdsrc, true ); m_statusbar.SetStatusText( CDVD_SourceLabels[g_Conf->CdvdSource], 1 ); } @@ -86,7 +85,7 @@ void MainEmuFrame::UpdateIsoSrcFile() label.Printf( L"%s -> %s", _("Iso"), exists ? g_Conf->CurrentIso.c_str() : _("Empty") ); - GetMenuBar()->SetLabel( MenuId_Src_Iso, label ); + sMenuBar.SetLabel( MenuId_Src_Iso, label ); } void MainEmuFrame::LoadSaveRecentIsoList( IniInterface& conf ) @@ -321,13 +320,13 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): // Initial menubar setup. This needs to be done first so that the menu bar's visible size // can be factored into the window size (which ends up being background+status+menus) - m_menubar.Append( &m_menuBoot, _("Boot") ); - m_menubar.Append( &m_menuEmu, _("Emulation") ); - m_menubar.Append( &m_menuConfig, _("Config") ); - m_menubar.Append( &m_menuVideo, _("Video") ); - m_menubar.Append( &m_menuAudio, _("Audio") ); - m_menubar.Append( &m_menuMisc, _("Misc") ); - m_menubar.Append( &m_menuDebug, _("Debug") ); + m_menubar.Append( &m_menuBoot, _("&Boot") ); + m_menubar.Append( &m_menuEmu, _("&System") ); + m_menubar.Append( &m_menuConfig, _("&Config") ); + m_menubar.Append( &m_menuVideo, _("&Video") ); + m_menubar.Append( &m_menuAudio, _("&Audio") ); + m_menubar.Append( &m_menuMisc, _("&Misc") ); + m_menubar.Append( &m_menuDebug, _("&Debug") ); SetMenuBar( &m_menubar ); // ------------------------------------------------------------------------ @@ -407,8 +406,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): _("Closing PCSX2 may be hazardous to your health")); // ------------------------------------------------------------------------ - m_menuEmu.Append(MenuId_Sys_SuspendResume, _("Suspend"), - _("Stops emulation dead in its tracks") )->Enable( SysHasValidState() ); + m_menuEmu.Append(MenuId_Sys_SuspendResume, _("Suspend") )->Enable( SysHasValidState() ); m_menuEmu.AppendSeparator(); @@ -428,7 +426,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): // ------------------------------------------------------------------------ - m_menuConfig.Append(MenuId_Config_Settings, _("General Settings") ); + m_menuConfig.Append(MenuId_Config_Settings, _("General &Settings") ); m_menuConfig.AppendSeparator(); m_menuConfig.Append(MenuId_Config_PAD, _("PAD"), &m_menuPad ); @@ -517,20 +515,44 @@ void MainEmuFrame::ReloadRecentLists() void MainEmuFrame::ApplyCoreStatus() { - bool valstate = SysHasValidState(); + wxMenuBar& menubar( *GetMenuBar() ); + if( !pxAssertMsg( &menubar!=NULL, "Mainframe menu bar is NULL!" ) ) return; - GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() ); - GetMenuBar()->Enable( MenuId_Sys_Reset, SysHasValidState() || (g_plugins!=NULL) ); + wxMenuItem& susres( *menubar.FindItem( MenuId_Sys_SuspendResume ) ); + if( !pxAssertMsg( &susres!=NULL, "Suspend/Resume Menubar Item is NULL!" ) ) return; - GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, CoreThread.IsOpen() ? _("Suspend") : _("Resume") ); + if( SysHasValidState() ) + { + susres.Enable(); + if( CoreThread.IsOpen() ) + { + susres.SetHelp( _("Safely pauses emulation and preserves the PS2 state.") ); + susres.SetText( _("Suspend") ); + } + else + { + susres.SetHelp( _("Resumes the suspended emulation state.") ); + susres.SetText( _("Resume") ); + } + } + else + { + susres.Enable( false ); + susres.SetHelp( _("No emulation state is active; cannot suspend or resume.") ); + } + + menubar.Enable( MenuId_Sys_Reset, SysHasValidState() || (g_plugins!=NULL) ); } void MainEmuFrame::ApplySettings() { - GetMenuBar()->Check( MenuId_SkipBiosToggle, g_Conf->EmuOptions.SkipBiosSplash ); + wxMenuBar& menubar( *GetMenuBar() ); + if( !pxAssertMsg( &menubar!=NULL, "Mainframe menu bar is NULL!" ) ) return; - GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled ); - GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled ); + menubar.Check( MenuId_SkipBiosToggle, g_Conf->EmuOptions.SkipBiosSplash ); + + menubar.Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled ); + menubar.Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled ); if( m_RecentIsoList ) { diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index eb9874030b..33c8eed057 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -186,17 +186,16 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event) { if( !SysHasValidState() ) return; - // Note: We manually update the menu here, even though it'll be updated again - // when the thread "officially" suspends or resumes (via listener callback), because - // the callback is tied to the actual thread status - - if( CoreThread.Suspend() ) - GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, _("Resume") ); - else + if( !CoreThread.Suspend() ) { sApp.SysExecute(); - GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, _("Suspend") ); } + + // Disable the menu item. The state of the menu is indeterminate until the core thread + // has responded (it updates status after the plugins are loaded and emulation has + // engaged successfully). + + GetMenuBar()->Enable( MenuId_Sys_SuspendResume, false ); } void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event) diff --git a/pcsx2/gui/Panels/BiosSelectorPanel.cpp b/pcsx2/gui/Panels/BiosSelectorPanel.cpp index 2d35998edc..91f71cec58 100644 --- a/pcsx2/gui/Panels/BiosSelectorPanel.cpp +++ b/pcsx2/gui/Panels/BiosSelectorPanel.cpp @@ -14,6 +14,7 @@ */ #include "PrecompiledHeader.h" +#include "App.h" #include "ConfigurationPanels.h" #include "Utilities/ScopedPtr.h" @@ -27,6 +28,7 @@ using namespace wxHelpers; // ------------------------------------------------------------------------ Panels::BaseSelectorPanel::BaseSelectorPanel( wxWindow& parent, int idealWidth ) : BaseApplicableConfigPanel( &parent, idealWidth ) +, m_ReloadSettingsBinding( wxGetApp().Source_SettingsApplied(), EventListener( this, OnAppliedSettings ) ) { Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(PluginSelectorPanel::OnFolderChanged), NULL, this ); } @@ -61,6 +63,11 @@ void Panels::BaseSelectorPanel::OnFolderChanged( wxFileDirPickerEvent& evt ) OnShown(); } +void Panels::BaseSelectorPanel::OnAppliedSettings( void* me, int& ) +{ + if( me == NULL ) return; + ((BaseSelectorPanel*)me)->ReloadSettings(); +} // ---------------------------------------------------------------------------- Panels::BiosSelectorPanel::BiosSelectorPanel( wxWindow& parent, int idealWidth ) : diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h index 236e70dc8e..9efdfe9321 100644 --- a/pcsx2/gui/Panels/ConfigurationPanels.h +++ b/pcsx2/gui/Panels/ConfigurationPanels.h @@ -31,6 +31,7 @@ #include "wxHelpers.h" #include "Utilities/SafeArray.h" #include "Utilities/Threading.h" +#include "Utilities/EventSource.h" class wxListBox; @@ -336,9 +337,10 @@ namespace Panels class BaseSelectorPanel: public BaseApplicableConfigPanel { protected: + EventListenerBinding m_ReloadSettingsBinding; public: - virtual ~BaseSelectorPanel(); + virtual ~BaseSelectorPanel() throw(); BaseSelectorPanel( wxWindow& parent, int idealWidth ); virtual bool Show( bool visible=true ); @@ -349,6 +351,9 @@ namespace Panels protected: virtual void DoRefresh()=0; virtual bool ValidateEnumerationStatus()=0; + virtual void ReloadSettings()=0; + + static void __evt_fastcall OnAppliedSettings( void* me, int& whatever ); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -363,12 +368,13 @@ namespace Panels public: BiosSelectorPanel( wxWindow& parent, int idealWidth ); virtual ~BiosSelectorPanel(); - void ReloadSettings(); protected: virtual void Apply(); virtual void DoRefresh(); virtual bool ValidateEnumerationStatus(); + + virtual void ReloadSettings(); }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -468,7 +474,6 @@ namespace Panels void CancelRefresh(); // used from destructor, stays non-virtual void Apply(); - void ReloadSettings(); protected: void OnConfigure_Clicked( wxCommandEvent& evt ); @@ -476,11 +481,14 @@ namespace Panels virtual void OnProgress( wxCommandEvent& evt ); virtual void OnEnumComplete( wxCommandEvent& evt ); + virtual void ReloadSettings(); + virtual void DoRefresh(); virtual bool ValidateEnumerationStatus(); int FileCount() const { return m_FileList->Count(); } const wxString& GetFilename( int i ) const { return (*m_FileList)[i]; } + friend class EnumThread; }; } diff --git a/pcsx2/gui/wxHelpers.cpp b/pcsx2/gui/wxHelpers.cpp index 2aa6e9c163..b9f0060462 100644 --- a/pcsx2/gui/wxHelpers.cpp +++ b/pcsx2/gui/wxHelpers.cpp @@ -272,7 +272,7 @@ wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxStr ++m_DialogIdents[GetId()]; if( hasContextHelp ) - wxHelpProvider::Set( new wxSimpleHelpProvider() ); + delete wxHelpProvider::Set( new wxSimpleHelpProvider() ); // Note: currently the Close (X) button doesn't appear to work in dialogs. Docs indicate // that it should, so I presume the problem is in wxWidgets and that (hopefully!) an updated