]> git.saurik.com Git - wxWidgets.git/commitdiff
Add support for id ranges to XRC.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 7 Nov 2010 14:00:59 +0000 (14:00 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 7 Nov 2010 14:00:59 +0000 (14:00 +0000)
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

37 files changed:
docs/changes.txt
docs/doxygen/overviews/xrc_format.h
include/wx/xrc/xmlres.h
samples/xrc/Makefile.in
samples/xrc/derivdlg.h
samples/xrc/makefile.bcc
samples/xrc/makefile.gcc
samples/xrc/makefile.unx
samples/xrc/makefile.vc
samples/xrc/makefile.wat
samples/xrc/myframe.cpp
samples/xrc/myframe.h
samples/xrc/objrefdlg.cpp [new file with mode: 0644]
samples/xrc/objrefdlg.h [new file with mode: 0644]
samples/xrc/rc/menu.xrc
samples/xrc/rc/objref.xrc [new file with mode: 0644]
samples/xrc/rc/objrefdlg.xpm [new file with mode: 0644]
samples/xrc/rc/toolbar.xrc
samples/xrc/xrcdemo.bkl
samples/xrc/xrcdemo.cpp
samples/xrc/xrcdemo.dsp
samples/xrc/xrcdemo.h
samples/xrc/xrcdemo_vc7.vcproj
samples/xrc/xrcdemo_vc8.vcproj
samples/xrc/xrcdemo_vc9.vcproj
src/xrc/xmlres.cpp
tests/Makefile.in
tests/makefile.bcc
tests/makefile.gcc
tests/makefile.vc
tests/makefile.wat
tests/test.bkl
tests/test_test_gui.dsp
tests/test_vc7_test_gui.vcproj
tests/test_vc8_test_gui.vcproj
tests/test_vc9_test_gui.vcproj
tests/xml/xrctest.cpp [new file with mode: 0644]

index 795d6871ff5bb2031e3053213d850ef57d957ffc..f12a9c8f130ee1706b80cd2321d8a0c39d8533fe 100644 (file)
@@ -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).
index 931aac7c01e0e2fdbddce1be4ec5086555ce33bb..b21050130fbc9425b169109da5236c8d7f8c25db 100644 (file)
@@ -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
+    <object class="wxButton" name="foo[start]">
+    <object class="wxButton" name="foo[end]">
+    <object class="wxButton" name="foo[2]">
+    ...
+    <object class="wxButton" name="bar[0]">
+    <object class="wxButton" name="bar[2]">
+    <object class="wxButton" name="bar[1]">
+    ...
+<ids-range name="foo" />
+<ids-range name="bar" size="30" start="10000" />
+@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
+<em>rangename</em>[0] or <em>rangename</em>[start]. Similarly
+<em>rangename</em>[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 <em>objref</em> 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
index a585bfaa56e40006a222f1798d423b42a5a5a072..1530fab993670cacd44e9e9820017ed94f29ccfc 100644 (file)
@@ -391,6 +391,8 @@ private:
 
     friend class wxXmlResourceHandler;
     friend class wxXmlResourceModule;
+    friend class wxIdRangeManager;
+    friend class wxIdRange;
 
     static wxXmlSubclassFactories *ms_subclassFactories;
 
index 4347831552a175e16201138aa82ba133c18ee878..82ff476729be3fba5b93631729ee5c689f31af04 100644 (file)
@@ -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
index e198fb1d9c481f26c35ca5d31c81ff1693140b9e..904a9dcccd02d4ba4de9d46cff654f4873e5d014 100644 (file)
@@ -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$
index 6139b7a092551ea2f71fe14f3120174b668cc403..6bb55818d55bf1f0f8ec5ca89cfdea5df3e885ca 100644 (file)
@@ -39,7 +39,8 @@ XRCDEMO_OBJECTS =  \
        $(OBJS)\xrcdemo_xrcdemo.obj \\r
        $(OBJS)\xrcdemo_myframe.obj \\r
        $(OBJS)\xrcdemo_derivdlg.obj \\r
-       $(OBJS)\xrcdemo_custclas.obj\r
+       $(OBJS)\xrcdemo_custclas.obj \
+       $(OBJS)\xrcdemo_objrefdlg.obj
 \r
 ### Conditionally set variables: ###\r
 \r
@@ -255,7 +256,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS)  $(OBJS)\xrcdemo_sample.res
 \r
 data: \r
        if not exist $(OBJS)\rc mkdir $(OBJS)\rc\r
-       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\r
        $(CXX) -q -c -P -o$@ $(XRCDEMO_CXXFLAGS) .\custclas.cpp\r
 \r
+$(OBJS)\xrcdemo_objrefdlg.obj: .\objrefdlg.cpp
+       $(CXX) -q -c -P -o$@ $(XRCDEMO_CXXFLAGS) .\objrefdlg.cpp
index d3a21e3299866bc5a21553ad06ddbf2a179374df..fc745f9d01edec1ccbdee10e93dd8b65917f61ee 100644 (file)
@@ -33,7 +33,8 @@ XRCDEMO_OBJECTS =  \
        $(OBJS)\xrcdemo_xrcdemo.o \\r
        $(OBJS)\xrcdemo_myframe.o \\r
        $(OBJS)\xrcdemo_derivdlg.o \\r
-       $(OBJS)\xrcdemo_custclas.o
+       $(OBJS)\xrcdemo_custclas.o \
+       $(OBJS)\xrcdemo_objrefdlg.o
 \r
 ### Conditionally set variables: ###\r
 \r
@@ -242,7 +243,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample_rc.o
 \r
 data: \r
        if not exist $(OBJS)\rc mkdir $(OBJS)\rc\r
-       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\r
        $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $<\r
 \r
+$(OBJS)\xrcdemo_objrefdlg.o: ./objrefdlg.cpp
+       $(CXX) -c -o $@ $(XRCDEMO_CXXFLAGS) $(CPPDEPS) $<
+
 .PHONY: all clean data\r
 \r
 \r
index 4e6522080ef17d6fc74e98a93432ede8e091657c..6e8df683cf15526c8bf3615f5a36ca08c31aeb19 100644 (file)
@@ -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
 
 
index 5aefe6bb9c106d010062a18213bc7fd7fbc333fe..37d8226e182d85a45f01f96821691632d9afa63c 100644 (file)
@@ -33,7 +33,8 @@ XRCDEMO_OBJECTS =  \
        $(OBJS)\xrcdemo_xrcdemo.obj \\r
        $(OBJS)\xrcdemo_myframe.obj \\r
        $(OBJS)\xrcdemo_derivdlg.obj \\r
-       $(OBJS)\xrcdemo_custclas.obj\r
+       $(OBJS)\xrcdemo_custclas.obj \
+       $(OBJS)\xrcdemo_objrefdlg.obj
 XRCDEMO_RESOURCES =  \\r
        $(OBJS)\xrcdemo_sample.res
 \r
@@ -365,7 +366,7 @@ $(OBJS)\xrcdemo.exe: $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample.res
 \r
 data: \r
        if not exist $(OBJS)\rc mkdir $(OBJS)\rc\r
-       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\r
        $(CXX) /c /nologo /TP /Fo$@ $(XRCDEMO_CXXFLAGS) .\custclas.cpp\r
 \r
+$(OBJS)\xrcdemo_objrefdlg.obj: .\objrefdlg.cpp
+       $(CXX) /c /nologo /TP /Fo$@ $(XRCDEMO_CXXFLAGS) .\objrefdlg.cpp
index 9b3f37fe4906054a9766f51dcbaf5395787122cf..26a340e1493fbf6133f867ab799501a4aba278f5 100644 (file)
@@ -252,7 +252,8 @@ XRCDEMO_OBJECTS =  &
        $(OBJS)\xrcdemo_xrcdemo.obj &\r
        $(OBJS)\xrcdemo_myframe.obj &\r
        $(OBJS)\xrcdemo_derivdlg.obj &\r
-       $(OBJS)\xrcdemo_custclas.obj\r
+       $(OBJS)\xrcdemo_custclas.obj &
+       $(OBJS)\xrcdemo_objrefdlg.obj
 \r
 \r
 all : $(OBJS)\r
@@ -285,7 +286,7 @@ $(OBJS)\xrcdemo.exe :  $(XRCDEMO_OBJECTS) $(OBJS)\xrcdemo_sample.res
 \r
 data : .SYMBOLIC \r
        if not exist $(OBJS)\rc mkdir $(OBJS)\rc\r
-       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\r
        $(CXX) -bt=nt -zq -fo=$^@ $(XRCDEMO_CXXFLAGS) $<\r
 \r
+$(OBJS)\xrcdemo_objrefdlg.obj :  .AUTODEPEND .\objrefdlg.cpp
+       $(CXX) -bt=nt -zq -fo=$^@ $(XRCDEMO_CXXFLAGS) $<
index 103d03b851d60843674ce90487ade3ec728ee038..4bc975327f7cfd8a98b3dd98509b5380b5607821 100644 (file)
@@ -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;
index 9701213ac8b998153fb58ac7e52d93a774d36a4b..b57bd7ce4042b4aad072c30141fa2f858ecb323c 100644 (file)
@@ -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 (file)
index 0000000..8fbb22a
--- /dev/null
@@ -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("<object class=\"notebookpage\">\n\t<object_ref ref=\"page1\"/>\n")
+                    + wxString("\t<label>Page 1 copy</label>\n</object>")
+                    + 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 (file)
index 0000000..da97d66
--- /dev/null
@@ -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_
index d60980dcf58adaead63d394c5fe8177030b2640a..309aaab9abfbc74a64e907fad76d4a049d9a2684 100644 (file)
     <object class="wxMenu" name="advanced_demos_menu">
         <label>_Advanced</label>
         <help>Advanced techniques with XRC</help>
+         <object class="wxMenuItem" name="obj_ref_tool_or_menuitem">
+            <label>_Object References Example</label>
+            <bitmap>objrefdlg.xpm</bitmap>
+            <help>Use of object references and event ranges</help>
+        </object>
          <object class="wxMenuItem" name="custom_class_tool_or_menuitem">
             <label>_Custom Class Example</label>
             <bitmap>custclas.xpm</bitmap>
diff --git a/samples/xrc/rc/objref.xrc b/samples/xrc/rc/objref.xrc
new file mode 100644 (file)
index 0000000..03876e6
--- /dev/null
@@ -0,0 +1,418 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<resource version="2.3.0.1" xmlns="http://www.wxwidgets.org/wxxrc">
+  <object class="wxDialog" name="objref_dialog">
+    <object class="wxFlexGridSizer">
+      <object class="sizeritem">
+        <object class="wxNotebook" name="objref_notebook">
+          <object class="notebookpage">
+            <object class="wxPanel" name="page1">
+              <object class="wxFlexGridSizer">
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="description_text">
+                    <value>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:   &lt;object__ref ref=&quot;bottom__panel&quot;/&gt;</value>
+                    <style>wxTE_MULTILINE|wxTE_READONLY</style>
+                  </object>
+                  <option>1</option>
+                  <flag>wxALL|wxEXPAND</flag>
+                  <border>5</border>
+                </object>
+                <object class="sizeritem">
+                  <object_ref ref="bottom_panel"/>
+                  <flag>wxEXPAND</flag>
+                </object>
+                <cols>1</cols>
+                <rows>0</rows>
+                <vgap>0</vgap>
+                <hgap>10</hgap>
+                <growablecols>0</growablecols>
+                <growablerows>0</growablerows>
+                <growablerows>1</growablerows>
+              </object>
+            </object>
+            <label>Page 1</label>
+          </object>
+          <object class="notebookpage">
+            <object_ref ref="page1"/>
+            <label>Page 1 copy</label>
+          </object>
+          <object class="notebookpage">
+            <object class="wxPanel" name="page2">
+              <object class="wxFlexGridSizer">
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="description_text">
+                    <value>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  &lt;ids-range name=&quot;check&quot; size=&quot;3&quot; start=&quot;10000&quot; /&gt;\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).</value>
+                    <style>wxTE_MULTILINE|wxTE_READONLY</style>
+                  </object>
+                  <option>1</option>
+                  <flag>wxALL|wxEXPAND</flag>
+                  <border>5</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxBoxSizer">
+                    <object class="spacer">
+                      <option>1</option>
+                      <flag>wxLEFT</flag>
+                      <border>100</border>
+                    </object>
+                    <object class="sizeritem">
+                      <object class="wxBoxSizer">
+                        <object class="sizeritem">
+                          <object class="wxStaticText">
+                            <label>Uncheck to disable a row</label>
+                          </object>
+                        </object>
+                        <object class="sizeritem">
+                          <object_ref ref="checkboxes"/>
+                          <flag>wxALIGN_CENTRE</flag>
+                        </object>
+                        <orient>wxVERTICAL</orient>
+                      </object>
+                      <flag>wxALIGN_CENTRE_VERTICAL</flag>
+                    </object>
+                    <orient>wxHORIZONTAL</orient>
+                    <object class="spacer">
+                      <size>50,-1</size>
+                    </object>
+                    <object class="sizeritem">
+                      <object class="wxStaticBoxSizer">
+                        <object class="sizeritem">
+                          <object class="wxGridSizer">
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="first_row[0]">
+                                <bitmap>update.gif</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="first_row[1]">
+                                <bitmap>basicdlg.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="first_row[2]">
+                                <bitmap>controls.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="second_row[0]">
+                                <bitmap>custclas.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="second_row[1]">
+                                <bitmap>derivdlg.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="second_row[2]">
+                                <bitmap>platform.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="third_row[0]">
+                                <bitmap>objrefdlg.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="third_row[1]">
+                                <bitmap>uncenter.xpm</bitmap>
+                              </object>
+                            </object>
+                            <object class="sizeritem">
+                              <object class="wxStaticBitmap" name="third_row[2]">
+                                <bitmap>variable.xpm</bitmap>
+                              </object>
+                            </object>
+                            <rows>3</rows>
+                            <vgap>7</vgap>
+                            <hgap>7</hgap>
+                          </object>
+                          <flag>wxALL</flag>
+                          <border>3</border>
+                        </object>
+                        <label>Icons</label>
+                        <orient>wxVERTICAL</orient>
+                      </object>
+                      <flag>wxALIGN_CENTRE_VERTICAL</flag>
+                    </object>
+                    <object class="spacer">
+                      <option>1</option>
+                      <flag>wxLEFT</flag>
+                      <border>100</border>
+                    </object>
+                  </object>
+                  <flag>wxTOP|wxBOTTOM|wxEXPAND</flag>
+                  <border>15</border>
+                </object>
+                <object class="sizeritem">
+                  <object_ref ref="bottom_panel"/>
+                  <option>1</option>
+                  <flag>wxEXPAND</flag>
+                </object>
+                <cols>1</cols>
+                <rows>0</rows>
+                <vgap>0</vgap>
+                <hgap>10</hgap>
+                <growablecols>0</growablecols>
+                <growablerows>0</growablerows>
+                <growablerows>2</growablerows>
+              </object>
+            </object>
+            <label>Icons</label>
+          </object>
+          <object class="notebookpage">
+            <object class="wxPanel" name="page3">
+              <object class="wxFlexGridSizer">
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="description_text">
+                    <value>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    &lt;ids-range name=&quot;digits&quot; size=&quot;8&quot;/&gt;\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: &lt;rangename&gt;[start] and  &lt;rangename&gt;[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(&quot;digits[start]&quot;); n &lt; XRCID(&quot;digits[end]&quot;); ++n) { DoFoo(n); }\nor\n    int index = event.GetId() - XRCID(&quot;digits[0]&quot;);</value>
+                    <style>wxTE_MULTILINE|wxTE_READONLY</style>
+                  </object>
+                  <option>1</option>
+                  <flag>wxALL|wxEXPAND</flag>
+                  <border>5</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticBoxSizer">
+                    <object class="sizeritem">
+                      <object class="wxGridBagSizer">
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[7]">
+                            <label>7</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>0,0</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[8]">
+                            <label>8</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>0,1</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[9]">
+                            <label>9</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>0,2</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[3]">
+                            <label>/</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>0,3</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[4]">
+                            <label>4</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>1,0</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[5]">
+                            <label>5</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>1,1</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[6]">
+                            <label>6</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>1,2</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[2]">
+                            <label>X</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>1,3</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[1]">
+                            <label>1</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>2,0</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[2]">
+                            <label>2</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>2,1</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[3]">
+                            <label>3</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>2,2</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[1]">
+                            <label>-</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>2,3</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="digits[0]">
+                            <label>0</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>3,0</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[4]">
+                            <label>=</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>3,1</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[end]">
+                            <label>Cl</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>3,2</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="operators[0]">
+                            <label>+</label>
+                            <style>wxBU_EXACTFIT</style>
+                          </object>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>3,3</cellpos>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxTextCtrl" name="result"/>
+                          <flag>wxEXPAND</flag>
+                          <cellpos>4,0</cellpos>
+                          <cellspan>1,4</cellspan>
+                        </object>
+                        <vgap>5</vgap>
+                        <hgap>5</hgap>
+                      </object>
+                      <flag>wxALL</flag>
+                      <border>5</border>
+                    </object>
+                    <label>Calculator</label>
+                    <orient>wxVERTICAL</orient>
+                  </object>
+                  <flag>wxTOP|wxALIGN_CENTRE_HORIZONTAL</flag>
+                  <border>10</border>
+                </object>
+                <object class="sizeritem">
+                  <object_ref ref="bottom_panel"/>
+                  <option>1</option>
+                  <flag>wxEXPAND</flag>
+                </object>
+                <cols>1</cols>
+                <rows>0</rows>
+                <vgap>0</vgap>
+                <hgap>10</hgap>
+                <growablecols>0</growablecols>
+                <growablerows>0</growablerows>
+                <growablerows>2</growablerows>
+              </object>
+            </object>
+            <label>Calc</label>
+          </object>
+        </object>
+        <option>1</option>
+        <flag>wxGROW|wxALIGN_CENTER_VERTICAL|wxALL</flag>
+        <border>5</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxButton" name="wxID_OK">
+          <label>OK</label>
+          <default>1</default>
+        </object>
+        <flag>wxTOP|wxBOTTOM|wxALIGN_CENTRE</flag>
+        <border>20</border>
+      </object>
+      <cols>1</cols>
+      <rows>0</rows>
+      <vgap>0</vgap>
+      <hgap>0</hgap>
+      <growablecols>0</growablecols>
+      <growablerows>0</growablerows>
+    </object>
+    <title>Object References and ID Ranges Example</title>
+    <centered>1</centered>
+    <style>wxCAPTION|wxSYSTEM_MENU|wxRESIZE_BORDER</style>
+    <exstyle>wxDIALOG_EX_CONTEXTHELP</exstyle>
+  </object>
+  <object class="wxPanel" name="bottom_panel">
+    <object class="wxBoxSizer">
+      <orient>wxVERTICAL</orient>
+      <object class="sizeritem">
+        <object class="wxStaticLine">
+          <style>wxLI_HORIZONTAL</style>
+        </object>
+        <flag>wxTOP|wxEXPAND</flag>
+        <border>20</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxStaticText">
+          <label>Log window:</label>
+        </object>
+        <flag>wxTOP</flag>
+        <border>5</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxTextCtrl" name="log_text">
+          <size>-1,100</size>
+          <style>wxTE_MULTILINE</style>
+        </object>
+        <option>1</option>
+        <flag>wxEXPAND</flag>
+      </object>
+    </object>
+  </object>
+  <object class="wxPanel" name="checkboxes">
+    <object class="wxBoxSizer">
+      <orient>wxVERTICAL</orient>
+      <object class="sizeritem">
+        <object class="wxCheckBox" name="check[0]">
+          <checked>1</checked>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <object class="wxCheckBox" name="check[1]">
+          <checked>1</checked>
+        </object>
+      </object>
+      <object class="sizeritem">
+        <object class="wxCheckBox" name="check[2]">
+          <checked>1</checked>
+        </object>
+      </object>
+    </object>
+  </object>
+  <ids-range name="check" size="3" start="10000"/>
+  <ids-range name="first_row"/>
+  <ids-range name="second_row"/>
+  <ids-range name="third_row"/>
+  <ids-range name="digits" size="8"/>
+  <ids-range name="operators"/>
+</resource>
diff --git a/samples/xrc/rc/objrefdlg.xpm b/samples/xrc/rc/objrefdlg.xpm
new file mode 100644 (file)
index 0000000..2cd9d98
--- /dev/null
@@ -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",
+"   .            ",
+"+++.++++++++++++",
+"+.@.@.@@@@@@.@@+",
+"+@...#######.#@+",
+"...@....@@.@.@.+",
+"+@...$$$$$$...@+",
+"+.$.@.@@@%%%$%..",
+"+@$.@@@@@@@%%%@+",
+"+@$.$$$$$$%$%$%+",
+"+@$$$$$$$$$$%$@+",
+"+@$$$$@@@@$$$$@+",
+"+@$$$$@@@@$$$$@+",
+"+@$$$$$$$$$$$$@+",
+"+@@@@@@@@@@@@@@+",
+"++++++++++++++++",
+"                "};
index 00d74ceb5c6d09caf980572d86dfa262f13ad735..48e08f2b2d474b2ba6a6bc17b8c51ea57c488f8d 100644 (file)
         <longhelp>Disable autocentering of a dialog on its parent</longhelp>
     </object>  
     <object class="separator"/>
+    <object class="tool" name="obj_ref_tool_or_menuitem">
+      <bitmap>objrefdlg.xpm</bitmap>
+      <tooltip>Object references and event ranges example</tooltip>
+      <longhelp>Use of object references and event ranges</longhelp>
+    </object>
     <object class="tool" name="custom_class_tool_or_menuitem">
         <tooltip>Custom Class Example</tooltip>
         <bitmap>custclas.xpm</bitmap>
index c4d734d65e7618ab2f92440b79432ace5921b753..0d949591dcb416a90b81a8f9c1d2a2b5ae29b6c7 100644 (file)
@@ -9,12 +9,14 @@
             myframe.cpp
             derivdlg.cpp
             custclas.cpp
+            objrefdlg.cpp
         </sources>
         <headers>
             derivdlg.h
             xrcdemo.h
             myframe.h
             custclas.h
+            objrefdlg.h
         </headers>
         <wx-lib>xrc</wx-lib>
         <wx-lib>html</wx-lib>
@@ -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
         </files>
index e82ea49d99e7899a4d3075ced80cf1555be941ca..9a770d9804a783b148437a622f147d4b92c03cca 100644 (file)
@@ -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
index f0bdd10e94b6fb88af9d1caa75d49809a85bd827..036f7f0e5812d743243f72230ec57d43a739b4e0 100644 (file)
@@ -264,10 +264,14 @@ SOURCE=.\myframe.cpp
 # End Source File\r
 # Begin Source File\r
 \r
-SOURCE=.\..\..\samples\sample.rc
+SOURCE=.\objrefdlg.cpp
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\..\..\samples\sample.rc
+# End Source File
+# Begin Source File
+
 SOURCE=.\xrcdemo.cpp
 # End Source File\r
 # End Group\r
@@ -288,6 +292,10 @@ SOURCE=.\myframe.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\objrefdlg.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\xrcdemo.h\r
 # End Source File\r
 # End Group\r
index a66bd240cd6c26f001c9f28e36f4bef3fb04be86..656ff3a4312ff4f183c57070997ef17683dfff4a 100644 (file)
@@ -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$
index 530a7de6527eb7c0bdb2957d04f32300a24264c8..ce0590b724aba6a70ac2ed38356cb870b7820da2 100644 (file)
                                RelativePath=".\myframe.cpp">\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.cpp">
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.cpp">\r
                        </File>\r
                </Filter>\r
                                RelativePath=".\myframe.h">\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.h">
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.h">\r
                        </File>\r
                </Filter>\r
index 3019d06193771a519db1aca6933a80ccda0942fb..be5446056a496255c70efe4a1e731611a7c90378 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.cpp"
+                               >
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.cpp"\r
                                >\r
                        </File>\r
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.h"
+                               >
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.h"\r
                                >\r
                        </File>\r
index 418d5915efcdf7ce3ecdcf67d04ab8e7177d7ff2..986cda6be15eba73b7f440dcbad3f9b1aca906b5 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.cpp"
+                               >
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.cpp"\r
                                >\r
                        </File>\r
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\objrefdlg.h"
+                               >
+                       </File>
+                       <File
                                RelativePath=".\xrcdemo.h"\r
                                >\r
                        </File>\r
index cc3649c9c3597b82b1a623e70ac50824f92437a4..10ff03fc3989730bba9192a39b17c5739690ec9b 100644 (file)
@@ -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<wxXmlResourceDataRecord*>
     // 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<wxIdRange*> 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<wxXmlResource *>(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<wxIdRange*>::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<wxIdRange*>::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<wxXmlSubclassFactory*>
 {
@@ -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();
index 47264145bad5c09c1545f177a04d577dae91684e..cadec5a327baeb9b23a6aaa6b341569a923733e5 100644 (file)
@@ -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
index f3637caa94b21e57fb26326cb4aabb8c485246ab..c093d698788abf40e58c2ff524d506e7f218b25b 100644 (file)
@@ -192,7 +192,8 @@ TEST_GUI_OBJECTS =  \
        $(OBJS)\test_gui_socket.obj \\r
        $(OBJS)\test_gui_boxsizer.obj \\r
        $(OBJS)\test_gui_clientsize.obj \\r
-       $(OBJS)\test_gui_setsize.obj\r
+       $(OBJS)\test_gui_setsize.obj \
+       $(OBJS)\test_gui_xrctest.obj
 \r
 ### Conditionally set variables: ###\r
 \r
@@ -889,3 +890,5 @@ $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp
 $(OBJS)\test_gui_setsize.obj: .\window\setsize.cpp\r
        $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\window\setsize.cpp\r
 \r
+$(OBJS)\test_gui_xrctest.obj: .\xml\xrctest.cpp
+       $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\xml\xrctest.cpp
index d164e97a38b0af16109ce6773373a8956e80da8a..bb9002c4e1eedb61fc9acffc59f13e575ea8ef7f 100644 (file)
@@ -185,7 +185,8 @@ TEST_GUI_OBJECTS =  \
        $(OBJS)\test_gui_socket.o \\r
        $(OBJS)\test_gui_boxsizer.o \\r
        $(OBJS)\test_gui_clientsize.o \\r
-       $(OBJS)\test_gui_setsize.o\r
+       $(OBJS)\test_gui_setsize.o \
+       $(OBJS)\test_gui_xrctest.o
 \r
 ### Conditionally set variables: ###\r
 \r
@@ -870,6 +871,9 @@ $(OBJS)\test_gui_clientsize.o: ./window/clientsize.cpp
 $(OBJS)\test_gui_setsize.o: ./window/setsize.cpp\r
        $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<\r
 \r
+$(OBJS)\test_gui_xrctest.o: ./xml/xrctest.cpp
+       $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
+
 .PHONY: all clean data fr\r
 \r
 \r
index 6160f751b0e11ed6fc96c09daaf282d17e3c6bc3..6663aad0a6601273d219a0ef85a100ecc3e0af57 100644 (file)
@@ -187,7 +187,8 @@ TEST_GUI_OBJECTS =  \
        $(OBJS)\test_gui_socket.obj \\r
        $(OBJS)\test_gui_boxsizer.obj \\r
        $(OBJS)\test_gui_clientsize.obj \\r
-       $(OBJS)\test_gui_setsize.obj\r
+       $(OBJS)\test_gui_setsize.obj \
+       $(OBJS)\test_gui_xrctest.obj
 TEST_GUI_RESOURCES =  \\r
        $(OBJS)\test_gui_sample.res\r
 \r
@@ -1015,3 +1016,5 @@ $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp
 $(OBJS)\test_gui_setsize.obj: .\window\setsize.cpp\r
        $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\window\setsize.cpp\r
 \r
+$(OBJS)\test_gui_xrctest.obj: .\xml\xrctest.cpp
+       $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\xml\xrctest.cpp
index 07d754ea86242d398ffb4aaa1eb5cc8370d7b533..9b0e3c89841c07dab6a9a2f6b5812f41aceacbb5 100644 (file)
@@ -427,7 +427,8 @@ TEST_GUI_OBJECTS =  &
        $(OBJS)\test_gui_socket.obj &\r
        $(OBJS)\test_gui_boxsizer.obj &\r
        $(OBJS)\test_gui_clientsize.obj &\r
-       $(OBJS)\test_gui_setsize.obj\r
+       $(OBJS)\test_gui_setsize.obj &
+       $(OBJS)\test_gui_xrctest.obj
 \r
 \r
 all : $(OBJS)\r
@@ -928,3 +929,5 @@ $(OBJS)\test_gui_clientsize.obj :  .AUTODEPEND .\window\clientsize.cpp
 $(OBJS)\test_gui_setsize.obj :  .AUTODEPEND .\window\setsize.cpp\r
        $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $<\r
 \r
+$(OBJS)\test_gui_xrctest.obj :  .AUTODEPEND .\xml\xrctest.cpp
+       $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $<
index 48f26f372a41d2dcc45cbd4579799c3a0d927d02..65dcac6ef3a1ad71035f43e6ac4a716588008a95 100644 (file)
             sizers/boxsizer.cpp
             window/clientsize.cpp
             window/setsize.cpp
+            xml/xrctest.cpp
         </sources>
         <wx-lib>richtext</wx-lib>
         <wx-lib>media</wx-lib>
index c7b1e713900ca60b93d4c4abd5b025f74a037f21..9e263e5d263f9319b094f5eb17b7bd236f040c93 100644 (file)
@@ -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\r
 # End Target\r
 # End Project\r
index 79d8f3292c95c3bd43afd8511ff5364e66fe9fd1..6445a4de4b06f1d916488f0833a26c674292eaf9 100644 (file)
                        <File
                                RelativePath=".\controls\windowtest.cpp">
                        </File>
+                       <File
+                               RelativePath=".\xml\xrctest.cpp">
+                       </File>
                </Filter>\r
                <Filter\r
                        Name="Resource Files"\r
index b372d2390cb2688520fba53eecadb1b0b2634a1b..8ac2aa16ae1718992bf1c9bdaa0379658d640171 100644 (file)
                                RelativePath=".\controls\windowtest.cpp"
                                >
                        </File>
+                       <File
+                               RelativePath=".\xml\xrctest.cpp"
+                               >
+                       </File>
                </Filter>\r
                <Filter\r
                        Name="Resource Files"\r
index 7cdfdd4ed50c299ce8f7ba181cf08a334c604f53..0f240f7cc9d0a1535dc8e33d6074c9f1e96450b7 100644 (file)
                                RelativePath=".\controls\windowtest.cpp"
                                >
                        </File>
+                       <File
+                               RelativePath=".\xml\xrctest.cpp"
+                               >
+                       </File>
                </Filter>\r
                <Filter\r
                        Name="Resource Files"\r
diff --git a/tests/xml/xrctest.cpp b/tests/xml/xrctest.cpp
new file mode 100644 (file)
index 0000000..6181e01
--- /dev/null
@@ -0,0 +1,229 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        tests/xml/xrctest.cpp
+// Purpose:     XRC classes unit test
+// Author:      wxWidgets team
+// Created:     2010-10-30
+// RCS-ID:      $Id$
+// Copyright:   (c) 2010 wxWidgets team
+///////////////////////////////////////////////////////////////////////////////
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#include "testprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+    #include "wx/wx.h"
+#endif // WX_PRECOMP
+
+#include "wx/xml/xml.h"
+#include "wx/sstream.h"
+#include "wx/wfstream.h"
+#include "wx/xrc/xmlres.h"
+
+#include <stdarg.h>
+
+// ----------------------------------------------------------------------------
+// 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 =
+    "<?xml version=\"1.0\" ?>"
+    "<resource>"
+    "  <object class=\"wxDialog\" name=\"dialog\">"
+    "    <object class=\"wxBoxSizer\">"
+    "      <orient>wxVERTICAL</orient>"
+    "      <object class=\"sizeritem\">"
+    "        <object class=\"wxPanel\" name=\"panel1\">"
+    "          <object class=\"wxBoxSizer\">"
+    "            <object class=\"sizeritem\">"
+    "              <object class=\"wxBoxSizer\">"
+    "                <orient>wxVERTICAL</orient>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"FirstCol[0]\">"
+    "                    <label>0</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"FirstCol[1]\">"
+    "                    <label>1</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"FirstCol[2]\">"
+    "                    <label>2</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"FirstCol[3]\">"
+    "                    <label>3</label>"
+    "                  </object>"
+    "                </object>"
+    "              </object>"
+    "            </object>"
+    "            <object class=\"sizeritem\">"
+    "              <object class=\"wxBoxSizer\">"
+    "                <orient>wxVERTICAL</orient>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"SecondCol[start]\">"
+    "                    <label>0</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"SecondCol[1]\">"
+    "                    <label>1</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"SecondCol[2]\">"
+    "                    <label>2</label>"
+    "                  </object>"
+    "                </object>"
+    "                <object class=\"sizeritem\">"
+    "                  <object class=\"wxButton\" name=\"SecondCol[end]\">"
+    "                    <label>3</label>"
+    "                  </object>"
+    "                </object>"
+    "              </object>"
+    "            </object>"
+    "            <orient>wxHORIZONTAL</orient>"
+    "          </object>"
+    "      </object>"
+    "    </object>"
+    "      <object class=\"sizeritem\">"
+    "        <object class=\"wxPanel\" name=\"ref_of_panel1\">"
+    "          <object_ref ref=\"panel1\"/>"
+    "        </object>"
+    "      </object>"
+    "    </object>"
+    "    <title>test</title>"
+    "  </object>"
+    "  <ids-range name=\"FirstCol\" size=\"2\" start=\"10000\"/>"
+    "  <ids-range name=\"SecondCol\" size=\"100\" />"
+    "</resource>"
+      ;
+
+    // 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) );
+    }
+}