927 changes: 927 additions & 0 deletions images/themes/gis/mActionFilter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
952 changes: 952 additions & 0 deletions images/themes/gis/mIconExpressionFilter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
974 changes: 974 additions & 0 deletions images/themes/gis/mIconExpressionPreview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
935 changes: 935 additions & 0 deletions images/themes/gis/mIconExpressionSelect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
947 changes: 947 additions & 0 deletions images/themes/gis/mIconSelectAdd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
938 changes: 938 additions & 0 deletions images/themes/gis/mIconSelectIntersect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
948 changes: 948 additions & 0 deletions images/themes/gis/mIconSelectRemove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions python/core/core.sip
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
%Include qgsvectordataprovider.sip
%Include qgsvectorfilewriter.sip
%Include qgsvectorlayer.sip
%Include qgsvectorlayercache.sip
%Include qgsvectorlayereditbuffer.sip
%Include qgsvectorlayerimport.sip
%Include qgsvectorlayerjoinbuffer.sip
Expand Down
31 changes: 31 additions & 0 deletions python/core/qgsvectorlayercache.sip
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* This class caches features of a given QgsVectorLayer.
* The features are automatically updated and/or invalidated, whenever a change happens to the feature.
*
* It is the callers responsibility to make sure, that he specifies all required fields in every select operation.
* There is no guarantee, that a cached feature will contain a field not specified in a previous select operation.
*
* Performance:
* Depending on the usage scenario, it can be a good idea to prepopulate the cache by calling select, nextFeature and
* and removeCachedFeature. This is especially interesting, whenever requesting a single feature from the dataProvider
* takes much longer than querying in bunches (e.g. network latency, slow views in the database...)
*/

class QgsVectorLayerCache : QObject
{
%TypeHeaderCode
#include "qgsvectorlayercache.h"
%End

public:
QgsVectorLayerCache( QgsVectorLayer* layer, int cacheSize, QObject* parent = NULL );

/**
* Sets the maximum number of features to keep in the cache. Some features will be removed from
* the cache if the number is smaller than the previous size of the cache.
*
* @param cacheSize indicates the maximum number of features to keep in the cache
*/
void setCacheSize( int cacheSize );
};

164 changes: 0 additions & 164 deletions python/gui/attributetable/qgsattributetablemodel.sip

This file was deleted.

11 changes: 7 additions & 4 deletions python/gui/attributetable/qgsattributetableview.sip
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ class QgsAttributeTableView : QTableView
virtual ~QgsAttributeTableView();

/**
* Sets the layer
* @param canvas canvas pointer
* @param layer layer pointer
* Autocreates the models
* @param layerCache The {@link QgsVectorLayerCache} to use ( as backend )
* @param canvas The {@link QgsMapCanvas} to use ( for the currently visible features filter )
*
* @deprecated
*/
void setCanvasAndLayer( QgsMapCanvas *canvas, QgsVectorLayer *layer );
void setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVectorLayerCache *layerCache );

protected:
/**
* Saves geometry to the settings on close
* @param event not used
Expand Down
1 change: 0 additions & 1 deletion python/gui/gui.sip
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
%Include qgstextannotationitem.sip
%Include qgsvertexmarker.sip

%Include attributetable/qgsattributetablemodel.sip
%Include attributetable/qgsattributetableview.sip

%Include raster/qgsmultibandcolorrendererwidget.sip
Expand Down
3 changes: 0 additions & 3 deletions src/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ SET(QGIS_APP_SRCS
qgsaddjoindialog.cpp
qgsannotationwidget.cpp
qgsattributeactiondialog.cpp
qgsattributedialog.cpp
qgsattributetypedialog.cpp
qgsattributetypeloaddialog.cpp
qgsattributetabledialog.cpp
Expand Down Expand Up @@ -113,7 +112,6 @@ SET(QGIS_APP_SRCS
qgstipfactory.cpp
qgsuniquevaluedialog.cpp
qgsvectorlayerproperties.cpp
qgshighlight.cpp
qgshandlebadlayers.cpp

composer/qgsattributeselectiondialog.cpp
Expand Down Expand Up @@ -170,7 +168,6 @@ SET (QGIS_APP_MOC_HDRS
qgsaddtaborgroup.h
qgsannotationwidget.h
qgsattributeactiondialog.h
qgsattributedialog.h
qgsattributetypedialog.h
qgsattributetypeloaddialog.h
qgsattributetabledialog.h
Expand Down
23 changes: 23 additions & 0 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
#include "qgserror.h"
#include "qgserrordialog.h"
#include "qgsexception.h"
#include "qgsexpressionselectiondialog.h"
#include "qgsfeature.h"
#include "qgsformannotationitem.h"
#include "qgshtmlannotationitem.h"
Expand Down Expand Up @@ -942,6 +943,7 @@ void QgisApp::createActions()
connect( mActionSelectFreehand, SIGNAL( triggered() ), this, SLOT( selectByFreehand() ) );
connect( mActionSelectRadius, SIGNAL( triggered() ), this, SLOT( selectByRadius() ) );
connect( mActionDeselectAll, SIGNAL( triggered() ), this, SLOT( deselectAll() ) );
connect( mActionSelectByExpression, SIGNAL( triggered() ), this, SLOT( selectByExpression() ) );
connect( mActionIdentify, SIGNAL( triggered() ), this, SLOT( identify() ) );
connect( mActionFeatureAction, SIGNAL( triggered() ), this, SLOT( doFeatureAction() ) );
connect( mActionMeasure, SIGNAL( triggered() ), this, SLOT( measure() ) );
Expand Down Expand Up @@ -1743,6 +1745,7 @@ void QgisApp::setTheme( QString theThemeName )
mActionSelectFreehand->setIcon( QgsApplication::getThemeIcon( "/mActionSelectFreehand.png" ) );
mActionSelectRadius->setIcon( QgsApplication::getThemeIcon( "/mActionSelectRadius.png" ) );
mActionDeselectAll->setIcon( QgsApplication::getThemeIcon( "/mActionDeselectAll.png" ) );
mActionSelectByExpression->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionSelect.svg" ) );
mActionOpenTable->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
mActionMeasure->setIcon( QgsApplication::getThemeIcon( "/mActionMeasure.png" ) );
mActionMeasureArea->setIcon( QgsApplication::getThemeIcon( "/mActionMeasureArea.png" ) );
Expand Down Expand Up @@ -5228,6 +5231,26 @@ void QgisApp::deselectAll()
mMapCanvas->setRenderFlag( true );
}

void QgisApp::selectByExpression()
{
QgsVectorLayer* vlayer = NULL;
if ( !mMapCanvas->currentLayer()
|| NULL == ( vlayer = qobject_cast<QgsVectorLayer *>( mMapCanvas->currentLayer() ) ) )
{
messageBar()->pushMessage(
QObject::tr( "No active vector layer" ),
QObject::tr( "To select features, choose a vector layer in the legend" ),
QgsMessageBar::INFO,
messageTimeout() );
}
else
{
QgsExpressionSelectionDialog* dlg = new QgsExpressionSelectionDialog( vlayer );
dlg->setAttribute( Qt::WA_DeleteOnClose );
dlg->show();
}
}

void QgisApp::addRing()
{
if ( mMapCanvas && mMapCanvas->isDrawing() )
Expand Down
3 changes: 3 additions & 0 deletions src/app/qgisapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,9 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
//! deselect features from all layers
void deselectAll();

//! select features by expression
void selectByExpression();

//! refresh map canvas
void refreshMapCanvas();

Expand Down
9 changes: 8 additions & 1 deletion src/app/qgisappinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgslegend.h"
#include "qgsshortcutsmanager.h"
#include "qgsattributedialog.h"
Expand Down Expand Up @@ -535,7 +536,13 @@ bool QgisAppInterface::openFeatureForm( QgsVectorLayer *vlayer, QgsFeature &f, b

QDialog* QgisAppInterface::getFeatureForm( QgsVectorLayer *l, QgsFeature &f )
{
QgsAttributeDialog *dialog = new QgsAttributeDialog( l, &f, false );
QgsDistanceArea myDa;

myDa.setSourceCrs( l->crs().srsid() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );

QgsAttributeDialog *dialog = new QgsAttributeDialog( l, &f, false, myDa );
return dialog->dialog();
}

Expand Down
840 changes: 297 additions & 543 deletions src/app/qgsattributetabledialog.cpp

Large diffs are not rendered by default.

126 changes: 32 additions & 94 deletions src/app/qgsattributetabledialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "ui_qgsattributetabledialog.h"
#include "qgscontexthelp.h"

#include "qgsattributedialog.h"
#include "qgsvectorlayer.h" //QgsFeatureIds

class QDialogButtonBox;
Expand All @@ -34,7 +35,7 @@ class QLineEdit;
class QComboBox;
class QMenu;
class QDockWidget;
class QProgressDialog;
class QSignalMapper;

class QgsAttributeTableModel;
class QgsAttributeTableFilterModel;
Expand All @@ -54,60 +55,23 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWidget *parent = 0, Qt::WindowFlags flags = Qt::Window );
~QgsAttributeTableDialog();

/**
* Sets the filter expression to filter visible features
* @param filterString filter query string. QgsExpression compatible.
*/
void setFilterExpression( QString filterString );

public slots:
/**
* Toggles editing mode
*/
void editingToggled();

void viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex );

void progress( int i, bool &cancel );
void finished();

private slots:
/**
* Launches search
*/
void search();
/**
* Launches advanced search
*/
void on_mAdvancedSearchButton_clicked();
/**
* Updates the selection
*/
void updateSelection();
/**
* Reads the selection from the layer
*/
void updateSelectionFromLayer();
/**
* Updates selection of a row
*/
void updateRowSelection( int index );
/**
* Updates the index pressed
*/
void updateRowPressed( int index );
/**
* Updates selection of specified rows
* @param first first row
* @param last last row
* @param clickType 0:Single click, 1:Shift, 2:Ctrl, 3:dragged click
*/
void updateRowSelection( int first, int last, int clickType );

/**
* Toggle showing of selected line only
* @param theFlag toggle on if true
*/
void on_cbxShowSelectedOnly_toggled( bool theFlag );
/**
* Copies selected rows to the clipboard
*/
void on_mCopySelectedRowsButton_clicked();

/**
* Toggles editing mode
*/
Expand All @@ -116,6 +80,8 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
* Saves edits
*/
void on_mSaveEditsButton_clicked();

void on_mTableView_selectionChangeFinished( const QgsFeatureIds &selectedFeatures );
/**
* Inverts selection
*/
Expand All @@ -135,15 +101,16 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
/**
* Moves selected lines to the top
*/
void on_mSelectedToTopButton_clicked();
void on_mSelectedToTopButton_toggled();

/**
* Shows advanced actions
* Opens dialog to add new attribute
*/
void showAdvanced();

/**Opens dialog to add new attribute*/
void on_mAddAttribute_clicked();
/**Opens dialog to remove attribute*/

/**
* Opens dialog to remove attribute
*/
void on_mRemoveAttribute_clicked();
/**
* Opens field calculator dialog
Expand All @@ -158,10 +125,20 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
/**
* add feature
*/
void addFeature();
void on_mAddFeature_clicked();

void on_mHelpButton_clicked() { QgsContextHelp::run( metaObject()->className() ); }

void on_mExpressionSelectButton_clicked();
void filterColumnChanged( QObject* filterAction );
void filterExpressionBuilder();
void filterShowAll();
void filterSelected();
void filterVisible();
void filterEdited();
void filterQueryChanged( const QString& query );
void filterQueryAccepted();

signals:
/**
* Informs that editing mode has been toggled
Expand All @@ -187,60 +164,21 @@ class QgsAttributeTableDialog : public QDialog, private Ui::QgsAttributeTableDia
* Initialize column box
*/
void columnBoxInit();
/**
* Returns id of a column
*/
int columnBoxColumnId();
/**
* Performs the search
* @param searchString search query string
*/
void doSearch( QString searchString );

/**
* update window title
*/
void updateTitle();

QLineEdit *mQuery;
QComboBox *mColumnBox;

QMenu* mMenuActions;
QAction* mActionToggleEditing;

QgsAttributeTableModel *mModel;
QgsAttributeTableFilterModel *mFilterModel;
QDockWidget *mDock;
QgsVectorLayer *mLayer;
QProgressDialog *mProgress;
QTime mStarted;
bool mWorkaround;
QgsFeatureIds mSelectedFeatures;
int mIndexPressed;

QItemSelectionModel* mSelectionModel;
int mLastClickedHeaderIndex;
};

QDockWidget* mDock;

class QgsAttributeTableAction : public QAction
{
Q_OBJECT
QMenu* mFilterColumnsMenu;
QSignalMapper* mFilterActionMapper;

public:
QgsAttributeTableAction( const QString &name, QgsAttributeTableView *view, QgsAttributeTableModel *model, int action, const QModelIndex &fieldIdx ) :
QAction( name, view ), mModel( model ), mAction( action ), mFieldIdx( fieldIdx )
{}

public slots:
void execute();
void featureForm();

private:
QgsAttributeTableModel *mModel;
int mAction;
QModelIndex mFieldIdx;
QgsVectorLayer* mLayer;
};


#endif
22 changes: 21 additions & 1 deletion src/app/qgsdiagramproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer* layer, QWidget* pare

setupUi( this );


mBackgroundColorButton->setColorDialogTitle( tr( "Background color" ) );
mBackgroundColorButton->setColorDialogOptions( QColorDialog::ShowAlphaChannel );
mDiagramPenColorButton->setColorDialogTitle( tr( "Pen color" ) );
Expand Down Expand Up @@ -112,6 +111,11 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer* layer, QWidget* pare
mDataDefinedXComboBox->addItem( tr( "None" ), -1 );
mDataDefinedYComboBox->addItem( tr( "None" ), -1 );

mAngleOffsetComboBox->addItem( tr( "Top" ), 90 * 16 );
mAngleOffsetComboBox->addItem( tr( "Right" ), 0 );
mAngleOffsetComboBox->addItem( tr( "Bottom" ), 270 * 16 );
mAngleOffsetComboBox->addItem( tr( "Left" ), 180 * 16 );

//insert all attributes into the combo boxes
const QgsFields& layerFields = layer->pendingFields();
for ( int idx = 0; idx < layerFields.count(); ++idx )
Expand Down Expand Up @@ -198,6 +202,8 @@ QgsDiagramProperties::QgsDiagramProperties( QgsVectorLayer* layer, QWidget* pare
mLabelPlacementComboBox->setCurrentIndex( 1 );
}

mAngleOffsetComboBox->setCurrentIndex( mAngleOffsetComboBox->findData( settingList.at( 0 ).angleOffset ) );

mOrientationLeftButton->setProperty( "direction", QgsDiagramSettings::Left );
mOrientationRightButton->setProperty( "direction", QgsDiagramSettings::Right );
mOrientationUpButton->setProperty( "direction", QgsDiagramSettings::Up );
Expand Down Expand Up @@ -346,6 +352,17 @@ void QgsDiagramProperties::on_mDiagramTypeComboBox_currentIndexChanged( int inde
mScaleDependencyComboBox->hide();
mScaleDependencyLabel->hide();
}

if ( DIAGRAM_NAME_PIE == diagramType )
{
mAngleOffsetComboBox->show();
mAngleOffsetLabel->show();
}
else
{
mAngleOffsetComboBox->hide();
mAngleOffsetLabel->hide();
}
}
void QgsDiagramProperties::addAttribute( QTreeWidgetItem * item )
{
Expand Down Expand Up @@ -521,6 +538,9 @@ void QgsDiagramProperties::apply()
ds.maxScaleDenominator = -1;
}

// Diagram angle offset (pie)
ds.angleOffset = mAngleOffsetComboBox->itemData( mAngleOffsetComboBox->currentIndex() ).toInt();

// Diagram orientation (histogram)
ds.diagramOrientation = static_cast<QgsDiagramSettings::DiagramOrientation>( mOrientationButtonGroup->checkedButton()->property( "direction" ).toInt() );

Expand Down
13 changes: 12 additions & 1 deletion src/app/qgsfeatureaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include "qgsidentifyresultsdialog.h"
#include "qgsattributedialog.h"
#include "qgslogger.h"
#include "qgsdistancearea.h"
#include "qgisapp.h"
#include "qgsproject.h"
#include "qgsmapcanvas.h"

#include <QPushButton>
#include <QSettings>
Expand All @@ -42,7 +46,14 @@ void QgsFeatureAction::execute()
QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature )
{
QgsFeature *f = cloneFeature ? new QgsFeature( mFeature ) : &mFeature;
QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature );

QgsDistanceArea myDa;

myDa.setSourceCrs( mLayer->crs().srsid() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );

QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature, myDa );

if ( mLayer->actions()->size() > 0 )
{
Expand Down
14 changes: 8 additions & 6 deletions src/app/qgsoptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "qgsnetworkaccessmanager.h"
#include "qgsproject.h"

#include "qgsattributetablefiltermodel.h"
#include "qgsrasterformatsaveoptionswidget.h"
#include "qgsrasterpyramidsoptionswidget.h"
#include "qgsdialog.h"
Expand Down Expand Up @@ -302,12 +303,13 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) :
// set the current theme
cmbTheme->setItemText( cmbTheme->currentIndex(), settings.value( "/Themes" ).toString() );

// set the attribute table behaviour
// set the attribute table default filter
cmbAttrTableBehaviour->clear();
cmbAttrTableBehaviour->addItem( tr( "Show all features" ) );
cmbAttrTableBehaviour->addItem( tr( "Show selected features" ) );
cmbAttrTableBehaviour->addItem( tr( "Show features in current canvas" ) );
cmbAttrTableBehaviour->setCurrentIndex( settings.value( "/qgis/attributeTableBehaviour", 0 ).toInt() );
cmbAttrTableBehaviour->addItem( tr( "Show all features" ), QgsAttributeTableFilterModel::ShowAll );
cmbAttrTableBehaviour->addItem( tr( "Show selected features" ), QgsAttributeTableFilterModel::ShowSelected );
cmbAttrTableBehaviour->addItem( tr( "Show features visible on map" ), QgsAttributeTableFilterModel::ShowVisible );
cmbAttrTableBehaviour->setCurrentIndex( cmbAttrTableBehaviour->findData( settings.value( "/qgis/attributeTableBehaviour", QgsAttributeTableFilterModel::ShowAll ).toInt() ) );


spinBoxAttrTableRowCache->setValue( settings.value( "/qgis/attributeTableRowCache", 10000 ).toInt() );

Expand Down Expand Up @@ -1010,7 +1012,7 @@ void QgsOptions::saveOptions()
settings.setValue( "/qgis/hideSplash", cbxHideSplash->isChecked() );
settings.setValue( "/qgis/showTips", cbxShowTips->isChecked() );
settings.setValue( "/qgis/dockAttributeTable", cbxAttributeTableDocked->isChecked() );
settings.setValue( "/qgis/attributeTableBehaviour", cmbAttrTableBehaviour->currentIndex() );
settings.setValue( "/qgis/attributeTableBehaviour", cmbAttrTableBehaviour->itemData( cmbAttrTableBehaviour->currentIndex() ) );
settings.setValue( "/qgis/attributeTableRowCache", spinBoxAttrTableRowCache->value() );
settings.setValue( "/qgis/promptForRasterSublayers", cmbPromptRasterSublayers->currentIndex() );
settings.setValue( "/qgis/scanItemsInBrowser2",
Expand Down
40 changes: 36 additions & 4 deletions src/browser/qgsbrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ QgsBrowser::QgsBrowser( QWidget *parent, Qt::WFlags flags )
, mDirtyAttributes( true )
, mLayer( 0 )
, mParamWidget( 0 )
, mAttributeTableFilterModel( 0 )
{
setupUi( this );

Expand Down Expand Up @@ -115,7 +116,7 @@ void QgsBrowser::itemClicked( const QModelIndex& index )
mDirtyAttributes = true;

// clear the previous stuff
attributeTable->setCanvasAndLayer( 0, 0 );
setLayer( 0 );

QList<QgsMapCanvasLayer> nolayers;
mapCanvas->setLayerSet( nolayers );
Expand Down Expand Up @@ -468,13 +469,12 @@ void QgsBrowser::updateCurrentTab()
{
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mLayer );
QApplication::setOverrideCursor( Qt::WaitCursor );
attributeTable->setCanvasAndLayer( mapCanvas, vlayer );
qobject_cast<QgsAttributeTableModel * >( dynamic_cast<QgsAttributeTableFilterModel *>( attributeTable->model() )->sourceModel() )->loadLayer();
setLayer( vlayer );
QApplication::restoreOverrideCursor();
}
else
{
attributeTable->setCanvasAndLayer( 0, 0 );
setLayer( 0 );
}
mDirtyAttributes = false;
}
Expand Down Expand Up @@ -528,3 +528,35 @@ void QgsBrowser::refresh( const QModelIndex& index )
}
}
}

void QgsBrowser::setLayer( QgsVectorLayer* vLayer )
{
attributeTable->setModel( NULL );

if ( mAttributeTableFilterModel )
{
// Cleanup
delete mAttributeTableFilterModel;
mAttributeTableFilterModel = NULL;
}

if ( vLayer )
{
// Initialize the cache
QSettings settings;
int cacheSize = qMax( 1, settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt() );
QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( vLayer, cacheSize, this );
layerCache->setCacheGeometry( false );

QgsAttributeTableModel *tableModel = new QgsAttributeTableModel( layerCache );

mAttributeTableFilterModel = new QgsAttributeTableFilterModel( NULL, tableModel, this );

// Let Qt do the garbage collection
layerCache->setParent( tableModel );
tableModel->setParent( mAttributeTableFilterModel );

attributeTable->setModel( mAttributeTableFilterModel );
tableModel->loadLayer();
}
}
3 changes: 2 additions & 1 deletion src/browser/qgsbrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class QgsBrowser : public QMainWindow, private Ui::QgsBrowserBase

// Expand to given path
void expandPath( QString path );

void setLayer( QgsVectorLayer* vLayer );


public slots:
Expand Down Expand Up @@ -79,6 +79,7 @@ class QgsBrowser : public QMainWindow, private Ui::QgsBrowserBase
QWidget *mParamWidget;
// last (selected) tab for each
QMap<QString, int> mLastTab;
QgsAttributeTableFilterModel* mAttributeTableFilterModel;
};

#endif // QGSBROWSER_H
11 changes: 11 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ SET(QGIS_CORE_SRCS
qgis.cpp
qgsapplication.cpp
qgsattributeaction.cpp
qgscachedfeatureiterator.cpp
qgscacheindex.cpp
qgscacheindexfeatureid.cpp
qgsbrowsermodel.cpp
qgscentralpointpositionmanager.cpp
qgsclipper.cpp
Expand All @@ -68,6 +71,7 @@ SET(QGIS_CORE_SRCS
qgsfeaturestore.cpp
qgsfield.cpp
qgsgeometry.cpp
qgsgeometrycache.cpp
qgsgeometryvalidator.cpp
qgsgml.cpp
qgsgmlschema.cpp
Expand Down Expand Up @@ -113,6 +117,7 @@ SET(QGIS_CORE_SRCS
qgscoordinatereferencesystem.cpp
qgstolerance.cpp
qgsvectordataprovider.cpp
qgsvectorlayercache.cpp
qgsvectorfilewriter.cpp
qgsvectorlayer.cpp
qgsvectorlayercache.cpp
Expand Down Expand Up @@ -318,6 +323,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectorlayereditbuffer.h
qgsnetworkaccessmanager.h
qgsvectordataprovider.h
qgsvectorlayercache.h
qgsgeometryvalidator.h

composer/qgsaddremoveitemcommand.h
Expand Down Expand Up @@ -375,6 +381,9 @@ SET(QGIS_CORE_HDRS
qgis.h
qgsapplication.h
qgsattributeaction.h
qgscachedfeatureiterator.h
qgscacheindex.h
qgscacheindexfeatureid.h
qgscentralpointpositionmanager.h
qgsclipper.h
qgscontexthelp.h
Expand All @@ -394,6 +403,7 @@ SET(QGIS_CORE_HDRS
qgsgeometry.h
qgsgml.h
qgsgmlschema.h
qgsgeometrycache.h
qgshttptransaction.h
qgslabel.h
qgslabelattributes.h
Expand Down Expand Up @@ -432,6 +442,7 @@ SET(QGIS_CORE_HDRS
qgssnapper.h
qgscoordinatereferencesystem.h
qgsvectordataprovider.h
qgsvectorlayercache.h
qgsvectorfilewriter.h
qgsvectorlayer.h
qgsvectorlayercache.h
Expand Down
1 change: 1 addition & 0 deletions src/core/composer/qgscomposerlegend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument
else if ( name == "subgroup" ) s = QgsComposerLegendStyle::Subgroup;
else if ( name == "symbol" ) s = QgsComposerLegendStyle::Symbol;
else if ( name == "symbolLabel" ) s = QgsComposerLegendStyle::SymbolLabel;
else continue;
setStyle( s, style );
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/diagram/qgspiediagram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void QgsPieDiagram::renderDiagram( const QgsAttributes& att, QgsRenderContext& c
QList< QColor >::const_iterator colIt = s.categoryColors.constBegin();
for ( ; valIt != values.constEnd(); ++valIt, ++colIt )
{
currentAngle = *valIt / valSum * 360 * 16;
currentAngle = *valIt / valSum * 360 * 16;
mCategoryBrush.setColor( *colIt );
p->setBrush( mCategoryBrush );
// if only 1 value is > 0, draw a circle
Expand All @@ -138,7 +138,7 @@ void QgsPieDiagram::renderDiagram( const QgsAttributes& att, QgsRenderContext& c
}
else
{
p->drawPie( baseX, baseY, w, h, totalAngle, currentAngle );
p->drawPie( baseX, baseY, w, h, totalAngle + s.angleOffset, currentAngle );
}
totalAngle += currentAngle;
}
Expand Down
89 changes: 89 additions & 0 deletions src/core/qgscachedfeatureiterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/***************************************************************************
qgscachedfeatureiterator.cpp
--------------------------------------
Date : 12.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 "qgscachedfeatureiterator.h"
#include "qgsvectorlayercache.h"
#include "qgsvectordataprovider.h"

QgsCachedFeatureIterator::QgsCachedFeatureIterator( QgsVectorLayerCache *vlCache, QgsFeatureRequest featureRequest, QgsFeatureIds featureIds )
: QgsAbstractFeatureIterator( featureRequest )
, mFeatureIds( featureIds )
, mVectorLayerCache( vlCache )
{
mFeatureIdIterator = featureIds.begin();
}

bool QgsCachedFeatureIterator::nextFeature( QgsFeature& f )
{
mFeatureIdIterator++;

if ( mFeatureIdIterator == mFeatureIds.end() )
{
return false;
}
else
{
f = QgsFeature( *mVectorLayerCache->mCache[*mFeatureIdIterator]->feature() );
return true;
}
}

bool QgsCachedFeatureIterator::rewind()
{
mFeatureIdIterator = mFeatureIds.begin();
return true;
}

bool QgsCachedFeatureIterator::close()
{
// Nothing to clean...
return true;
}

QgsCachedFeatureWriterIterator::QgsCachedFeatureWriterIterator( QgsVectorLayerCache *vlCache, QgsFeatureRequest featureRequest )
: QgsAbstractFeatureIterator( featureRequest )
, mVectorLayerCache( vlCache )
{
mFeatIt = vlCache->layer()->dataProvider()->getFeatures( featureRequest );
}

bool QgsCachedFeatureWriterIterator::nextFeature( QgsFeature& f )
{
if ( mFeatIt.nextFeature( f ) )
{
// As long as features can be fetched from the provider: Write them to cache
mVectorLayerCache->cacheFeature( f );
mFids.insert( f.id() );
return true;
}
else
{
// Once no more features can be fetched: Inform the cache, that
// the request has been completed
mVectorLayerCache->requestCompleted( mRequest, mFids );
return false;
}
}

bool QgsCachedFeatureWriterIterator::rewind()
{
mFids.clear();
return mFeatIt.rewind();
}

bool QgsCachedFeatureWriterIterator::close()
{
return mFeatIt.close();
}
115 changes: 115 additions & 0 deletions src/core/qgscachedfeatureiterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/***************************************************************************
qgscachedfeatureiterator.h
--------------------------------------
Date : 12.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 QGSCACHEDFEATUREITERATOR_H
#define QGSCACHEDFEATUREITERATOR_H

#include "qgsfeature.h"
#include "qgsfeatureiterator.h"

class QgsVectorLayerCache;

/**
* @brief
* Delivers features from the cache
*
*/
class CORE_EXPORT QgsCachedFeatureIterator : public QgsAbstractFeatureIterator
{
public:
/**
* @brief
* This constructor creates a feature iterator, that delivers only cached information, based on the
* {@link QgsFeatureIds}. No request is made to the backend.
*
* @param vlCache The vector layer cache to use
* @param featureRequest The feature request to answer
* @param featureIds The feature ids to return
*/
QgsCachedFeatureIterator( QgsVectorLayerCache* vlCache, QgsFeatureRequest featureRequest, QgsFeatureIds featureIds );

/**
* @brief
*
* @param f
* @return bool
*/
virtual bool nextFeature( QgsFeature& f );

/**
* @brief
*
* @return bool
*/
virtual bool rewind();

/**
* @brief
*
* @return bool
*/
virtual bool close();

private:
QgsFeatureIds mFeatureIds;
QgsVectorLayerCache* mVectorLayerCache;
QgsFeatureIds::Iterator mFeatureIdIterator;
};

/**
* @brief
* Uses another iterator as backend and writes features to the cache
*
*/
class CORE_EXPORT QgsCachedFeatureWriterIterator : public QgsAbstractFeatureIterator
{
public:
/**
* @brief
* This constructor creates a feature iterator, which queries the backend and caches retrieved features.
*
* @param vlCache The vector layer cache to use
* @param featureRequest The feature request to answer
*/
QgsCachedFeatureWriterIterator( QgsVectorLayerCache* vlCache, QgsFeatureRequest featureRequest );

/**
* @brief
*
* @param f
* @return bool
*/
virtual bool nextFeature( QgsFeature& f );

/**
* @brief
*
* @return bool
*/
virtual bool rewind();

/**
* @brief
*
* @return bool
*/
virtual bool close();

private:
QgsFeatureIterator mFeatIt;
QgsVectorLayerCache* mVectorLayerCache;
QgsFeatureIds mFids;
};
#endif // QGSCACHEDFEATUREITERATOR_H
31 changes: 31 additions & 0 deletions src/core/qgscacheindex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/***************************************************************************
qgscacheindex.cpp
--------------------------------------
Date : 13.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 "qgscacheindex.h"
#include "qgsfeaturerequest.h"

QgsAbstractCacheIndex::QgsAbstractCacheIndex()
{
}

QgsAbstractCacheIndex::~QgsAbstractCacheIndex()
{
}

void QgsAbstractCacheIndex::requestCompleted( QgsFeatureRequest featureRequest, QgsFeatureIds fids )
{
Q_UNUSED( featureRequest )
Q_UNUSED( fids )
}
73 changes: 73 additions & 0 deletions src/core/qgscacheindex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/***************************************************************************
qgscacheindex.h
--------------------------------------
Date : 13.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 QGSCACHEINDEX_H
#define QGSCACHEINDEX_H

#include "qgsfeature.h" // QgsFeatureIds

class QgsFeatureRequest;

/**
* @brief
* Abstract base class for cache indices
*/

class QgsAbstractCacheIndex
{
public:
QgsAbstractCacheIndex();
virtual ~QgsAbstractCacheIndex();

/**
* Is called, whenever a feature is removed from the cache. You should update your indexes, so
* they become invalid in case this feature was required to successfuly answer a request.
*/
virtual void flushFeature( const QgsFeatureId fid ) = 0;

/**
* Sometimes, the whole cache changes its state and its easier to just withdraw everything.
* In this case, this method is issued. Be sure to clear all cache information in here.
*/
virtual void flush() = 0;

/**
* @brief
* Implement this method to update the the indices, in case you need information contained by the request
* to properly index. (E.g. spatial index)
* Does nothing by default
*
* @param featureRequest The feature request that was answered
* @param fids The feature ids that have been returned
*/
virtual void requestCompleted( QgsFeatureRequest featureRequest, QgsFeatureIds fids );

/**
* Is called, when a feature request is issued on a cached layer.
* If this cache index is able to completely answer the feature request, it will return true
* and write the list of feature ids of cached features to cachedFeatures. If it is not able
* it will return false and the cachedFeatures state is undefined.
*
* @param cachedFeatures A reference to {@link QgsFeatureIds}, where a list of ids is written to,
* in case this index is able to answer the request.
* @param featureRequest The feature request, for which this index is queried.
*
* @return True, if this index holds the information to answer the request.
*
*/
virtual bool getCachedIds( QgsFeatureIds& cachedFeatures, const QgsFeatureRequest& featureRequest ) = 0;
};

#endif // QGSCACHEINDEX_H
24 changes: 24 additions & 0 deletions src/core/qgscacheindexfeatureid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/***************************************************************************
qgscacheindexfeatureid.cpp
--------------------------------------
Date : 13.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 "qgscacheindexfeatureid.h"

QgsCacheIndexFeatureId::QgsCacheIndexFeatureId( QgsCachedVectorLayer* cachedVectorLayer )
: QgsAbstractCacheIndex()
, C( cachedVectorLayer )
{

}

36 changes: 36 additions & 0 deletions src/core/qgscacheindexfeatureid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/***************************************************************************
qgscacheindexfeatureid.h
--------------------------------------
Date : 13.2.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx 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 QGSCACHEINDEXFEATUREID_H
#define QGSCACHEINDEXFEATUREID_H

#include "qgscacheindex.h"

class QgsCachedVectorLayer;

class QgsCacheIndexFeatureId : public QgsAbstractCacheIndex
{
public:
QgsCacheIndexFeatureId( QgsCachedVectorLayer* cachedVectorLayer );

signals:

public slots:

private:
QgsCachedVectorLayer* C;
};

#endif // QGSCACHEINDEXFEATUREID_H
3 changes: 3 additions & 0 deletions src/core/qgsdiagramrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ void QgsDiagramSettings::readXML( const QDomElement& elem )

barWidth = elem.attribute( "barWidth" ).toDouble();

angleOffset = elem.attribute( "angleOffset" ).toInt();

minimumSize = elem.attribute( "minimumSize" ).toDouble();

//colors
Expand Down Expand Up @@ -204,6 +206,7 @@ void QgsDiagramSettings::writeXML( QDomElement& rendererElem, QDomDocument& doc

categoryElem.setAttribute( "barWidth", QString::number( barWidth ) );
categoryElem.setAttribute( "minimumSize", QString::number( minimumSize ) );
categoryElem.setAttribute( "angleOffset", QString::number( angleOffset ) );

QString colors;
for ( int i = 0; i < categoryColors.size(); ++i )
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsdiagramrendererv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class CORE_EXPORT QgsDiagramSettings
double barWidth;
int transparency; // 0 - 100
bool scaleByArea;
int angleOffset;

//scale range (-1 if no lower / upper bound )
double minScaleDenominator;
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class CORE_EXPORT QgsExpression

int scale() {return mScale; }

const QString& expression() const { return mExpression; }

//! Return the parsed expression as a string - useful for debugging
QString dump() const;

Expand Down
1 change: 1 addition & 0 deletions src/core/qgsfeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ email : sherman at mrcc.com
#include <QList>
#include <QHash>
#include <QVector>
#include <QSet>

class QgsGeometry;
class QgsRectangle;
Expand Down
39 changes: 39 additions & 0 deletions src/core/qgsgeometrycache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "qgsgeometrycache.h"

#include "qgsvectorlayereditbuffer.h"

QgsGeometryCache::QgsGeometryCache( QgsVectorLayer* layer )
: L( layer )
{
}

QgsGeometryCache::~QgsGeometryCache()
{
// Destroy any cached geometries and clear the references to them
deleteCachedGeometries();
}

bool QgsGeometryCache::geometry( QgsFeatureId fid, QgsGeometry& geometry )
{
// no need to check changed geometries because all changed geometries are also cached

// first time this geometry has changed since last commit
if ( !mCachedGeometries.contains( fid ) )
return false;

geometry = mCachedGeometries[fid];
return true;
}

void QgsGeometryCache::cacheGeometry( QgsFeatureId fid, const QgsGeometry& geom )
{
mCachedGeometries[fid] = geom;
}


void QgsGeometryCache::deleteCachedGeometries()
{
// Destroy any cached geometries
mCachedGeometries.clear();
mCachedGeometriesRect = QgsRectangle();
}
46 changes: 46 additions & 0 deletions src/core/qgsgeometrycache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef QGSGEOMETRYCACHE_H
#define QGSGEOMETRYCACHE_H

#include "qgsfeature.h"

#include "qgsvectorlayer.h"

class QgsGeometryCache
{
public:
QgsGeometryCache( QgsVectorLayer* layer );
~QgsGeometryCache();

inline QgsGeometryMap& cachedGeometries() { return mCachedGeometries; }

//! fetch geometry from cache, return true if successful
bool geometry( QgsFeatureId fid, QgsGeometry& geometry );

//! store a geometry in the cache
void cacheGeometry( QgsFeatureId fid, const QgsGeometry& geom );

//! get rid of the cached geometry
void removeGeometry( QgsFeatureId fid ) { mCachedGeometries.remove( fid ); }


/** Deletes the geometries in mCachedGeometries */
void deleteCachedGeometries();

void setCachedGeometriesRect( const QgsRectangle& extent ) { mCachedGeometriesRect = extent; }
const QgsRectangle& cachedGeometriesRect() { return mCachedGeometriesRect; }

protected:

inline QgsVectorLayerEditBuffer* editBuffer() { return L->editBuffer(); }

QgsVectorLayer* L;

/** cache of the committed geometries retrieved *for the current display* */
QgsGeometryMap mCachedGeometries;

/** extent for which there are cached geometries */
QgsRectangle mCachedGeometriesRect;

};

#endif // QGSGEOMETRYCACHE_H
24 changes: 20 additions & 4 deletions src/core/qgsvectorlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
#include "qgsrendercontext.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayercache.h"
#include "qgsgeometrycache.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgsvectorlayereditutils.h"
#include "qgsvectorlayerfeatureiterator.h"
Expand Down Expand Up @@ -100,8 +100,7 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
, mLabel( 0 )
, mLabelOn( false )
, mVertexMarkerOnlyForSelection( false )
, mEditorLayout( GeneratedLayout )
, mCache( new QgsVectorLayerCache( this ) )
, mCache( new QgsGeometryCache( this ) )
, mEditBuffer( 0 )
, mJoinBuffer( 0 )
, mDiagramRenderer( 0 )
Expand Down Expand Up @@ -1256,7 +1255,7 @@ void QgsVectorLayer::invertSelection()
emit selectionChanged();
}

void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle &rect )
void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect )
{
// normalize the rectangle
rect.normalize();
Expand Down Expand Up @@ -2981,6 +2980,23 @@ bool QgsVectorLayer::deleteAttribute( int index )
return mEditBuffer->deleteAttribute( index );
}

bool QgsVectorLayer::deleteAttributes( QList<int> attrs )
{
bool deleted = false;

qSort( attrs.begin(), attrs.end(), qGreater<int>() );

foreach ( int attr, attrs )
{
if ( deleteAttribute( attr ) )
{
deleted = true;
}
}

return deleted;
}

bool QgsVectorLayer::deleteFeature( QgsFeatureId fid )
{
if ( !mEditBuffer )
Expand Down
15 changes: 12 additions & 3 deletions src/core/qgsvectorlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class QgsVectorLayerJoinBuffer;
class QgsFeatureRendererV2;
class QgsDiagramRendererV2;
class QgsDiagramLayerSettings;
class QgsVectorLayerCache;
class QgsGeometryCache;
class QgsVectorLayerEditBuffer;
class QgsSymbolV2;

Expand Down Expand Up @@ -667,6 +667,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/** delete an attribute field (but does not commit it) */
bool deleteAttribute( int attr );

/**
* Deletes a list of attribute fields (but does not commit it)
*
* @param attrs the indices of the attributes to delete
* @return true if at least one attribute has been deleted
*
*/
bool deleteAttributes( QList<int> attrs );

/** Insert a copy of the given features into the layer (but does not commit it) */
bool addFeatures( QgsFeatureList features, bool makeSelected = true );

Expand Down Expand Up @@ -855,7 +864,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer

QString metadata();

inline QgsVectorLayerCache* cache() { return mCache; }
inline QgsGeometryCache* cache() { return mCache; }

signals:

Expand Down Expand Up @@ -1066,7 +1075,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QString mAnnotationForm;

//! cache for some vector layer data - currently only geometries for faster editing
QgsVectorLayerCache* mCache;
QgsGeometryCache* mCache;

//! stores information about uncommitted changes to layer
QgsVectorLayerEditBuffer* mEditBuffer;
Expand Down
301 changes: 277 additions & 24 deletions src/core/qgsvectorlayercache.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/***************************************************************************
qgsvectorlayercache.cpp
---------------------
begin : Dezember 2012
copyright : (C) 2012 by Martin Dobias
email : wonder dot sk at gmail dot com
qgsvectorlayercache.cpp
Cache features of a vector layer
-------------------
begin : January 2013
copyright : (C) Matthias Kuhn
email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
Expand All @@ -12,42 +14,293 @@
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsvectorlayercache.h"
#include "qgscacheindex.h"
#include "qgscachedfeatureiterator.h"

QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer* layer, int cacheSize, QObject* parent )
: QObject( parent )
, mLayer( layer )
{
mCache.setMaxCost( cacheSize );

connect( mLayer, SIGNAL( featureDeleted( QgsFeatureId ) ), SLOT( featureDeleted( QgsFeatureId ) ) );
connect( mLayer, SIGNAL( featureAdded( QgsFeatureId ) ), SLOT( featureAdded( QgsFeatureId ) ) );
connect( mLayer, SIGNAL( layerDeleted() ), SLOT( layerDeleted() ) );

#include "qgsvectorlayereditbuffer.h"
setCacheGeometry( true );
setCacheSubsetOfAttributes( mLayer->pendingAllAttributesList() );
setCacheAddedAttributes( true );

QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer* layer )
: L( layer )
connect( mLayer, SIGNAL( attributeDeleted( int ) ), SLOT( attributeDeleted( int ) ) );
connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
}

void QgsVectorLayerCache::setCacheSize( int cacheSize )
{
mCache.setMaxCost( cacheSize );
}

QgsVectorLayerCache::~QgsVectorLayerCache()
int QgsVectorLayerCache::cacheSize()
{
// Destroy any cached geometries and clear the references to them
deleteCachedGeometries();
return mCache.maxCost();
}

bool QgsVectorLayerCache::geometry( QgsFeatureId fid, QgsGeometry& geometry )
void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
{
// no need to check changed geometries because all changed geometries are also cached
mCacheGeometry = cacheGeometry;
if ( cacheGeometry )
{
connect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
}
else
{
disconnect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), this, SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
}
}

// first time this geometry has changed since last commit
if ( !mCachedGeometries.contains( fid ) )
return false;
void QgsVectorLayerCache::setCacheSubsetOfAttributes( const QgsAttributeList& attributes )
{
mCachedAttributes = attributes;
}

geometry = mCachedGeometries[fid];
return true;
void QgsVectorLayerCache::setFullCache( bool fullCache )
{
mFullCache = fullCache;

if ( mFullCache )
{
// Add a little more than necessary...
setCacheSize( mLayer->featureCount() + 100 );

// Initialize the cache...
QgsFeatureIterator it = getFeatures( QgsFeatureRequest()
.setSubsetOfAttributes( mCachedAttributes )
.setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) );

int i = 0;

QTime t;
t.start();

QgsFeature f;
while ( it.nextFeature( f ) )
{
++i;

if ( t.elapsed() > 1000 )
{
bool cancel = false;
emit progress( i, cancel );
if ( cancel )
break;

t.restart();
}
}

it.close();
}
}

void QgsVectorLayerCache::cacheGeometry( QgsFeatureId fid, const QgsGeometry& geom )
void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
{
mCachedGeometries[fid] = geom;
if ( cacheAddedAttributes )
{
connect( mLayer, SIGNAL( attributeAdded( int ) ), SLOT( attributeAdded( int ) ) );
}
else
{
disconnect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
}
}

bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature& feature, bool skipCache )
{
bool featureFound = false;

QgsCachedFeature* cachedFeature = NULL;

if ( !skipCache )
{
cachedFeature = mCache[ featureId ];
}

if ( cachedFeature != NULL )
{
feature = QgsFeature( *cachedFeature->feature() );
featureFound = true;
}
else if ( mLayer->getFeatures( QgsFeatureRequest()
.setFilterFid( featureId )
.setSubsetOfAttributes( mCachedAttributes )
.setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) )
.nextFeature( feature ) )
{
cacheFeature( feature );
featureFound = true;
}

return featureFound;
}

bool QgsVectorLayerCache::removeCachedFeature( QgsFeatureId fid )
{
return mCache.remove( fid );
}

QgsVectorLayer* QgsVectorLayerCache::layer()
{
return mLayer;
}

void QgsVectorLayerCache::requestCompleted( QgsFeatureRequest featureRequest, QgsFeatureIds fids )
{
// If a request is too large for the cache don't notify to prevent from indexing incomplete requests
if ( fids.count() < mCache.size() )
{
foreach ( QgsAbstractCacheIndex* idx, mCacheIndices )
{
idx->requestCompleted( featureRequest, fids );
}
}
}

void QgsVectorLayerCache::featureRemoved( QgsFeatureId fid )
{
foreach ( QgsAbstractCacheIndex* idx, mCacheIndices )
{
idx->flushFeature( fid );
}
}

void QgsVectorLayerCache::deleteCachedGeometries()
void QgsVectorLayerCache::attributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
{
// Destroy any cached geometries
mCachedGeometries.clear();
mCachedGeometriesRect = QgsRectangle();
QgsCachedFeature* cachedFeat = mCache[ fid ];

if ( NULL != cachedFeat )
{
cachedFeat->mFeature->setAttribute( field, value );
}
}

void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
{
mCache.remove( fid );
}

void QgsVectorLayerCache::featureAdded( QgsFeatureId fid )
{
if ( mFullCache )
{
if ( cacheSize() <= mLayer->featureCount() )
{
setCacheSize( mLayer->featureCount() + 100 );
}

QgsFeature feat;
featureAtId( fid, feat );
}
}

void QgsVectorLayerCache::attributeAdded( int field )
{
Q_UNUSED( field )
mCachedAttributes.append( field );
mCache.clear();
}

void QgsVectorLayerCache::attributeDeleted( int field )
{
foreach ( QgsFeatureId fid, mCache.keys() )
{
mCache[ fid ]->mFeature->deleteAttribute( field );
}
}

void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, QgsGeometry& geom )
{
QgsCachedFeature* cachedFeat = mCache[ fid ];

if ( cachedFeat != NULL )
{
cachedFeat->mFeature->setGeometry( geom );
}
}

void QgsVectorLayerCache::layerDeleted()
{
emit ( cachedLayerDeleted() );

mLayer = NULL;
}

QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &featureRequest )
{
QgsFeatureIterator it;
bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.

if ( checkInformationCovered( featureRequest ) )
{
// Check if an index is able to deliver the requested features
foreach ( QgsAbstractCacheIndex *idx, mCacheIndices )
{
QgsFeatureIds featureIds;

if ( idx->getCachedIds( featureIds, featureRequest ) )
{
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest, featureIds ) );
requiresWriterIt = false;
break;
}
}
}
else
{
// Let the layer answer the request, so no caching of requests
// we don't want to cache is done
requiresWriterIt = false;
it = mLayer->getFeatures( featureRequest );
}

if ( requiresWriterIt && mLayer->dataProvider() )
{
// No index was able to satisfy the request
it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, featureRequest ) );
}

return it;
}

bool QgsVectorLayerCache::checkInformationCovered( const QgsFeatureRequest& featureRequest )
{
QgsAttributeList requestedAttributes;

if ( false == featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
{
requestedAttributes = mLayer->pendingAllAttributesList();
}
else
{
requestedAttributes = featureRequest.subsetOfAttributes();
}

// Check if we even cache the information requested
foreach ( int attr, requestedAttributes )
{
if ( !mCachedAttributes.contains( attr ) )
{
return false;
}
}

// If the request needs geometry but we don't cache this...
if ( false == featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
&& false == mCacheGeometry )
{
return false;
}

return true;
}
269 changes: 229 additions & 40 deletions src/core/qgsvectorlayercache.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/***************************************************************************
qgsvectorlayercache.h
---------------------
begin : Dezember 2012
copyright : (C) 2012 by Martin Dobias
email : wonder dot sk at gmail dot com
qgsvectorlayercache.h
Cache features of a vector layer
-------------------
begin : January 2013
copyright : (C) Matthias Kuhn
email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
Expand All @@ -12,49 +14,236 @@
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSVECTORLAYERCACHE_H
#define QGSVECTORLAYERCACHE_H

#include "qgsfeature.h"

#include "qgsvectorlayer.h"

class QgsVectorLayerCache
{
public:
QgsVectorLayerCache( QgsVectorLayer* layer );
~QgsVectorLayerCache();

inline QgsGeometryMap& cachedGeometries() { return mCachedGeometries; }
#ifndef QgsVectorLayerCache_H
#define QgsVectorLayerCache_H

//! fetch geometry from cache, return true if successful
bool geometry( QgsFeatureId fid, QgsGeometry& geometry );
#include <QCache>

//! store a geometry in the cache
void cacheGeometry( QgsFeatureId fid, const QgsGeometry& geom );
#include "qgsvectorlayer.h"

//! get rid of the cached geometry
void removeGeometry( QgsFeatureId fid ) { mCachedGeometries.remove( fid ); }
class QgsCachedFeatureIterator;
class QgsAbstractCacheIndex;

/**
* This class caches features of a given QgsVectorLayer.
*
* @brief
* The cached features can be indexed by @link {QgsAbstractCacheIndex}.
*
* Proper indexing for a given use-case may speed up performance substantially.
*/

/** Deletes the geometries in mCachedGeometries */
void deleteCachedGeometries();
class CORE_EXPORT QgsVectorLayerCache : public QObject
{
Q_OBJECT

private:
/**
* This is a wrapper class around a cached @link {QgsFeature}, which will inform
* the cache, when it has been deleted, so indexes can be updated that the wrapped
* feature needs to be fetched again if needed.
*/
class QgsCachedFeature
{
public:
/**
* Will create a new cached feature.
*
* @param feat The feature to cache. A copy will be made.
* @param vlCache The cache to inform when the feature has been removed from the cache.
*/
QgsCachedFeature( const QgsFeature& feat, QgsVectorLayerCache* vlCache )
: mCache( vlCache )
{
mFeature = new QgsFeature( feat );
}

~QgsCachedFeature()
{
// That's the reason we need this wrapper:
// Inform the cache that this feature has been removed
mCache->featureRemoved( mFeature->id() );
delete( mFeature );
}

inline const QgsFeature* feature() { return mFeature; }

private:
QgsFeature* mFeature;
QgsVectorLayerCache* mCache;

friend class QgsVectorLayerCache;
};

void setCachedGeometriesRect( const QgsRectangle& extent ) { mCachedGeometriesRect = extent; }
const QgsRectangle& cachedGeometriesRect() { return mCachedGeometriesRect; }
public:
QgsVectorLayerCache( QgsVectorLayer* layer, int cacheSize, QObject* parent = NULL );

/**
* Sets the maximum number of features to keep in the cache. Some features will be removed from
* the cache if the number is smaller than the previous size of the cache.
*
* @param cacheSize indicates the maximum number of features to keep in the cache
*/

void setCacheSize( int cacheSize );

/**
* @brief
* Returns the maximum number of features this cache will hold.
* In case full caching is enabled, this number can change, as new features get added.
*
* @return int
*/
int cacheSize();

/**
* Enable or disable the caching of geometries
*
* @param cacheGeometry Enable or disable the caching of geometries
*/
void setCacheGeometry( bool cacheGeometry );


/**
* Set the subset of attributes to be cached
*
* @param attributes The attributes to be cached
*/
void setCacheSubsetOfAttributes( const QgsAttributeList& attributes );

/**
* If this is enabled, the subset of cached attributes will automatically be extended
* to also include newly added attributes.
*
* @param cacheAddedAttributes Automatically cache new attributes
*/
void setCacheAddedAttributes( bool cacheAddedAttributes );

/**
* @brief
* This enables or disables full caching.
* If enabled, all features will be held in the cache. The cache size will incrementally
* be increased to offer space for all features.
* When enabled, all features will be read into cache. As this feature will most likely
* be used for slow data sources, be aware, that the call to this method might take a long time.
*
* @param fullCache True: enable full caching, False: disable full caching
*/
void setFullCache( bool fullCache );

/**
* @brief
* Adds a {@link QgsAbstractCacheIndex} to this cache. Cache indices know about features present
* in this cache and decide, if enough information is present in the cache to respond to a {@link QgsFeatureRequest}.
*
* @param cacheIndex The cache index to add.
*/
void addCacheIndex( const QgsAbstractCacheIndex& cacheIndex );

QgsFeatureIterator getFeatures( const QgsFeatureRequest& featureRequest );

/**
* Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features
* @param featureId The id of the feature to query
* @param feature The result of the operation will be written to this feature
* @param skipCache Will query the layer regardless if the feature is in the cache already
* @return true in case of success
*/
bool featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false );

/**
* Removes the feature identified by fid from the cache if present.
* @param fid The id of the feature to delete
* @return true if the feature was removed, false if the feature id was not found in the cache
*/
bool removeCachedFeature( QgsFeatureId fid );

/**
* Returns the layer to which this cache belongs
*/
QgsVectorLayer* layer();

protected:

inline QgsVectorLayerEditBuffer* editBuffer() { return L->editBuffer(); }

QgsVectorLayer* L;

/** cache of the committed geometries retrieved *for the current display* */
QgsGeometryMap mCachedGeometries;

/** extent for which there are cached geometries */
QgsRectangle mCachedGeometriesRect;

/**
* @brief
* Gets called, whenever the full list of feature ids for a certain request is known.
* Broadcasts this information to indices, so they can update their tables.
*
* @param featureRequest The feature request that was answered
* @param fids The feature ids that have been returned
*/
void requestCompleted( QgsFeatureRequest featureRequest, QgsFeatureIds fids );

/**
* @brief
* Gets called, whenever a feature has been removed.
* Broadcasts this information to indices, so they can invalidate their cache if required.
*
* @param fid The feature id of the removed feature.
*/
void featureRemoved( QgsFeatureId fid );

/**
* @brief
* Checks if the information required to complete the request is cached.
* i.e. If all attributes required and the geometry is held in the cache.
* Please note, that this does not check, if the requested features are cached.
*
*
* @param featureRequest The {@link QgsFeatureRequest} to be answered
* @return True if the information is being cached, false if not
*/
bool checkInformationCovered( const QgsFeatureRequest& featureRequest );


signals:

/**
* When filling the cache, this signal gets emited periodically to notify about the progress
* and to be able to cancel an operation.
*
* @param i The number of already fetched features
* @param cancel A reference to a boolean variable. Set to true and the operation will be canceled.
*/
void progress( int i, bool& cancel );

/**
* @brief Is emitted when the cached layer is deleted. Is emitted when the cached layers layerDelete()
* signal is being emitted, but before the local reference to it has been set to NULL. So call to
* @link {layer()} will still return a valid pointer for cleanup purpose.
*/
void cachedLayerDeleted();

public slots:
void attributeValueChanged( QgsFeatureId fid, int field, const QVariant& value );
void featureDeleted( QgsFeatureId fid );
void featureAdded( QgsFeatureId fid );
void attributeAdded( int field );
void attributeDeleted( int field );
void geometryChanged( QgsFeatureId fid, QgsGeometry& geom );
void layerDeleted();

private:

inline void cacheFeature( QgsFeature& feat )
{
QgsCachedFeature* cachedFeature = new QgsCachedFeature( feat, this );
mCache.insert( feat.id(), cachedFeature );
}

QgsVectorLayer* mLayer;
QCache< QgsFeatureId, QgsCachedFeature > mCache;

bool mCacheGeometry;
bool mFullCache;
QList<QgsAbstractCacheIndex*> mCacheIndices;

QgsAttributeList mCachedAttributes;

friend class QgsCachedFeatureIterator;
friend class QgsCachedFeatureWriterIterator;
friend class QgsCachedFeature;
};

#endif // QGSVECTORLAYERCACHE_H
#endif // QgsVectorLayerCache_H
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayereditutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "qgsvectorlayereditutils.h"

#include "qgsvectordataprovider.h"
#include "qgsvectorlayercache.h"
#include "qgsgeometrycache.h"
#include "qgsvectorlayereditbuffer.h"

#include <limits>
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsvectorlayereditutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@

#include "qgsvectorlayer.h"

class QgsGeometryCache;

class CORE_EXPORT QgsVectorLayerEditUtils
{
public:
QgsVectorLayerEditUtils( QgsVectorLayer* layer );

inline QgsVectorLayerCache* cache() { return L->cache(); }
inline QgsGeometryCache* cache() { return L->cache(); }


/** Insert a new vertex before the given vertex number,
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayerundocommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include "qgsgeometry.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayercache.h"
#include "qgsgeometrycache.h"
#include "qgsvectorlayereditbuffer.h"

#include "qgslogger.h"
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsvectorlayerundocommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "qgsfeature.h"

class QgsGeometry;
class QgsGeometryCache;

#include "qgsvectorlayer.h"
#include "qgsvectorlayereditbuffer.h"
Expand All @@ -36,7 +37,7 @@ class QgsVectorLayerUndoCommand : public QUndoCommand
public:
QgsVectorLayerUndoCommand( QgsVectorLayerEditBuffer* buffer ) : mBuffer( buffer ) {}
inline QgsVectorLayer* layer() { return mBuffer->L; }
inline QgsVectorLayerCache* cache() { return mBuffer->L->cache(); }
inline QgsGeometryCache* cache() { return mBuffer->L->cache(); }

protected:
QgsVectorLayerEditBuffer* mBuffer;
Expand Down
28 changes: 23 additions & 5 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ symbology-ng/qgslayerpropertieswidget.cpp
symbology-ng/qgssmartgroupeditordialog.cpp

attributetable/qgsattributetablemodel.cpp
attributetable/qgsattributetablememorymodel.cpp
attributetable/qgsattributetableview.cpp
attributetable/qgsattributetablefiltermodel.cpp
attributetable/qgsattributetableidcolumnpair.cpp
attributetable/qgsattributetabledelegate.cpp
attributetable/qgsfeaturelistview.cpp
attributetable/qgsfeaturelistmodel.cpp
attributetable/qgsfeaturelistviewdelegate.cpp
attributetable/qgsdualview.cpp

qgisgui.cpp
qgisinterface.cpp
qgsannotationitem.cpp
qgsattributeeditor.cpp
qgsattributedialog.cpp
qgslegendinterface.cpp
qgsblendmodecombobox.cpp
qgscharacterselectdialog.cpp
Expand All @@ -65,6 +68,7 @@ qgsfiledropedit.cpp
qgsfieldvalidator.cpp
qgsformannotationitem.cpp
qgshtmlannotationitem.cpp
qgshighlight.cpp
qgsgenericprojectionselector.cpp
qgsmanageconnectionsdialog.cpp
qgsmapcanvas.cpp
Expand Down Expand Up @@ -102,6 +106,7 @@ qgsludialog.cpp
qgssearchquerybuilder.cpp
qgsexpressionbuilderwidget.cpp
qgsexpressionbuilderdialog.cpp
qgsexpressionselectiondialog.cpp
qgsexpressionhighlighter.cpp
qgsquerybuilder.cpp
qgscollapsiblegroupbox.cpp
Expand Down Expand Up @@ -155,9 +160,14 @@ symbology-ng/qgssmartgroupeditordialog.h

attributetable/qgsattributetableview.h
attributetable/qgsattributetablemodel.h
attributetable/qgsattributetablememorymodel.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsdualview.h

qgsattributedialog.h
qgsattributeeditor.h
qgsblendmodecombobox.h
qgscharacterselectdialog.h
Expand Down Expand Up @@ -199,6 +209,7 @@ qgssearchquerybuilder.h
qgsscalecombobox.h
qgsexpressionbuilderwidget.h
qgsexpressionhighlighter.h
qgsexpressionselectiondialog.h
qgsquerybuilder.h
qgscollapsiblegroupbox.h
qgsfilterlineedit.h
Expand All @@ -217,6 +228,7 @@ qgscursors.h
qgsencodingfiledialog.h
qgsfiledropedit.h
qgsgenericprojectionselector.h
qgshighlight.h
qgsmapcanvas.h
qgsmapcanvasitem.h
qgsmapcanvasmap.h
Expand All @@ -240,19 +252,23 @@ qgsscalecombobox.h
qgsblendmodecombobox.h
qgssearchquerybuilder.h
qgsattributeeditor.h
qgsattributedialog.h
qgsfieldvalidator.h
qgsexpressionbuilderwidget.h
qgsexpressionbuilderdialog.h
qgsexpressionselectiondialog.h
qgsexpressionhighlighter.h
qgscollapsiblegroupbox.h
qgsfilterlineedit.h

attributetable/qgsattributetablemodel.h
attributetable/qgsattributetablememorymodel.h
attributetable/qgsattributetableview.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetableidcolumnpair.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsdualview.h
)

IF (WITH_TOUCH)
Expand All @@ -273,6 +289,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsprojectionselectorbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsquerybuilderbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilder.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilderdialogbase.h
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionselectiondialogbase.h
)

# ModelTest
Expand All @@ -292,6 +309,7 @@ INCLUDE_DIRECTORIES(
${QT_QTUITOOLS_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/symbology-ng
${CMAKE_CURRENT_SOURCE_DIR}/attributetable
../core
../core/composer
../core/raster
Expand Down
7 changes: 5 additions & 2 deletions src/gui/attributetable/qgsattributetabledelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#include "qgsattributeeditor.h"
#include "qgslogger.h"


// TODO: Remove this casting orgy

QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *model ) const
{
const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( model );
Expand All @@ -47,7 +50,7 @@ int QgsAttributeTableDelegate::fieldIdx( const QModelIndex &index ) const

const QgsAttributeTableFilterModel *fm = dynamic_cast<const QgsAttributeTableFilterModel *>( index.model() );
if ( fm )
return fm->tableModel()->fieldIdx( index.column() );
return fm->masterModel()->fieldIdx( index.column() );

return -1;
}
Expand All @@ -60,7 +63,7 @@ QgsFeatureId QgsAttributeTableDelegate::featureId( const QModelIndex &index ) co

const QgsAttributeTableFilterModel *fm = dynamic_cast<const QgsAttributeTableFilterModel *>( index.model() );
if ( fm )
return fm->tableModel()->rowToId( fm->mapToSource( index ).row() );
return fm->masterModel()->rowToId( fm->mapToSource( index ).row() );

return -1;
}
Expand Down
1 change: 1 addition & 0 deletions src/gui/attributetable/qgsattributetabledelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

class QPainter;
class QgsVectorLayer;
class QgsAttributeTableView;

/** \ingroup app
* A delegate item class for QgsAttributeTable (see Qt documentation for
Expand Down
317 changes: 307 additions & 10 deletions src/gui/attributetable/qgsattributetablefiltermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,328 @@
* *
***************************************************************************/

#include <QItemSelectionModel>

#include "qgsattributetablefiltermodel.h"
#include "qgsattributetablemodel.h"
#include "qgsvectorlayer.h"

#include "qgsfeature.h"
#include "qgsmapcanvas.h"
#include "qgslogger.h"
#include "qgsrendererv2.h"
#include "qgsvectorlayereditbuffer.h"
//////////////////
// Filter Model //
//////////////////

void QgsAttributeTableFilterModel::sort( int column, Qt::SortOrder order )
QgsAttributeTableFilterModel::QgsAttributeTableFilterModel( QgsMapCanvas* canvas, QgsAttributeTableModel* sourceModel, QObject* parent )
: QSortFilterProxyModel( parent )
, mCanvas( canvas )
, mFilterMode( ShowAll )
, mSelectedOnTop( false )
{
tableModel()->sort( column, order );
mMasterSelection = new QItemSelectionModel( this, this );
setSourceModel( sourceModel );
setDynamicSortFilter( true );
setSortRole( QgsAttributeTableModel::SortRole );
connect( layer(), SIGNAL( selectionChanged() ), SLOT( selectionChanged() ) );
}

QgsAttributeTableFilterModel::QgsAttributeTableFilterModel( QgsVectorLayer *theLayer )
bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
mLayer = theLayer;
mHideUnselected = false;
setDynamicSortFilter( true );
if ( mSelectedOnTop )
{
// TODO: does the model index (left/right) need to be converted to first?
bool leftSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( left.row() ) );
bool rightSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( right.row() ) );

if ( leftSelected && !rightSelected )
{
return true;
}
else if ( rightSelected && !leftSelected )
{
return false;
}
}

return QSortFilterProxyModel::lessThan( left, right );
}

void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
{
mSelectedOnTop = selectedOnTop;
if ( sortColumn() == -1 )
{
sort( 0, sortOrder() );
}
invalidate();
}


void QgsAttributeTableFilterModel::setSourceModel( QgsAttributeTableModel* sourceModel )
{
mTableModel = sourceModel;
delete mMasterSelection;
mMasterSelection = new QItemSelectionModel( sourceModel, this );

QSortFilterProxyModel::setSourceModel( sourceModel );

connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
// Issue a selectionChanged, so the selection gets synchronized to the map canvas selection
selectionChanged();
}

bool QgsAttributeTableFilterModel::selectedOnTop()
{
return mSelectedOnTop;
}

void QgsAttributeTableFilterModel::setFilteredFeatures( QgsFeatureIds ids )
{
mFilteredFeatures = ids;
mFilterMode = ShowFilteredList;
announcedInvalidateFilter();
}

void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
{
if ( filterMode != mFilterMode )
{
if ( filterMode == ShowVisible )
{
connect( mCanvas, SIGNAL( extentsChanged() ), SLOT( extentsChanged() ) );
generateListOfVisibleFeatures();
}
else
{
disconnect( SLOT( extentsChanged() ) );
}

mFilterMode = filterMode;
announcedInvalidateFilter();
}
}

void QgsAttributeTableFilterModel::selectionChanged()
{
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
mMasterSelection->clear();

QItemSelection selection;

foreach ( QgsFeatureId fid, layer()->selectedFeaturesIds() )
{
selection.append( QItemSelectionRange( mTableModel->idToIndex( fid ) ) );
}

mMasterSelection->select( selection, QItemSelectionModel::ClearAndSelect );

if ( mFilterMode == ShowSelected )
{
announcedInvalidateFilter();
}

if ( mSelectedOnTop )
{
invalidate();
sort( sortColumn(), sortOrder() );
}

connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
}

void QgsAttributeTableFilterModel::masterSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
{
disconnect( layer(), SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );

// Filter duplicate items (multiple columns)
QgsFeatureIds selectedFeatureIds;
QgsFeatureIds deselectedFeatureIds;

foreach ( QItemSelectionRange selectedRange, selected )
{
foreach ( QModelIndex selectedModelIdx, selectedRange.indexes() )
{
selectedFeatureIds << masterModel()->rowToId( selectedModelIdx.row() );
}
}

foreach ( QItemSelectionRange deselectedRange, deselected )
{
foreach ( QModelIndex deselectedModelIdx, deselectedRange.indexes() )
{
deselectedFeatureIds << masterModel()->rowToId( deselectedModelIdx.row() );
}
}

// Change selection without emitting a signal
foreach ( QgsFeatureId seletedFid, selectedFeatureIds )
{
layer()->select( seletedFid, false );
}
foreach ( QgsFeatureId deselectedFid, deselectedFeatureIds )
{
layer()->deselect( deselectedFid, false );
}

// Now emit the signal
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );

connect( layer(), SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
}

bool QgsAttributeTableFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
{
Q_UNUSED( sourceParent );
if ( mHideUnselected )
return mLayer->selectedFeaturesIds().contains( tableModel()->rowToId( sourceRow ) );
switch ( mFilterMode )
{
case ShowAll:
return true;
break;

case ShowFilteredList:
return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
break;

case ShowSelected:
return layer()->selectedFeaturesIds().contains( masterModel()->rowToId( sourceRow ) );
break;

case ShowVisible:
return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
break;

case ShowEdited:
{
QgsVectorLayerEditBuffer* editBuffer = layer()->editBuffer();
if ( editBuffer )
{
const QList<QgsFeatureId> addedFeatures = editBuffer->addedFeatures().keys();
const QList<QgsFeatureId> changedFeatures = editBuffer->changedAttributeValues().keys();
const QgsFeatureId fid = masterModel()->rowToId( sourceRow );
return addedFeatures.contains( fid ) || changedFeatures.contains( fid );
}
return false;
break;
}

default:
Q_ASSERT( false ); // In debug mode complain
return true; // In release mode accept row
break;
}
// returns are handled in their respective case statement above
}

void QgsAttributeTableFilterModel::extentsChanged()
{
generateListOfVisibleFeatures();
announcedInvalidateFilter();
}

void QgsAttributeTableFilterModel::announcedInvalidateFilter()
{
emit filterAboutToBeInvalidated();
invalidateFilter();
emit filterInvalidated();
}

void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
{
if ( !layer() )
return;

bool filter = false;
QgsRectangle rect = mCanvas->mapRenderer()->mapToLayerCoordinates( layer(), mCanvas->extent() );
QgsRenderContext renderContext;
QgsFeatureRendererV2* renderer = layer()->rendererV2();

mFilteredFeatures.clear();

if ( !renderer )
{
QgsDebugMsg( "Cannot get renderer" );
}

if ( layer()->hasScaleBasedVisibility() &&
( layer()->minimumScale() > mCanvas->mapRenderer()->scale() ||
layer()->maximumScale() <= mCanvas->mapRenderer()->scale() ) )
{
QgsDebugMsg( "Out of scale limits" );
}
else
{
if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
{
// setup scale
// mapRenderer()->renderContext()->scale is not automaticaly updated when
// render extent changes (because it's scale is used to identify if changed
// since last render) -> use local context
renderContext.setExtent( mCanvas->mapRenderer()->rendererContext()->extent() );
renderContext.setMapToPixel( mCanvas->mapRenderer()->rendererContext()->mapToPixel() );
renderContext.setRendererScale( mCanvas->mapRenderer()->scale() );
renderer->startRender( renderContext, layer() );
}

filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter;
}

return true;
QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setFilterRect( rect ) );

QgsFeature f;

while ( features.nextFeature( f ) )
{
if ( !filter || renderer->willRenderFeature( f ) )
{
mFilteredFeatures << f.id();
}
#if 0
if ( t.elapsed() > 5000 )
{
bool cancel = false;
emit progress( i, cancel );
if ( cancel )
break;

t.restart();
}
#endif
}

features.close();

if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
{
renderer->stopRender( renderContext );
}
}

QgsFeatureId QgsAttributeTableFilterModel::rowToId( const QModelIndex& row )
{
return masterModel()->rowToId( mapToSource( row ).row() );
}

QItemSelectionModel* QgsAttributeTableFilterModel::masterSelection()
{
return mMasterSelection;
}

QModelIndex QgsAttributeTableFilterModel::mapToMaster( const QModelIndex &proxyIndex ) const
{
// Master is source
return mapToSource( proxyIndex );
}

QModelIndex QgsAttributeTableFilterModel::mapFromMaster( const QModelIndex &sourceIndex ) const
{
// Master is source
return mapFromSource( sourceIndex );
}

QItemSelection QgsAttributeTableFilterModel::mapSelectionFromMaster( const QItemSelection& sourceSelection ) const
{
// Master is source
return mapSelectionFromMaster( sourceSelection );
}
171 changes: 156 additions & 15 deletions src/gui/attributetable/qgsattributetablefiltermodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,180 @@
#include <QSortFilterProxyModel>
#include <QModelIndex>

class QgsAttributeTableModel;
class QgsVectorLayer;
#include "qgsvectorlayer.h" //QgsFeatureIds
#include "qgsattributetablemodel.h"

class QgsAttributeTableFilterModel: public QSortFilterProxyModel
class QgsVectorLayerCache;
class QgsMapCanvas;
class QItemSelectionModel;

class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
{
Q_OBJECT

public:
enum FilterMode
{
ShowAll,
ShowSelected,
ShowVisible,
ShowFilteredList,
ShowEdited
};

/**
*
*
* Make sure, the master model is already loaded, so the selection will get synchronized.
*
* @param layerCache The layer cache to use
* @param sourceModel The QgsAttributeTableModel to use as source (mostly referred to as master model)
* @param canvas The mapCanvas. Used to identify the currently visible features.
*/
QgsAttributeTableFilterModel( QgsMapCanvas* canvas, QgsAttributeTableModel* sourceModel, QObject* parent = NULL );

void setSourceModel( QgsAttributeTableModel* sourceModel );

/**
* Constructor
* @param theLayer initializing layer pointer
* Changes the sort order of the features. If set to true, selected features
* will be sorted on top, regardless of the current sort column
*
* @param selectedOnTop Specify, if selected features should be sorted on top
*/
QgsAttributeTableFilterModel( QgsVectorLayer* theLayer );
void setSelectedOnTop( bool selectedOnTop );

/**
* Returns if selected features are currently shown on top
*
* @return True if selected are shown on top
*/
bool selectedOnTop();

/**
* Specify a list of features, which the filter will accept.
* The filter mode will automatically be adjusted to show only these features (ShowFilteredList).
*
* @param ids The list of feature ids which will be accepted by the filter
*/
virtual void setFilteredFeatures( QgsFeatureIds ids );

/**
* Set the filter mode the filter will use.
*
* @param filterMode Sets the current mode of the filter
*/
void setFilterMode( FilterMode filterMode );

/**
* Returns the layer this filter acts on.
*
* @return Abovementioned layer
*/
inline QgsVectorLayer *layer() const { return masterModel()->layer(); }

inline QgsVectorLayerCache *layerCache() const { return masterModel()->layerCache(); }

/**
* Returns the table model this filter is using
*
* @return the table model in quesion
*/
inline QgsAttributeTableModel *masterModel() const { return mTableModel; }

/**
* Returns the feature id for a given model index.
*
* @param row A model index of the row in question
*
* @return The feature id of the feature visible in the provided row
*/
QgsFeatureId rowToId( const QModelIndex& row );

/**
* Sorts model by the column
* @param column column to sort by
* @param order sorting order
* Returns a selection model which is mapped to the sourceModel (tableModel) of this proxy.
* This selection also contains the features not visible because of the current filter.
* Views using this filter model may update this selection and subscribe to changes in
* this selection. This selection will synchronize itself with the selection on the map
* canvas.
*
* @return The master selection
*/
virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );
QItemSelectionModel* masterSelection();

QgsVectorLayer *layer() const { return mLayer; }
QgsAttributeTableModel *tableModel() const { return reinterpret_cast<QgsAttributeTableModel*>( sourceModel() ); }
virtual QModelIndex mapToMaster( const QModelIndex &proxyIndex ) const;

void setHideUnselected( bool theFlag ) { mHideUnselected = theFlag; }
virtual QModelIndex mapFromMaster( const QModelIndex &sourceIndex ) const;

virtual QItemSelection mapSelectionFromMaster( const QItemSelection& sourceSelection ) const;

protected:
/**
* Returns true if the source row will be accepted
*
* @param sourceRow row from the source model
* @param sourceParent parent index in the source model
*/
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;

/**
* Updates the list of currently visible features on the map canvas.
* Is called automatically when the filter mode is adjusted or the extents changed.
*/
void generateListOfVisibleFeatures();

/**
* Used by the sorting algorithm. Compares the two model indices. Will also consider the
* selection state of the feature in case selected features are to be shown on top.
*/
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;

/**
* Calls invalidateFilter on the underlying QSortFilterProxyModel, but emits the signals
* filterAboutToBeInvalidated before and the signal filterInvalidated after the changes on the
* filter happen.
*/
void announcedInvalidateFilter();



public slots:
/**
* Is called upon every change of the selection on the map canvas.
* When an update is signalled, the filter is updated and invalidated if needed.
*
*/
void selectionChanged();

void masterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );

/**
* Is called upon every change of the visible extents on the map canvas.
* When a change is signalled, the filter is updated and invalidated if needed.
*
*/
void extentsChanged();

signals:
/**
* This signal is emitted, before the filter is invalidated. With the help of this signal,
* selections of views attached to this can disable synchronisation with the master selection
* before items currently not visible with the filter get removed from the selection.
*/
void filterAboutToBeInvalidated();

/**
* Is called after the filter has been invalidated and recomputed.
* See filterAboutToBeInvalidated.
*/
void filterInvalidated();

private:
QgsVectorLayer* mLayer;
bool mHideUnselected;
QgsFeatureIds mFilteredFeatures;
QgsMapCanvas* mCanvas;
FilterMode mFilterMode;
bool mSelectedOnTop;
QItemSelectionModel* mMasterSelection;
QgsAttributeTableModel* mTableModel;
};

#endif
47 changes: 0 additions & 47 deletions src/gui/attributetable/qgsattributetableidcolumnpair.cpp

This file was deleted.

38 changes: 0 additions & 38 deletions src/gui/attributetable/qgsattributetableidcolumnpair.h

This file was deleted.

Loading