[qgis] 01/09: Imported Upstream version 2.14.4+dfsg

Bas Couwenberg sebastic at debian.org
Fri Jul 8 15:47:35 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository qgis.

commit 6d603d945f0ac41298b02623d3c743e4e3fcb7b0
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Jul 8 14:22:04 2016 +0200

    Imported Upstream version 2.14.4+dfsg
---
 CMakeLists.txt                                     |   25 +-
 ChangeLog                                          | 1190 ++++++++++++++++++++
 cmake/PyQtMacros.cmake                             |    6 +-
 cmake_templates/qgsconfig.h.in                     |    2 +
 debian/changelog                                   |   10 +-
 debian/control                                     |    1 +
 debian/control.in                                  |   56 +-
 debian/rules                                       |   11 +-
 ms-windows/osgeo4w/creatensis.pl                   |   36 +-
 ms-windows/osgeo4w/package-nightly.cmd             |    2 +-
 ms-windows/osgeo4w/package.cmd                     |   19 +-
 python/core/__init__.py                            |    8 +-
 python/core/composer/qgscomposition.sip            |    2 +
 .../core/composer/qgsgroupungroupitemscommand.sip  |   43 +
 python/core/core.sip                               |    1 +
 python/core/dxf/qgsdxfexport.sip                   |   22 +-
 python/core/geometry/qgscurvepolygonv2.sip         |    2 +-
 python/core/qgsdataprovider.sip                    |   41 +
 python/core/qgsfeaturerequest.sip                  |    2 +-
 python/core/qgsmaprenderer.sip                     |   14 +-
 python/core/qgsmapsettings.sip                     |    4 +-
 python/core/qgspallabeling.sip                     |    6 +-
 python/core/qgsrendercontext.sip                   |    3 +-
 .../core/symbology-ng/qgsellipsesymbollayerv2.sip  |    2 +-
 .../symbology-ng/qgsinvertedpolygonrenderer.sip    |   15 +-
 .../core/symbology-ng/qgsmarkersymbollayerv2.sip   |    4 +-
 .../symbology-ng/qgspointdisplacementrenderer.sip  |    9 +-
 python/core/symbology-ng/qgsrendererv2.sip         |   15 +
 python/core/symbology-ng/qgssymbollayerv2.sip      |    8 +-
 .../symbology-ng/qgsvectorfieldsymbollayer.sip     |    3 +
 python/gui/qgsannotationitem.sip                   |   26 +-
 python/gui/qgscomposerview.sip                     |    7 +
 python/gui/qgsrubberband.sip                       |    8 +
 python/plugins/GdalTools/tools/GdalTools_utils.py  |   28 +-
 python/plugins/GdalTools/tools/doContour.py        |   10 +
 python/plugins/GdalTools/tools/doProjection.py     |    4 +-
 .../db_manager/db_plugins/oracle/connector.py      |    2 +
 .../plugins/db_manager/db_plugins/oracle/plugin.py |    2 +
 .../db_manager/db_plugins/postgis/connector.py     |    4 +-
 .../db_manager/db_plugins/postgis/info_model.py    |   13 +-
 .../db_manager/db_plugins/postgis/plugin.py        |    4 +
 python/plugins/db_manager/dlg_sql_window.py        |    1 +
 python/plugins/fTools/tools/doGeometry.py          |   12 +-
 python/plugins/fTools/tools/doPointsInPolygon.py   |    3 +
 .../plugins/processing/algs/gdal/GdalAlgorithm.py  |    8 +-
 python/plugins/processing/algs/gdal/GdalUtils.py   |   14 +-
 python/plugins/processing/algs/gdal/contour.py     |    7 +-
 python/plugins/processing/algs/grass/GrassUtils.py |   11 +-
 .../plugins/processing/algs/grass7/Grass7Utils.py  |    7 +-
 .../processing/algs/qgis/AutoincrementalField.py   |    2 +-
 python/plugins/processing/algs/qgis/Clip.py        |    9 -
 python/plugins/processing/algs/qgis/ConvexHull.py  |    4 +-
 .../processing/algs/qgis/CreateConstantRaster.py   |   15 +-
 python/plugins/processing/algs/qgis/Difference.py  |    8 -
 python/plugins/processing/algs/qgis/Dissolve.py    |    1 +
 .../processing/algs/qgis/ExtractByAttribute.py     |    2 +-
 python/plugins/processing/algs/qgis/HubDistance.py |   36 +-
 .../plugins/processing/algs/qgis/Intersection.py   |    8 -
 python/plugins/processing/algs/qgis/Merge.py       |    2 +-
 .../processing/algs/qgis/SelectByAttribute.py      |    2 +-
 python/plugins/processing/algs/qgis/SpatialJoin.py |    2 +-
 .../processing/algs/qgis/SymmetricalDifference.py  |    8 -
 python/plugins/processing/algs/qgis/Union.py       |    8 -
 .../processing/algs/qgis/ui/FieldsMappingPanel.py  |    7 +-
 python/plugins/processing/algs/r/RAlgorithm.py     |  286 +++--
 python/plugins/processing/core/Processing.py       |    2 +-
 .../plugins/processing/gui/AlgorithmDialogBase.py  |   31 +-
 .../plugins/processing/gui/GetScriptsAndModels.py  |    8 +-
 python/plugins/processing/gui/HelpEditionDialog.py |    2 +-
 python/plugins/processing/gui/ResultsDialog.py     |   13 +-
 .../plugins/processing/modeler/ModelerAlgorithm.py |   13 +
 .../processing/modeler/ModelerParametersDialog.py  |   52 +-
 .../plugins/processing/script/ScriptAlgorithm.py   |   32 +
 python/plugins/processing/tools/dataobjects.py     |    5 +-
 python/plugins/processing/tools/raster.py          |   14 +-
 python/plugins/processing/tools/vector.py          |    5 +-
 python/plugins/processing/ui/DlgAlgorithmBase.ui   |   18 +-
 .../processing/ui/DlgGetScriptsAndModels.ui        |    2 +-
 python/plugins/processing/ui/DlgHelpEdition.ui     |   17 +-
 python/plugins/processing/ui/DlgResults.ui         |   21 +-
 src/analysis/network/qgsgraph.cpp                  |    1 -
 .../network/qgslinevectorlayerdirector.cpp         |    9 +-
 src/app/composer/qgscomposer.cpp                   |    4 +-
 src/app/composer/qgscomposerlegendwidget.cpp       |  122 +-
 src/app/composer/qgscomposerlegendwidget.h         |    2 +
 src/app/gps/qgsgpsinformationwidget.cpp            |    7 +-
 src/app/nodetool/qgsmaptoolnodetool.cpp            |    5 +
 src/app/qgisapp.cpp                                |  187 +--
 src/app/qgisapp.h                                  |    2 +
 src/app/qgsapplayertreeviewmenuprovider.cpp        |  100 +-
 src/app/qgsattributetabledialog.cpp                |    4 +-
 src/app/qgscustomization.cpp                       |    3 +-
 src/app/qgsjoindialog.cpp                          |   17 +-
 src/app/qgsjoindialog.h                            |    2 +
 src/app/qgslabelingwidget.cpp                      |   19 +-
 src/app/qgslabelingwidget.h                        |    3 +
 src/app/qgsmaptoolmeasureangle.cpp                 |    3 +-
 src/app/qgsmaptoolselectradius.cpp                 |    3 +-
 src/app/qgsmaptoolselectutils.cpp                  |   47 +-
 src/app/qgsmeasuretool.cpp                         |    3 +-
 src/app/qgsnewspatialitelayerdialog.cpp            |    2 +-
 src/app/qgsoptions.cpp                             |   20 +-
 src/core/CMakeLists.txt                            |    6 +-
 src/core/composer/qgsaddremoveitemcommand.cpp      |    4 +
 src/core/composer/qgscomposerarrow.cpp             |   23 +-
 src/core/composer/qgscomposeritemgroup.cpp         |    2 +-
 src/core/composer/qgscomposerlegend.cpp            |    9 +-
 src/core/composer/qgscomposerlegend.h              |    2 +-
 src/core/composer/qgscomposition.cpp               |   66 +-
 src/core/composer/qgscomposition.h                 |    2 +
 src/core/composer/qgsgroupungroupitemscommand.cpp  |   96 ++
 src/core/composer/qgsgroupungroupitemscommand.h    |   75 ++
 src/core/dxf/qgsdxfexport.cpp                      |  206 +++-
 src/core/dxf/qgsdxfexport.h                        |   32 +-
 src/core/dxf/qgsdxfpallabeling.cpp                 |  142 +--
 src/core/dxf/qgsdxfpallabeling.h                   |   60 +-
 src/core/gps/parse.c                               |   16 +-
 src/core/gps/qgsnmeaconnection.cpp                 |    2 +-
 src/core/qgis.cpp                                  |    6 +
 src/core/qgsapplication.cpp                        |    8 +-
 src/core/qgscoordinatereferencesystem.cpp          |   14 +
 src/core/qgscoordinatetransform.cpp                |    2 -
 src/core/qgsdataitem.cpp                           |    2 +
 src/core/qgsdataprovider.h                         |   41 +
 src/core/qgsdistancearea.cpp                       |    8 +-
 src/core/qgsexpression.cpp                         |    9 +-
 src/core/qgsfeaturerequest.h                       |    2 +-
 src/core/qgslabelfeature.cpp                       |    1 -
 src/core/qgslegendrenderer.cpp                     |    8 +-
 src/core/qgslogger.cpp                             |    2 +-
 src/core/qgsmaplayer.cpp                           |   12 +
 src/core/qgsmaplayerregistry.cpp                   |   21 +-
 src/core/qgsmaprenderer.h                          |   19 +-
 src/core/qgsmapsettings.h                          |    4 +-
 src/core/qgspallabeling.cpp                        |   15 +-
 src/core/qgspallabeling.h                          |   10 +-
 src/core/qgsrendercontext.cpp                      |    1 +
 src/core/qgsrendercontext.h                        |    1 +
 src/core/qgsrulebasedlabeling.cpp                  |   16 +-
 src/core/qgsrulebasedlabeling.h                    |   10 +-
 src/core/qgsvectorlayer.cpp                        |  276 +++--
 src/core/qgsvectorlayerdiagramprovider.cpp         |    2 +-
 src/core/qgsvectorlayerfeatureiterator.cpp         |   74 +-
 src/core/qgsvectorlayerlabelprovider.cpp           |    2 +-
 src/core/qgsvectorlayerlabelprovider.h             |    1 -
 src/core/raster/qgscontrastenhancement.cpp         |    5 +-
 src/core/raster/qgsrasterfilewriter.cpp            |    2 +-
 src/core/raster/qgsrasterlayer.cpp                 |    4 +-
 src/core/raster/qgsrastershader.cpp                |    3 +-
 .../raster/qgssinglebandpseudocolorrenderer.cpp    |    4 +-
 .../qgscategorizedsymbolrendererv2.cpp             |    2 +-
 src/core/symbology-ng/qgsellipsesymbollayerv2.cpp  |   36 +-
 src/core/symbology-ng/qgsellipsesymbollayerv2.h    |    2 +-
 src/core/symbology-ng/qgsfillsymbollayerv2.cpp     |   11 +
 src/core/symbology-ng/qgsfillsymbollayerv2.h       |   11 +-
 .../symbology-ng/qgsinvertedpolygonrenderer.cpp    |   41 +-
 src/core/symbology-ng/qgsinvertedpolygonrenderer.h |   21 +-
 src/core/symbology-ng/qgsmarkersymbollayerv2.cpp   |   96 +-
 src/core/symbology-ng/qgsmarkersymbollayerv2.h     |    4 +-
 .../symbology-ng/qgspointdisplacementrenderer.cpp  |   59 +-
 .../symbology-ng/qgspointdisplacementrenderer.h    |   15 +-
 src/core/symbology-ng/qgsrendererv2.h              |   15 +
 src/core/symbology-ng/qgssvgcache.cpp              |   19 +-
 src/core/symbology-ng/qgssymbollayerv2.cpp         |   14 +-
 src/core/symbology-ng/qgssymbollayerv2.h           |    8 +-
 src/core/symbology-ng/qgssymbollayerv2registry.cpp |    2 +-
 src/core/symbology-ng/qgssymbolv2.cpp              |    7 +-
 .../symbology-ng/qgsvectorfieldsymbollayer.cpp     |   19 +-
 src/core/symbology-ng/qgsvectorfieldsymbollayer.h  |    3 +
 src/customwidgets/qgsextentgroupboxplugin.cpp      |    4 +-
 src/customwidgets/qgsextentgroupboxplugin.h        |    2 +-
 src/gui/attributetable/qgsattributetablemodel.cpp  |   77 +-
 .../editorwidgets/core/qgseditorwidgetregistry.cpp |   12 +
 .../editorwidgets/core/qgseditorwidgetregistry.h   |    7 +
 src/gui/editorwidgets/qgscolorwidgetwrapper.cpp    |    9 +-
 src/gui/editorwidgets/qgsdatetimeedit.cpp          |    2 +-
 src/gui/editorwidgets/qgsphotowidgetwrapper.cpp    |   37 +
 src/gui/editorwidgets/qgsphotowidgetwrapper.h      |    2 +
 src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp  |    2 +
 src/gui/qgsattributeform.cpp                       |    7 +-
 src/gui/qgscodeeditor.h                            |    4 +-
 src/gui/qgscollapsiblegroupbox.cpp                 |    4 +-
 src/gui/qgscolorbuttonv2.cpp                       |    4 +-
 src/gui/qgscomposerview.cpp                        |   17 +-
 src/gui/qgscomposerview.h                          |    7 +
 src/gui/qgsexternalresourcewidget.cpp              |   17 +-
 src/gui/qgsmapcanvas.cpp                           |   15 +-
 src/gui/qgsmessagelogviewer.cpp                    |    3 +-
 src/gui/qgspixmaplabel.cpp                         |   16 +-
 src/gui/qgsrasterlayersaveasdialog.cpp             |   40 +-
 src/gui/qgsrelationeditorwidget.cpp                |    8 +
 src/gui/qgsrubberband.cpp                          |   20 +
 src/gui/qgsrubberband.h                            |    8 +
 src/gui/symbology-ng/qgs25drendererwidget.cpp      |   19 +-
 src/plugins/georeferencer/qgsgeoreftransform.cpp   |    7 +-
 src/plugins/gps_importer/qgsgpsplugin.cpp          |    2 +-
 .../offline_editing/offline_editing_plugin_gui.cpp |    2 +-
 src/plugins/roadgraph/shortestpathwidget.cpp       |    2 +-
 .../qgsdelimitedtextfeatureiterator.cpp            |    2 +-
 src/providers/memory/qgsmemoryprovider.cpp         |    1 +
 src/providers/mssql/qgsmssqlprovider.cpp           |    4 +-
 src/providers/ogr/qgsogrfeatureiterator.cpp        |   23 +-
 src/providers/ogr/qgsogrprovider.cpp               |  310 ++++-
 src/providers/ogr/qgsogrprovider.h                 |   33 +-
 src/providers/oracle/qgsoracleconn.cpp             |    5 +-
 src/providers/oracle/qgsoracledataitems.cpp        |  111 +-
 src/providers/oracle/qgsoracledataitems.h          |    1 +
 .../oracle/qgsoracleexpressioncompiler.cpp         |   30 +-
 src/providers/oracle/qgsoraclefeatureiterator.cpp  |   99 +-
 src/providers/oracle/qgsoraclefeatureiterator.h    |    3 +-
 src/providers/oracle/qgsoraclenewconnection.cpp    |    2 +
 src/providers/oracle/qgsoracleprovider.cpp         |  112 +-
 src/providers/oracle/qgsoracleprovider.h           |    3 +
 src/providers/postgres/qgspostgresconn.cpp         |   37 +-
 .../postgres/qgspostgresfeatureiterator.cpp        |    2 +-
 src/providers/spatialite/qgsspatialiteconnection.h |   16 +-
 src/providers/spatialite/qgsspatialiteconnpool.h   |    8 +-
 .../spatialite/qgsspatialitefeatureiterator.cpp    |    2 +-
 src/providers/spatialite/qgsspatialiteprovider.cpp |   34 +-
 src/providers/spatialite/qgsspatialiteprovider.h   |    3 +
 src/providers/wms/qgswmsprovider.cpp               |   43 +-
 src/python/qgspythonutilsimpl.cpp                  |    2 +-
 src/server/qgsmslayercache.cpp                     |    6 +
 src/server/qgsserver.cpp                           |    7 +
 .../effects/qgseffectstackpropertieswidgetbase.ui  |    4 +-
 src/ui/qgscustomizationdialogbase.ui               |    2 +-
 src/ui/qgsoraclenewconnectionbase.ui               |   28 +-
 src/ui/qgsrasterlayersaveasdialogbase.ui           |   20 +-
 tests/src/app/CMakeLists.txt                       |    1 +
 tests/src/app/testqgisapppython.cpp                |   97 ++
 tests/src/app/testqgsmaptoolidentifyaction.cpp     |  137 +++
 tests/src/core/testqgscomposergroup.cpp            |  273 ++++-
 tests/src/core/testqgscomposerpicture.cpp          |   18 +
 .../src/core/testqgscoordinatereferencesystem.cpp  |    7 +
 tests/src/core/testqgsdistancearea.cpp             |   24 +
 tests/src/core/testqgsexpression.cpp               |   18 +
 tests/src/core/testqgsgeometry.cpp                 |    6 +-
 tests/src/core/testqgslabelingenginev2.cpp         |    5 +-
 tests/src/gui/testqgsrubberband.cpp                |   29 +
 tests/src/python/CMakeLists.txt                    |    8 +
 tests/src/python/providertestbase.py               |   70 +-
 tests/src/python/test_provider_ogr.py              |  175 +++
 tests/src/python/test_provider_oracle.py           |  107 ++
 tests/src/python/test_provider_shapefile.py        |  198 +++-
 tests/src/python/test_provider_spatialite.py       |   84 ++
 tests/src/python/test_provider_tabfile.py          |   28 +-
 tests/src/python/test_qgscolorbuttonv2.py          |   43 +
 tests/src/python/test_qgscomposerview.py           |   65 ++
 tests/src/python/test_qgsexpression.py             |   20 +-
 tests/src/python/test_qgsfeatureiterator.py        |   50 +-
 tests/src/python/test_qgssymbollayerv2.py          |   14 +
 tests/src/python/test_qgsvectorlayer.py            |   43 +-
 .../expected_composerpicture_issue_14644.png       |  Bin 0 -> 33704 bytes
 .../expected_composerpicture_issue_14644_mask.png  |  Bin 0 -> 13448 bytes
 .../expected_legend_basic_mask.png                 |  Bin 19044 -> 19941 bytes
 .../expected_legend_big_marker_mask.png            |  Bin 20496 -> 21900 bytes
 .../expected_legend_filter_by_expression.png       |  Bin 9073 -> 6893 bytes
 .../expected_legend_filter_by_expression_mask.png  |  Bin 12134 -> 5909 bytes
 .../expected_legend_filter_by_map.png              |  Bin 10637 -> 7965 bytes
 .../expected_legend_filter_by_map_mask.png         |  Bin 13766 -> 8402 bytes
 .../expected_legend_filter_by_map_dupe_mask.png    |  Bin 2873 -> 2922 bytes
 .../expected_legend_filter_by_polygon.png          |  Bin 9073 -> 6893 bytes
 .../expected_legend_filter_by_polygon_mask.png     |  Bin 12134 -> 6538 bytes
 .../expected_legend_long_symbol_text_mask.png      |  Bin 23507 -> 24351 bytes
 .../expected_legend_mapunits_mask.png              |  Bin 2192 -> 2698 bytes
 .../expected_legend_raster_border_mask.png         |  Bin 1014 -> 1049 bytes
 .../expected_legend_three_columns_mask.png         |  Bin 19184 -> 20020 bytes
 tests/testdata/elev.gpx                            |   15 +
 tests/testdata/noelev.gpx                          |   12 +
 tests/testdata/provider/testdata_oracle.sql        |   32 +
 tests/testdata/raster/test.asc                     |    6 +
 tests/testdata/svg/issue_14644.svg                 |  194 ++++
 272 files changed, 6209 insertions(+), 1525 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e16eac..afccf48 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 SET(CPACK_PACKAGE_VERSION_MAJOR "2")
 SET(CPACK_PACKAGE_VERSION_MINOR "14")
-SET(CPACK_PACKAGE_VERSION_PATCH "3")
+SET(CPACK_PACKAGE_VERSION_PATCH "4")
 SET(COMPLETE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH})
 SET(RELEASE_NAME "Essen")
 IF (POLICY CMP0048) # in CMake 3.0.0+
@@ -150,6 +150,11 @@ IF (MSVC AND CMAKE_GENERATOR MATCHES "NMake")
   SET (USING_NMAKE TRUE)
 ENDIF (MSVC AND CMAKE_GENERATOR MATCHES "NMake")
 
+IF (CMAKE_GENERATOR MATCHES "Ninja")
+  # following variable is also used in qgsconfig.h
+  SET (USING_NINJA TRUE)
+ENDIF (CMAKE_GENERATOR MATCHES "Ninja")
+
 #############################################################
 # check if lexer and parser are not missing
 # http://www.mail-archive.com/cmake@cmake.org/msg02861.html
@@ -393,9 +398,9 @@ IF (PEDANTIC)
   MESSAGE (STATUS "Pedantic compiler settings enabled")
   IF(MSVC)
     SET(_warnings "")
-    IF (NOT USING_NMAKE)
+    IF (NOT USING_NMAKE AND NOT USING_NINJA)
       SET(_warnings "${_warnings} /W4" )
-    ENDIF (NOT USING_NMAKE)
+    ENDIF (NOT USING_NMAKE AND NOT USING_NINJA)
 
     # disable warnings
     SET(_warnings "${_warnings} /wd4100 ")  # unused formal parameters
@@ -502,10 +507,10 @@ IF (WIN32)
     ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_WARNINGS)
 
     IF (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
-      IF (NOT USING_NMAKE)
+      IF (NOT USING_NMAKE AND NOT USING_NINJA)
         MESSAGE (STATUS "Generating browse files")
         ADD_DEFINITIONS( /FR )
-      ENDIF (NOT USING_NMAKE)
+      ENDIF (NOT USING_NMAKE AND NOT USING_NINJA)
     ENDIF (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
 
     IF (INSTALL_DEPS)
@@ -740,9 +745,15 @@ IF (EXISTS ${CMAKE_SOURCE_DIR}/.git/index)
   FIND_PROGRAM(GITCOMMAND git PATHS c:/cygwin/bin)
   IF(GITCOMMAND)
     IF(WIN32)
+      IF(USING_NINJA)
+       SET(ARG %a)
+      ELSE(USING_NINJA)
+       SET(ARG %%a)
+      ENDIF(USING_NINJA)
       ADD_CUSTOM_COMMAND(
-        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h
-        COMMAND for /f \"usebackq tokens=1\" %%a in "(`\"${GITCOMMAND}\" log -n1 --oneline`)" do echo \#define QGSVERSION \"%%a\" >${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h.temp
+        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h ${CMAKE_CURRENT_BINARY_DIR}/qgsversion.inc
+        COMMAND for /f \"usebackq tokens=1\" ${ARG} in "(`\"${GITCOMMAND}\" log -n1 --oneline`)" do echo \#define QGSVERSION \"${ARG}\" >${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h.temp
+        COMMAND for /f \"usebackq tokens=1\" ${ARG} in "(`\"${GITCOMMAND}\" log -n1 --oneline`)" do echo PROJECT_NUMBER = \"${COMPLETE_VERSION}-${RELEASE_NAME} \(${ARG}\)\" >${CMAKE_CURRENT_BINARY_DIR}/qgsversion.inc
         COMMAND ${CMAKE_COMMAND} -DSRC=${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h.temp -DDST=${CMAKE_CURRENT_BINARY_DIR}/qgsversion.h -P ${CMAKE_SOURCE_DIR}/cmake/CopyIfChanged.cmake
         MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/.git/index
         WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
diff --git a/ChangeLog b/ChangeLog
index 9760ef2..9c2bfa2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,1189 @@
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-08
+
+    Fix build
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-08
+
+    Fix incorrect label/diagram distance when map is rotated
+
+    (Cherry-picked from 873eb7f90a271970a904b5d4a37740f91b24f941)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-07-08
+
+    [roadgraph] fix invalid characters in message (fix #15233)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-07
+
+    [oracle] Fix handling of date/time types
+
+    Also add test for Oracle default values
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from f9d839fac5569d5ceb743643873a08a124ddf101)
+
+nirvn <nirvn.asia at gmail.com>	2016-06-12
+
+    fix crash when right-clicking on geometryless layers
+
+    (cherry picked from commit 054604bc709129efbc05098f09cacd8f0204efe9)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    Fix build
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Implement provider test suite
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from 38e65c3f75bd57c16b37cff95137337db032014b)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Fix feature request when expression compilation fails,
+    fix incorrect provider side use of limit when expression compilation
+    could not be used
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from d089cbaaa6a51ca54414c7ea817539bb51862c72)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Always keep geometry when fetched, as it may have been
+    requested for filter expressions or sorting
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from 369e130d48ee116916ab0ef8be7fa04406f11a34)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Fixes for oracle expression compilation
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from 64bfbaaf5b44153f068a5f2d140e1abe4d4b83f5)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Fix broken iterator rewind
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from d785b904ac51ebeb2d004da78bb7840e27e426a2)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Fix detection of geometry type when table contains some
+    empty geometries
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from fe93e6217548acb4710a63f94c4c18070f86811f)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Fix missing items in oracle connections in browser
+
+    On behalf of Faunalia, sponsored by ENEL
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Don't report import failures when user has cancelled import
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from a31a1d3f13101a906de88bd71981c61526ccb1ae)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-06
+
+    [oracle] Synchronise wording and behaviour of browser actions
+    with postgres provider
+
+    On behalf of Faunalia, sponsored by ENEL
+
+    (cherry-picked from 82be399fcb2cfdcd64c41039f1a50f9c89c0380a)
+
+Marco Hugentobler <marco.hugentobler at sourcepole.ch>	2015-12-17
+
+    Legend: leave away empty groups (fix #12969)
+
+    (cherry-picked from c78347)
+
+Martin Dobias <wonder.sk at gmail.com>	2016-07-05
+
+    Fix recording of points for live GPS tracking (fixes #14996)
+
+    (cherry picked from commit 1cb4adc0845744f92214842299722345c7c99859)
+
+rldhont <rldhont at gmail.com>	2016-06-23
+
+    [BUGFIX] QgsMapLayerRegistry: Check layers before removed
+
+    Probably fixed #15088 Segmentation fault when using layersRemoved SIGNAL
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-02
+
+    Fix margins on photo and web view edit widgets
+
+    (cherry-picked from ece46d1c43df31d5688404e3c4ff5ee016c307c1)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-02
+
+    Various fixes for photo editor and external resource widgets
+
+    - Clear picture when changing from a feature with a picture to
+    a feature with no picture set
+    - Don't try to load "NULL" as a filename
+    - Fix calculation of widget size so that widget can collapse
+    its size to nothing when it doesn't have a picture set
+    - Avoid incorrect scaling and cropping of pictures
+
+    (cherry-picked from 0c161950a9974aa1cf165ed6151ff854c924665a)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-02
+
+    Fix Travis build, take 2
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-02
+
+    Fix travis build
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-02
+
+    Fix test
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [ogr] Read GPX elevation values as geometry Z values
+
+    (cherry-picked from 4080aad0eef5d8ca78fdab2e3c4e3ce0815e6610)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix cannot set line symbol data defined properties for vector
+    field marker (refs #15131)
+
+    (cherry-picked from 6d6aa8dd271f8990ceb713e3deb4cb57cca6db99)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix vector field symbol does not use subsymbol color (fix #15130)
+
+    (cherry-picked from e6034e9a1ed69f6e345a7a2dc7e42754107896a3)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Capitalisation
+
+    (cherry-picked from 302f8d418f8bc0e228d8897fbbd4f65895430047)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Make measure dialog only stay on top of QGIS window (fix #12261)
+
+    (cherry-picked from 340a6f654b5803f21a89ccbc22950cca80f4331b)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix displacement renderer when using map unit sizes for symbols
+
+    (cherry-picked from 8868303dbd4fad26eeb88bc5a18ba3ffe5a0e719)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix displacement symbols with data defined properties (fix #9601)
+
+    Previously only the attributes of the first feature were being
+    used to render the points inside a group
+
+    (cherry-picked from a6f96ba51b736cc97b3d8d6a848940737a46a6bc)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [composer] Instant feedback for legend when linked map changes
+
+    (cherry-picked from f9ff5e25d3eb1a2df52d5460e2ae8944aaa6418f)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [composer] Double click legend item to edit text (fix #13578)
+
+    (cherry-picked from dbf8d89459fb37921c1ce0ddb36ef5c1dd4a608d)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Followup 71dc33, fix projective transform in georeferencer (fix #14551)
+
+    (cherry-picked from 83160632ace41ef0306c903d977ef27283f83dcb)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix selecting features by radius (fix #14748)
+
+    (cherry-picked from 33a5ee7ab47c8c6db2985ef755ef6bdca23b36a0)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix incorrect area calculation for polygon (fix #14675)
+
+    (cherry-picked from bf4cf51e1a259252d5a762845f20aaac12d672bf)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Save raster symbology with full precision (fix #14950)
+
+    (cherry-picked from a6cb81bf609196677118a87c468489ba58583bae)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-06-14
+
+    Remove "attribute table" from dialog title (fix #14959)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix actions are not enabled when loading layer with default style (fix #13910)
+
+    (cherry-picked from 1563526f0dc59ae2b1fdc4a58c07488c5603de3d)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix cannot deactivate customization widget catcher (fix #9732)
+
+    (cherry-picked from e76571959431b5c9b96862578f8485dbfd203521)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [composer] Make parameterised SVG arrow heads respect colors (fix #14997)
+
+    (cherry-picked from 78c434a6c4fbdb6004c725abe0121dc4dd6dd8dc)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix debug noise when using 25D geometry layers
+
+    (cherry-picked from 92830a25090aae54d9b01b56bfe31f48a5fed71f)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Classifications on joined fields should only consider values which
+    are matched to layer's features (fix #9051)
+
+    (cherry-picked from 16eb1e14d0ba2b78f2d12a0813887a7bc493204c)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Prevent creating invalid join in add join dialog (fixes crash)
+
+    (cherry-picked from 693ead28bbb8f7837d8d211c1472eb32069084d9)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Update test import
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix virtual fields which depend on other virtual fields may not be
+    calculated in some circumstances (fix #14939)
+
+    (cherry-picked from df0d5969aa8e3bf0ec653bb6d6395afe1cf58950)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Default to requesting all attributes for python expression functions
+
+    Fix #14985
+
+    (cherry-picked from 1bc17e6c4f40ab64a7d3443886e13f926dab23b7)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Always use string comparison in expressions for string fields
+
+    Fixes #13204
+
+    (cherry-picked from 8a2e8715fb3ffa21ecbd3d38310b04257029f656)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix issues rendering SVG with text (fix #14644, #14794)
+
+    (cherry-picked from 2265115f8003857e538f07287c1337fed463a39c)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix home dir being added as default svg and template path
+
+    Fixes #14662, #14652, #14883
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Add missing proxies for legend check behaviour to subrenderers
+    for inverted polygon and displacement renderers
+
+    (cherry-picked from 7a8d9dd50654a27c4ecaaccfd66974dded6aaaa9)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix inverted polygons and displacement renderer don't allow right
+    click on legend items (fix #14966)
+
+    (cherry-picked from b2c43cb99715194c79dd011656f372edb8745a77)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix inverted polygon & displacement renderer don't show
+    color wheel in legend menu
+
+    Move embedded renderer handling to QgsFeatureRendererV2 and
+    add support for embedded renders to legend context menu
+
+    Fix #14967
+
+    (cherry-picked from b32afce79d473738716834216d25eea9783ce619)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [effect] fix issue with svg marker and antialiasing (fixes #14960)
+
+    Credit for original patch to @nirvn
+
+    (cherry-picked from 179a92cd65a70a411c8085875ab3e20bf5fa5d46)
+
+Sandro Mani <manisandro at gmail.com>	2015-12-15
+
+    Only emit QgsCollapsibleGroupBoxBasic::collapsedStateChanged when state really has changed
+
+Sandro Mani <manisandro at gmail.com>	2016-05-12
+
+    Fix avoid crash when measuring empty geometry
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Add QgsDistanceArea test for empty polygon
+
+    (cherry-picked from ebdccf3869e8ccf1e495b7e5ba6119d94fec4980)
+
+Marco Hugentobler <marco.hugentobler at sourcepole.ch>	2016-02-02
+
+    Fix crash when saving categorized symbology
+
+Sandro Mani <manisandro at gmail.com>	2016-03-14
+
+    Fix QgsCurvePolygon sip bindings
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [processing] fix missing quotes to field name in refactor fields
+
+    (cherry-picked from 14342ce65a84c1efa377862eeac085642736ed1b)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Set cursor to pan cursor when space-dragging canvas
+
+    (cherry-picked from 79d640715e5a5bf0d4e5e1f0d9d43becc3990502)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [composer] Prevent zooming out/in too far
+
+    Would cause issues when scale became 0 and it was impossible
+    to further interact with the composer.
+
+    (cherry-picked from aa53cfe3871a149371059b2c92ddf4233576e00f)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Fix incorrect tooltips in effects widget
+
+    (cherry-picked from e2abea671c052ffdce98474dacb9e3d3f287eb8e)
+
+ThomasG77 <thomas_gratier at yahoo.fr>	2016-05-26
+
+    Cast each child of QgsAnnotationItem in SIP bindings for Python
+
+    (cherry-picked from eeea18ecba2ecce8c57d4539d00768821dd1e9e7)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Add some tooltips to relation subform buttons
+
+    (cherry-picked from fff938c5d9d3b4e213ce37334dfbc180591f8266)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    [gui] improve no color logic by keeping RGB values
+
+    (cherry-picked from 64823d10c57e7769aa27cbfa273a5f9496e528f5)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Add test for color button that setting color to transparent
+    will retain non-alpha color components
+
+    (cherry-picked from 4089cc52266aab0e41b83546ee33c22040c1f417)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-07-01
+
+    Always mark features as valid when added to memory provider
+
+    For other providers features will automatically be made valid
+    through the proces of adding to the provider's storage itself
+    and then later retrieving via iterators. But the memory
+    provider uses a direct copy of the feature, so if we don't
+    explicitly mark features as valid the provider may be
+    returning features incorrectly marked as invalid.
+
+    ...and add tests to ensure all providers always return valid
+    features
+
+    (cherry-picked from f2b70cf5e27c7de920ea3b54893019fb3f450cf6)
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-30
+
+    Indentation fix
+
+Merge: c237ba7 a3dfd9d
+Even Rouault <even.rouault at spatialys.com>	2016-06-30
+
+    Merge branch 'release-2_14' of github.com:qgis/QGIS into release-2_14
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-30
+
+    [Spatialite provider] Make sure to release dangling connections on provider closing
+
+    Fixes #15137
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-30
+
+    [OGR provider] Make sure to release dangling connections on provider closing
+
+    Fixes #15137
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-30
+
+    mssql provider: make mapping of import fields case-insensitive
+
+    (cherry picked from commit 6f7a999ebf3bdf1561cfce49e4908a23adf958b3)
+
+Martin Dobias <wonder.sk at gmail.com>	2016-06-30
+
+    Return zero from "points in polygons" instead of both zero/null (fixes #15158)
+
+Sandro Santilli <strk at kbt.io>	2016-06-28
+
+    Only insert segment snap points in the layer they belong
+
+    Fixes #13952
+
+aharfoot <aharfoot at users.noreply.github.com>	2016-06-24
+
+    Fix bug in GDALTools Assign Projection
+
+    Assign Projection uses gdalwarp, and this works correctly when a raster has no CRS assigned, however, in the case of a raster with an incorrect CRS assignment, then gdalwarp will reproject the raster instead of simply changing the assigned CRS, propagating the error. Switching the Assign Projection tool to use gdal_translate provides the intended behaviour in all situations.
+    (cherry picked from commit bb8156836203b4dde2a6bb8c3cef69a3263871df)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-29
+
+    add objectName to button
+
+    (cherry picked from commit 00832918ffa110b77036f8581b56dea62e7434d6)
+
+Tudor Bărăscu <tudor.barascu at qtibia.ro>	2016-06-29
+
+    Show cannot pan WARNING for NULL geometry (#3255)
+
+    refs [#15122](https://hub.qgis.org/issues/15122)
+
+rldhont <rldhont at gmail.com>	2016-06-28
+
+    [BUGFIX] QgsMSLayerCache: remove layer from QgsMapLayerRegistry before delete it
+
+    In QGIS Server, layers can be added to QgsMapLayerRegistry and delete by QgsMSLayerCache. This means that QgsMapLayerRegistry can have reference to deleted pointers.
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-27
+
+    QgsCoordinateTransform::transformCoords(): do not convert elevations to radians
+
+    Fixes #14702
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-28
+
+    [processing] add missed variable initialization (fix #15154)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-28
+
+    Fix debug output (followup a7dcaad)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-27
+
+    [processing] support more field types
+
+    (cherry picked from commit 1b60b088a22ce2b417ec8a2c69266b98efa663f2)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-24
+
+    don't apply raster style to vectors (fix #15001)
+
+    (cherry picked from commit 3ece8aca11f242a71be7a8775ae1109028c8ad3f)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-27
+
+    dxf export: complete doxymentation updates (followup 07113b0)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-27
+
+    dxf export: merge doxymentation updates (followup 4c4ad05)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-27
+
+    dxf export: more adaptions to labeling changes (backported from b3bf4a1)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-27
+
+    [processing] drop WebView dependency (backported from cc7eb27)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-23
+
+    oracle provider: by default skip additional geometry columns (on 64bit
+    Windows/Linux OCI crashes when there are more than three geometry
+    columns)
+
+    (cherry picked from commit 4b00182482a2e897c8318fe4b46fa7d5dcc8ac6d)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-22
+
+    db manager: re-enable margins with line numbers in sql editor
+    (fixes #15110)
+
+    (cherry picked from commit d9f934f9508b6388f8245fe695bab0b7cc649c1e)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-22
+
+    selection by polygon: use 40 instead of 4 points for selection rectangle for more accurate transformation (fixes #13754)
+
+    (cherry picked from commit 0a83f182f3e8aea684163096f70efc9f38399ad2)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-22
+
+    save as raster: fix vrt creation (fixes #14171)
+
+    (cherry picked from commit d69ec2e8bb1c3a4b036f063454f27dff60c4e643)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-21
+
+    postgres provider: allow database without postgis (fixes #6891)
+
+    (cherry picked from commit 94413b35a08a154e5b40c2a9cab13fcb0d0ee4a4)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-20
+
+    db manager: show database name in postgis connection details (fixes #3489)
+
+    (cherry picked from commit ecf3b3719d4f24dc7f369d3253150c4990c9c681)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-21
+
+    gdal tools: use native file dialogs (fixes #5500)
+
+    (cherry picked from commit 2c112f96cab55eed2f3922dbf2dd14522eac5aee)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-14
+
+    open message log on QgisApp::openMessageLog instead of toggling it
+
+    (cherry picked from commit 5bb2c7d175c9113b844738f18c38c00ecc76c6bc)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-14
+
+    update 'Report an issue' link
+
+    (cherry picked from commit 0db9556b642ce855548d3be6501e4d9935a62e0a)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-07
+
+    avoid closing the last tab of the message log viewer
+
+    (cherry picked from commit ee8e340d6e84aa91b7b4813e2c0668cc2a6fd5f6)
+
+Juergen E. Fischer <jef at norbit.de>	2016-04-12
+
+    dxf export: add support for expression contexts and rotated symbols (fixes #14495)
+
+    (cherry picked from commit c30f71ac73796bb2a39ff0fd4f2349b9fde222ac)
+
+Juergen E. Fischer <jef at norbit.de>	2016-04-11
+
+    dxf export: support rule based labeling (fixes #13757)
+
+    (cherry picked from commit f19a35c34e758ed17704c77a7ee334bf866bd46f)
+
+Juergen E. Fischer <jef at norbit.de>	2016-04-03
+
+    extent group box: fix header spelling
+
+    (cherry picked from commit 59de73ac3ba2c0c56ba7bf7412bafad8e739ac4e)
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-30
+
+    oracle provider: also try sdo_filter on queries
+
+    (cherry picked from commit 1cc82af899302107232832a4a31e1c782136c07c)
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-24
+
+    don't strip utf8 in log
+
+Matthias Kuhn <matthias at opengis.ch>	2016-06-26
+
+    Don't lock canvas when trying to pan to null geometry
+
+    Fix #15122
+
+Matthias Kuhn <matthias at opengis.ch>	2016-06-26
+
+    QgsMapLayerRegistry::removeMapLayers don't emit signals when empty
+
+    Fix #15088
+
+Richard Duivenvoorde <richard at duif.net>	2016-06-21
+
+    Adding &TRANSPARENT=true makes too big legend images look good
+
+    See http://hub.qgis.org/issues/15089 for screenshots & test service url
+
+Richard Duivenvoorde <richard at duif.net>	2016-06-17
+
+    WMS GetLegendGraphic fix #15055
+
+    See http://hub.qgis.org/issues/15055
+
+    When creating the legend image url, this tests for available queryparams
+    in a case-insensitive way...
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-24
+
+    [processing] add support for longlong fields in spatial join alg (fix #15072)
+
+    (cherry picked from commit 87fea73647a2319aaa3c110cb26967f7f217d7f4)
+
+Merge: a419515 e94c24d
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-24
+
+    Merge pull request #3183 from DHI-GRAS/release-2_14
+
+    [processing] fixes to GrassUtils and Grass7Utils (mostly cherry-picked jef-n)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-23
+
+    use QgsWKBTypes to check layer wkb type (follow up 904dc21625)
+
+    (cherry picked from commit e6970ba597a778afe47b551a6999f5305450f52b)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-23
+
+    support 25D layers in network analysis library (fix #11952)
+
+    (cherry picked from commit 904dc216251d183305ba1c5dc6c846be10a879db)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-23
+
+    fix signal-slot connection in New SpatiaLite layer dialog (fix #14343)
+
+    (cherry picked from commit 70b9296f371a057ab90f64a6edc39f482a26f21d)
+
+Sandro Santilli <strk at kbt.io>	2016-06-22
+
+    Fix comment for precision loss
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-22
+
+    [processing] replace original layer name with exported in the final OGR command (fix #15099)
+
+    (cherry picked from commit c81b14d59ec578bf678cab2c71b821de574ac0c3)
+
+volaya <volayaf at gmail.com>	2016-05-28
+
+    [processing] added ‘supported’ parameter to exportVectorLayer
+
+    (cherry picked from commit 9c2721b08b02641ab4c61f97f710aa6347b94c15)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-22
+
+    [processing] fix gdal_contour algorithm
+
+    (cherry picked from commit e4c1d896e97952743ea1c0c2144e33983fa5706a)
+
+    Conflicts:
+python/plugins/processing/algs/gdal/GdalUtils.py
+
+Sandro Santilli <strk at kbt.io>	2016-06-22
+
+    Port new MapToolIdentify tests from master
+
+    Includes the test for identifying invalid polygons showing
+    (still passing as of 2.14) - see #13635
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-21
+
+    [GDALTools] pass output format to gdal_contour (fix #6695)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-21
+
+    [processing] speedup Hub distance algorithm (fix #15012)
+
+    (cherry picked from commit e0c9733f6482f184aeeff1339fafef210d1a0709)
+
+    Conflicts:
+python/plugins/processing/algs/qgis/HubDistance.py
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-21
+
+    [processing] use bulk features loading to speedup spatial index creation
+
+    (cherry picked from commit 2d9b2a354b01e29b8fe39aea02829d892880b438)
+
+Matteo <matteo.ghetta at gmail.com>	2016-06-20
+
+    Small fix in write.csv option (#3225)
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-20
+
+    [OGR provider] Make changeGeometryValues() accept null geometry
+
+    Fixes #15081
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-20
+
+    QgsCoordinateReferenceSystem::setProj4String(): harden validation
+
+    OSRImportFromProj4() may accept strings that are not valid proj.4 strings,
+    e.g if they lack a +ellps parameter, it will automatically add +ellps=WGS84, but as
+    we use the original mProj4 with QgsCoordinateTransform, it will fail to initialize
+    so better detect it now.
+
+    (cherry-picked and adapted from master 85128c54191cfedeaee04ca9c4ac0341ab8f5088)
+
+    Fixes #14844
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-18
+
+    [DXF export] Replace newline character in layer name by underscore
+
+    Fixes #15067
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-18
+
+    [OGR provider] Do not return wkbUnknown25D, wkbUnknownM/Z/ZM layer geometry types
+
+    Those are illegal QgsWKBTypes::Type / QGis::WkbType values, and can cause
+    undefined behaviour outside of the provider.
+
+    Fixes #15064
+
+Sandro Santilli <strk at kbt.io>	2016-06-09
+
+    Fix crash in composer on ungrouping after group move.
+
+    Closes #11371.
+
+    Adds support for undo/redo grouping/ungrouping operations.
+
+    Enable pending test for the crash (now passing) and add many more
+    undo/redo related ones (including signals testing).
+
+    Includes a new QgsGroupUngroupItemsCommand class
+    and its SIP bindings.
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-06-17
+
+    Fix Polygon Centroids tool hangs on null geometry (fix #15045)
+
+Larry Shaffer <lshaffer at boundlessgeo.com>	2016-06-15
+
+    Fix file write error when offline.sqlite is saved to root dir by default
+
+    - Default to user's home directory instead
+
+    (cherry-picked from 23a3273)
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-14
+
+    don't save extents to style (fixes #15026, followup 92aed6e and d690d72)
+
+Merge: 671850f 4fb3a24
+Nyall Dawson <nyall.dawson at gmail.com>	2016-06-14
+
+    Merge pull request #3197 from DelazJ/patch-9
+
+    fix button label
+
+Harrissou Sant-anna <delazj at gmail.com>	2016-06-13
+
+    fix button label
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-13
+
+    Fix sorting in attribute table
+
+    Regression introduced with 3ec3daeb14a047c3f4efdd8646a51b33661c28ce (2.14.3) where
+    QgsAttributeTableModel::data(index, SortRole) returned an empty variant.
+
+    Fixes #14927
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-13
+
+    [Spatialite provider] prefer rowid as primary key where available
+
+    Adapted from 1050174532627ae44c4467e9911c1fca41e138e3 without the cleanups.
+
+    fixes #14575, fixes #14626, fixes #14999
+
+Even Rouault <even.rouault at spatialys.com>	2016-06-13
+
+    [WMS provider] Avoid excessive number of decimals in BBOX parameter
+
+    Fix #14928
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-06-13
+
+    [processing] allow 2.5D geometries (fix #14929)
+
+    (cherry picked from commit 0553f7b33b4a3294f9a1cfb24e8c238f9211503d)
+
+    Conflicts:
+python/plugins/processing/algs/qgis/Clip.py
+python/plugins/processing/algs/qgis/Difference.py
+python/plugins/processing/algs/qgis/Intersection.py
+python/plugins/processing/algs/qgis/SymmetricalDifference.py
+python/plugins/processing/algs/qgis/Union.py
+
+rldhont <rldhont at gmail.com>	2016-06-10
+
+    [BUGFIX] Emit layerWillBeRemoved like layersWillBeRemoved (#3194)
+
+    The signal layerWillBeremoved is only emitted when the layer is owned by QgsMapLayerRegistry.
+
+    To fix it just move the emitted layerWilBeRemoved out of the scope of layers owned by QgsMapLayerRegistry.
+
+rldhont <rldhont at gmail.com>	2016-06-09
+
+    QgsEditorWidgetRegistry disconnect signals when mapLayer will be removed (#3186)
+
+    Add a method to disconnect signals added when layerIsAdded to QgsMapLayerRegistry. This method is connect to layerWillBeRemoved.
+
+    This method will probably fix an issue in QGIS Server about performance deterioration.
+    Each time a layer is added to QgsMapLayerRegistry by QGIS Server, QgsEditorWidgetRegistry connects to appropriate signals from map layers to load and save style but NEVER disconnects its.
+
+Jonas <josl at dhi-gras.com>	2016-06-08
+
+    fix to grassWinShell
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-31
+
+    really fix ab5f06b (ouch again - machine mixup)
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-31
+
+    fix ab5f06b (ouch)
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-31
+
+    processing: base grass path on OSGEO4W_ROOT where available
+
+Larry Shaffer <lshaffer at boundlessgeo.com>	2016-06-08
+
+    Fix indentation test errors
+
+Ondřej Fibich <ondrej.fibich at gmail.com>	2016-03-04
+
+    Adds support for GNSS GNRMC messages
+
+    (cherry-picked from 88bddb8)
+
+Merge: 446daaa 50181ee
+rldhont <rldhont at gmail.com>	2016-06-07
+
+    Merge pull request #3181 from dmarteau/release-2_14
+
+    Clean up QgsExpressionContext in QgsServer::handleRequest
+
+David Marteau <david at innophi.com>	2016-06-06
+
+    Clean up QgsExpressionContext in QgsServer::handleRequest
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-07
+
+    fix windows build (followup 4648143c)
+
+Matthias Kuhn <matthias at opengis.ch>	2016-06-06
+
+    [gps] Fix default-misconfigured gpsbabel path
+
+    Fix #14866
+
+rldhont <rldhont at gmail.com>	2016-06-06
+
+    [BUGFIX][Processing] RScript: Insert None value as NULL
+
+rldhont <rldhont at gmail.com>	2016-06-06
+
+    [BUGFIX][Processing] RScript: Add name token
+
+Matthias Kuhn <matthias at opengis.ch>	2016-06-06
+
+    Fix crash when using 2.5D renderer with incompatible layer
+
+    Fixes #14814
+
+Nathan Woodrow <nathan_woodrow at technologyonecorp.com>	2016-05-24
+
+    More ninja changes
+
+Matthias Kuhn <matthias at opengis.ch>	2016-05-23
+
+    [build] Allow using ninja generator on non-win os'es
+
+Nathan Woodrow <nathan_woodrow at technologyonecorp.com>	2016-05-23
+
+    [build] Add better support for Ninja build system
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-01
+
+    fix recommends (followup ddcc2fb)
+
+Juergen E. Fischer <jef at norbit.de>	2016-05-31
+
+    debian packaging: disable globe plugin where osgearth >= 2.7
+
+Juergen E. Fischer <jef at norbit.de>	2016-06-01
+
+    osgeo4w: disable globe plugin (incompatible with OSGEarth 2.7)
+
+Juergen E. Fischer <jef at norbit.de>	2016-05-17
+
+    creatensis.pl: retrieve version earlier from CMakeLists.txt for
+    preremove
+
+Salvatore Larosa <lrssvtml at gmail.com>	2016-05-31
+
+    [processing] add again the algorithm name after being removed mistakenly in 507aeb0
+
+    (cherry-picked from 83502c5)
+
+rldhont <rldhont at gmail.com>	2016-05-31
+
+    [Processing] Add optional capabilities to R scripts
+
+rldhont <rldhont at gmail.com>	2016-05-31
+
+    [BUGFIX][Processing] R: Extent from raster package is "xmin, xmax, ymin, ymax"
+
+    Extent from raster package is like in Processing
+    http://www.inside-r.org/packages/cran/raster/docs/Extent
+
+rldhont <rldhont at gmail.com>	2016-05-31
+
+    [Processing] Fix getParameterDescriptions
+
+    Add import json for script and r
+    return descs and not None
+
+Merge: 7d7467f 8fafd3d
+Even Rouault <even.rouault at mines-paris.org>	2016-05-30
+
+    Merge pull request #3139 from rouault/ogr_concurrent_opening_branch_2_14
+
+    [Backport] [BUGFIX / FEATURE] [OGR] Allow concurrent edition of Shapefiles and Tabfiles in QGIS & MapInfo
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-29
+
+    Doc: mark QgsDataProvider::enterUpdateMode() / leaveUpdateMode() as available in QGIS 2.14.4
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-28
+
+    /test_provider_shapefile.py: do not test QgsVectorDataProvider.SimplifyGeometries since GDAL >= 1.11 is not available in Travis in this branch
+
+Even Rouault <even.rouault at spatialys.com>	2016-04-25
+
+    [BUGFIX] [OGR provider] Free OGR feature in changeAttributeValues() to avoid memory leak
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-14
+
+    OGR provider: fix Coverity warning about mFetchGeometry member being not always initialized
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-04
+
+    Use consistently dataSourceUri() with QgsOgrConnPool (follow up of https://github.com/qgis/QGIS/pull/3057)
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-04
+
+    QgsOgrProvider::addAttributes(): call invalidateConnections() for MapInfo
+
+Even Rouault <even.rouault at spatialys.com>	2016-05-04
+
+    Move QgsOgrConnPool::instance()->unref() from QgsOgrProvider::close() to destructor, since we can open()/close() several times
+
+Even Rouault <even.rouault at spatialys.com>	2016-04-26
+
+    Fix style in previous commit regarding comparisons against nullptr
+
+    Cherry-picked from ed08ffb2aa945dcb0c103aa3fbe3120c0cd0337b
+
+Even Rouault <even.rouault at spatialys.com>	2016-04-11
+
+    [BUGFIX / FEATURE] [OGR] Allow concurrent edition of Shapefiles and Tabfiles in QGIS & MapInfo
+
+    - Closes https://hub.qgis.org/issues/14378
+    - Adds new virtual methods in QgsDataProvider(): enterUpdateMode() and leaveUpdateMode()
+      and implement them in the OGR provider. Limited to shapefiles and tabfiles
+    - Implements QgsOGRProvider:reloadData()
+    - Robustify OGR provider methods so they don't crash if dataset re-opening fails.
+
+    Cherry picked from dc18b5b36bfc10605d4c2905329e0ccd937f0828
+
+rldhont <rldhont at gmail.com>	2016-05-28
+
+    [Processing] Add shortHelp for Scripts, Models and R
+
+    And enhance getParameterDescriptions
+
+rldhont <rldhont at gmail.com>	2016-05-27
+
+    ending store and restore layer extents in projects
+
+Juergen E. Fischer <jef at norbit.de>	2016-03-30
+
+    store and restore layer extents in projects
+
+rldhont <rldhont at gmail.com>	2016-05-26
+
+    [BUGFIX][Processing] Add getParameterDescriptions to R, Model and Script algo
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-05-26
+
+    [processing] fix typo in Add autoincremental layer alg (fix #14892)
+
+rldhont <rldhont at gmail.com>	2016-05-24
+
+    [BUGFIX][Processing][Rscript] Write output Table
+
+    Add support forwriting  output table to Rscript command.
+
+rldhont <rldhont at gmail.com>	2016-05-24
+
+    [BUGFIX][Processing][Rscript] Use CRS Parameter
+
+    Add support for CRS parameter to Rscript command.
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Fix distorted date time calendar popup
+
+    (cherry-picked from 4ce16e1e31b7b7466a25e143ff2a9ebbdc7b0b1a)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Fix invalid background/text colors showing in attribute table
+
+    (cherry-picked from 38e05026fb7a517cfef7b2b69a5a6e3cd7f0c355)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Fix logic in detecting whether attribute form widgets have changed
+
+    Since two null QVariants can be reported as not equal if they have
+    different underlying types we need to ensure that we don't flag
+    this situation as a changed value.
+
+    (cherry-picked from 2fddc0079f153c6dd7cb312c8de96548d9812094)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Correctly return null values from QgsColorWidgetWrapper
+
+    (cherry-picked from 94d88e65d847823dc8f94412ea25a5b91d472aa8)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Always mark features as valid when added to memory provider
+
+    For other providers features will automatically be made valid
+    through the proces of adding to the provider's storage itself
+    and then later retrieving via iterators. But the memory
+    provider uses a direct copy of the feature, so if we don't
+    explicitly mark features as valid the provider may be
+    returning features incorrectly marked as invalid.
+
+    (cherry-picked from f2b70cf5e27c7de920ea3b54893019fb3f450cf6)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-25
+
+    Allow opacity change for 25d renderer colors (fix #14877)
+
+    (cherry-picked from 7af95b10a0e3e4946940dfb0cb85486ff992e608)
+
+rldhont <rldhont at gmail.com>	2016-05-24
+
+    [BUGFIX][Processing][Rscript] Use Extent Parameter
+
+    Add support for extent parameter to Rscript command.
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-24
+
+    Followup 73733a, fix crash when running invalid python strings
+
+    (cherry-picked from 7a8c3e0d2a2aa70c9c50e15d050a28da77763422)
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-24
+
+    Revert "Other check for null pointer before calling decref"
+
+    This reverts commit 9b2be7a2e16757620c08e93bc2ac2ccc54289de0.
+
+Nyall Dawson <nyall.dawson at gmail.com>	2016-05-24
+
+    Revert "Avoid a segfault when python code fails"
+
+    This reverts commit a1e826e4de08df240f81d2da4343e91ed3de66de.
+
+Alessandro Pasotti <apasotti at boundlessgeo.com>	2016-05-23
+
+    Other check for null pointer before calling decref
+
+Alessandro Pasotti <apasotti at boundlessgeo.com>	2016-05-23
+
+    Avoid a segfault when python code fails
+
+    PyRun_StringFlags: Returns the result of executing
+    the code as a Python object, or NULL if an exception was raised.
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-05-23
+
+    [processing] correctly set default value in modeler algorithms (fix #12767)
+
+    (cherry picked from commit d2b21891d44a1beb5be2112a53f354c7abec9221)
+
+Alexander Bruy <alexander.bruy at gmail.com>	2016-05-23
+
+    [processing] restore CreateConstantRaster algorithm (fix #14860)
+
+    (cherry picked from commit 6207412bf731d718a16246b37de7dfcc6eec9d6f)
+
+Merge: cf2ebb8 272b16a
+Matthias Kuhn <matthias at opengis.ch>	2016-05-21
+
+    Merge pull request #3054 from dgoedkoop/loadstylelabels214
+
+    [Bugfix] Update labeling settings after loading style from file (fixes #14224)
+
+Juergen E. Fischer <jef at norbit.de>	2016-05-20
+
+    Release of 2.14.3
+
 Nyall Dawson <nyall.dawson at gmail.com>	2016-05-20
 
     Precise that scale function returns the denominator and not the scale itself
@@ -139,6 +1325,10 @@ Juergen E. Fischer <jef at norbit.de>	2016-05-04
 
     (cherry picked from commit 919c54ef5fabe7b7c2eef9c91094642c47b2eb7c)
 
+Daan Goedkoop <dgoedkoop at gmx.net>	2016-05-02
+
+    Update labeling settings in UI after loading style from file (fixes #14224)
+
 Matthias Kuhn <matthias at opengis.ch>	2016-05-02
 
     Fixup for AppStartup test which requires SIP API V1
diff --git a/cmake/PyQtMacros.cmake b/cmake/PyQtMacros.cmake
index 7a43f47..63c05f0 100644
--- a/cmake/PyQtMacros.cmake
+++ b/cmake/PyQtMacros.cmake
@@ -33,7 +33,11 @@ ENDIF(NOT PYUIC_PROGRAM)
 MACRO(PYQT_WRAP_UI outfiles )
   IF(WIN32)
     SET(PYUIC_WRAPPER "${CMAKE_SOURCE_DIR}/scripts/${PYUIC_PROG_NAME}-wrapper.bat")
-    SET(PYUIC_WRAPPER_PATH "${QGIS_OUTPUT_DIRECTORY}/bin/${CMAKE_BUILD_TYPE}")
+    IF(USING_NINJA OR USING_NMAKE)
+      SET(PYUIC_WRAPPER_PATH "${QGIS_OUTPUT_DIRECTORY}/bin")
+    ELSE(USING_NINJA OR USING_NMAKE)
+      SET(PYUIC_WRAPPER_PATH "${QGIS_OUTPUT_DIRECTORY}/bin/${CMAKE_BUILD_TYPE}")
+    ENDIF(USING_NINJA OR USING_NMAKE)
   ELSE(WIN32)
     # TODO osx
     SET(PYUIC_WRAPPER "${CMAKE_SOURCE_DIR}/scripts/pyuic4-wrapper.sh")
diff --git a/cmake_templates/qgsconfig.h.in b/cmake_templates/qgsconfig.h.in
index 0766b13..3a4abe9 100644
--- a/cmake_templates/qgsconfig.h.in
+++ b/cmake_templates/qgsconfig.h.in
@@ -40,6 +40,8 @@
 
 #cmakedefine USING_NMAKE
 
+#cmakedefine USING_NINJA
+
 #cmakedefine HAVE_POSTGRESQL
 
 #cmakedefine HAVE_SPATIALITE
diff --git a/debian/changelog b/debian/changelog
index 8d1b55c..da8fb0a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,14 @@
-qgis (2.14.3) UNRELEASED; urgency=medium
+qgis (2.14.4) UNRELEASED; urgency=medium
+
+  * Release of 2.14.4
+
+ -- Jürgen E. Fischer <jef at norbit.de>  Fri, 08 Jul 2016 14:00:33 +0200
+
+qgis (2.14.3) unstable; urgency=medium
 
   * Release of 2.14.3
 
- -- Jürgen E. Fischer <jef at norbit.de>  Fri, 20 May 2016 14:05:10 +0200
+ -- Jürgen E. Fischer <jef at norbit.de>  Fri, 08 Jul 2016 14:00:32 +0200
 
 qgis (2.14.2) unstable; urgency=medium
 
diff --git a/debian/control b/debian/control
index ec88fe3..d9dd69d 100644
--- a/debian/control
+++ b/debian/control
@@ -31,6 +31,7 @@ Build-Depends:
  python-dev,
  python-qt4-dev (>= 4.1.0),
  python-sip-dev (>= 4.5.0) | python-sip4-dev (>= 4.5.0) | sip4 (>= 4.5),
+ libosgearth-dev,
  git,
  txt2tags,
  doxygen
diff --git a/debian/control.in b/debian/control.in
index 9964b4a..a5ca8b4 100644
--- a/debian/control.in
+++ b/debian/control.in
@@ -82,9 +82,9 @@ Depends:
  qgis-providers (= ${binary:Version}),
  qgis-common (= ${source:Version})
 Recommends:
+#globe# qgis-plugin-globe,
  qgis-plugin-grass,
- qgis-provider-grass,
- qgis-plugin-globe
+ qgis-provider-grass
 Suggests: gpsbabel
 Conflicts: uim-qt3
 Description: Geographic Information System (GIS)
@@ -339,32 +339,32 @@ Description: GRASS plugin for QGIS - architecture-independent data
  This package contains architecture-independent supporting data files for use
  with the QGIS GRASS plugin.
 
-Package: qgis-plugin-globe
-Architecture: any
-Depends:
- qgis (= ${binary:Version}),
- qgis-plugin-globe-common (= ${source:Version}),
- openscenegraph-plugin-osgearth,
- ${shlibs:Depends},
- ${misc:Depends}
-Description: OSG globe plugin for QGIS
- QGIS is a Geographic Information System (GIS) which manages, analyzes and
- display databases of geographic information.
- .
- This plugin enables 3D viewing using OSG globe in the QGIS.
-
-Package: qgis-plugin-globe-common
-Architecture: all
-Depends:
- osgearth-data,
- ${misc:Depends}
-Description: OSG globe plugin for QGIS - architecture-independent data
- QGIS is a Geographic Information System (GIS) which manages, analyzes and
- display databases of geographic information.
- .
- This package contains architecture-independent supporting data files for use
- with the QGIS GLOBE plugin.
-
+#globe#Package: qgis-plugin-globe
+#globe#Architecture: any
+#globe#Depends:
+#globe# qgis (= ${binary:Version}),
+#globe# qgis-plugin-globe-common (= ${source:Version}),
+#globe# openscenegraph-plugin-osgearth,
+#globe# ${shlibs:Depends},
+#globe# ${misc:Depends}
+#globe#Description: OSG globe plugin for QGIS
+#globe# QGIS is a Geographic Information System (GIS) which manages, analyzes and
+#globe# display databases of geographic information.
+#globe# .
+#globe# This plugin enables 3D viewing using OSG globe in the QGIS.
+#globe#
+#globe#Package: qgis-plugin-globe-common
+#globe#Architecture: all
+#globe#Depends:
+#globe# osgearth-data,
+#globe# ${misc:Depends}
+#globe#Description: OSG globe plugin for QGIS - architecture-independent data
+#globe# QGIS is a Geographic Information System (GIS) which manages, analyzes and
+#globe# display databases of geographic information.
+#globe# .
+#globe# This package contains architecture-independent supporting data files for use
+#globe# with the QGIS GLOBE plugin.
+#globe#
 Package: python-qgis
 Architecture: any
 Section: python
diff --git a/debian/rules b/debian/rules
index f44c81f..90e72c7 100755
--- a/debian/rules
+++ b/debian/rules
@@ -50,6 +50,8 @@ QGIS_ABI=$(QGIS_MAJOR).$(QGIS_MINOR).$(QGIS_PATCH)
 GRASS=grass$(subst .,,$(shell pkg-config --modversion grass|cut -d. -f1,2))
 GRASSVER=$(subst .,,$(shell pkg-config --modversion grass|cut -d. -f1))
 
+WITH_GLOBE=$(shell dpkg --compare-versions "$$(dpkg-query -W --showformat='$${Version}' libosgearth-dev)" lt 2.7 && echo 1)
+
 CMAKE_OPTS := \
 	-DBUILDNAME=$(DEB_BUILD_NAME) \
 	-DCMAKE_VERBOSE_MAKEFILE=1 \
@@ -63,7 +65,6 @@ CMAKE_OPTS := \
 	-DQGIS_CGIBIN_SUBDIR=/usr/lib/cgi-bin \
 	-DWITH_APIDOC=TRUE \
 	-DWITH_CUSTOM_WIDGETS=TRUE \
-	-DWITH_GLOBE=TRUE \
 	-DWITH_INTERNAL_HTTPLIB2=FALSE \
 	-DWITH_INTERNAL_JINJA2=FALSE \
 	-DWITH_INTERNAL_MARKUPSAFE=FALSE \
@@ -97,6 +98,10 @@ else
 	CMAKE_OPTS += -DWITH_INTERNAL_NOSE2=FALSE -DWITH_INTERNAL_SIX=FALSE
 endif
 
+ifneq (,$(WITH_GLOBE))
+	CMAKE_OPTS += -DWITH_GLOBE=TRUE
+endif
+
 ifneq (,$(findstring $(DISTRIBUTION),"wheezy precise"))
 	CMAKE_OPTS += -DWITH_PYSPATIALITE=TRUE
 endif
@@ -181,6 +186,10 @@ endif
 
 CONTROL_EXPRESSIONS = $(DISTRIBUTION) grass$(GRASSVER)
 
+ifneq (,$(WITH_GLOBE))
+	CONTROL_EXPRESSIONS += globe
+endif
+
 ifneq (,$(WITH_ORACLE))
 	CONTROL_EXPRESSIONS += oracle
 endif
diff --git a/ms-windows/osgeo4w/creatensis.pl b/ms-windows/osgeo4w/creatensis.pl
index 01bd33b..0b0663a 100755
--- a/ms-windows/osgeo4w/creatensis.pl
+++ b/ms-windows/osgeo4w/creatensis.pl
@@ -265,6 +265,24 @@ unless(-d $unpacked ) {
 	chdir "..";
 }
 
+my($major, $minor, $patch);
+
+open F, "../../CMakeLists.txt";
+while(<F>) {
+	if(/SET\(CPACK_PACKAGE_VERSION_MAJOR "(\d+)"\)/) {
+		$major = $1;
+	} elsif(/SET\(CPACK_PACKAGE_VERSION_MINOR "(\d+)"\)/) {
+		$minor = $1;
+	} elsif(/SET\(CPACK_PACKAGE_VERSION_PATCH "(\d+)"\)/) {
+		$patch = $1;
+	} elsif(/SET\(RELEASE_NAME "(.+)"\)/) {
+		$releasename = $1 unless defined $releasename;
+	}
+}
+close F;
+
+$version = "$major.$minor.$patch" unless defined $version;
+
 #
 # Create postinstall.bat
 #
@@ -337,24 +355,6 @@ print F "ren preremove.bat preremove.bat.done$r";
 
 close F;
 
-my($major, $minor, $patch);
-
-open F, "../../CMakeLists.txt";
-while(<F>) {
-	if(/SET\(CPACK_PACKAGE_VERSION_MAJOR "(\d+)"\)/) {
-		$major = $1;
-	} elsif(/SET\(CPACK_PACKAGE_VERSION_MINOR "(\d+)"\)/) {
-		$minor = $1;
-	} elsif(/SET\(CPACK_PACKAGE_VERSION_PATCH "(\d+)"\)/) {
-		$patch = $1;
-	} elsif(/SET\(RELEASE_NAME "(.+)"\)/) {
-		$releasename = $1 unless defined $releasename;
-	}
-}
-close F;
-
-$version = "$major.$minor.$patch" unless defined $version;
-
 unless( defined $binary ) {
 	if( -f "binary$archpostfix-$version" ) {
 		open P, "binary$archpostfix-$version";
diff --git a/ms-windows/osgeo4w/package-nightly.cmd b/ms-windows/osgeo4w/package-nightly.cmd
index 18ffe6c..a0e9bfb 100644
--- a/ms-windows/osgeo4w/package-nightly.cmd
+++ b/ms-windows/osgeo4w/package-nightly.cmd
@@ -165,7 +165,7 @@ cmake %CMAKE_OPT% ^
 	-D WITH_GRASS7=TRUE ^
 	-D GRASS_PREFIX=%O4W_ROOT%/apps/grass/grass-%GRASS6_VERSION% ^
 	-D GRASS_PREFIX7=%GRASS70_PATH:\=/% ^
-	-D WITH_GLOBE=TRUE ^
+	-D WITH_GLOBE=FALSE ^
 	-D WITH_TOUCH=TRUE ^
 	-D WITH_ORACLE=TRUE ^
 	-D WITH_CUSTOM_WIDGETS=TRUE ^
diff --git a/ms-windows/osgeo4w/package.cmd b/ms-windows/osgeo4w/package.cmd
index b508376..694de15 100644
--- a/ms-windows/osgeo4w/package.cmd
+++ b/ms-windows/osgeo4w/package.cmd
@@ -161,7 +161,7 @@ cmake %CMAKE_OPT% ^
 	-D WITH_GRASS7=TRUE ^
 	-D GRASS_PREFIX=%O4W_ROOT%/apps/grass/grass-%GRASS6_VERSION% ^
 	-D GRASS_PREFIX7=%GRASS70_PATH:\=/% ^
-	-D WITH_GLOBE=TRUE ^
+	-D WITH_GLOBE=FALSE ^
 	-D WITH_TOUCH=TRUE ^
 	-D WITH_ORACLE=TRUE ^
 	-D WITH_CUSTOM_WIDGETS=TRUE ^
@@ -267,7 +267,8 @@ if not exist %OSGEO4W_ROOT%\httpd.d mkdir %OSGEO4W_ROOT%\httpd.d
 sed -e 's/@package@/%PACKAGENAME%/g' -e 's/@version@/%VERSION%/g' httpd.conf.tmpl >%OSGEO4W_ROOT%\httpd.d\httpd_%PACKAGENAME%.conf.tmpl
 if errorlevel 1 (echo creation of httpd.conf template failed & goto error)
 
-set packages="" "-common" "-server" "-devel" "-globe-plugin" "-oracle-provider" "-grass-plugin-common"
+set packages="" "-common" "-server" "-devel" "-oracle-provider" "-grass-plugin-common"
+REM set packages=%packages% "-globe-plugin"
 
 for %%g IN (%GRASS_VERSIONS%) do (
 	for /F "delims=." %%i in ("%%g") do set v=%%i
@@ -445,12 +446,12 @@ for %%g IN (%GRASS_VERSIONS%) do (
 	if errorlevel 1 (echo tar grass-plugin!w! failed & goto error)
 )
 
-tar -C %OSGEO4W_ROOT% -cjf %ARCH%/release/qgis/%PACKAGENAME%-globe-plugin/%PACKAGENAME%-globe-plugin-%VERSION%-%PACKAGE%.tar.bz2 ^
-	--exclude-from exclude ^
-	--exclude "*.pyc" ^
-	"apps/%PACKAGENAME%/globe" ^
-	"apps/%PACKAGENAME%/plugins/globeplugin.dll"
-if errorlevel 1 (echo tar globe-plugin failed & goto error)
+REM tar -C %OSGEO4W_ROOT% -cjf %ARCH%/release/qgis/%PACKAGENAME%-globe-plugin/%PACKAGENAME%-globe-plugin-%VERSION%-%PACKAGE%.tar.bz2 ^
+REM 	--exclude-from exclude ^
+REM 	--exclude "*.pyc" ^
+REM 	"apps/%PACKAGENAME%/globe" ^
+REM 	"apps/%PACKAGENAME%/plugins/globeplugin.dll"
+REM if errorlevel 1 (echo tar globe-plugin failed & goto error)
 
 tar -C %OSGEO4W_ROOT% -cjf %ARCH%/release/qgis/%PACKAGENAME%-oracle-provider/%PACKAGENAME%-oracle-provider-%VERSION%-%PACKAGE%.tar.bz2 ^
 	"apps/%PACKAGENAME%/plugins/oracleprovider.dll" ^
@@ -474,7 +475,7 @@ exit
 
 :error
 echo BUILD ERROR %ERRORLEVEL%: %DATE% %TIME%
-for %%i in ("" "-common" "-server" "-devel" "-grass-plugin" "-globe-plugin" "-oracle-provider") do (
+for %%i in (%packages%) do (
 	if exist %ARCH%\release\qgis\%PACKAGENAME%%%i\%PACKAGENAME%%%i-%VERSION%-%PACKAGE%.tar.bz2 del %ARCH%\release\qgis\%PACKAGENAME%%%i\%PACKAGENAME%%%i-%VERSION%-%PACKAGE%.tar.bz2
 )
 
diff --git a/python/core/__init__.py b/python/core/__init__.py
index e8fe1b7..a150bb4 100644
--- a/python/core/__init__.py
+++ b/python/core/__init__.py
@@ -35,7 +35,7 @@ from qgis._core import *
 from PyQt4.QtCore import QCoreApplication
 
 
-def register_function(function, arg_count, group, usesgeometry=False, **kwargs):
+def register_function(function, arg_count, group, usesgeometry=False, referenced_columns=[QgsFeatureRequest.AllAttributes], **kwargs):
     """
     Register a Python function to be used as a expression function.
 
@@ -64,8 +64,8 @@ def register_function(function, arg_count, group, usesgeometry=False, **kwargs):
     """
     class QgsExpressionFunction(QgsExpression.Function):
 
-        def __init__(self, func, name, args, group, helptext='', usesgeometry=True, expandargs=False):
-            QgsExpression.Function.__init__(self, name, args, group, helptext, usesgeometry)
+        def __init__(self, func, name, args, group, helptext='', usesgeometry=True, referencedColumns=QgsFeatureRequest.AllAttributes, expandargs=False):
+            QgsExpression.Function.__init__(self, name, args, group, helptext, usesgeometry, referencedColumns)
             self.function = func
             self.expandargs = expandargs
 
@@ -105,7 +105,7 @@ def register_function(function, arg_count, group, usesgeometry=False, **kwargs):
 
     function.__name__ = name
     helptext = helptemplate.safe_substitute(name=name, doc=helptext)
-    f = QgsExpressionFunction(function, name, arg_count, group, helptext, usesgeometry, expandargs)
+    f = QgsExpressionFunction(function, name, arg_count, group, helptext, usesgeometry, referenced_columns, expandargs)
 
     # This doesn't really make any sense here but does when used from a decorator context
     # so it can stay.
diff --git a/python/core/composer/qgscomposition.sip b/python/core/composer/qgscomposition.sip
index 4a206cf..d3b0771 100644
--- a/python/core/composer/qgscomposition.sip
+++ b/python/core/composer/qgscomposition.sip
@@ -830,6 +830,8 @@ class QgsComposition : QGraphicsScene
     void composerArrowAdded( QgsComposerArrow* arrow );
     /** Is emitted when a new composer html has been added to the view*/
     void composerHtmlFrameAdded( QgsComposerHtml* html, QgsComposerFrame* frame );
+    /** Is emitted when a new item group has been added to the view*/
+    void composerItemGroupAdded( QgsComposerItemGroup* group );
     /** Is emitted when new composer label has been added to the view*/
     void composerLabelAdded( QgsComposerLabel* label );
     /** Is emitted when new composer map has been added to the view*/
diff --git a/python/core/composer/qgsgroupungroupitemscommand.sip b/python/core/composer/qgsgroupungroupitemscommand.sip
new file mode 100644
index 0000000..756635c
--- /dev/null
+++ b/python/core/composer/qgsgroupungroupitemscommand.sip
@@ -0,0 +1,43 @@
+/** A composer command class for grouping / ungrouping composer items.
+ *
+ * If mState == Ungrouped, the command owns the group item
+ */
+class QgsGroupUngroupItemsCommand: QObject, QUndoCommand
+{
+%TypeHeaderCode
+#include "qgsgroupungroupitemscommand.h"
+%End
+
+  public:
+
+    /** Command kind, and state */
+    enum State
+    {
+      Grouped = 0,
+      Ungrouped
+    };
+
+    /** Create a group or ungroup command
+     *
+     * @param s command kind (@see State)
+     * @param item the group item being created or ungrouped
+     * @param c the composition including this group
+     * @param text command label
+     * @param parent parent command, if any
+     *
+     */
+    QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent = nullptr );
+    ~QgsGroupUngroupItemsCommand();
+
+    void redo();
+    void undo();
+
+  signals:
+    /** Signals addition of an item (the group) */
+    void itemAdded( QgsComposerItem* item );
+    /** Signals removal of an item (the group) */
+    void itemRemoved( QgsComposerItem* item );
+
+};
+
+
diff --git a/python/core/core.sip b/python/core/core.sip
index 657f745..f54fddd 100644
--- a/python/core/core.sip
+++ b/python/core/core.sip
@@ -158,6 +158,7 @@
 %Include auth/qgsauthmethod.sip
 
 %Include composer/qgsaddremoveitemcommand.sip
+%Include composer/qgsgroupungroupitemscommand.sip
 %Include composer/qgsaddremovemultiframecommand.sip
 %Include composer/qgsatlascomposition.sip
 %Include composer/qgscomposerarrow.sip
diff --git a/python/core/dxf/qgsdxfexport.sip b/python/core/dxf/qgsdxfexport.sip
index 7436fa5..00b1743 100644
--- a/python/core/dxf/qgsdxfexport.sip
+++ b/python/core/dxf/qgsdxfexport.sip
@@ -117,7 +117,7 @@ class QgsDxfExport
      * Get DXF palette index of nearest entry for given color
      * @param color
      */
-    static int closestColorMatch( QRgb pixel );
+    static int closestColorMatch( QRgb color );
 
     /**
      * Get layer name for feature
@@ -215,7 +215,7 @@ class QgsDxfExport
      * @param line polyline
      * @param layer layer name to use
      * @param lineStyleName line type to use
-     * @param color coolor to use
+     * @param color color to use
      * @param width line width to use
      */
     void writePolyline( const QgsPolyline &line, const QString &layer, const QString &lineStyleName, const QColor& color, double width = -1 );
@@ -225,7 +225,7 @@ class QgsDxfExport
      * @param polygon polygon
      * @param layer layer name to use
      * @param hatchPattern hatchPattern to use
-     * @param color coolor to use
+     * @param color color to use
      */
     void writePolygon( const QgsPolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor& color );
 
@@ -270,4 +270,20 @@ class QgsDxfExport
     //! return list of available DXF encodings
     static QStringList encodings();
 
+    /** Output the label
+     * @param layerId id of the layer
+     * @param context render context
+     * @param label position of label
+     * @param settings label settings
+     * @note not available in Python bindings
+     */
+    // void drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings );
+
+    /** Register name of layer for feature
+     * @param layerId id of layer
+     * @param fid id of feature
+     * @param layer dxf layer of feature
+     */
+    void registerDxfLayer( QString layerId, QgsFeatureId fid, QString layer );
+
 };
diff --git a/python/core/geometry/qgscurvepolygonv2.sip b/python/core/geometry/qgscurvepolygonv2.sip
index 861b84a..4843e16 100644
--- a/python/core/geometry/qgscurvepolygonv2.sip
+++ b/python/core/geometry/qgscurvepolygonv2.sip
@@ -80,7 +80,7 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
     virtual int vertexCount( int /*part*/ = 0, int ring = 0 ) const;
     virtual int ringCount( int /*part*/ = 0 ) const;
     virtual int partCount() const;
-    virtual QgsPointV2 vertexAt( QgsVertexId id ) const;
+    virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const;
 
     virtual bool addZValue( double zValue = 0 );
     virtual bool addMValue( double mValue = 0 );
diff --git a/python/core/qgsdataprovider.sip b/python/core/qgsdataprovider.sip
index 7ea28ec..aab6e1a 100644
--- a/python/core/qgsdataprovider.sip
+++ b/python/core/qgsdataprovider.sip
@@ -223,6 +223,47 @@ class QgsDataProvider : QObject
      */
     virtual void invalidateConnections( const QString& connection );
 
+    /** Enter update mode.
+     *
+     * This is aimed at providers that can open differently the connection to
+     * the datasource, according it to be in update mode or in read-only mode.
+     * A call to this method shall be balanced with a call to leaveUpdateMode(),
+     * if this method returns true.
+     *
+     * Most providers will have an empty implementation for that method.
+     *
+     * For backward compatibility, providers that implement enterUpdateMode() should
+     * still make sure to allow editing operations to work even if enterUpdateMode()
+     * is not explicitly called.
+     *
+     * Several successive calls to enterUpdateMode() can be done. So there is
+     * a concept of stack of calls that must be handled by the provider. Only the first
+     * call to enterUpdateMode() will really turn update mode on.
+     *
+     * @return true in case of success (or no-op implementation), false in case of failure
+     *
+     * @note added in QGIS 2.14.4
+     */
+    virtual bool enterUpdateMode();
+
+    /** Leave update mode.
+     *
+     * This is aimed at providers that can open differently the connection to
+     * the datasource, according it to be in update mode or in read-only mode.
+     * This method shall be balanced with a succesful call to enterUpdateMode().
+     *
+     * Most providers will have an empty implementation for that method.
+     *
+     * Several successive calls to enterUpdateMode() can be done. So there is
+     * a concept of stack of calls that must be handled by the provider. Only the last
+     * call to leaveUpdateMode() will really turn update mode off.
+     *
+     * @return true in case of success (or no-op implementation), false in case of failure
+     *
+     * @note added in QGIS 2.14.4
+     */
+    virtual bool leaveUpdateMode();
+
   signals:
 
     /**
diff --git a/python/core/qgsfeaturerequest.sip b/python/core/qgsfeaturerequest.sip
index ff191cd..40d7f50 100644
--- a/python/core/qgsfeaturerequest.sip
+++ b/python/core/qgsfeaturerequest.sip
@@ -302,7 +302,7 @@ class QgsFeatureRequest
      * Return the subset of attributes which at least need to be fetched
      * @return A list of attributes to be fetched
      */
-    const QgsAttributeList& subsetOfAttributes() const;
+    QgsAttributeList subsetOfAttributes() const;
 
     //! Set a subset of attributes by names that will be fetched
     QgsFeatureRequest& setSubsetOfAttributes( const QStringList& attrNames, const QgsFields& fields );
diff --git a/python/core/qgsmaprenderer.sip b/python/core/qgsmaprenderer.sip
index 0f62aeb..130069d 100644
--- a/python/core/qgsmaprenderer.sip
+++ b/python/core/qgsmaprenderer.sip
@@ -35,7 +35,7 @@ class QgsLabelingEngineInterface
 
     //! called when we're going to start with rendering
     //! @deprecated since 2.4 - use override with QgsMapSettings
-    virtual void init( QgsMapRenderer* mp ) = 0 /Deprecated/;
+    virtual void init( QgsMapRenderer *mp ) = 0 /Deprecated/;
     //! called when we're going to start with rendering
     virtual void init( const QgsMapSettings& mapSettings ) = 0;
     //! called to find out whether the layer is used for labeling
@@ -48,17 +48,17 @@ class QgsLabelingEngineInterface
     virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0;
     //! returns PAL layer settings for a registered layer
     //! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer()
-    virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0 /Deprecated/;
+    virtual QgsPalLayerSettings &layer( const QString &layerName ) = 0 /Deprecated/;
     //! 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, QStringList &attrNames, QgsRenderContext &ctx );
     //! adds a diagram layer to the labeling engine
     //! @deprecated since 2.12 - use prepareDiagramLayer()
-    virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) /Deprecated/;
+    virtual int addDiagramLayer( QgsVectorLayer *layer, const QgsDiagramLayerSettings *s ) /Deprecated/;
     //! called for every feature
-    virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null ) = 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
@@ -268,7 +268,7 @@ class QgsMapRenderer : QObject
     //! Accessor for render context
     QgsRenderContext* rendererContext();
 
-    //! Labeling engine (NULL if there's no custom engine)
+    //! Labeling engine (nullptr if there's no custom engine)
     QgsLabelingEngineInterface* labelingEngine();
 
     //! Set labeling engine. Previous engine (if any) is deleted.
diff --git a/python/core/qgsmapsettings.sip b/python/core/qgsmapsettings.sip
index e139aaa..fa581a4 100644
--- a/python/core/qgsmapsettings.sip
+++ b/python/core/qgsmapsettings.sip
@@ -78,10 +78,10 @@ class QgsMapSettings
     //! Get color that is used for drawing of selected vector features
     QColor selectionColor() const;
 
-    //! Enumeration of flags that adjust the way how map is rendered
+    //! Enumeration of flags that adjust the way the map is rendered
     enum Flag
     {
-      Antialiasing,               //!< Enable anti-aliasin for map rendering
+      Antialiasing,               //!< Enable anti-aliasing for map rendering
       DrawEditingInfo,            //!< Enable drawing of vertex markers for layers in editing mode
       ForceVectorOutput,          //!< Vector graphics should not be cached and drawn as raster images
       UseAdvancedEffects,         //!< Enable layer transparency and blending effects
diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip
index 0877730..0b3f813 100644
--- a/python/core/qgspallabeling.sip
+++ b/python/core/qgspallabeling.sip
@@ -551,7 +551,6 @@ class QgsPalLayerSettings
      * @param f feature to label
      * @param context render context. The QgsExpressionContext contained within the render context
      * must have already had the feature and fields sets prior to calling this method.
-     * @param dxfLayer dxfLayer name
      * @param labelFeature if using QgsLabelingEngineV2, this will receive the label feature. Not available
      * in Python bindings.
      * @param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry
@@ -560,7 +559,7 @@ class QgsPalLayerSettings
      * the feature's original geometry will be used as an obstacle for labels. Not available
      * in Python bindings.
      */
-    void registerFeature( QgsFeature& f, QgsRenderContext& context, const QString& dxfLayer );
+    void registerFeature( QgsFeature& f, QgsRenderContext& context );
 
     void readFromLayer( QgsVectorLayer* layer );
     void writeToLayer( QgsVectorLayer* layer );
@@ -867,9 +866,8 @@ class QgsPalLabeling : QgsLabelingEngineInterface
      * @param feat feature to label
      * @param context render context. The QgsExpressionContext contained within the render context
      * must have already had the feature and fields sets prior to calling this method.
-     * @param dxfLayer dxfLayer name
      */
-    virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null );
+    virtual void registerFeature( 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
diff --git a/python/core/qgsrendercontext.sip b/python/core/qgsrendercontext.sip
index e7f5be4..3764790 100644
--- a/python/core/qgsrendercontext.sip
+++ b/python/core/qgsrendercontext.sip
@@ -21,7 +21,8 @@ class QgsRenderContext
       UseRenderingOptimization, //!< Enable vector simplification and other rendering optimizations
       DrawSelection,            //!< Whether vector selections should be shown in the rendered map
       DrawSymbolBounds,         //!< Draw bounds of symbols (for debugging/testing)
-      RenderMapTile
+      RenderMapTile,            //!< Draw map such that there are no problems between adjacent tiles
+      Antialiasing,             //!< Use antialiasing while drawing
     };
     typedef QFlags<QgsRenderContext::Flag> Flags;
 
diff --git a/python/core/symbology-ng/qgsellipsesymbollayerv2.sip b/python/core/symbology-ng/qgsellipsesymbollayerv2.sip
index c8e6101..a94facd 100644
--- a/python/core/symbology-ng/qgsellipsesymbollayerv2.sip
+++ b/python/core/symbology-ng/qgsellipsesymbollayerv2.sip
@@ -21,7 +21,7 @@ class QgsEllipseSymbolLayerV2 : QgsMarkerSymbolLayerV2
     void toSld( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const;
     void writeSldMarker( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const;
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
+    bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
 
     void setSymbolName( const QString& name );
     QString symbolName() const;
diff --git a/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip
index e29b5b6..0e535c7 100644
--- a/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip
+++ b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip
@@ -6,9 +6,11 @@ class QgsInvertedPolygonRenderer : QgsFeatureRendererV2
   public:
 
     /** Constructor
-     * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned
+     * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned.
+     * Ownership will be transferred.
      */
-    QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* embeddedRenderer /Transfer/ = 0 );
+    QgsInvertedPolygonRenderer( QgsFeatureRendererV2* embeddedRenderer /Transfer/ = 0 );
+
     virtual ~QgsInvertedPolygonRenderer();
 
     /** Used to clone this feature renderer.*/
@@ -80,14 +82,11 @@ class QgsInvertedPolygonRenderer : QgsFeatureRendererV2
      */
     virtual QDomElement save( QDomDocument& doc );
 
-    /** Sets the embedded renderer
-     * @param subRenderer the embedded renderer (will be cloned)
-     */
-    void setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer );
-    /** @returns the current embedded renderer
-     */
+    void setEmbeddedRenderer( QgsFeatureRendererV2* subRenderer /Transfer/ );
     const QgsFeatureRendererV2* embeddedRenderer() const;
 
+    virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol );
+
     /** @returns true if the geometries are to be preprocessed (merged with an union) before rendering.*/
     bool preprocessingEnabled() const;
     /**
diff --git a/python/core/symbology-ng/qgsmarkersymbollayerv2.sip b/python/core/symbology-ng/qgsmarkersymbollayerv2.sip
index 1aa110f..90c039d 100644
--- a/python/core/symbology-ng/qgsmarkersymbollayerv2.sip
+++ b/python/core/symbology-ng/qgsmarkersymbollayerv2.sip
@@ -67,7 +67,7 @@ class QgsSimpleMarkerSymbolLayerV2 : QgsMarkerSymbolLayerV2
     void setOutlineWidthMapUnitScale( const QgsMapUnitScale& scale);
     const QgsMapUnitScale& outlineWidthMapUnitScale() const;
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
+    bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
 
     void setOutputUnit( QgsSymbolV2::OutputUnit unit );
     QgsSymbolV2::OutputUnit outputUnit() const;
@@ -146,7 +146,7 @@ class QgsSvgMarkerSymbolLayerV2 : QgsMarkerSymbolLayerV2
     void setMapUnitScale( const QgsMapUnitScale& scale );
     QgsMapUnitScale mapUnitScale() const;
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
+    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
 
     QRectF bounds( QPointF point, QgsSymbolV2RenderContext& context );
 };
diff --git a/python/core/symbology-ng/qgspointdisplacementrenderer.sip b/python/core/symbology-ng/qgspointdisplacementrenderer.sip
index 177194d..6542a02 100644
--- a/python/core/symbology-ng/qgspointdisplacementrenderer.sip
+++ b/python/core/symbology-ng/qgspointdisplacementrenderer.sip
@@ -68,9 +68,14 @@ class QgsPointDisplacementRenderer : QgsFeatureRendererV2
     void setLabelAttributeName( const QString& name );
     QString labelAttributeName() const;
 
-    /** Sets embedded renderer (takes ownership)*/
     void setEmbeddedRenderer( QgsFeatureRendererV2* r /Transfer/ );
-    QgsFeatureRendererV2* embeddedRenderer();
+    const QgsFeatureRendererV2* embeddedRenderer() const;
+
+    virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol );
+
+    virtual bool legendSymbolItemsCheckable() const;
+    virtual bool legendSymbolItemChecked( const QString& key );
+    virtual void checkLegendSymbolItem( const QString& key, bool state = true );
 
     //! not available in python bindings
     //! @deprecated since 2.4
diff --git a/python/core/symbology-ng/qgsrendererv2.sip b/python/core/symbology-ng/qgsrendererv2.sip
index 58916a9..0812774 100644
--- a/python/core/symbology-ng/qgsrendererv2.sip
+++ b/python/core/symbology-ng/qgsrendererv2.sip
@@ -384,6 +384,21 @@ class QgsFeatureRendererV2
      */
     void setOrderByEnabled( bool enabled );
 
+    /** Sets an embedded renderer (subrenderer) for this feature renderer. The base class implementation
+     * does nothing with subrenderers, but individual derived classes can use these to modify their behaviour.
+     * @param subRenderer the embedded renderer. Ownership will be transferred.
+     * @see embeddedRenderer()
+     * @note added in QGIS 2.16
+     */
+    virtual void setEmbeddedRenderer( QgsFeatureRendererV2* subRenderer /Transfer/ );
+
+    /** Returns the current embedded renderer (subrenderer) for this feature renderer. The base class
+     * implementation does not use subrenderers and will always return null.
+     * @see setEmbeddedRenderer()
+     * @note added in QGIS 2.16
+     */
+    virtual const QgsFeatureRendererV2* embeddedRenderer() const;
+
   protected:
     QgsFeatureRendererV2( const QString& type );
 
diff --git a/python/core/symbology-ng/qgssymbollayerv2.sip b/python/core/symbology-ng/qgssymbollayerv2.sip
index 102c6bd..fe9dd49 100644
--- a/python/core/symbology-ng/qgssymbollayerv2.sip
+++ b/python/core/symbology-ng/qgssymbollayerv2.sip
@@ -252,17 +252,13 @@ class QgsSymbolLayerV2
      */
     virtual QVariant evaluateDataDefinedProperty( const QString& property, const QgsSymbolV2RenderContext& context, const QVariant& defaultVal = QVariant(), bool *ok = 0 ) const;
 
-    virtual bool writeDxf( QgsDxfExport& e,
-                           double mmMapUnitScaleFactor,
-                           const QString& layerName,
-                           QgsSymbolV2RenderContext* context,
-                           const QgsFeature* f,
-                           QPointF shift = QPointF( 0.0, 0.0 ) ) const;
+    virtual bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
 
     virtual double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const;
     virtual double dxfOffset( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const;
 
     virtual QColor dxfColor( QgsSymbolV2RenderContext& context ) const;
+    virtual double dxfAngle( QgsSymbolV2RenderContext& context ) const;
 
     virtual QVector<qreal> dxfCustomDashPattern( QgsSymbolV2::OutputUnit& unit ) const;
     virtual Qt::PenStyle dxfPenStyle() const;
diff --git a/python/core/symbology-ng/qgsvectorfieldsymbollayer.sip b/python/core/symbology-ng/qgsvectorfieldsymbollayer.sip
index b7ee68f..b524991 100644
--- a/python/core/symbology-ng/qgsvectorfieldsymbollayer.sip
+++ b/python/core/symbology-ng/qgsvectorfieldsymbollayer.sip
@@ -34,6 +34,9 @@ class QgsVectorFieldSymbolLayer : QgsMarkerSymbolLayerV2
     bool setSubSymbol( QgsSymbolV2* symbol /Transfer/ );
     QgsSymbolV2* subSymbol();
 
+    void setColor( const QColor& color );
+    virtual QColor color() const;
+
     void renderPoint( QPointF point, QgsSymbolV2RenderContext& context );
     void startRender( QgsSymbolV2RenderContext& context );
     void stopRender( QgsSymbolV2RenderContext& context );
diff --git a/python/gui/qgsannotationitem.sip b/python/gui/qgsannotationitem.sip
index 70cbf67..2526e36 100644
--- a/python/gui/qgsannotationitem.sip
+++ b/python/gui/qgsannotationitem.sip
@@ -1,11 +1,33 @@
-/** An annotation item can be either placed either on screen corrdinates or on map coordinates.
-  It may reference a feature and displays that associatiation with a balloon like appearance*/
+/** An annotation item can be either placed either on screen coordinates or on map coordinates.
+  It may reference a feature and displays that association with a balloon like appearance*/
+
+%ModuleCode
+#include "qgsformannotationitem.h"
+#include "qgshtmlannotationitem.h"
+#include "qgssvgannotationitem.h"
+#include "qgstextannotationitem.h"
+%End
+
 class QgsAnnotationItem: QgsMapCanvasItem
 {
 %TypeHeaderCode
 #include <qgsannotationitem.h>
 %End
 
+%ConvertToSubClassCode
+  if (dynamic_cast<QgsFormAnnotationItem*>(sipCpp) )
+    sipType = sipType_QgsFormAnnotationItem;
+  else if (dynamic_cast<QgsHtmlAnnotationItem*>(sipCpp) )
+    sipType = sipType_QgsHtmlAnnotationItem;
+  else if (dynamic_cast<QgsSvgAnnotationItem*>(sipCpp) )
+    sipType = sipType_QgsSvgAnnotationItem;
+  else if (dynamic_cast<QgsTextAnnotationItem*>(sipCpp) )
+    sipType = sipType_QgsTextAnnotationItem;
+  else
+    sipType = 0;
+%End
+
+
   public:
     enum MouseMoveAction
     {
diff --git a/python/gui/qgscomposerview.sip b/python/gui/qgscomposerview.sip
index 057e20d..da7c342 100644
--- a/python/gui/qgscomposerview.sip
+++ b/python/gui/qgscomposerview.sip
@@ -112,6 +112,13 @@ class QgsComposerView : QGraphicsView
     /** Set zoom level, where a zoom level of 1.0 corresponds to 100%*/
     void setZoomLevel( double zoomLevel );
 
+    /** Scales the view in a safe way, by limiting the acceptable range
+     * of the scale applied.
+     * @param scale factor to scale view by
+     * @note added in QGIS 2.16
+     */
+    void scaleSafe( double scale );
+
     /** Sets whether a preview effect should be used to alter the view's appearance
      * @param enabled Set to true to enable the preview effect on the view
      * @note added in 2.3
diff --git a/python/gui/qgsrubberband.sip b/python/gui/qgsrubberband.sip
index 1a56469..beefe33 100644
--- a/python/gui/qgsrubberband.sip
+++ b/python/gui/qgsrubberband.sip
@@ -122,6 +122,14 @@ class QgsRubberBand: QgsMapCanvasItem
      */
     void addPoint( const QgsPoint & p, bool doUpdate = true, int geometryIndex = 0 );
 
+    /** Ensures that a polygon geometry is closed and that the last vertex equals the
+     * first vertex.
+     * @param doUpdate set to true to update the map canvas immediately
+     * @param geometryIndex index of the feature part (in case of multipart geometries)
+     * @note added in QGIS 2.16
+     */
+    void closePoints( bool doUpdate = true, int geometryIndex = 0 );
+
     /**
      * Remove a vertex from the rubberband and (optionally) update canvas.
      * @param index The index of the vertex/point to remove, negative indexes start at end
diff --git a/python/plugins/GdalTools/tools/GdalTools_utils.py b/python/plugins/GdalTools/tools/GdalTools_utils.py
index 9eded70..149a4d8 100644
--- a/python/plugins/GdalTools/tools/GdalTools_utils.py
+++ b/python/plugins/GdalTools/tools/GdalTools_utils.py
@@ -444,19 +444,39 @@ class FileDialog:
 
     @classmethod
     def getOpenFileNames(self, parent=None, caption='', filter='', selectedFilter=None, useEncoding=False):
-        return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.ExistingFiles, filter, selectedFilter, useEncoding)
+        if useEncoding:
+            return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.ExistingFiles, filter, selectedFilter, useEncoding)
+        res = QFileDialog.getOpenFileNames(parent, caption, getLastUsedDir(), filter)
+        if len(res) > 0:
+            setLastUsedDir(res[-1])
+        return res
 
     @classmethod
     def getOpenFileName(self, parent=None, caption='', filter='', selectedFilter=None, useEncoding=False):
-        return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.ExistingFile, filter, selectedFilter, useEncoding)
+        if useEncoding:
+            return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.ExistingFile, filter, selectedFilter, useEncoding)
+        res = QFileDialog.getOpenFileName(parent, caption, getLastUsedDir(), filter)
+        if res:
+            setLastUsedDir(res)
+        return res
 
     @classmethod
     def getSaveFileName(self, parent=None, caption='', filter='', selectedFilter=None, useEncoding=False):
-        return self.getDialog(parent, caption, QFileDialog.AcceptSave, QFileDialog.AnyFile, filter, selectedFilter, useEncoding)
+        if useEncoding:
+            return self.getDialog(parent, caption, QFileDialog.AcceptSave, QFileDialog.AnyFile, filter, selectedFilter, useEncoding)
+        res = QFileDialog.getSaveFileName(parent, caption, getLastUsedDir(), filter)
+        if res:
+            setLastUsedDir(res)
+        return res
 
     @classmethod
     def getExistingDirectory(self, parent=None, caption='', useEncoding=False):
-        return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.DirectoryOnly, '', None, useEncoding)
+        if useEncoding:
+            return self.getDialog(parent, caption, QFileDialog.AcceptOpen, QFileDialog.DirectoryOnly, '', None, useEncoding)
+        res = QFileDialog.getExistingDirectory(parent, caption, getLastUsedDir(), QFileDialog.ShowDirsOnly)
+        if res:
+            setLastUsedDir(res)
+        return res
 
 
 class FileFilter:
diff --git a/python/plugins/GdalTools/tools/doContour.py b/python/plugins/GdalTools/tools/doContour.py
index 8d179cd..f645ff2 100644
--- a/python/plugins/GdalTools/tools/doContour.py
+++ b/python/plugins/GdalTools/tools/doContour.py
@@ -47,6 +47,8 @@ class GdalToolsDialog(QWidget, Ui_Widget, BasePluginWidget):
 
         self.outSelector.setType(self.outSelector.FILE)
 
+        self.outputFormat = Utils.fillVectorOutputFormat()
+
         # set the default QSpinBoxes value
         self.intervalDSpinBox.setValue(10.0)
 
@@ -85,6 +87,7 @@ class GdalToolsDialog(QWidget, Ui_Widget, BasePluginWidget):
         if not self.useDirAsOutput:
             Utils.FileFilter.setLastUsedVectorFilter(lastUsedFilter)
 
+        self.outputFormat = Utils.fillVectorOutputFormat(lastUsedFilter, outputFile)
         self.outSelector.setFilename(outputFile)
         self.lastEncoding = encoding
 
@@ -96,8 +99,15 @@ class GdalToolsDialog(QWidget, Ui_Widget, BasePluginWidget):
         if True: # XXX in this moment the -i argument is not optional
             arguments.append("-i")
             arguments.append(unicode(self.intervalDSpinBox.value()))
+
+        outputFn = self.getOutputFileName()
+        if outputFn:
+            arguments.append("-f")
+            arguments.append(self.outputFormat)
+
         arguments.append(self.getInputFileName())
         arguments.append(self.outSelector.filename())
+
         return arguments
 
     def getInputFileName(self):
diff --git a/python/plugins/GdalTools/tools/doProjection.py b/python/plugins/GdalTools/tools/doProjection.py
index e01f3ec..8869cae 100644
--- a/python/plugins/GdalTools/tools/doProjection.py
+++ b/python/plugins/GdalTools/tools/doProjection.py
@@ -42,7 +42,7 @@ class GdalToolsDialog(QWidget, Ui_Widget, BaseBatchWidget):
         self.iface = iface
 
         self.setupUi(self)
-        BaseBatchWidget.__init__(self, self.iface, "gdalwarp")
+        BaseBatchWidget.__init__(self, self.iface, "gdal_translate")
 
         self.inSelector.setType(self.inSelector.FILE)
 
@@ -112,7 +112,7 @@ class GdalToolsDialog(QWidget, Ui_Widget, BaseBatchWidget):
     def getArguments(self):
         arguments = []
         if self.desiredSRSEdit.text():
-            arguments.append("-t_srs")
+            arguments.append("-a_srs")
             arguments.append(self.desiredSRSEdit.text())
         if self.batchCheck.isChecked():
             return arguments
diff --git a/python/plugins/db_manager/db_plugins/oracle/connector.py b/python/plugins/db_manager/db_plugins/oracle/connector.py
index e1dfb30..6fb46ac 100644
--- a/python/plugins/db_manager/db_plugins/oracle/connector.py
+++ b/python/plugins/db_manager/db_plugins/oracle/connector.py
@@ -86,6 +86,8 @@ class OracleDBConnector(DBConnector):
             'allowGeometrylessTables').lower() == "true"
         self.onlyExistingTypes = uri.param(
             'onlyExistingTypes').lower() == "true"
+        self.includeGeoAttributes = uri.param(
+            'includeGeoAttributes').lower() == "true"
 
         # For refreshing
         self.populated = False
diff --git a/python/plugins/db_manager/db_plugins/oracle/plugin.py b/python/plugins/db_manager/db_plugins/oracle/plugin.py
index 0a10703..ce5d0f1 100644
--- a/python/plugins/db_manager/db_plugins/oracle/plugin.py
+++ b/python/plugins/db_manager/db_plugins/oracle/plugin.py
@@ -112,6 +112,8 @@ class OracleDBPlugin(DBPlugin):
             settings.value("allowGeometrylessTables", False, type=bool)))
         uri.setParam('onlyExistingTypes', unicode(
             settings.value("onlyExistingTypes", False, type=bool)))
+        uri.setParam('includeGeoAttributes', unicode(
+            settings.value("includeGeoAttributes", False, type=bool)))
 
         settings.endGroup()
 
diff --git a/python/plugins/db_manager/db_plugins/postgis/connector.py b/python/plugins/db_manager/db_plugins/postgis/connector.py
index ec23ade..4e8c856 100644
--- a/python/plugins/db_manager/db_plugins/postgis/connector.py
+++ b/python/plugins/db_manager/db_plugins/postgis/connector.py
@@ -125,8 +125,8 @@ class PostGisDBConnector(DBConnector):
 
         self.connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
 
-        c = self._execute(None, u"SELECT current_user")
-        self.user = self._fetchone(c)
+        c = self._execute(None, u"SELECT current_user,current_database()")
+        self.user, self.dbname = self._fetchone(c)
         self._close_cursor(c)
 
         self._checkSpatial()
diff --git a/python/plugins/db_manager/db_plugins/postgis/info_model.py b/python/plugins/db_manager/db_plugins/postgis/info_model.py
index c4b0923..83299eb 100644
--- a/python/plugins/db_manager/db_plugins/postgis/info_model.py
+++ b/python/plugins/db_manager/db_plugins/postgis/info_model.py
@@ -22,10 +22,21 @@ email                : brush.tyler at gmail.com
 
 from PyQt4.QtGui import QApplication
 
-from ..info_model import TableInfo, VectorTableInfo, RasterTableInfo
+from ..info_model import TableInfo, VectorTableInfo, RasterTableInfo, DatabaseInfo
 from ..html_elems import HtmlSection, HtmlParagraph, HtmlTable, HtmlTableHeader, HtmlTableCol
 
 
+class PGDatabaseInfo(DatabaseInfo):
+
+    def connectionDetails(self):
+        tbl = [
+            (QApplication.translate("DBManagerPlugin", "Host:"), self.db.connector.host),
+            (QApplication.translate("DBManagerPlugin", "User:"), self.db.connector.user),
+            (QApplication.translate("DBManagerPlugin", "Database:"), self.db.connector.dbname)
+        ]
+        return HtmlTable(tbl)
+
+
 class PGTableInfo(TableInfo):
 
     def __init__(self, table):
diff --git a/python/plugins/db_manager/db_plugins/postgis/plugin.py b/python/plugins/db_manager/db_plugins/postgis/plugin.py
index 3f42a4c..1be0d17 100644
--- a/python/plugins/db_manager/db_plugins/postgis/plugin.py
+++ b/python/plugins/db_manager/db_plugins/postgis/plugin.py
@@ -111,6 +111,10 @@ class PGDatabase(Database):
     def dataTablesFactory(self, row, db, schema=None):
         return PGTable(row, db, schema)
 
+    def info(self):
+        from .info_model import PGDatabaseInfo
+        return PGDatabaseInfo(self)
+
     def vectorTablesFactory(self, row, db, schema=None):
         return PGVectorTable(row, db, schema)
 
diff --git a/python/plugins/db_manager/dlg_sql_window.py b/python/plugins/db_manager/dlg_sql_window.py
index e62719e..4efbf95 100644
--- a/python/plugins/db_manager/dlg_sql_window.py
+++ b/python/plugins/db_manager/dlg_sql_window.py
@@ -70,6 +70,7 @@ class DlgSqlWindow(QWidget, Ui_Dialog):
 
         self.editSql.setFocus()
         self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+        self.editSql.setMarginVisible(True)
         self.initCompleter()
 
         # allow copying results
diff --git a/python/plugins/fTools/tools/doGeometry.py b/python/plugins/fTools/tools/doGeometry.py
index 4f997a7..7ecdc49 100644
--- a/python/plugins/fTools/tools/doGeometry.py
+++ b/python/plugins/fTools/tools/doGeometry.py
@@ -598,22 +598,22 @@ class geometryThread(QThread):
         writer = QgsVectorFileWriter(self.myName, self.myEncoding, vprovider.fields(),
                                      QGis.WKBPoint, vprovider.crs())
         inFeat = QgsFeature()
-        outFeat = QgsFeature()
         nFeat = vprovider.featureCount()
         nElement = 0
         self.emit(SIGNAL("runStatus( PyQt_PyObject )"), 0)
         self.emit(SIGNAL("runRange( PyQt_PyObject )"), (0, nFeat))
         fit = vprovider.getFeatures()
         while fit.nextFeature(inFeat):
+            outFeat = QgsFeature()
             nElement += 1
             self.emit(SIGNAL("runStatus( PyQt_PyObject )"), nElement)
-            inGeom = inFeat.geometry()
+            if inFeat.constGeometry():
+                inGeom = inFeat.geometry()
+                outGeom = inGeom.centroid()
+                outFeat.setGeometry(QgsGeometry(outGeom))
+
             atMap = inFeat.attributes()
-            outGeom = inGeom.centroid()
-            if outGeom is None:
-                return "math_error"
             outFeat.setAttributes(atMap)
-            outFeat.setGeometry(QgsGeometry(outGeom))
             writer.addFeature(outFeat)
         del writer
         return True
diff --git a/python/plugins/fTools/tools/doPointsInPolygon.py b/python/plugins/fTools/tools/doPointsInPolygon.py
index 1b0d28d..84bf373 100644
--- a/python/plugins/fTools/tools/doPointsInPolygon.py
+++ b/python/plugins/fTools/tools/doPointsInPolygon.py
@@ -299,6 +299,9 @@ class PointsInPolygonThread(QThread):
                             value = math.sqrt(value)
                         atMap.append(value)
 
+            else:  # no intersection - store at least the zero count
+                atMap.append(0)
+
             outFeat.setAttributes(atMap)
             writer.addFeature(outFeat)
 
diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithm.py b/python/plugins/processing/algs/gdal/GdalAlgorithm.py
index e50a348..9b65d14 100644
--- a/python/plugins/processing/algs/gdal/GdalAlgorithm.py
+++ b/python/plugins/processing/algs/gdal/GdalAlgorithm.py
@@ -50,10 +50,16 @@ class GdalAlgorithm(GeoAlgorithm):
     def processAlgorithm(self, progress):
         commands = self.getConsoleCommands()
         layers = dataobjects.getVectorLayers()
+        supported = dataobjects.getSupportedOutputVectorLayerExtensions()
         for i, c in enumerate(commands):
             for layer in layers:
                 if layer.source() in c:
-                    c = c.replace(layer.source(), dataobjects.exportVectorLayer(layer))
+                    exported = dataobjects.exportVectorLayer(layer, supported)
+                    exportedFileName = os.path.splitext(os.path.split(exported)[1])[0]
+                    c = c.replace(layer.source(), exported)
+                    if os.path.isfile(layer.source()):
+                        fileName = os.path.splitext(os.path.split(layer.source())[1])[0]
+                        c = c.replace(fileName, exportedFileName)
 
             commands[i] = c
         GdalUtils.runGdal(commands, progress)
diff --git a/python/plugins/processing/algs/gdal/GdalUtils.py b/python/plugins/processing/algs/gdal/GdalUtils.py
index 2cb7781..d9e71aa 100644
--- a/python/plugins/processing/algs/gdal/GdalUtils.py
+++ b/python/plugins/processing/algs/gdal/GdalUtils.py
@@ -29,7 +29,7 @@ import os
 import subprocess
 import platform
 from PyQt4.QtCore import QSettings
-from qgis.core import QgsApplication
+from qgis.core import QgsApplication, QgsVectorFileWriter
 from processing.core.ProcessingLog import ProcessingLog
 
 try:
@@ -131,6 +131,18 @@ class GdalUtils:
         return allexts
 
     @staticmethod
+    def getVectorDriverFromFileName(filename):
+        ext = os.path.splitext(filename)[1]
+        if ext == '':
+            return 'ESRI Shapefile'
+
+        formats = QgsVectorFileWriter.supportedFiltersAndFormats()
+        for k, v in formats.iteritems():
+            if ext in k:
+                return v
+        return 'ESRI Shapefile'
+
+    @staticmethod
     def getFormatShortNameFromFilename(filename):
         ext = filename[filename.rfind('.') + 1:]
         supported = GdalUtils.getSupportedRasters()
diff --git a/python/plugins/processing/algs/gdal/contour.py b/python/plugins/processing/algs/gdal/contour.py
index eeefa6c..cd2ba3d 100644
--- a/python/plugins/processing/algs/gdal/contour.py
+++ b/python/plugins/processing/algs/gdal/contour.py
@@ -62,6 +62,7 @@ class contour(GdalAlgorithm):
                                     self.tr('Contours')))
 
     def getConsoleCommands(self):
+        output = self.getOutputValue(self.OUTPUT_VECTOR)
         interval = unicode(self.getParameterValue(self.INTERVAL))
         fieldName = unicode(self.getParameterValue(self.FIELD_NAME))
         extra = self.getParameterValue(self.EXTRA)
@@ -75,10 +76,14 @@ class contour(GdalAlgorithm):
         arguments.append('-i')
         arguments.append(interval)
 
+        driver = GdalUtils.getVectorDriverFromFileName(output)
+        arguments.append('-f')
+        arguments.append(driver)
+
         if extra and len(extra) > 0:
             arguments.append(extra)
 
         arguments.append(self.getParameterValue(self.INPUT_RASTER))
-        arguments.append(self.getOutputValue(self.OUTPUT_VECTOR))
+        arguments.append(output)
 
         return ['gdal_contour', GdalUtils.escapeAndJoin(arguments)]
diff --git a/python/plugins/processing/algs/grass/GrassUtils.py b/python/plugins/processing/algs/grass/GrassUtils.py
index 2d6c99e..1adcf29 100644
--- a/python/plugins/processing/algs/grass/GrassUtils.py
+++ b/python/plugins/processing/algs/grass/GrassUtils.py
@@ -91,11 +91,14 @@ class GrassUtils:
             folder = None
         if folder is None:
             if isWindows():
-                testfolder = os.path.dirname(QgsApplication.prefixPath())
+                if "OSGEO4W_ROOT" in os.environ:
+                    testfolder = os.path.join(unicode(os.environ['OSGEO4W_ROOT']), "apps")
+                else:
+                    testfolder = unicode(QgsApplication.prefixPath())
                 testfolder = os.path.join(testfolder, 'grass')
                 if os.path.isdir(testfolder):
                     for subfolder in os.listdir(testfolder):
-                        if subfolder.startswith('grass'):
+                        if subfolder.startswith('grass-6'):
                             folder = os.path.join(testfolder, subfolder)
                             break
             else:
@@ -110,10 +113,10 @@ class GrassUtils:
         folder = ProcessingConfig.getSetting(GrassUtils.GRASS_WIN_SHELL) or ''
         if not os.path.exists(folder):
             folder = None
-        if folder is None:
+        if folder is None and GrassUtils.grassPath():
             folder = os.path.dirname(unicode(QgsApplication.prefixPath()))
             folder = os.path.join(folder, 'msys')
-        return folder
+        return folder or ''
 
     @staticmethod
     def grassDescriptionPath():
diff --git a/python/plugins/processing/algs/grass7/Grass7Utils.py b/python/plugins/processing/algs/grass7/Grass7Utils.py
index 79ac28d..34b660f 100644
--- a/python/plugins/processing/algs/grass7/Grass7Utils.py
+++ b/python/plugins/processing/algs/grass7/Grass7Utils.py
@@ -88,7 +88,10 @@ class Grass7Utils:
             folder = None
         if folder is None:
             if isWindows():
-                testfolder = os.path.dirname(unicode(QgsApplication.prefixPath()))
+                if "OSGEO4W_ROOT" in os.environ:
+                    testfolder = os.path.join(unicode(os.environ['OSGEO4W_ROOT']), "apps")
+                else:
+                    testfolder = unicode(QgsApplication.prefixPath())
                 testfolder = os.path.join(testfolder, 'grass')
                 if os.path.isdir(testfolder):
                     for subfolder in os.listdir(testfolder):
@@ -100,7 +103,7 @@ class Grass7Utils:
                 if not os.path.isdir(folder):
                     folder = '/Applications/GRASS-7.0.app/Contents/MacOS'
 
-        return folder
+        return folder or ''
 
     @staticmethod
     def grassDescriptionPath():
diff --git a/python/plugins/processing/algs/qgis/AutoincrementalField.py b/python/plugins/processing/algs/qgis/AutoincrementalField.py
index 998c728..df7db61 100644
--- a/python/plugins/processing/algs/qgis/AutoincrementalField.py
+++ b/python/plugins/processing/algs/qgis/AutoincrementalField.py
@@ -62,7 +62,7 @@ class AutoincrementalField(GeoAlgorithm):
             geom = feat.geometry()
             outFeat.setGeometry(geom)
             attrs = feat.attributes()
-            attrs.append(count)
+            attrs.append(current)
             outFeat.setAttributes(attrs)
             writer.addFeature(outFeat)
         del writer
diff --git a/python/plugins/processing/algs/qgis/Clip.py b/python/plugins/processing/algs/qgis/Clip.py
index d8d1d5d..0df9c36 100644
--- a/python/plugins/processing/algs/qgis/Clip.py
+++ b/python/plugins/processing/algs/qgis/Clip.py
@@ -34,10 +34,6 @@ from processing.core.parameters import ParameterVector
 from processing.core.outputs import OutputVector
 from processing.tools import dataobjects, vector
 
-GEOM_25D = [QGis.WKBPoint25D, QGis.WKBLineString25D, QGis.WKBPolygon25D,
-            QGis.WKBMultiPoint25D, QGis.WKBMultiLineString25D,
-            QGis.WKBMultiPolygon25D]
-
 
 class Clip(GeoAlgorithm):
 
@@ -60,11 +56,6 @@ class Clip(GeoAlgorithm):
         layerB = dataobjects.getObjectFromUri(
             self.getParameterValue(Clip.OVERLAY))
 
-        geomType = layerA.dataProvider().geometryType()
-        if geomType in GEOM_25D:
-            raise GeoAlgorithmExecutionException(
-                self.tr('Input layer has unsupported geometry type {}').format(geomType))
-
         writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
             layerA.pendingFields(),
             layerA.dataProvider().geometryType(),
diff --git a/python/plugins/processing/algs/qgis/ConvexHull.py b/python/plugins/processing/algs/qgis/ConvexHull.py
index 4b56831..5ade572 100644
--- a/python/plugins/processing/algs/qgis/ConvexHull.py
+++ b/python/plugins/processing/algs/qgis/ConvexHull.py
@@ -70,8 +70,8 @@ class ConvexHull(GeoAlgorithm):
         if useField:
             index = layer.fieldNameIndex(fieldName)
             fType = layer.pendingFields()[index].type()
-            if fType == QVariant.Int:
-                f.setType(QVariant.Int)
+            if fType in [QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]:
+                f.setType(fType)
                 f.setLength(20)
             elif fType == QVariant.Double:
                 f.setType(QVariant.Double)
diff --git a/python/plugins/processing/algs/qgis/CreateConstantRaster.py b/python/plugins/processing/algs/qgis/CreateConstantRaster.py
index f877105..cb494ed 100644
--- a/python/plugins/processing/algs/qgis/CreateConstantRaster.py
+++ b/python/plugins/processing/algs/qgis/CreateConstantRaster.py
@@ -25,6 +25,8 @@ __copyright__ = '(C) 2012, Victor Olaya'
 
 __revision__ = '$Format:%H$'
 
+from osgeo import gdal
+
 from processing.core.GeoAlgorithm import GeoAlgorithm
 from processing.core.parameters import ParameterRaster
 from processing.core.parameters import ParameterNumber
@@ -46,7 +48,8 @@ class CreateConstantRaster(GeoAlgorithm):
         self.addParameter(ParameterRaster(self.INPUT,
                                           self.tr('Reference layer')))
         self.addParameter(ParameterNumber(self.NUMBER,
-                                          self.tr('Constant value'), default=1.0))
+                                          self.tr('Constant value'),
+                                          default=1.0))
 
         self.addOutput(OutputRaster(self.OUTPUT,
                                     self.tr('Constant')))
@@ -54,10 +57,13 @@ class CreateConstantRaster(GeoAlgorithm):
     def processAlgorithm(self, progress):
         layer = dataobjects.getObjectFromUri(
             self.getParameterValue(self.INPUT))
-        value = self.getOutputValue(self.NUMBER)
+        value = self.getParameterValue(self.NUMBER)
 
         output = self.getOutputFromName(self.OUTPUT)
 
+        raster = gdal.Open(layer.source(), gdal.GA_ReadOnly)
+        geoTransform = raster.GetGeoTransform()
+
         cellsize = (layer.extent().xMaximum() - layer.extent().xMinimum()) \
             / layer.width()
 
@@ -68,7 +74,8 @@ class CreateConstantRaster(GeoAlgorithm):
                          layer.extent().yMaximum(),
                          cellsize,
                          1,
-                         self.crs,
+                         layer.crs(),
+                         geoTransform
                          )
-        w.matrix[:] = value
+        w.matrix.fill(value)
         w.close()
diff --git a/python/plugins/processing/algs/qgis/Difference.py b/python/plugins/processing/algs/qgis/Difference.py
index 3032631..d375c30 100644
--- a/python/plugins/processing/algs/qgis/Difference.py
+++ b/python/plugins/processing/algs/qgis/Difference.py
@@ -33,10 +33,6 @@ from processing.core.parameters import ParameterVector
 from processing.core.outputs import OutputVector
 from processing.tools import dataobjects, vector
 
-GEOM_25D = [QGis.WKBPoint25D, QGis.WKBLineString25D, QGis.WKBPolygon25D,
-            QGis.WKBMultiPoint25D, QGis.WKBMultiLineString25D,
-            QGis.WKBMultiPolygon25D]
-
 
 class Difference(GeoAlgorithm):
 
@@ -65,10 +61,6 @@ class Difference(GeoAlgorithm):
             self.getParameterValue(Difference.OVERLAY))
 
         geomType = layerA.dataProvider().geometryType()
-        if geomType in GEOM_25D:
-            raise GeoAlgorithmExecutionException(
-                self.tr('Input layer has unsupported geometry type {}').format(geomType))
-
         writer = self.getOutputFromName(
             Difference.OUTPUT).getVectorWriter(layerA.pendingFields(),
                                                geomType,
diff --git a/python/plugins/processing/algs/qgis/Dissolve.py b/python/plugins/processing/algs/qgis/Dissolve.py
index b063416..80af1fc 100644
--- a/python/plugins/processing/algs/qgis/Dissolve.py
+++ b/python/plugins/processing/algs/qgis/Dissolve.py
@@ -125,6 +125,7 @@ class Dissolve(GeoAlgorithm):
 
             features = None
 
+            nElement = 0
             for key, value in myDict.items():
                 nElement += 1
                 progress.setPercentage(int(nElement * 100 / nFeat))
diff --git a/python/plugins/processing/algs/qgis/ExtractByAttribute.py b/python/plugins/processing/algs/qgis/ExtractByAttribute.py
index 2d6fa06..481244c 100644
--- a/python/plugins/processing/algs/qgis/ExtractByAttribute.py
+++ b/python/plugins/processing/algs/qgis/ExtractByAttribute.py
@@ -95,7 +95,7 @@ class ExtractByAttribute(GeoAlgorithm):
             raise GeoAlgorithmExecutionException(
                 self.tr('Operators %s can be used only with string fields.' % op))
 
-        if fieldType in [QVariant.Int, QVariant.Double]:
+        if fieldType in [QVariant.Int, QVariant.Double, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]:
             expr = '"%s" %s %s' % (fieldName, operator, value)
         elif fieldType == QVariant.String:
             if operator not in self.OPERATORS[-2:]:
diff --git a/python/plugins/processing/algs/qgis/HubDistance.py b/python/plugins/processing/algs/qgis/HubDistance.py
index de625b6..617318d 100644
--- a/python/plugins/processing/algs/qgis/HubDistance.py
+++ b/python/plugins/processing/algs/qgis/HubDistance.py
@@ -26,7 +26,7 @@ __copyright__ = '(C) 2010, Michael Minn'
 __revision__ = '$Format:%H$'
 
 from PyQt4.QtCore import QVariant
-from qgis.core import QGis, QgsField, QgsGeometry, QgsDistanceArea, QgsFeature
+from qgis.core import QGis, QgsField, QgsGeometry, QgsDistanceArea, QgsFeature, QgsFeatureRequest
 from processing.core.GeoAlgorithm import GeoAlgorithm
 from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
 from processing.core.parameters import ParameterVector
@@ -106,12 +106,7 @@ class HubDistance(GeoAlgorithm):
         writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
             fields, geomType, layerPoints.crs())
 
-        # Create array of hubs in memory
-        hubs = []
-        features = vector.features(layerHubs)
-        for f in features:
-            hubs.append(Hub(f.geometry().boundingBox().center(),
-                            unicode(f[fieldName])))
+        index = vector.spatialindex(layerHubs)
 
         distance = QgsDistanceArea()
         distance.setSourceCrs(layerPoints.crs().srsid())
@@ -123,17 +118,13 @@ class HubDistance(GeoAlgorithm):
         for current, f in enumerate(features):
             src = f.geometry().boundingBox().center()
 
-            closest = hubs[0]
-            hubDist = distance.measureLine(src, closest.point)
-
-            for hub in hubs:
-                dist = distance.measureLine(src, hub.point)
-                if dist < hubDist:
-                    closest = hub
-                    hubDist = dist
+            neighbors = index.nearestNeighbor(src, 1)
+            ft = layerHubs.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0])).next()
+            closest = ft.geometry().boundingBox().center()
+            hubDist = distance.measureLine(src, closest)
 
             attributes = f.attributes()
-            attributes.append(closest.name)
+            attributes.append(ft[fieldName])
             if units == 'Feet':
                 attributes.append(hubDist * 3.2808399)
             elif units == 'Miles':
@@ -142,8 +133,8 @@ class HubDistance(GeoAlgorithm):
                 attributes.append(hubDist / 1000.0)
             elif units != 'Meters':
                 attributes.append(sqrt(
-                    pow(src.x() - closest.point.x(), 2.0) +
-                    pow(src.y() - closest.point.y(), 2.0)))
+                    pow(src.x() - closest.x(), 2.0) +
+                    pow(src.y() - closest.y(), 2.0)))
             else:
                 attributes.append(hubDist)
 
@@ -153,16 +144,9 @@ class HubDistance(GeoAlgorithm):
             if geomType == QGis.WKBPoint:
                 feat.setGeometry(QgsGeometry.fromPoint(src))
             else:
-                feat.setGeometry(QgsGeometry.fromPolyline([src, closest.point]))
+                feat.setGeometry(QgsGeometry.fromPolyline([src, closest]))
 
             writer.addFeature(feat)
             progress.setPercentage(int(current * total))
 
         del writer
-
-
-class Hub:
-
-    def __init__(self, point, name):
-        self.point = point
-        self.name = name
diff --git a/python/plugins/processing/algs/qgis/Intersection.py b/python/plugins/processing/algs/qgis/Intersection.py
index 832d97c..b71da9a 100644
--- a/python/plugins/processing/algs/qgis/Intersection.py
+++ b/python/plugins/processing/algs/qgis/Intersection.py
@@ -43,10 +43,6 @@ for key, value in wkbTypeGroups.items():
     for const in value:
         wkbTypeGroups[const] = key
 
-GEOM_25D = [QGis.WKBPoint25D, QGis.WKBLineString25D, QGis.WKBPolygon25D,
-            QGis.WKBMultiPoint25D, QGis.WKBMultiLineString25D,
-            QGis.WKBMultiPolygon25D]
-
 
 class Intersection(GeoAlgorithm):
 
@@ -71,10 +67,6 @@ class Intersection(GeoAlgorithm):
         vproviderA = vlayerA.dataProvider()
 
         geomType = vproviderA.geometryType()
-        if geomType in GEOM_25D:
-            raise GeoAlgorithmExecutionException(
-                self.tr('Input layer has unsupported geometry type {}').format(geomType))
-
         fields = vector.combineVectorFields(vlayerA, vlayerB)
         writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields,
                                                                      geomType, vproviderA.crs())
diff --git a/python/plugins/processing/algs/qgis/Merge.py b/python/plugins/processing/algs/qgis/Merge.py
index 626653f..0c9f750 100644
--- a/python/plugins/processing/algs/qgis/Merge.py
+++ b/python/plugins/processing/algs/qgis/Merge.py
@@ -92,7 +92,7 @@ class Merge(GeoAlgorithm):
                 sattributes = feature.attributes()
                 dattributes = []
                 for dindex, dfield in enumerate(fields):
-                    if (dfield.type() == QVariant.Int):
+                    if (dfield.type() == QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong):
                         dattribute = 0
                     elif (dfield.type() == QVariant.Double):
                         dattribute = 0.0
diff --git a/python/plugins/processing/algs/qgis/SelectByAttribute.py b/python/plugins/processing/algs/qgis/SelectByAttribute.py
index cb8c368..49786f4 100644
--- a/python/plugins/processing/algs/qgis/SelectByAttribute.py
+++ b/python/plugins/processing/algs/qgis/SelectByAttribute.py
@@ -94,7 +94,7 @@ class SelectByAttribute(GeoAlgorithm):
             raise GeoAlgorithmExecutionException(
                 self.tr('Operators %s can be used only with string fields.' % op))
 
-        if fieldType in [QVariant.Int, QVariant.Double]:
+        if fieldType in [QVariant.Int, QVariant.Double, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]:
             expr = '"%s" %s %s' % (fieldName, operator, value)
         elif fieldType == QVariant.String:
             if operator not in self.OPERATORS[-2:]:
diff --git a/python/plugins/processing/algs/qgis/SpatialJoin.py b/python/plugins/processing/algs/qgis/SpatialJoin.py
index 2474719..6fb5adc 100644
--- a/python/plugins/processing/algs/qgis/SpatialJoin.py
+++ b/python/plugins/processing/algs/qgis/SpatialJoin.py
@@ -113,7 +113,7 @@ class SpatialJoin(GeoAlgorithm):
         else:
             numFields = {}
             for j in xrange(len(joinFields)):
-                if joinFields[j].type() in [QVariant.Int, QVariant.Double]:
+                if joinFields[j].type() in [QVariant.Int, QVariant.Double, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong]:
                     numFields[j] = []
                     for i in sumList:
                         field = QgsField(i + unicode(joinFields[j].name()), QVariant.Double, '', 24, 16)
diff --git a/python/plugins/processing/algs/qgis/SymmetricalDifference.py b/python/plugins/processing/algs/qgis/SymmetricalDifference.py
index 05f13c4..5392da2 100644
--- a/python/plugins/processing/algs/qgis/SymmetricalDifference.py
+++ b/python/plugins/processing/algs/qgis/SymmetricalDifference.py
@@ -33,10 +33,6 @@ from processing.core.parameters import ParameterVector
 from processing.core.outputs import OutputVector
 from processing.tools import dataobjects, vector
 
-GEOM_25D = [QGis.WKBPoint25D, QGis.WKBLineString25D, QGis.WKBPolygon25D,
-            QGis.WKBMultiPoint25D, QGis.WKBMultiLineString25D,
-            QGis.WKBMultiPolygon25D]
-
 
 class SymmetricalDifference(GeoAlgorithm):
 
@@ -64,10 +60,6 @@ class SymmetricalDifference(GeoAlgorithm):
         providerB = layerB.dataProvider()
 
         geomType = providerA.geometryType()
-        if geomType in GEOM_25D:
-            raise GeoAlgorithmExecutionException(
-                self.tr('Input layer has unsupported geometry type {}').format(geomType))
-
         fields = vector.combineVectorFields(layerA, layerB)
         writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
             fields, geomType, providerA.crs())
diff --git a/python/plugins/processing/algs/qgis/Union.py b/python/plugins/processing/algs/qgis/Union.py
index 9d5e07c..f64c1ca 100644
--- a/python/plugins/processing/algs/qgis/Union.py
+++ b/python/plugins/processing/algs/qgis/Union.py
@@ -42,10 +42,6 @@ for key, value in wkbTypeGroups.items():
     for const in value:
         wkbTypeGroups[const] = key
 
-GEOM_25D = [QGis.WKBPoint25D, QGis.WKBLineString25D, QGis.WKBPolygon25D,
-            QGis.WKBMultiPoint25D, QGis.WKBMultiLineString25D,
-            QGis.WKBMultiPolygon25D]
-
 
 class Union(GeoAlgorithm):
 
@@ -69,10 +65,6 @@ class Union(GeoAlgorithm):
         vproviderA = vlayerA.dataProvider()
 
         geomType = vproviderA.geometryType()
-        if geomType in GEOM_25D:
-            raise GeoAlgorithmExecutionException(
-                self.tr('Input layer has unsupported geometry type {}').format(geomType))
-
         fields = vector.combineVectorFields(vlayerA, vlayerB)
         writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields,
                                                                       geomType, vproviderA.crs())
diff --git a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
index 8436969..872d347 100644
--- a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
+++ b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
@@ -217,7 +217,7 @@ class FieldsMappingModel(QtCore.QAbstractTableModel):
                 'type': field.type(),
                 'length': field.length(),
                 'precision': field.precision(),
-                'expression': field.name()}
+                'expression': QgsExpression.quotedColumnRef(field.name())}
 
     def loadLayerFields(self, layer):
         self.beginResetModel()
@@ -289,7 +289,10 @@ class FieldDelegate(QtGui.QStyledItemDelegate):
 
         elif fieldType == QgsExpression:
             (value, isExpression, isValid) = editor.currentField()
-            model.setData(index, value)
+            if isExpression is True:
+                model.setData(index, value)
+            else:
+                model.setData(index, QgsExpression.quotedColumnRef(value))
 
         else:
             QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
diff --git a/python/plugins/processing/algs/r/RAlgorithm.py b/python/plugins/processing/algs/r/RAlgorithm.py
index 051db38..1fe7b30 100644
--- a/python/plugins/processing/algs/r/RAlgorithm.py
+++ b/python/plugins/processing/algs/r/RAlgorithm.py
@@ -26,6 +26,7 @@ __copyright__ = '(C) 2012, Victor Olaya'
 __revision__ = '$Format:%H$'
 
 import os
+import json
 
 from PyQt4.QtGui import QIcon
 
@@ -43,6 +44,7 @@ from processing.core.parameters import ParameterBoolean
 from processing.core.parameters import ParameterSelection
 from processing.core.parameters import ParameterTableField
 from processing.core.parameters import ParameterExtent
+from processing.core.parameters import ParameterCrs
 from processing.core.parameters import ParameterFile
 from processing.core.outputs import OutputTable
 from processing.core.outputs import OutputVector
@@ -149,84 +151,128 @@ class RAlgorithm(GeoAlgorithm):
         tokens = line.split('=')
         desc = self.createDescriptiveName(tokens[0])
         if tokens[1].lower().strip() == 'group':
-            self.group = tokens[0]
+            self.group = self.i18n_group = tokens[0]
             return
-        if tokens[1].lower().strip().startswith('raster'):
-            param = ParameterRaster(tokens[0], desc, False)
-        elif tokens[1].lower().strip() == 'vector':
-            param = ParameterVector(tokens[0], desc,
+        if tokens[1].lower().strip() == 'name':
+            self.name = self.i18n_name = tokens[0]
+            return
+
+        if tokens[1].lower().strip().startswith('output'):
+            outToken = tokens[1].strip()[len('output') + 1:]
+            out = self.processOutputParameterToken(outToken)
+
+        elif tokens[1].lower().strip().startswith('optional'):
+            optToken = tokens[1].strip()[len('optional') + 1:]
+            param = self.processInputParameterToken(optToken, tokens[0])
+            if param:
+                param.optional = True
+
+        else:
+            param = self.processInputParameterToken(tokens[1], tokens[0])
+
+        if param is not None:
+            self.addParameter(param)
+        elif out is not None:
+            out.name = tokens[0]
+            out.description = tokens[0]
+            self.addOutput(out)
+        else:
+            raise WrongScriptException(
+                self.tr('Could not load R script: %s.\n Problem with line %s' % (self.descriptionFile, line)))
+
+    def processInputParameterToken(self, token, name):
+        param = None
+
+        desc = self.createDescriptiveName(name)
+
+        if token.lower().strip().startswith('raster'):
+            param = ParameterRaster(name, desc, False)
+        elif token.lower().strip() == 'vector':
+            param = ParameterVector(name, desc,
                                     [ParameterVector.VECTOR_TYPE_ANY])
-        elif tokens[1].lower().strip() == 'vector point':
-            param = ParameterVector(tokens[0], desc,
+        elif token.lower().strip() == 'vector point':
+            param = ParameterVector(name, desc,
                                     [ParameterVector.VECTOR_TYPE_POINT])
-        elif tokens[1].lower().strip() == 'vector line':
-            param = ParameterVector(tokens[0], desc,
+        elif token.lower().strip() == 'vector line':
+            param = ParameterVector(name, desc,
                                     [ParameterVector.VECTOR_TYPE_LINE])
-        elif tokens[1].lower().strip() == 'vector polygon':
-            param = ParameterVector(tokens[0], desc,
+        elif token.lower().strip() == 'vector polygon':
+            param = ParameterVector(name, desc,
                                     [ParameterVector.VECTOR_TYPE_POLYGON])
-        elif tokens[1].lower().strip() == 'table':
-            param = ParameterTable(tokens[0], desc, False)
-        elif tokens[1].lower().strip().startswith('multiple raster'):
-            param = ParameterMultipleInput(tokens[0], desc,
+        elif token.lower().strip() == 'table':
+            param = ParameterTable(name, desc, False)
+        elif token.lower().strip().startswith('multiple raster'):
+            param = ParameterMultipleInput(name, desc,
                                            ParameterMultipleInput.TYPE_RASTER)
             param.optional = False
-        elif tokens[1].lower().strip() == 'multiple vector':
-            param = ParameterMultipleInput(tokens[0], desc,
+        elif token.lower().strip() == 'multiple vector':
+            param = ParameterMultipleInput(name, desc,
                                            ParameterMultipleInput.TYPE_VECTOR_ANY)
             param.optional = False
-        elif tokens[1].lower().strip().startswith('selection'):
-            options = tokens[1].strip()[len('selection'):].split(';')
-            param = ParameterSelection(tokens[0], desc, options)
-        elif tokens[1].lower().strip().startswith('boolean'):
-            default = tokens[1].strip()[len('boolean') + 1:]
-            param = ParameterBoolean(tokens[0], desc, default)
-        elif tokens[1].lower().strip().startswith('number'):
-            try:
-                default = float(tokens[1].strip()[len('number') + 1:])
-                param = ParameterNumber(tokens[0], desc, default=default)
-            except:
-                raise WrongScriptException(
-                    self.tr('Could not load R script: %s.\n Problem with line %s' % (self.descriptionFile, line)))
-        elif tokens[1].lower().strip().startswith('field'):
-            field = tokens[1].strip()[len('field') + 1:]
+        elif token.lower().strip().startswith('selection'):
+            options = token.strip()[len('selection'):].split(';')
+            param = ParameterSelection(name, desc, options)
+        elif token.lower().strip().startswith('boolean'):
+            default = token.strip()[len('boolean') + 1:]
+            if default:
+                param = ParameterBoolean(name, desc, default)
+            else:
+                param = ParameterBoolean(name, desc)
+        elif token.lower().strip().startswith('number'):
+            default = token.strip()[len('number') + 1:]
+            if default:
+                param = ParameterNumber(name, desc, default=default)
+            else:
+                param = ParameterNumber(name, desc)
+        elif token.lower().strip().startswith('field'):
+            field = token.strip()[len('field') + 1:]
             found = False
             for p in self.parameters:
                 if p.name == field:
                     found = True
                     break
             if found:
-                param = ParameterTableField(tokens[0], tokens[0], field)
-        elif tokens[1].lower().strip() == 'extent':
-            param = ParameterExtent(tokens[0], desc)
-        elif tokens[1].lower().strip() == 'file':
-            param = ParameterFile(tokens[0], desc, False)
-        elif tokens[1].lower().strip() == 'folder':
-            param = ParameterFile(tokens[0], desc, True)
-        elif tokens[1].lower().strip().startswith('string'):
-            default = tokens[1].strip()[len('string') + 1:]
-            param = ParameterString(tokens[0], desc, default)
-        elif tokens[1].lower().strip().startswith('longstring'):
-            default = tokens[1].strip()[len('longstring') + 1:]
-            param = ParameterString(tokens[0], desc, default, multiline=True)
-        elif tokens[1].lower().strip().startswith('output raster'):
+                param = ParameterTableField(name, desc, field)
+        elif token.lower().strip() == 'extent':
+            param = ParameterExtent(name, desc)
+        elif token.lower().strip() == 'file':
+            param = ParameterFile(name, desc, False)
+        elif token.lower().strip() == 'folder':
+            param = ParameterFile(name, desc, True)
+        elif token.lower().strip().startswith('string'):
+            default = token.strip()[len('string') + 1:]
+            if default:
+                param = ParameterString(name, desc, default)
+            else:
+                param = ParameterString(name, desc)
+        elif token.lower().strip().startswith('longstring'):
+            default = token.strip()[len('longstring') + 1:]
+            if default:
+                param = ParameterString(name, desc, default, multiline=True)
+            else:
+                param = ParameterString(name, desc, multiline=True)
+        elif token.lower().strip() == 'crs':
+            default = token.strip()[len('crs') + 1:]
+            if default:
+                param = ParameterCrs(name, desc, default)
+            else:
+                param = ParameterCrs(name, desc)
+
+        return param
+
+    def processOutputParameterToken(self, token):
+        out = None
+
+        if token.lower().strip().startswith('raster'):
             out = OutputRaster()
-        elif tokens[1].lower().strip().startswith('output vector'):
+        elif token.lower().strip().startswith('vector'):
             out = OutputVector()
-        elif tokens[1].lower().strip().startswith('output table'):
+        elif token.lower().strip().startswith('table'):
             out = OutputTable()
-        elif tokens[1].lower().strip().startswith('output file'):
+        elif token.lower().strip().startswith('file'):
             out = OutputFile()
 
-        if param is not None:
-            self.addParameter(param)
-        elif out is not None:
-            out.name = tokens[0]
-            out.description = tokens[0]
-            self.addOutput(out)
-        else:
-            raise WrongScriptException(
-                self.tr('Could not load R script: %s.\n Problem with line %s' % (self.descriptionFile, line)))
+        return out
 
     def processAlgorithm(self, progress):
         if isWindows():
@@ -275,7 +321,7 @@ class RAlgorithm(GeoAlgorithm):
                         value = value + '.tif'
                     commands.append('writeGDAL(' + out.name + ',"' + value
                                     + '")')
-            if isinstance(out, OutputVector):
+            elif isinstance(out, OutputVector):
                 value = out.value
                 if not value.endswith('shp'):
                     value = value + '.shp'
@@ -284,6 +330,10 @@ class RAlgorithm(GeoAlgorithm):
                 filename = filename[:-4]
                 commands.append('writeOGR(' + out.name + ',"' + value + '","'
                                 + filename + '", driver="ESRI Shapefile")')
+            elif isinstance(out, OutputTable):
+                value = out.value
+                value = value.replace('\\', '/')
+                commands.append('write.csv(' + out.name + ',"' + value + '")')
 
         if self.showPlots:
             commands.append('dev.off()')
@@ -310,42 +360,70 @@ class RAlgorithm(GeoAlgorithm):
 
         for param in self.parameters:
             if isinstance(param, ParameterRaster):
-                value = param.value
-                value = value.replace('\\', '/')
-                if self.passFileNames:
-                    commands.append(param.name + ' = "' + value + '"')
-                elif self.useRasterPackage:
-                    commands.append(param.name + ' = ' + 'brick("' + value
-                                    + '")')
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
                 else:
-                    commands.append(param.name + ' = ' + 'readGDAL("' + value
-                                    + '")')
-            if isinstance(param, ParameterVector):
-                value = param.getSafeExportedLayer()
-                value = value.replace('\\', '/')
-                filename = os.path.basename(value)
-                filename = filename[:-4]
-                folder = os.path.dirname(value)
-                if self.passFileNames:
-                    commands.append(param.name + ' = "' + value + '"')
+                    value = param.value
+                    value = value.replace('\\', '/')
+                    if self.passFileNames:
+                        commands.append(param.name + ' = "' + value + '"')
+                    elif self.useRasterPackage:
+                        commands.append(param.name + ' = ' + 'brick("' + value
+                                        + '")')
+                    else:
+                        commands.append(param.name + ' = ' + 'readGDAL("' + value
+                                        + '")')
+            elif isinstance(param, ParameterVector):
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
+                else:
+                    value = param.getSafeExportedLayer()
+                    value = value.replace('\\', '/')
+                    filename = os.path.basename(value)
+                    filename = filename[:-4]
+                    folder = os.path.dirname(value)
+                    if self.passFileNames:
+                        commands.append(param.name + ' = "' + value + '"')
+                    else:
+                        commands.append(param.name + ' = readOGR("' + folder
+                                        + '",layer="' + filename + '")')
+            elif isinstance(param, ParameterTable):
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
                 else:
-                    commands.append(param.name + ' = readOGR("' + folder
-                                    + '",layer="' + filename + '")')
-            if isinstance(param, ParameterTable):
-                value = param.value
-                if not value.lower().endswith('csv'):
-                    raise GeoAlgorithmExecutionException(
-                        'Unsupported input file format.\n' + value)
-                if self.passFileNames:
-                    commands.append(param.name + ' = "' + value + '"')
+                    value = param.value
+                    if not value.lower().endswith('csv'):
+                        raise GeoAlgorithmExecutionException(
+                            'Unsupported input file format.\n' + value)
+                    if self.passFileNames:
+                        commands.append(param.name + ' = "' + value + '"')
+                    else:
+                        commands.append(param.name + ' <- read.csv("' + value
+                                        + '", head=TRUE, sep=",")')
+            elif isinstance(param, ParameterExtent):
+                if param.value:
+                    tokens = unicode(param.value).split(',')
+                    # Extent from raster package is "xmin, xmax, ymin, ymax" like in Processing
+                    # http://www.inside-r.org/packages/cran/raster/docs/Extent
+                    commands.append(param.name + ' = extent(' + tokens[0] + ',' + tokens[1] + ',' + tokens[2] + ',' + tokens[3] + ')')
                 else:
-                    commands.append(param.name + ' <- read.csv("' + value
-                                    + '", head=TRUE, sep=",")')
+                    commands.append(param.name + ' = NULL')
+            elif isinstance(param, ParameterCrs):
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
+                else:
+                    commands.append(param.name + ' = "' + param.value + '"')
             elif isinstance(param, (ParameterTableField, ParameterString,
                                     ParameterFile)):
-                commands.append(param.name + '="' + param.value + '"')
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
+                else:
+                    commands.append(param.name + '="' + param.value + '"')
             elif isinstance(param, (ParameterNumber, ParameterSelection)):
-                commands.append(param.name + '=' + unicode(param.value))
+                if param.value is None:
+                    commands.append(param.name + '= NULL')
+                else:
+                    commands.append(param.name + '=' + unicode(param.value))
             elif isinstance(param, ParameterBoolean):
                 if param.value:
                     commands.append(param.name + '=TRUE')
@@ -416,6 +494,36 @@ class RAlgorithm(GeoAlgorithm):
         else:
             return False, None
 
+    def shortHelp(self):
+        if self.descriptionFile is None:
+            return None
+        helpFile = unicode(self.descriptionFile) + '.help'
+        if os.path.exists(helpFile):
+            with open(helpFile) as f:
+                try:
+                    descriptions = json.load(f)
+                    if 'ALG_DESC' in descriptions:
+                        return self._formatHelp(unicode(descriptions['ALG_DESC']))
+                except:
+                    return None
+        return None
+
+    def getParameterDescriptions(self):
+        descs = {}
+        if self.descriptionFile is None:
+            return descs
+        helpFile = unicode(self.descriptionFile) + '.help'
+        if os.path.exists(helpFile):
+            with open(helpFile) as f:
+                try:
+                    descriptions = json.load(f)
+                    for param in self.parameters:
+                        if param.name in descriptions:
+                            descs[param.name] = unicode(descriptions[param.name])
+                except:
+                    return descs
+        return descs
+
     def checkBeforeOpeningParametersDialog(self):
         msg = RUtils.checkRIsInstalled()
         if msg is not None:
diff --git a/python/plugins/processing/core/Processing.py b/python/plugins/processing/core/Processing.py
index 426f37f..d889ff2 100644
--- a/python/plugins/processing/core/Processing.py
+++ b/python/plugins/processing/core/Processing.py
@@ -358,7 +358,7 @@ class Processing:
         if kwargs is not None and "progress" in kwargs.keys():
             progress = kwargs["progress"]
         elif iface is not None:
-            progress = MessageBarProgress()
+            progress = MessageBarProgress(alg.name)
 
         ret = runalg(alg, progress)
         if ret:
diff --git a/python/plugins/processing/gui/AlgorithmDialogBase.py b/python/plugins/processing/gui/AlgorithmDialogBase.py
index 3c93881..4dd1ad5 100644
--- a/python/plugins/processing/gui/AlgorithmDialogBase.py
+++ b/python/plugins/processing/gui/AlgorithmDialogBase.py
@@ -31,6 +31,7 @@ import webbrowser
 from PyQt4 import uic
 from PyQt4.QtCore import QCoreApplication, QSettings, QByteArray, SIGNAL, QUrl
 from PyQt4.QtGui import QApplication, QDialogButtonBox, QDesktopWidget
+from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply
 
 from qgis.utils import iface
 from qgis.core import *
@@ -64,9 +65,9 @@ class AlgorithmDialogBase(BASE, WIDGET):
 
         self.setWindowTitle(AlgorithmClassification.getDisplayName(self.alg))
 
-        desktop = QDesktopWidget()
-        if desktop.physicalDpiX() > 96:
-            self.textHelp.setZoomFactor(desktop.physicalDpiX() / 96)
+        #~ desktop = QDesktopWidget()
+        #~ if desktop.physicalDpiX() > 96:
+        #~     self.textHelp.setZoomFactor(desktop.physicalDpiX() / 96)
 
         algHelp = self.alg.shortHelp()
         if algHelp is None:
@@ -84,20 +85,22 @@ class AlgorithmDialogBase(BASE, WIDGET):
 
         def linkClicked(url):
             webbrowser.open(url.toString())
-        self.textShortHelp.connect(self.textShortHelp, SIGNAL("anchorClicked(const QUrl&)"), linkClicked)
 
-        self.textHelp.page().setNetworkAccessManager(QgsNetworkAccessManager.instance())
+        self.textShortHelp.anchorClicked.connect(linkClicked)
 
         isText, algHelp = self.alg.help()
         if algHelp is not None:
             algHelp = algHelp if isText else QUrl(algHelp)
             try:
                 if isText:
-                    self.textHelp.setHtml(algHelp)
+                    self.txtHelp.setHtml(algHelp)
                 else:
-                    self.textHelp.settings().clearMemoryCaches()
-                    self.textHelp.load(algHelp)
-            except:
+                    html = self.tr('<p>Downloading algorithm help... Please wait.</p>')
+                    self.txtHelp.setHtml(html)
+                    rq = QNetworkRequest(algHelp)
+                    self.reply = QgsNetworkAccessManager.instance().get(rq)
+                    self.reply.finished.connect(self.requestFinished)
+            except Exception, e:
                 self.tabWidget.removeTab(2)
         else:
             self.tabWidget.removeTab(2)
@@ -105,6 +108,16 @@ class AlgorithmDialogBase(BASE, WIDGET):
         self.showDebug = ProcessingConfig.getSetting(
             ProcessingConfig.SHOW_DEBUG_IN_DIALOG)
 
+    def requestFinished(self):
+        """Change the webview HTML content"""
+        reply = self.sender()
+        if reply.error() != QNetworkReply.NoError:
+            html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString()))
+        else:
+            html = unicode(reply.readAll())
+        reply.deleteLater()
+        self.txtHelp.setHtml(html)
+
     def closeEvent(self, evt):
         self.settings.setValue("/Processing/dialogBase", self.saveGeometry())
         super(AlgorithmDialogBase, self).closeEvent(evt)
diff --git a/python/plugins/processing/gui/GetScriptsAndModels.py b/python/plugins/processing/gui/GetScriptsAndModels.py
index d928f9d..76487e6 100644
--- a/python/plugins/processing/gui/GetScriptsAndModels.py
+++ b/python/plugins/processing/gui/GetScriptsAndModels.py
@@ -207,10 +207,10 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
         self.tree.addTopLevelItem(self.notinstalledItem)
         self.tree.addTopLevelItem(self.uptodateItem)
 
-        self.webView.setHtml(self.HELP_TEXT)
+        self.txtHelp.setHtml(self.HELP_TEXT)
 
     def setHelp(self, reply, item):
-        """Change the webview HTML content"""
+        """Change the HTML content"""
         QApplication.restoreOverrideCursor()
         if reply.error() != QNetworkReply.NoError:
             html = self.tr('<h2>No detailed description available for this script</h2>')
@@ -222,14 +222,14 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
             html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
             html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
         reply.deleteLater()
-        self.webView.setHtml(html)
+        self.txtHelp.setHtml(html)
 
     def currentItemChanged(self, item, prev):
         if isinstance(item, TreeItem):
             url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
             self.grabHTTP(url, self.setHelp, item)
         else:
-            self.webView.setHtml(self.HELP_TEXT)
+            self.txtHelp.setHtml(self.HELP_TEXT)
 
     def getTreeBranchForState(self, filename, version):
         if not os.path.exists(os.path.join(self.folder, filename)):
diff --git a/python/plugins/processing/gui/HelpEditionDialog.py b/python/plugins/processing/gui/HelpEditionDialog.py
index cfed621..61937fa 100644
--- a/python/plugins/processing/gui/HelpEditionDialog.py
+++ b/python/plugins/processing/gui/HelpEditionDialog.py
@@ -138,7 +138,7 @@ class HelpEditionDialog(BASE, WIDGET):
                 self.updateHtmlView()
 
     def updateHtmlView(self):
-        self.webView.setHtml(self.getHtml())
+        self.txtPreview.setHtml(self.getHtml())
 
     def getDescription(self, name):
         if name in self.descriptions:
diff --git a/python/plugins/processing/gui/ResultsDialog.py b/python/plugins/processing/gui/ResultsDialog.py
index 8793fd9..051eb43 100644
--- a/python/plugins/processing/gui/ResultsDialog.py
+++ b/python/plugins/processing/gui/ResultsDialog.py
@@ -26,6 +26,7 @@ __copyright__ = '(C) 2012, Victor Olaya'
 __revision__ = '$Format:%H$'
 
 import os
+import codecs
 
 from PyQt4 import uic
 from PyQt4.QtCore import QUrl
@@ -52,7 +53,7 @@ class ResultsDialog(BASE, WIDGET):
         self.fillTree()
 
         if self.lastUrl:
-            self.webView.load(self.lastUrl)
+            self.txtResults.setHtml(self.loadResults(self.lastUrl))
 
     def fillTree(self):
         elements = ProcessingResults.getResults()
@@ -63,13 +64,17 @@ class ResultsDialog(BASE, WIDGET):
             item = TreeResultItem(element)
             item.setIcon(0, self.keyIcon)
             self.tree.addTopLevelItem(item)
-        self.lastUrl = QUrl(elements[-1].filename)
+        self.lastUrl = elements[-1].filename
 
     def changeResult(self):
         item = self.tree.currentItem()
         if isinstance(item, TreeResultItem):
-            url = QUrl(item.filename)
-            self.webView.load(url)
+            self.txtResults.setHtml(self.loadResults(item.filename))
+
+    def loadResults(self, fileName):
+        with codecs.open(fileName, encoding='utf-8') as f:
+            content = f.read()
+        return content
 
 
 class TreeResultItem(QTreeWidgetItem):
diff --git a/python/plugins/processing/modeler/ModelerAlgorithm.py b/python/plugins/processing/modeler/ModelerAlgorithm.py
index 10a43dc..0872485 100644
--- a/python/plugins/processing/modeler/ModelerAlgorithm.py
+++ b/python/plugins/processing/modeler/ModelerAlgorithm.py
@@ -533,6 +533,19 @@ class ModelerAlgorithm(GeoAlgorithm):
         except:
             return False, None
 
+    def shortHelp(self):
+        if 'ALG_DESC' in self.helpContent:
+            return self._formatHelp(unicode(self.helpContent['ALG_DESC']))
+        return None
+
+    def getParameterDescriptions(self):
+        descs = {}
+        descriptions = self.helpContent
+        for param in self.parameters:
+            if param.name in descriptions:
+                descs[param.name] = unicode(descriptions[param.name])
+        return descs
+
     def todict(self):
         keys = ["inputs", "group", "name", "algs", "helpContent"]
         return {k: v for k, v in self.__dict__.iteritems() if k in keys}
diff --git a/python/plugins/processing/modeler/ModelerParametersDialog.py b/python/plugins/processing/modeler/ModelerParametersDialog.py
index 499ed58..72f8336 100644
--- a/python/plugins/processing/modeler/ModelerParametersDialog.py
+++ b/python/plugins/processing/modeler/ModelerParametersDialog.py
@@ -27,7 +27,9 @@ __revision__ = '$Format:%H$'
 
 from PyQt4.QtCore import Qt, QUrl, QMetaObject
 from PyQt4.QtGui import QDialog, QDialogButtonBox, QLabel, QLineEdit, QFrame, QPushButton, QSizePolicy, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget, QScrollArea, QComboBox, QTableWidgetItem, QMessageBox
-from PyQt4.QtWebKit import QWebView
+from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply
+
+from qgis.core import QgsNetworkAccessManager
 
 from processing.modeler.ModelerAlgorithm import ValueFromInput, \
     ValueFromOutput, Algorithm, ModelerOutput
@@ -166,27 +168,27 @@ class ModelerParametersDialog(QDialog):
         self.scrollArea.setWidget(self.paramPanel)
         self.scrollArea.setWidgetResizable(True)
         self.tabWidget.addTab(self.scrollArea, self.tr('Parameters'))
-        self.webView = QWebView()
+
+        self.txtHelp = QTextBrowser()
 
         html = None
         url = None
-        isText, help = self._alg.help()
-        if help is not None:
-            if isText:
-                html = help
-            else:
-                url = QUrl(help)
-        else:
-            html = self.tr('<h2>Sorry, no help is available for this '
-                           'algorithm.</h2>')
-        try:
-            if html:
-                self.webView.setHtml(html)
-            elif url:
-                self.webView.load(url)
-        except:
-            self.webView.setHtml(self.tr('<h2>Could not open help file :-( </h2>'))
-        self.tabWidget.addTab(self.webView, 'Help')
+        isText, algHelp = self._alg.help()
+        if algHelp is not None:
+            algHelp = algHelp if isText else QUrl(algHelp)
+            try:
+                if isText:
+                    self.txtHelp.setHtml(algHelp)
+                else:
+                    html = self.tr('<p>Downloading algorithm help... Please wait.</p>')
+                    self.txtHelp.setHtml(html)
+                    self.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp))
+                    self.reply.finished.connect(self.requestFinished)
+            except:
+                self.txtHelp.setHtml(self.tr('<h2>No help available for this algorithm</h2>'))
+
+        self.tabWidget.addTab(self.txtHelp, 'Help')
+
         self.verticalLayout2.addWidget(self.tabWidget)
         self.verticalLayout2.addWidget(self.buttonBox)
         self.setLayout(self.verticalLayout2)
@@ -205,6 +207,16 @@ class ModelerParametersDialog(QDialog):
                 opts.append(alg)
         return opts
 
+    def requestFinished(self):
+        """Change the webview HTML content"""
+        reply = self.sender()
+        if reply.error() != QNetworkReply.NoError:
+            html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString()))
+        else:
+            html = unicode(reply.readAll())
+        reply.deleteLater()
+        self.txtHelp.setHtml(html)
+
     def getDependenciesPanel(self):
         return MultipleInputPanel([alg.algorithm.name for alg in self.getAvailableDependencies()])
 
@@ -286,7 +298,7 @@ class ModelerParametersDialog(QDialog):
         elif isinstance(param, ParameterSelection):
             item = QComboBox()
             item.addItems(param.options)
-            item.setCurrentIndex(param.default or 1)
+            item.setCurrentIndex(param.default or 0)
         elif isinstance(param, ParameterFixedTable):
             item = FixedTablePanel(param)
         elif isinstance(param, ParameterRange):
diff --git a/python/plugins/processing/script/ScriptAlgorithm.py b/python/plugins/processing/script/ScriptAlgorithm.py
index 834f4a5..00922ef 100644
--- a/python/plugins/processing/script/ScriptAlgorithm.py
+++ b/python/plugins/processing/script/ScriptAlgorithm.py
@@ -27,6 +27,8 @@ __revision__ = '$Format:%H$'
 
 import os
 from PyQt4 import QtGui
+import re
+import json
 from processing.core.GeoAlgorithm import GeoAlgorithm
 from processing.gui.Help2Html import getHtmlFromHelpFile
 from processing.core.parameters import ParameterRaster
@@ -330,3 +332,33 @@ class ScriptAlgorithm(GeoAlgorithm):
             return True, getHtmlFromHelpFile(self, helpfile)
         else:
             return False, None
+
+    def shortHelp(self):
+        if self.descriptionFile is None:
+            return None
+        helpFile = unicode(self.descriptionFile) + '.help'
+        if os.path.exists(helpFile):
+            with open(helpFile) as f:
+                try:
+                    descriptions = json.load(f)
+                    if 'ALG_DESC' in descriptions:
+                        return self._formatHelp(unicode(descriptions['ALG_DESC']))
+                except:
+                    return None
+        return None
+
+    def getParameterDescriptions(self):
+        descs = {}
+        if self.descriptionFile is None:
+            return descs
+        helpFile = unicode(self.descriptionFile) + '.help'
+        if os.path.exists(helpFile):
+            with open(helpFile) as f:
+                try:
+                    descriptions = json.load(f)
+                    for param in self.parameters:
+                        if param.name in descriptions:
+                            descs[param.name] = unicode(descriptions[param.name])
+                except:
+                    return descs
+        return descs
diff --git a/python/plugins/processing/tools/dataobjects.py b/python/plugins/processing/tools/dataobjects.py
index 3e98422..b00c57b 100644
--- a/python/plugins/processing/tools/dataobjects.py
+++ b/python/plugins/processing/tools/dataobjects.py
@@ -271,7 +271,7 @@ def getObjectFromUri(uri, forceLoad=True):
         return None
 
 
-def exportVectorLayer(layer):
+def exportVectorLayer(layer, supported=None):
     """Takes a QgsVectorLayer and returns the filename to refer to it,
     which allows external apps which support only file-based layers to
     use it. It performs the necessary export in case the input layer
@@ -285,6 +285,7 @@ def exportVectorLayer(layer):
     a new file if the original one contains non-ascii characters.
     """
 
+    supported = supported or ["shp"]
     settings = QSettings()
     systemEncoding = settings.value('/UI/encoding', 'System')
 
@@ -317,7 +318,7 @@ def exportVectorLayer(layer):
             unicode(layer.source()).decode('ascii')
         except UnicodeEncodeError:
             isASCII = False
-        if not unicode(layer.source()).endswith('shp') or not isASCII:
+        if not os.path.splitext()[1] in supported or not isASCII:
             writer = QgsVectorFileWriter(
                 output, systemEncoding,
                 layer.pendingFields(), provider.geometryType(),
diff --git a/python/plugins/processing/tools/raster.py b/python/plugins/processing/tools/raster.py
index 4cfd3e9..6c71351 100644
--- a/python/plugins/processing/tools/raster.py
+++ b/python/plugins/processing/tools/raster.py
@@ -84,17 +84,18 @@ class RasterWriter:
     NODATA = -99999.0
 
     def __init__(self, fileName, minx, miny, maxx, maxy, cellsize,
-                 nbands, crs):
+                 nbands, crs, geotransform=None):
         self.fileName = fileName
         self.nx = int((maxx - minx) / float(cellsize))
         self.ny = int((maxy - miny) / float(cellsize))
         self.nbands = nbands
-        self.matrix = numpy.ones(shape=(self.ny, self.nx), dtype=numpy.float32)
-        self.matrix[:] = self.NODATA
+        self.matrix = numpy.empty(shape=(self.ny, self.nx), dtype=numpy.float32)
+        self.matrix.fill(self.NODATA)
         self.cellsize = cellsize
         self.crs = crs
         self.minx = minx
         self.maxy = maxy
+        self.geotransform = geotransform
 
     def setValue(self, value, x, y, band=0):
         try:
@@ -114,7 +115,10 @@ class RasterWriter:
         dst_ds = driver.Create(self.fileName, self.nx, self.ny, 1,
                                gdal.GDT_Float32)
         dst_ds.SetProjection(str(self.crs.toWkt()))
-        dst_ds.SetGeoTransform([self.minx, self.cellsize, 0,
-                                self.maxy, self.cellsize, 0])
+        if self.geotransform is None:
+            dst_ds.SetGeoTransform([self.minx, self.cellsize, 0,
+                                    self.maxy, self.cellsize, 0])
+        else:
+            dst_ds.SetGeoTransform(self.geotransform)
         dst_ds.GetRasterBand(1).WriteArray(self.matrix)
         dst_ds = None
diff --git a/python/plugins/processing/tools/vector.py b/python/plugins/processing/tools/vector.py
index a84e284..84e47d1 100644
--- a/python/plugins/processing/tools/vector.py
+++ b/python/plugins/processing/tools/vector.py
@@ -201,10 +201,7 @@ def testForUniqueness(fieldList1, fieldList2):
 def spatialindex(layer):
     """Creates a spatial index for the passed vector layer.
     """
-    idx = QgsSpatialIndex()
-    feats = features(layer)
-    for ft in feats:
-        idx.insertFeature(ft)
+    idx = QgsSpatialIndex(layer.getFeatures())
     return idx
 
 
diff --git a/python/plugins/processing/ui/DlgAlgorithmBase.ui b/python/plugins/processing/ui/DlgAlgorithmBase.ui
index e76058e..0e0a9bb 100644
--- a/python/plugins/processing/ui/DlgAlgorithmBase.ui
+++ b/python/plugins/processing/ui/DlgAlgorithmBase.ui
@@ -47,9 +47,6 @@
          </property>
          <item>
           <widget class="QTextEdit" name="txtLog">
-           <property name="frameShape">
-            <enum>QFrame::NoFrame</enum>
-           </property>
            <property name="readOnly">
             <bool>true</bool>
            </property>
@@ -69,13 +66,7 @@
           <number>0</number>
          </property>
          <item>
-          <widget class="QWebView" name="textHelp">
-           <property name="url">
-            <url>
-             <string>about:blank</string>
-            </url>
-           </property>
-          </widget>
+          <widget class="QTextBrowser" name="txtHelp"/>
          </item>
         </layout>
        </widget>
@@ -131,13 +122,6 @@
    </item>
   </layout>
  </widget>
- <customwidgets>
-  <customwidget>
-   <class>QWebView</class>
-   <extends>QWidget</extends>
-   <header>QtWebKit/QWebView</header>
-  </customwidget>
- </customwidgets>
  <resources/>
  <connections>
   <connection>
diff --git a/python/plugins/processing/ui/DlgGetScriptsAndModels.ui b/python/plugins/processing/ui/DlgGetScriptsAndModels.ui
index 735a0ce..62a0372 100644
--- a/python/plugins/processing/ui/DlgGetScriptsAndModels.ui
+++ b/python/plugins/processing/ui/DlgGetScriptsAndModels.ui
@@ -74,7 +74,7 @@
         <number>0</number>
        </property>
        <item>
-        <widget class="QWebView" name="webView">
+        <widget class="QTextEdit" name="txtHelp">
          <property name="maximumSize">
           <size>
            <width>10000</width>
diff --git a/python/plugins/processing/ui/DlgHelpEdition.ui b/python/plugins/processing/ui/DlgHelpEdition.ui
index 3f06653..c00fc13 100644
--- a/python/plugins/processing/ui/DlgHelpEdition.ui
+++ b/python/plugins/processing/ui/DlgHelpEdition.ui
@@ -21,17 +21,13 @@
     <number>9</number>
    </property>
    <item>
+    <widget class="QTextEdit" name="txtPreview"/>
+   </item>
+   <item>
     <widget class="QSplitter" name="splitter_2">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
-     <widget class="QWebView" name="webView">
-      <property name="url">
-       <url>
-        <string>about:blank</string>
-       </url>
-      </property>
-     </widget>
      <widget class="QSplitter" name="splitter">
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
@@ -110,13 +106,6 @@
    </item>
   </layout>
  </widget>
- <customwidgets>
-  <customwidget>
-   <class>QWebView</class>
-   <extends>QWidget</extends>
-   <header>QtWebKit/QWebView</header>
-  </customwidget>
- </customwidgets>
  <resources/>
  <connections>
   <connection>
diff --git a/python/plugins/processing/ui/DlgResults.ui b/python/plugins/processing/ui/DlgResults.ui
index aef1ad2..7772bc2 100644
--- a/python/plugins/processing/ui/DlgResults.ui
+++ b/python/plugins/processing/ui/DlgResults.ui
@@ -41,19 +41,7 @@
        </property>
       </column>
      </widget>
-     <widget class="QWebView" name="webView">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>0</height>
-       </size>
-      </property>
-      <property name="url">
-       <url>
-        <string>about:blank</string>
-       </url>
-      </property>
-     </widget>
+     <widget class="QTextEdit" name="txtResults"/>
     </widget>
    </item>
    <item>
@@ -68,13 +56,6 @@
    </item>
   </layout>
  </widget>
- <customwidgets>
-  <customwidget>
-   <class>QWebView</class>
-   <extends>QWidget</extends>
-   <header>QtWebKit/QWebView</header>
-  </customwidget>
- </customwidgets>
  <resources/>
  <connections>
   <connection>
diff --git a/src/analysis/network/qgsgraph.cpp b/src/analysis/network/qgsgraph.cpp
index c1784cf..007ea5e 100644
--- a/src/analysis/network/qgsgraph.cpp
+++ b/src/analysis/network/qgsgraph.cpp
@@ -52,7 +52,6 @@ const QgsGraphArc& QgsGraph::arc( int idx ) const
   return mGraphArc[ idx ];
 }
 
-
 int QgsGraph::vertexCount() const
 {
   return mGraphVertexes.size();
diff --git a/src/analysis/network/qgslinevectorlayerdirector.cpp b/src/analysis/network/qgslinevectorlayerdirector.cpp
index d4a0aa8..ed4c6f0 100644
--- a/src/analysis/network/qgslinevectorlayerdirector.cpp
+++ b/src/analysis/network/qgslinevectorlayerdirector.cpp
@@ -23,6 +23,7 @@
 #include <qgspoint.h>
 #include <qgsgeometry.h>
 #include <qgsdistancearea.h>
+#include <qgswkbtypes.h>
 
 // QT includes
 #include <QString>
@@ -162,9 +163,9 @@ void QgsLineVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, c
   while ( fit.nextFeature( feature ) )
   {
     QgsMultiPolyline mpl;
-    if ( feature.constGeometry()->wkbType() == QGis::WKBMultiLineString )
+    if ( QgsWKBTypes::flatType( feature.constGeometry()->geometry()->wkbType() ) == QgsWKBTypes::MultiLineString )
       mpl = feature.constGeometry()->asMultiPolyline();
-    else if ( feature.constGeometry()->wkbType() == QGis::WKBLineString )
+    else if ( QgsWKBTypes::flatType( feature.constGeometry()->geometry()->wkbType() ) == QgsWKBTypes::LineString )
       mpl.push_back( feature.constGeometry()->asPolyline() );
 
     QgsMultiPolyline::iterator mplIt;
@@ -296,9 +297,9 @@ void QgsLineVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, c
 
     // begin features segments and add arc to the Graph;
     QgsMultiPolyline mpl;
-    if ( feature.constGeometry()->wkbType() == QGis::WKBMultiLineString )
+    if ( QgsWKBTypes::flatType( feature.constGeometry()->geometry()->wkbType() ) == QgsWKBTypes::MultiLineString )
       mpl = feature.constGeometry()->asMultiPolyline();
-    else if ( feature.constGeometry()->wkbType() == QGis::WKBLineString )
+    else if ( QgsWKBTypes::flatType( feature.constGeometry()->geometry()->wkbType() ) == QgsWKBTypes::LineString )
       mpl.push_back( feature.constGeometry()->asPolyline() );
 
     QgsMultiPolyline::iterator mplIt;
diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp
index 53a8386..33c76be 100644
--- a/src/app/composer/qgscomposer.cpp
+++ b/src/app/composer/qgscomposer.cpp
@@ -1227,7 +1227,7 @@ void QgsComposer::on_mActionZoomAll_triggered()
 
 void QgsComposer::on_mActionZoomIn_triggered()
 {
-  mView->scale( 2, 2 );
+  mView->scaleSafe( 2 );
   mView->updateRulers();
   mView->update();
   emit zoomLevelChanged();
@@ -1235,7 +1235,7 @@ void QgsComposer::on_mActionZoomIn_triggered()
 
 void QgsComposer::on_mActionZoomOut_triggered()
 {
-  mView->scale( .5, .5 );
+  mView->scaleSafe( 0.5 );
   mView->updateRulers();
   mView->update();
   emit zoomLevelChanged();
diff --git a/src/app/composer/qgscomposerlegendwidget.cpp b/src/app/composer/qgscomposerlegendwidget.cpp
index 4aa9889..f3de5bb 100644
--- a/src/app/composer/qgscomposerlegendwidget.cpp
+++ b/src/app/composer/qgscomposerlegendwidget.cpp
@@ -728,62 +728,7 @@ void QgsComposerLegendWidget::on_mEditPushButton_clicked()
   }
 
   QModelIndex idx = mItemTreeView->selectionModel()->currentIndex();
-  QgsLayerTreeModel* model = mItemTreeView->layerTreeModel();
-  QgsLayerTreeNode* currentNode = model->index2node( idx );
-  QgsLayerTreeModelLegendNode* legendNode = model->index2legendNode( idx );
-  QString currentText;
-
-  if ( QgsLayerTree::isGroup( currentNode ) )
-  {
-    currentText = QgsLayerTree::toGroup( currentNode )->name();
-  }
-  else if ( QgsLayerTree::isLayer( currentNode ) )
-  {
-    currentText = QgsLayerTree::toLayer( currentNode )->layerName();
-    QVariant v = currentNode->customProperty( "legend/title-label" );
-    if ( !v.isNull() )
-      currentText = v.toString();
-  }
-  else if ( legendNode )
-  {
-    currentText = legendNode->data( Qt::EditRole ).toString();
-  }
-
-  bool ok;
-  QString newText = QInputDialog::getText( this, tr( "Legend item properties" ), tr( "Item text" ),
-                    QLineEdit::Normal, currentText, &ok );
-  if ( !ok || newText == currentText )
-    return;
-
-  mLegend->beginCommand( tr( "Legend item edited" ) );
-
-  if ( QgsLayerTree::isGroup( currentNode ) )
-  {
-    QgsLayerTree::toGroup( currentNode )->setName( newText );
-  }
-  else if ( QgsLayerTree::isLayer( currentNode ) )
-  {
-    currentNode->setCustomProperty( "legend/title-label", newText );
-
-    // force update of label of the legend node with embedded icon (a bit clumsy i know)
-    QList<QgsLayerTreeModelLegendNode*> nodes = model->layerLegendNodes( QgsLayerTree::toLayer( currentNode ) );
-    if ( nodes.count() == 1 && nodes[0]->isEmbeddedInParent() )
-      nodes[0]->setUserLabel( QString() );
-  }
-  else if ( legendNode )
-  {
-    QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( legendNode->layerNode() );
-    //find unfiltered row number
-    QList<QgsLayerTreeModelLegendNode*> layerLegendNodes = model->layerOriginalLegendNodes( legendNode->layerNode() );
-    int unfilteredRowIndex = layerLegendNodes.indexOf( legendNode );
-    int originalIndex = ( unfilteredRowIndex >= 0 && unfilteredRowIndex < order.count() ? order[unfilteredRowIndex] : -1 );
-    QgsMapLayerLegendUtils::setLegendNodeUserLabel( legendNode->layerNode(), originalIndex, newText );
-    model->refreshLayerLegend( legendNode->layerNode() );
-  }
-
-  mLegend->adjustBoxSize();
-  mLegend->updateItem();
-  mLegend->endCommand();
+  on_mItemTreeView_doubleClicked( idx );
 }
 
 void QgsComposerLegendWidget::resetLayerNodeToDefaults()
@@ -1056,6 +1001,71 @@ void QgsComposerLegendWidget::updateFilterLegendByAtlasButton()
   mFilterLegendByAtlasCheckBox->setEnabled( atlas.enabled() && atlas.coverageLayer() && atlas.coverageLayer()->geometryType() == QGis::Polygon );
 }
 
+void QgsComposerLegendWidget::on_mItemTreeView_doubleClicked( const QModelIndex &idx )
+{
+  if ( !mLegend || !idx.isValid() )
+  {
+    return;
+  }
+
+  QgsLayerTreeModel* model = mItemTreeView->layerTreeModel();
+  QgsLayerTreeNode* currentNode = model->index2node( idx );
+  QgsLayerTreeModelLegendNode* legendNode = model->index2legendNode( idx );
+  QString currentText;
+
+  if ( QgsLayerTree::isGroup( currentNode ) )
+  {
+    currentText = QgsLayerTree::toGroup( currentNode )->name();
+  }
+  else if ( QgsLayerTree::isLayer( currentNode ) )
+  {
+    currentText = QgsLayerTree::toLayer( currentNode )->layerName();
+    QVariant v = currentNode->customProperty( "legend/title-label" );
+    if ( !v.isNull() )
+      currentText = v.toString();
+  }
+  else if ( legendNode )
+  {
+    currentText = legendNode->data( Qt::EditRole ).toString();
+  }
+
+  bool ok;
+  QString newText = QInputDialog::getText( this, tr( "Legend item properties" ), tr( "Item text" ),
+                    QLineEdit::Normal, currentText, &ok );
+  if ( !ok || newText == currentText )
+    return;
+
+  mLegend->beginCommand( tr( "Legend item edited" ) );
+
+  if ( QgsLayerTree::isGroup( currentNode ) )
+  {
+    QgsLayerTree::toGroup( currentNode )->setName( newText );
+  }
+  else if ( QgsLayerTree::isLayer( currentNode ) )
+  {
+    currentNode->setCustomProperty( "legend/title-label", newText );
+
+    // force update of label of the legend node with embedded icon (a bit clumsy i know)
+    QList<QgsLayerTreeModelLegendNode*> nodes = model->layerLegendNodes( QgsLayerTree::toLayer( currentNode ) );
+    if ( nodes.count() == 1 && nodes[0]->isEmbeddedInParent() )
+      nodes[0]->setUserLabel( QString() );
+  }
+  else if ( legendNode )
+  {
+    QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( legendNode->layerNode() );
+    //find unfiltered row number
+    QList<QgsLayerTreeModelLegendNode*> layerLegendNodes = model->layerOriginalLegendNodes( legendNode->layerNode() );
+    int unfilteredRowIndex = layerLegendNodes.indexOf( legendNode );
+    int originalIndex = ( unfilteredRowIndex >= 0 && unfilteredRowIndex < order.count() ? order[unfilteredRowIndex] : -1 );
+    QgsMapLayerLegendUtils::setLegendNodeUserLabel( legendNode->layerNode(), originalIndex, newText );
+    model->refreshLayerLegend( legendNode->layerNode() );
+  }
+
+  mLegend->adjustBoxSize();
+  mLegend->updateItem();
+  mLegend->endCommand();
+}
+
 
 //
 // QgsComposerLegendMenuProvider
diff --git a/src/app/composer/qgscomposerlegendwidget.h b/src/app/composer/qgscomposerlegendwidget.h
index b1ecd46..738951e 100644
--- a/src/app/composer/qgscomposerlegendwidget.h
+++ b/src/app/composer/qgscomposerlegendwidget.h
@@ -102,6 +102,8 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
     /** Update the enabling state of the filter by atlas button */
     void updateFilterLegendByAtlasButton();
 
+    void on_mItemTreeView_doubleClicked( const QModelIndex &index );
+
   private:
     QgsComposerLegendWidget();
     void blockAllSignals( bool b );
diff --git a/src/app/gps/qgsgpsinformationwidget.cpp b/src/app/gps/qgsgpsinformationwidget.cpp
index 9669151..f81d8d3 100644
--- a/src/app/gps/qgsgpsinformationwidget.cpp
+++ b/src/app/gps/qgsgpsinformationwidget.cpp
@@ -817,19 +817,16 @@ void QgsGPSInformationWidget::on_mBtnCloseFeature_clicked()
   {
     QgsFeature* f = new QgsFeature( 0 );
 
-    int size = 0;
-    int wkbtype = 0;
-
     QgsCoordinateTransform t( mWgs84CRS, vlayer->crs() );
     QgsPoint myPoint = t.transform( mLastGpsPosition );
     double x = myPoint.x();
     double y = myPoint.y();
 
-    size = 1 + sizeof( int ) + 2 * sizeof( double );
+    int size = 1 + sizeof( int ) + 2 * sizeof( double );
     unsigned char *buf = new unsigned char[size];
 
     QgsWkbPtr wkbPtr( buf, size );
-    wkbPtr << ( char ) QgsApplication::endian() << wkbtype << x << y;
+    wkbPtr << ( char ) QgsApplication::endian() << QGis::WKBPoint << x << y;
 
     QgsGeometry *g = new QgsGeometry();
     g->fromWkb( buf, size );
diff --git a/src/app/nodetool/qgsmaptoolnodetool.cpp b/src/app/nodetool/qgsmaptoolnodetool.cpp
index 6c4fdac..b92df9d 100644
--- a/src/app/nodetool/qgsmaptoolnodetool.cpp
+++ b/src/app/nodetool/qgsmaptoolnodetool.cpp
@@ -762,6 +762,11 @@ int QgsMapToolNodeTool::insertSegmentVerticesForSnap( const QList<QgsSnappingRes
   QList<QgsSnappingResult>::const_iterator it = snapResults.constBegin();
   for ( ; it != snapResults.constEnd(); ++it )
   {
+    //skip if snappingResult is in a different layer
+    //See http://hub.qgis.org/issues/13952#note-29
+    if ( it->layer != editedLayer )
+      continue;
+
     //skip if id is in skip list or we have already added a vertex to a feature
     if ( skipFids.contains( it->snappedAtGeometry ) || addedFeatures.contains( it->snappedAtGeometry ) )
       continue;
diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp
index cb05993..8b71c47 100644
--- a/src/app/qgisapp.cpp
+++ b/src/app/qgisapp.cpp
@@ -1853,6 +1853,7 @@ void QgisApp::createToolBars()
   bt->addActions( selectActions );
   bt->setDefaultAction( mActionSelectByExpression );
   QAction* selectionAction = mAttributesToolBar->insertWidget( mActionDeselectAll, bt );
+  selectionAction->setObjectName( "ActionSelection" );
 
   // select tool button
 
@@ -2689,7 +2690,7 @@ void QgisApp::toggleLogMessageIcon( bool hasLogMessage )
 
 void QgisApp::openMessageLog()
 {
-  mMessageButton->toggle();
+  mMessageButton->setChecked( true );
 }
 
 void QgisApp::addUserInputWidget( QWidget *widget )
@@ -3329,6 +3330,7 @@ bool QgisApp::addVectorLayers( const QStringList &theLayerQStringList, const QSt
     bool ok;
     l->loadDefaultStyle( ok );
   }
+  activateDeactivateLayerRelatedActions( activeLayer() );
 
   // Only update the map if we frozen in this method
   // Let the caller do it otherwise
@@ -5538,110 +5540,118 @@ void QgisApp::saveAsRasterFile()
                                 mMapCanvas->extent(), rasterLayer->crs(),
                                 mMapCanvas->mapSettings().destinationCrs(),
                                 this );
-  if ( d.exec() == QDialog::Accepted )
-  {
-    QSettings settings;
-    settings.setValue( "/UI/lastRasterFileDir", QFileInfo( d.outputFileName() ).absolutePath() );
+  if ( d.exec() == QDialog::Rejected )
+    return;
 
-    QgsRasterFileWriter fileWriter( d.outputFileName() );
-    if ( d.tileMode() )
-    {
-      fileWriter.setTiledMode( true );
-      fileWriter.setMaxTileWidth( d.maximumTileSizeX() );
-      fileWriter.setMaxTileHeight( d.maximumTileSizeY() );
-    }
+  QSettings settings;
+  settings.setValue( "/UI/lastRasterFileDir", QFileInfo( d.outputFileName() ).absolutePath() );
 
-    QProgressDialog pd( nullptr, tr( "Abort..." ), 0, 0 );
-    // Show the dialo immediately because cloning pipe can take some time (WCS)
-    pd.setLabelText( tr( "Reading raster" ) );
-    pd.setWindowTitle( tr( "Saving raster" ) );
-    pd.show();
-    pd.setWindowModality( Qt::WindowModal );
+  QgsRasterFileWriter fileWriter( d.outputFileName() );
+  if ( d.tileMode() )
+  {
+    fileWriter.setTiledMode( true );
+    fileWriter.setMaxTileWidth( d.maximumTileSizeX() );
+    fileWriter.setMaxTileHeight( d.maximumTileSizeY() );
+  }
 
-    // TODO: show error dialogs
-    // TODO: this code should go somewhere else, but probably not into QgsRasterFileWriter
-    // clone pipe/provider is not really necessary, ready for threads
-    QScopedPointer<QgsRasterPipe> pipe( nullptr );
+  QProgressDialog pd( nullptr, tr( "Abort..." ), 0, 0 );
+  // Show the dialo immediately because cloning pipe can take some time (WCS)
+  pd.setLabelText( tr( "Reading raster" ) );
+  pd.setWindowTitle( tr( "Saving raster" ) );
+  pd.show();
+  pd.setWindowModality( Qt::WindowModal );
 
-    if ( d.mode() == QgsRasterLayerSaveAsDialog::RawDataMode )
-    {
-      QgsDebugMsg( "Writing raw data" );
-      //QgsDebugMsg( QString( "Writing raw data" ).arg( pos ) );
-      pipe.reset( new QgsRasterPipe() );
-      if ( !pipe->set( rasterLayer->dataProvider()->clone() ) )
-      {
-        QgsDebugMsg( "Cannot set pipe provider" );
-        return;
-      }
+  // TODO: show error dialogs
+  // TODO: this code should go somewhere else, but probably not into QgsRasterFileWriter
+  // clone pipe/provider is not really necessary, ready for threads
+  QScopedPointer<QgsRasterPipe> pipe( nullptr );
 
-      QgsRasterNuller *nuller = new QgsRasterNuller();
-      for ( int band = 1; band <= rasterLayer->dataProvider()->bandCount(); band ++ )
-      {
-        nuller->setNoData( band, d.noData() );
-      }
-      if ( !pipe->insert( 1, nuller ) )
-      {
-        QgsDebugMsg( "Cannot set pipe nuller" );
-        return;
-      }
+  if ( d.mode() == QgsRasterLayerSaveAsDialog::RawDataMode )
+  {
+    QgsDebugMsg( "Writing raw data" );
+    //QgsDebugMsg( QString( "Writing raw data" ).arg( pos ) );
+    pipe.reset( new QgsRasterPipe() );
+    if ( !pipe->set( rasterLayer->dataProvider()->clone() ) )
+    {
+      QgsDebugMsg( "Cannot set pipe provider" );
+      return;
+    }
 
-      // add projector if necessary
-      if ( d.outputCrs() != rasterLayer->crs() )
-      {
-        QgsRasterProjector * projector = new QgsRasterProjector;
-        projector->setCRS( rasterLayer->crs(), d.outputCrs() );
-        if ( !pipe->insert( 2, projector ) )
-        {
-          QgsDebugMsg( "Cannot set pipe projector" );
-          return;
-        }
-      }
+    QgsRasterNuller *nuller = new QgsRasterNuller();
+    for ( int band = 1; band <= rasterLayer->dataProvider()->bandCount(); band ++ )
+    {
+      nuller->setNoData( band, d.noData() );
+    }
+    if ( !pipe->insert( 1, nuller ) )
+    {
+      QgsDebugMsg( "Cannot set pipe nuller" );
+      return;
     }
-    else // RenderedImageMode
+
+    // add projector if necessary
+    if ( d.outputCrs() != rasterLayer->crs() )
     {
-      // clone the whole pipe
-      QgsDebugMsg( "Writing rendered image" );
-      pipe.reset( new QgsRasterPipe( *rasterLayer->pipe() ) );
-      QgsRasterProjector *projector = pipe->projector();
-      if ( !projector )
+      QgsRasterProjector * projector = new QgsRasterProjector;
+      projector->setCRS( rasterLayer->crs(), d.outputCrs() );
+      if ( !pipe->insert( 2, projector ) )
       {
-        QgsDebugMsg( "Cannot get pipe projector" );
+        QgsDebugMsg( "Cannot set pipe projector" );
         return;
       }
-      projector->setCRS( rasterLayer->crs(), d.outputCrs() );
     }
-
-    if ( !pipe->last() )
+  }
+  else // RenderedImageMode
+  {
+    // clone the whole pipe
+    QgsDebugMsg( "Writing rendered image" );
+    pipe.reset( new QgsRasterPipe( *rasterLayer->pipe() ) );
+    QgsRasterProjector *projector = pipe->projector();
+    if ( !projector )
     {
+      QgsDebugMsg( "Cannot get pipe projector" );
       return;
     }
-    fileWriter.setCreateOptions( d.createOptions() );
+    projector->setCRS( rasterLayer->crs(), d.outputCrs() );
+  }
+
+  if ( !pipe->last() )
+  {
+    return;
+  }
+  fileWriter.setCreateOptions( d.createOptions() );
 
-    fileWriter.setBuildPyramidsFlag( d.buildPyramidsFlag() );
-    fileWriter.setPyramidsList( d.pyramidsList() );
-    fileWriter.setPyramidsResampling( d.pyramidsResamplingMethod() );
-    fileWriter.setPyramidsFormat( d.pyramidsFormat() );
-    fileWriter.setPyramidsConfigOptions( d.pyramidsConfigOptions() );
+  fileWriter.setBuildPyramidsFlag( d.buildPyramidsFlag() );
+  fileWriter.setPyramidsList( d.pyramidsList() );
+  fileWriter.setPyramidsResampling( d.pyramidsResamplingMethod() );
+  fileWriter.setPyramidsFormat( d.pyramidsFormat() );
+  fileWriter.setPyramidsConfigOptions( d.pyramidsConfigOptions() );
 
-    QgsRasterFileWriter::WriterError err = fileWriter.writeRaster( pipe.data(), d.nColumns(), d.nRows(), d.outputRectangle(), d.outputCrs(), &pd );
-    if ( err != QgsRasterFileWriter::NoError )
-    {
-      QMessageBox::warning( this, tr( "Error" ),
-                            tr( "Cannot write raster error code: %1" ).arg( err ),
-                            QMessageBox::Ok );
+  QgsRasterFileWriter::WriterError err = fileWriter.writeRaster( pipe.data(), d.nColumns(), d.nRows(), d.outputRectangle(), d.outputCrs(), &pd );
+  if ( err != QgsRasterFileWriter::NoError )
+  {
+    QMessageBox::warning( this, tr( "Error" ),
+                          tr( "Cannot write raster error code: %1" ).arg( err ),
+                          QMessageBox::Ok );
 
+  }
+  else
+  {
+    QString fileName( d.outputFileName() );
+    if ( d.tileMode() )
+    {
+      QFileInfo outputInfo( fileName );
+      fileName = QString( "%1/%2.vrt" ).arg( fileName, outputInfo.fileName() );
     }
-    else
+
+    if ( d.addToCanvas() )
     {
-      if ( d.addToCanvas() )
-      {
-        addRasterLayers( QStringList( d.outputFileName() ) );
-      }
-      emit layerSavedAs( rasterLayer, d.outputFileName() );
-      messageBar()->pushMessage( tr( "Saving done" ),
-                                 tr( "Export to raster file has been completed" ),
-                                 QgsMessageBar::INFO, messageTimeout() );
+      addRasterLayers( QStringList( fileName ) );
     }
+
+    emit layerSavedAs( rasterLayer, fileName );
+    messageBar()->pushMessage( tr( "Saving done" ),
+                               tr( "Export to raster file has been completed" ),
+                               QgsMessageBar::INFO, messageTimeout() );
   }
 }
 
@@ -7172,6 +7182,13 @@ void QgisApp::pasteStyle( QgsMapLayer * destinationLayer )
         return;
       }
 
+      bool isVectorStyle = doc.elementsByTagName( "pipe" ).isEmpty();
+      if (( selectionLayer->type() == QgsMapLayer::RasterLayer && isVectorStyle ) ||
+          ( selectionLayer->type() == QgsMapLayer::VectorLayer && !isVectorStyle ) )
+      {
+        return;
+      }
+
       if ( !selectionLayer->importNamedStyle( doc, errorMsg ) )
       {
         messageBar()->pushMessage( tr( "Cannot paste style" ),
@@ -8545,7 +8562,7 @@ void QgisApp::apiDocumentation()
 
 void QgisApp::reportaBug()
 {
-  openURL( tr( "https://qgis.org/en/site/getinvolved/development/index.html#bugs-features-and-issues" ), false );
+  openURL( tr( "https://qgis.org/en/site/getinvolved/development/bugreporting.html" ), false );
 }
 void QgisApp::supportProviders()
 {
diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h
index f45e412..26666bb 100644
--- a/src/app/qgisapp.h
+++ b/src/app/qgisapp.h
@@ -1735,6 +1735,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
     bool gestureEvent( QGestureEvent *event );
     void tapAndHoldTriggered( QTapAndHoldGesture *gesture );
 #endif
+
+    friend class TestQgisAppPython;
 };
 
 #ifdef ANDROID
diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp
index f556b9b..4faa702 100644
--- a/src/app/qgsapplayertreeviewmenuprovider.cpp
+++ b/src/app/qgsapplayertreeviewmenuprovider.cpp
@@ -129,7 +129,11 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
 
         if ( vlayer )
         {
-          QgsSingleSymbolRendererV2* singleRenderer = dynamic_cast< QgsSingleSymbolRendererV2* >( vlayer->rendererV2() );
+          const QgsSingleSymbolRendererV2* singleRenderer = dynamic_cast< const QgsSingleSymbolRendererV2* >( vlayer->rendererV2() );
+          if ( !singleRenderer && vlayer->rendererV2() && vlayer->rendererV2()->embeddedRenderer() )
+          {
+            singleRenderer = dynamic_cast< const QgsSingleSymbolRendererV2* >( vlayer->rendererV2()->embeddedRenderer() );
+          }
           if ( singleRenderer && singleRenderer->symbol() )
           {
             //single symbol renderer, so add set color/edit symbol actions
@@ -260,44 +264,44 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
         menu->addAction( QgsApplication::getThemeIcon( "/mActionHideAllLayers.png" ), tr( "&Hide All Items" ),
                          symbolNode, SLOT( uncheckAllItems() ) );
         menu->addSeparator();
+      }
 
-        if ( symbolNode->symbol() )
+      if ( symbolNode->symbol() )
+      {
+        QgsColorWheel* colorWheel = new QgsColorWheel( menu );
+        colorWheel->setColor( symbolNode->symbol()->color() );
+        QgsColorWidgetAction* colorAction = new QgsColorWidgetAction( colorWheel, menu, menu );
+        colorAction->setDismissOnColorSelection( false );
+        connect( colorAction, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setSymbolLegendNodeColor( const QColor& ) ) );
+        //store the layer id and rule key in action, so we can later retrieve the corresponding
+        //legend node, if it still exists
+        colorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
+        colorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
+        menu->addAction( colorAction );
+
+        //add recent colors action
+        QList<QgsRecentColorScheme *> recentSchemes;
+        QgsColorSchemeRegistry::instance()->schemes( recentSchemes );
+        if ( !recentSchemes.isEmpty() )
         {
-          QgsColorWheel* colorWheel = new QgsColorWheel( menu );
-          colorWheel->setColor( symbolNode->symbol()->color() );
-          QgsColorWidgetAction* colorAction = new QgsColorWidgetAction( colorWheel, menu, menu );
-          colorAction->setDismissOnColorSelection( false );
-          connect( colorAction, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setSymbolLegendNodeColor( const QColor& ) ) );
-          //store the layer id and rule key in action, so we can later retrieve the corresponding
-          //legend node, if it still exists
-          colorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
-          colorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
-          menu->addAction( colorAction );
-
-          //add recent colors action
-          QList<QgsRecentColorScheme *> recentSchemes;
-          QgsColorSchemeRegistry::instance()->schemes( recentSchemes );
-          if ( !recentSchemes.isEmpty() )
-          {
-            QgsColorSwatchGridAction* recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menu, "symbology", menu );
-            recentColorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
-            recentColorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
-            recentColorAction->setDismissOnColorSelection( false );
-            menu->addAction( recentColorAction );
-            connect( recentColorAction, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setSymbolLegendNodeColor( const QColor& ) ) );
-          }
-
-          menu->addSeparator();
+          QgsColorSwatchGridAction* recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menu, "symbology", menu );
+          recentColorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
+          recentColorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
+          recentColorAction->setDismissOnColorSelection( false );
+          menu->addAction( recentColorAction );
+          connect( recentColorAction, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setSymbolLegendNodeColor( const QColor& ) ) );
         }
 
-        QAction* editSymbolAction = new QAction( tr( "Edit Symbol..." ), menu );
-        //store the layer id and rule key in action, so we can later retrieve the corresponding
-        //legend node, if it still exists
-        editSymbolAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
-        editSymbolAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
-        connect( editSymbolAction, SIGNAL( triggered() ), this, SLOT( editSymbolLegendNodeSymbol() ) );
-        menu->addAction( editSymbolAction );
+        menu->addSeparator();
       }
+
+      QAction* editSymbolAction = new QAction( tr( "Edit Symbol..." ), menu );
+      //store the layer id and rule key in action, so we can later retrieve the corresponding
+      //legend node, if it still exists
+      editSymbolAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
+      editSymbolAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
+      connect( editSymbolAction, SIGNAL( triggered() ), this, SLOT( editSymbolLegendNodeSymbol() ) );
+      menu->addAction( editSymbolAction );
     }
   }
 
@@ -488,12 +492,34 @@ void QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor( const QColor& color
     return;
 
   QgsSingleSymbolRendererV2* singleRenderer = dynamic_cast< QgsSingleSymbolRendererV2* >( layer->rendererV2() );
-  if ( !singleRenderer || !singleRenderer->symbol() )
+  QgsSymbolV2* newSymbol = nullptr;
+
+  if ( singleRenderer && singleRenderer->symbol() )
+    newSymbol = singleRenderer->symbol()->clone();
+
+  const QgsSingleSymbolRendererV2* embeddedRenderer = nullptr;
+  if ( !newSymbol && layer->rendererV2()->embeddedRenderer() )
+  {
+    embeddedRenderer = dynamic_cast< const QgsSingleSymbolRendererV2* >( layer->rendererV2()->embeddedRenderer() );
+    if ( embeddedRenderer && embeddedRenderer->symbol() )
+      newSymbol = embeddedRenderer->symbol()->clone();
+  }
+
+  if ( !newSymbol )
     return;
 
-  QgsSymbolV2* newSymbol = singleRenderer->symbol()->clone();
   newSymbol->setColor( color );
-  singleRenderer->setSymbol( newSymbol );
+  if ( singleRenderer )
+  {
+    singleRenderer->setSymbol( newSymbol );
+  }
+  else if ( embeddedRenderer )
+  {
+    QgsSingleSymbolRendererV2* newRenderer = embeddedRenderer->clone();
+    newRenderer->setSymbol( newSymbol );
+    layer->rendererV2()->setEmbeddedRenderer( newRenderer );
+  }
+
   layer->triggerRepaint();
   mView->refreshLayerSymbology( layer->id() );
 }
diff --git a/src/app/qgsattributetabledialog.cpp b/src/app/qgsattributetabledialog.cpp
index 63bad96..fd69f58 100644
--- a/src/app/qgsattributetabledialog.cpp
+++ b/src/app/qgsattributetabledialog.cpp
@@ -155,7 +155,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
   bool myDockFlag = settings.value( "/qgis/dockAttributeTable", false ).toBool();
   if ( myDockFlag )
   {
-    mDock = new QgsAttributeTableDock( tr( "Attribute table - %1 (%n Feature(s))", "feature count", mMainView->featureCount() ).arg( mLayer->name() ), QgisApp::instance() );
+    mDock = new QgsAttributeTableDock( tr( "%1 (%n Feature(s))", "feature count", mMainView->featureCount() ).arg( mLayer->name() ), QgisApp::instance() );
     mDock->setAllowedAreas( Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea );
     mDock->setWidget( this );
     connect( this, SIGNAL( destroyed() ), mDock, SLOT( close() ) );
@@ -252,7 +252,7 @@ QgsAttributeTableDialog::~QgsAttributeTableDialog()
 void QgsAttributeTableDialog::updateTitle()
 {
   QWidget *w = mDock ? qobject_cast<QWidget*>( mDock ) : qobject_cast<QWidget*>( this );
-  w->setWindowTitle( tr( "Attribute table - %1 :: Features total: %2, filtered: %3, selected: %4%5" )
+  w->setWindowTitle( tr( " %1 :: Features total: %2, filtered: %3, selected: %4%5" )
                      .arg( mLayer->name() )
                      .arg( mMainView->featureCount() )
                      .arg( mMainView->filteredFeatureCount() )
diff --git a/src/app/qgscustomization.cpp b/src/app/qgscustomization.cpp
index f51dc14..0f9c5c7 100644
--- a/src/app/qgscustomization.cpp
+++ b/src/app/qgscustomization.cpp
@@ -388,10 +388,11 @@ bool QgsCustomizationDialog::switchWidget( QWidget *widget, QMouseEvent *e )
   QgsDebugMsg( "Entered" );
   if ( !actionCatch->isChecked() )
     return false;
+
   QString path = widgetPath( widget );
   QgsDebugMsg( "path = " + path );
 
-  if ( path.startsWith( "/QgsCustomizationDialogBase" ) )
+  if ( path.contains( "/QgsCustomizationDialogBase" ) )
   {
     // do not allow modification of this dialog
     return false;
diff --git a/src/app/qgsjoindialog.cpp b/src/app/qgsjoindialog.cpp
index 4059f46..80e7984 100644
--- a/src/app/qgsjoindialog.cpp
+++ b/src/app/qgsjoindialog.cpp
@@ -24,6 +24,7 @@
 #include "qgsfieldcombobox.h"
 
 #include <QStandardItemModel>
+#include <QPushButton>
 
 QgsJoinDialog::QgsJoinDialog( QgsVectorLayer* layer, QList<QgsMapLayer*> alreadyJoinedLayers, QWidget * parent, Qt::WindowFlags f )
     : QDialog( parent, f )
@@ -53,6 +54,12 @@ QgsJoinDialog::QgsJoinDialog( QgsVectorLayer* layer, QList<QgsMapLayer*> already
     mJoinFieldComboBox->setLayer( joinLayer );
     joinedLayerChanged( joinLayer );
   }
+
+  connect( mJoinLayerComboBox, SIGNAL( layerChanged( QgsMapLayer* ) ), this, SLOT( checkDefinitionValid() ) );
+  connect( mJoinFieldComboBox, SIGNAL( fieldChanged( QString ) ), this, SLOT( checkDefinitionValid() ) );
+  connect( mTargetFieldComboBox, SIGNAL( fieldChanged( QString ) ), this, SLOT( checkDefinitionValid() ) );
+
+  checkDefinitionValid();
 }
 
 QgsJoinDialog::~QgsJoinDialog()
@@ -98,7 +105,8 @@ void QgsJoinDialog::setJoinInfo( const QgsVectorJoinInfo& joinInfo )
 QgsVectorJoinInfo QgsJoinDialog::joinInfo() const
 {
   QgsVectorJoinInfo info;
-  info.joinLayerId = mJoinLayerComboBox->currentLayer()->id();
+  if ( mJoinLayerComboBox->currentLayer() )
+    info.joinLayerId = mJoinLayerComboBox->currentLayer()->id();
   info.joinFieldName = mJoinFieldComboBox->currentField();
   info.targetFieldName = mTargetFieldComboBox->currentField();
   info.memoryCache = mCacheInMemoryCheckBox->isChecked();
@@ -173,3 +181,10 @@ void QgsJoinDialog::joinedLayerChanged( QgsMapLayer* layer )
     mCustomPrefix->setText( layer->name() + '_' );
   }
 }
+
+void QgsJoinDialog::checkDefinitionValid()
+{
+  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( mJoinLayerComboBox->currentIndex() != -1
+      && mJoinFieldComboBox->currentIndex() != -1
+      && mTargetFieldComboBox->currentIndex() != -1 );
+}
diff --git a/src/app/qgsjoindialog.h b/src/app/qgsjoindialog.h
index 71f800d..3a48bb0 100644
--- a/src/app/qgsjoindialog.h
+++ b/src/app/qgsjoindialog.h
@@ -42,6 +42,8 @@ class APP_EXPORT QgsJoinDialog: public QDialog, private Ui::QgsJoinDialogBase
   private slots:
     void joinedLayerChanged( QgsMapLayer* layer );
 
+    void checkDefinitionValid();
+
   private:
     /** Target layer*/
     QgsVectorLayer* mLayer;
diff --git a/src/app/qgslabelingwidget.cpp b/src/app/qgslabelingwidget.cpp
index 15f2067..7d879d9 100644
--- a/src/app/qgslabelingwidget.cpp
+++ b/src/app/qgslabelingwidget.cpp
@@ -41,6 +41,10 @@ void QgsLabelingWidget::adaptToLayer()
 {
   mLabelModeComboBox->setCurrentIndex( -1 );
 
+  // Delete the widget, so that labelModeChanged() recreates it with
+  // settings loaded from the layer
+  deleteWidget();
+
   // pick the right mode of the layer
   if ( mLayer->labeling() && mLayer->labeling()->type() == "rule-based" )
   {
@@ -107,11 +111,7 @@ void QgsLabelingWidget::labelModeChanged( int index )
 
   // in general case we need to recreate the widget
 
-  if ( mWidget )
-    mStackedWidget->removeWidget( mWidget );
-
-  delete mWidget;
-  mWidget = nullptr;
+  deleteWidget();
 
   if ( index == 2 )
   {
@@ -139,3 +139,12 @@ void QgsLabelingWidget::showEngineConfigDialog()
   QgsLabelEngineConfigDialog dlg( this );
   dlg.exec();
 }
+
+void QgsLabelingWidget::deleteWidget()
+{
+  if ( mWidget )
+    mStackedWidget->removeWidget( mWidget );
+
+  delete mWidget;
+  mWidget = nullptr;
+}
diff --git a/src/app/qgslabelingwidget.h b/src/app/qgslabelingwidget.h
index c72f475..a432370 100644
--- a/src/app/qgslabelingwidget.h
+++ b/src/app/qgslabelingwidget.h
@@ -38,6 +38,9 @@ class QgsLabelingWidget : public QWidget, private Ui::QgsLabelingWidget
     QgsMapCanvas* mCanvas;
 
     QWidget* mWidget;
+
+    //! Delete the child widget
+    void deleteWidget();
 };
 
 #endif // QGSLABELINGWIDGET_H
diff --git a/src/app/qgsmaptoolmeasureangle.cpp b/src/app/qgsmaptoolmeasureangle.cpp
index 4815ecf..50243e9 100644
--- a/src/app/qgsmaptoolmeasureangle.cpp
+++ b/src/app/qgsmaptoolmeasureangle.cpp
@@ -93,7 +93,8 @@ void QgsMapToolMeasureAngle::canvasReleaseEvent( QgsMapMouseEvent* e )
   {
     if ( !mResultDisplay )
     {
-      mResultDisplay = new QgsDisplayAngle( this, Qt::WindowStaysOnTopHint );
+      mResultDisplay = new QgsDisplayAngle( this );
+      mResultDisplay->setWindowFlags( mResultDisplay->windowFlags() | Qt::Tool );
       QObject::connect( mResultDisplay, SIGNAL( rejected() ), this, SLOT( stopMeasuring() ) );
     }
     configureDistanceArea();
diff --git a/src/app/qgsmaptoolselectradius.cpp b/src/app/qgsmaptoolselectradius.cpp
index b0b0a29..e0431ce 100644
--- a/src/app/qgsmaptoolselectradius.cpp
+++ b/src/app/qgsmaptoolselectradius.cpp
@@ -109,6 +109,7 @@ void QgsMapToolSelectRadius::setRadiusRubberBand( QgsPoint & radiusEdge )
     double theta = i * ( 2.0 * M_PI / RADIUS_SEGMENTS );
     QgsPoint radiusPoint( mRadiusCenter.x() + r * cos( theta ),
                           mRadiusCenter.y() + r * sin( theta ) );
-    mRubberBand->addPoint( radiusPoint );
+    mRubberBand->addPoint( radiusPoint, false );
   }
+  mRubberBand->closePoints( true );
 }
diff --git a/src/app/qgsmaptoolselectutils.cpp b/src/app/qgsmaptoolselectutils.cpp
index 25924ec..0325b52 100644
--- a/src/app/qgsmaptoolselectutils.cpp
+++ b/src/app/qgsmaptoolselectutils.cpp
@@ -102,20 +102,51 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
   // the rubber band.
   // For example, if you project a world map onto a globe using EPSG 2163
   // and then click somewhere off the globe, an exception will be thrown.
-  QgsGeometry selectGeomTrans( *selectGeometry );
+  QScopedPointer<QgsGeometry> selectGeomTrans( new QgsGeometry( *selectGeometry ) );
 
   if ( canvas->mapSettings().hasCrsTransformEnabled() )
   {
     try
     {
       QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() );
-      selectGeomTrans.transform( ct );
+
+      if ( !ct.isShortCircuited() && selectGeomTrans->type() == QGis::Polygon )
+      {
+        // convert add more points to the edges of the rectangle
+        // improve transformation result
+        QgsPolygon poly( selectGeomTrans->asPolygon() );
+        if ( poly.size() == 1 && poly.at( 0 ).size() == 5 )
+        {
+          const QgsPolyline &ringIn = poly.at( 0 );
+
+          QgsPolygon newpoly( 1 );
+          newpoly[0].resize( 41 );
+          QgsPolyline &ringOut = newpoly[0];
+
+          ringOut[ 0 ] = ringIn.at( 0 );
+
+          int i = 1;
+          for ( int j = 1; j < 5; j++ )
+          {
+            QgsVector v(( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 );
+            for ( int k = 0; k < 9; k++ )
+            {
+              ringOut[ i ] = ringOut[ i - 1 ] + v;
+              i++;
+            }
+            ringOut[ i++ ] = ringIn.at( j );
+          }
+          selectGeomTrans.reset( QgsGeometry::fromPolygon( newpoly ) );
+        }
+      }
+
+      selectGeomTrans->transform( ct );
     }
     catch ( QgsCsException &cse )
     {
       Q_UNUSED( cse );
       // catch exception for 'invalid' point and leave existing selection unchanged
-      QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) );
+      QgsDebugMsg( "Caught CRS exception " );
       QgisApp::instance()->messageBar()->pushMessage(
         QObject::tr( "CRS Exception" ),
         QObject::tr( "Selection extends beyond layer's coordinate system" ),
@@ -128,7 +159,7 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
   QApplication::setOverrideCursor( Qt::WaitCursor );
 
   QgsDebugMsg( "Selection layer: " + vlayer->name() );
-  QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() );
+  QgsDebugMsg( "Selection polygon: " + selectGeomTrans->exportToWkt() );
   QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) );
   QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) );
 
@@ -139,7 +170,7 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
     r->startRender( context, vlayer->fields() );
 
   QgsFeatureRequest request;
-  request.setFilterRect( selectGeomTrans.boundingBox() );
+  request.setFilterRect( selectGeomTrans->boundingBox() );
   request.setFlags( QgsFeatureRequest::ExactIntersect );
   if ( r )
     request.setSubsetOfAttributes( r->usedAttributes(), vlayer->fields() );
@@ -163,18 +194,18 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
     const QgsGeometry* g = f.constGeometry();
     if ( doContains )
     {
-      if ( !selectGeomTrans.contains( g ) )
+      if ( !selectGeomTrans->contains( g ) )
         continue;
     }
     else
     {
-      if ( !selectGeomTrans.intersects( g ) )
+      if ( !selectGeomTrans->intersects( g ) )
         continue;
     }
     if ( singleSelect )
     {
       foundSingleFeature = true;
-      double distance = g->distance( selectGeomTrans );
+      double distance = g->distance( *selectGeomTrans );
       if ( distance <= closestFeatureDist )
       {
         closestFeatureDist = distance;
diff --git a/src/app/qgsmeasuretool.cpp b/src/app/qgsmeasuretool.cpp
index f47e6ca..a3bd9b8 100644
--- a/src/app/qgsmeasuretool.cpp
+++ b/src/app/qgsmeasuretool.cpp
@@ -47,7 +47,8 @@ QgsMeasureTool::QgsMeasureTool( QgsMapCanvas* canvas, bool measureArea )
   // Append point we will move
   mPoints.append( QgsPoint( 0, 0 ) );
 
-  mDialog = new QgsMeasureDialog( this, Qt::WindowStaysOnTopHint );
+  mDialog = new QgsMeasureDialog( this );
+  mDialog->setWindowFlags( mDialog->windowFlags() | Qt::Tool );
   mDialog->restorePosition();
 
   connect( canvas, SIGNAL( destinationCrsChanged() ),
diff --git a/src/app/qgsnewspatialitelayerdialog.cpp b/src/app/qgsnewspatialitelayerdialog.cpp
index 24e65e1..24f8fdf 100644
--- a/src/app/qgsnewspatialitelayerdialog.cpp
+++ b/src/app/qgsnewspatialitelayerdialog.cpp
@@ -84,7 +84,7 @@ QgsNewSpatialiteLayerDialog::QgsNewSpatialiteLayerDialog( QWidget *parent, Qt::W
 
   connect( mNameEdit, SIGNAL( textChanged( QString ) ), this, SLOT( nameChanged( QString ) ) );
   connect( mAttributeView, SIGNAL( itemSelectionChanged() ), this, SLOT( selectionChanged() ) );
-  connect( leLayerName, SIGNAL( textChanged( const QString& text ) ), this, SLOT( checkOk() ) );
+  connect( leLayerName, SIGNAL( textChanged( QString ) ), this, SLOT( checkOk() ) );
   connect( checkBoxPrimaryKey, SIGNAL( clicked() ), this, SLOT( checkOk() ) );
 
   mAddAttributeButton->setEnabled( false );
diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp
index 678a32e..4afba0b 100644
--- a/src/app/qgsoptions.cpp
+++ b/src/app/qgsoptions.cpp
@@ -232,29 +232,25 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
   }
 
   //local directories to search when looking for an SVG with a given basename
-  myPaths = mSettings->value( "svg/searchPathsForSVG", QDir::homePath() ).toString();
-  if ( !myPaths.isEmpty() )
+  QStringList svgPaths = QgsApplication::svgPaths();
+  if ( !svgPaths.isEmpty() )
   {
-    QStringList myPathList = myPaths.split( '|' );
-    QStringList::const_iterator pathIt = myPathList.constBegin();
-    for ( ; pathIt != myPathList.constEnd(); ++pathIt )
+    Q_FOREACH ( const QString& path, svgPaths )
     {
       QListWidgetItem* newItem = new QListWidgetItem( mListSVGPaths );
-      newItem->setText( *pathIt );
+      newItem->setText( path );
       newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable );
       mListSVGPaths->addItem( newItem );
     }
   }
 
-  myPaths = mSettings->value( "composer/searchPathsForTemplates", "" ).toString();
-  if ( !myPaths.isEmpty() )
+  QStringList templatePaths = QgsApplication::composerTemplatePaths();
+  if ( !templatePaths.isEmpty() )
   {
-    QStringList myPathList = myPaths.split( '|' );
-    QStringList::const_iterator pathIt = myPathList.constBegin();
-    for ( ; pathIt != myPathList.constEnd(); ++pathIt )
+    Q_FOREACH ( const QString& path, templatePaths )
     {
       QListWidgetItem* newItem = new QListWidgetItem( mListComposerTemplatePaths );
-      newItem->setText( *pathIt );
+      newItem->setText( path );
       newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable );
       mListComposerTemplatePaths->addItem( newItem );
     }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ac5b91c..bbbdf8d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -253,6 +253,7 @@ SET(QGIS_CORE_SRCS
   composer/qgscomposerutils.cpp
   composer/qgscomposition.cpp
   composer/qgsdoubleboxscalebarstyle.cpp
+  composer/qgsgroupungroupitemscommand.cpp
   composer/qgslegendmodel.cpp
   composer/qgsnumericscalebarstyle.cpp
   composer/qgspaperitem.cpp
@@ -342,7 +343,9 @@ SET(QGIS_CORE_SRCS
 )
 
 FILE(GLOB JSON_HELP_FILES "${CMAKE_SOURCE_DIR}/resources/function_help/json/*")
-STRING(REPLACE "$" "$$" JSON_HELP_FILES "${JSON_HELP_FILES}")
+IF(NOT USING_NINJA)
+  STRING(REPLACE "$" "$$" JSON_HELP_FILES "${JSON_HELP_FILES}")
+ENDIF(NOT USING_NINJA)
 STRING(REPLACE "\(" "\\(" JSON_HELP_FILES "${JSON_HELP_FILES}")
 STRING(REPLACE "\)" "\\)" JSON_HELP_FILES "${JSON_HELP_FILES}")
 ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/qgsexpression_texts.cpp
@@ -508,6 +511,7 @@ SET(QGIS_CORE_MOC_HDRS
   composer/qgscomposertablev2.h
   composer/qgscomposertexttable.h
   composer/qgscomposition.h
+  composer/qgsgroupungroupitemscommand.h
   composer/qgslegendmodel.h
   composer/qgspaperitem.h
 
diff --git a/src/core/composer/qgsaddremoveitemcommand.cpp b/src/core/composer/qgsaddremoveitemcommand.cpp
index 10f60f9..a8714d0 100644
--- a/src/core/composer/qgsaddremoveitemcommand.cpp
+++ b/src/core/composer/qgsaddremoveitemcommand.cpp
@@ -36,6 +36,7 @@ QgsAddRemoveItemCommand::~QgsAddRemoveItemCommand()
 
 void QgsAddRemoveItemCommand::redo()
 {
+  QUndoCommand::redo(); // call redo() on all childs
   if ( mFirstRun )
   {
     mFirstRun = false;
@@ -46,6 +47,7 @@ void QgsAddRemoveItemCommand::redo()
 
 void QgsAddRemoveItemCommand::undo()
 {
+  QUndoCommand::undo(); // call undo() on all childs, in reverse order
   if ( mFirstRun )
   {
     mFirstRun = false;
@@ -58,6 +60,7 @@ void QgsAddRemoveItemCommand::switchState()
 {
   if ( mState == Added )
   {
+    // Remove
     if ( mComposition )
     {
       mComposition->itemsModel()->setItemRemoved( mItem );
@@ -68,6 +71,7 @@ void QgsAddRemoveItemCommand::switchState()
   }
   else //Removed
   {
+    // Add
     if ( mComposition )
     {
       mComposition->itemsModel()->setItemRestored( mItem );
diff --git a/src/core/composer/qgscomposerarrow.cpp b/src/core/composer/qgscomposerarrow.cpp
index 6e07059..cf50f3a 100644
--- a/src/core/composer/qgscomposerarrow.cpp
+++ b/src/core/composer/qgscomposerarrow.cpp
@@ -19,6 +19,7 @@
 #include "qgscomposition.h"
 #include "qgscomposerutils.h"
 #include "qgssymbollayerv2utils.h"
+#include "qgssvgcache.h"
 #include <QPainter>
 #include <QSvgRenderer>
 #include <QVector2D>
@@ -252,22 +253,14 @@ void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QStrin
     imageFixPoint.setY( 0 );
   }
 
-  //rasterize svg
+  QString svgFileName = ( type == StartMarker ? mStartMarkerFile : mEndMarkerFile );
+  if ( svgFileName.isEmpty() )
+    return;
+
   QSvgRenderer r;
-  if ( type == StartMarker )
-  {
-    if ( mStartMarkerFile.isEmpty() || !r.load( mStartMarkerFile ) )
-    {
-      return;
-    }
-  }
-  else //end marker
-  {
-    if ( mEndMarkerFile.isEmpty() || !r.load( mEndMarkerFile ) )
-    {
-      return;
-    }
-  }
+  const QByteArray &svgContent = QgsSvgCache::instance()->svgContent( svgFileName, mArrowHeadWidth, mArrowHeadFillColor, mArrowHeadOutlineColor, mArrowHeadOutlineWidth,
+                                 1.0, 1.0 );
+  r.load( svgContent );
 
   p->save();
   p->setRenderHint( QPainter::Antialiasing );
diff --git a/src/core/composer/qgscomposeritemgroup.cpp b/src/core/composer/qgscomposeritemgroup.cpp
index b419ecf..cadf1f3 100644
--- a/src/core/composer/qgscomposeritemgroup.cpp
+++ b/src/core/composer/qgscomposeritemgroup.cpp
@@ -36,7 +36,7 @@ QgsComposerItemGroup::~QgsComposerItemGroup()
   //loop through group members and remove them from the scene
   Q_FOREACH ( QgsComposerItem* item, mItems )
   {
-    if ( !item )
+    if ( !item || item->isRemoved() )
       continue;
 
     //inform model that we are about to remove an item from the scene
diff --git a/src/core/composer/qgscomposerlegend.cpp b/src/core/composer/qgscomposerlegend.cpp
index b5e559e..d51b5aa 100644
--- a/src/core/composer/qgscomposerlegend.cpp
+++ b/src/core/composer/qgscomposerlegend.cpp
@@ -306,7 +306,7 @@ void QgsComposerLegend::updateLegend()
 
 void QgsComposerLegend::updateItem()
 {
-  updateFilterByMap();
+  updateFilterByMap( false );
   QgsComposerItem::updateItem();
 }
 
@@ -605,7 +605,7 @@ void QgsComposerLegend::mapLayerStyleOverridesChanged()
   {
     // legend is being filtered by map, so we need to re run the hit test too
     // as the style overrides may also have affected the visible symbols
-    updateFilterByMap();
+    updateFilterByMap( false );
   }
   else
   {
@@ -619,7 +619,7 @@ void QgsComposerLegend::mapLayerStyleOverridesChanged()
   updateItem();
 }
 
-void QgsComposerLegend::updateFilterByMap()
+void QgsComposerLegend::updateFilterByMap( bool redraw )
 {
   if ( isRemoved() )
     return;
@@ -627,6 +627,9 @@ void QgsComposerLegend::updateFilterByMap()
   // the actual update will take place before the redraw.
   // This is to avoid multiple calls to the filter
   mFilterAskedForUpdate = true;
+
+  if ( redraw )
+    QgsComposerItem::updateItem();
 }
 
 void QgsComposerLegend::doUpdateFilterByMap()
diff --git a/src/core/composer/qgscomposerlegend.h b/src/core/composer/qgscomposerlegend.h
index b7332da..9a4090c 100644
--- a/src/core/composer/qgscomposerlegend.h
+++ b/src/core/composer/qgscomposerlegend.h
@@ -255,7 +255,7 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
     void invalidateCurrentMap();
 
   private slots:
-    void updateFilterByMap();
+    void updateFilterByMap( bool redraw = true );
 
     //! update legend in case style of associated map has changed
     void mapLayerStyleOverridesChanged();
diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp
index d3b3d91..1f7f83a 100644
--- a/src/core/composer/qgscomposition.cpp
+++ b/src/core/composer/qgscomposition.cpp
@@ -33,6 +33,7 @@
 #include "qgscomposerattributetablev2.h"
 #include "qgsaddremovemultiframecommand.h"
 #include "qgscomposermultiframecommand.h"
+#include "qgsgroupungroupitemscommand.h"
 #include "qgspaintenginehack.h"
 #include "qgspaperitem.h"
 #include "qgsproject.h"
@@ -1515,6 +1516,10 @@ void QgsComposition::addItemsFromXML( const QDomElement& elem, const QDomDocumen
     QgsComposerItemGroup *newGroup = new QgsComposerItemGroup( this );
     newGroup->readXML( groupElem, doc );
     addItem( newGroup );
+    if ( addUndoCommands )
+    {
+      pushAddRemoveCommand( newGroup, tr( "Group added" ) );
+    }
   }
 
   //Since this function adds items grouped by type, and each item is added to end of
@@ -1940,14 +1945,28 @@ QgsComposerItemGroup *QgsComposition::groupItems( QList<QgsComposerItem *> items
   }
 
   QgsComposerItemGroup* itemGroup = new QgsComposerItemGroup( this );
+  QgsDebugMsg( QString( "itemgroup created with %1 items (%2 to be added)" ) .arg( itemGroup->items().size() ).arg( items.size() ) );
 
   QList<QgsComposerItem*>::iterator itemIter = items.begin();
   for ( ; itemIter != items.end(); ++itemIter )
   {
     itemGroup->addItem( *itemIter );
+    QgsDebugMsg( QString( "itemgroup now has %1" )
+                 .arg( itemGroup->items().size() ) );
   }
 
   addItem( itemGroup );
+
+  QgsGroupUngroupItemsCommand* c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Grouped, itemGroup, this, tr( "Items grouped" ) );
+  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
+  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
+
+  undoStack()->push( c );
+  QgsProject::instance()->setDirty( true );
+  //QgsDebugMsg( QString( "itemgroup after pushAddRemove has %1" ) .arg( itemGroup->items().size() ) );
+
+  emit composerItemGroupAdded( itemGroup );
+
   return itemGroup;
 }
 
@@ -1959,6 +1978,17 @@ QList<QgsComposerItem *> QgsComposition::ungroupItems( QgsComposerItemGroup* gro
     return ungroupedItems;
   }
 
+  // group ownership transferred to QgsGroupUngroupItemsCommand
+  // Call this before removing group items so it can keep note
+  // of contents
+  QgsGroupUngroupItemsCommand* c = new QgsGroupUngroupItemsCommand( QgsGroupUngroupItemsCommand::Ungrouped, group, this, tr( "Items ungrouped" ) );
+  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
+  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
+
+  undoStack()->push( c );
+  QgsProject::instance()->setDirty( true );
+
+
   QSet<QgsComposerItem*> groupedItems = group->items();
   QSet<QgsComposerItem*>::iterator itemIt = groupedItems.begin();
   for ( ; itemIt != groupedItems.end(); ++itemIt )
@@ -1967,10 +1997,9 @@ QList<QgsComposerItem *> QgsComposition::ungroupItems( QgsComposerItemGroup* gro
   }
 
   group->removeItems();
-  removeComposerItem( group, false, false );
 
-  emit itemRemoved( group );
-  delete( group );
+  // note: emits itemRemoved
+  removeComposerItem( group, false, false );
 
   return ungroupedItems;
 }
@@ -2542,6 +2571,7 @@ void QgsComposition::addComposerTableFrame( QgsComposerAttributeTableV2 *table,
   emit composerTableFrameAdded( table, frame );
 }
 
+/* public */
 void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool createCommand, const bool removeGroupItems )
 {
   QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
@@ -2550,25 +2580,36 @@ void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool creat
   {
     mItemsModel->setItemRemoved( item );
     removeItem( item );
+    emit itemRemoved( item );
+
+    QgsDebugMsg( QString( "removeComposerItem called, createCommand:%1 removeGroupItems:%2" )
+                 .arg( createCommand ).arg( removeGroupItems ) );
 
     QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
     if ( itemGroup && removeGroupItems )
     {
-      //add add/remove item command for every item in the group
-      QUndoCommand* parentCommand = new QUndoCommand( tr( "Remove item group" ) );
+      QgsDebugMsg( QString( "itemGroup && removeGroupItems" ) );
+
+      // Takes ownership of itemGroup
+      QgsAddRemoveItemCommand* parentCommand = new QgsAddRemoveItemCommand(
+        QgsAddRemoveItemCommand::Removed, itemGroup, this,
+        tr( "Remove item group" ) );
+      connectAddRemoveCommandSignals( parentCommand );
 
+      //add add/remove item command for every item in the group
       QSet<QgsComposerItem*> groupedItems = itemGroup->items();
+      QgsDebugMsg( QString( "itemGroup contains %1 items" ) .arg( groupedItems.size() ) );
       QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
       for ( ; it != groupedItems.end(); ++it )
       {
+        mItemsModel->setItemRemoved( *it );
+        removeItem( *it );
         QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
         connectAddRemoveCommandSignals( subcommand );
         emit itemRemoved( *it );
       }
 
       undoStack()->push( parentCommand );
-      emit itemRemoved( itemGroup );
-      delete itemGroup;
     }
     else
     {
@@ -2580,19 +2621,13 @@ void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool creat
         {
           multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
           item->beginItemCommand( tr( "Frame deleted" ) );
-          emit itemRemoved( item );
           item->endItemCommand();
         }
         else
         {
-          emit itemRemoved( item );
           pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
         }
       }
-      else
-      {
-        emit itemRemoved( item );
-      }
 
       //check if there are frames left. If not, remove the multi frame
       if ( frameItem && multiFrame )
@@ -2715,6 +2750,11 @@ void QgsComposition::sendItemAddedSignal( QgsComposerItem* item )
     emit selectedItemChanged( frame );
     return;
   }
+  QgsComposerItemGroup* group = dynamic_cast<QgsComposerItemGroup*>( item );
+  if ( group )
+  {
+    emit composerItemGroupAdded( group );
+  }
 }
 
 void QgsComposition::updatePaperItems()
diff --git a/src/core/composer/qgscomposition.h b/src/core/composer/qgscomposition.h
index 8ad5918..9f30742 100644
--- a/src/core/composer/qgscomposition.h
+++ b/src/core/composer/qgscomposition.h
@@ -1061,6 +1061,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
     void composerArrowAdded( QgsComposerArrow* arrow );
     /** Is emitted when a new composer html has been added to the view*/
     void composerHtmlFrameAdded( QgsComposerHtml* html, QgsComposerFrame* frame );
+    /** Is emitted when a new item group has been added to the view*/
+    void composerItemGroupAdded( QgsComposerItemGroup* group );
     /** Is emitted when new composer label has been added to the view*/
     void composerLabelAdded( QgsComposerLabel* label );
     /** Is emitted when new composer map has been added to the view*/
diff --git a/src/core/composer/qgsgroupungroupitemscommand.cpp b/src/core/composer/qgsgroupungroupitemscommand.cpp
new file mode 100644
index 0000000..0ee18d8
--- /dev/null
+++ b/src/core/composer/qgsgroupungroupitemscommand.cpp
@@ -0,0 +1,96 @@
+/***************************************************************************
+                          qgsgroupungroupitemscommand.cpp
+                          ---------------------------
+    begin                : 2016-06-09
+    copyright            : (C) 2016 by Sandro Santilli
+    email                : strk at kbt dot io
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "qgsgroupungroupitemscommand.h"
+#include "qgscomposeritem.h"
+#include "qgscomposeritemgroup.h"
+#include "qgscomposition.h"
+#include "qgsproject.h"
+#include "qgscomposermodel.h"
+#include "qgslogger.h"
+
+QgsGroupUngroupItemsCommand::QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent ):
+    QUndoCommand( text, parent ), mGroup( item ), mComposition( c ), mState( s ), mFirstRun( true )
+{
+  mItems = mGroup->items();
+}
+
+QgsGroupUngroupItemsCommand::~QgsGroupUngroupItemsCommand()
+{
+  if ( mState == Ungrouped )
+  {
+    //command class stores the item if ungrouped from the composition
+    delete mGroup;
+  }
+}
+
+void QgsGroupUngroupItemsCommand::redo()
+{
+  if ( mFirstRun )
+  {
+    mFirstRun = false;
+    return;
+  }
+  switchState();
+}
+
+void QgsGroupUngroupItemsCommand::undo()
+{
+  if ( mFirstRun )
+  {
+    mFirstRun = false;
+    return;
+  }
+  switchState();
+}
+
+void QgsGroupUngroupItemsCommand::switchState()
+{
+  if ( mState == Grouped )
+  {
+    // ungroup
+    if ( mComposition )
+    {
+      // This is probably redundant
+      mComposition->itemsModel()->setItemRemoved( mGroup );
+      mComposition->removeItem( mGroup );
+    }
+    mGroup->removeItems();
+    emit itemRemoved( mGroup );
+    mState = Ungrouped;
+  }
+  else //Ungrouped
+  {
+    // group
+    if ( mComposition )
+    {
+      //delete mGroup; mGroup = new QgsComposerItemGroup( mCompoiser );
+      QSet<QgsComposerItem*>::iterator itemIter = mItems.begin();
+      for ( ; itemIter != mItems.end(); ++itemIter )
+      {
+        mGroup->addItem( *itemIter );
+        QgsDebugMsg( QString( "itemgroup now has %1" ) .arg( mGroup->items().size() ) );
+      }
+      // Add the group
+      mComposition->itemsModel()->setItemRestored( mGroup );
+      mComposition->addItem( mGroup );
+    }
+    mState = Grouped;
+    emit itemAdded( mGroup );
+  }
+  QgsProject::instance()->setDirty( true );
+}
diff --git a/src/core/composer/qgsgroupungroupitemscommand.h b/src/core/composer/qgsgroupungroupitemscommand.h
new file mode 100644
index 0000000..0c2b548
--- /dev/null
+++ b/src/core/composer/qgsgroupungroupitemscommand.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+                          qgsgroupungroupitemscommand.h
+                          ------------------------
+    begin                : 2016-06-09
+    copyright            : (C) 2016 by Sandro Santilli
+    email                : strk at kbt dot io
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 QGSGROUPUNGROUPITEMSCOMMAND_H
+#define QGSGROUPUNGROUPITEMSCOMMAND_H
+
+#include <QUndoCommand>
+#include "qgscomposeritemgroup.h"
+class QgsComposerItem;
+class QgsComposition;
+
+/** A composer command class for grouping / ungrouping composer items.
+ *
+ * If mState == Ungrouped, the command owns the group item
+ */
+class CORE_EXPORT QgsGroupUngroupItemsCommand: public QObject, public QUndoCommand
+{
+    Q_OBJECT
+
+  public:
+
+    /** Command kind, and state */
+    enum State
+    {
+      Grouped = 0,
+      Ungrouped
+    };
+
+    /** Create a group or ungroup command
+     *
+     * @param s command kind (@see State)
+     * @param item the group item being created or ungrouped
+     * @param c the composition including this group
+     * @param text command label
+     * @param parent parent command, if any
+     *
+     */
+    QgsGroupUngroupItemsCommand( State s, QgsComposerItemGroup* item, QgsComposition* c, const QString& text, QUndoCommand* parent = nullptr );
+    ~QgsGroupUngroupItemsCommand();
+
+    void redo() override;
+    void undo() override;
+
+  signals:
+    /** Signals addition of an item (the group) */
+    void itemAdded( QgsComposerItem* item );
+    /** Signals removal of an item (the group) */
+    void itemRemoved( QgsComposerItem* item );
+
+  private:
+    QgsComposerItemGroup* mGroup;
+    QSet<QgsComposerItem*> mItems;
+    QgsComposition* mComposition;
+    State mState;
+    bool mFirstRun; //flag to prevent execution when the command is pushed to the QUndoStack
+
+    //changes between added / removed state
+    void switchState();
+};
+
+#endif // QGSGROUPUNGROUPITEMSCOMMAND_H
diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp
index b3a7142..3950a53 100644
--- a/src/core/dxf/qgsdxfexport.cpp
+++ b/src/core/dxf/qgsdxfexport.cpp
@@ -37,6 +37,11 @@
 #include "qgsvectorlayer.h"
 #include "qgsmaplayerregistry.h"
 #include "qgsunittypes.h"
+#include "qgstextlabelfeature.h"
+
+#include "pal/feature.h"
+#include "pal/pointset.h"
+#include "pal/labelposition.h"
 
 #include <QIODevice>
 
@@ -865,7 +870,7 @@ void QgsDxfExport::writeBlocks()
     writeGroup( 1, "" );
 
     // maplayer 0 -> block receives layer from INSERT statement
-    ml->writeDxf( *this, mapUnitScaleFactor( mSymbologyScaleDenominator, ml->sizeUnit(), mMapUnits ), "0", &ctx, nullptr );
+    ml->writeDxf( *this, mapUnitScaleFactor( mSymbologyScaleDenominator, ml->sizeUnit(), mMapUnits ), "0", ctx );
 
     writeGroup( 0, "ENDBLK" );
     writeHandle();
@@ -914,6 +919,7 @@ void QgsDxfExport::writeEntities()
 
   // label engine
   QgsLabelingEngineV2 engine;
+  engine.readSettingsFromProject();
   engine.setMapSettings( mapSettings );
 
   // iterate through the maplayers
@@ -942,12 +948,32 @@ void QgsDxfExport::writeEntities()
         attributes << layerAttr;
     }
 
-    QgsDxfLabelProvider* lp = new QgsDxfLabelProvider( vl, this );
-    engine.addProvider( lp );
-    if ( !lp->prepare( ctx, attributes ) )
+    const QgsAbstractVectorLayerLabeling *labeling = vl->labeling();
+    QgsDxfLabelProvider *lp = nullptr;
+    QgsDxfRuleBasedLabelProvider *rblp = nullptr;
+    if ( dynamic_cast<const QgsRuleBasedLabeling*>( labeling ) )
+    {
+      const QgsRuleBasedLabeling *rbl = dynamic_cast<const QgsRuleBasedLabeling*>( labeling );
+      rblp = new QgsDxfRuleBasedLabelProvider( *rbl, vl, this );
+      rblp->reinit( vl );
+      engine.addProvider( rblp );
+
+      if ( !rblp->prepare( ctx, attributes ) )
+      {
+        engine.removeProvider( rblp );
+        rblp = nullptr;
+      }
+    }
+    else
     {
-      engine.removeProvider( lp );
-      lp = nullptr;
+      lp = new QgsDxfLabelProvider( vl, this, nullptr );
+      engine.addProvider( lp );
+
+      if ( !lp->prepare( ctx, attributes ) )
+      {
+        engine.removeProvider( lp );
+        lp = nullptr;
+      }
     }
 
     if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
@@ -959,7 +985,7 @@ void QgsDxfExport::writeEntities()
       continue;
     }
 
-    QgsFeatureRequest freq = QgsFeatureRequest().setSubsetOfAttributes( attributes, vl->fields() );
+    QgsFeatureRequest freq = QgsFeatureRequest().setSubsetOfAttributes( attributes, vl->fields() ).setExpressionContext( ctx.expressionContext() );
     if ( !mExtent.isEmpty() )
     {
       freq.setFilterRect( mExtent );
@@ -1012,6 +1038,10 @@ void QgsDxfExport::writeEntities()
         {
           lp->registerDxfFeature( fet, ctx, lName );
         }
+        else if ( rblp )
+        {
+          rblp->registerDxfFeature( fet, ctx, lName );
+        }
       }
     }
 
@@ -3300,7 +3330,7 @@ void QgsDxfExport::endSection()
   writeGroup( 0, "ENDSEC" );
 }
 
-void QgsDxfExport::writePoint( const QgsPoint& pt, const QString& layer, const QColor& color, const QgsFeature* f, const QgsSymbolLayerV2* symbolLayer, const QgsSymbolV2* symbol )
+void QgsDxfExport::writePoint( const QgsPoint& pt, const QString& layer, const QColor& color, QgsSymbolV2RenderContext &ctx, const QgsSymbolLayerV2* symbolLayer, const QgsSymbolV2* symbol, double angle )
 {
 #if 0
   // debug: draw rectangle for debugging
@@ -3327,9 +3357,7 @@ void QgsDxfExport::writePoint( const QgsPoint& pt, const QString& layer, const Q
     const QgsMarkerSymbolLayerV2* msl = dynamic_cast< const QgsMarkerSymbolLayerV2* >( symbolLayer );
     if ( msl && symbol )
     {
-      QgsRenderContext ct;
-      QgsSymbolV2RenderContext ctx( ct, QgsSymbolV2::MapUnit, symbol->alpha(), false, symbol->renderHints(), f );
-      if ( symbolLayer->writeDxf( *this, mapUnitScaleFactor( mSymbologyScaleDenominator, msl->sizeUnit(), mMapUnits ), layer, &ctx, f, QPointF( pt.x(), pt.y() ) ) )
+      if ( symbolLayer->writeDxf( *this, mapUnitScaleFactor( mSymbologyScaleDenominator, msl->sizeUnit(), mMapUnits ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
       {
         return;
       }
@@ -3345,6 +3373,7 @@ void QgsDxfExport::writePoint( const QgsPoint& pt, const QString& layer, const Q
     writeGroup( 100, "AcDbBlockReference" );
     writeGroup( 8, layer );
     writeGroup( 2, blockIt.value() ); // Block name
+    writeGroup( 50, angle ); // angle
     writeGroup( 0, pt );  // Insertion point (in OCS)
   }
 }
@@ -3630,10 +3659,12 @@ void QgsDxfExport::addFeature( QgsSymbolV2RenderContext& ctx, const QString& lay
   Qt::BrushStyle brushStyle( Qt::NoBrush );
   double width = -1;
   double offset = 0.0;
+  double angle = 0.0;
   if ( mSymbologyExport != NoSymbology && symbolLayer )
   {
     width = symbolLayer->dxfWidth( *this, ctx );
     offset = symbolLayer->dxfOffset( *this, ctx );
+    angle = symbolLayer->dxfAngle( ctx );
     penStyle = symbolLayer->dxfPenStyle();
     brushStyle = symbolLayer->dxfBrushStyle();
 
@@ -3650,7 +3681,7 @@ void QgsDxfExport::addFeature( QgsSymbolV2RenderContext& ctx, const QString& lay
   // single point
   if ( geometryType == QGis::WKBPoint || geometryType == QGis::WKBPoint25D )
   {
-    writePoint( geom->asPoint(), layer, penColor, fet, symbolLayer, symbol );
+    writePoint( geom->asPoint(), layer, penColor, ctx, symbolLayer, symbol, angle );
     return;
   }
 
@@ -3661,7 +3692,7 @@ void QgsDxfExport::addFeature( QgsSymbolV2RenderContext& ctx, const QString& lay
     QgsMultiPoint::const_iterator it = multiPoint.constBegin();
     for ( ; it != multiPoint.constEnd(); ++it )
     {
-      writePoint( *it, layer, penColor, fet, symbolLayer, symbol );
+      writePoint( *it, layer, penColor, ctx, symbolLayer, symbol, angle );
     }
 
     return;
@@ -4101,6 +4132,11 @@ QString QgsDxfExport::dxfLayerName( const QString& name )
   layerName.replace( '=', '_' );
   layerName.replace( '\'', '_' );
 
+  // also remove newline characters (#15067)
+  layerName.replace( "\r\n", "_" );
+  layerName.replace( '\r', '_' );
+  layerName.replace( '\n', '_' );
+
   return layerName.trimmed();
 }
 
@@ -4170,3 +4206,147 @@ QString QgsDxfExport::layerName( QgsVectorLayer *vl ) const
   Q_ASSERT( vl );
   return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
 }
+
+void QgsDxfExport::drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings )
+{
+  Q_UNUSED( context );
+
+  if ( !settings.drawLabels )
+    return;
+
+  QgsTextLabelFeature* lf = dynamic_cast<QgsTextLabelFeature*>( label->getFeaturePart()->feature() );
+
+  // Copy to temp, editable layer settings
+  // these settings will be changed by any data defined values, then used for rendering label components
+  // settings may be adjusted during rendering of components
+  QgsPalLayerSettings tmpLyr( settings );
+
+  // apply any previously applied data defined settings for the label
+  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = lf->dataDefinedValues();
+
+  //font
+  QFont dFont = lf->definedFont();
+  QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString(), tmpLyr.textFont.styleName() ), 4 );
+  QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
+  tmpLyr.textFont = dFont;
+
+  if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiFollowPlacement )
+  {
+    //calculate font alignment based on label quadrant
+    switch ( label->getQuadrant() )
+    {
+      case pal::LabelPosition::QuadrantAboveLeft:
+      case pal::LabelPosition::QuadrantLeft:
+      case pal::LabelPosition::QuadrantBelowLeft:
+        tmpLyr.multilineAlign = QgsPalLayerSettings::MultiRight;
+        break;
+      case pal::LabelPosition::QuadrantAbove:
+      case pal::LabelPosition::QuadrantOver:
+      case pal::LabelPosition::QuadrantBelow:
+        tmpLyr.multilineAlign = QgsPalLayerSettings::MultiCenter;
+        break;
+      case pal::LabelPosition::QuadrantAboveRight:
+      case pal::LabelPosition::QuadrantRight:
+      case pal::LabelPosition::QuadrantBelowRight:
+        tmpLyr.multilineAlign = QgsPalLayerSettings::MultiLeft;
+        break;
+    }
+  }
+
+  // update tmpLyr with any data defined text style values
+  QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
+
+  // update tmpLyr with any data defined text buffer values
+  QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
+
+  // update tmpLyr with any data defined text formatting values
+  QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
+
+  // add to the results
+  QString txt = label->getFeaturePart()->feature()->labelText();
+
+  QgsFeatureId fid = label->getFeaturePart()->featureId();
+  QString dxfLayer = mDxfLayerNames[layerId][fid];
+
+  QString wrapchr = tmpLyr.wrapChar.isEmpty() ? "\n" : tmpLyr.wrapChar;
+
+  //add the direction symbol if needed
+  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.addDirectionSymbol )
+  {
+    bool prependSymb = false;
+    QString symb = tmpLyr.rightDirectionSymbol;
+
+    if ( label->getReversed() )
+    {
+      prependSymb = true;
+      symb = tmpLyr.leftDirectionSymbol;
+    }
+
+    if ( tmpLyr.reverseDirectionSymbol )
+    {
+      if ( symb == tmpLyr.rightDirectionSymbol )
+      {
+        prependSymb = true;
+        symb = tmpLyr.leftDirectionSymbol;
+      }
+      else
+      {
+        prependSymb = false;
+        symb = tmpLyr.rightDirectionSymbol;
+      }
+    }
+
+    if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
+    {
+      prependSymb = true;
+      symb = symb + wrapchr;
+    }
+    else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
+    {
+      prependSymb = false;
+      symb = wrapchr + symb;
+    }
+
+    if ( prependSymb )
+    {
+      txt.prepend( symb );
+    }
+    else
+    {
+      txt.append( symb );
+    }
+  }
+
+  txt = txt.replace( wrapchr, "\\P" );
+
+  if ( tmpLyr.textFont.underline() )
+  {
+    txt.prepend( "\\L" ).append( "\\l" );
+  }
+
+  if ( tmpLyr.textFont.overline() )
+  {
+    txt.prepend( "\\O" ).append( "\\o" );
+  }
+
+  if ( tmpLyr.textFont.strikeOut() )
+  {
+    txt.prepend( "\\K" ).append( "\\k" );
+  }
+
+  txt.prepend( QString( "\\f%1|i%2|b%3;\\H%4;\\W0.75;" )
+               .arg( tmpLyr.textFont.family() )
+               .arg( tmpLyr.textFont.italic() ? 1 : 0 )
+               .arg( tmpLyr.textFont.bold() ? 1 : 0 )
+               .arg( label->getHeight() / ( 1 + txt.count( "\\P" ) ) * 0.75 ) );
+
+  writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.textColor );
+}
+
+void QgsDxfExport::registerDxfLayer( QString layerId, QgsFeatureId fid, QString layerName )
+{
+  if ( !mDxfLayerNames.contains( layerId ) )
+    mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
+
+  mDxfLayerNames[layerId][fid] = layerName;
+}
diff --git a/src/core/dxf/qgsdxfexport.h b/src/core/dxf/qgsdxfexport.h
index cc5c232..0e92a3e 100644
--- a/src/core/dxf/qgsdxfexport.h
+++ b/src/core/dxf/qgsdxfexport.h
@@ -20,6 +20,7 @@
 
 #include "qgsgeometry.h"
 #include "qgssymbolv2.h"
+
 #include <QColor>
 #include <QList>
 #include <QTextStream>
@@ -28,6 +29,12 @@ class QgsMapLayer;
 class QgsPoint;
 class QgsSymbolLayerV2;
 class QIODevice;
+class QgsPalLayerSettings;
+
+namespace pal
+{
+  class LabelPosition;
+};
 
 class CORE_EXPORT QgsDxfExport
 {
@@ -228,7 +235,7 @@ class CORE_EXPORT QgsDxfExport
      * @param line polyline
      * @param layer layer name to use
      * @param lineStyleName line type to use
-     * @param color coolor to use
+     * @param color color to use
      * @param width line width to use
      */
     void writePolyline( const QgsPolyline &line, const QString &layer, const QString &lineStyleName, const QColor& color, double width = -1 );
@@ -238,7 +245,7 @@ class CORE_EXPORT QgsDxfExport
      * @param polygon polygon
      * @param layer layer name to use
      * @param hatchPattern hatchPattern to use
-     * @param color coolor to use
+     * @param color color to use
      */
     void writePolygon( const QgsPolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor& color );
 
@@ -283,6 +290,22 @@ class CORE_EXPORT QgsDxfExport
     //! return list of available DXF encodings
     static QStringList encodings();
 
+    /** Output the label
+     * @param layerId id of the layer
+     * @param context render context
+     * @param label position of label
+     * @param settings label settings
+     * @note not available in Python bindings
+     */
+    void drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings );
+
+    /** Register name of layer for feature
+     * @param layerId id of layer
+     * @param fid id of feature
+     * @param layer dxf layer of feature
+     */
+    void registerDxfLayer( QString layerId, QgsFeatureId fid, QString layer );
+
   private:
     QList< QPair<QgsVectorLayer*, int> > mLayers;
 
@@ -317,7 +340,7 @@ class CORE_EXPORT QgsDxfExport
     void startSection();
     void endSection();
 
-    void writePoint( const QgsPoint &pt, const QString &layer, const QColor& color, const QgsFeature *f, const QgsSymbolLayerV2 *symbolLayer, const QgsSymbolV2 *symbol );
+    void writePoint( const QgsPoint &pt, const QString &layer, const QColor& color, QgsSymbolV2RenderContext &ctx, const QgsSymbolLayerV2 *symbolLayer, const QgsSymbolV2 *symbol, double angle );
     void writeVertex( const QgsPoint &pt, const QString &layer );
     void writeDefaultLinetypes();
     void writeSymbolLayerLinetype( const QgsSymbolLayerV2 *symbolLayer );
@@ -350,6 +373,9 @@ class CORE_EXPORT QgsDxfExport
 
     QHash<QString, int> mBlockHandles;
     QString mBlockHandle;
+
+    //! DXF layer name for each label feature
+    QMap< QString, QMap<QgsFeatureId, QString> > mDxfLayerNames;
 };
 
 #endif // QGSDXFEXPORT_H
diff --git a/src/core/dxf/qgsdxfpallabeling.cpp b/src/core/dxf/qgsdxfpallabeling.cpp
index 9785f47..928c14b 100644
--- a/src/core/dxf/qgsdxfpallabeling.cpp
+++ b/src/core/dxf/qgsdxfpallabeling.cpp
@@ -17,131 +17,57 @@
 
 #include "qgsdxfpallabeling.h"
 #include "qgsdxfexport.h"
-#include "qgstextlabelfeature.h"
 #include "qgspallabeling.h"
 #include "qgsmapsettings.h"
+#include "qgslogger.h"
 
-#include "pal/feature.h"
-#include "pal/pointset.h"
-#include "pal/labelposition.h"
 
-
-QgsDxfLabelProvider::QgsDxfLabelProvider( QgsVectorLayer* layer , QgsDxfExport* dxf )
-    : QgsVectorLayerLabelProvider( layer, false )
+QgsDxfLabelProvider::QgsDxfLabelProvider( QgsVectorLayer* layer, QgsDxfExport* dxf, const QgsPalLayerSettings *settings )
+    : QgsVectorLayerLabelProvider( layer, false, settings )
     , mDxfExport( dxf )
 {
 }
 
 void QgsDxfLabelProvider::drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const
 {
-  Q_UNUSED( context );
-
-  //debug: print label infos
-  if ( mDxfExport )
-  {
-    QgsTextLabelFeature* lf = dynamic_cast<QgsTextLabelFeature*>( label->getFeaturePart()->feature() );
-    if ( !lf )
-      return;
-
-    const QgsPalLayerSettings& tmpLyr = mSettings;
-
-    //label text
-    QString txt = lf->text( label->getPartId() );
-
-    //angle
-    double angle = label->getAlpha() * 180 / M_PI;
-
-    QgsFeatureId fid = label->getFeaturePart()->featureId();
-    QString dxfLayer = mDxfLayerNames[fid];
-
-    //debug: show label rectangle
-#if 0
-    QgsPolyline line;
-    for ( int i = 0; i < 4; ++i )
-    {
-      line.append( QgsPoint( label->getX( i ), label->getY( i ) ) );
-    }
-    mDxfExport->writePolyline( line, dxfLayer, "CONTINUOUS", 1, 0.01, true );
-#endif
-
-    QString wrapchr = tmpLyr.wrapChar.isEmpty() ? "\n" : tmpLyr.wrapChar;
-
-    //add the direction symbol if needed
-    if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.addDirectionSymbol )
-    {
-      bool prependSymb = false;
-      QString symb = tmpLyr.rightDirectionSymbol;
-
-      if ( label->getReversed() )
-      {
-        prependSymb = true;
-        symb = tmpLyr.leftDirectionSymbol;
-      }
-
-      if ( tmpLyr.reverseDirectionSymbol )
-      {
-        if ( symb == tmpLyr.rightDirectionSymbol )
-        {
-          prependSymb = true;
-          symb = tmpLyr.leftDirectionSymbol;
-        }
-        else
-        {
-          prependSymb = false;
-          symb = tmpLyr.rightDirectionSymbol;
-        }
-      }
-
-      if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
-      {
-        prependSymb = true;
-        symb = symb + wrapchr;
-      }
-      else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
-      {
-        prependSymb = false;
-        symb = wrapchr + symb;
-      }
-
-      if ( prependSymb )
-      {
-        txt.prepend( symb );
-      }
-      else
-      {
-        txt.append( symb );
-      }
-    }
-
-    txt = txt.replace( wrapchr, "\\P" );
+  Q_ASSERT( mDxfExport );
+  mDxfExport->drawLabel( layerId(), context, label, mSettings );
+}
 
-    if ( tmpLyr.textFont.underline() )
-    {
-      txt.prepend( "\\L" ).append( "\\l" );
-    }
+void QgsDxfLabelProvider::registerDxfFeature( QgsFeature& feature, QgsRenderContext& context, const QString& dxfLayerName )
+{
+  registerFeature( feature, context );
+  mDxfExport->registerDxfLayer( layerId(), feature.id(), dxfLayerName );
+}
 
-    if ( tmpLyr.textFont.overline() )
-    {
-      txt.prepend( "\\O" ).append( "\\o" );
-    }
+QgsDxfRuleBasedLabelProvider::QgsDxfRuleBasedLabelProvider( const QgsRuleBasedLabeling &rules, QgsVectorLayer* layer, QgsDxfExport* dxf )
+    : QgsRuleBasedLabelProvider( rules, layer, false )
+    , mDxfExport( dxf )
+{
+}
 
-    if ( tmpLyr.textFont.strikeOut() )
-    {
-      txt.prepend( "\\K" ).append( "\\k" );
-    }
+void QgsDxfRuleBasedLabelProvider::reinit( QgsVectorLayer* layer )
+{
+  QgsDebugMsg( "Entering." );
+  mRules.rootRule()->createSubProviders( layer, mSubProviders, this );
+}
 
-    txt.prepend( QString( "\\f%1|i%2|b%3;\\H%4;\\W0.75;" )
-                 .arg( tmpLyr.textFont.family() )
-                 .arg( tmpLyr.textFont.italic() ? 1 : 0 )
-                 .arg( tmpLyr.textFont.bold() ? 1 : 0 )
-                 .arg( label->getHeight() / ( 1 + txt.count( "\\P" ) ) * 0.75 ) );
+QgsVectorLayerLabelProvider *QgsDxfRuleBasedLabelProvider::createProvider( QgsVectorLayer *layer, bool withFeatureLoop, const QgsPalLayerSettings *settings )
+{
+  QgsDebugMsg( "Entering." );
+  Q_UNUSED( withFeatureLoop );
+  return new QgsDxfLabelProvider( layer, mDxfExport, settings );
+}
 
-    mDxfExport->writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth() * 1.1, angle, tmpLyr.textColor );
-  }
+void QgsDxfRuleBasedLabelProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const
+{
+  QgsDebugMsg( "Entering." );
+  Q_ASSERT( mDxfExport );
+  mDxfExport->drawLabel( layerId(), context, label, mSettings );
 }
 
-void QgsDxfLabelProvider::registerDxfFeature( QgsFeature& feature, QgsRenderContext& context, const QString& dxfLayerName )
+void QgsDxfRuleBasedLabelProvider::registerDxfFeature( QgsFeature &feature, QgsRenderContext &context, const QString &dxfLayerName )
 {
   registerFeature( feature, context );
-  mDxfLayerNames[feature.id()] = dxfLayerName;
+  mDxfExport->registerDxfLayer( layerId(), feature.id(), dxfLayerName );
 }
diff --git a/src/core/dxf/qgsdxfpallabeling.h b/src/core/dxf/qgsdxfpallabeling.h
index f051b50..f30b235 100644
--- a/src/core/dxf/qgsdxfpallabeling.h
+++ b/src/core/dxf/qgsdxfpallabeling.h
@@ -21,8 +21,11 @@
 #include "qgsmaprenderer.h"
 #include "qgsrendercontext.h"
 #include "qgsvectorlayerlabelprovider.h"
+#include "qgsrulebasedlabeling.h"
 
 class QgsDxfExport;
+class QgsPalLayerSettings;
+class QgsRuleBasedLabeling;
 
 
 /** Implements a derived label provider internally used for DXF export
@@ -34,19 +37,64 @@ class QgsDxfLabelProvider : public QgsVectorLayerLabelProvider
 {
   public:
     //! construct the provider
-    explicit QgsDxfLabelProvider( QgsVectorLayer* layer, QgsDxfExport* dxf );
+    explicit QgsDxfLabelProvider( QgsVectorLayer* layer, QgsDxfExport* dxf, const QgsPalLayerSettings *settings );
 
-    //! re-implementation that writes to DXF file instead of drawing with QPainter
-    virtual void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override;
+    /** Re-implementation that writes to DXF file instead of drawing with QPainter
+     * @param context render context
+     * @param label label
+     */
+    void drawLabel( QgsRenderContext& context, pal::LabelPosition* label ) const override;
 
-    //! registration method that keeps track of DXF layer names of individual features
+    /** Registration method that keeps track of DXF layer names of individual features
+     * @param feature feature
+     * @param context render context
+     * @param dxfLayerName name of dxf layer
+     */
     void registerDxfFeature( QgsFeature& feature, QgsRenderContext &context, const QString& dxfLayerName );
 
   protected:
     //! pointer to parent DXF export where this instance is used
     QgsDxfExport* mDxfExport;
-    //! DXF layer name for each label feature
-    QMap<QgsFeatureId, QString> mDxfLayerNames;
 };
 
+/** Implements a derived label provider for rule based labels internally used
+ * for DXF export
+ *
+ * Internal class, not in public API. Backported from QGIS 2.15
+ * @note not available in Python bindings
+ */
+class QgsDxfRuleBasedLabelProvider : public QgsRuleBasedLabelProvider
+{
+  public:
+    //! construct the provider
+    explicit QgsDxfRuleBasedLabelProvider( const QgsRuleBasedLabeling &rules, QgsVectorLayer* layer, QgsDxfExport* dxf );
+
+    /** Reinitialize the subproviders with QgsDxfLabelProviders
+     * @param layer layer
+     */
+    void reinit( QgsVectorLayer* layer );
+
+    /** Re-implementation that writes to DXF file instead of drawing with QPainter
+     * @param context render context
+     * @param label label
+     */
+    void drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const override;
+
+    /** Registration method that keeps track of DXF layer names of individual features
+     * @param feature feature
+     * @param context render context
+     * @param dxfLayerName name of dxf layer
+     */
+    void registerDxfFeature( QgsFeature& feature, QgsRenderContext &context, const QString& dxfLayerName );
+
+    //! create QgsDxfLabelProvider
+    virtual QgsVectorLayerLabelProvider *createProvider( QgsVectorLayer *layer, bool withFeatureLoop, const QgsPalLayerSettings *settings ) override;
+
+  protected:
+    //! pointer to parent DXF export where this instance is used
+    QgsDxfExport* mDxfExport;
+};
+
+
+
 #endif // QGSDXFPALLABELING_H
diff --git a/src/core/gps/parse.c b/src/core/gps/parse.c
index 526b208..a2d2dbf 100644
--- a/src/core/gps/parse.c
+++ b/src/core/gps/parse.c
@@ -133,6 +133,7 @@ int nmea_pack_type( const char *buff, int buff_sz )
     "GPGSV",
     "GPRMC",
     "GPVTG",
+    "GNRMC",
   };
 
   NMEA_ASSERT( buff );
@@ -149,6 +150,8 @@ int nmea_pack_type( const char *buff, int buff_sz )
     return GPRMC;
   else if ( 0 == memcmp( buff, pheads[4], 5 ) )
     return GPVTG;
+  else if ( 0 == memcmp( buff, pheads[5], 5 ) )
+    return GPRMC;
 
   return GPNON;
 }
@@ -322,6 +325,7 @@ int nmea_parse_GPGSV( const char *buff, int buff_sz, nmeaGPGSV *pack )
 int nmea_parse_GPRMC( const char *buff, int buff_sz, nmeaGPRMC *pack )
 {
   int nsen;
+  char type;
   char time_buff[NMEA_TIMEPARSE_BUF];
 
   NMEA_ASSERT( buff && pack );
@@ -331,19 +335,25 @@ int nmea_parse_GPRMC( const char *buff, int buff_sz, nmeaGPRMC *pack )
   nmea_trace_buff( buff, buff_sz );
 
   nsen = nmea_scanf( buff, buff_sz,
-                     "$GPRMC,%s,%C,%f,%C,%f,%C,%f,%f,%2d%2d%2d,%f,%C,%C*",
-                     &( time_buff[0] ),
+                     "$G%CRMC,%s,%C,%f,%C,%f,%C,%f,%f,%2d%2d%2d,%f,%C,%C*",
+                     &( type ), &( time_buff[0] ),
                      &( pack->status ), &( pack->lat ), &( pack->ns ), &( pack->lon ), &( pack->ew ),
                      &( pack->speed ), &( pack->direction ),
                      &( pack->utc.day ), &( pack->utc.mon ), &( pack->utc.year ),
                      &( pack->declination ), &( pack->declin_ew ), &( pack->mode ) );
 
-  if ( nsen != 13 && nsen != 14 )
+  if ( nsen != 14 && nsen != 15 )
   {
     nmea_error( "GPRMC parse error!" );
     return 0;
   }
 
+  if ( type != 'P' && type != 'N' )
+  {
+    nmea_error( "G?RMC invalid type " );
+    return 0;
+  }
+
   if ( 0 != _nmea_parse_time( &time_buff[0], ( int )strlen( &time_buff[0] ), &( pack->utc ) ) )
   {
     nmea_error( "GPRMC time parse error!" );
diff --git a/src/core/gps/qgsnmeaconnection.cpp b/src/core/gps/qgsnmeaconnection.cpp
index 44e9efb..f116010 100644
--- a/src/core/gps/qgsnmeaconnection.cpp
+++ b/src/core/gps/qgsnmeaconnection.cpp
@@ -103,7 +103,7 @@ void QgsNMEAConnection::processStringBuffer()
           mStatus = GPSDataReceived;
           QgsDebugMsg( "*******************GPS data received****************" );
         }
-        else if ( substring.startsWith( "$GPRMC" ) )
+        else if ( substring.startsWith( "$GPRMC" ) || substring.startsWith( "$GNRMC" ) )
         {
           QgsDebugMsg( substring );
           processRMCSentence( ba.data(), ba.length() );
diff --git a/src/core/qgis.cpp b/src/core/qgis.cpp
index 786837f..d6a7ba1 100644
--- a/src/core/qgis.cpp
+++ b/src/core/qgis.cpp
@@ -160,16 +160,22 @@ QGis::WkbType QGis::fromNewWkbType( QgsWKBTypes::Type type )
     case QgsWKBTypes::NoGeometry:
       return QGis::WKBNoGeometry;
     case QgsWKBTypes::PointZ:
+    case QgsWKBTypes::Point25D:
       return QGis::WKBPoint25D;
     case QgsWKBTypes::LineStringZ:
+    case QgsWKBTypes::LineString25D:
       return QGis::WKBLineString25D;
     case QgsWKBTypes::PolygonZ:
+    case QgsWKBTypes::Polygon25D:
       return QGis::WKBPolygon25D;
     case QgsWKBTypes::MultiPointZ:
+    case QgsWKBTypes::MultiPoint25D:
       return QGis::WKBMultiPoint25D;
     case QgsWKBTypes::MultiLineStringZ:
+    case QgsWKBTypes::MultiLineString25D:
       return QGis::WKBMultiLineString25D;
     case QgsWKBTypes::MultiPolygonZ:
+    case QgsWKBTypes::MultiPolygon25D:
       return QGis::WKBMultiPolygon25D;
     default:
       break;
diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp
index dc6f26b..a1ca088 100644
--- a/src/core/qgsapplication.cpp
+++ b/src/core/qgsapplication.cpp
@@ -153,14 +153,14 @@ void QgsApplication::init( QString customConfigPath )
   {
     // we run from source directory - not installed to destination (specified prefix)
     ABISYM( mPrefixPath ) = QString(); // set invalid path
-#if defined(_MSC_VER) && !defined(USING_NMAKE)
+#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
     setPluginPath( ABISYM( mBuildOutputPath ) + '/' + QString( QGIS_PLUGIN_SUBDIR ) + '/' + ABISYM( mCfgIntDir ) );
 #else
     setPluginPath( ABISYM( mBuildOutputPath ) + '/' + QString( QGIS_PLUGIN_SUBDIR ) );
 #endif
     setPkgDataPath( ABISYM( mBuildSourcePath ) ); // directly source path - used for: doc, resources, svg
     ABISYM( mLibraryPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_LIB_SUBDIR + '/';
-#if defined(_MSC_VER) && !defined(USING_NMAKE)
+#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
     ABISYM( mLibexecPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_LIBEXEC_SUBDIR + '/' + ABISYM( mCfgIntDir ) + '/';
 #else
     ABISYM( mLibexecPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_LIBEXEC_SUBDIR + '/';
@@ -696,7 +696,7 @@ QStringList QgsApplication::svgPaths()
   //defined by user in options dialog
   QSettings settings;
   QStringList myPathList;
-  QString myPaths = settings.value( "svg/searchPathsForSVG", QDir::homePath() ).toString();
+  QString myPaths = settings.value( "svg/searchPathsForSVG", QString() ).toString();
   if ( !myPaths.isEmpty() )
   {
     myPathList = myPaths.split( '|' );
@@ -715,7 +715,7 @@ QStringList QgsApplication::composerTemplatePaths()
   //defined by user in options dialog
   QSettings settings;
   QStringList myPathList;
-  QString myPaths = settings.value( "composer/searchPathsForTemplates", QDir::homePath() ).toString();
+  QString myPaths = settings.value( "composer/searchPathsForTemplates", QString() ).toString();
   if ( !myPaths.isEmpty() )
   {
     myPathList = myPaths.split( '|' );
diff --git a/src/core/qgscoordinatereferencesystem.cpp b/src/core/qgscoordinatereferencesystem.cpp
index 6a50078..7c00a63 100644
--- a/src/core/qgscoordinatereferencesystem.cpp
+++ b/src/core/qgscoordinatereferencesystem.cpp
@@ -927,6 +927,20 @@ void QgsCoordinateReferenceSystem::setProj4String( const QString& theProj4String
   OSRDestroySpatialReference( mCRS );
   mCRS = OSRNewSpatialReference( nullptr );
   mIsValidFlag = OSRImportFromProj4( mCRS, theProj4String.trimmed().toLatin1().constData() ) == OGRERR_NONE;
+  // OSRImportFromProj4() may accept strings that are not valid proj.4 strings,
+  // e.g if they lack a +ellps parameter, it will automatically add +ellps=WGS84, but as
+  // we use the original mProj4 with QgsCoordinateTransform, it will fail to initialize
+  // so better detect it now.
+  projPJ theProj = pj_init_plus( theProj4String.trimmed().toLatin1().constData() );
+  if ( !theProj )
+  {
+    QgsDebugMsg( "proj.4 string rejected by pj_init_plus()" );
+    mIsValidFlag = false;
+  }
+  else
+  {
+    pj_free( theProj );
+  }
   mWkt.clear();
   setMapUnits();
 
diff --git a/src/core/qgscoordinatetransform.cpp b/src/core/qgscoordinatetransform.cpp
index 0518000..cdce4c5 100644
--- a/src/core/qgscoordinatetransform.cpp
+++ b/src/core/qgscoordinatetransform.cpp
@@ -668,7 +668,6 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
     {
       x[i] *= DEG_TO_RAD;
       y[i] *= DEG_TO_RAD;
-      z[i] *= DEG_TO_RAD;
     }
 
   }
@@ -736,7 +735,6 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
     {
       x[i] *= RAD_TO_DEG;
       y[i] *= RAD_TO_DEG;
-      z[i] *= RAD_TO_DEG;
     }
   }
 #ifdef COORDINATE_TRANSFORM_VERBOSE
diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp
index 33bc1b0..df1c5ba 100644
--- a/src/core/qgsdataitem.cpp
+++ b/src/core/qgsdataitem.cpp
@@ -670,6 +670,8 @@ void QgsDataItem::setState( State state )
   mPopulated = state == Populated;
 
   emit stateChanged( this, oldState );
+  if ( state == Populated )
+    emitDataChanged();
 }
 
 // ---------------------------------------------------------------------
diff --git a/src/core/qgsdataprovider.h b/src/core/qgsdataprovider.h
index b4b3f6e..0c64ec4 100644
--- a/src/core/qgsdataprovider.h
+++ b/src/core/qgsdataprovider.h
@@ -311,6 +311,47 @@ class CORE_EXPORT QgsDataProvider : public QObject
      */
     virtual void invalidateConnections( const QString& connection ) { Q_UNUSED( connection ); }
 
+    /** Enter update mode.
+     *
+     * This is aimed at providers that can open differently the connection to
+     * the datasource, according it to be in update mode or in read-only mode.
+     * A call to this method shall be balanced with a call to leaveUpdateMode(),
+     * if this method returns true.
+     *
+     * Most providers will have an empty implementation for that method.
+     *
+     * For backward compatibility, providers that implement enterUpdateMode() should
+     * still make sure to allow editing operations to work even if enterUpdateMode()
+     * is not explicitly called.
+     *
+     * Several successive calls to enterUpdateMode() can be done. So there is
+     * a concept of stack of calls that must be handled by the provider. Only the first
+     * call to enterUpdateMode() will really turn update mode on.
+     *
+     * @return true in case of success (or no-op implementation), false in case of failure.
+     *
+     * @note added in QGIS 2.14.4
+     */
+    virtual bool enterUpdateMode() { return true; }
+
+    /** Leave update mode.
+     *
+     * This is aimed at providers that can open differently the connection to
+     * the datasource, according it to be in update mode or in read-only mode.
+     * This method shall be balanced with a succesful call to enterUpdateMode().
+     *
+     * Most providers will have an empty implementation for that method.
+     *
+     * Several successive calls to enterUpdateMode() can be done. So there is
+     * a concept of stack of calls that must be handled by the provider. Only the last
+     * call to leaveUpdateMode() will really turn update mode off.
+     *
+     * @return true in case of success (or no-op implementation), false in case of failure.
+     *
+     * @note added in QGIS 2.14.4
+     */
+    virtual bool leaveUpdateMode() { return true; }
+
   signals:
 
     /**
diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp
index b46ec67..01b8b48 100644
--- a/src/core/qgsdistancearea.cpp
+++ b/src/core/qgsdistancearea.cpp
@@ -890,6 +890,11 @@ void QgsDistanceArea::computeAreaInit()
 
 double QgsDistanceArea::computePolygonArea( const QList<QgsPoint>& points ) const
 {
+  if ( points.isEmpty() )
+  {
+    return 0;
+  }
+
   double x1, y1, x2, y2, dx, dy;
   double Qbar1, Qbar2;
   double area;
@@ -926,7 +931,8 @@ double QgsDistanceArea::computePolygonArea( const QList<QgsPoint>& points ) cons
     dx = x2 - x1;
     area += dx * ( m_Qp - getQ( y2 ) );
 
-    if (( dy = y2 - y1 ) != 0.0 )
+    dy = y2 - y1;
+    if ( !qgsDoubleNear( dy, 0.0 ) )
       area += dx * getQ( y2 ) - ( dx / dy ) * ( Qbar2 - Qbar1 );
   }
   if (( area *= m_AE ) < 0.0 )
diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp
index a478da7..26853f1 100644
--- a/src/core/qgsexpression.cpp
+++ b/src/core/qgsexpression.cpp
@@ -3751,9 +3751,11 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression *parent, const Q
       {
         return TVL_Unknown;
       }
-      else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) )
+      else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) &&
+                ( vL.type() != QVariant::String || vR.type() != QVariant::String ) )
       {
-        // do numeric comparison if both operators can be converted to numbers
+        // do numeric comparison if both operators can be converted to numbers,
+        // and they aren't both string
         double fL = getDoubleValue( vL, parent );
         ENSURE_NO_EVAL_ERROR;
         double fR = getDoubleValue( vR, parent );
@@ -3780,7 +3782,8 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression *parent, const Q
       else // both operators non-null
       {
         bool equal = false;
-        if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) )
+        if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) &&
+             ( vL.type() != QVariant::String || vR.type() != QVariant::String ) )
         {
           double fL = getDoubleValue( vL, parent );
           ENSURE_NO_EVAL_ERROR;
diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h
index 0fc468a..8eaa77a 100644
--- a/src/core/qgsfeaturerequest.h
+++ b/src/core/qgsfeaturerequest.h
@@ -370,7 +370,7 @@ class CORE_EXPORT QgsFeatureRequest
      * Return the subset of attributes which at least need to be fetched
      * @return A list of attributes to be fetched
      */
-    const QgsAttributeList& subsetOfAttributes() const { return mAttrs; }
+    QgsAttributeList subsetOfAttributes() const { return mAttrs; }
 
     //! Set a subset of attributes by names that will be fetched
     QgsFeatureRequest& setSubsetOfAttributes( const QStringList& attrNames, const QgsFields& fields );
diff --git a/src/core/qgslabelfeature.cpp b/src/core/qgslabelfeature.cpp
index 4f0cd62..4ed75b7 100644
--- a/src/core/qgslabelfeature.cpp
+++ b/src/core/qgslabelfeature.cpp
@@ -13,7 +13,6 @@
  *                                                                         *
  ***************************************************************************/
 #include "qgslabelfeature.h"
-
 #include "feature.h"
 
 
diff --git a/src/core/qgslegendrenderer.cpp b/src/core/qgslegendrenderer.cpp
index 0306007..74cbd35 100644
--- a/src/core/qgslegendrenderer.cpp
+++ b/src/core/qgslegendrenderer.cpp
@@ -161,6 +161,7 @@ QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList( QgsLayerTreeGr
 
       // Group subitems
       QList<Atom> groupAtoms = createAtomList( nodeGroup, splitLayer );
+      bool hasSubItems = groupAtoms.size() > 0;
 
       if ( nodeLegendStyle( nodeGroup ) != QgsComposerLegendStyle::Hidden )
       {
@@ -188,7 +189,12 @@ QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList( QgsLayerTreeGr
           groupAtoms.append( atom );
         }
       }
-      atoms.append( groupAtoms );
+
+      if ( hasSubItems ) //leave away groups without content
+      {
+        atoms.append( groupAtoms );
+      }
+
     }
     else if ( QgsLayerTree::isLayer( node ) )
     {
diff --git a/src/core/qgslogger.cpp b/src/core/qgslogger.cpp
index 250e050..c45066c 100644
--- a/src/core/qgslogger.cpp
+++ b/src/core/qgslogger.cpp
@@ -103,7 +103,7 @@ void QgsLogger::debug( const QString& msg, int debuglevel, const char* file, con
 
   if ( sLogFile.isEmpty() )
   {
-    qDebug( "%s", m.toLocal8Bit().constData() );
+    qDebug( "%s", m.toUtf8().constData() );
   }
   else
   {
diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp
index d710c18..08f9338 100644
--- a/src/core/qgsmaplayer.cpp
+++ b/src/core/qgsmaplayer.cpp
@@ -48,6 +48,7 @@
 #include "qgsvectorlayer.h"
 #include "qgsvectordataprovider.h"
 #include "qgsmaplayerregistry.h"
+#include "qgsxmlutils.h"
 
 
 QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
@@ -425,6 +426,12 @@ bool QgsMapLayer::readLayerXML( const QDomElement& layerElement )
   setMinimumScale( layerElement.attribute( "minimumScale" ).toFloat() );
   setMaximumScale( layerElement.attribute( "maximumScale" ).toFloat() );
 
+  QDomNode extentNode = layerElement.namedItem( "extent" );
+  if ( !extentNode.isNull() )
+  {
+    setExtent( QgsXmlUtils::readRectangle( extentNode.toElement() ) );
+  }
+
   // set name
   mnl = layerElement.namedItem( "layername" );
   mne = mnl.toElement();
@@ -531,6 +538,11 @@ bool QgsMapLayer::writeLayerXML( QDomElement& layerElement, QDomDocument& docume
   layerElement.setAttribute( "minimumScale", QString::number( minimumScale() ) );
   layerElement.setAttribute( "maximumScale", QString::number( maximumScale() ) );
 
+  if ( !mExtent.isNull() )
+  {
+    layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent, document ) );
+  }
+
   // ID
   QDomElement layerId = document.createElement( "id" );
   QDomText layerIdText = document.createTextNode( id() );
diff --git a/src/core/qgsmaplayerregistry.cpp b/src/core/qgsmaplayerregistry.cpp
index 6a9cbe0..f13a913 100644
--- a/src/core/qgsmaplayerregistry.cpp
+++ b/src/core/qgsmaplayerregistry.cpp
@@ -127,27 +127,32 @@ void QgsMapLayerRegistry::removeMapLayers( const QStringList& theLayerIds )
 
 void QgsMapLayerRegistry::removeMapLayers( const QList<QgsMapLayer*>& layers )
 {
+  if ( layers.isEmpty() )
+    return;
+
   QStringList layerIds;
+  QList<QgsMapLayer*> layerList;
 
   Q_FOREACH ( QgsMapLayer* layer, layers )
   {
-    if ( layer )
+    // check layer and the registry contains it
+    if ( layer && mMapLayers.contains( layer->id() ) )
+    {
       layerIds << layer->id();
+      layerList << layer;
+    }
   }
 
   emit layersWillBeRemoved( layerIds );
-  emit layersWillBeRemoved( layers );
+  emit layersWillBeRemoved( layerList );
 
-  Q_FOREACH ( QgsMapLayer* lyr, layers )
+  Q_FOREACH ( QgsMapLayer* lyr, layerList )
   {
-    if ( !lyr )
-      continue;
-
     QString myId( lyr->id() );
+    emit layerWillBeRemoved( myId );
+    emit layerWillBeRemoved( lyr );
     if ( mOwnedLayers.contains( lyr ) )
     {
-      emit layerWillBeRemoved( myId );
-      emit layerWillBeRemoved( lyr );
       delete lyr;
       mOwnedLayers.remove( lyr );
     }
diff --git a/src/core/qgsmaprenderer.h b/src/core/qgsmaprenderer.h
index 15ebb87..648a38a 100644
--- a/src/core/qgsmaprenderer.h
+++ b/src/core/qgsmaprenderer.h
@@ -46,9 +46,10 @@ class QgsDiagramLayerSettings;
 class CORE_EXPORT QgsLabelPosition
 {
   public:
-    QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, const QString& labeltext, const QFont& labelfont, bool upside_down, bool diagram = false, bool pinned = false ):
-        featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), labelText( labeltext ), labelFont( labelfont ), upsideDown( upside_down ), isDiagram( diagram ), isPinned( pinned ) {}
-    QgsLabelPosition(): featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), labelText( "" ), labelFont( QFont() ), upsideDown( false ), isDiagram( false ), isPinned( false ) {}
+    QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, const QString& labeltext, const QFont& labelfont, bool upside_down, bool diagram = false, bool pinned = false )
+        : featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), labelText( labeltext ), labelFont( labelfont ), upsideDown( upside_down ), isDiagram( diagram ), isPinned( pinned ) {}
+    QgsLabelPosition()
+        : featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), labelText( "" ), labelFont( QFont() ), upsideDown( false ), isDiagram( false ), isPinned( false ) {}
     int featureId;
     double rotation;
     QVector< QgsPoint > cornerPoints;
@@ -72,7 +73,7 @@ class CORE_EXPORT QgsLabelingEngineInterface
 
     //! called when we're going to start with rendering
     //! @deprecated since 2.4 - use override with QgsMapSettings
-    Q_DECL_DEPRECATED virtual void init( QgsMapRenderer* mp ) = 0;
+    Q_DECL_DEPRECATED virtual void init( QgsMapRenderer *mp ) = 0;
     //! called when we're going to start with rendering
     virtual void init( const QgsMapSettings& mapSettings ) = 0;
     //! called to find out whether the layer is used for labeling
@@ -85,19 +86,19 @@ class CORE_EXPORT QgsLabelingEngineInterface
     virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0;
     //! returns PAL layer settings for a registered layer
     //! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer()
-    Q_DECL_DEPRECATED virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0;
+    Q_DECL_DEPRECATED virtual QgsPalLayerSettings &layer( const QString &layerName ) = 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, QStringList &attrNames, QgsRenderContext &ctx )
     { Q_UNUSED( layer ); Q_UNUSED( attrNames ); Q_UNUSED( ctx ); return 0; }
     //! adds a diagram layer to the labeling engine
     //! @deprecated since 2.12 - use prepareDiagramLayer()
-    Q_DECL_DEPRECATED virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s )
+    Q_DECL_DEPRECATED virtual int addDiagramLayer( QgsVectorLayer *layer, const QgsDiagramLayerSettings *s )
     { Q_UNUSED( layer ); Q_UNUSED( s ); return 0; }
     //! called for every feature
-    virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null ) = 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 )
     { Q_UNUSED( layerID ); Q_UNUSED( feat ); Q_UNUSED( context ); }
     //! called when the map is drawn and labels should be placed
     virtual void drawLabeling( QgsRenderContext& context ) = 0;
diff --git a/src/core/qgsmapsettings.h b/src/core/qgsmapsettings.h
index 1a80b5e..24aa1c8 100644
--- a/src/core/qgsmapsettings.h
+++ b/src/core/qgsmapsettings.h
@@ -125,10 +125,10 @@ class CORE_EXPORT QgsMapSettings
     //! Get color that is used for drawing of selected vector features
     QColor selectionColor() const { return mSelectionColor; }
 
-    //! Enumeration of flags that adjust the way how map is rendered
+    //! Enumeration of flags that adjust the way the map is rendered
     enum Flag
     {
-      Antialiasing             = 0x01,  //!< Enable anti-aliasin for map rendering
+      Antialiasing             = 0x01,  //!< Enable anti-aliasing for map rendering
       DrawEditingInfo          = 0x02,  //!< Enable drawing of vertex markers for layers in editing mode
       ForceVectorOutput        = 0x04,  //!< Vector graphics should not be cached and drawn as raster images
       UseAdvancedEffects       = 0x08,  //!< Enable layer transparency and blending effects
diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp
index 7fc6cd3..d88b771 100644
--- a/src/core/qgspallabeling.cpp
+++ b/src/core/qgspallabeling.cpp
@@ -2093,13 +2093,11 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
 #endif
 }
 
-void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, const QString& dxfLayer, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
+void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
 {
   // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngineV2 (labelFeature is set)
   Q_ASSERT( labelFeature );
 
-  Q_UNUSED( dxfLayer ); // now handled in QgsDxfLabelProvider
-
   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
   mCurFeat = &f;
 
@@ -2114,7 +2112,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
   {
     if ( isObstacle )
     {
-      registerObstacleFeature( f, context, QString(), labelFeature, obstacleGeometry );
+      registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
     }
     return;
   }
@@ -2900,7 +2898,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
     {
       distance *= vectorScaleFactor;
     }
-    double d = qAbs( ptOne.x() - ptZero.x() ) * distance;
+    double d = sqrt( ptOne.sqrDist( ptZero ) ) * distance;
     ( *labelFeature )->setDistLabel( d );
   }
 
@@ -2966,10 +2964,8 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
   lf->setDataDefinedValues( dataDefinedValues );
 }
 
-void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, const QString& dxfLayer, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
+void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
 {
-  Q_UNUSED( dxfLayer ); // now handled in QgsDxfLabelProvider
-
   mCurFeat = &f;
 
   const QgsGeometry* geom = nullptr;
@@ -3963,9 +3959,8 @@ int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLaye
   return 0;
 }
 
-void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, QgsRenderContext &context, const QString& dxfLayer )
+void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, QgsRenderContext &context )
 {
-  Q_UNUSED( dxfLayer ); // now handled by QgsDxfLabelProvider
   if ( QgsVectorLayerLabelProvider* provider = mLabelProviders.value( layerID, nullptr ) )
     provider->registerFeature( f, context );
 }
diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h
index da9e259..c966c09 100644
--- a/src/core/qgspallabeling.h
+++ b/src/core/qgspallabeling.h
@@ -58,6 +58,7 @@ class QgsMapSettings;
 class QgsLabelFeature;
 class QgsLabelingEngineV2;
 class QgsVectorLayerLabelProvider;
+class QgsDxfExport;
 class QgsVectorLayerDiagramProvider;
 
 class CORE_EXPORT QgsPalLayerSettings
@@ -533,7 +534,6 @@ class CORE_EXPORT QgsPalLayerSettings
      * @param f feature to label
      * @param context render context. The QgsExpressionContext contained within the render context
      * must have already had the feature and fields sets prior to calling this method.
-     * @param dxfLayer dxfLayer name
      * @param labelFeature if using QgsLabelingEngineV2, this will receive the label feature. Not available
      * in Python bindings.
      * @param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry
@@ -542,7 +542,7 @@ class CORE_EXPORT QgsPalLayerSettings
      * the feature's original geometry will be used as an obstacle for labels. Not available
      * in Python bindings.
      */
-    void registerFeature( QgsFeature& f, QgsRenderContext& context, const QString& dxfLayer, QgsLabelFeature** labelFeature = nullptr, QgsGeometry* obstacleGeometry = nullptr );
+    void registerFeature( QgsFeature& f, QgsRenderContext& context, QgsLabelFeature** labelFeature = nullptr, QgsGeometry* obstacleGeometry = nullptr );
 
     void readFromLayer( QgsVectorLayer* layer );
     void writeToLayer( QgsVectorLayer* layer );
@@ -712,7 +712,7 @@ class CORE_EXPORT QgsPalLayerSettings
 
     /** Registers a feature as an obstacle only (no label rendered)
      */
-    void registerObstacleFeature( QgsFeature &f, QgsRenderContext &context, const QString& dxfLayer, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry = nullptr );
+    void registerObstacleFeature( QgsFeature &f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry = nullptr );
 
     QMap<DataDefinedProperties, QVariant> dataDefinedValues;
     QgsExpression* expression;
@@ -949,9 +949,8 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
      * @param feat feature to label
      * @param context render context. The QgsExpressionContext contained within the render context
      * must have already had the feature and fields sets prior to calling this method.
-     * @param dxfLayer dxfLayer name
      */
-    virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null ) override;
+    virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context ) override;
 
     virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context ) override;
     //! called when the map is drawn and labels should be placed
@@ -1057,6 +1056,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
                                        const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues );
 
     friend class QgsVectorLayerLabelProvider; // to allow calling the static methods above
+    friend class QgsDxfExport;                // to allow calling the static methods above
 
     void deleteTemporaryData();
 
diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp
index d6e8fe6..0b9ef79 100644
--- a/src/core/qgsrendercontext.cpp
+++ b/src/core/qgsrendercontext.cpp
@@ -123,6 +123,7 @@ QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings& mapSet
   ctx.setFlag( DrawSelection, mapSettings.testFlag( QgsMapSettings::DrawSelection ) );
   ctx.setFlag( DrawSymbolBounds, mapSettings.testFlag( QgsMapSettings::DrawSymbolBounds ) );
   ctx.setFlag( RenderMapTile, mapSettings.testFlag( QgsMapSettings::RenderMapTile ) );
+  ctx.setFlag( Antialiasing, mapSettings.testFlag( QgsMapSettings::Antialiasing ) );
   ctx.setRasterScaleFactor( 1.0 );
   ctx.setScaleFactor( mapSettings.outputDpi() / 25.4 ); // = pixels per mm
   ctx.setRendererScale( mapSettings.scale() );
diff --git a/src/core/qgsrendercontext.h b/src/core/qgsrendercontext.h
index 58df97a..be7d132 100644
--- a/src/core/qgsrendercontext.h
+++ b/src/core/qgsrendercontext.h
@@ -63,6 +63,7 @@ class CORE_EXPORT QgsRenderContext
       DrawSelection            = 0x10,  //!< Whether vector selections should be shown in the rendered map
       DrawSymbolBounds         = 0x20,  //!< Draw bounds of symbols (for debugging/testing)
       RenderMapTile            = 0x40,  //!< Draw map such that there are no problems between adjacent tiles
+      Antialiasing             = 0x80,  //!< Use antialiasing while drawing
     };
     Q_DECLARE_FLAGS( Flags, Flag )
 
diff --git a/src/core/qgsrulebasedlabeling.cpp b/src/core/qgsrulebasedlabeling.cpp
index 54fbf21..4f89475 100644
--- a/src/core/qgsrulebasedlabeling.cpp
+++ b/src/core/qgsrulebasedlabeling.cpp
@@ -15,12 +15,11 @@
 #include "qgsrulebasedlabeling.h"
 
 
-
 QgsRuleBasedLabelProvider::QgsRuleBasedLabelProvider( const QgsRuleBasedLabeling& rules, QgsVectorLayer* layer, bool withFeatureLoop )
     : QgsVectorLayerLabelProvider( layer, withFeatureLoop )
     , mRules( rules )
 {
-  mRules.rootRule()->createSubProviders( layer, mSubProviders );
+  mRules.rootRule()->createSubProviders( layer, mSubProviders, this );
 }
 
 QgsRuleBasedLabelProvider::~QgsRuleBasedLabelProvider()
@@ -28,6 +27,10 @@ QgsRuleBasedLabelProvider::~QgsRuleBasedLabelProvider()
   // sub-providers owned by labeling engine
 }
 
+QgsVectorLayerLabelProvider *QgsRuleBasedLabelProvider::createProvider( QgsVectorLayer *layer, bool withFeatureLoop, const QgsPalLayerSettings *settings )
+{
+  return new QgsVectorLayerLabelProvider( layer, withFeatureLoop, settings );
+}
 
 bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames )
 {
@@ -213,19 +216,20 @@ QDomElement QgsRuleBasedLabeling::Rule::save( QDomDocument& doc ) const
   return ruleElem;
 }
 
-void QgsRuleBasedLabeling::Rule::createSubProviders( QgsVectorLayer* layer, QgsRuleBasedLabeling::RuleToProviderMap& subProviders )
+void QgsRuleBasedLabeling::Rule::createSubProviders( QgsVectorLayer* layer, QgsRuleBasedLabeling::RuleToProviderMap& subProviders, QgsRuleBasedLabelProvider *provider )
 {
   if ( mSettings )
   {
     // add provider!
-    QgsVectorLayerLabelProvider* p = new QgsVectorLayerLabelProvider( layer, false, mSettings );
+    QgsVectorLayerLabelProvider *p = provider->createProvider( layer, false, mSettings );
+    delete subProviders.value( this, nullptr );
     subProviders[this] = p;
   }
 
   // call recursively
   Q_FOREACH ( Rule* rule, mChildren )
   {
-    rule->createSubProviders( layer, subProviders );
+    rule->createSubProviders( layer, subProviders, provider );
   }
 }
 
@@ -262,6 +266,8 @@ QgsRuleBasedLabeling::Rule::RegisterResult QgsRuleBasedLabeling::Rule::registerF
 
   bool registered = false;
 
+  Q_ASSERT( !mSettings == subProviders.contains( this ) );
+
   // do we have active subprovider for the rule?
   if ( subProviders.contains( this ) && mIsActive )
   {
diff --git a/src/core/qgsrulebasedlabeling.h b/src/core/qgsrulebasedlabeling.h
index ac4d0fb..8d39b8e 100644
--- a/src/core/qgsrulebasedlabeling.h
+++ b/src/core/qgsrulebasedlabeling.h
@@ -19,6 +19,7 @@
 #include <QMap>
 
 #include "qgsvectorlayerlabeling.h"
+#include "qgsvectorlayerlabelprovider.h"
 
 class QDomDocument;
 class QDomElement;
@@ -28,6 +29,7 @@ class QgsFeature;
 class QgsPalLayerSettings;
 class QgsRenderContext;
 class QgsGeometry;
+class QgsRuleBasedLabelProvider;
 
 /**
  * @class QgsRuleBasedLabeling
@@ -42,7 +44,6 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
     typedef QList<Rule*> RuleList;
     typedef QMap<Rule*, QgsVectorLayerLabelProvider*> RuleToProviderMap;
 
-
     /**
      * @class QgsRuleBasedLabeling::Rule
      * @note not available in Python bindings
@@ -214,7 +215,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
         // evaluation
 
         //! add providers
-        void createSubProviders( QgsVectorLayer* layer, RuleToProviderMap& subProviders );
+        void createSubProviders( QgsVectorLayer* layer, RuleToProviderMap& subProviders, QgsRuleBasedLabelProvider *provider );
 
         //! call prepare() on sub-providers and populate attributeNames
         void prepare( const QgsRenderContext& context, QStringList& attributeNames, RuleToProviderMap& subProviders );
@@ -285,15 +286,13 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
 
     virtual QString type() const override;
     virtual QDomElement save( QDomDocument& doc ) const override;
-    virtual QgsVectorLayerLabelProvider* provider( QgsVectorLayer* layer ) const override;
+    virtual QgsVectorLayerLabelProvider *provider( QgsVectorLayer* layer ) const override;
 
   protected:
     Rule* mRootRule;
 };
 
 
-#include "qgsvectorlayerlabelprovider.h"
-
 /**
  * @class QgsRuleBasedLabelProvider
  * @note not available in Python bindings
@@ -312,6 +311,7 @@ class CORE_EXPORT QgsRuleBasedLabelProvider : public QgsVectorLayerLabelProvider
     virtual void registerFeature( QgsFeature& feature, QgsRenderContext& context, QgsGeometry* obstacleGeometry = nullptr ) override;
 
     // new methods
+    virtual QgsVectorLayerLabelProvider *createProvider( QgsVectorLayer *layer, bool withFeatureLoop, const QgsPalLayerSettings *settings );
 
     virtual QList<QgsAbstractLabelProvider*> subProviders() override;
 
diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp
index 851954d..c8d4d71 100644
--- a/src/core/qgsvectorlayer.cpp
+++ b/src/core/qgsvectorlayer.cpp
@@ -379,6 +379,7 @@ void QgsVectorLayer::reload()
   if ( mDataProvider )
   {
     mDataProvider->reloadData();
+    updateFields();
   }
 }
 
@@ -1380,6 +1381,8 @@ bool QgsVectorLayer::startEditing()
 
   emit beforeEditingStarted();
 
+  mDataProvider->enterUpdateMode();
+
   if ( mDataProvider->transaction() )
   {
     mEditBuffer = new QgsVectorLayerEditPassthrough( this );
@@ -2334,6 +2337,8 @@ bool QgsVectorLayer::commitChanges()
   updateFields();
   mDataProvider->updateExtents();
 
+  mDataProvider->leaveUpdateMode();
+
   emit repaintRequested();
 
   return success;
@@ -2384,6 +2389,8 @@ bool QgsVectorLayer::rollBack( bool deleteBuffer )
   if ( rollbackExtent )
     updateExtents();
 
+  mDataProvider->leaveUpdateMode();
+
   emit repaintRequested();
   return true;
 }
@@ -2931,87 +2938,81 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
   }
 
   QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
-  if ( origin == QgsFields::OriginUnknown )
-  {
-    return;
-  }
-
-  if ( origin == QgsFields::OriginProvider ) //a provider field
+  switch ( origin )
   {
-    mDataProvider->uniqueValues( index, uniqueValues, limit );
+    case QgsFields::OriginUnknown:
+      return;
 
-    if ( mEditBuffer )
+    case QgsFields::OriginProvider: //a provider field
     {
-      QSet<QString> vals;
-      Q_FOREACH ( const QVariant& v, uniqueValues )
-      {
-        vals << v.toString();
-      }
+      mDataProvider->uniqueValues( index, uniqueValues, limit );
 
-      QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() );
-      while ( it.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) )
+      if ( mEditBuffer )
       {
-        it.next();
-        QVariant v = it.value().value( index );
-        if ( v.isValid() )
+        QSet<QString> vals;
+        Q_FOREACH ( const QVariant& v, uniqueValues )
+        {
+          vals << v.toString();
+        }
+
+        QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() );
+        while ( it.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) )
         {
-          QString vs = v.toString();
-          if ( !vals.contains( vs ) )
+          it.next();
+          QVariant v = it.value().value( index );
+          if ( v.isValid() )
           {
-            vals << vs;
-            uniqueValues << v;
+            QString vs = v.toString();
+            if ( !vals.contains( vs ) )
+            {
+              vals << vs;
+              uniqueValues << v;
+            }
           }
         }
       }
-    }
-
-    return;
-  }
-  else if ( origin == QgsFields::OriginJoin )
-  {
-    int sourceLayerIndex;
-    const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
-    Q_ASSERT( join );
-
-    QgsVectorLayer *vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) );
-
-    if ( vl )
-      vl->dataProvider()->uniqueValues( sourceLayerIndex, uniqueValues, limit );
 
-    return;
-  }
-  else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
-  {
-    // the layer is editable, but in certain cases it can still be avoided going through all features
-    if ( origin == QgsFields::OriginEdit && mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() )
-    {
-      mDataProvider->uniqueValues( index, uniqueValues, limit );
       return;
     }
 
-    // we need to go through each feature
-    QgsAttributeList attList;
-    attList << index;
+    case QgsFields::OriginEdit:
+      // the layer is editable, but in certain cases it can still be avoided going through all features
+      if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
+           mEditBuffer->mAddedFeatures.isEmpty() &&
+           !mEditBuffer->mDeletedAttributeIds.contains( index ) &&
+           mEditBuffer->mChangedAttributeValues.isEmpty() )
+      {
+        mDataProvider->uniqueValues( index, uniqueValues, limit );
+        return;
+      }
+      FALLTHROUGH;
+      //we need to go through each feature
+    case QgsFields::OriginJoin:
+    case QgsFields::OriginExpression:
+    {
+      QgsAttributeList attList;
+      attList << index;
 
-    QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
-                                          .setFlags( QgsFeatureRequest::NoGeometry )
-                                          .setSubsetOfAttributes( attList ) );
+      QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
+                                            .setFlags( QgsFeatureRequest::NoGeometry )
+                                            .setSubsetOfAttributes( attList ) );
 
-    QgsFeature f;
-    QVariant currentValue;
-    QHash<QString, QVariant> val;
-    while ( fit.nextFeature( f ) )
-    {
-      currentValue = f.attribute( index );
-      val.insert( currentValue.toString(), currentValue );
-      if ( limit >= 0 && val.size() >= limit )
+      QgsFeature f;
+      QVariant currentValue;
+      QHash<QString, QVariant> val;
+      while ( fit.nextFeature( f ) )
       {
-        break;
+        currentValue = f.attribute( index );
+        val.insert( currentValue.toString(), currentValue );
+        if ( limit >= 0 && val.size() >= limit )
+        {
+          break;
+        }
       }
-    }
 
-    uniqueValues = val.values();
-    return;
+      uniqueValues = val.values();
+      return;
+    }
   }
 
   Q_ASSERT_X( false, "QgsVectorLayer::uniqueValues()", "Unknown source of the field!" );
@@ -3025,58 +3026,52 @@ QVariant QgsVectorLayer::minimumValue( int index )
   }
 
   QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
-  if ( origin == QgsFields::OriginUnknown )
-  {
-    return QVariant();
-  }
 
-  if ( origin == QgsFields::OriginProvider ) //a provider field
-  {
-    return mDataProvider->minimumValue( index );
-  }
-  else if ( origin == QgsFields::OriginJoin )
+  switch ( origin )
   {
-    int sourceLayerIndex;
-    const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
-    Q_ASSERT( join );
+    case QgsFields::OriginUnknown:
+      return QVariant();
 
-    QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) );
-    Q_ASSERT( vl );
+    case QgsFields::OriginProvider: //a provider field
+      return mDataProvider->minimumValue( index );
 
-    return vl->minimumValue( sourceLayerIndex );
-  }
-  else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
-  {
-    // the layer is editable, but in certain cases it can still be avoided going through all features
-    if ( origin == QgsFields::OriginEdit &&
-         mEditBuffer->mDeletedFeatureIds.isEmpty() &&
-         mEditBuffer->mAddedFeatures.isEmpty() && !
-         mEditBuffer->mDeletedAttributeIds.contains( index ) &&
-         mEditBuffer->mChangedAttributeValues.isEmpty() )
+    case QgsFields::OriginEdit:
     {
-      return mDataProvider->minimumValue( index );
+      // the layer is editable, but in certain cases it can still be avoided going through all features
+      if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
+           mEditBuffer->mAddedFeatures.isEmpty() && !
+           mEditBuffer->mDeletedAttributeIds.contains( index ) &&
+           mEditBuffer->mChangedAttributeValues.isEmpty() )
+      {
+        return mDataProvider->minimumValue( index );
+      }
     }
+    FALLTHROUGH;
+    // no choice but to go through all features
+    case QgsFields::OriginExpression:
+    case QgsFields::OriginJoin:
+    {
+      // we need to go through each feature
+      QgsAttributeList attList;
+      attList << index;
 
-    // we need to go through each feature
-    QgsAttributeList attList;
-    attList << index;
-
-    QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
-                                          .setFlags( QgsFeatureRequest::NoGeometry )
-                                          .setSubsetOfAttributes( attList ) );
+      QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
+                                            .setFlags( QgsFeatureRequest::NoGeometry )
+                                            .setSubsetOfAttributes( attList ) );
 
-    QgsFeature f;
-    double minimumValue = std::numeric_limits<double>::max();
-    double currentValue = 0;
-    while ( fit.nextFeature( f ) )
-    {
-      currentValue = f.attribute( index ).toDouble();
-      if ( currentValue < minimumValue )
+      QgsFeature f;
+      double minimumValue = std::numeric_limits<double>::max();
+      double currentValue = 0;
+      while ( fit.nextFeature( f ) )
       {
-        minimumValue = currentValue;
+        currentValue = f.attribute( index ).toDouble();
+        if ( currentValue < minimumValue )
+        {
+          minimumValue = currentValue;
+        }
       }
+      return QVariant( minimumValue );
     }
-    return QVariant( minimumValue );
   }
 
   Q_ASSERT_X( false, "QgsVectorLayer::minimumValue()", "Unknown source of the field!" );
@@ -3091,58 +3086,49 @@ QVariant QgsVectorLayer::maximumValue( int index )
   }
 
   QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index );
-  if ( origin == QgsFields::OriginUnknown )
+  switch ( origin )
   {
-    return QVariant();
-  }
+    case QgsFields::OriginUnknown:
+      return QVariant();
 
-  if ( origin == QgsFields::OriginProvider ) //a provider field
-  {
-    return mDataProvider->maximumValue( index );
-  }
-  else if ( origin == QgsFields::OriginJoin )
-  {
-    int sourceLayerIndex;
-    const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex );
-    Q_ASSERT( join );
+    case QgsFields::OriginProvider: //a provider field
+      return mDataProvider->maximumValue( index );
 
-    QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) );
-    Q_ASSERT( vl );
+    case QgsFields::OriginEdit:
+      // the layer is editable, but in certain cases it can still be avoided going through all features
+      if ( mEditBuffer->mDeletedFeatureIds.isEmpty() &&
+           mEditBuffer->mAddedFeatures.isEmpty() &&
+           !mEditBuffer->mDeletedAttributeIds.contains( index ) &&
+           mEditBuffer->mChangedAttributeValues.isEmpty() )
+      {
+        return mDataProvider->maximumValue( index );
+      }
 
-    return vl->maximumValue( sourceLayerIndex );
-  }
-  else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression )
-  {
-    // the layer is editable, but in certain cases it can still be avoided going through all features
-    if ( origin == QgsFields::OriginEdit &&
-         mEditBuffer->mDeletedFeatureIds.isEmpty() &&
-         mEditBuffer->mAddedFeatures.isEmpty() &&
-         !mEditBuffer->mDeletedAttributeIds.contains( index ) &&
-         mEditBuffer->mChangedAttributeValues.isEmpty() )
+      FALLTHROUGH;
+      //no choice but to go through each feature
+    case QgsFields::OriginJoin:
+    case QgsFields::OriginExpression:
     {
-      return mDataProvider->maximumValue( index );
-    }
-
-    // we need to go through each feature
-    QgsAttributeList attList;
-    attList << index;
+      QgsAttributeList attList;
+      attList << index;
 
-    QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
-                                          .setFlags( QgsFeatureRequest::NoGeometry )
-                                          .setSubsetOfAttributes( attList ) );
+      QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
+                                            .setFlags( QgsFeatureRequest::NoGeometry )
+                                            .setSubsetOfAttributes( attList ) );
 
-    QgsFeature f;
-    double maximumValue = -std::numeric_limits<double>::max();
-    double currentValue = 0;
-    while ( fit.nextFeature( f ) )
-    {
-      currentValue = f.attribute( index ).toDouble();
-      if ( currentValue > maximumValue )
+      QgsFeature f;
+      double maximumValue = -std::numeric_limits<double>::max();
+      double currentValue = 0;
+      while ( fit.nextFeature( f ) )
       {
-        maximumValue = currentValue;
+        currentValue = f.attribute( index ).toDouble();
+        if ( currentValue > maximumValue )
+        {
+          maximumValue = currentValue;
+        }
       }
+      return QVariant( maximumValue );
     }
-    return QVariant( maximumValue );
   }
 
   Q_ASSERT_X( false, "QgsVectorLayer::maximumValue()", "Unknown source of the field!" );
diff --git a/src/core/qgsvectorlayerdiagramprovider.cpp b/src/core/qgsvectorlayerdiagramprovider.cpp
index 52467ca..13daa59 100644
--- a/src/core/qgsvectorlayerdiagramprovider.cpp
+++ b/src/core/qgsvectorlayerdiagramprovider.cpp
@@ -368,6 +368,6 @@ QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& fea
 
   QgsPoint ptZero = mSettings.xform->toMapCoordinates( 0, 0 );
   QgsPoint ptOne = mSettings.xform->toMapCoordinates( 1, 0 );
-  lf->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * mSettings.dist );
+  lf->setDistLabel( sqrt( ptOne.sqrDist( ptZero ) ) * mSettings.dist );
   return lf;
 }
diff --git a/src/core/qgsvectorlayerfeatureiterator.cpp b/src/core/qgsvectorlayerfeatureiterator.cpp
index 184e5ca..7448ba9 100644
--- a/src/core/qgsvectorlayerfeatureiterator.cpp
+++ b/src/core/qgsvectorlayerfeatureiterator.cpp
@@ -538,6 +538,7 @@ void QgsVectorLayerFeatureIterator::prepareExpressions()
   mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
   mExpressionContext->setFields( mSource->mFields );
 
+  QList< int > virtualFieldsToFetch;
   for ( int i = 0; i < mSource->mFields.count(); i++ )
   {
     if ( mSource->mFields.fieldOrigin( i ) == QgsFields::OriginExpression )
@@ -546,38 +547,53 @@ void QgsVectorLayerFeatureIterator::prepareExpressions()
       if ( !( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
            || mRequest.subsetOfAttributes().contains( i ) )
       {
-        int oi = mSource->mFields.fieldOriginIndex( i );
-        QgsExpression* exp = new QgsExpression( exps[oi].cachedExpression );
-
-        QgsDistanceArea da;
-        da.setSourceCrs( mSource->mCrsId );
-        da.setEllipsoidalMode( true );
-        da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
-        exp->setGeomCalculator( da );
-        exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
-        exp->setAreaUnits( QgsProject::instance()->areaUnits() );
-
-        exp->prepare( mExpressionContext.data() );
-        mExpressionFieldInfo.insert( i, exp );
-
-        if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
-        {
-          QgsAttributeList attrs;
-          Q_FOREACH ( const QString& col, exp->referencedColumns() )
-          {
-            attrs.append( mSource->mFields.fieldNameIndex( col ) );
-          }
-
-          mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() + attrs );
-        }
-
-        if ( exp->needsGeometry() )
-        {
-          mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
-        }
+        virtualFieldsToFetch << i;
       }
     }
   }
+
+  QList< int > virtualFieldsProcessed;
+  while ( !virtualFieldsToFetch.isEmpty() )
+  {
+    int fieldIdx = virtualFieldsToFetch.takeFirst();
+    if ( virtualFieldsProcessed.contains( fieldIdx ) )
+      continue;
+
+    virtualFieldsProcessed << fieldIdx;
+
+
+    int oi = mSource->mFields.fieldOriginIndex( fieldIdx );
+    QgsExpression* exp = new QgsExpression( exps[oi].cachedExpression );
+
+    QgsDistanceArea da;
+    da.setSourceCrs( mSource->mCrsId );
+    da.setEllipsoidalMode( true );
+    da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
+    exp->setGeomCalculator( da );
+    exp->setDistanceUnits( QgsProject::instance()->distanceUnits() );
+    exp->setAreaUnits( QgsProject::instance()->areaUnits() );
+
+    exp->prepare( mExpressionContext.data() );
+    mExpressionFieldInfo.insert( fieldIdx, exp );
+
+    Q_FOREACH ( const QString& col, exp->referencedColumns() )
+    {
+      int dependantFieldIdx = mSource->mFields.fieldNameIndex( col );
+      if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
+      {
+        mRequest.setSubsetOfAttributes( mRequest.subsetOfAttributes() << dependantFieldIdx );
+      }
+      // also need to fetch this dependant field
+      if ( mSource->mFields.fieldOrigin( dependantFieldIdx ) == QgsFields::OriginExpression )
+        virtualFieldsToFetch << dependantFieldIdx;
+    }
+
+    if ( exp->needsGeometry() )
+    {
+      mRequest.setFlags( mRequest.flags() & ~QgsFeatureRequest::NoGeometry );
+    }
+  }
+
 }
 
 void QgsVectorLayerFeatureIterator::addJoinedAttributes( QgsFeature &f )
diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp
index 4807264..9780a60 100644
--- a/src/core/qgsvectorlayerlabelprovider.cpp
+++ b/src/core/qgsvectorlayerlabelprovider.cpp
@@ -316,7 +316,7 @@ QList<QgsLabelFeature*> QgsVectorLayerLabelProvider::labelFeatures( QgsRenderCon
 void QgsVectorLayerLabelProvider::registerFeature( QgsFeature& feature, QgsRenderContext& context, QgsGeometry* obstacleGeometry )
 {
   QgsLabelFeature* label = nullptr;
-  mSettings.registerFeature( feature, context, QString(), &label, obstacleGeometry );
+  mSettings.registerFeature( feature, context, &label, obstacleGeometry );
   if ( label )
     mLabels << label;
 }
diff --git a/src/core/qgsvectorlayerlabelprovider.h b/src/core/qgsvectorlayerlabelprovider.h
index 3ad6049..fb3efaf 100644
--- a/src/core/qgsvectorlayerlabelprovider.h
+++ b/src/core/qgsvectorlayerlabelprovider.h
@@ -116,5 +116,4 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider
     QList<QgsLabelFeature*> mLabels;
 };
 
-
 #endif // QGSVECTORLAYERLABELPROVIDER_H
diff --git a/src/core/raster/qgscontrastenhancement.cpp b/src/core/raster/qgscontrastenhancement.cpp
index 1fa98b2..c1d323a 100644
--- a/src/core/raster/qgscontrastenhancement.cpp
+++ b/src/core/raster/qgscontrastenhancement.cpp
@@ -25,6 +25,7 @@ class originally created circa 2004 by T.Sutton, Gary E.Sherman, Steve Halasz
 #include "qgslinearminmaxenhancement.h"
 #include "qgslinearminmaxenhancementwithclip.h"
 #include "qgscliptominmaxenhancement.h"
+#include "qgsrasterblock.h"
 #include <QDomDocument>
 #include <QDomElement>
 
@@ -361,13 +362,13 @@ void QgsContrastEnhancement::writeXML( QDomDocument& doc, QDomElement& parentEle
 {
   //minimum value
   QDomElement minElem = doc.createElement( "minValue" );
-  QDomText minText = doc.createTextNode( QString::number( mMinimumValue ) );
+  QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
   minElem.appendChild( minText );
   parentElem.appendChild( minElem );
 
   //maximum value
   QDomElement maxElem = doc.createElement( "maxValue" );
-  QDomText maxText = doc.createTextNode( QString::number( mMaximumValue ) );
+  QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
   maxElem.appendChild( maxText );
   parentElem.appendChild( maxElem );
 
diff --git a/src/core/raster/qgsrasterfilewriter.cpp b/src/core/raster/qgsrasterfilewriter.cpp
index af388d5..aa8c474 100644
--- a/src/core/raster/qgsrasterfilewriter.cpp
+++ b/src/core/raster/qgsrasterfilewriter.cpp
@@ -355,7 +355,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
 
   // hmm why is there a for(;;) here ..
   // not good coding practice IMHO, it might be better to use [ for() and break ] or  [ while (test) ]
-  for ( ;; )
+  Q_FOREVER
   {
     for ( int i = 1; i <= nBands; ++i )
     {
diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp
index 2e41268..0fb031e 100644
--- a/src/core/raster/qgsrasterlayer.cpp
+++ b/src/core/raster/qgsrasterlayer.cpp
@@ -1528,8 +1528,8 @@ bool QgsRasterLayer::writeXml( QDomNode & layer_node,
     {
       QDomElement noDataRange =  document.createElement( "noDataRange" );
 
-      noDataRange.setAttribute( "min", range.min() );
-      noDataRange.setAttribute( "max", range.max() );
+      noDataRange.setAttribute( "min", QgsRasterBlock::printValue( range.min() ) );
+      noDataRange.setAttribute( "max", QgsRasterBlock::printValue( range.max() ) );
       noDataRangeList.appendChild( noDataRange );
     }
 
diff --git a/src/core/raster/qgsrastershader.cpp b/src/core/raster/qgsrastershader.cpp
index f601840..e1f5e95 100644
--- a/src/core/raster/qgsrastershader.cpp
+++ b/src/core/raster/qgsrastershader.cpp
@@ -19,6 +19,7 @@ email                : ersts at amnh.org
 #include "qgslogger.h"
 #include "qgscolorrampshader.h"
 #include "qgsrastershader.h"
+#include "qgsrasterblock.h"
 #include <QDomDocument>
 #include <QDomElement>
 
@@ -151,7 +152,7 @@ void QgsRasterShader::writeXML( QDomDocument& doc, QDomElement& parent ) const
     {
       QDomElement itemElem = doc.createElement( "item" );
       itemElem.setAttribute( "label", itemIt->label );
-      itemElem.setAttribute( "value", QString::number( itemIt->value ) );
+      itemElem.setAttribute( "value", QgsRasterBlock::printValue( itemIt->value ) );
       itemElem.setAttribute( "color", itemIt->color.name() );
       itemElem.setAttribute( "alpha", itemIt->color.alpha() );
       colorRampShaderElem.appendChild( itemElem );
diff --git a/src/core/raster/qgssinglebandpseudocolorrenderer.cpp b/src/core/raster/qgssinglebandpseudocolorrenderer.cpp
index 9dc8f59..ae67c5d 100644
--- a/src/core/raster/qgssinglebandpseudocolorrenderer.cpp
+++ b/src/core/raster/qgssinglebandpseudocolorrenderer.cpp
@@ -224,8 +224,8 @@ void QgsSingleBandPseudoColorRenderer::writeXML( QDomDocument& doc, QDomElement&
   {
     mShader->writeXML( doc, rasterRendererElem ); //todo: include color ramp items directly in this renderer
   }
-  rasterRendererElem.setAttribute( "classificationMin", QString::number( mClassificationMin ) );
-  rasterRendererElem.setAttribute( "classificationMax", QString::number( mClassificationMax ) );
+  rasterRendererElem.setAttribute( "classificationMin", QgsRasterBlock::printValue( mClassificationMin ) );
+  rasterRendererElem.setAttribute( "classificationMax", QgsRasterBlock::printValue( mClassificationMax ) );
   rasterRendererElem.setAttribute( "classificationMinMaxOrigin", QgsRasterRenderer::minMaxOriginName( mClassificationMinMaxOrigin ) );
 
   parentElem.appendChild( rasterRendererElem );
diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
index d39409a..11ca33f 100644
--- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
+++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
@@ -724,7 +724,7 @@ QDomElement QgsCategorizedSymbolRendererV2::save( QDomDocument& doc )
   QgsSymbolV2Map symbols;
   QDomElement catsElem = doc.createElement( "categories" );
   QgsCategoryList::const_iterator it = mCategories.constBegin();
-  for ( ; it != mCategories.end(); ++it )
+  for ( ; it != mCategories.constEnd(); ++it )
   {
     const QgsRendererCategoryV2& cat = *it;
     QString symbolName = QString::number( i );
diff --git a/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp b/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
index 16b14db..ef7ba6a 100644
--- a/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsellipsesymbollayerv2.cpp
@@ -686,17 +686,17 @@ QRectF QgsEllipseSymbolLayerV2::bounds( QPointF point, QgsSymbolV2RenderContext&
   return symbolBounds;
 }
 
-bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext *context, const QgsFeature*, QPointF shift ) const
+bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext &context, QPointF shift ) const
 {
   //width
   double symbolWidth = mSymbolWidth;
 
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH ) ) //1. priority: data defined setting on symbol layer le
   {
-    context->setOriginalValueVariable( mSymbolWidth );
-    symbolWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH, *context, mSymbolWidth ).toDouble();
+    context.setOriginalValueVariable( mSymbolWidth );
+    symbolWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_WIDTH, context, mSymbolWidth ).toDouble();
   }
-  else if ( context->renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
+  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
   {
     symbolWidth = mSize;
   }
@@ -709,10 +709,10 @@ bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFa
   double symbolHeight = mSymbolHeight;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_HEIGHT ) ) //1. priority: data defined setting on symbol layer level
   {
-    context->setOriginalValueVariable( mSymbolHeight );
-    symbolHeight = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_HEIGHT, *context, mSymbolHeight ).toDouble();
+    context.setOriginalValueVariable( mSymbolHeight );
+    symbolHeight = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_HEIGHT, context, mSymbolHeight ).toDouble();
   }
-  else if ( context->renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
+  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
   {
     symbolHeight = mSize;
   }
@@ -726,8 +726,8 @@ bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFa
 
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH ) )
   {
-    context->setOriginalValueVariable( mOutlineWidth );
-    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, *context, mOutlineWidth ).toDouble();
+    context.setOriginalValueVariable( mOutlineWidth );
+    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, context, mOutlineWidth ).toDouble();
   }
   if ( mOutlineWidthUnit == QgsSymbolV2::MM )
   {
@@ -739,8 +739,8 @@ bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFa
   QColor fc = mColor;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL_COLOR ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL_COLOR, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL_COLOR, context, QVariant(), &ok ).toString();
     if ( ok )
       fc = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
@@ -749,8 +749,8 @@ bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFa
   QColor oc = mOutlineColor;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_COLOR ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_COLOR, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_COLOR, context, QVariant(), &ok ).toString();
     if ( ok )
       oc = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
@@ -759,22 +759,22 @@ bool QgsEllipseSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFa
   QString symbolName = mSymbolName;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SYMBOL_NAME ) )
   {
-    context->setOriginalValueVariable( mSymbolName );
-    symbolName = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SYMBOL_NAME, *context, mSymbolName ).toString();
+    context.setOriginalValueVariable( mSymbolName );
+    symbolName = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SYMBOL_NAME, context, mSymbolName ).toString();
   }
 
   //offset
   double offsetX = 0;
   double offsetY = 0;
-  markerOffset( *context, offsetX, offsetY );
+  markerOffset( context, offsetX, offsetY );
   QPointF off( offsetX, offsetY );
 
   //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
   double rotation = 0.0;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ROTATION ) )
   {
-    context->setOriginalValueVariable( mAngle );
-    rotation = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ROTATION, *context, mAngle ).toDouble() + mLineAngle;
+    context.setOriginalValueVariable( mAngle );
+    rotation = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ROTATION, context, mAngle ).toDouble() + mLineAngle;
   }
   else if ( !qgsDoubleNear( mAngle + mLineAngle, 0.0 ) )
   {
diff --git a/src/core/symbology-ng/qgsellipsesymbollayerv2.h b/src/core/symbology-ng/qgsellipsesymbollayerv2.h
index 090db06..811600a 100644
--- a/src/core/symbology-ng/qgsellipsesymbollayerv2.h
+++ b/src/core/symbology-ng/qgsellipsesymbollayerv2.h
@@ -40,7 +40,7 @@ class CORE_EXPORT QgsEllipseSymbolLayerV2: public QgsMarkerSymbolLayerV2
     void toSld( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const override;
     void writeSldMarker( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const override;
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
+    bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
 
     void setSymbolName( const QString& name ) { mSymbolName = name; }
     QString symbolName() const { return mSymbolName; }
diff --git a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
index ba5229e..445e68a 100644
--- a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp
@@ -422,6 +422,17 @@ QColor QgsSimpleFillSymbolLayerV2::dxfColor( QgsSymbolV2RenderContext &context )
   return mBorderColor;
 }
 
+double QgsSimpleFillSymbolLayerV2::dxfAngle( QgsSymbolV2RenderContext &context ) const
+{
+  double angle = mAngle;
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE ) )
+  {
+    context.setOriginalValueVariable( mAngle );
+    angle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, mAngle ).toDouble();
+  }
+  return angle;
+}
+
 Qt::PenStyle QgsSimpleFillSymbolLayerV2::dxfPenStyle() const
 {
   return mBorderStyle;
diff --git a/src/core/symbology-ng/qgsfillsymbollayerv2.h b/src/core/symbology-ng/qgsfillsymbollayerv2.h
index d14dd30..d5409e4 100644
--- a/src/core/symbology-ng/qgsfillsymbollayerv2.h
+++ b/src/core/symbology-ng/qgsfillsymbollayerv2.h
@@ -118,6 +118,8 @@ class CORE_EXPORT QgsSimpleFillSymbolLayerV2 : public QgsFillSymbolLayerV2
 
     double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const override;
     QColor dxfColor( QgsSymbolV2RenderContext& context ) const override;
+    double dxfAngle( QgsSymbolV2RenderContext& context ) const override;
+
     Qt::PenStyle dxfPenStyle() const override;
     QColor dxfBrushColor( QgsSymbolV2RenderContext &context ) const override;
     Qt::BrushStyle dxfBrushStyle() const override;
@@ -562,14 +564,15 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayerV2
     void setOutputUnit( QgsSymbolV2::OutputUnit unit ) override;
     QgsSymbolV2::OutputUnit outputUnit() const override;
 
-    void setMapUnitScale( const QgsMapUnitScale& scale ) override;
+    void setMapUnitScale( const QgsMapUnitScale &scale ) override;
     QgsMapUnitScale mapUnitScale() const override;
 
     virtual double estimateMaxBleed() const override;
 
-    virtual double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const override;
-    virtual QColor dxfColor( QgsSymbolV2RenderContext& context ) const override;
-    virtual Qt::PenStyle dxfPenStyle() const override;
+    double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const override;
+    QColor dxfColor( QgsSymbolV2RenderContext& context ) const override;
+
+    Qt::PenStyle dxfPenStyle() const override;
 
     QSet<QString> usedAttributes() const override;
 
diff --git a/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp
index 82202b1..e401c15 100644
--- a/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp
+++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp
@@ -29,7 +29,7 @@
 #include <QDomDocument>
 #include <QDomElement>
 
-QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* subRenderer )
+QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer( QgsFeatureRendererV2* subRenderer )
     : QgsFeatureRendererV2( "invertedPolygonRenderer" )
     , mPreprocessingEnabled( false )
 {
@@ -47,11 +47,11 @@ QgsInvertedPolygonRenderer::~QgsInvertedPolygonRenderer()
 {
 }
 
-void QgsInvertedPolygonRenderer::setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer )
+void QgsInvertedPolygonRenderer::setEmbeddedRenderer( QgsFeatureRendererV2* subRenderer )
 {
   if ( subRenderer )
   {
-    mSubRenderer.reset( const_cast<QgsFeatureRendererV2*>( subRenderer )->clone() );
+    mSubRenderer.reset( subRenderer );
   }
   else
   {
@@ -64,6 +64,38 @@ const QgsFeatureRendererV2* QgsInvertedPolygonRenderer::embeddedRenderer() const
   return mSubRenderer.data();
 }
 
+void QgsInvertedPolygonRenderer::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
+{
+  if ( !mSubRenderer )
+    return;
+
+  mSubRenderer->setLegendSymbolItem( key, symbol );
+}
+
+bool QgsInvertedPolygonRenderer::legendSymbolItemsCheckable() const
+{
+  if ( !mSubRenderer )
+    return false;
+
+  return mSubRenderer->legendSymbolItemsCheckable();
+}
+
+bool QgsInvertedPolygonRenderer::legendSymbolItemChecked( const QString& key )
+{
+  if ( !mSubRenderer )
+    return false;
+
+  return mSubRenderer->legendSymbolItemChecked( key );
+}
+
+void QgsInvertedPolygonRenderer::checkLegendSymbolItem( const QString& key, bool state )
+{
+  if ( !mSubRenderer )
+    return;
+
+  return mSubRenderer->checkLegendSymbolItem( key, state );
+}
+
 void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
 {
   if ( !mSubRenderer )
@@ -341,7 +373,7 @@ QgsInvertedPolygonRenderer* QgsInvertedPolygonRenderer::clone() const
   }
   else
   {
-    newRenderer = new QgsInvertedPolygonRenderer( mSubRenderer.data() );
+    newRenderer = new QgsInvertedPolygonRenderer( mSubRenderer.data()->clone() );
   }
   newRenderer->setPreprocessingEnabled( preprocessingEnabled() );
   copyRendererData( newRenderer );
@@ -357,7 +389,6 @@ QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element )
   {
     QgsFeatureRendererV2* renderer = QgsFeatureRendererV2::load( embeddedRendererElem );
     r->setEmbeddedRenderer( renderer );
-    delete renderer;
   }
   r->setPreprocessingEnabled( element.attribute( "preprocessing", "0" ).toInt() == 1 );
   return r;
diff --git a/src/core/symbology-ng/qgsinvertedpolygonrenderer.h b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h
index 8a64326..6f3f08b 100644
--- a/src/core/symbology-ng/qgsinvertedpolygonrenderer.h
+++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h
@@ -42,9 +42,11 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
   public:
 
     /** Constructor
-     * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned
+     * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned.
+     * Ownership will be transferred.
      */
-    QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* embeddedRenderer = nullptr );
+    QgsInvertedPolygonRenderer( QgsFeatureRendererV2* embeddedRenderer = nullptr );
+
     virtual ~QgsInvertedPolygonRenderer();
 
     /** Used to clone this feature renderer.*/
@@ -116,13 +118,14 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
      */
     virtual QDomElement save( QDomDocument& doc ) override;
 
-    /** Sets the embedded renderer
-     * @param subRenderer the embedded renderer (will be cloned)
-     */
-    void setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer );
-    /** @returns the current embedded renderer
-     */
-    const QgsFeatureRendererV2* embeddedRenderer() const;
+    void setEmbeddedRenderer( QgsFeatureRendererV2* subRenderer ) override;
+    const QgsFeatureRendererV2* embeddedRenderer() const override;
+
+    virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol ) override;
+
+    virtual bool legendSymbolItemsCheckable() const override;
+    virtual bool legendSymbolItemChecked( const QString& key ) override;
+    virtual void checkLegendSymbolItem( const QString& key, bool state = true ) override;
 
     /** @returns true if the geometries are to be preprocessed (merged with an union) before rendering.*/
     bool preprocessingEnabled() const { return mPreprocessingEnabled; }
diff --git a/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp b/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
index 4f849c4..ebf4183 100644
--- a/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgsmarkersymbollayerv2.cpp
@@ -830,16 +830,12 @@ void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderCon
   }
 }
 
-bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext *context, const QgsFeature*, QPointF shift ) const
+bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext &context, QPointF shift ) const
 {
   //data defined size?
   double size = mSize;
 
-  bool hasDataDefinedSize = false;
-  if ( context )
-  {
-    hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE );
-  }
+  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE );
 
   //data defined size
   bool ok = true;
@@ -847,8 +843,8 @@ bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitSc
   {
     if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE ) )
     {
-      context->setOriginalValueVariable( mSize );
-      size = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE, *context, mSize, &ok ).toDouble();
+      context.setOriginalValueVariable( mSize );
+      size = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE, context, mSize, &ok ).toDouble();
     }
 
     if ( ok )
@@ -863,7 +859,7 @@ bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitSc
       }
     }
 
-    size = QgsSymbolLayerV2Utils::convertToPainterUnits( context->renderContext(), size, mSizeUnit, mSizeMapUnitScale );
+    size = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(), size, mSizeUnit, mSizeMapUnitScale );
   }
   if ( mSizeUnit == QgsSymbolV2::MM )
   {
@@ -874,10 +870,10 @@ bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitSc
   //outlineWidth
   double outlineWidth = mOutlineWidth;
 
-  if ( context && hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH ) )
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH ) )
   {
-    context->setOriginalValueVariable( mOutlineWidth );
-    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, *context, mOutlineWidth ).toDouble();
+    context.setOriginalValueVariable( mOutlineWidth );
+    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, context, mOutlineWidth ).toDouble();
   }
   if ( mSizeUnit == QgsSymbolV2::MM )
   {
@@ -887,17 +883,17 @@ bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitSc
   //color
   QColor pc = mPen.color();
   QColor bc = mBrush.color();
-  if ( context && hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR ) )
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR, context, QVariant(), &ok ).toString();
     if ( ok )
       bc = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
-  if ( context && hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR_BORDER ) )
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR_BORDER ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mBorderColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR_BORDER, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mBorderColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_COLOR_BORDER, context, QVariant(), &ok ).toString();
     if ( ok )
       pc = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
@@ -905,25 +901,23 @@ bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitSc
   //offset
   double offsetX = 0;
   double offsetY = 0;
-  if ( context )
-  {
-    markerOffset( *context, offsetX, offsetY );
-  }
+  markerOffset( context, offsetX, offsetY );
+
   QPointF off( offsetX, offsetY );
 
   //angle
   double angle = mAngle + mLineAngle;
-  if ( context && hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE ) )
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE ) )
   {
-    context->setOriginalValueVariable( mAngle );
-    angle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, *context, mAngle ).toDouble() + mLineAngle;
+    context.setOriginalValueVariable( mAngle );
+    angle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, mAngle ).toDouble() + mLineAngle;
   }
 
   QString name( mName );
-  if ( context && hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME ) )
+  if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME ) )
   {
-    context->setOriginalValueVariable( mName );
-    name = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( mName );
+    name = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME, context, QVariant(), &ok ).toString();
   }
 
   angle = -angle; //rotation in Qt is counterclockwise
@@ -1443,6 +1437,13 @@ void QgsSvgMarkerSymbolLayerV2::renderPoint( QPointF point, QgsSymbolV2RenderCon
   }
 
   p->restore();
+
+  if ( context.renderContext().flags() & QgsRenderContext::Antialiasing )
+  {
+    // workaround issue with nested QPictures forgetting antialiasing flag - see http://hub.qgis.org/issues/14960
+    p->setRenderHint( QPainter::Antialiasing );
+  }
+
 }
 
 double QgsSvgMarkerSymbolLayerV2::calculateSize( QgsSymbolV2RenderContext& context, bool& hasDataDefinedSize ) const
@@ -1654,8 +1655,7 @@ QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element
   return m;
 }
 
-bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext *context, const QgsFeature*,
-    QPointF shift ) const
+bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext &context, QPointF shift ) const
 {
   Q_UNUSED( layerName );
   Q_UNUSED( shift ); //todo...
@@ -1663,13 +1663,13 @@ bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScale
   //size
   double size = mSize;
 
-  bool hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE );
+  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE );
 
   bool ok = true;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE ) )
   {
-    context->setOriginalValueVariable( mSize );
-    size = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE, *context, mSize, &ok ).toDouble();
+    context.setOriginalValueVariable( mSize );
+    size = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_SIZE, context, mSize, &ok ).toDouble();
   }
 
   if ( hasDataDefinedSize && ok )
@@ -1696,8 +1696,8 @@ bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScale
 
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodePoint( mOffset ) );
-    QString offsetString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodePoint( mOffset ) );
+    QString offsetString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET, context, QVariant(), &ok ).toString();
     if ( ok )
       offset = QgsSymbolLayerV2Utils::decodePoint( offsetString );
   }
@@ -1714,8 +1714,8 @@ bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScale
   double angle = mAngle + mLineAngle;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE ) )
   {
-    context->setOriginalValueVariable( mAngle );
-    angle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, *context, mAngle ).toDouble() + mLineAngle;
+    context.setOriginalValueVariable( mAngle );
+    angle = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_ANGLE, context, mAngle ).toDouble() + mLineAngle;
   }
   //angle = -angle; //rotation in Qt is counterclockwise
   if ( angle )
@@ -1724,23 +1724,23 @@ bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScale
   QString path = mPath;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME ) )
   {
-    context->setOriginalValueVariable( mPath );
-    path = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME, *context, mPath ).toString();
+    context.setOriginalValueVariable( mPath );
+    path = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_NAME, context, mPath ).toString();
   }
 
   double outlineWidth = mOutlineWidth;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH ) )
   {
-    context->setOriginalValueVariable( mOutlineWidth );
-    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, *context, mOutlineWidth ).toDouble();
+    context.setOriginalValueVariable( mOutlineWidth );
+    outlineWidth = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE_WIDTH, context, mOutlineWidth ).toDouble();
   }
-  outlineWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context->renderContext(), outlineWidth, mOutlineWidthUnit, mOutlineWidthMapUnitScale );
+  outlineWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(), outlineWidth, mOutlineWidthUnit, mOutlineWidthMapUnitScale );
 
   QColor fillColor = mColor;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_FILL, context, QVariant(), &ok ).toString();
     if ( ok )
       fillColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
@@ -1748,15 +1748,15 @@ bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScale
   QColor outlineColor = mOutlineColor;
   if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE ) )
   {
-    context->setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ) );
-    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE, *context, QVariant(), &ok ).toString();
+    context.setOriginalValueVariable( QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ) );
+    QString colorString = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OUTLINE, context, QVariant(), &ok ).toString();
     if ( ok )
       outlineColor = QgsSymbolLayerV2Utils::decodeColor( colorString );
   }
 
   const QByteArray &svgContent = QgsSvgCache::instance()->svgContent( path, size, fillColor, outlineColor, outlineWidth,
-                                 context->renderContext().scaleFactor(),
-                                 context->renderContext().rasterScaleFactor() );
+                                 context.renderContext().scaleFactor(),
+                                 context.renderContext().rasterScaleFactor() );
 
   //if current entry image is 0: cache image for entry
   // checks to see if image will fit into cache
diff --git a/src/core/symbology-ng/qgsmarkersymbollayerv2.h b/src/core/symbology-ng/qgsmarkersymbollayerv2.h
index 76780a8..d82bd79 100644
--- a/src/core/symbology-ng/qgsmarkersymbollayerv2.h
+++ b/src/core/symbology-ng/qgsmarkersymbollayerv2.h
@@ -96,7 +96,7 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
     void setOutlineWidthMapUnitScale( const QgsMapUnitScale& scale ) { mOutlineWidthMapUnitScale = scale; }
     const QgsMapUnitScale& outlineWidthMapUnitScale() const { return mOutlineWidthMapUnitScale; }
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
+    bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
 
     void setOutputUnit( QgsSymbolV2::OutputUnit unit ) override;
     QgsSymbolV2::OutputUnit outputUnit() const override;
@@ -201,7 +201,7 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
     void setMapUnitScale( const QgsMapUnitScale& scale ) override;
     QgsMapUnitScale mapUnitScale() const override;
 
-    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext* context, const QgsFeature* f, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
+    bool writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const override;
 
     QRectF bounds( QPointF point, QgsSymbolV2RenderContext& context ) override;
 
diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
index df6c519..093f7de 100644
--- a/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
+++ b/src/core/symbology-ng/qgspointdisplacementrenderer.cpp
@@ -156,13 +156,15 @@ void QgsPointDisplacementRenderer::drawGroup( const DisplacementGroup& group, Qg
 
   //get list of labels and symbols
   QStringList labelAttributeList;
-  QList<QgsMarkerSymbolV2*> symbolList;
+  QList< QgsMarkerSymbolV2* > symbolList;
+  QgsFeatureList featureList;
 
   QgsMultiPointV2* groupMultiPoint = new QgsMultiPointV2();
   for ( DisplacementGroup::const_iterator attIt = group.constBegin(); attIt != group.constEnd(); ++attIt )
   {
     labelAttributeList << ( mDrawLabels ? getLabel( attIt.value().first ) : QString() );
     symbolList << dynamic_cast<QgsMarkerSymbolV2*>( attIt.value().second );
+    featureList << attIt.value().first;
     groupMultiPoint->addGeometry( attIt.value().first.constGeometry()->geometry()->clone() );
   }
 
@@ -181,7 +183,7 @@ void QgsPointDisplacementRenderer::drawGroup( const DisplacementGroup& group, Qg
     {
       diagonal = qMax( diagonal, QgsSymbolLayerV2Utils::convertToPainterUnits( context,
                        M_SQRT2 * symbol->size(),
-                       symbol->outputUnit(), symbol->mapUnitScale() ) );
+                       symbol->sizeUnit(), symbol->sizeMapUnitScale() ) );
     }
   }
 
@@ -210,7 +212,7 @@ void QgsPointDisplacementRenderer::drawGroup( const DisplacementGroup& group, Qg
   }
 
   //draw symbols on the circle
-  drawSymbols( feature, context, symbolList, symbolPositions, selected );
+  drawSymbols( featureList, context, symbolList, symbolPositions, selected );
   //and also the labels
   drawLabels( pt, symbolContext, labelPositions, labelAttributeList );
 }
@@ -221,6 +223,43 @@ void QgsPointDisplacementRenderer::setEmbeddedRenderer( QgsFeatureRendererV2* r
   mRenderer = r;
 }
 
+const QgsFeatureRendererV2* QgsPointDisplacementRenderer::embeddedRenderer() const
+{
+  return mRenderer;
+}
+
+void QgsPointDisplacementRenderer::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
+{
+  if ( !mRenderer )
+    return;
+
+  mRenderer->setLegendSymbolItem( key, symbol );
+}
+
+bool QgsPointDisplacementRenderer::legendSymbolItemsCheckable() const
+{
+  if ( !mRenderer )
+    return false;
+
+  return mRenderer->legendSymbolItemsCheckable();
+}
+
+bool QgsPointDisplacementRenderer::legendSymbolItemChecked( const QString& key )
+{
+  if ( !mRenderer )
+    return false;
+
+  return mRenderer->legendSymbolItemChecked( key );
+}
+
+void QgsPointDisplacementRenderer::checkLegendSymbolItem( const QString& key, bool state )
+{
+  if ( !mRenderer )
+    return;
+
+  return mRenderer->checkLegendSymbolItem( key, state );
+}
+
 QList<QString> QgsPointDisplacementRenderer::usedAttributes()
 {
   QList<QString> attributeList;
@@ -534,7 +573,7 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolV2
     {
       double centerDiagonal = QgsSymbolLayerV2Utils::convertToPainterUnits( symbolContext.renderContext(),
                               M_SQRT2 * mCenterSymbol->size(),
-                              mCenterSymbol->outputUnit(), mCenterSymbol->mapUnitScale() );
+                              mCenterSymbol->sizeUnit(), mCenterSymbol->sizeMapUnitScale() );
 
       int pointsRemaining = nPosition;
       int ringNumber = 1;
@@ -582,15 +621,21 @@ void QgsPointDisplacementRenderer::drawCircle( double radiusPainterUnits, QgsSym
   p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
 }
 
-void QgsPointDisplacementRenderer::drawSymbols( const QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected )
+void QgsPointDisplacementRenderer::drawSymbols( const QgsFeatureList& features, QgsRenderContext& context,
+    const QList< QgsMarkerSymbolV2* >& symbolList, const QList<QPointF>& symbolPositions, bool selected )
 {
   QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
   QList<QgsMarkerSymbolV2*>::const_iterator symbolIt = symbolList.constBegin();
-  for ( ; symbolPosIt != symbolPositions.constEnd() && symbolIt != symbolList.constEnd(); ++symbolPosIt, ++symbolIt )
+  QgsFeatureList::const_iterator featIt = features.constBegin();
+  for ( ; symbolPosIt != symbolPositions.constEnd() && symbolIt != symbolList.constEnd() && featIt != features.constEnd();
+        ++symbolPosIt, ++symbolIt, ++featIt )
   {
     if ( *symbolIt )
     {
-      ( *symbolIt )->renderPoint( *symbolPosIt, &f, context, -1, selected );
+      context.expressionContext().setFeature( *featIt );
+      ( *symbolIt )->startRender( context );
+      ( *symbolIt )->renderPoint( *symbolPosIt, &( *featIt ), context, -1, selected );
+      ( *symbolIt )->stopRender( context );
     }
   }
 }
diff --git a/src/core/symbology-ng/qgspointdisplacementrenderer.h b/src/core/symbology-ng/qgspointdisplacementrenderer.h
index 9d00362..e079ede 100644
--- a/src/core/symbology-ng/qgspointdisplacementrenderer.h
+++ b/src/core/symbology-ng/qgspointdisplacementrenderer.h
@@ -95,9 +95,14 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
     void setLabelAttributeName( const QString& name ) { mLabelAttributeName = name; }
     QString labelAttributeName() const { return mLabelAttributeName; }
 
-    /** Sets embedded renderer (takes ownership)*/
-    void setEmbeddedRenderer( QgsFeatureRendererV2* r );
-    QgsFeatureRendererV2* embeddedRenderer() const { return mRenderer;}
+    void setEmbeddedRenderer( QgsFeatureRendererV2* r ) override;
+    const QgsFeatureRendererV2* embeddedRenderer() const override;
+
+    virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol ) override;
+
+    virtual bool legendSymbolItemsCheckable() const override;
+    virtual bool legendSymbolItemChecked( const QString& key ) override;
+    virtual void checkLegendSymbolItem( const QString& key, bool state = true ) override;
 
     //! not available in python bindings
     //! @deprecated since 2.4
@@ -230,7 +235,7 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
     QMap<QgsFeatureId, int> mGroupIndex;
     /** Spatial index for fast lookup of close points*/
     QgsSpatialIndex* mSpatialIndex;
-    /** Keeps trask which features are selected */
+    /** Keeps track which features are selected */
     QSet<QgsFeatureId> mSelectedFeatures;
 
     /** Creates a search rectangle with specified distance tolerance */
@@ -249,7 +254,7 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
     void calculateSymbolAndLabelPositions( QgsSymbolV2RenderContext &symbolContext, QPointF centerPoint, int nPosition, double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts , double &circleRadius ) const;
     void drawGroup( const DisplacementGroup& group, QgsRenderContext& context );
     void drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, QPointF centerPoint, int nSymbols );
-    void drawSymbols( const QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected = false );
+    void drawSymbols( const QgsFeatureList& features, QgsRenderContext& context, const QList< QgsMarkerSymbolV2* >& symbolList, const QList<QPointF>& symbolPositions, bool selected = false );
     void drawLabels( QPointF centerPoint, QgsSymbolV2RenderContext& context, const QList<QPointF>& labelShifts, const QStringList& labelList );
     /** Returns first symbol for feature or 0 if none*/
     QgsSymbolV2* firstSymbolForFeature( QgsFeatureRendererV2* r, QgsFeature& f, QgsRenderContext& context );
diff --git a/src/core/symbology-ng/qgsrendererv2.h b/src/core/symbology-ng/qgsrendererv2.h
index 312cc5d..58716d3 100644
--- a/src/core/symbology-ng/qgsrendererv2.h
+++ b/src/core/symbology-ng/qgsrendererv2.h
@@ -405,6 +405,21 @@ class CORE_EXPORT QgsFeatureRendererV2
      */
     void setOrderByEnabled( bool enabled );
 
+    /** Sets an embedded renderer (subrenderer) for this feature renderer. The base class implementation
+     * does nothing with subrenderers, but individual derived classes can use these to modify their behaviour.
+     * @param subRenderer the embedded renderer. Ownership will be transferred.
+     * @see embeddedRenderer()
+     * @note added in QGIS 2.16
+     */
+    virtual void setEmbeddedRenderer( QgsFeatureRendererV2* subRenderer ) { delete subRenderer; }
+
+    /** Returns the current embedded renderer (subrenderer) for this feature renderer. The base class
+     * implementation does not use subrenderers and will always return null.
+     * @see setEmbeddedRenderer()
+     * @note added in QGIS 2.16
+     */
+    virtual const QgsFeatureRendererV2* embeddedRenderer() const { return nullptr; }
+
   protected:
     QgsFeatureRendererV2( const QString& type );
 
diff --git a/src/core/symbology-ng/qgssvgcache.cpp b/src/core/symbology-ng/qgssvgcache.cpp
index 56fb4ef..ebab07b 100644
--- a/src/core/symbology-ng/qgssvgcache.cpp
+++ b/src/core/symbology-ng/qgssvgcache.cpp
@@ -328,7 +328,13 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry )
   entry->viewboxSize = viewboxSize;
   replaceElemParams( docElem, entry->fill, entry->outline, entry->outlineWidth * sizeScaleFactor );
 
-  entry->svgContent = svgDoc.toByteArray();
+  entry->svgContent = svgDoc.toByteArray( 0 );
+
+  // toByteArray screws up tspans inside text by adding new lines before and after each span... this should help, at the
+  // risk of potentially breaking some svgs where the newline is desired
+  entry->svgContent.replace( "\n<tspan", "<tspan" );
+  entry->svgContent.replace( "</tspan>\n", "</tspan>" );
+
   mTotalSize += entry->svgContent.size();
 }
 
@@ -342,16 +348,23 @@ double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry* entry, const QDomElem
 
   //find svg viewbox attribute
   //first check if docElem is svg element
-  if ( docElem.tagName() == "svg" )
+  if ( docElem.tagName() == "svg" && docElem.hasAttribute( "viewBox" ) )
   {
     viewBox = docElem.attribute( "viewBox", QString() );
   }
+  else if ( docElem.tagName() == "svg" && docElem.hasAttribute( "viewbox" ) )
+  {
+    viewBox = docElem.attribute( "viewbox", QString() );
+  }
   else
   {
     QDomElement svgElem = docElem.firstChildElement( "svg" ) ;
     if ( !svgElem.isNull() )
     {
-      viewBox = svgElem.attribute( "viewBox", QString() );
+      if ( svgElem.hasAttribute( "viewBox" ) )
+        viewBox = svgElem.attribute( "viewBox", QString() );
+      else if ( svgElem.hasAttribute( "viewbox" ) )
+        viewBox = svgElem.attribute( "viewbox", QString() );
     }
   }
 
diff --git a/src/core/symbology-ng/qgssymbollayerv2.cpp b/src/core/symbology-ng/qgssymbollayerv2.cpp
index 44b114a..fa18e74 100644
--- a/src/core/symbology-ng/qgssymbollayerv2.cpp
+++ b/src/core/symbology-ng/qgssymbollayerv2.cpp
@@ -256,18 +256,12 @@ QVariant QgsSymbolLayerV2::evaluateDataDefinedProperty( const QString& property,
   return defaultVal;
 }
 
-bool QgsSymbolLayerV2::writeDxf( QgsDxfExport& e,
-                                 double mmMapUnitScaleFactor,
-                                 const QString& layerName,
-                                 QgsSymbolV2RenderContext *context,
-                                 const QgsFeature* f,
-                                 QPointF shift ) const
+bool QgsSymbolLayerV2::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift ) const
 {
   Q_UNUSED( e );
   Q_UNUSED( mmMapUnitScaleFactor );
   Q_UNUSED( layerName );
   Q_UNUSED( context );
-  Q_UNUSED( f );
   Q_UNUSED( shift );
   return false;
 }
@@ -292,6 +286,12 @@ QColor QgsSymbolLayerV2::dxfColor( QgsSymbolV2RenderContext &context ) const
   return color();
 }
 
+double QgsSymbolLayerV2::dxfAngle( QgsSymbolV2RenderContext &context ) const
+{
+  Q_UNUSED( context );
+  return 0.0;
+}
+
 QVector<qreal> QgsSymbolLayerV2::dxfCustomDashPattern( QgsSymbolV2::OutputUnit& unit ) const
 {
   Q_UNUSED( unit );
diff --git a/src/core/symbology-ng/qgssymbollayerv2.h b/src/core/symbology-ng/qgssymbollayerv2.h
index e9166a9..be260d3 100644
--- a/src/core/symbology-ng/qgssymbollayerv2.h
+++ b/src/core/symbology-ng/qgssymbollayerv2.h
@@ -235,17 +235,13 @@ class CORE_EXPORT QgsSymbolLayerV2
      */
     virtual QVariant evaluateDataDefinedProperty( const QString& property, const QgsSymbolV2RenderContext& context, const QVariant& defaultVal = QVariant(), bool *ok = nullptr ) const;
 
-    virtual bool writeDxf( QgsDxfExport& e,
-                           double mmMapUnitScaleFactor,
-                           const QString& layerName,
-                           QgsSymbolV2RenderContext* context,
-                           const QgsFeature* f,
-                           QPointF shift = QPointF( 0.0, 0.0 ) ) const;
+    virtual bool writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolV2RenderContext &context, QPointF shift = QPointF( 0.0, 0.0 ) ) const;
 
     virtual double dxfWidth( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const;
     virtual double dxfOffset( const QgsDxfExport& e, QgsSymbolV2RenderContext& context ) const;
 
     virtual QColor dxfColor( QgsSymbolV2RenderContext& context ) const;
+    virtual double dxfAngle( QgsSymbolV2RenderContext& context ) const;
 
     virtual QVector<qreal> dxfCustomDashPattern( QgsSymbolV2::OutputUnit& unit ) const;
     virtual Qt::PenStyle dxfPenStyle() const;
diff --git a/src/core/symbology-ng/qgssymbollayerv2registry.cpp b/src/core/symbology-ng/qgssymbollayerv2registry.cpp
index 197b89b..9329ff6 100644
--- a/src/core/symbology-ng/qgssymbollayerv2registry.cpp
+++ b/src/core/symbology-ng/qgssymbollayerv2registry.cpp
@@ -38,7 +38,7 @@ QgsSymbolLayerV2Registry::QgsSymbolLayerV2Registry()
                       QgsFontMarkerSymbolLayerV2::create, QgsFontMarkerSymbolLayerV2::createFromSld ) );
   addSymbolLayerType( new QgsSymbolLayerV2Metadata( "EllipseMarker", QObject::tr( "Ellipse marker" ), QgsSymbolV2::Marker,
                       QgsEllipseSymbolLayerV2::create, QgsEllipseSymbolLayerV2::createFromSld ) );
-  addSymbolLayerType( new QgsSymbolLayerV2Metadata( "VectorField", QObject::tr( "Vector Field marker" ), QgsSymbolV2::Marker,
+  addSymbolLayerType( new QgsSymbolLayerV2Metadata( "VectorField", QObject::tr( "Vector field marker" ), QgsSymbolV2::Marker,
                       QgsVectorFieldSymbolLayer::create ) );
 
   addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SimpleFill", QObject::tr( "Simple fill" ), QgsSymbolV2::Fill,
diff --git a/src/core/symbology-ng/qgssymbolv2.cpp b/src/core/symbology-ng/qgssymbolv2.cpp
index 1154f12..534fa43 100644
--- a/src/core/symbology-ng/qgssymbolv2.cpp
+++ b/src/core/symbology-ng/qgssymbolv2.cpp
@@ -469,8 +469,11 @@ void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsFields* field
 void QgsSymbolV2::stopRender( QgsRenderContext& context )
 {
   Q_UNUSED( context )
-  Q_FOREACH ( QgsSymbolLayerV2* layer, mLayers )
-    layer->stopRender( *mSymbolRenderContext );
+  if ( mSymbolRenderContext )
+  {
+    Q_FOREACH ( QgsSymbolLayerV2* layer, mLayers )
+      layer->stopRender( *mSymbolRenderContext );
+  }
 
   delete mSymbolRenderContext;
   mSymbolRenderContext = nullptr;
diff --git a/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp b/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
index c39a0d7..b2d7f16 100644
--- a/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
+++ b/src/core/symbology-ng/qgsvectorfieldsymbollayer.cpp
@@ -283,7 +283,7 @@ void QgsVectorFieldSymbolLayer::drawPreviewIcon( QgsSymbolV2RenderContext& conte
 
 QSet<QString> QgsVectorFieldSymbolLayer::usedAttributes() const
 {
-  QSet<QString> attributes;
+  QSet<QString> attributes = QgsMarkerSymbolLayerV2::usedAttributes();
   if ( !mXAttribute.isEmpty() )
   {
     attributes.insert( mXAttribute );
@@ -292,6 +292,10 @@ QSet<QString> QgsVectorFieldSymbolLayer::usedAttributes() const
   {
     attributes.insert( mYAttribute );
   }
+  if ( mLineSymbol )
+  {
+    attributes.unite( mLineSymbol->usedAttributes() );
+  }
   return attributes;
 }
 
@@ -319,4 +323,17 @@ void QgsVectorFieldSymbolLayer::convertPolarToCartesian( double length, double a
   y = length * cos( angle );
 }
 
+void QgsVectorFieldSymbolLayer::setColor( const QColor& color )
+{
+  if ( mLineSymbol )
+    mLineSymbol->setColor( color );
+
+  mColor = color;
+}
+
+QColor QgsVectorFieldSymbolLayer::color() const
+{
+  return mLineSymbol ? mLineSymbol->color() : mColor;
+}
+
 
diff --git a/src/core/symbology-ng/qgsvectorfieldsymbollayer.h b/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
index cac4284..fe92f72 100644
--- a/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
+++ b/src/core/symbology-ng/qgsvectorfieldsymbollayer.h
@@ -54,6 +54,9 @@ class CORE_EXPORT QgsVectorFieldSymbolLayer: public QgsMarkerSymbolLayerV2
     bool setSubSymbol( QgsSymbolV2* symbol ) override;
     QgsSymbolV2* subSymbol() override { return mLineSymbol; }
 
+    void setColor( const QColor& color ) override;
+    virtual QColor color() const override;
+
     void renderPoint( QPointF point, QgsSymbolV2RenderContext& context ) override;
     void startRender( QgsSymbolV2RenderContext& context ) override;
     void stopRender( QgsSymbolV2RenderContext& context ) override;
diff --git a/src/customwidgets/qgsextentgroupboxplugin.cpp b/src/customwidgets/qgsextentgroupboxplugin.cpp
index e421bfa..639b83c 100644
--- a/src/customwidgets/qgsextentgroupboxplugin.cpp
+++ b/src/customwidgets/qgsextentgroupboxplugin.cpp
@@ -1,5 +1,5 @@
 /***************************************************************************
-   qgsextentroupboxplugin.cpp
+   qgsextentgroupboxplugin.cpp
     --------------------------------------
    Date                 : 28.07.2015
    Copyright            : (C) 2015 Denis Rouzaud
@@ -37,7 +37,7 @@ QString QgsExtentGroupBoxPlugin::group() const
 
 QString QgsExtentGroupBoxPlugin::includeFile() const
 {
-  return "qgsextentroupbox.h";
+  return "qgsextentgroupbox.h";
 }
 
 QIcon QgsExtentGroupBoxPlugin::icon() const
diff --git a/src/customwidgets/qgsextentgroupboxplugin.h b/src/customwidgets/qgsextentgroupboxplugin.h
index 00cab41..f3167d9 100644
--- a/src/customwidgets/qgsextentgroupboxplugin.h
+++ b/src/customwidgets/qgsextentgroupboxplugin.h
@@ -1,5 +1,5 @@
 /***************************************************************************
-   qgsextentroupboxplugin.h
+   qgsextentgroupboxplugin.h
     --------------------------------------
    Date                 : 28.07.2015
    Copyright            : (C) 2015 Denis Rouzaud
diff --git a/src/gui/attributetable/qgsattributetablemodel.cpp b/src/gui/attributetable/qgsattributetablemodel.cpp
index 93c57b6..0fa131b 100644
--- a/src/gui/attributetable/qgsattributetablemodel.cpp
+++ b/src/gui/attributetable/qgsattributetablemodel.cpp
@@ -553,7 +553,7 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
   if ( index.column() >= mFieldCount )
     return role == Qt::DisplayRole ? rowId : QVariant();
 
-  int fieldId = mAttributes[index.column()];
+  int fieldId = mAttributes.at( index.column() );
 
   if ( role == FieldIndexRole )
     return fieldId;
@@ -593,46 +593,61 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
     val = mFeat.attribute( fieldId );
   }
 
-  if ( role == Qt::DisplayRole )
+  if ( role == SortRole )
   {
-    return mWidgetFactories[index.column()]->representValue( layer(), fieldId, mWidgetConfigs[index.column()], mAttributeWidgetCaches[index.column()], val );
+    return val;
   }
 
-  if ( role == Qt::BackgroundColorRole || role == Qt::TextColorRole || role == Qt::DecorationRole || role == Qt::FontRole )
+  switch ( role )
   {
-    mExpressionContext.setFeature( mFeat );
-    QList<QgsConditionalStyle> styles;
-    if ( mRowStylesMap.contains( index.row() ) )
-    {
-      styles = mRowStylesMap[index.row()];
-    }
-    else
+    case Qt::DisplayRole:
+      return mWidgetFactories.at( index.column() )->representValue( layer(), fieldId, mWidgetConfigs.at( index.column() ),
+             mAttributeWidgetCaches.at( index.column() ), val );
+
+    case Qt::EditRole:
+      return val;
+
+    case Qt::BackgroundColorRole:
+    case Qt::TextColorRole:
+    case Qt::DecorationRole:
+    case Qt::FontRole:
     {
-      styles = QgsConditionalStyle::matchingConditionalStyles( layer()->conditionalStyles()->rowStyles(), QVariant(),  mExpressionContext );
-      mRowStylesMap.insert( index.row(), styles );
+      mExpressionContext.setFeature( mFeat );
+      QList<QgsConditionalStyle> styles;
+      if ( mRowStylesMap.contains( index.row() ) )
+      {
+        styles = mRowStylesMap[index.row()];
+      }
+      else
+      {
+        styles = QgsConditionalStyle::matchingConditionalStyles( layer()->conditionalStyles()->rowStyles(), QVariant(),  mExpressionContext );
+        mRowStylesMap.insert( index.row(), styles );
 
-    }
+      }
 
-    QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles );
-    styles = layer()->conditionalStyles()->fieldStyles( field.name() );
-    styles = QgsConditionalStyle::matchingConditionalStyles( styles , val,  mExpressionContext );
-    styles.insert( 0, rowstyle );
-    QgsConditionalStyle style = QgsConditionalStyle::compressStyles( styles );
+      QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles );
+      styles = layer()->conditionalStyles()->fieldStyles( field.name() );
+      styles = QgsConditionalStyle::matchingConditionalStyles( styles , val,  mExpressionContext );
+      styles.insert( 0, rowstyle );
+      QgsConditionalStyle style = QgsConditionalStyle::compressStyles( styles );
 
-    if ( style.isValid() )
-    {
-      if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
-        return style.backgroundColor();
-      if ( role == Qt::TextColorRole && style.validTextColor() )
-        return style.textColor();
-      if ( role == Qt::DecorationRole )
-        return style.icon();
-      if ( role == Qt::FontRole )
-        return style.font();
-    }
+      if ( style.isValid() )
+      {
+        if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
+          return style.backgroundColor();
+        if ( role == Qt::TextColorRole && style.validTextColor() )
+          return style.textColor();
+        if ( role == Qt::DecorationRole )
+          return style.icon();
+        if ( role == Qt::FontRole )
+          return style.font();
+      }
 
+      return QVariant();
+    }
   }
-  return val;
+
+  return QVariant();
 }
 
 bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
diff --git a/src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp b/src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
index af34dac..3ec347f 100644
--- a/src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
+++ b/src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
@@ -79,6 +79,7 @@ QgsEditorWidgetRegistry::QgsEditorWidgetRegistry()
   connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
   // connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
 
+  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QgsMapLayer* ) ), this, SLOT( mapLayerWillBeRemoved( QgsMapLayer* ) ) );
   connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( mapLayerAdded( QgsMapLayer* ) ) );
 }
 
@@ -318,6 +319,17 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
   layerElem.appendChild( editTypesNode );
 }
 
+void QgsEditorWidgetRegistry::mapLayerWillBeRemoved( QgsMapLayer* mapLayer )
+{
+  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
+
+  if ( vl )
+  {
+    disconnect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
+    disconnect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
+  }
+}
+
 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
 {
   QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
diff --git a/src/gui/editorwidgets/core/qgseditorwidgetregistry.h b/src/gui/editorwidgets/core/qgseditorwidgetregistry.h
index 687d01b..10cc6d3 100644
--- a/src/gui/editorwidgets/core/qgseditorwidgetregistry.h
+++ b/src/gui/editorwidgets/core/qgseditorwidgetregistry.h
@@ -174,6 +174,13 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
     void mapLayerAdded( QgsMapLayer* mapLayer );
 
     /**
+     * Will disconnect to appropriate signals from map layers to load and save style
+     *
+     * @param mapLayer The layer to disconnect
+     */
+    void mapLayerWillBeRemoved( QgsMapLayer* mapLayer );
+
+    /**
      * Loads layer symbology for the layer that emitted the signal
      *
      * @param element The XML element containing the style information
diff --git a/src/gui/editorwidgets/qgscolorwidgetwrapper.cpp b/src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
index 2aff246..a0f1af3 100644
--- a/src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
+++ b/src/gui/editorwidgets/qgscolorwidgetwrapper.cpp
@@ -24,12 +24,11 @@ QgsColorWidgetWrapper::QgsColorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx,
 
 QVariant QgsColorWidgetWrapper::value() const
 {
-  QVariant v;
-
+  QColor c;
   if ( mColorButton )
-    v = mColorButton->color();
+    c = mColorButton->color();
 
-  return v;
+  return c.isValid() ? QVariant( c ) : QVariant( QVariant::Color );
 }
 
 QWidget* QgsColorWidgetWrapper::createWidget( QWidget* parent )
@@ -54,5 +53,5 @@ bool QgsColorWidgetWrapper::valid() const
 void QgsColorWidgetWrapper::setValue( const QVariant& value )
 {
   if ( mColorButton )
-    mColorButton->setColor( QColor( value.toString() ) );
+    mColorButton->setColor( !value.isNull() ? QColor( value.toString() ) : QColor() );
 }
diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp
index 5594051..beb1862 100644
--- a/src/gui/editorwidgets/qgsdatetimeedit.cpp
+++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp
@@ -41,7 +41,7 @@ QgsDateTimeEdit::QgsDateTimeEdit( QWidget *parent )
   mNullLabel->setStyleSheet( "position: absolute; border: none; font-style: italic; color: grey;" );
   mNullLabel->hide();
 
-  setStyleSheet( QString( "padding-right: %1px;" ).arg( mClearButton->sizeHint().width() + spinButtonWidth() + frameWidth() + 1 ) );
+  setStyleSheet( QString( ".QWidget, QLineEdit, QToolButton { padding-right: %1px; }" ).arg( mClearButton->sizeHint().width() + spinButtonWidth() + frameWidth() + 1 ) );
 
   QSize msz = minimumSizeHint();
   setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ),
diff --git a/src/gui/editorwidgets/qgsphotowidgetwrapper.cpp b/src/gui/editorwidgets/qgsphotowidgetwrapper.cpp
index 9d74e6e..0ddc897 100644
--- a/src/gui/editorwidgets/qgsphotowidgetwrapper.cpp
+++ b/src/gui/editorwidgets/qgsphotowidgetwrapper.cpp
@@ -56,6 +56,18 @@ void QgsPhotoWidgetWrapper::selectFileName()
 
 void QgsPhotoWidgetWrapper::loadPixmap( const QString& fileName )
 {
+  if ( fileName.isEmpty() )
+  {
+#ifdef WITH_QTWEBKIT
+    if ( mWebView )
+    {
+      mWebView->setUrl( QString() );
+    }
+#endif
+    clearPicture();
+    return;
+  }
+
   QString filePath = fileName;
 
   if ( QUrl( fileName ).isRelative() )
@@ -98,6 +110,24 @@ void QgsPhotoWidgetWrapper::loadPixmap( const QString& fileName )
       mPhotoLabel->setPixmap( pm );
     }
   }
+  else
+  {
+    clearPicture();
+  }
+}
+
+void QgsPhotoWidgetWrapper::clearPicture()
+{
+  if ( mPhotoLabel )
+  {
+    mPhotoLabel->clear();
+    mPhotoLabel->setMinimumSize( QSize( 0, 0 ) );
+
+    if ( mPhotoPixmapLabel )
+      mPhotoPixmapLabel->setPixmap( QPixmap() );
+    else
+      mPhotoLabel->setPixmap( QPixmap() );
+  }
 }
 
 QVariant QgsPhotoWidgetWrapper::value() const
@@ -128,6 +158,8 @@ QWidget* QgsPhotoWidgetWrapper::createWidget( QWidget* parent )
   layout->addWidget( label, 0, 0, 1, 2 );
   layout->addWidget( le, 1, 0 );
   layout->addWidget( pb, 1, 1 );
+  layout->setMargin( 0 );
+  layout->setContentsMargins( 0, 0, 0, 0 );
 
   container->setLayout( layout );
 
@@ -200,7 +232,12 @@ void QgsPhotoWidgetWrapper::setValue( const QVariant& value )
   if ( mLineEdit )
   {
     if ( value.isNull() )
+    {
+      mLineEdit->blockSignals( true );
       mLineEdit->setText( QSettings().value( "qgis/nullValue", "NULL" ).toString() );
+      mLineEdit->blockSignals( false );
+      clearPicture();
+    }
     else
       mLineEdit->setText( value.toString() );
   }
diff --git a/src/gui/editorwidgets/qgsphotowidgetwrapper.h b/src/gui/editorwidgets/qgsphotowidgetwrapper.h
index cc4a1e8..6776a0f 100644
--- a/src/gui/editorwidgets/qgsphotowidgetwrapper.h
+++ b/src/gui/editorwidgets/qgsphotowidgetwrapper.h
@@ -77,6 +77,8 @@ class GUI_EXPORT QgsPhotoWidgetWrapper : public QgsEditorWidgetWrapper
     QLineEdit* mLineEdit;
     //! The button to open the file chooser dialog
     QPushButton* mButton;
+
+    void clearPicture();
 };
 
 #endif // QGSPHOTOWIDGETWRAPPER_H
diff --git a/src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp b/src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp
index 89dc5ba..039cd85 100644
--- a/src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp
+++ b/src/gui/editorwidgets/qgswebviewwidgetwrapper.cpp
@@ -70,6 +70,8 @@ QWidget* QgsWebViewWidgetWrapper::createWidget( QWidget* parent )
   layout->addWidget( webView, 0, 0, 1, 2 );
   layout->addWidget( le, 1, 0 );
   layout->addWidget( pb, 1, 1 );
+  layout->setMargin( 0 );
+  layout->setContentsMargins( 0, 0, 0, 0 );
 
   container->setLayout( layout );
 
diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp
index e51c9c9..afbd079 100644
--- a/src/gui/qgsattributeform.cpp
+++ b/src/gui/qgsattributeform.cpp
@@ -176,9 +176,14 @@ bool QgsAttributeForm::save()
       {
         QVariant dstVar = dst.at( eww->fieldIdx() );
         QVariant srcVar = eww->value();
+
         // need to check dstVar.isNull() != srcVar.isNull()
         // otherwise if dstVar=NULL and scrVar=0, then dstVar = srcVar
-        if (( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() && !mLayer->editFormConfig()->readOnly( eww->fieldIdx() ) )
+        // be careful- sometimes two null qvariants will be reported as not equal!! (eg different types)
+        bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
+                       || ( dstVar.isNull() != srcVar.isNull() );
+        if ( changed && srcVar.isValid()
+             && !mLayer->editFormConfig()->readOnly( eww->fieldIdx() ) )
         {
           dst[eww->fieldIdx()] = srcVar;
 
diff --git a/src/gui/qgscodeeditor.h b/src/gui/qgscodeeditor.h
index 9117e15..f957c53 100644
--- a/src/gui/qgscodeeditor.h
+++ b/src/gui/qgscodeeditor.h
@@ -39,8 +39,8 @@ class GUI_EXPORT QgsCodeEditor : public QsciScintilla
      *
      * @param parent The parent QWidget
      * @param title The title to show in the code editor dialog
-     * @param folding False: Enable margin for code editor
-     * @param margin False: Enable folding for code editor
+     * @param folding false: Enable folding for code editor
+     * @param margin false: Enable margin for code editor
      * @note added in 2.6
      */
     QgsCodeEditor( QWidget *parent = nullptr, const QString& title = "", bool folding = false, bool margin = false );
diff --git a/src/gui/qgscollapsiblegroupbox.cpp b/src/gui/qgscollapsiblegroupbox.cpp
index 90ea75c..28bce83 100644
--- a/src/gui/qgscollapsiblegroupbox.cpp
+++ b/src/gui/qgscollapsiblegroupbox.cpp
@@ -408,6 +408,7 @@ void QgsCollapsibleGroupBoxBasic::updateStyle()
 
 void QgsCollapsibleGroupBoxBasic::setCollapsed( bool collapse )
 {
+  bool changed = collapse != mCollapsed;
   mCollapsed = collapse;
 
   if ( !isVisible() )
@@ -444,7 +445,8 @@ void QgsCollapsibleGroupBoxBasic::setCollapsed( bool collapse )
     mParentScrollArea->setUpdatesEnabled( true );
   }
   // emit signal for connections using collapsed state
-  emit collapsedStateChanged( isCollapsed() );
+  if ( changed )
+    emit collapsedStateChanged( isCollapsed() );
 }
 
 void QgsCollapsibleGroupBoxBasic::collapseExpandFixes()
diff --git a/src/gui/qgscolorbuttonv2.cpp b/src/gui/qgscolorbuttonv2.cpp
index 63d3568..c08fd24 100644
--- a/src/gui/qgscolorbuttonv2.cpp
+++ b/src/gui/qgscolorbuttonv2.cpp
@@ -159,7 +159,9 @@ void QgsColorButtonV2::setToNoColor()
 {
   if ( mAllowAlpha )
   {
-    setColor( QColor( 0, 0, 0, 0 ) );
+    QColor noColor = QColor( mColor );
+    noColor.setAlpha( 0 );
+    setColor( noColor );
   }
 }
 
diff --git a/src/gui/qgscomposerview.cpp b/src/gui/qgscomposerview.cpp
index 679919d..8787aef 100644
--- a/src/gui/qgscomposerview.cpp
+++ b/src/gui/qgscomposerview.cpp
@@ -47,6 +47,9 @@
 #include "qgscursors.h"
 #include "qgscomposerutils.h"
 
+#define MIN_VIEW_SCALE 0.05
+#define MAX_VIEW_SCALE 1000.0
+
 QgsComposerView::QgsComposerView( QWidget* parent, const char* name, const Qt::WindowFlags& f )
     : QGraphicsView( parent )
     , mCurrentTool( Select )
@@ -1651,11 +1654,11 @@ void QgsComposerView::wheelZoom( QWheelEvent * event )
   //zoom composition
   if ( zoomIn )
   {
-    scale( zoomFactor, zoomFactor );
+    scaleSafe( zoomFactor );
   }
   else
   {
-    scale( 1 / zoomFactor, 1 / zoomFactor );
+    scaleSafe( 1 / zoomFactor );
   }
 
   //update composition for new zoom
@@ -1683,7 +1686,7 @@ void QgsComposerView::setZoomLevel( double zoomLevel )
     dpi = 72;
 
   //desired pixel width for 1mm on screen
-  double scale = zoomLevel * dpi / 25.4;
+  double scale = qBound( MIN_VIEW_SCALE, zoomLevel * dpi / 25.4, MAX_VIEW_SCALE );
   setTransform( QTransform::fromScale( scale, scale ) );
 
   updateRulers();
@@ -1691,6 +1694,14 @@ void QgsComposerView::setZoomLevel( double zoomLevel )
   emit zoomLevelChanged();
 }
 
+void QgsComposerView::scaleSafe( double scale )
+{
+  double currentScale = transform().m11();
+  scale *= currentScale;
+  scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE );
+  setTransform( QTransform::fromScale( scale, scale ) );
+}
+
 void QgsComposerView::setPreviewModeEnabled( bool enabled )
 {
   if ( !mPreviewEffect )
diff --git a/src/gui/qgscomposerview.h b/src/gui/qgscomposerview.h
index eafa1c9..d33a3be 100644
--- a/src/gui/qgscomposerview.h
+++ b/src/gui/qgscomposerview.h
@@ -143,6 +143,13 @@ class GUI_EXPORT QgsComposerView: public QGraphicsView
     /** Set zoom level, where a zoom level of 1.0 corresponds to 100%*/
     void setZoomLevel( double zoomLevel );
 
+    /** Scales the view in a safe way, by limiting the acceptable range
+     * of the scale applied.
+     * @param scale factor to scale view by
+     * @note added in QGIS 2.16
+     */
+    void scaleSafe( double scale );
+
     /** Sets whether a preview effect should be used to alter the view's appearance
      * @param enabled Set to true to enable the preview effect on the view
      * @note added in 2.3
diff --git a/src/gui/qgsexternalresourcewidget.cpp b/src/gui/qgsexternalresourcewidget.cpp
index d061108..5605410 100644
--- a/src/gui/qgsexternalresourcewidget.cpp
+++ b/src/gui/qgsexternalresourcewidget.cpp
@@ -142,7 +142,11 @@ void QgsExternalResourceWidget::updateDocumentViewer()
   {
     const QPixmap* pm = mPixmapLabel->pixmap();
 
-    if ( pm )
+    if ( !pm || pm->isNull() )
+    {
+      mPixmapLabel->setMinimumSize( QSize( 0, 0 ) );
+    }
+    else
     {
       QSize size( mDocumentViewerWidth, mDocumentViewerHeight );
       if ( size.width() == 0 && size.height() > 0 )
@@ -165,7 +169,7 @@ void QgsExternalResourceWidget::updateDocumentViewer()
 
 void QgsExternalResourceWidget::loadDocument( const QString& path )
 {
-  if ( path.isNull() )
+  if ( path.isEmpty() )
   {
 #ifdef WITH_QTWEBKIT
     if ( mDocumentViewerContent == Web )
@@ -176,6 +180,7 @@ void QgsExternalResourceWidget::loadDocument( const QString& path )
     if ( mDocumentViewerContent == Image )
     {
       mPixmapLabel->clear();
+      updateDocumentViewer();
     }
   }
 
@@ -190,12 +195,8 @@ void QgsExternalResourceWidget::loadDocument( const QString& path )
   if ( mDocumentViewerContent == Image )
   {
     QPixmap pm( path );
-    if ( !pm.isNull() )
-    {
-      mPixmapLabel->setPixmap( pm );
-
-      updateDocumentViewer();
-    }
+    mPixmapLabel->setPixmap( pm );
+    updateDocumentViewer();
   }
 }
 
diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp
index babf0c4..856d16b 100644
--- a/src/gui/qgsmapcanvas.cpp
+++ b/src/gui/qgsmapcanvas.cpp
@@ -862,7 +862,7 @@ QgsRectangle QgsMapCanvas::fullExtent() const
 } // extent
 
 
-void QgsMapCanvas::setExtent( QgsRectangle const & r )
+void QgsMapCanvas::setExtent( const QgsRectangle& r )
 {
   QgsRectangle current = extent();
 
@@ -1134,8 +1134,14 @@ void QgsMapCanvas::panToSelected( QgsVectorLayer* layer )
     return;
 
   QgsRectangle rect = mapSettings().layerExtentToOutputExtent( layer, layer->boundingBoxOfSelected() );
-  setExtent( QgsRectangle( rect.center(), rect.center() ) );
-  refresh();
+  if ( !rect.isNull() )
+  {
+    setCenter( rect.center() );
+  }
+  else
+  {
+    emit messageEmitted( tr( "Cannot pan to selected feature(s)" ), tr( "Geometry is NULL" ), QgsMessageBar::WARNING );
+  }
 } // panToSelected
 
 void QgsMapCanvas::keyPressEvent( QKeyEvent * e )
@@ -1192,6 +1198,7 @@ void QgsMapCanvas::keyPressEvent( QKeyEvent * e )
         //mCanvasProperties->dragging = true;
         if ( ! e->isAutoRepeat() )
         {
+          QApplication::setOverrideCursor( Qt::ClosedHandCursor );
           mCanvasProperties->panSelectorDown = true;
           mCanvasProperties->rubberStartPoint = mCanvasProperties->mouseLastXY;
         }
@@ -1245,7 +1252,7 @@ void QgsMapCanvas::keyReleaseEvent( QKeyEvent * e )
       if ( !e->isAutoRepeat() && mCanvasProperties->panSelectorDown )
       {
         QgsDebugMsg( "Releasing pan selector" );
-
+        QApplication::restoreOverrideCursor();
         mCanvasProperties->panSelectorDown = false;
         panActionEnd( mCanvasProperties->mouseLastXY );
       }
diff --git a/src/gui/qgsmessagelogviewer.cpp b/src/gui/qgsmessagelogviewer.cpp
index 92a379d..bbd4c93 100644
--- a/src/gui/qgsmessagelogviewer.cpp
+++ b/src/gui/qgsmessagelogviewer.cpp
@@ -78,5 +78,6 @@ void QgsMessageLogViewer::logMessage( QString message, QString tag, QgsMessageLo
 
 void QgsMessageLogViewer::closeTab( int index )
 {
-  tabWidget->removeTab( index );
+  if ( tabWidget->count() > 1 )
+    tabWidget->removeTab( index );
 }
diff --git a/src/gui/qgspixmaplabel.cpp b/src/gui/qgspixmaplabel.cpp
index 7411a9e..c853e70 100644
--- a/src/gui/qgspixmaplabel.cpp
+++ b/src/gui/qgspixmaplabel.cpp
@@ -24,17 +24,31 @@ QgsPixmapLabel::QgsPixmapLabel( QWidget *parent ) :
 
 void QgsPixmapLabel::setPixmap( const QPixmap & p )
 {
+  bool sizeChanged = ( p.size() != mPixmap.size() );
   mPixmap = p;
-  QLabel::setPixmap( p );
+
+  if ( sizeChanged )
+  {
+    updateGeometry();
+  }
+
+  QLabel::setPixmap( mPixmap.scaled( this->size(),
+                                     Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
 }
 
 int QgsPixmapLabel::heightForWidth( int width ) const
 {
+  if ( mPixmap.isNull() )
+    return 0;
+
   return (( qreal )mPixmap.height()*width ) / mPixmap.width();
 }
 
 QSize QgsPixmapLabel::sizeHint() const
 {
+  if ( mPixmap.isNull() )
+    return QSize( 0, 0 );
+
   int w = this->width();
   return QSize( w, heightForWidth( w ) );
 }
diff --git a/src/gui/qgsrasterlayersaveasdialog.cpp b/src/gui/qgsrasterlayersaveasdialog.cpp
index 779a232..e09c84c 100644
--- a/src/gui/qgsrasterlayersaveasdialog.cpp
+++ b/src/gui/qgsrasterlayersaveasdialog.cpp
@@ -163,13 +163,14 @@ void QgsRasterLayerSaveAsDialog::on_mBrowseButton_clicked()
 
   if ( mTileModeCheckBox->isChecked() )
   {
-    while ( true )
+    Q_FOREVER
     {
       // TODO: would not it be better to select .vrt file instead of directory?
       fileName = QFileDialog::getExistingDirectory( this, tr( "Select output directory" ), dirName );
       //fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), QString(), tr( "VRT" ) + " (*.vrt *.VRT)" );
 
-      if ( fileName.isEmpty() ) break; // canceled
+      if ( fileName.isEmpty() )
+        break; // canceled
 
       // Check if directory is empty
       QDir dir( fileName );
@@ -177,39 +178,30 @@ void QgsRasterLayerSaveAsDialog::on_mBrowseButton_clicked()
       QStringList filters;
       filters << QString( "%1.*" ).arg( baseName );
       QStringList files = dir.entryList( filters );
-      if ( !files.isEmpty() )
-      {
-        QMessageBox::StandardButton button = QMessageBox::warning( this, tr( "Warning" ),
-                                             tr( "The directory %1 contains files which will be overwritten: %2" ).arg( dir.absolutePath(), files.join( ", " ) ),
-                                             QMessageBox::Ok | QMessageBox::Cancel );
+      if ( files.isEmpty() )
+        break;
 
-        if ( button == QMessageBox::Ok )
-        {
-          break;
-        }
-        else
-        {
-          fileName = "";
-        }
-      }
-      else
-      {
+      if ( QMessageBox::warning( this, tr( "Warning" ),
+                                 tr( "The directory %1 contains files which will be overwritten: %2" ).arg( dir.absolutePath(), files.join( ", " ) ),
+                                 QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Ok )
         break;
-      }
+
+      fileName = "";
     }
   }
   else
   {
     fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), dirName, tr( "GeoTIFF" ) + " (*.tif *.tiff *.TIF *.TIFF)" );
-  }
 
-  if ( !fileName.isEmpty() )
-  {
-    // ensure the user never ommited the extension from the file name
-    if ( !fileName.endsWith( ".tif", Qt::CaseInsensitive ) && !fileName.endsWith( ".tiff", Qt::CaseInsensitive ) )
+    // ensure the user never omits the extension from the file name
+    if ( !fileName.isEmpty() && !fileName.endsWith( ".tif", Qt::CaseInsensitive ) && !fileName.endsWith( ".tiff", Qt::CaseInsensitive ) )
     {
       fileName += ".tif";
     }
+  }
+
+  if ( !fileName.isEmpty() )
+  {
     mSaveAsLineEdit->setText( fileName );
   }
 }
diff --git a/src/gui/qgsrelationeditorwidget.cpp b/src/gui/qgsrelationeditorwidget.cpp
index 9472399..06adc31 100644
--- a/src/gui/qgsrelationeditorwidget.cpp
+++ b/src/gui/qgsrelationeditorwidget.cpp
@@ -47,35 +47,41 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget* parent )
   mToggleEditingButton->setText( tr( "Toggle editing" ) );
   mToggleEditingButton->setEnabled( false );
   mToggleEditingButton->setCheckable( true );
+  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
   buttonLayout->addWidget( mToggleEditingButton );
   // save Edits
   mSaveEditsButton = new QToolButton( this );
   mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( "/mActionSaveEdits.svg" ) );
   mSaveEditsButton->setText( tr( "Save layer edits" ) );
+  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
   mSaveEditsButton->setEnabled( true );
   buttonLayout->addWidget( mSaveEditsButton );
   // add feature
   mAddFeatureButton = new QToolButton( this );
   mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionAdd.svg" ) );
   mAddFeatureButton->setText( tr( "Add feature" ) );
+  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
   mAddFeatureButton->setObjectName( "mAddFeatureButton" );
   buttonLayout->addWidget( mAddFeatureButton );
   // delete feature
   mDeleteFeatureButton = new QToolButton( this );
   mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.svg" ) );
   mDeleteFeatureButton->setText( tr( "Delete feature" ) );
+  mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) );
   mDeleteFeatureButton->setObjectName( "mDeleteFeatureButton" );
   buttonLayout->addWidget( mDeleteFeatureButton );
   // link feature
   mLinkFeatureButton = new QToolButton( this );
   mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionLink.svg" ) );
   mLinkFeatureButton->setText( tr( "Link feature" ) );
+  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
   mLinkFeatureButton->setObjectName( "mLinkFeatureButton" );
   buttonLayout->addWidget( mLinkFeatureButton );
   // unlink feature
   mUnlinkFeatureButton = new QToolButton( this );
   mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionUnlink.svg" ) );
   mUnlinkFeatureButton->setText( tr( "Unlink feature" ) );
+  mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) );
   mUnlinkFeatureButton->setObjectName( "mUnlinkFeatureButton" );
   buttonLayout->addWidget( mUnlinkFeatureButton );
   // spacer
@@ -83,6 +89,7 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget* parent )
   // form view
   mFormViewButton = new QToolButton( this );
   mFormViewButton->setText( tr( "Form view" ) );
+  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
   mFormViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) );
   mFormViewButton->setCheckable( true );
   mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
@@ -90,6 +97,7 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget* parent )
   // table view
   mTableViewButton = new QToolButton( this );
   mTableViewButton->setText( tr( "Table view" ) );
+  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
   mTableViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.svg" ) );
   mTableViewButton->setCheckable( true );
   mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
diff --git a/src/gui/qgsrubberband.cpp b/src/gui/qgsrubberband.cpp
index c480af1..5d7b1c7 100644
--- a/src/gui/qgsrubberband.cpp
+++ b/src/gui/qgsrubberband.cpp
@@ -188,6 +188,26 @@ void QgsRubberBand::addPoint( const QgsPoint & p, bool doUpdate /* = true */, in
   }
 }
 
+void QgsRubberBand::closePoints( bool doUpdate, int geometryIndex )
+{
+  if ( geometryIndex < 0 || geometryIndex >= mPoints.size() )
+  {
+    return;
+  }
+
+  if ( mPoints.at( geometryIndex ).at( 0 ) != mPoints.at( geometryIndex ).at( mPoints.at( geometryIndex ).size() - 1 ) )
+  {
+    mPoints[geometryIndex] << mPoints.at( geometryIndex ).at( 0 );
+  }
+
+  if ( doUpdate )
+  {
+    setVisible( true );
+    updateRect();
+    update();
+  }
+}
+
 
 void QgsRubberBand::removePoint( int index, bool doUpdate/* = true*/, int geometryIndex/* = 0*/ )
 {
diff --git a/src/gui/qgsrubberband.h b/src/gui/qgsrubberband.h
index 5a2cc80..65f64a5 100644
--- a/src/gui/qgsrubberband.h
+++ b/src/gui/qgsrubberband.h
@@ -149,6 +149,14 @@ class GUI_EXPORT QgsRubberBand: public QgsMapCanvasItem
      */
     void addPoint( const QgsPoint & p, bool doUpdate = true, int geometryIndex = 0 );
 
+    /** Ensures that a polygon geometry is closed and that the last vertex equals the
+     * first vertex.
+     * @param doUpdate set to true to update the map canvas immediately
+     * @param geometryIndex index of the feature part (in case of multipart geometries)
+     * @note added in QGIS 2.16
+     */
+    void closePoints( bool doUpdate = true, int geometryIndex = 0 );
+
     /**
      * Remove a vertex from the rubberband and (optionally) update canvas.
      * @param index The index of the vertex/point to remove, negative indexes start at end
diff --git a/src/gui/symbology-ng/qgs25drendererwidget.cpp b/src/gui/symbology-ng/qgs25drendererwidget.cpp
index ce1b168..2aaf868 100644
--- a/src/gui/symbology-ng/qgs25drendererwidget.cpp
+++ b/src/gui/symbology-ng/qgs25drendererwidget.cpp
@@ -38,6 +38,16 @@ Qgs25DRendererWidget::Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* s
 
   setupUi( this );
 
+  mWallColorButton->setColorDialogTitle( tr( "Select wall color" ) );
+  mWallColorButton->setAllowAlpha( true );
+  mWallColorButton->setContext( "symbology" );
+  mRoofColorButton->setColorDialogTitle( tr( "Select roof color" ) );
+  mRoofColorButton->setAllowAlpha( true );
+  mRoofColorButton->setContext( "symbology" );
+  mShadowColorButton->setColorDialogTitle( tr( "Select shadow color" ) );
+  mShadowColorButton->setAllowAlpha( true );
+  mShadowColorButton->setContext( "symbology" );
+
   if ( renderer )
   {
     mRenderer = Qgs25DRenderer::convertFromRenderer( renderer );
@@ -86,10 +96,13 @@ void Qgs25DRendererWidget::updateRenderer()
 
 void Qgs25DRendererWidget::apply()
 {
-  QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_height", mHeightWidget->currentText() );
-  QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_angle", mAngleWidget->value() );
+  if ( mHeightWidget )
+  {
+    QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_height", mHeightWidget->currentText() );
+    QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_angle", mAngleWidget->value() );
 
-  emit layerVariablesChanged();
+    emit layerVariablesChanged();
+  }
 }
 
 QgsRendererV2Widget* Qgs25DRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
diff --git a/src/plugins/georeferencer/qgsgeoreftransform.cpp b/src/plugins/georeferencer/qgsgeoreftransform.cpp
index 8af66a6..e36b4d9 100644
--- a/src/plugins/georeferencer/qgsgeoreftransform.cpp
+++ b/src/plugins/georeferencer/qgsgeoreftransform.cpp
@@ -575,10 +575,11 @@ bool QgsProjectiveGeorefTransform::updateParametersFromGCPs( const QVector<QgsPo
     return false;
 
   // HACK: flip y coordinates, because georeferencer and gdal use different conventions
-  QVector<QgsPoint> flippedPixelCoords( pixelCoords.size() );
-  for ( int i = 0; i < pixelCoords.size(); i++ )
+  QVector<QgsPoint> flippedPixelCoords;
+  flippedPixelCoords.reserve( pixelCoords.size() );
+  Q_FOREACH ( const QgsPoint& coord, pixelCoords )
   {
-    flippedPixelCoords[i] = pixelCoords[i];
+    flippedPixelCoords << QgsPoint( coord.x(), -coord.y() );
   }
 
   QgsLeastSquares::projective( mapCoords, flippedPixelCoords, mParameters.H );
diff --git a/src/plugins/gps_importer/qgsgpsplugin.cpp b/src/plugins/gps_importer/qgsgpsplugin.cpp
index f82eedb..9bab68f 100644
--- a/src/plugins/gps_importer/qgsgpsplugin.cpp
+++ b/src/plugins/gps_importer/qgsgpsplugin.cpp
@@ -570,7 +570,7 @@ void QgsGPSPlugin::setupBabel()
 {
   // where is gpsbabel?
   QSettings settings;
-  mBabelPath = settings.value( "/Plugin-GPS/gpsbabelpath", QDir::homePath() ).toString();
+  mBabelPath = settings.value( "/Plugin-GPS/gpsbabelpath", QString() ).toString();
   if ( mBabelPath.isEmpty() )
     mBabelPath = "gpsbabel";
   // the importable formats
diff --git a/src/plugins/offline_editing/offline_editing_plugin_gui.cpp b/src/plugins/offline_editing/offline_editing_plugin_gui.cpp
index 7dd54af..0dc47d0 100644
--- a/src/plugins/offline_editing/offline_editing_plugin_gui.cpp
+++ b/src/plugins/offline_editing/offline_editing_plugin_gui.cpp
@@ -170,7 +170,7 @@ void QgsOfflineEditingPluginGui::on_buttonBox_helpRequested()
 void QgsOfflineEditingPluginGui::restoreState()
 {
   QSettings settings;
-  mOfflineDataPath = settings.value( "Plugin-OfflineEditing/offline_data_path", QDir().absolutePath() ).toString();
+  mOfflineDataPath = settings.value( "Plugin-OfflineEditing/offline_data_path", QDir::homePath() ).toString();
   restoreGeometry( settings.value( "Plugin-OfflineEditing/geometry" ).toByteArray() );
 }
 
diff --git a/src/plugins/roadgraph/shortestpathwidget.cpp b/src/plugins/roadgraph/shortestpathwidget.cpp
index 3b6ac10..7b84102 100644
--- a/src/plugins/roadgraph/shortestpathwidget.cpp
+++ b/src/plugins/roadgraph/shortestpathwidget.cpp
@@ -248,7 +248,7 @@ QgsGraph* RgShortestPathWidget::getPath( QgsPoint& p1, QgsPoint& p2 )
     const QgsGraphDirector *director = mPlugin->director();
     if ( !director )
     {
-      QMessageBox::critical( this, tr( "Plugin isn't configured" ), tr( "Plugin isn't configured! Please go to the Vector → Road graph → Settings to configure it." ) );
+      QMessageBox::critical( this, tr( "Plugin isn't configured" ), tr( "Plugin isn't configured! Please go to the Vector, Road graph, Settings to configure it." ) );
       return nullptr;
     }
     connect( director, SIGNAL( buildProgress( int, int ) ), mPlugin->iface()->mainWindow(), SLOT( showProgress( int, int ) ) );
diff --git a/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp b/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp
index 43eacf8..0638034 100644
--- a/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp
+++ b/src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp
@@ -341,7 +341,7 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )
 
     if ( ! mTestSubset && ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) )
     {
-      const QgsAttributeList& attrs = mRequest.subsetOfAttributes();
+      QgsAttributeList attrs = mRequest.subsetOfAttributes();
       for ( QgsAttributeList::const_iterator i = attrs.begin(); i != attrs.end(); ++i )
       {
         int fieldIdx = *i;
diff --git a/src/providers/memory/qgsmemoryprovider.cpp b/src/providers/memory/qgsmemoryprovider.cpp
index 1f74b11..c029533 100644
--- a/src/providers/memory/qgsmemoryprovider.cpp
+++ b/src/providers/memory/qgsmemoryprovider.cpp
@@ -334,6 +334,7 @@ bool QgsMemoryProvider::addFeatures( QgsFeatureList & flist )
     mFeatures[mNextFeatureId] = *it;
     QgsFeature& newfeat = mFeatures[mNextFeatureId];
     newfeat.setFeatureId( mNextFeatureId );
+    newfeat.setValid( true );
     it->setFeatureId( mNextFeatureId );
 
     // update spatial index
diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp
index 4bfdc57..df1ea3a 100644
--- a/src/providers/mssql/qgsmssqlprovider.cpp
+++ b/src/providers/mssql/qgsmssqlprovider.cpp
@@ -1851,7 +1851,7 @@ QgsVectorLayerImport::ImportError QgsMssqlProvider::createEmptyLayer(
       QgsField fld = fields[i];
       if ( oldToNewAttrIdxMap && fld.name() == primaryKey )
       {
-        oldToNewAttrIdxMap->insert( fields.indexFromName( fld.name() ), 0 );
+        oldToNewAttrIdxMap->insert( fields.fieldNameIndex( fld.name() ), 0 );
         continue;
       }
 
@@ -1872,7 +1872,7 @@ QgsVectorLayerImport::ImportError QgsMssqlProvider::createEmptyLayer(
 
       flist.append( fld );
       if ( oldToNewAttrIdxMap )
-        oldToNewAttrIdxMap->insert( fields.indexFromName( fld.name() ), offset++ );
+        oldToNewAttrIdxMap->insert( fields.fieldNameIndex( fld.name() ), offset++ );
     }
 
     if ( !provider->addAttributes( flist ) )
diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp
index 8850109..426c117 100644
--- a/src/providers/ogr/qgsogrfeatureiterator.cpp
+++ b/src/providers/ogr/qgsogrfeatureiterator.cpp
@@ -36,14 +36,19 @@
 
 QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
     : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request )
+    , mFeatureFetched( false )
+    , mConn( nullptr )
     , ogrLayer( nullptr )
     , mSubsetStringSet( false )
+    , mFetchGeometry( false )
     , mGeometrySimplifier( nullptr )
     , mExpressionCompiled( false )
 {
-  mFeatureFetched = false;
-
   mConn = QgsOgrConnPool::instance()->acquireConnection( mSource->mProvider->dataSourceUri() );
+  if ( !mConn->ds )
+  {
+    return;
+  }
 
   if ( mSource->mLayerName.isNull() )
   {
@@ -53,10 +58,18 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
   {
     ogrLayer = OGR_DS_GetLayerByName( mConn->ds, TO8( mSource->mLayerName ) );
   }
+  if ( !ogrLayer )
+  {
+    return;
+  }
 
   if ( !mSource->mSubsetString.isEmpty() )
   {
     ogrLayer = QgsOgrUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString );
+    if ( !ogrLayer )
+    {
+      return;
+    }
     mSubsetStringSet = true;
   }
 
@@ -204,7 +217,7 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature )
 {
   feature.setValid( false );
 
-  if ( mClosed )
+  if ( mClosed || !ogrLayer )
     return false;
 
   if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
@@ -249,7 +262,7 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature )
 
 bool QgsOgrFeatureIterator::rewind()
 {
-  if ( mClosed )
+  if ( mClosed || !ogrLayer )
     return false;
 
   OGR_L_ResetReading( ogrLayer );
@@ -391,7 +404,7 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
   // fetch attributes
   if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
   {
-    const QgsAttributeList& attrs = mRequest.subsetOfAttributes();
+    QgsAttributeList attrs = mRequest.subsetOfAttributes();
     for ( QgsAttributeList::const_iterator it = attrs.begin(); it != attrs.end(); ++it )
     {
       getFeatureAttribute( fet, feature, *it );
diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp
index a170890..3249e4a 100644
--- a/src/providers/ogr/qgsogrprovider.cpp
+++ b/src/providers/ogr/qgsogrprovider.cpp
@@ -139,7 +139,7 @@ bool QgsOgrProvider::convertField( QgsField &field, const QTextCodec &encoding )
 
 void QgsOgrProvider::repack()
 {
-  if ( ogrDriverName != "ESRI Shapefile" || !ogrOrigLayer )
+  if ( !mValid || ogrDriverName != "ESRI Shapefile" || !ogrOrigLayer )
     return;
 
   QByteArray layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrOrigLayer ) );
@@ -283,7 +283,11 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
     , geomType( wkbUnknown )
     , mFeaturesCounted( -1 )
     , mWriteAccess( false )
+    , mWriteAccessPossible( false )
+    , mDynamicWriteAccess( false )
     , mShapefileMayBeCorrupted( false )
+    , mUpdateModeStackDepth( 0 )
+    , mCapabilities( 0 )
 {
   QgsApplication::registerOgrDrivers();
 
@@ -354,7 +358,7 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
     }
   }
 
-  open();
+  open( OpenModeInitial );
 
   mNativeTypes
   << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), "integer", QVariant::Int, 1, 10 )
@@ -380,6 +384,10 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
 QgsOgrProvider::~QgsOgrProvider()
 {
   close();
+  QgsOgrConnPool::instance()->unref( dataSourceUri() );
+  // We must also make sure to flush unusef cached connections so that
+  // the file can be removed (#15137)
+  QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
 }
 
 QgsAbstractFeatureSource* QgsOgrProvider::featureSource() const
@@ -391,6 +399,9 @@ bool QgsOgrProvider::setSubsetString( const QString& theSQL, bool updateFeatureC
 {
   QgsCPLErrorHandler handler;
 
+  if ( !ogrDataSource )
+    return false;
+
   if ( theSQL == mSubsetString && mFeaturesCounted >= 0 )
     return true;
 
@@ -657,6 +668,11 @@ OGRwkbGeometryType QgsOgrProvider::getOgrGeomType( OGRLayerH ogrLayer )
   {
     geomType = OGR_FD_GetGeomType( fdef );
 
+    // Handle wkbUnknown and its Z/M variants. QGIS has no unknown Z/M variants,
+    // so just use flat wkbUnknown
+    if ( wkbFlatten( geomType ) == wkbUnknown )
+      geomType = wkbUnknown;
+
     // Some ogr drivers (e.g. GML) are not able to determine the geometry type of a layer like this.
     // In such cases, we use virtual sublayers for each geometry if the layer contains
     // multiple geometries (see subLayers) otherwise we guess geometry type from first feature
@@ -688,6 +704,8 @@ void QgsOgrProvider::loadFields()
   QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
   //the attribute fields need to be read again when the encoding changes
   mAttributeFields.clear();
+  if ( !ogrLayer )
+    return;
 
   if ( mOgrGeometryTypeFilter != wkbUnknown )
   {
@@ -898,6 +916,8 @@ void QgsOgrProvider::updateExtents()
 
 size_t QgsOgrProvider::layerCount() const
 {
+  if ( !mValid )
+    return 0;
   return OGR_DS_GetLayerCount( ogrDataSource );
 } // QgsOgrProvider::layerCount()
 
@@ -1059,6 +1079,9 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
 
 bool QgsOgrProvider::addFeatures( QgsFeatureList & flist )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   setRelevantFields( ogrLayer, true, attributeIndexes() );
 
   bool returnvalue = true;
@@ -1085,6 +1108,16 @@ bool QgsOgrProvider::addFeatures( QgsFeatureList & flist )
 
 bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
+  if ( ogrDriverName == "MapInfo File" )
+  {
+    // adding attributes in mapinfo requires to be able to delete the .dat file
+    // so drop any cached connections.
+    QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
+  }
+
   bool returnvalue = true;
 
   for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
@@ -1142,6 +1175,9 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
 
 bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
 #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1900
   bool res = true;
   QList<int> attrsLst = attributes.toList();
@@ -1167,6 +1203,9 @@ bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
 
 bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   if ( attr_map.isEmpty() )
     return true;
 
@@ -1270,6 +1309,8 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
     {
       pushError( tr( "OGR error setting feature %1: %2" ).arg( fid ).arg( CPLGetLastErrorMsg() ) );
     }
+
+    OGR_F_Destroy( of );
   }
 
   if ( OGR_L_SyncToDisk( ogrLayer ) != OGRERR_NONE )
@@ -1282,8 +1323,8 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
 
 bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
 {
-  OGRFeatureH theOGRFeature = nullptr;
-  OGRGeometryH theNewGeometry = nullptr;
+  if ( !doInitialActionsForEdition() )
+    return false;
 
   setRelevantFields( ogrLayer, true, attributeIndexes() );
 
@@ -1295,37 +1336,47 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
       continue;
     }
 
-    theOGRFeature = OGR_L_GetFeature( ogrLayer, static_cast<long>( FID_TO_NUMBER( it.key() ) ) );
+    OGRFeatureH theOGRFeature = OGR_L_GetFeature( ogrLayer, static_cast<long>( FID_TO_NUMBER( it.key() ) ) );
     if ( !theOGRFeature )
     {
       pushError( tr( "OGR error changing geometry: feature %1 not found" ).arg( it.key() ) );
       continue;
     }
 
-    //create an OGRGeometry
-    if ( OGR_G_CreateFromWkb( const_cast<unsigned char*>( it->asWkb() ),
-                              OGR_L_GetSpatialRef( ogrLayer ),
-                              &theNewGeometry,
-                              it->wkbSize() ) != OGRERR_NONE )
+    OGRGeometryH theNewGeometry = nullptr;
+    // We might receive null geometries. It is ok, but don't go through the
+    // OGR_G_CreateFromWkb() route then
+    if ( it->wkbSize() != 0 )
     {
-      pushError( tr( "OGR error creating geometry for feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
-      OGR_G_DestroyGeometry( theNewGeometry );
-      theNewGeometry = nullptr;
-      continue;
-    }
+      //create an OGRGeometry
+      if ( OGR_G_CreateFromWkb( const_cast<unsigned char*>( it->asWkb() ),
+                                OGR_L_GetSpatialRef( ogrLayer ),
+                                &theNewGeometry,
+                                it->wkbSize() ) != OGRERR_NONE )
+      {
+        pushError( tr( "OGR error creating geometry for feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
+        OGR_G_DestroyGeometry( theNewGeometry );
+        theNewGeometry = nullptr;
+        OGR_F_Destroy( theOGRFeature );
+        continue;
+      }
 
-    if ( !theNewGeometry )
-    {
-      pushError( tr( "OGR error in feature %1: geometry is null" ).arg( it.key() ) );
-      continue;
+      if ( !theNewGeometry )
+      {
+        pushError( tr( "OGR error in feature %1: geometry is null" ).arg( it.key() ) );
+        OGR_F_Destroy( theOGRFeature );
+        continue;
+      }
     }
 
     //set the new geometry
     if ( OGR_F_SetGeometryDirectly( theOGRFeature, theNewGeometry ) != OGRERR_NONE )
     {
       pushError( tr( "OGR error setting geometry of feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
-      OGR_G_DestroyGeometry( theNewGeometry );
-      theNewGeometry = nullptr;
+      // Shouldn't happen normally. If it happens, ownership of the geometry
+      // may be not really well defined, so better not destroy it, but just
+      // the feature.
+      OGR_F_Destroy( theOGRFeature );
       continue;
     }
 
@@ -1333,8 +1384,7 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
     if ( OGR_L_SetFeature( ogrLayer, theOGRFeature ) != OGRERR_NONE )
     {
       pushError( tr( "OGR error setting feature %1: %2" ).arg( it.key() ).arg( CPLGetLastErrorMsg() ) );
-      OGR_G_DestroyGeometry( theNewGeometry );
-      theNewGeometry = nullptr;
+      OGR_F_Destroy( theOGRFeature );
       continue;
     }
     mShapefileMayBeCorrupted = true;
@@ -1347,6 +1397,9 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
 
 bool QgsOgrProvider::createSpatialIndex()
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   if ( ogrDriverName != "ESRI Shapefile" )
     return false;
 
@@ -1367,6 +1420,9 @@ bool QgsOgrProvider::createSpatialIndex()
 
 bool QgsOgrProvider::createAttributeIndex( int field )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   QByteArray quotedLayerName = quotedIdentifier( OGR_FD_GetName( OGR_L_GetLayerDefn( ogrOrigLayer ) ) );
   QByteArray dropSql = "DROP INDEX ON " + quotedLayerName;
   OGR_DS_ExecuteSQL( ogrDataSource, dropSql.constData(), OGR_L_GetSpatialFilter( ogrOrigLayer ), nullptr );
@@ -1381,6 +1437,9 @@ bool QgsOgrProvider::createAttributeIndex( int field )
 
 bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds & id )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   bool returnvalue = true;
   for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
   {
@@ -1406,6 +1465,9 @@ bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds & id )
 
 bool QgsOgrProvider::deleteFeature( QgsFeatureId id )
 {
+  if ( !doInitialActionsForEdition() )
+    return false;
+
   if ( FID_TO_NUMBER( id ) > std::numeric_limits<long>::max() )
   {
     pushError( tr( "OGR error on feature %1: id too large" ).arg( id ) );
@@ -1423,8 +1485,28 @@ bool QgsOgrProvider::deleteFeature( QgsFeatureId id )
   return true;
 }
 
+bool QgsOgrProvider::doInitialActionsForEdition()
+{
+  if ( !mValid )
+    return false;
+
+  if ( !mWriteAccess && mWriteAccessPossible && mDynamicWriteAccess )
+  {
+    QgsDebugMsg( "Enter update mode implictly" );
+    if ( !enterUpdateMode() )
+      return false;
+  }
+
+  return true;
+}
+
 int QgsOgrProvider::capabilities() const
 {
+  return mCapabilities;
+}
+
+void QgsOgrProvider::computeCapabilities()
+{
   int ability = 0;
 
   // collect abilities reported by OGR
@@ -1446,19 +1528,19 @@ int QgsOgrProvider::capabilities() const
       ability |= QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::SelectGeometryAtId;
     }
 
-    if ( mWriteAccess && OGR_L_TestCapability( ogrLayer, "SequentialWrite" ) )
+    if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "SequentialWrite" ) )
       // true if the CreateFeature() method works for this layer.
     {
       ability |= QgsVectorDataProvider::AddFeatures;
     }
 
-    if ( mWriteAccess && OGR_L_TestCapability( ogrLayer, "DeleteFeature" ) )
+    if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "DeleteFeature" ) )
       // true if this layer can delete its features
     {
       ability |= DeleteFeatures;
     }
 
-    if ( mWriteAccess && OGR_L_TestCapability( ogrLayer, "RandomWrite" ) )
+    if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "RandomWrite" ) )
       // true if the SetFeature() method is operational on this layer.
     {
       // TODO According to http://shapelib.maptools.org/ (Shapefile C Library V1.2)
@@ -1506,12 +1588,12 @@ int QgsOgrProvider::capabilities() const
     }
 #endif
 
-    if ( mWriteAccess && OGR_L_TestCapability( ogrLayer, "CreateField" ) )
+    if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "CreateField" ) )
     {
       ability |= AddAttributes;
     }
 
-    if ( mWriteAccess && OGR_L_TestCapability( ogrLayer, "DeleteField" ) )
+    if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "DeleteField" ) )
     {
       ability |= DeleteAttributes;
     }
@@ -1561,7 +1643,7 @@ int QgsOgrProvider::capabilities() const
 #endif
   }
 
-  return ability;
+  mCapabilities = ability;
 }
 
 
@@ -2359,6 +2441,8 @@ QgsCoordinateReferenceSystem QgsOgrProvider::crs()
   QgsDebugMsg( "Entering." );
 
   QgsCoordinateReferenceSystem srs;
+  if ( !mValid )
+    return srs;
 
   if ( ogrDriver )
   {
@@ -2410,7 +2494,7 @@ void QgsOgrProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, int
 {
   uniqueValues.clear();
 
-  if ( index < 0 || index >= mAttributeFields.count() )
+  if ( !mValid || index < 0 || index >= mAttributeFields.count() )
     return;
 
   const QgsField& fld = mAttributeFields.at( index );
@@ -2457,7 +2541,7 @@ void QgsOgrProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, int
 
 QVariant QgsOgrProvider::minimumValue( int index )
 {
-  if ( index < 0 || index >= mAttributeFields.count() )
+  if ( !mValid || index < 0 || index >= mAttributeFields.count() )
   {
     return QVariant();
   }
@@ -2496,7 +2580,7 @@ QVariant QgsOgrProvider::minimumValue( int index )
 
 QVariant QgsOgrProvider::maximumValue( int index )
 {
-  if ( index < 0 || index >= mAttributeFields.count() )
+  if ( !mValid || index < 0 || index >= mAttributeFields.count() )
   {
     return QVariant();
   }
@@ -2590,7 +2674,7 @@ QString QgsOgrUtils::quotedValue( const QVariant& value )
 bool QgsOgrProvider::syncToDisc()
 {
   //for shapefiles, remove spatial index files and create a new index
-  QgsOgrConnPool::instance()->unref( mFilePath );
+  QgsOgrConnPool::instance()->unref( dataSourceUri() );
   bool shapeIndex = false;
   if ( ogrDriverName == "ESRI Shapefile" )
   {
@@ -2607,7 +2691,9 @@ bool QgsOgrProvider::syncToDisc()
       close();
       QgsOgrConnPool::instance()->invalidateConnections( dataSourceUri() );
       QFile::remove( sbnIndexFile );
-      open();
+      open( OpenModeSameAsCurrent );
+      if ( !mValid )
+        return false;
     }
   }
 
@@ -2632,6 +2718,12 @@ bool QgsOgrProvider::syncToDisc()
 
 void QgsOgrProvider::recalculateFeatureCount()
 {
+  if ( !ogrLayer )
+  {
+    mFeaturesCounted = 0;
+    return;
+  }
+
   OGRGeometryH filter = OGR_L_GetSpatialFilter( ogrLayer );
   if ( filter )
   {
@@ -2724,7 +2816,7 @@ OGRLayerH QgsOgrUtils::setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTex
   return OGR_DS_ExecuteSQL( ds, sql.constData(), nullptr, nullptr );
 }
 
-void QgsOgrProvider::open()
+void QgsOgrProvider::open( OpenMode mode )
 {
   bool openReadOnly = false;
 
@@ -2749,30 +2841,38 @@ void QgsOgrProvider::open()
   QgsDebugMsg( "mLayerName: " + mLayerName );
   QgsDebugMsg( "mSubsetString: " + mSubsetString );
   CPLSetConfigOption( "OGR_ORGANIZE_POLYGONS", "ONLY_CCW" );  // "SKIP" returns MULTIPOLYGONs for multiringed POLYGONs
+  CPLSetConfigOption( "GPX_ELE_AS_25D", "YES" );  // use GPX elevation as z values
 
   if ( mFilePath.startsWith( "MySQL:" ) && !mLayerName.isEmpty() && !mFilePath.endsWith( ",tables=" + mLayerName ) )
   {
     mFilePath += ",tables=" + mLayerName;
   }
 
+  if ( mode == OpenModeForceReadOnly )
+    openReadOnly = true;
+  else if ( mode == OpenModeSameAsCurrent && !mWriteAccess )
+    openReadOnly = true;
+
   // first try to open in update mode (unless specified otherwise)
   if ( !openReadOnly )
     ogrDataSource = OGROpen( TO8F( mFilePath ), true, &ogrDriver );
 
+  mValid = false;
   if ( ogrDataSource )
   {
     mWriteAccess = true;
+    mWriteAccessPossible = true;
   }
   else
   {
-    QgsDebugMsg( "OGR failed to opened in update mode, trying in read-only mode" );
+    mWriteAccess = false;
+    if ( !openReadOnly )
+    {
+      QgsDebugMsg( "OGR failed to opened in update mode, trying in read-only mode" );
+    }
 
     // try to open read-only
     ogrDataSource = OGROpen( TO8F( mFilePath ), false, &ogrDriver );
-
-    //TODO Need to set a flag or something to indicate that the layer
-    //TODO is in read-only mode, otherwise edit ops will fail
-    //TODO: capabilities() should now reflect this; need to test.
   }
 
   if ( ogrDataSource )
@@ -2801,6 +2901,10 @@ void QgsOgrProvider::open()
       mValid = setSubsetString( mSubsetString );
       if ( mValid )
       {
+        if ( mode == OpenModeInitial )
+        {
+          computeCapabilities();
+        }
         QgsDebugMsg( "Data source is valid" );
       }
       else
@@ -2817,6 +2921,59 @@ void QgsOgrProvider::open()
   {
     QgsMessageLog::logMessage( tr( "Data source is invalid (%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ), tr( "OGR" ) );
   }
+
+  // For shapefiles or MapInfo .tab, so as to allow concurrent opening between
+  // QGIS and MapInfo, we go back to read-only mode for now.
+  // We limit to those drivers as re-opening is relatively cheap (other drivers
+  // like GeoJSON might do full content ingestion for example)
+  if ( mValid && mode == OpenModeInitial && mWriteAccess &&
+       ( ogrDriverName == "ESRI Shapefile" || ogrDriverName == "MapInfo File" ) )
+  {
+    OGR_DS_Destroy( ogrDataSource );
+    ogrLayer = ogrOrigLayer = nullptr;
+    mValid = false;
+
+    ogrDataSource = OGROpen( TO8F( mFilePath ), false, &ogrDriver );
+
+    mWriteAccess = false;
+
+    if ( ogrDataSource )
+    {
+      // We get the layer which was requested by the uri. The layername
+      // has precedence over the layerid if both are given.
+      if ( mLayerName.isNull() )
+      {
+        ogrOrigLayer = OGR_DS_GetLayer( ogrDataSource, mLayerIndex );
+      }
+      else
+      {
+        ogrOrigLayer = OGR_DS_GetLayerByName( ogrDataSource, TO8( mLayerName ) );
+      }
+
+      ogrLayer = ogrOrigLayer;
+    }
+    if ( ogrLayer )
+    {
+      mValid = true;
+      mDynamicWriteAccess = true;
+
+      if ( !mSubsetString.isEmpty() )
+      {
+        int featuresCountedBackup = mFeaturesCounted;
+        mFeaturesCounted = -1;
+        mValid = setSubsetString( mSubsetString, false );
+        mFeaturesCounted = featuresCountedBackup;
+      }
+    }
+  }
+
+  // For debug/testing purposes
+  if ( !mValid )
+    setProperty( "_debug_open_mode", "invalid" );
+  else if ( mWriteAccess )
+    setProperty( "_debug_open_mode", "read-write" );
+  else
+    setProperty( "_debug_open_mode", "read-only" );
 }
 
 void QgsOgrProvider::close()
@@ -2831,10 +2988,81 @@ void QgsOgrProvider::close()
     OGR_DS_Destroy( ogrDataSource );
   }
   ogrDataSource = nullptr;
+  ogrLayer = nullptr;
+  ogrOrigLayer = nullptr;
+  mValid = false;
+  setProperty( "_debug_open_mode", "invalid" );
 
   updateExtents();
+}
+
+void QgsOgrProvider::reloadData()
+{
+  forceReload();
+  close();
+  open( OpenModeSameAsCurrent );
+  if ( !mValid )
+    pushError( tr( "Cannot reopen datasource %1" ).arg( dataSourceUri() ) );
+}
+
+bool QgsOgrProvider::enterUpdateMode()
+{
+  if ( !mWriteAccessPossible )
+  {
+    return false;
+  }
+  if ( mWriteAccess )
+  {
+    ++mUpdateModeStackDepth;
+    return true;
+  }
+  if ( mUpdateModeStackDepth == 0 )
+  {
+    Q_ASSERT( mDynamicWriteAccess );
+    QgsDebugMsg( QString( "Reopening %1 in update mode" ).arg( dataSourceUri() ) );
+    close();
+    open( OpenModeForceUpdate );
+    if ( !ogrDataSource || !mWriteAccess )
+    {
+      QgsMessageLog::logMessage( tr( "Cannot reopen datasource %1 in update mode" ).arg( dataSourceUri() ), tr( "OGR" ) );
+      pushError( tr( "Cannot reopen datasource %1 in update mode" ).arg( dataSourceUri() ) );
+      return false;
+    }
+  }
+  ++mUpdateModeStackDepth;
+  return true;
+}
 
-  QgsOgrConnPool::instance()->unref( mFilePath );
+bool QgsOgrProvider::leaveUpdateMode()
+{
+  if ( !mWriteAccessPossible )
+  {
+    return false;
+  }
+  --mUpdateModeStackDepth;
+  if ( mUpdateModeStackDepth < 0 )
+  {
+    QgsMessageLog::logMessage( tr( "Unbalanced call to leaveUpdateMode() w.r.t. enterUpdateMode()" ), tr( "OGR" ) );
+    mUpdateModeStackDepth = 0;
+    return false;
+  }
+  if ( !mDynamicWriteAccess )
+  {
+    return true;
+  }
+  if ( mUpdateModeStackDepth == 0 )
+  {
+    QgsDebugMsg( QString( "Reopening %1 in read-only mode" ).arg( dataSourceUri() ) );
+    close();
+    open( OpenModeForceReadOnly );
+    if ( !ogrDataSource )
+    {
+      QgsMessageLog::logMessage( tr( "Cannot reopen datasource %1 in read-only mode" ).arg( dataSourceUri() ), tr( "OGR" ) );
+      pushError( tr( "Cannot reopen datasource %1 in read-only mode" ).arg( dataSourceUri() ) );
+      return false;
+    }
+  }
+  return true;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/src/providers/ogr/qgsogrprovider.h b/src/providers/ogr/qgsogrprovider.h
index 101c859..c1cf3aa 100644
--- a/src/providers/ogr/qgsogrprovider.h
+++ b/src/providers/ogr/qgsogrprovider.h
@@ -174,6 +174,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
 
     virtual void setEncoding( const QString& e ) override;
 
+    virtual bool enterUpdateMode() override;
+
+    virtual bool leaveUpdateMode() override;
 
     /** Return vector file filter string
      *
@@ -271,6 +274,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
      */
     void forceReload() override;
 
+    /** Closes and re-open the datasource */
+    void reloadData() override;
+
   protected:
     /** Loads fields from input file to member attributeFields */
     void loadFields();
@@ -287,7 +293,15 @@ class QgsOgrProvider : public QgsVectorDataProvider
     /** Clean shapefile from features which are marked as deleted */
     void repack();
 
-    void open();
+    enum OpenMode
+    {
+      OpenModeInitial,
+      OpenModeSameAsCurrent,
+      OpenModeForceReadOnly,
+      OpenModeForceUpdate,
+    };
+
+    void open( OpenMode mode );
     void close();
 
   private:
@@ -355,7 +369,24 @@ class QgsOgrProvider : public QgsVectorDataProvider
     /** Whether the file is opened in write mode*/
     bool mWriteAccess;
 
+    /** Whether the file can potentially be opened in write mode (but not necessarily currently) */
+    bool mWriteAccessPossible;
+
+    /** Whether the open mode of the datasource changes w.r.t calls to enterUpdateMode() / leaveUpdateMode() */
+    bool mDynamicWriteAccess;
+
     bool mShapefileMayBeCorrupted;
+
+    /** Converts the geometry to the layer type if necessary. Takes ownership of the passed geometry */
+    OGRGeometryH ConvertGeometryIfNecessary( OGRGeometryH );
+
+    int mUpdateModeStackDepth;
+
+    void computeCapabilities();
+
+    int mCapabilities;
+
+    bool doInitialActionsForEdition();
 };
 
 
diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp
index d081bd4..2fa7d40 100644
--- a/src/providers/oracle/qgsoracleconn.cpp
+++ b/src/providers/oracle/qgsoracleconn.cpp
@@ -361,7 +361,7 @@ QString QgsOracleConn::fieldExpression( const QgsField &fld )
 
 void QgsOracleConn::retrieveLayerTypes( QgsOracleLayerProperty &layerProperty, bool useEstimatedMetadata, bool onlyExistingTypes )
 {
-  QgsDebugMsg( "entering: " + layerProperty.toString() );
+  QgsDebugMsgLevel( "entering: " + layerProperty.toString(), 3 );
 
   if ( layerProperty.isView )
   {
@@ -657,6 +657,7 @@ void QgsOracleConn::deleteConnection( QString theConnName )
   settings.remove( key + "/allowGeometrylessTables" );
   settings.remove( key + "/estimatedMetadata" );
   settings.remove( key + "/onlyExistingTypes" );
+  settings.remove( key + "/includeGeoAttributes" );
   settings.remove( key + "/saveUsername" );
   settings.remove( key + "/savePassword" );
   settings.remove( key + "/save" );
@@ -677,7 +678,7 @@ void QgsOracleConn::setSelectedConnection( QString name )
 
 QgsDataSourceURI QgsOracleConn::connUri( QString theConnName )
 {
-  QgsDebugMsg( "theConnName = " + theConnName );
+  QgsDebugMsgLevel( "theConnName = " + theConnName, 3 );
 
   QSettings settings;
 
diff --git a/src/providers/oracle/qgsoracledataitems.cpp b/src/providers/oracle/qgsoracledataitems.cpp
index 8bfe203..bb4c813 100644
--- a/src/providers/oracle/qgsoracledataitems.cpp
+++ b/src/providers/oracle/qgsoracledataitems.cpp
@@ -30,7 +30,7 @@ QGISEXTERN bool deleteLayer( const QString& uri, QString& errCause );
 // ---------------------------------------------------------------------------
 QgsOracleConnectionItem::QgsOracleConnectionItem( QgsDataItem* parent, QString name, QString path )
     : QgsDataCollectionItem( parent, name, path )
-    , mColumnTypeThread( 0 )
+    , mColumnTypeThread( nullptr )
 {
   mIconName = "mIconConnect.png";
 }
@@ -47,14 +47,12 @@ void QgsOracleConnectionItem::stop()
     mColumnTypeThread->stop();
     mColumnTypeThread->wait();
     delete mColumnTypeThread;
-    mColumnTypeThread = 0;
+    mColumnTypeThread = nullptr;
   }
 }
 
 void QgsOracleConnectionItem::refresh()
 {
-  QApplication::setOverrideCursor( Qt::WaitCursor );
-
   stop();
 
   Q_FOREACH ( QgsDataItem *child, mChildren )
@@ -66,18 +64,28 @@ void QgsOracleConnectionItem::refresh()
   {
     addChildItem( item, true );
   }
+}
 
-  QApplication::restoreOverrideCursor();
+void QgsOracleConnectionItem::setAllAsPopulated()
+{
+  Q_FOREACH ( QgsDataItem *child, mChildren )
+  {
+    child->setState( Populated );
+  }
+  setState( Populated );
 }
 
 QVector<QgsDataItem*> QgsOracleConnectionItem::createChildren()
 {
-  QgsDebugMsg( "Entered" );
+  setState( Populating );
 
   mOwnerMap.clear();
 
   stop();
 
+  if ( deferredDelete() )
+    return QVector<QgsDataItem*>();
+
   if ( !mColumnTypeThread )
   {
     mColumnTypeThread = new QgsOracleColumnTypeThread( mName,
@@ -99,26 +107,31 @@ QVector<QgsDataItem*> QgsOracleConnectionItem::createChildren()
   }
 
   if ( mColumnTypeThread )
+  {
     mColumnTypeThread->start();
+  }
+  else
+  {
+    setAllAsPopulated();
+  }
 
   return QVector<QgsDataItem*>();
 }
 
 void QgsOracleConnectionItem::threadStarted()
 {
-  QgsDebugMsg( "Entering." );
-  qApp->setOverrideCursor( Qt::BusyCursor );
+  QgsDebugMsgLevel( "Entering.", 3 );
 }
 
 void QgsOracleConnectionItem::threadFinished()
 {
-  QgsDebugMsg( "Entering." );
-  qApp->restoreOverrideCursor();
+  QgsDebugMsgLevel( "Entering.", 3 );
+  setAllAsPopulated();
 }
 
 void QgsOracleConnectionItem::setLayerType( QgsOracleLayerProperty layerProperty )
 {
-  QgsDebugMsg( layerProperty.toString() );
+  QgsDebugMsgLevel( layerProperty.toString(), 3 );
   QgsOracleOwnerItem *ownerItem = mOwnerMap.value( layerProperty.ownerName, 0 );
 
   for ( int i = 0 ; i < layerProperty.size(); i++ )
@@ -126,19 +139,20 @@ void QgsOracleConnectionItem::setLayerType( QgsOracleLayerProperty layerProperty
     QGis::WkbType wkbType = layerProperty.types.at( i );
     if ( wkbType == QGis::WKBUnknown )
     {
-      QgsDebugMsg( "skip unknown geometry type" );
+      QgsDebugMsgLevel( "skip unknown geometry type", 3 );
       continue;
     }
 
     if ( !ownerItem )
     {
       ownerItem = new QgsOracleOwnerItem( this, layerProperty.ownerName, mPath + "/" + layerProperty.ownerName );
-      QgsDebugMsg( "add owner item: " + layerProperty.ownerName );
+      ownerItem->setState( Populating );
+      QgsDebugMsgLevel( "add owner item: " + layerProperty.ownerName, 3 );
       addChildItem( ownerItem, true );
       mOwnerMap[ layerProperty.ownerName ] = ownerItem;
     }
 
-    QgsDebugMsg( "ADD LAYER" );
+    QgsDebugMsgLevel( "ADD LAYER", 3 );
     ownerItem->addLayer( layerProperty.at( i ) );
   }
 }
@@ -158,18 +172,22 @@ QList<QAction*> QgsOracleConnectionItem::actions()
 {
   QList<QAction*> lst;
 
-  QAction* actionEdit = new QAction( tr( "Edit..." ), this );
+  QAction* actionRefresh = new QAction( tr( "Refresh" ), this );
+  connect( actionRefresh, SIGNAL( triggered() ), this, SLOT( refreshConnection() ) );
+  lst.append( actionRefresh );
+
+  QAction* separator = new QAction( this );
+  separator->setSeparator( true );
+  lst.append( separator );
+
+  QAction* actionEdit = new QAction( tr( "Edit Connection..." ), this );
   connect( actionEdit, SIGNAL( triggered() ), this, SLOT( editConnection() ) );
   lst.append( actionEdit );
 
-  QAction* actionDelete = new QAction( tr( "Delete" ), this );
+  QAction* actionDelete = new QAction( tr( "Delete Connection" ), this );
   connect( actionDelete, SIGNAL( triggered() ), this, SLOT( deleteConnection() ) );
   lst.append( actionDelete );
 
-  QAction* actionRefresh = new QAction( tr( "Refresh" ), this );
-  connect( actionRefresh, SIGNAL( triggered() ), this, SLOT( refreshConnection() ) );
-  lst.append( actionRefresh );
-
   return lst;
 }
 
@@ -185,10 +203,16 @@ void QgsOracleConnectionItem::editConnection()
 
 void QgsOracleConnectionItem::deleteConnection()
 {
+  if ( QMessageBox::question( nullptr, QObject::tr( "Delete Connection" ),
+                              QObject::tr( "Are you sure you want to delete the connection to %1?" ).arg( mName ),
+                              QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
+    return;
+
   QgsOracleConn::deleteConnection( mName );
 
   // the parent should be updated
-  mParent->refresh();
+  if ( mParent )
+    mParent->refresh();
 }
 
 void QgsOracleConnectionItem::refreshConnection()
@@ -207,13 +231,15 @@ bool QgsOracleConnectionItem::handleDrop( const QMimeData * data, Qt::DropAction
 
   qApp->setOverrideCursor( Qt::WaitCursor );
 
-  QProgressDialog *progress = new QProgressDialog( tr( "Copying features..." ), tr( "Abort" ), 0, 0, 0 );
+  QProgressDialog *progress = new QProgressDialog( tr( "Copying features..." ), tr( "Abort" ), 0, 0, nullptr );
   progress->setWindowTitle( tr( "Import layer" ) );
   progress->setWindowModality( Qt::WindowModal );
   progress->show();
 
   QStringList importResults;
   bool hasError = false;
+  bool cancelled = false;
+
   QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
   Q_FOREACH ( const QgsMimeDataUtils::Uri& u, lst )
   {
@@ -230,18 +256,20 @@ bool QgsOracleConnectionItem::handleDrop( const QMimeData * data, Qt::DropAction
     if ( srcLayer->isValid() )
     {
       uri.setDataSource( QString(), u.name.left( 30 ).toUpper(), "GEOM" );
-      uri.setWkbType( srcLayer->wkbType() );
+      uri.setWkbType( QGis::fromOldWkbType( srcLayer->wkbType() ) );
       QString authid = srcLayer->crs().authid();
       if ( authid.startsWith( "EPSG:", Qt::CaseInsensitive ) )
       {
         uri.setSrid( authid.mid( 5 ) );
       }
-      QgsDebugMsg( "URI " + uri.uri() );
+      QgsDebugMsgLevel( "URI " + uri.uri(), 3 );
       QgsVectorLayerImport::ImportError err;
       QString importError;
-      err = QgsVectorLayerImport::importLayer( srcLayer, uri.uri(), "oracle", &srcLayer->crs(), false, &importError, false, 0, progress );
+      err = QgsVectorLayerImport::importLayer( srcLayer, uri.uri(), "oracle", &srcLayer->crs(), false, &importError, false, nullptr, progress );
       if ( err == QgsVectorLayerImport::NoError )
         importResults.append( tr( "%1: OK!" ).arg( u.name ) );
+      else if ( err == QgsVectorLayerImport::ErrUserCancelled )
+        cancelled = true;
       else
       {
         importResults.append( QString( "%1: %2" ).arg( u.name ).arg( importError ) );
@@ -261,7 +289,12 @@ bool QgsOracleConnectionItem::handleDrop( const QMimeData * data, Qt::DropAction
 
   qApp->restoreOverrideCursor();
 
-  if ( hasError )
+  if ( cancelled )
+  {
+    QMessageBox::information( nullptr, tr( "Import to Oracle database" ), tr( "Import cancelled." ) );
+    refresh();
+  }
+  else if ( hasError )
   {
     QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
     output->setTitle( tr( "Import to Oracle database" ) );
@@ -274,6 +307,11 @@ bool QgsOracleConnectionItem::handleDrop( const QMimeData * data, Qt::DropAction
     refresh();
   }
 
+  if ( state() == Populated )
+    refresh();
+  else
+    populate();
+
   return true;
 }
 
@@ -294,7 +332,7 @@ QList<QAction*> QgsOracleLayerItem::actions()
 {
   QList<QAction*> lst;
 
-  QAction* actionDeleteLayer = new QAction( tr( "Delete layer" ), this );
+  QAction* actionDeleteLayer = new QAction( tr( "Delete Table" ), this );
   connect( actionDeleteLayer, SIGNAL( triggered() ), this, SLOT( deleteLayer() ) );
   lst.append( actionDeleteLayer );
 
@@ -303,15 +341,20 @@ QList<QAction*> QgsOracleLayerItem::actions()
 
 void QgsOracleLayerItem::deleteLayer()
 {
+  if ( QMessageBox::question( nullptr, QObject::tr( "Delete Table" ),
+                              QObject::tr( "Are you sure you want to delete %1.%2?" ).arg( mLayerProperty.ownerName, mLayerProperty.tableName ),
+                              QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
+    return;
+
   QString errCause;
   bool res = ::deleteLayer( mUri, errCause );
   if ( !res )
   {
-    QMessageBox::warning( 0, tr( "Delete layer" ), errCause );
+    QMessageBox::warning( 0, tr( "Delete Table" ), errCause );
   }
   else
   {
-    QMessageBox::information( 0, tr( "Delete layer" ), tr( "Layer deleted successfully." ) );
+    QMessageBox::information( 0, tr( "Delete Table" ), tr( "Table deleted successfully." ) );
     deleteLater();
   }
 }
@@ -330,10 +373,10 @@ QString QgsOracleLayerItem::createUri()
   QgsDataSourceURI uri = QgsOracleConn::connUri( connItem->name() );
   uri.setDataSource( mLayerProperty.ownerName, mLayerProperty.tableName, mLayerProperty.geometryColName, mLayerProperty.sql, QString::null );
   uri.setSrid( QString::number( mLayerProperty.srids.at( 0 ) ) );
-  uri.setWkbType( mLayerProperty.types.at( 0 ) );
+  uri.setWkbType( QGis::fromOldWkbType( mLayerProperty.types.at( 0 ) ) );
   if ( mLayerProperty.isView && mLayerProperty.pkCols.size() > 0 )
     uri.setKeyColumn( mLayerProperty.pkCols[0] );
-  QgsDebugMsg( QString( "layer uri: %1" ).arg( uri.uri() ) );
+  QgsDebugMsgLevel( QString( "layer uri: %1" ).arg( uri.uri() ), 3 );
   return uri.uri();
 }
 
@@ -342,11 +385,13 @@ QgsOracleOwnerItem::QgsOracleOwnerItem( QgsDataItem* parent, QString name, QStri
     : QgsDataCollectionItem( parent, name, path )
 {
   mIconName = "mIconDbOwner.png";
+  //not fertile, since children are created by QgsOracleConnectionItem
+  mCapabilities &= ~( Fertile );
 }
 
 QVector<QgsDataItem*> QgsOracleOwnerItem::createChildren()
 {
-  QgsDebugMsg( "Entering." );
+  QgsDebugMsgLevel( "Entering.", 3 );
   return QVector<QgsDataItem*>();
 }
 
@@ -356,7 +401,7 @@ QgsOracleOwnerItem::~QgsOracleOwnerItem()
 
 void QgsOracleOwnerItem::addLayer( QgsOracleLayerProperty layerProperty )
 {
-  QgsDebugMsg( layerProperty.toString() );
+  QgsDebugMsgLevel( layerProperty.toString(), 3 );
 
   Q_ASSERT( layerProperty.size() == 1 );
   QGis::WkbType wkbType = layerProperty.types.at( 0 );
diff --git a/src/providers/oracle/qgsoracledataitems.h b/src/providers/oracle/qgsoracledataitems.h
index 00e5d7f..999039d 100644
--- a/src/providers/oracle/qgsoracledataitems.h
+++ b/src/providers/oracle/qgsoracledataitems.h
@@ -85,6 +85,7 @@ class QgsOracleConnectionItem : public QgsDataCollectionItem
     void stop();
     QMap<QString, QgsOracleOwnerItem * > mOwnerMap;
     QgsOracleColumnTypeThread *mColumnTypeThread;
+    void setAllAsPopulated();
 };
 
 class QgsOracleOwnerItem : public QgsDataCollectionItem
diff --git a/src/providers/oracle/qgsoracleexpressioncompiler.cpp b/src/providers/oracle/qgsoracleexpressioncompiler.cpp
index 88453bf..3b7ffa9 100644
--- a/src/providers/oracle/qgsoracleexpressioncompiler.cpp
+++ b/src/providers/oracle/qgsoracleexpressioncompiler.cpp
@@ -29,8 +29,15 @@ QgsSqlExpressionCompiler::Result QgsOracleExpressionCompiler::compileNode( const
 
     switch ( bin->op() )
     {
+      case QgsExpression::boConcat:
+        // oracle's handling of || WRT null is not standards compliant
+        return Fail;
+
       case QgsExpression::boPow:
       case QgsExpression::boRegexp:
+      case QgsExpression::boILike:
+      case QgsExpression::boNotILike:
+      case QgsExpression::boMod:
       {
         QString op1, op2;
 
@@ -48,6 +55,18 @@ QgsSqlExpressionCompiler::Result QgsOracleExpressionCompiler::compileNode( const
             result = QString( "regexp_like(%1,%2)" ).arg( op1, op2 );
             return Complete;
 
+          case QgsExpression::boILike:
+            result = QString( "lower(%1) LIKE lower(%2)" ).arg( op1, op2 );
+            return Complete;
+
+          case QgsExpression::boNotILike:
+            result = QString( "NOT lower(%1) LIKE lower(%2)" ).arg( op1, op2 );
+            return Complete;
+
+          case QgsExpression::boMod  :
+            result = QString( "MOD(%1,%2)" ).arg( op1, op2 );
+            return Complete;
+
           default:
             break;
         }
@@ -70,5 +89,14 @@ QString QgsOracleExpressionCompiler::quotedIdentifier( const QString& identifier
 QString QgsOracleExpressionCompiler::quotedValue( const QVariant& value, bool& ok )
 {
   ok = true;
-  return QgsOracleConn::quotedValue( value );
+
+  switch ( value.type() )
+  {
+    case QVariant::Bool:
+      //no boolean literal support in Oracle, so fake it
+      return value.toBool() ? "(1=1)" : "(1=0)";
+
+    default:
+      return QgsOracleConn::quotedValue( value );
+  }
 }
diff --git a/src/providers/oracle/qgsoraclefeatureiterator.cpp b/src/providers/oracle/qgsoraclefeatureiterator.cpp
index f3ae92e..e2c79df 100644
--- a/src/providers/oracle/qgsoraclefeatureiterator.cpp
+++ b/src/providers/oracle/qgsoraclefeatureiterator.cpp
@@ -61,7 +61,7 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* sour
   else
     mAttributeList = mSource->mFields.allAttributesList();
 
-
+  bool limitAtProvider = ( mRequest.limit() >= 0 );
   QString whereClause;
 
   if ( !mSource->mGeometryColumn.isNull() )
@@ -139,15 +139,7 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* sour
       break;
 
     case QgsFeatureRequest::FilterExpression:
-      if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
-      {
-        QgsOracleExpressionCompiler compiler( mSource );
-        if ( compiler.compile( mRequest.filterExpression() ) == QgsSqlExpressionCompiler::Complete )
-        {
-          whereClause = QgsOracleUtils::andWhereClauses( whereClause, compiler.result() );
-          mExpressionCompiled = true;
-        }
-      }
+      //handled below
       break;
 
     case QgsFeatureRequest::FilterRect:
@@ -163,22 +155,67 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* sour
     whereClause += QgsOracleConn::databaseTypeFilter( "FEATUREREQUEST", mSource->mGeometryColumn, mSource->mRequestedGeomType );
   }
 
-  if ( mRequest.limit() >= 0 )
+  if ( !mSource->mSqlWhereClause.isEmpty() )
   {
     if ( !whereClause.isEmpty() )
       whereClause += " AND ";
+    whereClause += "(" + mSource->mSqlWhereClause + ")";
+  }
 
-    whereClause += QString( "rownum<=%1" ).arg( mRequest.limit() );
+  //NOTE - must be last added!
+  mExpressionCompiled = false;
+  QString fallbackStatement;
+  bool useFallback = false;
+  if ( request.filterType() == QgsFeatureRequest::FilterExpression )
+  {
+    if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
+    {
+      QgsOracleExpressionCompiler compiler( mSource );
+      QgsSqlExpressionCompiler::Result result = compiler.compile( mRequest.filterExpression() );
+      if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
+      {
+        fallbackStatement = whereClause;
+        useFallback = true;
+        whereClause = QgsOracleUtils::andWhereClauses( whereClause, compiler.result() );
+
+        //if only partial success when compiling expression, we need to double-check results using QGIS' expressions
+        mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete );
+        limitAtProvider = mExpressionCompiled;
+      }
+      else
+      {
+        limitAtProvider = false;
+      }
+    }
+    else
+    {
+      limitAtProvider = false;
+    }
   }
 
-  if ( !mSource->mSqlWhereClause.isEmpty() )
+  if ( !mRequest.orderBy().isEmpty() )
+  {
+    limitAtProvider = false;
+  }
+
+  if ( mRequest.limit() >= 0 && limitAtProvider )
   {
     if ( !whereClause.isEmpty() )
       whereClause += " AND ";
-    whereClause += "(" + mSource->mSqlWhereClause + ")";
+
+    whereClause += QString( "rownum<=%1" ).arg( mRequest.limit() );
+    fallbackStatement += QString( "rownum<=%1" ).arg( mRequest.limit() );
   }
 
-  openQuery( whereClause );
+  bool result = openQuery( whereClause, !useFallback );
+  if ( !result && useFallback )
+  {
+    result = openQuery( fallbackStatement );
+    if ( result )
+    {
+      mExpressionCompiled = false;
+    }
+  }
 }
 
 QgsOracleFeatureIterator::~QgsOracleFeatureIterator()
@@ -209,10 +246,16 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature& feature )
     if ( mRewind )
     {
       mRewind = false;
-      if ( !mQry.first() )
-        return true;
+      if ( !QgsOracleProvider::exec( mQry, mSql ) )
+      {
+        QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL:%1\nError: %2" )
+                                   .arg( mQry.lastQuery() )
+                                   .arg( mQry.lastError().text() ),
+                                   QObject::tr( "Oracle" ) );
+        return false;
+      }
     }
-    else if ( !mQry.next() )
+    if ( !mQry.next() )
     {
       return false;
     }
@@ -269,12 +312,6 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature& feature )
           }
         }
       }
-
-      if (( mRequest.flags() & QgsFeatureRequest::NoGeometry ) != 0 )
-      {
-        // clear not requested geometry
-        feature.setGeometry( 0 );
-      }
     }
 
     QgsFeatureId fid = 0;
@@ -392,7 +429,7 @@ bool QgsOracleFeatureIterator::close()
   return true;
 }
 
-bool QgsOracleFeatureIterator::openQuery( QString whereClause )
+bool QgsOracleFeatureIterator::openQuery( QString whereClause, bool showLog )
 {
   try
   {
@@ -444,12 +481,16 @@ bool QgsOracleFeatureIterator::openQuery( QString whereClause )
       query += QString( " WHERE %1" ).arg( whereClause );
 
     QgsDebugMsg( QString( "Fetch features: %1" ).arg( query ) );
+    mSql = query;
     if ( !QgsOracleProvider::exec( mQry, query ) )
     {
-      QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL:%1\nError: %2" )
-                                 .arg( mQry.lastQuery() )
-                                 .arg( mQry.lastError().text() ),
-                                 QObject::tr( "Oracle" ) );
+      if ( showLog )
+      {
+        QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL:%1\nError: %2" )
+                                   .arg( mQry.lastQuery() )
+                                   .arg( mQry.lastError().text() ),
+                                   QObject::tr( "Oracle" ) );
+      }
       return false;
     }
   }
diff --git a/src/providers/oracle/qgsoraclefeatureiterator.h b/src/providers/oracle/qgsoraclefeatureiterator.h
index 8c3430a..ce8901f 100644
--- a/src/providers/oracle/qgsoraclefeatureiterator.h
+++ b/src/providers/oracle/qgsoraclefeatureiterator.h
@@ -76,7 +76,7 @@ class QgsOracleFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Qgs
     //! fetch next feature filter expression
     bool nextFeatureFilterExpression( QgsFeature& f ) override;
 
-    bool openQuery( QString whereClause );
+    bool openQuery( QString whereClause, bool showLog = true );
 
     QgsOracleConn *mConnection;
     QSqlQuery mQry;
@@ -84,6 +84,7 @@ class QgsOracleFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Qgs
     bool mExpressionCompiled;
     bool mFetchGeometry;
     QgsAttributeList mAttributeList;
+    QString mSql;
 };
 
 #endif // QGSORACLEFEATUREITERATOR_H
diff --git a/src/providers/oracle/qgsoraclenewconnection.cpp b/src/providers/oracle/qgsoraclenewconnection.cpp
index 0443cf5..7d53331 100644
--- a/src/providers/oracle/qgsoraclenewconnection.cpp
+++ b/src/providers/oracle/qgsoraclenewconnection.cpp
@@ -51,6 +51,7 @@ QgsOracleNewConnection::QgsOracleNewConnection( QWidget *parent, const QString&
     cb_allowGeometrylessTables->setChecked( settings.value( key + "/allowGeometrylessTables", false ).toBool() );
     cb_useEstimatedMetadata->setChecked( settings.value( key + "/estimatedMetadata", false ).toBool() );
     cb_onlyExistingTypes->setChecked( settings.value( key + "/onlyExistingTypes", true ).toBool() );
+    cb_includeGeoAttributes->setChecked( settings.value( key + "/includeGeoAttributes", false ).toBool() );
 
     if ( settings.value( key + "/saveUsername" ).toString() == "true" )
     {
@@ -125,6 +126,7 @@ void QgsOracleNewConnection::accept()
   settings.setValue( baseKey + "/allowGeometrylessTables", cb_allowGeometrylessTables->isChecked() );
   settings.setValue( baseKey + "/estimatedMetadata", cb_useEstimatedMetadata->isChecked() ? "true" : "false" );
   settings.setValue( baseKey + "/onlyExistingTypes", cb_onlyExistingTypes->isChecked() ? "true" : "false" );
+  settings.setValue( baseKey + "/includeGeoAttributes", cb_includeGeoAttributes->isChecked() ? "true" : "false" );
   settings.setValue( baseKey + "/saveUsername", chkStoreUsername->isChecked() ? "true" : "false" );
   settings.setValue( baseKey + "/savePassword", chkStorePassword->isChecked() ? "true" : "false" );
   settings.setValue( baseKey + "/dboptions", txtOptions->text() );
diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp
index fbdb0eb..61e8b5e 100644
--- a/src/providers/oracle/qgsoracleprovider.cpp
+++ b/src/providers/oracle/qgsoracleprovider.cpp
@@ -69,6 +69,7 @@ QgsOracleProvider::QgsOracleProvider( QString const & uri )
   mSrid = mUri.srid().toInt();
   mRequestedGeomType = mUri.wkbType();
   mUseEstimatedMetadata = mUri.useEstimatedMetadata();
+  mIncludeGeoAttributes = mUri.hasParam( "includegeoattributes" ) ? mUri.param( "includegeoattributes" ) == "true" : false;
 
   mConnection = QgsOracleConn::connectDb( mUri.connectionInfo() );
   if ( !mConnection )
@@ -157,6 +158,10 @@ QgsOracleProvider::QgsOracleProvider( QString const & uri )
   << QgsVectorDataProvider::NativeType( tr( "Text, fixed length (char)" ), "CHAR", QVariant::String, 1, 255 )
   << QgsVectorDataProvider::NativeType( tr( "Text, limited variable length (varchar2)" ), "VARCHAR2", QVariant::String, 1, 255 )
   << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (long)" ), "LONG", QVariant::String )
+
+  // date type
+  << QgsVectorDataProvider::NativeType( tr( "Date" ), "DATE", QVariant::Date, 38, 38, 0, 0 )
+  << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), "TIMESTAMP(6)", QVariant::DateTime, 38, 38, 6, 6 )
   ;
 
   QString key;
@@ -602,11 +607,12 @@ bool QgsOracleProvider::loadFields()
                              ",t.char_used"
                              ",t.data_default"
                              " FROM all_tab_columns t"
-                             " WHERE t.owner=%1 AND t.table_name=%2%3"
+                             " WHERE t.owner=%1 AND t.table_name=%2%3%4"
                              " ORDER BY t.column_id" )
                .arg( quotedValue( mOwnerName ) )
                .arg( quotedValue( mTableName ) )
                .arg( mGeometryColumn.isEmpty() ? "" : QString( " AND t.column_name<>%1 " ).arg( quotedValue( mGeometryColumn ) ) )
+               .arg( mIncludeGeoAttributes ? "" : " AND (t.data_type_owner<>'MDSYS' OR t.data_type<>'SDO_GEOMETRY')" )
              ) )
     {
       while ( qry.next() )
@@ -693,29 +699,31 @@ bool QgsOracleProvider::loadFields()
                                    .arg( qry.lastError().text() ),
                                    tr( "Oracle" ) );
       }
-
-      if ( !mHasSpatialIndex )
-      {
-        mHasSpatialIndex = qry.exec( QString( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" )
-                                     .arg( mQuery )
-                                     .arg( quotedIdentifier( mGeometryColumn ) )
-                                     .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) );
-        if ( !mHasSpatialIndex )
-        {
-          QgsMessageLog::logMessage( tr( "No spatial index on column %1.%2.%3 found - expect poor performance." )
-                                     .arg( mOwnerName )
-                                     .arg( mTableName )
-                                     .arg( mGeometryColumn ),
-                                     tr( "Oracle" ) );
-        }
-      }
     }
 
-    qry.finish();
-
     mEnabledCapabilities |= QgsVectorDataProvider::CreateSpatialIndex;
   }
 
+  if ( !mGeometryColumn.isEmpty() )
+  {
+    if ( !mHasSpatialIndex )
+    {
+      mHasSpatialIndex = qry.exec( QString( "SELECT %2 FROM %1 WHERE sdo_filter(%2,mdsys.sdo_geometry(2003,%3,NULL,mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(-1,-1,1,1)))='TRUE'" )
+                                   .arg( mQuery )
+                                   .arg( quotedIdentifier( mGeometryColumn ) )
+                                   .arg( mSrid < 1 ? "NULL" : QString::number( mSrid ) ) );
+    }
+
+    if ( !mHasSpatialIndex )
+    {
+      QgsMessageLog::logMessage( tr( "No spatial index on column %1 found - expect poor performance." )
+                                 .arg( mGeometryColumn ),
+                                 tr( "Oracle" ) );
+    }
+  }
+
+  qry.finish();
+
   if ( !exec( qry, QString( "SELECT * FROM %1 WHERE 1=0" ).arg( mQuery ) ) )
   {
     QgsMessageLog::logMessage( tr( "Retrieving fields from '%1' failed [%2]" ).arg( mQuery ).arg( qry.lastError().text() ), tr( "Oracle" ) );
@@ -734,7 +742,15 @@ bool QgsOracleProvider::loadFields()
     if ( !mIsQuery && !types.contains( field.name() ) )
       continue;
 
-    mAttributeFields.append( QgsField( field.name(), field.type(), types.value( field.name() ), field.length(), field.precision(), comments.value( field.name() ) ) );
+    QVariant::Type type = field.type();
+
+    if ( types.value( field.name() ) == "DATE" )
+    {
+      // date types are incorrectly detected as datetime
+      type = QVariant::Date;
+    }
+
+    mAttributeFields.append( QgsField( field.name(), type, types.value( field.name() ), field.length(), field.precision(), comments.value( field.name() ) ) );
     mDefaultValues.append( defvalues.value( field.name(), QVariant() ) );
   }
 
@@ -816,7 +832,6 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
                                  .arg( qry.lastQuery() ),
                                  tr( "Oracle" ) );
     }
-
   }
   else
   {
@@ -2056,38 +2071,40 @@ QgsRectangle QgsOracleProvider::extent()
   {
     QString sql;
     QSqlQuery qry( *mConnection );
+    bool ok = false;
 
-    if ( mUseEstimatedMetadata )
+    if ( !mIsQuery )
     {
-      if ( exec( qry, QString( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=%1 AND table_name=%2 AND column_name=%3 AND sdo_dimname='X'" )
-                 .arg( quotedValue( mOwnerName ) )
-                 .arg( quotedValue( mTableName ) )
-                 .arg( quotedValue( mGeometryColumn ) ) ) && qry.next() )
+      if ( mUseEstimatedMetadata )
       {
-        mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() );
-        mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() );
-
-        if ( exec( qry, QString( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=%1 AND table_name=%2 AND column_name=%3 AND sdo_dimname='Y'" )
+        if ( exec( qry, QString( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=%1 AND table_name=%2 AND column_name=%3 AND sdo_dimname='X'" )
                    .arg( quotedValue( mOwnerName ) )
                    .arg( quotedValue( mTableName ) )
-                   .arg( quotedValue( mGeometryColumn ) ) )  && qry.next() )
+                   .arg( quotedValue( mGeometryColumn ) ) ) && qry.next() )
         {
-          mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() );
-          mLayerExtent.setYMaximum( qry.value( 1 ).toDouble() );
-          return mLayerExtent;
+          mLayerExtent.setXMinimum( qry.value( 0 ).toDouble() );
+          mLayerExtent.setXMaximum( qry.value( 1 ).toDouble() );
+
+          if ( exec( qry, QString( "SELECT sdo_lb,sdo_ub FROM mdsys.all_sdo_geom_metadata m, table(m.diminfo) WHERE owner=%1 AND table_name=%2 AND column_name=%3 AND sdo_dimname='Y'" )
+                     .arg( quotedValue( mOwnerName ) )
+                     .arg( quotedValue( mTableName ) )
+                     .arg( quotedValue( mGeometryColumn ) ) )  && qry.next() )
+          {
+            mLayerExtent.setYMinimum( qry.value( 0 ).toDouble() );
+            mLayerExtent.setYMaximum( qry.value( 1 ).toDouble() );
+            return mLayerExtent;
+          }
         }
       }
-    }
-
-    bool ok = false;
 
-    if ( mHasSpatialIndex && ( mUseEstimatedMetadata || mSqlWhereClause.isEmpty() ) )
-    {
-      sql = QString( "SELECT SDO_TUNE.EXTENT_OF(%1,%2) FROM dual" )
-            .arg( quotedValue( QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) ) )
-            .arg( quotedValue( mGeometryColumn ) );
+      if ( mHasSpatialIndex && ( mUseEstimatedMetadata || mSqlWhereClause.isEmpty() ) )
+      {
+        sql = QString( "SELECT SDO_TUNE.EXTENT_OF(%1,%2) FROM dual" )
+              .arg( quotedValue( QString( "%1.%2" ).arg( mOwnerName ).arg( mTableName ) ) )
+              .arg( quotedValue( mGeometryColumn ) );
 
-      ok = exec( qry, sql );
+        ok = exec( qry, sql );
+      }
     }
 
     if ( !ok )
@@ -2191,8 +2208,8 @@ bool QgsOracleProvider::getGeometryDetails()
     }
 
     if ( exec( qry, QString( mUseEstimatedMetadata
-                             ?  "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE rownum<1000) WHERE rownum<=2"
-                             :  "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE rownum<=2" ).arg( quotedIdentifier( geomCol ) ).arg( mQuery ) ) )
+                             ?  "SELECT DISTINCT gtype FROM (SELECT t.%1.sdo_gtype AS gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<1000) WHERE rownum<=2"
+                             :  "SELECT DISTINCT t.%1.sdo_gtype FROM %2 t WHERE t.%1 IS NOT NULL AND rownum<=2" ).arg( quotedIdentifier( geomCol ) ).arg( mQuery ) ) )
     {
       if ( qry.next() )
       {
@@ -2421,6 +2438,11 @@ bool QgsOracleProvider::convertField( QgsField &field )
       break;
 
     case QVariant::DateTime:
+      fieldType = "TIMESTAMP";
+      fieldPrec = -1;
+      break;
+
+
     case QVariant::Time:
     case QVariant::String:
       fieldType = "VARCHAR2(2047)";
diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h
index cfc6fd7..89015c0 100644
--- a/src/providers/oracle/qgsoracleprovider.h
+++ b/src/providers/oracle/qgsoracleprovider.h
@@ -348,6 +348,9 @@ class QgsOracleProvider : public QgsVectorDataProvider
     /* Use estimated metadata. Uses fast table counts, geometry type and extent determination */
     bool mUseEstimatedMetadata;
 
+    /* Include additional geo attributes */
+    bool mIncludeGeoAttributes;
+
     struct OracleFieldNotFound {}; //! Exception to throw
 
     struct OracleException
diff --git a/src/providers/postgres/qgspostgresconn.cpp b/src/providers/postgres/qgspostgresconn.cpp
index 6e5ff4e..65bc445 100644
--- a/src/providers/postgres/qgspostgresconn.cpp
+++ b/src/providers/postgres/qgspostgresconn.cpp
@@ -290,12 +290,21 @@ QgsPostgresConn::QgsPostgresConn( const QString& conninfo, bool readOnly, bool s
   deduceEndian();
 
   /* Check to see if we have working PostGIS support */
-  if ( postgisVersion().isNull() )
+  if ( !postgisVersion().isNull() )
   {
-    QgsMessageLog::logMessage( tr( "Your database has no working PostGIS support." ), tr( "PostGIS" ) );
-    PQfinish();
-    mRef = 0;
-    return;
+    /* Check to see if we have GEOS support and if not, warn the user about
+       the problems they will see :) */
+    QgsDebugMsg( "Checking for GEOS support" );
+
+    if ( !hasGEOS() )
+    {
+      QgsMessageLog::logMessage( tr( "Your PostGIS installation has no GEOS support. Feature selection and identification will not work properly. Please install PostGIS with GEOS support (http://geos.refractions.net)" ), tr( "PostGIS" ) );
+    }
+
+    if ( hasTopology() )
+    {
+      QgsDebugMsg( "Topology support available!" );
+    }
   }
 
   if ( mPostgresqlVersion >= 90000 )
@@ -303,19 +312,6 @@ QgsPostgresConn::QgsPostgresConn( const QString& conninfo, bool readOnly, bool s
     PQexecNR( "SET application_name='QGIS'" );
   }
 
-  /* Check to see if we have GEOS support and if not, warn the user about
-     the problems they will see :) */
-  QgsDebugMsg( "Checking for GEOS support" );
-
-  if ( !hasGEOS() )
-  {
-    QgsMessageLog::logMessage( tr( "Your PostGIS installation has no GEOS support. Feature selection and identification will not work properly. Please install PostGIS with GEOS support (http://geos.refractions.net)" ), tr( "PostGIS" ) );
-  }
-
-  if ( hasTopology() )
-  {
-    QgsDebugMsg( "Topology support available!" );
-  }
 
   PQsetNoticeProcessor( mConn, noticeProcessor, nullptr );
 }
@@ -836,10 +832,11 @@ QString QgsPostgresConn::postgisVersion()
 
   mPostgresqlVersion = PQserverVersion( mConn );
 
-  QgsPostgresResult result( PQexec( "SELECT postgis_version()" ) );
+  QgsPostgresResult result( PQexec( "SELECT postgis_version()", false ) );
   if ( result.PQntuples() != 1 )
   {
-    QgsMessageLog::logMessage( tr( "Retrieval of postgis version failed" ), tr( "PostGIS" ) );
+    QgsMessageLog::logMessage( tr( "No PostGIS support in the database." ), tr( "PostGIS" ) );
+    mGotPostgisVersion = true;
     return QString::null;
   }
 
diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp
index c66eaf3..ddf2060 100644
--- a/src/providers/postgres/qgspostgresfeatureiterator.cpp
+++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp
@@ -685,7 +685,7 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
   QgsFeatureId fid = 0;
 
   bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
-  const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();
+  QgsAttributeList fetchAttributes = mRequest.subsetOfAttributes();
 
   switch ( mSource->mPrimaryKeyType )
   {
diff --git a/src/providers/spatialite/qgsspatialiteconnection.h b/src/providers/spatialite/qgsspatialiteconnection.h
index e951ef6..d6023a8 100644
--- a/src/providers/spatialite/qgsspatialiteconnection.h
+++ b/src/providers/spatialite/qgsspatialiteconnection.h
@@ -131,7 +131,10 @@ class QgsSqliteHandle
     //
   public:
     QgsSqliteHandle( sqlite3 * handle, const QString& dbPath, bool shared )
-        : ref( shared ? 1 : -1 ), sqlite_handle( handle ), mDbPath( dbPath )
+        : ref( shared ? 1 : -1 )
+        , sqlite_handle( handle )
+        , mDbPath( dbPath )
+        , mIsValid( true )
     {
     }
 
@@ -145,6 +148,16 @@ class QgsSqliteHandle
       return mDbPath;
     }
 
+    bool isValid() const
+    {
+      return mIsValid;
+    }
+
+    void invalidate()
+    {
+      mIsValid = false;
+    }
+
     //
     // libsqlite3 wrapper
     //
@@ -165,6 +178,7 @@ class QgsSqliteHandle
     int ref;
     sqlite3 *sqlite_handle;
     QString mDbPath;
+    bool mIsValid;
 
     static QMap < QString, QgsSqliteHandle * > handles;
 };
diff --git a/src/providers/spatialite/qgsspatialiteconnpool.h b/src/providers/spatialite/qgsspatialiteconnpool.h
index d66eea1..6f37fec 100644
--- a/src/providers/spatialite/qgsspatialiteconnpool.h
+++ b/src/providers/spatialite/qgsspatialiteconnpool.h
@@ -36,13 +36,15 @@ inline void qgsConnectionPool_ConnectionDestroy( QgsSqliteHandle* c )
 
 inline void qgsConnectionPool_InvalidateConnection( QgsSqliteHandle* c )
 {
-  Q_UNUSED( c );
+  /* Invalidation is used in particular by the WFS provider that uses a */
+  /* temporary spatialite DB and want to delete it at some point. For that */
+  /* it must invalidate all handles pointing to it */
+  c->invalidate();
 }
 
 inline bool qgsConnectionPool_ConnectionIsValid( QgsSqliteHandle* c )
 {
-  Q_UNUSED( c );
-  return true;
+  return c->isValid();
 }
 
 
diff --git a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp
index d2b5b79..bfa28f1 100644
--- a/src/providers/spatialite/qgsspatialitefeatureiterator.cpp
+++ b/src/providers/spatialite/qgsspatialitefeatureiterator.cpp
@@ -291,7 +291,7 @@ bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString& whereClause,
 
     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
     {
-      const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();
+      QgsAttributeList fetchAttributes = mRequest.subsetOfAttributes();
       for ( QgsAttributeList::const_iterator it = fetchAttributes.constBegin(); it != fetchAttributes.constEnd(); ++it )
       {
         sql += ',' + fieldName( mSource->mFields.field( *it ) );
diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp
index 149a18f..5156225 100644
--- a/src/providers/spatialite/qgsspatialiteprovider.cpp
+++ b/src/providers/spatialite/qgsspatialiteprovider.cpp
@@ -568,6 +568,11 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri )
     return;
   }
 
+  if ( mTableBased && hasRowid() )
+  {
+    mPrimaryKey = "ROWID";
+  }
+
   // retrieve version information
   spatialiteVersion();
 
@@ -584,6 +589,7 @@ QgsSpatiaLiteProvider::QgsSpatiaLiteProvider( QString const &uri )
 QgsSpatiaLiteProvider::~QgsSpatiaLiteProvider()
 {
   closeDb();
+  invalidateConnections( mSqlitePath );
 }
 
 QgsAbstractFeatureSource* QgsSpatiaLiteProvider::featureSource() const
@@ -649,9 +655,6 @@ void QgsSpatiaLiteProvider::loadFieldsAbstractInterface( gaiaVectorLayerPtr lyr
     fld = fld->Next;
   }
 
-  mPrimaryKey.clear();
-  mPrimaryKeyAttrs.clear();
-
   QString sql = QString( "PRAGMA table_info(%1)" ).arg( quotedIdentifier( mTableName ) );
 
   char **results;
@@ -668,8 +671,10 @@ void QgsSpatiaLiteProvider::loadFieldsAbstractInterface( gaiaVectorLayerPtr lyr
       if ( pk.toInt() == 0 )
         continue;
 
-      if ( mPrimaryKey.isEmpty() )
+      if ( mPrimaryKeyAttrs.isEmpty() )
         mPrimaryKey = name;
+      else
+        mPrimaryKey.clear();
       mPrimaryKeyAttrs << i - 1;
     }
   }
@@ -766,7 +771,10 @@ void QgsSpatiaLiteProvider::loadFields()
         {
           // found a Primary Key column
           pkCount++;
-          pkName = name;
+          if ( mPrimaryKeyAttrs.isEmpty() )
+            pkName = name;
+          else
+            pkName.clear();
           mPrimaryKeyAttrs << i - 1;
           QgsDebugMsg( "found primaryKey " + name );
         }
@@ -841,7 +849,10 @@ void QgsSpatiaLiteProvider::loadFields()
         if ( name == mPrimaryKey )
         {
           pkCount++;
-          pkName = name;
+          if ( mPrimaryKeyAttrs.isEmpty() )
+            pkName = name;
+          else
+            pkName.clear();
           mPrimaryKeyAttrs << i - 1;
           QgsDebugMsg( "found primaryKey " + name );
         }
@@ -939,6 +950,17 @@ bool QgsSpatiaLiteProvider::hasTriggers()
   return ( ret == SQLITE_OK && rows > 0 );
 }
 
+bool QgsSpatiaLiteProvider::hasRowid()
+{
+  if ( attributeFields.fieldNameIndex( "ROWID" ) >= 0 )
+    return false;
+
+  // table without rowid column
+  QString sql = QString( "SELECT rowid FROM %1 WHERE 0" ).arg( quotedIdentifier( mTableName ) );
+  char *errMsg = nullptr;
+  return sqlite3_exec( sqliteHandle, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK;
+}
+
 
 QString QgsSpatiaLiteProvider::storageType() const
 {
diff --git a/src/providers/spatialite/qgsspatialiteprovider.h b/src/providers/spatialite/qgsspatialiteprovider.h
index 1b9929f..6973120 100644
--- a/src/providers/spatialite/qgsspatialiteprovider.h
+++ b/src/providers/spatialite/qgsspatialiteprovider.h
@@ -266,6 +266,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
     /** Check if a table/view has any triggers.  Triggers can be used on views to make them editable.*/
     bool hasTriggers();
 
+    //! Check if a table has a row id (internal primary key)
+    bool hasRowid();
+
     /** Convert a QgsField to work with SL */
     static bool convertField( QgsField &field );
 
diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp
index 9df9a3f..a39354c 100644
--- a/src/providers/wms/qgswmsprovider.cpp
+++ b/src/providers/wms/qgswmsprovider.cpp
@@ -2952,20 +2952,29 @@ QUrl QgsWmsProvider::getLegendGraphicFullURL( double scale, const QgsRectangle&
 
   QUrl url( lurl );
 
-  if ( !url.hasQueryItem( "SERVICE" ) )
+  // query names are NOT case-sensitive, so make an uppercase list for proper comparison
+  QStringList qnames = QStringList();
+  for ( int i = 0; i < url.queryItems().size(); i++ )
+  {
+    qnames << url.queryItems().at( i ).first.toUpper();
+  }
+  if ( !qnames.contains( "SERVICE" ) )
     setQueryItem( url, "SERVICE", "WMS" );
-  if ( !url.hasQueryItem( "VERSION" ) )
+  if ( !qnames.contains( "VERSION" ) )
     setQueryItem( url, "VERSION", mCaps.mCapabilities.version );
-  if ( !url.hasQueryItem( "SLD_VERSION" ) )
+  if ( !qnames.contains( "SLD_VERSION" ) )
     setQueryItem( url, "SLD_VERSION", "1.1.0" ); // can not determine SLD_VERSION
-  if ( !url.hasQueryItem( "REQUEST" ) )
+  if ( !qnames.contains( "REQUEST" ) )
     setQueryItem( url, "REQUEST", "GetLegendGraphic" );
-  if ( !url.hasQueryItem( "FORMAT" ) )
+  if ( !qnames.contains( "FORMAT" ) )
     setFormatQueryItem( url );
-  if ( !url.hasQueryItem( "LAYER" ) )
+  if ( !qnames.contains( "LAYER" ) )
     setQueryItem( url, "LAYER", mSettings.mActiveSubLayers[0] );
-  if ( !url.hasQueryItem( "STYLE" ) )
+  if ( !qnames.contains( "STYLE" ) )
     setQueryItem( url, "STYLE", mSettings.mActiveSubStyles[0] );
+  // by setting TRANSPARENT=true, even too big legend images will look good
+  if ( !qnames.contains( "TRANSPARENT" ) )
+    setQueryItem( url, "TRANSPARENT", "true" );
 
   // add config parameter related to resolution
   QSettings s;
@@ -3605,14 +3614,24 @@ void QgsWmsTiledImageDownloadHandler::repeatTileRequest( QNetworkRequest const &
   connect( reply, SIGNAL( finished() ), this, SLOT( tileReplyFinished() ) );
 }
 
+// Some servers like http://glogow.geoportal2.pl/map/wms/wms.php? do not BBOX
+// to be formatted with excessive precision. As a double is exactly represented
+// with 19 decimal figures, do not attempt to output more
+static QString formatDouble( double x )
+{
+  if ( x == 0.0 )
+    return "0";
+  const int numberOfDecimals = qMax( 0, 19 - static_cast<int>( ceil( log10( fabs( x ) ) ) ) );
+  return qgsDoubleToString( x, numberOfDecimals );
+}
+
 QString QgsWmsProvider::toParamValue( const QgsRectangle& rect, bool changeXY )
 {
-  // Warning: does not work with scientific notation
   return QString( changeXY ? "%2,%1,%4,%3" : "%1,%2,%3,%4" )
-         .arg( qgsDoubleToString( rect.xMinimum() ),
-               qgsDoubleToString( rect.yMinimum() ),
-               qgsDoubleToString( rect.xMaximum() ),
-               qgsDoubleToString( rect.yMaximum() ) );
+         .arg( formatDouble( rect.xMinimum() ),
+               formatDouble( rect.yMinimum() ),
+               formatDouble( rect.xMaximum() ),
+               formatDouble( rect.yMaximum() ) );
 }
 
 void QgsWmsProvider::setSRSQueryItem( QUrl& url )
diff --git a/src/python/qgspythonutilsimpl.cpp b/src/python/qgspythonutilsimpl.cpp
index 1c8f5ad..5ac0352 100644
--- a/src/python/qgspythonutilsimpl.cpp
+++ b/src/python/qgspythonutilsimpl.cpp
@@ -300,7 +300,7 @@ bool QgsPythonUtilsImpl::runStringUnsafe( const QString& command, bool single )
   // (non-unicode strings can be mangled)
   PyObject* obj = PyRun_String( command.toUtf8().data(), single ? Py_single_input : Py_file_input, mMainDict, mMainDict );
   bool res = nullptr == PyErr_Occurred();
-  Py_DECREF( obj );
+  Py_XDECREF( obj );
 
   // we are done calling python API, release global interpreter lock
   PyGILState_Release( gstate );
diff --git a/src/server/qgsmslayercache.cpp b/src/server/qgsmslayercache.cpp
index 363f747..fe01c26 100644
--- a/src/server/qgsmslayercache.cpp
+++ b/src/server/qgsmslayercache.cpp
@@ -17,6 +17,8 @@
 
 #include "qgsmslayercache.h"
 #include "qgsmessagelog.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsmaplayer.h"
 #include "qgsvectorlayer.h"
 #include "qgslogger.h"
 #include <QFile>
@@ -187,6 +189,10 @@ void QgsMSLayerCache::removeLeastUsedEntry()
 
 void QgsMSLayerCache::freeEntryRessources( QgsMSLayerCacheEntry& entry )
 {
+  // remove layer from QgsMapLayerRegistry before delete it
+  if ( QgsMapLayerRegistry::instance()->mapLayer( entry.layerPointer->id() ) )
+    QgsMapLayerRegistry::instance()->removeMapLayer( entry.layerPointer->id() );
+
   delete entry.layerPointer;
 
   //remove the temporary files of a layer
diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp
index 0392b42..22600f8 100644
--- a/src/server/qgsserver.cpp
+++ b/src/server/qgsserver.cpp
@@ -465,6 +465,13 @@ QPair<QByteArray, QByteArray> QgsServer::handleRequest( const QString& queryStri
   int logLevel = QgsServerLogger::instance()->logLevel();
   QTime time; //used for measuring request time if loglevel < 1
   QgsMapLayerRegistry::instance()->removeAllMapLayers();
+
+  // Clean up  Expression Context
+  // because each call to QgsMapLayer::draw add items to QgsExpressionContext scope
+  // list. This prevent the scope list to grow indefinitely and seriously deteriorate
+  // performances and memory in the long run
+  sMapRenderer->rendererContext()->setExpressionContext( QgsExpressionContext() );
+
   sQgsApplication->processEvents();
   if ( logLevel < 1 )
   {
diff --git a/src/ui/effects/qgseffectstackpropertieswidgetbase.ui b/src/ui/effects/qgseffectstackpropertieswidgetbase.ui
index 5e4abb7..5fdf574 100644
--- a/src/ui/effects/qgseffectstackpropertieswidgetbase.ui
+++ b/src/ui/effects/qgseffectstackpropertieswidgetbase.ui
@@ -83,7 +83,7 @@
           </size>
          </property>
          <property name="toolTip">
-          <string>Add symbol layer</string>
+          <string>Add new effect</string>
          </property>
         </widget>
        </item>
@@ -96,7 +96,7 @@
           </size>
          </property>
          <property name="toolTip">
-          <string>Remove symbol layer</string>
+          <string>Remove effect</string>
          </property>
         </widget>
        </item>
diff --git a/src/ui/qgscustomizationdialogbase.ui b/src/ui/qgscustomizationdialogbase.ui
index 3c5d3e8..a0cd1de 100644
--- a/src/ui/qgscustomizationdialogbase.ui
+++ b/src/ui/qgscustomizationdialogbase.ui
@@ -151,7 +151,7 @@
      <normaloff>:/images/themes/default/mActionSelectAllTree.svg</normaloff>:/images/themes/default/mActionSelectAllTree.svg</iconset>
    </property>
    <property name="text">
-    <string>Select All</string>
+    <string>Check All</string>
    </property>
   </action>
  </widget>
diff --git a/src/ui/qgsoraclenewconnectionbase.ui b/src/ui/qgsoraclenewconnectionbase.ui
index 674c582..347e24d 100644
--- a/src/ui/qgsoraclenewconnectionbase.ui
+++ b/src/ui/qgsoraclenewconnectionbase.ui
@@ -45,6 +45,13 @@
       <string>Connection Information</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_1">
+      <item row="9" column="0" colspan="2">
+       <widget class="QCheckBox" name="chkStorePassword">
+        <property name="text">
+         <string>Save Password</string>
+        </property>
+       </widget>
+      </item>
       <item row="13" column="0" colspan="3">
        <widget class="QCheckBox" name="cb_useEstimatedMetadata">
         <property name="toolTip">
@@ -187,13 +194,6 @@
       <item row="1" column="1" colspan="2">
        <widget class="QLineEdit" name="txtDatabase"/>
       </item>
-      <item row="9" column="0" colspan="2">
-       <widget class="QCheckBox" name="chkStorePassword">
-        <property name="text">
-         <string>Save Password</string>
-        </property>
-       </widget>
-      </item>
       <item row="0" column="1" colspan="2">
        <widget class="QLineEdit" name="txtName">
         <property name="toolTip">
@@ -235,6 +235,19 @@
         </property>
        </widget>
       </item>
+      <item row="15" column="0" colspan="3">
+       <widget class="QCheckBox" name="cb_includeGeoAttributes">
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="whatsThis">
+         <string/>
+        </property>
+        <property name="text">
+         <string>Include additional geometry attributes</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -257,6 +270,7 @@
   <tabstop>cb_allowGeometrylessTables</tabstop>
   <tabstop>cb_useEstimatedMetadata</tabstop>
   <tabstop>cb_onlyExistingTypes</tabstop>
+  <tabstop>cb_includeGeoAttributes</tabstop>
   <tabstop>buttonBox</tabstop>
  </tabstops>
  <resources/>
diff --git a/src/ui/qgsrasterlayersaveasdialogbase.ui b/src/ui/qgsrasterlayersaveasdialogbase.ui
index 507a36b..70296be 100644
--- a/src/ui/qgsrasterlayersaveasdialogbase.ui
+++ b/src/ui/qgsrasterlayersaveasdialogbase.ui
@@ -564,8 +564,8 @@ datasets with maximum width and height specified below.</string>
                <string>...</string>
               </property>
               <property name="icon">
-               <iconset>
-                <normaloff>../../images/themes/default/mActionNewAttribute.png</normaloff>../../images/themes/default/mActionNewAttribute.png</iconset>
+               <iconset resource="../../images/images.qrc">
+                <normaloff>:/images/themes/default/mActionNewAttribute.svg</normaloff>:/images/themes/default/mActionNewAttribute.svg</iconset>
               </property>
              </widget>
             </item>
@@ -578,8 +578,8 @@ datasets with maximum width and height specified below.</string>
                <string>...</string>
               </property>
               <property name="icon">
-               <iconset>
-                <normaloff>../../images/themes/default/mActionCopySelected.png</normaloff>../../images/themes/default/mActionCopySelected.png</iconset>
+               <iconset resource="../../images/images.qrc">
+                <normaloff>:/images/themes/default/mActionCopySelected.png</normaloff>:/images/themes/default/mActionCopySelected.png</iconset>
               </property>
              </widget>
             </item>
@@ -595,8 +595,8 @@ datasets with maximum width and height specified below.</string>
                <string>...</string>
               </property>
               <property name="icon">
-               <iconset>
-                <normaloff>../../images/themes/default/mActionDeleteAttribute.png</normaloff>../../images/themes/default/mActionDeleteAttribute.png</iconset>
+               <iconset resource="../../images/images.qrc">
+                <normaloff>:/images/themes/default/mActionDeleteAttribute.png</normaloff>:/images/themes/default/mActionDeleteAttribute.png</iconset>
               </property>
              </widget>
             </item>
@@ -609,8 +609,8 @@ datasets with maximum width and height specified below.</string>
                <string>...</string>
               </property>
               <property name="icon">
-               <iconset>
-                <normaloff>../../images/themes/default/mActionRemove.png</normaloff>../../images/themes/default/mActionRemove.png</iconset>
+               <iconset resource="../../images/images.qrc">
+                <normaloff>:/images/themes/default/mActionRemove.png</normaloff>:/images/themes/default/mActionRemove.png</iconset>
               </property>
              </widget>
             </item>
@@ -714,7 +714,9 @@ datasets with maximum width and height specified below.</string>
   <tabstop>mLoadTransparentNoDataToolButton</tabstop>
   <tabstop>mRemoveAllNoDataToolButton</tabstop>
  </tabstops>
- <resources/>
+ <resources>
+  <include location="../../images/images.qrc"/>
+ </resources>
  <connections>
   <connection>
    <sender>mButtonBox</sender>
diff --git a/tests/src/app/CMakeLists.txt b/tests/src/app/CMakeLists.txt
index 2829e95..9f098ce 100644
--- a/tests/src/app/CMakeLists.txt
+++ b/tests/src/app/CMakeLists.txt
@@ -101,6 +101,7 @@ ENDMACRO (ADD_QGIS_TEST)
 #############################################################
 # Tests:
 
+ADD_QGIS_TEST(apppythontest testqgisapppython.cpp)
 ADD_QGIS_TEST(qgisappclipboard testqgisappclipboard.cpp)
 ADD_QGIS_TEST(attributetabletest testqgsattributetable.cpp)
 ADD_QGIS_TEST(fieldcalculatortest testqgsfieldcalculator.cpp)
diff --git a/tests/src/app/testqgisapppython.cpp b/tests/src/app/testqgisapppython.cpp
new file mode 100644
index 0000000..b75c6d9
--- /dev/null
+++ b/tests/src/app/testqgisapppython.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+     testqgsapppython.cpp
+     --------------------
+    Date                 : May 2016
+    Copyright            : (C) 2016 by Nyall Dawson
+    Email                : nyall dot dawson at gmail 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 <QApplication>
+#include <QObject>
+#include <QSplashScreen>
+#include <QString>
+#include <QStringList>
+#include <QtTest/QtTest>
+
+#include <qgisapp.h>
+#include <qgsapplication.h>
+
+/** \ingroup UnitTests
+ * This is a unit test for the QgisApp python support.
+ */
+class TestQgisAppPython : public QObject
+{
+    Q_OBJECT
+
+  public:
+    TestQgisAppPython();
+
+  private slots:
+    void initTestCase();// will be called before the first testfunction is executed.
+    void cleanupTestCase();// will be called after the last testfunction was executed.
+    void init() {} // will be called before each testfunction is executed.
+    void cleanup() {} // will be called after every testfunction.
+
+    void runString();
+    void evalString();
+
+  private:
+    QgisApp * mQgisApp;
+    QString mTestDataDir;
+};
+
+TestQgisAppPython::TestQgisAppPython()
+    : mQgisApp( nullptr )
+{
+
+}
+
+//runs before all tests
+void TestQgisAppPython::initTestCase()
+{
+  // Set up the QSettings environment
+  QCoreApplication::setOrganizationName( "QGIS" );
+  QCoreApplication::setOrganizationDomain( "qgis.org" );
+  QCoreApplication::setApplicationName( "QGIS-TEST" );
+
+  qDebug() << "TestQgisAppClipboard::initTestCase()";
+  // init QGIS's paths - true means that all path will be inited from prefix
+  QgsApplication::init();
+  QgsApplication::initQgis();
+  mTestDataDir = QString( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
+  mQgisApp = new QgisApp();
+  mQgisApp->loadPythonSupport();
+}
+
+//runs after all tests
+void TestQgisAppPython::cleanupTestCase()
+{
+  QgsApplication::exitQgis();
+}
+
+void TestQgisAppPython::runString()
+{
+  QVERIFY( mQgisApp->mPythonUtils->runString( "a=1+1" ) );
+  QVERIFY( !mQgisApp->mPythonUtils->runString( "x" ) );
+  QVERIFY( !mQgisApp->mPythonUtils->runString( "" ) );
+}
+
+void TestQgisAppPython::evalString()
+{
+  QString result;
+  //good string
+  QVERIFY( mQgisApp->mPythonUtils->evalString( "1+1", result ) );
+  QCOMPARE( result, QString( "2" ) );
+
+  //bad string
+  QVERIFY( !mQgisApp->mPythonUtils->evalString( "1+", result ) );
+}
+
+QTEST_MAIN( TestQgisAppPython )
+#include "testqgisapppython.moc"
diff --git a/tests/src/app/testqgsmaptoolidentifyaction.cpp b/tests/src/app/testqgsmaptoolidentifyaction.cpp
index 1a932eb..d98997b 100644
--- a/tests/src/app/testqgsmaptoolidentifyaction.cpp
+++ b/tests/src/app/testqgsmaptoolidentifyaction.cpp
@@ -16,6 +16,7 @@
 #include <QtTest/QtTest>
 #include "qgsapplication.h"
 #include "qgsvectorlayer.h"
+#include "qgsrasterlayer.h"
 #include "qgsfeature.h"
 #include "qgsgeometry.h"
 #include "qgsvectordataprovider.h"
@@ -24,6 +25,8 @@
 #include "qgsunittypes.h"
 #include "qgsmaptoolidentifyaction.h"
 
+#include "cpl_conv.h"
+
 class TestQgsMapToolIdentifyAction : public QObject
 {
     Q_OBJECT
@@ -40,9 +43,38 @@ class TestQgsMapToolIdentifyAction : public QObject
     void lengthCalculation(); //test calculation of derived length attributes
     void perimeterCalculation(); //test calculation of derived perimeter attribute
     void areaCalculation(); //test calculation of derived area attribute
+    void identifyRasterFloat32(); // test pixel identification and decimal precision
+    void identifyRasterFloat64(); // test pixel identification and decimal precision
+    void identifyInvalidPolygons(); // test selecting invalid polygons
 
   private:
     QgsMapCanvas* canvas;
+
+    QString testIdentifyRaster( QgsRasterLayer* layer, double xGeoref, double yGeoref );
+    QList<QgsMapToolIdentify::IdentifyResult> testIdentifyVector( QgsVectorLayer* layer, double xGeoref, double yGeoref );
+
+    // Release return with delete []
+    unsigned char *
+    hex2bytes( const char *hex, int *size )
+    {
+      QByteArray ba = QByteArray::fromHex( hex );
+      unsigned char *out = new unsigned char[ba.size()];
+      memcpy( out, ba.data(), ba.size() );
+      *size = ba.size();
+      return out;
+    }
+
+    // TODO: make this a QgsGeometry member...
+    QgsGeometry geomFromHexWKB( const char *hexwkb )
+    {
+      int wkbsize;
+      unsigned char *wkb = hex2bytes( hexwkb, &wkbsize );
+      QgsGeometry geom;
+      // NOTE: QgsGeometry takes ownership of wkb
+      geom.fromWkb( wkb, wkbsize );
+      return geom;
+    }
+
 };
 
 void TestQgsMapToolIdentifyAction::initTestCase()
@@ -241,6 +273,111 @@ void TestQgsMapToolIdentifyAction::areaCalculation()
   QVERIFY( qgsDoubleNear( area, 389.6117, 0.001 ) );
 }
 
+// private
+QString TestQgsMapToolIdentifyAction::testIdentifyRaster( QgsRasterLayer* layer, double xGeoref, double yGeoref )
+{
+  QScopedPointer< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
+  QgsPoint mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
+  QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer*>() << layer );
+  if ( result.length() != 1 )
+    return "";
+  return result[0].mAttributes["Band 1"];
+}
+
+// private
+QList<QgsMapToolIdentify::IdentifyResult>
+TestQgsMapToolIdentifyAction::testIdentifyVector( QgsVectorLayer* layer, double xGeoref, double yGeoref )
+{
+  QScopedPointer< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
+  QgsPoint mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
+  QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer*>() << layer );
+  return result;
+}
+
+void TestQgsMapToolIdentifyAction::identifyRasterFloat32()
+{
+  //create a temporary layer
+  QString raster = QString( TEST_DATA_DIR ) + "/raster/test.asc";
+
+  // By default the QgsRasterLayer forces AAIGRID_DATATYPE=Float64
+  CPLSetConfigOption( "AAIGRID_DATATYPE", "Float32" );
+  QScopedPointer< QgsRasterLayer> tempLayer( new QgsRasterLayer( raster ) );
+  CPLSetConfigOption( "AAIGRID_DATATYPE", nullptr );
+
+  QVERIFY( tempLayer->isValid() );
+
+  canvas->setExtent( QgsRectangle( 0, 0, 7, 1 ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 0.5, 0.5 ), QString( "-999.9" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 1.5, 0.5 ), QString( "-999.987" ) );
+
+  // More than 6 significant digits for corresponding value in .asc:
+  // precision loss in Float32
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 2.5, 0.5 ), QString( "1.2345678" ) ); // in .asc file : 1.2345678
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 3.5, 0.5 ), QString( "123456" ) );
+
+  // More than 6 significant digits: no precision loss here for that particular value
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 4.5, 0.5 ), QString( "1234567" ) );
+
+  // More than 6 significant digits: no precision loss here for that particular value
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 5.5, 0.5 ), QString( "-999.9876" ) );
+
+  // More than 6 significant digits for corresponding value in .asc:
+  // precision loss in Float32
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 6.5, 0.5 ), QString( "1.2345678901234" ) ); // in .asc file : 1.2345678901234
+}
+
+void TestQgsMapToolIdentifyAction::identifyRasterFloat64()
+{
+  //create a temporary layer
+  QString raster = QString( TEST_DATA_DIR ) + "/raster/test.asc";
+  QScopedPointer< QgsRasterLayer> tempLayer( new QgsRasterLayer( raster ) );
+  QVERIFY( tempLayer->isValid() );
+
+  canvas->setExtent( QgsRectangle( 0, 0, 7, 1 ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 0.5, 0.5 ), QString( "-999.9" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 1.5, 0.5 ), QString( "-999.987" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 2.5, 0.5 ), QString( "1.2345678" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 3.5, 0.5 ), QString( "123456" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 4.5, 0.5 ), QString( "1234567" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 5.5, 0.5 ), QString( "-999.9876" ) );
+
+  QCOMPARE( testIdentifyRaster( tempLayer.data(), 6.5, 0.5 ), QString( "1.2345678901234" ) );
+}
+
+void TestQgsMapToolIdentifyAction::identifyInvalidPolygons()
+{
+  //create a temporary layer
+  QScopedPointer< QgsVectorLayer > memoryLayer( new QgsVectorLayer( "Polygon?field=pk:int", "vl", "memory" ) );
+  QVERIFY( memoryLayer->isValid() );
+  QgsFeature f1( memoryLayer->dataProvider()->fields(), 1 );
+  f1.setAttribute( "pk", 1 );
+  f1.setGeometry( geomFromHexWKB(
+                    "010300000001000000030000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000"
+                  ) );
+  // TODO: check why we need the ->dataProvider() part, since
+  //       there's a QgsVectorLayer::addFeatures method too
+  //memoryLayer->addFeatures( QgsFeatureList() << f1 );
+  memoryLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
+
+  canvas->setExtent( QgsRectangle( 0, 0, 10, 10 ) );
+  QList<QgsMapToolIdentify::IdentifyResult> identified;
+  identified = testIdentifyVector( memoryLayer.data(), 4, 6 );
+  QCOMPARE( identified.length(), 0 );
+  identified = testIdentifyVector( memoryLayer.data(), 6, 4 );
+  QCOMPARE( identified.length(), 1 );
+  QCOMPARE( identified[0].mFeature.attribute( "pk" ), QVariant( 1 ) );
+
+}
+
 
 QTEST_MAIN( TestQgsMapToolIdentifyAction )
 #include "testqgsmaptoolidentifyaction.moc"
diff --git a/tests/src/core/testqgscomposergroup.cpp b/tests/src/core/testqgscomposergroup.cpp
index 4886375..d7213a4 100644
--- a/tests/src/core/testqgscomposergroup.cpp
+++ b/tests/src/core/testqgscomposergroup.cpp
@@ -17,11 +17,14 @@
 
 #include "qgscomposeritemgroup.h"
 #include "qgscomposerlabel.h"
+#include "qgscomposerarrow.h"
 #include "qgscomposition.h"
 #include "qgscompositionchecker.h"
 #include "qgsapplication.h"
+#include "qgslogger.h"
 
 #include <QObject>
+#include <QtTest/QSignalSpy>
 #include <QtTest/QtTest>
 
 class TestQgsComposerGroup : public QObject
@@ -48,6 +51,9 @@ class TestQgsComposerGroup : public QObject
     void undoRedo(); //test that group/ungroup undo/redo commands don't crash
 
   private:
+
+    void dumpUndoStack( const QUndoStack&, QString prefix = "" ) const;
+
     QgsComposition *mComposition;
     QgsMapSettings *mMapSettings;
     QgsComposerLabel* mItem1;
@@ -56,6 +62,18 @@ class TestQgsComposerGroup : public QObject
     QString mReport;
 };
 
+// private
+void TestQgsComposerGroup::dumpUndoStack( const QUndoStack& us, QString prefix ) const
+{
+  if ( ! prefix.isEmpty() ) prefix += ": ";
+  for ( int i = 0; i < us.count(); ++i )
+  {
+    QgsDebugMsg( QString( "%4US %1: %2%3" )
+                 .arg( i ). arg( i >= us.index() ? "-" : "" )
+                 .arg( us.text( i ) ) .arg( prefix ) );
+  }
+}
+
 void TestQgsComposerGroup::initTestCase()
 {
   QgsApplication::init();
@@ -161,36 +179,261 @@ void TestQgsComposerGroup::deleteGroup()
   QCOMPARE( items.size(), 1 );
   QVERIFY( mItem1->isRemoved() );
   QVERIFY( mItem2->isRemoved() );
+  QVERIFY( mGroup->isRemoved() );
 }
 
+Q_DECLARE_METATYPE( QgsComposerItemGroup * );
+Q_DECLARE_METATYPE( QgsComposerArrow * );
+Q_DECLARE_METATYPE( QgsComposerItem * );
+
 void TestQgsComposerGroup::undoRedo()
 {
-#if 0 //expected fail - see #11371
+  QgsComposerArrow *item1, *item2;
+  int arrowsAdded = 0;
+  int groupsAdded = 0;
+  int itemsRemoved = 0;
+
+  qRegisterMetaType<QgsComposerArrow *>();
+  QSignalSpy spyArrowAdded( mComposition, SIGNAL( composerArrowAdded( QgsComposerArrow* ) ) );
+  QCOMPARE( spyArrowAdded.count(), 0 );
+
+  qRegisterMetaType<QgsComposerItemGroup *>();
+  QSignalSpy spyGroupAdded( mComposition, SIGNAL( composerItemGroupAdded( QgsComposerItemGroup* ) ) );
+  QCOMPARE( spyGroupAdded.count(), 0 );
+
+  qRegisterMetaType<QgsComposerItem *>();
+  QSignalSpy spyItemRemoved( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
+  QCOMPARE( spyItemRemoved.count(), 0 );
+
   //test for crash when undo/redoing with groups
+  // Set initial condition
+  QUndoStack *us = mComposition->undoStack();
+  QgsDebugMsg( QString( "clearing" ) );
+  us->clear();
+  QgsDebugMsg( QString( "clearing completed" ) );
+  QList<QgsComposerItem*> items;
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 1 ); // paper only
+  QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
 
   //create some items
-  mItem1 = new QgsComposerLabel( mComposition );
-  mComposition->addItem( mItem1 );
-  mItem2 = new QgsComposerLabel( mComposition );
-  mComposition->addItem( mItem2 );
+  item1 = new QgsComposerArrow( QPointF( 0, 0 ), QPointF( 1, 1 ), mComposition );
+  item1->setArrowHeadWidth( 0 );
+  item1->setArrowHeadOutlineWidth( 0 );
+  mComposition->addComposerArrow( item1 );
+  QCOMPARE( spyArrowAdded.count(), ++arrowsAdded );
+  item2 = new QgsComposerArrow( QPointF( -1, -2 ), QPointF( 1, 1 ), mComposition );
+  item2->setArrowHeadOutlineWidth( 0 );
+  item2->setArrowHeadWidth( 0 );
+  mComposition->addComposerArrow( item2 );
+  QCOMPARE( spyArrowAdded.count(), ++arrowsAdded );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 3 ); // paper, 2 shapes
+  QgsDebugMsg( QString( "addedItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
+  QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
+  QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
+  //dumpUndoStack(*us, "after initial items addition");
 
   //group items
-  QList<QgsComposerItem*> items;
-  items << mItem1 << mItem2;
+  items.clear();
+  items << item1 << item2;
   mGroup = mComposition->groupItems( items );
-
-  //move, and ungroup
-  mGroup->beginCommand( "move" );
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  QCOMPARE( mGroup->items().size(), 2 );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QVERIFY( ! item1->isRemoved() );
+  QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
+  QVERIFY( ! item2->isRemoved() );
+  QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
+  QVERIFY( ! mGroup->isRemoved() );
+  QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
+  //dumpUndoStack(*us, "after initial items addition");
+
+  //move group
+  QgsDebugMsg( QString( "moving group" ) );
+  mGroup->beginCommand( "move group" );
   mGroup->move( 10.0, 20.0 );
   mGroup->endCommand();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  QgsDebugMsg( QString( "groupItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
+  QCOMPARE( mGroup->items().size(), 2 );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QVERIFY( ! item1->isRemoved() );
+  QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
+  QVERIFY( ! item2->isRemoved() );
+  QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
+  QVERIFY( ! mGroup->isRemoved() );
+  QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
+
+  //ungroup
+  QgsDebugMsg( QString( "ungrouping" ) );
   mComposition->ungroupItems( mGroup );
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 3 ); // paper, 2 shapes
+  QVERIFY( ! item1->isRemoved() );
+  QVERIFY( ! item2->isRemoved() );
+  QVERIFY( mGroup->isRemoved() );
+  QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) ); // should not rely on this
+  //dumpUndoStack(*us, "after ungroup");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: Remove item group
+
+  //undo (groups again) -- crashed here before #11371 got fixed
+  QgsDebugMsg( QString( "undo ungrouping" ) );
+  us->undo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  QCOMPARE( mGroup->items().size(), 2 ); // WARNING: might not be alive anymore
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QVERIFY( ! item1->isRemoved() );
+  QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
+  QVERIFY( ! item2->isRemoved() );
+  QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
+  QVERIFY( ! mGroup->isRemoved() );
+  QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
+  //dumpUndoStack(*us, "after undo ungroup");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: -Remove item group
+
+  //remove group
+  QgsDebugMsg( QString( "remove group" ) );
+  mComposition->removeComposerItem( mGroup, true, true );
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  itemsRemoved += 3; // the group and the two items
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 1 ); // paper only
+  QgsDebugMsg( QString( "remove stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
+  //dumpUndoStack(*us, "after remove group");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: Remove item group
+
+  //undo remove group
+  QgsDebugMsg( QString( "undo remove group" ) );
+  us->undo();
+  arrowsAdded += 2;
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( mGroup->items().size(), 2 );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
+  //dumpUndoStack(*us, "after undo remove group");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: -Remove item group
+
+  //undo move group
+  QgsDebugMsg( QString( "undo move group" ) );
+  us->undo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  QCOMPARE( mGroup->items().size(), 2 );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QCOMPARE( item1->isGroupMember(), true );
+  QCOMPARE( item2->isGroupMember(), true );
+  QVERIFY( ! item1->isRemoved() );
+  QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
+  QVERIFY( ! item2->isRemoved() );
+  QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
+  QVERIFY( ! mGroup->isRemoved() );
+  QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
+  //dumpUndoStack(*us, "after undo move group");
+  // US 0: Items grouped
+  // US 1: -move group
+  // US 2: -Remove item group
+
+  //undo group
+  QgsDebugMsg( QString( "undo group" ) );
+  us->undo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
+  //QCOMPARE( mGroup->items().size(), 2 ); // not important
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 3 ); // paper, 2 shapes
+  QCOMPARE( item1->isGroupMember(), false );
+  QCOMPARE( item2->isGroupMember(), false );
+  QVERIFY( ! item1->isRemoved() );
+  QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
+  QVERIFY( ! item2->isRemoved() );
+  QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
+  QVERIFY( mGroup->isRemoved() );
+  //QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
+  //dumpUndoStack(*us, "after undo group");
+  // US 0: -Items grouped
+  // US 1: -move group
+  // US 2: -Remove item group
+
+  //redo group
+  QgsDebugMsg( QString( "redo group" ) );
+  us->redo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QCOMPARE( item1->isGroupMember(), true );
+  QCOMPARE( item2->isGroupMember(), true );
+  //// QCOMPARE( mGroup->pos(), QPointF( 0, 0 ) ); // getting nan,nan here
+  //dumpUndoStack(*us, "after redo group");
+  // US 0: Items grouped
+  // US 1: -move group
+  // US 2: -Remove item group
+
+  //redo move group
+  QgsDebugMsg( QString( "redo move group" ) );
+  us->redo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
+  QCOMPARE( item1->isGroupMember(), true );
+  QCOMPARE( item2->isGroupMember(), true );
+  QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
+  //dumpUndoStack(*us, "after redo move group");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: -Remove item group
+
+  //redo remove group
+  QgsDebugMsg( QString( "redo remove group" ) );
+  us->redo();
+  QCOMPARE( spyArrowAdded.count(), arrowsAdded );
+  QCOMPARE( spyGroupAdded.count(), groupsAdded );
+  itemsRemoved += 3; // 1 group, 2 contained items
+  QCOMPARE( spyItemRemoved.count(), itemsRemoved );
+  mComposition->composerItems( items );
+  QCOMPARE( items.size(), 1 ); // paper only
+  QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
+  //dumpUndoStack(*us, "after redo remove group");
+  // US 0: Items grouped
+  // US 1: move group
+  // US 2: Remove item group
 
-  //undo
-  mComposition->undoStack()->undo();
+  //unwind the whole stack
+  us->clear();
 
-  //redo
-  mComposition->undoStack()->redo();
-#endif
+  QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
 }
 
 QTEST_MAIN( TestQgsComposerGroup )
diff --git a/tests/src/core/testqgscomposerpicture.cpp b/tests/src/core/testqgscomposerpicture.cpp
index 92b582c..5557cee 100644
--- a/tests/src/core/testqgscomposerpicture.cpp
+++ b/tests/src/core/testqgscomposerpicture.cpp
@@ -57,10 +57,12 @@ class TestQgsComposerPicture : public QObject
     void pictureSvgFrameToImage();
 
     void svgParameters();
+    void issue_14644();
 
     void pictureExpression();
     void pictureInvalidExpression();
 
+
   private:
     QgsComposition* mComposition;
     QgsComposerPicture* mComposerPicture;
@@ -387,6 +389,22 @@ void TestQgsComposerPicture::svgParameters()
   mComposerPicture->setPicturePath( mPngImage );
 }
 
+void TestQgsComposerPicture::issue_14644()
+{
+  //test rendering SVG file with text
+  mComposition->addComposerPicture( mComposerPicture );
+  mComposerPicture->setResizeMode( QgsComposerPicture::Zoom );
+  mComposerPicture->setPicturePath( QString( TEST_DATA_DIR ) + "/svg/issue_14644.svg" );
+
+  QgsCompositionChecker checker( "composerpicture_issue_14644", mComposition );
+  checker.setControlPathPrefix( "composer_picture" );
+  QVERIFY( checker.testComposition( mReport, 0, 0 ) );
+
+  mComposition->removeItem( mComposerPicture );
+  mComposerPicture->setSceneRect( QRectF( 70, 70, 100, 100 ) );
+  mComposerPicture->setPicturePath( mPngImage );
+}
+
 void TestQgsComposerPicture::pictureExpression()
 {
   //test picture source via expression
diff --git a/tests/src/core/testqgscoordinatereferencesystem.cpp b/tests/src/core/testqgscoordinatereferencesystem.cpp
index 9b1f576..afa1c02 100644
--- a/tests/src/core/testqgscoordinatereferencesystem.cpp
+++ b/tests/src/core/testqgscoordinatereferencesystem.cpp
@@ -61,6 +61,7 @@ class TestQgsCoordinateReferenceSystem: public QObject
     void mapUnits();
     void setValidationHint();
     void axisInverted();
+    void createFromProj4Invalid();
   private:
     void debugPrint( QgsCoordinateReferenceSystem &theCrs );
     // these used by createFromESRIWkt()
@@ -464,5 +465,11 @@ void TestQgsCoordinateReferenceSystem::debugPrint(
   }
 }
 
+void TestQgsCoordinateReferenceSystem::createFromProj4Invalid()
+{
+  QgsCoordinateReferenceSystem myCrs;
+  QVERIFY( !myCrs.createFromProj4( "+proj=longlat +no_defs" ) );
+}
+
 QTEST_MAIN( TestQgsCoordinateReferenceSystem )
 #include "testqgscoordinatereferencesystem.moc"
diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp
index 9ebbdc7..02f337c 100644
--- a/tests/src/core/testqgsdistancearea.cpp
+++ b/tests/src/core/testqgsdistancearea.cpp
@@ -42,6 +42,8 @@ class TestQgsDistanceArea: public QObject
     void collections();
     void measureUnits();
     void measureAreaAndUnits();
+    void emptyPolygon();
+    void regression14675();
 
 };
 
@@ -344,6 +346,28 @@ void TestQgsDistanceArea::measureAreaAndUnits()
   QVERIFY( qgsDoubleNear( area, 220240.8172549, 0.00001 ) );
 }
 
+void TestQgsDistanceArea::emptyPolygon()
+{
+  QgsDistanceArea da;
+  da.setSourceCrs( 3452 );
+  da.setEllipsoidalMode( true );
+  da.setEllipsoid( "WGS84" );
+
+  //test that measuring an empty polygon doesn't crash
+  da.measurePolygon( QList< QgsPoint >() );
+}
+
+void TestQgsDistanceArea::regression14675()
+{
+  //test regression #14675
+  QgsDistanceArea calc;
+  calc.setEllipsoidalMode( true );
+  calc.setEllipsoid( "GRS80" );
+  calc.setSourceCrs( 145L );
+  QgsGeometry geom( QgsGeometryFactory::geomFromWkt( "Polygon ((917593.5791854317067191 6833700.00807378999888897, 917596.43389983859378844 6833700.67099479306489229, 917599.53056440979707986 6833700.78673478215932846, 917593.5791854317067191 6833700.00807378999888897))" ) );
+  QVERIFY( qgsDoubleNear( calc.measureArea( &geom ), 0.83301, 0.0001 ) );
+}
+
 QTEST_MAIN( TestQgsDistanceArea )
 #include "testqgsdistancearea.moc"
 
diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp
index 4edfe0b..b76db34 100644
--- a/tests/src/core/testqgsexpression.cpp
+++ b/tests/src/core/testqgsexpression.cpp
@@ -299,6 +299,20 @@ class TestQgsExpression: public QObject
       QTest::newRow( "'nan'='x'" ) << "'nan'='x'" << false << QVariant( 0 );
       QTest::newRow( "'inf'='inf'" ) << "'inf'='inf'" << false << QVariant( 1 );
       QTest::newRow( "'inf'='x'" ) << "'inf'='x'" << false << QVariant( 0 );
+      QTest::newRow( "'1.1'='1.1'" ) << "'1.1'='1.1'" << false << QVariant( 1 );
+      QTest::newRow( "'1.1'!='1.1'" ) << "'1.1'!='1.1'" << false << QVariant( 0 );
+      QTest::newRow( "'1.1'='1.10'" ) << "'1.1'='1.10'" << false << QVariant( 0 );
+      QTest::newRow( "'1.1'!='1.10'" ) << "'1.1'!='1.10'" << false << QVariant( 1 );
+      QTest::newRow( "1.1=1.10" ) << "1.1=1.10" << false << QVariant( 1 );
+      QTest::newRow( "1.1 != 1.10" ) << "1.1 != 1.10" << false << QVariant( 0 );
+      QTest::newRow( "'1.1'=1.1" ) << "'1.1'=1.1" << false << QVariant( 1 );
+      QTest::newRow( "'1.10'=1.1" ) << "'1.10'=1.1" << false << QVariant( 1 );
+      QTest::newRow( "1.1='1.10'" ) << "1.1='1.10'" << false << QVariant( 1 );
+      QTest::newRow( "'1.1'='1.10000'" ) << "'1.1'='1.10000'" << false << QVariant( 0 );
+      QTest::newRow( "'1E-23'='1E-23'" ) << "'1E-23'='1E-23'" << false << QVariant( 1 );
+      QTest::newRow( "'1E-23'!='1E-23'" ) << "'1E-23'!='1E-23'" << false << QVariant( 0 );
+      QTest::newRow( "'1E-23'='2E-23'" ) << "'1E-23'='2E-23'" << false << QVariant( 0 );
+      QTest::newRow( "'1E-23'!='2E-23'" ) << "'1E-23'!='2E-23'" << false << QVariant( 1 );
 
       // is, is not
       QTest::newRow( "is null,null" ) << "null is null" << false << QVariant( 1 );
@@ -309,6 +323,10 @@ class TestQgsExpression: public QObject
       QTest::newRow( "is not int" ) << "1 is not 1" << false << QVariant( 0 );
       QTest::newRow( "is text" ) << "'x' is 'y'" << false << QVariant( 0 );
       QTest::newRow( "is not text" ) << "'x' is not 'y'" << false << QVariant( 1 );
+      QTest::newRow( "'1.1' is '1.10'" ) << "'1.1' is '1.10'" << false << QVariant( 0 );
+      QTest::newRow( "'1.1' is '1.10000'" ) << "'1.1' is '1.10000'" << false << QVariant( 0 );
+      QTest::newRow( "1.1 is '1.10'" ) << "1.1 is '1.10'" << false << QVariant( 1 );
+      QTest::newRow( "'1.10' is 1.1" ) << "'1.10' is 1.1" << false << QVariant( 1 );
 
       //  logical
       QTest::newRow( "T or F" ) << "1=1 or 2=3" << false << QVariant( 1 );
diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp
index 9a05468..8fe4cdd 100644
--- a/tests/src/core/testqgsgeometry.cpp
+++ b/tests/src/core/testqgsgeometry.cpp
@@ -604,7 +604,7 @@ void TestQgsGeometry::pointV2()
   p16.transform( tr, QgsCoordinateTransform::ForwardTransform );
   QVERIFY( qgsDoubleNear( p16.x(), 175.771, 0.001 ) );
   QVERIFY( qgsDoubleNear( p16.y(), -39.722, 0.001 ) );
-  QVERIFY( qgsDoubleNear( p16.z(), 57.2958, 0.001 ) );
+  QVERIFY( qgsDoubleNear( p16.z(), 1.0, 0.001 ) );
   QCOMPARE( p16.m(), 2.0 );
   p16.transform( tr, QgsCoordinateTransform::ReverseTransform );
   QVERIFY( qgsDoubleNear( p16.x(), 6374985, 1 ) );
@@ -1489,11 +1489,11 @@ void TestQgsGeometry::lineStringV2()
   l22.transform( tr, QgsCoordinateTransform::ForwardTransform );
   QVERIFY( qgsDoubleNear( l22.pointN( 0 ).x(), 175.771, 0.001 ) );
   QVERIFY( qgsDoubleNear( l22.pointN( 0 ).y(), -39.722, 0.001 ) );
-  QVERIFY( qgsDoubleNear( l22.pointN( 0 ).z(), 57.2958, 0.001 ) );
+  QVERIFY( qgsDoubleNear( l22.pointN( 0 ).z(), 1.0, 0.001 ) );
   QCOMPARE( l22.pointN( 0 ).m(), 2.0 );
   QVERIFY( qgsDoubleNear( l22.pointN( 1 ).x(), 176.959, 0.001 ) );
   QVERIFY( qgsDoubleNear( l22.pointN( 1 ).y(), -38.798, 0.001 ) );
-  QVERIFY( qgsDoubleNear( l22.pointN( 1 ).z(), 171.887, 0.001 ) );
+  QVERIFY( qgsDoubleNear( l22.pointN( 1 ).z(), 3.0, 0.001 ) );
   QCOMPARE( l22.pointN( 1 ).m(), 4.0 );
 
   //reverse transform
diff --git a/tests/src/core/testqgslabelingenginev2.cpp b/tests/src/core/testqgslabelingenginev2.cpp
index daea089..9f70edf 100644
--- a/tests/src/core/testqgslabelingenginev2.cpp
+++ b/tests/src/core/testqgslabelingenginev2.cpp
@@ -259,7 +259,7 @@ void TestQgsLabelingEngineV2::testRuleBased()
 
   delete rl2;
 
-  /*
+#if 0
   QPainter p( &img );
   QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
   context.setPainter( &p );
@@ -267,7 +267,8 @@ void TestQgsLabelingEngineV2::testRuleBased()
   QgsLabelingEngineV2 engine;
   engine.setMapSettings( mapSettings );
   engine.addProvider( new QgsRuleBasedLabelProvider( , vl ) );
-  engine.run( context );*/
+  engine.run( context );
+#endif
 }
 
 void TestQgsLabelingEngineV2::zOrder()
diff --git a/tests/src/gui/testqgsrubberband.cpp b/tests/src/gui/testqgsrubberband.cpp
index e5715b0..258e3b5 100644
--- a/tests/src/gui/testqgsrubberband.cpp
+++ b/tests/src/gui/testqgsrubberband.cpp
@@ -46,6 +46,7 @@ class TestQgsRubberband : public QObject
     void testAddSingleMultiGeometries(); //test for #7728
     void testBoundingRect(); //test for #12392
     void testVisibility(); //test for 12486
+    void testClose(); //test closing geometry
 
   private:
     QgsMapCanvas* mCanvas;
@@ -193,6 +194,34 @@ void TestQgsRubberband::testVisibility()
 
 }
 
+void TestQgsRubberband::testClose()
+{
+  QgsRubberBand r( mCanvas, QGis::Polygon );
+
+  // try closing empty rubber band, don't want to crash
+  r.closePoints();
+  QCOMPARE( r.partSize( 0 ), 0 );
+
+  r.addPoint( QgsPoint( 1, 2 ) );
+  r.addPoint( QgsPoint( 1, 3 ) );
+  r.addPoint( QgsPoint( 2, 3 ) );
+  QCOMPARE( r.partSize( 0 ), 3 );
+
+  // test with some bad geometry indexes - don't want to crash!
+  r.closePoints( true, -1 );
+  QCOMPARE( r.partSize( 0 ), 3 );
+  r.closePoints( true, 100 );
+  QCOMPARE( r.partSize( 0 ), 3 );
+
+  // valid close
+  r.closePoints();
+  QCOMPARE( r.partSize( 0 ), 4 );
+
+  // close already closed polygon, should be no change
+  r.closePoints();
+  QCOMPARE( r.partSize( 0 ), 4 );
+}
+
 
 QTEST_MAIN( TestQgsRubberband )
 #include "testqgsrubberband.moc"
diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt
index 4eb1a52..38ab9e9 100644
--- a/tests/src/python/CMakeLists.txt
+++ b/tests/src/python/CMakeLists.txt
@@ -1,4 +1,5 @@
 SET (ENABLE_MSSQLTEST FALSE CACHE BOOL "Enable MsSQL provider tests")
+SET (ENABLE_ORACLETEST FALSE CACHE BOOL "Enable Oracle provider tests")
 
 INCLUDE(UsePythonTest)
 # Run one of the two server tests at the beginning so they don't run in
@@ -14,6 +15,7 @@ ADD_PYTHON_TEST(PyQgsAttributeTableModel test_qgsattributetablemodel.py)
 #ADD_PYTHON_TEST(PyQgsAuthenticationSystem test_qgsauthsystem.py)
 ADD_PYTHON_TEST(PyQgsBlendModes test_qgsblendmodes.py)
 ADD_PYTHON_TEST(PyQgsCategorizedSymbolRendererV2 test_qgscategorizedsymbolrendererv2.py)
+ADD_PYTHON_TEST(PyQgsColorButtonV2 test_qgscolorbuttonv2.py)
 ADD_PYTHON_TEST(PyQgsColorScheme test_qgscolorscheme.py)
 ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py)
 ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py)
@@ -23,6 +25,7 @@ ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)
 ADD_PYTHON_TEST(PyQgsComposerMapGrid test_qgscomposermapgrid.py)
 ADD_PYTHON_TEST(PyQgsComposerPicture test_qgscomposerpicture.py)
 ADD_PYTHON_TEST(PyQgsComposerShapes test_qgscomposershapes.py)
+ADD_PYTHON_TEST(PyQgsComposerView test_qgscomposerview.py)
 ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py)
 ADD_PYTHON_TEST(PyQgsConditionalStyle test_qgsconditionalstyle.py)
 ADD_PYTHON_TEST(PyQgsCoordinateTransform test_qgscoordinatetransform.py)
@@ -56,6 +59,7 @@ ADD_PYTHON_TEST(PyQgsRulebasedRenderer test_qgsrulebasedrenderer.py)
 ADD_PYTHON_TEST(PyQgsSingleSymbolRenderer test_qgssinglesymbolrenderer.py)
 ADD_PYTHON_TEST(PyQgsShapefileProvider test_provider_shapefile.py)
 ADD_PYTHON_TEST(PyQgsTabfileProvider test_provider_tabfile.py)
+ADD_PYTHON_TEST(PyQgsOGRProvider test_provider_ogr.py)
 ADD_PYTHON_TEST(PyQgsSpatialIndex test_qgsspatialindex.py)
 ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_provider_spatialite.py)
 ADD_PYTHON_TEST(PyQgsSymbolLayerV2 test_qgssymbollayerv2.py)
@@ -94,6 +98,10 @@ IF (ENABLE_MSSQLTEST)
   ADD_PYTHON_TEST(PyQgsMssqlProvider test_provider_mssql.py)
 ENDIF (ENABLE_MSSQLTEST)
 
+IF (ENABLE_ORACLETEST)
+  ADD_PYTHON_TEST(PyQgsOracleProvider test_provider_oracle.py)
+ENDIF (ENABLE_ORACLETEST)
+
 IF (WITH_APIDOC)
   ADD_PYTHON_TEST(PyQgsDocCoverage test_qgsdoccoverage.py)
   ADD_PYTHON_TEST(PyQgsSipCoverage test_qgssipcoverage.py)
diff --git a/tests/src/python/providertestbase.py b/tests/src/python/providertestbase.py
index c2ff215..df76136 100644
--- a/tests/src/python/providertestbase.py
+++ b/tests/src/python/providertestbase.py
@@ -33,6 +33,8 @@ class ProviderTestCase(object):
         attributes = {}
         geometries = {}
         while it.nextFeature(f):
+            # expect feature to be valid
+            self.assertTrue(f.isValid())
             # split off the first 5 attributes only - some provider test datasets will include
             # additional attributes which we ignore
             attrs = f.attributes()[0:5]
@@ -62,8 +64,10 @@ class ProviderTestCase(object):
                 self.assertFalse(geometries[pk], 'Expected null geometry for {}'.format(pk))
 
     def assert_query(self, provider, expression, expected):
-        result = set([f['pk'] for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression(expression).setFlags(QgsFeatureRequest.NoGeometry))])
+        request = QgsFeatureRequest().setFilterExpression(expression).setFlags(QgsFeatureRequest.NoGeometry)
+        result = set([f['pk'] for f in provider.getFeatures(request)])
         assert set(expected) == result, 'Expected {} and got {} when testing expression "{}"'.format(set(expected), result, expression)
+        self.assertTrue(all(f.isValid() for f in provider.getFeatures(request)))
 
         # Also check that filter works when referenced fields are not being retrieved by request
         result = set([f['pk'] for f in provider.getFeatures(QgsFeatureRequest().setFilterExpression(expression).setSubsetOfAttributes([0]))])
@@ -180,18 +184,23 @@ class ProviderTestCase(object):
         self.provider.setSubsetString(subset)
         self.assertEqual(self.provider.subsetString(), subset)
         result = set([f['pk'] for f in self.provider.getFeatures()])
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures()))
         self.provider.setSubsetString(None)
 
         expected = set([2, 3, 4])
         assert set(expected) == result, 'Expected {} and got {} when testing subset string {}'.format(set(expected), result, subset)
+        self.assertTrue(all_valid)
 
         # Subset string AND filter rect
         self.provider.setSubsetString(subset)
         extent = QgsRectangle(-70, 70, -60, 75)
-        result = set([f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))])
+        request = QgsFeatureRequest().setFilterRect(extent)
+        result = set([f['pk'] for f in self.provider.getFeatures(request)])
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         self.provider.setSubsetString(None)
         expected = set([2])
         assert set(expected) == result, 'Expected {} and got {} when testing subset string {}'.format(set(expected), result, subset)
+        self.assertTrue(all_valid)
 
         # Subset string AND filter rect, version 2
         self.provider.setSubsetString(subset)
@@ -203,10 +212,13 @@ class ProviderTestCase(object):
 
         # Subset string AND expression
         self.provider.setSubsetString(subset)
-        result = set([f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterExpression('length("name")=5'))])
+        request = QgsFeatureRequest().setFilterExpression('length("name")=5')
+        result = set([f['pk'] for f in self.provider.getFeatures(request)])
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         self.provider.setSubsetString(None)
         expected = set([2, 4])
         assert set(expected) == result, 'Expected {} and got {} when testing subset string {}'.format(set(expected), result, subset)
+        self.assertTrue(all_valid)
 
     def getSubsetString(self):
         """Individual providers may need to override this depending on their subset string formats"""
@@ -309,16 +321,32 @@ class ProviderTestCase(object):
         fids = [f.id() for f in self.provider.getFeatures()]
         assert len(fids) == 5, 'Expected 5 features, got {} instead'.format(len(fids))
         for id in fids:
-            result = [f.id() for f in self.provider.getFeatures(QgsFeatureRequest().setFilterFid(id))]
+            features = [f for f in self.provider.getFeatures(QgsFeatureRequest().setFilterFid(id))]
+            self.assertEqual(len(features), 1)
+            feature = features[0]
+            self.assertTrue(feature.isValid())
+
+            result = [feature.id()]
             expected = [id]
             assert result == expected, 'Expected {} and got {} when testing for feature ID filter'.format(expected, result)
 
+        # bad features
+        it = self.provider.getFeatures(QgsFeatureRequest().setFilterFid(-99999999))
+        feature = QgsFeature(5)
+        feature.setValid(False)
+        self.assertFalse(it.nextFeature(feature))
+        self.assertFalse(feature.isValid())
+
     def testGetFeaturesFidsTests(self):
         fids = [f.id() for f in self.provider.getFeatures()]
+        self.assertEqual(len(fids), 5)
 
-        result = set([f.id() for f in self.provider.getFeatures(QgsFeatureRequest().setFilterFids([fids[0], fids[2]]))])
+        request = QgsFeatureRequest().setFilterFids([fids[0], fids[2]])
+        result = set([f.id() for f in self.provider.getFeatures(request)])
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         expected = set([fids[0], fids[2]])
         assert result == expected, 'Expected {} and got {} when testing for feature IDs filter'.format(expected, result)
+        self.assertTrue(all_valid)
 
         result = set([f.id() for f in self.provider.getFeatures(QgsFeatureRequest().setFilterFids([fids[1], fids[3], fids[4]]))])
         expected = set([fids[1], fids[3], fids[4]])
@@ -330,8 +358,11 @@ class ProviderTestCase(object):
 
     def testGetFeaturesFilterRectTests(self):
         extent = QgsRectangle(-70, 67, -60, 80)
-        features = [f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))]
+        request = QgsFeatureRequest().setFilterRect(extent)
+        features = [f['pk'] for f in self.provider.getFeatures(request)]
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         assert set(features) == set([2, 4]), 'Got {} instead'.format(features)
+        self.assertTrue(all_valid)
 
     def testGetFeaturesPolyFilterRectTests(self):
         """ Test fetching features from a polygon layer with filter rect"""
@@ -342,20 +373,33 @@ class ProviderTestCase(object):
             return
 
         extent = QgsRectangle(-73, 70, -63, 80)
-        features = [f['pk'] for f in self.poly_provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))]
+        request = QgsFeatureRequest().setFilterRect(extent)
+        features = [f['pk'] for f in self.poly_provider.getFeatures(request)]
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         # Some providers may return the exact intersection matches (2, 3) even without the ExactIntersect flag, so we accept that too
         assert set(features) == set([2, 3]) or set(features) == set([1, 2, 3]), 'Got {} instead'.format(features)
+        self.assertTrue(all_valid)
 
         # Test with exact intersection
-        features = [f['pk'] for f in self.poly_provider.getFeatures(QgsFeatureRequest().setFilterRect(extent).setFlags(QgsFeatureRequest.ExactIntersect))]
+        request = QgsFeatureRequest().setFilterRect(extent).setFlags(QgsFeatureRequest.ExactIntersect)
+        features = [f['pk'] for f in self.poly_provider.getFeatures(request)]
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         assert set(features) == set([2, 3]), 'Got {} instead'.format(features)
+        self.assertTrue(all_valid)
+
+        # test with an empty rectangle
+        extent = QgsRectangle()
+        features = [f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))]
+        assert set(features) == set([1, 2, 3, 4, 5]), 'Got {} instead'.format(features)
 
     def testRectAndExpression(self):
         extent = QgsRectangle(-70, 67, -60, 80)
-        result = set([f['pk'] for f in self.provider.getFeatures(
-            QgsFeatureRequest().setFilterExpression('"cnt">200').setFilterRect(extent))])
+        request = QgsFeatureRequest().setFilterExpression('"cnt">200').setFilterRect(extent)
+        result = set([f['pk'] for f in self.provider.getFeatures(request)])
+        all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
         expected = [4]
         assert set(expected) == result, 'Expected {} and got {} when testing for combination of filterRect and expression'.format(set(expected), result)
+        self.assertTrue(all_valid)
 
     def testGetFeaturesLimit(self):
         it = self.provider.getFeatures(QgsFeatureRequest().setLimit(2))
@@ -465,6 +509,8 @@ class ProviderTestCase(object):
                  'cnt': set([-200, 300, 100, 200, 400]),
                  'name': set(['Pear', 'Orange', 'Apple', 'Honey', NULL]),
                  'name2': set(['NuLl', 'PEaR', 'oranGe', 'Apple', 'Honey'])}
-        for field, expected in tests.iteritems():
-            result = set([f[field] for f in self.provider.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field], self.provider.fields()))])
+        for field, expected in tests.items():
+            request = QgsFeatureRequest().setSubsetOfAttributes([field], self.provider.fields())
+            result = set([f[field] for f in self.provider.getFeatures(request)])
+            all_valid = (all(f.isValid() for f in self.provider.getFeatures(request)))
             self.assertEqual(result, expected, 'Expected {}, got {}'.format(expected, result))
diff --git a/tests/src/python/test_provider_ogr.py b/tests/src/python/test_provider_ogr.py
new file mode 100644
index 0000000..9a0e824
--- /dev/null
+++ b/tests/src/python/test_provider_ogr.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for the non-shapefile, non-tabfile datasources handled by OGR provider.
+
+.. 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__ = 'Even Rouault'
+__date__ = '2016-04-11'
+__copyright__ = 'Copyright 2016, Even Rouault'
+# This will get replaced with a git SHA1 when you do a git archive
+__revision__ = '$Format:%H$'
+
+import os
+import shutil
+import sys
+import tempfile
+
+from qgis.core import QgsVectorLayer, QgsVectorDataProvider, QgsWKBTypes
+from qgis.testing import (
+    start_app,
+    unittest
+)
+from utilities import unitTestDataPath
+
+start_app()
+TEST_DATA_DIR = unitTestDataPath()
+
+# Note - doesn't implement ProviderTestCase as most OGR provider is tested by the shapefile provider test
+
+
+def count_opened_filedescriptors(filename_to_test):
+    count = -1
+    if sys.platform.startswith('linux'):
+        count = 0
+        open_files_dirname = '/proc/%d/fd' % os.getpid()
+        filenames = os.listdir(open_files_dirname)
+        for filename in filenames:
+            full_filename = open_files_dirname + '/' + filename
+            if os.path.exists(full_filename):
+                link = os.readlink(full_filename)
+                if os.path.basename(link) == os.path.basename(filename_to_test):
+                    count += 1
+    return count
+
+
+class PyQgsOGRProvider(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        """Run before all tests"""
+        # Create test layer
+        cls.basetestpath = tempfile.mkdtemp()
+        cls.datasource = os.path.join(cls.basetestpath, 'test.csv')
+        with open(cls.datasource, 'wt') as f:
+            f.write('id,WKT\n')
+            f.write('1,POINT(2 49)\n')
+
+        cls.dirs_to_cleanup = [cls.basetestpath]
+
+    @classmethod
+    def tearDownClass(cls):
+        """Run after all tests"""
+        for dirname in cls.dirs_to_cleanup:
+            shutil.rmtree(dirname, True)
+
+    def testUpdateMode(self):
+
+        vl = QgsVectorLayer(u'{}|layerid=0'.format(self.datasource), u'test', u'ogr')
+        self.assertTrue(vl.isValid())
+        caps = vl.dataProvider().capabilities()
+        self.assertTrue(caps & QgsVectorDataProvider.AddFeatures)
+
+        self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        # No-op
+        self.assertTrue(vl.dataProvider().enterUpdateMode())
+        self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        # No-op
+        self.assertTrue(vl.dataProvider().leaveUpdateMode())
+        self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+    def testNoDanglingFileDescriptorAfterCloseVariant1(self):
+        ''' Test that when closing the provider all file handles are released '''
+
+        datasource = os.path.join(self.basetestpath, 'testNoDanglingFileDescriptorAfterCloseVariant1.csv')
+        with open(datasource, 'wt') as f:
+            f.write('id,WKT\n')
+            f.write('1,\n')
+            f.write('2,POINT(2 49)\n')
+
+        vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl.isValid())
+        # The iterator will take one extra connection
+        myiter = vl.getFeatures()
+        # Consume one feature but the iterator is still opened
+        f = next(myiter)
+        self.assertTrue(f.isValid())
+
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(datasource), 2)
+
+        # Should release one file descriptor
+        del vl
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(datasource), 1)
+
+        f = next(myiter)
+        self.assertTrue(f.isValid())
+
+        # Should release one file descriptor
+        del myiter
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(datasource), 0)
+
+        # Check that deletion works well (can only fail on Windows)
+        os.unlink(datasource)
+        self.assertFalse(os.path.exists(datasource))
+
+    def testNoDanglingFileDescriptorAfterCloseVariant2(self):
+        ''' Test that when closing the provider all file handles are released '''
+
+        datasource = os.path.join(self.basetestpath, 'testNoDanglingFileDescriptorAfterCloseVariant2.csv')
+        with open(datasource, 'wt') as f:
+            f.write('id,WKT\n')
+            f.write('1,\n')
+            f.write('2,POINT(2 49)\n')
+
+        vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl.isValid())
+        # Consume all features.
+        myiter = vl.getFeatures()
+        for feature in myiter:
+            pass
+        # The iterator is closed, but the corresponding connection still not closed
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(datasource), 2)
+
+        # Should release one file descriptor
+        del vl
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(datasource), 0)
+
+        # Check that deletion works well (can only fail on Windows)
+        os.unlink(datasource)
+        self.assertFalse(os.path.exists(datasource))
+
+    def testGpxElevation(self):
+        # GPX without elevation data
+        datasource = os.path.join(TEST_DATA_DIR, 'noelev.gpx')
+        vl = QgsVectorLayer(u'{}|layername=routes'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl.isValid())
+        f = next(vl.getFeatures())
+        self.assertEqual(f.constGeometry().geometry().wkbType(), QgsWKBTypes.LineString)
+
+        # GPX with elevation data
+        datasource = os.path.join(TEST_DATA_DIR, 'elev.gpx')
+        vl = QgsVectorLayer(u'{}|layername=routes'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl.isValid())
+        f = next(vl.getFeatures())
+        self.assertEqual(f.constGeometry().geometry().wkbType(), QgsWKBTypes.LineString25D)
+        self.assertEqual(f.constGeometry().geometry().pointN(0).z(), 1)
+        self.assertEqual(f.constGeometry().geometry().pointN(1).z(), 2)
+        self.assertEqual(f.constGeometry().geometry().pointN(2).z(), 3)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/src/python/test_provider_oracle.py b/tests/src/python/test_provider_oracle.py
new file mode 100644
index 0000000..12ce1d8
--- /dev/null
+++ b/tests/src/python/test_provider_oracle.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for the Oracle provider.
+
+.. 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__ = '2016-07-06'
+__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
+
+import os
+
+from qgis.core import QgsVectorLayer, QgsFeatureRequest, NULL
+
+from qgis.PyQt.QtCore import QSettings, QDate, QTime, QDateTime, QVariant
+
+from utilities import unitTestDataPath
+from qgis.testing import start_app, unittest
+from providertestbase import ProviderTestCase
+
+start_app()
+TEST_DATA_DIR = unitTestDataPath()
+
+
+class TestPyQgsOracleProvider(unittest.TestCase, ProviderTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        """Run before all tests"""
+        cls.dbconn = u"host=localhost port=1521 user='QGIS' password='qgis'"
+        if 'QGIS_ORACLETEST_DB' in os.environ:
+            cls.dbconn = os.environ['QGIS_ORACLETEST_DB']
+        # Create test layers
+        cls.vl = QgsVectorLayer(
+            cls.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POINT table="QGIS"."SOME_DATA" (GEOM) sql=', 'test', 'oracle')
+        assert(cls.vl.isValid())
+        cls.provider = cls.vl.dataProvider()
+        cls.poly_vl = QgsVectorLayer(
+            cls.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="QGIS"."SOME_POLY_DATA" (GEOM) sql=', 'test', 'oracle')
+        assert(cls.poly_vl.isValid())
+        cls.poly_provider = cls.poly_vl.dataProvider()
+
+    @classmethod
+    def tearDownClass(cls):
+        """Run after all tests"""
+
+    def enableCompiler(self):
+        QSettings().setValue(u'/qgis/compileExpressions', True)
+
+    def disableCompiler(self):
+        QSettings().setValue(u'/qgis/compileExpressions', False)
+
+    def uncompiledFilters(self):
+        filters = set([
+            '(name = \'Apple\') is not null',
+            '"name" || \' \' || "name" = \'Orange Orange\'',
+            '"name" || \' \' || "cnt" = \'Orange 100\'',
+            '\'x\' || "name" IS NOT NULL',
+            '\'x\' || "name" IS NULL',
+            'false and NULL',
+            'true and NULL',
+            'NULL and false',
+            'NULL and true',
+            'NULL and NULL',
+            'false or NULL',
+            'true or NULL',
+            'NULL or false',
+            'NULL or true',
+            'NULL or NULL',
+            'not null',
+            'intersects($geometry,geom_from_wkt( \'Polygon ((-72.2 66.1, -65.2 66.1, -65.2 72.0, -72.2 72.0, -72.2 66.1))\'))'])
+        return filters
+
+    # HERE GO THE PROVIDER SPECIFIC TESTS
+    def testDateTimeTypes(self):
+        vl = QgsVectorLayer('%s table="QGIS"."DATE_TIMES" sql=' %
+                            (self.dbconn), "testdatetimes", "oracle")
+        self.assertTrue(vl.isValid())
+
+        fields = vl.dataProvider().fields()
+        self.assertEqual(fields.at(fields.indexFromName(
+            'date_field')).type(), QVariant.Date)
+        self.assertEqual(fields.at(fields.indexFromName(
+            'datetime_field')).type(), QVariant.DateTime)
+
+        f = next(vl.getFeatures(QgsFeatureRequest()))
+
+        date_idx = vl.fieldNameIndex('date_field')
+        self.assertTrue(isinstance(f.attributes()[date_idx], QDate))
+        self.assertEqual(f.attributes()[date_idx], QDate(2004, 3, 4))
+        datetime_idx = vl.fieldNameIndex('datetime_field')
+        self.assertTrue(isinstance(f.attributes()[datetime_idx], QDateTime))
+        self.assertEqual(f.attributes()[datetime_idx], QDateTime(
+            QDate(2004, 3, 4), QTime(13, 41, 52)))
+
+    def testDefaultValue(self):
+        self.assertEqual(self.provider.defaultValue(1), NULL)
+        self.assertEqual(self.provider.defaultValue(2), "'qgis'")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/src/python/test_provider_shapefile.py b/tests/src/python/test_provider_shapefile.py
index d198941..31577de 100644
--- a/tests/src/python/test_provider_shapefile.py
+++ b/tests/src/python/test_provider_shapefile.py
@@ -12,20 +12,25 @@ __copyright__ = 'Copyright 2015, The QGIS Project'
 # This will get replaced with a git SHA1 when you do a git archive
 __revision__ = '$Format:%H$'
 
-import qgis
 import os
 import tempfile
 import shutil
 import glob
+import osgeo.gdal
+import sys
 
-from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
-from PyQt4.QtCore import QSettings
-from qgis.testing import (start_app,
-                          unittest
-                          )
+from qgis.core import QgsFeature, QgsField, QgsGeometry, QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
+from PyQt4.QtCore import QSettings, QVariant
+from qgis.testing import start_app, unittest
 from utilities import unitTestDataPath
 from providertestbase import ProviderTestCase
 
+try:
+    from osgeo import gdal
+    gdal_ok = True
+except:
+    gdal_ok = False
+
 start_app()
 TEST_DATA_DIR = unitTestDataPath()
 
@@ -55,11 +60,13 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase):
         assert (cls.vl_poly.isValid())
         cls.poly_provider = cls.vl_poly.dataProvider()
 
+        cls.dirs_to_cleanup = [cls.basetestpath, cls.repackfilepath]
+
     @classmethod
     def tearDownClass(cls):
         """Run after all tests"""
-        shutil.rmtree(cls.basetestpath, True)
-        shutil.rmtree(cls.repackfilepath, True)
+        for dirname in cls.dirs_to_cleanup:
+            shutil.rmtree(dirname, True)
 
     def enableCompiler(self):
         QSettings().setValue(u'/qgis/compileExpressions', True)
@@ -79,6 +86,181 @@ class TestPyQgsShapefileProvider(unittest.TestCase, ProviderTestCase):
         assert vl.commitChanges()
         assert vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]['pk'] == 1
 
+    def testUpdateMode(self):
+        """ Test that on-the-fly re-opening in update/read-only mode works """
+
+        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(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        caps = vl.dataProvider().capabilities()
+        self.assertTrue(caps & QgsVectorDataProvider.AddFeatures)
+        self.assertTrue(caps & QgsVectorDataProvider.DeleteFeatures)
+        self.assertTrue(caps & QgsVectorDataProvider.ChangeAttributeValues)
+        self.assertTrue(caps & QgsVectorDataProvider.AddAttributes)
+        self.assertTrue(caps & QgsVectorDataProvider.DeleteAttributes)
+        self.assertTrue(caps & QgsVectorDataProvider.CreateSpatialIndex)
+        self.assertTrue(caps & QgsVectorDataProvider.SelectAtId)
+        self.assertTrue(caps & QgsVectorDataProvider.ChangeGeometries)
+        #self.assertTrue(caps & QgsVectorDataProvider.ChangeFeatures)
+
+        # We should be really opened in read-only mode even if write capabilities are declared
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
+
+        # Unbalanced call to leaveUpdateMode()
+        self.assertFalse(vl.dataProvider().leaveUpdateMode())
+
+        # Test that startEditing() / commitChanges() plays with enterUpdateMode() / leaveUpdateMode()
+        self.assertTrue(vl.startEditing())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+        self.assertTrue(vl.dataProvider().isValid())
+
+        self.assertTrue(vl.commitChanges())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
+        self.assertTrue(vl.dataProvider().isValid())
+
+        # Manual enterUpdateMode() / leaveUpdateMode() with 2 depths
+        self.assertTrue(vl.dataProvider().enterUpdateMode())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+        caps = vl.dataProvider().capabilities()
+        self.assertTrue(caps & QgsVectorDataProvider.AddFeatures)
+
+        f = QgsFeature()
+        f.setAttributes([200])
+        f.setGeometry(QgsGeometry.fromWkt('Point (2 49)'))
+        (ret, feature_list) = vl.dataProvider().addFeatures([f])
+        self.assertTrue(ret)
+        fid = feature_list[0].id()
+
+        features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))]
+        values = [f_iter['pk'] for f_iter in features]
+        self.assertEquals(values, [200])
+
+        got_geom = [f_iter.geometry() for f_iter in features][0].geometry()
+        self.assertEquals((got_geom.x(), got_geom.y()), (2.0, 49.0))
+
+        self.assertTrue(vl.dataProvider().changeGeometryValues({fid: QgsGeometry.fromWkt('Point (3 50)')}))
+        self.assertTrue(vl.dataProvider().changeAttributeValues({fid: {0: 100}}))
+
+        features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))]
+        values = [f_iter['pk'] for f_iter in features]
+
+        got_geom = [f_iter.geometry() for f_iter in features][0].geometry()
+        self.assertEquals((got_geom.x(), got_geom.y()), (3.0, 50.0))
+
+        self.assertTrue(vl.dataProvider().deleteFeatures([fid]))
+
+        # Check that it has really disappeared
+        if gdal_ok:
+            gdal.PushErrorHandler('CPLQuietErrorHandler')
+        features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))]
+        if gdal_ok:
+            gdal.PopErrorHandler()
+        self.assertEquals(features, [])
+
+        self.assertTrue(vl.dataProvider().addAttributes([QgsField("new_field", QVariant.Int, "integer")]))
+        self.assertTrue(vl.dataProvider().deleteAttributes([len(vl.dataProvider().fields()) - 1]))
+
+        self.assertTrue(vl.startEditing())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        self.assertTrue(vl.commitChanges())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        self.assertTrue(vl.dataProvider().enterUpdateMode())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        self.assertTrue(vl.dataProvider().leaveUpdateMode())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+        self.assertTrue(vl.dataProvider().leaveUpdateMode())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
+
+        # Test that update mode will be implictly enabled if doing an action
+        # that requires update mode
+        (ret, _) = vl.dataProvider().addFeatures([QgsFeature()])
+        self.assertTrue(ret)
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+
+    def testUpdateModeFailedReopening(self):
+        ''' Test that methods on provider don't crash after a failed reopening '''
+
+        # Windows doesn't like removing files opened by OGR, whatever
+        # their open mode, so that makes it hard to test
+        if sys.platform == 'win32':
+            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(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+
+        os.unlink(datasource)
+
+        self.assertFalse(vl.dataProvider().enterUpdateMode())
+        self.assertFalse(vl.dataProvider().enterUpdateMode())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "invalid")
+
+        self.assertFalse(vl.dataProvider().isValid())
+        self.assertEquals(len([f for f in vl.dataProvider().getFeatures()]), 0)
+        self.assertEquals(len(vl.dataProvider().subLayers()), 0)
+        self.assertFalse(vl.dataProvider().setSubsetString('TRUE'))
+        (ret, _) = vl.dataProvider().addFeatures([QgsFeature()])
+        self.assertFalse(ret)
+        self.assertFalse(vl.dataProvider().deleteFeatures([1]))
+        self.assertFalse(vl.dataProvider().addAttributes([QgsField()]))
+        self.assertFalse(vl.dataProvider().deleteAttributes([1]))
+        self.assertFalse(vl.dataProvider().changeGeometryValues({0: QgsGeometry.fromWkt('Point (3 50)')}))
+        self.assertFalse(vl.dataProvider().changeAttributeValues({0: {0: 0}}))
+        self.assertFalse(vl.dataProvider().createSpatialIndex())
+        self.assertFalse(vl.dataProvider().createAttributeIndex(0))
+
+    def testreloadData(self):
+        ''' Test reloadData() '''
+
+        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')
+
+        vl1 = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        vl2 = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl1.startEditing())
+        self.assertTrue(vl1.deleteAttributes([1]))
+        self.assertTrue(vl1.commitChanges())
+        self.assertEquals(len(vl1.fields()) + 1, len(vl2.fields()))
+        # Reload
+        vl2.reload()
+        # And now check that fields are up-to-date
+        self.assertEquals(len(vl1.fields()), len(vl2.fields()))
+
+    def testDeleteGeometry(self):
+        ''' Test changeGeometryValues() with a null geometry '''
+
+        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(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        self.assertTrue(vl.dataProvider().changeGeometryValues({0: QgsGeometry()}))
+        vl = None
+
+        vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
+        fet = next(vl.getFeatures())
+        self.assertIsNone(fet.geometry())
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/src/python/test_provider_spatialite.py b/tests/src/python/test_provider_spatialite.py
index ef7ca26..21dd102 100644
--- a/tests/src/python/test_provider_spatialite.py
+++ b/tests/src/python/test_provider_spatialite.py
@@ -15,6 +15,7 @@ __revision__ = '$Format:%H$'
 import qgis
 import os
 import shutil
+import sys
 import tempfile
 
 from qgis.core import QgsVectorLayer, QgsPoint, QgsFeature
@@ -41,6 +42,21 @@ def die(error_message):
     raise Exception(error_message)
 
 
+def count_opened_filedescriptors(filename_to_test):
+    count = -1
+    if sys.platform.startswith('linux'):
+        count = 0
+        open_files_dirname = '/proc/%d/fd' % os.getpid()
+        filenames = os.listdir(open_files_dirname)
+        for filename in filenames:
+            full_filename = open_files_dirname + '/' + filename
+            if os.path.exists(full_filename):
+                link = os.readlink(full_filename)
+                if os.path.basename(link) == os.path.basename(filename_to_test):
+                    count += 1
+    return count
+
+
 class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase):
 
     @classmethod
@@ -214,5 +230,73 @@ class TestQgsSpatialiteProvider(unittest.TestCase, ProviderTestCase):
         layer = None
         os.unlink(corrupt_dbname)
 
+    def testNoDanglingFileDescriptorAfterCloseVariant1(self):
+        ''' Test that when closing the provider all file handles are released '''
+
+        temp_dbname = self.dbname + '.no_dangling_test1'
+        shutil.copy(self.dbname, temp_dbname)
+
+        vl = QgsVectorLayer("dbname=%s table=test_n (geometry)" % temp_dbname, "test_n", "spatialite")
+        self.assertTrue(vl.isValid())
+        # The iterator will take one extra connection
+        myiter = vl.getFeatures()
+        print(vl.featureCount())
+        # Consume one feature but the iterator is still opened
+        f = next(myiter)
+        self.assertTrue(f.isValid())
+
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(temp_dbname), 2)
+
+        # does NO release one file descriptor, because shared with the iterator
+        del vl
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(temp_dbname), 2)
+
+        f = next(myiter)
+        self.assertTrue(f.isValid())
+
+        # Should release one file descriptor
+        del myiter
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(temp_dbname), 0)
+
+        # Check that deletion works well (can only fail on Windows)
+        os.unlink(temp_dbname)
+        self.assertFalse(os.path.exists(temp_dbname))
+
+    def testNoDanglingFileDescriptorAfterCloseVariant2(self):
+        ''' Test that when closing the provider all file handles are released '''
+
+        temp_dbname = self.dbname + '.no_dangling_test2'
+        shutil.copy(self.dbname, temp_dbname)
+
+        vl = QgsVectorLayer("dbname=%s table=test_n (geometry)" % temp_dbname, "test_n", "spatialite")
+        self.assertTrue(vl.isValid())
+        self.assertTrue(vl.isValid())
+        # Consume all features.
+        myiter = vl.getFeatures()
+        for feature in myiter:
+            pass
+        # The iterator is closed
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(temp_dbname), 2)
+
+        # Should release one file descriptor
+        del vl
+
+        # Non portable, but Windows testing is done with trying to unlink
+        if sys.platform.startswith('linux'):
+            self.assertEqual(count_opened_filedescriptors(temp_dbname), 0)
+
+        # Check that deletion works well (can only fail on Windows)
+        os.unlink(temp_dbname)
+        self.assertFalse(os.path.exists(temp_dbname))
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/src/python/test_provider_tabfile.py b/tests/src/python/test_provider_tabfile.py
index a6ff204..365c4ed 100644
--- a/tests/src/python/test_provider_tabfile.py
+++ b/tests/src/python/test_provider_tabfile.py
@@ -17,12 +17,13 @@ import tempfile
 import shutil
 import glob
 
-from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
+from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
 from PyQt4.QtCore import QSettings, QDate, QTime, QDateTime, QVariant
 from qgis.testing import (
     start_app,
     unittest
 )
+import osgeo.gdal
 from utilities import unitTestDataPath
 
 start_app()
@@ -55,5 +56,30 @@ class TestPyQgsTabfileProvider(unittest.TestCase):
         assert isinstance(f.attributes()[datetime_idx], QDateTime)
         self.assertEqual(f.attributes()[datetime_idx], QDateTime(QDate(2004, 5, 3), QTime(13, 41, 00)))
 
+    # This test fails with GDAL version < 2 because tab update is new in GDAL 2.0
+    # @unittest.expectedFailure(int(osgeo.gdal.VersionInfo()[:1]) < 2)
+    def testUpdateMode(self):
+        """ Test that on-the-fly re-opening in update/read-only mode works """
+
+        if int(osgeo.gdal.VersionInfo()[:1]) < 2:
+            return
+
+        basetestfile = os.path.join(TEST_DATA_DIR, 'tab_file.tab')
+        vl = QgsVectorLayer(u'{}|layerid=0'.format(basetestfile), u'test', u'ogr')
+        caps = vl.dataProvider().capabilities()
+        self.assertTrue(caps & QgsVectorDataProvider.AddFeatures)
+
+        # We should be really opened in read-only mode even if write capabilities are declared
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
+
+        # Test that startEditing() / commitChanges() plays with enterUpdateMode() / leaveUpdateMode()
+        self.assertTrue(vl.startEditing())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-write")
+        self.assertTrue(vl.dataProvider().isValid())
+
+        self.assertTrue(vl.commitChanges())
+        self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
+        self.assertTrue(vl.dataProvider().isValid())
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/src/python/test_qgscolorbuttonv2.py b/tests/src/python/test_qgscolorbuttonv2.py
new file mode 100644
index 0000000..595c6e5
--- /dev/null
+++ b/tests/src/python/test_qgscolorbuttonv2.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for QgsColorButtonV2.
+
+.. 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__ = '25/05/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.gui import QgsColorButtonV2
+from qgis.testing import start_app, unittest
+from qgis.PyQt.QtGui import QColor
+start_app()
+
+
+class TestQgsColorButtonV2(unittest.TestCase):
+
+    def testClearingColors(self):
+        """
+        Test setting colors to transparent
+        """
+
+        # start with a valid color
+        button = QgsColorButtonV2()
+        button.setAllowAlpha(True)
+        button.setColor(QColor(255, 100, 200, 255))
+        self.assertEqual(button.color(), QColor(255, 100, 200, 255))
+
+        # now set to no color
+        button.setToNoColor()
+        # ensure that only the alpha channel has changed - not the other color components
+        self.assertEqual(button.color(), QColor(255, 100, 200, 0))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/src/python/test_qgscomposerview.py b/tests/src/python/test_qgscomposerview.py
new file mode 100644
index 0000000..05b8807
--- /dev/null
+++ b/tests/src/python/test_qgscomposerview.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for QgsComposerView.
+
+.. 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__ = '29/05/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
+
+import os
+
+from qgis.gui import (QgsComposerView)
+from qgis.PyQt.QtCore import QRectF
+from qgis.PyQt.QtGui import QTransform
+
+from qgis.testing import start_app, unittest
+
+start_app()
+
+
+class TestQgsComposerView(unittest.TestCase):
+
+    def testScaleSafe(self):
+        """ test scaleSafe method """
+
+        view = QgsComposerView()
+        view.fitInView(QRectF(0, 0, 10, 10))
+        scale = view.transform().m11()
+        view.scaleSafe(2)
+        self.assertAlmostEqual(view.transform().m11(), 2)
+        view.scaleSafe(4)
+        self.assertAlmostEqual(view.transform().m11(), 8)
+
+        # try to zoom in heaps
+        view.scaleSafe(99999999)
+        # assume we have hit the limit
+        scale = view.transform().m11()
+        view.scaleSafe(2)
+        self.assertAlmostEqual(view.transform().m11(), scale)
+
+        view.setTransform(QTransform.fromScale(1, 1))
+        self.assertAlmostEqual(view.transform().m11(), 1)
+        # test zooming out
+        view.scaleSafe(0.5)
+        self.assertAlmostEqual(view.transform().m11(), 0.5)
+        view.scaleSafe(0.1)
+        self.assertAlmostEqual(view.transform().m11(), 0.05)
+
+        # try zooming out heaps
+        view.scaleSafe(0.000000001)
+        # assume we have hit the limit
+        scale = view.transform().m11()
+        view.scaleSafe(0.5)
+        self.assertAlmostEqual(view.transform().m11(), scale)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/src/python/test_qgsexpression.py b/tests/src/python/test_qgsexpression.py
index e52463a..bee50ab 100644
--- a/tests/src/python/test_qgsexpression.py
+++ b/tests/src/python/test_qgsexpression.py
@@ -15,7 +15,7 @@ __revision__ = '$Format:%H$'
 import qgis
 from qgis.testing import unittest
 from qgis.utils import qgsfunction
-from qgis.core import QgsExpression
+from qgis.core import QgsExpression, QgsFeatureRequest
 
 
 class TestQgsExpressionCustomFunctions(unittest.TestCase):
@@ -45,6 +45,14 @@ class TestQgsExpressionCustomFunctions(unittest.TestCase):
     def geomtest(values, feature, parent):
         pass
 
+    @qgsfunction(args=0, group='testing', register=False)
+    def no_referenced_columns_set(values, feature, parent):
+        return 1
+
+    @qgsfunction(args=0, group='testing', register=False, referenced_columns=['a', 'b'])
+    def referenced_columns_set(values, feature, parent):
+        return 2
+
     def tearDown(self):
         QgsExpression.unregisterFunction('testfun')
 
@@ -117,6 +125,16 @@ class TestQgsExpressionCustomFunctions(unittest.TestCase):
         success = QgsExpression.registerFunction(self.geomtest)
         self.assertTrue(success)
 
+    def testReferencedColumnsNoSet(self):
+        success = QgsExpression.registerFunction(self.no_referenced_columns_set)
+        exp = QgsExpression('no_referenced_columns_set()')
+        self.assertEqual(exp.referencedColumns(), [QgsFeatureRequest.AllAttributes])
+
+    def testReferencedColumnsSet(self):
+        success = QgsExpression.registerFunction(self.referenced_columns_set)
+        exp = QgsExpression('referenced_columns_set()')
+        self.assertEqual(exp.referencedColumns(), ['a', 'b'])
+
     def testCantOverrideBuiltinsWithUnregister(self):
         success = QgsExpression.unregisterFunction("sqrt")
         self.assertFalse(success)
diff --git a/tests/src/python/test_qgsfeatureiterator.py b/tests/src/python/test_qgsfeatureiterator.py
index 2e6da68..526157b 100644
--- a/tests/src/python/test_qgsfeatureiterator.py
+++ b/tests/src/python/test_qgsfeatureiterator.py
@@ -15,10 +15,11 @@ __revision__ = '$Format:%H$'
 import qgis
 import os
 
-from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature
+from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsField, NULL
 from qgis.testing import (start_app,
                           unittest
                           )
+from PyQt4.QtCore import QVariant
 from utilities import (unitTestDataPath,
                        compareWkt
                        )
@@ -111,5 +112,52 @@ class TestQgsFeatureIterator(unittest.TestCase):
         feat['Staff'] = 2
         vl.addFeature(feat)
 
+    def test_ExpressionFieldNested(self):
+        myShpFile = os.path.join(TEST_DATA_DIR, 'points.shp')
+        layer = QgsVectorLayer(myShpFile, 'Points', 'ogr')
+        self.assertTrue(layer.isValid())
+
+        cnt = layer.pendingFields().count()
+        idx = layer.addExpressionField('"Staff"*2', QgsField('exp1', QVariant.LongLong))
+        idx = layer.addExpressionField('"exp1"-1', QgsField('exp2', QVariant.LongLong))
+
+        fet = next(layer.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['exp2'], layer.fields())))
+        self.assertEqual(fet['Class'], NULL)
+        # nested virtual fields should make all these attributes be fetched
+        self.assertEqual(fet['Staff'], 2)
+        self.assertEqual(fet['exp2'], 3)
+        self.assertEqual(fet['exp1'], 4)
+
+    def test_ExpressionFieldNestedGeometry(self):
+        myShpFile = os.path.join(TEST_DATA_DIR, 'points.shp')
+        layer = QgsVectorLayer(myShpFile, 'Points', 'ogr')
+        self.assertTrue(layer.isValid())
+
+        cnt = layer.pendingFields().count()
+        idx = layer.addExpressionField('$x*2', QgsField('exp1', QVariant.LongLong))
+        idx = layer.addExpressionField('"exp1"/1.5', QgsField('exp2', QVariant.LongLong))
+
+        fet = next(layer.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes(['exp2'], layer.fields())))
+        # nested virtual fields should have made geometry be fetched
+        self.assertEqual(fet['exp2'], -156)
+        self.assertEqual(fet['exp1'], -234)
+
+    def test_ExpressionFieldNestedCircular(self):
+        """ test circular virtual field definitions """
+
+        myShpFile = os.path.join(TEST_DATA_DIR, 'points.shp')
+        layer = QgsVectorLayer(myShpFile, 'Points', 'ogr')
+        self.assertTrue(layer.isValid())
+
+        cnt = layer.pendingFields().count()
+        idx = layer.addExpressionField('"exp3"*2', QgsField('exp1', QVariant.LongLong))
+        idx = layer.addExpressionField('"exp1"-1', QgsField('exp2', QVariant.LongLong))
+        idx = layer.addExpressionField('"exp2"*3', QgsField('exp3', QVariant.LongLong))
+
+        # really just testing that this doesn't hang/crash... there's no good result here!
+        fet = next(layer.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['exp2'], layer.fields())))
+        self.assertEqual(fet['Class'], NULL)
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/src/python/test_qgssymbollayerv2.py b/tests/src/python/test_qgssymbollayerv2.py
index bc96748..088247b 100644
--- a/tests/src/python/test_qgssymbollayerv2.py
+++ b/tests/src/python/test_qgssymbollayerv2.py
@@ -730,5 +730,19 @@ class TestQgsSymbolLayerV2(unittest.TestCase):
         mMessage = 'Expected "%s" got "%s"' % (mExpectedValue, mValue)
         assert mExpectedValue == mValue, mMessage
 
+    def testQgsVectorFieldSymbolLayer(self):
+        """
+        Test QgsVectorFieldSymbolLayer
+        """
+        # test colors, need to make sure colors are passed/retrieved from subsymbol
+        mSymbolLayer = QgsVectorFieldSymbolLayer.create()
+
+        mSymbolLayer.setColor(QColor(150, 50, 100))
+        self.assertEqual(mSymbolLayer.color(), QColor(150, 50, 100))
+        self.assertEqual(mSymbolLayer.subSymbol().color(), QColor(150, 50, 100))
+        mSymbolLayer.subSymbol().setColor(QColor(250, 150, 200))
+        self.assertEqual(mSymbolLayer.subSymbol().color(), QColor(250, 150, 200))
+        self.assertEqual(mSymbolLayer.color(), QColor(250, 150, 200))
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py
index 6849901..97ce5a2 100644
--- a/tests/src/python/test_qgsvectorlayer.py
+++ b/tests/src/python/test_qgsvectorlayer.py
@@ -58,6 +58,21 @@ def createLayerWithOnePoint():
     return layer
 
 
+def createLayerWithTwoPoints():
+    layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
+                           "addfeat", "memory")
+    pr = layer.dataProvider()
+    f = QgsFeature()
+    f.setAttributes(["test", 123])
+    f.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
+    f2 = QgsFeature()
+    f2.setAttributes(["test2", 457])
+    f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
+    assert pr.addFeatures([f, f2])
+    assert layer.pendingFeatureCount() == 2
+    return layer
+
+
 def createJoinLayer():
     joinLayer = QgsVectorLayer(
         "Point?field=x:string&field=y:integer&field=z:integer",
@@ -69,8 +84,14 @@ def createJoinLayer():
     f2 = QgsFeature()
     f2.setAttributes(["bar", 456, 654])
     f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
-    assert pr.addFeatures([f1, f2])
-    assert joinLayer.pendingFeatureCount() == 2
+    f3 = QgsFeature()
+    f3.setAttributes(["qar", 457, 111])
+    f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
+    f4 = QgsFeature()
+    f4.setAttributes(["a", 458, 19])
+    f4.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
+    assert pr.addFeatures([f1, f2, f3, f4])
+    assert joinLayer.pendingFeatureCount() == 4
     return joinLayer
 
 
@@ -900,6 +921,24 @@ class TestQgsVectorLayer(unittest.TestCase):
         assert f2[2] == "foo"
         assert f2[3] == 321
 
+    def test_JoinStats(self):
+        """ test calculating min/max/uniqueValues on joined field """
+        joinLayer = createJoinLayer()
+        layer = createLayerWithTwoPoints()
+        QgsMapLayerRegistry.instance().addMapLayers([joinLayer, layer])
+
+        join = QgsVectorJoinInfo()
+        join.targetFieldName = "fldint"
+        join.joinLayerId = joinLayer.id()
+        join.joinFieldName = "y"
+        join.memoryCache = True
+        layer.addJoin(join)
+
+        # stats on joined fields should only include values present by join
+        self.assertEqual(layer.minimumValue(3), 111)
+        self.assertEqual(layer.maximumValue(3), 321)
+        self.assertEqual(set(layer.uniqueValues(3)), set([111, 321]))
+
     def test_InvalidOperations(self):
         layer = createLayerWithOnePoint()
 
diff --git a/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644.png b/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644.png
new file mode 100644
index 0000000..ff586a9
Binary files /dev/null and b/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644.png differ
diff --git a/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644_mask.png b/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644_mask.png
new file mode 100644
index 0000000..cf8d78c
Binary files /dev/null and b/tests/testdata/control_images/composer_picture/expected_composerpicture_issue_14644/expected_composerpicture_issue_14644_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_basic/expected_legend_basic_mask.png b/tests/testdata/control_images/legend/expected_legend_basic/expected_legend_basic_mask.png
index 52f5ef1..9599faa 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_basic/expected_legend_basic_mask.png and b/tests/testdata/control_images/legend/expected_legend_basic/expected_legend_basic_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_big_marker/expected_legend_big_marker_mask.png b/tests/testdata/control_images/legend/expected_legend_big_marker/expected_legend_big_marker_mask.png
index 135770c..3eabee2 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_big_marker/expected_legend_big_marker_mask.png and b/tests/testdata/control_images/legend/expected_legend_big_marker/expected_legend_big_marker_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression.png b/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression.png
index 383075e..2372844 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression_mask.png b/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression_mask.png
index 239d050..4a2e874 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression_mask.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map.png b/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map.png
index 2f2e292..4d3ee3a 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map_mask.png b/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map_mask.png
index 4f0a269..d91b7a2 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map_mask.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_map/expected_legend_filter_by_map_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_map_dupe/expected_legend_filter_by_map_dupe_mask.png b/tests/testdata/control_images/legend/expected_legend_filter_by_map_dupe/expected_legend_filter_by_map_dupe_mask.png
index 3f6a2b7..a361927 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_map_dupe/expected_legend_filter_by_map_dupe_mask.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_map_dupe/expected_legend_filter_by_map_dupe_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon.png b/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon.png
index 383075e..2372844 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon_mask.png b/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon_mask.png
index 239d050..6521453 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon_mask.png and b/tests/testdata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_long_symbol_text/expected_legend_long_symbol_text_mask.png b/tests/testdata/control_images/legend/expected_legend_long_symbol_text/expected_legend_long_symbol_text_mask.png
index ebe1fb3..3ea7733 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_long_symbol_text/expected_legend_long_symbol_text_mask.png and b/tests/testdata/control_images/legend/expected_legend_long_symbol_text/expected_legend_long_symbol_text_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_mapunits/expected_legend_mapunits_mask.png b/tests/testdata/control_images/legend/expected_legend_mapunits/expected_legend_mapunits_mask.png
index 74a3009..1f6d335 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_mapunits/expected_legend_mapunits_mask.png and b/tests/testdata/control_images/legend/expected_legend_mapunits/expected_legend_mapunits_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_raster_border/expected_legend_raster_border_mask.png b/tests/testdata/control_images/legend/expected_legend_raster_border/expected_legend_raster_border_mask.png
index fce1c35..2147885 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_raster_border/expected_legend_raster_border_mask.png and b/tests/testdata/control_images/legend/expected_legend_raster_border/expected_legend_raster_border_mask.png differ
diff --git a/tests/testdata/control_images/legend/expected_legend_three_columns/expected_legend_three_columns_mask.png b/tests/testdata/control_images/legend/expected_legend_three_columns/expected_legend_three_columns_mask.png
index 0b50ada..cf00342 100644
Binary files a/tests/testdata/control_images/legend/expected_legend_three_columns/expected_legend_three_columns_mask.png and b/tests/testdata/control_images/legend/expected_legend_three_columns/expected_legend_three_columns_mask.png differ
diff --git a/tests/testdata/elev.gpx b/tests/testdata/elev.gpx
new file mode 100644
index 0000000..961a591
--- /dev/null
+++ b/tests/testdata/elev.gpx
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<gpx version="1.1" creator="GDAL 1.11.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
+<metadata><bounds minlat="56.134840267383296" minlon="-3.877982991747363" maxlat="56.135514185147493" maxlon="-3.852565020533009"/></metadata>                  
+<rte>
+  <rtept lat="56.134840267383296" lon="-3.877982991747363">
+    <ele>1</ele>
+  </rtept>
+  <rtept lat="56.134933668432737" lon="-3.865962243986197">
+    <ele>2</ele>
+  </rtept>
+  <rtept lat="56.135514185147493" lon="-3.852565020533009">
+    <ele>3</ele>
+  </rtept>
+</rte>
+</gpx>
diff --git a/tests/testdata/noelev.gpx b/tests/testdata/noelev.gpx
new file mode 100644
index 0000000..2c56fad
--- /dev/null
+++ b/tests/testdata/noelev.gpx
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<gpx version="1.1" creator="GDAL 1.11.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
+<metadata><bounds minlat="56.134840267383240" minlon="-3.877982991747359" maxlat="56.135514185147436" maxlon="-3.852565020533005"/></metadata>                  
+<rte>
+  <rtept lat="56.13484026738324" lon="-3.877982991747359">
+  </rtept>
+  <rtept lat="56.13493366843268" lon="-3.865962243986193">
+  </rtept>
+  <rtept lat="56.135514185147436" lon="-3.852565020533005">
+  </rtept>
+</rte>
+</gpx>
diff --git a/tests/testdata/provider/testdata_oracle.sql b/tests/testdata/provider/testdata_oracle.sql
new file mode 100644
index 0000000..4117c99
--- /dev/null
+++ b/tests/testdata/provider/testdata_oracle.sql
@@ -0,0 +1,32 @@
+CREATE TABLE QGIS.SOME_DATA ( "pk" INTEGER PRIMARY KEY, "cnt" INTEGER, "name" VARCHAR2(100) DEFAULT 'qgis', "name2" VARCHAR2(100) DEFAULT 'qgis', "num_char" VARCHAR2(100), GEOM SDO_GEOMETRY);
+
+INSERT INTO QGIS.SOME_DATA ("pk", "cnt", "name", "name2", "num_char", GEOM)
+      SELECT 5, -200, NULL, 'NuLl', '5', SDO_GEOMETRY( 2001,4326,SDO_POINT_TYPE(-71.123, 78.23, NULL), NULL, NULL) from dual
+  UNION ALL SELECT 3,  300, 'Pear', 'PEaR', '3', NULL from dual
+  UNION ALL SELECT 1,  100, 'Orange', 'oranGe', '1', SDO_GEOMETRY( 2001,4326,SDO_POINT_TYPE(-70.332, 66.33, NULL), NULL, NULL) from dual
+  UNION ALL SELECT 2,  200, 'Apple', 'Apple', '2', SDO_GEOMETRY( 2001,4326,SDO_POINT_TYPE(-68.2, 70.8, NULL), NULL, NULL) from dual
+  UNION ALL SELECT 4,  400, 'Honey', 'Honey', '4', SDO_GEOMETRY( 2001,4326,SDO_POINT_TYPE(-65.32, 78.3, NULL), NULL, NULL) from dual;
+
+INSERT INTO user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) VALUES ( 'SOME_DATA', 'GEOM', sdo_dim_array(sdo_dim_element('X',-75,-55,0.005),sdo_dim_element('Y',65,85,0.005)),4326);
+
+CREATE INDEX some_data_spatial_idx ON QGIS.SOME_DATA(GEOM) INDEXTYPE IS MDSYS.SPATIAL_INDEX;
+
+CREATE TABLE QGIS.SOME_POLY_DATA ( "pk" INTEGER PRIMARY KEY, GEOM SDO_GEOMETRY);
+
+INSERT INTO QGIS.SOME_POLY_DATA ("pk", GEOM)
+      SELECT 1, SDO_GEOMETRY( 2003,4326,NULL, SDO_ELEM_INFO_ARRAY(1,1003,1), SDO_ORDINATE_ARRAY(-69.0,81.4 , -69.0,80.2 , -73.7,80.2 , -73.7,76.3 , -74.9,76.3 , -74.9,81.4 , -69.0,81.4)) from dual
+  UNION ALL SELECT 2, SDO_GEOMETRY( 2003,4326,NULL, SDO_ELEM_INFO_ARRAY(1,1003,1), SDO_ORDINATE_ARRAY(-67.6,81.2 , -66.3,81.2 , -66.3,76.9 , -67.6,76.9 , -67.6,81.2))from dual
+  UNION ALL SELECT 3, SDO_GEOMETRY( 2003,4326,NULL, SDO_ELEM_INFO_ARRAY(1,1003,1), SDO_ORDINATE_ARRAY(-68.4,75.8 , -67.5,72.6 , -68.6,73.7 , -70.2,72.9 , -68.4,75.8)) from dual
+  UNION ALL SELECT 4,  NULL from dual;
+
+
+INSERT INTO user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) VALUES ( 'SOME_POLY_DATA', 'GEOM', sdo_dim_array(sdo_dim_element('X',-80,-55,0.005),sdo_dim_element('Y',65,85,0.005)),4326);
+
+CREATE INDEX some_poly_data_spatial_idx ON QGIS.SOME_POLY_DATA(GEOM) INDEXTYPE IS MDSYS.SPATIAL_INDEX;
+
+
+CREATE TABLE QGIS.DATE_TIMES ( "id" INTEGER PRIMARY KEY, "date_field" DATE, "datetime_field" TIMESTAMP );
+
+INSERT INTO QGIS.DATE_TIMES ("id", "date_field", "datetime_field" ) VALUES (1, DATE '2004-03-04', TIMESTAMP '2004-03-04 13:41:52' );
+
+COMMIT;
diff --git a/tests/testdata/raster/test.asc b/tests/testdata/raster/test.asc
new file mode 100644
index 0000000..8fc0131
--- /dev/null
+++ b/tests/testdata/raster/test.asc
@@ -0,0 +1,6 @@
+ncols        7
+nrows        1
+xllcorner    0
+yllcorner    0
+cellsize     1
+ -999.9 -999.987 1.2345678 123456 1234567 -999.9876 1.2345678901234
diff --git a/tests/testdata/svg/issue_14644.svg b/tests/testdata/svg/issue_14644.svg
new file mode 100644
index 0000000..9f5f971
--- /dev/null
+++ b/tests/testdata/svg/issue_14644.svg
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="242.95674"
+   height="88.58268"
+   viewbox="0 0 245 90"
+   id="svg3960"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="qgis_problem.svg">
+  <defs
+     id="defs3962" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2"
+     inkscape:cx="167.38856"
+     inkscape:cy="12.175374"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1855"
+     inkscape:window-height="1056"
+     inkscape:window-x="1345"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     units="cm" />
+  <metadata
+     id="metadata3965">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-122.7856,54.496612)">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:46.9240303px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#999999;fill-opacity:1;stroke:none"
+       x="257.45532"
+       y="33.536175"
+       id="text3831-0"
+       sodipodi:linespacing="125%"><tspan
+         id="tspan3833-2"
+         sodipodi:role="line"
+         x="257.45532"
+         y="33.536175">Talk</tspan></text>
+    <path
+       inkscape:export-ydpi="300.08182"
+       inkscape:export-xdpi="300.08182"
+       inkscape:export-filename=""
+       clip-path="none"
+       style="display:inline;fill:#80808f;fill-opacity:1"
+       d="m 154.59272,-22.11995 c 0.435,0.257398 0.832,0.533097 1.17019,0.849395 1.1575,1.082353 2.28139,2.683174 2.09539,2.989872 -0.05,0.092 -1.41289,1.128324 -3.01358,2.292017 l -2.90768,2.098448 -1.2877,-0.787795 -1.26989,-0.820196 -0.137,-3.888867 -0.187,-3.874268 1.2034,-0.054 c 1.39269,-0.053 3.02958,0.424797 4.33367,1.195983 z m 4.17368,5.551238 0.27999,1.787709 c 0.277,1.886729 0.111,5.5380377 -0.27599,6.177644 -0.127,0.2111988 -1.5082,0.347998 -3.08789,0.2815984 -1.57979,-0.067 [...]
+       id="path3918-6"
+       inkscape:connector-curvature="0" />
+    <text
+       sodipodi:linespacing="125%"
+       id="text3077-7"
+       y="32.731239"
+       x="256.65042"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:46.9240303px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#006500;fill-opacity:1;stroke:none"
+       xml:space="preserve"><tspan
+         y="32.731239"
+         x="256.65042"
+         sodipodi:role="line"
+         id="tspan3875-7">Talk</tspan></text>
+    <g
+       style="display:inline;fill:#000098;fill-opacity:1"
+       id="g3815-5"
+       transform="matrix(1.0425656,0,0,0.72452471,50.973221,-59.466354)">
+      <path
+         sodipodi:nodetypes="cscscc"
+         inkscape:connector-curvature="0"
+         id="path3817-8"
+         d="m 223.195,72.653166 c 10.3555,1.148817 16.80222,5.350565 16.82722,11.886671 0.0232,6.052693 -6.44914,11.048103 -16.7397,11.818553 5.84387,-1.06162 12.78687,-5.88369 12.78687,-11.682316 0,-6.576511 -7.78791,-10.843594 -12.67003,-11.903701 -0.17966,0.110931 -0.16845,-0.09826 -0.20436,-0.119207 z"
+         style="fill:#000098;fill-opacity:1;stroke:none" />
+      <path
+         style="fill:#000098;fill-opacity:1;stroke:none"
+         d="m 230.54121,67.335169 c 15.00178,1.664265 24.341,7.751243 24.37721,17.219952 0.0336,8.7684 -9.34272,16.005139 -24.25042,17.121269 8.46588,-1.53794 18.52405,-8.523567 18.52405,-16.923905 0,-9.527243 -11.28217,-15.70887 -18.35479,-17.244623 -0.26027,0.160703 -0.24403,-0.142347 -0.29605,-0.172693 z"
+         id="path3819-5"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cscscc" />
+      <path
+         sodipodi:nodetypes="cscscc"
+         inkscape:connector-curvature="0"
+         id="path3821-4"
+         d="m 238.7263,63.198946 c 18.61555,2.065169 30.20449,9.618436 30.24943,21.368059 0.0417,10.880616 -11.59329,19.860605 -30.0921,21.245605 10.50523,-1.90842 22.9863,-10.576806 22.9863,-21.000699 0,-11.822257 -13.99993,-19.492973 -22.77626,-21.398673 -0.32297,0.199415 -0.30282,-0.176637 -0.36737,-0.214292 z"
+         style="fill:#000098;fill-opacity:1;stroke:none" />
+    </g>
+    <text
+       clip-path="none"
+       sodipodi:linespacing="125%"
+       id="text3910-8"
+       y="-2.5704708"
+       x="129.89906"
+       style="font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:61.58447266px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold Oblique';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#999999;fill-opacity:1;stroke:none"
+       xml:space="preserve"><tspan
+         y="-2.5704708"
+         x="129.89906"
+         id="tspan3912-7"
+         sodipodi:role="line">Walking</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:61.58447266px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold Oblique';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000098;fill-opacity:1;stroke:none"
+       x="129.09416"
+       y="-3.8301911"
+       id="text2985-1"
+       sodipodi:linespacing="125%"
+       clip-path="none"><tspan
+         sodipodi:role="line"
+         id="tspan2987-7"
+         x="129.09416"
+         y="-3.8301911">Walking</tspan></text>
+    <text
+       sodipodi:linespacing="125%"
+       id="text3835"
+       y="15.369775"
+       x="174.28571"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.6932888px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#999999;fill-opacity:1;stroke:none"
+       xml:space="preserve"><tspan
+         y="15.369775"
+         x="174.28571"
+         id="tspan3837"
+         sodipodi:role="line">the</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.6932888px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#4d4d4d;fill-opacity:1;stroke:none"
+       x="173.83084"
+       y="14.914875"
+       id="text3879-6"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3881-4"
+         x="173.83084"
+         y="14.914875">the</tspan></text>
+    <g
+       transform="matrix(-1.0425656,0,0,0.72452471,492.68186,-57.574645)"
+       id="g3867-9"
+       style="display:inline;fill:#006500;fill-opacity:1">
+      <path
+         style="fill:#006500;fill-opacity:1;stroke:none"
+         d="m 223.195,72.653166 c 10.3555,1.148817 16.80222,5.350565 16.82722,11.886671 0.0232,6.052693 -6.44914,11.048103 -16.7397,11.818553 5.84387,-1.06162 12.78687,-5.88369 12.78687,-11.682316 0,-6.576511 -7.78791,-10.843594 -12.67003,-11.903701 -0.17966,0.110931 -0.16845,-0.09826 -0.20436,-0.119207 z"
+         id="path3869-6"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cscscc" />
+      <path
+         sodipodi:nodetypes="cscscc"
+         inkscape:connector-curvature="0"
+         id="path3871-0"
+         d="m 230.54121,67.335169 c 15.00178,1.664265 24.341,7.751243 24.37721,17.219952 0.0336,8.7684 -9.34272,16.005139 -24.25042,17.121269 8.46588,-1.53794 18.52405,-8.523567 18.52405,-16.923905 0,-9.527243 -11.28217,-15.70887 -18.35479,-17.244623 -0.26027,0.160703 -0.24403,-0.142347 -0.29605,-0.172693 z"
+         style="fill:#006500;fill-opacity:1;stroke:none" />
+      <path
+         style="fill:#006500;fill-opacity:1;stroke:none"
+         d="m 238.7263,63.198946 c 18.61555,2.065169 30.20449,9.618436 30.24943,21.368059 0.0417,10.880616 -11.59329,19.860605 -30.0921,21.245605 10.50523,-1.90842 22.9863,-10.576806 22.9863,-21.000699 0,-11.822257 -13.99993,-19.492973 -22.77626,-21.398673 -0.32297,0.199415 -0.30282,-0.176637 -0.36737,-0.214292 z"
+         id="path3873-5"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cscscc" />
+    </g>
+    <path
+       inkscape:connector-curvature="0"
+       id="path3700-5"
+       d="m 194.13099,-53.29621 c 0.435,0.257399 0.832,0.533097 1.17019,0.849395 1.1575,1.082334 2.28139,2.683155 2.09539,2.989873 -0.05,0.092 -1.41289,1.128313 -3.01358,2.291997 l -2.90768,2.098477 -1.2877,-0.787795 -1.26989,-0.820195 -0.137,-3.888878 -0.187,-3.874277 1.2034,-0.055 c 1.39259,-0.053 3.02948,0.424797 4.33367,1.195993 z m 4.17368,5.551218 0.28,1.78772 c 0.27699,1.886729 0.111,5.538028 -0.276,6.177644 -0.127,0.211199 -1.50819,0.347998 -3.08788,0.281598 -1.5798,-0.067 -3.029 [...]
+       style="display:inline;fill:#80808f;fill-opacity:1"
+       clip-path="none"
+       inkscape:export-filename=""
+       inkscape:export-xdpi="300.08182"
+       inkscape:export-ydpi="300.08182" />
+  </g>
+</svg>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/qgis.git



More information about the Pkg-grass-devel mailing list