From d6a137b730d5d249e2fb99b5558ee6dd3a97ce56 Mon Sep 17 00:00:00 2001 From: jensgoe Date: Sun, 18 Nov 2018 22:48:28 +0100 Subject: [PATCH 01/16] Improve wxDataViewCtrl performance with wxDV_VARIABLE_LINE_HEIGHT Store the line heights in a cache to make the (generic) wxDataViewCtrl usable with this style. --- Makefile.in | 36 ++- build/bakefiles/files.bkl | 1 + build/cmake/files.cmake | 1 + build/files | 1 + build/msw/makefile.bcc | 44 ++- build/msw/makefile.gcc | 44 ++- build/msw/makefile.vc | 44 ++- build/msw/wx_core.vcxproj | 1 + build/msw/wx_core.vcxproj.filters | 3 + build/msw/wx_vc7_core.vcproj | 3 + build/msw/wx_vc8_core.vcproj | 4 + build/msw/wx_vc9_core.vcproj | 4 + docs/changes.txt | 1 + include/wx/generic/private/rowheightcache.h | 142 +++++++++ src/generic/datavgen.cpp | 163 +++++++--- src/generic/rowheightcache.cpp | 337 ++++++++++++++++++++ tests/Makefile.in | 4 + tests/makefile.bcc | 4 + tests/makefile.gcc | 4 + tests/makefile.vc | 4 + tests/rowheightcache/rowheightcachetest.cpp | 280 ++++++++++++++++ tests/test.bkl | 1 + tests/test_vc7_test_gui.vcproj | 3 + tests/test_vc8_test_gui.vcproj | 4 + tests/test_vc9_test_gui.vcproj | 4 + 25 files changed, 1068 insertions(+), 69 deletions(-) create mode 100644 include/wx/generic/private/rowheightcache.h create mode 100644 src/generic/rowheightcache.cpp create mode 100644 tests/rowheightcache/rowheightcachetest.cpp diff --git a/Makefile.in b/Makefile.in index e44bc57695..564c1011e7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4579,7 +4579,8 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS = \ monodll_datavgen.o \ monodll_editlbox.o \ monodll_laywin.o \ - monodll_calctrlg.o + monodll_calctrlg.o \ + monodll_rowheightcache.o @COND_USE_GUI_1_WXUNIV_0@__CORE_SRC_OBJECTS = $(COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS) COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS = \ $(__LOWLEVEL_SRC_OBJECTS_1) \ @@ -4839,7 +4840,8 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS = \ monodll_datavgen.o \ monodll_editlbox.o \ monodll_laywin.o \ - monodll_calctrlg.o + monodll_calctrlg.o \ + monodll_rowheightcache.o @COND_USE_GUI_1_WXUNIV_1@__CORE_SRC_OBJECTS = $(COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS) COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS = \ monodll_fontmgrcmn.o \ @@ -6555,7 +6557,8 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_1 = \ monolib_datavgen.o \ monolib_editlbox.o \ monolib_laywin.o \ - monolib_calctrlg.o + monolib_calctrlg.o \ + monolib_rowheightcache.o @COND_USE_GUI_1_WXUNIV_0@__CORE_SRC_OBJECTS_1 = $(COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_1) COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_1 = \ $(__LOWLEVEL_SRC_OBJECTS_3) \ @@ -6815,7 +6818,8 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_1 = \ monolib_datavgen.o \ monolib_editlbox.o \ monolib_laywin.o \ - monolib_calctrlg.o + monolib_calctrlg.o \ + monolib_rowheightcache.o @COND_USE_GUI_1_WXUNIV_1@__CORE_SRC_OBJECTS_1 = $(COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_1) COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_2 = \ monolib_fontmgrcmn.o \ @@ -8678,7 +8682,8 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_2 = \ coredll_datavgen.o \ coredll_editlbox.o \ coredll_laywin.o \ - coredll_calctrlg.o + coredll_calctrlg.o \ + coredll_rowheightcache.o @COND_USE_GUI_1_WXUNIV_0@__CORE_SRC_OBJECTS_2 = $(COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_2) COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_2 = \ $(__LOWLEVEL_SRC_OBJECTS_5) \ @@ -8938,7 +8943,8 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_2 = \ coredll_datavgen.o \ coredll_editlbox.o \ coredll_laywin.o \ - coredll_calctrlg.o + coredll_calctrlg.o \ + coredll_rowheightcache.o @COND_USE_GUI_1_WXUNIV_1@__CORE_SRC_OBJECTS_2 = $(COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_2) COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_4 = \ coredll_fontmgrcmn.o \ @@ -10396,7 +10402,8 @@ COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_3 = \ corelib_datavgen.o \ corelib_editlbox.o \ corelib_laywin.o \ - corelib_calctrlg.o + corelib_calctrlg.o \ + corelib_rowheightcache.o @COND_USE_GUI_1_WXUNIV_0@__CORE_SRC_OBJECTS_3 = $(COND_USE_GUI_1_WXUNIV_0___CORE_SRC_OBJECTS_3) COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_3 = \ $(__LOWLEVEL_SRC_OBJECTS_7) \ @@ -10656,7 +10663,8 @@ COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_3 = \ corelib_datavgen.o \ corelib_editlbox.o \ corelib_laywin.o \ - corelib_calctrlg.o + corelib_calctrlg.o \ + corelib_rowheightcache.o @COND_USE_GUI_1_WXUNIV_1@__CORE_SRC_OBJECTS_3 = $(COND_USE_GUI_1_WXUNIV_1___CORE_SRC_OBJECTS_3) COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_6 = \ corelib_fontmgrcmn.o \ @@ -20698,6 +20706,9 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP) @COND_USE_GUI_1@monodll_calctrlg.o: $(srcdir)/src/generic/calctrlg.cpp $(MONODLL_ODEP) @COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/generic/calctrlg.cpp +@COND_USE_GUI_1@monodll_rowheightcache.o: $(srcdir)/src/generic/rowheightcache.cpp $(MONODLL_ODEP) +@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/generic/rowheightcache.cpp + @COND_TOOLKIT_OSX_COCOA_USE_GUI_1@monodll_osx_cocoa_mediactrl.o: $(srcdir)/src/osx/cocoa/mediactrl.mm $(MONODLL_ODEP) @COND_TOOLKIT_OSX_COCOA_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_OBJCXXFLAGS) $(srcdir)/src/osx/cocoa/mediactrl.mm @@ -25951,6 +25962,9 @@ monolib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONOLIB_ODEP) @COND_USE_GUI_1@monolib_calctrlg.o: $(srcdir)/src/generic/calctrlg.cpp $(MONOLIB_ODEP) @COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/generic/calctrlg.cpp +@COND_USE_GUI_1@monolib_rowheightcache.o: $(srcdir)/src/generic/rowheightcache.cpp $(MONOLIB_ODEP) +@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/generic/rowheightcache.cpp + @COND_TOOLKIT_OSX_COCOA_USE_GUI_1@monolib_osx_cocoa_mediactrl.o: $(srcdir)/src/osx/cocoa/mediactrl.mm $(MONOLIB_ODEP) @COND_TOOLKIT_OSX_COCOA_USE_GUI_1@ $(CXXC) -c -o $@ $(MONOLIB_OBJCXXFLAGS) $(srcdir)/src/osx/cocoa/mediactrl.mm @@ -31297,6 +31311,9 @@ coredll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(COREDLL_ODEP) @COND_USE_GUI_1@coredll_calctrlg.o: $(srcdir)/src/generic/calctrlg.cpp $(COREDLL_ODEP) @COND_USE_GUI_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/generic/calctrlg.cpp +@COND_USE_GUI_1@coredll_rowheightcache.o: $(srcdir)/src/generic/rowheightcache.cpp $(COREDLL_ODEP) +@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/generic/rowheightcache.cpp + corelib_event.o: $(srcdir)/src/common/event.cpp $(CORELIB_ODEP) $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/common/event.cpp @@ -35545,6 +35562,9 @@ corelib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(CORELIB_ODEP) @COND_USE_GUI_1@corelib_calctrlg.o: $(srcdir)/src/generic/calctrlg.cpp $(CORELIB_ODEP) @COND_USE_GUI_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/generic/calctrlg.cpp +@COND_USE_GUI_1@corelib_rowheightcache.o: $(srcdir)/src/generic/rowheightcache.cpp $(CORELIB_ODEP) +@COND_USE_GUI_1@ $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/generic/rowheightcache.cpp + advdll_version_rc.o: $(srcdir)/src/msw/version.rc $(ADVDLL_ODEP) $(WINDRES) -i$< -o$@ --define __WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p_67) $(__DEBUG_DEFINE_p_66) $(__EXCEPTIONS_DEFINE_p_65) $(__RTTI_DEFINE_p_65) $(__THREAD_DEFINE_p_65) --define WXBUILDING --define WXDLLNAME=$(WXDLLNAMEPREFIXGUI)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_adv$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG) $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include $(__INC_TIFF_BUILD_p_66) $(__INC_TIFF_p_66) $(__INC_JPEG_p_66) $(__INC_PNG_p_65) $(__INC_ZLIB_p_67) $(__INC_REGEX_p_65) $(__INC_EXPAT_p_65) --define WXUSINGDLL --define WXMAKINGDLL_ADV diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index 3f84e21ae5..6001ccbd34 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -1005,6 +1005,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! src/generic/editlbox.cpp src/generic/laywin.cpp src/generic/calctrlg.cpp + src/generic/rowheightcache.cpp wx/affinematrix2dbase.h diff --git a/build/cmake/files.cmake b/build/cmake/files.cmake index 3111a384c2..c126d0f237 100644 --- a/build/cmake/files.cmake +++ b/build/cmake/files.cmake @@ -908,6 +908,7 @@ set(GUI_CMN_SRC src/generic/wizard.cpp src/generic/editlbox.cpp src/generic/datavgen.cpp + src/generic/rowheightcache.cpp ) set(GUI_CMN_HDR diff --git a/build/files b/build/files index d774633b94..13a542b9c9 100644 --- a/build/files +++ b/build/files @@ -899,6 +899,7 @@ GUI_CMN_SRC = src/generic/renderg.cpp src/generic/richmsgdlgg.cpp src/generic/richtooltipg.cpp + src/generic/rowheightcache.cpp src/generic/sashwin.cpp src/generic/scrlwing.cpp src/generic/selstore.cpp diff --git a/build/msw/makefile.bcc b/build/msw/makefile.bcc index 8112f30f89..ca37c2f67e 100644 --- a/build/msw/makefile.bcc +++ b/build/msw/makefile.bcc @@ -2126,7 +2126,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.obj \ $(OBJS)\monodll_editlbox.obj \ $(OBJS)\monodll_laywin.obj \ - $(OBJS)\monodll_calctrlg.obj + $(OBJS)\monodll_calctrlg.obj \ + $(OBJS)\monodll_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_OBJECTS = \ @@ -2451,7 +2452,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.obj \ $(OBJS)\monodll_editlbox.obj \ $(OBJS)\monodll_laywin.obj \ - $(OBJS)\monodll_calctrlg.obj + $(OBJS)\monodll_calctrlg.obj \ + $(OBJS)\monodll_rowheightcache.obj !endif !if "$(USE_STC)" == "1" ____MONOLIB_STC_SRC_FILENAMES_OBJECTS = \ @@ -2955,7 +2957,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.obj \ $(OBJS)\monolib_editlbox.obj \ $(OBJS)\monolib_laywin.obj \ - $(OBJS)\monolib_calctrlg.obj + $(OBJS)\monolib_calctrlg.obj \ + $(OBJS)\monolib_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_1_OBJECTS = \ @@ -3280,7 +3283,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.obj \ $(OBJS)\monolib_editlbox.obj \ $(OBJS)\monolib_laywin.obj \ - $(OBJS)\monolib_calctrlg.obj + $(OBJS)\monolib_calctrlg.obj \ + $(OBJS)\monolib_rowheightcache.obj !endif !if "$(USE_STC)" == "1" ____MONOLIB_STC_SRC_FILENAMES_1_OBJECTS = \ @@ -3659,7 +3663,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.obj \ $(OBJS)\coredll_editlbox.obj \ $(OBJS)\coredll_laywin.obj \ - $(OBJS)\coredll_calctrlg.obj + $(OBJS)\coredll_calctrlg.obj \ + $(OBJS)\coredll_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_2_OBJECTS = \ @@ -3984,7 +3989,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.obj \ $(OBJS)\coredll_editlbox.obj \ $(OBJS)\coredll_laywin.obj \ - $(OBJS)\coredll_calctrlg.obj + $(OBJS)\coredll_calctrlg.obj \ + $(OBJS)\coredll_rowheightcache.obj !endif !if "$(MONOLITHIC)" == "0" && "$(SHARED)" == "0" && "$(USE_GUI)" == "1" __corelib___depname = \ @@ -4329,7 +4335,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.obj \ $(OBJS)\corelib_editlbox.obj \ $(OBJS)\corelib_laywin.obj \ - $(OBJS)\corelib_calctrlg.obj + $(OBJS)\corelib_calctrlg.obj \ + $(OBJS)\corelib_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_3_OBJECTS = \ @@ -4654,7 +4661,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.obj \ $(OBJS)\corelib_editlbox.obj \ $(OBJS)\corelib_laywin.obj \ - $(OBJS)\corelib_calctrlg.obj + $(OBJS)\corelib_calctrlg.obj \ + $(OBJS)\corelib_rowheightcache.obj !endif !if "$(SHARED)" == "1" ____wxcore_namedll_DEP = $(__coredll___depname) @@ -8937,6 +8945,11 @@ $(OBJS)\monodll_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) -q -c -P -o$@ $(MONODLL_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\monodll_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) -q -c -P -o$@ $(MONODLL_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\monodll_version.res: ..\..\src\msw\version.rc brcc32 -32 -r -fo$@ -i$(BCCDIR)\include -d__WXMSW__ $(__WXUNIV_DEFINE_p_67) $(__DEBUG_DEFINE_p_66) $(__NDEBUG_DEFINE_p_65) $(__EXCEPTIONS_DEFINE_p_65) $(__RTTI_DEFINE_p_65) $(__THREAD_DEFINE_p_65) $(__UNICODE_DEFINE_p_67) -i$(SETUPHDIR) -i..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_4) -dWXBUILDING -dWXDLLNAME=wx$(PORTNAME)$(WXUNIVNAME)$(WX_VERSION_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)$(WXCOMPILER)$(VENDORTAG) -i$(BCCDIR)\include\windows\sdk -i..\..\src\tiff\libtiff -i..\..\src\jpeg -i..\..\src\png -i..\..\src\zlib -i..\..\src\regex -i..\..\src\expat\expat\lib -i..\..\src\stc\scintilla\include -i..\..\src\stc\scintilla\lexlib -i..\..\src\stc\scintilla\src -d__WX__ -dSCI_LEXER -dNO_CXX11_REGEX -dLINK_LEXERS -dwxUSE_BASE=1 -dWXMAKINGDLL ..\..\src\msw\version.rc @@ -11478,6 +11491,11 @@ $(OBJS)\monolib_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) -q -c -P -o$@ $(MONOLIB_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\monolib_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) -q -c -P -o$@ $(MONOLIB_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\basedll_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) -q -c -P -o$@ $(BASEDLL_CXXFLAGS) -H ..\..\src\common\dummy.cpp @@ -13983,6 +14001,11 @@ $(OBJS)\coredll_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) -q -c -P -o$@ $(COREDLL_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\coredll_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) -q -c -P -o$@ $(COREDLL_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\corelib_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) -q -c -P -o$@ $(CORELIB_CXXFLAGS) -H ..\..\src\common\dummy.cpp @@ -15711,6 +15734,11 @@ $(OBJS)\corelib_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) -q -c -P -o$@ $(CORELIB_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\corelib_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) -q -c -P -o$@ $(CORELIB_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\advdll_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) -q -c -P -o$@ $(ADVDLL_CXXFLAGS) -H ..\..\src\common\dummy.cpp diff --git a/build/msw/makefile.gcc b/build/msw/makefile.gcc index c6a2042c6b..6de7992c59 100644 --- a/build/msw/makefile.gcc +++ b/build/msw/makefile.gcc @@ -2152,7 +2152,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.o \ $(OBJS)\monodll_editlbox.o \ $(OBJS)\monodll_laywin.o \ - $(OBJS)\monodll_calctrlg.o + $(OBJS)\monodll_calctrlg.o \ + $(OBJS)\monodll_rowheightcache.o endif endif ifeq ($(USE_GUI),1) @@ -2479,7 +2480,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.o \ $(OBJS)\monodll_editlbox.o \ $(OBJS)\monodll_laywin.o \ - $(OBJS)\monodll_calctrlg.o + $(OBJS)\monodll_calctrlg.o \ + $(OBJS)\monodll_rowheightcache.o endif endif ifeq ($(USE_STC),1) @@ -2987,7 +2989,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.o \ $(OBJS)\monolib_editlbox.o \ $(OBJS)\monolib_laywin.o \ - $(OBJS)\monolib_calctrlg.o + $(OBJS)\monolib_calctrlg.o \ + $(OBJS)\monolib_rowheightcache.o endif endif ifeq ($(USE_GUI),1) @@ -3314,7 +3317,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.o \ $(OBJS)\monolib_editlbox.o \ $(OBJS)\monolib_laywin.o \ - $(OBJS)\monolib_calctrlg.o + $(OBJS)\monolib_calctrlg.o \ + $(OBJS)\monolib_rowheightcache.o endif endif ifeq ($(USE_STC),1) @@ -3707,7 +3711,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.o \ $(OBJS)\coredll_editlbox.o \ $(OBJS)\coredll_laywin.o \ - $(OBJS)\coredll_calctrlg.o + $(OBJS)\coredll_calctrlg.o \ + $(OBJS)\coredll_rowheightcache.o endif endif ifeq ($(USE_GUI),1) @@ -4034,7 +4039,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.o \ $(OBJS)\coredll_editlbox.o \ $(OBJS)\coredll_laywin.o \ - $(OBJS)\coredll_calctrlg.o + $(OBJS)\coredll_calctrlg.o \ + $(OBJS)\coredll_rowheightcache.o endif endif ifeq ($(MONOLITHIC),0) @@ -4385,7 +4391,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.o \ $(OBJS)\corelib_editlbox.o \ $(OBJS)\corelib_laywin.o \ - $(OBJS)\corelib_calctrlg.o + $(OBJS)\corelib_calctrlg.o \ + $(OBJS)\corelib_rowheightcache.o endif endif ifeq ($(USE_GUI),1) @@ -4712,7 +4719,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.o \ $(OBJS)\corelib_editlbox.o \ $(OBJS)\corelib_laywin.o \ - $(OBJS)\corelib_calctrlg.o + $(OBJS)\corelib_calctrlg.o \ + $(OBJS)\corelib_rowheightcache.o endif endif ifeq ($(SHARED),1) @@ -9119,6 +9127,11 @@ $(OBJS)\monodll_calctrlg.o: ../../src/generic/calctrlg.cpp $(CXX) -c -o $@ $(MONODLL_CXXFLAGS) $(CPPDEPS) $< endif +ifeq ($(USE_GUI),1) +$(OBJS)\monodll_rowheightcache.o: ../../src/generic/rowheightcache.cpp + $(CXX) -c -o $@ $(MONODLL_CXXFLAGS) $(CPPDEPS) $< +endif + $(OBJS)\monodll_version_rc.o: ../../src/msw/version.rc $(WINDRES) -i$< -o$@ --define __WXMSW__ $(__WXUNIV_DEFINE_p_67) $(__DEBUG_DEFINE_p_66) $(__NDEBUG_DEFINE_p_65) $(__EXCEPTIONS_DEFINE_p_65) $(__RTTI_DEFINE_p_65) $(__THREAD_DEFINE_p_65) $(__UNICODE_DEFINE_p_67) --include-dir $(SETUPHDIR) --include-dir ../../include $(__CAIRO_INCLUDEDIR_p) --define WXBUILDING --define WXDLLNAME=wx$(PORTNAME)$(WXUNIVNAME)$(WX_VERSION_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)$(WXCOMPILER)$(VENDORTAG) --include-dir ../../src/tiff/libtiff --include-dir ../../src/jpeg --include-dir ../../src/png --include-dir ../../src/zlib --include-dir ../../src/regex --include-dir ../../src/expat/expat/lib --include-dir ../../src/stc/scintilla/include --include-dir ../../src/stc/scintilla/lexlib --include-dir ../../src/stc/scintilla/src --define __WX__ --define SCI_LEXER --define NO_CXX11_REGEX --define LINK_LEXERS --define wxUSE_BASE=1 --define WXMAKINGDLL @@ -11660,6 +11673,11 @@ $(OBJS)\monolib_calctrlg.o: ../../src/generic/calctrlg.cpp $(CXX) -c -o $@ $(MONOLIB_CXXFLAGS) $(CPPDEPS) $< endif +ifeq ($(USE_GUI),1) +$(OBJS)\monolib_rowheightcache.o: ../../src/generic/rowheightcache.cpp + $(CXX) -c -o $@ $(MONOLIB_CXXFLAGS) $(CPPDEPS) $< +endif + $(OBJS)\basedll_dummy.o: ../../src/common/dummy.cpp $(CXX) -c -o $@ $(BASEDLL_CXXFLAGS) $(CPPDEPS) $< @@ -14165,6 +14183,11 @@ $(OBJS)\coredll_calctrlg.o: ../../src/generic/calctrlg.cpp $(CXX) -c -o $@ $(COREDLL_CXXFLAGS) $(CPPDEPS) $< endif +ifeq ($(USE_GUI),1) +$(OBJS)\coredll_rowheightcache.o: ../../src/generic/rowheightcache.cpp + $(CXX) -c -o $@ $(COREDLL_CXXFLAGS) $(CPPDEPS) $< +endif + $(OBJS)\corelib_dummy.o: ../../src/common/dummy.cpp $(CXX) -c -o $@ $(CORELIB_CXXFLAGS) $(CPPDEPS) $< @@ -15893,6 +15916,11 @@ $(OBJS)\corelib_calctrlg.o: ../../src/generic/calctrlg.cpp $(CXX) -c -o $@ $(CORELIB_CXXFLAGS) $(CPPDEPS) $< endif +ifeq ($(USE_GUI),1) +$(OBJS)\corelib_rowheightcache.o: ../../src/generic/rowheightcache.cpp + $(CXX) -c -o $@ $(CORELIB_CXXFLAGS) $(CPPDEPS) $< +endif + $(OBJS)\advdll_dummy.o: ../../src/common/dummy.cpp $(CXX) -c -o $@ $(ADVDLL_CXXFLAGS) $(CPPDEPS) $< diff --git a/build/msw/makefile.vc b/build/msw/makefile.vc index c1ce7f10bd..13d3cc7abc 100644 --- a/build/msw/makefile.vc +++ b/build/msw/makefile.vc @@ -2443,7 +2443,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.obj \ $(OBJS)\monodll_editlbox.obj \ $(OBJS)\monodll_laywin.obj \ - $(OBJS)\monodll_calctrlg.obj + $(OBJS)\monodll_calctrlg.obj \ + $(OBJS)\monodll_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_OBJECTS = \ @@ -2768,7 +2769,8 @@ ____CORE_SRC_FILENAMES_OBJECTS = \ $(OBJS)\monodll_datavgen.obj \ $(OBJS)\monodll_editlbox.obj \ $(OBJS)\monodll_laywin.obj \ - $(OBJS)\monodll_calctrlg.obj + $(OBJS)\monodll_calctrlg.obj \ + $(OBJS)\monodll_rowheightcache.obj !endif !if "$(USE_STC)" == "1" ____MONOLIB_STC_SRC_FILENAMES_OBJECTS = \ @@ -3278,7 +3280,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.obj \ $(OBJS)\monolib_editlbox.obj \ $(OBJS)\monolib_laywin.obj \ - $(OBJS)\monolib_calctrlg.obj + $(OBJS)\monolib_calctrlg.obj \ + $(OBJS)\monolib_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_1_OBJECTS = \ @@ -3603,7 +3606,8 @@ ____CORE_SRC_FILENAMES_1_OBJECTS = \ $(OBJS)\monolib_datavgen.obj \ $(OBJS)\monolib_editlbox.obj \ $(OBJS)\monolib_laywin.obj \ - $(OBJS)\monolib_calctrlg.obj + $(OBJS)\monolib_calctrlg.obj \ + $(OBJS)\monolib_rowheightcache.obj !endif !if "$(USE_STC)" == "1" ____MONOLIB_STC_SRC_FILENAMES_1_OBJECTS = \ @@ -4048,7 +4052,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.obj \ $(OBJS)\coredll_editlbox.obj \ $(OBJS)\coredll_laywin.obj \ - $(OBJS)\coredll_calctrlg.obj + $(OBJS)\coredll_calctrlg.obj \ + $(OBJS)\coredll_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_2_OBJECTS = \ @@ -4373,7 +4378,8 @@ ____CORE_SRC_FILENAMES_2_OBJECTS = \ $(OBJS)\coredll_datavgen.obj \ $(OBJS)\coredll_editlbox.obj \ $(OBJS)\coredll_laywin.obj \ - $(OBJS)\coredll_calctrlg.obj + $(OBJS)\coredll_calctrlg.obj \ + $(OBJS)\coredll_rowheightcache.obj !endif !if "$(MONOLITHIC)" == "0" && "$(SHARED)" == "0" && "$(USE_GUI)" == "1" __corelib___depname = \ @@ -4724,7 +4730,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.obj \ $(OBJS)\corelib_editlbox.obj \ $(OBJS)\corelib_laywin.obj \ - $(OBJS)\corelib_calctrlg.obj + $(OBJS)\corelib_calctrlg.obj \ + $(OBJS)\corelib_rowheightcache.obj !endif !if "$(USE_GUI)" == "1" && "$(WXUNIV)" == "1" ____CORE_SRC_FILENAMES_3_OBJECTS = \ @@ -5049,7 +5056,8 @@ ____CORE_SRC_FILENAMES_3_OBJECTS = \ $(OBJS)\corelib_datavgen.obj \ $(OBJS)\corelib_editlbox.obj \ $(OBJS)\corelib_laywin.obj \ - $(OBJS)\corelib_calctrlg.obj + $(OBJS)\corelib_calctrlg.obj \ + $(OBJS)\corelib_rowheightcache.obj !endif !if "$(SHARED)" == "1" ____wxcore_namedll_DEP = $(__coredll___depname) @@ -9646,6 +9654,11 @@ $(OBJS)\monodll_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) /c /nologo /TP /Fo$@ $(MONODLL_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\monodll_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) /c /nologo /TP /Fo$@ $(MONODLL_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\monodll_version.res: ..\..\src\msw\version.rc rc /fo$@ /d WIN32 $(____DEBUGRUNTIME_6) /d _CRT_SECURE_NO_DEPRECATE=1 /d _CRT_NON_CONFORMING_SWPRINTFS=1 /d _SCL_SECURE_NO_WARNINGS=1 $(__NO_VC_CRTDBG_p_72) /d __WXMSW__ $(__WXUNIV_DEFINE_p_67) $(__DEBUG_DEFINE_p_66) $(__NDEBUG_DEFINE_p_65) $(__EXCEPTIONS_DEFINE_p_65) $(__RTTI_DEFINE_p_65) $(__THREAD_DEFINE_p_65) $(__UNICODE_DEFINE_p_67) /i $(SETUPHDIR) /i ..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_4) /d WXBUILDING /d WXDLLNAME=wx$(PORTNAME)$(WXUNIVNAME)$(WX_VERSION_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)$(WXCOMPILER)$(VENDORTAG) /i ..\..\src\tiff\libtiff /i ..\..\src\jpeg /i ..\..\src\png /i ..\..\src\zlib /i ..\..\src\regex /i ..\..\src\expat\expat\lib /i ..\..\src\stc\scintilla\include /i ..\..\src\stc\scintilla\lexlib /i ..\..\src\stc\scintilla\src /d __WX__ /d SCI_LEXER /d NO_CXX11_REGEX /d LINK_LEXERS /d wxUSE_BASE=1 /d WXMAKINGDLL ..\..\src\msw\version.rc @@ -12187,6 +12200,11 @@ $(OBJS)\monolib_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) /c /nologo /TP /Fo$@ $(MONOLIB_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\monolib_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) /c /nologo /TP /Fo$@ $(MONOLIB_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\basedll_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) /c /nologo /TP /Fo$@ $(BASEDLL_CXXFLAGS) /Ycwx/wxprec.h ..\..\src\common\dummy.cpp @@ -14692,6 +14710,11 @@ $(OBJS)\coredll_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) /c /nologo /TP /Fo$@ $(COREDLL_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\coredll_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) /c /nologo /TP /Fo$@ $(COREDLL_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\corelib_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) /c /nologo /TP /Fo$@ $(CORELIB_CXXFLAGS) /Ycwx/wxprec.h ..\..\src\common\dummy.cpp @@ -16420,6 +16443,11 @@ $(OBJS)\corelib_calctrlg.obj: ..\..\src\generic\calctrlg.cpp $(CXX) /c /nologo /TP /Fo$@ $(CORELIB_CXXFLAGS) ..\..\src\generic\calctrlg.cpp !endif +!if "$(USE_GUI)" == "1" +$(OBJS)\corelib_rowheightcache.obj: ..\..\src\generic\rowheightcache.cpp + $(CXX) /c /nologo /TP /Fo$@ $(CORELIB_CXXFLAGS) ..\..\src\generic\rowheightcache.cpp +!endif + $(OBJS)\advdll_dummy.obj: ..\..\src\common\dummy.cpp $(CXX) /c /nologo /TP /Fo$@ $(ADVDLL_CXXFLAGS) /Ycwx/wxprec.h ..\..\src\common\dummy.cpp diff --git a/build/msw/wx_core.vcxproj b/build/msw/wx_core.vcxproj index bf3ea86b5a..87c60a35e1 100644 --- a/build/msw/wx_core.vcxproj +++ b/build/msw/wx_core.vcxproj @@ -1061,6 +1061,7 @@ + diff --git a/build/msw/wx_core.vcxproj.filters b/build/msw/wx_core.vcxproj.filters index 7e17bd7ad0..83c265118a 100644 --- a/build/msw/wx_core.vcxproj.filters +++ b/build/msw/wx_core.vcxproj.filters @@ -603,6 +603,9 @@ Generic Sources + + Generic Sources + Generic Sources diff --git a/build/msw/wx_vc7_core.vcproj b/build/msw/wx_vc7_core.vcproj index f64feb56e0..abd7d4a0ad 100644 --- a/build/msw/wx_vc7_core.vcproj +++ b/build/msw/wx_vc7_core.vcproj @@ -1286,6 +1286,9 @@ + + diff --git a/build/msw/wx_vc8_core.vcproj b/build/msw/wx_vc8_core.vcproj index b734aa02bd..c2f829be68 100644 --- a/build/msw/wx_vc8_core.vcproj +++ b/build/msw/wx_vc8_core.vcproj @@ -2127,6 +2127,10 @@ RelativePath="..\..\src\generic\richtooltipg.cpp" > + + diff --git a/build/msw/wx_vc9_core.vcproj b/build/msw/wx_vc9_core.vcproj index 0b33c64c19..6f8f12e632 100644 --- a/build/msw/wx_vc9_core.vcproj +++ b/build/msw/wx_vc9_core.vcproj @@ -2123,6 +2123,10 @@ RelativePath="..\..\src\generic\richtooltipg.cpp" > + + diff --git a/docs/changes.txt b/docs/changes.txt index 4d09c8f13b..1bda884cde 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -144,6 +144,7 @@ All (GUI): - Adapt AUI colours to system colour changes (Daniel Kulp). - Fix removing and inserting pages in wxToolbook (Stefan Ziegler). - Fix bug in template selection in docview framework (jwiesemann). +- Improve wxDataViewCtrl performance with variable line heights (Jens Goepfert). wxGTK: diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h new file mode 100644 index 0000000000..d9586f16e7 --- /dev/null +++ b/include/wx/generic/private/rowheightcache.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/rowheightcache.h +// Purpose: height cache of rows in a dataview +// Author: Jens Goepfert (mail@jensgoepfert.de) +// Created: 2018-03-06 +// Copyright: (c) wxWidgets team +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_ROWHEIGHTCACHE_H_ +#define _WX_PRIVATE_ROWHEIGHTCACHE_H_ + +#include "wx/hashmap.h" +#include "wx/vector.h" + +// struct describing a range of rows which contains rows .. +struct RowRange +{ + unsigned int from; + unsigned int to; +}; + +/** +@class RowRanges +A helper class that manages a set of RowRange objects. +It stores the indices that are members of a group in a memory +efficiant way. +*/ +class WXDLLIMPEXP_CORE RowRanges +//class RowRanges +{ +public: + /** + Adds a row index to this group by adding it to an existing RowRange + or by creating a new one. + */ + void Add(unsigned int row); + + /** + Removes a row index and all indices after idx from this group. + */ + void Remove(unsigned int row); + + /** + Checks whether a row index is contained in this group. + */ + bool Has(unsigned int row) const; + + /** + Returns the number of row indices that are contained in this group. + */ + unsigned int CountAll() const; + + /** + Returns the number of rows that are in this group before the given row index. + not including given row. + */ + unsigned int CountTo(unsigned int row) const; + unsigned int GetSize() const; // for debugging statistics + +private: + wxVector m_ranges; + /** + If a new row index was inserted Cleanup checks if the neighbour ranges + of idx can includes the same row indices and discards + unnecessary RowRange objects. + */ + void CleanUp(unsigned int idx); +}; + +WX_DECLARE_HASH_MAP(unsigned int, RowRanges*, wxIntegerHash, wxIntegerEqual, + HeightToRowRangesMap); + +/** +@class HeightCache + +HeightCache implements a cache mechanism for the DataViewCtrl to give +fast access to: +* the height of one line (GetLineHeight) +* the y-coordinate where a row starts (GetLineStart) +* and vice versa (GetLineAt) + +The layout of the cache is a hashmap where the keys are all exisiting +row heights in pixels. The values are RowRange objects that represents +all having the specified height. + +{ +22: RowRange([0..10], [15..17], [20..2000]), +42: RowRange([11..12], [18..18]), +62: RowRange([13..14], [19..19]) +} + +Examples +======== + +GetLineStart +------------ +To retrieve the y-coordinate of item 1000 it is neccessary to look into +each key of the hashmap *m_heightToRowRange*. Get the row count of +indices lower than 1000 (RowRange::CountTo) and multiplies it wich the +according height. + +RowRange([0..10], [15..17], [20..2000]).CountTo(1000) +--> 0..10 are 11 items, 15..17 are 3 items and 20..1000 are 980 items (1000-20) += 11 + 3 + 980 = 994 items + +GetLineStart(1000) --> (22 * 994) + (42 * 3) + (62 * 3) = 22180 + +GetLineHeight +------------- +To retrieve the line height look into each key and check if row is +contained in RowRange (RowRange::Has) + +GetLineAt +--------- +To retrieve the row that starts at a specific y-coordinate. +Look into each key and count all rows. +Use bisect algorithm in combination with GetLineStart() to +find the appropriate item +*/ +class WXDLLIMPEXP_ADV HeightCache +{ +public: + bool GetLineStart(unsigned int row, int &start); + bool GetLineHeight(unsigned int row, int &height); + bool GetLineAt(int y, unsigned int &row); + + void Put(const unsigned int row, const int height); + /** + removes the stored height of the given row from the cache + and invalidates all cached rows (including row) + */ + void Remove(const unsigned int row); + void Clear(); + +private: + bool GetLineInfo(unsigned int row, int &start, int &height); + HeightToRowRangesMap m_heightToRowRange; +}; + + +#endif // _WX_PRIVATE_ROWHEIGHTCACHE_H_ diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 448a58bf5b..9674e53d5b 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -53,6 +53,7 @@ #include "wx/stopwatch.h" #include "wx/weakref.h" #include "wx/generic/private/markuptext.h" +#include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" @@ -910,6 +911,7 @@ private: bool m_hasFocus; bool m_useCellFocus; bool m_currentColSetByKeyboard; + HeightCache *m_rowHeightCache; #if wxUSE_DRAG_AND_DROP int m_dragCount; @@ -1946,6 +1948,14 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i m_useCellFocus = false; m_currentRow = (unsigned)-1; m_lineHeight = GetDefaultRowHeight(); + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + { + m_rowHeightCache = new HeightCache(); + } + else + { + m_rowHeightCache = NULL; + } #if wxUSE_DRAG_AND_DROP m_dragCount = 0; @@ -1982,6 +1992,11 @@ wxDataViewMainWindow::~wxDataViewMainWindow() { DestroyTree(); delete m_renameTimer; + if (m_rowHeightCache != NULL) + { + m_rowHeightCache->Clear(); + delete m_rowHeightCache; + } } @@ -2739,6 +2754,12 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData } else { + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + { + // specific position (row) is unclear, so clear whole height cache + m_rowHeightCache->Clear(); + } + wxDataViewTreeNode *parentNode = FindNode(parent); if ( !parentNode ) @@ -2880,6 +2901,9 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent, return true; } + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + m_rowHeightCache->Remove(GetRowByItem(parent) + itemPosInNode); + // Delete the item from wxDataViewTreeNode representation: const int itemsDeleted = 1 + itemNode->GetSubTreeCount(); @@ -2944,6 +2968,9 @@ bool wxDataViewMainWindow::DoItemChanged(const wxDataViewItem & item, int view_c { if ( !IsVirtualList() ) { + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + m_rowHeightCache->Remove(GetRowByItem(item)); + // Move this node to its new correct place after it was updated. // // In principle, we could skip the call to PutInSortOrder() if the modified @@ -3277,6 +3304,9 @@ void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to ) { + if (from == to && from == ((unsigned)-1)) + return; + wxRect rect = GetLinesRect(from, to); m_owner->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y); @@ -3325,21 +3355,35 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { - // TODO make more efficient - int start = 0; + if (m_rowHeightCache->GetLineStart(row, start)) + return start; unsigned int r; for (r = 0; r < row; r++) { - const wxDataViewTreeNode* node = GetTreeNodeByRow(r); - if (!node) return start; + int height = 0; + if (m_rowHeightCache->GetLineHeight(r, height)) + { + start += height; + continue; + } - wxDataViewItem item = node->GetItem(); + wxDataViewItem item; + if (IsList()) + { + item = GetItemByRow(r); + } + else + { + const wxDataViewTreeNode* node = GetTreeNodeByRow(r); + if (!node) return start; + item = node->GetItem(); + } unsigned int cols = GetOwner()->GetColumnCount(); unsigned int col; - int height = m_lineHeight; + height = m_lineHeight; for (col = 0; col < cols; col++) { const wxDataViewColumn *column = GetOwner()->GetColumn(col); @@ -3359,6 +3403,8 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const } start += height; + + m_rowHeightCache->Put(r, height); } return start; @@ -3377,39 +3423,58 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) return y / m_lineHeight; - // TODO make more efficient unsigned int row = 0; unsigned int yy = 0; + + if (m_rowHeightCache->GetLineAt(y, row)) + return row; + for (;;) { - const wxDataViewTreeNode* node = GetTreeNodeByRow(row); - if (!node) + int height = 0; + if (!m_rowHeightCache->GetLineHeight(row, height)) { - // not really correct... - return row + ((y-yy) / m_lineHeight); - } + // row height not in cache -> get it from the renderer... - wxDataViewItem item = node->GetItem(); + wxDataViewItem item; + if (IsList()) + { + item = GetItemByRow(row); + } + else + { + const wxDataViewTreeNode* node = GetTreeNodeByRow(row); + if (!node) + { + // not really correct... + return row + ((y - yy) / m_lineHeight); + } + item = node->GetItem(); + } - unsigned int cols = GetOwner()->GetColumnCount(); - unsigned int col; - int height = m_lineHeight; - for (col = 0; col < cols; col++) - { - const wxDataViewColumn *column = GetOwner()->GetColumn(col); - if (column->IsHidden()) - continue; // skip it! + unsigned int cols = GetOwner()->GetColumnCount(); + unsigned int col; + height = m_lineHeight; + for (col = 0; col < cols; col++) + { + const wxDataViewColumn *column = GetOwner()->GetColumn(col); + if (column->IsHidden()) + continue; // skip it! - if ((col != 0) && - model->IsContainer(item) && - !model->HasContainerColumns(item)) - continue; // skip it! + if ((col != 0) && + model->IsContainer(item) && + !model->HasContainerColumns(item)) + continue; // skip it! - wxDataViewRenderer *renderer = - const_cast(column->GetRenderer()); - renderer->PrepareForItem(model, item, column->GetModelColumn()); + wxDataViewRenderer *renderer = + const_cast(column->GetRenderer()); + renderer->PrepareForItem(model, item, column->GetModelColumn()); - height = wxMax( height, renderer->GetSize().y ); + height = wxMax( height, renderer->GetSize().y ); + } + + // ... and store the height in the cache + m_rowHeightCache->Put(row, height); } yy += height; @@ -3426,16 +3491,24 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { - wxASSERT( !IsVirtualList() ); + int height = 0; + if (m_rowHeightCache->GetLineHeight(row, height)) + return height; - const wxDataViewTreeNode* node = GetTreeNodeByRow(row); - // wxASSERT( node ); - if (!node) return m_lineHeight; - - wxDataViewItem item = node->GetItem(); - - int height = m_lineHeight; + wxDataViewItem item; + if (IsList()) + { + item = GetItemByRow(row); + } + else + { + const wxDataViewTreeNode* node = GetTreeNodeByRow(row); + // wxASSERT( node ); + if (!node) return m_lineHeight; + item = node->GetItem(); + } + height = m_lineHeight; unsigned int cols = GetOwner()->GetColumnCount(); unsigned int col; for (col = 0; col < cols; col++) @@ -3456,6 +3529,8 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const height = wxMax( height, renderer->GetSize().y ); } + m_rowHeightCache->Put(row, height); + return height; } else @@ -3602,6 +3677,13 @@ void wxDataViewMainWindow::Expand( unsigned int row ) if (!node->HasChildren()) return; + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + { + // Expand makes new rows visible thus we invalidates all following + // rows in the height cache + m_rowHeightCache->Remove(row); + } + if (!node->IsOpen()) { if ( !SendExpanderEvent(wxEVT_DATAVIEW_ITEM_EXPANDING, node->GetItem()) ) @@ -3651,6 +3733,13 @@ void wxDataViewMainWindow::Collapse(unsigned int row) if (!node->HasChildren()) return; + if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + { + // Collapse hides rows thus we invalidates all following + // rows in the height cache + m_rowHeightCache->Remove(row); + } + if (node->IsOpen()) { if ( !SendExpanderEvent(wxEVT_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) ) diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp new file mode 100644 index 0000000000..f3c43d5aa1 --- /dev/null +++ b/src/generic/rowheightcache.cpp @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/rowheightcache.h +// Purpose: height cache of rows in a dataview +// Author: Jens Goepfert (mail@jensgoepfert.de) +// Created: 2018-03-06 +// Copyright: (c) wxWidgets team +// 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 + #include "wx/log.h" +#endif // WX_PRECOMP + +#include "wx/generic/private/rowheightcache.h" + +// ---------------------------------------------------------------------------- +// private structs +// ---------------------------------------------------------------------------- + +// ============================================================================ +// implementation +// ============================================================================ + + +// ---------------------------------------------------------------------------- +// RowRanges +// ---------------------------------------------------------------------------- + +void RowRanges::Add(const unsigned int row) +{ + size_t count = m_ranges.size(); + size_t rngIdx = 0; + for (rngIdx = 0; rngIdx < count; ++rngIdx) + { + RowRange &rng = m_ranges[rngIdx]; + + if (row >= rng.from && rng.to > row) + { + // index already in range + return; + } + + if (row == rng.from - 1) + { + // extend range at the beginning (to the left) + rng.from = row; + // no cleanup necessary + return; + } + if (row == rng.to) + { + // extend range at the end (set to row+1 because 'to' is not including) + rng.to = row + 1; + CleanUp(rngIdx); + return; + } + + if (rng.from > row + 1) + { + // this range is already behind row index, so break here and insert a new range before + break; + } + } + // wxLogMessage("New Range: %d" , count); + + RowRange newRange; + newRange.from = row; + newRange.to = row + 1; + m_ranges.insert(m_ranges.begin() + rngIdx, newRange); +} + +void RowRanges::Remove(const unsigned int row) +{ + size_t count = m_ranges.size(); + size_t rngIdx = 0; + while (rngIdx < count) + { + RowRange &rng = m_ranges[rngIdx]; + if (rng.from >= row) + { + // this range starts behind row index, so remove it + m_ranges.erase(m_ranges.begin() + rngIdx); + count--; + continue; + } + if (rng.to > row) + { + // this ranges includes row, so cut off at row index to exclude row + rng.to = row; + } + + rngIdx += 1; + } +} + + +void RowRanges::CleanUp(unsigned int idx) +{ + size_t count = m_ranges.size(); + size_t rngIdx = 0; + if (idx > 0) + { + // start one RowRange before + rngIdx = idx - 1; + } + if (idx >= count) + { + // should never reached, due CleanUp is private and internal called correctly + return; + } + RowRange *prevRng = &m_ranges[rngIdx]; + rngIdx++; + while (rngIdx <= idx + 1 && rngIdx < count) + { + RowRange &rng = m_ranges[rngIdx]; + + if (prevRng->to == rng.from) + { + // this range starts where the previous range began, so remove this + // and set the to-value of the previous range to the to-value of this range + prevRng->to = rng.to; + m_ranges.erase(m_ranges.begin() + rngIdx); + count--; + continue; + } + + prevRng = &rng; + rngIdx += 1; + } +} + +bool RowRanges::Has(unsigned int row) const +{ + size_t count = m_ranges.size(); + for (size_t rngIdx = 0; rngIdx < count; rngIdx++) + { + const RowRange &rng = m_ranges[rngIdx]; + if (rng.from <= row && row < rng.to) + { + return true; + } + } + return false; +} + +unsigned int RowRanges::CountAll() const +{ + unsigned int ctr = 0; + size_t count = m_ranges.size(); + for (size_t rngIdx = 0; rngIdx < count; rngIdx++) + { + const RowRange &rng = m_ranges[rngIdx]; + ctr += rng.to - rng.from; + } + return ctr; +} + +unsigned int RowRanges::CountTo(unsigned int row) const +{ + unsigned int ctr = 0; + size_t count = m_ranges.size(); + for (size_t rngIdx = 0; rngIdx < count; rngIdx++) + { + const RowRange &rng = m_ranges[rngIdx]; + if (rng.from > row) + { + break; + } + else if (rng.to < row) + { + ctr += rng.to - rng.from; + } + else + { + ctr += row - rng.from; + break; + } + } + return ctr; +} + +unsigned int RowRanges::GetSize() const // for debugging statistics +{ + return m_ranges.size(); +} + + +// ---------------------------------------------------------------------------- +// HeightCache +// ---------------------------------------------------------------------------- + +bool HeightCache::GetLineInfo(unsigned int row, int &start, int &height) +{ + int y = 0; + bool found = false; + HeightToRowRangesMap::iterator it; + for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) + { + int rowHeight = it->first; + RowRanges* rowRanges = it->second; + if (rowRanges->Has(row)) + { + height = rowHeight; + found = true; + } + y += rowHeight * (rowRanges->CountTo(row)); + } + if (found) + { + start = y; + } + return found; +} + +bool HeightCache::GetLineStart(unsigned int row, int &start) +{ + int height = 0; + return GetLineInfo(row, start, height); +} + +bool HeightCache::GetLineHeight(unsigned int row, int &height) +{ + HeightToRowRangesMap::iterator it; + for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) + { + int rowHeight = it->first; + RowRanges* rowRanges = it->second; + if (rowRanges->Has(row)) + { + height = rowHeight; + return true; + } + } + return false; +} + +bool HeightCache::GetLineAt(int y, unsigned int &row) +{ + unsigned int total = 0; + HeightToRowRangesMap::iterator it; + for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) + { + RowRanges* rowRanges = it->second; + total += rowRanges->CountAll(); + } + + if (total == 0) + { + return false; + } + + int lo = 0; + int hi = total; + int start, height; + while (lo < hi) + { + int mid = (lo + hi) / 2; + if (GetLineInfo(mid, start, height)) + { + if (start + height <= y) + { + lo = mid + 1; + } + else + { + hi = mid; + } + } + else + { + // should never happen, except the HeightCache has gaps which is an invalid state + return false; + } + } + if (GetLineInfo(lo, start, height)) + { + if (y < start) + { + // given y point is before the first row + return false; + } + row = lo; + return true; + } + else + { + // given y point is after the last row + return false; + } +} + +void HeightCache::Put(const unsigned int row, const int height) +{ + RowRanges *rowRanges = m_heightToRowRange[height]; + if (rowRanges == NULL) + { + rowRanges = new RowRanges(); + m_heightToRowRange[height] = rowRanges; + } + rowRanges->Add(row); +} + +void HeightCache::Remove(const unsigned int row) +{ + HeightToRowRangesMap::iterator it; + for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) + { + RowRanges* rowRanges = it->second; + rowRanges->Remove(row); + } +} + +void HeightCache::Clear() +{ + HeightToRowRangesMap::iterator it; + for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) + { + RowRanges* rowRanges = it->second; + delete rowRanges; + } + m_heightToRowRange.clear(); +} diff --git a/tests/Makefile.in b/tests/Makefile.in index f5dfdc06dc..ce5b95441e 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -254,6 +254,7 @@ TEST_GUI_OBJECTS = \ test_gui_socket.o \ test_gui_tlw.o \ test_gui_dataview.o \ + test_gui_rowheightcachetest.o \ test_gui_boxsizer.o \ test_gui_gridsizer.o \ test_gui_wrapsizer.o \ @@ -1030,6 +1031,9 @@ test_gui_tlw.o: $(srcdir)/persistence/tlw.cpp $(TEST_GUI_ODEP) test_gui_dataview.o: $(srcdir)/persistence/dataview.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/persistence/dataview.cpp +test_gui_rowheightcachetest.o: $(srcdir)/rowheightcache/rowheightcachetest.cpp $(TEST_GUI_ODEP) + $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/rowheightcache/rowheightcachetest.cpp + test_gui_boxsizer.o: $(srcdir)/sizers/boxsizer.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/sizers/boxsizer.cpp diff --git a/tests/makefile.bcc b/tests/makefile.bcc index a0892cbd57..8d81a43d47 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -241,6 +241,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.obj \ $(OBJS)\test_gui_tlw.obj \ $(OBJS)\test_gui_dataview.obj \ + $(OBJS)\test_gui_rowheightcachetest.obj \ $(OBJS)\test_gui_boxsizer.obj \ $(OBJS)\test_gui_gridsizer.obj \ $(OBJS)\test_gui_wrapsizer.obj \ @@ -1084,6 +1085,9 @@ $(OBJS)\test_gui_tlw.obj: .\persistence\tlw.cpp $(OBJS)\test_gui_dataview.obj: .\persistence\dataview.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\persistence\dataview.cpp +$(OBJS)\test_gui_rowheightcachetest.obj: .\rowheightcache\rowheightcachetest.cpp + $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\rowheightcache\rowheightcachetest.cpp + $(OBJS)\test_gui_boxsizer.obj: .\sizers\boxsizer.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\sizers\boxsizer.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index a4e16537b8..5df276613e 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -236,6 +236,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.o \ $(OBJS)\test_gui_tlw.o \ $(OBJS)\test_gui_dataview.o \ + $(OBJS)\test_gui_rowheightcachetest.o \ $(OBJS)\test_gui_boxsizer.o \ $(OBJS)\test_gui_gridsizer.o \ $(OBJS)\test_gui_wrapsizer.o \ @@ -1061,6 +1062,9 @@ $(OBJS)\test_gui_tlw.o: ./persistence/tlw.cpp $(OBJS)\test_gui_dataview.o: ./persistence/dataview.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_gui_rowheightcachetest.o: ./rowheightcache/rowheightcachetest.cpp + $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_gui_boxsizer.o: ./sizers/boxsizer.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index e197998717..0fbadd461a 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -247,6 +247,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_socket.obj \ $(OBJS)\test_gui_tlw.obj \ $(OBJS)\test_gui_dataview.obj \ + $(OBJS)\test_gui_rowheightcachetest.obj \ $(OBJS)\test_gui_boxsizer.obj \ $(OBJS)\test_gui_gridsizer.obj \ $(OBJS)\test_gui_wrapsizer.obj \ @@ -1275,6 +1276,9 @@ $(OBJS)\test_gui_tlw.obj: .\persistence\tlw.cpp $(OBJS)\test_gui_dataview.obj: .\persistence\dataview.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\persistence\dataview.cpp +$(OBJS)\test_gui_rowheightcachetest.obj: .\rowheightcache\rowheightcachetest.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\rowheightcache\rowheightcachetest.cpp + $(OBJS)\test_gui_boxsizer.obj: .\sizers\boxsizer.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\sizers\boxsizer.cpp diff --git a/tests/rowheightcache/rowheightcachetest.cpp b/tests/rowheightcache/rowheightcachetest.cpp new file mode 100644 index 0000000000..51e0ac1daa --- /dev/null +++ b/tests/rowheightcache/rowheightcachetest.cpp @@ -0,0 +1,280 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/rowheightcache/rowheightcachetest.cpp +// Purpose: unit tests for the row height cache of a dataview +// Author: Jens Goepfert (mail@jensgoepfert.de) +// Created: 2018-03-06 +// Copyright: (c) wxWidgets team +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#endif + +#include "wx/generic/private/rowheightcache.h" + +// ---------------------------------------------------------------------------- +// local functions +// ---------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------- +// TestRowRangesAdd +// ---------------------------------------------------------------------------- +TEST_CASE("RowHeightCacheTestCase::TestRowRangesSimple", "[dataview][heightcache]") +{ + RowRanges *rr = new RowRanges(); + + CHECK(rr->CountAll() == 0); + + for (unsigned int i = 0; i <= 10; i++) + { + CHECK(rr->Has(i) == false); + + rr->Add(i); + + CHECK(rr->CountAll() == i+1); + CHECK(rr->CountTo(i) == i); + CHECK(rr->Has(i) == true); + } + + CHECK(rr->GetSize() == 1); // every row is sorted in the same range, so count == 1 + CHECK(rr->CountAll() == 11); // 11 rows collected + CHECK(rr->CountTo(10) == 10); + + rr->Add(5); // row 5 already contained -> does nothing + + CHECK(rr->GetSize() == 1); // every row is sorted in the same range, so count == 1 + CHECK(rr->CountAll() == 11); // 11 rows collected + CHECK(rr->CountTo(10) == 10); + + for (int i = 10; i >= 0; i--) + { + CHECK(rr->CountAll() == (unsigned)i+1); + CHECK(rr->CountTo((unsigned)i) == (unsigned)i); + + rr->Remove(i); + + CHECK(rr->CountAll() == (unsigned)i); + CHECK(rr->CountTo((unsigned)i) == (unsigned)i); + } + + CHECK(rr->CountAll() == 0); // everything removed, no row range is left behind + for (int i = 10; i > 0; i--) + { + CHECK(rr->CountTo(i) == 0); + } + CHECK(rr->GetSize() == 0); +} + +// ---------------------------------------------------------------------------- +// TestRowRangesRemove +// ---------------------------------------------------------------------------- +TEST_CASE("RowHeightCacheTestCase::TestRowRangesGapsMod2", "[dataview][heightcache]") +{ + RowRanges *rr = new RowRanges(); + for (int i = 0; i < 100; i++) + { + CHECK(rr->Has(i) == false); + + if (i % 2 == 0) + { + rr->Add(i); + } + } + CHECK(rr->CountAll() == 50); + CHECK(rr->CountTo(100) == 50); + + for (unsigned int i = 99; i > 0; i--) + { + if (i % 2 == 0) + { + CHECK(rr->Has(i) == true); + rr->Remove(i); + CHECK(rr->Has(i) == false); + } + else + { + CHECK(rr->Has(i) == false); + } + } + + // only row 0 is in the RowRanges, so remove 1 does nothing + rr->Remove(1); + + CHECK(rr->CountAll() == 1); + CHECK(rr->CountTo(0) == 0); + CHECK(rr->CountTo(1) == 1); + CHECK(rr->CountTo(100) == 1); + CHECK(rr->GetSize() == 1); + + rr->Remove(0); // last row is beeing removed + + CHECK(rr->CountAll() == 0); + CHECK(rr->CountTo(0) == 0); + CHECK(rr->CountTo(1) == 0); + CHECK(rr->CountTo(100) == 0); + CHECK(rr->GetSize() == 0); + + rr->Add(10); + CHECK(rr->GetSize() == 1); + CHECK(rr->CountTo(1) == 0); // tests CountTo first break + rr->Add(5); // inserts a range at the beginning + CHECK(rr->GetSize() == 2); + rr->Remove(10); + rr->Remove(5); + CHECK(rr->GetSize() == 0); +} + +// ---------------------------------------------------------------------------- +// TestRowRangesCleanUp1 +// ---------------------------------------------------------------------------- +TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp1", "[dataview][heightcache]") +{ + RowRanges *rr = new RowRanges(); + for (unsigned int i = 0; i < 100; i++) + { + CHECK(rr->Has(i) == false); + + if (i % 2 == 0) + { + rr->Add(i); + } + } + CHECK(rr->GetSize() == 50); // adding 50 rows (only even) results in 50 range objects + CHECK(rr->CountAll() == 50); + CHECK(rr->CountTo(100) == 50); + + for (unsigned int i = 0; i < 100; i++) + { + if (i % 2 == 1) + { + rr->Add(i); + } + } + + CHECK(rr->GetSize() == 1); // adding 50 rows (only odd) should combined to 1 range object + CHECK(rr->CountAll() == 100); + CHECK(rr->CountTo(100) == 100); +} + +// ---------------------------------------------------------------------------- +// TestRowRangesCleanUp2 +// ---------------------------------------------------------------------------- +TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp2", "[dataview][heightcache]") +{ + RowRanges *rr = new RowRanges(); + for (unsigned int i = 0; i < 10; i++) + { + rr->Add(i); + } + CHECK(rr->GetSize() == 1); // adding 10 sequent rows results in 1 range objects + CHECK(rr->CountAll() == 10); + CHECK(rr->CountTo(100) == 10); + + for (unsigned int i = 12; i < 20; i++) + { + rr->Add(i); + } + + CHECK(rr->GetSize() == 2); + rr->Add(11); // tests extending a range at the beginning (to the left) + CHECK(rr->GetSize() == 2); + rr->Add(10); // extends a range and reduces them + CHECK(rr->GetSize() == 1); +} + +// ---------------------------------------------------------------------------- +// TestHeightCache +// ---------------------------------------------------------------------------- +TEST_CASE("RowHeightCacheTestCase::TestHeightCache", "[dataview][heightcache]") +{ + HeightCache *hc = new HeightCache(); + + for (unsigned int i = 0; i <= 10; i++) + { + hc->Put(i, 22); + } + for (unsigned int i = 15; i <= 17; i++) + { + hc->Put(i, 22); + } + for (unsigned int i = 20; i <= 2000; i++) + { + hc->Put(i, 22); + } + + hc->Put(11, 42); + hc->Put(12, 42); + hc->Put(18, 42); + + hc->Put(13, 62); + hc->Put(14, 62); + hc->Put(19, 62); + + int start = 0; + int height = 0; + unsigned int row = 666; + + CHECK(hc->GetLineStart(1000, start) == true); + CHECK(start == 22180); + + CHECK(hc->GetLineHeight(1000, height) == true); + CHECK(height == 22); + + CHECK(hc->GetLineHeight(5000, start) == false); + + // test invalid y + CHECK(hc->GetLineAt(-1, row) == false); + CHECK(row == 666); + + // test start of first row + CHECK(hc->GetLineAt(0, row) == true); + CHECK(row == 0); + + // test end of first row + CHECK(hc->GetLineAt(21, row) == true); + CHECK(row == 0); + + // test start of second row + CHECK(hc->GetLineAt(22, row) == true); + CHECK(row == 1); + + hc->Remove(1000); // Delete row 1000 and everything behind + + CHECK(hc->GetLineAt(22179, row) == true); + CHECK(row == 999); + CHECK(hc->GetLineHeight(999, height) == true); + CHECK(height == 22); + + row = 666; + height = 666; + CHECK(hc->GetLineAt(22180, row) == false); + CHECK(row == 666); + CHECK(hc->GetLineHeight(1000, height) == false); + CHECK(height == 666); + + hc->Clear(); // Clear all items + for (int i = 20; i <= 2000; i++) + { + height = 666; + CHECK(hc->GetLineHeight(i, height) == false); + CHECK(height == 666); + } + + hc->Clear(); // Clear twice should not crash + + row = 666; + height = 666; + CHECK(hc->GetLineAt(22180, row) == false); + CHECK(row == 666); +} diff --git a/tests/test.bkl b/tests/test.bkl index b8c42324c8..62eeddde4f 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -266,6 +266,7 @@ net/socket.cpp persistence/tlw.cpp persistence/dataview.cpp + rowheightcache/rowheightcachetest.cpp sizers/boxsizer.cpp sizers/gridsizer.cpp sizers/wrapsizer.cpp diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj index d02aa25b43..62698892cc 100644 --- a/tests/test_vc7_test_gui.vcproj +++ b/tests/test_vc7_test_gui.vcproj @@ -523,6 +523,9 @@ + + diff --git a/tests/test_vc8_test_gui.vcproj b/tests/test_vc8_test_gui.vcproj index f82a0b7ed7..cc0031584f 100644 --- a/tests/test_vc8_test_gui.vcproj +++ b/tests/test_vc8_test_gui.vcproj @@ -1166,6 +1166,10 @@ RelativePath=".\controls\richtextctrltest.cpp" > + + diff --git a/tests/test_vc9_test_gui.vcproj b/tests/test_vc9_test_gui.vcproj index 9c5b2a55b9..dc7119cdfc 100644 --- a/tests/test_vc9_test_gui.vcproj +++ b/tests/test_vc9_test_gui.vcproj @@ -1138,6 +1138,10 @@ RelativePath=".\controls\richtextctrltest.cpp" > + + From 7a949b456636035d15386f70bd71174225edcd46 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 03:57:07 +0100 Subject: [PATCH 02/16] Remove unnecessary wx/log.h from rowheightcache.cpp Also remove commented out wxLogMessage() call. --- src/generic/rowheightcache.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp index f3c43d5aa1..f1e3e57768 100644 --- a/src/generic/rowheightcache.cpp +++ b/src/generic/rowheightcache.cpp @@ -23,7 +23,6 @@ #endif #ifndef WX_PRECOMP - #include "wx/log.h" #endif // WX_PRECOMP #include "wx/generic/private/rowheightcache.h" @@ -76,7 +75,6 @@ void RowRanges::Add(const unsigned int row) break; } } - // wxLogMessage("New Range: %d" , count); RowRange newRange; newRange.from = row; From 85557d818d9b79cb01cdd27194312cd1992331e5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 03:58:54 +0100 Subject: [PATCH 03/16] Replace a silent "impossible" return with a wxCHECK_RET() Instead of writing a comment saying that something is not supposed to happen, add an assertion verifying that it actually doesn't happen. --- src/generic/rowheightcache.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp index f1e3e57768..c5fa2b665b 100644 --- a/src/generic/rowheightcache.cpp +++ b/src/generic/rowheightcache.cpp @@ -110,17 +110,16 @@ void RowRanges::Remove(const unsigned int row) void RowRanges::CleanUp(unsigned int idx) { size_t count = m_ranges.size(); + + wxCHECK_RET( idx < count, "Wrong index" ); + size_t rngIdx = 0; if (idx > 0) { // start one RowRange before rngIdx = idx - 1; } - if (idx >= count) - { - // should never reached, due CleanUp is private and internal called correctly - return; - } + RowRange *prevRng = &m_ranges[rngIdx]; rngIdx++; while (rngIdx <= idx + 1 && rngIdx < count) From 9eea5cae23af8dce2e6c974204938a8378ff1f21 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:00:35 +0100 Subject: [PATCH 04/16] Make RowRanges::GetSize() inline No real changes. --- include/wx/generic/private/rowheightcache.h | 8 +++++++- src/generic/rowheightcache.cpp | 6 ------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h index d9586f16e7..23dbcbbd1b 100644 --- a/include/wx/generic/private/rowheightcache.h +++ b/include/wx/generic/private/rowheightcache.h @@ -56,7 +56,13 @@ public: not including given row. */ unsigned int CountTo(unsigned int row) const; - unsigned int GetSize() const; // for debugging statistics + + /** + Returns the size of the range. + + This is only used for testing and debugging. + */ + unsigned int GetSize() const { return m_ranges.size(); } private: wxVector m_ranges; diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp index c5fa2b665b..5168b86622 100644 --- a/src/generic/rowheightcache.cpp +++ b/src/generic/rowheightcache.cpp @@ -191,12 +191,6 @@ unsigned int RowRanges::CountTo(unsigned int row) const return ctr; } -unsigned int RowRanges::GetSize() const // for debugging statistics -{ - return m_ranges.size(); -} - - // ---------------------------------------------------------------------------- // HeightCache // ---------------------------------------------------------------------------- From 090491cdbc4c1bd2b83c341e5e821f607221fa6f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:00:52 +0100 Subject: [PATCH 05/16] Minor style and formatting clean up of the new code Fix typos in comments and indent them. Wrap over-long lines. Remove useless top-level const. --- include/wx/generic/private/rowheightcache.h | 125 +++++++++++--------- src/generic/datavgen.cpp | 2 +- src/generic/rowheightcache.cpp | 27 ++--- tests/rowheightcache/rowheightcachetest.cpp | 5 - 4 files changed, 80 insertions(+), 79 deletions(-) diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h index 23dbcbbd1b..6cb4489e7c 100644 --- a/include/wx/generic/private/rowheightcache.h +++ b/include/wx/generic/private/rowheightcache.h @@ -21,39 +21,40 @@ struct RowRange }; /** -@class RowRanges -A helper class that manages a set of RowRange objects. -It stores the indices that are members of a group in a memory -efficiant way. + A helper class that manages a set of RowRange objects. + + It stores the indices that are members of a group in a memory + efficient way. */ class WXDLLIMPEXP_CORE RowRanges -//class RowRanges { public: /** - Adds a row index to this group by adding it to an existing RowRange - or by creating a new one. + Adds a row index to this group by adding it to an existing RowRange + or by creating a new one. */ void Add(unsigned int row); /** - Removes a row index and all indices after idx from this group. + Removes a row index and all indices after idx from this group. */ void Remove(unsigned int row); /** - Checks whether a row index is contained in this group. + Checks whether a row index is contained in this group. */ bool Has(unsigned int row) const; /** - Returns the number of row indices that are contained in this group. + Returns the number of row indices that are contained in this group. */ unsigned int CountAll() const; /** - Returns the number of rows that are in this group before the given row index. - not including given row. + Returns the number of rows that are in this group before the given row + index. + + Not that this doesn't include the given row. */ unsigned int CountTo(unsigned int row) const; @@ -66,10 +67,11 @@ public: private: wxVector m_ranges; + /** - If a new row index was inserted Cleanup checks if the neighbour ranges - of idx can includes the same row indices and discards - unnecessary RowRange objects. + If a new row index was inserted, Cleanup() checks if the neighbour + ranges of idx can includes the same row indices and discards + unnecessary RowRange objects. */ void CleanUp(unsigned int idx); }; @@ -78,70 +80,75 @@ WX_DECLARE_HASH_MAP(unsigned int, RowRanges*, wxIntegerHash, wxIntegerEqual, HeightToRowRangesMap); /** -@class HeightCache + HeightCache implements a cache mechanism for wxDataViewCtrl. -HeightCache implements a cache mechanism for the DataViewCtrl to give -fast access to: -* the height of one line (GetLineHeight) -* the y-coordinate where a row starts (GetLineStart) -* and vice versa (GetLineAt) + It gives fast access to: + * the height of one line (GetLineHeight) + * the y-coordinate where a row starts (GetLineStart) + * and vice versa (GetLineAt) -The layout of the cache is a hashmap where the keys are all exisiting -row heights in pixels. The values are RowRange objects that represents -all having the specified height. + The layout of the cache is a hashmap where the keys are all existing row + heights in pixels. The values are RowRange objects that represent all rows + having the specified height. -{ -22: RowRange([0..10], [15..17], [20..2000]), -42: RowRange([11..12], [18..18]), -62: RowRange([13..14], [19..19]) -} + An example: + @code + { + 22: RowRange([0..10], [15..17], [20..2000]), + 42: RowRange([11..12], [18..18]), + 62: RowRange([13..14], [19..19]) + } + @endcode -Examples -======== + Examples + ======== -GetLineStart ------------- -To retrieve the y-coordinate of item 1000 it is neccessary to look into -each key of the hashmap *m_heightToRowRange*. Get the row count of -indices lower than 1000 (RowRange::CountTo) and multiplies it wich the -according height. + GetLineStart + ------------ + To retrieve the y-coordinate of item 1000 it is necessary to look into + each key of the hashmap *m_heightToRowRange*. Get the row count of + indices lower than 1000 (RowRange::CountTo) and multiplies it which the + according height. -RowRange([0..10], [15..17], [20..2000]).CountTo(1000) ---> 0..10 are 11 items, 15..17 are 3 items and 20..1000 are 980 items (1000-20) -= 11 + 3 + 980 = 994 items + RowRange([0..10], [15..17], [20..2000]).CountTo(1000) + --> 0..10 are 11 items, 15..17 are 3 items and 20..1000 are 980 items (1000-20) + = 11 + 3 + 980 = 994 items -GetLineStart(1000) --> (22 * 994) + (42 * 3) + (62 * 3) = 22180 + GetLineStart(1000) --> (22 * 994) + (42 * 3) + (62 * 3) = 22180 -GetLineHeight -------------- -To retrieve the line height look into each key and check if row is -contained in RowRange (RowRange::Has) + GetLineHeight + ------------- + To retrieve the line height look into each key and check if row is + contained in RowRange (RowRange::Has) -GetLineAt ---------- -To retrieve the row that starts at a specific y-coordinate. -Look into each key and count all rows. -Use bisect algorithm in combination with GetLineStart() to -find the appropriate item + GetLineAt + --------- + To retrieve the row that starts at a specific y-coordinate. + Look into each key and count all rows. + Use bisect algorithm in combination with GetLineStart() to + find the appropriate item */ class WXDLLIMPEXP_ADV HeightCache { public: - bool GetLineStart(unsigned int row, int &start); - bool GetLineHeight(unsigned int row, int &height); - bool GetLineAt(int y, unsigned int &row); + bool GetLineStart(unsigned int row, int& start); + bool GetLineHeight(unsigned int row, int& height); + bool GetLineAt(int y, unsigned int& row); + + void Put(unsigned int row, int height); - void Put(const unsigned int row, const int height); /** - removes the stored height of the given row from the cache - and invalidates all cached rows (including row) + Removes the stored height of the given row from the cache and + invalidates all cached rows (including the given one). */ - void Remove(const unsigned int row); + void Remove(unsigned int row); + void Clear(); private: bool GetLineInfo(unsigned int row, int &start, int &height); - HeightToRowRangesMap m_heightToRowRange; + + HeightToRowRangesMap m_heightToRowRange; }; diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 9674e53d5b..1c601f583f 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -3447,7 +3447,7 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const if (!node) { // not really correct... - return row + ((y - yy) / m_lineHeight); + return row + ((y-yy) / m_lineHeight); } item = node->GetItem(); } diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp index 5168b86622..21529bccb6 100644 --- a/src/generic/rowheightcache.cpp +++ b/src/generic/rowheightcache.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: wx/private/rowheightcache.h +// Name: generic/rowheightcache.cpp // Purpose: height cache of rows in a dataview // Author: Jens Goepfert (mail@jensgoepfert.de) // Created: 2018-03-06 @@ -27,20 +27,15 @@ #include "wx/generic/private/rowheightcache.h" -// ---------------------------------------------------------------------------- -// private structs -// ---------------------------------------------------------------------------- - // ============================================================================ // implementation // ============================================================================ - // ---------------------------------------------------------------------------- // RowRanges // ---------------------------------------------------------------------------- -void RowRanges::Add(const unsigned int row) +void RowRanges::Add(unsigned int row) { size_t count = m_ranges.size(); size_t rngIdx = 0; @@ -63,7 +58,8 @@ void RowRanges::Add(const unsigned int row) } if (row == rng.to) { - // extend range at the end (set to row+1 because 'to' is not including) + // extend range at the end (set to row+1 because 'to' is not + // including) rng.to = row + 1; CleanUp(rngIdx); return; @@ -71,7 +67,8 @@ void RowRanges::Add(const unsigned int row) if (rng.from > row + 1) { - // this range is already behind row index, so break here and insert a new range before + // this range is already behind row index, so break here and insert + // a new range before break; } } @@ -82,7 +79,7 @@ void RowRanges::Add(const unsigned int row) m_ranges.insert(m_ranges.begin() + rngIdx, newRange); } -void RowRanges::Remove(const unsigned int row) +void RowRanges::Remove(unsigned int row) { size_t count = m_ranges.size(); size_t rngIdx = 0; @@ -129,7 +126,8 @@ void RowRanges::CleanUp(unsigned int idx) if (prevRng->to == rng.from) { // this range starts where the previous range began, so remove this - // and set the to-value of the previous range to the to-value of this range + // and set the to-value of the previous range to the to-value of + // this range prevRng->to = rng.to; m_ranges.erase(m_ranges.begin() + rngIdx); count--; @@ -274,7 +272,8 @@ bool HeightCache::GetLineAt(int y, unsigned int &row) } else { - // should never happen, except the HeightCache has gaps which is an invalid state + // should never happen, except the HeightCache has gaps which is an + // invalid state return false; } } @@ -295,7 +294,7 @@ bool HeightCache::GetLineAt(int y, unsigned int &row) } } -void HeightCache::Put(const unsigned int row, const int height) +void HeightCache::Put(unsigned int row, int height) { RowRanges *rowRanges = m_heightToRowRange[height]; if (rowRanges == NULL) @@ -306,7 +305,7 @@ void HeightCache::Put(const unsigned int row, const int height) rowRanges->Add(row); } -void HeightCache::Remove(const unsigned int row) +void HeightCache::Remove(unsigned int row) { HeightToRowRangesMap::iterator it; for (it = m_heightToRowRange.begin(); it != m_heightToRowRange.end(); ++it) diff --git a/tests/rowheightcache/rowheightcachetest.cpp b/tests/rowheightcache/rowheightcachetest.cpp index 51e0ac1daa..35867565a1 100644 --- a/tests/rowheightcache/rowheightcachetest.cpp +++ b/tests/rowheightcache/rowheightcachetest.cpp @@ -22,11 +22,6 @@ #include "wx/generic/private/rowheightcache.h" -// ---------------------------------------------------------------------------- -// local functions -// ---------------------------------------------------------------------------- - - // ---------------------------------------------------------------------------- // TestRowRangesAdd // ---------------------------------------------------------------------------- From d1b02b8dd691e607f1180715bb35bbb59a9eb336 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:06:51 +0100 Subject: [PATCH 06/16] Fix memory leaks in newly added unit test There is no need to allocate neither RowRanges nor HeightCache on the heap, just create them on the stack -- if nothing else, this ensures we don't leak memory, unlike before. --- tests/rowheightcache/rowheightcachetest.cpp | 196 ++++++++++---------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/tests/rowheightcache/rowheightcachetest.cpp b/tests/rowheightcache/rowheightcachetest.cpp index 35867565a1..04085cc745 100644 --- a/tests/rowheightcache/rowheightcachetest.cpp +++ b/tests/rowheightcache/rowheightcachetest.cpp @@ -27,48 +27,48 @@ // ---------------------------------------------------------------------------- TEST_CASE("RowHeightCacheTestCase::TestRowRangesSimple", "[dataview][heightcache]") { - RowRanges *rr = new RowRanges(); + RowRanges rr; - CHECK(rr->CountAll() == 0); + CHECK(rr.CountAll() == 0); for (unsigned int i = 0; i <= 10; i++) { - CHECK(rr->Has(i) == false); + CHECK(rr.Has(i) == false); - rr->Add(i); + rr.Add(i); - CHECK(rr->CountAll() == i+1); - CHECK(rr->CountTo(i) == i); - CHECK(rr->Has(i) == true); + CHECK(rr.CountAll() == i+1); + CHECK(rr.CountTo(i) == i); + CHECK(rr.Has(i) == true); } - CHECK(rr->GetSize() == 1); // every row is sorted in the same range, so count == 1 - CHECK(rr->CountAll() == 11); // 11 rows collected - CHECK(rr->CountTo(10) == 10); + CHECK(rr.GetSize() == 1); // every row is sorted in the same range, so count == 1 + CHECK(rr.CountAll() == 11); // 11 rows collected + CHECK(rr.CountTo(10) == 10); - rr->Add(5); // row 5 already contained -> does nothing + rr.Add(5); // row 5 already contained -> does nothing - CHECK(rr->GetSize() == 1); // every row is sorted in the same range, so count == 1 - CHECK(rr->CountAll() == 11); // 11 rows collected - CHECK(rr->CountTo(10) == 10); + CHECK(rr.GetSize() == 1); // every row is sorted in the same range, so count == 1 + CHECK(rr.CountAll() == 11); // 11 rows collected + CHECK(rr.CountTo(10) == 10); for (int i = 10; i >= 0; i--) { - CHECK(rr->CountAll() == (unsigned)i+1); - CHECK(rr->CountTo((unsigned)i) == (unsigned)i); + CHECK(rr.CountAll() == (unsigned)i+1); + CHECK(rr.CountTo((unsigned)i) == (unsigned)i); - rr->Remove(i); + rr.Remove(i); - CHECK(rr->CountAll() == (unsigned)i); - CHECK(rr->CountTo((unsigned)i) == (unsigned)i); + CHECK(rr.CountAll() == (unsigned)i); + CHECK(rr.CountTo((unsigned)i) == (unsigned)i); } - CHECK(rr->CountAll() == 0); // everything removed, no row range is left behind + CHECK(rr.CountAll() == 0); // everything removed, no row range is left behind for (int i = 10; i > 0; i--) { - CHECK(rr->CountTo(i) == 0); + CHECK(rr.CountTo(i) == 0); } - CHECK(rr->GetSize() == 0); + CHECK(rr.GetSize() == 0); } // ---------------------------------------------------------------------------- @@ -76,58 +76,58 @@ TEST_CASE("RowHeightCacheTestCase::TestRowRangesSimple", "[dataview][heightcache // ---------------------------------------------------------------------------- TEST_CASE("RowHeightCacheTestCase::TestRowRangesGapsMod2", "[dataview][heightcache]") { - RowRanges *rr = new RowRanges(); + RowRanges rr; for (int i = 0; i < 100; i++) { - CHECK(rr->Has(i) == false); + CHECK(rr.Has(i) == false); if (i % 2 == 0) { - rr->Add(i); + rr.Add(i); } } - CHECK(rr->CountAll() == 50); - CHECK(rr->CountTo(100) == 50); + CHECK(rr.CountAll() == 50); + CHECK(rr.CountTo(100) == 50); for (unsigned int i = 99; i > 0; i--) { if (i % 2 == 0) { - CHECK(rr->Has(i) == true); - rr->Remove(i); - CHECK(rr->Has(i) == false); + CHECK(rr.Has(i) == true); + rr.Remove(i); + CHECK(rr.Has(i) == false); } else { - CHECK(rr->Has(i) == false); + CHECK(rr.Has(i) == false); } } // only row 0 is in the RowRanges, so remove 1 does nothing - rr->Remove(1); + rr.Remove(1); - CHECK(rr->CountAll() == 1); - CHECK(rr->CountTo(0) == 0); - CHECK(rr->CountTo(1) == 1); - CHECK(rr->CountTo(100) == 1); - CHECK(rr->GetSize() == 1); + CHECK(rr.CountAll() == 1); + CHECK(rr.CountTo(0) == 0); + CHECK(rr.CountTo(1) == 1); + CHECK(rr.CountTo(100) == 1); + CHECK(rr.GetSize() == 1); - rr->Remove(0); // last row is beeing removed + rr.Remove(0); // last row is beeing removed - CHECK(rr->CountAll() == 0); - CHECK(rr->CountTo(0) == 0); - CHECK(rr->CountTo(1) == 0); - CHECK(rr->CountTo(100) == 0); - CHECK(rr->GetSize() == 0); + CHECK(rr.CountAll() == 0); + CHECK(rr.CountTo(0) == 0); + CHECK(rr.CountTo(1) == 0); + CHECK(rr.CountTo(100) == 0); + CHECK(rr.GetSize() == 0); - rr->Add(10); - CHECK(rr->GetSize() == 1); - CHECK(rr->CountTo(1) == 0); // tests CountTo first break - rr->Add(5); // inserts a range at the beginning - CHECK(rr->GetSize() == 2); - rr->Remove(10); - rr->Remove(5); - CHECK(rr->GetSize() == 0); + rr.Add(10); + CHECK(rr.GetSize() == 1); + CHECK(rr.CountTo(1) == 0); // tests CountTo first break + rr.Add(5); // inserts a range at the beginning + CHECK(rr.GetSize() == 2); + rr.Remove(10); + rr.Remove(5); + CHECK(rr.GetSize() == 0); } // ---------------------------------------------------------------------------- @@ -135,31 +135,31 @@ TEST_CASE("RowHeightCacheTestCase::TestRowRangesGapsMod2", "[dataview][heightcac // ---------------------------------------------------------------------------- TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp1", "[dataview][heightcache]") { - RowRanges *rr = new RowRanges(); + RowRanges rr; for (unsigned int i = 0; i < 100; i++) { - CHECK(rr->Has(i) == false); + CHECK(rr.Has(i) == false); if (i % 2 == 0) { - rr->Add(i); + rr.Add(i); } } - CHECK(rr->GetSize() == 50); // adding 50 rows (only even) results in 50 range objects - CHECK(rr->CountAll() == 50); - CHECK(rr->CountTo(100) == 50); + CHECK(rr.GetSize() == 50); // adding 50 rows (only even) results in 50 range objects + CHECK(rr.CountAll() == 50); + CHECK(rr.CountTo(100) == 50); for (unsigned int i = 0; i < 100; i++) { if (i % 2 == 1) { - rr->Add(i); + rr.Add(i); } } - CHECK(rr->GetSize() == 1); // adding 50 rows (only odd) should combined to 1 range object - CHECK(rr->CountAll() == 100); - CHECK(rr->CountTo(100) == 100); + CHECK(rr.GetSize() == 1); // adding 50 rows (only odd) should combined to 1 range object + CHECK(rr.CountAll() == 100); + CHECK(rr.CountTo(100) == 100); } // ---------------------------------------------------------------------------- @@ -167,25 +167,25 @@ TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp1", "[dataview][heightcac // ---------------------------------------------------------------------------- TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp2", "[dataview][heightcache]") { - RowRanges *rr = new RowRanges(); + RowRanges rr; for (unsigned int i = 0; i < 10; i++) { - rr->Add(i); + rr.Add(i); } - CHECK(rr->GetSize() == 1); // adding 10 sequent rows results in 1 range objects - CHECK(rr->CountAll() == 10); - CHECK(rr->CountTo(100) == 10); + CHECK(rr.GetSize() == 1); // adding 10 sequent rows results in 1 range objects + CHECK(rr.CountAll() == 10); + CHECK(rr.CountTo(100) == 10); for (unsigned int i = 12; i < 20; i++) { - rr->Add(i); + rr.Add(i); } - CHECK(rr->GetSize() == 2); - rr->Add(11); // tests extending a range at the beginning (to the left) - CHECK(rr->GetSize() == 2); - rr->Add(10); // extends a range and reduces them - CHECK(rr->GetSize() == 1); + CHECK(rr.GetSize() == 2); + rr.Add(11); // tests extending a range at the beginning (to the left) + CHECK(rr.GetSize() == 2); + rr.Add(10); // extends a range and reduces them + CHECK(rr.GetSize() == 1); } // ---------------------------------------------------------------------------- @@ -193,83 +193,83 @@ TEST_CASE("RowHeightCacheTestCase::TestRowRangesCleanUp2", "[dataview][heightcac // ---------------------------------------------------------------------------- TEST_CASE("RowHeightCacheTestCase::TestHeightCache", "[dataview][heightcache]") { - HeightCache *hc = new HeightCache(); + HeightCache hc; for (unsigned int i = 0; i <= 10; i++) { - hc->Put(i, 22); + hc.Put(i, 22); } for (unsigned int i = 15; i <= 17; i++) { - hc->Put(i, 22); + hc.Put(i, 22); } for (unsigned int i = 20; i <= 2000; i++) { - hc->Put(i, 22); + hc.Put(i, 22); } - hc->Put(11, 42); - hc->Put(12, 42); - hc->Put(18, 42); + hc.Put(11, 42); + hc.Put(12, 42); + hc.Put(18, 42); - hc->Put(13, 62); - hc->Put(14, 62); - hc->Put(19, 62); + hc.Put(13, 62); + hc.Put(14, 62); + hc.Put(19, 62); int start = 0; int height = 0; unsigned int row = 666; - CHECK(hc->GetLineStart(1000, start) == true); + CHECK(hc.GetLineStart(1000, start) == true); CHECK(start == 22180); - CHECK(hc->GetLineHeight(1000, height) == true); + CHECK(hc.GetLineHeight(1000, height) == true); CHECK(height == 22); - CHECK(hc->GetLineHeight(5000, start) == false); + CHECK(hc.GetLineHeight(5000, start) == false); // test invalid y - CHECK(hc->GetLineAt(-1, row) == false); + CHECK(hc.GetLineAt(-1, row) == false); CHECK(row == 666); // test start of first row - CHECK(hc->GetLineAt(0, row) == true); + CHECK(hc.GetLineAt(0, row) == true); CHECK(row == 0); // test end of first row - CHECK(hc->GetLineAt(21, row) == true); + CHECK(hc.GetLineAt(21, row) == true); CHECK(row == 0); // test start of second row - CHECK(hc->GetLineAt(22, row) == true); + CHECK(hc.GetLineAt(22, row) == true); CHECK(row == 1); - hc->Remove(1000); // Delete row 1000 and everything behind + hc.Remove(1000); // Delete row 1000 and everything behind - CHECK(hc->GetLineAt(22179, row) == true); + CHECK(hc.GetLineAt(22179, row) == true); CHECK(row == 999); - CHECK(hc->GetLineHeight(999, height) == true); + CHECK(hc.GetLineHeight(999, height) == true); CHECK(height == 22); row = 666; height = 666; - CHECK(hc->GetLineAt(22180, row) == false); + CHECK(hc.GetLineAt(22180, row) == false); CHECK(row == 666); - CHECK(hc->GetLineHeight(1000, height) == false); + CHECK(hc.GetLineHeight(1000, height) == false); CHECK(height == 666); - hc->Clear(); // Clear all items + hc.Clear(); // Clear all items for (int i = 20; i <= 2000; i++) { height = 666; - CHECK(hc->GetLineHeight(i, height) == false); + CHECK(hc.GetLineHeight(i, height) == false); CHECK(height == 666); } - hc->Clear(); // Clear twice should not crash + hc.Clear(); // Clear twice should not crash row = 666; height = 666; - CHECK(hc->GetLineAt(22180, row) == false); + CHECK(hc.GetLineAt(22180, row) == false); CHECK(row == 666); } From 39e540edb7491079464c856f30d790b57a2c6340 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:12:39 +0100 Subject: [PATCH 07/16] Fix HeightCache DLL export declaration Use CORE, not ADV, which doesn't exist any more. --- include/wx/generic/private/rowheightcache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h index 6cb4489e7c..61394223f6 100644 --- a/include/wx/generic/private/rowheightcache.h +++ b/include/wx/generic/private/rowheightcache.h @@ -128,7 +128,7 @@ WX_DECLARE_HASH_MAP(unsigned int, RowRanges*, wxIntegerHash, wxIntegerEqual, Use bisect algorithm in combination with GetLineStart() to find the appropriate item */ -class WXDLLIMPEXP_ADV HeightCache +class WXDLLIMPEXP_CORE HeightCache { public: bool GetLineStart(unsigned int row, int& start); From cee89508d38bd3e06871040ce1ef53d207e6c81a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:14:59 +0100 Subject: [PATCH 08/16] Simply destroying row heights cache There is no reason to neither check that the pointer is non-null nor to call Clear() on it before deleting it, so don't do it. --- src/generic/datavgen.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 1c601f583f..34e6237a16 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -1992,11 +1992,7 @@ wxDataViewMainWindow::~wxDataViewMainWindow() { DestroyTree(); delete m_renameTimer; - if (m_rowHeightCache != NULL) - { - m_rowHeightCache->Clear(); - delete m_rowHeightCache; - } + delete m_rowHeightCache; } From 9a91f593991360e7d5fe4700e4e276b7ff723ade Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 7 Dec 2018 04:16:29 +0100 Subject: [PATCH 09/16] Revert the change to RefreshRows() in a recent commit There doesn't seem to be any reason for this check to exist. --- src/generic/datavgen.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 34e6237a16..471ac78fc6 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -3300,9 +3300,6 @@ void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to ) { - if (from == to && from == ((unsigned)-1)) - return; - wxRect rect = GetLinesRect(from, to); m_owner->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y); From 1f0e32e48562110029df44328e5138d79c0805fb Mon Sep 17 00:00:00 2001 From: jensgoe Date: Mon, 10 Dec 2018 00:14:15 +0100 Subject: [PATCH 10/16] fixed handling of NULL items --- src/generic/datavgen.cpp | 167 ++++++++++++--------------------------- 1 file changed, 50 insertions(+), 117 deletions(-) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 471ac78fc6..b5e37120d8 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -710,6 +710,7 @@ public: bool Cleared(); void Resort() { + m_rowHeightCache->Clear(); if (!IsVirtualList()) { m_root->Resort(this); @@ -822,6 +823,7 @@ public: int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode + int QueryAndCacheLineHeight(unsigned int row, wxDataViewItem item) const; void SetRowHeight( int lineHeight ) { m_lineHeight = lineHeight; } int GetRowHeight() const { return m_lineHeight; } @@ -3344,60 +3346,29 @@ wxRect wxDataViewMainWindow::GetLinesRect( unsigned int rowFrom, unsigned int ro int wxDataViewMainWindow::GetLineStart( unsigned int row ) const { - const wxDataViewModel *model = GetModel(); - if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { int start = 0; - if (m_rowHeightCache->GetLineStart(row, start)) + if ( m_rowHeightCache->GetLineStart(row, start) ) return start; unsigned int r; for (r = 0; r < row; r++) { int height = 0; - if (m_rowHeightCache->GetLineHeight(r, height)) + if ( m_rowHeightCache->GetLineHeight(r, height) ) { start += height; continue; } - wxDataViewItem item; - if (IsList()) - { - item = GetItemByRow(r); - } - else - { - const wxDataViewTreeNode* node = GetTreeNodeByRow(r); - if (!node) return start; - item = node->GetItem(); - } + wxDataViewItem item = GetItemByRow(r); + if ( !item ) + break; - unsigned int cols = GetOwner()->GetColumnCount(); - unsigned int col; - height = m_lineHeight; - for (col = 0; col < cols; col++) - { - const wxDataViewColumn *column = GetOwner()->GetColumn(col); - if (column->IsHidden()) - continue; // skip it! - - if ((col != 0) && - model->IsContainer(item) && - !model->HasContainerColumns(item)) - continue; // skip it! - - wxDataViewRenderer *renderer = - const_cast(column->GetRenderer()); - renderer->PrepareForItem(model, item, column->GetModelColumn()); - - height = wxMax( height, renderer->GetSize().y ); - } + height = QueryAndCacheLineHeight(r, item); start += height; - - m_rowHeightCache->Put(r, height); } return start; @@ -3410,8 +3381,6 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const int wxDataViewMainWindow::GetLineAt( unsigned int y ) const { - const wxDataViewModel *model = GetModel(); - // check for the easy case first if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) return y / m_lineHeight; @@ -3419,55 +3388,21 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const unsigned int row = 0; unsigned int yy = 0; - if (m_rowHeightCache->GetLineAt(y, row)) + if ( m_rowHeightCache->GetLineAt(y, row) ) return row; for (;;) { int height = 0; - if (!m_rowHeightCache->GetLineHeight(row, height)) + if ( !m_rowHeightCache->GetLineHeight(row, height) ) { // row height not in cache -> get it from the renderer... - wxDataViewItem item; - if (IsList()) - { - item = GetItemByRow(row); - } - else - { - const wxDataViewTreeNode* node = GetTreeNodeByRow(row); - if (!node) - { - // not really correct... - return row + ((y-yy) / m_lineHeight); - } - item = node->GetItem(); - } + wxDataViewItem item = GetItemByRow(row); + if ( !item ) + return row; // should be the last row - unsigned int cols = GetOwner()->GetColumnCount(); - unsigned int col; - height = m_lineHeight; - for (col = 0; col < cols; col++) - { - const wxDataViewColumn *column = GetOwner()->GetColumn(col); - if (column->IsHidden()) - continue; // skip it! - - if ((col != 0) && - model->IsContainer(item) && - !model->HasContainerColumns(item)) - continue; // skip it! - - wxDataViewRenderer *renderer = - const_cast(column->GetRenderer()); - renderer->PrepareForItem(model, item, column->GetModelColumn()); - - height = wxMax( height, renderer->GetSize().y ); - } - - // ... and store the height in the cache - m_rowHeightCache->Put(row, height); + height = QueryAndCacheLineHeight(row, item); } yy += height; @@ -3480,50 +3415,17 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const { - const wxDataViewModel *model = GetModel(); - if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) { int height = 0; - if (m_rowHeightCache->GetLineHeight(row, height)) + if ( m_rowHeightCache->GetLineHeight(row, height) ) return height; - wxDataViewItem item; - if (IsList()) - { - item = GetItemByRow(row); - } - else - { - const wxDataViewTreeNode* node = GetTreeNodeByRow(row); - // wxASSERT( node ); - if (!node) return m_lineHeight; - item = node->GetItem(); - } - - height = m_lineHeight; - unsigned int cols = GetOwner()->GetColumnCount(); - unsigned int col; - for (col = 0; col < cols; col++) - { - const wxDataViewColumn *column = GetOwner()->GetColumn(col); - if (column->IsHidden()) - continue; // skip it! - - if ((col != 0) && - model->IsContainer(item) && - !model->HasContainerColumns(item)) - continue; // skip it! - - wxDataViewRenderer *renderer = - const_cast(column->GetRenderer()); - renderer->PrepareForItem(model, item, column->GetModelColumn()); - - height = wxMax( height, renderer->GetSize().y ); - } - - m_rowHeightCache->Put(row, height); + wxDataViewItem item = GetItemByRow(row); + if ( !item ) + return m_lineHeight; + height = QueryAndCacheLineHeight(row, item); return height; } else @@ -3533,6 +3435,37 @@ int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const } +int wxDataViewMainWindow::QueryAndCacheLineHeight(unsigned int row, wxDataViewItem item) const +{ + const wxDataViewModel *model = GetModel(); + int height = m_lineHeight; + unsigned int cols = GetOwner()->GetColumnCount(); + unsigned int col; + for (col = 0; col < cols; col++) + { + const wxDataViewColumn *column = GetOwner()->GetColumn(col); + if (column->IsHidden()) + continue; // skip it! + + if ((col != 0) && + model->IsContainer(item) && + !model->HasContainerColumns(item)) + continue; // skip it! + + wxDataViewRenderer *renderer = + const_cast(column->GetRenderer()); + renderer->PrepareForItem(model, item, column->GetModelColumn()); + + height = wxMax(height, renderer->GetSize().y); + } + + // ... and store the height in the cache + m_rowHeightCache->Put(row, height); + + return height; +} + + class RowToTreeNodeJob: public DoJob { public: From 7ab9e992b6f9f79b8e658ef35cc1e58a26a260a2 Mon Sep 17 00:00:00 2001 From: jensgoe Date: Wed, 19 Dec 2018 20:45:00 +0100 Subject: [PATCH 11/16] ensure row >= GetRowCount() if GetLineAt reaches invalid item refactored method structure for better readability --- include/wx/generic/private/rowheightcache.h | 3 +- src/generic/datavgen.cpp | 101 +++++++++++--------- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h index 61394223f6..768edd4687 100644 --- a/include/wx/generic/private/rowheightcache.h +++ b/include/wx/generic/private/rowheightcache.h @@ -134,6 +134,7 @@ public: bool GetLineStart(unsigned int row, int& start); bool GetLineHeight(unsigned int row, int& height); bool GetLineAt(int y, unsigned int& row); + bool GetLineInfo(unsigned int row, int &start, int &height); void Put(unsigned int row, int height); @@ -146,8 +147,6 @@ public: void Clear(); private: - bool GetLineInfo(unsigned int row, int &start, int &height); - HeightToRowRangesMap m_heightToRowRange; }; diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index b5e37120d8..09704c2df0 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -3346,37 +3346,32 @@ wxRect wxDataViewMainWindow::GetLinesRect( unsigned int rowFrom, unsigned int ro int wxDataViewMainWindow::GetLineStart( unsigned int row ) const { - if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) + // check for the easy case first + if (!GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + return row * m_lineHeight; + + int start = 0; + if ( m_rowHeightCache->GetLineStart(row, start) ) + return start; + + unsigned int r; + for (r = 0; r < row; r++) { - int start = 0; - if ( m_rowHeightCache->GetLineStart(row, start) ) - return start; - - unsigned int r; - for (r = 0; r < row; r++) + int height = 0; + if ( !m_rowHeightCache->GetLineHeight(r, height) ) { - int height = 0; - if ( m_rowHeightCache->GetLineHeight(r, height) ) - { - start += height; - continue; - } - - wxDataViewItem item = GetItemByRow(r); - if ( !item ) + // row height not in cache -> get it from the renderer... + wxDataViewItem item = GetItemByRow(r); + if (!item) break; height = QueryAndCacheLineHeight(r, item); - - start += height; } - return start; - } - else - { - return row * m_lineHeight; + start += height; } + + return start; } int wxDataViewMainWindow::GetLineAt( unsigned int y ) const @@ -3386,54 +3381,68 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const return y / m_lineHeight; unsigned int row = 0; - unsigned int yy = 0; - if ( m_rowHeightCache->GetLineAt(y, row) ) return row; + // OnPaint asks GetLineAt for the very last y position and this is always + // below the last item (--> an invalid item). To prevent iterating over all + // items, check if y is below the last row. + // Because this is done very often (for each repaint) its worth to handle + // this special case separately. + int height = 0; + int start = 0; + unsigned int rowCount = GetRowCount(); + if (rowCount == 0 || + (m_rowHeightCache->GetLineInfo(rowCount - 1, start, height) && + y >= start + height)) + { + return rowCount; + } + + // sum all item heights until y is reached + unsigned int yy = 0; for (;;) { - int height = 0; + height = 0; if ( !m_rowHeightCache->GetLineHeight(row, height) ) { // row height not in cache -> get it from the renderer... - wxDataViewItem item = GetItemByRow(row); if ( !item ) - return row; // should be the last row + { + wxASSERT(row >= GetRowCount()); + break; + } height = QueryAndCacheLineHeight(row, item); } yy += height; if (y < yy) - return row; + break; row++; } + return row; } int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const { - if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) - { - int height = 0; - if ( m_rowHeightCache->GetLineHeight(row, height) ) - return height; - - wxDataViewItem item = GetItemByRow(row); - if ( !item ) - return m_lineHeight; - - height = QueryAndCacheLineHeight(row, item); - return height; - } - else - { + // check for the easy case first + if (!GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) return m_lineHeight; - } -} + int height = 0; + if ( m_rowHeightCache->GetLineHeight(row, height) ) + return height; + + wxDataViewItem item = GetItemByRow(row); + if ( !item ) + return m_lineHeight; + + height = QueryAndCacheLineHeight(row, item); + return height; +} int wxDataViewMainWindow::QueryAndCacheLineHeight(unsigned int row, wxDataViewItem item) const { From 12edcbccc8e8d180ddb50f4064a6ba47cf3c1e3d Mon Sep 17 00:00:00 2001 From: jensgoe Date: Wed, 19 Dec 2018 20:45:32 +0100 Subject: [PATCH 12/16] clear height cache if model is cleared --- src/generic/datavgen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 09704c2df0..79ee360da9 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -3018,6 +3018,7 @@ bool wxDataViewMainWindow::Cleared() DestroyTree(); m_selection.Clear(); m_currentRow = (unsigned)-1; + m_rowHeightCache->Clear(); if (GetModel()) { From c922c95c96a3fded2fae5394d5353f3ce36aaca6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 20 Dec 2018 02:21:53 +0100 Subject: [PATCH 13/16] Fix the just introduced signed/unsigned comparison warning Cast to unsigned before comparing with an unsigned variable. --- src/generic/datavgen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 79ee360da9..192a281047 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -3395,7 +3395,7 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const unsigned int rowCount = GetRowCount(); if (rowCount == 0 || (m_rowHeightCache->GetLineInfo(rowCount - 1, start, height) && - y >= start + height)) + y >= static_cast(start + height))) { return rowCount; } From 8c64209df1789cb6da1702a6e4bbb6d8dda55075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20G=C3=B6pfert?= Date: Mon, 7 Jan 2019 11:56:08 +0100 Subject: [PATCH 14/16] ensure m_rowHeightCache is not NULL before accessing it --- src/generic/datavgen.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index ed8a60b549..aadd365495 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -710,7 +710,9 @@ public: bool Cleared(); void Resort() { - m_rowHeightCache->Clear(); + if ( m_rowHeightCache ) + m_rowHeightCache->Clear(); + if (!IsVirtualList()) { m_root->Resort(this); @@ -2752,7 +2754,7 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData } else { - if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( m_rowHeightCache ) { // specific position (row) is unclear, so clear whole height cache m_rowHeightCache->Clear(); @@ -2899,7 +2901,7 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent, return true; } - if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( m_rowHeightCache ) m_rowHeightCache->Remove(GetRowByItem(parent) + itemPosInNode); // Delete the item from wxDataViewTreeNode representation: @@ -2966,7 +2968,7 @@ bool wxDataViewMainWindow::DoItemChanged(const wxDataViewItem & item, int view_c { if ( !IsVirtualList() ) { - if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( m_rowHeightCache ) m_rowHeightCache->Remove(GetRowByItem(item)); // Move this node to its new correct place after it was updated. @@ -3018,7 +3020,9 @@ bool wxDataViewMainWindow::Cleared() DestroyTree(); m_selection.Clear(); m_currentRow = (unsigned)-1; - m_rowHeightCache->Clear(); + + if ( m_rowHeightCache ) + m_rowHeightCache->Clear(); if (GetModel()) { @@ -3348,7 +3352,7 @@ wxRect wxDataViewMainWindow::GetLinesRect( unsigned int rowFrom, unsigned int ro int wxDataViewMainWindow::GetLineStart( unsigned int row ) const { // check for the easy case first - if (!GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( !m_rowHeightCache || !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) return row * m_lineHeight; int start = 0; @@ -3378,7 +3382,7 @@ int wxDataViewMainWindow::GetLineStart( unsigned int row ) const int wxDataViewMainWindow::GetLineAt( unsigned int y ) const { // check for the easy case first - if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) + if ( !m_rowHeightCache || !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) return y / m_lineHeight; unsigned int row = 0; @@ -3430,7 +3434,7 @@ int wxDataViewMainWindow::GetLineAt( unsigned int y ) const int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const { // check for the easy case first - if (!GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( !m_rowHeightCache || !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) ) return m_lineHeight; int height = 0; @@ -3613,7 +3617,7 @@ void wxDataViewMainWindow::Expand( unsigned int row ) if (!node->HasChildren()) return; - if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( m_rowHeightCache ) { // Expand makes new rows visible thus we invalidates all following // rows in the height cache @@ -3669,7 +3673,7 @@ void wxDataViewMainWindow::Collapse(unsigned int row) if (!node->HasChildren()) return; - if (GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT)) + if ( m_rowHeightCache ) { // Collapse hides rows thus we invalidates all following // rows in the height cache From 14ca16ffaf7b5b02bea97ea4858ff21341c7ebc2 Mon Sep 17 00:00:00 2001 From: jensgoe Date: Fri, 21 Dec 2018 17:56:01 +0100 Subject: [PATCH 15/16] example for DataView with wxDV_VARIABLE_LINE_HEIGHT flag --- samples/dataview/dataview.cpp | 93 ++++++++++++++++++++++++++++++++++- samples/dataview/mymodels.cpp | 13 +++++ samples/dataview/mymodels.h | 11 +++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 51273a6106..b1e1f7c54b 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -165,11 +165,12 @@ private: // the controls stored in the various tabs of the main notebook: - wxDataViewCtrl* m_ctrl[4]; + wxDataViewCtrl* m_ctrl[5]; // the models associated with the first two DVC: wxObjectDataPtr m_music_model; + wxObjectDataPtr m_long_music_model; wxObjectDataPtr m_list_model; // other data: @@ -287,6 +288,53 @@ private: }; +// ---------------------------------------------------------------------------- +// MultiLineCustomRenderer +// ---------------------------------------------------------------------------- + +class MultiLineCustomRenderer : public wxDataViewCustomRenderer +{ +public: + // a simple renderer that wraps each word on a new line + explicit MultiLineCustomRenderer() + : wxDataViewCustomRenderer("string", wxDATAVIEW_CELL_INERT, 0) + { } + + virtual bool Render(wxRect rect, wxDC *dc, int state) wxOVERRIDE + { + RenderText(m_value, 0, rect, dc, state); + return true; + } + + virtual wxSize GetSize() const wxOVERRIDE + { + wxSize txtSize = GetTextExtent(m_value); + int lines = m_value.Freq('\n') + 1; + txtSize.SetHeight(txtSize.GetHeight() * lines); + return txtSize; + } + + virtual bool SetValue(const wxVariant &value) wxOVERRIDE + { + m_value = value.GetString(); + m_value.Replace(" ", "\n"); + return true; + } + + virtual bool GetValue(wxVariant &WXUNUSED(value)) const wxOVERRIDE { return true; } + +#if wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const wxOVERRIDE + { + return m_value; + } +#endif // wxUSE_ACCESSIBILITY + +private: + wxString m_value; +}; + + // ============================================================================ // implementation // ============================================================================ @@ -459,6 +507,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int m_ctrl[1] = NULL; m_ctrl[2] = NULL; m_ctrl[3] = NULL; + m_ctrl[4] = NULL; m_eventFromProgram = false; @@ -606,6 +655,16 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int fourthPanelSz->Add(button_sizer4); fourthPanel->SetSizerAndFit(fourthPanelSz); + // fifth page of the notebook + // --------------------------- + + wxPanel *fifthPanel = new wxPanel(m_notebook, wxID_ANY); + + BuildDataViewCtrl(fifthPanel, 4); // sets m_ctrl[4] + + wxSizer *fifthPanelSz = new wxBoxSizer(wxVERTICAL); + fifthPanelSz->Add(m_ctrl[4], 1, wxGROW | wxALL, 5); + fifthPanel->SetSizerAndFit(fifthPanelSz); // complete GUI @@ -615,6 +674,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int m_notebook->AddPage(secondPanel, "MyListModel"); m_notebook->AddPage(thirdPanel, "wxDataViewListCtrl"); m_notebook->AddPage(fourthPanel, "wxDataViewTreeCtrl"); + m_notebook->AddPage(fifthPanel, "wxDataViewTreeCtrl Variable line height"); wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); @@ -837,6 +897,35 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l tc->Expand(cont); } break; + + case 4: + { + wxASSERT(!m_ctrl[4] && !m_long_music_model); + m_ctrl[4] = + new wxDataViewCtrl( parent, wxID_ANY, wxDefaultPosition, + wxDefaultSize, style | wxDV_VARIABLE_LINE_HEIGHT ); + + m_long_music_model = new MyLongMusicTreeModel; + m_ctrl[4]->AssociateModel(m_long_music_model.get()); + + // column 0 of the view control: + MultiLineCustomRenderer *tr = + new MultiLineCustomRenderer(); + wxDataViewColumn *column0 = + new wxDataViewColumn("title", tr, 0, 200, wxALIGN_LEFT, + wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_RESIZABLE); + m_ctrl[4]->AppendColumn(column0); + + // column 1 of the view control: + tr = new MultiLineCustomRenderer(); + wxDataViewColumn *column1 = + new wxDataViewColumn("artist", tr, 1, 150, wxALIGN_LEFT, + wxDATAVIEW_COL_SORTABLE | wxDATAVIEW_COL_REORDERABLE | + wxDATAVIEW_COL_RESIZABLE); + column1->SetMinWidth(150); // this column can't be resized to be smaller + m_ctrl[4]->AppendColumn(column1); + } + break; } } @@ -1013,6 +1102,8 @@ void MyFrame::OnStyleChange( wxCommandEvent& WXUNUSED(event) ) m_music_model.reset(NULL); else if (nPanel == 1) m_list_model.reset(NULL); + else if (nPanel == 4) + m_long_music_model.reset(NULL); // rebuild the DVC for the selected panel: BuildDataViewCtrl((wxPanel*)m_notebook->GetPage(nPanel), nPanel, style); diff --git a/samples/dataview/mymodels.cpp b/samples/dataview/mymodels.cpp index fcffc59d69..e00ba68099 100644 --- a/samples/dataview/mymodels.cpp +++ b/samples/dataview/mymodels.cpp @@ -312,6 +312,19 @@ unsigned int MyMusicTreeModel::GetChildren( const wxDataViewItem &parent, } +// ---------------------------------------------------------------------------- +// MyLongMusicTreeModel +// ---------------------------------------------------------------------------- + +MyLongMusicTreeModel::MyLongMusicTreeModel() : MyMusicTreeModel() +{ + for (int i = 0; i < 50; i++) + { + AddToClassical("The Four Seasons", "Antonio Vivaldi", 1721); + AddToClassical("La costanza trionfante degl'amori e de gl'odii", "Antonio Vivaldi", 1716); + } +} + // ---------------------------------------------------------------------------- // MyListModel diff --git a/samples/dataview/mymodels.h b/samples/dataview/mymodels.h index 59d3c984a7..db089197a9 100644 --- a/samples/dataview/mymodels.h +++ b/samples/dataview/mymodels.h @@ -188,6 +188,17 @@ private: }; +// ---------------------------------------------------------------------------- +// MyLongMusicTreeModel +// ---------------------------------------------------------------------------- + +class MyLongMusicTreeModel : public MyMusicTreeModel +{ +public: + MyLongMusicTreeModel(); +}; + + // ---------------------------------------------------------------------------- // MyListModel // ---------------------------------------------------------------------------- From a8d89b9cedab655ff3a884aac3b5aa9cf1406a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20G=C3=B6pfert?= Date: Mon, 7 Jan 2019 11:52:04 +0100 Subject: [PATCH 16/16] fixed memory leak --- include/wx/generic/private/rowheightcache.h | 1 + src/generic/rowheightcache.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/wx/generic/private/rowheightcache.h b/include/wx/generic/private/rowheightcache.h index 768edd4687..9136a3863c 100644 --- a/include/wx/generic/private/rowheightcache.h +++ b/include/wx/generic/private/rowheightcache.h @@ -131,6 +131,7 @@ WX_DECLARE_HASH_MAP(unsigned int, RowRanges*, wxIntegerHash, wxIntegerEqual, class WXDLLIMPEXP_CORE HeightCache { public: + ~HeightCache(); bool GetLineStart(unsigned int row, int& start); bool GetLineHeight(unsigned int row, int& height); bool GetLineAt(int y, unsigned int& row); diff --git a/src/generic/rowheightcache.cpp b/src/generic/rowheightcache.cpp index 21529bccb6..3eb0e68a24 100644 --- a/src/generic/rowheightcache.cpp +++ b/src/generic/rowheightcache.cpp @@ -325,3 +325,8 @@ void HeightCache::Clear() } m_heightToRowRange.clear(); } + +HeightCache::~HeightCache() +{ + Clear(); +}