diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 121701be0b..fc46f8354c 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -41,3 +41,28 @@ jobs: - name: Run codespell run: | CODESPELL=$HOME/.local/bin/codespell ./misc/scripts/spellcheck + + + check-whitespace: + runs-on: ubuntu-20.04 + name: Check Whitespace + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Check for trailing whitespace and TABs + run: | + git fetch --depth=1 origin master + git -c core.whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol,tab-in-indent \ + diff --check origin/master \ + ':!Makefile.in' \ + ':!config.guess' \ + ':!config.sub' \ + ':!configure' \ + ':!descrip.mms' \ + ':!install-sh' \ + ':!**/*akefile*' \ + ':!**/*.sln' \ + ':!**/*.vcproj' \ + ':!**/*.xpm' diff --git a/Makefile.in b/Makefile.in index 62beaa8482..74838af176 100644 --- a/Makefile.in +++ b/Makefile.in @@ -949,8 +949,8 @@ MONODLL_CFLAGS = $(__monodll_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(__webviewdll_ext_dir_define_p) \ - $(PIC_FLAG) $(WX_CFLAGS) $(CPPFLAGS) $(CFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(PIC_FLAG) $(WX_CFLAGS) \ + $(CPPFLAGS) $(CFLAGS) MONODLL_CXXFLAGS = $(__monodll_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) $(__INC_REGEX_p) \ $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) \ @@ -959,8 +959,8 @@ MONODLL_CXXFLAGS = $(__monodll_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(__webviewdll_ext_dir_define_p) \ - $(PIC_FLAG) $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(PIC_FLAG) $(WX_CXXFLAGS) \ + $(CPPFLAGS) $(CXXFLAGS) MONODLL_OBJCXXFLAGS = $(__monodll_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_TIFF_p) $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ @@ -969,8 +969,8 @@ MONODLL_OBJCXXFLAGS = $(__monodll_PCH_INC) $(__INC_TIFF_BUILD_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(__webviewdll_ext_dir_define_p) \ - $(PIC_FLAG) $(CPPFLAGS) $(OBJCXXFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 -DWXMAKINGDLL $(PIC_FLAG) $(CPPFLAGS) \ + $(OBJCXXFLAGS) MONODLL_OBJECTS = \ monodll_any.o \ monodll_appbase.o \ @@ -1092,8 +1092,7 @@ MONOLIB_CFLAGS = $(__monolib_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 $(__webviewdll_ext_dir_define_p) $(WX_CFLAGS) \ - $(CPPFLAGS) $(CFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 $(WX_CFLAGS) $(CPPFLAGS) $(CFLAGS) MONOLIB_CXXFLAGS = $(__monolib_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) $(__INC_REGEX_p) \ $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p) \ @@ -1102,8 +1101,7 @@ MONOLIB_CXXFLAGS = $(__monolib_PCH_INC) $(__INC_TIFF_BUILD_p) $(__INC_TIFF_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 $(__webviewdll_ext_dir_define_p) \ - $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) MONOLIB_OBJCXXFLAGS = $(__monolib_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_TIFF_p) $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ @@ -1112,8 +1110,7 @@ MONOLIB_OBJCXXFLAGS = $(__monolib_PCH_INC) $(__INC_TIFF_BUILD_p) \ -I$(top_srcdir)/src/stc/scintilla/include \ -I$(top_srcdir)/src/stc/scintilla/lexlib \ -I$(top_srcdir)/src/stc/scintilla/src -D__WX__ -DSCI_LEXER -DNO_CXX11_REGEX \ - -DLINK_LEXERS -DwxUSE_BASE=1 $(__webviewdll_ext_dir_define_p) $(CPPFLAGS) \ - $(OBJCXXFLAGS) + -DLINK_LEXERS -DwxUSE_BASE=1 $(CPPFLAGS) $(OBJCXXFLAGS) MONOLIB_OBJECTS = \ monolib_any.o \ monolib_appbase.o \ @@ -1669,16 +1666,15 @@ WEBVIEWDLL_CXXFLAGS = $(__webviewdll_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) \ $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) -DWXBUILDING -DWXUSINGDLL \ - -DWXMAKINGDLL_WEBVIEW $(__webviewdll_ext_dir_define_p) \ - $(__webview_additional_include_p) $(PIC_FLAG) $(WX_CXXFLAGS) $(CPPFLAGS) \ - $(CXXFLAGS) + -DWXMAKINGDLL_WEBVIEW $(__webview_additional_include_p) $(PIC_FLAG) \ + $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) WEBVIEWDLL_OBJCXXFLAGS = $(__webviewdll_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_TIFF_p) $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) \ $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) -DWXBUILDING -DWXUSINGDLL \ - -DWXMAKINGDLL_WEBVIEW $(__webviewdll_ext_dir_define_p) \ - $(__webview_additional_include_p) $(PIC_FLAG) $(CPPFLAGS) $(OBJCXXFLAGS) + -DWXMAKINGDLL_WEBVIEW $(__webview_additional_include_p) $(PIC_FLAG) \ + $(CPPFLAGS) $(OBJCXXFLAGS) WEBVIEWDLL_OBJECTS = \ $(__WEBVIEW_SRC_PLATFORM_OBJECTS_2) \ webviewdll_webview.o \ @@ -1691,15 +1687,13 @@ WEBVIEWLIB_CXXFLAGS = $(__webviewlib_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) \ $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) -DWXBUILDING \ - $(__webviewdll_ext_dir_define_p) $(__webview_additional_include_p) \ - $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) + $(__webview_additional_include_p) $(WX_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) WEBVIEWLIB_OBJCXXFLAGS = $(__webviewlib_PCH_INC) $(__INC_TIFF_BUILD_p) \ $(__INC_TIFF_p) $(__INC_JPEG_p) $(__INC_PNG_p) $(__INC_ZLIB_p) \ $(__INC_REGEX_p) $(__INC_EXPAT_p) $(WX_CPPFLAGS) -D__WX$(TOOLKIT)__ \ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) \ $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) -DWXBUILDING \ - $(__webviewdll_ext_dir_define_p) $(__webview_additional_include_p) \ - $(CPPFLAGS) $(OBJCXXFLAGS) + $(__webview_additional_include_p) $(CPPFLAGS) $(OBJCXXFLAGS) WEBVIEWLIB_OBJECTS = \ $(__WEBVIEW_SRC_PLATFORM_OBJECTS_3) \ webviewlib_webview.o \ @@ -13341,9 +13335,6 @@ COND_PLATFORM_MACOSX_1___OSX_LOWLEVEL_SRC_OBJECTS = \ monodll_core_timer.o \ monodll_utilsexc_cf.o @COND_PLATFORM_MACOSX_1@__OSX_LOWLEVEL_SRC_OBJECTS = $(COND_PLATFORM_MACOSX_1___OSX_LOWLEVEL_SRC_OBJECTS) -COND_USE_WEBVIEW_WEBKIT2_1___webviewdll_ext_dir_define_p_0 = --define \ - WX_WEB_EXTENSIONS_DIRECTORY=\"$(PLUGINS_INST_DIR)/web-extensions\" -@COND_USE_WEBVIEW_WEBKIT2_1@__webviewdll_ext_dir_define_p_0 = $(COND_USE_WEBVIEW_WEBKIT2_1___webviewdll_ext_dir_define_p_0) @COND_PLATFORM_MACOSX_1@__PLATFORM_SRC_OBJECTS_0 = monolib_unix_apptraits.o @COND_PLATFORM_UNIX_1@__PLATFORM_SRC_OBJECTS_0 = monolib_unix_apptraits.o COND_PLATFORM_MACOSX_1___OSX_COMMON_SRC_OBJECTS_0 = \ @@ -13758,9 +13749,6 @@ COND_PLATFORM_MACOSX_1___OSX_LOWLEVEL_SRC_OBJECTS_1_4 = \ corelib_core_timer.o \ corelib_utilsexc_cf.o @COND_PLATFORM_MACOSX_1@__OSX_LOWLEVEL_SRC_OBJECTS_1_4 = $(COND_PLATFORM_MACOSX_1___OSX_LOWLEVEL_SRC_OBJECTS_1_4) -@COND_USE_WEBVIEW_WEBKIT2_1@__webviewdll_ext_dir_define_p \ -@COND_USE_WEBVIEW_WEBKIT2_1@ = \ -@COND_USE_WEBVIEW_WEBKIT2_1@ -DWX_WEB_EXTENSIONS_DIRECTORY=\"$(PLUGINS_INST_DIR)/web-extensions\" @COND_TOOLKIT_MSW@__webview_additional_include_p = \ @COND_TOOLKIT_MSW@ -I$(top_srcdir)/3rdparty/webview2/build/native/include @COND_MONOLITHIC_0_SHARED_1_USE_GUI_1_USE_HTML_1@__htmldll_library_link_DEP \ @@ -20872,7 +20860,7 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP) @COND_PLATFORM_MACOSX_1_USE_GUI_1@ $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/html/chm.cpp monodll_version_rc.o: $(srcdir)/src/msw/version.rc $(MONODLL_ODEP) - $(WINDRES) -i$< -o$@ $(__INC_TIFF_BUILD_p_54) $(__INC_TIFF_p_54) $(__INC_JPEG_p_54) $(__INC_PNG_p_53) $(__INC_ZLIB_p_67) $(__INC_REGEX_p_65) $(__INC_EXPAT_p_65) --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)$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG) $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include --include-dir $(top_srcdir)/src/stc/scintilla/include --include-dir $(top_srcdir)/src/stc/scintilla/lexlib --include-dir $(top_srcdir)/src/stc/scintilla/src --define __WX__ --define SCI_LEXER --define NO_CXX11_REGEX --define LINK_LEXERS --define wxUSE_BASE=1 --define WXMAKINGDLL $(__webviewdll_ext_dir_define_p_0) + $(WINDRES) -i$< -o$@ $(__INC_TIFF_BUILD_p_54) $(__INC_TIFF_p_54) $(__INC_JPEG_p_54) $(__INC_PNG_p_53) $(__INC_ZLIB_p_67) $(__INC_REGEX_p_65) $(__INC_EXPAT_p_65) --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)$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG) $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include --include-dir $(top_srcdir)/src/stc/scintilla/include --include-dir $(top_srcdir)/src/stc/scintilla/lexlib --include-dir $(top_srcdir)/src/stc/scintilla/src --define __WX__ --define SCI_LEXER --define NO_CXX11_REGEX --define LINK_LEXERS --define wxUSE_BASE=1 --define WXMAKINGDLL monolib_any.o: $(srcdir)/src/common/any.cpp $(MONOLIB_ODEP) $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/common/any.cpp @@ -36010,7 +35998,7 @@ webviewdll_webviewfshandler.o: $(srcdir)/src/common/webviewfshandler.cpp $(WEBVI $(CXXC) -c -o $@ $(WEBVIEWDLL_CXXFLAGS) $(srcdir)/src/common/webviewfshandler.cpp webviewdll_version_rc.o: $(srcdir)/src/msw/version.rc $(WEBVIEWDLL_ODEP) - $(WINDRES) -i$< -o$@ $(__INC_TIFF_BUILD_p_54) $(__INC_TIFF_p_54) $(__INC_JPEG_p_54) $(__INC_PNG_p_53) $(__INC_ZLIB_p_67) $(__INC_REGEX_p_65) $(__INC_EXPAT_p_65) --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)_webview$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG) $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include --define WXUSINGDLL --define WXMAKINGDLL_WEBVIEW $(__webviewdll_ext_dir_define_p_0) $(__webview_additional_include_p_1) + $(WINDRES) -i$< -o$@ $(__INC_TIFF_BUILD_p_54) $(__INC_TIFF_p_54) $(__INC_JPEG_p_54) $(__INC_PNG_p_53) $(__INC_ZLIB_p_67) $(__INC_REGEX_p_65) $(__INC_EXPAT_p_65) --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)_webview$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG) $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include --define WXUSINGDLL --define WXMAKINGDLL_WEBVIEW $(__webview_additional_include_p_1) webviewlib_webview_ie.o: $(srcdir)/src/msw/webview_ie.cpp $(WEBVIEWLIB_ODEP) $(CXXC) -c -o $@ $(WEBVIEWLIB_CXXFLAGS) $(srcdir)/src/msw/webview_ie.cpp diff --git a/appveyor.yml b/appveyor.yml index 0c75abc35b..0b43c6da5c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,7 @@ skip_commits: - misc/ - include/wx/osx/ - src/osx/ + - .github/workflows/ - .travis.yml - build/tools/travis-ci.sh - build/tools/before_install.sh diff --git a/autoconf_inc.m4 b/autoconf_inc.m4 index eae0f23231..9f06e653b9 100644 --- a/autoconf_inc.m4 +++ b/autoconf_inc.m4 @@ -980,7 +980,7 @@ dnl ### begin block 20_COND_USE_THREADS_1[../../demos/bombs/bombs.bkl,../../demo COND_USE_THREADS_1="" fi AC_SUBST(COND_USE_THREADS_1) -dnl ### begin block 20_COND_USE_WEBVIEW_WEBKIT2_1[../../demos/bombs/bombs.bkl,../../demos/forty/forty.bkl,../../demos/fractal/fractal.bkl,../../demos/life/life.bkl,../../demos/poem/poem.bkl,../../samples/access/access.bkl,../../samples/animate/anitest.bkl,../../samples/archive/archive.bkl,../../samples/artprov/artprov.bkl,../../samples/aui/auidemo.bkl,../../samples/calendar/calendar.bkl,../../samples/caret/caret.bkl,../../samples/clipboard/clipboard.bkl,../../samples/collpane/collpane.bkl,../../samples/combo/combo.bkl,../../samples/config/config.bkl,../../samples/console/console.bkl,../../samples/dataview/dataview.bkl,../../samples/debugrpt/debugrpt.bkl,../../samples/dialogs/dialogs.bkl,../../samples/dialup/dialup.bkl,../../samples/display/display.bkl,../../samples/dll/dll.bkl,../../samples/dnd/dnd.bkl,../../samples/docview/docview.bkl,../../samples/dragimag/dragimag.bkl,../../samples/drawing/drawing.bkl,../../samples/erase/erase.bkl,../../samples/event/event.bkl,../../samples/except/except.bkl,../../samples/exec/exec.bkl,../../samples/font/font.bkl,../../samples/fswatcher/fswatcher.bkl,../../samples/grid/grid.bkl,../../samples/help/help.bkl,../../samples/htlbox/htlbox.bkl,../../samples/html/about/about.bkl,../../samples/html/help/help.bkl,../../samples/html/helpview/helpview.bkl,../../samples/html/htmlctrl/htmlctrl.bkl,../../samples/html/printing/printing.bkl,../../samples/html/test/test.bkl,../../samples/html/virtual/virtual.bkl,../../samples/html/widget/widget.bkl,../../samples/html/zip/zip.bkl,../../samples/image/image.bkl,../../samples/internat/internat.bkl,../../samples/ipc/ipc.bkl,../../samples/joytest/joytest.bkl,../../samples/keyboard/keyboard.bkl,../../samples/layout/layout.bkl,../../samples/listctrl/listctrl.bkl,../../samples/mdi/mdi.bkl,../../samples/mediaplayer/mediaplayer.bkl,../../samples/memcheck/memcheck.bkl,../../samples/menu/menu.bkl,../../samples/minimal/minimal.bkl,../../samples/nativdlg/nativdlg.bkl,../../samples/notebook/notebook.bkl,../../samples/oleauto/oleauto.bkl,../../samples/opengl/cube/cube.bkl,../../samples/opengl/isosurf/isosurf.bkl,../../samples/opengl/penguin/penguin.bkl,../../samples/opengl/pyramid/pyramid.bkl,../../samples/ownerdrw/ownerdrw.bkl,../../samples/popup/popup.bkl,../../samples/power/power.bkl,../../samples/preferences/preferences.bkl,../../samples/printing/printing.bkl,../../samples/propgrid/propgrid.bkl,../../samples/regtest/regtest.bkl,../../samples/render/render.bkl,../../samples/ribbon/ribbon.bkl,../../samples/richtext/richtext.bkl,../../samples/sashtest/sashtest.bkl,../../samples/scroll/scroll.bkl,../../samples/secretstore/secretstore.bkl,../../samples/shaped/shaped.bkl,../../samples/sockets/sockets.bkl,../../samples/sound/sound.bkl,../../samples/splash/splash.bkl,../../samples/splitter/splitter.bkl,../../samples/statbar/statbar.bkl,../../samples/stc/stctest.bkl,../../samples/svg/svgtest.bkl,../../samples/taborder/taborder.bkl,../../samples/taskbar/taskbar.bkl,../../samples/taskbarbutton/taskbarbutton.bkl,../../samples/text/text.bkl,../../samples/thread/thread.bkl,../../samples/toolbar/toolbar.bkl,../../samples/treectrl/treectrl.bkl,../../samples/treelist/treelist.bkl,../../samples/typetest/typetest.bkl,../../samples/uiaction/uiaction.bkl,../../samples/validate/validate.bkl,../../samples/vscroll/vscroll.bkl,../../samples/webview/webview.bkl,../../samples/widgets/widgets.bkl,../../samples/wizard/wizard.bkl,../../samples/wrapsizer/wrapsizer.bkl,../../samples/xrc/xrcdemo.bkl,../../samples/xti/xti.bkl,../../tests/benchmarks/bench.bkl,../../tests/test.bkl,../../utils/emulator/src/emulator.bkl,../../utils/execmon/execmon.bkl,../../utils/helpview/src/helpview.bkl,../../utils/hhp2cached/hhp2cached.bkl,../../utils/ifacecheck/src/ifacecheck.bkl,../../utils/screenshotgen/src/screenshotgen.bkl,../../utils/wxrc/wxrc.bkl,wx.bkl] ### +dnl ### begin block 20_COND_USE_WEBVIEW_WEBKIT2_1[wx.bkl] ### COND_USE_WEBVIEW_WEBKIT2_1="#" if test "x$USE_WEBVIEW_WEBKIT2" = "x1" ; then COND_USE_WEBVIEW_WEBKIT2_1="" diff --git a/build/autogen.mk b/build/autogen.mk index f72b447808..537a3f5438 100644 --- a/build/autogen.mk +++ b/build/autogen.mk @@ -56,7 +56,6 @@ ACLOCAL_SOURCES = \ build/aclocal/ax_func_which_gethostbyname_r.m4 \ build/aclocal/bakefile-lang.m4 \ build/aclocal/bakefile.m4 \ - build/aclocal/cppunit.m4 \ build/aclocal/gst-element-check.m4 \ build/aclocal/gtk-2.0.m4 \ build/aclocal/gtk.m4 \ diff --git a/build/bakefiles/common.bkl b/build/bakefiles/common.bkl index e22e718bbb..62748bbb32 100644 --- a/build/bakefiles/common.bkl +++ b/build/bakefiles/common.bkl @@ -897,9 +897,4 @@ $(TAB)cl /EP /nologo "$(DOLLAR)(InputPath)" > "$(SETUPHDIR)\wx\msw\rcdefs.h" top_srcdir - - WX_WEB_EXTENSIONS_DIRECTORY=\"$(PLUGINS_INST_DIR)/web-extensions\" - - - diff --git a/build/bakefiles/monolithic.bkl b/build/bakefiles/monolithic.bkl index 278a308af4..90cdc085ed 100644 --- a/build/bakefiles/monolithic.bkl +++ b/build/bakefiles/monolithic.bkl @@ -28,7 +28,6 @@ WXMAKINGDLL - $(webviewdll_ext_dir_define) $(EXTRALIBS_XML) $(EXTRALIBS_HTML) $(EXTRALIBS_MEDIA) @@ -40,7 +39,6 @@ - $(webviewdll_ext_dir_define) 8192 diff --git a/build/bakefiles/multilib.bkl b/build/bakefiles/multilib.bkl index 3367df44bc..c378688dd7 100644 --- a/build/bakefiles/multilib.bkl +++ b/build/bakefiles/multilib.bkl @@ -184,7 +184,6 @@ cond="SHARED=='1' and USE_GUI=='1' and USE_WEBVIEW=='1' and MONOLITHIC=='0'"> WXUSINGDLL WXMAKINGDLL_WEBVIEW - $(webviewdll_ext_dir_define) $(WEBVIEW_SRC) coredll basedll @@ -195,7 +194,6 @@ - $(webviewdll_ext_dir_define) $(WEBVIEW_SRC) $(WEBVIEW_HDR) $(webview_additional_include) diff --git a/build/cmake/config.cmake b/build/cmake/config.cmake index 6978cc2725..15dff884ea 100644 --- a/build/cmake/config.cmake +++ b/build/cmake/config.cmake @@ -33,7 +33,12 @@ macro(wx_get_dependencies var lib) get_target_property(deps wx${lib} LINK_LIBRARIES) foreach(dep IN LISTS deps) if(TARGET ${dep}) - get_target_property(dep_name ${dep} OUTPUT_NAME) + get_target_property(dep_type ${dep} TYPE) + if (dep_type STREQUAL "INTERFACE_LIBRARY") + get_target_property(dep_name ${dep} INTERFACE_OUTPUT_NAME) + else() + get_target_property(dep_name ${dep} OUTPUT_NAME) + endif() set(dep_name "-l${dep_name}") else() get_filename_component(dep_name ${dep} NAME) diff --git a/build/cmake/lib/webview/CMakeLists.txt b/build/cmake/lib/webview/CMakeLists.txt index dc9850fa27..1e2e7057a9 100644 --- a/build/cmake/lib/webview/CMakeLists.txt +++ b/build/cmake/lib/webview/CMakeLists.txt @@ -26,10 +26,11 @@ endif() wx_add_library(wxwebview ${WEBVIEW_FILES}) if(WXGTK AND wxUSE_WEBVIEW_WEBKIT2) - set(WX_WEB_EXTENSIONS_DIRECTORY "lib/wx/${wxMAJOR_VERSION}.${wxMINOR_VERSION}/web-extensions") - wx_lib_compile_definitions(wxwebview PRIVATE - -DWX_WEB_EXTENSIONS_DIRECTORY="${CMAKE_INSTALL_PREFIX}/${WX_WEB_EXTENSIONS_DIRECTORY}" - ) + if(wxVERSION_IS_DEV) + set(WX_WEB_EXTENSIONS_DIRECTORY "lib/wx/${wxMAJOR_VERSION}.${wxMINOR_VERSION}.${wxRELEASE_NUMBER}/web-extensions") + else() + set(WX_WEB_EXTENSIONS_DIRECTORY "lib/wx/${wxMAJOR_VERSION}.${wxMINOR_VERSION}/web-extensions") + endif() endif() if(APPLE) diff --git a/build/cmake/tests/gui/CMakeLists.txt b/build/cmake/tests/gui/CMakeLists.txt index c0f62ff77e..61cc082cc6 100644 --- a/build/cmake/tests/gui/CMakeLists.txt +++ b/build/cmake/tests/gui/CMakeLists.txt @@ -22,9 +22,11 @@ set(TEST_GUI_SRC graphics/affinematrix.cpp graphics/boundingbox.cpp graphics/clippingbox.cpp + graphics/coords.cpp graphics/graphmatrix.cpp graphics/graphpath.cpp config/config.cpp + controls/auitest.cpp controls/bitmapcomboboxtest.cpp controls/bitmaptogglebuttontest.cpp controls/bookctrlbasetest.cpp @@ -102,11 +104,13 @@ set(TEST_GUI_SRC net/socket.cpp persistence/tlw.cpp persistence/dataview.cpp + rowheightcache/rowheightcachetest.cpp sizers/boxsizer.cpp sizers/gridsizer.cpp sizers/wrapsizer.cpp toplevel/toplevel.cpp validators/valnum.cpp + validators/valtext.cpp window/clientsize.cpp window/setsize.cpp xml/xrctest.cpp @@ -169,6 +173,9 @@ wx_add_test(test_gui ${TEST_GUI_SRC} RES ../samples/sample.rc ) wx_exe_link_libraries(test_gui wxcore) +if(wxUSE_AUI) + wx_exe_link_libraries(test_gui wxaui) +endif() if(wxUSE_RICHTEXT) wx_exe_link_libraries(test_gui wxrichtext) endif() diff --git a/docs/changes.txt b/docs/changes.txt index a5196548aa..ba14312ee6 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -105,6 +105,8 @@ Changes in behaviour not resulting in compilation errors - wxFileDialog::GetPath() and wxFileDialog::GetFilename() now assert and return an empty string if called on dialogs with the wxFD_MULTIPLE style. +- wxGetInstallPrefix() now returns wxString. + Changes in behaviour which may result in build errors ----------------------------------------------------- diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 36bbbf3132..030a80bc3c 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -384,6 +384,7 @@ GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO +TOC_INCLUDE_HEADINGS = 3 #--------------------------------------------------------------------------- diff --git a/docs/doxygen/images/overview_highdpi_text_144.png b/docs/doxygen/images/overview_highdpi_text_144.png new file mode 100644 index 0000000000..10d430d7b9 Binary files /dev/null and b/docs/doxygen/images/overview_highdpi_text_144.png differ diff --git a/docs/doxygen/images/overview_highdpi_text_72.png b/docs/doxygen/images/overview_highdpi_text_72.png new file mode 100644 index 0000000000..f1173329ed Binary files /dev/null and b/docs/doxygen/images/overview_highdpi_text_72.png differ diff --git a/docs/doxygen/mainpages/topics.h b/docs/doxygen/mainpages/topics.h index 7a0a6228b5..ceb5b4c6ea 100644 --- a/docs/doxygen/mainpages/topics.h +++ b/docs/doxygen/mainpages/topics.h @@ -99,5 +99,6 @@ topics related to building applications with wxWidgets. @li @subpage overview_windowdeletion @li @subpage overview_envvars @li @subpage overview_customwidgets +@li @subpage overview_high_dpi */ diff --git a/docs/doxygen/overviews/high_dpi.md b/docs/doxygen/overviews/high_dpi.md new file mode 100644 index 0000000000..9770a232d2 --- /dev/null +++ b/docs/doxygen/overviews/high_dpi.md @@ -0,0 +1,149 @@ +High DPI Support in wxWidgets {#overview_high_dpi} +============================= +[TOC] + +Introduction +============ + +Many modern displays have way more pixels on the same surface than used to be +the norm, resulting in much higher values of DPI (dots, i.e. pixels, per inch) +than the traditionally used values. This allows to render texts, or geometric +shapes in general much more smoothly. + +As an illustration here are two scaled up views of the same text in 11 pt +Helvetica using up the same space on screen. First on an original Mac display +at 72 dpi, then on a High DPI Display, called "Retina" by Apple with twice as +many pixels in both dimensions (144 dpi), thus 4 times the number of pixels on +the same surface. Using these the contours are much more detailed. + +![11 pt Helvetica at 72 DPI](overview_highdpi_text_72.png) + +![11 pt Helvetica at 144 DPI](overview_highdpi_text_144.png) + +To the user the DPI is typically expressed using a scaling factor, by which the +baseline DPI value is multiplied. For example, MSW systems may use 125% or 150% +scaling, meaning that they use DPI of 120 or 144 respectively, as baseline DPI +value is 96. Similarly, Linux systems may use "2x" scaling, resulting in DPI +value of 192. Macs are slightly different, as even they also may use "2x" +scaling, as in the example above, the effective DPI corresponding to it is 144, +as the baseline value on this platform is 72. + + +The Problem with High DPI Displays +---------------------------------- + +If high DPI displays were treated in the same way as normal ones, existing +applications would look tiny of them. For example, a square window 500 pixels +in size would take half of a standard 1920×1080 ("Full HD") display vertically, +but only a quarter on a 3840×2160 ("4K UHD") display. To prevent this from +happening, most platforms automatically scale the windows by the scaling +factor, defined above, when displaying them on high DPI displays. In this +example, scaling factor is 2 and so the actual size of the window on screen +would become 1000 when automatic scaling is in effect. + +Automatic scaling is convenient, but doesn't really allow the application to +use the extra pixels available on the display. Visually, this means that the +scaled application appears blurry, in contrast to sharper applications using +the full display resolution, so a better solution for interpreting pixel values +on high DPI displays is needed: one which allows to scale some pixel values +(e.g. the total window size), but not some other ones (e.g. those used for +drawing, which should remain unscaled to use the full available resolution). + + +Pixel Values in wxWidgets +========================= + +Logical and Device-Independent Pixels +------------------------------------- + +Some systems like eg Apple's OSes automatically scale all the coordinates by +the DPI scaling factor, however not all systems supported by wxWidgets do it -- +notably, MSW does not. This means that **logical pixels**, in which all +coordinates and sizes are expressed in wxWidgets API, do _not_ have the same +meaning on all platforms when using high DPI displays. So while on macOS you +can always pass in a size of (500,500) to create the window from the previous +paragraph, whatever the resolution of the display is, you would have to +increase this to (1000,1000) on MSW when working on a 200% display. To hide +this difference from the application, wxWidgets provides **device-independent +pixels**, abbreviated as "DIP", that are always of the same size on all +displays and all platforms. + +Thus, the first thing do when preparing your application for high DPI support +is to stop using raw pixel values. Actually, using any pixel values is not +recommended and replacing them with the values based on the text metrics, i.e. +obtained using wxWindow::GetTextExtent(), or expressing them in dialog units +(see wxWindow::ConvertDialogToPixels()) is preferable. However the simplest +change is to just replace the pixel values with the values in DIP: for this, +just use wxWindow::FromDIP() to convert from one to the other. + +For example, if you have the existing code: +```cpp +myFrame->SetClientSize(wxSize(400, 300)); +``` +you can just replace it with +```cpp +myFrame->SetClientSize(myFrame->FromDIP(wxSize(400, 300))); +``` + +Physical Pixels +--------------- + +In addition to (logical) pixels and DIPs discussed above, you may also need to +work in physical pixel coordinates, corresponding to the actual display pixels. +Physical pixels are never scaled, on any platform, and must be used when +drawing graphics elements to ensure that the best possible resolution is used. +For example, all operations on wxGLCanvas use physical pixels. + +To convert between logical and physical pixels, you can use +wxWindow::GetContentScaleFactor(): this is a value greater than or equal to 1, +so a value in logical pixels needs to be multiplied by it in order to obtain +the value in physical pixels. + +For example, in a wxGLCanvas created with the size of 100 (logical) pixels, the +rightmost physical pixel coordinate will be `100*GetContentScaleFactor()`. + + +High-Resolution Images and Artwork +================================== + +In order to benefit from the increased detail on High DPI devices you might want +to provide the images or artwork your application uses in higher resolutions as +well. Note that it is not recommended to just provide a high-resolution version +and let the system scale that down on 1x displays. Apart from performance +consideration also the quality might suffer, contours become more blurry. + +You can use vector based graphics like SVG or you can add the same image at different +sizes / resolutions. + +[comment]: # (TODO: API and Use Cases) + +Platform-Specific Build Issues +============================== + +Generally speaking, all systems handle applications not specifically marked as +being "DPI-aware" by emulating low-resolution display for them and scaling them +up, resulting in blurry graphics and fonts, but globally preserving the +application appearance. For the best results, the application needs to be +explicitly marked as DPI-aware in a platform-dependent way. + +MSW +--- + +The behaviour of the application when running on a high-DPI display depends on +the values in its [manifest][1]. If your application include `wx/msw/wx.rc` +from its resource file, you need to predefine `wxUSE_DPI_AWARE_MANIFEST` to +opt-in into [high DPI support][2]: define it as `1` for minimal DPI awareness and +`2` for full, per-monitor DPI awareness supported by Windows 10 version 1703 or +later. + +[1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests +[2]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows + +macOS +----- + +DPI-aware applications must set their `NSPrincipalClass` to `wxNSApplication` +(or at least `NSApplication`) in their `Info.plist` file. Also see Apple [high +resolution guidelines][2] for more information. + +[2]: https://developer.apple.com/library/archive/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html diff --git a/include/wx/caret.h b/include/wx/caret.h index e6e9728ff8..b625b5f0ac 100644 --- a/include/wx/caret.h +++ b/include/wx/caret.h @@ -153,6 +153,7 @@ protected: m_window = window; m_width = width; m_height = height; + DoSize(); return true; } diff --git a/include/wx/convauto.h b/include/wx/convauto.h index 3da6c6adc4..d7ed45592f 100644 --- a/include/wx/convauto.h +++ b/include/wx/convauto.h @@ -78,6 +78,8 @@ public: virtual size_t GetMBNulLen() const wxOVERRIDE { return m_conv->GetMBNulLen(); } + virtual bool IsUTF8() const wxOVERRIDE { return m_conv && m_conv->IsUTF8(); } + virtual wxMBConv *Clone() const wxOVERRIDE { return new wxConvAuto(*this); } // return the BOM type of this buffer @@ -91,6 +93,14 @@ public: return m_bomType; } + wxFontEncoding GetEncoding() const; + + // Return true if the fall-back encoding is used + bool IsUsingFallbackEncoding() const + { + return m_ownsConv && m_bomType == wxBOM_None; + } + private: // common part of all ctors void Init() diff --git a/include/wx/dc.h b/include/wx/dc.h index 7cf8ba7873..6dcf24c1c3 100644 --- a/include/wx/dc.h +++ b/include/wx/dc.h @@ -325,6 +325,8 @@ public: // coordinates conversions and transforms virtual wxPoint DeviceToLogical(wxCoord x, wxCoord y) const; virtual wxPoint LogicalToDevice(wxCoord x, wxCoord y) const; + virtual wxSize DeviceToLogicalRel(int x, int y) const; + virtual wxSize LogicalToDeviceRel(int x, int y) const; // bounding box @@ -1012,6 +1014,14 @@ public: { return m_pimpl->DeviceToLogicalXRel(x); } wxCoord DeviceToLogicalYRel(wxCoord y) const { return m_pimpl->DeviceToLogicalYRel(y); } + wxPoint DeviceToLogical(const wxPoint& pt) const + { return m_pimpl->DeviceToLogical(pt.x, pt.y); } + wxPoint DeviceToLogical(wxCoord x, wxCoord y) const + { return m_pimpl->DeviceToLogical(x, y); } + wxSize DeviceToLogicalRel(const wxSize& dim) const + { return m_pimpl->DeviceToLogicalRel(dim.x, dim.y); } + wxSize DeviceToLogicalRel(int x, int y) const + { return m_pimpl->DeviceToLogicalRel(x, y); } wxCoord LogicalToDeviceX(wxCoord x) const { return m_pimpl->LogicalToDeviceX(x); } wxCoord LogicalToDeviceY(wxCoord y) const @@ -1020,6 +1030,14 @@ public: { return m_pimpl->LogicalToDeviceXRel(x); } wxCoord LogicalToDeviceYRel(wxCoord y) const { return m_pimpl->LogicalToDeviceYRel(y); } + wxPoint LogicalToDevice(const wxPoint& pt) const + { return m_pimpl->LogicalToDevice(pt.x, pt.y); } + wxPoint LogicalToDevice(wxCoord x, wxCoord y) const + { return m_pimpl->LogicalToDevice(x, y); } + wxSize LogicalToDeviceRel(const wxSize& dim) const + { return m_pimpl->LogicalToDeviceRel(dim.x, dim.y); } + wxSize LogicalToDeviceRel(int x, int y) const + { return m_pimpl->LogicalToDeviceRel(x, y); } void SetMapMode(wxMappingMode mode) { m_pimpl->SetMapMode(mode); } diff --git a/include/wx/dcgraph.h b/include/wx/dcgraph.h index 4622a3534a..e483d58e81 100644 --- a/include/wx/dcgraph.h +++ b/include/wx/dcgraph.h @@ -124,6 +124,8 @@ public: // coordinates conversions and transforms virtual wxPoint DeviceToLogical(wxCoord x, wxCoord y) const wxOVERRIDE; virtual wxPoint LogicalToDevice(wxCoord x, wxCoord y) const wxOVERRIDE; + virtual wxSize DeviceToLogicalRel(int x, int y) const wxOVERRIDE; + virtual wxSize LogicalToDeviceRel(int x, int y) const wxOVERRIDE; // the true implementations virtual bool DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, diff --git a/include/wx/generic/bmpcbox.h b/include/wx/generic/bmpcbox.h index 90a6ba4351..058a389ad9 100644 --- a/include/wx/generic/bmpcbox.h +++ b/include/wx/generic/bmpcbox.h @@ -84,6 +84,8 @@ public: virtual ~wxBitmapComboBox(); + virtual wxString GetStringSelection() const wxOVERRIDE; + // Adds item with image to the end of the combo box. int Append(const wxString& item, const wxBitmap& bitmap = wxNullBitmap); int Append(const wxString& item, const wxBitmap& bitmap, void *clientData); diff --git a/include/wx/generic/private/listctrl.h b/include/wx/generic/private/listctrl.h index c5741b6b13..4087889098 100644 --- a/include/wx/generic/private/listctrl.h +++ b/include/wx/generic/private/listctrl.h @@ -519,14 +519,26 @@ public: // all these functions only do something if the line is currently visible + // Make sure that _line_ is the only item highlighted in the control. + // _oldLine_ is the old focused item. + void HighlightOnly( size_t line, size_t oldLine = (size_t)-1 ); + + // In multiple selection mode, instead of sending one notification per item + // (which is too slow if a lot of items are selected) we send only one notification + // for all of them which is the wxMSW behaviour. Currently done for virtual + // list controls and for deselection only. + enum SendEvent { SendEvent_None, SendEvent_Normal }; + // change the line "selected" state, return true if it really changed - bool HighlightLine( size_t line, bool highlight = true); + bool HighlightLine( size_t line, bool highlight = true, + SendEvent sendEvent = SendEvent_Normal ); // as HighlightLine() but do it for the range of lines: this is incredibly // more efficient for virtual list controls! // // NB: unlike HighlightLine() this one does refresh the lines on screen - void HighlightLines( size_t lineFrom, size_t lineTo, bool on = true ); + void HighlightLines( size_t lineFrom, size_t lineTo, bool on = true, + SendEvent sendEvent = SendEvent_Normal ); // toggle the line state and refresh it void ReverseHighlight( size_t line ) @@ -753,6 +765,16 @@ public: return m_hasFocus; } + void UpdateSelectionCount(bool selected) + { + wxASSERT_MSG( !IsVirtual(), "Can be called for non virtual lists only" ); + + if ( IsSingleSel() ) + return; + + selected ? ++m_selCount : --m_selCount; + } + protected: // the array of all line objects for a non virtual list control (for the // virtual list control we only ever use m_lines[0]) @@ -803,11 +825,18 @@ protected: m_lineBeforeLastClicked, m_lineSelectSingleOnUp; + // Multiple selection extends from the anchor. Not used in single-selection mode. + size_t m_anchor; + bool m_hasCheckBoxes; protected: wxWindow *GetMainWindowOfCompositeControl() wxOVERRIDE { return GetParent(); } + // the total count of items selected in a non virtual list control with + // multiple selections (always 0 otherwise) + size_t m_selCount; + // the total count of items in a virtual list control size_t m_countVirt; @@ -858,6 +887,24 @@ private: // initialize the current item if needed void UpdateCurrent(); + // change the current (== focused) item, without sending any event + // return true if m_current really changed. + bool ChangeCurrentWithoutEvent(size_t current); + + // Trying to activate the current item from keyboard is only possible + // if it is actually selected. We don't send wxEVT_LIST_ITEM_ACTIVATED + // event if it is not, and wxEVT_LIST_KEY_DOWN event should carry -1 + // in this case, as the wxMSW implementation does. + bool ShouldSendEventForCurrent() const + { + return HasCurrent() && IsHighlighted(m_current); + } + + // For multiple selection mode. + // Change the selected range from [anchor, oldCurrent] to [anchor, newCurrent] + // without generating unnecessary wxEVT_LIST_ITEM_{DE}SELECTED events. + void ExtendSelection(size_t oldCurrent, size_t newCurrent); + // delete all items but don't refresh: called from dtor void DoDeleteAllItems(); diff --git a/include/wx/gtk/radiobut.h b/include/wx/gtk/radiobut.h index cf6492a623..8a19a022dd 100644 --- a/include/wx/gtk/radiobut.h +++ b/include/wx/gtk/radiobut.h @@ -13,7 +13,7 @@ // wxRadioButton //----------------------------------------------------------------------------- -class WXDLLIMPEXP_CORE wxRadioButton: public wxControl +class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase { public: wxRadioButton() { } @@ -39,8 +39,8 @@ public: const wxString& name = wxASCII_STR(wxRadioButtonNameStr) ); virtual void SetLabel(const wxString& label) wxOVERRIDE; - virtual void SetValue(bool val); - virtual bool GetValue() const; + virtual void SetValue(bool val) wxOVERRIDE; + virtual bool GetValue() const wxOVERRIDE; static wxVisualAttributes GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); diff --git a/include/wx/gtk/textctrl.h b/include/wx/gtk/textctrl.h index 13bf1a36a5..ebc2f02e0a 100644 --- a/include/wx/gtk/textctrl.h +++ b/include/wx/gtk/textctrl.h @@ -143,6 +143,7 @@ public: GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); void GTKOnTextChanged() wxOVERRIDE; + void GTKAfterLayout(); protected: // overridden wxWindow virtual methods @@ -216,8 +217,9 @@ private: // a dummy one when frozen GtkTextBuffer *m_buffer; - GtkTextMark* m_showPositionOnThaw; + GtkTextMark* m_showPositionDefer; GSList* m_anonymousMarkList; + unsigned m_afterLayoutId; // For wxTE_AUTO_URL void OnUrlMouseEvent(wxMouseEvent&); diff --git a/include/wx/gtk/webview_webkit.h b/include/wx/gtk/webview_webkit.h index acabc88e94..fccdfcd8d8 100644 --- a/include/wx/gtk/webview_webkit.h +++ b/include/wx/gtk/webview_webkit.h @@ -162,6 +162,7 @@ private: #if wxUSE_WEBVIEW_WEBKIT2 bool CanExecuteEditingCommand(const gchar* command) const; void SetupWebExtensionServer(); + GDBusProxy *GetExtensionProxy() const; bool RunScriptSync(const wxString& javascript, wxString* output = NULL); #endif diff --git a/include/wx/gtk1/radiobut.h b/include/wx/gtk1/radiobut.h index d958079729..d467f7100c 100644 --- a/include/wx/gtk1/radiobut.h +++ b/include/wx/gtk1/radiobut.h @@ -13,7 +13,7 @@ // wxRadioButton //----------------------------------------------------------------------------- -class WXDLLIMPEXP_CORE wxRadioButton: public wxControl +class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase { public: wxRadioButton() { } diff --git a/include/wx/html/helpwnd.h b/include/wx/html/helpwnd.h index 54639ad877..ffa8369c41 100644 --- a/include/wx/html/helpwnd.h +++ b/include/wx/html/helpwnd.h @@ -80,7 +80,7 @@ class WXDLLIMPEXP_HTML wxHtmlHelpWindow : public wxWindow public: wxHtmlHelpWindow(wxHtmlHelpData* data = NULL) { Init(data); } - wxHtmlHelpWindow(wxWindow* parent, wxWindowID wxWindowID, + wxHtmlHelpWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, int style = wxTAB_TRAVERSAL|wxNO_BORDER, diff --git a/include/wx/image.h b/include/wx/image.h index d53ff0b116..f73fa70fab 100644 --- a/include/wx/image.h +++ b/include/wx/image.h @@ -73,6 +73,16 @@ enum wxImageResizeQuality wxIMAGE_QUALITY_HIGH = 4 }; +// Constants for wxImage::Paste() for specifying alpha blending option. +enum wxImageAlphaBlendMode +{ + // Overwrite the original alpha values with the ones being pasted. + wxIMAGE_ALPHA_BLEND_OVER = 0, + + // Compose the original alpha values with the ones being pasted. + wxIMAGE_ALPHA_BLEND_COMPOSE = 1 +}; + // alpha channel values: fully transparent, default threshold separating // transparent pixels from opaque for a few functions dealing with alpha and // fully opaque @@ -348,9 +358,12 @@ public: wxImage Size( const wxSize& size, const wxPoint& pos, int r = -1, int g = -1, int b = -1 ) const; - // pastes image into this instance and takes care of - // the mask colour and out of bounds problems - void Paste( const wxImage &image, int x, int y ); + // Copy the data of the given image to the specified position of this one + // taking care of the out of bounds problems. Mask is respected, but alpha + // is simply replaced by default, use wxIMAGE_ALPHA_BLEND_COMPOSE to + // combine it with the original image alpha values if needed. + void Paste(const wxImage& image, int x, int y, + wxImageAlphaBlendMode alphaBlend = wxIMAGE_ALPHA_BLEND_OVER); // return the new image with size width*height wxImage Scale( int width, int height, diff --git a/include/wx/math.h b/include/wx/math.h index 241c3841ea..8c39bb2aa5 100644 --- a/include/wx/math.h +++ b/include/wx/math.h @@ -68,7 +68,7 @@ wxASSERT_MSG(x > (double)INT_MIN - 0.5 && x < (double)INT_MAX + 0.5, wxT("argument out of supported range")); - return std::lround(x); + return (int)std::lround(x); } #else /* C++98 */ diff --git a/include/wx/motif/radiobut.h b/include/wx/motif/radiobut.h index 43414438a7..0233364946 100644 --- a/include/wx/motif/radiobut.h +++ b/include/wx/motif/radiobut.h @@ -11,7 +11,7 @@ #ifndef _WX_RADIOBUT_H_ #define _WX_RADIOBUT_H_ -class WXDLLIMPEXP_CORE wxRadioButton: public wxControl +class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase { wxDECLARE_DYNAMIC_CLASS(wxRadioButton); public: diff --git a/include/wx/msw/dc.h b/include/wx/msw/dc.h index faaab34283..677eeac823 100644 --- a/include/wx/msw/dc.h +++ b/include/wx/msw/dc.h @@ -88,6 +88,8 @@ public: virtual wxPoint DeviceToLogical(wxCoord x, wxCoord y) const wxOVERRIDE; virtual wxPoint LogicalToDevice(wxCoord x, wxCoord y) const wxOVERRIDE; + virtual wxSize DeviceToLogicalRel(int x, int y) const wxOVERRIDE; + virtual wxSize LogicalToDeviceRel(int x, int y) const wxOVERRIDE; #if wxUSE_DC_TRANSFORM_MATRIX virtual bool CanUseTransformMatrix() const wxOVERRIDE; diff --git a/include/wx/msw/radiobut.h b/include/wx/msw/radiobut.h index a568316edf..765f6bcc8f 100644 --- a/include/wx/msw/radiobut.h +++ b/include/wx/msw/radiobut.h @@ -13,7 +13,7 @@ #include "wx/msw/ownerdrawnbutton.h" -class WXDLLIMPEXP_CORE wxRadioButton : public wxMSWOwnerDrawnButton +class WXDLLIMPEXP_CORE wxRadioButton : public wxMSWOwnerDrawnButton { public: // ctors and creation functions @@ -43,8 +43,8 @@ public: const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); // implement the radio button interface - virtual void SetValue(bool value); - virtual bool GetValue() const; + virtual void SetValue(bool value) wxOVERRIDE; + virtual bool GetValue() const wxOVERRIDE; // implementation only from now on virtual bool MSWCommand(WXUINT param, WXWORD id) wxOVERRIDE; diff --git a/include/wx/osx/radiobut.h b/include/wx/osx/radiobut.h index e968d80acd..3934f839f8 100644 --- a/include/wx/osx/radiobut.h +++ b/include/wx/osx/radiobut.h @@ -11,7 +11,7 @@ #ifndef _WX_RADIOBUT_H_ #define _WX_RADIOBUT_H_ -class WXDLLIMPEXP_CORE wxRadioButton: public wxControl +class WXDLLIMPEXP_CORE wxRadioButton: public wxRadioButtonBase { wxDECLARE_DYNAMIC_CLASS(wxRadioButton); @@ -35,8 +35,8 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); - virtual void SetValue(bool val); - virtual bool GetValue() const ; + virtual void SetValue(bool val) wxOVERRIDE; + virtual bool GetValue() const wxOVERRIDE; // implementation diff --git a/include/wx/private/unicode.h b/include/wx/private/unicode.h new file mode 100644 index 0000000000..6c81c23504 --- /dev/null +++ b/include/wx/private/unicode.h @@ -0,0 +1,16 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/private/unicode.h +// Purpose: Unicode private declsrations +// Author: Pavel Tyunin +// Created: 2020-10-06 +// Copyright: (c) 2020 Pavel Tyunin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_UNICODEH__ +#define _WX_PRIVATE_UNICODEH__ + +// this table gives the length of the UTF-8 encoding from its first character: +extern const unsigned char tableUtf8Lengths[256]; + +#endif // _WX_PRIVATE_UNICODEH__ diff --git a/include/wx/propgrid/propgrid.h b/include/wx/propgrid/propgrid.h index 7a34b8f4f1..6f2bff9894 100644 --- a/include/wx/propgrid/propgrid.h +++ b/include/wx/propgrid/propgrid.h @@ -1591,11 +1591,12 @@ protected: // 0 = not dragging, 1 = drag just started, 2 = drag in progress unsigned char m_dragStatus; +#if WXWIN_COMPATIBILITY_3_0 + // Unused variable. // 0 = margin, 1 = label, 2 = value. unsigned char m_mouseSide; // True when editor control is focused. -#if WXWIN_COMPATIBILITY_3_0 unsigned char m_editorFocused; #else bool m_editorFocused; @@ -1634,8 +1635,8 @@ protected: int m_clearThisMany; #endif - // Mouse is hovering over this column (index) - unsigned int m_colHover; + // Mouse is hovering over this column (index), -1 for margin + int m_colHover; // pointer to property that has mouse hovering wxPGProperty* m_propHover; diff --git a/include/wx/qt/radiobut.h b/include/wx/qt/radiobut.h index 5a1ab5ad14..145b97a4ea 100644 --- a/include/wx/qt/radiobut.h +++ b/include/wx/qt/radiobut.h @@ -10,7 +10,7 @@ class QRadioButton; -class WXDLLIMPEXP_CORE wxRadioButton : public wxControl +class WXDLLIMPEXP_CORE wxRadioButton : public wxRadioButtonBase { public: wxRadioButton(); @@ -32,8 +32,8 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxASCII_STR(wxRadioButtonNameStr) ); - virtual void SetValue(bool value); - virtual bool GetValue() const; + virtual void SetValue(bool value) wxOVERRIDE; + virtual bool GetValue() const wxOVERRIDE; virtual QWidget *GetHandle() const wxOVERRIDE; diff --git a/include/wx/radiobut.h b/include/wx/radiobut.h index 11f466cdbe..f4cf3b1a09 100644 --- a/include/wx/radiobut.h +++ b/include/wx/radiobut.h @@ -15,23 +15,42 @@ #if wxUSE_RADIOBTN -/* - There is no wxRadioButtonBase class as wxRadioButton interface is the same - as wxCheckBox(Base), but under some platforms wxRadioButton really - derives from wxCheckBox and on the others it doesn't. - - The pseudo-declaration of wxRadioButtonBase would look like this: - - class wxRadioButtonBase : public ... - { - public: - virtual void SetValue(bool value); - virtual bool GetValue() const; - }; - */ - #include "wx/control.h" +class WXDLLIMPEXP_FWD_CORE wxRadioButton; + +// TODO: In wxUniv, wxRadioButton must derive from wxCheckBox as it reuses +// much of its code. This should be fixed by refactoring wxCheckBox to allow +// this class to reuse its functionality without inheriting from it, but for +// now use this hack to allow the existing code to compile. +#ifdef __WXUNIVERSAL__ + #include "wx/checkbox.h" + + typedef wxCheckBox wxRadioButtonBaseBase; +#else + typedef wxControl wxRadioButtonBaseBase; +#endif + +class WXDLLIMPEXP_CORE wxRadioButtonBase : public wxRadioButtonBaseBase +{ +public: + wxRadioButtonBase() { } + + // Methods to be implemented by the derived classes: + virtual void SetValue(bool value) = 0; + virtual bool GetValue() const = 0; + + + // Methods implemented by this class itself. + wxRadioButton* GetFirstInGroup() const; + wxRadioButton* GetLastInGroup() const; + wxRadioButton* GetPreviousInGroup() const; + wxRadioButton* GetNextInGroup() const; + +private: + wxDECLARE_NO_COPY_CLASS(wxRadioButtonBase); +}; + extern WXDLLIMPEXP_DATA_CORE(const char) wxRadioButtonNameStr[]; #if defined(__WXUNIVERSAL__) diff --git a/include/wx/stringops.h b/include/wx/stringops.h index 150554f341..dd46e6616c 100644 --- a/include/wx/stringops.h +++ b/include/wx/stringops.h @@ -94,15 +94,15 @@ struct WXDLLIMPEXP_BASE wxStringOperationsUtf8 return (c <= 0x7F) || (c >= 0xC2 && c <= 0xF4); } - // table of offsets to skip forward when iterating over UTF-8 sequence - static const unsigned char ms_utf8IterTable[256]; + // returns offset to skip forward when iterating over UTF-8 sequence + static unsigned char GetUTF8IterOffset(unsigned char c); template static void IncIter(Iterator& i) { wxASSERT( IsValidUtf8LeadByte(*i) ); - i += ms_utf8IterTable[(unsigned char)*i]; + i += GetUTF8IterOffset(*i); } template @@ -178,7 +178,7 @@ struct WXDLLIMPEXP_BASE wxStringOperationsUtf8 static size_t GetUtf8CharLength(char c) { wxASSERT( IsValidUtf8LeadByte(c) ); - return ms_utf8IterTable[(unsigned char)c]; + return GetUTF8IterOffset(c); } // decodes single UTF-8 character from UTF-8 string diff --git a/include/wx/tbarbase.h b/include/wx/tbarbase.h index 6d1b423015..a4b380d6ee 100644 --- a/include/wx/tbarbase.h +++ b/include/wx/tbarbase.h @@ -295,10 +295,10 @@ public: wxItemKind kind = wxITEM_NORMAL, const wxString& shortHelp = wxEmptyString, const wxString& longHelp = wxEmptyString, - wxObject *data = NULL) + wxObject *clientData = NULL) { return DoAddTool(toolid, label, bitmap, bmpDisabled, kind, - shortHelp, longHelp, data); + shortHelp, longHelp, clientData); } // the most common AddTool() version @@ -318,10 +318,10 @@ public: const wxBitmap& bmpDisabled = wxNullBitmap, const wxString& shortHelp = wxEmptyString, const wxString& longHelp = wxEmptyString, - wxObject *data = NULL) + wxObject *clientData = NULL) { return AddTool(toolid, label, bitmap, bmpDisabled, wxITEM_CHECK, - shortHelp, longHelp, data); + shortHelp, longHelp, clientData); } // add a radio tool, i.e. a tool which can be toggled and releases any @@ -332,10 +332,10 @@ public: const wxBitmap& bmpDisabled = wxNullBitmap, const wxString& shortHelp = wxEmptyString, const wxString& longHelp = wxEmptyString, - wxObject *data = NULL) + wxObject *clientData = NULL) { return AddTool(toolid, label, bitmap, bmpDisabled, wxITEM_RADIO, - shortHelp, longHelp, data); + shortHelp, longHelp, clientData); } diff --git a/include/wx/univ/radiobut.h b/include/wx/univ/radiobut.h index ae16aa25ce..711e0a676c 100644 --- a/include/wx/univ/radiobut.h +++ b/include/wx/univ/radiobut.h @@ -11,13 +11,11 @@ #ifndef _WX_UNIV_RADIOBUT_H_ #define _WX_UNIV_RADIOBUT_H_ -#include "wx/checkbox.h" - // ---------------------------------------------------------------------------- // wxRadioButton // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_CORE wxRadioButton : public wxCheckBox +class WXDLLIMPEXP_CORE wxRadioButton : public wxRadioButtonBase { public: // constructors @@ -46,6 +44,10 @@ public: const wxValidator& validator = wxDefaultValidator, const wxString& name = wxASCII_STR(wxRadioButtonNameStr)); + // (re)implement pure virtuals from wxRadioButtonBase + virtual void SetValue(bool value) wxOVERRIDE { return wxCheckBox::SetValue(value); } + virtual bool GetValue() const wxOVERRIDE { return wxCheckBox::GetValue(); } + // override some base class methods virtual void ChangeValue(bool value) wxOVERRIDE; diff --git a/include/wx/utils.h b/include/wx/utils.h index c6d35299c2..3f4aea8daf 100644 --- a/include/wx/utils.h +++ b/include/wx/utils.h @@ -162,7 +162,7 @@ WXDLLIMPEXP_BASE wxLinuxDistributionInfo wxGetLinuxDistributionInfo(); WXDLLIMPEXP_BASE wxString wxNow(); // Return path where wxWidgets is installed (mostly useful in Unices) -WXDLLIMPEXP_BASE const wxChar *wxGetInstallPrefix(); +WXDLLIMPEXP_BASE wxString wxGetInstallPrefix(); // Return path to wxWin data (/usr/share/wx/%{version}) (Unices) WXDLLIMPEXP_BASE wxString wxGetDataDir(); diff --git a/include/wx/xml/xml.h b/include/wx/xml/xml.h index a596ae92e4..a816ce22ff 100644 --- a/include/wx/xml/xml.h +++ b/include/wx/xml/xml.h @@ -241,10 +241,12 @@ class WXDLLIMPEXP_XML wxXmlDoctype { public: explicit - wxXmlDoctype(const wxString& name = wxString(), - const wxString& sysid = wxString(), - const wxString& pubid = wxString()) - : m_rootName(name), m_systemId(sysid), m_publicId(pubid) + wxXmlDoctype(const wxString& rootName = wxString(), + const wxString& systemId = wxString(), + const wxString& publicId = wxString()) + : m_rootName(rootName), + m_systemId(systemId), + m_publicId(publicId) {} // Default copy ctor and assignment operators are ok. diff --git a/interface/wx/clipbrd.h b/interface/wx/clipbrd.h index c8fb6fc277..ab9ed206cb 100644 --- a/interface/wx/clipbrd.h +++ b/interface/wx/clipbrd.h @@ -57,7 +57,7 @@ the end-user's machine. In order for the clipboard data to persist after the window closes, a clipboard manager must be installed. Some clipboard managers will automatically flush the clipboard after each new piece of - data is added, while others will not. The @Flush() function will force + data is added, while others will not. The Flush() function will force the clipboard manager to flush the data. @library{wxcore} diff --git a/interface/wx/convauto.h b/interface/wx/convauto.h index 7ddfb26927..324b5b24e7 100644 --- a/interface/wx/convauto.h +++ b/interface/wx/convauto.h @@ -146,6 +146,22 @@ public: */ wxBOM GetBOM() const; + /** + Return the detected encoding + + Returns @c wxFONTENCODING_MAX if called before the first use. + + @since 3.1.5 + */ + wxBOM GetEncoding() const; + + /** + Check if the fall-back encoding is used. + + @since 3.1.5 + */ + bool IsUsingFallbackEncoding() const; + /** Return a pointer to the characters that makes up this BOM. diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index f975b5c286..2abf7e778b 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -73,9 +73,10 @@ associating the model with a control like this: @code - wxDataViewCtrl *musicCtrl = new wxDataViewCtrl( this, wxID_ANY ); + wxDataViewCtrl *musicCtrl = new wxDataViewCtrl(this, wxID_ANY); wxDataViewModel *musicModel = new MyMusicModel; - m_musicCtrl->AssociateModel( musicModel ); + + musicCtrl->AssociateModel(musicModel); musicModel->DecRef(); // avoid memory leak !! // add columns now @@ -84,11 +85,10 @@ A potentially better way to avoid memory leaks is to use wxObjectDataPtr @code - wxObjectDataPtr musicModel; - - wxDataViewCtrl *musicCtrl = new wxDataViewCtrl( this, wxID_ANY ); - musicModel = new MyMusicModel; - m_musicCtrl->AssociateModel( musicModel.get() ); + wxDataViewCtrl *musicCtrl = new wxDataViewCtrl(this, wxID_ANY); + wxObjectDataPtr musicModel(new MyMusicModel); + + musicCtrl->AssociateModel(musicModel.get()); // add columns now @endcode diff --git a/interface/wx/dc.h b/interface/wx/dc.h index bf1ca7db64..e5af4c2491 100644 --- a/interface/wx/dc.h +++ b/interface/wx/dc.h @@ -206,55 +206,151 @@ public: /** Convert @e device X coordinate to logical coordinate, using the current mapping mode, user scale factor, device origin and axis orientation. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord DeviceToLogicalX(wxCoord x) const; /** Convert @e device X coordinate to relative logical coordinate, using the current mapping mode and user scale factor but ignoring the - axis orientation. Use this for converting a width, for example. + axis orientation. Use this for converting a horizontal distance like + for example a width. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord DeviceToLogicalXRel(wxCoord x) const; /** Converts @e device Y coordinate to logical coordinate, using the current mapping mode, user scale factor, device origin and axis orientation. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord DeviceToLogicalY(wxCoord y) const; /** Convert @e device Y coordinate to relative logical coordinate, using the current mapping mode and user scale factor but ignoring the - axis orientation. Use this for converting a height, for example. + axis orientation. Use this for converting a vertical distance like + for example a height. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord DeviceToLogicalYRel(wxCoord y) const; /** Converts logical X coordinate to device coordinate, using the current mapping mode, user scale factor, device origin and axis orientation. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord LogicalToDeviceX(wxCoord x) const; /** Converts logical X coordinate to relative device coordinate, using the current mapping mode and user scale factor but ignoring the - axis orientation. Use this for converting a width, for example. + axis orientation. Use this for converting a horizontal distance like + for example a width. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord LogicalToDeviceXRel(wxCoord x) const; /** Converts logical Y coordinate to device coordinate, using the current mapping mode, user scale factor, device origin and axis orientation. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord LogicalToDeviceY(wxCoord y) const; /** Converts logical Y coordinate to relative device coordinate, using the current mapping mode and user scale factor but ignoring the - axis orientation. Use this for converting a height, for example. + axis orientation. Use this for converting a vertical distance like + for example a height. + + @note Affine transformation applied to the coordinate system + with SetTransformMatrix() is not taken into account. */ wxCoord LogicalToDeviceYRel(wxCoord y) const; + /** + Converts device (@a x, @a y) coordinates to logical coordinates + taking into account all applied transformations like the current + mapping mode, scale factors, device origin, axes orientation, + affine transformation. + + @since 3.1.5 + */ + wxPoint DeviceToLogical(wxCoord x, wxCoord y) const; + + /** + @overload + + @since 3.1.5 + */ + wxPoint DeviceToLogical(const wxPoint& pt) const; + + /** + Converts device @a x, @a y coordinates to relative logical coordinates + taking into account all applied transformations like the current + mapping mode, scale factors, affine transformation. + Use this for converting distances like e.g. width and height. + + @since 3.1.5 + */ + wxSize DeviceToLogicalRel(int x, int y) const; + + /** + @overload + + @since 3.1.5 + */ + wxSize DeviceToLogicalRel(const wxSize& dim) const; + + /** + Converts logical (@a x, @a y) coordinates to device coordinates + taking into account all applied transformations like the current + mapping mode, scale factors, device origin, axes orientation, + affine transformation. + + @since 3.1.5 + */ + wxPoint LogicalToDevice(wxCoord x, wxCoord y) const; + + /** + @overload + + @since 3.1.5 + */ + wxPoint LogicalToDevice(const wxPoint& pt) const; + + /** + Converts logical @a x, @a y coordinates to relative device coordinates + taking into account all applied transformations like the current + mapping mode, scale factors, affine transformation. + Use this for converting distances like e.g. width and height. + + @since 3.1.5 + */ + wxSize LogicalToDeviceRel(int x, int y) const; + + /** + @overload + + @since 3.1.5 + */ + wxSize LogicalToDeviceRel(const wxSize& dim) const; + //@} diff --git a/interface/wx/html/helpwnd.h b/interface/wx/html/helpwnd.h index e2b17d7fbe..99f67fc0e5 100644 --- a/interface/wx/html/helpwnd.h +++ b/interface/wx/html/helpwnd.h @@ -77,7 +77,7 @@ public: For the values of @a helpStyle, please see the documentation for wxHtmlHelpController. */ - wxHtmlHelpWindow(wxWindow* parent, int wxWindowID, + wxHtmlHelpWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, int style = wxTAB_TRAVERSAL|wxBORDER_NONE, diff --git a/interface/wx/image.h b/interface/wx/image.h index 57bee46b41..1c7288bd82 100644 --- a/interface/wx/image.h +++ b/interface/wx/image.h @@ -60,6 +60,21 @@ enum wxImageResizeQuality wxIMAGE_QUALITY_HIGH }; + +/** + Constants for wxImage::Paste() for specifying alpha blending option. + + @since 3.1.5 +*/ +enum wxImageAlphaBlendMode +{ + /// Overwrite the original alpha values with the ones being pasted. + wxIMAGE_ALPHA_BLEND_OVER = 0, + + /// Compose the original alpha values with the ones being pasted. + wxIMAGE_ALPHA_BLEND_COMPOSE = 1 +}; + /** Possible values for PNG image type option. @@ -803,8 +818,17 @@ public: /** Copy the data of the given @a image to the specified position in this image. + + Takes care of the mask colour and out of bounds problems. + + @param alphaBlend + This parameter (new in wx 3.1.5) determines whether the alpha values + of the original image replace (default) or are composed with the + alpha channel of this image. Notice that alpha blending overrides + the mask handling. */ - void Paste(const wxImage& image, int x, int y); + void Paste(const wxImage& image, int x, int y, + wxImageAlphaBlendMode alphaBlend = wxIMAGE_ALPHA_BLEND_OVER); /** Replaces the colour specified by @e r1,g1,b1 by the colour @e r2,g2,b2. diff --git a/interface/wx/radiobut.h b/interface/wx/radiobut.h index d037412e55..0a42c1c0f9 100644 --- a/interface/wx/radiobut.h +++ b/interface/wx/radiobut.h @@ -12,18 +12,28 @@ mutually exclusive options. It has a text label next to a (usually) round button. - You can create a group of mutually-exclusive radio buttons by specifying - @c wxRB_GROUP for the first in the group. The group ends when another - radio button group is created, or there are no more radio buttons. + Radio buttons are typically used in groups of mutually-exclusive buttons, + i.e. exactly one of the buttons in the group is checked, and the other ones + are unchecked automatically. Such groups are created implicitly, but can + also be started explicitly by using @c wxRB_GROUP style: a button with this + style starts a new group and will become the initial selection in this + group. Alternatively, a radio button may be excluded from the group that it + would otherwise belong to by using @c wxRB_SINGLE style. + + To find the other elements of the same radio button group, you can use + GetFirstInGroup(), GetPreviousInGroup(), GetNextInGroup() and + GetLastInGroup() functions. + @beginStyleTable @style{wxRB_GROUP} Marks the beginning of a new group of radio buttons. @style{wxRB_SINGLE} - In some circumstances, radio buttons that are not consecutive - siblings trigger a hang bug in Windows (only). If this happens, add - this style to mark the button as not belonging to a group, and - implement the mutually-exclusive group behaviour yourself. + Creates a radio button which is not part of any radio button group. + When this style is used, no other radio buttons will be turned off + automatically when this button is turned on and such behaviour will + need to be implemented manually, in the event handler for this + button. @endStyleTable @beginEventEmissionTable{wxCommandEvent} @@ -119,5 +129,64 @@ public: @true to check, @false to uncheck. */ virtual void SetValue(bool value); + + /** + Returns the first button of the radio button group this button belongs + to. + + For a radio button with @c wxRB_SINGLE style, this function returns this + button itself, as it is the only member of its group. Otherwise, the + function returns the closest previous radio button with @c wxRB_GROUP + style (which could still be this button itself) or the first radio + button in the same window. + + The returned value is never @NULL. + + @see GetPreviousInGroup(), GetNextInGroup(), GetLastInGroup() + + @since 3.1.5 + */ + wxRadioButton* GetFirstInGroup() const; + + /** + Returns the last button of the radio button group this button belongs + to. + + Similarly to GetFirstInGroup(), this function returns this button + itself if it has @c wxRB_SINGLE style. Otherwise, the function returns + the last button before the next button with @c wxRB_GROUP style or the + last radio button in the same window. + + The returned value is never @NULL. + + @see GetPreviousInGroup(), GetNextInGroup() + + @since 3.1.5 + */ + wxRadioButton* GetLastInGroup() const; + + /** + Returns the previous radio button in the same group. + + The return value is @NULL if there is no predecessor or if this button + has @c wxRB_SINGLE style. + + @see GetFirstInGroup(), GetNextInGroup(), GetLastInGroup() + + @since 3.1.5 + */ + wxRadioButton* GetPreviousInGroup() const; + + /** + Returns the next radio button in the same group. + + The return value is @NULL if there is no successor or if this button + has @c wxRB_SINGLE style. + + @see GetFirstInGroup(), GetPreviousInGroup(), GetLastInGroup() + + @since 3.1.5 + */ + wxRadioButton* GetNextInGroup() const; }; diff --git a/interface/wx/ribbon/art.h b/interface/wx/ribbon/art.h index 167f440c3a..d551f3466f 100644 --- a/interface/wx/ribbon/art.h +++ b/interface/wx/ribbon/art.h @@ -1251,6 +1251,12 @@ public: wxRect* normal_region, wxRect* dropdown_region); + wxCoord GetButtonBarButtonTextWidth( + wxDC& dc, + const wxString& label, + wxRibbonButtonKind kind, + wxRibbonButtonBarButtonState size); + wxSize GetMinimisedPanelMinimumSize( wxDC& dc, const wxRibbonPanel* wnd, diff --git a/interface/wx/ribbon/toolbar.h b/interface/wx/ribbon/toolbar.h index cb6045d665..77fff36f0e 100644 --- a/interface/wx/ribbon/toolbar.h +++ b/interface/wx/ribbon/toolbar.h @@ -130,7 +130,7 @@ public: The UI help string to associate with the new tool. @param kind The kind of tool to add. - @param client_data + @param clientData Client data to associate with the new tool. @return An opaque pointer which can be used only with other tool bar @@ -144,7 +144,7 @@ public: const wxBitmap& bitmap_disabled = wxNullBitmap, const wxString& help_string = wxEmptyString, wxRibbonButtonKind kind = wxRIBBON_BUTTON_NORMAL, - wxObject* client_data = NULL); + wxObject* clientData = NULL); /** Add a separator to the tool bar. @@ -232,7 +232,7 @@ public: The UI help string to associate with the new tool. @param kind The kind of tool to add. - @param client_data + @param clientData Client data to associate with the new tool. @return An opaque pointer which can be used only with other tool bar @@ -249,7 +249,7 @@ public: const wxBitmap& bitmap_disabled = wxNullBitmap, const wxString& help_string = wxEmptyString, wxRibbonButtonKind kind = wxRIBBON_BUTTON_NORMAL, - wxObject* client_data = NULL); + wxObject* clientData = NULL); /** Insert a separator to the tool bar at the specified position. diff --git a/interface/wx/toolbar.h b/interface/wx/toolbar.h index 84506d9589..3cdb064c73 100644 --- a/interface/wx/toolbar.h +++ b/interface/wx/toolbar.h @@ -462,9 +462,9 @@ public: whenever another button in the group is checked. ::wxITEM_DROPDOWN specifies that a drop-down menu button will appear next to the tool button (only GTK+ and MSW). Call SetDropdownMenu() afterwards. - @param shortHelpString + @param shortHelp This string is used for the tools tooltip. - @param longHelpString + @param longHelp This string is shown in the statusbar (if any) of the parent frame when the mouse pointer is inside the tool. @param clientData @@ -481,8 +481,8 @@ public: const wxBitmap& bitmap, const wxBitmap& bmpDisabled, wxItemKind kind = wxITEM_NORMAL, - const wxString& shortHelpString = wxEmptyString, - const wxString& longHelpString = wxEmptyString, + const wxString& shortHelp = wxEmptyString, + const wxString& longHelp = wxEmptyString, wxObject* clientData = NULL); //@} diff --git a/interface/wx/xml/xml.h b/interface/wx/xml/xml.h index 0eaad72423..4c58080b75 100644 --- a/interface/wx/xml/xml.h +++ b/interface/wx/xml/xml.h @@ -494,16 +494,16 @@ public: /** Creates and possible initializes the DOCTYPE. - @param name + @param rootName The root name. - @param sysid + @param systemId The system identifier. - @param pubid + @param publicId The public identifier. */ - wxXmlDoctype(const wxString& name = wxString(), - const wxString& sysid = wxString(), - const wxString& pubid = wxString()); + wxXmlDoctype(const wxString& rootName = wxString(), + const wxString& systemId = wxString(), + const wxString& publicId = wxString()); /** Removes all the DOCTYPE values. diff --git a/samples/drawing/drawing.cpp b/samples/drawing/drawing.cpp index acd2eeaa47..89e785a6ba 100644 --- a/samples/drawing/drawing.cpp +++ b/samples/drawing/drawing.cpp @@ -1338,8 +1338,8 @@ void MyCanvas::DrawSplines(wxDC& dc) { angle += angles[ angle_pos ]; int r = R * radii[ radius_pos ] / 100; - pts[ i ].x = center.x + (wxCoord)( r * cos( M_PI * angle / 180.0) ); - pts[ i ].y = center.y + (wxCoord)( r * sin( M_PI * angle / 180.0) ); + pts[ i ].x = center.x + (wxCoord)( r * cos(wxDegToRad(angle)) ); + pts[ i ].y = center.y + (wxCoord)( r * sin(wxDegToRad(angle)) ); angle_pos++; if ( angle_pos >= WXSIZEOF(angles) ) angle_pos = 0; @@ -2013,11 +2013,9 @@ void MyCanvas::OnMouseMove(wxMouseEvent &event) PrepareDC(dc); m_owner->PrepareDC(dc); - wxPoint pos = event.GetPosition(); - long x = dc.DeviceToLogicalX( pos.x ); - long y = dc.DeviceToLogicalY( pos.y ); + wxPoint pos = dc.DeviceToLogical(event.GetPosition()); wxString str; - str.Printf( "Current mouse position: %d,%d", (int)x, (int)y ); + str.Printf( "Current mouse position: %d,%d", pos.x, pos.y ); m_owner->SetStatusText( str ); } diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 1c4213098b..4ef123fa73 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -564,6 +564,15 @@ void MyFrame::InitWithReportItems() itemCol.SetAlign(wxLIST_FORMAT_RIGHT); m_listCtrl->InsertColumn(2, itemCol); + if ( m_numListItems <= 0 ) + { + m_listCtrl->SetColumnWidth( 0, 100 ); + m_listCtrl->SetColumnWidth( 1, wxLIST_AUTOSIZE ); + m_listCtrl->SetColumnWidth( 2, wxLIST_AUTOSIZE_USEHEADER ); + + return; + } + // to speed up inserting we hide the control temporarily m_listCtrl->Hide(); @@ -584,25 +593,38 @@ void MyFrame::InitWithReportItems() item.SetTextColour(*wxRED); m_listCtrl->SetItem( item ); - item.m_itemId = 2; - item.SetTextColour(*wxGREEN); - m_listCtrl->SetItem( item ); - item.m_itemId = 4; - item.SetTextColour(*wxLIGHT_GREY); - item.SetFont(*wxITALIC_FONT); - item.SetBackgroundColour(*wxRED); - m_listCtrl->SetItem( item ); + if ( m_numListItems > 2 ) + { + item.m_itemId = 2; + item.SetTextColour(*wxGREEN); + m_listCtrl->SetItem( item ); + } + + if ( m_numListItems > 4 ) + { + item.m_itemId = 4; + item.SetTextColour(*wxLIGHT_GREY); + item.SetFont(*wxITALIC_FONT); + item.SetBackgroundColour(*wxRED); + m_listCtrl->SetItem( item ); + } m_listCtrl->SetTextColour(*wxBLUE); - // Set images in columns - m_listCtrl->SetItemColumnImage(1, 1, 0); + if ( m_numListItems > 1 ) + { + // Set images in columns + m_listCtrl->SetItemColumnImage(1, 1, 0); + } - wxListItem info; - info.SetImage(0); - info.SetId(3); - info.SetColumn(2); - m_listCtrl->SetItem(info); + if ( m_numListItems > 3 ) + { + wxListItem info; + info.SetImage(0); + info.SetId(3); + info.SetColumn(2); + m_listCtrl->SetItem(info); + } // test SetItemFont too m_listCtrl->SetItemFont(0, *wxITALIC_FONT); @@ -1073,6 +1095,11 @@ void MyListCtrl::OnColClick(wxListEvent& event) { int col = event.GetColumn(); + if ( col == -1 ) + { + return; // clicked outside any column. + } + // set or unset image static bool x = false; x = !x; @@ -1409,8 +1436,6 @@ void MyListCtrl::OnListKeyDown(wxListEvent& event) wxFALLTHROUGH; default: - LogEvent(event, "OnListKeyDown"); - event.Skip(); } } @@ -1442,7 +1467,6 @@ void MyListCtrl::OnRightClick(wxMouseEvent& event) case wxLIST_HITTEST_NOWHERE: where = "nowhere near"; break; case wxLIST_HITTEST_ONITEMICON: where = "on icon of"; break; case wxLIST_HITTEST_ONITEMLABEL: where = "on label of"; break; - case wxLIST_HITTEST_ONITEMRIGHT: where = "right on"; break; case wxLIST_HITTEST_TOLEFT: where = "to the left of"; break; case wxLIST_HITTEST_TORIGHT: where = "to the right of"; break; default: where = "not clear exactly where on"; break; diff --git a/samples/widgets/slider.cpp b/samples/widgets/slider.cpp index 32bf2fad60..383be8b14a 100644 --- a/samples/widgets/slider.cpp +++ b/samples/widgets/slider.cpp @@ -119,7 +119,8 @@ protected: void OnCheckOrRadioBox(wxCommandEvent& event); - void OnSlider(wxScrollEvent& event); + void OnSliderScroll(wxScrollEvent& event); + void OnSlider(wxCommandEvent& event); void OnUpdateUIValueButton(wxUpdateUIEvent& event); void OnUpdateUIMinMaxButton(wxUpdateUIEvent& event); @@ -161,6 +162,8 @@ protected: bool IsValidValue(int val) const { return (val >= m_min) && (val <= m_max); } + static int ms_numSliderEvents; + // the slider range int m_min, m_max; @@ -200,6 +203,8 @@ private: DECLARE_WIDGETS_PAGE(SliderWidgetsPage) }; +int SliderWidgetsPage::ms_numSliderEvents = 0; + // ---------------------------------------------------------------------------- // event tables // ---------------------------------------------------------------------------- @@ -229,7 +234,8 @@ wxBEGIN_EVENT_TABLE(SliderWidgetsPage, WidgetsPage) EVT_UPDATE_UI(SliderPage_CurValueText, SliderWidgetsPage::OnUpdateUICurValueText) - EVT_COMMAND_SCROLL(SliderPage_Slider, SliderWidgetsPage::OnSlider) + EVT_COMMAND_SCROLL(SliderPage_Slider, SliderWidgetsPage::OnSliderScroll) + EVT_SLIDER(SliderPage_Slider, SliderWidgetsPage::OnSlider) EVT_CHECKBOX(wxID_ANY, SliderWidgetsPage::OnCheckOrRadioBox) EVT_RADIOBOX(wxID_ANY, SliderWidgetsPage::OnCheckOrRadioBox) @@ -801,7 +807,7 @@ void SliderWidgetsPage::OnUpdateUISelectRange(wxUpdateUIEvent& event) #endif // defined(__WXMSW__) } -void SliderWidgetsPage::OnSlider(wxScrollEvent& event) +void SliderWidgetsPage::OnSliderScroll(wxScrollEvent& event) { wxASSERT_MSG( event.GetInt() == m_slider->GetValue(), "slider value should be the same" ); @@ -835,14 +841,17 @@ void SliderWidgetsPage::OnSlider(wxScrollEvent& event) wxASSERT_MSG(index >= 0 && (size_t)index < WXSIZEOF(eventNames), "Unknown slider event" ); - - static int s_numSliderEvents = 0; - wxLogMessage("Slider event #%d: %s (pos = %d, int value = %d)", - s_numSliderEvents++, + ms_numSliderEvents++, eventNames[index], event.GetPosition(), event.GetInt()); } +void SliderWidgetsPage::OnSlider(wxCommandEvent& event) +{ + wxLogMessage("Slider event #%d: wxEVT_SLIDER (value = %d)", + ms_numSliderEvents++, event.GetInt()); +} + #endif // wxUSE_SLIDER diff --git a/src/aui/auibar.cpp b/src/aui/auibar.cpp index b07b2ca296..72ee06218d 100644 --- a/src/aui/auibar.cpp +++ b/src/aui/auibar.cpp @@ -2839,7 +2839,7 @@ void wxAuiToolBar::OnMotion(wxMouseEvent& evt) const bool button_pressed = HasCapture(); // start a drag event - if (!m_dragging && button_pressed && + if (!m_dragging && button_pressed && m_actionItem && abs(evt.GetX() - m_actionPos.x) + abs(evt.GetY() - m_actionPos.y) > 5) { // TODO: sending this event only makes sense if there is an 'END_DRAG' diff --git a/src/common/containr.cpp b/src/common/containr.cpp index b418a3b8c2..cf5f630665 100644 --- a/src/common/containr.cpp +++ b/src/common/containr.cpp @@ -234,101 +234,19 @@ void wxControlContainer::SetLastFocus(wxWindow *win) } // -------------------------------------------------------------------- -// The following four functions are used to find other radio buttons -// within the same group. Used by wxSetFocusToChild +// The following functions is used by wxSetFocusToChild() // -------------------------------------------------------------------- -#if wxUSE_RADIOBTN +#if defined(USE_RADIOBTN_NAV) -wxRadioButton* wxGetPreviousButtonInGroup(wxRadioButton *btn) +namespace { - if ( btn->HasFlag(wxRB_GROUP) || btn->HasFlag(wxRB_SINGLE) ) - return NULL; - const wxWindowList& siblings = btn->GetParent()->GetChildren(); - wxWindowList::compatibility_iterator nodeThis = siblings.Find(btn); - wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") ); - - // Iterate over all previous siblings until we find the next radio button - wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious(); - wxRadioButton *prevBtn = 0; - while (nodeBefore) - { - prevBtn = wxDynamicCast(nodeBefore->GetData(), wxRadioButton); - if (prevBtn) - break; - - nodeBefore = nodeBefore->GetPrevious(); - } - - if (!prevBtn || prevBtn->HasFlag(wxRB_SINGLE)) - { - // no more buttons in group - return NULL; - } - - return prevBtn; -} - -wxRadioButton* wxGetNextButtonInGroup(wxRadioButton *btn) -{ - if (btn->HasFlag(wxRB_SINGLE)) - return NULL; - - const wxWindowList& siblings = btn->GetParent()->GetChildren(); - wxWindowList::compatibility_iterator nodeThis = siblings.Find(btn); - wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") ); - - // Iterate over all previous siblings until we find the next radio button - wxWindowList::compatibility_iterator nodeNext = nodeThis->GetNext(); - wxRadioButton *nextBtn = 0; - while (nodeNext) - { - nextBtn = wxDynamicCast(nodeNext->GetData(), wxRadioButton); - if (nextBtn) - break; - - nodeNext = nodeNext->GetNext(); - } - - if ( !nextBtn || nextBtn->HasFlag(wxRB_GROUP) || nextBtn->HasFlag(wxRB_SINGLE) ) - { - // no more buttons or the first button of the next group - return NULL; - } - - return nextBtn; -} - -wxRadioButton* wxGetFirstButtonInGroup(wxRadioButton *btn) -{ - while (true) - { - wxRadioButton* prevBtn = wxGetPreviousButtonInGroup(btn); - if (!prevBtn) - return btn; - - btn = prevBtn; - } -} - -wxRadioButton* wxGetLastButtonInGroup(wxRadioButton *btn) -{ - while (true) - { - wxRadioButton* nextBtn = wxGetNextButtonInGroup(btn); - if (!nextBtn) - return btn; - - btn = nextBtn; - } -} - -wxRadioButton* wxGetSelectedButtonInGroup(wxRadioButton *btn) +wxRadioButton* wxGetSelectedButtonInGroup(const wxRadioButton *btn) { // Find currently selected button if (btn->GetValue()) - return btn; + return const_cast(btn); if (btn->HasFlag(wxRB_SINGLE)) return NULL; @@ -336,19 +254,21 @@ wxRadioButton* wxGetSelectedButtonInGroup(wxRadioButton *btn) wxRadioButton *selBtn; // First check all previous buttons - for (selBtn = wxGetPreviousButtonInGroup(btn); selBtn; selBtn = wxGetPreviousButtonInGroup(selBtn)) + for (selBtn = btn->GetPreviousInGroup(); selBtn; selBtn = selBtn->GetPreviousInGroup()) if (selBtn->GetValue()) return selBtn; // Now all following buttons - for (selBtn = wxGetNextButtonInGroup(btn); selBtn; selBtn = wxGetNextButtonInGroup(selBtn)) + for (selBtn = btn->GetNextInGroup(); selBtn; selBtn = selBtn->GetNextInGroup()) if (selBtn->GetValue()) return selBtn; return NULL; } -#endif // wxUSE_RADIOBTN +} // anonymous namespace + +#endif // USE_RADIOBTN_NAV // ---------------------------------------------------------------------------- // Keyboard handling - this is the place where the TAB traversal logic is @@ -468,7 +388,7 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event ) // If we are in a radio button group, start from the first item in the // group if ( event.IsFromTab() && wxIsKindOf(winFocus, wxRadioButton ) ) - winFocus = wxGetFirstButtonInGroup((wxRadioButton*)winFocus); + winFocus = static_cast(winFocus)->GetFirstInGroup(); #endif // USE_RADIOBTN_NAV // ok, we found the focus - now is it our child? start_node = children.Find( winFocus ); @@ -586,20 +506,20 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event ) // find the correct radio button to focus if ( forward ) { - child = wxGetNextButtonInGroup(lastBtn); + child = lastBtn->GetNextInGroup(); if ( !child ) { // no next button in group, set it to the first button - child = wxGetFirstButtonInGroup(lastBtn); + child = lastBtn->GetFirstInGroup(); } } else { - child = wxGetPreviousButtonInGroup(lastBtn); + child = lastBtn->GetPreviousInGroup(); if ( !child ) { // no previous button in group, set it to the last button - child = wxGetLastButtonInGroup(lastBtn); + child = lastBtn->GetLastInGroup(); } } diff --git a/src/common/convauto.cpp b/src/common/convauto.cpp index 952b4455f5..708096de5d 100644 --- a/src/common/convauto.cpp +++ b/src/common/convauto.cpp @@ -23,6 +23,7 @@ #endif #include "wx/convauto.h" +#include "wx/private/unicode.h" // we use latin1 by default as it seems the least bad choice: the files we need // to detect input of don't always come from the user system (they are often @@ -266,6 +267,23 @@ bool wxConvAuto::InitFromInput(const char *src, size_t len) return true; } +// checks if the input can be the beginning of a valid UTF-8 sequence +static bool wxCanBeUTF8SequencePrefix(const char *src, size_t len) +{ + size_t i = 0; + unsigned char l = tableUtf8Lengths[(unsigned char)src[i]]; + if ( !l ) + return false; // invalid leading byte + while ( --l ) + { + if ( ++i == len ) + return true; // truncated sequence + if ( (src[i] & 0xC0) != 0x80 ) + return false; // invalid continuation byte + } + return false; // complete sequence +} + size_t wxConvAuto::ToWChar(wchar_t *dst, size_t dstLen, const char *src, size_t srcLen) const @@ -307,25 +325,28 @@ wxConvAuto::ToWChar(wchar_t *dst, size_t dstLen, // try to convert using the auto-detected encoding size_t rc = m_conv->ToWChar(dst, dstLen, src, srcLen); - if ( rc == wxCONV_FAILED && m_bomType == wxBOM_None ) + if ( rc == wxCONV_FAILED && m_bomType == wxBOM_None && !m_ownsConv ) { // we may need more bytes before we can decode the input, don't switch // to the fall-back conversion in this case as it would prevent us from // decoding UTF-8 input when fed it byte by byte, as done by // wxTextInputStream, for example - if ( srcLen < m_conv->GetMaxCharLen() ) + // up to 2 extra bytes are needed for inputs that start with null bytes + // that look like the start of UTF-32BE BOM, but can be in UTF-8 too + size_t nNull = 0; + if ( srcLen != wxNO_LEN && srcLen >= 2 && !src[0] ) + nNull = ( src[1]? 1 : 2 ); + if ( srcLen < nNull + m_conv->GetMaxCharLen() && + wxCanBeUTF8SequencePrefix(src + nNull, srcLen - nNull) ) return wxCONV_FAILED; // if the conversion failed but we didn't really detect anything and // simply tried UTF-8 by default, retry it using the fall-back + if ( m_encDefault == wxFONTENCODING_DEFAULT ) + self->m_encDefault = GetFallbackEncoding(); if ( m_encDefault != wxFONTENCODING_MAX ) { - if ( m_ownsConv ) - delete m_conv; - - self->m_conv = new wxCSConv(m_encDefault == wxFONTENCODING_DEFAULT - ? GetFallbackEncoding() - : m_encDefault); + self->m_conv = new wxCSConv(m_encDefault); self->m_ownsConv = true; rc = m_conv->ToWChar(dst, dstLen, src, srcLen); @@ -351,3 +372,32 @@ wxConvAuto::FromWChar(char *dst, size_t dstLen, return m_conv->FromWChar(dst, dstLen, src, srcLen); } + +wxFontEncoding wxConvAuto::GetEncoding() const +{ + switch ( m_bomType ) + { + case wxBOM_UTF32BE: + return wxFONTENCODING_UTF32BE; + case wxBOM_UTF32LE: + return wxFONTENCODING_UTF32LE; + case wxBOM_UTF16BE: + return wxFONTENCODING_UTF16BE; + case wxBOM_UTF16LE: + return wxFONTENCODING_UTF16LE; + case wxBOM_UTF8: + return wxFONTENCODING_UTF8; + + case wxBOM_Unknown: + case wxBOM_None: + if ( !m_conv ) + return wxFONTENCODING_MAX; + else if ( !m_ownsConv ) + return wxFONTENCODING_UTF8; + else + return m_encDefault; + } + + wxFAIL_MSG( "unknown BOM type" ); + return wxFONTENCODING_MAX; +} diff --git a/src/common/dcbase.cpp b/src/common/dcbase.cpp index 59f61bce0a..6f351dfdc1 100644 --- a/src/common/dcbase.cpp +++ b/src/common/dcbase.cpp @@ -512,6 +512,16 @@ wxPoint wxDCImpl::LogicalToDevice(wxCoord x, wxCoord y) const return wxPoint(LogicalToDeviceX(x), LogicalToDeviceY(y)); } +wxSize wxDCImpl::DeviceToLogicalRel(int x, int y) const +{ + return wxSize(DeviceToLogicalXRel(x), DeviceToLogicalYRel(y)); +} + +wxSize wxDCImpl::LogicalToDeviceRel(int x, int y) const +{ + return wxSize(LogicalToDeviceXRel(x), LogicalToDeviceYRel(y)); +} + void wxDCImpl::ComputeScaleAndOrigin() { m_scaleX = m_logicalScaleX * m_userScaleX; diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index cd6c8d206e..2e3afe25fe 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -27,12 +27,6 @@ #include "wx/geometry.h" #endif -//----------------------------------------------------------------------------- -// constants -//----------------------------------------------------------------------------- - -static const double RAD2DEG = 180.0 / M_PI; - //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- @@ -613,6 +607,22 @@ wxPoint wxGCDCImpl::LogicalToDevice(wxCoord x, wxCoord y) const return wxPoint(wxRound(px), wxRound(py)); } +wxSize wxGCDCImpl::DeviceToLogicalRel(int x, int y) const +{ + wxDouble dx = x; + wxDouble dy = y; + m_matrixCurrentInv.TransformDistance(&dx, &dy); + return wxSize(wxRound(dx), wxRound(dy)); +} + +wxSize wxGCDCImpl::LogicalToDeviceRel(int x, int y) const +{ + wxDouble dx = x; + wxDouble dy = y; + m_matrixCurrent.TransformDistance(&dx, &dy); + return wxSize(wxRound(dx), wxRound(dy)); +} + bool wxGCDCImpl::DoFloodFill(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxColour& WXUNUSED(col), wxFloodFillStyle WXUNUSED(style)) @@ -670,11 +680,11 @@ void wxGCDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, double dy = y1 - yc; double radius = sqrt((double)(dx * dx + dy * dy)); wxCoord rad = (wxCoord)radius; - double sa, ea; + double sa, ea; // In radians if (x1 == x2 && y1 == y2) { sa = 0.0; - ea = 360.0; + ea = 2.0 * M_PI; } else if (radius == 0.0) { @@ -683,11 +693,11 @@ void wxGCDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, else { sa = (x1 - xc == 0) ? - (y1 - yc < 0) ? 90.0 : -90.0 : - -atan2(double(y1 - yc), double(x1 - xc)) * RAD2DEG; + (y1 - yc < 0) ? M_PI / 2.0 : -M_PI / 2.0 : + -atan2(double(y1 - yc), double(x1 - xc)); ea = (x2 - xc == 0) ? - (y2 - yc < 0) ? 90.0 : -90.0 : - -atan2(double(y2 - yc), double(x2 - xc)) * RAD2DEG; + (y2 - yc < 0) ? M_PI / 2.0 : -M_PI / 2.0 : + -atan2(double(y2 - yc), double(x2 - xc)); } bool fill = m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT; @@ -697,7 +707,7 @@ void wxGCDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, path.MoveToPoint( xc, yc ); // since these angles (ea,sa) are measured counter-clockwise, we invert them to // get clockwise angles - path.AddArc( xc, yc , rad, wxDegToRad(-sa), wxDegToRad(-ea), false ); + path.AddArc( xc, yc , rad, -sa, -ea, false ); if ( fill && ((x1!=x2)||(y1!=y2)) ) path.AddLineToPoint( xc, yc ); m_graphicContext->DrawPath(path); diff --git a/src/common/dynlib.cpp b/src/common/dynlib.cpp index 37fea09768..8130f93f9d 100644 --- a/src/common/dynlib.cpp +++ b/src/common/dynlib.cpp @@ -227,6 +227,8 @@ wxString wxDynamicLibrary::GetPluginsDirectory() { #ifdef __UNIX__ wxString format = wxGetInstallPrefix(); + if ( format.empty() ) + return wxEmptyString; wxString dir; format << wxFILE_SEP_PATH << wxT("lib") << wxFILE_SEP_PATH diff --git a/src/common/geometry.cpp b/src/common/geometry.cpp index 57bdcb40c7..cd2fc3c59f 100644 --- a/src/common/geometry.cpp +++ b/src/common/geometry.cpp @@ -176,8 +176,7 @@ wxDouble wxPoint2DInt::GetVectorAngle() const return 180; } - // casts needed for MIPSpro compiler under SGI - wxDouble deg = atan2( (double)m_y , (double)m_x ) * 180 / M_PI; + wxDouble deg = wxRadToDeg(atan2( (double)m_y , (double)m_x )); if ( deg < 0 ) { deg += 360; @@ -189,8 +188,9 @@ wxDouble wxPoint2DInt::GetVectorAngle() const void wxPoint2DInt::SetVectorAngle( wxDouble degrees ) { wxDouble length = GetVectorLength(); - m_x = (int)(length * cos( degrees / 180 * M_PI )); - m_y = (int)(length * sin( degrees / 180 * M_PI )); + double rad = wxDegToRad(degrees); + m_x = (int)(length * cos(rad)); + m_y = (int)(length * sin(rad)); } wxDouble wxPoint2DDouble::GetVectorAngle() const @@ -209,7 +209,7 @@ wxDouble wxPoint2DDouble::GetVectorAngle() const else return 180; } - wxDouble deg = atan2( m_y , m_x ) * 180 / M_PI; + wxDouble deg = wxRadToDeg(atan2( m_y , m_x )); if ( deg < 0 ) { deg += 360; @@ -220,8 +220,9 @@ wxDouble wxPoint2DDouble::GetVectorAngle() const void wxPoint2DDouble::SetVectorAngle( wxDouble degrees ) { wxDouble length = GetVectorLength(); - m_x = length * cos( degrees / 180 * M_PI ); - m_y = length * sin( degrees / 180 * M_PI ); + double rad = wxDegToRad(degrees); + m_x = length * cos(rad); + m_y = length * sin(rad); } // wxRect2D diff --git a/src/common/image.cpp b/src/common/image.cpp index 880c1c4ea7..d6febece49 100644 --- a/src/common/image.cpp +++ b/src/common/image.cpp @@ -1608,7 +1608,9 @@ wxImage wxImage::Size( const wxSize& size, const wxPoint& pos, return image; } -void wxImage::Paste( const wxImage &image, int x, int y ) +void +wxImage::Paste(const wxImage & image, int x, int y, + wxImageAlphaBlendMode alphaBlend) { wxCHECK_RET( IsOk(), wxT("invalid image") ); wxCHECK_RET( image.IsOk(), wxT("invalid image") ); @@ -1639,15 +1641,18 @@ void wxImage::Paste( const wxImage &image, int x, int y ) if (width < 1) return; if (height < 1) return; + bool copiedPixels = false; + // If we can, copy the data using memcpy() as this is the fastest way. But - // for this the image being pasted must have "compatible" mask with this - // one meaning that either it must not have one at all or it must use the - // same masked colour. - if ( !image.HasMask() || + // for this we must not do alpha compositing and the image being pasted + // must have "compatible" mask with this one meaning that either it must + // not have one at all or it must use the same masked colour. + if (alphaBlend == wxIMAGE_ALPHA_BLEND_OVER && + (!image.HasMask() || ((HasMask() && (GetMaskRed()==image.GetMaskRed()) && (GetMaskGreen()==image.GetMaskGreen()) && - (GetMaskBlue()==image.GetMaskBlue()))) ) + (GetMaskBlue()==image.GetMaskBlue())))) ) { const unsigned char* source_data = image.GetData() + 3*(xx + yy*image.GetWidth()); int source_step = image.GetWidth()*3; @@ -1660,6 +1665,8 @@ void wxImage::Paste( const wxImage &image, int x, int y ) source_data += source_step; target_data += target_step; } + + copiedPixels = true; } // Copy over the alpha channel from the original image @@ -1668,45 +1675,126 @@ void wxImage::Paste( const wxImage &image, int x, int y ) if ( !HasAlpha() ) InitAlpha(); - const unsigned char* source_data = image.GetAlpha() + xx + yy*image.GetWidth(); - int source_step = image.GetWidth(); + const unsigned char* + alpha_source_data = image.GetAlpha() + xx + yy * image.GetWidth(); + const int source_step = image.GetWidth(); - unsigned char* target_data = GetAlpha() + (x+xx) + (y+yy)*M_IMGDATA->m_width; - int target_step = M_IMGDATA->m_width; + unsigned char* + alpha_target_data = GetAlpha() + (x + xx) + (y + yy) * M_IMGDATA->m_width; + const int target_step = M_IMGDATA->m_width; - for (int j = 0; j < height; j++, - source_data += source_step, - target_data += target_step) + switch (alphaBlend) { - memcpy( target_data, source_data, width ); + case wxIMAGE_ALPHA_BLEND_OVER: + { + // Copy just the alpha values. + for (int j = 0; j < height; j++, + alpha_source_data += source_step, + alpha_target_data += target_step) + { + memcpy(alpha_target_data, alpha_source_data, width); + } + break; + } + case wxIMAGE_ALPHA_BLEND_COMPOSE: + { + const unsigned char* + source_data = image.GetData() + 3 * (xx + yy * image.GetWidth()); + + unsigned char* + target_data = GetData() + 3 * ((x + xx) + (y + yy) * M_IMGDATA->m_width); + + // Combine the alpha values but also apply alpha blending to + // the pixels themselves while we copy them. + for (int j = 0; j < height; j++, + alpha_source_data += source_step, + alpha_target_data += target_step, + source_data += 3 * source_step, + target_data += 3 * target_step) + { + for (int i = 0; i < width; i++) + { + float source_alpha = alpha_source_data[i] / 255.0f; + float light_left = (alpha_target_data[i] / 255.0f) * (1.0f - source_alpha); + float result_alpha = source_alpha + light_left; + alpha_target_data[i] = (unsigned char)((result_alpha * 255) +0.5); + for (int c = 3 * i; c < 3 * (i + 1); c++) + { + target_data[c] = + (unsigned char)(((source_data[c] * source_alpha + + target_data[c] * light_left) / + result_alpha) + 0.5); + } + } + } + + copiedPixels = true; + break; + } } + } - if (!HasMask() && image.HasMask()) + // If we hadn't copied them yet we must need to take the mask of the image + // being pasted into account. + if (!copiedPixels) { - unsigned char r = image.GetMaskRed(); - unsigned char g = image.GetMaskGreen(); - unsigned char b = image.GetMaskBlue(); + const unsigned char* source_data = image.GetData() + 3 * (xx + yy * image.GetWidth()); + int source_step = image.GetWidth() * 3; - const unsigned char* source_data = image.GetData() + 3*(xx + yy*image.GetWidth()); - int source_step = image.GetWidth()*3; + unsigned char* target_data = GetData() + 3 * ((x + xx) + (y + yy) * M_IMGDATA->m_width); + int target_step = M_IMGDATA->m_width * 3; - unsigned char* target_data = GetData() + 3*((x+xx) + (y+yy)*M_IMGDATA->m_width); - int target_step = M_IMGDATA->m_width*3; - - for (int j = 0; j < height; j++) + unsigned char* alpha_target_data = NULL; + const int target_alpha_step = M_IMGDATA->m_width; + if (HasAlpha()) { - for (int i = 0; i < width*3; i+=3) + alpha_target_data = GetAlpha() + (x + xx) + (y + yy) * M_IMGDATA->m_width; + } + + // The mask colours should only be taken into account if the mask is actually enabled + if (!image.HasMask()) + { + // Copy all pixels + for (int j = 0; j < height; j++) { - if ((source_data[i] != r) || - (source_data[i+1] != g) || - (source_data[i+2] != b)) + memcpy(target_data, source_data, width * 3); + source_data += source_step; + target_data += target_step; + // Make all the copied pixels fully opaque + if (alpha_target_data != NULL) { - memcpy( target_data+i, source_data+i, 3 ); + memset(alpha_target_data, wxALPHA_OPAQUE, width); + alpha_target_data += target_alpha_step; } } - source_data += source_step; - target_data += target_step; + } + else + { + // Copy all 'non masked' pixels + unsigned char r = image.GetMaskRed(); + unsigned char g = image.GetMaskGreen(); + unsigned char b = image.GetMaskBlue(); + + for (int j = 0; j < height; j++) + { + for (int i = 0; i < width * 3; i += 3) + { + if ((source_data[i] != r) || + (source_data[i + 1] != g) || + (source_data[i + 2] != b)) + { + // Copy the non masked pixel + memcpy(target_data + i, source_data + i, 3); + if (alpha_target_data != NULL) // Make the copied pixel fully opaque + alpha_target_data[i / 3] = wxALPHA_OPAQUE; + } + } + source_data += source_step; + target_data += target_step; + if (alpha_target_data != NULL) + alpha_target_data += target_alpha_step; + } } } } diff --git a/src/common/radiobtncmn.cpp b/src/common/radiobtncmn.cpp index dc2bd09240..5e847c7ab9 100644 --- a/src/common/radiobtncmn.cpp +++ b/src/common/radiobtncmn.cpp @@ -92,4 +92,96 @@ wxCONSTRUCTOR_6( wxRadioButton, wxWindow*, Parent, wxWindowID, Id, \ wxString, Label, wxPoint, Position, wxSize, Size, long, WindowStyle ) +// ---------------------------------------------------------------------------- +// wxRadioButton group navigation +// ---------------------------------------------------------------------------- + +wxRadioButton* wxRadioButtonBase::GetFirstInGroup() const +{ + wxRadioButton* + btn = static_cast(const_cast(this)); + while (true) + { + wxRadioButton* prevBtn = btn->GetPreviousInGroup(); + if (!prevBtn) + return btn; + + btn = prevBtn; + } +} + +wxRadioButton* wxRadioButtonBase::GetLastInGroup() const +{ + wxRadioButton* + btn = static_cast(const_cast(this)); + while (true) + { + wxRadioButton* nextBtn = btn->GetNextInGroup(); + if (!nextBtn) + return btn; + + btn = nextBtn; + } +} + +wxRadioButton* wxRadioButtonBase::GetPreviousInGroup() const +{ + if ( HasFlag(wxRB_GROUP) || HasFlag(wxRB_SINGLE) ) + return NULL; + + const wxWindowList& siblings = GetParent()->GetChildren(); + wxWindowList::compatibility_iterator nodeThis = siblings.Find(this); + wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") ); + + // Iterate over all previous siblings until we find the next radio button + wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious(); + wxRadioButton *prevBtn = 0; + while (nodeBefore) + { + prevBtn = wxDynamicCast(nodeBefore->GetData(), wxRadioButton); + if (prevBtn) + break; + + nodeBefore = nodeBefore->GetPrevious(); + } + + if (!prevBtn || prevBtn->HasFlag(wxRB_SINGLE)) + { + // no more buttons in group + return NULL; + } + + return prevBtn; +} + +wxRadioButton* wxRadioButtonBase::GetNextInGroup() const +{ + if ( HasFlag(wxRB_SINGLE) ) + return NULL; + + const wxWindowList& siblings = GetParent()->GetChildren(); + wxWindowList::compatibility_iterator nodeThis = siblings.Find(this); + wxCHECK_MSG( nodeThis, NULL, wxT("radio button not a child of its parent?") ); + + // Iterate over all previous siblings until we find the next radio button + wxWindowList::compatibility_iterator nodeNext = nodeThis->GetNext(); + wxRadioButton *nextBtn = 0; + while (nodeNext) + { + nextBtn = wxDynamicCast(nodeNext->GetData(), wxRadioButton); + if (nextBtn) + break; + + nodeNext = nodeNext->GetNext(); + } + + if ( !nextBtn || nextBtn->HasFlag(wxRB_GROUP) || nextBtn->HasFlag(wxRB_SINGLE) ) + { + // no more buttons or the first button of the next group + return NULL; + } + + return nextBtn; +} + #endif // wxUSE_RADIOBTN diff --git a/src/common/strconv.cpp b/src/common/strconv.cpp index ba25dae157..23c2b0a545 100644 --- a/src/common/strconv.cpp +++ b/src/common/strconv.cpp @@ -46,6 +46,7 @@ #include "wx/encconv.h" #include "wx/fontmap.h" +#include "wx/private/unicode.h" #ifdef __DARWIN__ #include "wx/osx/core/private/strconv_cf.h" @@ -921,7 +922,7 @@ const wxUint32 wxUnicodePUA = 0x100000; const wxUint32 wxUnicodePUAEnd = wxUnicodePUA + 256; // this table gives the length of the UTF-8 encoding from its first character: -const unsigned char tableUtf8Lengths[256] = { +extern const unsigned char tableUtf8Lengths[256] = { // single-byte sequences (ASCII): 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00..0F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10..1F diff --git a/src/common/stringops.cpp b/src/common/stringops.cpp index 85629406a3..84017ae523 100644 --- a/src/common/stringops.cpp +++ b/src/common/stringops.cpp @@ -23,6 +23,8 @@ #include "wx/stringops.h" #endif +#include "wx/private/unicode.h" + // =========================================================================== // implementation // =========================================================================== @@ -97,40 +99,13 @@ wxWxCharBuffer wxStringOperationsWchar::EncodeNChars(size_t n, const wxUniChar& // UTF-8 sequences lengths // --------------------------------------------------------------------------- -const unsigned char wxStringOperationsUtf8::ms_utf8IterTable[256] = { - // single-byte sequences (ASCII): - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00..0F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10..1F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20..2F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30..3F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40..4F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50..5F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60..6F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70..7F - - // these are invalid, we use step 1 to skip - // over them (should never happen): - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80..8F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90..9F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0..AF - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0..BF - 1, 1, // C0,C1 - - // two-byte sequences: - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C2..CF - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0..DF - - // three-byte sequences: - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0..EF - - // four-byte sequences: - 4, 4, 4, 4, 4, // F0..F4 - - // these are invalid again (5- or 6-byte - // sequences and sequences for code points - // above U+10FFFF, as restricted by RFC 3629): - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F5..FF -}; +unsigned char wxStringOperationsUtf8::GetUTF8IterOffset(unsigned char c) +{ + unsigned char l = tableUtf8Lengths[c]; + if ( !l ) //skip over invalid characters + l = 1; + return l; +} // --------------------------------------------------------------------------- // UTF-8 operations @@ -166,7 +141,7 @@ bool wxStringOperationsUtf8::IsValidUtf8String(const char *str, size_t len) { // if the string is not NULL-terminated, verify we have enough // bytes in it left for current character's encoding: - if ( c + ms_utf8IterTable[*c] > end ) + if ( c + GetUTF8IterOffset(*c) > end ) return false; } @@ -364,7 +339,7 @@ wxCharBuffer wxStringOperationsUtf8::EncodeNChars(size_t n, const wxUniChar& ch) { Utf8CharBuffer once(EncodeChar(ch)); // the IncIter() table can be used to determine the length of ch's encoding: - size_t len = ms_utf8IterTable[(unsigned char)once.data[0]]; + size_t len = GetUTF8IterOffset(once.data[0]); wxCharBuffer buf(n * len); char *ptr = buf.data(); diff --git a/src/common/txtstrm.cpp b/src/common/txtstrm.cpp index c38f7c29ab..1332bb3cf0 100644 --- a/src/common/txtstrm.cpp +++ b/src/common/txtstrm.cpp @@ -97,10 +97,11 @@ wxChar wxTextInputStream::GetChar() m_validEnd = 0; } - // We may need to decode up to 4 characters if we have input starting with - // 3 BOM-like bytes, but not actually containing a BOM, as decoding it will - // only succeed when 4 bytes are read -- and will yield 4 wide characters. - wxChar wbuf[4]; + // We may need to decode up to 6 characters if we have input starting with + // 2 null bytes (like in UTF-32BE BOM), and then 3 bytes that look like + // the start of UTF-8 sequence, as decoding it will only succeed when + // 6 bytes are read -- and will yield 6 wide characters. + wxChar wbuf[6]; for(size_t inlen = 0; inlen < sizeof(m_lastBytes); inlen++) { if ( inlen >= m_validEnd ) @@ -134,12 +135,13 @@ wxChar wxTextInputStream::GetChar() // one extra byte, the only explanation is that we were using a // wxConvAuto conversion recognizing the initial BOM and that // it couldn't detect the presence or absence of BOM so far, - // but now finally has enough data to see that there is none. - // As we must have fallen back to Latin-1 in this case, return - // just the first byte and keep the other ones for the next - // time. - m_validBegin = 1; - return wbuf[0]; + // but now finally has enough data to see that there is none, or + // it was trying to decode the data as UTF-8 sequence, but now + // recognized that it's not valid UTF-8 and switched to fallback. + // We don't know how long is the first character or if it's decoded + // as 1 or 2 wchar_t characters, so we need to start with 1 byte again. + inlen = -1; + break; #if SIZEOF_WCHAR_T == 2 case 2: diff --git a/src/common/ustring.cpp b/src/common/ustring.cpp index 6e1768064b..531ee41b9c 100644 --- a/src/common/ustring.cpp +++ b/src/common/ustring.cpp @@ -15,6 +15,7 @@ #endif #include "wx/ustring.h" +#include "wx/private/unicode.h" #ifndef WX_PRECOMP #include "wx/crt.h" @@ -67,41 +68,6 @@ wxUString &wxUString::assignFromAscii( const char *str, size_type n ) // UTF-8 // ---------------------------------------------------------------------------- -// this table gives the length of the UTF-8 encoding from its first character: -const unsigned char tableUtf8Lengths[256] = { - // single-byte sequences (ASCII): - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00..0F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10..1F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20..2F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30..3F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40..4F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50..5F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60..6F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70..7F - - // these are invalid: - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0..AF - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0..BF - 0, 0, // C0,C1 - - // two-byte sequences: - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C2..CF - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0..DF - - // three-byte sequences: - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0..EF - - // four-byte sequences: - 4, 4, 4, 4, 4, // F0..F4 - - // these are invalid again (5- or 6-byte - // sequences and sequences for code points - // above U+10FFFF, as restricted by RFC 3629): - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F5..FF -}; - wxUString &wxUString::assignFromUTF8( const char *str ) { if (!str) diff --git a/src/common/utilscmn.cpp b/src/common/utilscmn.cpp index 05b6fa002f..bcfee48b33 100644 --- a/src/common/utilscmn.cpp +++ b/src/common/utilscmn.cpp @@ -182,12 +182,12 @@ void wxUsleep(unsigned long milliseconds) } #endif -const wxChar *wxGetInstallPrefix() +wxString wxGetInstallPrefix() { wxString prefix; if ( wxGetEnv(wxT("WXPREFIX"), &prefix) ) - return prefix.c_str(); + return prefix; #ifdef wxINSTALL_PREFIX return wxT(wxINSTALL_PREFIX); diff --git a/src/generic/bmpcboxg.cpp b/src/generic/bmpcboxg.cpp index 6432bccaf0..1e3bfff452 100644 --- a/src/generic/bmpcboxg.cpp +++ b/src/generic/bmpcboxg.cpp @@ -135,6 +135,11 @@ wxBitmapComboBox::~wxBitmapComboBox() DoClear(); } +wxString wxBitmapComboBox::GetStringSelection() const +{ + return wxItemContainer::GetStringSelection(); +} + // ---------------------------------------------------------------------------- // Item manipulation // ---------------------------------------------------------------------------- diff --git a/src/generic/caret.cpp b/src/generic/caret.cpp index 0f5aa7ac51..603888570b 100644 --- a/src/generic/caret.cpp +++ b/src/generic/caret.cpp @@ -103,7 +103,8 @@ void wxCaret::InitGeneric() #ifndef wxHAS_CARET_USING_OVERLAYS m_xOld = m_yOld = -1; - m_bmpUnderCaret.Create(m_width, m_height); + if (m_width && m_height) + m_bmpUnderCaret.Create(m_width, m_height); #endif } @@ -174,7 +175,10 @@ void wxCaret::DoSize() m_overlay.Reset(); #else // Change bitmap size - m_bmpUnderCaret = wxBitmap(m_width, m_height); + if (m_width && m_height) + m_bmpUnderCaret = wxBitmap(m_width, m_height); + else + m_bmpUnderCaret = wxBitmap(); #endif if (countVisible > 0) { diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 45e698960d..c47314ee24 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -950,6 +950,7 @@ bool wxListLineData::Highlight( bool on ) return false; m_highlighted = on; + m_owner->UpdateSelectionCount(on); return true; } @@ -1571,6 +1572,7 @@ wxEND_EVENT_TABLE() void wxListMainWindow::Init() { m_dirty = true; + m_selCount = m_countVirt = 0; m_lineFrom = m_lineTo = (size_t)-1; @@ -1598,7 +1600,8 @@ void wxListMainWindow::Init() m_current = m_lineLastClicked = m_lineSelectSingleOnUp = - m_lineBeforeLastClicked = (size_t)-1; + m_lineBeforeLastClicked = + m_anchor = (size_t)-1; m_hasCheckBoxes = false; } @@ -1866,8 +1869,13 @@ bool wxListMainWindow::IsHighlighted(size_t line) const void wxListMainWindow::HighlightLines( size_t lineFrom, size_t lineTo, - bool highlight ) + bool highlight, + SendEvent sendEvent ) { + // It is safe to swap the bounds here if they are not in order. + if ( lineFrom > lineTo ) + wxSwap(lineFrom, lineTo); + if ( IsVirtual() ) { wxArrayInt linesChanged; @@ -1890,13 +1898,13 @@ void wxListMainWindow::HighlightLines( size_t lineFrom, { for ( size_t line = lineFrom; line <= lineTo; line++ ) { - if ( HighlightLine(line, highlight) ) + if ( HighlightLine(line, highlight, sendEvent) ) RefreshLine(line); } } } -bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) +bool wxListMainWindow::HighlightLine( size_t line, bool highlight, SendEvent sendEvent ) { bool changed; @@ -1912,7 +1920,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) changed = ld->Highlight(highlight); } - if ( changed ) + if ( changed && sendEvent ) { SendNotify( line, highlight ? wxEVT_LIST_ITEM_SELECTED : wxEVT_LIST_ITEM_DESELECTED ); @@ -2197,6 +2205,58 @@ void wxListMainWindow::HighlightAll( bool on ) } } +void wxListMainWindow::HighlightOnly( size_t line, size_t oldLine ) +{ + const unsigned selCount = GetSelectedItemCount(); + + if ( selCount == 1 && IsHighlighted(line) ) + { + return; // Nothing changed. + } + + if ( oldLine != (size_t)-1 ) + { + IsHighlighted(oldLine) ? ReverseHighlight(oldLine) + : RefreshLine(oldLine); // refresh the old focus to remove it + } + + if ( selCount > 1 ) // multiple-selection only + { + // Deselecting many items at once will generate wxEVT_XXX_DESELECTED event + // for each one of them. although this may be inefficient if the number of + // deselected items is too much, we keep doing this (for non-virtual list + // controls) for backward compatibility concerns. For virtual listctrl (in + // multi-selection mode), wxMSW sends only a notification to indicate that + // something has been deselected. Notice that to be fully compatible with + // wxMSW behaviour, _line_ shouldn't be deselected if it was selected. + + const SendEvent sendEvent = IsVirtual() ? SendEvent_None : SendEvent_Normal; + + size_t lineFrom = 0, + lineTo = GetItemCount() - 1; + + if ( line > lineFrom && line < lineTo ) + { + HighlightLines(lineFrom, line - 1, false, sendEvent); + HighlightLines(line + 1, lineTo, false, sendEvent); + } + else // _line_ is equal to lineFrom or lineTo + { + line == lineFrom ? ++lineFrom : --lineTo; + HighlightLines(lineFrom, lineTo, false, sendEvent); + } + + // If we didn't send the event for individual items above, send it for all of them now. + if ( sendEvent == SendEvent_None ) + SendNotify((size_t)-1, wxEVT_LIST_ITEM_DESELECTED); + } + + // _line_ should be the only selected item. + HighlightLine(line); + // refresh the new focus to add it. + RefreshLine(line); +} + void wxListMainWindow::OnChildFocus(wxChildFocusEvent& WXUNUSED(event)) { // Do nothing here. This prevents the default handler in wxScrolledWindow @@ -2241,8 +2301,13 @@ void wxListMainWindow::SendNotify( size_t line, GetParent()->GetEventHandler()->ProcessEvent( le ); } -void wxListMainWindow::ChangeCurrent(size_t current) +bool wxListMainWindow::ChangeCurrentWithoutEvent(size_t current) { + if ( current == m_current ) + { + return false; // Nothing changed! + } + m_current = current; // as the current item changed, we shouldn't start editing it when the @@ -2250,7 +2315,13 @@ void wxListMainWindow::ChangeCurrent(size_t current) if ( m_renameTimer->IsRunning() ) m_renameTimer->Stop(); - SendNotify(current, wxEVT_LIST_ITEM_FOCUSED); + return true; +} + +void wxListMainWindow::ChangeCurrent(size_t current) +{ + if ( ChangeCurrentWithoutEvent(current) ) + SendNotify(current, wxEVT_LIST_ITEM_FOCUSED); } wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass) @@ -2396,7 +2467,11 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) evtCtx.SetEventObject(GetParent()); GetParent()->GetEventHandler()->ProcessEvent(evtCtx); } - return; + + if ( IsEmpty() ) + return; + + // Continue processing... } if (m_dirty) @@ -2521,8 +2596,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if (m_lineSelectSingleOnUp != (size_t)-1) { // select single line - HighlightAll( false ); - ReverseHighlight(m_lineSelectSingleOnUp); + HighlightOnly(m_lineSelectSingleOnUp); } if (m_lastOnSame) @@ -2542,6 +2616,14 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_lastOnSame = false; } + if ( GetSelectedItemCount() == 1 || event.CmdDown() ) + { + // In multiple selection mode, the anchor is set to the first selected + // item or can be changed to m_current if Ctrl key is down, as is the + // case under wxMSW. + m_anchor = m_current; + } + m_lineSelectSingleOnUp = (size_t)-1; } else @@ -2561,9 +2643,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) // Multi-selections should not be cleared if a selected item is clicked. if (!IsHighlighted(current)) { - HighlightAll(false); ChangeCurrent(current); - ReverseHighlight(m_current); + HighlightOnly(m_current); } SendNotify( current, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); @@ -2581,7 +2662,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_lineLastClicked = current; size_t oldCurrent = m_current; - bool oldWasSelected = IsHighlighted(m_current); + bool oldWasSelected = HasCurrent() && IsHighlighted(m_current); bool cmdModifierDown = event.CmdDown(); if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) ) @@ -2592,11 +2673,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) } else if (IsSingleSel() || !IsHighlighted(current)) { - HighlightAll(false); - ChangeCurrent(current); - - ReverseHighlight(m_current); + HighlightOnly(m_current, oldWasSelected ? oldCurrent : (size_t)-1); } else // multi sel & current is highlighted & no mod keys { @@ -2616,16 +2694,15 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) { ChangeCurrent(current); - size_t lineFrom = oldCurrent, - lineTo = current; - - if ( lineTo < lineFrom ) + if ( oldCurrent == (size_t)-1 ) { - lineTo = lineFrom; - lineFrom = m_current; + // Highlight m_current only if there is no previous selection. + HighlightLine(m_current); + } + else if ( oldCurrent != current && m_anchor != (size_t)-1 ) + { + ExtendSelection(oldCurrent, current); } - - HighlightLines(lineFrom, lineTo); } else // !ctrl, !shift { @@ -2634,7 +2711,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) } } - if (m_current != oldCurrent) + if (m_current != oldCurrent && oldCurrent != (size_t)-1) RefreshLine( oldCurrent ); // Set the flag telling us whether the next click on this item should @@ -2739,6 +2816,81 @@ bool wxListMainWindow::ScrollList(int WXUNUSED(dx), int dy) // keyboard handling // ---------------------------------------------------------------------------- +// Helper function which handles items selection correctly and efficiently. and +// simply, mimic the wxMSW behaviour. And The benefit of this function is that it +// ensures that wxEVT_LIST_ITEM_{DE}SELECTED events are only generated for items +// freshly (de)selected (i.e. items that have really changed state). Useful for +// multi-selection mode when selecting using mouse or arrows with Shift key down. +void wxListMainWindow::ExtendSelection(size_t oldCurrent, size_t newCurrent) +{ + // Refresh the old/new focus to remove/add it + RefreshLine(oldCurrent); + RefreshLine(newCurrent); + + // Given a selection [anchor, old], to change/extend it to new (i.e. the + // selection becomes [anchor, new]) we discriminate three possible cases: + // + // Case 1) new < old <= anchor || anchor <= old < new + // i.e. oldCurrent between anchor and newCurrent, in which case we: + // - Highlight everything between anchor and newCurrent (inclusive). + // + // Case 2) old < new <= anchor || anchor <= new < old + // i.e. newCurrent between anchor and oldCurrent, in which case we: + // - Unhighlight everything between oldCurrent and newCurrent (exclusive). + // + // Case 3) old < anchor < new || new < anchor < old + // i.e. anchor between oldCurrent and newCurrent, in which case we + // - Highlight everything between anchor and newCurrent (inclusive). + // - Unhighlight everything between anchor (exclusive) and oldCurrent. + + size_t lineFrom, lineTo; + + if ( (newCurrent < oldCurrent && oldCurrent <= m_anchor) || + (newCurrent > oldCurrent && oldCurrent >= m_anchor) ) + { + lineFrom = m_anchor; + lineTo = newCurrent; + + HighlightLines(lineFrom, lineTo); + } + else if ( (oldCurrent < newCurrent && newCurrent <= m_anchor) || + (oldCurrent > newCurrent && newCurrent >= m_anchor) ) + { + lineFrom = oldCurrent; + lineTo = newCurrent; + + // Exclude newCurrent from being deselected + (lineTo < lineFrom) ? ++lineTo : --lineTo; + + HighlightLines(lineFrom, lineTo, false); + + // For virtual listctrl (in multi-selection mode), wxMSW sends only + // a notification to indicate that something has been deselected. + if ( IsVirtual() ) + SendNotify((size_t)-1, wxEVT_LIST_ITEM_DESELECTED); + } + else if ( (oldCurrent < m_anchor && m_anchor < newCurrent) || + (newCurrent < m_anchor && m_anchor < oldCurrent) ) + { + lineFrom = m_anchor; + lineTo = oldCurrent; + + // Exclude anchor from being deselected + (lineTo < lineFrom) ? --lineFrom : ++lineFrom; + + HighlightLines(lineFrom, lineTo, false); + + // See above. + if ( IsVirtual() ) + SendNotify((size_t)-1, wxEVT_LIST_ITEM_DESELECTED); + + lineFrom = m_anchor; + lineTo = newCurrent; + + HighlightLines(lineFrom, lineTo); + } +} + void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) { wxCHECK_RET( newCurrent < (size_t)GetItemCount(), @@ -2746,43 +2898,34 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) size_t oldCurrent = m_current; + ChangeCurrent(newCurrent); + // in single selection we just ignore Shift as we can't select several // items anyhow if ( event.ShiftDown() && !IsSingleSel() ) { - ChangeCurrent(newCurrent); - - // refresh the old focus to remove it - RefreshLine( oldCurrent ); - - // select all the items between the old and the new one - if ( oldCurrent > newCurrent ) - { - newCurrent = oldCurrent; - oldCurrent = m_current; - } - - HighlightLines(oldCurrent, newCurrent); + ExtendSelection(oldCurrent, newCurrent); } else // !shift { - // all previously selected items are unselected unless ctrl is held - // in a multiselection control + // all previously selected items are unselected unless ctrl is held in + // a multi-selection control. in single selection mode we must always + // have a selected item. if ( !event.ControlDown() || IsSingleSel() ) - HighlightAll(false); + { + HighlightOnly(m_current, oldCurrent); - ChangeCurrent(newCurrent); - - // refresh the old focus to remove it - RefreshLine( oldCurrent ); - - // in single selection mode we must always have a selected item - if ( !event.ControlDown() || IsSingleSel() ) - HighlightLine( m_current, true ); + // Update anchor + m_anchor = m_current; + } + else + { + // refresh the old/new focus to remove/add it + RefreshLine(oldCurrent); + RefreshLine(m_current); + } } - RefreshLine( m_current ); - MoveToFocus(); } @@ -2799,10 +2942,11 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event ) // send a list event wxListEvent le( wxEVT_LIST_KEY_DOWN, parent->GetId() ); + const size_t current = ShouldSendEventForCurrent() ? m_current : (size_t)-1; le.m_item.m_itemId = - le.m_itemIndex = m_current; - if (HasCurrent()) - GetLine(m_current)->GetItem( 0, le.m_item ); + le.m_itemIndex = current; + if ( current != (size_t)-1 ) + GetLine(current)->GetItem( 0, le.m_item ); le.m_code = event.GetKeyCode(); le.SetEventObject( parent ); if (parent->GetEventHandler()->ProcessEvent( le )) @@ -2956,7 +3100,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) { ReverseHighlight(m_current); } - else // normal space press + else if ( ShouldSendEventForCurrent() ) // normal space press { SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED ); } @@ -2969,7 +3113,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) case WXK_RETURN: case WXK_EXECUTE: - if ( event.HasModifiers() ) + if ( event.HasModifiers() || !ShouldSendEventForCurrent() ) { event.Skip(); break; @@ -3078,6 +3222,7 @@ void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) { m_hasFocus = true; + UpdateCurrent(); RefreshSelected(); } } @@ -3621,17 +3766,7 @@ int wxListMainWindow::GetSelectedItemCount() const if ( IsVirtual() ) return m_selStore.GetSelectedCount(); - // TODO: we probably should maintain the number of items selected even for - // non virtual controls as enumerating all lines is really slow... - size_t countSel = 0; - size_t count = GetItemCount(); - for ( size_t line = 0; line < count; line++ ) - { - if ( GetLine(line)->IsHighlighted() ) - countSel++; - } - - return countSel; + return m_selCount; } // ---------------------------------------------------------------------------- @@ -4036,9 +4171,6 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh) if ( !noRefresh ) { - // FIXME: why should we call it from here? - UpdateCurrent(); - RefreshAll(); } } @@ -4059,7 +4191,13 @@ void wxListMainWindow::RefreshAll() void wxListMainWindow::UpdateCurrent() { if ( !HasCurrent() && !IsEmpty() ) - ChangeCurrent(0); + { + // Initialise m_current to the first item without sending any + // wxEVT_LIST_ITEM_FOCUSED event (typicaly when the control gains focus) + // and this is to allow changing the focused item using the arrow keys. + // which is the behaviour found in the wxMSW port. + ChangeCurrentWithoutEvent(0); + } } long wxListMainWindow::GetNextItem( long item, @@ -4248,6 +4386,10 @@ void wxListMainWindow::DoDeleteAllItems() m_countVirt = 0; m_selStore.Clear(); } + else + { + m_selCount = 0; + } if ( InReportView() ) ResetVisibleLinesRange(); diff --git a/src/gtk/textctrl.cpp b/src/gtk/textctrl.cpp index 4e4cf7a5b5..977954ca35 100644 --- a/src/gtk/textctrl.cpp +++ b/src/gtk/textctrl.cpp @@ -684,8 +684,9 @@ void wxTextCtrl::Init() m_text = NULL; m_buffer = NULL; - m_showPositionOnThaw = NULL; + m_showPositionDefer = NULL; m_anonymousMarkList = NULL; + m_afterLayoutId = 0; } wxTextCtrl::~wxTextCtrl() @@ -702,6 +703,8 @@ wxTextCtrl::~wxTextCtrl() if (m_anonymousMarkList) g_slist_free(m_anonymousMarkList); + if (m_afterLayoutId) + g_source_remove(m_afterLayoutId); } wxTextCtrl::wxTextCtrl( wxWindow *parent, @@ -1102,6 +1105,25 @@ bool wxTextCtrl::IsEmpty() const return wxTextEntry::IsEmpty(); } +void wxTextCtrl::GTKAfterLayout() +{ + m_afterLayoutId = 0; + if (m_showPositionDefer && !IsFrozen()) + { + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), m_showPositionDefer); + m_showPositionDefer = NULL; + } +} + +extern "C" { +static gboolean afterLayout(void* data) +{ + wxTextCtrl* win = static_cast(data); + win->GTKAfterLayout(); + return false; +} +} + void wxTextCtrl::WriteText( const wxString &text ) { wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") ); @@ -1163,15 +1185,16 @@ void wxTextCtrl::WriteText( const wxString &text ) gtk_text_buffer_delete_selection(m_buffer, false, true); // Insert the text + GtkTextMark* insertMark = gtk_text_buffer_get_insert(m_buffer); GtkTextIter iter; - gtk_text_buffer_get_iter_at_mark( m_buffer, &iter, - gtk_text_buffer_get_insert (m_buffer) ); + gtk_text_buffer_get_iter_at_mark(m_buffer, &iter, insertMark); + + const bool insertIsEnd = gtk_text_iter_is_end(&iter) != 0; gtk_text_buffer_insert( m_buffer, &iter, buffer, buffer.length() ); - // Scroll to cursor, but only if scrollbar thumb is at the very bottom - // won't work when frozen, text view is not using m_buffer then - if (!IsFrozen()) + // Scroll to cursor, if it is at the end and scrollbar thumb is at the bottom + if (insertIsEnd) { GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget)); const double value = gtk_adjustment_get_value(adj); @@ -1179,10 +1202,19 @@ void wxTextCtrl::WriteText( const wxString &text ) const double page_size = gtk_adjustment_get_page_size(adj); if (wxIsSameDouble(value, upper - page_size)) { - gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(m_text), - gtk_text_buffer_get_insert(m_buffer), 0, false, 0, 1); + if (!IsFrozen()) + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), insertMark); + + // GtkTextView's incremental background layout makes scrolling + // to end unreliable until the layout has been completed + m_showPositionDefer = insertMark; } } + if (m_afterLayoutId == 0) + { + m_afterLayoutId = + g_idle_add_full(GTK_TEXT_VIEW_PRIORITY_VALIDATE + 1, afterLayout, this, NULL); + } } wxString wxTextCtrl::GetLineText( long lineNo ) const @@ -1363,7 +1395,7 @@ void wxTextCtrl::SetInsertionPoint( long pos ) GtkTextMark* mark = gtk_text_buffer_get_insert(m_buffer); if (IsFrozen()) // defer until Thaw, text view is not using m_buffer now - m_showPositionOnThaw = mark; + m_showPositionDefer = mark; else gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), mark); } @@ -1480,7 +1512,7 @@ void wxTextCtrl::ShowPosition( long pos ) gtk_text_buffer_move_mark(m_buffer, mark, &iter); if (IsFrozen()) // defer until Thaw, text view is not using m_buffer now - m_showPositionOnThaw = mark; + m_showPositionDefer = mark; else gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), mark); } @@ -2136,11 +2168,11 @@ void wxTextCtrl::DoThaw() g_object_unref(m_buffer); g_signal_handler_disconnect(m_buffer, sig_id); - if (m_showPositionOnThaw != NULL) + if (m_showPositionDefer) { - gtk_text_view_scroll_mark_onscreen( - GTK_TEXT_VIEW(m_text), m_showPositionOnThaw); - m_showPositionOnThaw = NULL; + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(m_text), m_showPositionDefer); + if (m_afterLayoutId == 0) + m_showPositionDefer = NULL; } } diff --git a/src/gtk/webview_webkit2.cpp b/src/gtk/webview_webkit2.cpp index 69583a546a..c507d4b64a 100644 --- a/src/gtk/webview_webkit2.cpp +++ b/src/gtk/webview_webkit2.cpp @@ -14,6 +14,7 @@ #include "wx/dir.h" #include "wx/dynlib.h" #include "wx/filename.h" +#include "wx/stdpaths.h" #include "wx/stockitem.h" #include "wx/gtk/webview_webkit.h" #include "wx/gtk/control.h" @@ -369,7 +370,7 @@ wxgtk_webview_webkit_counted_matches(WebKitFindController *, } // This function checks if the specified directory contains our web extension. -static bool CheckDirectoryForWebExt(const char* dirname) +static bool CheckDirectoryForWebExt(const wxString& dirname) { wxDir dir; if ( !wxDir::Exists(dirname) || !dir.Open(dirname) ) @@ -399,6 +400,23 @@ static bool CheckDirectoryForWebExt(const char* dirname) return false; } +static bool TrySetWebExtensionsDirectory(WebKitWebContext *context, const wxString& dir) +{ + if (dir.empty() || !CheckDirectoryForWebExt(dir)) + return false; + + webkit_web_context_set_web_extensions_directory(context, dir.utf8_str()); + return true; +} + +static wxString GetStandardWebExtensionsDir() +{ + wxString dir = wxDynamicLibrary::GetPluginsDirectory(); + if ( !dir.empty() ) + dir += "/web-extensions"; + return dir; +} + static void wxgtk_initialize_web_extensions(WebKitWebContext *context, GDBusServer *dbusServer) @@ -406,36 +424,28 @@ wxgtk_initialize_web_extensions(WebKitWebContext *context, const char *address = g_dbus_server_get_client_address(dbusServer); GVariant *user_data = g_variant_new("(s)", address); - // The first value is the location in which the extension is supposed to be - // normally installed, while the other three are used as fallbacks to allow - // running the tests and sample using wxWebView before installing it. - const char* const directories[] = + // Try to setup extension loading from the location it is supposed to be + // normally installed in. + if ( !TrySetWebExtensionsDirectory(context, GetStandardWebExtensionsDir()) ) { - WX_WEB_EXTENSIONS_DIRECTORY, - "..", - "../..", - "lib", - }; - - const char* dir = NULL; - for ( size_t n = 0; n < WXSIZEOF(directories); ++n ) - { - if ( CheckDirectoryForWebExt(directories[n]) ) + // These relative locations are used as fallbacks to allow running + // the tests and sample using wxWebView before installing it. + wxString exepath = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath(); + if ( !exepath.empty() ) { - dir = directories[n]; - break; - } - } + wxString const directories[] = + { + exepath + "/..", + exepath + "/../..", + exepath + "/lib", + }; - if ( dir ) - { - webkit_web_context_set_web_extensions_directory(context, dir); - } - else - { - wxLogWarning(_("Web extension not found in \"%s\", " - "some wxWebView functionality will be not available"), - WX_WEB_EXTENSIONS_DIRECTORY); + for ( size_t n = 0; n < WXSIZEOF(directories); ++n ) + { + if ( !TrySetWebExtensionsDirectory(context, directories[n]) ) + break; + } + } } webkit_web_context_set_web_extensions_initialization_user_data(context, @@ -1020,10 +1030,11 @@ bool wxWebViewWebKit::IsEditable() const void wxWebViewWebKit::DeleteSelection() { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "DeleteSelection", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1037,10 +1048,11 @@ void wxWebViewWebKit::DeleteSelection() bool wxWebViewWebKit::HasSelection() const { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "HasSelection", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1064,10 +1076,11 @@ void wxWebViewWebKit::SelectAll() wxString wxWebViewWebKit::GetSelectedText() const { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "GetSelectedText", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1085,10 +1098,11 @@ wxString wxWebViewWebKit::GetSelectedText() const wxString wxWebViewWebKit::GetSelectedSource() const { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "GetSelectedSource", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1106,10 +1120,11 @@ wxString wxWebViewWebKit::GetSelectedSource() const void wxWebViewWebKit::ClearSelection() { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "ClearSelection", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1123,10 +1138,11 @@ void wxWebViewWebKit::ClearSelection() wxString wxWebViewWebKit::GetPageText() const { - if (m_extension) + GDBusProxy *extension = GetExtensionProxy(); + if (extension) { guint64 page_id = webkit_web_view_get_page_id(m_web_view); - GVariant *retval = g_dbus_proxy_call_sync(m_extension, + GVariant *retval = g_dbus_proxy_call_sync(extension, "GetPageText", g_variant_new("(t)", page_id), G_DBUS_CALL_FLAGS_NONE, -1, @@ -1410,4 +1426,15 @@ void wxWebViewWebKit::SetupWebExtensionServer() g_object_unref(observer); } +GDBusProxy *wxWebViewWebKit::GetExtensionProxy() const +{ + if (!m_extension) + { + g_warning("Web extension not found in \"%s\", " + "some wxWebView functionality will be not available", + (const char*)GetStandardWebExtensionsDir().utf8_str()); + } + return m_extension; +} + #endif // wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT2 diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 6ebc77b68e..fe7fcd7737 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2118,6 +2118,28 @@ wxPoint wxMSWDCImpl::LogicalToDevice(wxCoord x, wxCoord y) const return wxPoint(p[0].x, p[0].y); } +wxSize wxMSWDCImpl::DeviceToLogicalRel(int x, int y) const +{ + POINT p[2]; + p[0].x = 0; + p[0].y = 0; + p[1].x = x; + p[1].y = y; + ::DPtoLP(GetHdc(), p, WXSIZEOF(p)); + return wxSize(p[1].x-p[0].x, p[1].y-p[0].y); +} + +wxSize wxMSWDCImpl::LogicalToDeviceRel(int x, int y) const +{ + POINT p[2]; + p[0].x = 0; + p[0].y = 0; + p[1].x = x; + p[1].y = y; + ::LPtoDP(GetHdc(), p, WXSIZEOF(p)); + return wxSize(p[1].x-p[0].x, p[1].y-p[0].y); +} + // ---------------------------------------------------------------------------- // Transform matrix // ---------------------------------------------------------------------------- diff --git a/src/msw/graphicsd2d.cpp b/src/msw/graphicsd2d.cpp index a436dd6243..cc6bdb08d4 100644 --- a/src/msw/graphicsd2d.cpp +++ b/src/msw/graphicsd2d.cpp @@ -4494,11 +4494,11 @@ void wxD2DContext::DrawBitmap(const wxGraphicsBitmap& bmp, wxDouble x, wxDouble wxD2DBitmapData* bitmapData = wxGetD2DBitmapData(bmp); bitmapData->Bind(this); - D2D1_SIZE_F imgSize = bitmapData->GetD2DBitmap()->GetSize(); + wxBitmap const& bitmap = static_cast(bitmapData->GetNativeBitmap())->GetSourceBitmap(); m_renderTargetHolder->DrawBitmap( bitmapData->GetD2DBitmap(), - D2D1::RectF(0, 0, imgSize.width, imgSize.height), + D2D1::RectF(0, 0, bitmap.GetWidth(), bitmap.GetHeight()), D2D1::RectF(x, y, x + w, y + h), GetInterpolationQuality(), GetCompositionMode()); diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index b5d912a764..33031f3d70 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -788,7 +788,15 @@ bool wxListCtrl::SetColumnWidth(int col, int width) else if ( width == wxLIST_AUTOSIZE_USEHEADER) width = LVSCW_AUTOSIZE_USEHEADER; - return ListView_SetColumnWidth(GetHwnd(), col, width) != 0; + if ( !ListView_SetColumnWidth(GetHwnd(), col, width) ) + return false; + + // Failure to explicitly refresh the control with horizontal rules results + // in corrupted rules display. + if ( HasFlag(wxLC_HRULES) ) + Refresh(); + + return true; } // ---------------------------------------------------------------------------- @@ -3261,12 +3269,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.SetPen(pen); dc.SetBrush(* wxTRANSPARENT_BRUSH); - // Find the coordinate of the right most visible point: this is not the - // same as GetClientSize().x because the window might not be fully visible, - // it could be clipped by its parent. - const int availableWidth = GetParent()->GetClientSize().x - GetPosition().x; - int visibleWidth = wxMin(GetClientSize().x, - availableWidth - GetWindowBorderSize().x); + wxSize clientSize = GetClientSize(); const int countPerPage = GetCountPerPage(); if (countPerPage < 0) @@ -3278,9 +3281,6 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) const long top = GetTopItem(); const long bottom = wxMin(top + countPerPage, itemCount - 1); - wxRect clipRect; - dc.GetClippingBox(clipRect); - if (drawHRules) { wxRect itemRect; @@ -3289,23 +3289,9 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) if (GetItemRect(i, itemRect)) { const int cy = itemRect.GetBottom(); - dc.DrawLine(clipRect.x, cy, clipRect.GetRight() + 1, cy); + dc.DrawLine(0, cy, clientSize.x, cy); } } - - /* - The drawing can be clipped horizontally to the rightmost column. - This happens when an item is added (and visible) and results in a - horizontal rule being clipped instead of drawn across the entire - list control. In that case we request for the part to the right of - the rightmost column to be drawn as well. - */ - if ( clipRect.GetRight() < visibleWidth - 1 && clipRect.width ) - { - RefreshRect(wxRect(clipRect.GetRight(), clipRect.y, - visibleWidth - clipRect.width, clipRect.height), - false /* don't erase background */); - } } if (drawVRules) @@ -3347,7 +3333,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) wxDCBrushChanger changeBrush(dc, GetBackgroundColour()); dc.DrawRectangle(0, topItemRect.GetY() - gap, - visibleWidth, gap); + clientSize.GetWidth(), gap); } const int numCols = GetColumnCount(); diff --git a/src/msw/slider.cpp b/src/msw/slider.cpp index d859ea5d55..3cb247965d 100644 --- a/src/msw/slider.cpp +++ b/src/msw/slider.cpp @@ -313,15 +313,28 @@ bool wxSlider::MSWOnScroll(int WXUNUSED(orientation), SetValue(newPos); wxScrollEvent event(scrollEvent, m_windowId); + bool processed = false; + event.SetPosition(newPos); event.SetEventObject( this ); - HandleWindowEvent(event); + processed = HandleWindowEvent(event); - wxCommandEvent cevent( wxEVT_SLIDER, GetId() ); - cevent.SetInt( newPos ); - cevent.SetEventObject( this ); + // Do not generate wxEVT_SLIDER when the native scroll message + // parameter is SB_ENDSCROLL, which always follows only after + // another scroll message which already changed the slider value. + // Therefore, sending wxEVT_SLIDER after SB_ENDSCROLL + // would result in two wxEVT_SLIDER events with the same value. + if ( wParam != SB_ENDSCROLL ) + { + wxCommandEvent cevent( wxEVT_SLIDER, GetId() ); - return HandleWindowEvent( cevent ); + cevent.SetInt( newPos ); + cevent.SetEventObject( this ); + + processed = HandleWindowEvent( cevent ); + } + + return processed; } void wxSlider::Command (wxCommandEvent & event) diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 813bb1face..7f26b8585e 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -518,7 +518,7 @@ void wxWidgetCocoaImpl::SetupKeyEvent(wxKeyEvent &wxevent , NSEvent * nsEvent, N } } - if ( !keyval ) + if ( !keyval && aunichar < 256 ) // only for ASCII characters { if ( wxevent.GetEventType() == wxEVT_KEY_UP || wxevent.GetEventType() == wxEVT_KEY_DOWN ) keyval = wxToupper( aunichar ) ; diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 03921a9ce9..96405ffba6 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -355,7 +355,6 @@ void wxPropertyGrid::Init1() m_inOnValidationFailure = false; m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT; m_dragStatus = 0; - m_mouseSide = 16; m_editorFocused = false; // Set up default unspecified value 'colour' @@ -5002,8 +5001,11 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, int splitterHit; int splitterHitOffset; int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset ); - int splitterX = x - splitterHitOffset; + #if wxUSE_TOOLTIPS + wxPGProperty* prevHover = m_propHover; + int prevCol = m_colHover; + #endif m_colHover = columnHit; if ( m_dragStatus > 0 ) @@ -5012,6 +5014,7 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, x < (m_pState->GetVirtualWidth() - wxPG_DRAG_MARGIN) ) { + int splitterX = x - splitterHitOffset; int newSplitterX = x - m_dragOffset; // Splitter redraw required? @@ -5042,10 +5045,6 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, int ih = m_lineHeight; int sy = y; - #if wxUSE_TOOLTIPS - wxPGProperty* prevHover = m_propHover; - unsigned char prevSide = m_mouseSide; - #endif int curPropHoverY = y - (y % ih); // On which item it hovers @@ -5064,20 +5063,13 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, } #if wxUSE_TOOLTIPS - // Store which side we are on - m_mouseSide = 0; - if ( columnHit == 1 ) - m_mouseSide = 2; - else if ( columnHit == 0 ) - m_mouseSide = 1; - // // If tooltips are enabled, show label or value as a tip // in case it doesn't otherwise show in full length. // if ( m_windowStyle & wxPG_TOOLTIPS ) { - if ( m_propHover != prevHover || prevSide != m_mouseSide ) + if ( m_propHover != prevHover || prevCol != m_colHover ) { if ( m_propHover && !m_propHover->IsCategory() ) { @@ -5089,40 +5081,59 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, SetToolTip(tipString); } - else + else if ( m_colHover >= 0 && m_colHover < (int)m_pState->GetColumnCount()) { // Show cropped value string as a tooltip wxString tipString; - int space = 0; + wxPGCell cell; + int item = ( m_colHover == 1 ? m_propHover->GetChoiceSelection() : -1 ); + m_propHover->GetDisplayInfo(m_colHover, item, 0, &tipString, &cell); + int space = m_pState->GetColumnWidth(m_colHover); - if ( m_mouseSide == 1 ) + int imageWidth = 0; + const wxBitmap& bmp = cell.GetBitmap(); + if ( bmp.IsOk() ) { - tipString = m_propHover->GetLabel(); - space = splitterX-m_marginWidth-3; - } - else if ( m_mouseSide == 2 ) - { - tipString = m_propHover->GetDisplayedString(); - - space = m_width - splitterX; - if ( m_propHover->HasFlag(wxPG_PROP_CUSTOMIMAGE) ) - space -= wxPG_CUSTOM_IMAGE_WIDTH + - wxCC_CUSTOM_IMAGE_MARGIN1 + - wxCC_CUSTOM_IMAGE_MARGIN2; + imageWidth = bmp.GetWidth(); + int hMax = m_lineHeight - wxPG_CUSTOM_IMAGE_SPACINGY - 1; + if ( bmp.GetHeight() > hMax ) + imageWidth *= (double)hMax / bmp.GetHeight(); } - if ( space ) + if ( m_colHover == 0 ) { - int tw, th; - GetTextExtent( tipString, &tw, &th, 0, 0 ); - if ( tw > space ) - SetToolTip( tipString ); + if ( !(m_windowStyle & wxPG_HIDE_CATEGORIES) || m_propHover->GetParent() != m_pState->DoGetRoot() ) + space -= (m_propHover->GetDepth()-1)*m_subgroup_extramargin; } - else + else if ( m_colHover == 1 && !m_propHover->IsValueUnspecified()) { - SetToolTip(wxEmptyString); + wxSize imageSize = GetImageSize(m_propHover, -1); + if ( imageSize.x > 0 ) + imageWidth = imageSize.x; + tipString = m_propHover->GetValueAsString(); + if ( GetColumnCount() <= 2 ) + { + wxString unitsString = m_propHover->GetAttribute(wxPG_ATTR_UNITS, wxEmptyString); + if ( !unitsString.empty() ) + tipString = wxString::Format(wxS("%s %s"), tipString, unitsString ); + } } + space -= m_propHover->GetImageOffset(imageWidth); + space -= (wxPG_XBEFORETEXT + 1); + int tw, th; + const wxFont* font = NULL; + if ( (m_windowStyle & wxPG_BOLD_MODIFIED) && m_propHover->HasFlag(wxPG_PROP_MODIFIED) ) + font = &m_captionFont; + if ( cell.GetFont().IsOk() ) + font = &cell.GetFont(); + GetTextExtent( tipString, &tw, &th, 0, 0, font ); + if ( tw > space ) + SetToolTip( tipString ); + } + else + { + SetToolTip(wxEmptyString); } } else diff --git a/src/unix/snglinst.cpp b/src/unix/snglinst.cpp index 0c8d4275ef..9c64ffbf6f 100644 --- a/src/unix/snglinst.cpp +++ b/src/unix/snglinst.cpp @@ -298,7 +298,7 @@ bool wxSingleInstanceCheckerImpl::Create(const wxString& name) // message is that the previous instance of the program // crashed), don't show it by default, i.e. unless the // program is running with --verbose command line option. - wxLogInfo(_("Deleted stale lock file '%s'."), + wxLogVerbose(_("Deleted stale lock file '%s'."), name.c_str()); // retry now diff --git a/tests/Makefile.in b/tests/Makefile.in index 5cd6cc7bda..f99b4761e1 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -185,6 +185,7 @@ TEST_GUI_OBJECTS = \ test_gui_affinematrix.o \ test_gui_boundingbox.o \ test_gui_clippingbox.o \ + test_gui_coords.o \ test_gui_graphmatrix.o \ test_gui_graphpath.o \ test_gui_config.o \ @@ -547,7 +548,7 @@ data: data-images: @mkdir -p image - @for f in horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png; do \ + @for f in horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png; do \ if test ! -f image/$$f -a ! -d image/$$f ; \ then x=yep ; \ else x=`find $(srcdir)/image/$$f -newer image/$$f -print` ; \ @@ -899,6 +900,9 @@ test_gui_boundingbox.o: $(srcdir)/graphics/boundingbox.cpp $(TEST_GUI_ODEP) test_gui_clippingbox.o: $(srcdir)/graphics/clippingbox.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/graphics/clippingbox.cpp +test_gui_coords.o: $(srcdir)/graphics/coords.cpp $(TEST_GUI_ODEP) + $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/graphics/coords.cpp + test_gui_graphmatrix.o: $(srcdir)/graphics/graphmatrix.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/graphics/graphmatrix.cpp diff --git a/tests/controls/bitmapcomboboxtest.cpp b/tests/controls/bitmapcomboboxtest.cpp index 2a011b3869..904eb6079f 100644 --- a/tests/controls/bitmapcomboboxtest.cpp +++ b/tests/controls/bitmapcomboboxtest.cpp @@ -24,9 +24,6 @@ #include "itemcontainertest.h" #include "asserthelper.h" -//Test only if we are based off of wxComboBox -#ifndef wxGENERIC_BITMAPCOMBOBOX - class BitmapComboBoxTestCase : public TextEntryTestCase, public ItemContainerTestCase, public CppUnit::TestCase @@ -82,9 +79,9 @@ void BitmapComboBoxTestCase::Bitmap() wxArrayString items; items.push_back("item 0"); items.push_back("item 1"); - - //We need this otherwise MSVC complains as it cannot find a suitable append - static_cast(m_combo)->Append(items); + // TODO: Add wxBitmapComboBoxBase::Append(wxArrayString ) + for( unsigned int i = 0; i < items.size(); ++i ) + m_combo->Append(items[i]); CPPUNIT_ASSERT(!m_combo->GetItemBitmap(0).IsOk()); @@ -104,8 +101,10 @@ void BitmapComboBoxTestCase::Bitmap() CPPUNIT_ASSERT(m_combo->GetItemBitmap(0).IsOk()); CPPUNIT_ASSERT_EQUAL(wxSize(16, 16), m_combo->GetBitmapSize()); + + m_combo->SetSelection( 1 ); + + CPPUNIT_ASSERT_EQUAL( m_combo->GetStringSelection(), "item with bitmap" ); } -#endif //wxGENERIC_BITMAPCOMBOBOX - #endif //wxUSE_BITMAPCOMBOBOX diff --git a/tests/controls/listbasetest.cpp b/tests/controls/listbasetest.cpp index 2bee21811d..5673676e74 100644 --- a/tests/controls/listbasetest.cpp +++ b/tests/controls/listbasetest.cpp @@ -26,6 +26,7 @@ #include "wx/uiaction.h" #include "wx/imaglist.h" #include "wx/artprov.h" +#include "wx/stopwatch.h" void ListBaseTestCase::ColumnsOrder() { @@ -177,6 +178,123 @@ void ListBaseTestCase::ChangeMode() CPPUNIT_ASSERT_EQUAL( "First", list->GetItemText(0) ); } +#ifdef __WXGTK__ + #define wxGTK_TIMED_YIELD(t) \ + if ( !IsRunningUnderXVFB() ) \ + for ( wxStopWatch sw; sw.Time() < t; ) wxYield() +#else // !__WXGTK__ + #define wxGTK_TIMED_YIELD(t) +#endif // __WXGTK__ + +void ListBaseTestCase::MultiSelect() +{ +#if wxUSE_UIACTIONSIMULATOR + +#ifndef __WXMSW__ + // FIXME: This test fails on Travis CI although works fine on + // development machine, no idea why though! + if ( IsAutomaticTest() ) + return; +#endif // !__WXMSW__ + + wxListCtrl* const list = GetList(); + + EventCounter focused(list, wxEVT_LIST_ITEM_FOCUSED); + EventCounter selected(list, wxEVT_LIST_ITEM_SELECTED); + EventCounter deselected(list, wxEVT_LIST_ITEM_DESELECTED); + + list->InsertColumn(0, "Header"); + + for ( int i = 0; i < 10; ++i ) + list->InsertItem(i, wxString::Format("Item %d", i)); + + wxUIActionSimulator sim; + + wxRect pos; + list->GetItemRect(2, pos); // Choose the third item as anchor + + // We move in slightly so we are not on the edge + wxPoint point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 10); + + sim.MouseMove(point); + wxYield(); + + sim.MouseClick(); // select the anchor + wxYield(); + + wxGTK_TIMED_YIELD(50); + + list->GetItemRect(5, pos); + point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 10); + + sim.MouseMove(point); + wxYield(); + + sim.KeyDown(WXK_SHIFT); + sim.MouseClick(); + sim.KeyUp(WXK_SHIFT); + wxYield(); + + wxGTK_TIMED_YIELD(10); + + // when the first item was selected the focus changes to it, but not + // on subsequent clicks + CPPUNIT_ASSERT_EQUAL(4, list->GetSelectedItemCount()); // item 2 to 5 (inclusive) are selected + CPPUNIT_ASSERT_EQUAL(2, focused.GetCount()); // count the focus which was on the anchor + CPPUNIT_ASSERT_EQUAL(4, selected.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, deselected.GetCount()); + + focused.Clear(); + selected.Clear(); + deselected.Clear(); + + sim.Char(WXK_END, wxMOD_SHIFT); // extend the selection to the last item + wxYield(); + + wxGTK_TIMED_YIELD(10); + + CPPUNIT_ASSERT_EQUAL(8, list->GetSelectedItemCount()); // item 2 to 9 (inclusive) are selected + CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); // focus is on the last item + CPPUNIT_ASSERT_EQUAL(4, selected.GetCount()); // only newly selected items got the event + CPPUNIT_ASSERT_EQUAL(0, deselected.GetCount()); + + focused.Clear(); + selected.Clear(); + deselected.Clear(); + + sim.Char(WXK_HOME, wxMOD_SHIFT); // select from anchor to the first item + wxYield(); + + wxGTK_TIMED_YIELD(10); + + CPPUNIT_ASSERT_EQUAL(3, list->GetSelectedItemCount()); // item 0 to 2 (inclusive) are selected + CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); // focus is on item 0 + CPPUNIT_ASSERT_EQUAL(2, selected.GetCount()); // events are only generated for item 0 and 1 + CPPUNIT_ASSERT_EQUAL(7, deselected.GetCount()); // item 2 (exclusive) to 9 are deselected + + focused.Clear(); + selected.Clear(); + deselected.Clear(); + + list->EnsureVisible(0); + wxYield(); + + list->GetItemRect(2, pos); + point = list->ClientToScreen(pos.GetPosition()) + wxPoint(10, 10); + + sim.MouseMove(point); + wxYield(); + + sim.MouseClick(); + wxYield(); + + CPPUNIT_ASSERT_EQUAL(1, list->GetSelectedItemCount()); // anchor is the only selected item + CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); // because the focus changed from item 0 to anchor + CPPUNIT_ASSERT_EQUAL(0, selected.GetCount()); // anchor is already in selection state + CPPUNIT_ASSERT_EQUAL(2, deselected.GetCount()); // items 0 and 1 are deselected +#endif // wxUSE_UIACTIONSIMULATOR +} + void ListBaseTestCase::ItemClick() { #if wxUSE_UIACTIONSIMULATOR @@ -236,16 +354,9 @@ void ListBaseTestCase::ItemClick() // when the first item was selected the focus changes to it, but not // on subsequent clicks - - // FIXME: This test fail under wxGTK & wxOSX because we get 3 FOCUSED events and - // 2 SELECTED ones instead of the one of each we expect for some - // reason, this needs to be debugged as it may indicate a bug in the - // generic wxListCtrl implementation. -#ifndef _WX_GENERIC_LISTCTRL_H_ CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); CPPUNIT_ASSERT_EQUAL(1, selected.GetCount()); CPPUNIT_ASSERT_EQUAL(1, deselected.GetCount()); -#endif CPPUNIT_ASSERT_EQUAL(1, activated.GetCount()); CPPUNIT_ASSERT_EQUAL(1, rclick.GetCount()); #endif // wxUSE_UIACTIONSIMULATOR diff --git a/tests/controls/listbasetest.h b/tests/controls/listbasetest.h index d5840767c9..8b8df808b7 100644 --- a/tests/controls/listbasetest.h +++ b/tests/controls/listbasetest.h @@ -26,6 +26,7 @@ protected: CPPUNIT_TEST( ChangeMode ); \ WXUISIM_TEST( ItemClick ); \ WXUISIM_TEST( KeyDown ); \ + WXUISIM_TEST( MultiSelect ); \ CPPUNIT_TEST( DeleteItems ); \ CPPUNIT_TEST( InsertItem ); \ CPPUNIT_TEST( Find ); \ @@ -40,6 +41,7 @@ protected: void ItemRect(); void ItemText(); void ChangeMode(); + void MultiSelect(); void ItemClick(); void KeyDown(); void DeleteItems(); diff --git a/tests/controls/listviewtest.cpp b/tests/controls/listviewtest.cpp index 94162d3eab..1ddd6c6f33 100644 --- a/tests/controls/listviewtest.cpp +++ b/tests/controls/listviewtest.cpp @@ -20,6 +20,7 @@ #include "wx/listctrl.h" #include "listbasetest.h" +#include "testableframe.h" class ListViewTestCase : public ListBaseTestCase, public CppUnit::TestCase { @@ -61,7 +62,8 @@ void ListViewTestCase::setUp() void ListViewTestCase::tearDown() { - wxDELETE(m_list); + DeleteTestWindow(m_list); + m_list = NULL; } void ListViewTestCase::Selection() @@ -104,6 +106,8 @@ void ListViewTestCase::Selection() void ListViewTestCase::Focus() { + EventCounter focused(m_list, wxEVT_LIST_ITEM_FOCUSED); + m_list->InsertColumn(0, "Column 0"); m_list->InsertItem(0, "Item 0"); @@ -111,10 +115,12 @@ void ListViewTestCase::Focus() m_list->InsertItem(2, "Item 2"); m_list->InsertItem(3, "Item 3"); + CPPUNIT_ASSERT_EQUAL(0, focused.GetCount()); CPPUNIT_ASSERT_EQUAL(-1, m_list->GetFocusedItem()); m_list->Focus(0); + CPPUNIT_ASSERT_EQUAL(1, focused.GetCount()); CPPUNIT_ASSERT_EQUAL(0, m_list->GetFocusedItem()); } diff --git a/tests/controls/radiobuttontest.cpp b/tests/controls/radiobuttontest.cpp index a81d6aec61..cdcc144972 100644 --- a/tests/controls/radiobuttontest.cpp +++ b/tests/controls/radiobuttontest.cpp @@ -27,40 +27,19 @@ #include "testableframe.h" #include "testwindow.h" -class RadioButtonTestCase : public CppUnit::TestCase +class RadioButtonTestCase { public: - RadioButtonTestCase() { } - - void setUp() wxOVERRIDE; - void tearDown() wxOVERRIDE; - -private: - CPPUNIT_TEST_SUITE( RadioButtonTestCase ); - WXUISIM_TEST( Click ); - CPPUNIT_TEST( Value ); - CPPUNIT_TEST( Group ); - CPPUNIT_TEST( Single ); - CPPUNIT_TEST_SUITE_END(); - - void Click(); - void Value(); - void Group(); - void Single(); + RadioButtonTestCase(); + ~RadioButtonTestCase(); +protected: wxRadioButton* m_radio; wxDECLARE_NO_COPY_CLASS(RadioButtonTestCase); }; -// register in the unnamed registry so that these tests are run by default -CPPUNIT_TEST_SUITE_REGISTRATION( RadioButtonTestCase ); - -// also include in its own registry so that these tests can be run alone -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( RadioButtonTestCase, - "RadioButtonTestCase" ); - -void RadioButtonTestCase::setUp() +RadioButtonTestCase::RadioButtonTestCase() { m_radio = new wxRadioButton(wxTheApp->GetTopWindow(), wxID_ANY, "wxRadioButton"); @@ -68,12 +47,12 @@ void RadioButtonTestCase::setUp() m_radio->Refresh(); } -void RadioButtonTestCase::tearDown() +RadioButtonTestCase::~RadioButtonTestCase() { - wxDELETE(m_radio); + delete m_radio; } -void RadioButtonTestCase::Click() +TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Click", "[radiobutton]") { // OS X doesn't support selecting a single radio button #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__) @@ -87,87 +66,115 @@ void RadioButtonTestCase::Click() wxYield(); - CPPUNIT_ASSERT_EQUAL( 1, selected.GetCount() ); + CHECK(selected.GetCount() == 1); #endif } -void RadioButtonTestCase::Value() +TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Value", "[radiobutton]") { #ifndef __WXGTK__ EventCounter selected(m_radio, wxEVT_RADIOBUTTON); m_radio->SetValue(true); - CPPUNIT_ASSERT(m_radio->GetValue()); + CHECK(m_radio->GetValue()); m_radio->SetValue(false); - CPPUNIT_ASSERT(!m_radio->GetValue()); + CHECK(!m_radio->GetValue()); - CPPUNIT_ASSERT_EQUAL(0, selected.GetCount()); + CHECK(selected.GetCount() == 0); #endif } -void RadioButtonTestCase::Group() +TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Group", "[radiobutton]") { wxWindow* const parent = wxTheApp->GetTopWindow(); // Create two different radio groups. - wxRadioButton* g1radio0 = new wxRadioButton(parent, wxID_ANY, "radio 1.0", + wxScopedPtr g1radio0(new wxRadioButton(parent, wxID_ANY, "radio 1.0", wxDefaultPosition, wxDefaultSize, - wxRB_GROUP); + wxRB_GROUP)); - wxRadioButton* g1radio1 = new wxRadioButton(parent, wxID_ANY, "radio 1.1"); + wxScopedPtr g1radio1(new wxRadioButton(parent, wxID_ANY, "radio 1.1")); - wxRadioButton* g2radio0 = new wxRadioButton(parent, wxID_ANY, "radio 2.0", + wxScopedPtr g2radio0(new wxRadioButton(parent, wxID_ANY, "radio 2.0", wxDefaultPosition, wxDefaultSize, - wxRB_GROUP); + wxRB_GROUP)); - wxRadioButton* g2radio1 = new wxRadioButton(parent, wxID_ANY, "radio 2.1"); + wxScopedPtr g2radio1(new wxRadioButton(parent, wxID_ANY, "radio 2.1")); // Check that having another control between radio buttons doesn't break // grouping. - wxStaticText* text = new wxStaticText(parent, wxID_ANY, "Label"); - wxRadioButton* g2radio2 = new wxRadioButton(parent, wxID_ANY, "radio 2.1"); + wxScopedPtr text(new wxStaticText(parent, wxID_ANY, "Label")); + wxScopedPtr g2radio2(new wxRadioButton(parent, wxID_ANY, "radio 2.2")); g1radio0->SetValue(true); g2radio0->SetValue(true); - CPPUNIT_ASSERT(g1radio0->GetValue()); - CPPUNIT_ASSERT(!g1radio1->GetValue()); - CPPUNIT_ASSERT(g2radio0->GetValue()); - CPPUNIT_ASSERT(!g2radio1->GetValue()); + CHECK(g1radio0->GetValue()); + CHECK(!g1radio1->GetValue()); + CHECK(g2radio0->GetValue()); + CHECK(!g2radio1->GetValue()); g1radio1->SetValue(true); g2radio1->SetValue(true); - CPPUNIT_ASSERT(!g1radio0->GetValue()); - CPPUNIT_ASSERT(g1radio1->GetValue()); - CPPUNIT_ASSERT(!g2radio0->GetValue()); - CPPUNIT_ASSERT(g2radio1->GetValue()); + CHECK(!g1radio0->GetValue()); + CHECK(g1radio1->GetValue()); + CHECK(!g2radio0->GetValue()); + CHECK(g2radio1->GetValue()); g2radio2->SetValue(true); - CPPUNIT_ASSERT(!g2radio0->GetValue()); - CPPUNIT_ASSERT(!g2radio1->GetValue()); - CPPUNIT_ASSERT(g2radio2->GetValue()); + CHECK(!g2radio0->GetValue()); + CHECK(!g2radio1->GetValue()); + CHECK(g2radio2->GetValue()); g1radio0->SetValue(true); g2radio0->SetValue(true); - CPPUNIT_ASSERT(g1radio0->GetValue()); - CPPUNIT_ASSERT(!g1radio1->GetValue()); - CPPUNIT_ASSERT(g2radio0->GetValue()); - CPPUNIT_ASSERT(!g2radio1->GetValue()); + CHECK(g1radio0->GetValue()); + CHECK(!g1radio1->GetValue()); + CHECK(g2radio0->GetValue()); + CHECK(!g2radio1->GetValue()); - wxDELETE(g1radio0); - wxDELETE(g1radio1); - wxDELETE(g2radio0); - wxDELETE(g2radio1); - wxDELETE(g2radio2); - wxDELETE(text); + + // Check that group navigation functions behave as expected. + + // GetFirstInGroup() + CHECK_SAME_WINDOW(g1radio0->GetFirstInGroup(), g1radio0); + CHECK_SAME_WINDOW(g1radio1->GetFirstInGroup(), g1radio0); + + CHECK_SAME_WINDOW(g2radio0->GetFirstInGroup(), g2radio0); + CHECK_SAME_WINDOW(g2radio1->GetFirstInGroup(), g2radio0); + CHECK_SAME_WINDOW(g2radio2->GetFirstInGroup(), g2radio0); + + // GetLastInGroup() + CHECK_SAME_WINDOW(g1radio0->GetLastInGroup(), g1radio1); + CHECK_SAME_WINDOW(g1radio1->GetLastInGroup(), g1radio1); + + CHECK_SAME_WINDOW(g2radio0->GetLastInGroup(), g2radio2); + CHECK_SAME_WINDOW(g2radio1->GetLastInGroup(), g2radio2); + CHECK_SAME_WINDOW(g2radio2->GetLastInGroup(), g2radio2); + + // GetNextInGroup() + CHECK_SAME_WINDOW(g1radio0->GetNextInGroup(), g1radio1); + CHECK_SAME_WINDOW(g1radio1->GetNextInGroup(), NULL); + + CHECK_SAME_WINDOW(g2radio0->GetNextInGroup(), g2radio1); + CHECK_SAME_WINDOW(g2radio1->GetNextInGroup(), g2radio2); + CHECK_SAME_WINDOW(g2radio2->GetNextInGroup(), NULL); + + // GetPreviousInGroup() + CHECK_SAME_WINDOW(g1radio0->GetPreviousInGroup(), NULL); + CHECK_SAME_WINDOW(g1radio1->GetPreviousInGroup(), g1radio0); + + CHECK_SAME_WINDOW(g2radio0->GetPreviousInGroup(), NULL); + CHECK_SAME_WINDOW(g2radio1->GetPreviousInGroup(), g2radio0); + CHECK_SAME_WINDOW(g2radio2->GetPreviousInGroup(), g2radio1); } -void RadioButtonTestCase::Single() +TEST_CASE_METHOD(RadioButtonTestCase, "RadioButton::Single", "[radiobutton]") { //Create a group of 2 buttons, having second button selected wxScopedPtr gradio0(new wxRadioButton(wxTheApp->GetTopWindow(), @@ -197,9 +204,15 @@ void RadioButtonTestCase::Single() CHECK(gradio1->GetValue()); CHECK(ngradio->GetValue()); + + // Also check that navigation works as expected with "single" buttons. + CHECK_SAME_WINDOW(sradio->GetFirstInGroup(), sradio); + CHECK_SAME_WINDOW(sradio->GetLastInGroup(), sradio); + CHECK_SAME_WINDOW(sradio->GetPreviousInGroup(), NULL); + CHECK_SAME_WINDOW(sradio->GetNextInGroup(), NULL); } -TEST_CASE("wxRadioButton::Focus", "[radiobutton][focus]") +TEST_CASE("RadioButton::Focus", "[radiobutton][focus]") { // Create a container panel just to be able to destroy all the windows // created here at once by simply destroying it. diff --git a/tests/controls/slidertest.cpp b/tests/controls/slidertest.cpp index 9ecc97327c..aac59a2bca 100644 --- a/tests/controls/slidertest.cpp +++ b/tests/controls/slidertest.cpp @@ -35,6 +35,7 @@ private: #ifndef __WXOSX__ WXUISIM_TEST( PageUpDown ); WXUISIM_TEST( LineUpDown ); + WXUISIM_TEST( EvtSlider ); WXUISIM_TEST( LinePageSize ); #endif CPPUNIT_TEST( Value ); @@ -47,6 +48,7 @@ private: void PageUpDown(); void LineUpDown(); + void EvtSlider(); void LinePageSize(); void Value(); void Range(); @@ -125,6 +127,24 @@ void SliderTestCase::LineUpDown() #endif } +void SliderTestCase::EvtSlider() +{ +#if wxUSE_UIACTIONSIMULATOR + EventCounter slider(m_slider, wxEVT_SLIDER); + + wxUIActionSimulator sim; + wxYield(); + m_slider->SetFocus(); + + sim.Char(WXK_UP); + sim.Char(WXK_DOWN); + + wxYield(); + + CPPUNIT_ASSERT_EQUAL(2, slider.GetCount()); +#endif +} + void SliderTestCase::LinePageSize() { #if wxUSE_UIACTIONSIMULATOR diff --git a/tests/controls/windowtest.cpp b/tests/controls/windowtest.cpp index 372317e67b..cfcc4a0f6d 100644 --- a/tests/controls/windowtest.cpp +++ b/tests/controls/windowtest.cpp @@ -136,10 +136,22 @@ TEST_CASE_METHOD(WindowTestCase, "Window::Mouse", "[window]") CHECK(m_window->GetCursor().IsOk()); #if wxUSE_CARET - //A plain window doesn't have a caret CHECK(!m_window->GetCaret()); - wxCaret* caret = new wxCaret(m_window, 16, 16); + wxCaret* caret; + + // Try creating the caret in two different, but normally equivalent, ways. + SECTION("Caret 1-step") + { + caret = new wxCaret(m_window, 16, 16); + } + + SECTION("Caret 2-step") + { + caret = new wxCaret(); + caret->Create(m_window, 16, 16); + } + m_window->SetCaret(caret); CHECK(m_window->GetCaret()->IsOk()); diff --git a/tests/graphics/coords.cpp b/tests/graphics/coords.cpp new file mode 100644 index 0000000000..061900cf99 --- /dev/null +++ b/tests/graphics/coords.cpp @@ -0,0 +1,1430 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/graphics/coords.cpp +// Purpose: Coordinates conversion unit tests +// Author: Artur Wieczorek +// Created: 2020-09-25 +// Copyright: (c) 2020 wxWidgets development team +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/bitmap.h" +#include "wx/dcgraph.h" +#include "wx/dcmemory.h" + + +// ---------------------------------------------------------------------------- +// test class +// ---------------------------------------------------------------------------- + +static const wxSize s_dcSize(100, 100); +static const wxPoint s_posDev(24, 57); +static const wxSize s_dimDev(40, 15); + +// ==================== +// wxDC / wxGCDC tests +// ==================== + +class CoordinatesDCTestCaseBase +{ +public: + CoordinatesDCTestCaseBase() + { + m_bmp.Create(s_dcSize); + m_dc = NULL; + } + + virtual ~CoordinatesDCTestCaseBase() + { + m_bmp = wxNullBitmap; + } + +protected: + wxBitmap m_bmp; + wxDC* m_dc; +}; + +// =========== +// wxDC tests +// =========== + +class CoordinatesDCTestCase : public CoordinatesDCTestCaseBase +{ +public: + CoordinatesDCTestCase() + { + m_mdc.SelectObject(m_bmp); + m_dc = &m_mdc; + } + + virtual ~CoordinatesDCTestCase() + { + m_mdc.SelectObject(wxNullBitmap); + } + +protected: + wxMemoryDC m_mdc; +}; + +#if wxUSE_GRAPHICS_CONTEXT +// ============= +// wxGCDC tests +// ============= + +class CoordinatesGCDCTestCase : public CoordinatesDCTestCase +{ +public: + CoordinatesGCDCTestCase() + { + m_gcdc = new wxGCDC(m_mdc); + + wxGraphicsContext* ctx = m_gcdc->GetGraphicsContext(); + ctx->SetAntialiasMode(wxANTIALIAS_NONE); + ctx->DisableOffset(); + + m_dc = m_gcdc; + } + + virtual ~CoordinatesGCDCTestCase() + { + delete m_gcdc; + } + +protected: + wxGCDC* m_gcdc; +}; +#endif // wxUSE_GRAPHICS_CONTEXT + +// ===== Implementation ===== + +static void InitialState(wxDC* dc) +{ + // Check initial state + + wxPoint origin = dc->GetDeviceOrigin(); + CHECK(origin.x == 0); + CHECK(origin.y == 0); + + origin = dc->GetLogicalOrigin(); + CHECK(origin.x == 0); + CHECK(origin.y == 0); + + double sx, sy; + dc->GetUserScale(&sx, &sy); + CHECK(sx == 1.0); + CHECK(sy == 1.0); + + dc->GetLogicalScale(&sx, &sy); + CHECK(sx == 1.0); + CHECK(sy == 1.0); + +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + wxAffineMatrix2D m = dc->GetTransformMatrix(); + CHECK(m.IsIdentity() == true); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void NoTransform(wxDC *dc) +{ + // No transformations + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == s_posDev.x); + CHECK(posLog.y == s_posDev.y); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void NoTransformEx(wxDC * dc) +{ + // No transformations + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == s_posDev.x); + CHECK(posLog.y == s_posDev.y); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void DeviceOriginChanged(wxDC* dc) +{ + // Only device origin is changed + const wxCoord dx = 10; + const wxCoord dy = 15; + dc->SetDeviceOrigin(dx, dy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == s_posDev.x - dx); + CHECK(posLog.y == s_posDev.y - dy); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void DeviceOriginChangedEx(wxDC * dc) +{ + // Only device origin is changed + const wxCoord dx = 10; + const wxCoord dy = 15; + dc->SetDeviceOrigin(dx, dy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == s_posDev.x - dx); + CHECK(posLog.y == s_posDev.y - dy); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void LogicalOriginChanged(wxDC* dc) +{ + // Only logical origin is changed + const wxCoord dx = -15; + const wxCoord dy = -20; + dc->SetLogicalOrigin(dx, dy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == s_posDev.x + dx); + CHECK(posLog.y == s_posDev.y + dy); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void LogicalOriginChangedEx(wxDC * dc) +{ + // Only logical origin is changed + const wxCoord dx = -15; + const wxCoord dy = -20; + dc->SetLogicalOrigin(dx, dy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == s_posDev.x + dx); + CHECK(posLog.y == s_posDev.y + dy); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void UserScaleChanged(wxDC* dc) +{ + // Only user scale is changed + const double sx = 2.0; + const double sy = 3.0; + dc->SetUserScale(sx, sy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == wxRound(s_posDev.x / sx)); + CHECK(posLog.y == wxRound(s_posDev.y / sy)); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == wxRound(s_dimDev.x / sx)); + CHECK(dimLog.y == wxRound(s_dimDev.y / sy)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void UserScaleChangedEx(wxDC * dc) +{ + // Only user scale is changed + const double sx = 2.0; + const double sy = 3.0; + dc->SetUserScale(sx, sy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == wxRound(s_posDev.x / sx)); + CHECK(posLog.y == wxRound(s_posDev.y / sy)); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == wxRound(s_dimDev.x / sx)); + CHECK(dimLog.y == wxRound(s_dimDev.y / sy)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void LogicalScaleChanged(wxDC* dc) +{ + // Only logical scale is changed + const double sx = 2.0; + const double sy = 3.0; + dc->SetLogicalScale(sx, sy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == wxRound(s_posDev.x / sx)); + CHECK(posLog.y == wxRound(s_posDev.y / sy)); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == wxRound(s_dimDev.x / sx)); + CHECK(dimLog.y == wxRound(s_dimDev.y / sy)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void LogicalScaleChangedEx(wxDC * dc) +{ + // Only logical scale is changed + const double sx = 2.0; + const double sy = 3.0; + dc->SetLogicalScale(sx, sy); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == wxRound(s_posDev.x / sx)); + CHECK(posLog.y == wxRound(s_posDev.y / sy)); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == wxRound(s_dimDev.x / sx)); + CHECK(dimLog.y == wxRound(s_dimDev.y / sy)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void TransformedStd(wxDC* dc) +{ + // Apply all standardd transformations + dc->SetDeviceOrigin(10, 15); + dc->SetUserScale(0.5, 2.0); + dc->SetLogicalScale(4.0, 1.5); + dc->SetLogicalOrigin(-15, -20); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK(posLog.x == -8); + CHECK(posLog.y == -6); + + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK(dimLog.x == 20); + CHECK(dimLog.y == 5); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void TransformedStdEx(wxDC * dc) +{ + // Apply all standardd transformations + dc->SetDeviceOrigin(10, 15); + dc->SetUserScale(0.5, 2.0); + dc->SetLogicalScale(4.0, 1.5); + dc->SetLogicalOrigin(-15, -20); + + // First convert from device to logical coordinates + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == -8); + CHECK(posLog.y == -6); + + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == 20); + CHECK(dimLog.y == 5); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); +} + +static void TransformedWithMatrix(wxDC* dc) +{ + // Apply transformation matrix only +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + // Apply translation and scaling only + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Translate(10, 15); + m.Scale(2.0, 3.0); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + // Results should be nagative because legacy functions + // don't take affine transformation into account. + m.Invert(); + wxPoint2DDouble posLogRef = m.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK_FALSE(posLog.x == wxRound(posLogRef.m_x)); + CHECK_FALSE(posLog.y == wxRound(posLogRef.m_y)); + CHECK(posLog.x == s_posDev.x); + CHECK(posLog.y == s_posDev.y); + + wxPoint2DDouble dimLogRef = m.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK_FALSE(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK_FALSE(dimLog.y == wxRound(dimLogRef.m_y)); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void TransformedWithMatrixEx(wxDC * dc) +{ + // Apply transformation matrix only +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + // Apply translation and scaling only + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Translate(10, 15); + m.Scale(2.0, 3.0); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + m.Invert(); + wxPoint2DDouble posLogRef = m.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == wxRound(posLogRef.m_x)); + CHECK(posLog.y == wxRound(posLogRef.m_y)); + + wxPoint2DDouble dimLogRef = m.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK(dimLog.y == wxRound(dimLogRef.m_y)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void TransformedWithMatrixAndStd(wxDC* dc) +{ + // Apply combination of standard and matrix transformations +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + dc->SetDeviceOrigin(10, 15); + + dc->SetUserScale(0.5, 1.5); + dc->SetLogicalScale(4.0, 2.0); + dc->SetLogicalOrigin(-15, -20); + + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Translate(10, 18); + m.Scale(2.0, 0.5); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + // Results should be nagative because legacy functions + // don't take affine transformation into account. + wxAffineMatrix2D m1; + m1.Translate(10 - (-15) * (0.5 * 4.0), 15 - (-20) * (1.5 * 2.0)); + m1.Scale(0.5 * 4.0, 1.5 * 2.0); + m1.Concat(m); + m1.Invert(); + + wxPoint2DDouble posLogRef = m1.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK_FALSE(posLog.x == wxRound(posLogRef.m_x)); + CHECK_FALSE(posLog.y == wxRound(posLogRef.m_y)); + + wxPoint2DDouble dimLogRef = m1.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK_FALSE(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK_FALSE(dimLog.y == wxRound(dimLogRef.m_y)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void TransformedWithMatrixAndStdEx(wxDC * dc) +{ + // Apply combination of standard and matrix transformations +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + dc->SetDeviceOrigin(10, 15); + + dc->SetUserScale(0.5, 1.5); + dc->SetLogicalScale(4.0, 2.0); + dc->SetLogicalOrigin(-15, -20); + + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Translate(10, 18); + m.Scale(2.0, 0.5); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + wxAffineMatrix2D m1; + m1.Translate(10 - (-15) * (0.5 * 4.0), 15 - (-20) * (1.5 * 2.0)); + m1.Scale(0.5 * 4.0, 1.5 * 2.0); + m1.Concat(m); + m1.Invert(); + + wxPoint2DDouble posLogRef = m1.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == wxRound(posLogRef.m_x)); + CHECK(posLog.y == wxRound(posLogRef.m_y)); + + wxPoint2DDouble dimLogRef = m1.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK(dimLog.y == wxRound(dimLogRef.m_y)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void RotatedWithMatrix(wxDC* dc) +{ + // Apply matrix transformations with rotation component +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Rotate(6 * M_PI / 180.0); + m.Translate(10, 15); + m.Scale(2.0, 3.0); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + // Results should be nagative because legacy functions + // don't take affine transformation into account. + m.Invert(); + wxPoint2DDouble posLogRef = m.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog.x = dc->DeviceToLogicalX(s_posDev.x); + posLog.y = dc->DeviceToLogicalY(s_posDev.y); + CHECK_FALSE(posLog.x == wxRound(posLogRef.m_x)); + CHECK_FALSE(posLog.y == wxRound(posLogRef.m_y)); + CHECK(posLog.x == s_posDev.x); + CHECK(posLog.y == s_posDev.y); + + wxPoint2DDouble dimLogRef = m.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog.x = dc->DeviceToLogicalXRel(s_dimDev.x); + dimLog.y = dc->DeviceToLogicalYRel(s_dimDev.y); + CHECK_FALSE(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK_FALSE(dimLog.y == wxRound(dimLogRef.m_y)); + CHECK(dimLog.x == s_dimDev.x); + CHECK(dimLog.y == s_dimDev.y); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev.x = dc->LogicalToDeviceX(posLog.x); + posDev.y = dc->LogicalToDeviceY(posLog.y); + CHECK(posDev.x == s_posDev.x); + CHECK(posDev.y == s_posDev.y); + + wxSize dimDev; + dimDev.x = dc->LogicalToDeviceXRel(dimLog.x); + dimDev.y = dc->LogicalToDeviceYRel(dimLog.y); + CHECK(dimDev.x == s_dimDev.x); + CHECK(dimDev.y == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +static void RotatedWithMatrixEx(wxDC * dc) +{ + // Apply matrix transformations with rotation component +#if wxUSE_DC_TRANSFORM_MATRIX + if ( dc->CanUseTransformMatrix() ) + { + wxAffineMatrix2D m = dc->GetTransformMatrix(); + m.Rotate(6 * M_PI / 180.0); + m.Translate(10, 15); + m.Scale(2.0, 3.0); + dc->SetTransformMatrix(m); + + // First convert from device to logical coordinates + m.Invert(); + wxPoint2DDouble posLogRef = m.TransformPoint(wxPoint2DDouble(s_posDev)); + wxPoint posLog; + posLog = dc->DeviceToLogical(s_posDev); + CHECK(posLog.x == wxRound(posLogRef.m_x)); + CHECK(posLog.y == wxRound(posLogRef.m_y)); + + wxPoint2DDouble dimLogRef = m.TransformDistance(wxPoint2DDouble(s_dimDev.x, s_dimDev.y)); + wxSize dimLog; + dimLog = dc->DeviceToLogicalRel(s_dimDev); + CHECK(dimLog.x == wxRound(dimLogRef.m_x)); + CHECK(dimLog.y == wxRound(dimLogRef.m_y)); + + // And next back from logical to device coordinates + wxPoint posDev; + posDev = dc->LogicalToDevice(posLog); + CHECK(Approx(posDev.x).margin(1) == s_posDev.x); + CHECK(Approx(posDev.y).margin(1) == s_posDev.y); + + wxSize dimDev; + dimDev = dc->LogicalToDeviceRel(dimLog); + CHECK(Approx(dimDev.x).margin(1) == s_dimDev.x); + CHECK(Approx(dimDev.y).margin(1) == s_dimDev.y); + } +#endif // wxUSE_DC_TRANSFORM_MATRIX +} + +// For GTK+ 3 and OSX wxDC is equivalent to wxGCDC +// so it doesn't need to be tested individually. +#if !defined(__WXGTK3__) && !defined(__WXOSX__) +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::InitialState", "[coordinates]") +{ + // Check initial state + InitialState(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::NoTransform", "[coordinates]") +{ + // No transformations + NoTransform(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::NoTransformEx", "[coordinates]") +{ + // No transformations + NoTransformEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::DeviceOriginChanged", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::DeviceOriginChangedEx", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::LogicalOriginChanged", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::LogicalOriginChangedEx", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::UserScaleChanged", "[coordinates]") +{ + // Only user scale is changed + UserScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::UserScaleChangedEx", "[coordinates]") +{ + // Only user scale is changed + UserScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::LogicalScaleChanged", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::LogicalScaleChangedEx", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedStd", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedStdEx", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedWithMatrix", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedWithMatrixEx", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrixEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedWithMatrixAndStd", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::TransformedWithMatrixAndStdEx", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::RotatedWithMatrix", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesDCTestCase, "CoordinatesDC::RotatedWithMatrixEx", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrixEx(m_dc); +} +#endif // !__WXGTK3__ && !_WXOSX__ + +#if wxUSE_GRAPHICS_CONTEXT +// For MSW we have individual test cases for each graphics renderer +// so we don't need to test wxGCDC with default renderer. +#ifndef __WXMSW__ +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::InitialState", "[coordinates]") +{ + // Check initial state + InitialState(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::NoTransform", "[coordinates]") +{ + // No transformations + NoTransform(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::NoTransformEx", "[coordinates]") +{ + // No transformations + NoTransformEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::DeviceOriginChanged", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::DeviceOriginChangedEx", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::LogicalOriginChanged", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::LogicalOriginChangedEx", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::UserScaleChanged", "[coordinates]") +{ + // Only user scale is changed + UserScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::UserScaleChangedEx", "[coordinates]") +{ + // Only user scale is changed + UserScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::LogicalScaleChanged", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::LogicalScaleChangedEx", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedStd", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedStdEx", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedWithMatrix", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedWithMatrixEx", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrixEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedWithMatrixAndStd", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::TransformedWithMatrixAndStdEx", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::RotatedWithMatrix", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCTestCase, "CoordinatesGCDC::RotatedWithMatrixEx", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrixEx(m_dc); +} +#else// GDI+ and Direct2D are available only under MSW. +#if wxUSE_GRAPHICS_GDIPLUS +class CoordinatesGCDCGDIPlusTestCase : public CoordinatesGCDCTestCase +{ +public: + CoordinatesGCDCGDIPlusTestCase() + { + wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer(); + wxGraphicsContext* ctx = rend->CreateContext(m_mdc); + REQUIRE(ctx != NULL); + m_gcdc->SetGraphicsContext(ctx); + } + + virtual ~CoordinatesGCDCGDIPlusTestCase() {} +}; + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::InitialState", "[coordinates]") +{ + // Check initial state + InitialState(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::NoTransform", "[coordinates]") +{ + // No transformations + NoTransform(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::NoTransformEx", "[coordinates]") +{ + // No transformations + NoTransformEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::DeviceOriginChanged", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::DeviceOriginChangedEx", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::LogicalOriginChanged", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::LogicalOriginChangedEx", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::UserScaleChanged", "[coordinates]") +{ + // Only user scale is changed + UserScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::UserScaleChangedEx", "[coordinates]") +{ + // Only user scale is changed + UserScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::LogicalScaleChanged", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::LogicalScaleChangedex", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedStd", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedStdEx", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedWithMatrix", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedWithMatrixEx", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrixEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedWithMatrixAndStd", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::TransformedWithMatrixAndStdEx", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::RotatedWithMatrix", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCGDIPlusTestCase, "CoordinatesGCDCGDIPlus::RotatedWithMatrixEx", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrixEx(m_dc); +} +#endif // wxUSE_GRAPHICS_GDIPLUS + +#if wxUSE_GRAPHICS_DIRECT2D +class CoordinatesGCDCDirect2DTestCase : public CoordinatesGCDCTestCase +{ +public: + CoordinatesGCDCDirect2DTestCase() + { + wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer(); + wxGraphicsContext* ctx = rend->CreateContext(m_mdc); + REQUIRE(ctx != NULL); + m_gcdc->SetGraphicsContext(ctx); + } + + virtual ~CoordinatesGCDCDirect2DTestCase() {} +}; + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::InitialState", "[coordinates]") +{ + // Check initial state + InitialState(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::NoTransform", "[coordinates]") +{ + // No transformations + NoTransform(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::NoTransformEx", "[coordinates]") +{ + // No transformations + NoTransformEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::DeviceOriginChanged", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::DeviceOriginChangedEx", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::LogicalOriginChanged", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::LogicalOriginChangedEx", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::UserScaleChanged", "[coordinates]") +{ + // Only user scale is changed + UserScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::UserScaleChangedEx", "[coordinates]") +{ + // Only user scale is changed + UserScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::LogicalScaleChanged", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::LogicalScaleChangedEx", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedStd", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedStdEx", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedWithMatrix", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedWithMatrixEx", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrixEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedWithMatrixAndStd", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::TransformedWithMatrixAndStdEx", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::RotatedWithMatrix", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCDirect2DTestCase, "CoordinatesGCDCDirect2D::RotatedWithMatrixEx", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrixEx(m_dc); +} +#endif // wxUSE_GRAPHICS_DIRECT2D +#endif // __WXMSW__/!__WXMSW__ + +#if wxUSE_CAIRO +class CoordinatesGCDCCairoTestCase : public CoordinatesGCDCTestCase +{ +public: + CoordinatesGCDCCairoTestCase() + { + wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer(); + wxGraphicsContext* ctx = rend->CreateContext(m_mdc); + REQUIRE(ctx != NULL); + m_gcdc->SetGraphicsContext(ctx); + } + + virtual ~CoordinatesGCDCCairoTestCase() {} +}; + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::InitialState", "[coordinates]") +{ + // Check initial state + InitialState(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::NoTransform", "[coordinates]") +{ + // No transformations + NoTransform(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::NoTransformEx", "[coordinates]") +{ + // No transformations + NoTransformEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::DeviceOriginChanged", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::DeviceOriginChangedEx", "[coordinates]") +{ + // Only device origin is changed + DeviceOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::LogicalOriginChanged", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::LogicalOriginChangedEx", "[coordinates]") +{ + // Only logical origin is changed + LogicalOriginChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::UserScaleChanged", "[coordinates]") +{ + // Only user scale is changed + UserScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::UserScaleChangedEx", "[coordinates]") +{ + // Only user scale is changed + UserScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::LogicalScaleChanged", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChanged(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::LogicalScaleChangedEx", "[coordinates]") +{ + // Only logical scale is changed + LogicalScaleChangedEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedStd", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedStdEx", "[coordinates]") +{ + // Apply all standardd transformations + TransformedStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedWithMatrix", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedWithMatrixEx", "[coordinates]") +{ + // Apply transformation matrix only + TransformedWithMatrixEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedWithMatrixAndStd", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStd(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::TransformedWithMatrixAndStdEx", "[coordinates]") +{ + // Apply combination of standard and matrix transformations + TransformedWithMatrixAndStdEx(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::RotatedWithMatrix", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrix(m_dc); +} + +TEST_CASE_METHOD(CoordinatesGCDCCairoTestCase, "CoordinatesGCDCCairo::RotatedWithMatrixEx", "[coordinates]") +{ + // Apply matrix transformations with rotation component + RotatedWithMatrixEx(m_dc); +} +#endif // wxUSE_CAIRO +#endif // wxUSE_GRAPHICS_CONTEXT diff --git a/tests/image/image.cpp b/tests/image/image.cpp index f76e79162c..c825a445a4 100644 --- a/tests/image/image.cpp +++ b/tests/image/image.cpp @@ -1454,6 +1454,438 @@ void ImageTestCase::ScaleCompare() #endif //wxUSE_IMAGE +TEST_CASE("wxImage::Paste", "[image][paste]") +{ + const static char* squares_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FFFF00", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "wwwwwwwww", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo" + }; + + const static char* toggle_equal_size_xpm[] = + { + "9 9 2 1", + " c None", + "y c #FFFF00", + "y y y y y", + " y y y y ", + "y y y y y", + " y y y y ", + "y y y y y", + " y y y y ", + "y y y y y", + " y y y y ", + "y y y y y", + }; + + const static char* transparent_image_xpm[] = + { + "5 5 2 1", + " c None", // Mask + "y c #FFFF00", + " ", + " ", + " ", + " ", + " ", + }; + + const static char* light_image_xpm[] = + { + "5 5 2 1", + " c None", + "y c #FFFF00", + "yyyyy", + "yyyyy", + "yyyyy", + "yyyyy", + "yyyyy", + }; + + const static char* black_image_xpm[] = + { + "5 5 2 1", + " c #000000", + "y c None", // Mask + " ", + " ", + " ", + " ", + " ", + }; + + // Execute AddHandler() just once. + static const bool + registeredHandler = (wxImage::AddHandler(new wxPNGHandler()), true); + + SECTION("Paste same size image") + { + wxImage actual(squares_xpm); + wxImage paste(toggle_equal_size_xpm); + wxImage expected(toggle_equal_size_xpm); + actual.Paste(paste, 0, 0); + CHECK_THAT(actual, RGBSameAs(expected)); + + // Without alpha using "compose" doesn't change anything. + actual.Paste(paste, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste larger image") + { + const static char* toggle_larger_size_xpm[] = + { + "13 13 2 1", + " c None", + "y c #FFFF00", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + " y y y y y y ", + "y y y y y y y", + }; + + wxImage actual(squares_xpm); + wxImage paste(toggle_larger_size_xpm); + wxImage expected(toggle_equal_size_xpm); + actual.Paste(paste, -2, -2); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste smaller image") + { + const static char* toggle_smaller_size_xpm[] = + { + "5 5 2 1", + " c None", + "y c #FFFF00", + "y y y", + " y y ", + "y y y", + " y y ", + "y y y", + }; + + const static char* expected_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FFFF00", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "rrrrwgggg", + "rrrrwgggg", + "rry y ygg", + "rr y y gg", + "wwy y yww", + "bb y y oo", + "bby y yoo", + "bbbbwoooo", + "bbbbwoooo" + }; + + wxImage actual(squares_xpm); + wxImage paste(toggle_smaller_size_xpm); + wxImage expected(expected_xpm); + actual.Paste(paste, 2, 2); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste beyond top left corner") + { + const static char* expected_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FFFF00", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "oooowgggg", + "oooowgggg", + "oooowgggg", + "oooowgggg", + "wwwwwwwww", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo" + }; + + wxImage actual(squares_xpm); + wxImage paste(squares_xpm); + wxImage expected(expected_xpm); + actual.Paste(paste, -5, -5); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste beyond top right corner") + { + const static char* expected_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FFFF00", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "rrrrwbbbb", + "rrrrwbbbb", + "rrrrwbbbb", + "rrrrwbbbb", + "wwwwwwwww", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo", + "bbbbwoooo" + }; + wxImage actual(squares_xpm); + wxImage paste(squares_xpm); + wxImage expected(expected_xpm); + actual.Paste(paste, 5, -5); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste beyond bottom right corner") + { + const static char* expected_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FF0000", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "wwwwwwwww", + "bbbbwrrrr", + "bbbbwrrrr", + "bbbbwrrrr", + "bbbbwrrrr" + }; + wxImage actual(squares_xpm); + wxImage paste(squares_xpm); + wxImage expected(expected_xpm); + actual.Paste(paste, 5, 5); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste beyond bottom left corner") + { + const static char* expected_xpm[] = + { + "9 9 7 1", + " c None", + "y c #FFFF00", + "r c #FF0000", + "g c #00FF00", + "b c #0000FF", + "o c #FF6600", + "w c #FFFFFF", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "rrrrwgggg", + "wwwwwwwww", + "ggggwoooo", + "ggggwoooo", + "ggggwoooo", + "ggggwoooo" + }; + wxImage actual(squares_xpm); + wxImage paste(squares_xpm); + wxImage expected(expected_xpm); + actual.Paste(paste, -5, 5); + CHECK_THAT(actual, RGBSameAs(expected)); + } + + SECTION("Paste fully opaque image onto blank image without alpha") + { + const wxImage background("image/paste_input_background.png"); + REQUIRE(background.IsOk()); + + wxImage actual(background.GetSize()); + actual.Paste(background, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(background)); + CHECK(!actual.HasAlpha()); + } + SECTION("Paste fully opaque image onto blank image with alpha") + { + const wxImage background("image/paste_input_background.png"); + REQUIRE(background.IsOk()); + + wxImage actual(background.GetSize()); + actual.InitAlpha(); + actual.Paste(background, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(background)); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste fully transparent image") + { + const wxImage background("image/paste_input_background.png"); + REQUIRE(background.IsOk()); + + wxImage actual = background.Copy(); + wxImage transparent(actual.GetSize()); + transparent.InitAlpha(); + memset(transparent.GetAlpha(), 0, transparent.GetWidth() * transparent.GetHeight()); + actual.Paste(transparent, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(background)); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste image with transparent region") + { + wxImage actual("image/paste_input_background.png"); + REQUIRE(actual.IsOk()); + + const wxImage opaque_square("image/paste_input_overlay_transparent_border_opaque_square.png"); + REQUIRE(opaque_square.IsOk()); + + actual.Paste(opaque_square, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(wxImage("image/paste_result_background_plus_overlay_transparent_border_opaque_square.png"))); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste image with semi transparent region") + { + wxImage actual("image/paste_input_background.png"); + REQUIRE(actual.IsOk()); + + const wxImage transparent_square("image/paste_input_overlay_transparent_border_semitransparent_square.png"); + REQUIRE(transparent_square.IsOk()); + + actual.Paste(transparent_square, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(wxImage("image/paste_result_background_plus_overlay_transparent_border_semitransparent_square.png"))); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste two semi transparent images on top of background") + { + wxImage actual("image/paste_input_background.png"); + REQUIRE(actual.IsOk()); + + const wxImage transparent_square("image/paste_input_overlay_transparent_border_semitransparent_square.png"); + REQUIRE(transparent_square.IsOk()); + + const wxImage transparent_circle("image/paste_input_overlay_transparent_border_semitransparent_circle.png"); + REQUIRE(transparent_circle.IsOk()); + + actual.Paste(transparent_circle, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + actual.Paste(transparent_square, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSimilarTo(wxImage("image/paste_result_background_plus_circle_plus_square.png"), 1)); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste two semi transparent images together first, then on top of background") + { + wxImage actual("image/paste_input_background.png"); + REQUIRE(actual.IsOk()); + + const wxImage transparent_square("image/paste_input_overlay_transparent_border_semitransparent_square.png"); + REQUIRE(transparent_square.IsOk()); + + const wxImage transparent_circle("image/paste_input_overlay_transparent_border_semitransparent_circle.png"); + REQUIRE(transparent_circle.IsOk()); + + wxImage circle = transparent_circle.Copy(); + circle.Paste(transparent_square, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + actual.Paste(circle, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + // When applied in this order, two times a rounding difference is triggered. + CHECK_THAT(actual, RGBSimilarTo(wxImage("image/paste_result_background_plus_circle_plus_square.png"), 2)); + CHECK_THAT(actual, CenterAlphaPixelEquals(wxALPHA_OPAQUE)); + } + SECTION("Paste semitransparent image over transparent image") + { + const wxImage transparent_square("image/paste_input_overlay_transparent_border_semitransparent_square.png"); + REQUIRE(transparent_square.IsOk()); + + const wxImage transparent_circle("image/paste_input_overlay_transparent_border_semitransparent_circle.png"); + REQUIRE(transparent_circle.IsOk()); + + wxImage actual(transparent_circle.GetSize()); + actual.InitAlpha(); + memset(actual.GetAlpha(), 0, actual.GetWidth() * actual.GetHeight()); + actual.Paste(transparent_circle, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, CenterAlphaPixelEquals(192)); + actual.Paste(transparent_square, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSimilarTo(wxImage("image/paste_result_no_background_square_over_circle.png"), 1)); + CHECK_THAT(actual, CenterAlphaPixelEquals(224)); + } + SECTION("Paste fully transparent (masked) image over light image") // todo make test case for 'blend with mask' + { + wxImage actual(light_image_xpm); + actual.InitAlpha(); + wxImage paste(transparent_image_xpm); + wxImage expected(light_image_xpm); + actual.Paste(paste, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(expected)); + } + SECTION("Paste fully black (masked) image over light image") // todo make test case for 'blend with mask' + { + wxImage actual(light_image_xpm); + actual.InitAlpha(); + wxImage paste(black_image_xpm); + wxImage expected(black_image_xpm); + actual.Paste(paste, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, RGBSameAs(expected)); + } + SECTION("Paste dark image over light image") + { + wxImage black("image/paste_input_black.png"); + wxImage actual("image/paste_input_background.png"); + actual.InitAlpha(); + actual.Paste(black, 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE); + CHECK_THAT(actual, CenterAlphaPixelEquals(255)); + CHECK_THAT(actual, RGBSameAs(black)); + } + SECTION("Paste large image with negative vertical offset") + { + wxImage target(442, 249); + wxImage to_be_pasted(345, 24900); + target.InitAlpha(); + target.Paste(to_be_pasted, 48, -12325, wxIMAGE_ALPHA_BLEND_COMPOSE); + } + SECTION("Paste large image with negative horizontal offset") + { + wxImage target(249, 442); + wxImage to_be_pasted(24900, 345); + target.InitAlpha(); + target.Paste(to_be_pasted, -12325, 48, wxIMAGE_ALPHA_BLEND_COMPOSE); + } + +} /* TODO: add lots of more tests to wxImage functions diff --git a/tests/image/paste_input_background.png b/tests/image/paste_input_background.png new file mode 100644 index 0000000000..fd93b550ce Binary files /dev/null and b/tests/image/paste_input_background.png differ diff --git a/tests/image/paste_input_black.png b/tests/image/paste_input_black.png new file mode 100644 index 0000000000..43f4668bba Binary files /dev/null and b/tests/image/paste_input_black.png differ diff --git a/tests/image/paste_input_overlay_transparent_border_opaque_square.png b/tests/image/paste_input_overlay_transparent_border_opaque_square.png new file mode 100644 index 0000000000..fa32073e5c Binary files /dev/null and b/tests/image/paste_input_overlay_transparent_border_opaque_square.png differ diff --git a/tests/image/paste_input_overlay_transparent_border_semitransparent_circle.png b/tests/image/paste_input_overlay_transparent_border_semitransparent_circle.png new file mode 100644 index 0000000000..af6d42ffbd Binary files /dev/null and b/tests/image/paste_input_overlay_transparent_border_semitransparent_circle.png differ diff --git a/tests/image/paste_input_overlay_transparent_border_semitransparent_square.png b/tests/image/paste_input_overlay_transparent_border_semitransparent_square.png new file mode 100644 index 0000000000..d609cd5a45 Binary files /dev/null and b/tests/image/paste_input_overlay_transparent_border_semitransparent_square.png differ diff --git a/tests/image/paste_result_background_plus_circle_plus_square.png b/tests/image/paste_result_background_plus_circle_plus_square.png new file mode 100644 index 0000000000..20af553c7d Binary files /dev/null and b/tests/image/paste_result_background_plus_circle_plus_square.png differ diff --git a/tests/image/paste_result_background_plus_overlay_transparent_border_opaque_square.png b/tests/image/paste_result_background_plus_overlay_transparent_border_opaque_square.png new file mode 100644 index 0000000000..60da07730d Binary files /dev/null and b/tests/image/paste_result_background_plus_overlay_transparent_border_opaque_square.png differ diff --git a/tests/image/paste_result_background_plus_overlay_transparent_border_semitransparent_square.png b/tests/image/paste_result_background_plus_overlay_transparent_border_semitransparent_square.png new file mode 100644 index 0000000000..a6e6fef28e Binary files /dev/null and b/tests/image/paste_result_background_plus_overlay_transparent_border_semitransparent_square.png differ diff --git a/tests/image/paste_result_no_background_square_over_circle.png b/tests/image/paste_result_no_background_square_over_circle.png new file mode 100644 index 0000000000..d27564a130 Binary files /dev/null and b/tests/image/paste_result_no_background_square_over_circle.png differ diff --git a/tests/makefile.bcc b/tests/makefile.bcc index 6be3ae8a33..b505662946 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -168,6 +168,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_affinematrix.obj \ $(OBJS)\test_gui_boundingbox.obj \ $(OBJS)\test_gui_clippingbox.obj \ + $(OBJS)\test_gui_coords.obj \ $(OBJS)\test_gui_graphmatrix.obj \ $(OBJS)\test_gui_graphpath.obj \ $(OBJS)\test_gui_config.obj \ @@ -570,7 +571,7 @@ data: data-images: if not exist image mkdir image - for %f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png) do if not exist image\%f copy .\image\%f image + for %f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png) do if not exist image\%f copy .\image\%f image fr: if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr @@ -907,6 +908,9 @@ $(OBJS)\test_gui_boundingbox.obj: .\graphics\boundingbox.cpp $(OBJS)\test_gui_clippingbox.obj: .\graphics\clippingbox.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\graphics\clippingbox.cpp +$(OBJS)\test_gui_coords.obj: .\graphics\coords.cpp + $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\graphics\coords.cpp + $(OBJS)\test_gui_graphmatrix.obj: .\graphics\graphmatrix.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\graphics\graphmatrix.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index 88cb64af69..c074804243 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -163,6 +163,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_affinematrix.o \ $(OBJS)\test_gui_boundingbox.o \ $(OBJS)\test_gui_clippingbox.o \ + $(OBJS)\test_gui_coords.o \ $(OBJS)\test_gui_graphmatrix.o \ $(OBJS)\test_gui_graphpath.o \ $(OBJS)\test_gui_config.o \ @@ -555,7 +556,7 @@ data: data-images: if not exist image mkdir image - for %%f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png) do if not exist image\%%f copy .\image\%%f image + for %%f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png) do if not exist image\%%f copy .\image\%%f image fr: if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr @@ -892,6 +893,9 @@ $(OBJS)\test_gui_boundingbox.o: ./graphics/boundingbox.cpp $(OBJS)\test_gui_clippingbox.o: ./graphics/clippingbox.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_gui_coords.o: ./graphics/coords.cpp + $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_gui_graphmatrix.o: ./graphics/graphmatrix.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index 990d47e105..c5652c9a21 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -177,6 +177,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_affinematrix.obj \ $(OBJS)\test_gui_boundingbox.obj \ $(OBJS)\test_gui_clippingbox.obj \ + $(OBJS)\test_gui_coords.obj \ $(OBJS)\test_gui_graphmatrix.obj \ $(OBJS)\test_gui_graphpath.obj \ $(OBJS)\test_gui_config.obj \ @@ -983,7 +984,7 @@ data: data-images: if not exist image mkdir image - for %f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png) do if not exist image\%f copy .\image\%f image + for %f in (horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png) do if not exist image\%f copy .\image\%f image fr: if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr @@ -1320,6 +1321,9 @@ $(OBJS)\test_gui_boundingbox.obj: .\graphics\boundingbox.cpp $(OBJS)\test_gui_clippingbox.obj: .\graphics\clippingbox.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\graphics\clippingbox.cpp +$(OBJS)\test_gui_coords.obj: .\graphics\coords.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\graphics\coords.cpp + $(OBJS)\test_gui_graphmatrix.obj: .\graphics\graphmatrix.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\graphics\graphmatrix.cpp diff --git a/tests/mbconv/convautotest.cpp b/tests/mbconv/convautotest.cpp index 6a5d5791d1..12e19c21ed 100644 --- a/tests/mbconv/convautotest.cpp +++ b/tests/mbconv/convautotest.cpp @@ -34,7 +34,9 @@ public: private: CPPUNIT_TEST_SUITE( ConvAutoTestCase ); + CPPUNIT_TEST( Init ); CPPUNIT_TEST( Empty ); + CPPUNIT_TEST( Encode ); CPPUNIT_TEST( Short ); CPPUNIT_TEST( None ); CPPUNIT_TEST( UTF32LE ); @@ -42,22 +44,53 @@ private: CPPUNIT_TEST( UTF16LE ); CPPUNIT_TEST( UTF16BE ); CPPUNIT_TEST( UTF8 ); + CPPUNIT_TEST( UTF8NoBom ); + CPPUNIT_TEST( Fallback ); + CPPUNIT_TEST( FallbackMultibyte ); + CPPUNIT_TEST( FallbackShort ); CPPUNIT_TEST( StreamUTF8NoBOM ); CPPUNIT_TEST( StreamUTF8 ); CPPUNIT_TEST( StreamUTF16LE ); CPPUNIT_TEST( StreamUTF16BE ); CPPUNIT_TEST( StreamUTF32LE ); CPPUNIT_TEST( StreamUTF32BE ); + CPPUNIT_TEST( StreamFallback ); + CPPUNIT_TEST( StreamFallbackMultibyte ); CPPUNIT_TEST_SUITE_END(); + // expected converter state, UTF-8 without BOM by default + struct ConvState + { + ConvState( wxBOM bom = wxBOM_None, + wxFontEncoding enc = wxFONTENCODING_UTF8, + bool fallback = false ) + : m_bom(bom), m_enc(enc), m_fallback(fallback) {} + + void Check(const wxConvAuto& conv) const + { + CPPUNIT_ASSERT( conv.GetBOM() == m_bom ); + CPPUNIT_ASSERT( conv.GetEncoding() == m_enc ); + CPPUNIT_ASSERT( conv.IsUsingFallbackEncoding() == m_fallback ); + CPPUNIT_ASSERT( conv.IsUTF8() == (m_enc == wxFONTENCODING_UTF8) ); + } + + wxBOM m_bom; + wxFontEncoding m_enc; + bool m_fallback; + }; + // real test function: check that converting the src multibyte string to // wide char using wxConvAuto yields wch as the first result // // the length of the string may need to be passed explicitly if it has // embedded NULs, otherwise it's not necessary - void TestFirstChar(const char *src, wchar_t wch, size_t len = wxNO_LEN); + void TestFirstChar(const char *src, wchar_t wch, size_t len = wxNO_LEN, + ConvState st = ConvState(), + wxFontEncoding fe = wxFONTENCODING_DEFAULT); + void Init(); void Empty(); + void Encode(); void Short(); void None(); void UTF32LE(); @@ -65,12 +98,17 @@ private: void UTF16LE(); void UTF16BE(); void UTF8(); + void UTF8NoBom(); + void Fallback(); + void FallbackMultibyte(); + void FallbackShort(); // test whether two lines of text are converted properly from a stream void TestTextStream(const char *src, size_t srclength, const wxString& line1, - const wxString& line2); + const wxString& line2, + wxFontEncoding fe = wxFONTENCODING_DEFAULT); void StreamUTF8NoBOM(); void StreamUTF8(); @@ -78,6 +116,8 @@ private: void StreamUTF16BE(); void StreamUTF32LE(); void StreamUTF32BE(); + void StreamFallback(); + void StreamFallbackMultibyte(); }; // register in the unnamed registry so that these tests are run by default @@ -90,16 +130,36 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConvAutoTestCase, "ConvAutoTestCase"); // tests // ---------------------------------------------------------------------------- -void ConvAutoTestCase::TestFirstChar(const char *src, wchar_t wch, size_t len) +void ConvAutoTestCase::TestFirstChar(const char *src, wchar_t wch, size_t len, + ConvState st, wxFontEncoding fe) { - wxWCharBuffer wbuf = wxConvAuto().cMB2WC(src, len, NULL); + wxConvAuto conv(fe); + wxWCharBuffer wbuf = conv.cMB2WC(src, len, NULL); CPPUNIT_ASSERT( wbuf ); CPPUNIT_ASSERT_EQUAL( wch, *wbuf ); + st.Check(conv); +} + +void ConvAutoTestCase::Init() +{ + ConvState(wxBOM_Unknown, wxFONTENCODING_MAX).Check(wxConvAuto()); } void ConvAutoTestCase::Empty() { - CPPUNIT_ASSERT( !wxConvAuto().cMB2WC("") ); + wxConvAuto conv; + CPPUNIT_ASSERT( !conv.cMB2WC("") ); + ConvState(wxBOM_Unknown, wxFONTENCODING_MAX).Check(conv); +} + +void ConvAutoTestCase::Encode() +{ + wxConvAuto conv; + wxString str = wxString::FromUTF8("\xd0\x9f\xe3\x81\x82"); + wxCharBuffer buf = conv.cWC2MB(str.wc_str()); + CPPUNIT_ASSERT( buf ); + CPPUNIT_ASSERT_EQUAL( str, wxString::FromUTF8(buf) ); + ConvState(wxBOM_Unknown, wxFONTENCODING_UTF8).Check(conv); } void ConvAutoTestCase::Short() @@ -114,38 +174,71 @@ void ConvAutoTestCase::None() void ConvAutoTestCase::UTF32LE() { - TestFirstChar("\xff\xfe\0\0A\0\0\0", wxT('A'), 8); + TestFirstChar("\xff\xfe\0\0A\0\0\0", wxT('A'), 8, ConvState(wxBOM_UTF32LE, wxFONTENCODING_UTF32LE)); } void ConvAutoTestCase::UTF32BE() { - TestFirstChar("\0\0\xfe\xff\0\0\0B", wxT('B'), 8); + TestFirstChar("\0\0\xfe\xff\0\0\0B", wxT('B'), 8, ConvState(wxBOM_UTF32BE, wxFONTENCODING_UTF32BE)); } void ConvAutoTestCase::UTF16LE() { - TestFirstChar("\xff\xfeZ\0", wxT('Z'), 4); + TestFirstChar("\xff\xfeZ\0", wxT('Z'), 4, ConvState(wxBOM_UTF16LE, wxFONTENCODING_UTF16LE)); } void ConvAutoTestCase::UTF16BE() { - TestFirstChar("\xfe\xff\0Y", wxT('Y'), 4); + TestFirstChar("\xfe\xff\0Y", wxT('Y'), 4, ConvState(wxBOM_UTF16BE, wxFONTENCODING_UTF16BE)); } void ConvAutoTestCase::UTF8() { #ifdef wxHAVE_U_ESCAPE - TestFirstChar("\xef\xbb\xbf\xd0\x9f", L'\u041f'); + TestFirstChar("\xef\xbb\xbf\xd0\x9f", L'\u041f', wxNO_LEN, ConvState(wxBOM_UTF8, wxFONTENCODING_UTF8)); #endif } +void ConvAutoTestCase::UTF8NoBom() +{ +#ifdef wxHAVE_U_ESCAPE + TestFirstChar("\xd0\x9f\xe3\x81\x82", L'\u041f', wxNO_LEN, ConvState(wxBOM_None, wxFONTENCODING_UTF8)); +#endif +} + +void ConvAutoTestCase::Fallback() +{ +#ifdef wxHAVE_U_ESCAPE + TestFirstChar("\xbf", L'\u041f', wxNO_LEN, + ConvState(wxBOM_None, wxFONTENCODING_ISO8859_5, true), + wxFONTENCODING_ISO8859_5); +#endif +} + +void ConvAutoTestCase::FallbackMultibyte() +{ +#ifdef wxHAVE_U_ESCAPE + TestFirstChar("\x84\x50", L'\u041f', wxNO_LEN, + ConvState(wxBOM_None, wxFONTENCODING_CP932, true), + wxFONTENCODING_CP932); +#endif +} + +void ConvAutoTestCase::FallbackShort() +{ + TestFirstChar("\x61\xc4", 'a', 2, + ConvState(wxBOM_None, wxFONTENCODING_ISO8859_5, true), + wxFONTENCODING_ISO8859_5); +} + void ConvAutoTestCase::TestTextStream(const char *src, size_t srclength, const wxString& line1, - const wxString& line2) + const wxString& line2, + wxFontEncoding fe) { wxMemoryInputStream instream(src, srclength); - wxTextInputStream text(instream); + wxTextInputStream text(instream, wxT(" \t"), wxConvAuto(fe)); CPPUNIT_ASSERT_EQUAL( line1, text.ReadLine() ); CPPUNIT_ASSERT_EQUAL( line2, text.ReadLine() ); @@ -166,16 +259,8 @@ const wxString line2 = wxString::FromUTF8("\xce\xb2"); void ConvAutoTestCase::StreamUTF8NoBOM() { - // currently this test doesn't work because without the BOM wxConvAuto - // decides that the string is in Latin-1 after finding the first (but not - // the two subsequent ones which are part of the same UTF-8 sequence!) - // 8-bit character - // - // FIXME: we need to fix this at wxTextInputStream level, see #11570 -#if 0 TestTextStream("\x61\xE3\x81\x82\x0A\xCE\xB2", 7, line1, line2); -#endif } void ConvAutoTestCase::StreamUTF8() @@ -210,4 +295,17 @@ void ConvAutoTestCase::StreamUTF32BE() 20, line1, line2); } +void ConvAutoTestCase::StreamFallback() +{ + TestTextStream("\x61\xbf\x0A\xe0", + 4, wxString::FromUTF8("a\xd0\x9f"), wxString::FromUTF8("\xd1\x80"), + wxFONTENCODING_ISO8859_5); +} + +void ConvAutoTestCase::StreamFallbackMultibyte() +{ + TestTextStream("\x61\x82\xa0\x0A\x83\xc0", + 6, line1, line2, wxFONTENCODING_CP932); +} + #endif // wxUSE_UNICODE diff --git a/tests/streams/textstreamtest.cpp b/tests/streams/textstreamtest.cpp index edb6eaa8a2..c6497b23f2 100644 --- a/tests/streams/textstreamtest.cpp +++ b/tests/streams/textstreamtest.cpp @@ -324,6 +324,46 @@ TEST_CASE("wxTextInputStream::GetChar", "[text][input][stream][char]") REQUIRE( tis.GetChar() == 0x00 ); CHECK( tis.GetInputStream().Eof() ); } + + // Two null bytes that look like the start of UTF-32BE BOM, + // followed by 4 byte UTF-8 sequence. + // Needs wxConvAuto to not switch to fallback on <6 bytes. + SECTION("UTF8-with-nulls") + { + const wxUint8 buf[] = { 0x00, 0x00, 0xf0, 0x90, 0x8c, 0x98 }; + wxMemoryInputStream mis(buf, sizeof(buf)); + wxTextInputStream tis(mis); + + wxCharTypeBuffer e = wxString::FromUTF8((char*)buf, sizeof(buf)) + .tchar_str(); + for ( size_t i = 0; i < e.length(); ++i ) + { + INFO("i = " << i); + REQUIRE( tis.GetChar() == e[i] ); + } + REQUIRE( tis.GetChar() == 0x00 ); + CHECK( tis.GetInputStream().Eof() ); + } + + // Two null bytes that look like the start of UTF-32BE BOM, + // then 3 bytes that look like the start of UTF-8 sequence. + // Needs 6 character output buffer in GetChar(). + SECTION("almost-UTF8-with-nulls") + { + const wxUint8 buf[] = { 0x00, 0x00, 0xf0, 0x90, 0x8c, 0xe0 }; + wxMemoryInputStream mis(buf, sizeof(buf)); + wxTextInputStream tis(mis); + + wxCharTypeBuffer e = wxString((char*)buf, wxCSConv(wxFONTENCODING_ISO8859_1), + sizeof(buf)).tchar_str(); + for ( size_t i = 0; i < e.length(); ++i ) + { + INFO("i = " << i); + REQUIRE( tis.GetChar() == e[i] ); + } + REQUIRE( tis.GetChar() == 0x00 ); + CHECK( tis.GetInputStream().Eof() ); + } } #endif // wxUSE_UNICODE diff --git a/tests/test.bkl b/tests/test.bkl index 2588593b05..0aa49e95cd 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -181,6 +181,7 @@ graphics/affinematrix.cpp graphics/boundingbox.cpp graphics/clippingbox.cpp + graphics/coords.cpp graphics/graphmatrix.cpp graphics/graphpath.cpp config/config.cpp @@ -359,6 +360,16 @@ cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png + + paste_input_background.png + paste_input_black.png + paste_input_overlay_transparent_border_opaque_square.png + paste_input_overlay_transparent_border_semitransparent_circle.png + paste_input_overlay_transparent_border_semitransparent_square.png + paste_result_background_plus_circle_plus_square.png + paste_result_background_plus_overlay_transparent_border_opaque_square.png + paste_result_background_plus_overlay_transparent_border_semitransparent_square.png + paste_result_no_background_square_over_circle.png diff --git a/tests/test_gui.vcxproj b/tests/test_gui.vcxproj index 754fdb8d44..7032b4ce33 100644 --- a/tests/test_gui.vcxproj +++ b/tests/test_gui.vcxproj @@ -547,6 +547,7 @@ + diff --git a/tests/test_gui.vcxproj.filters b/tests/test_gui.vcxproj.filters index e61c1bf4fd..0eede89fd6 100644 --- a/tests/test_gui.vcxproj.filters +++ b/tests/test_gui.vcxproj.filters @@ -293,6 +293,9 @@ Source Files + + Source Files + Source Files diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj index f18a1a38ce..ec237145b5 100644 --- a/tests/test_vc7_test_gui.vcproj +++ b/tests/test_vc7_test_gui.vcproj @@ -358,6 +358,9 @@ + + diff --git a/tests/test_vc8_test_gui.vcproj b/tests/test_vc8_test_gui.vcproj index ccc8576f4a..dd1700c187 100644 --- a/tests/test_vc8_test_gui.vcproj +++ b/tests/test_vc8_test_gui.vcproj @@ -914,6 +914,10 @@ RelativePath=".\config\config.cpp" > + + diff --git a/tests/test_vc9_test_gui.vcproj b/tests/test_vc9_test_gui.vcproj index c9c6d5a4e8..ff01099d23 100644 --- a/tests/test_vc9_test_gui.vcproj +++ b/tests/test_vc9_test_gui.vcproj @@ -886,6 +886,10 @@ RelativePath=".\config\config.cpp" > + + diff --git a/tests/testimage.h b/tests/testimage.h index e922e36d63..f4f20d8ddc 100644 --- a/tests/testimage.h +++ b/tests/testimage.h @@ -30,8 +30,9 @@ namespace Catch class ImageRGBMatcher : public Catch::MatcherBase { public: - ImageRGBMatcher(const wxImage& image) + ImageRGBMatcher(const wxImage& image, int tolerance) : m_image(image) + , m_tolerance(tolerance) { } @@ -53,7 +54,8 @@ public: { for ( int y = 0; y < m_image.GetHeight(); ++y ) { - if ( *d1 != *d2 ) + const unsigned char diff = *d1 > * d2 ? *d1 - *d2 : *d2 - *d1; + if (diff > m_tolerance) { m_diffDesc.Printf ( @@ -72,11 +74,11 @@ public: } } - // We should never get here as we know that the images are different - // and so should have returned from inside the loop above. - wxFAIL_MSG("unreachable"); + // We can only get here when the images are different AND we've not exited the + // method from the loop. That implies the tolerance must have caused this. + wxASSERT_MSG(m_tolerance > 0, "Unreachable without tolerance"); - return false; + return true; } std::string describe() const wxOVERRIDE @@ -92,12 +94,67 @@ public: private: const wxImage m_image; + const int m_tolerance; mutable wxString m_diffDesc; }; inline ImageRGBMatcher RGBSameAs(const wxImage& image) { - return ImageRGBMatcher(image); + return ImageRGBMatcher(image, 0); +} + +// Allows small differences (within given tolerance) for r, g, and b values. +inline ImageRGBMatcher RGBSimilarTo(const wxImage& image, int tolerance) +{ + return ImageRGBMatcher(image, tolerance); +} + +class ImageAlphaMatcher : public Catch::MatcherBase +{ +public: + ImageAlphaMatcher(unsigned char alpha) + : m_alpha(alpha) + { + } + + bool match(const wxImage& other) const wxOVERRIDE + { + if (!other.HasAlpha()) + { + m_diffDesc = "no alpha data"; + return false; + } + + unsigned char center_alpha = + *(other.GetAlpha() + (other.GetWidth() / 2) + (other.GetHeight() / 2 * other.GetWidth())); + + if (m_alpha != center_alpha) + { + m_diffDesc.Printf("got alpha %u", center_alpha); + return false; + } + + return true; + } + + std::string describe() const wxOVERRIDE + { + std::string desc; + + if (!m_diffDesc.empty()) + desc = m_diffDesc.ToStdString(wxConvUTF8); + + return desc; + } + +private: + const unsigned char m_alpha; + mutable wxString m_diffDesc; +}; + +inline ImageAlphaMatcher CenterAlphaPixelEquals(unsigned char alpha) +{ + return ImageAlphaMatcher(alpha); } #endif // _WX_TESTS_TESTIMAGE_H_ diff --git a/tests/testwindow.h b/tests/testwindow.h index 64b455b261..00f8003ec5 100644 --- a/tests/testwindow.h +++ b/tests/testwindow.h @@ -9,6 +9,7 @@ #ifndef _WX_TESTS_TESTWINDOW_H_ #define _WX_TESTS_TESTWINDOW_H_ +#include "wx/scopedptr.h" #include "wx/window.h" // We need to wrap wxWindow* in a class as specializing StringMaker for @@ -17,6 +18,8 @@ class wxWindowPtr { public: explicit wxWindowPtr(wxWindow* win) : m_win(win) {} + template + explicit wxWindowPtr(const wxScopedPtr& win) : m_win(win.get()) {} wxString Dump() const { @@ -44,7 +47,9 @@ private: // Macro providing more information about the current focus if comparison // fails. -#define CHECK_FOCUS_IS(w) CHECK(wxWindowPtr(wxWindow::FindFocus()) == wxWindowPtr(w)) +#define CHECK_SAME_WINDOW(w1, w2) CHECK(wxWindowPtr(w1) == wxWindowPtr(w2)) + +#define CHECK_FOCUS_IS(w) CHECK_SAME_WINDOW(wxWindow::FindFocus(), w) namespace Catch { diff --git a/wx-config.in b/wx-config.in index 16013d35b9..441f88ce92 100755 --- a/wx-config.in +++ b/wx-config.in @@ -1311,12 +1311,12 @@ if [ -n "$output_option_libs" ]; then _ldflags="-L$libdir" if [ -n "$MAC_FRAMEWORK" ]; then - wx_libs="-framework $MAC_FRAMEWORK" - if [ -n "$MAC_FRAMEWORK_PREFIX" ]; then - _ldflags="-F$MAC_FRAMEWORK_PREFIX" - else - _ldflags="" - fi + wx_libs="-framework $MAC_FRAMEWORK" + if [ -n "$MAC_FRAMEWORK_PREFIX" ]; then + _ldflags="-F$MAC_FRAMEWORK_PREFIX" + else + _ldflags="" + fi fi is_installed || [ -n "$flag_option_no_rpath" ] || _rpath="@WXCONFIG_RPATH@"