diff --git a/Makefile.in b/Makefile.in
index 8238041d2b..82cd930de3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -4553,6 +4553,7 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS = \
monodll_layout.o \
monodll_lboxcmn.o \
monodll_listctrlcmn.o \
+ monodll_markupparser.o \
monodll_matrix.o \
monodll_menucmn.o \
monodll_mousemanager.o \
@@ -4767,6 +4768,7 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS = \
monodll_layout.o \
monodll_lboxcmn.o \
monodll_listctrlcmn.o \
+ monodll_markupparser.o \
monodll_matrix.o \
monodll_menucmn.o \
monodll_mousemanager.o \
@@ -6467,6 +6469,7 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_1 = \
monolib_layout.o \
monolib_lboxcmn.o \
monolib_listctrlcmn.o \
+ monolib_markupparser.o \
monolib_matrix.o \
monolib_menucmn.o \
monolib_mousemanager.o \
@@ -6681,6 +6684,7 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_1 = \
monolib_layout.o \
monolib_lboxcmn.o \
monolib_listctrlcmn.o \
+ monolib_markupparser.o \
monolib_matrix.o \
monolib_menucmn.o \
monolib_mousemanager.o \
@@ -8565,6 +8569,7 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_2 = \
coredll_layout.o \
coredll_lboxcmn.o \
coredll_listctrlcmn.o \
+ coredll_markupparser.o \
coredll_matrix.o \
coredll_menucmn.o \
coredll_mousemanager.o \
@@ -8779,6 +8784,7 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_2 = \
coredll_layout.o \
coredll_lboxcmn.o \
coredll_listctrlcmn.o \
+ coredll_markupparser.o \
coredll_matrix.o \
coredll_menucmn.o \
coredll_mousemanager.o \
@@ -10141,6 +10147,7 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_3 = \
corelib_layout.o \
corelib_lboxcmn.o \
corelib_listctrlcmn.o \
+ corelib_markupparser.o \
corelib_matrix.o \
corelib_menucmn.o \
corelib_mousemanager.o \
@@ -10355,6 +10362,7 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_3 = \
corelib_layout.o \
corelib_lboxcmn.o \
corelib_listctrlcmn.o \
+ corelib_markupparser.o \
corelib_matrix.o \
corelib_menucmn.o \
corelib_mousemanager.o \
@@ -19956,6 +19964,9 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP)
@COND_USE_GUI_1@monodll_listctrlcmn.o: $(srcdir)/src/common/listctrlcmn.cpp $(MONODLL_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/common/listctrlcmn.cpp
+@COND_USE_GUI_1@monodll_markupparser.o: $(srcdir)/src/common/markupparser.cpp $(MONODLL_ODEP)
+@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/common/markupparser.cpp
+
@COND_USE_GUI_1@monodll_matrix.o: $(srcdir)/src/common/matrix.cpp $(MONODLL_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/common/matrix.cpp
@@ -25239,6 +25250,9 @@ monolib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONOLIB_ODEP)
@COND_USE_GUI_1@monolib_listctrlcmn.o: $(srcdir)/src/common/listctrlcmn.cpp $(MONOLIB_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/common/listctrlcmn.cpp
+@COND_USE_GUI_1@monolib_markupparser.o: $(srcdir)/src/common/markupparser.cpp $(MONOLIB_ODEP)
+@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/common/markupparser.cpp
+
@COND_USE_GUI_1@monolib_matrix.o: $(srcdir)/src/common/matrix.cpp $(MONOLIB_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/common/matrix.cpp
@@ -30702,6 +30716,9 @@ coredll_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(COREDLL_ODEP)
@COND_USE_GUI_1@coredll_listctrlcmn.o: $(srcdir)/src/common/listctrlcmn.cpp $(COREDLL_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/common/listctrlcmn.cpp
+@COND_USE_GUI_1@coredll_markupparser.o: $(srcdir)/src/common/markupparser.cpp $(COREDLL_ODEP)
+@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/common/markupparser.cpp
+
@COND_USE_GUI_1@coredll_matrix.o: $(srcdir)/src/common/matrix.cpp $(COREDLL_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/common/matrix.cpp
@@ -34668,6 +34685,9 @@ corelib_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(CORELIB_ODEP)
@COND_USE_GUI_1@corelib_listctrlcmn.o: $(srcdir)/src/common/listctrlcmn.cpp $(CORELIB_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/common/listctrlcmn.cpp
+@COND_USE_GUI_1@corelib_markupparser.o: $(srcdir)/src/common/markupparser.cpp $(CORELIB_ODEP)
+@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/common/markupparser.cpp
+
@COND_USE_GUI_1@corelib_matrix.o: $(srcdir)/src/common/matrix.cpp $(CORELIB_ODEP)
@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/common/matrix.cpp
diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl
index ef758cb73e..041a497f3b 100644
--- a/build/bakefiles/files.bkl
+++ b/build/bakefiles/files.bkl
@@ -726,6 +726,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
src/common/layout.cpp
src/common/lboxcmn.cpp
src/common/listctrlcmn.cpp
+ src/common/markupparser.cpp
src/common/matrix.cpp
src/common/menucmn.cpp
src/common/mousemanager.cpp
diff --git a/build/msw/makefile.bcc b/build/msw/makefile.bcc
index d6cc10a67f..5b4b295f0c 100644
--- a/build/msw/makefile.bcc
+++ b/build/msw/makefile.bcc
@@ -1865,6 +1865,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.obj \
$(OBJS)\monodll_lboxcmn.obj \
$(OBJS)\monodll_listctrlcmn.obj \
+ $(OBJS)\monodll_markupparser.obj \
$(OBJS)\monodll_matrix.obj \
$(OBJS)\monodll_menucmn.obj \
$(OBJS)\monodll_mousemanager.obj \
@@ -2128,6 +2129,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.obj \
$(OBJS)\monodll_lboxcmn.obj \
$(OBJS)\monodll_listctrlcmn.obj \
+ $(OBJS)\monodll_markupparser.obj \
$(OBJS)\monodll_matrix.obj \
$(OBJS)\monodll_menucmn.obj \
$(OBJS)\monodll_mousemanager.obj \
@@ -2622,6 +2624,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.obj \
$(OBJS)\monolib_lboxcmn.obj \
$(OBJS)\monolib_listctrlcmn.obj \
+ $(OBJS)\monolib_markupparser.obj \
$(OBJS)\monolib_matrix.obj \
$(OBJS)\monolib_menucmn.obj \
$(OBJS)\monolib_mousemanager.obj \
@@ -2885,6 +2888,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.obj \
$(OBJS)\monolib_lboxcmn.obj \
$(OBJS)\monolib_listctrlcmn.obj \
+ $(OBJS)\monolib_markupparser.obj \
$(OBJS)\monolib_matrix.obj \
$(OBJS)\monolib_menucmn.obj \
$(OBJS)\monolib_mousemanager.obj \
@@ -3267,6 +3271,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.obj \
$(OBJS)\coredll_lboxcmn.obj \
$(OBJS)\coredll_listctrlcmn.obj \
+ $(OBJS)\coredll_markupparser.obj \
$(OBJS)\coredll_matrix.obj \
$(OBJS)\coredll_menucmn.obj \
$(OBJS)\coredll_mousemanager.obj \
@@ -3530,6 +3535,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.obj \
$(OBJS)\coredll_lboxcmn.obj \
$(OBJS)\coredll_listctrlcmn.obj \
+ $(OBJS)\coredll_markupparser.obj \
$(OBJS)\coredll_matrix.obj \
$(OBJS)\coredll_menucmn.obj \
$(OBJS)\coredll_mousemanager.obj \
@@ -3801,6 +3807,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.obj \
$(OBJS)\corelib_lboxcmn.obj \
$(OBJS)\corelib_listctrlcmn.obj \
+ $(OBJS)\corelib_markupparser.obj \
$(OBJS)\corelib_matrix.obj \
$(OBJS)\corelib_menucmn.obj \
$(OBJS)\corelib_mousemanager.obj \
@@ -4064,6 +4071,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.obj \
$(OBJS)\corelib_lboxcmn.obj \
$(OBJS)\corelib_listctrlcmn.obj \
+ $(OBJS)\corelib_markupparser.obj \
$(OBJS)\corelib_matrix.obj \
$(OBJS)\corelib_menucmn.obj \
$(OBJS)\corelib_mousemanager.obj \
@@ -7630,6 +7638,11 @@ $(OBJS)\monodll_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\monodll_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) -q -c -P -o$@ $(MONODLL_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\monodll_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) -q -c -P -o$@ $(MONODLL_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -9962,6 +9975,11 @@ $(OBJS)\monolib_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\monolib_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) -q -c -P -o$@ $(MONOLIB_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\monolib_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) -q -c -P -o$@ $(MONOLIB_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -12288,6 +12306,11 @@ $(OBJS)\coredll_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\coredll_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) -q -c -P -o$@ $(COREDLL_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\coredll_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) -q -c -P -o$@ $(COREDLL_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -13675,6 +13698,11 @@ $(OBJS)\corelib_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\corelib_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) -q -c -P -o$@ $(CORELIB_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\corelib_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) -q -c -P -o$@ $(CORELIB_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
diff --git a/build/msw/makefile.gcc b/build/msw/makefile.gcc
index 7e7bca6276..789e3105d7 100644
--- a/build/msw/makefile.gcc
+++ b/build/msw/makefile.gcc
@@ -1876,6 +1876,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.o \
$(OBJS)\monodll_lboxcmn.o \
$(OBJS)\monodll_listctrlcmn.o \
+ $(OBJS)\monodll_markupparser.o \
$(OBJS)\monodll_matrix.o \
$(OBJS)\monodll_menucmn.o \
$(OBJS)\monodll_mousemanager.o \
@@ -2141,6 +2142,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.o \
$(OBJS)\monodll_lboxcmn.o \
$(OBJS)\monodll_listctrlcmn.o \
+ $(OBJS)\monodll_markupparser.o \
$(OBJS)\monodll_matrix.o \
$(OBJS)\monodll_menucmn.o \
$(OBJS)\monodll_mousemanager.o \
@@ -2639,6 +2641,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.o \
$(OBJS)\monolib_lboxcmn.o \
$(OBJS)\monolib_listctrlcmn.o \
+ $(OBJS)\monolib_markupparser.o \
$(OBJS)\monolib_matrix.o \
$(OBJS)\monolib_menucmn.o \
$(OBJS)\monolib_mousemanager.o \
@@ -2904,6 +2907,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.o \
$(OBJS)\monolib_lboxcmn.o \
$(OBJS)\monolib_listctrlcmn.o \
+ $(OBJS)\monolib_markupparser.o \
$(OBJS)\monolib_matrix.o \
$(OBJS)\monolib_menucmn.o \
$(OBJS)\monolib_mousemanager.o \
@@ -3300,6 +3304,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.o \
$(OBJS)\coredll_lboxcmn.o \
$(OBJS)\coredll_listctrlcmn.o \
+ $(OBJS)\coredll_markupparser.o \
$(OBJS)\coredll_matrix.o \
$(OBJS)\coredll_menucmn.o \
$(OBJS)\coredll_mousemanager.o \
@@ -3565,6 +3570,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.o \
$(OBJS)\coredll_lboxcmn.o \
$(OBJS)\coredll_listctrlcmn.o \
+ $(OBJS)\coredll_markupparser.o \
$(OBJS)\coredll_matrix.o \
$(OBJS)\coredll_menucmn.o \
$(OBJS)\coredll_mousemanager.o \
@@ -3842,6 +3848,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.o \
$(OBJS)\corelib_lboxcmn.o \
$(OBJS)\corelib_listctrlcmn.o \
+ $(OBJS)\corelib_markupparser.o \
$(OBJS)\corelib_matrix.o \
$(OBJS)\corelib_menucmn.o \
$(OBJS)\corelib_mousemanager.o \
@@ -4107,6 +4114,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.o \
$(OBJS)\corelib_lboxcmn.o \
$(OBJS)\corelib_listctrlcmn.o \
+ $(OBJS)\corelib_markupparser.o \
$(OBJS)\corelib_matrix.o \
$(OBJS)\corelib_menucmn.o \
$(OBJS)\corelib_mousemanager.o \
@@ -7780,6 +7788,11 @@ $(OBJS)\monodll_listctrlcmn.o: ../../src/common/listctrlcmn.cpp
endif
ifeq ($(USE_GUI),1)
+$(OBJS)\monodll_markupparser.o: ../../src/common/markupparser.cpp
+ $(CXX) -c -o $@ $(MONODLL_CXXFLAGS) $(CPPDEPS) $<
+endif
+
+ifeq ($(USE_GUI),1)
$(OBJS)\monodll_matrix.o: ../../src/common/matrix.cpp
$(CXX) -c -o $@ $(MONODLL_CXXFLAGS) $(CPPDEPS) $<
endif
@@ -10112,6 +10125,11 @@ $(OBJS)\monolib_listctrlcmn.o: ../../src/common/listctrlcmn.cpp
endif
ifeq ($(USE_GUI),1)
+$(OBJS)\monolib_markupparser.o: ../../src/common/markupparser.cpp
+ $(CXX) -c -o $@ $(MONOLIB_CXXFLAGS) $(CPPDEPS) $<
+endif
+
+ifeq ($(USE_GUI),1)
$(OBJS)\monolib_matrix.o: ../../src/common/matrix.cpp
$(CXX) -c -o $@ $(MONOLIB_CXXFLAGS) $(CPPDEPS) $<
endif
@@ -12438,6 +12456,11 @@ $(OBJS)\coredll_listctrlcmn.o: ../../src/common/listctrlcmn.cpp
endif
ifeq ($(USE_GUI),1)
+$(OBJS)\coredll_markupparser.o: ../../src/common/markupparser.cpp
+ $(CXX) -c -o $@ $(COREDLL_CXXFLAGS) $(CPPDEPS) $<
+endif
+
+ifeq ($(USE_GUI),1)
$(OBJS)\coredll_matrix.o: ../../src/common/matrix.cpp
$(CXX) -c -o $@ $(COREDLL_CXXFLAGS) $(CPPDEPS) $<
endif
@@ -13825,6 +13848,11 @@ $(OBJS)\corelib_listctrlcmn.o: ../../src/common/listctrlcmn.cpp
endif
ifeq ($(USE_GUI),1)
+$(OBJS)\corelib_markupparser.o: ../../src/common/markupparser.cpp
+ $(CXX) -c -o $@ $(CORELIB_CXXFLAGS) $(CPPDEPS) $<
+endif
+
+ifeq ($(USE_GUI),1)
$(OBJS)\corelib_matrix.o: ../../src/common/matrix.cpp
$(CXX) -c -o $@ $(CORELIB_CXXFLAGS) $(CPPDEPS) $<
endif
diff --git a/build/msw/makefile.vc b/build/msw/makefile.vc
index 41467604fd..888543651a 100644
--- a/build/msw/makefile.vc
+++ b/build/msw/makefile.vc
@@ -2073,6 +2073,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.obj \
$(OBJS)\monodll_lboxcmn.obj \
$(OBJS)\monodll_listctrlcmn.obj \
+ $(OBJS)\monodll_markupparser.obj \
$(OBJS)\monodll_matrix.obj \
$(OBJS)\monodll_menucmn.obj \
$(OBJS)\monodll_mousemanager.obj \
@@ -2336,6 +2337,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = \
$(OBJS)\monodll_layout.obj \
$(OBJS)\monodll_lboxcmn.obj \
$(OBJS)\monodll_listctrlcmn.obj \
+ $(OBJS)\monodll_markupparser.obj \
$(OBJS)\monodll_matrix.obj \
$(OBJS)\monodll_menucmn.obj \
$(OBJS)\monodll_mousemanager.obj \
@@ -2836,6 +2838,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.obj \
$(OBJS)\monolib_lboxcmn.obj \
$(OBJS)\monolib_listctrlcmn.obj \
+ $(OBJS)\monolib_markupparser.obj \
$(OBJS)\monolib_matrix.obj \
$(OBJS)\monolib_menucmn.obj \
$(OBJS)\monolib_mousemanager.obj \
@@ -3099,6 +3102,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \
$(OBJS)\monolib_layout.obj \
$(OBJS)\monolib_lboxcmn.obj \
$(OBJS)\monolib_listctrlcmn.obj \
+ $(OBJS)\monolib_markupparser.obj \
$(OBJS)\monolib_matrix.obj \
$(OBJS)\monolib_menucmn.obj \
$(OBJS)\monolib_mousemanager.obj \
@@ -3547,6 +3551,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.obj \
$(OBJS)\coredll_lboxcmn.obj \
$(OBJS)\coredll_listctrlcmn.obj \
+ $(OBJS)\coredll_markupparser.obj \
$(OBJS)\coredll_matrix.obj \
$(OBJS)\coredll_menucmn.obj \
$(OBJS)\coredll_mousemanager.obj \
@@ -3810,6 +3815,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \
$(OBJS)\coredll_layout.obj \
$(OBJS)\coredll_lboxcmn.obj \
$(OBJS)\coredll_listctrlcmn.obj \
+ $(OBJS)\coredll_markupparser.obj \
$(OBJS)\coredll_matrix.obj \
$(OBJS)\coredll_menucmn.obj \
$(OBJS)\coredll_mousemanager.obj \
@@ -4087,6 +4093,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.obj \
$(OBJS)\corelib_lboxcmn.obj \
$(OBJS)\corelib_listctrlcmn.obj \
+ $(OBJS)\corelib_markupparser.obj \
$(OBJS)\corelib_matrix.obj \
$(OBJS)\corelib_menucmn.obj \
$(OBJS)\corelib_mousemanager.obj \
@@ -4350,6 +4357,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \
$(OBJS)\corelib_layout.obj \
$(OBJS)\corelib_lboxcmn.obj \
$(OBJS)\corelib_listctrlcmn.obj \
+ $(OBJS)\corelib_markupparser.obj \
$(OBJS)\corelib_matrix.obj \
$(OBJS)\corelib_menucmn.obj \
$(OBJS)\corelib_mousemanager.obj \
@@ -8210,6 +8218,11 @@ $(OBJS)\monodll_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\monodll_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) /c /nologo /TP /Fo$@ $(MONODLL_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\monodll_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) /c /nologo /TP /Fo$@ $(MONODLL_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -10542,6 +10555,11 @@ $(OBJS)\monolib_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\monolib_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) /c /nologo /TP /Fo$@ $(MONOLIB_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\monolib_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) /c /nologo /TP /Fo$@ $(MONOLIB_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -12868,6 +12886,11 @@ $(OBJS)\coredll_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\coredll_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) /c /nologo /TP /Fo$@ $(COREDLL_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\coredll_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) /c /nologo /TP /Fo$@ $(COREDLL_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
@@ -14255,6 +14278,11 @@ $(OBJS)\corelib_listctrlcmn.obj: ..\..\src\common\listctrlcmn.cpp
!endif
!if "$(USE_GUI)" == "1"
+$(OBJS)\corelib_markupparser.obj: ..\..\src\common\markupparser.cpp
+ $(CXX) /c /nologo /TP /Fo$@ $(CORELIB_CXXFLAGS) ..\..\src\common\markupparser.cpp
+!endif
+
+!if "$(USE_GUI)" == "1"
$(OBJS)\corelib_matrix.obj: ..\..\src\common\matrix.cpp
$(CXX) /c /nologo /TP /Fo$@ $(CORELIB_CXXFLAGS) ..\..\src\common\matrix.cpp
!endif
diff --git a/build/msw/makefile.wat b/build/msw/makefile.wat
index c3da4d7c58..0f9c1c8c8a 100644
--- a/build/msw/makefile.wat
+++ b/build/msw/makefile.wat
@@ -427,6 +427,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = &
$(OBJS)\monodll_layout.obj &
$(OBJS)\monodll_lboxcmn.obj &
$(OBJS)\monodll_listctrlcmn.obj &
+ $(OBJS)\monodll_markupparser.obj &
$(OBJS)\monodll_matrix.obj &
$(OBJS)\monodll_menucmn.obj &
$(OBJS)\monodll_mousemanager.obj &
@@ -692,6 +693,7 @@ ____CORE_SRC_FILENAMES_OBJECTS = &
$(OBJS)\monodll_layout.obj &
$(OBJS)\monodll_lboxcmn.obj &
$(OBJS)\monodll_listctrlcmn.obj &
+ $(OBJS)\monodll_markupparser.obj &
$(OBJS)\monodll_matrix.obj &
$(OBJS)\monodll_menucmn.obj &
$(OBJS)\monodll_mousemanager.obj &
@@ -1195,6 +1197,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = &
$(OBJS)\monolib_layout.obj &
$(OBJS)\monolib_lboxcmn.obj &
$(OBJS)\monolib_listctrlcmn.obj &
+ $(OBJS)\monolib_markupparser.obj &
$(OBJS)\monolib_matrix.obj &
$(OBJS)\monolib_menucmn.obj &
$(OBJS)\monolib_mousemanager.obj &
@@ -1460,6 +1463,7 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = &
$(OBJS)\monolib_layout.obj &
$(OBJS)\monolib_lboxcmn.obj &
$(OBJS)\monolib_listctrlcmn.obj &
+ $(OBJS)\monolib_markupparser.obj &
$(OBJS)\monolib_matrix.obj &
$(OBJS)\monolib_menucmn.obj &
$(OBJS)\monolib_mousemanager.obj &
@@ -1867,6 +1871,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = &
$(OBJS)\coredll_layout.obj &
$(OBJS)\coredll_lboxcmn.obj &
$(OBJS)\coredll_listctrlcmn.obj &
+ $(OBJS)\coredll_markupparser.obj &
$(OBJS)\coredll_matrix.obj &
$(OBJS)\coredll_menucmn.obj &
$(OBJS)\coredll_mousemanager.obj &
@@ -2132,6 +2137,7 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = &
$(OBJS)\coredll_layout.obj &
$(OBJS)\coredll_lboxcmn.obj &
$(OBJS)\coredll_listctrlcmn.obj &
+ $(OBJS)\coredll_markupparser.obj &
$(OBJS)\coredll_matrix.obj &
$(OBJS)\coredll_menucmn.obj &
$(OBJS)\coredll_mousemanager.obj &
@@ -2411,6 +2417,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = &
$(OBJS)\corelib_layout.obj &
$(OBJS)\corelib_lboxcmn.obj &
$(OBJS)\corelib_listctrlcmn.obj &
+ $(OBJS)\corelib_markupparser.obj &
$(OBJS)\corelib_matrix.obj &
$(OBJS)\corelib_menucmn.obj &
$(OBJS)\corelib_mousemanager.obj &
@@ -2676,6 +2683,7 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = &
$(OBJS)\corelib_layout.obj &
$(OBJS)\corelib_lboxcmn.obj &
$(OBJS)\corelib_listctrlcmn.obj &
+ $(OBJS)\corelib_markupparser.obj &
$(OBJS)\corelib_matrix.obj &
$(OBJS)\corelib_menucmn.obj &
$(OBJS)\corelib_mousemanager.obj &
@@ -8039,6 +8047,11 @@ $(OBJS)\monodll_listctrlcmn.obj : .AUTODEPEND ..\..\src\common\listctrlcmn.cpp
!endif
!ifeq USE_GUI 1
+$(OBJS)\monodll_markupparser.obj : .AUTODEPEND ..\..\src\common\markupparser.cpp
+ $(CXX) -bt=nt -zq -fo=$^@ $(MONODLL_CXXFLAGS) $<
+!endif
+
+!ifeq USE_GUI 1
$(OBJS)\monodll_matrix.obj : .AUTODEPEND ..\..\src\common\matrix.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(MONODLL_CXXFLAGS) $<
!endif
@@ -10371,6 +10384,11 @@ $(OBJS)\monolib_listctrlcmn.obj : .AUTODEPEND ..\..\src\common\listctrlcmn.cpp
!endif
!ifeq USE_GUI 1
+$(OBJS)\monolib_markupparser.obj : .AUTODEPEND ..\..\src\common\markupparser.cpp
+ $(CXX) -bt=nt -zq -fo=$^@ $(MONOLIB_CXXFLAGS) $<
+!endif
+
+!ifeq USE_GUI 1
$(OBJS)\monolib_matrix.obj : .AUTODEPEND ..\..\src\common\matrix.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(MONOLIB_CXXFLAGS) $<
!endif
@@ -12697,6 +12715,11 @@ $(OBJS)\coredll_listctrlcmn.obj : .AUTODEPEND ..\..\src\common\listctrlcmn.cpp
!endif
!ifeq USE_GUI 1
+$(OBJS)\coredll_markupparser.obj : .AUTODEPEND ..\..\src\common\markupparser.cpp
+ $(CXX) -bt=nt -zq -fo=$^@ $(COREDLL_CXXFLAGS) $<
+!endif
+
+!ifeq USE_GUI 1
$(OBJS)\coredll_matrix.obj : .AUTODEPEND ..\..\src\common\matrix.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(COREDLL_CXXFLAGS) $<
!endif
@@ -14084,6 +14107,11 @@ $(OBJS)\corelib_listctrlcmn.obj : .AUTODEPEND ..\..\src\common\listctrlcmn.cpp
!endif
!ifeq USE_GUI 1
+$(OBJS)\corelib_markupparser.obj : .AUTODEPEND ..\..\src\common\markupparser.cpp
+ $(CXX) -bt=nt -zq -fo=$^@ $(CORELIB_CXXFLAGS) $<
+!endif
+
+!ifeq USE_GUI 1
$(OBJS)\corelib_matrix.obj : .AUTODEPEND ..\..\src\common\matrix.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(CORELIB_CXXFLAGS) $<
!endif
diff --git a/build/msw/wx_core.dsp b/build/msw/wx_core.dsp
index c83236a012..ed7f043b32 100644
--- a/build/msw/wx_core.dsp
+++ b/build/msw/wx_core.dsp
@@ -566,6 +566,10 @@ SOURCE=..\..\src\common\listctrlcmn.cpp
# End Source File
# Begin Source File
+SOURCE=..\..\src\common\markupparser.cpp
+# End Source File
+# Begin Source File
+
SOURCE=..\..\src\common\matrix.cpp
# End Source File
# Begin Source File
diff --git a/build/msw/wx_vc7_core.vcproj b/build/msw/wx_vc7_core.vcproj
index 33f28f7fe6..cfed66795e 100644
--- a/build/msw/wx_vc7_core.vcproj
+++ b/build/msw/wx_vc7_core.vcproj
@@ -817,6 +817,9 @@
RelativePath="..\..\src\common\listctrlcmn.cpp">
+
+
+
+
diff --git a/build/msw/wx_vc9_core.vcproj b/build/msw/wx_vc9_core.vcproj
index 709266b643..1510b5a7e7 100644
--- a/build/msw/wx_vc9_core.vcproj
+++ b/build/msw/wx_vc9_core.vcproj
@@ -1119,6 +1119,10 @@
>
+
+
diff --git a/include/wx/private/markupparser.h b/include/wx/private/markupparser.h
new file mode 100644
index 0000000000..e0eb0a8099
--- /dev/null
+++ b/include/wx/private/markupparser.h
@@ -0,0 +1,167 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name: wx/private/markupparser.h
+// Purpose: Classes for parsing simple markup.
+// Author: Vadim Zeitlin
+// Created: 2011-02-16
+// RCS-ID: $Id: $
+// Copyright: (c) 2011 Vadim Zeitlin
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_PRIVATE_MARKUPPARSER_H_
+#define _WX_PRIVATE_MARKUPPARSER_H_
+
+#include "wx/string.h"
+
+// ----------------------------------------------------------------------------
+// wxMarkupSpanAttributes: information about attributes for a markup span.
+// ----------------------------------------------------------------------------
+
+struct wxMarkupSpanAttributes
+{
+ enum OptionalBool
+ {
+ Unspecified = -1,
+ No,
+ Yes
+ };
+
+ wxMarkupSpanAttributes()
+ {
+ m_sizeKind = Size_Unspecified;
+
+ m_isBold =
+ m_isItalic =
+ m_isUnderlined =
+ m_isStrikethrough = Unspecified;
+ }
+
+ // If a string is empty, it means that the corresponding attribute is not
+ // set.
+ wxString m_fgCol,
+ m_bgCol,
+ m_fontFace;
+
+ // There are many ways of specifying the size. First of all, the size may
+ // be relative in which case m_fontSize is either -1 or +1 meaning that
+ // it's one step smaller or larger than the current font. Second, it may be
+ // absolute in which case m_fontSize contains either the size in 1024th of
+ // a point (Pango convention) or its values are in [-3, 3] interval and map
+ // to [xx-small, xx-large] CSS-like font size specification. And finally it
+ // may be not specified at all, of course, in which case the value of
+ // m_fontSize doesn't matter and it shouldn't be used.
+ enum
+ {
+ Size_Unspecified,
+ Size_Relative,
+ Size_Symbolic,
+ Size_PointParts
+ } m_sizeKind;
+ int m_fontSize;
+
+ // If the value is Unspecified, the attribute wasn't given.
+ OptionalBool m_isBold,
+ m_isItalic,
+ m_isUnderlined,
+ m_isStrikethrough;
+};
+
+// ----------------------------------------------------------------------------
+// wxMarkupParserOutput: gathers the results of parsing markup.
+// ----------------------------------------------------------------------------
+
+class wxMarkupParserOutput
+{
+public:
+ wxMarkupParserOutput() { }
+
+ // Virtual functions called by wxMarkupParser while parsing the markup.
+
+ // Called for a run of normal text.
+ virtual void OnText(const wxString& text) = 0;
+
+ // These functions correspond to the simple tags without parameters.
+ virtual void OnBoldStart() = 0;
+ virtual void OnBoldEnd() = 0;
+
+ virtual void OnItalicStart() = 0;
+ virtual void OnItalicEnd() = 0;
+
+ virtual void OnUnderlinedStart() = 0;
+ virtual void OnUnderlinedEnd() = 0;
+
+ virtual void OnStrikethroughStart() = 0;
+ virtual void OnStrikethroughEnd() = 0;
+
+ virtual void OnBigStart() = 0;
+ virtual void OnBigEnd() = 0;
+
+ virtual void OnSmallStart() = 0;
+ virtual void OnSmallEnd() = 0;
+
+ virtual void OnTeletypeStart() = 0;
+ virtual void OnTeletypeEnd() = 0;
+
+ // The generic span start and end functions.
+ virtual void OnSpanStart(const wxMarkupSpanAttributes& attrs) = 0;
+ virtual void OnSpanEnd(const wxMarkupSpanAttributes& attrs) = 0;
+
+private:
+ wxDECLARE_NO_COPY_CLASS(wxMarkupParserOutput);
+};
+
+// ----------------------------------------------------------------------------
+// wxMarkupParser: parses the given markup text into wxMarkupParserOutput.
+// ----------------------------------------------------------------------------
+
+class WXDLLIMPEXP_CORE wxMarkupParser
+{
+public:
+ // Initialize the parser with the object that will receive parsing results.
+ // This object lifetime must be greater than ours.
+ explicit wxMarkupParser(wxMarkupParserOutput& output)
+ : m_output(output)
+ {
+ }
+
+ // Parse the entire string and call wxMarkupParserOutput methods.
+ //
+ // Return true if the string was successfully parsed or false if it failed
+ // (presumably because of syntax errors in the markup).
+ bool Parse(const wxString& text);
+
+ // Quote a normal string, not meant to be interpreted as markup, so that it
+ // produces the same string when parsed as markup. This means, for example,
+ // replacing '<' in the input string with "<" to prevent them from being
+ // interpreted as tag opening characters.
+ static wxString Quote(const wxString& text);
+
+private:
+ // Simple struct combining the name of a tag and its attributes.
+ struct TagAndAttrs
+ {
+ TagAndAttrs(const wxString& name_) : name(name_) { }
+
+ wxString name;
+ wxMarkupSpanAttributes attrs;
+ };
+
+ // Call the wxMarkupParserOutput method corresponding to the given tag.
+ //
+ // Return false if the tag doesn't match any of the known ones.
+ bool OutputTag(const TagAndAttrs& tagAndAttrs, bool start);
+
+ // Parse the attributes and fill the provided TagAndAttrs object with the
+ // information about them. Does nothing if attrs string is empty.
+ //
+ // Returns empty string on success of a [fragment of an] error message if
+ // we failed to parse the attributes.
+ wxString ParseAttrs(wxString attrs, TagAndAttrs& tagAndAttrs);
+
+
+ wxMarkupParserOutput& m_output;
+
+ wxDECLARE_NO_COPY_CLASS(wxMarkupParser);
+};
+
+#endif // _WX_PRIVATE_MARKUPPARSER_H_
diff --git a/src/common/markupparser.cpp b/src/common/markupparser.cpp
new file mode 100644
index 0000000000..535e62bade
--- /dev/null
+++ b/src/common/markupparser.cpp
@@ -0,0 +1,427 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name: src/common/markupparser.cpp
+// Purpose: Implementation of wxMarkupParser.
+// Author: Vadim Zeitlin
+// Created: 2011-02-16
+// RCS-ID: $Id: $
+// Copyright: (c) 2011 Vadim Zeitlin
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#endif // WX_PRECOMP
+
+#include "wx/private/markupparser.h"
+
+#include "wx/stack.h"
+
+namespace
+{
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// Array containing the predefined XML 1.0 entities.
+const struct XMLEntity
+{
+ const char *name;
+ int len; // == strlen(name)
+ char value;
+} xmlEntities[] =
+{
+ { "lt", 2, '<' },
+ { "gt", 2, '>' },
+ { "amp", 3, '&' },
+ { "apos", 4, '\''},
+ { "quot", 4, '"' },
+};
+
+// ----------------------------------------------------------------------------
+// helper functions
+// ----------------------------------------------------------------------------
+
+wxString
+ExtractUntil(char ch, wxString::const_iterator& it, wxString::const_iterator end)
+{
+ wxString str;
+ for ( ; it != end; ++it )
+ {
+ if ( *it == ch )
+ return str;
+
+ str += *it;
+ }
+
+ // Return empty string to indicate that we didn't find ch at all.
+ return wxString();
+}
+
+} // anonymous namespace
+
+// ============================================================================
+// wxMarkupParser implementation
+// ============================================================================
+
+wxString
+wxMarkupParser::ParseAttrs(wxString attrs, TagAndAttrs& tagAndAttrs)
+{
+ if ( tagAndAttrs.name.CmpNoCase("span") != 0 && !attrs.empty() )
+ {
+ return wxString::Format("tag \"%s\" can't have attributes",
+ tagAndAttrs.name);
+ }
+
+ // TODO: Parse more attributes described at
+ // http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html
+ // and at least ignore them gracefully instead of giving errors (but
+ // quite a few of them could be supported as well, notable font_desc).
+
+ wxMarkupSpanAttributes& spanAttrs = tagAndAttrs.attrs;
+
+ while ( !attrs.empty() )
+ {
+ wxString rest;
+ const wxString attr = attrs.BeforeFirst(' ', &rest);
+ attrs = rest;
+
+ // The "original" versions are used for error messages only.
+ wxString valueOrig;
+ const wxString nameOrig = attr.BeforeFirst('=', &valueOrig);
+
+ const wxString name = nameOrig.Lower();
+ wxString value = valueOrig.Lower();
+
+ // All attributes values must be quoted.
+ if ( value.length() < 2 ||
+ (value[0] != value.Last()) ||
+ (value[0] != '"' && value[0] != '\'') )
+ {
+ return wxString::Format("bad quoting for value of \"%s\"",
+ nameOrig);
+ }
+
+ value.assign(value, 1, value.length() - 2);
+
+ if ( name == "foreground" || name == "fgcolor" || name == "color" )
+ {
+ spanAttrs.m_fgCol = value;
+ }
+ else if ( name == "background" || name == "bgcolor" )
+ {
+ spanAttrs.m_bgCol = value;
+ }
+ else if ( name == "font_family" || name == "face" )
+ {
+ spanAttrs.m_fontFace = value;
+ }
+ else if ( name == "font_weight" || name == "weight" )
+ {
+ unsigned long weight;
+
+ if ( value == "ultralight" || value == "light" || value == "normal" )
+ spanAttrs.m_isBold = wxMarkupSpanAttributes::No;
+ else if ( value == "bold" || value == "ultrabold" || value == "heavy" )
+ spanAttrs.m_isBold = wxMarkupSpanAttributes::Yes;
+ else if ( value.ToULong(&weight) )
+ spanAttrs.m_isBold = weight >= 600 ? wxMarkupSpanAttributes::Yes
+ : wxMarkupSpanAttributes::No;
+ else
+ return wxString::Format("invalid font weight \"%s\"", valueOrig);
+ }
+ else if ( name == "font_style" || name == "style" )
+ {
+ if ( value == "normal" )
+ spanAttrs.m_isItalic = wxMarkupSpanAttributes::No;
+ else if ( value == "oblique" || value == "italic" )
+ spanAttrs.m_isItalic = wxMarkupSpanAttributes::Yes;
+ else
+ return wxString::Format("invalid font style \"%s\"", valueOrig);
+ }
+ else if ( name == "size" )
+ {
+ unsigned long size;
+ if ( value.ToULong(&size) )
+ {
+ spanAttrs.m_sizeKind = wxMarkupSpanAttributes::Size_PointParts;
+ spanAttrs.m_fontSize = size;
+ }
+ else if ( value == "smaller" || value == "larger" )
+ {
+ spanAttrs.m_sizeKind = wxMarkupSpanAttributes::Size_Relative;
+ spanAttrs.m_fontSize = value == "smaller" ? -1 : +1;
+ }
+ else // Must be a CSS-like size specification
+ {
+ int cssSize = 1;
+ wxString rest;
+ if ( value.StartsWith("xx-", &rest) )
+ cssSize = 3;
+ else if ( value.StartsWith("x-", &rest) )
+ cssSize = 2;
+ else if ( value == "medium" )
+ cssSize = 0;
+ else
+ rest = value;
+
+ if ( cssSize != 0 )
+ {
+ if ( rest == "small" )
+ cssSize = -cssSize;
+ else if ( rest != "large" )
+ return wxString::Format("invalid font size \"%s\"",
+ valueOrig);
+ }
+
+ spanAttrs.m_sizeKind = wxMarkupSpanAttributes::Size_Symbolic;
+ spanAttrs.m_fontSize = cssSize;
+ }
+ }
+ }
+
+ return wxString();
+}
+
+bool wxMarkupParser::OutputTag(const TagAndAttrs& tagAndAttrs, bool start)
+{
+ if ( tagAndAttrs.name.CmpNoCase("span") == 0 )
+ {
+ if ( start )
+ m_output.OnSpanStart(tagAndAttrs.attrs);
+ else
+ m_output.OnSpanEnd(tagAndAttrs.attrs);
+
+ return true;
+ }
+ else // non-span tag
+ {
+ static const struct TagHandler
+ {
+ const char *name;
+ void (wxMarkupParserOutput::*startFunc)();
+ void (wxMarkupParserOutput::*endFunc)();
+ } tagHandlers[] =
+ {
+ { "b", &wxMarkupParserOutput::OnBoldStart,
+ &wxMarkupParserOutput::OnBoldEnd },
+ { "i", &wxMarkupParserOutput::OnItalicStart,
+ &wxMarkupParserOutput::OnItalicEnd },
+ { "u", &wxMarkupParserOutput::OnUnderlinedStart,
+ &wxMarkupParserOutput::OnUnderlinedEnd },
+ { "s", &wxMarkupParserOutput::OnStrikethroughStart,
+ &wxMarkupParserOutput::OnStrikethroughEnd },
+ { "big", &wxMarkupParserOutput::OnBigStart,
+ &wxMarkupParserOutput::OnBigEnd },
+ { "small", &wxMarkupParserOutput::OnSmallStart,
+ &wxMarkupParserOutput::OnSmallEnd },
+ { "tt", &wxMarkupParserOutput::OnTeletypeStart,
+ &wxMarkupParserOutput::OnTeletypeEnd },
+ };
+
+ for ( unsigned n = 0; n < WXSIZEOF(tagHandlers); n++ )
+ {
+ const TagHandler& h = tagHandlers[n];
+
+ if ( tagAndAttrs.name.CmpNoCase(h.name) == 0 )
+ {
+ if ( start )
+ (m_output.*(h.startFunc))();
+ else
+ (m_output.*(h.endFunc))();
+
+ return true;
+ }
+ }
+ }
+
+ // Unknown tag name.
+ return false;
+}
+
+bool wxMarkupParser::Parse(const wxString& text)
+{
+ // The stack containing the names and corresponding attributes (which are
+ // actually only used for tags) of all of the currently opened tag
+ // or none if we're not inside any tag.
+ wxStack tags;
+
+ // Current run of text.
+ wxString current;
+
+ const wxString::const_iterator end = text.end();
+ for ( wxString::const_iterator it = text.begin(); it != end; ++it )
+ {
+ switch ( (*it).GetValue() )
+ {
+ case '<':
+ {
+ // Flush the text preceding the tag, if any.
+ if ( !current.empty() )
+ {
+ m_output.OnText(current);
+ current.clear();
+ }
+
+ // Remember the tag starting position for the error
+ // messages.
+ const size_t pos = it - text.begin();
+
+ bool start = true;
+ if ( ++it != end && *it == '/' )
+ {
+ start = false;
+ ++it;
+ }
+
+ const wxString tag = ExtractUntil('>', it, end);
+ if ( tag.empty() )
+ {
+ wxLogDebug("%s at %lu.",
+ it == end ? "Unclosed tag starting"
+ : "Empty tag",
+ pos);
+ return false;
+ }
+
+ if ( start )
+ {
+ wxString attrs;
+ const wxString name = tag.BeforeFirst(' ', &attrs);
+
+ TagAndAttrs tagAndAttrs(name);
+ const wxString err = ParseAttrs(attrs, tagAndAttrs);
+ if ( !err.empty() )
+ {
+ wxLogDebug("Bad attributes for \"%s\" "
+ "at %lu: %s.",
+ name, pos, err);
+ return false;
+ }
+
+ tags.push(tagAndAttrs);
+ }
+ else // end tag
+ {
+ if ( tags.empty() || tags.top().name != tag )
+ {
+ wxLogDebug("Unmatched closing tag \"%s\" at %lu.",
+ tag, pos);
+ return false;
+ }
+ }
+
+ if ( !OutputTag(tags.top(), start) )
+ {
+ wxLogDebug("Unknown tag at %lu.", pos);
+ return false;
+ }
+
+ if ( !start )
+ tags.pop();
+ }
+ break;
+
+ case '>':
+ wxLogDebug("'>' should be escaped as \">\"; at %lu.",
+ it - text.begin());
+ break;
+
+ case '&':
+ // Processing is somewhat complicated: we need to recognize at
+ // least the "<" entity to allow escaping left square
+ // brackets in the markup and, in fact, we recognize all of the
+ // standard XML entities for consistency with Pango markup
+ // parsing.
+ //
+ // However we also allow '&' to appear unescaped, i.e. directly
+ // and not as "&" when it is used to introduce the mnemonic
+ // for the label. In this case we simply leave it alone.
+ //
+ // Notice that this logic makes it impossible to have a label
+ // with "lt;" inside it and using "l" as mnemonic but hopefully
+ // this shouldn't be a problem in practice.
+ {
+ const size_t pos = it - text.begin() + 1;
+
+ unsigned n;
+ for ( n = 0; n < WXSIZEOF(xmlEntities); n++ )
+ {
+ const XMLEntity& xmlEnt = xmlEntities[n];
+ if ( text.compare(pos, xmlEnt.len, xmlEnt.name) == 0
+ && text[pos + xmlEnt.len] == ';' )
+ {
+ // Escape the ampersands if needed to protect them
+ // from being interpreted as mnemonics indicators.
+ if ( xmlEnt.value == '&' )
+ current += "&&";
+ else
+ current += xmlEnt.value;
+
+ it += xmlEnt.len + 1; // +1 for '&' itself
+
+ break;
+ }
+ }
+
+ if ( n < WXSIZEOF(xmlEntities) )
+ break;
+ //else: fall through, '&' is not special
+ }
+
+ default:
+ current += *it;
+ }
+ }
+
+ if ( !tags.empty() )
+ {
+ wxLogDebug("Missing closing tag for \"%s\"", tags.top().name);
+ return false;
+ }
+
+ if ( !current.empty() )
+ m_output.OnText(current);
+
+ return true;
+}
+
+/* static */
+wxString wxMarkupParser::Quote(const wxString& text)
+{
+ wxString quoted;
+ quoted.reserve(text.length());
+
+ for ( wxString::const_iterator it = text.begin(); it != text.end(); ++it )
+ {
+ unsigned n;
+ for ( n = 0; n < WXSIZEOF(xmlEntities); n++ )
+ {
+ const XMLEntity& xmlEnt = xmlEntities[n];
+ if ( *it == xmlEnt.value )
+ {
+ quoted << '&' << xmlEnt.name << ';';
+ break;
+ }
+ }
+
+ if ( n == WXSIZEOF(xmlEntities) )
+ quoted += *it;
+ }
+
+ return quoted;
+}
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 9c57cd3d22..1df92ac2d3 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -178,6 +178,7 @@ TEST_GUI_OBJECTS = \
test_gui_listboxtest.o \
test_gui_listctrltest.o \
test_gui_listviewtest.o \
+ test_gui_markuptest.o \
test_gui_notebooktest.o \
test_gui_ownerdrawncomboboxtest.o \
test_gui_pickerbasetest.o \
@@ -764,6 +765,9 @@ test_gui_listctrltest.o: $(srcdir)/controls/listctrltest.cpp $(TEST_GUI_ODEP)
test_gui_listviewtest.o: $(srcdir)/controls/listviewtest.cpp $(TEST_GUI_ODEP)
$(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/controls/listviewtest.cpp
+test_gui_markuptest.o: $(srcdir)/controls/markuptest.cpp $(TEST_GUI_ODEP)
+ $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/controls/markuptest.cpp
+
test_gui_notebooktest.o: $(srcdir)/controls/notebooktest.cpp $(TEST_GUI_ODEP)
$(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/controls/notebooktest.cpp
diff --git a/tests/controls/markuptest.cpp b/tests/controls/markuptest.cpp
new file mode 100644
index 0000000000..789598b946
--- /dev/null
+++ b/tests/controls/markuptest.cpp
@@ -0,0 +1,198 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name: tests/controls/markup.cpp
+// Purpose: wxMarkupParser and related classes unit tests
+// Author: Vadim Zeitlin
+// Created: 2011-02-17
+// RCS-ID: $Id$
+// Copyright: (c) 2011 Vadim Zeitlin
+///////////////////////////////////////////////////////////////////////////////
+
+#include "testprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#endif // WX_PRECOMP
+
+#include "wx/private/markupparser.h"
+
+class MarkupTestCase : public CppUnit::TestCase
+{
+public:
+ MarkupTestCase() { }
+
+private:
+ CPPUNIT_TEST_SUITE( MarkupTestCase );
+ CPPUNIT_TEST( RoundTrip );
+ CPPUNIT_TEST( Quote );
+ CPPUNIT_TEST_SUITE_END();
+
+ void RoundTrip();
+ void Quote();
+
+ wxDECLARE_NO_COPY_CLASS(MarkupTestCase);
+};
+
+// register in the unnamed registry so that these tests are run by default
+CPPUNIT_TEST_SUITE_REGISTRATION( MarkupTestCase );
+
+// also include in it's own registry so that these tests can be run alone
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MarkupTestCase, "MarkupTestCase" );
+
+void MarkupTestCase::RoundTrip()
+{
+ // Define a wxMarkupParserOutput object which produces the same markup
+ // string on output. This is, of course, perfectly useless, but allows us
+ // to test that parsing works as expected.
+ class RoundTripOutput : public wxMarkupParserOutput
+ {
+ public:
+ RoundTripOutput() { }
+
+ void Reset() { m_text.clear(); }
+
+ const wxString& GetText() const { return m_text; }
+
+
+ virtual void OnText(const wxString& text) { m_text += text; }
+
+ virtual void OnBoldStart() { m_text += ""; }
+ virtual void OnBoldEnd() { m_text += ""; }
+
+ virtual void OnItalicStart() { m_text += ""; }
+ virtual void OnItalicEnd() { m_text += ""; }
+
+ virtual void OnUnderlinedStart() { m_text += ""; }
+ virtual void OnUnderlinedEnd() { m_text += ""; }
+
+ virtual void OnStrikethroughStart() { m_text += ""; }
+ virtual void OnStrikethroughEnd() { m_text += ""; }
+
+ virtual void OnBigStart() { m_text += ""; }
+ virtual void OnBigEnd() { m_text += ""; }
+
+ virtual void OnSmallStart() { m_text += ""; }
+ virtual void OnSmallEnd() { m_text += ""; }
+
+ virtual void OnTeletypeStart() { m_text += ""; }
+ virtual void OnTeletypeEnd() { m_text += ""; }
+
+ virtual void OnSpanStart(const wxMarkupSpanAttributes& attrs)
+ {
+ m_text << " 0 ? "larger" : "smaller");
+ break;
+
+ case wxMarkupSpanAttributes::Size_Symbolic:
+ {
+ CPPUNIT_ASSERT( attrs.m_fontSize >= -3 &&
+ attrs.m_fontSize <= 3 );
+ static const char *cssSizes[] =
+ {
+ "xx-small", "x-small", "small",
+ "medium",
+ "large", "x-large", "xx-large",
+ };
+
+ size << cssSizes[attrs.m_fontSize + 3];
+ }
+ break;
+
+ case wxMarkupSpanAttributes::Size_PointParts:
+ size.Printf("%u", attrs.m_fontSize);
+ break;
+ }
+
+ if ( !size.empty() )
+ m_text << " size=\"" << size << '"';
+
+ // TODO: Handle the rest of attributes.
+
+ m_text << ">";
+ }
+
+ virtual void OnSpanEnd(const wxMarkupSpanAttributes& WXUNUSED(attrs))
+ {
+ m_text += "";
+ }
+
+ private:
+ wxString m_text;
+ };
+
+
+ RoundTripOutput output;
+ wxMarkupParser parser(output);
+
+ #define CHECK_PARSES_OK(text) \
+ output.Reset(); \
+ CPPUNIT_ASSERT( parser.Parse(text) ); \
+ CPPUNIT_ASSERT_EQUAL( text, output.GetText() )
+
+ #define CHECK_PARSES_AS(text, result) \
+ output.Reset(); \
+ CPPUNIT_ASSERT( parser.Parse(text) ); \
+ CPPUNIT_ASSERT_EQUAL( result, output.GetText() )
+
+ #define CHECK_DOESNT_PARSE(text) \
+ CPPUNIT_ASSERT( !parser.Parse(text) )
+
+ CHECK_PARSES_OK( "" );
+ CHECK_PARSES_OK( "foo" );
+ CHECK_PARSES_OK( "foobar" );
+ CHECK_PARSES_OK( "123456" );
+ CHECK_PARSES_OK( "first second last" );
+ CHECK_PARSES_OK( "first second last" );
+ CHECK_PARSES_OK( "10pt" );
+ CHECK_PARSES_OK( "much smaller" );
+ CHECK_PARSES_OK( "larger" );
+ CHECK_PARSES_OK
+ (
+ "Please notice: any "
+ "bugs in this code are NOT allowed."
+ );
+
+ CHECK_PARSES_OK( "foo&bar" );
+ CHECK_PARSES_AS( "foo&bar", "foo&&bar" );
+ CHECK_PARSES_AS( "<O'Reilly>", "" );
+
+ CHECK_DOESNT_PARSE( "<" );
+ CHECK_DOESNT_PARSE( "" );
+ CHECK_DOESNT_PARSE( "" );
+ CHECK_DOESNT_PARSE( "" );
+ CHECK_DOESNT_PARSE( "" );
+
+ #undef CHECK_PARSES_OK
+ #undef CHECK_DOESNT_PARSE
+}
+
+void MarkupTestCase::Quote()
+{
+ CPPUNIT_ASSERT_EQUAL( "", wxMarkupParser::Quote("") );
+ CPPUNIT_ASSERT_EQUAL( "foo", wxMarkupParser::Quote("foo") );
+ CPPUNIT_ASSERT_EQUAL( "<foo>", wxMarkupParser::Quote("") );
+ CPPUNIT_ASSERT_EQUAL( "B&B", wxMarkupParser::Quote("B&B") );
+ CPPUNIT_ASSERT_EQUAL( """", wxMarkupParser::Quote("\"\"") );
+}
diff --git a/tests/makefile.bcc b/tests/makefile.bcc
index f6a49b636b..2f07c1b1cd 100644
--- a/tests/makefile.bcc
+++ b/tests/makefile.bcc
@@ -163,6 +163,7 @@ TEST_GUI_OBJECTS = \
$(OBJS)\test_gui_listboxtest.obj \
$(OBJS)\test_gui_listctrltest.obj \
$(OBJS)\test_gui_listviewtest.obj \
+ $(OBJS)\test_gui_markuptest.obj \
$(OBJS)\test_gui_notebooktest.obj \
$(OBJS)\test_gui_ownerdrawncomboboxtest.obj \
$(OBJS)\test_gui_pickerbasetest.obj \
@@ -812,6 +813,9 @@ $(OBJS)\test_gui_listctrltest.obj: .\controls\listctrltest.cpp
$(OBJS)\test_gui_listviewtest.obj: .\controls\listviewtest.cpp
$(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\controls\listviewtest.cpp
+$(OBJS)\test_gui_markuptest.obj: .\controls\markuptest.cpp
+ $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\controls\markuptest.cpp
+
$(OBJS)\test_gui_notebooktest.obj: .\controls\notebooktest.cpp
$(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\controls\notebooktest.cpp
diff --git a/tests/makefile.gcc b/tests/makefile.gcc
index 52490dd62f..d8559c080b 100644
--- a/tests/makefile.gcc
+++ b/tests/makefile.gcc
@@ -156,6 +156,7 @@ TEST_GUI_OBJECTS = \
$(OBJS)\test_gui_listboxtest.o \
$(OBJS)\test_gui_listctrltest.o \
$(OBJS)\test_gui_listviewtest.o \
+ $(OBJS)\test_gui_markuptest.o \
$(OBJS)\test_gui_notebooktest.o \
$(OBJS)\test_gui_ownerdrawncomboboxtest.o \
$(OBJS)\test_gui_pickerbasetest.o \
@@ -793,6 +794,9 @@ $(OBJS)\test_gui_listctrltest.o: ./controls/listctrltest.cpp
$(OBJS)\test_gui_listviewtest.o: ./controls/listviewtest.cpp
$(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
+$(OBJS)\test_gui_markuptest.o: ./controls/markuptest.cpp
+ $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
+
$(OBJS)\test_gui_notebooktest.o: ./controls/notebooktest.cpp
$(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
diff --git a/tests/makefile.vc b/tests/makefile.vc
index 19f365c0ad..7d33b327e6 100644
--- a/tests/makefile.vc
+++ b/tests/makefile.vc
@@ -158,6 +158,7 @@ TEST_GUI_OBJECTS = \
$(OBJS)\test_gui_listboxtest.obj \
$(OBJS)\test_gui_listctrltest.obj \
$(OBJS)\test_gui_listviewtest.obj \
+ $(OBJS)\test_gui_markuptest.obj \
$(OBJS)\test_gui_notebooktest.obj \
$(OBJS)\test_gui_ownerdrawncomboboxtest.obj \
$(OBJS)\test_gui_pickerbasetest.obj \
@@ -938,6 +939,9 @@ $(OBJS)\test_gui_listctrltest.obj: .\controls\listctrltest.cpp
$(OBJS)\test_gui_listviewtest.obj: .\controls\listviewtest.cpp
$(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\controls\listviewtest.cpp
+$(OBJS)\test_gui_markuptest.obj: .\controls\markuptest.cpp
+ $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\controls\markuptest.cpp
+
$(OBJS)\test_gui_notebooktest.obj: .\controls\notebooktest.cpp
$(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\controls\notebooktest.cpp
diff --git a/tests/makefile.wat b/tests/makefile.wat
index 6500836f23..7c563fdaae 100644
--- a/tests/makefile.wat
+++ b/tests/makefile.wat
@@ -402,6 +402,7 @@ TEST_GUI_OBJECTS = &
$(OBJS)\test_gui_listboxtest.obj &
$(OBJS)\test_gui_listctrltest.obj &
$(OBJS)\test_gui_listviewtest.obj &
+ $(OBJS)\test_gui_markuptest.obj &
$(OBJS)\test_gui_notebooktest.obj &
$(OBJS)\test_gui_ownerdrawncomboboxtest.obj &
$(OBJS)\test_gui_pickerbasetest.obj &
@@ -852,6 +853,9 @@ $(OBJS)\test_gui_listctrltest.obj : .AUTODEPEND .\controls\listctrltest.cpp
$(OBJS)\test_gui_listviewtest.obj : .AUTODEPEND .\controls\listviewtest.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $<
+$(OBJS)\test_gui_markuptest.obj : .AUTODEPEND .\controls\markuptest.cpp
+ $(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $<
+
$(OBJS)\test_gui_notebooktest.obj : .AUTODEPEND .\controls\notebooktest.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_GUI_CXXFLAGS) $<
diff --git a/tests/test.bkl b/tests/test.bkl
index bea939d1ae..2f5d0aa81b 100644
--- a/tests/test.bkl
+++ b/tests/test.bkl
@@ -159,6 +159,7 @@
controls/listboxtest.cpp
controls/listctrltest.cpp
controls/listviewtest.cpp
+ controls/markuptest.cpp
controls/notebooktest.cpp
controls/ownerdrawncomboboxtest.cpp
controls/pickerbasetest.cpp
diff --git a/tests/test_test_gui.dsp b/tests/test_test_gui.dsp
index 382ecd8890..89461de8e3 100644
--- a/tests/test_test_gui.dsp
+++ b/tests/test_test_gui.dsp
@@ -393,6 +393,10 @@ SOURCE=.\controls\listviewtest.cpp
# End Source File
# Begin Source File
+SOURCE=.\controls\markuptest.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\graphics\measuring.cpp
# End Source File
# Begin Source File
diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj
index d2bd4c2d73..8694367444 100644
--- a/tests/test_vc7_test_gui.vcproj
+++ b/tests/test_vc7_test_gui.vcproj
@@ -731,6 +731,9 @@
RelativePath=".\controls\listviewtest.cpp">
+
+
+
+
diff --git a/tests/test_vc9_test_gui.vcproj b/tests/test_vc9_test_gui.vcproj
index c11080f593..a6b4a266a3 100644
--- a/tests/test_vc9_test_gui.vcproj
+++ b/tests/test_vc9_test_gui.vcproj
@@ -1020,6 +1020,10 @@
>
+
+