diff --git a/ci/travis/linux/install.sh b/ci/travis/linux/install.sh index 2e0ebee66b6..344607830e7 100755 --- a/ci/travis/linux/install.sh +++ b/ci/travis/linux/install.sh @@ -42,6 +42,8 @@ cmake \ -DCMAKE_PREFIX_PATH=/home/travis/osgeo4travis \ -DWITH_STAGED_PLUGINS=ON \ -DWITH_GRASS=ON \ + -DWITH_GRASS7=ON \ + -DGRASS_PREFIX7=/home/travis/osgeo4travis/grass-7.0.4 \ -DSUPPRESS_QT_WARNINGS=ON \ -DENABLE_MODELTEST=ON \ -DENABLE_PGTEST=ON \ diff --git a/ci/travis/linux/script.sh b/ci/travis/linux/script.sh index 0ab8c4c1b41..d7e680ea9c7 100755 --- a/ci/travis/linux/script.sh +++ b/ci/travis/linux/script.sh @@ -21,5 +21,5 @@ export CCACHE_TEMPDIR=/tmp DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -xvfb-run ctest -V -E "qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsWFSProviderGUI|qgis_ziplayertest|qgis_ogcutilstest|$(cat ${DIR}/blacklist.txt | paste -sd '|' -)" -S ./qgis-test-travis.ctest --output-on-failure +xvfb-run ctest -V -E "qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsWFSProviderGUI|qgis_ziplayertest|$(cat ${DIR}/blacklist.txt | paste -sd '|' -)" -S ./qgis-test-travis.ctest --output-on-failure # xvfb-run ctest -V -E "qgis_openstreetmaptest|qgis_wcsprovidertest" -S ./qgis-test-travis.ctest --output-on-failure diff --git a/debian/changelog b/debian/changelog index 1eb61d82c0f..722b5c3fdeb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,9 @@ qgis (2.99.0) UNRELEASED; urgency=medium * New development version 2.999 after branch of 2.16 - * move to qt5/python3 + * move to qt5/python3/ninja - -- Jürgen E. Fischer Fri, 08 Jul 2016 14:12:36 +0200 + -- Jürgen E. Fischer Thu, 06 Oct 2016 13:30:42 +0200 qgis (2.16.0) unstable; urgency=medium diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000000..ec635144f60 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/compat.in b/debian/compat.in deleted file mode 100644 index 1d51f689fbd..00000000000 --- a/debian/compat.in +++ /dev/null @@ -1,2 +0,0 @@ -#stretch sid jessie trusty xenial#9 -#jessie#8 diff --git a/debian/control b/debian/control index c797fc73b8b..34d66b5086e 100644 --- a/debian/control +++ b/debian/control @@ -6,35 +6,41 @@ Priority: optional Build-Depends: bison, cmake (>= 2.8), - debhelper (>= 7), + debhelper (>= 9), + dh-python, flex, grass-dev, libexpat1-dev, libfcgi-dev, - libgdal1-dev | libgdal-dev, + libgdal-dev (>= 1.11), + libgsl-dev, libgeos-dev (>= 3.0.0), - libgsl0-dev, libpq-dev, libproj-dev, - qtbase5-dev, - libqca-qt5-2-dev, - libqwt-qt5-dev, libspatialite-dev, libsqlite3-dev, libspatialindex-dev, + qtbase5-dev, qttools5-dev-tools, qttools5-dev, qtscript5-dev, qtpositioning5-dev, + libqt5svg5-dev, libqt5xmlpatterns5-dev, libqt5webkit5-dev, libqt5opengl5-dev, libqt5sql5-sqlite, libqt5scintilla2-dev, + libqwt-qt5-dev, libqca-qt5-2-dev, libqca-qt5-2-plugins, + python3-dev, python3-all-dev, python3-sip, python3-sip-dev, + pyqt5-dev-tools, pyqt5-dev, pyqt5.qsci-dev, + python3-pyqt5, python3-pyqt5.qsci, python3-pyqt5.qtsql, python3-pyqt5.qtsvg, + python3-gdal, + python3-nose2, python3-yaml, python3-mock, python3-psycopg2, python3-future, python3-termcolor, pkg-config, - pyqt5-dev, - python3-dev, - python3-all-dev, - python3-sip, - python3-sip-dev, - python3-pyqt5, git, txt2tags, - doxygen + doxygen, + gdal-bin, + spawn-fcgi, lighttpd, poppler-utils, + graphviz, + xvfb, xauth, + xfonts-base, xfonts-100dpi, xfonts-75dpi, xfonts-scalable, + libosgearth-dev, + locales, ca-certificates, ninja-build Build-Conflicts: libqgis-dev, qgis-dev -Standards-Version: 3.8.4 -XS-Python-Version: current +Standards-Version: 3.9.7 Vcs-Browser: https://github.com/qgis/QGIS/ Vcs-Git: https://github.com/qgis/QGIS.git Homepage: http://qgis.org/ @@ -64,7 +70,9 @@ Description: Geographic Information System (GIS) Package: qgis-common Architecture: all Depends: - ${misc:Depends} + libjs-jquery, + libjs-leaflet, + ${misc:Depends} Description: QGIS - architecture-independent data QGIS is a Geographic Information System (GIS) which manages, analyzes and display databases of geographic information. @@ -72,20 +80,20 @@ Description: QGIS - architecture-independent data This package contains architecture-independent supporting data files for use with QGIS. -Package: libqgis-app3.0.0 +Package: libqgis-app2.99.0 Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends} -Replaces: libqgis{QGIS_ABI} +Replaces: libqgis2.99.0 Description: QGIS - shared app library QGIS is a Geographic Information System (GIS) which manages, analyzes and display databases of geographic information. . This package contains the shared app library. -Package: libqgis-core3.0.0 +Package: libqgis-core2.99.0 Architecture: any Section: libs Depends: @@ -97,7 +105,7 @@ Description: QGIS - shared core library . This package contains the shared core library. -Package: libqgis-gui3.0.0 +Package: libqgis-gui2.99.0 Architecture: any Section: libs Depends: @@ -109,7 +117,7 @@ Description: QGIS - shared gui library . This package contains the shared gui library. -Package: libqgis-analysis3.0.0 +Package: libqgis-analysis2.99.0 Architecture: any Section: libs Depends: @@ -121,7 +129,7 @@ Description: QGIS - shared analysis library . This package contains the shared analysis library. -Package: libqgis-networkanalysis3.0.0 +Package: libqgis-networkanalysis2.99.0 Architecture: any Section: libs Depends: @@ -133,7 +141,7 @@ Description: QGIS - shared network analysis library . This package contains the shared network analysis library. -Package: libqgisgrass3.0.0 +Package: libqgisgrass7-2.99.0 Architecture: any Section: libs Depends: @@ -145,13 +153,13 @@ Description: QGIS - shared grass library . This package contains the shared grass library. -Package: libqgispython3.0.0 +Package: libqgispython2.99.0 Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends} -Replaces: libqgis3.0.0 +Replaces: libqgis2.99.0 Description: QGIS - shared Python library QGIS is a Geographic Information System (GIS) which manages, analyzes and display databases of geographic information. @@ -170,7 +178,7 @@ Description: QGIS custom widgets for Qt Designer . This package contains a library to use specific QGIS widgets in Qt Designer. -Package: libqgis-server{QGIS_ABI} +Package: libqgis-server2.99.0 Architecture: any Depends: ${shlibs:Depends}, @@ -187,18 +195,18 @@ Section: libdevel Depends: grass-dev, libexpat1-dev, - libgdal1-dev, + libgdal-dev (>= 1.11), libgeos-dev (>= 3.0.0), - libgsl0-dev, + libgsl-dev, libpq-dev, libproj-dev, - libqgis-app3.0.0 (= ${binary:Version}), - libqgis-core3.0.0 (= ${binary:Version}), - libqgis-gui3.0.0 (= ${binary:Version}), - libqgis-analysis3.0.0 (= ${binary:Version}), - libqgis-networkanalysis3.0.0 (= ${binary:Version}), - libqgisgrass7.0.4-3.0.0 (= ${binary:Version}), - libqgispython3.0.0 (= ${binary:Version}), + libqgis-app2.99.0 (= ${binary:Version}), + libqgis-core2.99.0 (= ${binary:Version}), + libqgis-gui2.99.0 (= ${binary:Version}), + libqgis-analysis2.99.0 (= ${binary:Version}), + libqgis-networkanalysis2.99.0 (= ${binary:Version}), + libqgisgrass7-2.99.0 (= ${binary:Version}), + libqgispython2.99.0 (= ${binary:Version}), libsqlite3-dev, qtbase5-dev, python3-pyqt5, qttools5-dev, libqt5svg5-dev, pyqt5.qsci-dev, @@ -229,13 +237,13 @@ Architecture: any Section: debug Priority: extra Depends: - libqgis-app{QGIS_ABI} (= ${binary:Version}), - libqgis-core{QGIS_ABI} (= ${binary:Version}), - libqgis-gui{QGIS_ABI} (= ${binary:Version}), - libqgis-analysis{QGIS_ABI} (= ${binary:Version}), - libqgis-networkanalysis{QGIS_ABI} (= ${binary:Version}), - libqgisgrass{GRASSVER}-{QGIS_ABI} (= ${binary:Version}), - libqgispython{QGIS_ABI} (= ${binary:Version}), + libqgis-app2.99.0 (= ${binary:Version}), + libqgis-core2.99.0 (= ${binary:Version}), + libqgis-gui2.99.0 (= ${binary:Version}), + libqgis-analysis2.99.0 (= ${binary:Version}), + libqgis-networkanalysis2.99.0 (= ${binary:Version}), + libqgisgrass7-2.99.0 (= ${binary:Version}), + libqgispython2.99.0 (= ${binary:Version}), ${misc:Depends} Suggests: gdb Description: QGIS - debugging symbols @@ -304,7 +312,7 @@ Depends: python3-six, python3-yaml, python3-future, - libqgispython3.0.0, + libqgispython2.99.0, ${shlibs:Depends}, ${python:Depends}, ${misc:Depends}, diff --git a/debian/control.in b/debian/control.in index 322a4f5ba7b..85ac9a81b3a 100644 --- a/debian/control.in +++ b/debian/control.in @@ -6,14 +6,14 @@ Priority: optional Build-Depends: bison, cmake (>= 2.8), -#sid stretch jessie trusty xenial# debhelper (>= 9), -#sid stretch jessie trusty xenial# dh-python, + debhelper (>= 9), + dh-python, flex, grass-dev, libexpat1-dev, libfcgi-dev, -#sid stretch jessie trusty xenial# libgdal-dev (>= 1.10.1-0~), -#jessie trusty# libgsl0-dev, + libgdal-dev (>= 1.11), +#trusty# libgsl0-dev, #sid stretch xenial# libgsl-dev, libgeos-dev (>= 3.0.0), libpq-dev, @@ -40,12 +40,10 @@ Build-Depends: xfonts-base, xfonts-100dpi, xfonts-75dpi, xfonts-scalable, #sid# libosgearth-dev, #oracle# oracle-instantclient12.1-devel, - locales, ca-certificates + locales, ca-certificates, ninja-build Build-Conflicts: libqgis-dev, qgis-dev #sid stretch xenial#Standards-Version: 3.9.7 -#jessie#Standards-Version: 3.9.6 #trusty#Standards-Version: 3.8.4 -#sid stretch jessie#X-Python-Version: >= 2.7, << 2.8 #trusty xenial#XS-Python-Version: current Vcs-Browser: https://github.com/qgis/QGIS/ Vcs-Git: https://github.com/qgis/QGIS.git @@ -202,9 +200,9 @@ Section: libdevel Depends: grass-dev, libexpat1-dev, -#sid stretch jessie trusty xenial# libgdal-dev (>= 1.10.1-0~), + libgdal-dev (>= 1.11), libgeos-dev (>= 3.0.0), -#jessie trusty# libgsl0-dev, +#trusty# libgsl0-dev, #sid stretch xenial# libgsl-dev, libpq-dev, libproj-dev, diff --git a/debian/rules b/debian/rules index 84b4b5f8324..3af8146ba98 100755 --- a/debian/rules +++ b/debian/rules @@ -36,16 +36,16 @@ endif QT_PLUGINS_DIR = usr/lib/$(DEB_BUILD_MULTIARCH)/qt5/plugins -ifneq ($(DISTRIBUTION),$(findstring $(DISTRIBUTION),"stretch xenial")) +ifneq ($(DISTRIBUTION),$(findstring $(DISTRIBUTION),"trusty stretch xenial")) DISTRIBUTION := sid endif DEB_BUILD_NAME ?= $(DISTRIBUTION)-$(DEB_BUILD_ARCH) ifeq (,$(DISPLAY)) -TESTMAKE=xvfb-run -a -n 1 -s "-screen 0 1280x1024x24 -dpi 96" $(MAKE) +TESTMAKE=xvfb-run -a -n 1 -s "-screen 0 1280x1024x24 -dpi 96" ninja else -TESTMAKE=$(MAKE) +TESTMAKE=ninja endif QGIS_MAJOR=$(shell sed -ne 's/SET(CPACK_PACKAGE_VERSION_MAJOR "\([0-9]*\)")/\1/p' CMakeLists.txt) @@ -56,6 +56,7 @@ GRASS=grass$(subst .,,$(shell pkg-config --modversion grass|cut -d. -f1,2)) GRASSVER=$(subst .,,$(shell pkg-config --modversion grass|cut -d. -f1)) CMAKE_OPTS := \ + -G Ninja \ -DBUILDNAME=$(DEB_BUILD_NAME) \ -DCMAKE_VERBOSE_MAKEFILE=1 \ -DCMAKE_INSTALL_PREFIX=/usr \ @@ -84,7 +85,8 @@ CMAKE_OPTS := \ -DWITH_QSPATIALITE=TRUE \ -DWITH_PYSPATIALITE=TRUE \ -DQT_PLUGINS_DIR=$(QT_PLUGINS_DIR) \ - -DPYTHON_LIBRARY=$(shell python3-config --ldflags | sed -e 's\#-L\(.*\) -L/usr/lib -l\([^ ]*\) .*$$\#\1/lib\2.so\#') + -DPYTHON_LIBRARY=$(shell python3-config --ldflags | sed -e 's\#-L\(.*\) -L/usr/lib -l\([^ ]*\) .*$$\#\1/lib\2.so\#') \ + -DDOXYGEN_ON_DEMAND=TRUE ifneq ($(SHA),) CMAKE_OPTS += -DSHA=$(SHA) @@ -136,7 +138,7 @@ endif -DORACLE_INCLUDEDIR=$(ORACLE_INCLUDEDIR) endif -ifneq (,$(findstring $(DISTRIBUTION),"sid stretch jessie")) +ifneq (,$(findstring $(DISTRIBUTION),"sid stretch")) CMAKE_OPTS += -DSPATIALINDEX_LIBRARY=/usr/lib/$(DEB_BUILD_MULTIARCH)/libspatialindex.so endif @@ -154,12 +156,10 @@ else CMAKE_OPTS += -DENABLE_TESTS=TRUE endif -ifneq (,$(findstring $(DISTRIBUTION),"jessie stretch trusty sid")) CPPFLAGS := $(shell dpkg-buildflags --get CPPFLAGS) CFLAGS := $(shell dpkg-buildflags --get CFLAGS) $(CPPFLAGS) CXXFLAGS := $(shell dpkg-buildflags --get CXXFLAGS) $(CPPFLAGS) LDFLAGS := $(shell dpkg-buildflags --get LDFLAGS) -endif ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s @@ -236,6 +236,12 @@ override_dh_clean: cleantemplates override_dh_auto_configure: dh_auto_configure -- $(CMAKE_OPTS) +override_dh_auto_build-arch: + ninja -C $(QGIS_BUILDDIR) + +override_dh_auto_build-indep: + ninja -C $(QGIS_BUILDDIR) apidoc + override_dh_auto_test: test-stamp test-stamp: @@ -254,7 +260,7 @@ endif touch test-stamp override_dh_auto_install: - dh_auto_install + DESTDIR=$(CURDIR)/debian/tmp ninja -C $(QGIS_BUILDDIR) install # remove unwanted files $(RM) $(CURDIR)/debian/tmp/usr/share/qgis/doc/api/installdox diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 5c8edeffe46..c1802517118 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -103,7 +103,14 @@ IF(WITH_APIDOC) STRING(REPLACE ";" " " DOXYGEN_INPUT "${DOXYGEN_INPUT}") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake_templates/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) - ADD_CUSTOM_TARGET(apidoc ALL DEPENDS ${QHP_FILES}) + + SET (DOXYGEN_ON_DEMAND FALSE CACHE BOOL "Determines whether the QGIS API doxygen documentation should be build on demand only") + + IF(DOXYGEN_ON_DEMAND) + ADD_CUSTOM_TARGET(apidoc DEPENDS ${QHP_FILES}) + ELSE(DOXYGEN_ON_DEMAND) + ADD_CUSTOM_TARGET(apidoc ALL DEPENDS ${QHP_FILES}) + ENDIF(DOXYGEN_ON_DEMAND) ADD_CUSTOM_COMMAND( OUTPUT ${QHP_FILES} DEPENDS ${DOXYGEN_FILES} diff --git a/doc/api_break.dox b/doc/api_break.dox index dc5ecb545f6..3e0a804fca6 100644 --- a/doc/api_break.dox +++ b/doc/api_break.dox @@ -710,6 +710,9 @@ version instead.
  • helptext() has been renamed to helpText()
  • isValid() has been renamed to checkExpression()
  • acceptVisitor() has been removed
  • +
  • QgsExpression::referencedColumns() returns QSet instead of QStringList
  • +
  • QgsExpression::Node::referencedColumns() returns QSet instead of QStringList
  • +
  • QgsExpression::Function::referencedColumns() returns QSet instead of QStringList
  • \subsection qgis_api_break_3_0_QgsFeature QgsFeature @@ -1087,6 +1090,14 @@ be used instead of a null pointer if no transformation is required.
  • prepareGeometry() and geometryRequiresPreparation() now take a QgsCoordinateTransform reference, not a pointer. An invalid QgsCoordinateTransform should be used instead of a null pointer if no transformation is required.
  • +\subsection qgis_api_break_3_0_QgsPanelWidgetStack QgsPanelWidgetStack + + + \subsection qgis_api_break_3_0_QgsPluginLayer QgsPluginLayer diff --git a/mac/app.info.plist.in b/mac/app.info.plist.in index 5c103012b61..4513d99ab0e 100644 --- a/mac/app.info.plist.in +++ b/mac/app.info.plist.in @@ -7,7 +7,7 @@ CFBundleName QGIS CFBundleIdentifier - org.qgis.qgis2 + org.qgis.qgis3 CFBundleExecutable ${QGIS_APP_NAME} CFBundlePackageType diff --git a/mac/browser.info.plist.in b/mac/browser.info.plist.in index 38617f88565..c2eac319918 100644 --- a/mac/browser.info.plist.in +++ b/mac/browser.info.plist.in @@ -7,7 +7,7 @@ CFBundleName QGIS Browser CFBundleIdentifier - org.qgis.qgis2_browser + org.qgis.qgis3_browser CFBundleExecutable QGIS Browser CFBundlePackageType diff --git a/ms-windows/osgeo4w/package-nightly.cmd b/ms-windows/osgeo4w/package-nightly.cmd index df96c18e673..042b7703ef5 100644 --- a/ms-windows/osgeo4w/package-nightly.cmd +++ b/ms-windows/osgeo4w/package-nightly.cmd @@ -29,7 +29,7 @@ if "%ARCH%"=="" goto usage if not "%SHA%"=="" set SHA=-%SHA% if "%SITE%"=="" set SITE=qgis.org -set BUILDDIR=%CD%\build-nightly-%ARCH% +set BUILDDIR=%CD%\build-%PACKAGENAME%-%ARCH% if "%OSGEO4W_ROOT%"=="" ( if "%ARCH%"=="x86" ( @@ -52,35 +52,36 @@ if not "%PROGRAMFILES(X86)%"=="" set PF86=%PROGRAMFILES(X86)% if "%PF86%"=="" set PF86=%PROGRAMFILES% if "%PF86%"=="" (echo PROGRAMFILES not set & goto error) -if "%ARCH%"=="x86" goto devenv_x86 -goto devenv_x86_64 +if "%ARCH%"=="x86" goto cmake_x86 +goto cmake_x86_64 -:devenv_x86 +:cmake_x86 set GRASS6_VERSION=6.4.4 call "%PF86%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 if exist "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" call "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /x86 /Release path %path%;%PF86%\Microsoft Visual Studio 10.0\VC\bin +set CMAKE_COMPILER_PATH=%PF86%\Microsoft Visual Studio 10.0\VC\bin set CMAKE_OPT=^ - -G "Visual Studio 10" ^ -D SIP_BINARY_PATH=%O4W_ROOT%/apps/Python27/sip.exe ^ -D QWT_LIBRARY=%O4W_ROOT%/lib/qwt.lib ^ -D CMAKE_CXX_FLAGS_RELWITHDEBINFO="/MD /ZI /MP /Od /D NDEBUG /D QGISDEBUG" ^ - -D CMAKE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO=%BUILDDIR%\apps\%PACKAGENAME%\pdb -goto devenv + -D CMAKE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO=%BUILDDIR%\apps\%PACKAGENAME%\pdb ^ + -D SPATIALINDEX_LIBRARY=%O4W_ROOT%/lib/spatialindex_i.lib +goto cmake -:devenv_x86_64 +:cmake_x86_64 set GRASS6_VERSION=6.4.3 call "%PF86%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" amd64 if exist "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" call "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /x64 /Release path %path%;%PF86%\Microsoft Visual Studio 10.0\VC\bin +set CMAKE_COMPILER_PATH=%PF86%\Microsoft Visual Studio 10.0\VC\bin\amd64 set SETUPAPI_LIBRARY=%PF86%\Microsoft SDKs\Windows\v7.0A\Lib\x64\SetupAPI.Lib if not exist "%SETUPAPI_LIBRARY%" set SETUPAPI_LIBRARY=%PROGRAMFILES%\Microsoft SDKs\Windows\v7.1\Lib\x64\SetupAPI.lib if not exist "%SETUPAPI_LIBRARY%" (echo SETUPAPI_LIBRARY not found & goto error) set CMAKE_OPT=^ - -G "Visual Studio 10 Win64" ^ -D SPATIALINDEX_LIBRARY=%O4W_ROOT%/lib/spatialindex-64.lib ^ -D SIP_BINARY_PATH=%O4W_ROOT%/bin/sip.exe ^ -D QWT_LIBRARY=%O4W_ROOT%/lib/qwt5.lib ^ @@ -89,7 +90,7 @@ set CMAKE_OPT=^ -D SETUPAPI_LIBRARY="%SETUPAPI_LIBRARY%" ^ -D CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS=TRUE -:devenv +:cmake for /f "usebackq tokens=1" %%a in (`%OSGEO4W_ROOT%\bin\grass70 --config path`) do set GRASS70_PATH=%%a for %%i in ("%GRASS70_PATH%") do set GRASS70_VERSION=%%~nxi set GRASS70_VERSION=%GRASS70_VERSION:grass-=% @@ -153,7 +154,10 @@ if errorlevel 1 goto error set LIB=%LIB%;%OSGEO4W_ROOT%\lib set INCLUDE=%INCLUDE%;%OSGEO4W_ROOT%\include -cmake %CMAKE_OPT% ^ +cmake -G Ninja ^ + -D CMAKE_CXX_COMPILER="%CMAKE_COMPILER_PATH:\=/%/cl.exe" ^ + -D CMAKE_C_COMPILER="%CMAKE_COMPILER_PATH:\=/%/cl.exe" ^ + -D CMAKE_LINKER="%CMAKE_COMPILER_PATH:\=/%/link.exe" ^ -D BUILDNAME="%PACKAGENAME%-%VERSION%%SHA%-Nightly-VC10-%ARCH%" ^ -D SITE="%SITE%" ^ -D PEDANTIC=TRUE ^ @@ -195,7 +199,8 @@ cmake %CMAKE_OPT% ^ -D WITH_INTERNAL_MOCK=FALSE ^ -D WITH_INTERNAL_HTTPLIB2=FALSE ^ -D WITH_INTERNAL_FUTURE=FALSE ^ - %SRCDIR% + %CMAKE_OPT% ^ + %SRCDIR:\=/% if errorlevel 1 (echo cmake failed & goto error) :skipcmake @@ -244,7 +249,7 @@ if exist "%PKGDIR%" ( ) echo INSTALL: %DATE% %TIME% -cmake --build %BUILDDIR% --target INSTALL --config %BUILDCONF% +cmake --build %BUILDDIR% --target install --config %BUILDCONF% if errorlevel 1 (echo INSTALL failed & goto error) :package diff --git a/ms-windows/osgeo4w/package.cmd b/ms-windows/osgeo4w/package.cmd index 67bac59be67..7d02cbb0518 100644 --- a/ms-windows/osgeo4w/package.cmd +++ b/ms-windows/osgeo4w/package.cmd @@ -29,7 +29,7 @@ if "%ARCH%"=="" goto usage if not "%SHA%"=="" set SHA=-%SHA% if "%SITE%"=="" set SITE=qgis.org -set BUILDDIR=%CD%\build-%ARCH% +set BUILDDIR=%CD%\build-%PACKAGENAME%-%ARCH% if "%OSGEO4W_ROOT%"=="" ( if "%ARCH%"=="x86" ( @@ -52,22 +52,22 @@ if not "%PROGRAMFILES(X86)%"=="" set PF86=%PROGRAMFILES(X86)% if "%PF86%"=="" set PF86=%PROGRAMFILES% if "%PF86%"=="" (echo PROGRAMFILES not set & goto error) -if "%ARCH%"=="x86" goto devenv_x86 -goto devenv_x86_64 +if "%ARCH%"=="x86" goto cmake_x86 +goto cmake_x86_64 -:devenv_x86 +:cmake_x86 set GRASS6_VERSION=6.4.4 call "%PF86%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 if exist "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" call "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /x86 /Release path %path%;%PF86%\Microsoft Visual Studio 10.0\VC\bin +set CMAKE_COMPILER_PATH=%PF86%\Microsoft Visual Studio 10.0\VC\bin set CMAKE_OPT=^ - -G "Visual Studio 10" ^ -D SIP_BINARY_PATH=%O4W_ROOT%/apps/Python27/sip.exe ^ -D QWT_LIBRARY=%O4W_ROOT%/lib/qwt.lib -goto devenv +goto cmake -:devenv_x86_64 +:cmake_x86_64 set GRASS6_VERSION=6.4.3 call "%PF86%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" amd64 if exist "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" call "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /x64 /Release @@ -78,14 +78,13 @@ if not exist "%SETUPAPI_LIBRARY%" set SETUPAPI_LIBRARY=%PROGRAMFILES%\Microsoft if not exist "%SETUPAPI_LIBRARY%" (echo SETUPAPI_LIBRARY not found & goto error) set CMAKE_OPT=^ - -G "Visual Studio 10 Win64" ^ -D SPATIALINDEX_LIBRARY=%O4W_ROOT%/lib/spatialindex-64.lib ^ -D SIP_BINARY_PATH=%O4W_ROOT%/bin/sip.exe ^ -D QWT_LIBRARY=%O4W_ROOT%/lib/qwt5.lib ^ -D SETUPAPI_LIBRARY="%SETUPAPI_LIBRARY%" ^ -D CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS=TRUE -:devenv +:cmake for /f "usebackq tokens=1" %%a in (`%OSGEO4W_ROOT%\bin\grass70 --config path`) do set GRASS70_PATH=%%a for %%i in ("%GRASS70_PATH%") do set GRASS70_VERSION=%%~nxi set GRASS70_VERSION=%GRASS70_VERSION:grass-=% @@ -149,7 +148,7 @@ if errorlevel 1 goto error set LIB=%LIB%;%OSGEO4W_ROOT%\lib set INCLUDE=%INCLUDE%;%OSGEO4W_ROOT%\include -cmake %CMAKE_OPT% ^ +cmake -G Ninja ^ -D BUILDNAME="%PACKAGENAME%-%VERSION%%SHA%-Release-VC10-%ARCH%" ^ -D SITE="%SITE%" ^ -D PEDANTIC=TRUE ^ @@ -189,7 +188,8 @@ cmake %CMAKE_OPT% ^ -D WITH_INTERNAL_PYTZ=FALSE ^ -D WITH_INTERNAL_SIX=FALSE ^ -D WITH_INTERNAL_FUTURE=FALSE ^ - %SRCDIR% + %CMAKE_OPT% ^ + %SRCDIR:\=/% if errorlevel 1 (echo cmake failed & goto error) :skipcmake @@ -229,7 +229,7 @@ if exist "%PKGDIR%" ( ) echo INSTALL: %DATE% %TIME% -cmake --build %BUILDDIR% --target INSTALL --config %BUILDCONF% +cmake --build %BUILDDIR% --target install --config %BUILDCONF% if errorlevel 1 (echo INSTALL failed & goto error) echo PACKAGE: %DATE% %TIME% diff --git a/ms-windows/plugins.nsh b/ms-windows/plugins.nsh index a4384c54780..f751da0d410 100644 --- a/ms-windows/plugins.nsh +++ b/ms-windows/plugins.nsh @@ -7,22 +7,22 @@ # Please don't remove this header. # ################################################################################ -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "coordinatecaptureplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "diagramoverlay" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "dxf2shpconverterplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "evis" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "georefplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "globeplugin" "false" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "gpsimporterplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "grassplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "heatmapplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "interpolationplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "offlineeditingplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "oracleplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "rasterterrainplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "roadgraphplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "spatialqueryplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "topolplugin" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\Plugins" "zonalstatisticsplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "coordinatecaptureplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "diagramoverlay" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "dxf2shpconverterplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "evis" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "georefplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "globeplugin" "false" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "gpsimporterplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "grassplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "heatmapplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "interpolationplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "offlineeditingplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "oracleplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "rasterterrainplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "roadgraphplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "spatialqueryplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "topolplugin" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\Plugins" "zonalstatisticsplugin" "true" ############################### reg2nsis end ################################# diff --git a/ms-windows/python_plugins.nsh b/ms-windows/python_plugins.nsh index dec5c76dadc..7a86b37ec82 100644 --- a/ms-windows/python_plugins.nsh +++ b/ms-windows/python_plugins.nsh @@ -7,8 +7,8 @@ # Please don't remove this header. # ################################################################################ -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\PythonPlugins" "GdalTools" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\PythonPlugins" "db_manager" "true" -WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS2\PythonPlugins" "processing" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\PythonPlugins" "GdalTools" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\PythonPlugins" "db_manager" "true" +WriteRegStr HKEY_CURRENT_USER "Software\QGIS\QGIS3\PythonPlugins" "processing" "true" ############################### reg2nsis end ################################# diff --git a/python/core/auth/qgsauthmanager.sip b/python/core/auth/qgsauthmanager.sip index f2f6200c1c0..4be77a4dbc6 100644 --- a/python/core/auth/qgsauthmanager.sip +++ b/python/core/auth/qgsauthmanager.sip @@ -44,7 +44,7 @@ class QgsAuthManager : QObject /** Standard message for when QCA's qca-ossl plugin is missing and system is disabled */ const QString disabledMessage() const; - /** The standard authentication database file in ~/.qgis2/ or defined location + /** The standard authentication database file in ~/.qgis3/ or defined location * @see QgsApplication::qgisAuthDbFilePath */ const QString authenticationDbPath() const; diff --git a/python/core/composer/qgscomposeritemcommand.sip b/python/core/composer/qgscomposeritemcommand.sip index 0f517ffdd46..564d6c5a770 100644 --- a/python/core/composer/qgscomposeritemcommand.sip +++ b/python/core/composer/qgscomposeritemcommand.sip @@ -49,9 +49,14 @@ class QgsComposerMergeCommand : QgsComposerItemCommand //composer label ComposerLabelSetText, ComposerLabelSetId, + ComposerLabelFontColor, //composer map ComposerMapRotation, ComposerMapAnnotationDistance, + ComposerMapGridFramePenColor, + ComposerMapGridFrameFill1Color, + ComposerMapGridFrameFill2Color, + ComposerMapGridAnnotationFontColor, //composer legend ComposerLegendText, LegendColumnCount, @@ -69,8 +74,12 @@ class QgsComposerMergeCommand : QgsComposerItemCommand LegendBoxSpace, LegendColumnSpace, LegendRasterBorderWidth, + LegendFontColor, + LegendRasterBorderColor, //composer picture ComposerPictureRotation, + ComposerPictureFillColor, + ComposerPictureOutlineColor, // composer scalebar ScaleBarLineWidth, ScaleBarHeight, @@ -81,6 +90,10 @@ class QgsComposerMergeCommand : QgsComposerItemCommand ScaleBarMapUnitsSegment, ScaleBarLabelBarSize, ScaleBarBoxContentSpace, + ScaleBarFontColor, + ScaleBarFillColor, + ScaleBarFill2Color, + ScaleBarStrokeColor, // composer table TableMaximumFeatures, TableMargin, @@ -90,9 +103,13 @@ class QgsComposerMergeCommand : QgsComposerItemCommand ShapeOutlineWidth, //composer arrow ArrowOutlineWidth, + ArrowHeadFillColor, + ArrowHeadOutlineColor, ArrowHeadWidth, //item ItemOutlineWidth, + ItemOutlineColor, + ItemBackgroundColor, ItemMove, ItemRotation, ItemTransparency, diff --git a/python/core/composer/qgscomposermultiframecommand.sip b/python/core/composer/qgscomposermultiframecommand.sip index e405fa291ac..23738b81f81 100644 --- a/python/core/composer/qgscomposermultiframecommand.sip +++ b/python/core/composer/qgscomposermultiframecommand.sip @@ -49,7 +49,11 @@ class QgsComposerMultiFrameMergeCommand: QgsComposerMultiFrameCommand TableMaximumFeatures, TableMargin, TableGridStrokeWidth, - TableCellStyle + TableCellStyle, + TableHeaderFontColor, + TableContentFontColor, + TableGridColor, + TableBackgroundColor, }; QgsComposerMultiFrameMergeCommand( Context c, QgsComposerMultiFrame* multiFrame, const QString& text ); diff --git a/python/core/qgsapplication.sip b/python/core/qgsapplication.sip index 05bcc95a51f..8a8dd5a24ce 100644 --- a/python/core/qgsapplication.sip +++ b/python/core/qgsapplication.sip @@ -124,7 +124,7 @@ static void qtgui_UpdatePyArgv(PyObject *argvlist, int argc, char **argv) static void setUITheme( const QString &themeName ); /** - * @brief All themes found in ~/.qgis2/themes folder. + * @brief All themes found in ~/.qgis3/themes folder. * The path is to the root folder for the theme * @note Valid theme folders must contain a style.qss file. * @return A hash of theme name and theme path. Valid theme folders contain style.qss diff --git a/python/core/qgsdatadefined.sip b/python/core/qgsdatadefined.sip index f753eda2af8..f769081fdea 100644 --- a/python/core/qgsdatadefined.sip +++ b/python/core/qgsdatadefined.sip @@ -128,7 +128,7 @@ class QgsDataDefined * @param context expression context, used for preparing the expression if required * @note added in QGIS 2.12 */ - QStringList referencedColumns( const QgsExpressionContext& context = QgsExpressionContext() ); + QSet referencedColumns( const QgsExpressionContext& context = QgsExpressionContext() ); /** * Get the field which this QgsDataDefined represents. Be aware that this may return diff --git a/python/core/qgsexpression.sip b/python/core/qgsexpression.sip index caa560ef0c9..f4dc271ecd8 100644 --- a/python/core/qgsexpression.sip +++ b/python/core/qgsexpression.sip @@ -51,7 +51,7 @@ class QgsExpression * * TODO QGIS3: Return QSet */ - QStringList referencedColumns() const; + QSet referencedColumns() const; /** * Return a list of field name indexes obtained from the provided fields. @@ -301,7 +301,7 @@ class QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = false ); @@ -314,7 +314,7 @@ class QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = false ); @@ -350,7 +350,7 @@ class QgsExpression */ bool lazyEval() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; /** Returns whether the function is only available if provided by a QgsExpressionContext object. * @note added in QGIS 2.12 @@ -517,7 +517,7 @@ class QgsExpression * * @return A list of columns required to evaluate this expression */ - virtual QStringList referencedColumns() const = 0; + virtual QSet referencedColumns() const = 0; /** * Abstract virtual method which returns if the geometry is required to evaluate @@ -596,7 +596,7 @@ class QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; }; @@ -616,7 +616,7 @@ class QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; @@ -650,7 +650,7 @@ class QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; }; @@ -670,7 +670,7 @@ class QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; @@ -692,7 +692,7 @@ class QgsExpression virtual QString dump() const; virtual QgsExpression::Node* clone() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; }; @@ -709,7 +709,7 @@ class QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; @@ -741,7 +741,7 @@ class QgsExpression virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ); virtual QString dump() const; - virtual QStringList referencedColumns() const; + virtual QSet referencedColumns() const; virtual bool needsGeometry() const; virtual QgsExpression::Node* clone() const; }; diff --git a/python/core/qgsexpressioncontext.sip b/python/core/qgsexpressioncontext.sip index f2712123675..8aece97fac6 100644 --- a/python/core/qgsexpressioncontext.sip +++ b/python/core/qgsexpressioncontext.sip @@ -17,7 +17,7 @@ class QgsScopedExpressionFunction : QgsExpression::Function const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = true ); diff --git a/python/core/qgsexpressionfieldbuffer.sip b/python/core/qgsexpressionfieldbuffer.sip index dfa3aee5e8f..8bceb4a67db 100644 --- a/python/core/qgsexpressionfieldbuffer.sip +++ b/python/core/qgsexpressionfieldbuffer.sip @@ -27,6 +27,16 @@ class QgsExpressionFieldBuffer */ void removeExpression( int index ); + /** + * Renames an expression field at a given index + * + * @param index The index of the expression to change + * @param name New name for field + * + * @note added in 3.0 + */ + void renameExpression( int index, const QString& name ); + /** * Changes the expression at a given index * diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index 71c11d37f55..4c7ab13e101 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -122,14 +122,14 @@ class QgsLabelingEngineInterface //! clears data defined objects from PAL layer settings for a registered layer virtual void clearActiveLayer( const QString& layerID ) = 0; //! called when starting rendering of a layer - virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0; + virtual int prepareLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) = 0; //! adds a diagram layer to the labeling engine //! @note added in QGIS 2.12 - virtual int prepareDiagramLayer( QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx ); + virtual int prepareDiagramLayer( QgsVectorLayer *layer, QSet& attrNames, QgsRenderContext& ctx ); //! called for every feature - virtual void registerFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context ) = 0; + virtual void registerFeature( const QString &layerID, QgsFeature& feat, QgsRenderContext& context ) = 0; //! called for every diagram feature - virtual void registerDiagramFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context ); + virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context ); //! called when the map is drawn and labels should be placed virtual void drawLabeling( QgsRenderContext& context ) = 0; //! called when we're done with rendering @@ -915,10 +915,10 @@ class QgsPalLabeling : QgsLabelingEngineInterface //! clears data defined objects from PAL layer settings for a registered layer virtual void clearActiveLayer( const QString& layerID ); //! hook called when drawing layer before issuing select() - virtual int prepareLayer( QgsVectorLayer* layer, QStringList &attrNames, QgsRenderContext& ctx ); + virtual int prepareLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ); //! adds a diagram layer to the labeling engine //! @note added in QGIS 2.12 - virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ); + virtual int prepareDiagramLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ); /** Register a feature for labelling. * @param layerID string identifying layer associated with label diff --git a/python/core/qgsrelation.sip b/python/core/qgsrelation.sip index ea4a1f5dc86..cc347aa2a3b 100644 --- a/python/core/qgsrelation.sip +++ b/python/core/qgsrelation.sip @@ -171,6 +171,12 @@ class QgsRelation */ QString id() const; + /** + * Generate a (project-wide) unique id for this relation + * @note added in QGIS 3.0 + */ + void generateId(); + /** * Access the referencing (child) layer's id * This is the layer which has the field(s) which point to another layer @@ -241,6 +247,15 @@ class QgsRelation */ bool isValid() const; + /** + * Compares the two QgsRelation, ignoring the name and the ID. + * + * @param other The other relation + * @return true if they are similar + * @note added in QGIS 3.0 + */ + bool hasEqualDefinition( const QgsRelation& other ) const; + protected: /** * Updates the validity status of this relation. diff --git a/python/core/qgsrelationmanager.sip b/python/core/qgsrelationmanager.sip index c77d1676659..4704694fe12 100644 --- a/python/core/qgsrelationmanager.sip +++ b/python/core/qgsrelationmanager.sip @@ -90,6 +90,16 @@ class QgsRelationManager : QObject */ QList referencedRelations( QgsVectorLayer *layer = 0 ) const; + /** + * Discover all the relations available from the current layers. + * + * @param existingRelations the existing relations to filter them out + * @param layers the current layers + * @return the list of discovered relations + * @note added in QGIS 3.0 + */ + static QList discoverRelations( const QList& existingRelations, const QList& layers ); + signals: /** This signal is emitted when the relations were loaded after reading a project */ void relationsLoaded(); diff --git a/python/core/qgsvectordataprovider.sip b/python/core/qgsvectordataprovider.sip index 90185af6601..b9c62809c02 100644 --- a/python/core/qgsvectordataprovider.sip +++ b/python/core/qgsvectordataprovider.sip @@ -371,6 +371,15 @@ class QgsVectorDataProvider : QgsDataProvider */ virtual QSet dependencies() const; + /** + * Discover the available relations with the given layers. + * @param self the layer using this data provider. + * @param layers the other layers. + * @return the list of N-1 relations from this provider. + * @note added in QGIS 3.0 + */ + virtual QList discoverRelations( const QgsVectorLayer* self, const QList& layers ) const; + signals: /** Signals an error in this provider */ void raiseError( const QString& msg ); diff --git a/python/core/symbology-ng/qgs25drenderer.sip b/python/core/symbology-ng/qgs25drenderer.sip index 56c58f60091..dce3ace659d 100644 --- a/python/core/symbology-ng/qgs25drenderer.sip +++ b/python/core/symbology-ng/qgs25drenderer.sip @@ -33,7 +33,7 @@ class Qgs25DRenderer : QgsFeatureRenderer void startRender( QgsRenderContext& context, const QgsFields& fields ); void stopRender( QgsRenderContext& context ); - QList usedAttributes(); + QSet usedAttributes() const; QgsFeatureRenderer* clone() const; virtual QgsSymbol* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ); diff --git a/python/core/symbology-ng/qgscategorizedsymbolrenderer.sip b/python/core/symbology-ng/qgscategorizedsymbolrenderer.sip index 7c27c6053f7..f7b143b602d 100644 --- a/python/core/symbology-ng/qgscategorizedsymbolrenderer.sip +++ b/python/core/symbology-ng/qgscategorizedsymbolrenderer.sip @@ -59,7 +59,7 @@ class QgsCategorizedSymbolRenderer : QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; virtual QString dump() const; diff --git a/python/core/symbology-ng/qgsgraduatedsymbolrenderer.sip b/python/core/symbology-ng/qgsgraduatedsymbolrenderer.sip index 7a32a49a8a5..c9f7d6de439 100644 --- a/python/core/symbology-ng/qgsgraduatedsymbolrenderer.sip +++ b/python/core/symbology-ng/qgsgraduatedsymbolrenderer.sip @@ -102,7 +102,7 @@ class QgsGraduatedSymbolRenderer : QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; virtual QString dump() const; diff --git a/python/core/symbology-ng/qgsheatmaprenderer.sip b/python/core/symbology-ng/qgsheatmaprenderer.sip index c6664ee99da..3e9da2d1569 100644 --- a/python/core/symbology-ng/qgsheatmaprenderer.sip +++ b/python/core/symbology-ng/qgsheatmaprenderer.sip @@ -18,7 +18,7 @@ class QgsHeatmapRenderer : QgsFeatureRenderer //! @note symbol2 in python bindings virtual QgsSymbolList symbols( QgsRenderContext& context ); virtual QString dump() const; - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; static QgsFeatureRenderer* create( QDomElement& element ) /Factory/; virtual QDomElement save( QDomDocument& doc ); static QgsHeatmapRenderer* convertFromRenderer( const QgsFeatureRenderer* renderer ) /Factory/; diff --git a/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip index 65f97d54cff..ca39659faf3 100644 --- a/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip +++ b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip @@ -39,7 +39,7 @@ class QgsInvertedPolygonRenderer : QgsFeatureRenderer virtual QString dump() const; /** Proxy that will call this method on the embedded renderer. */ - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; /** Proxy that will call this method on the embedded renderer. */ virtual QgsFeatureRenderer::Capabilities capabilities(); /** Proxy that will call this method on the embedded renderer. diff --git a/python/core/symbology-ng/qgsnullsymbolrenderer.sip b/python/core/symbology-ng/qgsnullsymbolrenderer.sip index 3f140595503..7fa52b478b0 100644 --- a/python/core/symbology-ng/qgsnullsymbolrenderer.sip +++ b/python/core/symbology-ng/qgsnullsymbolrenderer.sip @@ -24,7 +24,7 @@ class QgsNullSymbolRenderer : QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ); virtual bool willRenderFeature( QgsFeature& feat, QgsRenderContext& context ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; virtual QString dump() const; virtual QgsFeatureRenderer* clone() const /Factory/; virtual QgsSymbolList symbols( QgsRenderContext& context ); diff --git a/python/core/symbology-ng/qgspointclusterrenderer.sip b/python/core/symbology-ng/qgspointclusterrenderer.sip index 57e6a530885..298e88cfb4f 100644 --- a/python/core/symbology-ng/qgspointclusterrenderer.sip +++ b/python/core/symbology-ng/qgspointclusterrenderer.sip @@ -16,7 +16,7 @@ class QgsPointClusterRenderer : QgsPointDistanceRenderer virtual void startRender( QgsRenderContext& context, const QgsFields& fields ); void stopRender( QgsRenderContext& context ); QDomElement save( QDomDocument& doc ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; //! Create a renderer from XML element static QgsFeatureRenderer* create( QDomElement& symbologyElem ) /Factory/; diff --git a/python/core/symbology-ng/qgspointdisplacementrenderer.sip b/python/core/symbology-ng/qgspointdisplacementrenderer.sip index 966c3f6d17f..36713b2ca23 100644 --- a/python/core/symbology-ng/qgspointdisplacementrenderer.sip +++ b/python/core/symbology-ng/qgspointdisplacementrenderer.sip @@ -26,7 +26,7 @@ class QgsPointDisplacementRenderer : QgsPointDistanceRenderer virtual void startRender( QgsRenderContext& context, const QgsFields& fields ); void stopRender( QgsRenderContext& context ); QDomElement save( QDomDocument& doc ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; //! Create a renderer from XML element static QgsFeatureRenderer* create( QDomElement& symbologyElem ) /Factory/; diff --git a/python/core/symbology-ng/qgspointdistancerenderer.sip b/python/core/symbology-ng/qgspointdistancerenderer.sip index c2bead6b27b..761c0df5dfd 100644 --- a/python/core/symbology-ng/qgspointdistancerenderer.sip +++ b/python/core/symbology-ng/qgspointdistancerenderer.sip @@ -50,7 +50,7 @@ class QgsPointDistanceRenderer : QgsFeatureRenderer virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const; bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; virtual Capabilities capabilities(); virtual QgsSymbolList symbols( QgsRenderContext& context ); virtual QgsSymbol* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ); diff --git a/python/core/symbology-ng/qgsrenderer.sip b/python/core/symbology-ng/qgsrenderer.sip index bf0cdd6b25e..b09f7e95427 100644 --- a/python/core/symbology-ng/qgsrenderer.sip +++ b/python/core/symbology-ng/qgsrenderer.sip @@ -119,10 +119,8 @@ class QgsFeatureRenderer /** * Returns a set of attributes required for this renderer. - * - * TODO QGIS3: Change QList to QSet */ - virtual QList usedAttributes() = 0; + virtual QSet usedAttributes() const = 0; /** * Returns true if this renderer requires the geometry to apply the filter. diff --git a/python/core/symbology-ng/qgsrulebasedrenderer.sip b/python/core/symbology-ng/qgsrulebasedrenderer.sip index 8eff5f1a8b0..e65197fb364 100644 --- a/python/core/symbology-ng/qgsrulebasedrenderer.sip +++ b/python/core/symbology-ng/qgsrulebasedrenderer.sip @@ -340,7 +340,7 @@ class QgsRuleBasedRenderer : QgsFeatureRenderer virtual QString filter( const QgsFields& fields = QgsFields() ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; virtual bool filterNeedsGeometry() const; diff --git a/python/core/symbology-ng/qgssinglesymbolrenderer.sip b/python/core/symbology-ng/qgssinglesymbolrenderer.sip index fa162e1069d..11bcb202a92 100644 --- a/python/core/symbology-ng/qgssinglesymbolrenderer.sip +++ b/python/core/symbology-ng/qgssinglesymbolrenderer.sip @@ -19,7 +19,7 @@ class QgsSingleSymbolRenderer : QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ); - virtual QList usedAttributes(); + virtual QSet usedAttributes() const; QgsSymbol* symbol() const; void setSymbol( QgsSymbol* s /Transfer/ ); diff --git a/python/gui/qgspanelwidgetstack.sip b/python/gui/qgspanelwidgetstack.sip index e13357945f6..17566eb0410 100644 --- a/python/gui/qgspanelwidgetstack.sip +++ b/python/gui/qgspanelwidgetstack.sip @@ -19,26 +19,33 @@ class QgsPanelWidgetStack: public QWidget QgsPanelWidgetStack( QWidget* parent = nullptr ); /** - * Adds the main widget to the stack and selects it for the user + * Adds the main panel widget to the stack and selects it for the user * The main widget can not be closed and only the showPanel signal is attached * to handle children widget opening panels. * @param panel The panel to set as the first widget in the stack. + * @note a stack can have only one main panel. Any existing main panel + * should be removed by first calling takeMainPanel(). + * @see mainPanel() + * @see takeMainPanel() */ - void addMainPanel( QgsPanelWidget* panel ); + void setMainPanel( QgsPanelWidget* panel ); /** - * The main widget that is set in the stack. The main widget can not be closed + * The main panel widget that is set in the stack. The main widget can not be closed * and doesn't display a back button. * @return The main QgsPanelWidget that is active in the stack. + * @see setMainPanel() */ - QgsPanelWidget* mainWidget(); + QgsPanelWidget* mainPanel(); /** - * Removes the main widget from the stack and transfers ownsership to the + * Removes the main panel widget from the stack and transfers ownsership to the * caller. * @return The main widget that is set in the stack. + * @see mainPanel() + * @see setMainPanel() */ - QgsPanelWidget* takeMainWidget(); + QgsPanelWidget* takeMainPanel(); /** * Clear the stack of all widgets. Unless the panels autoDelete is set to false @@ -46,15 +53,29 @@ class QgsPanelWidgetStack: public QWidget */ void clear(); + /** + * Returns the panel currently shown in the stack. + * @note added in QGIS 3.0 + */ + QgsPanelWidget* currentPanel(); public slots: /** * Accept the current active widget in the stack. * * Calls the panelAccepeted signal on the active widget. + * @see acceptAllPanels() */ void acceptCurrentPanel(); + /** + * Accepts all panel widgets open in the stack in turn until until only the mainPanel() + * remains. + * @see acceptCurrentPanel(); + * @note added in QGIS 3.0 + */ + void acceptAllPanels(); + /** * Show a panel in the stack widget. Will connect to the panels showPanel event to handle * nested panels. Auto switches the the given panel for the user. diff --git a/python/plugins/GdalTools/tools/doRasterize.py b/python/plugins/GdalTools/tools/doRasterize.py index b682da930f9..9b88dabf2b5 100644 --- a/python/plugins/GdalTools/tools/doRasterize.py +++ b/python/plugins/GdalTools/tools/doRasterize.py @@ -153,6 +153,10 @@ class GdalToolsDialog(QWidget, Ui_Widget, BasePluginWidget): self.inSelector.setLayer(None) return - ncodec = QTextCodec.codecForName(self.lastEncoding) + # GDAL Python3 bindings return fields as str and not bytes + # so no recoding is needed. But this assumes that the underlying + # OGR driver always return a Unicode string. hum... + #ncodec = QTextCodec.codecForName(self.lastEncoding) for name in names: - self.attributeComboBox.addItem(ncodec.toUnicode(name)) + self.attributeComboBox.addItem(name) + #self.attributeComboBox.addItem(ncodec.toUnicode(name)) diff --git a/python/plugins/MetaSearch/pavement.py b/python/plugins/MetaSearch/pavement.py index 8a4f4293697..10abad95897 100644 --- a/python/plugins/MetaSearch/pavement.py +++ b/python/plugins/MetaSearch/pavement.py @@ -49,7 +49,7 @@ options( home=BASEDIR, plugin=path(BASEDIR), ui=path(BASEDIR) / 'plugin' / PLUGIN_NAME / 'ui', - install=path('%s/.qgis2/python/plugins/MetaSearch' % USERDIR), + install=path('%s/.qgis3/python/plugins/MetaSearch' % USERDIR), ext_libs=path('plugin/MetaSearch/ext-libs'), tmp=path(path('%s/MetaSearch-dist' % USERDIR)), version=VERSION @@ -86,7 +86,7 @@ def clean(): def install(): """install plugin into user QGIS environment""" - plugins_dir = path(USERDIR) / '.qgis2/python/plugins' + plugins_dir = path(USERDIR) / '.qgis3/python/plugins' if os.path.exists(options.base.install): if os.path.islink(options.base.install): diff --git a/python/plugins/processing/algs/gdal/warp.py b/python/plugins/processing/algs/gdal/warp.py index e729d19af85..7040e11da99 100644 --- a/python/plugins/processing/algs/gdal/warp.py +++ b/python/plugins/processing/algs/gdal/warp.py @@ -88,7 +88,7 @@ class warp(GdalAlgorithm): 0.0, None, 0.0)) self.addParameter(ParameterSelection(self.METHOD, self.tr('Resampling method'), self.METHOD_OPTIONS)) - self.addParameter(ParameterExtent(self.RAST_EXT, self.tr('Raster extent'))) + self.addParameter(ParameterExtent(self.RAST_EXT, self.tr('Raster extent'), optional=True)) if GdalUtils.version() >= 2000000: self.addParameter(ParameterCrs(self.EXT_CRS, diff --git a/python/plugins/processing/algs/otb/maintenance/README b/python/plugins/processing/algs/otb/maintenance/README index ba7c6122925..487759026cd 100644 --- a/python/plugins/processing/algs/otb/maintenance/README +++ b/python/plugins/processing/algs/otb/maintenance/README @@ -19,7 +19,7 @@ export LD_LIBRARY_PATH=/path/to/OTB/install/lib/:$LD_LIBRARY_PATH Set QGIS environment --------------------- export QGIS_PREFIX_PATH=/path/to/QGIS/install -export PYTHONPATH=:/usr/share/qgis/python/plugins:~/.qgis2/python/plugins:$PYTHONPATH +export PYTHONPATH=:/usr/share/qgis/python/plugins:~/.qgis3/python/plugins:$PYTHONPATH # Set LD_LIBRARY_PATH export LD_LIBRARY_PATH=$QGIS_PREFIX_PATH/lib/:$LD_LIBRARY_PATH # Add maintenance folder to python path diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index a8d32091592..e5f0ebe5e2d 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -37,7 +37,7 @@ from osgeo.gdalconst import GA_ReadOnly from numpy import nan_to_num from qgis.PyQt.QtCore import QCoreApplication, QMetaObject -from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QTextEdit +from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QTextEdit, QMessageBox from processing.core.Processing import Processing from processing.core.outputs import ( @@ -232,6 +232,15 @@ def createTest(text): if isinstance(out, (OutputNumber, OutputString)): results[out.name] = str(out) elif isinstance(out, OutputRaster): + if token is None: + QMessageBox.warning(None, + tr('Error'), + tr('Seems some outputs are temporary ' + 'files. To create test you need to ' + 'redirect all algorithm outputs to ' + 'files')) + return + dataset = gdal.Open(token, GA_ReadOnly) dataArray = nan_to_num(dataset.ReadAsArray(0)) strhash = hashlib.sha224(dataArray.data).hexdigest() diff --git a/python/plugins/processing/tools/postgis.py b/python/plugins/processing/tools/postgis.py index 1ac4441f0b5..4140ac63437 100644 --- a/python/plugins/processing/tools/postgis.py +++ b/python/plugins/processing/tools/postgis.py @@ -186,6 +186,7 @@ class GeoDB(object): passwd=None, service=None, uri=None): # Regular expression for identifiers without need to quote them self.re_ident_ok = re.compile(r"^\w+$") + port = str(port) if uri: self.uri = uri @@ -199,7 +200,7 @@ class GeoDB(object): conninfo = self.uri.connectionInfo(False) err = None for i in range(4): - expandedConnInfo = uri.connectionInfo(True) + expandedConnInfo = self.uri.connectionInfo(True) try: self.con = psycopg2.connect(expandedConnInfo.encode('utf-8')) if err is not None: diff --git a/python/plugins/processing/tools/vector.py b/python/plugins/processing/tools/vector.py index 169fe6d78ba..d8b135b8582 100644 --- a/python/plugins/processing/tools/vector.py +++ b/python/plugins/processing/tools/vector.py @@ -52,17 +52,6 @@ from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecution from processing.tools import dataobjects, spatialite, postgis -GEOM_TYPE_MAP = { - QgsWkbTypes.NullGeometry: 'none', - QgsWkbTypes.Point: 'Point', - QgsWkbTypes.LineString: 'LineString', - QgsWkbTypes.Polygon: 'Polygon', - QgsWkbTypes.MultiPoint: 'MultiPoint', - QgsWkbTypes.MultiLineString: 'MultiLineString', - QgsWkbTypes.MultiPolygon: 'MultiPolygon', -} - - TYPE_MAP = { str: QVariant.String, float: QVariant.Double, @@ -561,7 +550,7 @@ class VectorWriter(object): if self.destination.startswith(self.MEMORY_LAYER_PREFIX): self.isNotFileBased = True - uri = GEOM_TYPE_MAP[geometryType] + "?uuid=" + str(uuid.uuid4()) + uri = QgsWkbTypes.displayString(geometryType) + "?uuid=" + str(uuid.uuid4()) if crs.isValid(): uri += '&crs=' + crs.authid() fieldsdesc = [] @@ -608,7 +597,7 @@ class VectorWriter(object): if geometryType != QgsWkbTypes.NullGeometry: _runSQL("SELECT AddGeometryColumn('{schema}', '{table}', 'the_geom', {srid}, '{typmod}', 2)".format( table=uri.table().lower(), schema=uri.schema(), srid=crs.authid().split(":")[-1], - typmod=GEOM_TYPE_MAP[geometryType].upper())) + typmod=QgsWkbTypes.displayString(geometryType).upper())) self.layer = QgsVectorLayer(uri.uri(), uri.table(), "postgres") self.writer = self.layer.dataProvider() @@ -640,7 +629,7 @@ class VectorWriter(object): if geometryType != QgsWkbTypes.NullGeometry: _runSQL("SELECT AddGeometryColumn('{table}', 'the_geom', {srid}, '{typmod}', 2)".format( table=uri.table().lower(), srid=crs.authid().split(":")[-1], - typmod=GEOM_TYPE_MAP[geometryType].upper())) + typmod=QgsWkbTypes.displayString(geometryType).upper())) self.layer = QgsVectorLayer(uri.uri(), uri.table(), "spatialite") self.writer = self.layer.dataProvider() diff --git a/resources/context_help/HeatmapGui b/resources/context_help/HeatmapGui index e722f10e928..612d8d28347 100644 --- a/resources/context_help/HeatmapGui +++ b/resources/context_help/HeatmapGui @@ -26,7 +26,7 @@ clustering of points.

    Rows and Columns

    Used to change the dimensions of the output raster file. These values are also linked to the Cell size X and Cell size Y values. -Increasing the number of rows or colums will decrease the cell size and increase the file size of the output file. The values in Rows and Columns +Increasing the number of rows or columns will decrease the cell size and increase the file size of the output file. The values in Rows and Columns are also linked, so doubling the number of rows will automatically double the number of columns and the cell sizes will also be halved. The geographical area of the output raster will remain the same!

    Cell size X and Y

    diff --git a/resources/context_help/PythonConsole b/resources/context_help/PythonConsole index 041f2ad0394..1554b9d227c 100644 --- a/resources/context_help/PythonConsole +++ b/resources/context_help/PythonConsole @@ -52,7 +52,7 @@ uncomment code, check syntax, share the code via codepad.org and much more).
  • Open PyQGIS Cookbook by typing .

  • Save and clear the command history accessing from context menu of input pane. -The history will be saved into the file ~/.qgis2/console_history.txt
  • +The history will be saved into the file ~/.qgis3/console_history.txt
    diff --git a/scripts/mkuidefaults.py b/scripts/mkuidefaults.py index 1a3ad6c7de0..223bf79d4e2 100644 --- a/scripts/mkuidefaults.py +++ b/scripts/mkuidefaults.py @@ -32,7 +32,7 @@ def chunks(l, n): QCoreApplication.setOrganizationName("QGIS") QCoreApplication.setOrganizationDomain("qgis.org") -QCoreApplication.setApplicationName("QGIS2") +QCoreApplication.setApplicationName("QGIS3") s = QSettings() diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index e3580471708..5da66b09cbe 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -167,7 +167,7 @@ ELSE(NOT APPLE) FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis2_analysis + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_analysis BUILD_WITH_INSTALL_RPATH TRUE PUBLIC_HEADER "${QGIS_ANALYSIS_HDRS}" LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" diff --git a/src/analysis/network/CMakeLists.txt b/src/analysis/network/CMakeLists.txt index 2a83a23c817..e59d7c21d41 100644 --- a/src/analysis/network/CMakeLists.txt +++ b/src/analysis/network/CMakeLists.txt @@ -61,7 +61,7 @@ ELSE(NOT APPLE) FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis2_networkanalysis + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_networkanalysis BUILD_WITH_INSTALL_RPATH TRUE PUBLIC_HEADER "${QGIS_NETWORK_ANALYSIS_HDRS};${QGIS_NETWORK_ANALYSIS_MOC_HDRS}" LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index aaab88c269e..8907d907a27 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -28,6 +28,7 @@ SET(QGIS_APP_SRCS qgsdecorationscalebardialog.cpp qgsdecorationgrid.cpp qgsdecorationgriddialog.cpp + qgsdiscoverrelationsdlg.cpp qgsdxfexportdialog.cpp qgsformannotationdialog.cpp qgsguivectorlayertools.cpp @@ -207,6 +208,7 @@ SET (QGIS_APP_MOC_HDRS qgsdecorationgriddialog.h qgsdelattrdialog.h qgsdiagramproperties.h + qgsdiscoverrelationsdlg.h qgsdisplayangle.h qgsdxfexportdialog.h qgsfeatureaction.h diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index 06444932188..cd55b1f2740 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -67,6 +67,7 @@ #include "qgsvectorlayer.h" #include "qgscomposerimageexportoptionsdialog.h" #include "ui_qgssvgexportoptions.h" +#include "qgspanelwidgetstack.h" #include #include @@ -578,6 +579,8 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title ) mItemDock = new QgsDockWidget( tr( "Item properties" ), this ); mItemDock->setObjectName( "ItemDock" ); mItemDock->setMinimumWidth( minDockWidth ); + mItemPropertiesStack = new QgsPanelWidgetStack(); + mItemDock->setWidget( mItemPropertiesStack ); mPanelMenu->addAction( mItemDock->toggleViewAction() ); mUndoDock = new QgsDockWidget( tr( "Command history" ), this ); mUndoDock->setObjectName( "CommandDock" ); @@ -996,28 +999,27 @@ void QgsComposer::updateStatusAtlasMsg( const QString& message ) void QgsComposer::showItemOptions( QgsComposerItem* item ) { - QWidget* currentWidget = mItemDock->widget(); - if ( !item ) { - mItemDock->setWidget( nullptr ); + mItemPropertiesStack->takeMainPanel(); return; } - QMap::const_iterator it = mItemWidgetMap.constFind( item ); + QMap::const_iterator it = mItemWidgetMap.constFind( item ); if ( it == mItemWidgetMap.constEnd() ) { return; } - QWidget* newWidget = it.value(); - - if ( !newWidget || newWidget == currentWidget ) //bail out if new widget does not exist or is already there + QgsPanelWidget* newWidget = it.value(); + if ( !newWidget || newWidget == mItemPropertiesStack->mainPanel() ) //bail out if new widget does not exist or is already there { return; } - mItemDock->setWidget( newWidget ); + ( void ) mItemPropertiesStack->takeMainPanel(); + newWidget->setDockMode( true ); + mItemPropertiesStack->setMainPanel( newWidget ); } void QgsComposer::on_mActionOptions_triggered() @@ -3774,7 +3776,7 @@ void QgsComposer::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* void QgsComposer::deleteItem( QgsComposerItem* item ) { - QMap::const_iterator it = mItemWidgetMap.constFind( item ); + QMap::const_iterator it = mItemWidgetMap.constFind( item ); if ( it == mItemWidgetMap.constEnd() ) { @@ -3800,7 +3802,7 @@ void QgsComposer::setSelectionTool() bool QgsComposer::containsWmsLayer() const { - QMap::const_iterator item_it = mItemWidgetMap.constBegin(); + QMap::const_iterator item_it = mItemWidgetMap.constBegin(); QgsComposerItem* currentItem = nullptr; QgsComposerMap* currentMap = nullptr; @@ -3822,7 +3824,7 @@ bool QgsComposer::containsWmsLayer() const bool QgsComposer::containsAdvancedEffects() const { // Check if composer contains any blend modes or flattened layers for transparency - QMap::const_iterator item_it = mItemWidgetMap.constBegin(); + QMap::const_iterator item_it = mItemWidgetMap.constBegin(); QgsComposerItem* currentItem = nullptr; QgsComposerMap* currentMap = nullptr; @@ -3893,7 +3895,7 @@ void QgsComposer::showAdvancedEffectsWarning() void QgsComposer::cleanupAfterTemplateRead() { - QMap::const_iterator itemIt = mItemWidgetMap.constBegin(); + QMap::const_iterator itemIt = mItemWidgetMap.constBegin(); for ( ; itemIt != mItemWidgetMap.constEnd(); ++itemIt ) { //update all legends completely diff --git a/src/app/composer/qgscomposer.h b/src/app/composer/qgscomposer.h index 05043a3a4f6..664d693ff93 100644 --- a/src/app/composer/qgscomposer.h +++ b/src/app/composer/qgscomposer.h @@ -18,6 +18,7 @@ #define QGSCOMPOSER_H #include "ui_qgscomposerbase.h" +#include "qgspanelwidget.h" class QgisApp; class QgsComposerArrow; class QgsComposerPolygon; @@ -44,6 +45,7 @@ class QgsDockWidget; class QgsMapLayer; class QgsFeature; class QgsVectorLayer; +class QgsPanelWidgetStack; class QGridLayout; class QDomNode; @@ -568,7 +570,7 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase QSizeGrip *mSizeGrip; //! To know which item to show if selection changes - QMap mItemWidgetMap; + QMap mItemWidgetMap; //! Window menu action to select this window QAction *mWindowAction; @@ -597,6 +599,7 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase QMap< QgsComposerMap*, int > mMapsToRestore; QgsDockWidget* mItemDock; + QgsPanelWidgetStack* mItemPropertiesStack; QgsDockWidget* mUndoDock; QgsDockWidget* mGeneralDock; QgsDockWidget* mAtlasDock; diff --git a/src/app/composer/qgscomposerarrowwidget.cpp b/src/app/composer/qgscomposerarrowwidget.cpp index 47effd16247..abaaa6d6f51 100644 --- a/src/app/composer/qgscomposerarrowwidget.cpp +++ b/src/app/composer/qgscomposerarrowwidget.cpp @@ -28,6 +28,7 @@ QgsComposerArrowWidget::QgsComposerArrowWidget( QgsComposerArrow* arrow ): QgsComposerItemBaseWidget( nullptr, arrow ), mArrow( arrow ) { setupUi( this ); + setPanelTitle( tr( "Arrow properties" ) ); mRadioButtonGroup = new QButtonGroup( this ); mRadioButtonGroup->addButton( mDefaultMarkerRadioButton ); mRadioButtonGroup->addButton( mNoMarkerRadioButton ); @@ -98,7 +99,7 @@ void QgsComposerArrowWidget::on_mArrowHeadFillColorButton_colorChanged( const QC return; } - mArrow->beginCommand( tr( "Arrow head fill color" ) ); + mArrow->beginCommand( tr( "Arrow head fill color" ), QgsComposerMergeCommand::ArrowHeadFillColor ); mArrow->setArrowHeadFillColor( newColor ); mArrow->update(); mArrow->endCommand(); @@ -111,7 +112,7 @@ void QgsComposerArrowWidget::on_mArrowHeadOutlineColorButton_colorChanged( const return; } - mArrow->beginCommand( tr( "Arrow head outline color" ) ); + mArrow->beginCommand( tr( "Arrow head outline color" ), QgsComposerMergeCommand::ArrowHeadOutlineColor ); mArrow->setArrowHeadOutlineColor( newColor ); mArrow->update(); mArrow->endCommand(); diff --git a/src/app/composer/qgscomposerattributetablewidget.cpp b/src/app/composer/qgscomposerattributetablewidget.cpp index 963a128d587..a2a264e0b96 100644 --- a/src/app/composer/qgscomposerattributetablewidget.cpp +++ b/src/app/composer/qgscomposerattributetablewidget.cpp @@ -39,6 +39,7 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt , mFrame( frame ) { setupUi( this ); + setPanelTitle( tr( "Table properties" ) ); blockAllSignals( true ); @@ -268,7 +269,7 @@ void QgsComposerAttributeTableWidget::on_mHeaderFontColorButton_colorChanged( co QgsComposition* composition = mComposerTable->composition(); if ( composition ) { - composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font color" ) ); + composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font color" ), QgsComposerMultiFrameMergeCommand::TableHeaderFontColor ); } mComposerTable->setHeaderFontColor( newColor ); if ( composition ) @@ -309,7 +310,7 @@ void QgsComposerAttributeTableWidget::on_mContentFontColorButton_colorChanged( c QgsComposition* composition = mComposerTable->composition(); if ( composition ) { - composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font color" ) ); + composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font color" ), QgsComposerMultiFrameMergeCommand::TableContentFontColor ); } mComposerTable->setContentFontColor( newColor ); if ( composition ) @@ -347,7 +348,7 @@ void QgsComposerAttributeTableWidget::on_mGridColorButton_colorChanged( const QC QgsComposition* composition = mComposerTable->composition(); if ( composition ) { - composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid color" ) ); + composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid color" ), QgsComposerMultiFrameMergeCommand::TableGridColor ); } mComposerTable->setGridColor( newColor ); if ( composition ) @@ -385,7 +386,7 @@ void QgsComposerAttributeTableWidget::on_mBackgroundColorButton_colorChanged( co QgsComposition* composition = mComposerTable->composition(); if ( composition ) { - composition->beginMultiFrameCommand( mComposerTable, tr( "Table background color" ) ); + composition->beginMultiFrameCommand( mComposerTable, tr( "Table background color" ), QgsComposerMultiFrameMergeCommand::TableBackgroundColor ); } mComposerTable->setBackgroundColor( newColor ); if ( composition ) diff --git a/src/app/composer/qgscomposerhtmlwidget.cpp b/src/app/composer/qgscomposerhtmlwidget.cpp index 5c0a904c856..ca4c48eba1a 100644 --- a/src/app/composer/qgscomposerhtmlwidget.cpp +++ b/src/app/composer/qgscomposerhtmlwidget.cpp @@ -31,6 +31,7 @@ QgsComposerHtmlWidget::QgsComposerHtmlWidget( QgsComposerHtml* html, QgsComposer , mFrame( frame ) { setupUi( this ); + setPanelTitle( tr( "HTML properties" ) ); //setup html editor mHtmlEditor = new QgsCodeEditorHTML( this ); diff --git a/src/app/composer/qgscomposeritemwidget.cpp b/src/app/composer/qgscomposeritemwidget.cpp index f699d972372..30393dfdaf9 100644 --- a/src/app/composer/qgscomposeritemwidget.cpp +++ b/src/app/composer/qgscomposeritemwidget.cpp @@ -30,18 +30,20 @@ //QgsComposerItemBaseWidget -QgsComposerItemBaseWidget::QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject *composerObject ): QWidget( parent ), mComposerObject( composerObject ) +QgsComposerConfigObject::QgsComposerConfigObject( QWidget* parent, QgsComposerObject *composerObject ) + : QObject( parent ) + , mComposerObject( composerObject ) { connect( atlasComposition(), SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateDataDefinedButtons() ) ); connect( atlasComposition(), SIGNAL( toggled( bool ) ), this, SLOT( updateDataDefinedButtons() ) ); } -QgsComposerItemBaseWidget::~QgsComposerItemBaseWidget() +QgsComposerConfigObject::~QgsComposerConfigObject() { } -void QgsComposerItemBaseWidget::updateDataDefinedProperty() +void QgsComposerConfigObject::updateDataDefinedProperty() { //match data defined button to item's data defined property QgsDataDefinedButton* ddButton = dynamic_cast( sender() ); @@ -64,7 +66,7 @@ void QgsComposerItemBaseWidget::updateDataDefinedProperty() mComposerObject->refreshDataDefinedProperty( property ); } -void QgsComposerItemBaseWidget::updateDataDefinedButtons() +void QgsComposerConfigObject::updateDataDefinedButtons() { Q_FOREACH ( QgsDataDefinedButton* button, findChildren< QgsDataDefinedButton* >() ) { @@ -72,7 +74,7 @@ void QgsComposerItemBaseWidget::updateDataDefinedButtons() } } -void QgsComposerItemBaseWidget::setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p ) +void QgsComposerConfigObject::setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p ) { if ( !mComposerObject ) { @@ -83,7 +85,7 @@ void QgsComposerItemBaseWidget::setDataDefinedProperty( const QgsDataDefinedButt mComposerObject->setDataDefinedProperty( p, map.value( "active" ).toInt(), map.value( "useexpr" ).toInt(), map.value( "expression" ), map.value( "field" ) ); } -void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property, +void QgsComposerConfigObject::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property, QgsDataDefinedButton::DataType type, const QString& description ) { button->blockSignals( true ); @@ -98,7 +100,7 @@ void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton* button->blockSignals( false ); } -QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const +QgsAtlasComposition* QgsComposerConfigObject::atlasComposition() const { if ( !mComposerObject ) { @@ -115,7 +117,7 @@ QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const return &composition->atlasComposition(); } -QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const +QgsVectorLayer* QgsComposerConfigObject::atlasCoverageLayer() const { QgsAtlasComposition* atlasMap = atlasComposition(); @@ -140,8 +142,9 @@ void QgsComposerItemWidget::updateVariables() } QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* item ) - : QgsComposerItemBaseWidget( parent, item ) + : QWidget( parent ) , mItem( item ) + , mConfigObject( new QgsComposerConfigObject( this, item ) ) , mFreezeXPosSpin( false ) , mFreezeYPosSpin( false ) , mFreezeWidthSpin( false ) @@ -184,18 +187,6 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* connect( mItem->composition(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) ); } -QgsComposerItemWidget::QgsComposerItemWidget() - : QgsComposerItemBaseWidget( nullptr, nullptr ) - , mItem( nullptr ) - , mFreezeXPosSpin( false ) - , mFreezeYPosSpin( false ) - , mFreezeWidthSpin( false ) - , mFreezeHeightSpin( false ) - , mFreezePageSpin( false ) -{ - -} - QgsComposerItemWidget::~QgsComposerItemWidget() { @@ -219,7 +210,7 @@ void QgsComposerItemWidget::on_mFrameColorButton_colorChanged( const QColor& new { return; } - mItem->beginCommand( tr( "Frame color changed" ) ); + mItem->beginCommand( tr( "Frame color changed" ), QgsComposerMergeCommand::ItemOutlineColor ); mItem->setFrameOutlineColor( newFrameColor ); mItem->update(); mItem->endCommand(); @@ -239,7 +230,7 @@ void QgsComposerItemWidget::on_mBackgroundColorButton_colorChanged( const QColor { return; } - mItem->beginCommand( tr( "Background color changed" ) ); + mItem->beginCommand( tr( "Background color changed" ), QgsComposerMergeCommand::ItemBackgroundColor ); mItem->setBackgroundColor( newBackgroundColor ); //if the item is a composer map, we need to regenerate the map image @@ -552,22 +543,22 @@ void QgsComposerItemWidget::setValuesForGuiNonPositionElements() void QgsComposerItemWidget::populateDataDefinedButtons() { - registerDataDefinedButton( mXPositionDDBtn, QgsComposerObject::PositionX, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); - registerDataDefinedButton( mYPositionDDBtn, QgsComposerObject::PositionY, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); - registerDataDefinedButton( mWidthDDBtn, QgsComposerObject::ItemWidth, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); - registerDataDefinedButton( mHeightDDBtn, QgsComposerObject::ItemHeight, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); - registerDataDefinedButton( mItemRotationDDBtn, QgsComposerObject::ItemRotation, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() ); - registerDataDefinedButton( mTransparencyDDBtn, QgsComposerObject::Transparency, - QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() ); - registerDataDefinedButton( mBlendModeDDBtn, QgsComposerObject::BlendMode, - QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() ); - registerDataDefinedButton( mExcludePrintsDDBtn, QgsComposerObject::ExcludeFromExports, - QgsDataDefinedButton::String, QgsDataDefinedButton::boolDesc() ); + mConfigObject->registerDataDefinedButton( mXPositionDDBtn, QgsComposerObject::PositionX, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); + mConfigObject->registerDataDefinedButton( mYPositionDDBtn, QgsComposerObject::PositionY, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); + mConfigObject->registerDataDefinedButton( mWidthDDBtn, QgsComposerObject::ItemWidth, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); + mConfigObject->registerDataDefinedButton( mHeightDDBtn, QgsComposerObject::ItemHeight, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() ); + mConfigObject->registerDataDefinedButton( mItemRotationDDBtn, QgsComposerObject::ItemRotation, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() ); + mConfigObject->registerDataDefinedButton( mTransparencyDDBtn, QgsComposerObject::Transparency, + QgsDataDefinedButton::AnyType, QgsDataDefinedButton::intTranspDesc() ); + mConfigObject->registerDataDefinedButton( mBlendModeDDBtn, QgsComposerObject::BlendMode, + QgsDataDefinedButton::String, QgsDataDefinedButton::blendModesDesc() ); + mConfigObject->registerDataDefinedButton( mExcludePrintsDDBtn, QgsComposerObject::ExcludeFromExports, + QgsDataDefinedButton::String, QgsDataDefinedButton::boolDesc() ); } void QgsComposerItemWidget::setValuesForGuiElements() @@ -786,3 +777,25 @@ void QgsComposerItemWidget::on_mExcludeFromPrintsCheckBox_toggled( bool checked mItem->endCommand(); } } + +QgsComposerItemBaseWidget::QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject ) + : QgsPanelWidget( parent ) + , mConfigObject( new QgsComposerConfigObject( this, composerObject ) ) +{ + +} + +void QgsComposerItemBaseWidget::registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property, QgsDataDefinedButton::DataType type, const QString& description ) +{ + mConfigObject->registerDataDefinedButton( button, property, type, description ); +} + +QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const +{ + return mConfigObject->atlasCoverageLayer(); +} + +QgsAtlasComposition* QgsComposerItemBaseWidget::atlasComposition() const +{ + return mConfigObject->atlasComposition(); +} diff --git a/src/app/composer/qgscomposeritemwidget.h b/src/app/composer/qgscomposeritemwidget.h index b4004be3205..defdec92514 100644 --- a/src/app/composer/qgscomposeritemwidget.h +++ b/src/app/composer/qgscomposeritemwidget.h @@ -20,29 +20,42 @@ #include "ui_qgscomposeritemwidgetbase.h" #include "qgscomposeritem.h" +#include "qgspanelwidget.h" class QgsComposerItem; class QgsAtlasComposition; class QgsDataDefinedButton; -/** A base class for property widgets for composer items. All composer item widgets should inherit from - * this base class. + +// NOTE - the inheritance here is tricky, as we need to avoid the multiple inheritance +// diamond problem and the ideal base object (QgsComposerConfigObject) MUST be a QObject +// because of its slots. + +// So here we go: +// QgsComposerItemWidget is just a QWidget which is embedded inside specific item property +// widgets and contains common settings like position and rotation of the items. While the +// actual individual item type widgets MUST be QgsPanelWidgets unfortunately QgsComposerItemWidget +// CANNOT be a QgsPanelWidget and must instead be a generic QWidget (otherwise a QgsPanelWidget +// contains a child QgsPanelWidget, which breaks lots of assumptions made in QgsPanelWidget +// and related classes). +// So QgsComposerItemWidget HAS a QgsComposerConfigObject to handle these common tasks. +// Specific item property widgets (eg QgsComposerMapWidget) should inherit from QgsComposerItemBaseWidget +// (which is a QgsPanelWidget) and also HAS a QgsComposerConfigObject, with protected methods +// which are just proxied through to the QgsComposerConfigObject. +// phew! +// long story short - don't change this without good reason. If you add a new item type, inherit +// from QgsComposerItemWidget and trust that everything else has been done for you. + +/** An object for property widgets for composer items. All composer config type widgets should contain + * this object. */ -class QgsComposerItemBaseWidget: public QWidget +class QgsComposerConfigObject: public QObject { Q_OBJECT public: - QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject ); - ~QgsComposerItemBaseWidget(); + QgsComposerConfigObject( QWidget* parent, QgsComposerObject* composerObject ); + ~QgsComposerConfigObject(); - protected slots: - /** Must be called when a data defined button changes*/ - void updateDataDefinedProperty(); - - //! Updates data defined buttons to reflect current state of atlas (eg coverage layer) - void updateDataDefinedButtons(); - - protected: /** Sets a data defined property for the item from its current data defined button settings*/ void setDataDefinedProperty( const QgsDataDefinedButton *ddBtn, QgsComposerObject::DataDefinedProperty p ); @@ -61,12 +74,54 @@ class QgsComposerItemBaseWidget: public QWidget /** Returns the atlas for the composition*/ QgsAtlasComposition *atlasComposition() const; + private slots: + /** Must be called when a data defined button changes*/ + void updateDataDefinedProperty(); + + //! Updates data defined buttons to reflect current state of atlas (eg coverage layer) + void updateDataDefinedButtons(); + + private: + QgsComposerObject* mComposerObject; }; +/** + * A base class for property widgets for composer items. All composer item widgets should inherit from + * this base class. + */ +class QgsComposerItemBaseWidget: public QgsPanelWidget +{ + Q_OBJECT + + public: + QgsComposerItemBaseWidget( QWidget* parent, QgsComposerObject* composerObject ); + + protected: + + /** Registers a data defined button, setting up its initial value, connections and description. + * @param button button to register + * @param property correponding data defined property + * @param type valid data types for button + * @param description user visible description for data defined property + */ + void registerDataDefinedButton( QgsDataDefinedButton* button, QgsComposerObject::DataDefinedProperty property, + QgsDataDefinedButton::DataType type, const QString& description ); + + /** Returns the current atlas coverage layer (if set)*/ + QgsVectorLayer* atlasCoverageLayer() const; + + /** Returns the atlas for the composition*/ + QgsAtlasComposition *atlasComposition() const; + + private: + + QgsComposerConfigObject* mConfigObject; +}; + /** A class to enter generic properties for composer items (e.g. background, outline, frame). This widget can be embedded into other item widgets*/ -class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsComposerItemWidgetBase +class QgsComposerItemWidget: public QWidget, private Ui::QgsComposerItemWidgetBase { Q_OBJECT public: @@ -132,9 +187,9 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo void populateDataDefinedButtons(); private: - QgsComposerItemWidget(); QgsComposerItem* mItem; + QgsComposerConfigObject* mConfigObject; bool mFreezeXPosSpin; bool mFreezeYPosSpin; diff --git a/src/app/composer/qgscomposerlabelwidget.cpp b/src/app/composer/qgscomposerlabelwidget.cpp index 1fd9fdc1807..31d8b80fc96 100644 --- a/src/app/composer/qgscomposerlabelwidget.cpp +++ b/src/app/composer/qgscomposerlabelwidget.cpp @@ -29,6 +29,7 @@ QgsComposerLabelWidget::QgsComposerLabelWidget( QgsComposerLabel* label ): QgsComposerItemBaseWidget( nullptr, label ), mComposerLabel( label ) { setupUi( this ); + setPanelTitle( tr( "Label properties" ) ); //add widget for general composer item properties QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, label ); @@ -124,7 +125,7 @@ void QgsComposerLabelWidget::on_mFontColorButton_colorChanged( const QColor &new return; } - mComposerLabel->beginCommand( tr( "Label color changed" ) ); + mComposerLabel->beginCommand( tr( "Label color changed" ), QgsComposerMergeCommand::ComposerLabelFontColor ); mComposerLabel->setFontColor( newLabelColor ); mComposerLabel->update(); mComposerLabel->endCommand(); diff --git a/src/app/composer/qgscomposerlegendwidget.cpp b/src/app/composer/qgscomposerlegendwidget.cpp index 2cd9e12452f..6bd43017536 100644 --- a/src/app/composer/qgscomposerlegendwidget.cpp +++ b/src/app/composer/qgscomposerlegendwidget.cpp @@ -47,6 +47,7 @@ QgsComposerLegendWidget::QgsComposerLegendWidget( QgsComposerLegend* legend ) , mLegend( legend ) { setupUi( this ); + setPanelTitle( tr( "Legend properties" ) ); // setup icons mAddToolButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) ); @@ -405,7 +406,7 @@ void QgsComposerLegendWidget::on_mFontColorButton_colorChanged( const QColor& ne return; } - mLegend->beginCommand( tr( "Legend font color changed" ) ); + mLegend->beginCommand( tr( "Legend font color changed" ), QgsComposerMergeCommand::LegendFontColor ); mLegend->setFontColor( newFontColor ); mLegend->update(); mLegend->endCommand(); @@ -623,7 +624,7 @@ void QgsComposerLegendWidget::on_mRasterBorderColorButton_colorChanged( const QC return; } - mLegend->beginCommand( tr( "Legend raster border color" ) ); + mLegend->beginCommand( tr( "Legend raster border color" ), QgsComposerMergeCommand::LegendRasterBorderColor ); mLegend->setRasterBorderColor( newColor ); mLegend->update(); mLegend->endCommand(); diff --git a/src/app/composer/qgscomposermapwidget.cpp b/src/app/composer/qgscomposermapwidget.cpp index 753c5f913ab..82d1569c16c 100644 --- a/src/app/composer/qgscomposermapwidget.cpp +++ b/src/app/composer/qgscomposermapwidget.cpp @@ -48,6 +48,7 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ) , mComposerMap( composerMap ) { setupUi( this ); + setPanelTitle( tr( "Map properties" ) ); //add widget for general composer item properties QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerMap ); @@ -291,6 +292,99 @@ void QgsComposerMapWidget::onPresetsChanged() } } +void QgsComposerMapWidget::updateGridLineStyleFromWidget() +{ + QgsComposerMapGrid* grid = currentGrid(); + if ( !grid ) + { + return; + } + + QgsSymbolSelectorWidget* w = qobject_cast( sender() ); + grid->setLineSymbol( dynamic_cast< QgsLineSymbol* >( w->symbol()->clone() ) ); + mComposerMap->update(); +} + +void QgsComposerMapWidget::cleanUpGridLineStyleSelector( QgsPanelWidget* container ) +{ + QgsSymbolSelectorWidget* w = qobject_cast( container ); + if ( !w ) + return; + + delete w->symbol(); + + QgsComposerMapGrid* grid = currentGrid(); + if ( !grid ) + { + return; + } + + updateGridLineSymbolMarker( grid ); + mComposerMap->endCommand(); +} + +void QgsComposerMapWidget::updateGridMarkerStyleFromWidget() +{ + QgsComposerMapGrid* grid = currentGrid(); + if ( !grid ) + { + return; + } + + QgsSymbolSelectorWidget* w = qobject_cast( sender() ); + grid->setMarkerSymbol( dynamic_cast< QgsMarkerSymbol* >( w->symbol()->clone() ) ); + mComposerMap->update(); +} + +void QgsComposerMapWidget::cleanUpGridMarkerStyleSelector( QgsPanelWidget* container ) +{ + QgsSymbolSelectorWidget* w = qobject_cast( container ); + if ( !w ) + return; + + delete w->symbol(); + + QgsComposerMapGrid* grid = currentGrid(); + if ( !grid ) + { + return; + } + + updateGridMarkerSymbolMarker( grid ); + mComposerMap->endCommand(); +} + +void QgsComposerMapWidget::updateOverviewFrameStyleFromWidget() +{ + QgsComposerMapOverview* overview = currentOverview(); + if ( !overview ) + { + return; + } + + QgsSymbolSelectorWidget* w = qobject_cast( sender() ); + overview->setFrameSymbol( dynamic_cast< QgsFillSymbol* >( w->symbol()->clone() ) ); + mComposerMap->update(); +} + +void QgsComposerMapWidget::cleanUpOverviewFrameStyleSelector( QgsPanelWidget* container ) +{ + QgsSymbolSelectorWidget* w = qobject_cast( container ); + if ( !w ) + return; + + delete w->symbol(); + + QgsComposerMapOverview* overview = currentOverview(); + if ( !overview ) + { + return; + } + + updateOverviewFrameSymbolMarker( overview ); + mComposerMap->endCommand(); +} + void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked ) { if ( !mComposerMap ) @@ -1495,21 +1589,21 @@ void QgsComposerMapWidget::on_mGridLineStyleButton_clicked() return; } - QgsLineSymbol* newSymbol = static_cast( grid->lineSymbol()->clone() ); - QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), nullptr, this ); + // use the atlas coverage layer, if any + QgsVectorLayer* coverageLayer = atlasCoverageLayer(); - if ( d.exec() == QDialog::Accepted ) - { - mComposerMap->beginCommand( tr( "Grid line style changed" ) ); - grid->setLineSymbol( newSymbol ); - updateGridLineSymbolMarker( grid ); - mComposerMap->endCommand(); - mComposerMap->update(); - } - else - { - delete newSymbol; - } + QgsLineSymbol* newSymbol = static_cast( grid->lineSymbol()->clone() ); + QgsExpressionContext context = mComposerMap->createExpressionContext(); + + QgsSymbolSelectorWidget* d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr ); + QgsSymbolWidgetContext symbolContext; + symbolContext.setExpressionContext( &context ); + d->setContext( symbolContext ); + + connect( d, SIGNAL( widgetChanged() ), this, SLOT( updateGridLineStyleFromWidget() ) ); + connect( d, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( cleanUpGridLineStyleSelector( QgsPanelWidget* ) ) ); + openPanel( d ); + mComposerMap->beginCommand( tr( "Grid line style changed" ) ); } void QgsComposerMapWidget::on_mGridMarkerStyleButton_clicked() @@ -1520,21 +1614,21 @@ void QgsComposerMapWidget::on_mGridMarkerStyleButton_clicked() return; } - QgsMarkerSymbol* newSymbol = static_cast( grid->markerSymbol()->clone() ); - QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), nullptr, this ); + // use the atlas coverage layer, if any + QgsVectorLayer* coverageLayer = atlasCoverageLayer(); - if ( d.exec() == QDialog::Accepted ) - { - mComposerMap->beginCommand( tr( "Grid markers style changed" ) ); - grid->setMarkerSymbol( newSymbol ); - updateGridMarkerSymbolMarker( grid ); - mComposerMap->endCommand(); - mComposerMap->update(); - } - else - { - delete newSymbol; - } + QgsMarkerSymbol* newSymbol = static_cast( grid->markerSymbol()->clone() ); + QgsExpressionContext context = mComposerMap->createExpressionContext(); + + QgsSymbolSelectorWidget* d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr ); + QgsSymbolWidgetContext symbolContext; + symbolContext.setExpressionContext( &context ); + d->setContext( symbolContext ); + + connect( d, SIGNAL( widgetChanged() ), this, SLOT( updateGridMarkerStyleFromWidget() ) ); + connect( d, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( cleanUpGridMarkerStyleSelector( QgsPanelWidget* ) ) ); + openPanel( d ); + mComposerMap->beginCommand( tr( "Grid markers style changed" ) ); } void QgsComposerMapWidget::on_mIntervalXSpinBox_editingFinished() @@ -1729,7 +1823,7 @@ void QgsComposerMapWidget::on_mGridFramePenColorButton_colorChanged( const QColo return; } - mComposerMap->beginCommand( tr( "Grid frame color changed" ) ); + mComposerMap->beginCommand( tr( "Grid frame color changed" ), QgsComposerMergeCommand::ComposerMapGridFramePenColor ); grid->setFramePenColor( newColor ); mComposerMap->update(); mComposerMap->endCommand(); @@ -1743,7 +1837,7 @@ void QgsComposerMapWidget::on_mGridFrameFill1ColorButton_colorChanged( const QCo return; } - mComposerMap->beginCommand( tr( "Grid frame first fill color changed" ) ); + mComposerMap->beginCommand( tr( "Grid frame first fill color changed" ), QgsComposerMergeCommand::ComposerMapGridFrameFill1Color ); grid->setFrameFillColor1( newColor ); mComposerMap->update(); mComposerMap->endCommand(); @@ -1757,7 +1851,7 @@ void QgsComposerMapWidget::on_mGridFrameFill2ColorButton_colorChanged( const QCo return; } - mComposerMap->beginCommand( tr( "Grid frame second fill color changed" ) ); + mComposerMap->beginCommand( tr( "Grid frame second fill color changed" ), QgsComposerMergeCommand::ComposerMapGridFrameFill2Color ); grid->setFrameFillColor2( newColor ); mComposerMap->update(); mComposerMap->endCommand(); @@ -2076,7 +2170,7 @@ void QgsComposerMapWidget::on_mAnnotationFontColorButton_colorChanged( const QCo return; } - mComposerMap->beginCommand( tr( "Annotation color changed" ) ); + mComposerMap->beginCommand( tr( "Annotation color changed" ), QgsComposerMergeCommand::ComposerMapGridAnnotationFontColor ); grid->setAnnotationFontColor( color ); mComposerMap->update(); mComposerMap->endCommand(); @@ -2462,21 +2556,21 @@ void QgsComposerMapWidget::on_mOverviewFrameStyleButton_clicked() return; } - QgsFillSymbol* newSymbol = static_cast( overview->frameSymbol()->clone() ); - QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), nullptr, this ); + // use the atlas coverage layer, if any + QgsVectorLayer* coverageLayer = atlasCoverageLayer(); - if ( d.exec() == QDialog::Accepted ) - { - mComposerMap->beginCommand( tr( "Overview frame style changed" ) ); - overview->setFrameSymbol( newSymbol ); - updateOverviewFrameSymbolMarker( overview ); - mComposerMap->endCommand(); - mComposerMap->update(); - } - else - { - delete newSymbol; - } + QgsFillSymbol* newSymbol = static_cast( overview->frameSymbol()->clone() ); + QgsExpressionContext context = mComposerMap->createExpressionContext(); + + QgsSymbolSelectorWidget* d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr ); + QgsSymbolWidgetContext symbolContext; + symbolContext.setExpressionContext( &context ); + d->setContext( symbolContext ); + + connect( d, SIGNAL( widgetChanged() ), this, SLOT( updateOverviewFrameStyleFromWidget() ) ); + connect( d, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( cleanUpOverviewFrameStyleSelector( QgsPanelWidget* ) ) ); + openPanel( d ); + mComposerMap->beginCommand( tr( "Overview frame style changed" ) ); } void QgsComposerMapWidget::on_mOverviewBlendModeComboBox_currentIndexChanged( int index ) diff --git a/src/app/composer/qgscomposermapwidget.h b/src/app/composer/qgscomposermapwidget.h index a02a16c3232..727bc4cb347 100644 --- a/src/app/composer/qgscomposermapwidget.h +++ b/src/app/composer/qgscomposermapwidget.h @@ -174,6 +174,13 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom void onPresetsChanged(); + void updateGridLineStyleFromWidget(); + void cleanUpGridLineStyleSelector( QgsPanelWidget* container ); + void updateGridMarkerStyleFromWidget(); + void cleanUpGridMarkerStyleSelector( QgsPanelWidget* container ); + void updateOverviewFrameStyleFromWidget(); + void cleanUpOverviewFrameStyleSelector( QgsPanelWidget* container ); + private: QgsComposerMap* mComposerMap; diff --git a/src/app/composer/qgscomposerpicturewidget.cpp b/src/app/composer/qgscomposerpicturewidget.cpp index b1087196108..cc055d8de15 100644 --- a/src/app/composer/qgscomposerpicturewidget.cpp +++ b/src/app/composer/qgscomposerpicturewidget.cpp @@ -36,6 +36,7 @@ QgsComposerPictureWidget::QgsComposerPictureWidget( QgsComposerPicture* picture ): QgsComposerItemBaseWidget( nullptr, picture ), mPicture( picture ), mPreviewsLoaded( false ) { setupUi( this ); + setPanelTitle( tr( "Picture properties" ) ); mFillColorButton->setAllowAlpha( true ); mFillColorButton->setColorDialogTitle( tr( "Select fill color" ) ); @@ -632,7 +633,7 @@ void QgsComposerPictureWidget::loadPicturePreviews( bool collapsed ) void QgsComposerPictureWidget::on_mFillColorButton_colorChanged( const QColor& color ) { - mPicture->beginCommand( tr( "Picture fill color changed" ) ); + mPicture->beginCommand( tr( "Picture fill color changed" ), QgsComposerMergeCommand::ComposerPictureFillColor ); mPicture->setSvgFillColor( color ); mPicture->endCommand(); mPicture->update(); @@ -640,7 +641,7 @@ void QgsComposerPictureWidget::on_mFillColorButton_colorChanged( const QColor& c void QgsComposerPictureWidget::on_mOutlineColorButton_colorChanged( const QColor& color ) { - mPicture->beginCommand( tr( "Picture border color changed" ) ); + mPicture->beginCommand( tr( "Picture border color changed" ), QgsComposerMergeCommand::ComposerPictureOutlineColor ); mPicture->setSvgBorderColor( color ); mPicture->endCommand(); mPicture->update(); diff --git a/src/app/composer/qgscomposerpolygonwidget.cpp b/src/app/composer/qgscomposerpolygonwidget.cpp index 8a0832814a8..cf20635f5a5 100644 --- a/src/app/composer/qgscomposerpolygonwidget.cpp +++ b/src/app/composer/qgscomposerpolygonwidget.cpp @@ -26,6 +26,7 @@ QgsComposerPolygonWidget::QgsComposerPolygonWidget( QgsComposerPolygon* composer , mComposerPolygon( composerPolygon ) { setupUi( this ); + setPanelTitle( tr( "Polygon properties" ) ); //add widget for general composer item properties QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerPolygon ); diff --git a/src/app/composer/qgscomposerpolylinewidget.cpp b/src/app/composer/qgscomposerpolylinewidget.cpp index f16bef6c823..5e0777713ca 100644 --- a/src/app/composer/qgscomposerpolylinewidget.cpp +++ b/src/app/composer/qgscomposerpolylinewidget.cpp @@ -26,6 +26,7 @@ QgsComposerPolylineWidget::QgsComposerPolylineWidget( QgsComposerPolyline* compo , mComposerPolyline( composerPolyline ) { setupUi( this ); + setPanelTitle( tr( "Polyline properties" ) ); //add widget for general composer item properties QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerPolyline ); diff --git a/src/app/composer/qgscomposerscalebarwidget.cpp b/src/app/composer/qgscomposerscalebarwidget.cpp index 4992ddcbd70..102b6dc1dff 100644 --- a/src/app/composer/qgscomposerscalebarwidget.cpp +++ b/src/app/composer/qgscomposerscalebarwidget.cpp @@ -27,6 +27,8 @@ QgsComposerScaleBarWidget::QgsComposerScaleBarWidget( QgsComposerScaleBar* scaleBar ): QgsComposerItemBaseWidget( nullptr, scaleBar ), mComposerScaleBar( scaleBar ) { setupUi( this ); + setPanelTitle( tr( "Scalebar properties" ) ); + connectUpdateSignal(); //add widget for general composer item properties @@ -261,7 +263,7 @@ void QgsComposerScaleBarWidget::on_mFontColorButton_colorChanged( const QColor& return; } - mComposerScaleBar->beginCommand( tr( "Scalebar font color changed" ) ); + mComposerScaleBar->beginCommand( tr( "Scalebar font color changed" ), QgsComposerMergeCommand::ScaleBarFontColor ); disconnectUpdateSignal(); mComposerScaleBar->setFontColor( newColor ); mComposerScaleBar->update(); @@ -276,7 +278,7 @@ void QgsComposerScaleBarWidget::on_mFillColorButton_colorChanged( const QColor& return; } - mComposerScaleBar->beginCommand( tr( "Scalebar color changed" ) ); + mComposerScaleBar->beginCommand( tr( "Scalebar color changed" ), QgsComposerMergeCommand::ScaleBarFillColor ); disconnectUpdateSignal(); QBrush newBrush = mComposerScaleBar->brush(); newBrush.setColor( newColor ); @@ -293,7 +295,7 @@ void QgsComposerScaleBarWidget::on_mFillColor2Button_colorChanged( const QColor return; } - mComposerScaleBar->beginCommand( tr( "Scalebar secondary color changed" ) ); + mComposerScaleBar->beginCommand( tr( "Scalebar secondary color changed" ), QgsComposerMergeCommand::ScaleBarFill2Color ); disconnectUpdateSignal(); QBrush newBrush = mComposerScaleBar->brush2(); newBrush.setColor( newColor ); @@ -310,7 +312,7 @@ void QgsComposerScaleBarWidget::on_mStrokeColorButton_colorChanged( const QColor return; } - mComposerScaleBar->beginCommand( tr( "Scalebar line color changed" ) ); + mComposerScaleBar->beginCommand( tr( "Scalebar line color changed" ), QgsComposerMergeCommand::ScaleBarStrokeColor ); disconnectUpdateSignal(); QPen newPen = mComposerScaleBar->pen(); newPen.setColor( newColor ); diff --git a/src/app/composer/qgscomposershapewidget.cpp b/src/app/composer/qgscomposershapewidget.cpp index fc9770413e4..b076b14cff4 100644 --- a/src/app/composer/qgscomposershapewidget.cpp +++ b/src/app/composer/qgscomposershapewidget.cpp @@ -27,11 +27,12 @@ QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape* composerShape ): QgsComposerItemBaseWidget( nullptr, composerShape ), mComposerShape( composerShape ) { setupUi( this ); + setPanelTitle( tr( "Shape properties" ) ); //add widget for general composer item properties QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, composerShape ); - //shapes don't use background or frame, since the symbol style is set through a QgsSymbolSelectorDialog + //shapes don't use background or frame, since the symbol style is set through a QgsSymbolSelectorWidget itemPropertiesWidget->showBackgroundGroup( false ); itemPropertiesWidget->showFrameGroup( false ); @@ -109,19 +110,16 @@ void QgsComposerShapeWidget::on_mShapeStyleButton_clicked() QgsFillSymbol* newSymbol = mComposerShape->shapeStyleSymbol()->clone(); QgsExpressionContext context = mComposerShape->createExpressionContext(); - QgsSymbolSelectorDialog d( newSymbol, QgsStyle::defaultStyle(), coverageLayer, this ); + + QgsSymbolSelectorWidget* d = new QgsSymbolSelectorWidget( newSymbol, QgsStyle::defaultStyle(), coverageLayer, nullptr ); QgsSymbolWidgetContext symbolContext; symbolContext.setExpressionContext( &context ); - d.setContext( symbolContext ); + d->setContext( symbolContext ); - if ( d.exec() == QDialog::Accepted ) - { - mComposerShape->beginCommand( tr( "Shape style changed" ) ); - mComposerShape->setShapeStyleSymbol( newSymbol ); - updateShapeStyle(); - mComposerShape->endCommand(); - } - delete newSymbol; + connect( d, SIGNAL( widgetChanged() ), this, SLOT( updateSymbolFromWidget() ) ); + connect( d, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( cleanUpSymbolSelector( QgsPanelWidget* ) ) ); + openPanel( d ); + mComposerShape->beginCommand( tr( "Shape style changed" ) ); } void QgsComposerShapeWidget::updateShapeStyle() @@ -182,5 +180,22 @@ void QgsComposerShapeWidget::toggleRadiusSpin( const QString& shapeText ) } } +void QgsComposerShapeWidget::updateSymbolFromWidget() +{ + QgsSymbolSelectorWidget* w = qobject_cast( sender() ); + mComposerShape->setShapeStyleSymbol( dynamic_cast< QgsFillSymbol* >( w->symbol() ) ); +} + +void QgsComposerShapeWidget::cleanUpSymbolSelector( QgsPanelWidget* container ) +{ + QgsSymbolSelectorWidget* w = qobject_cast( container ); + if ( !w ) + return; + + delete w->symbol(); + updateShapeStyle(); + mComposerShape->endCommand(); +} + diff --git a/src/app/composer/qgscomposershapewidget.h b/src/app/composer/qgscomposershapewidget.h index 053af65970c..21b86ea55ba 100644 --- a/src/app/composer/qgscomposershapewidget.h +++ b/src/app/composer/qgscomposershapewidget.h @@ -49,6 +49,8 @@ class QgsComposerShapeWidget: public QgsComposerItemBaseWidget, private Ui::QgsC /** Enables or disables the rounded radius spin box based on shape type*/ void toggleRadiusSpin( const QString& shapeText ); + void updateSymbolFromWidget(); + void cleanUpSymbolSelector( QgsPanelWidget* container ); }; #endif // QGSCOMPOSERSHAPEWIDGET_H diff --git a/src/app/gps/qwtpolar-0.1/qwt_polar_plot.cpp b/src/app/gps/qwtpolar-0.1/qwt_polar_plot.cpp index 623d6ac7480..b2af0cc441e 100644 --- a/src/app/gps/qwtpolar-0.1/qwt_polar_plot.cpp +++ b/src/app/gps/qwtpolar-0.1/qwt_polar_plot.cpp @@ -186,7 +186,7 @@ const QwtTextLabel *QwtPolarPlot::titleLabel() const \param legend Legend \param pos The legend's position. For top/left position the number - of colums will be limited to 1, otherwise it will be set to + of columns will be limited to 1, otherwise it will be set to unlimited. \param ratio Ratio between legend and the bounding rect diff --git a/src/app/gps/qwtpolar-1.0/qwt_polar_plot.cpp b/src/app/gps/qwtpolar-1.0/qwt_polar_plot.cpp index aa6c4041290..add40f7f02f 100644 --- a/src/app/gps/qwtpolar-1.0/qwt_polar_plot.cpp +++ b/src/app/gps/qwtpolar-1.0/qwt_polar_plot.cpp @@ -167,7 +167,7 @@ const QwtTextLabel *QwtPolarPlot::titleLabel() const \param legend Legend \param pos The legend's position. For top/left position the number - of colums will be limited to 1, otherwise it will be set to + of columns will be limited to 1, otherwise it will be set to unlimited. \param ratio Ratio between legend and the bounding rect diff --git a/src/app/gps/qwtpolar-1.1.1/qwt_polar_plot.cpp b/src/app/gps/qwtpolar-1.1.1/qwt_polar_plot.cpp index 09cc5403505..b1b8b53dab5 100644 --- a/src/app/gps/qwtpolar-1.1.1/qwt_polar_plot.cpp +++ b/src/app/gps/qwtpolar-1.1.1/qwt_polar_plot.cpp @@ -171,7 +171,7 @@ const QwtTextLabel *QwtPolarPlot::titleLabel() const \param legend Legend \param pos The legend's position. For top/left position the number - of colums will be limited to 1, otherwise it will be set to + of columns will be limited to 1, otherwise it will be set to unlimited. \param ratio Ratio between legend and the bounding rect diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index c7ce395726b..04493279e7a 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -7505,7 +7505,7 @@ QgsVectorLayer *QgisApp::pasteToNewMemoryVector() if ( !feature.hasGeometry() ) continue; - QgsWkbTypes::Type type = QgsWkbTypes::flatType( feature.geometry().wkbType() ); + QgsWkbTypes::Type type = feature.geometry().wkbType(); if ( type == QgsWkbTypes::Unknown || type == QgsWkbTypes::NoGeometry ) continue; @@ -7535,7 +7535,7 @@ QgsVectorLayer *QgisApp::pasteToNewMemoryVector() QgsWkbTypes::Type wkbType = !typeCounts.isEmpty() ? typeCounts.keys().value( 0 ) : QgsWkbTypes::NoGeometry; - QString typeName = wkbType != QgsWkbTypes::NoGeometry ? QString( QgsWkbTypes::displayString( wkbType ) ).remove( "WKB" ) : "none"; + QString typeName = wkbType != QgsWkbTypes::NoGeometry ? QgsWkbTypes::displayString( wkbType ) : "none"; if ( features.isEmpty() ) { @@ -7590,7 +7590,7 @@ QgsVectorLayer *QgisApp::pasteToNewMemoryVector() if ( !feature.hasGeometry() ) continue; - QgsWkbTypes::Type type = QgsWkbTypes::flatType( feature.geometry().wkbType() ); + QgsWkbTypes::Type type = feature.geometry().wkbType(); if ( type == QgsWkbTypes::Unknown || type == QgsWkbTypes::NoGeometry ) continue; diff --git a/src/app/qgsdiscoverrelationsdlg.cpp b/src/app/qgsdiscoverrelationsdlg.cpp new file mode 100644 index 00000000000..a524867f3f0 --- /dev/null +++ b/src/app/qgsdiscoverrelationsdlg.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + qgsdiscoverrelationsdlg.cpp + --------------------- + begin : September 2016 + copyright : (C) 2016 by Patrick Valsecchi + email : patrick dot valsecchi at camptocamp dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgsdiscoverrelationsdlg.h" +#include "qgsvectorlayer.h" +#include "qgsrelationmanager.h" + +#include + +QgsDiscoverRelationsDlg::QgsDiscoverRelationsDlg( const QList& existingRelations, const QList& layers, QWidget *parent ) + : QDialog( parent ) + , mLayers( layers ) +{ + setupUi( this ); + + mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + connect( mRelationsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsDiscoverRelationsDlg::onSelectionChanged ); + + mFoundRelations = QgsRelationManager::discoverRelations( existingRelations, layers ); + Q_FOREACH ( const QgsRelation& relation, mFoundRelations ) addRelation( relation ); + + mRelationsTable->resizeColumnsToContents(); + +} + +void QgsDiscoverRelationsDlg::addRelation( const QgsRelation &rel ) +{ + const int row = mRelationsTable->rowCount(); + mRelationsTable->insertRow( row ); + mRelationsTable->setItem( row, 0, new QTableWidgetItem( rel.name() ) ); + mRelationsTable->setItem( row, 1, new QTableWidgetItem( rel.referencingLayer()->name() ) ); + mRelationsTable->setItem( row, 2, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencingField() ) ); + mRelationsTable->setItem( row, 3, new QTableWidgetItem( rel.referencedLayer()->name() ) ); + mRelationsTable->setItem( row, 4, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencedField() ) ); +} + +QList QgsDiscoverRelationsDlg::relations() const +{ + QList result; + Q_FOREACH ( const QModelIndex& row, mRelationsTable->selectionModel()->selectedRows() ) + { + result.append( mFoundRelations.at( row.row() ) ); + } + return result; +} + +void QgsDiscoverRelationsDlg::onSelectionChanged() +{ + mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( mRelationsTable->selectionModel()->hasSelection() ); +} \ No newline at end of file diff --git a/src/app/qgsdiscoverrelationsdlg.h b/src/app/qgsdiscoverrelationsdlg.h new file mode 100644 index 00000000000..b890b524e12 --- /dev/null +++ b/src/app/qgsdiscoverrelationsdlg.h @@ -0,0 +1,53 @@ +/*************************************************************************** + qgsdiscoverrelationsdlg.h + --------------------- + begin : September 2016 + copyright : (C) 2016 by Patrick Valsecchi + email : patrick dot valsecchi at camptocamp dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSDISCOVERRELATIONSDLG_H +#define QGSDISCOVERRELATIONSDLG_H + +#include +#include "ui_qgsdiscoverrelationsdlgbase.h" +#include "qgsrelation.h" + +class QgsRelationManager; +class QgsVectorLayer; + +/** + * Shows the list of relations discovered from the providers. + * + * The user can select some of them to add them to his project. + */ +class APP_EXPORT QgsDiscoverRelationsDlg : public QDialog, private Ui::QgsDiscoverRelationsDlgBase +{ + Q_OBJECT + + public: + explicit QgsDiscoverRelationsDlg( const QList& existingRelations, const QList& layers, QWidget *parent = nullptr ); + + /** + * Get the selected relations. + */ + QList relations() const; + + private slots: + void onSelectionChanged(); + + private: + QList mLayers; + QList mFoundRelations; + + void addRelation( const QgsRelation &rel ); + +}; + +#endif // QGSDISCOVERRELATIONSDLG_H diff --git a/src/app/qgslayerstylingwidget.cpp b/src/app/qgslayerstylingwidget.cpp index 0d6f575b67e..27c170dadff 100644 --- a/src/app/qgslayerstylingwidget.cpp +++ b/src/app/qgslayerstylingwidget.cpp @@ -226,7 +226,7 @@ void QgsLayerStylingWidget::apply() QString undoName = "Style Change"; - QWidget* current = mWidgetStack->mainWidget(); + QWidget* current = mWidgetStack->mainPanel(); bool styleWasChanged = false; if ( QgsLabelingWidget* widget = qobject_cast( current ) ) @@ -268,8 +268,7 @@ void QgsLayerStylingWidget::apply() { emit styleChanged( mCurrentLayer ); QgsProject::instance()->setDirty( true ); - mMapCanvas->clearCache(); - mMapCanvas->refresh(); + mCurrentLayer->triggerRepaint(); } connect( mCurrentLayer, SIGNAL( styleChanged() ), this, SLOT( updateCurrentWidgetLayer() ) ); } @@ -307,7 +306,7 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() mStackedWidget->setCurrentIndex( mLayerPage ); - QgsPanelWidget* current = mWidgetStack->takeMainWidget(); + QgsPanelWidget* current = mWidgetStack->takeMainPanel(); if ( current ) { if ( QgsLabelingWidget* widget = qobject_cast( current ) ) @@ -334,15 +333,14 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() if ( panel ) { connect( panel, SIGNAL( widgetChanged( QgsPanelWidget* ) ), this, SLOT( autoApply() ) ); - panel->setDockMode( true ); - mWidgetStack->addMainPanel( panel ); + mWidgetStack->setMainPanel( panel ); } } // The last widget is always the undo stack. if ( row == mOptionsListWidget->count() - 1 ) { - mWidgetStack->addMainPanel( mUndoWidget ); + mWidgetStack->setMainPanel( mUndoWidget ); } else if ( mCurrentLayer->type() == QgsMapLayer::VectorLayer ) { @@ -359,7 +357,7 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() QgsPanelWidgetWrapper* wrapper = new QgsPanelWidgetWrapper( styleWidget, mStackedWidget ); wrapper->setDockMode( true ); connect( styleWidget, SIGNAL( showPanel( QgsPanelWidget* ) ), wrapper, SLOT( openPanel( QgsPanelWidget* ) ) ); - mWidgetStack->addMainPanel( wrapper ); + mWidgetStack->setMainPanel( wrapper ); break; } case 1: // Labels @@ -371,7 +369,7 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() connect( mLabelingWidget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) ); } mLabelingWidget->setLayer( vlayer ); - mWidgetStack->addMainPanel( mLabelingWidget ); + mWidgetStack->setMainPanel( mLabelingWidget ); break; } default: @@ -388,14 +386,14 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() mRasterStyleWidget = new QgsRendererRasterPropertiesWidget( rlayer, mMapCanvas, mWidgetStack ); mRasterStyleWidget->setDockMode( true ); connect( mRasterStyleWidget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) ); - mWidgetStack->addMainPanel( mRasterStyleWidget ); + mWidgetStack->setMainPanel( mRasterStyleWidget ); break; case 1: // Transparency { QgsRasterTransparencyWidget* transwidget = new QgsRasterTransparencyWidget( rlayer, mMapCanvas, mWidgetStack ); transwidget->setDockMode( true ); connect( transwidget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) ); - mWidgetStack->addMainPanel( transwidget ); + mWidgetStack->setMainPanel( transwidget ); break; } case 2: // Histogram @@ -417,7 +415,7 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() widget->setRendererWidget( name, mRasterStyleWidget->currentRenderWidget() ); widget->setDockMode( true ); - mWidgetStack->addMainPanel( widget ); + mWidgetStack->setMainPanel( widget ); } break; } diff --git a/src/app/qgsrelationmanagerdialog.cpp b/src/app/qgsrelationmanagerdialog.cpp index a7c78d6b69d..04314ada37c 100644 --- a/src/app/qgsrelationmanagerdialog.cpp +++ b/src/app/qgsrelationmanagerdialog.cpp @@ -13,6 +13,7 @@ * * ***************************************************************************/ +#include "qgsdiscoverrelationsdlg.h" #include "qgsrelationadddlg.h" #include "qgsrelationmanagerdialog.h" #include "qgsrelationmanager.h" @@ -46,6 +47,7 @@ void QgsRelationManagerDialog::setLayers( const QList< QgsVectorLayer* >& layers void QgsRelationManagerDialog::addRelation( const QgsRelation &rel ) { + mRelationsTable->setSortingEnabled( false ); int row = mRelationsTable->rowCount(); mRelationsTable->insertRow( row ); @@ -54,7 +56,6 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel ) item->setData( Qt::UserRole, QVariant::fromValue( rel ) ); mRelationsTable->setItem( row, 0, item ); - item = new QTableWidgetItem( rel.referencingLayer()->name() ); item->setFlags( Qt::ItemIsEditable ); mRelationsTable->setItem( row, 1, item ); @@ -74,6 +75,7 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel ) item = new QTableWidgetItem( rel.id() ); item->setFlags( Qt::ItemIsEditable ); mRelationsTable->setItem( row, 5, item ); + mRelationsTable->setSortingEnabled( true ); } void QgsRelationManagerDialog::on_mBtnAddRelation_clicked() @@ -118,6 +120,18 @@ void QgsRelationManagerDialog::on_mBtnAddRelation_clicked() } } +void QgsRelationManagerDialog::on_mBtnDiscoverRelations_clicked() +{ + QgsDiscoverRelationsDlg discoverDlg( relations(), mLayers, this ); + if ( discoverDlg.exec() ) + { + Q_FOREACH ( const QgsRelation& relation, discoverDlg.relations() ) + { + addRelation( relation ); + } + } +} + void QgsRelationManagerDialog::on_mBtnRemoveRelation_clicked() { if ( mRelationsTable->currentIndex().isValid() ) diff --git a/src/app/qgsrelationmanagerdialog.h b/src/app/qgsrelationmanagerdialog.h index 8032beaa811..cf7ad5917f8 100644 --- a/src/app/qgsrelationmanagerdialog.h +++ b/src/app/qgsrelationmanagerdialog.h @@ -39,6 +39,7 @@ class APP_EXPORT QgsRelationManagerDialog : public QWidget, private Ui::QgsRelat public slots: void on_mBtnAddRelation_clicked(); + void on_mBtnDiscoverRelations_clicked(); void on_mBtnRemoveRelation_clicked(); private: diff --git a/src/browser/main.cpp b/src/browser/main.cpp index 7fb6d6d9a78..5fb4d3182ca 100644 --- a/src/browser/main.cpp +++ b/src/browser/main.cpp @@ -51,7 +51,7 @@ int main( int argc, char ** argv ) // Set up the QSettings environment must be done after qapp is created QCoreApplication::setOrganizationName( "QGIS" ); QCoreApplication::setOrganizationDomain( "qgis.org" ); - QCoreApplication::setApplicationName( "QGIS2" ); + QCoreApplication::setApplicationName( "QGIS3" ); #ifdef Q_OS_MACX // If the GDAL plugins are bundled with the application and GDAL_DRIVER_PATH diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 44d48f8ec79..dd64fdb41d9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -917,7 +917,7 @@ ELSE(NOT APPLE) FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis2_core + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_core BUILD_WITH_INSTALL_RPATH TRUE PUBLIC_HEADER "${QGIS_CORE_HDRS};${QGIS_CORE_MOC_HDRS}" LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" diff --git a/src/core/auth/qgsauthmanager.h b/src/core/auth/qgsauthmanager.h index a6be82feedd..fee03683487 100644 --- a/src/core/auth/qgsauthmanager.h +++ b/src/core/auth/qgsauthmanager.h @@ -90,7 +90,7 @@ class CORE_EXPORT QgsAuthManager : public QObject /** Standard message for when QCA's qca-ossl plugin is missing and system is disabled */ const QString disabledMessage() const; - /** The standard authentication database file in ~/.qgis2/ or defined location + /** The standard authentication database file in ~/.qgis3/ or defined location * @see QgsApplication::qgisAuthDbFilePath */ const QString authenticationDbPath() const { return mAuthDbPath; } diff --git a/src/core/composer/qgscomposeritemcommand.h b/src/core/composer/qgscomposeritemcommand.h index 0a5a094b4f8..4b931186176 100644 --- a/src/core/composer/qgscomposeritemcommand.h +++ b/src/core/composer/qgscomposeritemcommand.h @@ -87,9 +87,14 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand //composer label ComposerLabelSetText, ComposerLabelSetId, + ComposerLabelFontColor, //composer map ComposerMapRotation, ComposerMapAnnotationDistance, + ComposerMapGridFramePenColor, + ComposerMapGridFrameFill1Color, + ComposerMapGridFrameFill2Color, + ComposerMapGridAnnotationFontColor, //composer legend ComposerLegendText, LegendColumnCount, @@ -107,8 +112,12 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand LegendBoxSpace, LegendColumnSpace, LegendRasterBorderWidth, + LegendFontColor, + LegendRasterBorderColor, //composer picture ComposerPictureRotation, + ComposerPictureFillColor, + ComposerPictureOutlineColor, // composer scalebar ScaleBarLineWidth, ScaleBarHeight, @@ -119,6 +128,10 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand ScaleBarMapUnitsSegment, ScaleBarLabelBarSize, ScaleBarBoxContentSpace, + ScaleBarFontColor, + ScaleBarFillColor, + ScaleBarFill2Color, + ScaleBarStrokeColor, // composer table TableMaximumFeatures, TableMargin, @@ -128,9 +141,13 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand ShapeOutlineWidth, //composer arrow ArrowOutlineWidth, + ArrowHeadFillColor, + ArrowHeadOutlineColor, ArrowHeadWidth, //item ItemOutlineWidth, + ItemOutlineColor, + ItemBackgroundColor, ItemMove, ItemRotation, ItemTransparency, diff --git a/src/core/composer/qgscomposermultiframecommand.h b/src/core/composer/qgscomposermultiframecommand.h index cc72fe3e844..255c5065fbf 100644 --- a/src/core/composer/qgscomposermultiframecommand.h +++ b/src/core/composer/qgscomposermultiframecommand.h @@ -78,7 +78,11 @@ class CORE_EXPORT QgsComposerMultiFrameMergeCommand: public QgsComposerMultiFram TableMaximumFeatures, TableMargin, TableGridStrokeWidth, - TableCellStyle + TableCellStyle, + TableHeaderFontColor, + TableContentFontColor, + TableGridColor, + TableBackgroundColor, }; QgsComposerMultiFrameMergeCommand( Context c, QgsComposerMultiFrame* multiFrame, const QString& text ); diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index 76263384de3..4c1d20920c3 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -965,12 +965,11 @@ void QgsDxfExport::writeEntities() } renderer->startRender( ctx, vl->fields() ); - QStringList attributes = renderer->usedAttributes(); + QSet attributes = renderer->usedAttributes(); if ( vl->fields().exists( layerIt->second ) ) { QString layerAttr = vl->fields().at( layerIt->second ).name(); - if ( !attributes.contains( layerAttr ) ) - attributes << layerAttr; + attributes << layerAttr; } const QgsAbstractVectorLayerLabeling *labeling = vl->labeling(); @@ -1106,7 +1105,7 @@ void QgsDxfExport::writeEntitiesSymbolLevels( QgsVectorLayer* layer ) { req.setFlags( QgsFeatureRequest::NoGeometry ); } - req.setSubsetOfAttributes( QStringList( renderer->usedAttributes() ), layer->fields() ); + req.setSubsetOfAttributes( renderer->usedAttributes(), layer->fields() ); req.setFilterRect( mMapSettings.mapToLayerCoordinates( layer, mExtent ) ); QgsFeatureIterator fit = layer->getFeatures( req ); diff --git a/src/core/qgsaggregatecalculator.cpp b/src/core/qgsaggregatecalculator.cpp index 46b16b377e2..59d32584ec0 100644 --- a/src/core/qgsaggregatecalculator.cpp +++ b/src/core/qgsaggregatecalculator.cpp @@ -71,9 +71,9 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag } } - QStringList lst; + QSet lst; if ( expression.isNull() ) - lst.append( fieldOrExpression ); + lst.insert( fieldOrExpression ); else lst = expression->referencedColumns(); diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 99c1465e2aa..898f9371e4e 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -83,7 +83,7 @@ QString QgsApplication::sPlatformName = "desktop"; const char* QgsApplication::QGIS_ORGANIZATION_NAME = "QGIS"; const char* QgsApplication::QGIS_ORGANIZATION_DOMAIN = "qgis.org"; -const char* QgsApplication::QGIS_APPLICATION_NAME = "QGIS2"; +const char* QgsApplication::QGIS_APPLICATION_NAME = "QGIS3"; /*! \class QgsApplication @@ -116,10 +116,7 @@ void QgsApplication::init( QString customConfigPath ) } else { - // TODO Switch to this for release. - //customConfigPath = QString( "%1/.qgis%2/" ).arg( QDir::homePath() ).arg( Qgis::QGIS_VERSION_INT / 10000 ); - // Use qgis-dev for dev versions of QGIS to avoid mixing 2 and 3 API plugins. - customConfigPath = QString( "%1/.qgis%2/" ).arg( QDir::homePath() ).arg( "-dev" ); + customConfigPath = QString( "%1/.qgis3/" ).arg( QDir::homePath() ); } } diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index 9bb559f7296..cb468852a3f 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -87,7 +87,7 @@ class CORE_EXPORT QgsApplication : public QApplication static void setUITheme( const QString &themeName ); /** - * @brief All themes found in ~/.qgis2/themes folder. + * @brief All themes found in ~/.qgis3/themes folder. * The path is to the root folder for the theme * @note Valid theme folders must contain a style.qss file. * @return A hash of theme name and theme path. Valid theme folders contain style.qss diff --git a/src/core/qgsdatadefined.cpp b/src/core/qgsdatadefined.cpp index c09d0c3fdd7..8600a4bca7f 100644 --- a/src/core/qgsdatadefined.cpp +++ b/src/core/qgsdatadefined.cpp @@ -187,7 +187,7 @@ QgsExpression *QgsDataDefined::expression() return d->expression; } -QStringList QgsDataDefined::referencedColumns( const QgsExpressionContext& context ) +QSet QgsDataDefined::referencedColumns( const QgsExpressionContext& context ) { if ( !d->exprRefColumns.isEmpty() ) { diff --git a/src/core/qgsdatadefined.h b/src/core/qgsdatadefined.h index 27ba85ba921..285fe6e2a08 100644 --- a/src/core/qgsdatadefined.h +++ b/src/core/qgsdatadefined.h @@ -154,7 +154,7 @@ class CORE_EXPORT QgsDataDefined * @param context expression context, used for preparing the expression if required * @note added in QGIS 2.12 */ - QStringList referencedColumns( const QgsExpressionContext& context = QgsExpressionContext() ); + QSet referencedColumns( const QgsExpressionContext& context = QgsExpressionContext() ); /** * Get the field which this QgsDataDefined represents. Be aware that this may return diff --git a/src/core/qgsdatadefined_p.h b/src/core/qgsdatadefined_p.h index 23d83db9139..8b7d4c1ede3 100644 --- a/src/core/qgsdatadefined_p.h +++ b/src/core/qgsdatadefined_p.h @@ -80,7 +80,7 @@ class QgsDataDefinedPrivate : public QSharedData QString field; bool expressionPrepared; - QStringList exprRefColumns; + QSet exprRefColumns; }; /// @endcond diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index c8ac0939ce2..36f6133753a 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -3427,43 +3427,43 @@ const QList& QgsExpression::Functions() << new StaticFunction( "scale_exp", 6, fcnExpScale, "Math" ) << new StaticFunction( "floor", 1, fcnFloor, "Math" ) << new StaticFunction( "ceil", 1, fcnCeil, "Math" ) - << new StaticFunction( "pi", 0, fcnPi, "Math", QString(), false, QStringList(), false, QStringList() << "$pi" ) - << new StaticFunction( "to_int", 1, fcnToInt, "Conversions", QString(), false, QStringList(), false, QStringList() << "toint" ) - << new StaticFunction( "to_real", 1, fcnToReal, "Conversions", QString(), false, QStringList(), false, QStringList() << "toreal" ) - << new StaticFunction( "to_string", 1, fcnToString, "Conversions", QString(), false, QStringList(), false, QStringList() << "tostring" ) - << new StaticFunction( "to_datetime", 1, fcnToDateTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "todatetime" ) - << new StaticFunction( "to_date", 1, fcnToDate, "Conversions", QString(), false, QStringList(), false, QStringList() << "todate" ) - << new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "totime" ) - << new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QStringList(), false, QStringList() << "tointerval" ) - << new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QStringList(), false, QStringList(), true ) - << new StaticFunction( "if", 3, fcnIf, "Conditionals", QString(), False, QStringList(), true ) + << new StaticFunction( "pi", 0, fcnPi, "Math", QString(), false, QSet(), false, QStringList() << "$pi" ) + << new StaticFunction( "to_int", 1, fcnToInt, "Conversions", QString(), false, QSet(), false, QStringList() << "toint" ) + << new StaticFunction( "to_real", 1, fcnToReal, "Conversions", QString(), false, QSet(), false, QStringList() << "toreal" ) + << new StaticFunction( "to_string", 1, fcnToString, "Conversions", QString(), false, QSet(), false, QStringList() << "tostring" ) + << new StaticFunction( "to_datetime", 1, fcnToDateTime, "Conversions", QString(), false, QSet(), false, QStringList() << "todatetime" ) + << new StaticFunction( "to_date", 1, fcnToDate, "Conversions", QString(), false, QSet(), false, QStringList() << "todate" ) + << new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QSet(), false, QStringList() << "totime" ) + << new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QSet(), false, QStringList() << "tointerval" ) + << new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QSet(), false, QStringList(), true ) + << new StaticFunction( "if", 3, fcnIf, "Conditionals", QString(), False, QSet(), true ) << new StaticFunction( "aggregate", ParameterList() << Parameter( "layer" ) << Parameter( "aggregate" ) << Parameter( "expression" ) - << Parameter( "filter", true ) << Parameter( "concatenator", true ), fcnAggregate, "Aggregates", QString(), false, QStringList(), true ) + << Parameter( "filter", true ) << Parameter( "concatenator", true ), fcnAggregate, "Aggregates", QString(), false, QSet(), true ) << new StaticFunction( "relation_aggregate", ParameterList() << Parameter( "relation" ) << Parameter( "aggregate" ) << Parameter( "expression" ) << Parameter( "concatenator", true ), - fcnAggregateRelation, "Aggregates", QString(), False, QStringList( QgsFeatureRequest::AllAttributes ), true ) + fcnAggregateRelation, "Aggregates", QString(), False, QSet() << QgsFeatureRequest::AllAttributes, true ) - << new StaticFunction( "count", aggParams, fcnAggregateCount, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "count_distinct", aggParams, fcnAggregateCountDistinct, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "count_missing", aggParams, fcnAggregateCountMissing, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "minimum", aggParams, fcnAggregateMin, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "maximum", aggParams, fcnAggregateMax, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "sum", aggParams, fcnAggregateSum, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "mean", aggParams, fcnAggregateMean, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "median", aggParams, fcnAggregateMedian, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "stdev", aggParams, fcnAggregateStdev, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "range", aggParams, fcnAggregateRange, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "minority", aggParams, fcnAggregateMinority, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "majority", aggParams, fcnAggregateMajority, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "q1", aggParams, fcnAggregateQ1, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "q3", aggParams, fcnAggregateQ3, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "iqr", aggParams, fcnAggregateIQR, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "min_length", aggParams, fcnAggregateMinLength, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "max_length", aggParams, fcnAggregateMaxLength, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "collect", aggParams, fcnAggregateCollectGeometry, "Aggregates", QString(), False, QStringList(), true ) - << new StaticFunction( "concatenate", aggParams << Parameter( "concatenator", true ), fcnAggregateStringConcat, "Aggregates", QString(), False, QStringList(), true ) + << new StaticFunction( "count", aggParams, fcnAggregateCount, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "count_distinct", aggParams, fcnAggregateCountDistinct, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "count_missing", aggParams, fcnAggregateCountMissing, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "minimum", aggParams, fcnAggregateMin, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "maximum", aggParams, fcnAggregateMax, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "sum", aggParams, fcnAggregateSum, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "mean", aggParams, fcnAggregateMean, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "median", aggParams, fcnAggregateMedian, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "stdev", aggParams, fcnAggregateStdev, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "range", aggParams, fcnAggregateRange, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "minority", aggParams, fcnAggregateMinority, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "majority", aggParams, fcnAggregateMajority, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "q1", aggParams, fcnAggregateQ1, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "q3", aggParams, fcnAggregateQ3, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "iqr", aggParams, fcnAggregateIQR, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "min_length", aggParams, fcnAggregateMinLength, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "max_length", aggParams, fcnAggregateMaxLength, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "collect", aggParams, fcnAggregateCollectGeometry, "Aggregates", QString(), False, QSet(), true ) + << new StaticFunction( "concatenate", aggParams << Parameter( "concatenator", true ), fcnAggregateStringConcat, "Aggregates", QString(), False, QSet(), true ) << new StaticFunction( "regexp_match", 2, fcnRegexpMatch, "Conditionals" ) - << new StaticFunction( "now", 0, fcnNow, "Date and Time", QString(), false, QStringList(), false, QStringList() << "$now" ) + << new StaticFunction( "now", 0, fcnNow, "Date and Time", QString(), false, QSet(), false, QStringList() << "$now" ) << new StaticFunction( "age", 2, fcnAge, "Date and Time" ) << new StaticFunction( "year", 1, fcnYear, "Date and Time" ) << new StaticFunction( "month", 1, fcnMonth, "Date and Time" ) @@ -3488,7 +3488,7 @@ const QList& QgsExpression::Functions() << new StaticFunction( "regexp_replace", 3, fcnRegexpReplace, "String" ) << new StaticFunction( "regexp_substr", 2, fcnRegexpSubstr, "String" ) << new StaticFunction( "substr", 3, fcnSubstr, "String" ) - << new StaticFunction( "concat", -1, fcnConcat, "String", QString(), false, QStringList(), false, QStringList(), true ) + << new StaticFunction( "concat", -1, fcnConcat, "String", QString(), false, QSet(), false, QStringList(), true ) << new StaticFunction( "strpos", 2, fcnStrpos, "String" ) << new StaticFunction( "left", 2, fcnLeft, "String" ) << new StaticFunction( "right", 2, fcnRight, "String" ) @@ -3531,16 +3531,16 @@ const QList& QgsExpression::Functions() << new StaticFunction( "make_point_m", 3, fcnMakePointM, "GeometryGroup" ) << new StaticFunction( "make_line", -1, fcnMakeLine, "GeometryGroup" ) << new StaticFunction( "make_polygon", -1, fcnMakePolygon, "GeometryGroup" ) - << new StaticFunction( "$x_at", 1, fcnXat, "GeometryGroup", QString(), true, QStringList(), false, QStringList() << "xat" << "x_at" ) - << new StaticFunction( "$y_at", 1, fcnYat, "GeometryGroup", QString(), true, QStringList(), false, QStringList() << "yat" << "y_at" ) - << new StaticFunction( "x_min", 1, fcnXMin, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "xmin" ) - << new StaticFunction( "x_max", 1, fcnXMax, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "xmax" ) - << new StaticFunction( "y_min", 1, fcnYMin, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "ymin" ) - << new StaticFunction( "y_max", 1, fcnYMax, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "ymax" ) - << new StaticFunction( "geom_from_wkt", 1, fcnGeomFromWKT, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromWKT" ) - << new StaticFunction( "geom_from_gml", 1, fcnGeomFromGML, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromGML" ) + << new StaticFunction( "$x_at", 1, fcnXat, "GeometryGroup", QString(), true, QSet(), false, QStringList() << "xat" << "x_at" ) + << new StaticFunction( "$y_at", 1, fcnYat, "GeometryGroup", QString(), true, QSet(), false, QStringList() << "yat" << "y_at" ) + << new StaticFunction( "x_min", 1, fcnXMin, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "xmin" ) + << new StaticFunction( "x_max", 1, fcnXMax, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "xmax" ) + << new StaticFunction( "y_min", 1, fcnYMin, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "ymin" ) + << new StaticFunction( "y_max", 1, fcnYMax, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "ymax" ) + << new StaticFunction( "geom_from_wkt", 1, fcnGeomFromWKT, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "geomFromWKT" ) + << new StaticFunction( "geom_from_gml", 1, fcnGeomFromGML, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "geomFromGML" ) << new StaticFunction( "relate", -1, fcnRelate, "GeometryGroup" ) - << new StaticFunction( "intersects_bbox", 2, fcnBbox, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "bbox" ) + << new StaticFunction( "intersects_bbox", 2, fcnBbox, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "bbox" ) << new StaticFunction( "disjoint", 2, fcnDisjoint, "GeometryGroup" ) << new StaticFunction( "intersects", 2, fcnIntersects, "GeometryGroup" ) << new StaticFunction( "touches", 2, fcnTouches, "GeometryGroup" ) @@ -3584,14 +3584,14 @@ const QList& QgsExpression::Functions() << new StaticFunction( "bounds_width", 1, fcnBoundsWidth, "GeometryGroup" ) << new StaticFunction( "bounds_height", 1, fcnBoundsHeight, "GeometryGroup" ) << new StaticFunction( "is_closed", 1, fcnIsClosed, "GeometryGroup" ) - << new StaticFunction( "convex_hull", 1, fcnConvexHull, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "convexHull" ) + << new StaticFunction( "convex_hull", 1, fcnConvexHull, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "convexHull" ) << new StaticFunction( "difference", 2, fcnDifference, "GeometryGroup" ) << new StaticFunction( "distance", 2, fcnDistance, "GeometryGroup" ) << new StaticFunction( "intersection", 2, fcnIntersection, "GeometryGroup" ) - << new StaticFunction( "sym_difference", 2, fcnSymDifference, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "symDifference" ) + << new StaticFunction( "sym_difference", 2, fcnSymDifference, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "symDifference" ) << new StaticFunction( "combine", 2, fcnCombine, "GeometryGroup" ) << new StaticFunction( "union", 2, fcnCombine, "GeometryGroup" ) - << new StaticFunction( "geom_to_wkt", -1, fcnGeomToWKT, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomToWKT" ) + << new StaticFunction( "geom_to_wkt", -1, fcnGeomToWKT, "GeometryGroup", QString(), false, QSet(), false, QStringList() << "geomToWKT" ) << new StaticFunction( "geometry", 1, fcnGetGeometry, "GeometryGroup", QString(), true ) << new StaticFunction( "transform", 3, fcnTransformGeometry, "GeometryGroup" ) << new StaticFunction( "extrude", 3, fcnExtrude, "GeometryGroup", QString() ) @@ -3610,16 +3610,16 @@ const QList& QgsExpression::Functions() << Parameter( "vertex" ), fcnDistanceToVertex, "GeometryGroup" ) << new StaticFunction( "$id", 0, fcnFeatureId, "Record" ) << new StaticFunction( "$currentfeature", 0, fcnFeature, "Record" ) - << new StaticFunction( "uuid", 0, fcnUuid, "Record", QString(), false, QStringList(), false, QStringList() << "$uuid" ) - << new StaticFunction( "get_feature", 3, fcnGetFeature, "Record", QString(), false, QStringList(), false, QStringList() << "getFeature" ) + << new StaticFunction( "uuid", 0, fcnUuid, "Record", QString(), false, QSet(), false, QStringList() << "$uuid" ) + << new StaticFunction( "get_feature", 3, fcnGetFeature, "Record", QString(), false, QSet(), false, QStringList() << "getFeature" ) << new StaticFunction( "layer_property", 2, fcnGetLayerProperty, "General" ) << new StaticFunction( "var", 1, fcnGetVariable, "General" ) //return all attributes string for referencedColumns - this is caught by // QgsFeatureRequest::setSubsetOfAttributes and causes all attributes to be fetched by the // feature request - << new StaticFunction( "eval", 1, fcnEval, "General", QString(), true, QStringList( QgsFeatureRequest::AllAttributes ) ) - << new StaticFunction( "attribute", 2, fcnAttribute, "Record", QString(), false, QStringList( QgsFeatureRequest::AllAttributes ) ) + << new StaticFunction( "eval", 1, fcnEval, "General", QString(), true, QSet() << QgsFeatureRequest::AllAttributes ) + << new StaticFunction( "attribute", 2, fcnAttribute, "Record", QString(), false, QSet() << QgsFeatureRequest::AllAttributes ) // functions for arrays << new StaticFunction( "array", -1, fcnArray, "Arrays" ) @@ -3800,28 +3800,20 @@ bool QgsExpression::hasParserError() const { return !d->mParserErrorString.isNul QString QgsExpression::parserErrorString() const { return d->mParserErrorString; } -QStringList QgsExpression::referencedColumns() const +QSet QgsExpression::referencedColumns() const { if ( !d->mRootNode ) - return QStringList(); + return QSet(); - QStringList columns = d->mRootNode->referencedColumns(); + return d->mRootNode->referencedColumns(); +} - // filter out duplicates - for ( int i = 0; i < columns.count(); i++ ) - { - QString col = columns.at( i ); - for ( int j = i + 1; j < columns.count(); j++ ) - { - if ( QString::compare( col, columns[j], Qt::CaseInsensitive ) == 0 ) - { - // this column is repeated: remove it! - columns.removeAt( j-- ); - } - } - } - - return columns; +bool QgsExpression::NodeInOperator::needsGeometry() const +{ + bool needs = false; + Q_FOREACH ( Node* n, mList->list() ) + needs |= n->needsGeometry(); + return needs; } QSet QgsExpression::referencedAttributeIndexes( const QgsFields& fields ) const @@ -3829,10 +3821,10 @@ QSet QgsExpression::referencedAttributeIndexes( const QgsFields& fields ) c if ( !d->mRootNode ) return QSet(); - QStringList referencedFields = d->mRootNode->referencedColumns(); + const QSet referencedFields = d->mRootNode->referencedColumns(); QSet referencedIndexes; - Q_FOREACH ( const QString& fieldName, referencedFields ) +for ( const QString& fieldName : referencedFields ) { if ( fieldName == QgsFeatureRequest::AllAttributes ) { @@ -4056,6 +4048,13 @@ double QgsExpression::evaluateToDouble( const QString &text, const double fallba /////////////////////////////////////////////// // nodes +void QgsExpression::NodeList::append( QgsExpression::NamedNode* node ) +{ + mList.append( node->node ); + mNameList.append( node->name.toLower() ); + mHasNamedNodes = true; +} + QgsExpression::NodeList* QgsExpression::NodeList::clone() const { NodeList* nl = new NodeList; @@ -4603,6 +4602,16 @@ QString QgsExpression::NodeBinaryOperator::dump() const return fmt.arg( mOpLeft->dump(), BinaryOperatorText[mOp], rdump ); } +QSet QgsExpression::NodeBinaryOperator::referencedColumns() const +{ + return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); +} + +bool QgsExpression::NodeBinaryOperator::needsGeometry() const +{ + return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); +} + QgsExpression::Node* QgsExpression::NodeBinaryOperator::clone() const { return new NodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() ); @@ -4718,6 +4727,46 @@ QVariant QgsExpression::NodeFunction::eval( QgsExpression *parent, const QgsExpr return res; } +QgsExpression::NodeFunction::NodeFunction( int fnIndex, QgsExpression::NodeList* args ) + : mFnIndex( fnIndex ) +{ + const ParameterList& functionParams = Functions()[mFnIndex]->parameters(); + if ( !args || functionParams.isEmpty() ) + { + // no parameters, or function does not support them + mArgs = args; + } + else + { + mArgs = new NodeList(); + + int idx = 0; + //first loop through unnamed arguments + while ( idx < args->names().size() && args->names().at( idx ).isEmpty() ) + { + mArgs->append( args->list().at( idx )->clone() ); + idx++; + } + + //next copy named parameters in order expected by function + for ( ; idx < functionParams.count(); ++idx ) + { + int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); + if ( nodeIdx < 0 ) + { + //parameter not found - insert default value for parameter + mArgs->append( new NodeLiteral( functionParams.at( idx ).defaultValue() ) ); + } + else + { + mArgs->append( args->list().at( nodeIdx )->clone() ); + } + } + + delete args; + } +} + bool QgsExpression::NodeFunction::prepare( QgsExpression *parent, const QgsExpressionContext *context ) { Function* fd = Functions()[mFnIndex]; @@ -4742,10 +4791,10 @@ QString QgsExpression::NodeFunction::dump() const return QString( "%1(%2)" ).arg( fd->name(), mArgs ? mArgs->dump() : QString() ); // function } -QStringList QgsExpression::NodeFunction::referencedColumns() const +QSet QgsExpression::NodeFunction::referencedColumns() const { Function* fd = Functions()[mFnIndex]; - QStringList functionColumns = fd->referencedColumns(); + QSet functionColumns = fd->referencedColumns(); if ( !mArgs ) { @@ -4755,11 +4804,21 @@ QStringList QgsExpression::NodeFunction::referencedColumns() const Q_FOREACH ( Node* n, mArgs->list() ) { - functionColumns.append( n->referencedColumns() ); + functionColumns.unite( n->referencedColumns() ); } - //remove duplicates and return - return functionColumns.toSet().toList(); + return functionColumns; +} + +bool QgsExpression::NodeFunction::needsGeometry() const +{ + bool needs = Functions()[mFnIndex]->usesGeometry(); + if ( mArgs ) + { + Q_FOREACH ( Node* n, mArgs->list() ) + needs |= n->needsGeometry(); + } + return needs; } QgsExpression::Node* QgsExpression::NodeFunction::clone() const @@ -4767,6 +4826,79 @@ QgsExpression::Node* QgsExpression::NodeFunction::clone() const return new NodeFunction( mFnIndex, mArgs ? mArgs->clone() : nullptr ); } +bool QgsExpression::NodeFunction::validateParams( int fnIndex, QgsExpression::NodeList* args, QString& error ) +{ + if ( !args || !args->hasNamedNodes() ) + return true; + + const ParameterList& functionParams = Functions()[fnIndex]->parameters(); + if ( functionParams.isEmpty() ) + { + error = QString( "%1 does not supported named parameters" ).arg( Functions()[fnIndex]->name() ); + return false; + } + else + { + QSet< int > providedArgs; + QSet< int > handledArgs; + int idx = 0; + //first loop through unnamed arguments + while ( args->names().at( idx ).isEmpty() ) + { + providedArgs << idx; + handledArgs << idx; + idx++; + } + + //next check named parameters + for ( ; idx < functionParams.count(); ++idx ) + { + int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); + if ( nodeIdx < 0 ) + { + if ( !functionParams.at( idx ).optional() ) + { + error = QString( "No value specified for parameter '%1' for %2" ).arg( functionParams.at( idx ).name(), Functions()[fnIndex]->name() ); + return false; + } + } + else + { + if ( providedArgs.contains( idx ) ) + { + error = QString( "Duplicate parameter specified for '%1' for %2" ).arg( functionParams.at( idx ).name(), Functions()[fnIndex]->name() ); + return false; + } + } + providedArgs << idx; + handledArgs << nodeIdx; + } + + //last check for bad names + idx = 0; + Q_FOREACH ( const QString& name, args->names() ) + { + if ( !name.isEmpty() && !functionParams.contains( name ) ) + { + error = QString( "Invalid parameter name '%1' for %2" ).arg( name, Functions()[fnIndex]->name() ); + return false; + } + if ( !name.isEmpty() && !handledArgs.contains( idx ) ) + { + int functionIdx = functionParams.indexOf( name ); + if ( providedArgs.contains( functionIdx ) ) + { + error = QString( "Duplicate parameter specified for '%1' for %2" ).arg( functionParams.at( functionIdx ).name(), Functions()[fnIndex]->name() ); + return false; + } + } + idx++; + } + + } + return true; +} + // QVariant QgsExpression::NodeLiteral::eval( QgsExpression *parent, const QgsExpressionContext *context ) @@ -4924,9 +5056,9 @@ QString QgsExpression::NodeCondition::dump() const return msg; } -QStringList QgsExpression::NodeCondition::referencedColumns() const +QSet QgsExpression::NodeCondition::referencedColumns() const { - QStringList lst; + QSet lst; Q_FOREACH ( WhenThen* cond, mConditions ) { lst += cond->mWhenExp->referencedColumns() + cond->mThenExp->referencedColumns(); @@ -5294,3 +5426,19 @@ const QgsExpression::Node* QgsExpression::rootNode() const { return d->mRootNode; } + +QSet QgsExpression::NodeInOperator::referencedColumns() const +{ + QSet lst( mNode->referencedColumns() ); + Q_FOREACH ( const Node* n, mList->list() ) + lst.unite( n->referencedColumns() ); + return lst; +} + +bool QgsExpression::Function::operator==( const QgsExpression::Function& other ) const +{ + if ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 ) + return true; + + return false; +} diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h index 663f3e47e88..2216afd0b6d 100644 --- a/src/core/qgsexpression.h +++ b/src/core/qgsexpression.h @@ -183,7 +183,7 @@ class CORE_EXPORT QgsExpression * * TODO QGIS3: Return QSet */ - QStringList referencedColumns() const; + QSet referencedColumns() const; /** * Return a list of field name indexes obtained from the provided fields. @@ -451,7 +451,7 @@ class CORE_EXPORT QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = false ) @@ -475,7 +475,7 @@ class CORE_EXPORT QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = false ) @@ -534,7 +534,7 @@ class CORE_EXPORT QgsExpression * Functions are non lazy default and will be given the node return value when called **/ bool lazyEval() const { return mLazyEval; } - virtual QStringList referencedColumns() const { return mReferencedColumns; } + virtual QSet referencedColumns() const { return mReferencedColumns; } /** Returns whether the function is only available if provided by a QgsExpressionContext object. * @note added in QGIS 2.12 @@ -555,13 +555,7 @@ class CORE_EXPORT QgsExpression */ virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) = 0; - bool operator==( const Function& other ) const - { - if ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 ) - return true; - - return false; - } + bool operator==( const Function& other ) const; virtual bool handlesNull() const { return mHandlesNull; } @@ -572,7 +566,7 @@ class CORE_EXPORT QgsExpression bool mUsesGeometry; QString mGroup; QString mHelpText; - QStringList mReferencedColumns; + QSet mReferencedColumns; bool mLazyEval; bool mHandlesNull; bool mIsContextual; //if true function is only available through an expression context @@ -594,7 +588,7 @@ class CORE_EXPORT QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, const QStringList& aliases = QStringList(), bool handlesNull = false ) @@ -611,7 +605,7 @@ class CORE_EXPORT QgsExpression const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, const QStringList& aliases = QStringList(), bool handlesNull = false ) @@ -781,7 +775,7 @@ class CORE_EXPORT QgsExpression * * @return A list of columns required to evaluate this expression */ - virtual QStringList referencedColumns() const = 0; + virtual QSet referencedColumns() const = 0; /** * Abstract virtual method which returns if the geometry is required to evaluate @@ -830,7 +824,7 @@ class CORE_EXPORT QgsExpression /** Adds a named node. Takes ownership of the provided node. * @note added in QGIS 2.16 */ - void append( NamedNode* node ) { mList.append( node->node ); mNameList.append( node->name.toLower() ); mHasNamedNodes = true; } + void append( NamedNode* node ); /** Returns the number of nodes in the list. */ @@ -879,7 +873,7 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override { return mOperand->referencedColumns(); } + virtual QSet referencedColumns() const override { return mOperand->referencedColumns(); } virtual bool needsGeometry() const override { return mOperand->needsGeometry(); } virtual Node* clone() const override; @@ -909,8 +903,8 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); } - virtual bool needsGeometry() const override { return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); } + virtual QSet referencedColumns() const override; + virtual bool needsGeometry() const override; virtual Node* clone() const override; int precedence() const; @@ -953,8 +947,8 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override { QStringList lst( mNode->referencedColumns() ); Q_FOREACH ( const Node* n, mList->list() ) lst.append( n->referencedColumns() ); return lst; } - virtual bool needsGeometry() const override { bool needs = false; Q_FOREACH ( Node* n, mList->list() ) needs |= n->needsGeometry(); return needs; } + virtual QSet referencedColumns() const override; + virtual bool needsGeometry() const override; virtual Node* clone() const override; protected: @@ -968,44 +962,7 @@ class CORE_EXPORT QgsExpression class CORE_EXPORT NodeFunction : public Node { public: - NodeFunction( int fnIndex, NodeList* args ) : mFnIndex( fnIndex ) - { - const ParameterList& functionParams = Functions()[mFnIndex]->parameters(); - if ( !args || functionParams.isEmpty() ) - { - // no parameters, or function does not support them - mArgs = args; - } - else - { - mArgs = new NodeList(); - - int idx = 0; - //first loop through unnamed arguments - while ( idx < args->names().size() && args->names().at( idx ).isEmpty() ) - { - mArgs->append( args->list().at( idx )->clone() ); - idx++; - } - - //next copy named parameters in order expected by function - for ( ; idx < functionParams.count(); ++idx ) - { - int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); - if ( nodeIdx < 0 ) - { - //parameter not found - insert default value for parameter - mArgs->append( new NodeLiteral( functionParams.at( idx ).defaultValue() ) ); - } - else - { - mArgs->append( args->list().at( nodeIdx )->clone() ); - } - } - - delete args; - } - } + NodeFunction( int fnIndex, NodeList* args ); virtual ~NodeFunction() { delete mArgs; } @@ -1017,83 +974,12 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override; - virtual bool needsGeometry() const override { bool needs = Functions()[mFnIndex]->usesGeometry(); if ( mArgs ) { Q_FOREACH ( Node* n, mArgs->list() ) needs |= n->needsGeometry(); } return needs; } + virtual QSet referencedColumns() const override; + virtual bool needsGeometry() const override; virtual Node* clone() const override; //! Tests whether the provided argument list is valid for the matching function - static bool validateParams( int fnIndex, NodeList* args, QString& error ) - { - if ( !args || !args->hasNamedNodes() ) - return true; - - const ParameterList& functionParams = Functions()[fnIndex]->parameters(); - if ( functionParams.isEmpty() ) - { - error = QString( "%1 does not supported named parameters" ).arg( Functions()[fnIndex]->name() ); - return false; - } - else - { - QSet< int > providedArgs; - QSet< int > handledArgs; - int idx = 0; - //first loop through unnamed arguments - while ( args->names().at( idx ).isEmpty() ) - { - providedArgs << idx; - handledArgs << idx; - idx++; - } - - //next check named parameters - for ( ; idx < functionParams.count(); ++idx ) - { - int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); - if ( nodeIdx < 0 ) - { - if ( !functionParams.at( idx ).optional() ) - { - error = QString( "No value specified for parameter '%1' for %2" ).arg( functionParams.at( idx ).name(), Functions()[fnIndex]->name() ); - return false; - } - } - else - { - if ( providedArgs.contains( idx ) ) - { - error = QString( "Duplicate parameter specified for '%1' for %2" ).arg( functionParams.at( idx ).name(), Functions()[fnIndex]->name() ); - return false; - } - } - providedArgs << idx; - handledArgs << nodeIdx; - } - - //last check for bad names - idx = 0; - Q_FOREACH ( const QString& name, args->names() ) - { - if ( !name.isEmpty() && !functionParams.contains( name ) ) - { - error = QString( "Invalid parameter name '%1' for %2" ).arg( name, Functions()[fnIndex]->name() ); - return false; - } - if ( !name.isEmpty() && !handledArgs.contains( idx ) ) - { - int functionIdx = functionParams.indexOf( name ); - if ( providedArgs.contains( functionIdx ) ) - { - error = QString( "Duplicate parameter specified for '%1' for %2" ).arg( functionParams.at( functionIdx ).name(), Functions()[fnIndex]->name() ); - return false; - } - } - idx++; - } - - } - return true; - } + static bool validateParams( int fnIndex, NodeList* args, QString& error ); protected: int mFnIndex; @@ -1118,7 +1004,7 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override { return QStringList(); } + virtual QSet referencedColumns() const override { return QSet(); } virtual bool needsGeometry() const override { return false; } virtual Node* clone() const override; @@ -1144,7 +1030,7 @@ class CORE_EXPORT QgsExpression virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override { return QStringList( mName ); } + virtual QSet referencedColumns() const override { return QSet() << mName; } virtual bool needsGeometry() const override { return false; } virtual Node* clone() const override; @@ -1195,7 +1081,7 @@ class CORE_EXPORT QgsExpression virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override; virtual QString dump() const override; - virtual QStringList referencedColumns() const override; + virtual QSet referencedColumns() const override; virtual bool needsGeometry() const override; virtual Node* clone() const override; @@ -1333,6 +1219,8 @@ class CORE_EXPORT QgsExpression friend class QgsOgcUtils; }; + + Q_DECLARE_METATYPE( QgsExpression::Node* ) #endif // QGSEXPRESSION_H diff --git a/src/core/qgsexpressioncontext.h b/src/core/qgsexpressioncontext.h index c7d33787e10..620f74b1b44 100644 --- a/src/core/qgsexpressioncontext.h +++ b/src/core/qgsexpressioncontext.h @@ -41,12 +41,17 @@ class QgsSymbol; class CORE_EXPORT QgsScopedExpressionFunction : public QgsExpression::Function { public: + /** + * Create a new QgsScopedExpressionFunction + * + * @note Added in QGIS 2.12 + */ QgsScopedExpressionFunction( const QString& fnname, int params, const QString& group, const QString& helpText = QString(), bool usesGeometry = false, - const QStringList& referencedColumns = QStringList(), + const QSet& referencedColumns = QSet(), bool lazyEval = false, bool handlesNull = false, bool isContextual = true ) diff --git a/src/core/qgsexpressionfieldbuffer.cpp b/src/core/qgsexpressionfieldbuffer.cpp index 84766c79ad9..9b73009998e 100644 --- a/src/core/qgsexpressionfieldbuffer.cpp +++ b/src/core/qgsexpressionfieldbuffer.cpp @@ -33,6 +33,11 @@ void QgsExpressionFieldBuffer::removeExpression( int index ) mExpressions.removeAt( index ); } +void QgsExpressionFieldBuffer::renameExpression( int index, const QString& name ) +{ + mExpressions[index].field.setName( name ); +} + void QgsExpressionFieldBuffer::updateExpression( int index, const QString& exp ) { mExpressions[index].cachedExpression = QgsExpression( exp ); diff --git a/src/core/qgsexpressionfieldbuffer.h b/src/core/qgsexpressionfieldbuffer.h index 6ac2d46a419..b73c4bd5f37 100644 --- a/src/core/qgsexpressionfieldbuffer.h +++ b/src/core/qgsexpressionfieldbuffer.h @@ -61,6 +61,16 @@ class CORE_EXPORT QgsExpressionFieldBuffer */ void removeExpression( int index ); + /** + * Renames an expression field at a given index + * + * @param index The index of the expression to change + * @param name New name for field + * + * @note added in 3.0 + */ + void renameExpression( int index, const QString& name ); + /** * Changes the expression at a given index * diff --git a/src/core/qgsfeaturerequest.cpp b/src/core/qgsfeaturerequest.cpp index a816b2ec229..8c010d7358c 100644 --- a/src/core/qgsfeaturerequest.cpp +++ b/src/core/qgsfeaturerequest.cpp @@ -216,6 +216,27 @@ QgsFeatureRequest& QgsFeatureRequest::setSubsetOfAttributes( const QStringList& return *this; } +QgsFeatureRequest& QgsFeatureRequest::setSubsetOfAttributes( const QSet& attrNames, const QgsFields& fields ) +{ + if ( attrNames.contains( QgsFeatureRequest::AllAttributes ) ) + { + //attribute string list contains the all attributes flag, so we must fetch all attributes + return *this; + } + + mFlags |= SubsetOfAttributes; + mAttrs.clear(); + + Q_FOREACH ( const QString& attrName, attrNames ) + { + int attrNum = fields.lookupField( attrName ); + if ( attrNum != -1 && !mAttrs.contains( attrNum ) ) + mAttrs.append( attrNum ); + } + + return *this; +} + QgsFeatureRequest& QgsFeatureRequest::setSimplifyMethod( const QgsSimplifyMethod& simplifyMethod ) { mSimplifyMethod = simplifyMethod; @@ -387,7 +408,7 @@ QSet QgsFeatureRequest::OrderBy::usedAttributes() const { const OrderByClause& clause = *it; - usedAttributes.unite( clause.expression().referencedColumns().toSet() ); + usedAttributes.unite( clause.expression().referencedColumns() ); } return usedAttributes; diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h index 006deae7b91..7c8d0a128ad 100644 --- a/src/core/qgsfeaturerequest.h +++ b/src/core/qgsfeaturerequest.h @@ -377,6 +377,9 @@ class CORE_EXPORT QgsFeatureRequest //! Set a subset of attributes by names that will be fetched QgsFeatureRequest& setSubsetOfAttributes( const QStringList& attrNames, const QgsFields& fields ); + //! Set a subset of attributes by names that will be fetched + QgsFeatureRequest& setSubsetOfAttributes( const QSet& attrNames, const QgsFields& fields ); + //! Set a simplification method for geometries that will be fetched //! @note added in 2.2 QgsFeatureRequest& setSimplifyMethod( const QgsSimplifyMethod& simplifyMethod ); diff --git a/src/core/qgsogcutils.cpp b/src/core/qgsogcutils.cpp index bd4a2584652..693cae80ae0 100644 --- a/src/core/qgsogcutils.cpp +++ b/src/core/qgsogcutils.cpp @@ -1666,6 +1666,10 @@ static int binaryOperatorFromTagName( const QString& tagName ) static QString binaryOperatorToTagName( QgsExpression::BinaryOperator op ) { + if ( op == QgsExpression::boILike ) + { + return "PropertyIsLike"; + } return binaryOperatorsTagNamesMap().key( op, QString() ); } @@ -1752,6 +1756,11 @@ QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( return nullptr; } + if ( op == QgsExpression::boLike && element.hasAttribute( "matchCase" ) && element.attribute( "matchCase" ) == "false" ) + { + op = QgsExpression::boILike; + } + QDomElement operandElem = element.firstChildElement(); QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr; if ( !expr ) @@ -1772,6 +1781,64 @@ QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter( return nullptr; } + if ( op == QgsExpression::boLike || op == QgsExpression::boILike ) + { + QString wildCard; + if ( element.hasAttribute( "wildCard" ) ) + { + wildCard = element.attribute( "wildCard" ); + } + QString singleChar; + if ( element.hasAttribute( "singleChar" ) ) + { + singleChar = element.attribute( "singleChar" ); + } + QString escape = "\\"; + if ( element.hasAttribute( "escape" ) ) + { + escape = element.attribute( "escape" ); + } + // replace + QString oprValue = static_cast( opRight )->value().toString(); + if ( !wildCard.isEmpty() && wildCard != "%" ) + { + oprValue.replace( '%', "\\%" ); + if ( oprValue.startsWith( wildCard ) ) + { + oprValue.replace( 0, 1, "%" ); + } + QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" ); + int pos = 0; + while (( pos = rx.indexIn( oprValue, pos ) ) != -1 ) + { + oprValue.replace( pos + 1, 1, "%" ); + pos += 1; + } + oprValue.replace( escape + wildCard, wildCard ); + } + if ( !singleChar.isEmpty() && singleChar != "_" ) + { + oprValue.replace( '_', "\\_" ); + if ( oprValue.startsWith( singleChar ) ) + { + oprValue.replace( 0, 1, "_" ); + } + QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" ); + int pos = 0; + while (( pos = rx.indexIn( oprValue, pos ) ) != -1 ) + { + oprValue.replace( pos + 1, 1, "_" ); + pos += 1; + } + oprValue.replace( escape + singleChar, singleChar ); + } + if ( !escape.isEmpty() && escape != "\\" ) + { + oprValue.replace( escape + escape, escape ); + } + opRight = new QgsExpression::NodeLiteral( oprValue ); + } + expr = new QgsExpression::NodeBinaryOperator( static_cast< QgsExpression::BinaryOperator >( op ), expr, opRight ); } @@ -2289,13 +2356,13 @@ QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const if ( op == QgsExpression::boILike ) boElem.setAttribute( "matchCase", "false" ); - // setup wildcards to + // setup wildCards to boElem.setAttribute( "wildCard", "%" ); - boElem.setAttribute( "singleChar", "?" ); + boElem.setAttribute( "singleChar", "_" ); if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 ) - boElem.setAttribute( "escape", "!" ); + boElem.setAttribute( "escape", "\\" ); else - boElem.setAttribute( "escapeChar", "!" ); + boElem.setAttribute( "escapeChar", "\\" ); } boElem.appendChild( leftElem ); @@ -2712,6 +2779,8 @@ QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement: opText = "PropertyIsGreaterThan"; else if ( op == QgsSQLStatement::boLike ) opText = "PropertyIsLike"; + else if ( op == QgsSQLStatement::boILike ) + opText = "PropertyIsLike"; if ( opText.isEmpty() ) { @@ -2727,13 +2796,13 @@ QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement: if ( op == QgsSQLStatement::boILike ) boElem.setAttribute( "matchCase", "false" ); - // setup wildcards to + // setup wildCards to boElem.setAttribute( "wildCard", "%" ); - boElem.setAttribute( "singleChar", "?" ); + boElem.setAttribute( "singleChar", "_" ); if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 ) - boElem.setAttribute( "escape", "!" ); + boElem.setAttribute( "escape", "\\" ); else - boElem.setAttribute( "escapeChar", "!" ); + boElem.setAttribute( "escapeChar", "\\" ); } boElem.appendChild( leftElem ); diff --git a/src/core/qgsogrutils.cpp b/src/core/qgsogrutils.cpp index a1833aa1741..3f1da65e0be 100644 --- a/src/core/qgsogrutils.cpp +++ b/src/core/qgsogrutils.cpp @@ -274,7 +274,7 @@ QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString& string, const Qg } OGR_DS_Destroy( hDS ); - VSIUnlink( "/vsimem/clipboard.dat" ); + VSIUnlink( TO8( randomFileName ) ); return features; } diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index af7c65606f4..e3f33f5754a 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -3946,7 +3946,7 @@ void QgsPalLabeling::clearActiveLayer( const QString &layerID ) } -int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) +int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) { if ( !willUseLayer( layer ) ) { @@ -3974,7 +3974,7 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, return 1; // init successful } -int QgsPalLabeling::prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) +int QgsPalLabeling::prepareDiagramLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) { QgsVectorLayerDiagramProvider* dp = new QgsVectorLayerDiagramProvider( layer, false ); // need to be added before calling prepare() - uses map settings from engine diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index 7e934e8c363..ef3635b10d5 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -135,11 +135,11 @@ class CORE_EXPORT QgsLabelingEngineInterface //! clears data defined objects from PAL layer settings for a registered layer virtual void clearActiveLayer( const QString& layerID ) = 0; //! called when starting rendering of a layer - virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0; + virtual int prepareLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) = 0; //! adds a diagram layer to the labeling engine //! @note added in QGIS 2.12 - virtual int prepareDiagramLayer( QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx ) + virtual int prepareDiagramLayer( QgsVectorLayer *layer, QSet& attrNames, QgsRenderContext &ctx ) { Q_UNUSED( layer ); Q_UNUSED( attrNames ); Q_UNUSED( ctx ); return 0; } //! called for every feature @@ -1037,10 +1037,10 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface //! clears data defined objects from PAL layer settings for a registered layer virtual void clearActiveLayer( const QString& layerID ) override; //! hook called when drawing layer before issuing select() - virtual int prepareLayer( QgsVectorLayer* layer, QStringList &attrNames, QgsRenderContext& ctx ) override; + virtual int prepareLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) override; //! adds a diagram layer to the labeling engine //! @note added in QGIS 2.12 - virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) override; + virtual int prepareDiagramLayer( QgsVectorLayer* layer, QSet& attrNames, QgsRenderContext& ctx ) override; /** Register a feature for labelling. * @param layerID string identifying layer associated with label diff --git a/src/core/qgsprojectproperty.cpp b/src/core/qgsprojectproperty.cpp index df756ceb406..221edea3d9f 100644 --- a/src/core/qgsprojectproperty.cpp +++ b/src/core/qgsprojectproperty.cpp @@ -21,6 +21,18 @@ #include #include +QgsProperty::QgsProperty() +{ +} + +QgsProperty::~QgsProperty() +{ +} + +QgsPropertyValue::~QgsPropertyValue() +{ +} + void QgsPropertyValue::dump( int tabs ) const { QString tabString; diff --git a/src/core/qgsprojectproperty.h b/src/core/qgsprojectproperty.h index 0a4b6e8b4d4..8eae560b0fe 100644 --- a/src/core/qgsprojectproperty.h +++ b/src/core/qgsprojectproperty.h @@ -47,12 +47,8 @@ class QDomDocument; class CORE_EXPORT QgsProperty { public: - - QgsProperty() - {} - - virtual ~QgsProperty() - {} + QgsProperty(); + virtual ~QgsProperty(); /** Dumps out the keys and values * @@ -125,7 +121,7 @@ class CORE_EXPORT QgsPropertyValue : public QgsProperty : value_( value ) {} - virtual ~QgsPropertyValue() {} + virtual ~QgsPropertyValue(); /** Returns true if is a QgsPropertyKey */ virtual bool isKey() const override { return false; } @@ -208,7 +204,7 @@ class CORE_EXPORT QgsPropertyKey : public QgsProperty /// add the given property key - QgsPropertyKey * addKey( const QString & keyName ) + QgsPropertyKey *addKey( const QString & keyName ) { delete mProperties.take( keyName ); mProperties.insert( keyName, new QgsPropertyKey( keyName ) ); diff --git a/src/core/qgsrelation.cpp b/src/core/qgsrelation.cpp index bdc3997d628..b2c67f8ee9e 100644 --- a/src/core/qgsrelation.cpp +++ b/src/core/qgsrelation.cpp @@ -248,6 +248,16 @@ QString QgsRelation::id() const return mRelationId; } +void QgsRelation::generateId() +{ + mRelationId = QString( "%1_%2_%3_%4" ) + .arg( referencingLayerId(), + mFieldPairs.at( 0 ).referencingField(), + referencedLayerId(), + mFieldPairs.at( 0 ).referencedField() ); + updateRelationStatus(); +} + QString QgsRelation::referencingLayerId() const { return mReferencingLayerId; @@ -301,6 +311,11 @@ bool QgsRelation::isValid() const return mValid; } +bool QgsRelation::hasEqualDefinition( const QgsRelation& other ) const +{ + return mReferencedLayerId == other.mReferencedLayerId && mReferencingLayerId == other.mReferencingLayerId && mFieldPairs == other.mFieldPairs; +} + void QgsRelation::updateRelationStatus() { const QMap& mapLayers = QgsMapLayerRegistry::instance()->mapLayers(); diff --git a/src/core/qgsrelation.h b/src/core/qgsrelation.h index 26fe3d56029..3cc48dcfe20 100644 --- a/src/core/qgsrelation.h +++ b/src/core/qgsrelation.h @@ -57,6 +57,8 @@ class CORE_EXPORT QgsRelation QString referencingField() const { return first; } //! Get the name of the referenced (parent) field QString referencedField() const { return second; } + + bool operator==( const FieldPair& other ) const { return first == other.first && second == other.second; } }; /** @@ -210,6 +212,12 @@ class CORE_EXPORT QgsRelation */ QString id() const; + /** + * Generate a (project-wide) unique id for this relation + * @note added in QGIS 3.0 + */ + void generateId(); + /** * Access the referencing (child) layer's id * This is the layer which has the field(s) which point to another layer @@ -272,6 +280,15 @@ class CORE_EXPORT QgsRelation */ bool isValid() const; + /** + * Compares the two QgsRelation, ignoring the name and the ID. + * + * @param other The other relation + * @return true if they are similar + * @note added in QGIS 3.0 + */ + bool hasEqualDefinition( const QgsRelation& other ) const; + protected: /** * Updates the validity status of this relation. diff --git a/src/core/qgsrelationmanager.cpp b/src/core/qgsrelationmanager.cpp index e077178c362..48e3d132e1c 100644 --- a/src/core/qgsrelationmanager.cpp +++ b/src/core/qgsrelationmanager.cpp @@ -19,6 +19,7 @@ #include "qgslogger.h" #include "qgsmaplayerregistry.h" #include "qgsproject.h" +#include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" QgsRelationManager::QgsRelationManager( QgsProject* project ) @@ -217,3 +218,28 @@ void QgsRelationManager::layersRemoved( const QStringList& layers ) emit changed(); } } + +static bool hasRelationWithEqualDefinition( const QList& existingRelations, const QgsRelation& relation ) +{ + Q_FOREACH ( const QgsRelation& cur, existingRelations ) + { + if ( cur.hasEqualDefinition( relation ) ) return true; + } + return false; +} + +QList QgsRelationManager::discoverRelations( const QList& existingRelations, const QList& layers ) +{ + QList result; + Q_FOREACH ( const QgsVectorLayer* layer, layers ) + { + Q_FOREACH ( const QgsRelation& relation, layer->dataProvider()->discoverRelations( layer, layers ) ) + { + if ( !hasRelationWithEqualDefinition( existingRelations, relation ) ) + { + result.append( relation ); + } + } + } + return result; +} diff --git a/src/core/qgsrelationmanager.h b/src/core/qgsrelationmanager.h index ff4d3c41c27..30450c6686d 100644 --- a/src/core/qgsrelationmanager.h +++ b/src/core/qgsrelationmanager.h @@ -117,6 +117,16 @@ class CORE_EXPORT QgsRelationManager : public QObject */ QList referencedRelations( QgsVectorLayer *layer = nullptr ) const; + /** + * Discover all the relations available from the current layers. + * + * @param existingRelations the existing relations to filter them out + * @param layers the current layers + * @return the list of discovered relations + * @note added in QGIS 3.0 + */ + static QList discoverRelations( const QList& existingRelations, const QList& layers ); + signals: /** This signal is emitted when the relations were loaded after reading a project */ void relationsLoaded(); diff --git a/src/core/qgsrulebasedlabeling.cpp b/src/core/qgsrulebasedlabeling.cpp index 840843a8af9..1846db12d88 100644 --- a/src/core/qgsrulebasedlabeling.cpp +++ b/src/core/qgsrulebasedlabeling.cpp @@ -32,7 +32,7 @@ QgsVectorLayerLabelProvider *QgsRuleBasedLabelProvider::createProvider( QgsVecto return new QgsVectorLayerLabelProvider( layer, providerId, withFeatureLoop, settings ); } -bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) +bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QSet& attributeNames ) { Q_FOREACH ( QgsVectorLayerLabelProvider* provider, mSubProviders ) provider->setEngine( mEngine ); @@ -264,7 +264,7 @@ void QgsRuleBasedLabeling::Rule::createSubProviders( QgsVectorLayer* layer, QgsR } } -void QgsRuleBasedLabeling::Rule::prepare( const QgsRenderContext& context, QStringList& attributeNames, QgsRuleBasedLabeling::RuleToProviderMap& subProviders ) +void QgsRuleBasedLabeling::Rule::prepare( const QgsRenderContext& context, QSet& attributeNames, QgsRuleBasedLabeling::RuleToProviderMap& subProviders ) { if ( mSettings ) { @@ -278,7 +278,7 @@ void QgsRuleBasedLabeling::Rule::prepare( const QgsRenderContext& context, QStri if ( mFilter ) { - attributeNames << mFilter->referencedColumns(); + attributeNames.unite( mFilter->referencedColumns() ); mFilter->prepare( &context.expressionContext() ); } diff --git a/src/core/qgsrulebasedlabeling.h b/src/core/qgsrulebasedlabeling.h index 3d778f4656a..addc6449500 100644 --- a/src/core/qgsrulebasedlabeling.h +++ b/src/core/qgsrulebasedlabeling.h @@ -238,7 +238,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling void subProviderIds( QStringList& list ) const; //! call prepare() on sub-providers and populate attributeNames - void prepare( const QgsRenderContext& context, QStringList& attributeNames, RuleToProviderMap& subProviders ); + void prepare( const QgsRenderContext& context, QSet& attributeNames, RuleToProviderMap& subProviders ); //! register individual features RegisterResult registerFeature( QgsFeature& feature, QgsRenderContext& context, RuleToProviderMap& subProviders, QgsGeometry* obstacleGeometry = nullptr ); @@ -330,7 +330,7 @@ class CORE_EXPORT QgsRuleBasedLabelProvider : public QgsVectorLayerLabelProvider // reimplemented - virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ) override; + virtual bool prepare( const QgsRenderContext& context, QSet& attributeNames ) override; virtual void registerFeature( QgsFeature& feature, QgsRenderContext& context, QgsGeometry* obstacleGeometry = nullptr ) override; diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index 5a0f73229a9..b689183db89 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -718,3 +718,8 @@ QgsGeometry* QgsVectorDataProvider::convertToProviderType( const QgsGeometry& ge } QStringList QgsVectorDataProvider::smEncodings; + +QList QgsVectorDataProvider::discoverRelations( const QgsVectorLayer*, const QList& ) const +{ + return QList(); +} \ No newline at end of file diff --git a/src/core/qgsvectordataprovider.h b/src/core/qgsvectordataprovider.h index 7d092d5e6a7..2addd3ac4c9 100644 --- a/src/core/qgsvectordataprovider.h +++ b/src/core/qgsvectordataprovider.h @@ -28,6 +28,7 @@ class QTextCodec; #include "qgsfeature.h" #include "qgsaggregatecalculator.h" #include "qgsmaplayerdependency.h" +#include "qgsrelation.h" typedef QList QgsAttributeList; typedef QSet QgsAttributeIds; @@ -433,6 +434,15 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider */ virtual QSet dependencies() const; + /** + * Discover the available relations with the given layers. + * @param self the layer using this data provider. + * @param layers the other layers. + * @return the list of N-1 relations from this provider. + * @note added in QGIS 3.0 + */ + virtual QList discoverRelations( const QgsVectorLayer* self, const QList& layers ) const; + signals: /** Signals an error in this provider */ void raiseError( const QString& msg ); diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 482fcd65856..b74dce3c74c 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -2863,13 +2863,13 @@ void QgsVectorFileWriter::addRendererAttributes( QgsVectorLayer* vl, QgsAttribut QgsFeatureRenderer* renderer = symbologyRenderer( vl ); if ( renderer ) { - QList rendererAttributes = renderer->usedAttributes(); - for ( int i = 0; i < rendererAttributes.size(); ++i ) + const QSet rendererAttributes = renderer->usedAttributes(); + for ( const QString& attr : rendererAttributes ) { - int index = vl->fields().lookupField( rendererAttributes.at( i ) ); + int index = vl->fields().lookupField( attr ); if ( index != -1 ) { - attList.push_back( vl->fields().lookupField( rendererAttributes.at( i ) ) ); + attList.append( index ); } } } diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 56623d5d51b..23c0cabfb07 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2125,10 +2125,41 @@ void QgsVectorLayer::removeFieldAlias( int attIndex ) bool QgsVectorLayer::renameAttribute( int index, const QString& newName ) { - if ( !mEditBuffer || !mDataProvider ) + if ( index < 0 || index >= fields().count() ) return false; - return mEditBuffer->renameAttribute( index, newName ); + switch ( mFields.fieldOrigin( index ) ) + { + case QgsFields::OriginExpression: + { + if ( mExpressionFieldBuffer ) + { + int oi = mFields.fieldOriginIndex( index ); + mExpressionFieldBuffer->renameExpression( oi, newName ); + updateFields(); + return true; + } + else + { + return false; + } + } + + case QgsFields::OriginProvider: + case QgsFields::OriginEdit: + + if ( !mEditBuffer || !mDataProvider ) + return false; + + return mEditBuffer->renameAttribute( index, newName ); + + case QgsFields::OriginJoin: + case QgsFields::OriginUnknown: + return false; + + } + + return false; // avoid warning } void QgsVectorLayer::setFieldAlias( int attIndex, const QString& aliasString ) @@ -3306,9 +3337,9 @@ QList QgsVectorLayer::getValues( const QString &fieldOrExpression, boo } QgsFeature f; - QStringList lst; + QSet lst; if ( expression.isNull() ) - lst.append( fieldOrExpression ); + lst.insert( fieldOrExpression ); else lst = expression->referencedColumns(); diff --git a/src/core/qgsvectorlayerdiagramprovider.cpp b/src/core/qgsvectorlayerdiagramprovider.cpp index ddaf4f97e12..6e9ada148c8 100644 --- a/src/core/qgsvectorlayerdiagramprovider.cpp +++ b/src/core/qgsvectorlayerdiagramprovider.cpp @@ -87,7 +87,7 @@ QList QgsVectorLayerDiagramProvider::labelFeatures( QgsRenderC return mFeatures; } - QStringList attributeNames; + QSet attributeNames; if ( !prepare( context, attributeNames ) ) return QList(); @@ -155,7 +155,7 @@ void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext& context, pal::L } -bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) +bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QSet& attributeNames ) { QgsDiagramLayerSettings& s2 = mSettings; const QgsMapSettings& mapSettings = mEngine->mapSettings(); @@ -177,11 +177,7 @@ bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QS s2.setRenderer( mDiagRenderer ); //add attributes needed by the diagram renderer - Q_FOREACH ( const QString& field, s2.referencedFields( context.expressionContext(), mFields ) ) - { - if ( !attributeNames.contains( field ) ) - attributeNames << field; - } + attributeNames.unite( s2.referencedFields( context.expressionContext(), mFields ) ); return true; } diff --git a/src/core/qgsvectorlayerdiagramprovider.h b/src/core/qgsvectorlayerdiagramprovider.h index 7d93dcfd41d..8db71242163 100644 --- a/src/core/qgsvectorlayerdiagramprovider.h +++ b/src/core/qgsvectorlayerdiagramprovider.h @@ -85,7 +85,7 @@ class CORE_EXPORT QgsVectorLayerDiagramProvider : public QgsAbstractLabelProvide * @param attributeNames list of attribute names to which additional required attributes shall be added * @return Whether the preparation was successful - if not, the provider shall not be used */ - virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ); + virtual bool prepare( const QgsRenderContext& context, QSet& attributeNames ); /** * Register a feature for labeling as one or more QgsLabelFeature objects stored into mFeatures diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index 3dedaab2520..0ce998f1ae9 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -128,7 +128,7 @@ QgsVectorLayerLabelProvider::~QgsVectorLayerLabelProvider() } -bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) +bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QSet& attributeNames ) { QgsPalLayerSettings& lyr = mSettings; const QgsMapSettings& mapSettings = mEngine->mapSettings(); @@ -178,12 +178,12 @@ bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QStr Q_FOREACH ( const QString& name, exp->referencedColumns() ) { QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 ); - attributeNames.append( name ); + attributeNames.insert( name ); } } else { - attributeNames.append( lyr.fieldName ); + attributeNames.insert( lyr.fieldName ); } // add field indices of data defined expression or field @@ -198,12 +198,12 @@ bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext& context, QStr // this will return columns for expressions or field name, depending upon what is set to be used // this also prepares any expressions, too - QStringList cols = dd->referencedColumns( context.expressionContext() ); + QSet cols = dd->referencedColumns( context.expressionContext() ); //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 ); Q_FOREACH ( const QString& name, cols ) { - attributeNames.append( name ); + attributeNames.insert( name ); } } } @@ -258,7 +258,7 @@ QList QgsVectorLayerLabelProvider::labelFeatures( QgsRenderCon return mLabels; } - QStringList attrNames; + QSet attrNames; if ( !prepare( ctx, attrNames ) ) return QList(); diff --git a/src/core/qgsvectorlayerlabelprovider.h b/src/core/qgsvectorlayerlabelprovider.h index c24adc42225..c3b88ca2f7f 100644 --- a/src/core/qgsvectorlayerlabelprovider.h +++ b/src/core/qgsvectorlayerlabelprovider.h @@ -66,7 +66,7 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider * @param attributeNames list of attribute names to which additional required attributes shall be added * @return Whether the preparation was successful - if not, the provider shall not be used */ - virtual bool prepare( const QgsRenderContext& context, QStringList& attributeNames ); + virtual bool prepare( const QgsRenderContext& context, QSet& attributeNames ); /** * Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index f07ed9fc773..9aec1cf58cb 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -534,7 +534,7 @@ void QgsVectorLayerRenderer::stopRenderer( QgsSingleSymbolRenderer* selRenderer -void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList& attributeNames ) +void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QSet& attributeNames ) { if ( !mContext.labelingEngine() ) { @@ -591,7 +591,7 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer* layer, QStringList } } -void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList& attributeNames ) +void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QSet& attributeNames ) { if ( !mContext.labelingEngine() ) { diff --git a/src/core/qgsvectorlayerrenderer.h b/src/core/qgsvectorlayerrenderer.h index 3a4ba17bf7c..a74ef62dfef 100644 --- a/src/core/qgsvectorlayerrenderer.h +++ b/src/core/qgsvectorlayerrenderer.h @@ -82,8 +82,8 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer @param layer diagram layer @param attributeNames attributes needed for labeling and diagrams will be added to the list */ - void prepareLabeling( QgsVectorLayer* layer, QStringList& attributeNames ); - void prepareDiagrams( QgsVectorLayer* layer, QStringList& attributeNames ); + void prepareLabeling( QgsVectorLayer* layer, QSet& attributeNames ); + void prepareDiagrams( QgsVectorLayer* layer, QSet& attributeNames ); /** Draw layer with renderer V2. QgsFeatureRenderer::startRender() needs to be called before using this method */ @@ -122,7 +122,7 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer QgsWkbTypes::GeometryType mGeometryType; - QStringList mAttrNames; + QSet mAttrNames; //! used with old labeling engine (QgsPalLabeling): whether labeling is enabled bool mLabeling; diff --git a/src/core/symbology-ng/qgs25drenderer.cpp b/src/core/symbology-ng/qgs25drenderer.cpp index 7f9bbcda1d3..5fc3ea2b11b 100644 --- a/src/core/symbology-ng/qgs25drenderer.cpp +++ b/src/core/symbology-ng/qgs25drenderer.cpp @@ -146,9 +146,9 @@ void Qgs25DRenderer::stopRender( QgsRenderContext& context ) mSymbol->stopRender( context ); } -QList Qgs25DRenderer::usedAttributes() +QSet Qgs25DRenderer::usedAttributes() const { - return mSymbol->usedAttributes().toList(); + return mSymbol->usedAttributes(); } QgsFeatureRenderer* Qgs25DRenderer::clone() const diff --git a/src/core/symbology-ng/qgs25drenderer.h b/src/core/symbology-ng/qgs25drenderer.h index 6d0a9a438d6..3708cd1da94 100644 --- a/src/core/symbology-ng/qgs25drenderer.h +++ b/src/core/symbology-ng/qgs25drenderer.h @@ -39,7 +39,7 @@ class CORE_EXPORT Qgs25DRenderer : public QgsFeatureRenderer void startRender( QgsRenderContext& context, const QgsFields& fields ) override; void stopRender( QgsRenderContext& context ) override; - QList usedAttributes() override; + QSet usedAttributes() const override; QgsFeatureRenderer* clone() const override; virtual QgsSymbol* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override; diff --git a/src/core/symbology-ng/qgscategorizedsymbolrenderer.cpp b/src/core/symbology-ng/qgscategorizedsymbolrenderer.cpp index 3e0aeccd52a..19617dc7c09 100644 --- a/src/core/symbology-ng/qgscategorizedsymbolrenderer.cpp +++ b/src/core/symbology-ng/qgscategorizedsymbolrenderer.cpp @@ -430,7 +430,7 @@ void QgsCategorizedSymbolRenderer::stopRender( QgsRenderContext& context ) mExpression.reset(); } -QList QgsCategorizedSymbolRenderer::usedAttributes() +QSet QgsCategorizedSymbolRenderer::usedAttributes() const { QSet attributes; @@ -442,7 +442,7 @@ QList QgsCategorizedSymbolRenderer::usedAttributes() QgsExpression testExpr( mAttrName ); if ( !testExpr.hasParserError() ) - attributes.unite( testExpr.referencedColumns().toSet() ); + attributes.unite( testExpr.referencedColumns() ); QgsCategoryList::const_iterator catIt = mCategories.constBegin(); for ( ; catIt != mCategories.constEnd(); ++catIt ) @@ -453,7 +453,7 @@ QList QgsCategorizedSymbolRenderer::usedAttributes() attributes.unite( catSymbol->usedAttributes() ); } } - return attributes.toList(); + return attributes; } QString QgsCategorizedSymbolRenderer::dump() const diff --git a/src/core/symbology-ng/qgscategorizedsymbolrenderer.h b/src/core/symbology-ng/qgscategorizedsymbolrenderer.h index 2d0f02a57f4..37302a87342 100644 --- a/src/core/symbology-ng/qgscategorizedsymbolrenderer.h +++ b/src/core/symbology-ng/qgscategorizedsymbolrenderer.h @@ -86,7 +86,7 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer virtual QgsSymbol* originalSymbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override; virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override; virtual void stopRender( QgsRenderContext& context ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual QString dump() const override; virtual QgsCategorizedSymbolRenderer* clone() const override; virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override; diff --git a/src/core/symbology-ng/qgsgeometrygeneratorsymbollayer.cpp b/src/core/symbology-ng/qgsgeometrygeneratorsymbollayer.cpp index 307f515a6b8..898685dae0e 100644 --- a/src/core/symbology-ng/qgsgeometrygeneratorsymbollayer.cpp +++ b/src/core/symbology-ng/qgsgeometrygeneratorsymbollayer.cpp @@ -178,7 +178,7 @@ bool QgsGeometryGeneratorSymbolLayer::setSubSymbol( QgsSymbol* symbol ) QSet QgsGeometryGeneratorSymbolLayer::usedAttributes() const { - return mSymbol->usedAttributes() + mExpression->referencedColumns().toSet(); + return mSymbol->usedAttributes() + mExpression->referencedColumns(); } bool QgsGeometryGeneratorSymbolLayer::isCompatibleWithSymbol( QgsSymbol* symbol ) const diff --git a/src/core/symbology-ng/qgsgraduatedsymbolrenderer.cpp b/src/core/symbology-ng/qgsgraduatedsymbolrenderer.cpp index 884acdd4a0a..fb312469269 100644 --- a/src/core/symbology-ng/qgsgraduatedsymbolrenderer.cpp +++ b/src/core/symbology-ng/qgsgraduatedsymbolrenderer.cpp @@ -412,7 +412,7 @@ void QgsGraduatedSymbolRenderer::stopRender( QgsRenderContext& context ) } } -QList QgsGraduatedSymbolRenderer::usedAttributes() +QSet QgsGraduatedSymbolRenderer::usedAttributes() const { QSet attributes; @@ -424,7 +424,7 @@ QList QgsGraduatedSymbolRenderer::usedAttributes() QgsExpression testExpr( mAttrName ); if ( !testExpr.hasParserError() ) - attributes.unite( testExpr.referencedColumns().toSet() ); + attributes.unite( testExpr.referencedColumns() ); QgsRangeList::const_iterator range_it = mRanges.constBegin(); for ( ; range_it != mRanges.constEnd(); ++range_it ) @@ -435,7 +435,7 @@ QList QgsGraduatedSymbolRenderer::usedAttributes() attributes.unite( symbol->usedAttributes() ); } } - return attributes.toList(); + return attributes; } bool QgsGraduatedSymbolRenderer::updateRangeSymbol( int rangeIndex, QgsSymbol* symbol ) diff --git a/src/core/symbology-ng/qgsgraduatedsymbolrenderer.h b/src/core/symbology-ng/qgsgraduatedsymbolrenderer.h index a0be327423d..8876567dd3b 100644 --- a/src/core/symbology-ng/qgsgraduatedsymbolrenderer.h +++ b/src/core/symbology-ng/qgsgraduatedsymbolrenderer.h @@ -140,7 +140,7 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer : public QgsFeatureRenderer virtual QgsSymbol* originalSymbolForFeature( QgsFeature& feature, QgsRenderContext &context ) override; virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override; virtual void stopRender( QgsRenderContext& context ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual QString dump() const override; virtual QgsGraduatedSymbolRenderer* clone() const override; virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override; diff --git a/src/core/symbology-ng/qgsheatmaprenderer.cpp b/src/core/symbology-ng/qgsheatmaprenderer.cpp index bc065011a25..34ba0532ef4 100644 --- a/src/core/symbology-ng/qgsheatmaprenderer.cpp +++ b/src/core/symbology-ng/qgsheatmaprenderer.cpp @@ -384,7 +384,7 @@ QgsSymbolList QgsHeatmapRenderer::symbols( QgsRenderContext& ) return QgsSymbolList(); } -QList QgsHeatmapRenderer::usedAttributes() +QSet QgsHeatmapRenderer::usedAttributes() const { QSet attributes; @@ -396,9 +396,9 @@ QList QgsHeatmapRenderer::usedAttributes() QgsExpression testExpr( mWeightExpressionString ); if ( !testExpr.hasParserError() ) - attributes.unite( testExpr.referencedColumns().toSet() ); + attributes.unite( testExpr.referencedColumns() ); - return attributes.toList(); + return attributes; } QgsHeatmapRenderer* QgsHeatmapRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer ) diff --git a/src/core/symbology-ng/qgsheatmaprenderer.h b/src/core/symbology-ng/qgsheatmaprenderer.h index 125ad362175..70cea65df7d 100644 --- a/src/core/symbology-ng/qgsheatmaprenderer.h +++ b/src/core/symbology-ng/qgsheatmaprenderer.h @@ -46,7 +46,7 @@ class CORE_EXPORT QgsHeatmapRenderer : public QgsFeatureRenderer //! @note symbol2 in python bindings virtual QgsSymbolList symbols( QgsRenderContext &context ) override; virtual QString dump() const override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; static QgsFeatureRenderer* create( QDomElement& element ); virtual QDomElement save( QDomDocument& doc ) override; static QgsHeatmapRenderer* convertFromRenderer( const QgsFeatureRenderer* renderer ); diff --git a/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp index b246bb52d23..1c27ea8d9e3 100644 --- a/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp +++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp @@ -464,11 +464,11 @@ QgsFeatureRenderer::Capabilities QgsInvertedPolygonRenderer::capabilities() return mSubRenderer->capabilities(); } -QList QgsInvertedPolygonRenderer::usedAttributes() +QSet QgsInvertedPolygonRenderer::usedAttributes() const { if ( !mSubRenderer ) { - return QList(); + return QSet(); } return mSubRenderer->usedAttributes(); } diff --git a/src/core/symbology-ng/qgsinvertedpolygonrenderer.h b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h index a33b0ab479f..a7e510acff2 100644 --- a/src/core/symbology-ng/qgsinvertedpolygonrenderer.h +++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h @@ -71,7 +71,7 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRenderer virtual QString dump() const override; /** Proxy that will call this method on the embedded renderer. */ - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; /** Proxy that will call this method on the embedded renderer. */ virtual Capabilities capabilities() override; /** Proxy that will call this method on the embedded renderer. diff --git a/src/core/symbology-ng/qgsnullsymbolrenderer.cpp b/src/core/symbology-ng/qgsnullsymbolrenderer.cpp index a013c2c5e75..5dabae67449 100644 --- a/src/core/symbology-ng/qgsnullsymbolrenderer.cpp +++ b/src/core/symbology-ng/qgsnullsymbolrenderer.cpp @@ -84,9 +84,9 @@ bool QgsNullSymbolRenderer::willRenderFeature( QgsFeature&, QgsRenderContext& ) return true; } -QList QgsNullSymbolRenderer::usedAttributes() +QSet QgsNullSymbolRenderer::usedAttributes() const { - return QList(); + return QSet(); } QString QgsNullSymbolRenderer::dump() const diff --git a/src/core/symbology-ng/qgsnullsymbolrenderer.h b/src/core/symbology-ng/qgsnullsymbolrenderer.h index eae2f8d0b29..9defdf3b1b9 100644 --- a/src/core/symbology-ng/qgsnullsymbolrenderer.h +++ b/src/core/symbology-ng/qgsnullsymbolrenderer.h @@ -42,7 +42,7 @@ class CORE_EXPORT QgsNullSymbolRenderer : public QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ) override; virtual bool willRenderFeature( QgsFeature& feat, QgsRenderContext& context ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual QString dump() const override; virtual QgsFeatureRenderer* clone() const override; virtual QgsSymbolList symbols( QgsRenderContext& context ) override; diff --git a/src/core/symbology-ng/qgspointclusterrenderer.cpp b/src/core/symbology-ng/qgspointclusterrenderer.cpp index 5bd0f11a85d..d0a7f01b03b 100644 --- a/src/core/symbology-ng/qgspointclusterrenderer.cpp +++ b/src/core/symbology-ng/qgspointclusterrenderer.cpp @@ -157,11 +157,11 @@ QDomElement QgsPointClusterRenderer::save( QDomDocument& doc ) return rendererElement; } -QList QgsPointClusterRenderer::usedAttributes() +QSet QgsPointClusterRenderer::usedAttributes() const { - QList attr = QgsPointDistanceRenderer::usedAttributes(); + QSet attr = QgsPointDistanceRenderer::usedAttributes(); if ( mClusterSymbol ) - attr.append( mClusterSymbol->usedAttributes().toList() ); + attr.unite( mClusterSymbol->usedAttributes() ); return attr; } diff --git a/src/core/symbology-ng/qgspointclusterrenderer.h b/src/core/symbology-ng/qgspointclusterrenderer.h index 575f03b1ad0..600de1d8a79 100644 --- a/src/core/symbology-ng/qgspointclusterrenderer.h +++ b/src/core/symbology-ng/qgspointclusterrenderer.h @@ -35,7 +35,7 @@ class CORE_EXPORT QgsPointClusterRenderer: public QgsPointDistanceRenderer virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override; void stopRender( QgsRenderContext& context ) override; QDomElement save( QDomDocument& doc ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; //! Creates a renderer from XML element static QgsFeatureRenderer* create( QDomElement& symbologyElem ); diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp index 74b7676341b..b189cf5db82 100644 --- a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp +++ b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp @@ -215,11 +215,11 @@ QDomElement QgsPointDisplacementRenderer::save( QDomDocument& doc ) return rendererElement; } -QList QgsPointDisplacementRenderer::usedAttributes() +QSet QgsPointDisplacementRenderer::usedAttributes() const { - QList attr = QgsPointDistanceRenderer::usedAttributes(); + QSet attr = QgsPointDistanceRenderer::usedAttributes(); if ( mCenterSymbol ) - attr.append( mCenterSymbol->usedAttributes().toList() ); + attr.unite( mCenterSymbol->usedAttributes() ); return attr; } diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.h b/src/core/symbology-ng/qgspointdisplacementrenderer.h index f5a4f66d46f..8a1e5cfade7 100644 --- a/src/core/symbology-ng/qgspointdisplacementrenderer.h +++ b/src/core/symbology-ng/qgspointdisplacementrenderer.h @@ -45,7 +45,7 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsPointDistanceRenderer virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override; void stopRender( QgsRenderContext& context ) override; QDomElement save( QDomDocument& doc ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; //! Create a renderer from XML element static QgsFeatureRenderer* create( QDomElement& symbologyElem ); diff --git a/src/core/symbology-ng/qgspointdistancerenderer.cpp b/src/core/symbology-ng/qgspointdistancerenderer.cpp index 626e1f62a7f..e67ef1dcb1b 100644 --- a/src/core/symbology-ng/qgspointdistancerenderer.cpp +++ b/src/core/symbology-ng/qgspointdistancerenderer.cpp @@ -204,12 +204,12 @@ QString QgsPointDistanceRenderer::filter( const QgsFields& fields ) return mRenderer->filter( fields ); } -QList QgsPointDistanceRenderer::usedAttributes() +QSet QgsPointDistanceRenderer::usedAttributes() const { - QList attributeList; + QSet attributeList; if ( !mLabelAttributeName.isEmpty() ) { - attributeList.push_back( mLabelAttributeName ); + attributeList.insert( mLabelAttributeName ); } if ( mRenderer ) { diff --git a/src/core/symbology-ng/qgspointdistancerenderer.h b/src/core/symbology-ng/qgspointdistancerenderer.h index 0dc13627468..758a9ebf7c9 100644 --- a/src/core/symbology-ng/qgspointdistancerenderer.h +++ b/src/core/symbology-ng/qgspointdistancerenderer.h @@ -76,7 +76,7 @@ class CORE_EXPORT QgsPointDistanceRenderer: public QgsFeatureRenderer virtual void toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props = QgsStringMap() ) const override; bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual Capabilities capabilities() override; virtual QgsSymbolList symbols( QgsRenderContext& context ) override; virtual QgsSymbol* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override; diff --git a/src/core/symbology-ng/qgsrenderer.h b/src/core/symbology-ng/qgsrenderer.h index 55494d7598e..b333413a82d 100644 --- a/src/core/symbology-ng/qgsrenderer.h +++ b/src/core/symbology-ng/qgsrenderer.h @@ -148,8 +148,7 @@ class CORE_EXPORT QgsFeatureRenderer * * @return A set of attributes */ - // TODO QGIS3: Change QList to QSet - virtual QList usedAttributes() = 0; + virtual QSet usedAttributes() const = 0; /** * Returns true if this renderer requires the geometry to apply the filter. diff --git a/src/core/symbology-ng/qgsrulebasedrenderer.cpp b/src/core/symbology-ng/qgsrulebasedrenderer.cpp index ec50e51184f..db7b25c6c13 100644 --- a/src/core/symbology-ng/qgsrulebasedrenderer.cpp +++ b/src/core/symbology-ng/qgsrulebasedrenderer.cpp @@ -185,7 +185,7 @@ QSet QgsRuleBasedRenderer::Rule::usedAttributes() const // attributes needed by this rule QSet attrs; if ( mFilter ) - attrs.unite( mFilter->referencedColumns().toSet() ); + attrs.unite( mFilter->referencedColumns() ); if ( mSymbol ) attrs.unite( mSymbol->usedAttributes() ); @@ -910,10 +910,9 @@ QString QgsRuleBasedRenderer::filter( const QgsFields& ) return mFilter; } -QList QgsRuleBasedRenderer::usedAttributes() +QSet QgsRuleBasedRenderer::usedAttributes() const { - QSet attrs = mRootRule->usedAttributes(); - return attrs.toList(); + return mRootRule->usedAttributes(); } bool QgsRuleBasedRenderer::filterNeedsGeometry() const diff --git a/src/core/symbology-ng/qgsrulebasedrenderer.h b/src/core/symbology-ng/qgsrulebasedrenderer.h index 57be8f7463c..6739c1c3e98 100644 --- a/src/core/symbology-ng/qgsrulebasedrenderer.h +++ b/src/core/symbology-ng/qgsrulebasedrenderer.h @@ -421,7 +421,7 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer virtual QString filter( const QgsFields& fields = QgsFields() ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual bool filterNeedsGeometry() const override; diff --git a/src/core/symbology-ng/qgssinglesymbolrenderer.cpp b/src/core/symbology-ng/qgssinglesymbolrenderer.cpp index fce4b14e356..dcd9e6c7b21 100644 --- a/src/core/symbology-ng/qgssinglesymbolrenderer.cpp +++ b/src/core/symbology-ng/qgssinglesymbolrenderer.cpp @@ -72,11 +72,12 @@ void QgsSingleSymbolRenderer::stopRender( QgsRenderContext& context ) mSymbol->stopRender( context ); } -QList QgsSingleSymbolRenderer::usedAttributes() +QSet QgsSingleSymbolRenderer::usedAttributes() const { QSet attributes; - if ( mSymbol.data() ) attributes.unite( mSymbol->usedAttributes() ); - return attributes.toList(); + if ( mSymbol.data() ) + attributes.unite( mSymbol->usedAttributes() ); + return attributes; } QgsSymbol* QgsSingleSymbolRenderer::symbol() const diff --git a/src/core/symbology-ng/qgssinglesymbolrenderer.h b/src/core/symbology-ng/qgssinglesymbolrenderer.h index e92f31e65a0..dc56f556be5 100644 --- a/src/core/symbology-ng/qgssinglesymbolrenderer.h +++ b/src/core/symbology-ng/qgssinglesymbolrenderer.h @@ -36,7 +36,7 @@ class CORE_EXPORT QgsSingleSymbolRenderer : public QgsFeatureRenderer virtual QgsSymbol* originalSymbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override; virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override; virtual void stopRender( QgsRenderContext& context ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; QgsSymbol* symbol() const; void setSymbol( QgsSymbol* s ); diff --git a/src/core/symbology-ng/qgssymbol.cpp b/src/core/symbology-ng/qgssymbol.cpp index 25648668d17..04e590645f3 100644 --- a/src/core/symbology-ng/qgssymbol.cpp +++ b/src/core/symbology-ng/qgssymbol.cpp @@ -47,6 +47,7 @@ #include #include +#include inline QgsDataDefined* rotateWholeSymbol( double additionalRotation, const QgsDataDefined& dd ) @@ -842,30 +843,49 @@ void QgsSymbol::renderFeature( const QgsFeature& feature, QgsRenderContext& cont const QgsGeometryCollection& geomCollection = dynamic_cast( *segmentizedGeometry.geometry() ); const unsigned int num = geomCollection.numGeometries(); + // Sort components by approximate area (probably a bit faster than using + // area() ) + std::map > mapAreaToPartNum; for ( unsigned int i = 0; i < num; ++i ) { - mSymbolRenderContext->setGeometryPartNum( i + 1 ); - mSymbolRenderContext->expressionContextScope()->setVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, i + 1 ); - - context.setGeometry( geomCollection.geometryN( i ) ); const QgsPolygonV2& polygon = dynamic_cast( *geomCollection.geometryN( i ) ); - _getPolygon( pts, holes, context, polygon, !tileMapRendering && clipFeaturesToExtent() ); - static_cast( this )->renderPolygon( pts, ( !holes.isEmpty() ? &holes : nullptr ), &feature, context, layer, selected ); + const QgsRectangle r( polygon.boundingBox() ); + mapAreaToPartNum[ r.width() * r.height()] << i; + } - if ( drawVertexMarker && !usingSegmentizedGeometry ) + // Draw starting with larger parts down to smaller parts, so that in + // case of a part being incorrectly inside another part, it is drawn + // on top of it (#15419) + std::map >::const_reverse_iterator iter = mapAreaToPartNum.rbegin(); + for ( ; iter != mapAreaToPartNum.rend(); ++iter ) + { + const QList& listPartIndex = iter->second; + for ( int idx = 0; idx < listPartIndex.size(); ++idx ) { - if ( i == 0 ) - { - markers = pts; - } - else - { - markers << pts; - } + const unsigned i = listPartIndex[idx]; + mSymbolRenderContext->setGeometryPartNum( i + 1 ); + mSymbolRenderContext->expressionContextScope()->setVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, i + 1 ); - Q_FOREACH ( const QPolygonF& hole, holes ) + context.setGeometry( geomCollection.geometryN( i ) ); + const QgsPolygonV2& polygon = dynamic_cast( *geomCollection.geometryN( i ) ); + _getPolygon( pts, holes, context, polygon, !tileMapRendering && clipFeaturesToExtent() ); + static_cast( this )->renderPolygon( pts, ( !holes.isEmpty() ? &holes : nullptr ), &feature, context, layer, selected ); + + if ( drawVertexMarker && !usingSegmentizedGeometry ) { - markers << hole; + if ( i == 0 ) + { + markers = pts; + } + else + { + markers << pts; + } + + Q_FOREACH ( const QPolygonF& hole, holes ) + { + markers << hole; + } } } } diff --git a/src/core/symbology-ng/qgssymbollayer.cpp b/src/core/symbology-ng/qgssymbollayer.cpp index ccd7cc86e14..51ba4764e87 100644 --- a/src/core/symbology-ng/qgssymbollayer.cpp +++ b/src/core/symbology-ng/qgssymbollayer.cpp @@ -303,18 +303,18 @@ bool QgsSymbolLayer::isCompatibleWithSymbol( QgsSymbol* symbol ) const QSet QgsSymbolLayer::usedAttributes() const { - QStringList columns; + QSet columns; QMap< QString, QgsDataDefined* >::const_iterator ddIt = mDataDefinedProperties.constBegin(); for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt ) { if ( ddIt.value() && ddIt.value()->isActive() ) { - columns.append( ddIt.value()->referencedColumns() ); + columns.unite( ddIt.value()->referencedColumns() ); } } - return columns.toSet(); + return columns; } void QgsSymbolLayer::saveDataDefinedProperties( QgsStringMap& stringMap ) const diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 1fa7da8522c..fb9cc6bc2d6 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -832,7 +832,7 @@ ELSE(NOT APPLE) FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis2_gui + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_gui BUILD_WITH_INSTALL_RPATH TRUE PUBLIC_HEADER "${QGIS_GUI_HDRS};${QGIS_GUI_MOC_HDRS}" LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" diff --git a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp index c39132d5753..73874d6a17b 100644 --- a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp +++ b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp @@ -518,7 +518,7 @@ void QgsRelationReferenceWidget::init() QgsExpression exp( mReferencedLayer->displayExpression() ); - requestedAttrs += exp.referencedColumns().toSet(); + requestedAttrs += exp.referencedColumns(); requestedAttrs << mRelation.fieldPairs().at( 0 ).second; QgsAttributeList attributes; @@ -527,7 +527,7 @@ void QgsRelationReferenceWidget::init() layerCache->setCacheSubsetOfAttributes( attributes ); mMasterModel = new QgsAttributeTableModel( layerCache ); - mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->fields() ) ); + mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs, mReferencedLayer->fields() ) ); mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel ); mFeatureListModel = new QgsFeatureListModel( mFilterModel, this ); mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() ); diff --git a/src/gui/qgsencodingfiledialog.cpp b/src/gui/qgsencodingfiledialog.cpp index 6a771122ecd..b59978b0e34 100644 --- a/src/gui/qgsencodingfiledialog.cpp +++ b/src/gui/qgsencodingfiledialog.cpp @@ -25,15 +25,17 @@ #include #include -QgsEncodingFileDialog::QgsEncodingFileDialog( QWidget * parent, - const QString & caption, const QString & directory, - const QString & filter, const QString & encoding ) +QgsEncodingFileDialog::QgsEncodingFileDialog( QWidget* parent, + const QString& caption, const QString& directory, + const QString& filter, const QString& encoding ) : QFileDialog( parent, caption, directory, filter ) { mCancelAll = false; mCancelAllButton = nullptr; mEncodingComboBox = new QComboBox( this ); QLabel* l = new QLabel( tr( "Encoding:" ), this ); + + setOption( QFileDialog::DontUseNativeDialog ); layout()->addWidget( l ); layout()->addWidget( mEncodingComboBox ); diff --git a/src/gui/qgsencodingfiledialog.h b/src/gui/qgsencodingfiledialog.h index 4b4342606c3..87fe19c4f5c 100644 --- a/src/gui/qgsencodingfiledialog.h +++ b/src/gui/qgsencodingfiledialog.h @@ -27,9 +27,9 @@ class GUI_EXPORT QgsEncodingFileDialog: public QFileDialog { Q_OBJECT public: - QgsEncodingFileDialog( QWidget * parent = nullptr, - const QString & caption = QString(), const QString & directory = QString(), - const QString & filter = QString(), const QString & encoding = QString() ); + QgsEncodingFileDialog( QWidget* parent = nullptr, + const QString& caption = QString(), const QString& directory = QString(), + const QString& filter = QString(), const QString& encoding = QString() ); ~QgsEncodingFileDialog(); /** Returns a string describing the chosen encoding*/ QString encoding() const; diff --git a/src/gui/qgsexpressionlineedit.cpp b/src/gui/qgsexpressionlineedit.cpp index cf5ae564751..e54279027fb 100644 --- a/src/gui/qgsexpressionlineedit.cpp +++ b/src/gui/qgsexpressionlineedit.cpp @@ -20,6 +20,7 @@ #include "qgsexpressionbuilderdialog.h" #include "qgsexpressioncontextgenerator.h" #include "qgscodeeditorsql.h" +#include "qgsvectorlayer.h" #include #include #include @@ -113,6 +114,8 @@ void QgsExpressionLineEdit::setGeomCalculator( const QgsDistanceArea &da ) void QgsExpressionLineEdit::setLayer( QgsVectorLayer* layer ) { + if ( !mExpressionContextGenerator || mExpressionContextGenerator == mLayer ) + mExpressionContextGenerator = layer; mLayer = layer; } diff --git a/src/gui/qgsexpressionlineedit.h b/src/gui/qgsexpressionlineedit.h index 7979f6cc3ea..eb6faff90ac 100644 --- a/src/gui/qgsexpressionlineedit.h +++ b/src/gui/qgsexpressionlineedit.h @@ -54,35 +54,44 @@ class GUI_EXPORT QgsExpressionLineEdit : public QWidget */ explicit QgsExpressionLineEdit( QWidget *parent = nullptr ); - /** Sets the title used in the expression builder dialog + /** + * Sets the title used in the expression builder dialog * @param title dialog title * @see expressionDialogTitle() */ void setExpressionDialogTitle( const QString& title ); - /** Returns the title used for the expression dialog. + /** + * Returns the title used for the expression dialog. * @see setExpressionDialogTitle() */ QString expressionDialogTitle() const { return mExpressionDialogTitle; } - /** Sets whether the widget should show a multiline text editor. + /** + * Sets whether the widget should show a multiline text editor. * @param multiLine set to true to show multiline editor, or false * to show single line editor (the default). */ void setMultiLine( bool multiLine ); - /** Set the geometry calculator used in the expression dialog. + /** + * Set the geometry calculator used in the expression dialog. * @param distanceArea calculator */ void setGeomCalculator( const QgsDistanceArea &distanceArea ); - /** Sets a layer associated with the widget. Required in order to get the fields and values - * from the layer. - * @param layer vector layer - */ + /** + * Sets a layer associated with the widget. Required in order to get the fields and values + * from the layer. + * This will also automatically register the layer as expression context generator if + * no generator has been set before or the previous layer has been used as generator. + * + * @see registerExpressionContextGenerator + */ void setLayer( QgsVectorLayer* layer ); - /** Returns the current expression shown in the widget. + /** + * Returns the current expression shown in the widget. * @see setExpression() */ QString expression() const; @@ -110,16 +119,19 @@ class GUI_EXPORT QgsExpressionLineEdit : public QWidget public slots: - /** Sets the current expression to show in the widget. + /** + * Sets the current expression to show in the widget. * @param expression expression string * @see expression() */ void setExpression( const QString& expression ); protected: + void changeEvent( QEvent* event ) override; private slots: + //! When the expression is edited by the user in the line edit, it will be checked for validity void expressionEdited( const QString& expression ); void expressionEdited(); diff --git a/src/gui/qgspanelwidgetstack.cpp b/src/gui/qgspanelwidgetstack.cpp index 3b153dad5c2..5a8e0195cf7 100644 --- a/src/gui/qgspanelwidgetstack.cpp +++ b/src/gui/qgspanelwidgetstack.cpp @@ -32,7 +32,7 @@ QgsPanelWidgetStack::QgsPanelWidgetStack( QWidget *parent ) connect( mBackButton, SIGNAL( pressed() ), this, SLOT( acceptCurrentPanel() ) ); } -void QgsPanelWidgetStack::addMainPanel( QgsPanelWidget *panel ) +void QgsPanelWidgetStack::setMainPanel( QgsPanelWidget *panel ) { // TODO Don't allow adding another main widget or else that would be strange for the user. connect( panel, SIGNAL( showPanel( QgsPanelWidget* ) ), this, SLOT( showPanel( QgsPanelWidget* ) ), @@ -43,13 +43,16 @@ void QgsPanelWidgetStack::addMainPanel( QgsPanelWidget *panel ) mStackedWidget->setCurrentIndex( 0 ); } -QgsPanelWidget *QgsPanelWidgetStack::mainWidget() +QgsPanelWidget *QgsPanelWidgetStack::mainPanel() { return qobject_cast( mStackedWidget->widget( 0 ) ); } -QgsPanelWidget *QgsPanelWidgetStack::takeMainWidget() +QgsPanelWidget *QgsPanelWidgetStack::takeMainPanel() { + // clear out the current stack + acceptAllPanels(); + QWidget* widget = mStackedWidget->widget( 0 ); mStackedWidget->removeWidget( widget ); return qobject_cast( widget ); @@ -57,7 +60,7 @@ QgsPanelWidget *QgsPanelWidgetStack::takeMainWidget() void QgsPanelWidgetStack::clear() { - for ( int i = mStackedWidget->count(); i >= 0; i-- ) + for ( int i = mStackedWidget->count() - 1; i >= 0; i-- ) { if ( QgsPanelWidget* panelWidget = qobject_cast( mStackedWidget->widget( i ) ) ) { @@ -79,16 +82,38 @@ void QgsPanelWidgetStack::clear() this->updateBreadcrumb(); } +QgsPanelWidget* QgsPanelWidgetStack::currentPanel() +{ + return qobject_cast( mStackedWidget->currentWidget() ); +} + void QgsPanelWidgetStack::acceptCurrentPanel() { // You can't accept the main panel. - if ( mStackedWidget->currentIndex() == 0 ) + if ( mStackedWidget->currentIndex() <= 0 ) return; - QgsPanelWidget* widget = qobject_cast( mStackedWidget->currentWidget() ); + QgsPanelWidget* widget = currentPanel(); widget->acceptPanel(); } +void QgsPanelWidgetStack::acceptAllPanels() +{ + //avoid messy multiple redraws + setUpdatesEnabled( false ); + mStackedWidget->setUpdatesEnabled( false ); + + for ( int i = mStackedWidget->count() - 1; i > 0; --i ) + { + if ( QgsPanelWidget* panelWidget = qobject_cast( mStackedWidget->widget( i ) ) ) + { + panelWidget->acceptPanel(); + } + } + setUpdatesEnabled( true ); + mStackedWidget->setUpdatesEnabled( true ); +} + void QgsPanelWidgetStack::showPanel( QgsPanelWidget *panel ) { mTitles.push( panel->panelTitle() ); diff --git a/src/gui/qgspanelwidgetstack.h b/src/gui/qgspanelwidgetstack.h index 9c6e30a694c..bafcefb2275 100644 --- a/src/gui/qgspanelwidgetstack.h +++ b/src/gui/qgspanelwidgetstack.h @@ -43,26 +43,35 @@ class GUI_EXPORT QgsPanelWidgetStack : public QWidget, private Ui::QgsRendererWi QgsPanelWidgetStack( QWidget* parent = nullptr ); /** - * Adds the main widget to the stack and selects it for the user + * Adds the main panel widget to the stack and selects it for the user * The main widget can not be closed and only the showPanel signal is attached * to handle children widget opening panels. * @param panel The panel to set as the first widget in the stack. + * @note a stack can have only one main panel. Any existing main panel + * should be removed by first calling takeMainPanel(). + * @see mainPanel() + * @see takeMainPanel() */ - void addMainPanel( QgsPanelWidget* panel ); + void setMainPanel( QgsPanelWidget* panel ); /** - * The main widget that is set in the stack. The main widget can not be closed + * The main panel widget that is set in the stack. The main widget can not be closed * and doesn't display a back button. * @return The main QgsPanelWidget that is active in the stack. + * @see setMainPanel() */ - QgsPanelWidget* mainWidget(); + QgsPanelWidget* mainPanel(); /** - * Removes the main widget from the stack and transfers ownsership to the + * Removes the main panel widget from the stack and transfers ownsership to the * caller. * @return The main widget that is set in the stack. + * @note Calling this will clear out any current stacked panels by accepting + * each panel in turn. + * @see mainPanel() + * @see setMainPanel() */ - QgsPanelWidget* takeMainWidget(); + QgsPanelWidget* takeMainPanel(); /** * Clear the stack of all widgets. Unless the panels autoDelete is set to false @@ -70,14 +79,29 @@ class GUI_EXPORT QgsPanelWidgetStack : public QWidget, private Ui::QgsRendererWi */ void clear(); + /** + * Returns the panel currently shown in the stack. + * @note added in QGIS 3.0 + */ + QgsPanelWidget* currentPanel(); + public slots: /** * Accept the current active widget in the stack. * * Calls the panelAccepeted signal on the active widget. + * @see acceptAllPanels() */ void acceptCurrentPanel(); + /** + * Accepts all panel widgets open in the stack in turn until until only the mainPanel() + * remains. + * @see acceptCurrentPanel(); + * @note added in QGIS 3.0 + */ + void acceptAllPanels(); + /** * Show a panel in the stack widget. Will connect to the panels showPanel event to handle * nested panels. Auto switches the the given panel for the user. diff --git a/src/gui/symbology-ng/qgssizescalewidget.cpp b/src/gui/symbology-ng/qgssizescalewidget.cpp index 4187bfc671b..9344aa23392 100644 --- a/src/gui/symbology-ng/qgssizescalewidget.cpp +++ b/src/gui/symbology-ng/qgssizescalewidget.cpp @@ -277,7 +277,7 @@ void QgsSizeScaleWidget::computeFromLayerTriggered() if ( ! expression.prepare( &context ) ) return; - QStringList lst( expression.referencedColumns() ); + QSet lst( expression.referencedColumns() ); QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFlags( expression.needsGeometry() diff --git a/src/helpviewer/main.cpp b/src/helpviewer/main.cpp index 5d0d9413baa..6988c92bff0 100644 --- a/src/helpviewer/main.cpp +++ b/src/helpviewer/main.cpp @@ -33,7 +33,7 @@ int main( int argc, char ** argv ) // Set up the QSettings environment must be done after qapp is created QCoreApplication::setOrganizationName( "QGIS" ); QCoreApplication::setOrganizationDomain( "qgis.org" ); - QCoreApplication::setApplicationName( "QGIS2" ); + QCoreApplication::setApplicationName( "QGIS3" ); QString myTranslationCode = ""; diff --git a/src/plugins/dxf2shp_converter/dxflib/src/dl_entities.h b/src/plugins/dxf2shp_converter/dxflib/src/dl_entities.h index dd2432d713f..654195b46a2 100644 --- a/src/plugins/dxf2shp_converter/dxflib/src/dl_entities.h +++ b/src/plugins/dxf2shp_converter/dxflib/src/dl_entities.h @@ -697,7 +697,7 @@ struct DXFLIB_EXPORT DL_InsertData { double sz; /*! Rotation angle in degrees. */ double angle; - /*! Number of colums if we insert an array of the block or 1. */ + /*! Number of columns if we insert an array of the block or 1. */ int cols; /*! Number of rows if we insert an array of the block or 1. */ int rows; diff --git a/src/plugins/globe/globe_plugin.cpp b/src/plugins/globe/globe_plugin.cpp index 2ddfe65541d..4b58ddc7527 100644 --- a/src/plugins/globe/globe_plugin.cpp +++ b/src/plugins/globe/globe_plugin.cpp @@ -342,7 +342,9 @@ void GlobePlugin::run() } else { - QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); + QString cacheDirectory = settings.value( "cache/directory" ).toString(); + if ( cacheDirectory.isEmpty() ) + cacheDirectory = QgsApplication::qgisSettingsDirPath() + "cache"; osgEarth::Drivers::FileSystemCacheOptions cacheOptions; cacheOptions.rootPath() = cacheDirectory.toStdString(); diff --git a/src/plugins/grass/qgsgrasseditrenderer.cpp b/src/plugins/grass/qgsgrasseditrenderer.cpp index 132fdcf1b6a..dc7cc139402 100644 --- a/src/plugins/grass/qgsgrasseditrenderer.cpp +++ b/src/plugins/grass/qgsgrasseditrenderer.cpp @@ -173,7 +173,7 @@ void QgsGrassEditRenderer::stopRender( QgsRenderContext& context ) mMarkerRenderer->stopRender( context ); } -QList QgsGrassEditRenderer::usedAttributes() +QSet QgsGrassEditRenderer::usedAttributes() const { return mLineRenderer->usedAttributes(); } diff --git a/src/plugins/grass/qgsgrasseditrenderer.h b/src/plugins/grass/qgsgrasseditrenderer.h index f60b0e330d0..1162b23f647 100644 --- a/src/plugins/grass/qgsgrasseditrenderer.h +++ b/src/plugins/grass/qgsgrasseditrenderer.h @@ -38,7 +38,7 @@ class QgsGrassEditRenderer : public QgsFeatureRenderer virtual void stopRender( QgsRenderContext& context ) override; - virtual QList usedAttributes() override; + virtual QSet usedAttributes() const override; virtual QgsFeatureRenderer* clone() const override; diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 53a3f31bc09..f22dba4ab84 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -600,7 +600,7 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent, char *tmpBlock = ( char * )qgsMalloc( dataSize * tmpWidth * tmpHeight ); if ( ! tmpBlock ) { - QgsDebugMsg( QString( "Coudn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ) ); + QgsDebugMsg( QString( "Couldn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ) ); return; } GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, theBandNo ); diff --git a/src/providers/grass/CMakeLists.txt b/src/providers/grass/CMakeLists.txt index ad0a53fe66f..5e24956f731 100644 --- a/src/providers/grass/CMakeLists.txt +++ b/src/providers/grass/CMakeLists.txt @@ -93,7 +93,7 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION) FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis2_grass + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_grass COMPILE_FLAGS "-DGRASS_BASE=\\\"${GRASS_PREFIX}\\\" \"-DGRASS_LIB_EXPORT=${DLLEXPORT}\" \"-DGRASS_EXPORT=${DLLIMPORT}\" ${GRASS_OFF_T_SIZE_DEF}" ) diff --git a/src/providers/memory/qgsmemoryprovider.cpp b/src/providers/memory/qgsmemoryprovider.cpp index 1843af6a828..3bb74fd2ee2 100644 --- a/src/providers/memory/qgsmemoryprovider.cpp +++ b/src/providers/memory/qgsmemoryprovider.cpp @@ -47,23 +47,14 @@ QgsMemoryProvider::QgsMemoryProvider( const QString& uri ) geometry = url.path(); } - geometry = geometry.toLower(); - if ( geometry == "point" ) - mWkbType = QgsWkbTypes::Point; - else if ( geometry == "linestring" ) - mWkbType = QgsWkbTypes::LineString; - else if ( geometry == "polygon" ) - mWkbType = QgsWkbTypes::Polygon; - else if ( geometry == "multipoint" ) - mWkbType = QgsWkbTypes::MultiPoint; - else if ( geometry == "multilinestring" ) - mWkbType = QgsWkbTypes::MultiLineString; - else if ( geometry == "multipolygon" ) - mWkbType = QgsWkbTypes::MultiPolygon; - else if ( geometry == "none" ) + if ( geometry.toLower() == "none" ) + { mWkbType = QgsWkbTypes::NoGeometry; + } else - mWkbType = QgsWkbTypes::Unknown; + { + mWkbType = QgsWkbTypes::parseType( geometry ); + } if ( url.hasQueryItem( "crs" ) ) { @@ -208,34 +199,7 @@ QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const Q_UNUSED( expandAuthConfig ) QUrl uri( "memory" ); - QString geometry; - switch ( mWkbType ) - { - case QgsWkbTypes::Point : - geometry = "Point"; - break; - case QgsWkbTypes::LineString : - geometry = "LineString"; - break; - case QgsWkbTypes::Polygon : - geometry = "Polygon"; - break; - case QgsWkbTypes::MultiPoint : - geometry = "MultiPoint"; - break; - case QgsWkbTypes::MultiLineString : - geometry = "MultiLineString"; - break; - case QgsWkbTypes::MultiPolygon : - geometry = "MultiPolygon"; - break; - case QgsWkbTypes::NoGeometry : - geometry = "None"; - break; - default: - geometry = ""; - break; - } + QString geometry = QgsWkbTypes::displayString( mWkbType ); uri.addQueryItem( "geometry", geometry ); if ( mCrs.isValid() ) diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp index a41304750b8..5e31dab1f62 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.cpp +++ b/src/providers/ogr/qgsogrfeatureiterator.cpp @@ -311,8 +311,13 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature ) else feature.clearGeometry(); - if (( useIntersect && ( !feature.hasGeometry() || !feature.geometry().intersects( mRequest.filterRect() ) ) ) - || ( geometryTypeFilter && ( !feature.hasGeometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry().wkbType() ) != mSource->mOgrGeometryTypeFilter ) ) ) + if ( mSource->mOgrGeometryTypeFilter == wkbGeometryCollection && + geom && wkbFlatten( OGR_G_GetGeometryType( geom ) ) == wkbGeometryCollection ) + { + // OK + } + else if (( useIntersect && ( !feature.hasGeometry() || !feature.geometry().intersects( mRequest.filterRect() ) ) ) + || ( geometryTypeFilter && ( !feature.hasGeometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry().wkbType() ) != mSource->mOgrGeometryTypeFilter ) ) ) { OGR_F_Destroy( fet ); return false; @@ -359,7 +364,7 @@ QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider* p ) mFieldsWithoutFid.append( mFields.at( i ) ); mDriverName = p->ogrDriverName; mFirstFieldIsFid = p->mFirstFieldIsFid; - mOgrGeometryTypeFilter = wkbFlatten( p->mOgrGeometryTypeFilter ); + mOgrGeometryTypeFilter = QgsOgrProvider::ogrWkbSingleFlatten( p->mOgrGeometryTypeFilter ); QgsOgrConnPool::instance()->ref( mDataSource ); } diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 8c8645fbaa3..f7a3d5c03a3 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -154,7 +154,12 @@ void QgsOgrProvider::repack() // run REPACK on shape files QByteArray sql = QByteArray( "REPACK " ) + layerName; // don't quote the layer name as it works with spaces in the name and won't work if the name is quoted QgsDebugMsg( QString( "SQL: %1" ).arg( FROM8( sql ) ) ); + CPLErrorReset(); OGR_DS_ExecuteSQL( ogrDataSource, sql.constData(), nullptr, nullptr ); + if ( CPLGetLastErrorType() != CE_None ) + { + pushError( tr( "OGR[%1] error %2: %3" ).arg( CPLGetLastErrorType() ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) ); + } if ( mFilePath.endsWith( ".shp", Qt::CaseInsensitive ) || mFilePath.endsWith( ".dbf", Qt::CaseInsensitive ) ) { @@ -3150,13 +3155,16 @@ void QgsOgrProvider::recalculateFeatureCount() setRelevantFields( ogrLayer, true, QgsAttributeList() ); OGR_L_ResetReading( ogrLayer ); OGRFeatureH fet; + const OGRwkbGeometryType flattenGeomTypeFilter = + QgsOgrProvider::ogrWkbSingleFlatten( mOgrGeometryTypeFilter ); while (( fet = OGR_L_GetNextFeature( ogrLayer ) ) ) { OGRGeometryH geom = OGR_F_GetGeometryRef( fet ); if ( geom ) { OGRwkbGeometryType gType = OGR_G_GetGeometryType( geom ); - if ( gType == mOgrGeometryTypeFilter ) mFeaturesCounted++; + gType = QgsOgrProvider::ogrWkbSingleFlatten( gType ); + if ( gType == flattenGeomTypeFilter ) mFeaturesCounted++; } OGR_F_Destroy( fet ); } @@ -3360,6 +3368,18 @@ void QgsOgrProvider::open( OpenMode mode ) ogrLayer = ogrOrigLayer = nullptr; mValid = false; +#if defined(GDAL_COMPUTE_VERSION) + // In the case where we deal with a shapefile, it is possible that it has + // pre-existing holes in the DBF (see #15407), so if using a GDAL version + // recent enough to have reliable packing, do a packing at the first edit + // action. + if ( ogrDriverName == "ESRI Shapefile" && + atoi( GDALVersionInfo( "VERSION_NUM" ) ) >= GDAL_COMPUTE_VERSION( 2, 1, 2 ) ) + { + mShapefileMayBeCorrupted = true; + } +#endif + ogrDataSource = QgsOgrProviderUtils::OGROpenWrapper( TO8F( mFilePath ), false, &ogrDriver ); mWriteAccess = false; diff --git a/src/providers/oracle/qgsoracletablecache.h b/src/providers/oracle/qgsoracletablecache.h index 709b63972ab..3687ff0fb1f 100644 --- a/src/providers/oracle/qgsoracletablecache.h +++ b/src/providers/oracle/qgsoracletablecache.h @@ -28,7 +28,7 @@ email : wonder.sk at gmail dot com * This class contains routines for local caching of listing of layers, so the add Oracle * layer dialog does not need to discover the tables every time the user wants to add a layer. * - * The cached entries are stored in SQLite database in QGIS user directory (usually ~/.qgis2). + * The cached entries are stored in SQLite database in QGIS user directory (usually ~/.qgis3). * The database can be used for other data sources, too. Each saved connection's list is stored * in one table "oracle_xyz" (where xyz is the name of the connection). There is one meta table * "meta_oracle" which has a list of cached connections and the combination of flags used for diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index 4bfcb11403f..08d862c58e1 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -3907,6 +3908,84 @@ QVariant QgsPostgresProvider::convertValue( QVariant::Type type, QVariant::Type } } +QList QgsPostgresProvider::searchLayers( const QList& layers, const QString& connectionInfo, const QString& schema, const QString& tableName ) +{ + QList result; + Q_FOREACH ( QgsVectorLayer* layer, layers ) + { + const QgsPostgresProvider* pgProvider = qobject_cast( layer->dataProvider() ); + if ( pgProvider && + pgProvider->mUri.connectionInfo( false ) == connectionInfo && pgProvider->mSchemaName == schema && pgProvider->mTableName == tableName ) + { + result.append( layer ); + } + } + return result; +} + +QList QgsPostgresProvider::discoverRelations( const QgsVectorLayer* self, const QList& layers ) const +{ + QList result; + QString sql( + "SELECT RC.CONSTRAINT_NAME, KCU1.COLUMN_NAME, KCU2.CONSTRAINT_SCHEMA, KCU2.TABLE_NAME, KCU2.COLUMN_NAME, KCU1.ORDINAL_POSITION " + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC " + "INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1 " + "ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME " + "INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU2 " + "ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME " + "AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION " + "WHERE KCU1.CONSTRAINT_SCHEMA=" + QgsPostgresConn::quotedValue( mSchemaName ) + " AND KCU1.TABLE_NAME=" + QgsPostgresConn::quotedValue( mTableName ) + + "ORDER BY KCU1.ORDINAL_POSITION" + ); + QgsPostgresResult sqlResult( connectionRO()->PQexec( sql ) ); + if ( sqlResult.PQresultStatus() != PGRES_TUPLES_OK ) + { + QgsLogger::warning( "Error getting the foreign keys of " + mTableName ); + return result; + } + + int nbFound = 0; + for ( int row = 0; row < sqlResult.PQntuples(); ++row ) + { + const QString name = sqlResult.PQgetvalue( row, 0 ); + const QString fkColumn = sqlResult.PQgetvalue( row, 1 ); + const QString refSchema = sqlResult.PQgetvalue( row, 2 ); + const QString refTable = sqlResult.PQgetvalue( row, 3 ); + const QString refColumn = sqlResult.PQgetvalue( row, 4 ); + const QString position = sqlResult.PQgetvalue( row, 5 ); + if ( position == "1" ) + { // first reference field => try to find if we have layers for the referenced table + const QList foundLayers = searchLayers( layers, mUri.connectionInfo( false ), refSchema, refTable ); + Q_FOREACH ( const QgsVectorLayer* foundLayer, foundLayers ) + { + QgsRelation relation; + relation.setRelationName( name ); + relation.setReferencingLayer( self->id() ); + relation.setReferencedLayer( foundLayer->id() ); + relation.addFieldPair( fkColumn, refColumn ); + relation.generateId(); + if ( relation.isValid() ) + { + result.append( relation ); + ++nbFound; + } + else + { + QgsLogger::warning( "Invalid relation for " + name ); + } + } + } + else + { // multi reference field => add the field pair to all the referenced layers found + for ( int i = 0; i < nbFound; ++i ) + { + result[result.size() - 1 - i].addFieldPair( fkColumn, refColumn ); + } + } + } + return result; +} + /** * Class factory to return a pointer to a newly created * QgsPostgresProvider object diff --git a/src/providers/postgres/qgspostgresprovider.h b/src/providers/postgres/qgspostgresprovider.h index f604241e224..339972a3e49 100644 --- a/src/providers/postgres/qgspostgresprovider.h +++ b/src/providers/postgres/qgspostgresprovider.h @@ -258,6 +258,8 @@ class QgsPostgresProvider : public QgsVectorDataProvider */ static QVariant convertValue( QVariant::Type type, QVariant::Type subType, const QString& value ); + virtual QList discoverRelations( const QgsVectorLayer* self, const QList& layers ) const override; + signals: /** * This is emitted whenever the worker thread has fully calculated the @@ -340,6 +342,11 @@ class QgsPostgresProvider : public QgsVectorDataProvider */ QgsPostgresPrimaryKeyType pkType( const QgsField& fld ) const; + /** + * Search all the layers using the given table. + */ + static QList searchLayers( const QList& layers, const QString& connectionInfo, const QString& schema, const QString& tableName ); + QgsFields mAttributeFields; QString mDataComment; diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index 2ceb1dcc8f8..e5e6622f718 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -31,6 +31,7 @@ email : a.furieri@lqt.it #include "qgsspatialitefeatureiterator.h" #include +#include #include #include @@ -5264,6 +5265,80 @@ QgsAttributeList QgsSpatiaLiteProvider::pkAttributeIndexes() const return mPrimaryKeyAttrs; } +QList QgsSpatiaLiteProvider::searchLayers( const QList& layers, const QString& connectionInfo, const QString& tableName ) +{ + QList result; + Q_FOREACH ( QgsVectorLayer* layer, layers ) + { + const QgsSpatiaLiteProvider* slProvider = qobject_cast( layer->dataProvider() ); + if ( slProvider && slProvider->mSqlitePath == connectionInfo && slProvider->mTableName == tableName ) + { + result.append( layer ); + } + } + return result; +} + + +QList QgsSpatiaLiteProvider::discoverRelations( const QgsVectorLayer* self, const QList& layers ) const +{ + QList output; + const QString sql = QString( "PRAGMA foreign_key_list(%1)" ).arg( QgsSpatiaLiteProvider::quotedIdentifier( mTableName ) ); + char **results; + int rows; + int columns; + char *errMsg = nullptr; + int ret = sqlite3_get_table( mSqliteHandle, sql.toUtf8().constData(), &results, &rows, &columns, &errMsg ); + if ( ret == SQLITE_OK ) + { + int nbFound = 0; + for ( int row = 1; row <= rows; ++row ) + { + const QString name = "fk_" + mTableName + "_" + QString::fromUtf8( results[row * columns + 0] ); + const QString position = QString::fromUtf8( results[row * columns + 1] ); + const QString refTable = QString::fromUtf8( results[row * columns + 2] ); + const QString fkColumn = QString::fromUtf8( results[row * columns + 3] ); + const QString refColumn = QString::fromUtf8( results[row * columns + 4] ); + if ( position == "0" ) + { // first reference field => try to find if we have layers for the referenced table + const QList foundLayers = searchLayers( layers, mSqlitePath, refTable ); + Q_FOREACH ( const QgsVectorLayer* foundLayer, foundLayers ) + { + QgsRelation relation; + relation.setRelationName( name ); + relation.setReferencingLayer( self->id() ); + relation.setReferencedLayer( foundLayer->id() ); + relation.addFieldPair( fkColumn, refColumn ); + relation.generateId(); + if ( relation.isValid() ) + { + output.append( relation ); + ++nbFound; + } + else + { + QgsLogger::warning( "Invalid relation for " + name ); + } + } + } + else + { // multi reference field => add the field pair to all the referenced layers found + for ( int i = 0; i < nbFound; ++i ) + { + output[output.size() - 1 - i].addFieldPair( fkColumn, refColumn ); + } + } + } + sqlite3_free_table( results ); + } + else + { + QgsLogger::warning( QString( "SQLite error discovering relations: %1" ).arg( errMsg ) ); + sqlite3_free( errMsg ); + } + return output; +} + // --------------------------------------------------------------------------- QGISEXTERN bool saveStyle( const QString& uri, const QString& qmlStyle, const QString& sldStyle, diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h index b38c9abe6c9..478796fcb24 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.h +++ b/src/providers/spatialite/qgsspatialiteprovider.h @@ -209,6 +209,8 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider void invalidateConnections( const QString& connection ) override; + QList discoverRelations( const QgsVectorLayer* self, const QList& layers ) const override; + // static functions static void convertToGeosWKB( const unsigned char *blob, int blob_size, unsigned char **wkb, int *geom_size ); @@ -291,6 +293,11 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider //! get SpatiaLite version string QString spatialiteVersion(); + /** + * Search all the layers using the given table. + */ + static QList searchLayers( const QList& layers, const QString& connectionInfo, const QString& tableName ); + QgsFields mAttributeFields; //! Flag indicating if the layer data source is a valid SpatiaLite layer diff --git a/src/providers/wfs/qgswfscapabilities.cpp b/src/providers/wfs/qgswfscapabilities.cpp index 605d220747e..50da4f219ad 100644 --- a/src/providers/wfs/qgswfscapabilities.cpp +++ b/src/providers/wfs/qgswfscapabilities.cpp @@ -35,7 +35,7 @@ QgsWfsCapabilities::~QgsWfsCapabilities() { } -bool QgsWfsCapabilities::requestCapabilities( bool synchronous ) +bool QgsWfsCapabilities::requestCapabilities( bool synchronous, bool forceRefresh ) { QUrl url( baseURL() ); url.addQueryItem( "REQUEST", "GetCapabilities" ); @@ -47,7 +47,7 @@ bool QgsWfsCapabilities::requestCapabilities( bool synchronous ) else url.addQueryItem( "VERSION", version ); - if ( !sendGET( url, synchronous, false ) ) + if ( !sendGET( url, synchronous, forceRefresh ) ) { emit gotCapabilities(); return false; diff --git a/src/providers/wfs/qgswfscapabilities.h b/src/providers/wfs/qgswfscapabilities.h index acb7fe77ff4..cb056e1fcc1 100644 --- a/src/providers/wfs/qgswfscapabilities.h +++ b/src/providers/wfs/qgswfscapabilities.h @@ -30,7 +30,7 @@ class QgsWfsCapabilities : public QgsWfsRequest virtual ~QgsWfsCapabilities(); //! start network connection to get capabilities - bool requestCapabilities( bool synchronous ); + bool requestCapabilities( bool synchronous, bool forceRefresh ); //! description of a vector layer struct FeatureType diff --git a/src/providers/wfs/qgswfsdataitems.cpp b/src/providers/wfs/qgswfsdataitems.cpp index 67cb0d76447..7a977246796 100644 --- a/src/providers/wfs/qgswfsdataitems.cpp +++ b/src/providers/wfs/qgswfsdataitems.cpp @@ -62,7 +62,9 @@ QVector QgsWfsConnectionItem::createChildren() QgsWfsCapabilities capabilities( mUri ); - capabilities.requestCapabilities( true ); + const bool synchronous = true; + const bool forceRefresh = false; + capabilities.requestCapabilities( synchronous, forceRefresh ); QVector layers; if ( capabilities.errorCode() == QgsWfsCapabilities::NoError ) diff --git a/src/providers/wfs/qgswfsfeatureiterator.cpp b/src/providers/wfs/qgswfsfeatureiterator.cpp index c1fedbe48b1..437037f69c9 100644 --- a/src/providers/wfs/qgswfsfeatureiterator.cpp +++ b/src/providers/wfs/qgswfsfeatureiterator.cpp @@ -310,7 +310,12 @@ QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool fo qgsDoubleToString( mShared->mRect.yMaximum() ) ) ); // Some servers like Geomedia need the srsname to be explictly appended // otherwise they are confused and do not interpret it properly - bbox += "," + mShared->srsName(); + if ( !mShared->mWFSVersion.startsWith( "1.0" ) ) + { + // but it is illegal in WFS 1.0 and some servers definitely not like + // it. See #15464 + bbox += "," + mShared->srsName(); + } getFeatureUrl.addQueryItem( "BBOX", bbox ); } else if ( !mShared->mWFSFilter.isEmpty() ) diff --git a/src/providers/wfs/qgswfsprovider.cpp b/src/providers/wfs/qgswfsprovider.cpp index 72de7c08a4a..4c9cbbd4b11 100644 --- a/src/providers/wfs/qgswfsprovider.cpp +++ b/src/providers/wfs/qgswfsprovider.cpp @@ -1249,8 +1249,15 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument& schemaDoc, for ( int i = 0; i < attributeNodeList.size(); ++i ) { QDomElement attributeElement = attributeNodeList.at( i ).toElement(); + //attribute name QString name = attributeElement.attribute( "name" ); + // Some servers like http://ogi.state.ok.us/geoserver/wfs on layer ogi:doq_centroids + // return attribute names padded with spaces. See http://hub.qgis.org/issues/3426 + // I'm not completely sure how legal this + // is but this validates with Xerces 3.1, and its schema analyzer does also the trimming. + name = name.trimmed(); + //attribute type QString type = attributeElement.attribute( "type" ); if ( type.isEmpty() ) @@ -1437,7 +1444,9 @@ bool QgsWFSProvider::getCapabilities() if ( mShared->mCaps.version.isEmpty() ) { QgsWfsCapabilities getCapabilities( mShared->mURI.uri( false ) ); - if ( !getCapabilities.requestCapabilities( true ) ) + const bool synchronous = true; + const bool forceRefresh = false; + if ( !getCapabilities.requestCapabilities( synchronous, forceRefresh ) ) { QgsMessageLog::logMessage( tr( "GetCapabilities failed for url %1: %2" ). arg( dataSourceUri() ).arg( getCapabilities.errorMessage() ), tr( "WFS" ) ); diff --git a/src/providers/wfs/qgswfssourceselect.cpp b/src/providers/wfs/qgswfssourceselect.cpp index e0d5585d2e2..3ed15d37da4 100644 --- a/src/providers/wfs/qgswfssourceselect.cpp +++ b/src/providers/wfs/qgswfssourceselect.cpp @@ -353,7 +353,9 @@ void QgsWFSSourceSelect::connectToServer() } if ( mCapabilities ) { - mCapabilities->requestCapabilities( false ); + const bool synchronous = false; + const bool forceRefresh = true; + mCapabilities->requestCapabilities( synchronous, forceRefresh ); } } diff --git a/src/providers/wfs/qgswfsutils.cpp b/src/providers/wfs/qgswfsutils.cpp index 23adf9e15d8..dbb2b897849 100644 --- a/src/providers/wfs/qgswfsutils.cpp +++ b/src/providers/wfs/qgswfsutils.cpp @@ -37,7 +37,9 @@ int QgsWFSUtils::gmCounter = 0; QString QgsWFSUtils::getBaseCacheDirectory( bool createIfNotExisting ) { QSettings settings; - QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); + QString cacheDirectory = settings.value( "cache/directory" ).toString(); + if ( cacheDirectory.isEmpty() ) + cacheDirectory = QgsApplication::qgisSettingsDirPath() + "cache"; if ( createIfNotExisting ) { QMutexLocker locker( &gmMutex ); diff --git a/src/python/qgspythonutilsimpl.cpp b/src/python/qgspythonutilsimpl.cpp index 3bb2e74db28..1ddecb8580a 100644 --- a/src/python/qgspythonutilsimpl.cpp +++ b/src/python/qgspythonutilsimpl.cpp @@ -595,9 +595,9 @@ QString QgsPythonUtilsImpl::pluginsPath() QString QgsPythonUtilsImpl::homePythonPath() { QString settingsDir = QgsApplication::qgisSettingsDirPath(); - if ( QDir::cleanPath( settingsDir ) == QDir::homePath() + QString( "/.qgis%1" ).arg( Qgis::QGIS_VERSION_INT / 10000 ) ) + if ( QDir::cleanPath( settingsDir ) == QDir::homePath() + QString( "/.qgis3" ) ) { - return QString( "b\"%1/.qgis%2/python\".decode('utf-8')" ).arg( QDir::homePath() ).arg( Qgis::QGIS_VERSION_INT / 10000 ); + return QString( "b\"%1/.qgis3/python\".decode('utf-8')" ).arg( QDir::homePath() ); } else { diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index 31dc5e175dc..36ad91d9d38 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -112,7 +112,9 @@ void QgsServer::setupNetworkAccessManager() QSettings settings; QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance(); QNetworkDiskCache *cache = new QNetworkDiskCache( nullptr ); - QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); + QString cacheDirectory = settings.value( "cache/directory" ).toString(); + if ( cacheDirectory.isEmpty() ) + cacheDirectory = QgsApplication::qgisSettingsDirPath() + "cache"; qint64 cacheSize = settings.value( "cache/size", 50 * 1024 * 1024 ).toULongLong(); QgsMessageLog::logMessage( QString( "setCacheDirectory: %1" ).arg( cacheDirectory ), "Server", QgsMessageLog::INFO ); QgsMessageLog::logMessage( QString( "setMaximumCacheSize: %1" ).arg( cacheSize ), "Server", QgsMessageLog::INFO ); @@ -364,7 +366,7 @@ bool QgsServer::init( ) QgsApplication::createDB(); //init qgis.db (e.g. necessary for user crs) // Instantiate authentication system - // creates or uses qgis-auth.db in ~/.qgis2/ or directory defined by QGIS_AUTH_DB_DIR_PATH env variable + // creates or uses qgis-auth.db in ~/.qgis3/ or directory defined by QGIS_AUTH_DB_DIR_PATH env variable // set the master password as first line of file defined by QGIS_AUTH_PASSWORD_FILE env variable // (QGIS_AUTH_PASSWORD_FILE variable removed from environment after accessing) QgsAuthManager::instance()->init( QgsApplication::pluginPath() ); diff --git a/src/server/qgsserverprojectparser.cpp b/src/server/qgsserverprojectparser.cpp index 42c421fa0de..1197cf1085a 100644 --- a/src/server/qgsserverprojectparser.cpp +++ b/src/server/qgsserverprojectparser.cpp @@ -28,6 +28,9 @@ #include "qgslayertreegroup.h" #include "qgslogger.h" +#include "qgslogger.h" +#include "qgsmessagelog.h" + #include #include #include @@ -234,8 +237,14 @@ QgsMapLayer* QgsServerProjectParser::createLayerFromElement( const QDomElement& if ( !QgsMapLayerRegistry::instance()->mapLayer( id ) ) QgsMapLayerRegistry::instance()->addMapLayer( layer, false, false ); if ( layer->type() == QgsMapLayer::VectorLayer ) + { addValueRelationLayersForLayer( dynamic_cast( layer ) ); - + // Reload joins and expression fields + QgsVectorLayer* vlayer = dynamic_cast( layer ); + QString subsetString = vlayer->subsetString(); + layer->readLayerXml( const_cast( elem ) ); + vlayer->setSubsetString( subsetString ); + } return layer; } @@ -1541,7 +1550,7 @@ void QgsServerProjectParser::addJoinLayersForElement( const QDomElement& layerEl { QString id = joinNodeList.at( i ).toElement().attribute( "joinLayerId" ); QgsMapLayer* layer = mapLayerFromLayerId( id ); - if ( layer ) + if ( layer && !QgsMapLayerRegistry::instance()->mapLayer( id ) ) { QgsMapLayerRegistry::instance()->addMapLayer( layer, false, false ); } diff --git a/src/ui/qgsdiscoverrelationsdlgbase.ui b/src/ui/qgsdiscoverrelationsdlgbase.ui new file mode 100644 index 00000000000..285a15f5a8a --- /dev/null +++ b/src/ui/qgsdiscoverrelationsdlgbase.ui @@ -0,0 +1,117 @@ + + + QgsDiscoverRelationsDlgBase + + + + 0 + 0 + 700 + 267 + + + + Discover relations + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::MultiSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + false + + + true + + + + Name + + + + + Referencing Layer + + + + + Referencing Field + + + + + Referenced Layer + + + + + Referenced Field + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + mButtonBox + + + + + mButtonBox + accepted() + QgsDiscoverRelationsDlgBase + accept() + + + 248 + 254 + + + 157 + 274 + + + + + mButtonBox + rejected() + QgsDiscoverRelationsDlgBase + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/ui/qgsrelationmanagerdialogbase.ui b/src/ui/qgsrelationmanagerdialogbase.ui index f9f22c4add0..894f5af3c94 100644 --- a/src/ui/qgsrelationmanagerdialogbase.ui +++ b/src/ui/qgsrelationmanagerdialogbase.ui @@ -83,6 +83,17 @@ + + + + Discover Relations + + + + :/images/themes/default/symbologyAdd.svg:/images/themes/default/symbologyAdd.svg + + + diff --git a/tests/bench/main.cpp b/tests/bench/main.cpp index 669e8ebedbd..83440169a7a 100644 --- a/tests/bench/main.cpp +++ b/tests/bench/main.cpp @@ -394,7 +394,7 @@ int main( int argc, char *argv[] ) // Set up the QSettings environment must be done after qapp is created QgsApplication::setOrganizationName( "QGIS" ); QgsApplication::setOrganizationDomain( "qgis.org" ); - QgsApplication::setApplicationName( "QGIS2" ); + QgsApplication::setApplicationName( "QGIS3" ); QgsProviderRegistry::instance( QgsApplication::pluginPath() ); diff --git a/tests/src/core/testqgsdatadefined.cpp b/tests/src/core/testqgsdatadefined.cpp index 0e6d203dc6f..73970942a8b 100644 --- a/tests/src/core/testqgsdatadefined.cpp +++ b/tests/src/core/testqgsdatadefined.cpp @@ -273,20 +273,20 @@ void TestQgsDataDefined::referencedColumns() dd.setActive( true ); dd.setUseExpression( true ); - QStringList cols = dd.referencedColumns(); + QSet cols = dd.referencedColumns(); QVERIFY( cols.isEmpty() ); //set as expression dd.setExpressionString( "1+col1+col2" ); cols = dd.referencedColumns(); - QCOMPARE( cols.length(), 2 ); + QCOMPARE( cols.size(), 2 ); QVERIFY( cols.contains( QString( "col1" ) ) ); QVERIFY( cols.contains( QString( "col2" ) ) ); //alter expression and check that referenced columns is updated dd.setExpressionString( "1+col1+col2+col3" ); cols = dd.referencedColumns(); - QCOMPARE( cols.length(), 3 ); + QCOMPARE( cols.size(), 3 ); QVERIFY( cols.contains( QString( "col1" ) ) ); QVERIFY( cols.contains( QString( "col2" ) ) ); QVERIFY( cols.contains( QString( "col3" ) ) ); @@ -298,13 +298,13 @@ void TestQgsDataDefined::referencedColumns() dd.setField( "field" ); cols = dd.referencedColumns(); - QCOMPARE( cols.length(), 1 ); + QCOMPARE( cols.size(), 1 ); QVERIFY( cols.contains( QString( "field" ) ) ); //switch back to expression dd.setUseExpression( true ); cols = dd.referencedColumns(); - QCOMPARE( cols.length(), 3 ); + QCOMPARE( cols.size(), 3 ); QVERIFY( cols.contains( QString( "col1" ) ) ); QVERIFY( cols.contains( QString( "col2" ) ) ); QVERIFY( cols.contains( QString( "col3" ) ) ); diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 24701b60f98..0b4656a918c 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -1494,7 +1494,7 @@ class TestQgsExpression: public QObject expectedCols << "foo" << "bar" << "ppp" << "qqq" << "rrr"; QgsExpression exp( "length(Bar || FOO) = 4 or foo + sqrt(bar) > 0 or case when ppp then qqq else rrr end" ); QCOMPARE( exp.hasParserError(), false ); - QStringList refCols = exp.referencedColumns(); + QSet refCols = exp.referencedColumns(); // make sure we have lower case QSet refColsSet; Q_FOREACH ( const QString& col, refCols ) @@ -1507,7 +1507,7 @@ class TestQgsExpression: public QObject { QgsExpression exp( "attribute($currentfeature,'test')" ); QCOMPARE( exp.hasParserError(), false ); - QStringList refCols = exp.referencedColumns(); + QSet refCols = exp.referencedColumns(); // make sure we get the all attributes flag bool allAttributesFlag = refCols.contains( QgsFeatureRequest::AllAttributes ); QCOMPARE( allAttributesFlag, true ); diff --git a/tests/src/core/testqgslabelingengine.cpp b/tests/src/core/testqgslabelingengine.cpp index b38633737e3..648333b9a6b 100644 --- a/tests/src/core/testqgslabelingengine.cpp +++ b/tests/src/core/testqgslabelingengine.cpp @@ -437,7 +437,7 @@ void TestQgsLabelingEngine::testSubstitutions() mapSettings.setLayers( QStringList() << vl->id() ); mapSettings.setOutputDpi( 96 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings ); - QStringList attributes; + QSet attributes; QgsLabelingEngine engine; engine.setMapSettings( mapSettings ); engine.addProvider( provider ); @@ -469,7 +469,7 @@ void TestQgsLabelingEngine::testCapitalization() mapSettings.setLayers( QStringList() << vl->id() ); mapSettings.setOutputDpi( 96 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings ); - QStringList attributes; + QSet attributes; QgsLabelingEngine engine; engine.setMapSettings( mapSettings ); diff --git a/tests/src/core/testqgsogcutils.cpp b/tests/src/core/testqgsogcutils.cpp index 4b4e621e087..9fd87c395ca 100644 --- a/tests/src/core/testqgsogcutils.cpp +++ b/tests/src/core/testqgsogcutils.cpp @@ -93,12 +93,126 @@ void TestQgsOgcUtils::testGeometryFromGML() QVERIFY( geomBox.wkbType() == QgsWkbTypes::Polygon ); } +static bool compareElements( QDomElement& element1, QDomElement& element2 ) +{ + QString tag1 = element1.tagName(); + tag1.replace( QRegExp( ".*:" ), "" ); + QString tag2 = element2.tagName(); + tag2.replace( QRegExp( ".*:" ), "" ); + if ( tag1 != tag2 ) + { + qDebug( "Different tag names: %s, %s", tag1.toAscii().data(), tag2.toAscii().data() ); + return false ; + } + + if ( element1.hasAttributes() != element2.hasAttributes() ) + { + qDebug( "Different hasAttributes: %s, %s", tag1.toAscii().data(), tag2.toAscii().data() ); + return false; + } + + if ( element1.hasAttributes() ) + { + QDomNamedNodeMap attrs1 = element1.attributes(); + QDomNamedNodeMap attrs2 = element2.attributes(); + + if ( attrs1.size() != attrs2.size() ) + { + qDebug( "Different attributes size: %s, %s", tag1.toAscii().data(), tag2.toAscii().data() ); + return false; + } + + for ( int i = 0 ; i < attrs1.size() ; ++i ) + { + QDomNode node1 = attrs1.item( i ); + QDomAttr attr1 = node1.toAttr(); + + if ( !element2.hasAttribute( attr1.name() ) ) + { + qDebug( "Element2 has not attribute: %s, %s, %s", tag1.toAscii().data(), tag2.toAscii().data(), attr1.name().toAscii().data() ); + return false; + } + + if ( element2.attribute( attr1.name() ) != attr1.value() ) + { + qDebug( "Element2 attribute has not the same value: %s, %s, %s", tag1.toAscii().data(), tag2.toAscii().data(), attr1.name().toAscii().data() ); + return false; + } + } + } + + if ( element1.hasChildNodes() != element2.hasChildNodes() ) + { + qDebug( "Different childNodes: %s, %s", tag1.toAscii().data(), tag2.toAscii().data() ); + return false; + } + + if ( element1.hasChildNodes() ) + { + QDomNodeList nodes1 = element1.childNodes(); + QDomNodeList nodes2 = element2.childNodes(); + + if ( nodes1.size() != nodes2.size() ) + { + qDebug( "Different childNodes size: %s, %s", tag1.toAscii().data(), tag2.toAscii().data() ); + return false; + } + + for ( int i = 0 ; i < nodes1.size() ; ++i ) + { + QDomNode node1 = nodes1.at( i ); + QDomNode node2 = nodes2.at( i ); + if ( node1.isElement() && node2.isElement() ) + { + QDomElement elt1 = node1.toElement(); + QDomElement elt2 = node2.toElement(); + + if ( !compareElements( elt1, elt2 ) ) + return false; + } + else if ( node1.isText() && node2.isText() ) + { + QDomText txt1 = node1.toText(); + QDomText txt2 = node2.toText(); + + if ( txt1.data() != txt2.data() ) + { + qDebug( "Different text data: %s %s", tag1.toAscii().data(), txt1.data().toAscii().data() ); + qDebug( "Different text data: %s %s", tag2.toAscii().data(), txt2.data().toAscii().data() ); + return false; + } + } + } + } + + if ( element1.text() != element2.text() ) + { + qDebug( "Different text: %s %s", tag1.toAscii().data(), element1.text().toAscii().data() ); + qDebug( "Different text: %s %s", tag2.toAscii().data(), element2.text().toAscii().data() ); + return false; + } + + return true; +} +static QDomElement comparableElement( const QString& xmlText ) +{ + QDomDocument doc; + if ( !doc.setContent( xmlText ) ) + return QDomElement(); + return doc.documentElement(); +} + + void TestQgsOgcUtils::testGeometryToGML() { QDomDocument doc; QgsGeometry geomPoint( QgsGeometry::fromPoint( QgsPoint( 111, 222 ) ) ); QgsGeometry geomLine( QgsGeometry::fromWkt( "LINESTRING(111 222, 222 222)" ) ); + // Elements to compare + QDomElement xmlElem; + QDomElement ogcElem; + // Test GML2 QDomElement elemInvalid = QgsOgcUtils::geometryToGML( 0, doc ); QVERIFY( elemInvalid.isNull() ); @@ -107,14 +221,18 @@ void TestQgsOgcUtils::testGeometryToGML() QVERIFY( !elemPoint.isNull() ); doc.appendChild( elemPoint ); - QCOMPARE( doc.toString( -1 ), QString( "111,222" ) ); + xmlElem = comparableElement( QString( "111,222" ) ); + ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); doc.removeChild( elemPoint ); QDomElement elemLine = QgsOgcUtils::geometryToGML( &geomLine, doc ); QVERIFY( !elemLine.isNull() ); doc.appendChild( elemLine ); - QCOMPARE( doc.toString( -1 ), QString( "111,222 222,222" ) ); + xmlElem = comparableElement( QString( "111,222 222,222" ) ); + ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); doc.removeChild( elemLine ); // Test GML3 @@ -125,14 +243,18 @@ void TestQgsOgcUtils::testGeometryToGML() QVERIFY( !elemPoint.isNull() ); doc.appendChild( elemPoint ); - QCOMPARE( doc.toString( -1 ), QString( "111 222" ) ); + xmlElem = comparableElement( QString( "111 222" ) ); + ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); doc.removeChild( elemPoint ); elemLine = QgsOgcUtils::geometryToGML( &geomLine, doc, "GML3" ); QVERIFY( !elemLine.isNull() ); doc.appendChild( elemLine ); - QCOMPARE( doc.toString( -1 ), QString( "111 222 222 222" ) ); + xmlElem = comparableElement( QString( "111 222 222 222" ) ); + ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); doc.removeChild( elemLine ); } @@ -180,13 +302,41 @@ void TestQgsOgcUtils::testExpressionFromOgcFilter_data() "" ) << QString( "POPULATION >= 100 AND POPULATION <= 200" ); - // TODO: needs to handle different wildcards, single chars, escape chars + // handle different wildcards, single chars, escape chars QTest::newRow( "like" ) << QString( "" - "" + "" "NAME*QGIS*" "" ) << QString( "NAME LIKE '*QGIS*'" ); + QTest::newRow( "ilike" ) << QString( + "" + "" + "NAME*QGIS*" + "" ) + << QString( "NAME ILIKE '*QGIS*'" ); + + // different wildCards + QTest::newRow( "like wildCard" ) << QString( + "" + "" + "NAME*%QGIS*\\*" + "" ) + << QString( "NAME LIKE '%\\\\%QGIS%*'" ); + // different single chars + QTest::newRow( "like single char" ) << QString( + "" + "" + "NAME._QGIS.\\." + "" ) + << QString( "NAME LIKE '_\\\\_QGIS_.'" ); + // different single chars + QTest::newRow( "like escape char" ) << QString( + "" + "" + "NAME_QGIS.!.!!%QGIS*!*" + "" ) + << QString( "NAME LIKE '\\\\_QGIS_.!\\\\%QGIS%*'" ); QTest::newRow( "is null" ) << QString( "" @@ -259,7 +409,10 @@ void TestQgsOgcUtils::testExpressionToOgcFilter() qDebug( "EXPR: %s", exp.expression().toAscii().data() ); qDebug( "OGC : %s", doc.toString( -1 ).toAscii().data() ); - QCOMPARE( xmlText, doc.toString( -1 ) ); + + QDomElement xmlElem = comparableElement( xmlText ); + QDomElement ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); } void TestQgsOgcUtils::testExpressionToOgcFilter_data() @@ -301,6 +454,22 @@ void TestQgsOgcUtils::testExpressionToOgcFilter_data() "" "" ); + QTest::newRow( "like" ) << QString( "NAME LIKE '*QGIS*'" ) << QString( + "" + "" + "NAME" + "*QGIS*" + "" + "" ); + + QTest::newRow( "ilike" ) << QString( "NAME ILIKE '*QGIS*'" ) << QString( + "" + "" + "NAME" + "*QGIS*" + "" + "" ); + QTest::newRow( "is null" ) << QString( "A IS NULL" ) << QString( "" "" @@ -359,7 +528,7 @@ void TestQgsOgcUtils::testExpressionToOgcFilter_data() "" "" "geometry" - "5,6 5,6" + "5,6 5,6" "" "" ); @@ -367,7 +536,7 @@ void TestQgsOgcUtils::testExpressionToOgcFilter_data() "" "" "geometry" - "5,6" + "5,6" "" "" ); @@ -375,7 +544,7 @@ void TestQgsOgcUtils::testExpressionToOgcFilter_data() "" "" "geometry" - "5,6" + "5,6" "" "" ); } @@ -405,7 +574,10 @@ void TestQgsOgcUtils::testExpressionToOgcFilterWFS11() qDebug( "SRSNAME: %s", srsName.toAscii().data() ); qDebug( "OGC : %s", doc.toString( -1 ).toAscii().data() ); - QCOMPARE( xmlText, doc.toString( -1 ) ); + + QDomElement xmlElem = comparableElement( xmlText ); + QDomElement ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); } void TestQgsOgcUtils::testExpressionToOgcFilterWFS11_data() @@ -429,14 +601,6 @@ void TestQgsOgcUtils::testExpressionToOgcFilterWFS11_data() "" ); } -static QString normalizeXML( const QString& xmlText ) -{ - QDomDocument doc; - if ( !doc.setContent( xmlText, true ) ) - return QString(); - return doc.toString( -1 ); -} - void TestQgsOgcUtils::testExpressionToOgcFilterWFS20() { QFETCH( QString, exprText ); @@ -462,15 +626,9 @@ void TestQgsOgcUtils::testExpressionToOgcFilterWFS20() qDebug( "SRSNAME: %s", srsName.toAscii().data() ); qDebug( "OGC : %s", doc.toString( -1 ).toAscii().data() ); - QString normalizedExpected( normalizeXML( xmlText ) ); - QString normalizedGot( normalizeXML( doc.toString( -1 ) ) ); - - if ( normalizedExpected != normalizedGot ) - { - qDebug( "Normalized expected: %s", normalizedExpected.toAscii().data() ); - qDebug( "Normalized got: %s", normalizedGot.toAscii().data() ); - } - QCOMPARE( normalizedExpected, normalizedGot ); + QDomElement xmlElem = comparableElement( xmlText ); + QDomElement ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); } void TestQgsOgcUtils::testExpressionToOgcFilterWFS20_data() @@ -570,15 +728,9 @@ void TestQgsOgcUtils::testSQLStatementToOgcFilter() filterVersion == QgsOgcUtils::FILTER_FES_2_0 ? "FES 2.0" : "unknown" ); qDebug( "OGC : %s", doc.toString( -1 ).toAscii().data() ); - QString normalizedExpected( normalizeXML( xmlText ) ); - QString normalizedGot( normalizeXML( doc.toString( -1 ) ) ); - - if ( normalizedExpected != normalizedGot ) - { - qDebug( "Normalized expected: %s", normalizedExpected.toAscii().data() ); - qDebug( "Normalized got: %s", normalizedGot.toAscii().data() ); - } - QCOMPARE( normalizedExpected, normalizedGot ); + QDomElement xmlElem = comparableElement( xmlText ); + QDomElement ogcElem = comparableElement( doc.toString( -1 ) ); + QVERIFY( compareElements( xmlElem, ogcElem ) ); } void TestQgsOgcUtils::testSQLStatementToOgcFilter_data() @@ -726,7 +878,7 @@ void TestQgsOgcUtils::testSQLStatementToOgcFilter_data() "" "" "geom" - "5,6" + "5,6" "" "" ); @@ -735,7 +887,7 @@ void TestQgsOgcUtils::testSQLStatementToOgcFilter_data() "" "" "geom" - "5,6" + "5,6" "" "" ); diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index c02fd02e36c..e86c5135605 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -69,6 +69,7 @@ ADD_PYTHON_TEST(PyQgsPalLabelingCanvas test_qgspallabeling_canvas.py) ADD_PYTHON_TEST(PyQgsPalLabelingComposer test_qgspallabeling_composer.py) ADD_PYTHON_TEST(PyQgsPalLabelingPlacement test_qgspallabeling_placement.py) ADD_PYTHON_TEST(PyQgsPanelWidget test_qgspanelwidget.py) +ADD_PYTHON_TEST(PyQgsPanelWidgetStack test_qgspanelwidgetstack.py) ADD_PYTHON_TEST(PyQgsPoint test_qgspoint.py) ADD_PYTHON_TEST(PyQgsPointClusterRenderer test_qgspointclusterrenderer.py) ADD_PYTHON_TEST(PyQgsPointDisplacementRenderer test_qgspointdisplacementrenderer.py) diff --git a/tests/src/python/test_provider_memory.py b/tests/src/python/test_provider_memory.py index c53335c6a2f..b1f6032cbad 100644 --- a/tests/src/python/test_provider_memory.py +++ b/tests/src/python/test_provider_memory.py @@ -127,6 +127,30 @@ class TestPyQgsMemoryProvider(unittest.TestCase, ProviderTestCase): ("MultiPoint", QgsWkbTypes.PointGeometry, QgsWkbTypes.MultiPoint), ("MultiLineString", QgsWkbTypes.LineGeometry, QgsWkbTypes.MultiLineString), ("MultiPolygon", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygon), + ("PointZ", QgsWkbTypes.PointGeometry, QgsWkbTypes.PointZ), + ("LineStringZ", QgsWkbTypes.LineGeometry, QgsWkbTypes.LineStringZ), + ("PolygonZ", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.PolygonZ), + ("MultiPointZ", QgsWkbTypes.PointGeometry, QgsWkbTypes.MultiPointZ), + ("MultiLineStringZ", QgsWkbTypes.LineGeometry, QgsWkbTypes.MultiLineStringZ), + ("MultiPolygonZ", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygonZ), + ("PointM", QgsWkbTypes.PointGeometry, QgsWkbTypes.PointM), + ("LineStringM", QgsWkbTypes.LineGeometry, QgsWkbTypes.LineStringM), + ("PolygonM", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.PolygonM), + ("MultiPointM", QgsWkbTypes.PointGeometry, QgsWkbTypes.MultiPointM), + ("MultiLineStringM", QgsWkbTypes.LineGeometry, QgsWkbTypes.MultiLineStringM), + ("MultiPolygonM", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygonM), + ("PointZM", QgsWkbTypes.PointGeometry, QgsWkbTypes.PointZM), + ("LineStringZM", QgsWkbTypes.LineGeometry, QgsWkbTypes.LineStringZM), + ("PolygonZM", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.PolygonZM), + ("MultiPointZM", QgsWkbTypes.PointGeometry, QgsWkbTypes.MultiPointZM), + ("MultiLineStringZM", QgsWkbTypes.LineGeometry, QgsWkbTypes.MultiLineStringZM), + ("MultiPolygonZM", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygonZM), + ("Point25D", QgsWkbTypes.PointGeometry, QgsWkbTypes.Point25D), + ("LineString25D", QgsWkbTypes.LineGeometry, QgsWkbTypes.LineString25D), + ("Polygon25D", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.Polygon25D), + ("MultiPoint25D", QgsWkbTypes.PointGeometry, QgsWkbTypes.MultiPoint25D), + ("MultiLineString25D", QgsWkbTypes.LineGeometry, QgsWkbTypes.MultiLineString25D), + ("MultiPolygon25D", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygon25D), ("None", QgsWkbTypes.NullGeometry, QgsWkbTypes.NoGeometry)] for v in testVectors: layer = QgsVectorLayer(v[0], "test", "memory") diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py index e9d4e3b341b..aebfcbcd994 100644 --- a/tests/src/python/test_provider_ogr.py +++ b/tests/src/python/test_provider_ogr.py @@ -221,5 +221,24 @@ class PyQgsOGRProvider(unittest.TestCase): os.unlink(datasource) self.assertFalse(os.path.exists(datasource)) + def testGeometryCollection(self): + ''' Test that we can at least retrieves attribute of features with geometry collection ''' + + datasource = os.path.join(self.basetestpath, 'testGeometryCollection.csv') + with open(datasource, 'wt') as f: + f.write('id,WKT\n') + f.write('1,POINT Z(2 49 0)\n') + f.write('2,GEOMETRYCOLLECTION Z (POINT Z (2 49 0))\n') + + vl = QgsVectorLayer('{}|layerid=0|geometrytype=GeometryCollection'.format(datasource), 'test', 'ogr') + self.assertTrue(vl.isValid()) + self.assertTrue(vl.featureCount(), 1) + values = [f['id'] for f in vl.getFeatures()] + self.assertEqual(values, ['2']) + del vl + + os.unlink(datasource) + self.assertFalse(os.path.exists(datasource)) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_shapefile.py b/tests/src/python/test_provider_shapefile.py index fd268b476db..040ed542710 100644 --- a/tests/src/python/test_provider_shapefile.py +++ b/tests/src/python/test_provider_shapefile.py @@ -30,6 +30,19 @@ start_app() TEST_DATA_DIR = unitTestDataPath() +def GDAL_COMPUTE_VERSION(maj, min, rev): + return ((maj) * 1000000 + (min) * 10000 + (rev) * 100) + + +class ErrorReceiver(): + + def __init__(self): + self.msg = None + + def receiveError(self, msg): + self.msg = msg + + class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): @classmethod @@ -380,5 +393,93 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase): vl = None + def testRepackUnderFileLocks(self): + ''' Test fix for #15570 and #15393 ''' + + # This requires a GDAL fix done per https://trac.osgeo.org/gdal/ticket/6672 + # but on non-Windows version the test would succeed + if int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2): + return + + tmpdir = tempfile.mkdtemp() + self.dirs_to_cleanup.append(tmpdir) + srcpath = os.path.join(TEST_DATA_DIR, 'provider') + for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): + shutil.copy(os.path.join(srcpath, file), tmpdir) + datasource = os.path.join(tmpdir, 'shapefile.shp') + + vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') + feature_count = vl.featureCount() + + # Keep a file descriptor opened on the .dbf, .shp and .shx + f_shp = open(os.path.join(tmpdir, 'shapefile.shp'), 'rb') + f_shx = open(os.path.join(tmpdir, 'shapefile.shx'), 'rb') + f_dbf = open(os.path.join(tmpdir, 'shapefile.dbf'), 'rb') + + # Delete a feature + self.assertTrue(vl.startEditing()) + self.assertTrue(vl.deleteFeature(1)) + + # Commit changes and check no error is emitted + cbk = ErrorReceiver() + vl.dataProvider().raiseError.connect(cbk.receiveError) + self.assertTrue(vl.commitChanges()) + self.assertIsNone(cbk.msg) + + vl = None + + del f_shp + del f_shx + del f_dbf + + # Test repacking has been done + ds = osgeo.ogr.Open(datasource) + self.assertTrue(ds.GetLayer(0).GetFeatureCount(), feature_count - 1) + ds = None + + def testRepackAtFirstSave(self): + ''' Test fix for #15407 ''' + + # This requires a GDAL fix done per https://trac.osgeo.org/gdal/ticket/6672 + # but on non-Windows version the test would succeed + if int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2): + return + + tmpdir = tempfile.mkdtemp() + self.dirs_to_cleanup.append(tmpdir) + srcpath = os.path.join(TEST_DATA_DIR, 'provider') + for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): + shutil.copy(os.path.join(srcpath, file), tmpdir) + datasource = os.path.join(tmpdir, 'shapefile.shp') + + ds = osgeo.ogr.Open(datasource) + lyr = ds.GetLayer(0) + original_feature_count = lyr.GetFeatureCount() + lyr.DeleteFeature(2) + ds = None + + vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') + + self.assertTrue(vl.featureCount(), original_feature_count) + + # Edit a feature (attribute change only) + self.assertTrue(vl.startEditing()) + self.assertTrue(vl.dataProvider().changeAttributeValues({0: {0: 100}})) + + # Commit changes and check no error is emitted + cbk = ErrorReceiver() + vl.dataProvider().raiseError.connect(cbk.receiveError) + self.assertTrue(vl.commitChanges()) + self.assertIsNone(cbk.msg) + + self.assertTrue(vl.featureCount(), original_feature_count - 1) + + vl = None + + # Test repacking has been done + ds = osgeo.ogr.Open(datasource) + self.assertTrue(ds.GetLayer(0).GetFeatureCount(), original_feature_count - 1) + ds = None + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py index 78167750991..6cf54085f35 100644 --- a/tests/src/python/test_provider_spatialite.py +++ b/tests/src/python/test_provider_spatialite.py @@ -19,7 +19,7 @@ import sys import shutil import tempfile -from qgis.core import QgsVectorLayer, QgsPoint, QgsFeature, QgsGeometry +from qgis.core import QgsVectorLayer, QgsPoint, QgsFeature, QgsGeometry, QgsProject, QgsMapLayerRegistry from qgis.testing import start_app, unittest from utilities import unitTestDataPath @@ -123,6 +123,18 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): sql += "VALUES (1, '[\"toto\",\"tutu\"]', '[1,-2,724562]', '[1.0, -232567.22]', GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))', 4326))" cur.execute(sql) + # 2 tables with relations + sql = "PRAGMA foreign_keys = ON;" + cur.execute(sql) + sql = "CREATE TABLE test_relation_a(artistid INTEGER PRIMARY KEY, artistname TEXT);" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_relation_a', 'Geometry', 4326, 'POLYGON', 'XY')" + cur.execute(sql) + sql = "CREATE TABLE test_relation_b(trackid INTEGER, trackname TEXT, trackartist INTEGER, FOREIGN KEY(trackartist) REFERENCES test_relation_a(artistid));" + cur.execute(sql) + sql = "SELECT AddGeometryColumn('test_relation_b', 'Geometry', 4326, 'POLYGON', 'XY')" + cur.execute(sql) + cur.execute("COMMIT") con.close() @@ -347,6 +359,29 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase): self.assertEqual(read_back['ints'], new_f['ints']) self.assertEqual(read_back['reals'], new_f['reals']) + def test_discover_relation(self): + artist = QgsVectorLayer("dbname=%s table=test_relation_a (geometry)" % self.dbname, "test_relation_a", "spatialite") + self.assertTrue(artist.isValid()) + track = QgsVectorLayer("dbname=%s table=test_relation_b (geometry)" % self.dbname, "test_relation_b", "spatialite") + self.assertTrue(track.isValid()) + QgsMapLayerRegistry.instance().addMapLayer(artist) + QgsMapLayerRegistry.instance().addMapLayer(track) + try: + relMgr = QgsProject.instance().relationManager() + relations = relMgr.discoverRelations([], [artist, track]) + relations = {r.name(): r for r in relations} + self.assertEqual({'fk_test_relation_b_0'}, set(relations.keys())) + + a2t = relations['fk_test_relation_b_0'] + self.assertTrue(a2t.isValid()) + self.assertEqual('test_relation_b', a2t.referencingLayer().name()) + self.assertEqual('test_relation_a', a2t.referencedLayer().name()) + self.assertEqual([2], a2t.referencingFields()) + self.assertEqual([0], a2t.referencedFields()) + finally: + QgsMapLayerRegistry.instance().removeMapLayer(track.id()) + QgsMapLayerRegistry.instance().removeMapLayer(artist.id()) + # This test would fail. It would require turning on WAL def XXXXXtestLocking(self): diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index 575b0475ff6..66eef5b6273 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -111,7 +111,8 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): - + + @@ -462,6 +463,31 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase): assert not vl.dataProvider().deleteFeatures([0]) + # Test with restrictToRequestBBOX=1 + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631&BBOX=400000,5400000,450000,5500000'), 'wb') as f: + f.write(""" + + unknown + + + + 426858,5427937 + + 100 + + +""".encode('UTF-8')) + + vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='1.0.0' restrictToRequestBBOX=1", 'test', 'WFS') + + extent = QgsRectangle(400000.0, 5400000.0, 450000.0, 5500000.0) + request = QgsFeatureRequest().setFilterRect(extent) + values = [f['INTFIELD'] for f in vl.getFeatures(request)] + self.assertEqual(values, [100]) + def testWFS10_latlongboundingbox_in_WGS84(self): """Test WFS 1.0 with non conformatn LatLongBoundingBox""" diff --git a/tests/src/python/test_qgsappstartup.py b/tests/src/python/test_qgsappstartup.py index 6acdef57e19..4bf21c0a6f5 100644 --- a/tests/src/python/test_qgsappstartup.py +++ b/tests/src/python/test_qgsappstartup.py @@ -101,7 +101,7 @@ class TestPyQgsAppStartup(unittest.TestCase): subdir = 'QGIS' # Linux if sys.platform[:3] == 'dar': # Mac subdir = 'qgis.org' - ini = os.path.join(subdir, 'QGIS2.ini') + ini = os.path.join(subdir, 'QGIS3.ini') for p in ['test_opts', 'test opts', 'test_optsé€']: self.doTestStartup(option="--optionspath", testDir=os.path.join(self.TMP_DIR, p), diff --git a/tests/src/python/test_qgsexpression.py b/tests/src/python/test_qgsexpression.py index 8115cab530a..7eece9e2d46 100644 --- a/tests/src/python/test_qgsexpression.py +++ b/tests/src/python/test_qgsexpression.py @@ -129,7 +129,8 @@ class TestQgsExpressionCustomFunctions(unittest.TestCase): def testReferencedColumnsNoSet(self): success = QgsExpression.registerFunction(self.no_referenced_columns_set) exp = QgsExpression('no_referenced_columns_set()') - self.assertEqual(exp.referencedColumns(), [QgsFeatureRequest.AllAttributes]) + self.assertEqual(exp.referencedColumns(), + {QgsFeatureRequest.AllAttributes}) def testReferencedColumnsSet(self): success = QgsExpression.registerFunction(self.referenced_columns_set) diff --git a/tests/src/python/test_qgspanelwidgetstack.py b/tests/src/python/test_qgspanelwidgetstack.py new file mode 100644 index 00000000000..4933acba253 --- /dev/null +++ b/tests/src/python/test_qgspanelwidgetstack.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsPanelWidgetStack. + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '05/10/2016' +__copyright__ = 'Copyright 2016, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.PyQt.QtWidgets import QWidget +from qgis.gui import QgsPanelWidget, QgsPanelWidgetStack +from qgis.testing import start_app, unittest +from qgis.PyQt.QtTest import QSignalSpy + +start_app() + + +class TestQgsPanelWidgetStack(unittest.TestCase): + + def testMainPanel(self): + """ test mainPanel methods """ + + s = QgsPanelWidgetStack() + + # no main panel + self.assertFalse(s.mainPanel()) + self.assertFalse(s.takeMainPanel()) + + # set main panel + p1 = QgsPanelWidget() + s.setMainPanel(p1) + self.assertEqual(s.mainPanel(), p1) + + # takeMainPanel() + self.assertEqual(s.takeMainPanel(), p1) + self.assertFalse(s.mainPanel()) + self.assertFalse(s.takeMainPanel()) + + def testAddingPanels(self): + """ test adding panels to stack """ + + s = QgsPanelWidgetStack() + mp = QgsPanelWidget() + s.setMainPanel(mp) + + p1 = QgsPanelWidget() + s.showPanel(p1) + self.assertEqual(s.currentPanel(), p1) + + p2 = QgsPanelWidget() + s.showPanel(p2) + self.assertEqual(s.currentPanel(), p2) + + def testAcceptCurrentPanel(self): + """ test accepting current panel """ + + s = QgsPanelWidgetStack() + # call on empty stack + s.acceptCurrentPanel() + + mp = QgsPanelWidget() + s.setMainPanel(mp) + # call on main panel - should be no effect + s.acceptCurrentPanel() + self.assertEqual(s.mainPanel(), mp) + self.assertEqual(s.currentPanel(), mp) + + # add panels + p1 = QgsPanelWidget() + s.showPanel(p1) + p2 = QgsPanelWidget() + s.showPanel(p2) + + # accept them + self.assertEqual(s.currentPanel(), p2) + p2_accept_spy = QSignalSpy(p2.panelAccepted) + s.acceptCurrentPanel() + self.assertEqual(s.currentPanel(), p1) + self.assertEqual(len(p2_accept_spy), 1) + p1_accept_spy = QSignalSpy(p1.panelAccepted) + s.acceptCurrentPanel() + self.assertEqual(s.currentPanel(), mp) + self.assertEqual(len(p1_accept_spy), 1) + + def testAcceptAllPanel(self): + """ test accepting all panels """ + s = QgsPanelWidgetStack() + # call on empty stack + s.acceptAllPanels() + + mp = QgsPanelWidget() + s.setMainPanel(mp) + # call on main panel - should be no effect + s.acceptAllPanels() + self.assertEqual(s.mainPanel(), mp) + self.assertEqual(s.currentPanel(), mp) + + # add panels + p1 = QgsPanelWidget() + s.showPanel(p1) + p1_accept_spy = QSignalSpy(p1.panelAccepted) + p2 = QgsPanelWidget() + s.showPanel(p2) + p2_accept_spy = QSignalSpy(p2.panelAccepted) + p3 = QgsPanelWidget() + s.showPanel(p3) + p3_accept_spy = QSignalSpy(p3.panelAccepted) + + # accept all + s.acceptAllPanels() + self.assertEqual(s.currentPanel(), mp) + self.assertEqual(len(p1_accept_spy), 1) + self.assertEqual(len(p2_accept_spy), 1) + self.assertEqual(len(p3_accept_spy), 1) + + def testClear(self): + """ test clearing stack """ + s = QgsPanelWidgetStack() + # call on empty stack + s.clear() + + # add panels + mp = QgsPanelWidget() + s.setMainPanel(mp) + p1 = QgsPanelWidget() + s.showPanel(p1) + p2 = QgsPanelWidget() + s.showPanel(p2) + p3 = QgsPanelWidget() + s.showPanel(p3) + + # clear + s.clear() + self.assertFalse(s.currentPanel()) + self.assertFalse(s.mainPanel()) + + def testTakeMainAcceptsAll(self): + """ test that taking the main panel accepts all open child panels""" + s = QgsPanelWidgetStack() + mp = QgsPanelWidget() + s.setMainPanel(mp) + p1 = QgsPanelWidget() + s.showPanel(p1) + p1_accept_spy = QSignalSpy(p1.panelAccepted) + p2 = QgsPanelWidget() + s.showPanel(p2) + p2_accept_spy = QSignalSpy(p2.panelAccepted) + p3 = QgsPanelWidget() + s.showPanel(p3) + p3_accept_spy = QSignalSpy(p3.panelAccepted) + + # take main + s.takeMainPanel() + self.assertEqual(len(p1_accept_spy), 1) + self.assertEqual(len(p2_accept_spy), 1) + self.assertEqual(len(p3_accept_spy), 1) +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgsrelationeditwidget.py b/tests/src/python/test_qgsrelationeditwidget.py index 245b7345b79..b6e1c84f97f 100644 --- a/tests/src/python/test_qgsrelationeditwidget.py +++ b/tests/src/python/test_qgsrelationeditwidget.py @@ -53,15 +53,15 @@ class TestQgsRelationEditWidget(unittest.TestCase): if 'QGIS_PGTEST_DB' in os.environ: cls.dbconn = os.environ['QGIS_PGTEST_DB'] # Create test layer - cls.vl_b = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."books" sql=', 'test', 'postgres') - cls.vl_a = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."authors" sql=', 'test', 'postgres') - cls.vl_link = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."books_authors" sql=', 'test', 'postgres') + cls.vl_b = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."books" sql=', 'books', 'postgres') + cls.vl_a = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."authors" sql=', 'authors', 'postgres') + cls.vl_link = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."books_authors" sql=', 'books_authors', 'postgres') QgsMapLayerRegistry.instance().addMapLayer(cls.vl_b) QgsMapLayerRegistry.instance().addMapLayer(cls.vl_a) QgsMapLayerRegistry.instance().addMapLayer(cls.vl_link) - relMgr = QgsProject.instance().relationManager() + cls.relMgr = QgsProject.instance().relationManager() cls.rel_a = QgsRelation() cls.rel_a.setReferencingLayer(cls.vl_link.id()) @@ -69,7 +69,7 @@ class TestQgsRelationEditWidget(unittest.TestCase): cls.rel_a.addFieldPair('fk_author', 'pk') cls.rel_a.setRelationId('rel_a') assert(cls.rel_a.isValid()) - relMgr.addRelation(cls.rel_a) + cls.relMgr.addRelation(cls.rel_a) cls.rel_b = QgsRelation() cls.rel_b.setReferencingLayer(cls.vl_link.id()) @@ -77,7 +77,7 @@ class TestQgsRelationEditWidget(unittest.TestCase): cls.rel_b.addFieldPair('fk_book', 'pk') cls.rel_b.setRelationId('rel_b') assert(cls.rel_b.isValid()) - relMgr.addRelation(cls.rel_b) + cls.relMgr.addRelation(cls.rel_b) # Our mock QgsVectorLayerTools, that allow injecting data where user input is expected cls.vltools = VlTools() @@ -125,7 +125,7 @@ class TestQgsRelationEditWidget(unittest.TestCase): self.assertEqual(self.table_view.model().rowCount(), 4) - @unittest.expectedFailure(os.environ['QT_VERSION'] == '4' and os.environ['TRAVIS_OS_NAME'] == 'linux') # It's probably not related to this variables at all, but that's the closest we can get to the real source of this problem at the moment... + @unittest.expectedFailure(os.environ.get('QT_VERSION', '5') == '4' and os.environ.get('TRAVIS_OS_NAME', '') == 'linux') # It's probably not related to this variables at all, but that's the closest we can get to the real source of this problem at the moment... def test_add_feature(self): """ Check if a new related feature is added @@ -201,6 +201,31 @@ class TestQgsRelationEditWidget(unittest.TestCase): self.assertEqual(2, self.table_view.model().rowCount()) + def test_discover_relations(self): + """ + Test the automatic discovery of relations + """ + relations = self.relMgr.discoverRelations([], [self.vl_a, self.vl_b, self.vl_link]) + relations = {r.name(): r for r in relations} + self.assertEqual({'books_authors_fk_book_fkey', 'books_authors_fk_author_fkey'}, set(relations.keys())) + + ba2b = relations['books_authors_fk_book_fkey'] + self.assertTrue(ba2b.isValid()) + self.assertEqual('books_authors', ba2b.referencingLayer().name()) + self.assertEqual('books', ba2b.referencedLayer().name()) + self.assertEqual([0], ba2b.referencingFields()) + self.assertEqual([0], ba2b.referencedFields()) + + ba2a = relations['books_authors_fk_author_fkey'] + self.assertTrue(ba2a.isValid()) + self.assertEqual('books_authors', ba2a.referencingLayer().name()) + self.assertEqual('authors', ba2a.referencedLayer().name()) + self.assertEqual([1], ba2a.referencingFields()) + self.assertEqual([0], ba2a.referencedFields()) + + self.assertEqual([], self.relMgr.discoverRelations([self.rel_a, self.rel_b], [self.vl_a, self.vl_b, self.vl_link])) + self.assertEqual(1, len(self.relMgr.discoverRelations([], [self.vl_a, self.vl_link]))) + def startTransaction(self): """ Start a new transaction and set all layers into transaction mode. diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 8bee7f37e0d..f289b761743 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -1058,6 +1058,16 @@ class TestQgsVectorLayer(unittest.TestCase): #layer.undoStack().redo() #checkFieldNames(['fldint']) + def test_RenameExpressionField(self): + layer = createLayerWithOnePoint() + exp_field_idx = layer.addExpressionField('1+1', QgsField('math_is_hard', QVariant.Int)) + + #rename and check + self.assertTrue(layer.renameAttribute(exp_field_idx, 'renamed')) + self.assertEqual(layer.fields()[exp_field_idx].name(), 'renamed') + f = next(layer.getFeatures()) + self.assertEqual(f.fields()[exp_field_idx].name(), 'renamed') + def test_fields(self): layer = createLayerWithOnePoint() diff --git a/tests/testdata/test_plugin_path/QGIS/QGIS2.ini b/tests/testdata/test_plugin_path/QGIS/QGIS3.ini similarity index 100% rename from tests/testdata/test_plugin_path/QGIS/QGIS2.ini rename to tests/testdata/test_plugin_path/QGIS/QGIS3.ini diff --git a/tests/testdata/test_plugin_path/qgis.org/QGIS2.ini b/tests/testdata/test_plugin_path/qgis.org/QGIS3.ini similarity index 100% rename from tests/testdata/test_plugin_path/qgis.org/QGIS2.ini rename to tests/testdata/test_plugin_path/qgis.org/QGIS3.ini