[QGIS Commit] r15155 - in trunk/qgis: . images images/themes/default/propertyicons src/app src/app/attributetable src/core src/core/symbology-ng src/providers/ogr src/providers/postgres src/providers/spatialite src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Fri Feb 11 11:23:17 EST 2011


Author: mhugent
Date: 2011-02-11 08:23:17 -0800 (Fri, 11 Feb 2011)
New Revision: 15155

Added:
   trunk/qgis/images/themes/default/propertyicons/join.png
   trunk/qgis/src/app/qgsaddjoindialog.cpp
   trunk/qgis/src/app/qgsaddjoindialog.h
   trunk/qgis/src/core/qgsvectorlayerjoinbuffer.cpp
   trunk/qgis/src/core/qgsvectorlayerjoinbuffer.h
   trunk/qgis/src/ui/qgsaddjoindialogbase.ui
Modified:
   trunk/qgis/
   trunk/qgis/images/images.qrc
   trunk/qgis/src/app/CMakeLists.txt
   trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp
   trunk/qgis/src/app/qgsvectorlayerproperties.cpp
   trunk/qgis/src/app/qgsvectorlayerproperties.h
   trunk/qgis/src/core/CMakeLists.txt
   trunk/qgis/src/core/qgsdataprovider.h
   trunk/qgis/src/core/qgsproject.cpp
   trunk/qgis/src/core/qgsvectordataprovider.cpp
   trunk/qgis/src/core/qgsvectordataprovider.h
   trunk/qgis/src/core/qgsvectorlayer.cpp
   trunk/qgis/src/core/qgsvectorlayer.h
   trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
   trunk/qgis/src/providers/ogr/qgsogrprovider.cpp
   trunk/qgis/src/providers/ogr/qgsogrprovider.h
   trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
   trunk/qgis/src/providers/postgres/qgspostgresprovider.h
   trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.cpp
   trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.h
   trunk/qgis/src/ui/qgsvectorlayerpropertiesbase.ui
Log:
[FEATURE]: experimental table join support refactored and ported from branch to trunk


Property changes on: trunk/qgis
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/symbology-ng-branch:10769-12136
   + /branches/symbology-ng-branch:10769-12136
/branches/table_join_branch:13934-14132

Modified: trunk/qgis/images/images.qrc
===================================================================
--- trunk/qgis/images/images.qrc	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/images/images.qrc	2011-02-11 16:23:17 UTC (rev 15155)
@@ -269,6 +269,7 @@
     <file>themes/default/propertyicons/digitising.png</file>
     <file>themes/default/propertyicons/general.png</file>
     <file>themes/default/propertyicons/histogram.png</file>
+    <file>themes/default/propertyicons/join.png</file>
     <file>themes/default/propertyicons/labels.png</file>
     <file>themes/default/propertyicons/locale.png</file>
     <file>themes/default/propertyicons/map_tools.png</file>

Copied: trunk/qgis/images/themes/default/propertyicons/join.png (from rev 14132, branches/table_join_branch/images/themes/default/propertyicons/join.png)
===================================================================
(Binary files differ)

Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/app/CMakeLists.txt	2011-02-11 16:23:17 UTC (rev 15155)
@@ -4,6 +4,7 @@
   qgisappinterface.cpp
   qgsabout.cpp
   qgsaddattrdialog.cpp
+  qgsaddjoindialog.cpp
   qgsannotationwidget.cpp
   qgsattributeactiondialog.cpp
   qgsattributedialog.cpp
@@ -144,6 +145,7 @@
   qgsabout.h
   qgsaddattrdialog.h
   qgsdisplayangle.h
+  qgsaddjoindialog.h
   qgsannotationwidget.h
   qgsattributeactiondialog.h
   qgsattributedialog.h

Modified: trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -234,7 +234,7 @@
       rect = QgisApp::instance()->mapCanvas()->extent();
     }
 
-    mLayer->select( mAttributes, rect, false );
+    mLayer->select( QgsAttributeList(), rect, false );
 
     QgsFeature f;
     for ( int i = 0; mLayer->nextFeature( f ); ++i )

Copied: trunk/qgis/src/app/qgsaddjoindialog.cpp (from rev 14132, branches/table_join_branch/src/app/qgsaddjoindialog.cpp)
===================================================================
--- trunk/qgis/src/app/qgsaddjoindialog.cpp	                        (rev 0)
+++ trunk/qgis/src/app/qgsaddjoindialog.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -0,0 +1,137 @@
+/***************************************************************************
+                              qgsaddjoindialog.cpp
+                              --------------------
+  begin                : July 10, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco dot hugentobler at sourcepole dot ch
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "qgsaddjoindialog.h"
+#include "qgsmaplayer.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsvectordataprovider.h"
+#include "qgsvectorlayer.h"
+
+QgsAddJoinDialog::QgsAddJoinDialog( QgsVectorLayer* layer, QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f ), mLayer( layer )
+{
+  setupUi( this );
+
+  if ( !mLayer )
+  {
+    return;
+  }
+
+  //insert possible vector layers into mJoinLayerComboBox
+
+  mJoinLayerComboBox->blockSignals( true );
+  const QMap<QString, QgsMapLayer*>& layerList = QgsMapLayerRegistry::instance()->mapLayers();
+  QMap<QString, QgsMapLayer*>::const_iterator layerIt = layerList.constBegin();
+  for ( ; layerIt != layerList.constEnd(); ++layerIt )
+  {
+    QgsMapLayer* currentLayer = layerIt.value();
+    if ( currentLayer->type() == QgsMapLayer::VectorLayer )
+    {
+      QgsVectorLayer* currentVectorLayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
+      if ( currentVectorLayer && currentVectorLayer != mLayer )
+      {
+        if ( currentVectorLayer->dataProvider() && currentVectorLayer->dataProvider()->supportsSubsetString() )
+          mJoinLayerComboBox->addItem( currentLayer->name(), QVariant( currentLayer->getLayerID() ) );
+      }
+    }
+  }
+  mJoinLayerComboBox->blockSignals( false );
+  on_mJoinLayerComboBox_currentIndexChanged( mJoinLayerComboBox->currentIndex() );
+
+  //insert possible target fields
+  const QgsFieldMap& layerFieldMap = mLayer->pendingFields();
+  QgsFieldMap::const_iterator fieldIt = layerFieldMap.constBegin();
+  for ( ; fieldIt != layerFieldMap.constEnd(); ++fieldIt )
+  {
+    mTargetFieldComboBox->addItem( fieldIt.value().name(), fieldIt.key() );
+  }
+
+  mCacheInMemoryCheckBox->setChecked( true );
+}
+
+QgsAddJoinDialog::~QgsAddJoinDialog()
+{
+}
+
+QString QgsAddJoinDialog::joinedLayerId() const
+{
+  return mJoinLayerComboBox->itemData( mJoinLayerComboBox->currentIndex() ).toString();
+}
+
+int QgsAddJoinDialog::joinField() const
+{
+  return mJoinFieldComboBox->itemData( mJoinFieldComboBox->currentIndex() ).toInt();
+}
+
+QString QgsAddJoinDialog::joinFieldName() const
+{
+  return mJoinFieldComboBox->itemText( mJoinFieldComboBox->currentIndex() );
+}
+
+int QgsAddJoinDialog::targetField() const
+{
+  return mTargetFieldComboBox->itemData( mTargetFieldComboBox->currentIndex() ).toInt();
+}
+
+QString QgsAddJoinDialog::targetFieldName() const
+{
+  return mTargetFieldComboBox->itemText( mTargetFieldComboBox->currentIndex() );
+}
+
+bool QgsAddJoinDialog::cacheInMemory() const
+{
+  return mCacheInMemoryCheckBox->isChecked();
+}
+
+bool QgsAddJoinDialog::createAttributeIndex() const
+{
+  return mCreateIndexCheckBox->isChecked();
+}
+
+void QgsAddJoinDialog::on_mJoinLayerComboBox_currentIndexChanged( int index )
+{
+  mJoinFieldComboBox->clear();
+  QString layerId = mJoinLayerComboBox->itemData( index ).toString();
+  QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
+  if ( !layer )
+  {
+    return;
+  }
+  QgsVectorLayer* vLayer = dynamic_cast<QgsVectorLayer*>( layer );
+  if ( !vLayer )
+  {
+    return;
+  }
+
+  const QgsFieldMap& layerFieldMap = vLayer->pendingFields();
+  QgsFieldMap::const_iterator fieldIt = layerFieldMap.constBegin();
+  for ( ; fieldIt != layerFieldMap.constEnd(); ++fieldIt )
+  {
+    mJoinFieldComboBox->addItem( fieldIt.value().name(), fieldIt.key() );
+  }
+
+  //does provider support creation of attribute indices?
+  QgsVectorDataProvider* dp = vLayer->dataProvider();
+  if ( dp && ( dp->capabilities() & QgsVectorDataProvider::CreateAttributeIndex ) )
+  {
+    mCreateIndexCheckBox->setEnabled( true );
+  }
+  else
+  {
+    mCreateIndexCheckBox->setEnabled( false );
+    mCreateIndexCheckBox->setChecked( false );
+  }
+}

Copied: trunk/qgis/src/app/qgsaddjoindialog.h (from rev 14132, branches/table_join_branch/src/app/qgsaddjoindialog.h)
===================================================================
--- trunk/qgis/src/app/qgsaddjoindialog.h	                        (rev 0)
+++ trunk/qgis/src/app/qgsaddjoindialog.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -0,0 +1,57 @@
+/***************************************************************************
+                              qgsaddjoindialog.h
+                              ------------------
+  begin                : July 10, 2010
+  copyright            : (C) 2010 by Marco Hugentobler
+  email                : marco dot hugentobler at sourcepole dot ch
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 QGSADDJOINDIALOG_H
+#define QGSADDJOINDIALOG_H
+
+#include "ui_qgsaddjoindialogbase.h"
+class QgsVectorLayer;
+
+class QgsAddJoinDialog: public QDialog, private Ui::QgsAddJoinDialogBase
+{
+    Q_OBJECT
+  public:
+    QgsAddJoinDialog( QgsVectorLayer* layer, QWidget * parent = 0, Qt::WindowFlags f = 0 );
+    ~QgsAddJoinDialog();
+
+    //retrieve results
+
+    /**Get the id of the layer to join*/
+    QString joinedLayerId() const;
+    /**Returns the index of the join field*/
+    int joinField() const;
+    /**Returns the name of the join field*/
+    QString joinFieldName() const;
+    /**Returns the index of the target field (join-to field)*/
+    int targetField() const;
+    /**Returns the name of the target field (join-to field)*/
+    QString targetFieldName() const;
+    /**True if joined layer should be cached in virtual memory*/
+    bool cacheInMemory() const;
+    /**Returns true if user wants to create an attribute index on the join field*/
+    bool createAttributeIndex() const;
+
+  private slots:
+    void on_mJoinLayerComboBox_currentIndexChanged( int index );
+
+  private:
+    /**Target layer*/
+    QgsVectorLayer* mLayer;
+};
+
+
+#endif // QGSADDJOINDIALOG_H

Modified: trunk/qgis/src/app/qgsvectorlayerproperties.cpp
===================================================================
--- trunk/qgis/src/app/qgsvectorlayerproperties.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/app/qgsvectorlayerproperties.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -21,6 +21,7 @@
 #include <limits>
 
 #include "qgisapp.h"
+#include "qgsaddjoindialog.h"
 #include "qgsapplication.h"
 #include "qgsattributeactiondialog.h"
 #include "qgsapplydialog.h"
@@ -33,6 +34,7 @@
 #include "qgslabel.h"
 #include "qgsgenericprojectionselector.h"
 #include "qgslogger.h"
+#include "qgsmaplayerregistry.h"
 #include "qgspluginmetadata.h"
 #include "qgspluginregistry.h"
 #include "qgsproject.h"
@@ -133,6 +135,13 @@
 
   connect( sliderTransparency, SIGNAL( valueChanged( int ) ), this, SLOT( sliderTransparency_valueChanged( int ) ) );
 
+  //insert existing join info
+  const QList< QgsVectorJoinInfo >& joins = layer->vectorJoins();
+  for ( int i = 0; i < joins.size(); ++i )
+  {
+    addJoinToTreeWidget( joins[i] );
+  }
+
   //for each overlay plugin create a new tab
   int position;
   QList<QgsVectorOverlayPlugin*> overlayPluginList = overlayPlugins();
@@ -813,7 +822,6 @@
   {
     xMin = QString( "%1" ).arg( myExtent.xMinimum() );
   }
-
   if ( qAbs( myExtent.yMinimum() ) > changeoverValue )
   {
     yMin = QString( "%1" ).arg( myExtent.yMinimum(), 0, 'f', 2 );
@@ -822,7 +830,6 @@
   {
     yMin = QString( "%1" ).arg( myExtent.yMinimum() );
   }
-
   if ( qAbs( myExtent.xMaximum() ) > changeoverValue )
   {
     xMax = QString( "%1" ).arg( myExtent.xMaximum(), 0, 'f', 2 );
@@ -831,7 +838,6 @@
   {
     xMax = QString( "%1" ).arg( myExtent.xMaximum() );
   }
-
   if ( qAbs( myExtent.yMaximum() ) > changeoverValue )
   {
     yMax = QString( "%1" ).arg( myExtent.yMaximum(), 0, 'f', 2 );
@@ -1171,6 +1177,70 @@
   updateSymbologyPage();
 }
 
+void QgsVectorLayerProperties::on_mButtonAddJoin_clicked()
+{
+  QgsAddJoinDialog d( layer );
+  if ( d.exec() == QDialog::Accepted )
+  {
+    QgsVectorJoinInfo info;
+    info.targetField = d.targetField();
+    info.joinLayerId = d.joinedLayerId();
+    info.joinField = d.joinField();
+    info.memoryCache = d.cacheInMemory();
+    if ( layer )
+    {
+      //create attribute index if possible. Todo: ask user if this should be done (e.g. in QgsAddJoinDialog)
+      if ( d.createAttributeIndex() )
+      {
+        QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( info.joinLayerId ) );
+        if ( joinLayer )
+        {
+          joinLayer->dataProvider()->createAttributeIndex( info.joinField );
+        }
+      }
+
+      layer->addJoin( info );
+      loadRows(); //update attribute tab
+      addJoinToTreeWidget( info );
+    }
+  }
+}
+
+void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorJoinInfo& join )
+{
+  QTreeWidgetItem* joinItem = new QTreeWidgetItem();
+
+  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join.joinLayerId ) );
+  if ( !joinLayer )
+  {
+    return;
+  }
+
+  joinItem->setText( 0, joinLayer->name() );
+  joinItem->setData( 0, Qt::UserRole, join.joinLayerId );
+  QString joinFieldName = joinLayer->pendingFields().value( join.joinField ).name();
+  QString targetFieldName = layer->pendingFields().value( join.targetField ).name();
+  joinItem->setText( 1, joinFieldName );
+  joinItem->setData( 1, Qt::UserRole, join.joinField );
+  joinItem->setText( 2, targetFieldName );
+  joinItem->setData( 2, Qt::UserRole, join.targetField );
+
+  mJoinTreeWidget->addTopLevelItem( joinItem );
+}
+
+void QgsVectorLayerProperties::on_mButtonRemoveJoin_clicked()
+{
+  QTreeWidgetItem* currentJoinItem = mJoinTreeWidget->currentItem();
+  if ( !layer || !currentJoinItem )
+  {
+    return;
+  }
+
+  layer->removeJoin( currentJoinItem->data( 0, Qt::UserRole ).toString() );
+  loadRows();
+  mJoinTreeWidget->takeTopLevelItem( mJoinTreeWidget->indexOfTopLevelItem( currentJoinItem ) );
+}
+
 void QgsVectorLayerProperties::useNewSymbology()
 {
   int res = QMessageBox::question( this, tr( "Symbology" ),

Modified: trunk/qgis/src/app/qgsvectorlayerproperties.h
===================================================================
--- trunk/qgis/src/app/qgsvectorlayerproperties.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/app/qgsvectorlayerproperties.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -117,6 +117,9 @@
     void useNewSymbology();
     void setUsingNewSymbology( bool useNewSymbology );
 
+    void on_mButtonAddJoin_clicked();
+    void on_mButtonRemoveJoin_clicked();
+
   signals:
 
     /** emitted when changes to layer were saved to update legend */
@@ -171,6 +174,9 @@
     /**Buffer pixmap which takes the picture of renderers before they are assigned to the vector layer*/
     //QPixmap bufferPixmap;
 
+    /**Adds a new join to mJoinTreeWidget*/
+    void addJoinToTreeWidget( const QgsVectorJoinInfo& join );
+
     static QMap< QgsVectorLayer::EditType, QString > editTypeMap;
     static void setupEditTypes();
     static QString editTypeButtonText( QgsVectorLayer::EditType type );

Modified: trunk/qgis/src/core/CMakeLists.txt
===================================================================
--- trunk/qgis/src/core/CMakeLists.txt	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/CMakeLists.txt	2011-02-11 16:23:17 UTC (rev 15155)
@@ -89,6 +89,7 @@
   qgsvectordataprovider.cpp
   qgsvectorfilewriter.cpp
   qgsvectorlayer.cpp
+  qgsvectorlayerjoinbuffer.cpp
   qgsvectorlayerundocommand.cpp
   qgsvectoroverlay.cpp
 

Modified: trunk/qgis/src/core/qgsdataprovider.h
===================================================================
--- trunk/qgis/src/core/qgsdataprovider.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsdataprovider.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -113,7 +113,7 @@
      * that can be used by the data provider to create a subset.
      * Must be implemented in the dataprovider.
      */
-    virtual bool setSubsetString( QString subset )
+    virtual bool setSubsetString( QString subset, bool updateFeatureCount = true )
     {
       // NOP by default
       Q_UNUSED( subset );

Modified: trunk/qgis/src/core/qgsproject.cpp
===================================================================
--- trunk/qgis/src/core/qgsproject.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsproject.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -675,6 +675,7 @@
   bool returnStatus = true;
 
   emit layerLoaded( 0, nl.count() );
+  QList<QgsVectorLayer*> vLayerList; //collect
 
   for ( int i = 0; i < nl.count(); i++ )
   {
@@ -714,6 +715,11 @@
     if ( mapLayer->readXML( node ) )
     {
       mapLayer = QgsMapLayerRegistry::instance()->addMapLayer( mapLayer );
+      QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
+      if ( vLayer )
+      {
+        vLayerList.push_back( vLayer );
+      }
     }
     else
     {
@@ -725,10 +731,18 @@
 
       brokenNodes.push_back( node );
     }
-
     emit layerLoaded( i + 1, nl.count() );
   }
 
+  //Update field map of layers with joins and create join caches if necessary
+  //Needs to be done here once all dependent layers are loaded
+  QList<QgsVectorLayer*>::iterator vIt = vLayerList.begin();
+  for ( ; vIt != vLayerList.end(); ++vIt )
+  {
+    ( *vIt )->createJoinCaches();
+    ( *vIt )->updateFieldMap();
+  }
+
   return qMakePair( returnStatus, brokenNodes );
 
 } // _getMapLayers

Modified: trunk/qgis/src/core/qgsvectordataprovider.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectordataprovider.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsvectordataprovider.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -126,6 +126,11 @@
   return false;
 }
 
+bool QgsVectorDataProvider::createAttributeIndex( int field )
+{
+  return true;
+}
+
 int QgsVectorDataProvider::capabilities() const
 {
   return QgsVectorDataProvider::NoCapabilities;

Modified: trunk/qgis/src/core/qgsvectordataprovider.h
===================================================================
--- trunk/qgis/src/core/qgsvectordataprovider.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsvectordataprovider.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -74,6 +74,7 @@
       RandomSelectGeometryAtId =     1 << 10,
       /** DEPRECATED - do not use */
       SequentialSelectGeometryAtId = 1 << 11,
+      CreateAttributeIndex = 1 << 12
     };
 
     /** bitmask of all provider's editing capabilities */
@@ -272,6 +273,9 @@
      */
     virtual bool createSpatialIndex();
 
+    /**Create an attribute index on the datasource*/
+    virtual bool createAttributeIndex( int field );
+
     /** Returns a bitmask containing the supported capabilities
         Note, some capabilities may change depending on whether
         a spatial filter is active on this provider, so it may

Modified: trunk/qgis/src/core/qgsvectorlayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsvectorlayer.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -68,6 +68,7 @@
 #include "qgssinglesymbolrenderer.h"
 #include "qgscoordinatereferencesystem.h"
 #include "qgsvectordataprovider.h"
+#include "qgsvectorlayerjoinbuffer.h"
 #include "qgsvectorlayerundocommand.h"
 #include "qgsvectoroverlay.h"
 #include "qgsmaplayerregistry.h"
@@ -110,7 +111,8 @@
     mLabel( 0 ),
     mLabelOn( false ),
     mVertexMarkerOnlyForSelection( false ),
-    mFetching( false )
+    mFetching( false ),
+    mJoinBuffer( 0 )
 {
   mActions = new QgsAttributeAction( this );
 
@@ -154,6 +156,11 @@
       }
     }
 
+    mJoinBuffer = new QgsVectorLayerJoinBuffer();
+
+    connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( checkJoinLayerRemove( QString ) ) );
+    updateFieldMap();
+
     // Get the update threshold from user settings. We
     // do this only on construction to avoid the penality of
     // fetching this each time the layer is drawn. If the user
@@ -175,13 +182,9 @@
 
   mValid = false;
 
-  if ( mRenderer )
-  {
-    delete mRenderer;
-  }
-  // delete the provider object
+  delete mRenderer;
   delete mDataProvider;
-
+  delete mJoinBuffer;
   delete mLabel;
 
   // Destroy any cached geometries and clear the references to them
@@ -1500,6 +1503,14 @@
 
 void QgsVectorLayer::updateFeatureAttributes( QgsFeature &f, bool all )
 {
+  if ( mDataProvider && ( all || ( mFetchAttributes.size() > 0 && mJoinBuffer->containsFetchJoins() ) ) )
+  {
+    int index = 0;
+    maxIndex( mDataProvider->fields(), index );
+    mJoinBuffer->updateFeatureAttributes( f, index, all );
+  }
+
+
   // do not update when we aren't in editing mode
   if ( !mEditable )
     return;
@@ -1523,6 +1534,73 @@
       f.changeAttribute( it.key(), QVariant( QString::null ) );
 }
 
+void QgsVectorLayer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
+    const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
+{
+  const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
+  if ( !memoryCache.isEmpty() ) //use join memory cache
+  {
+    QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
+    bool found = !featureAttributes.isEmpty();
+    QgsAttributeList::const_iterator attIt = attributes.constBegin();
+    for ( ; attIt != attributes.constEnd(); ++attIt )
+    {
+      if ( found )
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
+      }
+      else
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
+      }
+    }
+  }
+  else //work with subset string
+  {
+    QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
+    if ( !joinLayer )
+    {
+      return;
+    }
+
+    //no memory cache, query the joined values by setting substring
+    QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
+    QString bkSubsetString = subsetString;
+    if ( !subsetString.isEmpty() )
+    {
+      subsetString.append( " AND " );
+    }
+
+    subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
+    joinLayer->dataProvider()->setSubsetString( subsetString, false );
+
+    //select (no geometry)
+    joinLayer->select( attributes, QgsRectangle(), false, false );
+
+    //get first feature
+    QgsFeature fet;
+    if ( joinLayer->nextFeature( fet ) )
+    {
+      QgsAttributeMap attMap = fet.attributeMap();
+      QgsAttributeMap::const_iterator attIt = attMap.constBegin();
+      for ( ; attIt != attMap.constEnd(); ++attIt )
+      {
+        f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
+      }
+    }
+    else //no suitable join feature found, insert invalid variants
+    {
+      QgsAttributeList::const_iterator attIt = attributes.constBegin();
+      for ( ; attIt != attributes.constEnd(); ++attIt )
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
+      }
+    }
+
+    joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
+  }
+}
+
 void QgsVectorLayer::updateFeatureGeometry( QgsFeature &f )
 {
   if ( mChangedGeometries.contains( f.id() ) )
@@ -1539,8 +1617,8 @@
   mFetchRect       = rect;
   mFetchAttributes = attributes;
   mFetchGeometry   = fetchGeometries;
-
   mFetchConsidered = mDeletedFeatureIds;
+  QgsAttributeList targetJoinFieldList;
 
   if ( mEditable )
   {
@@ -1551,24 +1629,44 @@
   //look in the normal features of the provider
   if ( mFetchAttributes.size() > 0 )
   {
-    if ( mEditable )
+    if ( mEditable || mJoinBuffer->containsJoins() )
     {
-      // fetch only available field from provider
+      QgsAttributeList joinFields;
+
+      int maxProviderIndex = 0;
+      if ( mDataProvider )
+      {
+        maxIndex( mDataProvider->fields(), maxProviderIndex );
+      }
+
+      mJoinBuffer->select( mFetchAttributes, joinFields, maxProviderIndex );
+      QgsAttributeList::const_iterator joinFieldIt = joinFields.constBegin();
+      for ( ; joinFieldIt != joinFields.constEnd(); ++joinFieldIt )
+      {
+        if ( !mFetchAttributes.contains( *joinFieldIt ) )
+        {
+          mFetchAttributes.append( *joinFieldIt );
+        }
+      }
+
+      //detect which fields are from the provider
       mFetchProvAttributes.clear();
       for ( QgsAttributeList::iterator it = mFetchAttributes.begin(); it != mFetchAttributes.end(); it++ )
       {
-        if ( !mUpdatedFields.contains( *it ) || mAddedAttributeIds.contains( *it ) )
-          continue;
-
-        mFetchProvAttributes << *it;
+        if ( mDataProvider->fields().contains( *it ) )
+        {
+          mFetchProvAttributes << *it;
+        }
       }
 
       mDataProvider->select( mFetchProvAttributes, rect, fetchGeometries, useIntersect );
     }
     else
+    {
       mDataProvider->select( mFetchAttributes, rect, fetchGeometries, useIntersect );
+    }
   }
-  else
+  else //we don't need any attributes at all
   {
     mDataProvider->select( QgsAttributeList(), rect, fetchGeometries, useIntersect );
   }
@@ -1617,6 +1715,7 @@
               {
                 found = true;
                 f.setAttributeMap( it->attributeMap() );
+                updateFeatureAttributes( f );
                 break;
               }
             }
@@ -1678,12 +1777,13 @@
   while ( dataProvider()->nextFeature( f ) )
   {
     if ( mFetchConsidered.contains( f.id() ) )
+    {
       continue;
-
-    if ( mEditable )
-      updateFeatureAttributes( f );
-
-    // found it
+    }
+    if ( mFetchAttributes.size() > 0 )
+    {
+      updateFeatureAttributes( f ); //check joined attributes / changed attributes
+    }
     return true;
   }
 
@@ -2501,10 +2601,10 @@
 
   mEditable = true;
 
-  mUpdatedFields = mDataProvider->fields();
+  mAddedAttributeIds.clear();
+  mDeletedAttributeIds.clear();
+  updateFieldMap();
 
-  mMaxUpdatedIndex = -1;
-
   for ( QgsFieldMap::const_iterator it = mUpdatedFields.begin(); it != mUpdatedFields.end(); it++ )
     if ( it.key() > mMaxUpdatedIndex )
       mMaxUpdatedIndex = it.key();
@@ -2561,12 +2661,21 @@
     }
   }
 
+  //load vector joins
+  if ( !mJoinBuffer )
+  {
+    mJoinBuffer = new QgsVectorLayerJoinBuffer();
+  }
+  mJoinBuffer->readXml( layer_node );
+
   QString errorMsg;
   if ( !readSymbology( layer_node, errorMsg ) )
   {
     return false;
   }
 
+  updateFieldMap();
+
   return mValid;               // should be true if read successfully
 
 } // void QgsVectorLayer::readXml
@@ -2706,6 +2815,9 @@
     layer_node.appendChild( provider );
   }
 
+  //save joins
+  mJoinBuffer->writeXml( layer_node, document );
+
   // renderer specific settings
   QString errorMsg;
   return writeSymbology( layer_node, document, errorMsg );
@@ -3236,12 +3348,12 @@
 
 const QgsFieldMap &QgsVectorLayer::pendingFields() const
 {
-  return isEditable() ? mUpdatedFields : mDataProvider->fields();
+  return mUpdatedFields;
 }
 
 QgsAttributeList QgsVectorLayer::pendingAllAttributesList()
 {
-  return isEditable() ? mUpdatedFields.keys() : mDataProvider->attributeIndexes();
+  return mUpdatedFields.keys();
 }
 
 int QgsVectorLayer::pendingFeatureCount()
@@ -3302,9 +3414,12 @@
   if ( mAddedAttributeIds.size() > 0 )
   {
     QList<QgsField> addedAttributes;
-    for ( QgsAttributeIds::const_iterator it = mAddedAttributeIds.begin(); it != mAddedAttributeIds.end(); it++ )
-      addedAttributes << mUpdatedFields[ *it ];
+    for ( QgsAttributeIds::const_iterator it = mAddedAttributeIds.constBegin(); it != mAddedAttributeIds.constEnd(); it++ )
+    {
+      addedAttributes << mUpdatedFields[*it];
+    }
 
+
     if (( cap & QgsVectorDataProvider::AddAttributes ) && mDataProvider->addAttributes( addedAttributes ) )
     {
       mCommitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributeIds.size() );
@@ -3350,7 +3465,7 @@
     // (otherwise we'll loose updates when doing the remapping)
     if ( mAddedAttributeIds.size() > 0 )
     {
-      for ( QgsAttributeIds::const_iterator it = mAddedAttributeIds.begin(); it != mAddedAttributeIds.end(); it++ )
+      for ( QgsAttributeIds::const_iterator it = mAddedAttributeIds.constBegin(); it != mAddedAttributeIds.constEnd(); it++ )
       {
         QString name =  mUpdatedFields[ *it ].name();
         if ( dst.contains( name ) )
@@ -3532,13 +3647,11 @@
   {
     mEditable = false;
     setModified( false );
-
-    mUpdatedFields.clear();
-    mMaxUpdatedIndex = -1;
     undoStack()->clear();
     emit editingStopped();
   }
 
+  updateFieldMap();
   mDataProvider->updateExtents();
 
   triggerRepaint();
@@ -3590,9 +3703,7 @@
     // Roll back deleted features
     mDeletedFeatureIds.clear();
 
-    // clear private field map
-    mUpdatedFields.clear();
-    mMaxUpdatedIndex = -1;
+    updateFieldMap();
   }
 
   deleteCachedGeometries();
@@ -3643,6 +3754,7 @@
   QgsFeatureList features;
 
   QgsAttributeList allAttrs = mDataProvider->attributeIndexes();
+  mFetchAttributes = pendingAllAttributesList();
 
   for ( QgsFeatureIds::iterator it = mSelectedFeatureIds.begin(); it != mSelectedFeatureIds.end(); ++it )
   {
@@ -4728,6 +4840,99 @@
   return -1;
 }
 
+void QgsVectorLayer::addJoin( QgsVectorJoinInfo joinInfo )
+{
+  mJoinBuffer->addJoin( joinInfo );
+  updateFieldMap();
+}
+
+void QgsVectorLayer::checkJoinLayerRemove( QString theLayerId )
+{
+  removeJoin( theLayerId );
+}
+
+void QgsVectorLayer::removeJoin( const QString& joinLayerId )
+{
+  mJoinBuffer->removeJoin( joinLayerId );
+  updateFieldMap();
+}
+
+const QList< QgsVectorJoinInfo >& QgsVectorLayer::vectorJoins() const
+{
+  return mJoinBuffer->vectorJoins();
+}
+
+void QgsVectorLayer::updateFieldMap()
+{
+  //first backup mAddedAttributes
+  QgsFieldMap bkAddedAttributes;
+  QgsAttributeIds::const_iterator attIdIt = mAddedAttributeIds.constBegin();
+  for ( ; attIdIt != mAddedAttributeIds.constEnd(); ++attIdIt )
+  {
+    bkAddedAttributes.insert( *attIdIt, mUpdatedFields.value( *attIdIt ) );
+  }
+
+  mUpdatedFields = QgsFieldMap();
+  if ( mDataProvider )
+  {
+    mUpdatedFields = mDataProvider->fields();
+  }
+
+  int currentMaxIndex = 0; //maximum index of current layer
+  if ( !maxIndex( mUpdatedFields, currentMaxIndex ) )
+  {
+    return;
+  }
+
+  mMaxUpdatedIndex = currentMaxIndex;
+
+  //joined fields
+  if ( mJoinBuffer->containsJoins() )
+  {
+    mJoinBuffer->updateFieldMap( mUpdatedFields, mMaxUpdatedIndex );
+  }
+
+  //insert added attributes after provider fields and joined fields
+  mAddedAttributeIds.clear();
+  QgsFieldMap::const_iterator fieldIt = bkAddedAttributes.constBegin();
+  for ( ; fieldIt != bkAddedAttributes.constEnd(); ++fieldIt )
+  {
+    ++mMaxUpdatedIndex;
+    mUpdatedFields.insert( mMaxUpdatedIndex, fieldIt.value() );
+    mAddedAttributeIds.insert( mMaxUpdatedIndex );
+
+    //go through the changed attributes map and adapt indices of added attributes
+    for ( int i = 0; i < mChangedAttributeValues.size(); ++i )
+    {
+      updateAttributeMapIndex( mChangedAttributeValues[i], fieldIt.key(), mMaxUpdatedIndex );
+    }
+
+    //go through added features and adapt attribute maps
+    QgsFeatureList::iterator featureIt = mAddedFeatures.begin();
+    for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
+    {
+      QgsAttributeMap attMap = featureIt->attributeMap();
+      updateAttributeMapIndex( attMap, fieldIt.key(), mMaxUpdatedIndex );
+      featureIt->setAttributeMap( attMap );
+    }
+  }
+
+  //remove deleted attributes
+  QgsAttributeIds::const_iterator deletedIt = mDeletedAttributeIds.constBegin();
+  for ( ; deletedIt != mDeletedAttributeIds.constEnd(); ++deletedIt )
+  {
+    mUpdatedFields.remove( *deletedIt );
+  }
+}
+
+void QgsVectorLayer::createJoinCaches()
+{
+  if ( mJoinBuffer->containsJoins() )
+  {
+    mJoinBuffer->createJoinCaches();
+  }
+}
+
 void QgsVectorLayer::stopRendererV2( QgsRenderContext& rendererContext, QgsSingleSymbolRendererV2* selRenderer )
 {
   mRendererV2->stopRender( rendererContext );
@@ -4737,3 +4942,27 @@
     delete selRenderer;
   }
 }
+
+bool QgsVectorLayer::maxIndex( const QgsFieldMap& fMap, int& index ) const
+{
+  if ( fMap.size() < 1 )
+  {
+    return false;
+  }
+  QgsFieldMap::const_iterator endIt = fMap.constEnd();
+  --endIt;
+  index = endIt.key();
+  return true;
+}
+
+void QgsVectorLayer::updateAttributeMapIndex( QgsAttributeMap& map, int oldIndex, int newIndex ) const
+{
+  QgsAttributeMap::const_iterator it = map.find( oldIndex );
+  if ( it == map.constEnd() )
+  {
+    return;
+  }
+
+  map.insert( newIndex, it.value() );
+  map.remove( oldIndex );
+}

Modified: trunk/qgis/src/core/qgsvectorlayer.h
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/qgsvectorlayer.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -30,6 +30,7 @@
 #include "qgssnapper.h"
 #include "qgsfield.h"
 
+
 class QPainter;
 class QImage;
 
@@ -45,8 +46,8 @@
 class QgsVectorDataProvider;
 class QgsVectorOverlay;
 class QgsSingleSymbolRendererV2;
-
 class QgsRectangle;
+class QgsVectorLayerJoinBuffer;
 
 class QgsFeatureRendererV2;
 
@@ -54,7 +55,29 @@
 typedef QSet<int> QgsFeatureIds;
 typedef QSet<int> QgsAttributeIds;
 
+struct CORE_EXPORT QgsVectorJoinInfo
+{
+  /**Join field in the target layer*/
+  int targetField;
+  /**Source layer*/
+  QString joinLayerId;
+  /**Join field in the source layer*/
+  int joinField;
+  /**True if the join is cached in virtual memory*/
+  bool memoryCache;
+  /**Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)*/
+  QHash< QString, QgsAttributeMap> cachedAttributes;
+};
 
+/**Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAttributes().
+  Created in the select() method of QgsVectorLayerJoinBuffer for the joins that contain fetched attributes*/
+struct CORE_EXPORT QgsFetchJoinInfo
+{
+  const QgsVectorJoinInfo* joinInfo;
+  QgsAttributeList attributes; //attributes to fetch
+  int indexOffset; //index offset between this layer and join layer
+};
+
 /** \ingroup core
  * Vector layer backed by a data source provider.
  */
@@ -127,6 +150,16 @@
     /** Setup the coordinate system tranformation for the layer */
     void setCoordinateSystem();
 
+    /**Joins another vector layer to this layer
+      @param joinInfo join object containing join layer id, target and source field
+      @param cacheInMemory if true: caches the content of the join layer in virtual memory*/
+    void addJoin( QgsVectorJoinInfo joinInfo );
+
+    /**Removes  a vector layer join*/
+    void removeJoin( const QString& joinLayerId );
+
+    const QList< QgsVectorJoinInfo >& vectorJoins() const;
+
     /** Get the label object associated with this layer */
     QgsLabel *label();
 
@@ -594,6 +627,14 @@
       @note public and static from version 1.4 */
     static void drawVertexMarker( double x, double y, QPainter& p, QgsVectorLayer::VertexMarkerType type, int vertexSize );
 
+    /**Assembles mUpdatedFields considering provider fields, joined fields and added fields
+     @note added in version 1.6*/
+    void updateFieldMap();
+
+    /**Caches joined attributes if required (and not already done)*/
+    void createJoinCaches();
+
+
   public slots:
     /** Select feature by its ID, optionally emit signal selectionChanged() */
     void select( int featureId, bool emitSignal = true );
@@ -611,6 +652,9 @@
      */
     virtual void updateExtents();
 
+    /**Check if there is a join with a layer that will be removed*/
+    void checkJoinLayerRemove( QString theLayerId );
+
   signals:
 
     /** This signal is emited when selection was changed */
@@ -701,9 +745,19 @@
     /**Reads vertex marker size from settings*/
     static int currentVertexMarkerSize();
 
-    /**Update feature with uncommited attribute updates*/
+    /**Update feature with uncommited attribute updates and joined attributes*/
     void updateFeatureAttributes( QgsFeature &f, bool all = false );
 
+    /**Adds joined attributes to a feature
+      @param f the feature to add the attributes
+      @param joinInfo vector join
+      @param joinFieldName name of the (source) join Field
+      @param joinValue lookup value for join
+      @param attributes (join layer) attribute indices to add
+      @param attributeOffset index offset to get from join layer attribute index to layer index*/
+    void addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName, const QVariant& joinValue,
+                                     const QgsAttributeList& attributes, int attributeIndexOffset );
+
     /**Update feature with uncommited geometry updates*/
     void updateFeatureGeometry( QgsFeature &f );
 
@@ -722,6 +776,13 @@
     /** Stop version 2 renderer and selected renderer (if required) */
     void stopRendererV2( QgsRenderContext& rendererContext, QgsSingleSymbolRendererV2* selRenderer );
 
+    /** Helper function to find out the maximum index of a field map
+        @return true in case of success, otherwise false (e.g. empty map)*/
+    bool maxIndex( const QgsFieldMap& fMap, int& index ) const;
+
+    /**Updates an index in an attribute map to a new value (usually necessary because of a join operation)*/
+    void updateAttributeMapIndex( QgsAttributeMap& map, int oldIndex, int newIndex ) const;
+
   private:                       // Private attributes
 
     /** Update threshold for drawing features as they are read. A value of zero indicates
@@ -846,6 +907,9 @@
     QSet<int> mFetchConsidered;
     QgsGeometryMap::iterator mFetchChangedGeomIt;
     QgsFeatureList::iterator mFetchAddedFeaturesIt;
+
+    //stores information about joined layers
+    QgsVectorLayerJoinBuffer* mJoinBuffer;
 };
 
 #endif

Added: trunk/qgis/src/core/qgsvectorlayerjoinbuffer.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayerjoinbuffer.cpp	                        (rev 0)
+++ trunk/qgis/src/core/qgsvectorlayerjoinbuffer.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -0,0 +1,346 @@
+/***************************************************************************
+                          qgsvectorlayerjoinbuffer.cpp
+                          ----------------------------
+    begin                : Feb 09, 2011
+    copyright            : (C) 2011 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "qgsvectorlayerjoinbuffer.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsvectordataprovider.h"
+
+#include <QDomElement>
+
+QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer()
+{
+}
+
+QgsVectorLayerJoinBuffer::~QgsVectorLayerJoinBuffer()
+{
+}
+
+void QgsVectorLayerJoinBuffer::addJoin( QgsVectorJoinInfo joinInfo )
+{
+  mVectorJoins.push_back( joinInfo );
+
+  //cache joined layer to virtual memory if specified by user
+  if ( joinInfo.memoryCache )
+  {
+    cacheJoinLayer( mVectorJoins.last() );
+  }
+}
+
+void QgsVectorLayerJoinBuffer::removeJoin( const QString& joinLayerId )
+{
+  for ( int i = 0; i < mVectorJoins.size(); ++i )
+  {
+    if ( mVectorJoins.at( i ).joinLayerId == joinLayerId )
+    {
+      mVectorJoins.removeAt( i );
+      return;
+    }
+  }
+}
+
+void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorJoinInfo& joinInfo )
+{
+  //memory cache not required or already done
+  if ( !joinInfo.memoryCache || joinInfo.cachedAttributes.size() > 0 )
+  {
+    return;
+  }
+
+  QgsVectorLayer* cacheLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
+  if ( cacheLayer )
+  {
+    joinInfo.cachedAttributes.clear();
+    cacheLayer->select( cacheLayer->pendingAllAttributesList(), QgsRectangle(), false, false );
+    QgsFeature f;
+    while ( cacheLayer->nextFeature( f ) )
+    {
+      const QgsAttributeMap& map = f.attributeMap();
+      joinInfo.cachedAttributes.insert( map.value( joinInfo.joinField ).toString(), map );
+    }
+  }
+}
+
+void QgsVectorLayerJoinBuffer::updateFieldMap( QgsFieldMap& fields, int& maxIndex )
+{
+  int currentMaxIndex = 0; //maximum index of the current join layer
+
+  QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
+  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
+  {
+    QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
+    if ( !joinLayer )
+    {
+      continue;
+    }
+
+    const QgsFieldMap& joinFields = joinLayer->pendingFields();
+    QgsFieldMap::const_iterator fieldIt = joinFields.constBegin();
+    for ( ; fieldIt != joinFields.constEnd(); ++fieldIt )
+    {
+      fields.insert( maxIndex + 1 + fieldIt.key(), fieldIt.value() );
+    }
+
+    if ( maximumIndex( joinFields, currentMaxIndex ) )
+    {
+      maxIndex += ( currentMaxIndex + 1 ); //+1 because there are fields with index 0
+    }
+  }
+}
+
+void QgsVectorLayerJoinBuffer::createJoinCaches()
+{
+  QList< QgsVectorJoinInfo >::iterator joinIt = mVectorJoins.begin();
+  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
+  {
+    cacheJoinLayer( *joinIt );
+  }
+}
+
+void QgsVectorLayerJoinBuffer::select( const QgsAttributeList& fetchAttributes,
+                                       QgsAttributeList& sourceJoinFields, int maxProviderIndex )
+{
+  mFetchJoinInfos.clear();
+  sourceJoinFields.clear();
+
+  QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin();
+  for ( ; attIt != fetchAttributes.constEnd(); ++attIt )
+  {
+    int indexOffset;
+    const QgsVectorJoinInfo* joinInfo = joinForFieldIndex( *attIt, maxProviderIndex, indexOffset );
+    if ( joinInfo )
+    {
+      QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
+      if ( joinLayer )
+      {
+        mFetchJoinInfos[ joinLayer ].joinInfo = joinInfo;
+        mFetchJoinInfos[ joinLayer].attributes.push_back( *attIt - indexOffset ); //store provider index
+        mFetchJoinInfos[ joinLayer ].indexOffset = indexOffset;
+        //for joined fields, we always need to request the targetField from the provider too
+        if ( !fetchAttributes.contains( joinInfo->targetField ) )
+        {
+          sourceJoinFields << joinInfo->targetField;
+        }
+      }
+    }
+  }
+}
+
+void QgsVectorLayerJoinBuffer::updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all )
+{
+  if ( all )
+  {
+    int index = maxProviderIndex + 1;
+    int currentMaxIndex;
+
+    QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
+    for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
+    {
+      QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
+      if ( !joinLayer )
+      {
+        continue;
+      }
+
+      QString joinFieldName = joinLayer->pendingFields().value( joinIt->joinField ).name();
+      if ( joinFieldName.isEmpty() )
+      {
+        continue;
+      }
+
+      QVariant targetFieldValue = f.attributeMap().value( joinIt->targetField );
+      if ( !targetFieldValue.isValid() )
+      {
+        continue;
+      }
+
+      addJoinedFeatureAttributes( f, *joinIt, joinFieldName, targetFieldValue, joinLayer->pendingAllAttributesList(), index );
+
+      maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
+      index += ( currentMaxIndex + 1 );
+    }
+  }
+  else
+  {
+    QMap<QgsVectorLayer*, QgsFetchJoinInfo>::const_iterator joinIt = mFetchJoinInfos.constBegin();
+    for ( ; joinIt != mFetchJoinInfos.constEnd(); ++joinIt )
+    {
+      QgsVectorLayer* joinLayer = joinIt.key();
+      if ( !joinLayer )
+      {
+        continue;
+      }
+
+      QString joinFieldName = joinLayer->pendingFields().value( joinIt.value().joinInfo->joinField ).name();
+      if ( joinFieldName.isEmpty() )
+      {
+        continue;
+      }
+
+      QVariant targetFieldValue = f.attributeMap().value( joinIt->joinInfo->targetField );
+      if ( !targetFieldValue.isValid() )
+      {
+        continue;
+      }
+
+      addJoinedFeatureAttributes( f, *( joinIt.value().joinInfo ), joinFieldName, targetFieldValue, joinIt.value().attributes, joinIt.value().indexOffset );
+    }
+  }
+}
+
+void QgsVectorLayerJoinBuffer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
+    const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
+{
+  const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
+  if ( !memoryCache.isEmpty() ) //use join memory cache
+  {
+    QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
+    bool found = !featureAttributes.isEmpty();
+    QgsAttributeList::const_iterator attIt = attributes.constBegin();
+    for ( ; attIt != attributes.constEnd(); ++attIt )
+    {
+      if ( found )
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
+      }
+      else
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
+      }
+    }
+  }
+  else //work with subset string
+  {
+    QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
+    if ( !joinLayer )
+    {
+      return;
+    }
+
+    //no memory cache, query the joined values by setting substring
+    QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
+    QString bkSubsetString = subsetString;
+    if ( !subsetString.isEmpty() )
+    {
+      subsetString.append( " AND " );
+    }
+
+    subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
+    joinLayer->dataProvider()->setSubsetString( subsetString, false );
+
+    //select (no geometry)
+    joinLayer->select( attributes, QgsRectangle(), false, false );
+
+    //get first feature
+    QgsFeature fet;
+    if ( joinLayer->nextFeature( fet ) )
+    {
+      QgsAttributeMap attMap = fet.attributeMap();
+      QgsAttributeMap::const_iterator attIt = attMap.constBegin();
+      for ( ; attIt != attMap.constEnd(); ++attIt )
+      {
+        f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
+      }
+    }
+    else //no suitable join feature found, insert invalid variants
+    {
+      QgsAttributeList::const_iterator attIt = attributes.constBegin();
+      for ( ; attIt != attributes.constEnd(); ++attIt )
+      {
+        f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
+      }
+    }
+
+    joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
+  }
+}
+
+void QgsVectorLayerJoinBuffer::writeXml( QDomNode& layer_node, QDomDocument& document ) const
+{
+  QDomElement vectorJoinsElem = document.createElement( "vectorjoins" );
+  layer_node.appendChild( vectorJoinsElem );
+  QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
+  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
+  {
+    QDomElement joinElem = document.createElement( "join" );
+    joinElem.setAttribute( "targetField", joinIt->targetField );
+    joinElem.setAttribute( "joinLayerId", joinIt->joinLayerId );
+    joinElem.setAttribute( "joinField", joinIt->joinField );
+    joinElem.setAttribute( "memoryCache", !joinIt->cachedAttributes.isEmpty() );
+    vectorJoinsElem.appendChild( joinElem );
+  }
+}
+
+void QgsVectorLayerJoinBuffer::readXml( QDomNode& layer_node )
+{
+  mVectorJoins.clear();
+  QDomElement vectorJoinsElem = layer_node.firstChildElement( "vectorjoins" );
+  if ( !vectorJoinsElem.isNull() )
+  {
+    QDomNodeList joinList = vectorJoinsElem.elementsByTagName( "join" );
+    for ( int i = 0; i < joinList.size(); ++i )
+    {
+      QDomElement infoElem = joinList.at( i ).toElement();
+      QgsVectorJoinInfo info;
+      info.joinField = infoElem.attribute( "joinField" ).toInt();
+      info.joinLayerId = infoElem.attribute( "joinLayerId" );
+      info.targetField = infoElem.attribute( "targetField" ).toInt();
+      info.memoryCache = infoElem.attribute( "memoryCache" ).toInt();
+      addJoin( info );
+    }
+  }
+}
+
+const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const
+{
+  int currentMaxIndex = 0;
+  int totIndex = maxProviderIndex + 1;
+
+  //go through all the joins to search the index
+  QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
+  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
+  {
+    QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
+    if ( !joinLayer )
+    {
+      continue;
+    }
+
+    if ( joinLayer->pendingFields().contains( index - totIndex ) )
+    {
+      indexOffset = totIndex;
+      return &( *joinIt );
+    }
+
+    maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
+    totIndex += ( currentMaxIndex + 1 );
+  }
+
+  //an added field or a provider field
+  return 0;
+}
+
+bool QgsVectorLayerJoinBuffer::maximumIndex( const QgsFieldMap& fMap, int& index )
+{
+  if ( fMap.size() < 1 )
+  {
+    return false;
+  }
+  QgsFieldMap::const_iterator endIt = fMap.constEnd();
+  --endIt;
+  index = endIt.key();
+  return true;
+}

Added: trunk/qgis/src/core/qgsvectorlayerjoinbuffer.h
===================================================================
--- trunk/qgis/src/core/qgsvectorlayerjoinbuffer.h	                        (rev 0)
+++ trunk/qgis/src/core/qgsvectorlayerjoinbuffer.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -0,0 +1,104 @@
+/***************************************************************************
+                          qgsvectorlayerjoinbuffer.h
+                          ---------------------------
+    begin                : Feb 09, 2011
+    copyright            : (C) 2011 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 QGSVECTORLAYERJOINBUFFER_H
+#define QGSVECTORLAYERJOINBUFFER_H
+
+#include "qgsfeature.h"
+#include "qgsvectorlayer.h"
+
+#include <QHash>
+#include <QString>
+
+/**Manages joined field for a vector layer*/
+class CORE_EXPORT QgsVectorLayerJoinBuffer
+{
+  public:
+    QgsVectorLayerJoinBuffer();
+    ~QgsVectorLayerJoinBuffer();
+
+    /**Joins another vector layer to this layer
+      @param joinInfo join object containing join layer id, target and source field
+      @param cacheInMemory if true: caches the content of the join layer in virtual memory*/
+    void addJoin( QgsVectorJoinInfo joinInfo );
+
+    /**Removes  a vector layer join*/
+    void removeJoin( const QString& joinLayerId );
+
+    /**Creates QgsVectorLayerJoinBuffer for the joins containing attributes to fetch*/
+    void select( const QgsAttributeList& fetchAttributes,
+                 QgsAttributeList& sourceJoinFields, int maxProviderIndex );
+
+    /**Updates field map with joined attributes
+      @param fields map to append joined attributes
+      @param maxIndex in/out: maximum attribute index*/
+    void updateFieldMap( QgsFieldMap& fields, int& maxIndex );
+
+    /**Update feature with uncommited attribute updates and joined attributes*/
+    void updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all = false );
+
+    /**Calls cacheJoinLayer() for all vector joins*/
+    void createJoinCaches();
+
+    /**Saves mVectorJoins to xml under the layer node*/
+    void writeXml( QDomNode& layer_node, QDomDocument& document ) const;
+
+    /**Reads joins from project file*/
+    void readXml( QDomNode& layer_node );
+
+    /**Quick way to test if there is any join at all*/
+    bool containsJoins() const { return ( mVectorJoins.size() > 0 ); }
+    /**Quick way to test if there is a join to be fetched*/
+    bool containsFetchJoins() const { return ( mFetchJoinInfos.size() > 0 ); }
+
+    const QList< QgsVectorJoinInfo >& vectorJoins() const { return mVectorJoins; }
+
+    /** Helper function to find out the maximum index of a field map
+        @return true in case of success, otherwise false (e.g. empty map)*/
+    static bool maximumIndex( const QgsFieldMap& fMap, int& index );
+
+  private:
+
+    /**Joined vector layers*/
+    QList< QgsVectorJoinInfo > mVectorJoins;
+
+    /**Informations about joins used in the current select() statement.
+      Allows faster mapping of attribute ids compared to mVectorJoins*/
+    QMap<QgsVectorLayer*, QgsFetchJoinInfo> mFetchJoinInfos;
+
+    /**Caches attributes of join layer in memory if QgsVectorJoinInfo.memoryCache is true (and the cache is not already there)*/
+    void cacheJoinLayer( QgsVectorJoinInfo& joinInfo );
+
+    /**Adds joined attributes to a feature
+      @param f the feature to add the attributes
+      @param joinInfo vector join
+      @param joinFieldName name of the (source) join Field
+      @param joinValue lookup value for join
+      @param attributes (join layer) attribute indices to add
+      @param attributeOffset index offset to get from join layer attribute index to layer index*/
+    void addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName, const QVariant& joinValue,
+                                     const QgsAttributeList& attributes, int attributeIndexOffset );
+
+    /**Finds the vector join for a layer field index.
+      @param index this layers attribute index
+      @param maxProviderIndex maximum attribute index of the vectorlayer provider
+      @param indexOffset out: offset between layer index and original provider index
+       @return pointer to the join if the index belongs to a joined field, otherwise 0 (possibily provider field or added field)*/
+    const QgsVectorJoinInfo* joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const;
+};
+
+#endif // QGSVECTORLAYERJOINBUFFER_H

Modified: trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
===================================================================
--- trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -65,6 +65,10 @@
 
 void QgsSingleSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
 {
+  if ( !mSymbol )
+  {
+    return;
+  }
   mRotationFieldIdx  = mRotationField.isEmpty()  ? -1 : vlayer->fieldNameIndex( mRotationField );
   mSizeScaleFieldIdx = mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField );
 
@@ -99,6 +103,10 @@
 
 void QgsSingleSymbolRendererV2::stopRender( QgsRenderContext& context )
 {
+  if ( !mSymbol )
+  {
+    return;
+  }
   mSymbol->stopRender( context );
 
   if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
@@ -134,7 +142,14 @@
 
 QString QgsSingleSymbolRendererV2::dump()
 {
-  return QString( "SINGLE: %1" ).arg( mSymbol->dump() );
+  if ( mSymbol )
+  {
+    return QString( "SINGLE: %1" ).arg( mSymbol->dump() );
+  }
+  else
+  {
+    return "";
+  }
 }
 
 QgsFeatureRendererV2* QgsSingleSymbolRendererV2::clone()
@@ -205,10 +220,12 @@
 
 QgsLegendSymbologyList QgsSingleSymbolRendererV2::legendSymbologyItems( QSize iconSize )
 {
-  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol, iconSize );
-
   QgsLegendSymbologyList lst;
-  lst << qMakePair( QString(), pix );
+  if ( mSymbol )
+  {
+    QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol, iconSize );
+    lst << qMakePair( QString(), pix );
+  }
   return lst;
 }
 

Modified: trunk/qgis/src/providers/ogr/qgsogrprovider.cpp
===================================================================
--- trunk/qgis/src/providers/ogr/qgsogrprovider.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/ogr/qgsogrprovider.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -229,7 +229,7 @@
   }
 }
 
-bool QgsOgrProvider::setSubsetString( QString theSQL )
+bool QgsOgrProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
 {
   QgsCPLErrorHandler handler;
 
@@ -287,7 +287,10 @@
 
   // getting the total number of features in the layer
   // TODO: This can be expensive, do we really need it!
-  recalculateFeatureCount();
+  if ( updateFeatureCount )
+  {
+    recalculateFeatureCount();
+  }
 
   // check the validity of the layer
   QgsDebugMsg( "checking validity" );
@@ -1046,6 +1049,20 @@
   return indexfile.exists();
 }
 
+bool QgsOgrProvider::createAttributeIndex( int field )
+{
+  QString layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrOrigLayer ) );
+  QString dropSql = QString( "DROP INDEX ON %1" ).arg( quotedIdentifier( layerName ) );
+  QString createSql = QString( "CREATE INDEX ON %1 USING %2" ).arg( quotedIdentifier( layerName ) ).arg( fields()[field].name() );
+  OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( dropSql ).data(), OGR_L_GetSpatialFilter( ogrOrigLayer ), "SQL" );
+  OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( createSql ).data(), OGR_L_GetSpatialFilter( ogrOrigLayer ), "SQL" );
+
+  QFileInfo fi( mFilePath );     // to get the base name
+  //find out, if the .idm file is there
+  QFile indexfile( fi.path().append( "/" ).append( fi.completeBaseName() ).append( ".idm" ) );
+  return indexfile.exists();
+}
+
 bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds & id )
 {
   QgsCPLErrorHandler handler;
@@ -1182,6 +1199,7 @@
       // adding attributes was added in GDAL 1.6
       ability |= AddAttributes;
 #endif
+      ability |= CreateAttributeIndex;
 
       if ( mAttributeFields.size() == 0 )
       {
@@ -1816,7 +1834,14 @@
 
 void QgsOgrProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, int limit )
 {
-  QgsField fld = mAttributeFields[index];
+  uniqueValues.clear();
+
+  QgsField fld = mAttributeFields.value( index );
+  if ( fld.name().isNull() )
+  {
+    return; //not a provider field
+  }
+
   QString theLayerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrLayer ) );
 
   QString sql = QString( "SELECT DISTINCT %1 FROM %2" )
@@ -1830,8 +1855,6 @@
 
   sql += QString( " ORDER BY %1" ).arg( fld.name() );
 
-  uniqueValues.clear();
-
   QgsDebugMsg( QString( "SQL: %1" ).arg( sql ) );
   OGRLayerH l = OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( sql ).data(), NULL, "SQL" );
   if ( l == 0 )

Modified: trunk/qgis/src/providers/ogr/qgsogrprovider.h
===================================================================
--- trunk/qgis/src/providers/ogr/qgsogrprovider.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/ogr/qgsogrprovider.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -98,7 +98,7 @@
     virtual bool supportsSubsetString() { return true; }
 
     /** mutator for sql where clause used to limit dataset size */
-    virtual bool setSubsetString( QString theSQL );
+    virtual bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
 
     /**
      * Get feature type.
@@ -155,6 +155,9 @@
      @return true in case of success*/
     virtual bool createSpatialIndex();
 
+    /**Create an attribute index on the datasource*/
+    virtual bool createAttributeIndex( int field );
+
     /** Returns a bitmask containing the supported capabilities
         Note, some capabilities may change depending on whether
         a spatial filter is active on this provider, so it may

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -2793,7 +2793,7 @@
   return enabledCapabilities;
 }
 
-bool QgsPostgresProvider::setSubsetString( QString theSQL )
+bool QgsPostgresProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
 {
   QString prevWhere = sqlWhereClause;
 
@@ -2828,7 +2828,10 @@
   // uri? Perhaps this needs some rationalisation.....
   setDataSourceUri( mUri.uri() );
 
-  featuresCounted = -1;
+  if ( updateFeatureCount )
+  {
+    featuresCounted = -1;
+  }
   layerExtent.setMinimal();
 
   return true;

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.h
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -250,7 +250,7 @@
     QString subsetString();
 
     /** mutator for sql where clause used to limit dataset size */
-    bool setSubsetString( QString theSQL );
+    bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
 
     virtual bool supportsSubsetString() { return true; }
 

Modified: trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.cpp
===================================================================
--- trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.cpp	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.cpp	2011-02-11 16:23:17 UTC (rev 15155)
@@ -560,7 +560,7 @@
   return mSubsetString;
 }
 
-bool QgsSpatiaLiteProvider::setSubsetString( QString theSQL )
+bool QgsSpatiaLiteProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
 {
   QString prevSubsetString = mSubsetString;
   mSubsetString = theSQL;
@@ -571,8 +571,10 @@
   setDataSourceUri( uri.uri() );
 
   // update feature count and extents
-  if ( getTableSummary() )
+  if ( updateFeatureCount && getTableSummary() )
+  {
     return true;
+  }
 
   mSubsetString = prevSubsetString;
 

Modified: trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.h
===================================================================
--- trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.h	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/providers/spatialite/qgsspatialiteprovider.h	2011-02-11 16:23:17 UTC (rev 15155)
@@ -80,7 +80,7 @@
     virtual QString subsetString();
 
     /** mutator for sql where clause used to limit dataset size */
-    virtual bool setSubsetString( QString theSQL );
+    virtual bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
 
     virtual bool supportsSubsetString() { return true; }
 

Copied: trunk/qgis/src/ui/qgsaddjoindialogbase.ui (from rev 14132, branches/table_join_branch/src/ui/qgsaddjoindialogbase.ui)
===================================================================
--- trunk/qgis/src/ui/qgsaddjoindialogbase.ui	                        (rev 0)
+++ trunk/qgis/src/ui/qgsaddjoindialogbase.ui	2011-02-11 16:23:17 UTC (rev 15155)
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QgsAddJoinDialogBase</class>
+ <widget class="QDialog" name="QgsAddJoinDialogBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>281</width>
+    <height>160</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Add vector join</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="mJoinLayerLabel">
+     <property name="text">
+      <string>Join layer: </string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QComboBox" name="mJoinLayerComboBox"/>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="mJoinFieldLabel">
+     <property name="text">
+      <string>Join field:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QComboBox" name="mJoinFieldComboBox"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="mTargetFieldLabel">
+     <property name="text">
+      <string>Target field:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QComboBox" name="mTargetFieldComboBox"/>
+   </item>
+   <item row="5" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QCheckBox" name="mCreateIndexCheckBox">
+     <property name="text">
+      <string>Create attribute index on join field</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QCheckBox" name="mCacheInMemoryCheckBox">
+     <property name="text">
+      <string>Cache join layer in virtual memory</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>QgsAddJoinDialogBase</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>QgsAddJoinDialogBase</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

Modified: trunk/qgis/src/ui/qgsvectorlayerpropertiesbase.ui
===================================================================
--- trunk/qgis/src/ui/qgsvectorlayerpropertiesbase.ui	2011-02-10 21:46:31 UTC (rev 15154)
+++ trunk/qgis/src/ui/qgsvectorlayerpropertiesbase.ui	2011-02-11 16:23:17 UTC (rev 15155)
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>668</width>
+    <width>591</width>
     <height>426</height>
    </rect>
   </property>
@@ -372,8 +372,8 @@
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>384</width>
-            <height>408</height>
+            <width>432</width>
+            <height>419</height>
            </rect>
           </property>
           <layout class="QGridLayout" name="gridLayout_3">
@@ -611,6 +611,74 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="mJoinPage">
+      <attribute name="icon">
+       <iconset resource="../../images/images.qrc">
+        <normaloff>:/images/themes/default/propertyicons/join.png</normaloff>:/images/themes/default/propertyicons/join.png</iconset>
+      </attribute>
+      <attribute name="title">
+       <string>Joins</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_10">
+       <item row="0" column="0">
+        <widget class="QPushButton" name="mButtonAddJoin">
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset resource="../../images/images.qrc">
+           <normaloff>:/images/themes/default/symbologyAdd.png</normaloff>:/images/themes/default/symbologyAdd.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QPushButton" name="mButtonRemoveJoin">
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset resource="../../images/images.qrc">
+           <normaloff>:/images/themes/default/symbologyRemove.png</normaloff>:/images/themes/default/symbologyRemove.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>354</width>
+           <height>23</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="0" colspan="3">
+        <widget class="QTreeWidget" name="mJoinTreeWidget">
+         <property name="columnCount">
+          <number>3</number>
+         </property>
+         <column>
+          <property name="text">
+           <string>Join layer</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Join field</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Target field</string>
+          </property>
+         </column>
+        </widget>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
   </layout>



More information about the QGIS-commit mailing list