From: Vadim Zeitlin Date: Sun, 7 Nov 2010 14:00:59 +0000 (+0000) Subject: Add support for id ranges to XRC. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/0526c8cc07ac02aeb1d95afecfa0d9201a8ddea0?ds=sidebyside Add support for id ranges to XRC. Allow to declare ranges of consecutive IDs in XRC by using the "id[n]" syntax. Show this functionality in the xrc sample and test it in the new unit test. Also show and test the "object reference" XRC functionality. Closes #11431. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66059 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index 795d6871ff..f12a9c8f13 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -424,6 +424,7 @@ All (GUI): - Fix display of right aligned columns in wxGenericListCtrl (jl). - Restore text drag-and-drop in wxSTC broken by Scintilla 2 update (Jens Lody). - Improve wxGTK print/page setup dialog (rafravago). +- Add support for id ranges to XRC (David Hart). - Added wxToolbook XRC handler (Andrea Zanellato). - Added wxDocManager::FindTemplate() (troelsk). - Return bool, not void, from wxImage::ConvertAlphaToMask() (troelsk). diff --git a/docs/doxygen/overviews/xrc_format.h b/docs/doxygen/overviews/xrc_format.h index 931aac7c01..b21050130f 100644 --- a/docs/doxygen/overviews/xrc_format.h +++ b/docs/doxygen/overviews/xrc_format.h @@ -31,6 +31,7 @@ Table of contents: - @ref overview_xrcformat_sizers - @ref overview_xrcformat_other_objects - @ref overview_xrcformat_platform +- @ref overview_xrcformat_idranges - @ref overview_xrcformat_extending - @ref overview_xrcformat_extending_subclass - @ref overview_xrcformat_extending_unknown @@ -2060,6 +2061,64 @@ Examples: +@section overview_xrcformat_idranges ID Ranges + +Usually you won't care what value the XRCID macro returns for the ID of an +object. Sometimes though it is convenient to have a range of IDs that are +guaranteed to be consecutive. An example of this would be connecting a group of +similar controls to the same event handler. + +The following XRC fragment 'declares' an ID range called @em foo and another +called @em bar; each with some items. + +@code + + + + ... + + + + ... + + +@endcode + +For the range foo, no @em size or @em start parameters were given, so the size +will be calculated from the number of range items, and IDs allocated by +wxWindow::NewControlId (so they'll be negative). Range bar asked for a size of +30, so this will be its minimum size: should it have more items, the range will +automatically expand to fit them. It specified a start ID of 10000, so +XRCID("bar[0]") will be 10000, XRCID("bar[1]") 10001 etc. Note that if you +choose to supply a start value it must be positive, and it's your +responsibility to avoid clashes. + +For every ID range, the first item can be referenced either as +rangename[0] or rangename[start]. Similarly +rangename[end] is the last item. Using [start] and [end] is more +descriptive in e.g. a Bind() event range or a @em for loop, and they don't have +to be altered whenever the number of items changes. + +Whether a range has positive or negative IDs, [start] is always a smaller +number than [end]; so code like this works as expected: + +@code +for (int n=XRCID("foo[start]"); n < XRCID("foo[end]"); ++n) + ... +@endcode + +ID ranges can be seen in action in the objref dialog section of the +@sample{xrc}. + +@note +@li All the items in an ID range must be contained in the same XRC file. +@li You can't use an ID range in a situation where static initialisation +occurs; in particular, they won't work as expected in an event table. This is +because the event table's IDs are set to their integer values before the XRC +file is loaded, and aren't subsequently altered when the XRCID value changes. + +@since 2.9.2 + @section overview_xrcformat_extending Extending the XRC Format The XRC format is designed to be extensible and allows specifying and loading diff --git a/include/wx/xrc/xmlres.h b/include/wx/xrc/xmlres.h index a585bfaa56..1530fab993 100644 --- a/include/wx/xrc/xmlres.h +++ b/include/wx/xrc/xmlres.h @@ -391,6 +391,8 @@ private: friend class wxXmlResourceHandler; friend class wxXmlResourceModule; + friend class wxIdRangeManager; + friend class wxIdRange; static wxXmlSubclassFactories *ms_subclassFactories; diff --git a/samples/xrc/Makefile.in b/samples/xrc/Makefile.in index 4347831552..82ff476729 100644 --- a/samples/xrc/Makefile.in +++ b/samples/xrc/Makefile.in @@ -43,7 +43,7 @@ wx_top_builddir = @wx_top_builddir@ DESTDIR = WX_RELEASE = 2.9 -WX_VERSION = $(WX_RELEASE).1 +WX_VERSION = $(WX_RELEASE).2 LIBDIRNAME = $(wx_top_builddir)/lib XRCDEMO_CXXFLAGS = -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) \ $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) \ @@ -55,7 +55,8 @@ XRCDEMO_OBJECTS = \ xrcdemo_xrcdemo.o \ xrcdemo_myframe.o \ xrcdemo_derivdlg.o \ - xrcdemo_custclas.o + xrcdemo_custclas.o \ + xrcdemo_objrefdlg.o ### Conditionally set variables: ### @@ -195,7 +196,7 @@ xrcdemo$(EXEEXT): $(XRCDEMO_OBJECTS) $(__xrcdemo___win32rc) data: @mkdir -p ./rc - @for f in artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm; do \ + @for f in artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm; do \ if test ! -f ./rc/$$f -a ! -d ./rc/$$f ; \ then x=yep ; \ else x=`find $(srcdir)/rc/$$f -newer ./rc/$$f -print` ; \ @@ -220,6 +221,9 @@ xrcdemo_derivdlg.o: $(srcdir)/derivdlg.cpp xrcdemo_custclas.o: $(srcdir)/custclas.cpp $(CXXC) -c -o $@ $(XRCDEMO_CXXFLAGS) $(srcdir)/custclas.cpp +xrcdemo_objrefdlg.o: $(srcdir)/objrefdlg.cpp + $(CXXC) -c -o $@ $(XRCDEMO_CXXFLAGS) $(srcdir)/objrefdlg.cpp + # Include dependency info, if present: @IF_GNU_MAKE@-include ./.deps/*.d diff --git a/samples/xrc/derivdlg.h b/samples/xrc/derivdlg.h index e198fb1d9c..904a9dcccd 100644 --- a/samples/xrc/derivdlg.h +++ b/samples/xrc/derivdlg.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Name: xmldemo.cpp +// Name: derivdlg.h // Purpose: XML resources sample: A derived dialog // Author: Robert O'Connor (rob@medicalmnemonics.com), Vaclav Slavik // RCS-ID: $Id$ diff --git a/samples/xrc/makefile.bcc b/samples/xrc/makefile.bcc index 6139b7a092..6bb55818d5 100644 --- a/samples/xrc/makefile.bcc +++ b/samples/xrc/makefile.bcc @@ -39,7 +39,8 @@ XRCDEMO_OBJECTS = \ $(OBJS)\xrcdemo_xrcdemo.obj \ $(OBJS)\xrcdemo_myframe.obj \ $(OBJS)\xrcdemo_derivdlg.obj \ - $(OBJS)\xrcdemo_custclas.obj + $(OBJS)\xrcdemo_custclas.obj \ + $(OBJS)\xrcdemo_objrefdlg.obj ### Conditionally set variables: ### @@ -255,7 +256,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample.res data: if not exist $(OBJS)\rc mkdir $(OBJS)\rc - for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc + for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc $(OBJS)\xrcdemo_sample.res: .\..\..\samples\sample.rc brcc32 -32 -r -fo$@ -i$(BCCDIR)\include -d__WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) $(__MSLU_DEFINE_p_1) -i$(SETUPHDIR) -i.\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_1_p) -i. $(__DLLFLAG_p_1) -i.\..\..\samples -dNOPCH .\..\..\samples\sample.rc @@ -272,3 +273,5 @@ $(OBJS)\xrcdemo_derivdlg.obj: .\derivdlg.cpp $(OBJS)\xrcdemo_custclas.obj: .\custclas.cpp $(CXX) -q -c -P -o$@ $(XRCDEMO_CXXFLAGS) .\custclas.cpp +$(OBJS)\xrcdemo_objrefdlg.obj: .\objrefdlg.cpp + $(CXX) -q -c -P -o$@ $(XRCDEMO_CXXFLAGS) .\objrefdlg.cpp diff --git a/samples/xrc/makefile.gcc b/samples/xrc/makefile.gcc index d3a21e3299..fc745f9d01 100644 --- a/samples/xrc/makefile.gcc +++ b/samples/xrc/makefile.gcc @@ -33,7 +33,8 @@ XRCDEMO_OBJECTS = \ $(OBJS)\xrcdemo_xrcdemo.o \ $(OBJS)\xrcdemo_myframe.o \ $(OBJS)\xrcdemo_derivdlg.o \ - $(OBJS)\xrcdemo_custclas.o + $(OBJS)\xrcdemo_custclas.o \ + $(OBJS)\xrcdemo_objrefdlg.o ### Conditionally set variables: ### @@ -242,7 +243,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample_rc.o data: if not exist $(OBJS)\rc mkdir $(OBJS)\rc - for %%f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%%f copy .\rc\%%f $(OBJS)\rc + for %%f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%%f copy .\rc\%%f $(OBJS)\rc $(OBJS)\xrcdemo_sample_rc.o: ./../../samples/sample.rc windres --use-temp-file -i$< -o$@ --define __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) $(__MSLU_DEFINE_p_1) --include-dir $(SETUPHDIR) --include-dir ./../../include $(__CAIRO_INCLUDEDIR_p) --include-dir . $(__DLLFLAG_p_1) --include-dir ./../../samples --define NOPCH @@ -259,6 +260,9 @@ $(OBJS)\xrcdemo_derivdlg.o: ./derivdlg.cpp $(OBJS)\xrcdemo_custclas.o: ./custclas.cpp $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\xrcdemo_objrefdlg.o: ./objrefdlg.cpp + $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $< + .PHONY: all clean data diff --git a/samples/xrc/makefile.unx b/samples/xrc/makefile.unx index 4e6522080e..6e8df683cf 100644 --- a/samples/xrc/makefile.unx +++ b/samples/xrc/makefile.unx @@ -56,7 +56,8 @@ XRCDEMO_OBJECTS = \ xrcdemo_xrcdemo.o \ xrcdemo_myframe.o \ xrcdemo_derivdlg.o \ - xrcdemo_custclas.o + xrcdemo_custclas.o \ + xrcdemo_objrefdlg.o ### Conditionally set variables: ### @@ -105,6 +106,9 @@ xrcdemo_derivdlg.o: ./derivdlg.cpp xrcdemo_custclas.o: ./custclas.cpp $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $< +xrcdemo_objrefdlg.o: ./objrefdlg.cpp + $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $< + .PHONY: all install uninstall clean diff --git a/samples/xrc/makefile.vc b/samples/xrc/makefile.vc index 5aefe6bb9c..37d8226e18 100644 --- a/samples/xrc/makefile.vc +++ b/samples/xrc/makefile.vc @@ -33,7 +33,8 @@ XRCDEMO_OBJECTS = \ $(OBJS)\xrcdemo_xrcdemo.obj \ $(OBJS)\xrcdemo_myframe.obj \ $(OBJS)\xrcdemo_derivdlg.obj \ - $(OBJS)\xrcdemo_custclas.obj + $(OBJS)\xrcdemo_custclas.obj \ + $(OBJS)\xrcdemo_objrefdlg.obj XRCDEMO_RESOURCES = \ $(OBJS)\xrcdemo_sample.res @@ -365,7 +366,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample.res data: if not exist $(OBJS)\rc mkdir $(OBJS)\rc - for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc + for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc $(OBJS)\xrcdemo_sample.res: .\..\..\samples\sample.rc rc /fo$@ /d WIN32 $(____DEBUGRUNTIME_3_p_1) $(__NO_VC_CRTDBG_p_1) /d __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) $(__MSLU_DEFINE_p_1) /i $(SETUPHDIR) /i .\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_1_p) /i . $(__DLLFLAG_p_1) /d _WINDOWS /i .\..\..\samples /d NOPCH .\..\..\samples\sample.rc @@ -382,3 +383,5 @@ $(OBJS)\xrcdemo_derivdlg.obj: .\derivdlg.cpp $(OBJS)\xrcdemo_custclas.obj: .\custclas.cpp $(CXX) /c /nologo /TP /Fo$@ $(XRCDEMO_CXXFLAGS) .\custclas.cpp +$(OBJS)\xrcdemo_objrefdlg.obj: .\objrefdlg.cpp + $(CXX) /c /nologo /TP /Fo$@ $(XRCDEMO_CXXFLAGS) .\objrefdlg.cpp diff --git a/samples/xrc/makefile.wat b/samples/xrc/makefile.wat index 9b3f37fe49..26a340e149 100644 --- a/samples/xrc/makefile.wat +++ b/samples/xrc/makefile.wat @@ -252,7 +252,8 @@ XRCDEMO_OBJECTS = & $(OBJS)\xrcdemo_xrcdemo.obj & $(OBJS)\xrcdemo_myframe.obj & $(OBJS)\xrcdemo_derivdlg.obj & - $(OBJS)\xrcdemo_custclas.obj + $(OBJS)\xrcdemo_custclas.obj & + $(OBJS)\xrcdemo_objrefdlg.obj all : $(OBJS) @@ -285,7 +286,7 @@ $(OBJS)\xrcdemo.exe : $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample.res data : .SYMBOLIC if not exist $(OBJS)\rc mkdir $(OBJS)\rc - for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc + for %f in (artprov.xpm artprov.xrc basicdlg.xpm basicdlg.xrc controls.xpm controls.xrc custclas.xpm custclas.xrc derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm) do if not exist $(OBJS)\rc\%f copy .\rc\%f $(OBJS)\rc $(OBJS)\xrcdemo_sample.res : .AUTODEPEND .\..\..\samples\sample.rc wrc -q -ad -bt=nt -r -fo=$^@ -d__WXMSW__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__NDEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) $(__UNICODE_DEFINE_p) -i=$(SETUPHDIR) -i=.\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES) -i=. $(__DLLFLAG_p) -i=.\..\..\samples -dNOPCH $< @@ -302,3 +303,5 @@ $(OBJS)\xrcdemo_derivdlg.obj : .AUTODEPEND .\derivdlg.cpp $(OBJS)\xrcdemo_custclas.obj : .AUTODEPEND .\custclas.cpp $(CXX) -bt=nt -zq -fo=$^@ $(XRCDEMO_CXXFLAGS) $< +$(OBJS)\xrcdemo_objrefdlg.obj : .AUTODEPEND .\objrefdlg.cpp + $(CXX) -bt=nt -zq -fo=$^@ $(XRCDEMO_CXXFLAGS) $< diff --git a/samples/xrc/myframe.cpp b/samples/xrc/myframe.cpp index 103d03b851..4bc975327f 100644 --- a/samples/xrc/myframe.cpp +++ b/samples/xrc/myframe.cpp @@ -49,6 +49,8 @@ #include "derivdlg.h" // Our custom class, for the custom class example. #include "custclas.h" +// And our objref dialog, for the object reference and ID range example. +#include "objrefdlg.h" // For functions to manipulate our wxTreeCtrl and wxListCtrl #include "wx/treectrl.h" #include "wx/listctrl.h" @@ -83,6 +85,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(XRCID("derived_tool_or_menuitem"), MyFrame::OnDerivedDialogToolOrMenuCommand) EVT_MENU(XRCID("controls_tool_or_menuitem"), MyFrame::OnControlsToolOrMenuCommand) EVT_MENU(XRCID("uncentered_tool_or_menuitem"), MyFrame::OnUncenteredToolOrMenuCommand) + EVT_MENU(XRCID("obj_ref_tool_or_menuitem"), MyFrame::OnObjRefToolOrMenuCommand) EVT_MENU(XRCID("custom_class_tool_or_menuitem"), MyFrame::OnCustomClassToolOrMenuCommand) EVT_MENU(XRCID("platform_property_tool_or_menuitem"), MyFrame::OnPlatformPropertyToolOrMenuCommand) EVT_MENU(XRCID("art_provider_tool_or_menuitem"), MyFrame::OnArtProviderToolOrMenuCommand) @@ -268,6 +271,22 @@ void MyFrame::OnUncenteredToolOrMenuCommand(wxCommandEvent& WXUNUSED(event)) } +void MyFrame::OnObjRefToolOrMenuCommand(wxCommandEvent& WXUNUSED(event)) +{ + // The dialog redirects log messages, so save the old log target first + wxLog* oldlogtarget = wxLog::SetActiveTarget(NULL); + + // Make an instance of the dialog + ObjrefDialog* objrefDialog = new ObjrefDialog(this); + // Show the instance of the dialog, modally. + objrefDialog->ShowModal(); + objrefDialog->Destroy(); + + // Restore the old log target + delete wxLog::SetActiveTarget(oldlogtarget); +} + + void MyFrame::OnCustomClassToolOrMenuCommand(wxCommandEvent& WXUNUSED(event)) { wxDialog dlg; diff --git a/samples/xrc/myframe.h b/samples/xrc/myframe.h index 9701213ac8..b57bd7ce40 100644 --- a/samples/xrc/myframe.h +++ b/samples/xrc/myframe.h @@ -44,6 +44,7 @@ private: void OnDerivedDialogToolOrMenuCommand(wxCommandEvent& event); void OnControlsToolOrMenuCommand(wxCommandEvent& event); void OnUncenteredToolOrMenuCommand(wxCommandEvent& event); + void OnObjRefToolOrMenuCommand(wxCommandEvent& event); void OnCustomClassToolOrMenuCommand(wxCommandEvent& event); void OnPlatformPropertyToolOrMenuCommand(wxCommandEvent& event); void OnArtProviderToolOrMenuCommand(wxCommandEvent& event); diff --git a/samples/xrc/objrefdlg.cpp b/samples/xrc/objrefdlg.cpp new file mode 100644 index 0000000000..8fbb22acd3 --- /dev/null +++ b/samples/xrc/objrefdlg.cpp @@ -0,0 +1,290 @@ +//----------------------------------------------------------------------------- +// Name: objref.cpp +// Purpose: XML resources sample: Object references and ID ranges dialog +// Author: David Hart, Vaclav Slavik +// RCS-ID: $Id$ +// Copyright: (c) Vaclav Slavik +// Licence: wxWindows licence +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Standard wxWidgets headers +//----------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +// For all others, include the necessary headers (this file is usually all you +// need because it includes almost all "standard" wxWidgets headers) +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +//----------------------------------------------------------------------------- +// Header of this .cpp file +//----------------------------------------------------------------------------- + +#include "objrefdlg.h" + +//----------------------------------------------------------------------------- +// Remaining headers: Needed wx headers, then wx/contrib headers, then application headers +//----------------------------------------------------------------------------- + +#include "wx/xrc/xmlres.h" // XRC XML resouces + + + +//----------------------------------------------------------------------------- +// Public members +//----------------------------------------------------------------------------- +ObjrefDialog::ObjrefDialog(wxWindow* parent) +{ + wxXmlResource::Get()->LoadDialog(this, parent, wxT("objref_dialog")); + + nb = XRCCTRL(*this, "objref_notebook", wxNotebook); + wxCHECK_RET(nb, "failed to find objref_notebook"); + nb->Bind(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, &ObjrefDialog::OnNotebookPageChanged, this); + iconspage_bound = false; + calcpage_bound = false; +} + +ObjrefDialog::~ObjrefDialog() +{ + // Select page 0. Otherwise if the Calc page were selected, when it's removed the Icons page is selected + // and sets the log target again in idle time, *after* myframe restores the old one! + nb->ChangeSelection(0); +} + +//----------------------------------------------------------------------------- +// Private members (including the event handlers) +//----------------------------------------------------------------------------- +void ObjrefDialog::OnNotebookPageChanged( wxNotebookEvent &event ) +{ + switch(event.GetSelection()) + { + case copy_page: + { + // This is a straight object reference to the first page + // so change the text programmatically + nb->SetPageText(copy_page, "Page 1 copy"); + + wxNotebookPage *page = nb->GetPage(copy_page); + wxTextCtrl *text = XRCCTRL(*page, "description_text", wxTextCtrl); + text->ChangeValue( + wxString("This is a duplicate of page 1, using an object reference. ") + + wxString("It was created by this very simple xml:\n\n") + + wxString("\n\t\n") + + wxString("\t\n") + + wxString("\n\n(Then I'm cheating by inserting this text programmatically.)") + ); + break; + } + + case icons_page: + { + wxNotebookPage *page = nb->GetPage(icons_page); + if (!iconspage_bound) + { + iconspage_bound = true; + // We want to direct UpdateUI events for the ID range 'first_row' to OnUpdateUIFirst(). + // We could achieve this using first_row[0] and first_row[2], but what if a fourth + // column were added? It's safer to use the 'typedefs' for the two ends of the range: + page->Bind(wxEVT_UPDATE_UI, &ObjrefDialog::OnUpdateUIFirst, + this, XRCID("first_row[start]"), XRCID("first_row[end]")); + // Similarly for the other two rows + page->Bind(wxEVT_UPDATE_UI, &ObjrefDialog::OnUpdateUISecond, + this, XRCID("second_row[start]"), XRCID("second_row[end]")); + page->Bind(wxEVT_UPDATE_UI, &ObjrefDialog::OnUpdateUIThird, + this, XRCID("third_row[start]"), XRCID("third_row[end]")); + + } + + text = XRCCTRL(*page, "log_text", wxTextCtrl); + if (text) + delete wxLog::SetActiveTarget(new wxLogTextCtrl(text)); + break; + } + + case calc_page: + { + wxNotebookPage *page = nb->GetPage(calc_page); + if (!calcpage_bound) + { + calcpage_bound = true; + // Bind the id ranges, using the [start] and [end] 'typedefs' + page->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &ObjrefDialog::OnNumeralClick, + this, XRCID("digits[start]"), XRCID("digits[end]")); + page->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &ObjrefDialog::OnOperatorClick, + this, XRCID("operators[start]"), XRCID("operators[end]")); + } + + result_txt = XRCCTRL(*page, "result", wxTextCtrl); + text = XRCCTRL(*page, "log_text", wxTextCtrl); + if (text) + delete wxLog::SetActiveTarget(new wxLogTextCtrl(text)); + + ClearCalculator(); + break; + } + + default: return; + } +} + +// There are undoubtedly simpler ways of doing all this, but we're demonstrating the use of ID ranges +void ObjrefDialog::OnUpdateUIFirst(wxUpdateUIEvent& event) +{ + // The checkbox with the XRCID 'check[0]' controls this row of icons + wxCheckBox *chk = XRCCTRL(*(nb->GetPage(icons_page)), "check[0]", wxCheckBox); + if (chk) + event.Enable(chk->IsChecked()); + + // Let's create a log-window entry + static bool checked = true; + if (chk->IsChecked() != checked) + { + checked = chk->IsChecked(); + wxLogMessage("Row one has been %s by check[0], XRCID = %i", checked ? "enabled" : "disabled", XRCID("check[0]")); + wxLogMessage("XRCIDs: first_row[start] = %i, first_row[0] = %i, first_row[1] = %i, first_row[2] = %i, first_row[end] = %i", + XRCID("first_row[start]"), XRCID("first_row[0]"), XRCID("first_row[1]"), XRCID("first_row[2]"), XRCID("first_row[end]")); + } +} + +void ObjrefDialog::OnUpdateUISecond(wxUpdateUIEvent& event) +{ + // The checkbox with the XRCID 'check[1]' controls this row of icons + wxCheckBox *chk = XRCCTRL(*(nb->GetPage(icons_page)), "check[1]", wxCheckBox); + if (chk) + event.Enable(chk->IsChecked()); + + // Let's create a log-window entry + static bool checked = true; + if (chk->IsChecked() != checked) + { + checked = chk->IsChecked(); + wxLogMessage("Row two has been %s by check[1], XRCID = %i", checked ? "enabled" : "disabled", XRCID("check[1]")); + wxLogMessage("XRCIDs: second_row[start] = %i, second_row[0] = %i, second_row[1] = %i, second_row[2] = %i, second_row[end] = %i", + XRCID("second_row[start]"), XRCID("second_row[0]"), XRCID("second_row[1]"), XRCID("second_row[2]"), XRCID("second_row[end]")); + } +} + +void ObjrefDialog::OnUpdateUIThird(wxUpdateUIEvent& event) +{ + // The checkbox with the XRCID 'check[2]' controls this row of icons + wxCheckBox *chk = XRCCTRL(*(nb->GetPage(icons_page)), "check[2]", wxCheckBox); + if (chk) + event.Enable(chk->IsChecked()); + + // Let's create a log-window entry + static bool checked = true; + if (chk->IsChecked() != checked) + { + checked = chk->IsChecked(); + wxLogMessage("Row three has been %s by check[2], XRCID = %i", checked ? "enabled" : "disabled", XRCID("check[2]")); + wxLogMessage("XRCIDs: third_row[start] = %i, third_row[0] = %i, third_row[1] = %i, third_row[2] = %i, third_row[end] = %i", + XRCID("third_row[start]"), XRCID("third_row[0]"), XRCID("third_row[1]"), XRCID("third_row[2]"), XRCID("third_row[end]")); + } +} + +void ObjrefDialog::OnNumeralClick(wxCommandEvent& event) +{ + // See how the id range simplifies determining which numeral was clicked + int digit = event.GetId() - XRCID("digits[start]"); + + char c = '0' + digit; + if (current==0 && previous==0) + { + // We're just starting a calculation, so get rid of the placeholder '0' + result_txt->Clear(); + } + else if (operator_expected == true) + { + // If we've just finished one calculation, and now a digit is entered, clear + ClearCalculator(); + result_txt->Clear(); + } + (*result_txt) << c; + + + current = current*10 + digit; + + wxLogMessage("You clicked digits[%c], XRCID %i", c, event.GetId()); +} + +void ObjrefDialog::OnOperatorClick(wxCommandEvent& event) +{ + static const char symbols[] = "+-*/="; + + operator_expected = false; + int ID = event.GetId() - XRCID("operators[start]"); + + // We carefully used "operators[end]" as the name of the Clear button + if (event.GetId() == XRCID("operators[end]")) + { + wxLogMessage("You clicked operators[%i], XRCID %i, 'Clear'", ID, event.GetId()); + return ClearCalculator(); + } + + switch(ID) + { + case operator_plus: + case operator_minus: + case operator_multiply: + case operator_divide: + if (current!=0 || previous!=0) + { + // We're in the middle of a complex calculation, so do the first bit + Calculate(); + } + curr_operator = (CalcOperator)ID; + break; + + case operator_equals: + Calculate(); + wxLogMessage("You clicked operators[%i], XRCID %i, giving a '%c'", ID, event.GetId(), symbols[ID]); + curr_operator = operator_equals; + // Flag that the next entry should be an operator, not a digit + operator_expected = true; + return; + } + + (*result_txt) << ' ' << symbols[ID] << ' '; + + wxLogMessage("You clicked operators[%i], XRCID %i, giving a '%c'", ID, event.GetId(), symbols[ID]); +} + +void ObjrefDialog::Calculate() +{ + switch(curr_operator) + { + case operator_plus: + previous += current; break; + case operator_minus: + previous -= current; break; + case operator_multiply: + previous *= current; break; + case operator_divide: + if (current!=0) + previous /= current; + break; + default: return; + } + + curr_operator = operator_plus; + current = 0; + result_txt->Clear(); + + (*result_txt) << previous; +} + +void ObjrefDialog::ClearCalculator() +{ + current = previous = 0; + curr_operator = operator_plus; + operator_expected = false; + result_txt->ChangeValue("0"); +} diff --git a/samples/xrc/objrefdlg.h b/samples/xrc/objrefdlg.h new file mode 100644 index 0000000000..da97d664f9 --- /dev/null +++ b/samples/xrc/objrefdlg.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Name: objref.h +// Purpose: XML resources sample: Object references and ID ranges dialog +// Author: David Hart, Vaclav Slavik +// RCS-ID: $Id$ +// Copyright: (c) Vaclav Slavik +// Licence: wxWindows licence +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Begin single inclusion of this .h file condition +//----------------------------------------------------------------------------- + +#ifndef _OBJREFDLG_H_ +#define _OBJREFDLG_H_ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- + +#include "wx/dialog.h" +#include "wx/notebook.h" + +//----------------------------------------------------------------------------- +// Class definition: ObjrefDialog +//----------------------------------------------------------------------------- + +class ObjrefDialog : public wxDialog +{ + +public: + + // Constructor. + ObjrefDialog( wxWindow* parent ); + + // Destructor. + ~ObjrefDialog(); + +private: + enum PageNumbers { first_page, copy_page, icons_page, calc_page }; + enum CalcOperator { operator_plus, operator_minus, operator_multiply, operator_divide, operator_equals }; + + void OnNotebookPageChanged( wxNotebookEvent &event ); + void OnUpdateUIFirst(wxUpdateUIEvent& event); + void OnUpdateUISecond(wxUpdateUIEvent& event); + void OnUpdateUIThird(wxUpdateUIEvent& event); + void OnNumeralClick(wxCommandEvent& event); + void OnOperatorClick(wxCommandEvent& event); + void Calculate(); + void ClearCalculator(); + + wxNotebook *nb; + wxTextCtrl *text; + wxTextCtrl *result_txt; + bool iconspage_bound; + bool calcpage_bound; + int current; + int previous; + bool operator_expected; + CalcOperator curr_operator; + +}; + +#endif //_OBJREFDLG_H_ diff --git a/samples/xrc/rc/menu.xrc b/samples/xrc/rc/menu.xrc index d60980dcf5..309aaab9ab 100644 --- a/samples/xrc/rc/menu.xrc +++ b/samples/xrc/rc/menu.xrc @@ -49,6 +49,11 @@ Advanced techniques with XRC + + + objrefdlg.xpm + Use of object references and event ranges + custclas.xpm diff --git a/samples/xrc/rc/objref.xrc b/samples/xrc/rc/objref.xrc new file mode 100644 index 0000000000..03876e6a98 --- /dev/null +++ b/samples/xrc/rc/objref.xrc @@ -0,0 +1,418 @@ + + + + + + + + + + + + This dialog demonstrates the use of object references and ID arrays.\n\nAs you turn the pages of the notebook, you will notice that each has an identical section at the bottom. Instead of writing that section's xml several times, this is done just once, as a wxPanel named 'bottom__panel'. The panel is then added to each page's sizer by the single line: <object__ref ref="bottom__panel"/> + + + + wxALL|wxEXPAND + 5 + + + + wxEXPAND + + 1 + 0 + 0 + 10 + 0 + 0 + 1 + + + + + + + + + + + + + + ID ranges are a way to simplify the management of several similar controls, especially their event-handling.\nAn ID range is declared by putting something like this into the XRC file:\n <ids-range name="check" size="3" start="10000" />\n'size' and 'start' being optional.\n\nIf you then give an item the name 'check[2]', it will be allocated that ID in the range.\n\nBy default the IDs in a range are negative, being assigned by wxWindow::NewControlId. If you wish, you can specify the start of the range; if so, the IDs *must* be positive (and it's your responsibility to avoid clashes, so start above wxID__HIGHEST). + + + + wxALL|wxEXPAND + 5 + + + + + + wxLEFT + 100 + + + + + + + + + + + wxALIGN_CENTRE + + wxVERTICAL + + wxALIGN_CENTRE_VERTICAL + + wxHORIZONTAL + + 50,-1 + + + + + + + + update.gif + + + + + basicdlg.xpm + + + + + controls.xpm + + + + + custclas.xpm + + + + + derivdlg.xpm + + + + + platform.xpm + + + + + objrefdlg.xpm + + + + + uncenter.xpm + + + + + variable.xpm + + + 3 + 7 + 7 + + wxALL + 3 + + + wxVERTICAL + + wxALIGN_CENTRE_VERTICAL + + + + wxLEFT + 100 + + + wxTOP|wxBOTTOM|wxEXPAND + 15 + + + + + wxEXPAND + + 1 + 0 + 0 + 10 + 0 + 0 + 2 + + + + + + + + + + This not very useful calculator demonstrates some features of ID ranges. One range is defined for the number keys, and another for the operators.\n\nIf you look at the .xrc file, you'll see that I wrote:\n <ids-range name="digits" size="8"/>\ncreating that range with a size of 8 (I must have forgotten to count my thumbs). The code will still work, though: the actual number of range items is counted when the file is loaded, and the range extended if necessary to accommodate them. However if the size is too big, it isn't truncated.\n\nAn ID range always has the special items defined: <rangename>[start] and <rangename>[end]. So, for the range 'digits', digits[start] == digits[0], and digits[end] == digits[9]. [end] will always refer to the end of the range, even if there's no object with that ID.\n\nThe digits of the calculator are named 'digits[0]' to 'digits[9]'. Adjacent range items are guaranteed to be assigned consecutive IDs, so code such as this will work as expected:\n for (int n=XRCID("digits[start]"); n < XRCID("digits[end]"); ++n) { DoFoo(n); }\nor\n int index = event.GetId() - XRCID("digits[0]"); + + + + wxALL|wxEXPAND + 5 + + + + + + + + + + + wxEXPAND + 0,0 + + + + + + + wxEXPAND + 0,1 + + + + + + + wxEXPAND + 0,2 + + + + + + + wxEXPAND + 0,3 + + + + + + + wxEXPAND + 1,0 + + + + + + + wxEXPAND + 1,1 + + + + + + + wxEXPAND + 1,2 + + + + + + + wxEXPAND + 1,3 + + + + + + + wxEXPAND + 2,0 + + + + + + + wxEXPAND + 2,1 + + + + + + + wxEXPAND + 2,2 + + + + + + + wxEXPAND + 2,3 + + + + + + + wxEXPAND + 3,0 + + + + + + + wxEXPAND + 3,1 + + + + + + + wxEXPAND + 3,2 + + + + + + + wxEXPAND + 3,3 + + + + wxEXPAND + 4,0 + 1,4 + + 5 + 5 + + wxALL + 5 + + + wxVERTICAL + + wxTOP|wxALIGN_CENTRE_HORIZONTAL + 10 + + + + + wxEXPAND + + 1 + 0 + 0 + 10 + 0 + 0 + 2 + + + + + + + wxGROW|wxALIGN_CENTER_VERTICAL|wxALL + 5 + + + + + 1 + + wxTOP|wxBOTTOM|wxALIGN_CENTRE + 20 + + 1 + 0 + 0 + 0 + 0 + 0 + + Object References and ID Ranges Example + 1 + + wxDIALOG_EX_CONTEXTHELP + + + + wxVERTICAL + + + + + wxTOP|wxEXPAND + 20 + + + + + + wxTOP + 5 + + + + -1,100 + + + + wxEXPAND + + + + + + wxVERTICAL + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + diff --git a/samples/xrc/rc/objrefdlg.xpm b/samples/xrc/rc/objrefdlg.xpm new file mode 100644 index 0000000000..2cd9d98862 --- /dev/null +++ b/samples/xrc/rc/objrefdlg.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char *const objrefdlg_xpm[] = { +"16 16 7 1", +" c None", +". c #EBF70E", +"+ c #000000", +"@ c #808080", +"# c #0000FF", +"$ c #FFFFFF", +"% c #F8A836", +" . ", +"+++.++++++++++++", +"+.@.@.@@@@@@.@@+", +"+@...#######.#@+", +"...@....@@.@.@.+", +"+@...$$$$$$...@+", +"+.$.@.@@@%%%$%..", +"+@$.@@@@@@@%%%@+", +"+@$.$$$$$$%$%$%+", +"+@$$$$$$$$$$%$@+", +"+@$$$$@@@@$$$$@+", +"+@$$$$@@@@$$$$@+", +"+@$$$$$$$$$$$$@+", +"+@@@@@@@@@@@@@@+", +"++++++++++++++++", +" "}; diff --git a/samples/xrc/rc/toolbar.xrc b/samples/xrc/rc/toolbar.xrc index 00d74ceb5c..48e08f2b2d 100644 --- a/samples/xrc/rc/toolbar.xrc +++ b/samples/xrc/rc/toolbar.xrc @@ -26,6 +26,11 @@ Disable autocentering of a dialog on its parent + + objrefdlg.xpm + Object references and event ranges example + Use of object references and event ranges + Custom Class Example custclas.xpm diff --git a/samples/xrc/xrcdemo.bkl b/samples/xrc/xrcdemo.bkl index c4d734d65e..0d949591dc 100644 --- a/samples/xrc/xrcdemo.bkl +++ b/samples/xrc/xrcdemo.bkl @@ -9,12 +9,14 @@ myframe.cpp derivdlg.cpp custclas.cpp + objrefdlg.cpp derivdlg.h xrcdemo.h myframe.h custclas.h + objrefdlg.h xrc html @@ -33,6 +35,7 @@ derivdlg.xpm derivdlg.xrc fileopen.gif filesave.gif frame.xrc fuzzy.gif menu.xrc platform.xpm platform.xrc quotes.gif resource.xrc toolbar.xrc uncenter.xpm + objref.xrc objrefdlg.xpm uncenter.xrc update.gif variable.xpm variable.xrc throbber.gif stop.xpm diff --git a/samples/xrc/xrcdemo.cpp b/samples/xrc/xrcdemo.cpp index e82ea49d99..9a770d9804 100644 --- a/samples/xrc/xrcdemo.cpp +++ b/samples/xrc/xrcdemo.cpp @@ -64,10 +64,11 @@ bool MyApp::OnInit() return false; // If there is any of a certain format of image in the xrcs, then first - // load a handler for that image type. This example uses XPMs, but if - // you want PNGs, then add a PNG handler, etc. See wxImage::AddHandler() + // load a handler for that image type. This example uses XPMs & a gif, but + // if you want PNGs, then add a PNG handler, etc. See wxImage::AddHandler() // documentation for the types of image handlers available. wxImage::AddHandler(new wxXPMHandler); + wxImage::AddHandler(new wxGIFHandler); // Initialize all the XRC handlers. Always required (unless you feel like // going through and initializing a handler of each control type you will diff --git a/samples/xrc/xrcdemo.dsp b/samples/xrc/xrcdemo.dsp index f0bdd10e94..036f7f0e58 100644 --- a/samples/xrc/xrcdemo.dsp +++ b/samples/xrc/xrcdemo.dsp @@ -264,10 +264,14 @@ SOURCE=.\myframe.cpp # End Source File # Begin Source File -SOURCE=.\..\..\samples\sample.rc +SOURCE=.\objrefdlg.cpp # End Source File # Begin Source File +SOURCE=.\..\..\samples\sample.rc +# End Source File +# Begin Source File + SOURCE=.\xrcdemo.cpp # End Source File # End Group @@ -288,6 +292,10 @@ SOURCE=.\myframe.h # End Source File # Begin Source File +SOURCE=.\objrefdlg.h +# End Source File +# Begin Source File + SOURCE=.\xrcdemo.h # End Source File # End Group diff --git a/samples/xrc/xrcdemo.h b/samples/xrc/xrcdemo.h index a66bd240cd..656ff3a431 100644 --- a/samples/xrc/xrcdemo.h +++ b/samples/xrc/xrcdemo.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Name: xmldemo.cpp +// Name: xmldemo.h // Purpose: XML resources sample: Main application file // Author: Robert O'Connor (rob@medicalmnemonics.com), Vaclav Slavik // RCS-ID: $Id$ diff --git a/samples/xrc/xrcdemo_vc7.vcproj b/samples/xrc/xrcdemo_vc7.vcproj index 530a7de652..ce0590b724 100644 --- a/samples/xrc/xrcdemo_vc7.vcproj +++ b/samples/xrc/xrcdemo_vc7.vcproj @@ -551,6 +551,9 @@ RelativePath=".\myframe.cpp"> + + @@ -568,6 +571,9 @@ RelativePath=".\myframe.h"> + + diff --git a/samples/xrc/xrcdemo_vc8.vcproj b/samples/xrc/xrcdemo_vc8.vcproj index 3019d06193..be5446056a 100644 --- a/samples/xrc/xrcdemo_vc8.vcproj +++ b/samples/xrc/xrcdemo_vc8.vcproj @@ -816,6 +816,10 @@ > + + @@ -838,6 +842,10 @@ > + + diff --git a/samples/xrc/xrcdemo_vc9.vcproj b/samples/xrc/xrcdemo_vc9.vcproj index 418d5915ef..986cda6be1 100644 --- a/samples/xrc/xrcdemo_vc9.vcproj +++ b/samples/xrc/xrcdemo_vc9.vcproj @@ -788,6 +788,10 @@ > + + @@ -810,6 +814,10 @@ > + + diff --git a/src/xrc/xmlres.cpp b/src/xrc/xmlres.cpp index cc3649c9c3..10ff03fc39 100644 --- a/src/xrc/xmlres.cpp +++ b/src/xrc/xmlres.cpp @@ -47,6 +47,7 @@ #include "wx/imaglist.h" #include "wx/dir.h" #include "wx/xml/xml.h" +#include "wx/hashset.h" class wxXmlResourceDataRecord @@ -71,6 +72,69 @@ class wxXmlResourceDataRecords : public wxVector // this is a class so that it can be forward-declared }; +WX_DECLARE_HASH_SET(int, wxIntegerHash, wxIntegerEqual, wxHashSetInt); + +class wxIdRange // Holds data for a particular rangename +{ +protected: + wxIdRange(const wxXmlNode* node, + const wxString& rname, + const wxString& startno, + const wxString& rsize); + + // Note the existence of an item within the range + void NoteItem(const wxXmlNode* node, const wxString& item); + + // The manager is telling us that it's finished adding items + void Finalise(const wxXmlNode* node); + + wxString GetName() const { return m_name; } + bool IsFinalised() const { return m_finalised; } + + const wxString m_name; + int m_start; + int m_end; + unsigned int m_size; + bool m_item_end_found; + bool m_finalised; + wxHashSetInt m_indices; + + friend class wxIdRangeManager; +}; + +class wxIdRangeManager +{ +public: + ~wxIdRangeManager(); + // Gets the global resources object or creates one if none exists. + static wxIdRangeManager *Get(); + + // Sets the global resources object and returns a pointer to the previous + // one (may be NULL). + static wxIdRangeManager *Set(wxIdRangeManager *res); + + // Create a new IDrange from this node + void AddRange(const wxXmlNode* node); + // Tell the IdRange that this item exists, and should be pre-allocated an ID + void NotifyRangeOfItem(const wxXmlNode* node, const wxString& item) const; + // Tells all IDranges that they're now complete, and can create their IDs + void FinaliseRanges(const wxXmlNode* node) const; + // Searches for a known IdRange matching 'name', returning its index or -1 + int Find(const wxString& rangename) const; + // Removes, if it exists, an entry from the XRCID table. Used in id-ranges + // to replace defunct or statically-initialised entries with current values + static void RemoveXRCIDEntry(const char *str_id); + +protected: + wxIdRange* FindRangeForItem(const wxXmlNode* node, + const wxString& item, + wxString& value) const; + wxVector m_IdRanges; + +private: + static wxIdRangeManager *ms_instance; +}; + namespace { @@ -520,7 +584,31 @@ static void ProcessPlatformProperty(wxXmlNode *node) } } +static void PreprocessForIdRanges(wxXmlNode *rootnode) +{ + // First go through the top level, looking for the names of ID ranges + // as processing items is a lot easier if names are already known + wxXmlNode *c = rootnode->GetChildren(); + while (c) + { + if (c->GetName() == wxT("ids-range")) + wxIdRangeManager::Get()->AddRange(c); + c = c->GetNext(); + } + + // Next, examine every 'name' for the '[' that denotes an ID in a range + c = rootnode->GetChildren(); + while (c) + { + wxString name = c->GetAttribute(wxT("name")); + if (name.find('[') != wxString::npos) + wxIdRangeManager::Get()->NotifyRangeOfItem(rootnode, name); + // Do any children by recursion, then proceed to the next sibling + PreprocessForIdRanges(c); + c = c->GetNext(); + } +} bool wxXmlResource::UpdateResources() { @@ -631,6 +719,8 @@ bool wxXmlResource::UpdateResources() } ProcessPlatformProperty(rec->Doc->GetRoot()); + PreprocessForIdRanges(rec->Doc->GetRoot()); + wxIdRangeManager::Get()->FinaliseRanges(rec->Doc->GetRoot()); #if wxUSE_DATETIME #if wxUSE_FILESYSTEM rec->Time = file->GetModificationTime(); @@ -744,7 +834,7 @@ wxXmlResource::GetResourceNodeAndLocation(const wxString& name, bool recursive, wxString *path) const { - // ensure everything is up-to-date: this is needed to support on-remand + // ensure everything is up-to-date: this is needed to support on-demand // reloading of XRC files const_cast(this)->UpdateResources(); @@ -916,6 +1006,329 @@ wxXmlResource::DoCreateResFromNode(wxXmlNode& node, return NULL; } +wxIdRange::wxIdRange(const wxXmlNode* node, + const wxString& rname, + const wxString& startno, + const wxString& rsize) + : m_name(rname), + m_start(0), + m_size(0), + m_item_end_found(0), + m_finalised(0) +{ + long l; + if ( startno.ToLong(&l) ) + { + if ( l >= 0 ) + { + m_start = l; + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "a negative id-range start parameter was given" + ); + } + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "the id-range start parameter was malformed" + ); + } + + unsigned long ul; + if ( rsize.ToULong(&ul) ) + { + m_size = ul; + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "the id-range size parameter was malformed" + ); + } +} + +void wxIdRange::NoteItem(const wxXmlNode* node, const wxString& item) +{ + // Nothing gets added here, but the existence of each item is noted + // thus getting an accurate count. 'item' will be either an integer e.g. + // [0] [123]: will eventually create an XRCID as start+integer or [start] + // or [end] which are synonyms for [0] or [range_size-1] respectively. + wxString content(item.Mid(1, item.length()-2)); + + // Check that basename+item wasn't foo[] + if (content.empty()) + { + wxXmlResource::Get()->ReportError(node, "an empty id-range item found"); + return; + } + + if (content=="start") + { + // "start" means [0], so store that in the set + if (m_indices.count(0) == 0) + { + m_indices.insert(0); + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "duplicate id-range item found" + ); + } + } + else if (content=="end") + { + // We can't yet be certain which XRCID this will be equivalent to, so + // just note that there's an item with this name, in case we need to + // inc the range size + m_item_end_found = true; + } + else + { + // Anything else will be an integer, or rubbish + unsigned long l; + if ( content.ToULong(&l) ) + { + if (m_indices.count(l) == 0) + { + m_indices.insert(l); + // Check that this item wouldn't fall outside the current range + // extent + if (l >= m_size) + { + m_size = l + 1; + } + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "duplicate id-range item found" + ); + } + + } + else + { + wxXmlResource::Get()->ReportError + ( + node, + "an id-range item had a malformed index" + ); + } + } +} + +void wxIdRange::Finalise(const wxXmlNode* node) +{ + wxCHECK_RET( !IsFinalised(), + "Trying to finalise an already-finalised range" ); + + // Now we know about all the items, we can get an accurate range size + // Expand any requested range-size if there were more items than would fit + m_size = wxMax(m_size, m_indices.size()); + + // If an item is explicitly called foo[end], ensure it won't clash with + // another item + if ( m_item_end_found && m_indices.count(m_size-1) ) + ++m_size; + if (m_size == 0) + { + // This will happen if someone creates a range but no items in this xrc + // file Report the error and abort, but don't finalise, in case items + // appear later + wxXmlResource::Get()->ReportError + ( + node, + "trying to create an empty id-range" + ); + return; + } + + if (m_start==0) + { + // This is the usual case, where the user didn't specify a start ID + // So get the range using NewControlId(). + // + // NB: negative numbers, but NewControlId already returns the most + // negative + m_start = wxWindow::NewControlId(m_size); + wxCHECK_RET( m_start != wxID_NONE, + "insufficient IDs available to create range" ); + m_end = m_start + m_size - 1; + } + else + { + // The user already specified a start value, which must be positive + m_end = m_start + m_size - 1; + } + + // Create the XRCIDs + for (int i=m_start; i <= m_end; ++i) + { + // First clear any pre-existing XRCID + // Necessary for wxXmlResource::Unload() followed by Load() + wxIdRangeManager::RemoveXRCIDEntry( + m_name + wxString::Format("[%i]", i-m_start)); + + // Use the second parameter of GetXRCID to force it to take the value i + wxXmlResource::GetXRCID(m_name + wxString::Format("[%i]", i-m_start), i); + wxLogTrace("xrcrange","integer = %i %s now returns %i", i, + m_name + wxString::Format("[%i]", i-m_start).mb_str(), + XRCID(m_name + wxString::Format("[%i]", i-m_start).mb_str())); + } + // and these special ones + wxIdRangeManager::RemoveXRCIDEntry(m_name + "[start]"); + wxXmlResource::GetXRCID(m_name + "[start]", m_start); + wxIdRangeManager::RemoveXRCIDEntry(m_name + "[end]"); + wxXmlResource::GetXRCID(m_name + "[end]", m_end); + wxLogTrace("xrcrange","%s[start] = %i %s[end] = %i", + m_name.mb_str(),XRCID(wxString(m_name+"[start]").mb_str()), + m_name.mb_str(),XRCID(wxString(m_name+"[end]").mb_str())); + + m_finalised = true; +} + +wxIdRangeManager *wxIdRangeManager::ms_instance = NULL; + +/*static*/ wxIdRangeManager *wxIdRangeManager::Get() +{ + if ( !ms_instance ) + ms_instance = new wxIdRangeManager; + return ms_instance; +} + +/*static*/ wxIdRangeManager *wxIdRangeManager::Set(wxIdRangeManager *res) +{ + wxIdRangeManager *old = ms_instance; + ms_instance = res; + return old; +} + +wxIdRangeManager::~wxIdRangeManager() +{ + for ( wxVector::iterator i = m_IdRanges.begin(); + i != m_IdRanges.end(); ++i ) + { + delete *i; + } + m_IdRanges.clear(); + + delete ms_instance; +} + +void wxIdRangeManager::AddRange(const wxXmlNode* node) +{ + wxString name = node->GetAttribute("name"); + wxString start = node->GetAttribute("start", "0"); + wxString size = node->GetAttribute("size", "0"); + if (name.empty()) + { + wxXmlResource::Get()->ReportError + ( + node, + "xrc file contains an id-range without a name" + ); + return; + } + + int index = Find(name); + if (index == wxNOT_FOUND) + { + wxLogTrace("xrcrange", + "Adding ID range, name=%s start=%s size=%s", + name, start, size); + + m_IdRanges.push_back(new wxIdRange(node, name, start, size)); + } + else + { + // There was already a range with this name. Let's hope this is + // from an Unload()/(re)Load(), not an unintentional duplication + wxLogTrace("xrcrange", + "Replacing ID range, name=%s start=%s size=%s", + name, start, size); + + wxIdRange* oldrange = m_IdRanges.at(index); + m_IdRanges.at(index) = new wxIdRange(node, name, start, size); + delete oldrange; + } +} + +wxIdRange * +wxIdRangeManager::FindRangeForItem(const wxXmlNode* node, + const wxString& item, + wxString& value) const +{ + wxString basename = item.BeforeFirst('['); + wxCHECK_MSG( !basename.empty(), NULL, + "an id-range item without a range name" ); + + int index = Find(basename); + if (index == wxNOT_FOUND) + { + // Don't assert just because we've found an unexpected foo[123] + // Someone might just want such a name, nothing to do with ranges + return NULL; + } + + value = item.Mid(basename.Len()); + if (value.at(value.length()-1)==']') + { + return m_IdRanges.at(index); + } + wxXmlResource::Get()->ReportError(node, "a malformed id-range item"); + return NULL; +} + +void +wxIdRangeManager::NotifyRangeOfItem(const wxXmlNode* node, + const wxString& item) const +{ + wxString value; + wxIdRange* range = FindRangeForItem(node, item, value); + if (range) + range->NoteItem(node, value); +} + +int wxIdRangeManager::Find(const wxString& rangename) const +{ + for ( int i=0; i < (int)m_IdRanges.size(); ++i ) + { + if (m_IdRanges.at(i)->GetName() == rangename) + return i; + } + + return wxNOT_FOUND; +} + +void wxIdRangeManager::FinaliseRanges(const wxXmlNode* node) const +{ + for ( wxVector::const_iterator i = m_IdRanges.begin(); + i != m_IdRanges.end(); ++i ) + { + // Check if this range has already been finalised. Quite possible, + // as FinaliseRanges() gets called for each .xrc file loaded + if (!(*i)->IsFinalised()) + { + wxLogTrace("xrcrange", "Finalising ID range %s", (*i)->GetName()); + (*i)->Finalise(node); + } + } +} + class wxXmlSubclassFactories : public wxVector { @@ -2203,6 +2616,33 @@ wxString wxXmlResource::FindXRCIDById(int numId) return wxString(); } +/* static */ +void wxIdRangeManager::RemoveXRCIDEntry(const char *str_id) +{ + int index = 0; + + for (const char *c = str_id; *c != '\0'; c++) index += (int)*c; + index %= XRCID_TABLE_SIZE; + + XRCID_record **p_previousrec = &XRCID_Records[index]; + for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next) + { + if (wxStrcmp(rec->key, str_id) == 0) + { + // Found the item to be removed so delete its record; but first + // replace it in the table with any rec->next (usually == NULL) + (*p_previousrec) = rec->next; + free(rec->key); + delete rec; + return; + } + else + { + (*p_previousrec) = rec; + } + } +} + static void CleanXRCID_Record(XRCID_record *rec) { if (rec) @@ -2254,6 +2694,7 @@ public: void OnExit() { delete wxXmlResource::Set(NULL); + delete wxIdRangeManager::Set(NULL); if(wxXmlResource::ms_subclassFactories) { for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin(); diff --git a/tests/Makefile.in b/tests/Makefile.in index 47264145ba..cadec5a327 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -207,7 +207,8 @@ TEST_GUI_OBJECTS = \ test_gui_socket.o \ test_gui_boxsizer.o \ test_gui_clientsize.o \ - test_gui_setsize.o + test_gui_setsize.o \ + test_gui_xrctest.o TEST_GUI_ODEP = $(_____pch_testprec_test_gui_testprec_h_gch___depname) ### Conditionally set variables: ### @@ -843,6 +844,9 @@ test_gui_clientsize.o: $(srcdir)/window/clientsize.cpp $(TEST_GUI_ODEP) test_gui_setsize.o: $(srcdir)/window/setsize.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/window/setsize.cpp +test_gui_xrctest.o: $(srcdir)/xml/xrctest.cpp $(TEST_GUI_ODEP) + $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/xml/xrctest.cpp + # notice the ugly hack with using CXXWARNINGS: we can't use CPPFLAGS as # currently the value in the makefile would be ignored if we did, but diff --git a/tests/makefile.bcc b/tests/makefile.bcc index f3637caa94..c093d69878 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -192,7 +192,8 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.obj \ $(OBJS)\test_gui_boxsizer.obj \ $(OBJS)\test_gui_clientsize.obj \ - $(OBJS)\test_gui_setsize.obj + $(OBJS)\test_gui_setsize.obj \ + $(OBJS)\test_gui_xrctest.obj ### Conditionally set variables: ### @@ -889,3 +890,5 @@ $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp $(OBJS)\test_gui_setsize.obj: .\window\setsize.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\window\setsize.cpp +$(OBJS)\test_gui_xrctest.obj: .\xml\xrctest.cpp + $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\xml\xrctest.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index d164e97a38..bb9002c4e1 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -185,7 +185,8 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.o \ $(OBJS)\test_gui_boxsizer.o \ $(OBJS)\test_gui_clientsize.o \ - $(OBJS)\test_gui_setsize.o + $(OBJS)\test_gui_setsize.o \ + $(OBJS)\test_gui_xrctest.o ### Conditionally set variables: ### @@ -870,6 +871,9 @@ $(OBJS)\test_gui_clientsize.o: ./window/clientsize.cpp $(OBJS)\test_gui_setsize.o: ./window/setsize.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_gui_xrctest.o: ./xml/xrctest.cpp + $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< + .PHONY: all clean data fr diff --git a/tests/makefile.vc b/tests/makefile.vc index 6160f751b0..6663aad0a6 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -187,7 +187,8 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.obj \ $(OBJS)\test_gui_boxsizer.obj \ $(OBJS)\test_gui_clientsize.obj \ - $(OBJS)\test_gui_setsize.obj + $(OBJS)\test_gui_setsize.obj \ + $(OBJS)\test_gui_xrctest.obj TEST_GUI_RESOURCES = \ $(OBJS)\test_gui_sample.res @@ -1015,3 +1016,5 @@ $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp $(OBJS)\test_gui_setsize.obj: .\window\setsize.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\window\setsize.cpp +$(OBJS)\test_gui_xrctest.obj: .\xml\xrctest.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\xml\xrctest.cpp diff --git a/tests/makefile.wat b/tests/makefile.wat index 07d754ea86..9b0e3c8984 100644 --- a/tests/makefile.wat +++ b/tests/makefile.wat @@ -427,7 +427,8 @@ TEST_GUI_OBJECTS = & $(OBJS)\test_gui_socket.obj & $(OBJS)\test_gui_boxsizer.obj & $(OBJS)\test_gui_clientsize.obj & - $(OBJS)\test_gui_setsize.obj + $(OBJS)\test_gui_setsize.obj & + $(OBJS)\test_gui_xrctest.obj all : $(OBJS) @@ -928,3 +929,5 @@ $(OBJS)\test_gui_clientsize.obj : .AUTODEPEND .\window\clientsize.cpp $(OBJS)\test_gui_setsize.obj : .AUTODEPEND .\window\setsize.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $< +$(OBJS)\test_gui_xrctest.obj : .AUTODEPEND .\xml\xrctest.cpp + $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $< diff --git a/tests/test.bkl b/tests/test.bkl index 48f26f372a..65dcac6ef3 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -194,6 +194,7 @@ sizers/boxsizer.cpp window/clientsize.cpp window/setsize.cpp + xml/xrctest.cpp richtext media diff --git a/tests/test_test_gui.dsp b/tests/test_test_gui.dsp index c7b1e71390..9e263e5d26 100644 --- a/tests/test_test_gui.dsp +++ b/tests/test_test_gui.dsp @@ -499,6 +499,10 @@ SOURCE=.\controls\treectrltest.cpp SOURCE=.\controls\windowtest.cpp # End Source File +# Begin Source File + +SOURCE=.\xml\xrctest.cpp +# End Source File # End Group # End Target # End Project diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj index 79d8f3292c..6445a4de4b 100644 --- a/tests/test_vc7_test_gui.vcproj +++ b/tests/test_vc7_test_gui.vcproj @@ -808,6 +808,9 @@ + + + + + + + +// ---------------------------------------------------------------------------- +// helpers to create/save some xrc +// ---------------------------------------------------------------------------- + +namespace +{ + +static const char *TEST_XRC_FILE = "test.xrc"; + +// I'm hard-wiring the xrc into this function for now +// If different xrcs are wanted for future tests, it'll be easy to refactor +void CreateXrc() +{ + const char *xrcText = + "" + "" + " " + " " + " wxVERTICAL" + " " + " " + " " + " " + " " + " wxVERTICAL" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " wxVERTICAL" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " wxHORIZONTAL" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " test" + " " + " " + " " + "" + ; + + // afaict there's no elegant way to load xrc direct from a string + // So save it as a file, from which it can be loaded + wxStringInputStream sis(xrcText); + wxFFileOutputStream fos(TEST_XRC_FILE); + CPPUNIT_ASSERT(fos.IsOk()); + fos.Write(sis); + CPPUNIT_ASSERT(fos.Close()); +} + +} // anon namespace + + +// ---------------------------------------------------------------------------- +// test class +// ---------------------------------------------------------------------------- + +class XrcTestCase : public CppUnit::TestCase +{ +public: + XrcTestCase() {} + + virtual void setUp() { CreateXrc(); } + virtual void tearDown() { wxRemoveFile(TEST_XRC_FILE); } + +private: + CPPUNIT_TEST_SUITE( XrcTestCase ); + CPPUNIT_TEST( ObjectReferences ); + CPPUNIT_TEST( IDRanges ); + CPPUNIT_TEST_SUITE_END(); + + void ObjectReferences(); + void IDRanges(); + + DECLARE_NO_COPY_CLASS(XrcTestCase) +}; + +// register in the unnamed registry so that these tests are run by default +CPPUNIT_TEST_SUITE_REGISTRATION( XrcTestCase ); + +// also include in it's own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( XrcTestCase, "XrcTestCase" ); + + + +void XrcTestCase::ObjectReferences() +{ + wxXmlResource::Get()->InitAllHandlers(); + + for ( int n = 0; n < 2; ++n ) + { + // Load the xrc file we're just created + CPPUNIT_ASSERT( wxXmlResource::Get()->Load(TEST_XRC_FILE) ); + + // In xrc there's now a dialog containing two panels, one an object + // reference of the other + wxDialog dlg; + CPPUNIT_ASSERT( wxXmlResource::Get()->LoadDialog(&dlg, NULL, "dialog") ); + // Might as well test XRCCTRL too + wxPanel* panel1 = XRCCTRL(dlg,"panel1",wxPanel); + wxPanel* panel2 = XRCCTRL(dlg,"ref_of_panel1",wxPanel); + // Check that the object reference panel is a different object + CPPUNIT_ASSERT( panel2 != panel1 ); + + // Unload the xrc, so it can be reloaded and the test rerun + CPPUNIT_ASSERT( wxXmlResource::Get()->Unload(TEST_XRC_FILE) ); + } +} + +void XrcTestCase::IDRanges() +{ + // Tests ID ranges + for ( int n = 0; n < 2; ++n ) + { + // Load the xrc file we're just created + CPPUNIT_ASSERT( wxXmlResource::Get()->Load(TEST_XRC_FILE) ); + + // foo[start] should == foo[0] + CPPUNIT_ASSERT_EQUAL( XRCID("SecondCol[start]"), XRCID("SecondCol[0]") ); + // foo[start] should be < foo[end]. Usually that means more negative + CPPUNIT_ASSERT( XRCID("SecondCol[start]") < XRCID("SecondCol[end]") ); + // Check it works for the positive values in FirstCol too + CPPUNIT_ASSERT( XRCID("FirstCol[start]") < XRCID("FirstCol[end]") ); + + // Check that values are adjacent + CPPUNIT_ASSERT_EQUAL( XRCID("SecondCol[0]")+1, XRCID("SecondCol[1]") ); + CPPUNIT_ASSERT_EQUAL( XRCID("SecondCol[1]")+1, XRCID("SecondCol[2]") ); + // And for the positive range + CPPUNIT_ASSERT_EQUAL( XRCID("FirstCol[2]")+1, XRCID("FirstCol[3]") ); + + // Check that a large-enough range was created, despite the small + // 'size' parameter + CPPUNIT_ASSERT_EQUAL + ( + 4, + XRCID("FirstCol[end]") - XRCID("FirstCol[start]") + 1 + ); + + // Check that the far-too-large size range worked off the scale too + CPPUNIT_ASSERT( XRCID("SecondCol[start]") < XRCID("SecondCol[90]") ); + CPPUNIT_ASSERT( XRCID("SecondCol[90]") < XRCID("SecondCol[end]") ); + CPPUNIT_ASSERT_EQUAL( XRCID("SecondCol[90]")+1, XRCID("SecondCol[91]") ); + + // Check that the positive range-start parameter worked, even after a + // reload + CPPUNIT_ASSERT_EQUAL( XRCID("FirstCol[start]"), 10000 ); + + // Unload the xrc, so it can be reloaded and the tests rerun + CPPUNIT_ASSERT( wxXmlResource::Get()->Unload(TEST_XRC_FILE) ); + } +}