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.
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 : public 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
851 changes: 322 additions & 529 deletions src/app/qgsattributetabledialog.cpp

Large diffs are not rendered by default.

124 changes: 32 additions & 92 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,23 @@ 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;
QDockWidget* mDock;
QTime mStarted;
bool mWorkaround;
QgsFeatureIds mSelectedFeatures;
int mIndexPressed;

QItemSelectionModel* mSelectionModel;
int mLastClickedHeaderIndex;
};


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
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
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
24 changes: 19 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 @@ -156,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 @@ -219,6 +228,7 @@ qgscursors.h
qgsencodingfiledialog.h
qgsfiledropedit.h
qgsgenericprojectionselector.h
qgshighlight.h
qgsmapcanvas.h
qgsmapcanvasitem.h
qgsmapcanvasmap.h
Expand All @@ -242,6 +252,7 @@ qgsscalecombobox.h
qgsblendmodecombobox.h
qgssearchquerybuilder.h
qgsattributeeditor.h
qgsattributedialog.h
qgsfieldvalidator.h
qgsexpressionbuilderwidget.h
qgsexpressionbuilderdialog.h
Expand All @@ -251,11 +262,13 @@ 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 Down Expand Up @@ -296,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
3 changes: 2 additions & 1 deletion 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 All @@ -40,7 +41,7 @@ class QgsAttributeTableDelegate : public QItemDelegate
* @param parent parent object
*/
QgsAttributeTableDelegate( QObject* parent = NULL ) :
QItemDelegate( parent ) {};
QItemDelegate( parent ) {};
/** Used to create an editor for when the user tries to
* change the contents of a cell */
QWidget * createEditor(
Expand Down
313 changes: 303 additions & 10 deletions src/gui/attributetable/qgsattributetablefiltermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,324 @@
* *
***************************************************************************/

#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 );
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()
{
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 );
}
169 changes: 155 additions & 14 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 QgsVectorLayerCache;
class QgsMapCanvas;
class QItemSelectionModel;

class 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.

147 changes: 0 additions & 147 deletions src/gui/attributetable/qgsattributetablememorymodel.cpp

This file was deleted.

94 changes: 0 additions & 94 deletions src/gui/attributetable/qgsattributetablememorymodel.h

This file was deleted.

324 changes: 48 additions & 276 deletions src/gui/attributetable/qgsattributetablemodel.cpp

Large diffs are not rendered by default.

103 changes: 55 additions & 48 deletions src/gui/attributetable/qgsattributetablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,76 @@
#include <QHash>
#include <QQueue>

#include "qgsfeature.h" // QgsAttributeMap
#include "qgsvectorlayer.h" // QgsAttributeList
#include "qgsattributetableidcolumnpair.h"
#include "qgsvectorlayercache.h"

class QgsMapCanvas;

/**
* A model backed by a {@link QgsVectorLayerCache} which is able to provide
* feature/attribute information to a QAbstractItemView.
*
* @brief
* Is able to generate editor widgets for its QModelIndexes as well.
* Is mostly referred to as "master model" within this doc and the source.
*
* @see <a href="http://doc.qt.digia.com/qt/model-view-programming.html">Qt Model View Programming</a>
*/
class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
{
Q_OBJECT

public:
/**
* Constructor
* @param canvas map canvas pointer
* @param theLayer layer pointer
* @param parent parent pointer
* @param layerCache A layer cache to use as backend
* @param parent The parent QObject (owner)
*/
QgsAttributeTableModel( QgsMapCanvas *canvas, QgsVectorLayer *theLayer, QObject *parent = 0 );
QgsAttributeTableModel( QgsVectorLayerCache *layerCache, QObject *parent = 0 );

~QgsAttributeTableModel();
virtual ~QgsAttributeTableModel();

/**
* Loads the layer into the model
* Preferably to be called, before basing any other models on this model
*/
virtual void loadLayer();

/**
* Returns the number of rows
* @param parent parent index
*/
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;

/**
* Returns the number of columns
* @param parent parent index
*/
int columnCount( const QModelIndex &parent = QModelIndex() ) const;

/**
* Returns header data
* @param section required section
* @param orientation horizontal or vertical orientation
* @param role data role
*/
QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;

/**
* Returns data on the given index
* @param index model index
* @param role data role
*/
virtual QVariant data( const QModelIndex &index, int role ) const;

/**
* Updates data on given index
* @param index model index
* @param value new data value
* @param role data role
*/
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );

/**
* Returns item flags for the index
* @param index model index
Expand All @@ -86,46 +105,41 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
* @param index2 end index
*/
void reload( const QModelIndex &index1, const QModelIndex &index2 );

/**
* Remove rows
*/
bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() );

/**
* Resets the model
*/
void resetModel();
/**
* Layout has been changed
*/
void changeLayout();
/**
* Layout will be changed
*/
void incomingChangeLayout();

/**
* Maps feature id to table row
* @param id feature id
*/
int idToRow( QgsFeatureId id ) const;

QModelIndex idToIndex( QgsFeatureId id ) const;

/**
* get field index from column
*/
int fieldIdx( int col ) const;

/**
* get column from field index
*/
int fieldCol( int idx ) const;

/**
* Maps row to feature id
* @param row row number
*/
QgsFeatureId rowToId( int row ) const;
/**
* Sorts the model
* @param column column to sort by
* @param order sorting order
*/
virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );

/**
* Swaps two rows
* @param a first row
Expand All @@ -134,14 +148,22 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
void swapRows( QgsFeatureId a, QgsFeatureId b );

/**
* Returns layer pointer
* Returns the layer this model uses as backend. Retrieved from the layer cache.
*/
inline QgsVectorLayer* layer() const { return mLayerCache ? mLayerCache->layer() : NULL; }

/**
* Returns the layer cache this model uses as backend.
*/
QgsVectorLayer* layer() const { return mLayer; }
inline QgsVectorLayerCache* layerCache() const { return mLayerCache; }

/** Execute an action */
/**
* Execute an action
*/
void executeAction( int action, const QModelIndex &idx ) const;

/** return feature attributes at given index */
/**
* return feature attributes at given index */
QgsFeature feature( const QModelIndex &idx ) const;

signals:
Expand All @@ -154,11 +176,6 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
void progress( int i, bool &cancel );
void finished();

public slots:
void extentsChanged();

void layerRepaintRequested();

private slots:
/**
* Launched when attribute has been added
Expand Down Expand Up @@ -197,43 +214,33 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
virtual void layerDeleted();

protected:
QgsMapCanvas *mCanvas;
QgsVectorLayer *mLayer;
QgsVectorLayerCache *mLayerCache;
int mFieldCount;

mutable QgsFeature mFeat;
mutable QHash<QgsFeatureId, QgsFeature> mFeatureMap;

QgsAttributeList mAttributes;
QMap< int, const QMap<QString, QVariant> * > mValueMaps;

QList<QgsAttributeTableIdColumnPair> mSortList;
QHash<QgsFeatureId, int> mIdRowMap;
QHash<int, QgsFeatureId> mRowIdMap;

//! useful when showing only features from a particular extent
QgsRectangle mCurrentExtent;

/**
* Gets mFieldCount, mAttributes and mValueMaps
*/
virtual void loadAttributes();

public:
/**
* Loads the layer into the model
*/
virtual void loadLayer();

private:
mutable QQueue<QgsFeatureId> mFeatureQueue;

/**
* load feature fid into mFeat
* @param fid feature id
* Load feature fid into local cache (mFeat)
*
* @param fid feature id
*
* @return feature exists
*/
virtual bool featureAtId( QgsFeatureId fid ) const;
virtual bool loadFeatureAtId( QgsFeatureId fid ) const;

QgsFeatureRequest mFeatureRequest;
};


Expand Down
193 changes: 156 additions & 37 deletions src/gui/attributetable/qgsattributetableview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@

#include "qgsattributetableview.h"
#include "qgsattributetablemodel.h"
#include "qgsattributetablememorymodel.h"
#include "qgsattributetabledelegate.h"
#include "qgsattributetablefiltermodel.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayercache.h"
#include "qgsvectordataprovider.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"

QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
: QTableView( parent ), mModel( 0 ), mFilterModel( 0 ), mActionPopup( 0 )
: QTableView( parent ),
mMasterModel( NULL ),
mFilterModel( NULL ),
mActionPopup( NULL )
{
QSettings settings;
restoreGeometry( settings.value( "/BetterAttributeTable/geometry" ).toByteArray() );
Expand All @@ -40,57 +43,69 @@ QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
setItemDelegate( new QgsAttributeTableDelegate( this ) );

setSelectionBehavior( QAbstractItemView::SelectRows );
setSelectionMode( QAbstractItemView::NoSelection );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setSortingEnabled( true );

connect( verticalHeader(), SIGNAL( sectionClicked( int ) ), SLOT( onVerticalHeaderSectionClicked( int ) ) );
}

void QgsAttributeTableView::setCanvasAndLayer( QgsMapCanvas *canvas, QgsVectorLayer *layer )
QgsAttributeTableView::~QgsAttributeTableView()
{
if ( !layer )
if ( mActionPopup )
{
setModel( 0 );
delete mModel;
mModel = 0;
delete mFilterModel;
mFilterModel = 0;
return;
delete mActionPopup;
}
}

QgsAttributeTableModel* oldModel = mModel;
void QgsAttributeTableView::setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVectorLayerCache *layerCache )
{
QgsAttributeTableModel* oldModel = mMasterModel;
QgsAttributeTableFilterModel* filterModel = mFilterModel;

// in case the provider allows fast access to features
// we will use the model that calls featureAtId() to fetch only the
// features in the current view. Otherwise we'll have to store
// everything in the memory because using featureAtId() would be too slow
if ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::SelectAtId )
{
QgsDebugMsg( "SelectAtId supported" );
mModel = new QgsAttributeTableModel( canvas, layer );
}
else
{
QgsDebugMsg( "SelectAtId NOT supported" );
mModel = new QgsAttributeTableMemoryModel( canvas, layer );
}
mMasterModel = new QgsAttributeTableModel( layerCache, this );

connect( mModel, SIGNAL( progress( int, bool& ) ), this, SIGNAL( progress( int, bool& ) ) );
connect( mModel, SIGNAL( finished() ), this, SIGNAL( finished() ) );
mModel->loadLayer();
mLayerCache = layerCache;

mFilterModel = new QgsAttributeTableFilterModel( mModel->layer() );
mFilterModel->setSourceModel( mModel );
mMasterModel->loadLayer();

mFilterModel = new QgsAttributeTableFilterModel( canvas, mMasterModel, mMasterModel );
setModel( mFilterModel );

delete oldModel;
delete filterModel;
}

QgsAttributeTableView::~QgsAttributeTableView()
void QgsAttributeTableView::setModel( QgsAttributeTableFilterModel* filterModel )
{
if ( mFilterModel )
{
// Cleanup old model stuff if present
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );

disconnect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), this, SLOT( onFilterAboutToBeInvalidated() ) );
disconnect( mFilterModel, SIGNAL( filterInvalidated() ), this, SLOT( onFilterInvalidated() ) );
}

mFilterModel = filterModel;
QTableView::setModel( filterModel );

if ( filterModel )
{
// Connect new model stuff
mMasterSelection = mFilterModel->masterSelection();

connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );

connect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), SLOT( onFilterAboutToBeInvalidated() ) );
connect( mFilterModel, SIGNAL( filterInvalidated() ), SLOT( onFilterInvalidated() ) );
}
}

QItemSelectionModel* QgsAttributeTableView::masterSelection()
{
delete mModel;
delete mFilterModel;
delete mActionPopup;
return mMasterSelection;
}

void QgsAttributeTableView::closeEvent( QCloseEvent *e )
Expand All @@ -100,7 +115,109 @@ void QgsAttributeTableView::closeEvent( QCloseEvent *e )
settings.setValue( "/BetterAttributeTable/geometry", QVariant( saveGeometry() ) );
}

void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent *event )
void QgsAttributeTableView::mousePressEvent( QMouseEvent *event )
{
setSelectionMode( QAbstractItemView::NoSelection );
QTableView::mousePressEvent( event );
setSelectionMode( QAbstractItemView::ExtendedSelection );
}

void QgsAttributeTableView::mouseReleaseEvent( QMouseEvent *event )
{
setSelectionMode( QAbstractItemView::NoSelection );
QTableView::mouseReleaseEvent( event );
setSelectionMode( QAbstractItemView::ExtendedSelection );
}

void QgsAttributeTableView::mouseMoveEvent( QMouseEvent *event )
{
setSelectionMode( QAbstractItemView::NoSelection );
QTableView::mouseMoveEvent( event );
setSelectionMode( QAbstractItemView::ExtendedSelection );
}

void QgsAttributeTableView::keyPressEvent( QKeyEvent *event )
{
switch ( event->key() )
{

// Default Qt behavior would be to change the selection.
// We don't make it that easy for the user to trash his selection.
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
setSelectionMode( QAbstractItemView::NoSelection );
QTableView::keyPressEvent( event );
setSelectionMode( QAbstractItemView::ExtendedSelection );
break;

default:
QTableView::keyPressEvent( event );
break;
}
}

void QgsAttributeTableView::onVerticalHeaderSectionClicked( int logicalIndex )
{
Q_UNUSED( logicalIndex )

QgsFeatureIds selectedFeatures;

QModelIndexList selectedRows = selectionModel()->selectedRows();

foreach( QModelIndex row, selectedRows )
{
selectedFeatures.insert( mFilterModel->rowToId( row ) );
}

emit selectionChangeFinished( selectedFeatures );
}

void QgsAttributeTableView::onFilterAboutToBeInvalidated()
{
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}

void QgsAttributeTableView::onFilterInvalidated()
{
QItemSelection localSelection = mFilterModel->mapSelectionFromSource( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection,QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}

void QgsAttributeTableView::onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
disconnect( mMasterSelection, SIGNAL( selectionChanged(QItemSelection,QItemSelection)), this, SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
QItemSelection masterSelected = mFilterModel->mapSelectionToSource( selected );
QItemSelection masterDeselected = mFilterModel->mapSelectionToSource( deselected );

mMasterSelection->select( masterSelected, QItemSelectionModel::Select );
mMasterSelection->select( masterDeselected, QItemSelectionModel::Deselect );
connect( mMasterSelection, SIGNAL( selectionChanged(QItemSelection,QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
}

void QgsAttributeTableView::onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
Q_UNUSED( selected )
Q_UNUSED( deselected )
disconnect( selectionModel(), SIGNAL( selectionChanged(QItemSelection,QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );

// Synchronizing the whole selection seems to work faster than using the deltas (Deselecting takes pretty long)
QItemSelection localSelection = mFilterModel->mapSelectionFromSource( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );

connect( selectionModel(), SIGNAL( selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onSelectionChanged( QItemSelection, QItemSelection ) ) );
}

void QgsAttributeTableView::selectAll()
{
QItemSelection selection;
selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
selectionModel()->select( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
}

void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent* event )
{
if ( mActionPopup )
{
Expand All @@ -114,12 +231,14 @@ void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent *event )
return;
}

QgsVectorLayer *vlayer = mModel->layer();
QgsVectorLayer *vlayer = mFilterModel->layer();
if ( !vlayer )
return;

mActionPopup = new QMenu();

mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll );

// let some other parts of the application add some actions
emit willShowContextMenu( mActionPopup, idx );

Expand Down
112 changes: 102 additions & 10 deletions src/gui/attributetable/qgsattributetableview.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@
#include <QTableView>
#include <QAction>

#include "qgsfeature.h" // For QgsFeatureIds

class QgsAttributeTableModel;
class QgsAttributeTableFilterModel;
class QgsVectorLayerCache;

class QgsMapCanvas;
class QgsVectorLayer;
class QMenu;
class QProgressDialog;

/**
* @brief
* Provides a table view of features of a {@link QgsVectorLayer}.
*
* This can either be used as a standalone widget. {@link QgsBrowser} features a reference implementation.
* Or this can be used within the {@link QgsDualView} stacked widget.
*/

class GUI_EXPORT QgsAttributeTableView : public QTableView
{
Q_OBJECT
Expand All @@ -35,34 +46,115 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
QgsAttributeTableView( QWidget* parent = 0 );
virtual ~QgsAttributeTableView();

virtual void setModel( QgsAttributeTableFilterModel* filterModel );

/**
* The selection used for synchronisation with other views.
*
* @param masterSelection The selection to synchronize to and from
*/
QItemSelectionModel* masterSelection();

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

protected:
/**
* Called for mouse press events on a table cell.
* Disables selection change for these events.
*
* @param event The mouse event
*/
void mousePressEvent( QMouseEvent *event );

/**
* Called for mouse release events on a table cell.
* Disables selection change for these events.
*
* @param event The mouse event
*/
void mouseReleaseEvent( QMouseEvent *event );

/**
* Sets the layer
* @param canvas canvas pointer
* @param layer layer pointer
* Called for mouse move events on a table cell.
* Disables selection change for these events.
*
* @param event The mouse event
*/
void setCanvasAndLayer( QgsMapCanvas *canvas, QgsVectorLayer *layer );
void mouseMoveEvent( QMouseEvent *event );

/**
* Called for key press events
* Disables selection change by only pressing an arrow key
*
* @param event The mouse event
*/
void keyPressEvent( QKeyEvent *event );

/**
* @brief
* Is called when the context menu will be shown. Emits a {@link willShowContextMenu} signal,
* so the menu can be populated by other parts of the application.
*
* @param event The associated event object.
*/
void contextMenuEvent( QContextMenuEvent* event );

/**
* Saves geometry to the settings on close
* @param event not used
*/
void closeEvent( QCloseEvent *event );

void contextMenuEvent( QContextMenuEvent* );

signals:
/**
* @brief
* Is emitted, in order to provide a hook to add aditional menu entries to the context menu.
*
* @param menu If additional QMenuItems are added, they will show up in the context menu.
* @param atIndex The QModelIndex, to which the context menu belongs. Relative to the source model.
* In most cases, this will be a {@link QgsAttributeTableFilterModel}
*/
void willShowContextMenu( QMenu* menu, QModelIndex atIndex );

void finished();

//! @note not available in python bindings
void progress( int i, bool &cancel );
/**
* @brief
* Is emitted, after the selection has been changed.
*
* @param selectedFeatures A list of currently selected features.
*/
void selectionChangeFinished( const QgsFeatureIds &selectedFeatures );

public slots:
/**
* Is triggered after a mouse release event on the vertical header.
* Emits a selectionChangeFinished() signal, so the underlying sort filter
* can adapt to the current selection without disturbing the users current interaction.
*
* @param logicalIndex The section's logical index
*/
void onVerticalHeaderSectionClicked( int logicalIndex );
void onFilterAboutToBeInvalidated();
void onFilterInvalidated();
void onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
void onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
virtual void selectAll();

private:
QgsMapCanvas *mCanvas;
QgsAttributeTableModel* mModel;
QgsAttributeTableModel* mMasterModel;
QgsAttributeTableFilterModel* mFilterModel;
QAbstractItemModel* mModel; // Most likely the filter model
QMenu *mActionPopup;
QgsVectorLayerCache* mLayerCache;
QItemSelectionModel* mMasterSelection;
};

#endif
362 changes: 362 additions & 0 deletions src/gui/attributetable/qgsdualview.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,362 @@
/***************************************************************************
qgsdualview.cpp
--------------------------------------
Date : 10.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 "qgsdualview.h"
#include "qgsmapcanvas.h"
#include "qgsvectorlayercache.h"
#include "qgsattributetablemodel.h"
#include "qgsfeaturelistmodel.h"
#include "qgsattributedialog.h"
#include "qgsapplication.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsattributeaction.h"
#include "qgsvectordataprovider.h"

#include <QDialog>
#include <QMenu>
#include <QProgressDialog>

QgsDualView::QgsDualView( QWidget* parent ) :
QStackedWidget( parent ),
mProgressDlg( NULL )
{
setupUi( this );

mPreviewActionMapper = new QSignalMapper( this );

mPreviewColumnsMenu = new QMenu( this );
mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );

// Set preview icon
mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );

// Connect layer list preview signals
connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
}

QgsDualView::~QgsDualView()
{
delete mAttributeDialog;
}

void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QgsDistanceArea myDa )
{
mDistanceArea = myDa;

connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );

connect( layer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );

initLayerCache( layer );
initModels( mapCanvas );

mTableView->setModel( mFilterModel );
mFeatureList->setModel( mFeatureListModel );

mAttributeDialog = new QgsAttributeDialog( layer, 0, false, myDa );
mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );

columnBoxInit();
}

void QgsDualView::columnBoxInit()
{
QList< QAction* > previewActions = mFeatureListPreviewButton->actions();
foreach( QAction* a, previewActions )
{
if ( a != mActionExpressionPreview )
{
mPreviewActionMapper->removeMappings( a );
delete a;
}
}

mFeatureListPreviewButton->addAction( mActionExpressionPreview );
mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );

QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();

foreach( const QgsField field, fields )
{
if ( mLayerCache->layer()->editType( mLayerCache->layer()->fieldNameIndex( field.name() ) ) != QgsVectorLayer::Hidden )
{
QIcon icon = QgsApplication::getThemeIcon( "/mActionNewAttribute.png" );
QString text = field.name();

// Generate action for the preview popup button of the feature list
QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
mPreviewActionMapper->setMapping( previewAction, previewAction );
connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
mPreviewColumnsMenu->addAction( previewAction );

if ( text == mLayerCache->layer()->displayField() )
{
mFeatureListPreviewButton->setDefaultAction( previewAction );
}
}
}

// Most likely no displayField is defined
// Join primary key fields
if ( !mFeatureListPreviewButton->defaultAction() )
{
mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
QgsAttributeList pkAttrs = mLayerCache->layer()->pendingPkAttributesList();
// If there is a primary key defined
if ( pkAttrs.size() > 0 )
{
QStringList pkFields;

foreach( int attr, pkAttrs )
{
pkFields.append( "\"" + fields[attr].name() + "\"" );
}

mFeatureList->setDisplayExpression( pkFields.join( "||', '||" ) );
}
else if ( fields.size() > 0 )
{
QStringList fieldNames;
foreach ( QgsField field, fields)
{
fieldNames.append( "\"" + field.name() + "\"" );
}

mFeatureList->setDisplayExpression( fieldNames.join( "||', '||" ) );
}
else
{
mFeatureList->setDisplayExpression( "[Please define preview text]" );
}
}
else
{
mFeatureListPreviewButton->defaultAction()->trigger();
}
}

void QgsDualView::setView( QgsDualView::ViewMode view )
{
setCurrentIndex( view );
}

void QgsDualView::setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode )
{
mFilterModel->setFilterMode( filterMode );
}

void QgsDualView::setSelectedOnTop( bool selectedOnTop )
{
mFilterModel->setSelectedOnTop( selectedOnTop );
}

void QgsDualView::initLayerCache( QgsVectorLayer* layer )
{
// Initialize the cache
QSettings settings;
int cacheSize = qMax( 1, settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt() );
mLayerCache = new QgsVectorLayerCache( layer, cacheSize, this );
mLayerCache->setCacheGeometry( false );
if ( 0 == ( QgsVectorDataProvider::SelectAtId & mLayerCache->layer()->dataProvider()->capabilities() ) )
{
connect( mLayerCache, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ) );

mLayerCache->setFullCache( true );
}
}

void QgsDualView::initModels( QgsMapCanvas* mapCanvas )
{
mMasterModel = new QgsAttributeTableModel( mLayerCache, this );

connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );

mMasterModel->loadLayer();

mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
}

void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
{
// Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
QgsAttributeDialog* oldDialog = mAttributeDialog;

if ( mAttributeDialog->dialog() )
{
if ( mLayerCache->layer()->isEditable() )
{
// Get the current (unedited) feature
QgsFeature srcFeat;
QgsFeatureId fid = mAttributeDialog->feature()->id();
mLayerCache->featureAtId( fid, srcFeat );
QgsAttributes src = srcFeat.attributes();

// Let the dialog write the edited widget values to it's feature
mAttributeDialog->accept();
// Get the edited feature
const QgsAttributes &dst = mAttributeDialog->feature()->attributes();

mLayerCache->layer()->beginEditCommand( tr( "Attributes changed" ) );

for ( int i = 0; i < dst.count(); ++i )
{
if ( dst[i] != src[i] )
{
mLayerCache->layer()->changeAttributeValue( fid, i, dst[i] );
}
}

mLayerCache->layer()->endEditCommand();
}

mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
}

mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( feat ), true, mDistanceArea, this, false );
mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );

delete oldDialog;
}

void QgsDualView::setCurrentEditSelection( const QgsFeatureIds& fids )
{
mFeatureList->setEditSelection( fids );
}

void QgsDualView::previewExpressionBuilder()
{
// Show expression builder
QgsExpressionBuilderDialog dlg( mLayerCache->layer(), mFeatureList->displayExpression() , this );
dlg.setWindowTitle( tr( "Expression based preview" ) );
dlg.setExpressionText( mFeatureList->displayExpression() );

if ( dlg.exec() == QDialog::Accepted )
{
mFeatureList->setDisplayExpression( dlg.expressionText() );
mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
}
}

void QgsDualView::previewColumnChanged( QObject* action )
{
QAction* previewAction = qobject_cast< QAction* >( action );

if ( previewAction )
{
mFeatureList->setDisplayExpression( previewAction->text() );
mFeatureListPreviewButton->setDefaultAction( previewAction );
mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
}

Q_ASSERT( previewAction );
}

void QgsDualView::editingToggled()
{
// Reload the attribute dialog widget and commit changes if any
if ( mAttributeDialog->dialog() && mAttributeDialog->feature() )
{
on_mFeatureList_currentEditSelectionChanged( *mAttributeDialog->feature() );
}
}

int QgsDualView::featureCount()
{
return mMasterModel->rowCount();
}

int QgsDualView::filteredFeatureCount()
{
return mFilterModel->rowCount();
}

void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
{
QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );

if ( mLayerCache->layer()->actions()->size() != 0 )
{

QAction *a = menu->addAction( tr( "Run action" ) );
a->setEnabled( false );

for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
{
const QgsAction &action = mLayerCache->layer()->actions()->at( i );

if ( !action.runable() )
continue;

QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
menu->addAction( action.name(), a, SLOT( execute() ) );
}
}

QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ),this, -1, sourceIndex );
menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
}

void QgsDualView::setFilteredFeatures(QgsFeatureIds filteredFeatures)
{
mFilterModel->setFilteredFeatures( filteredFeatures );
}

void QgsDualView::progress( int i, bool& cancel )
{
if ( !mProgressDlg )
{
mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
mProgressDlg->setWindowModality( Qt::WindowModal );
mProgressDlg->show();
}

mProgressDlg->setValue( i );
mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );

QCoreApplication::processEvents();

cancel = mProgressDlg->wasCanceled();
}

void QgsDualView::finished()
{
delete mProgressDlg;
mProgressDlg = 0;
}


/*
* QgsAttributeTableAction
*/

void QgsAttributeTableAction::execute()
{
mDualView->masterModel()->executeAction( mAction, mFieldIdx );
}

void QgsAttributeTableAction::featureForm()
{
QgsFeatureIds editedIds;
editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
mDualView->setCurrentEditSelection( editedIds );
mDualView->setView( QgsDualView::AttributeEditor );
}
168 changes: 168 additions & 0 deletions src/gui/attributetable/qgsdualview.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/***************************************************************************
qgsdualview.h
--------------------------------------
Date : 10.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 QGSFEATURELIST_H
#define QGSFEATURELIST_H

#include <QStackedWidget>

#include "qgsdistancearea.h"
#include "qgsattributetablefiltermodel.h"
#include "qgscachedfeatureiterator.h"
#include "ui_qgsdualviewbase.h"

class QgsFeatureRequest;
class QgsAttributeDialog;
class QSignalMapper;

/**
* @brief
*/
class QgsDualView : public QStackedWidget, private Ui::QgsDualViewBase
{
Q_OBJECT

public:

/**
* @brief
*
*/
enum ViewMode
{
AttributeTable = 0,
AttributeEditor = 1
};

/**
* @brief
*
* @param parent
*/
explicit QgsDualView( QWidget* parent = 0 );
virtual ~QgsDualView();

void init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QgsDistanceArea myDa );
void columnBoxInit();

/**
* @brief
*
* @param view
*/
void setView( ViewMode view );

/**
* @brief
*
* @param filterMode
*/
void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode );

void setSelectedOnTop( bool selectedOnTop );

/**
* @brief
*
* @param featureRequest
*/
void setFeatureRequest( const QgsFeatureRequest* featureRequest );

/**
* @brief
*
*/
void setSelectionMode();

int featureCount();

int filteredFeatureCount();

void setFilteredFeatures( QgsFeatureIds filteredFeatures );

QgsAttributeTableModel* masterModel() const { return mMasterModel; }

public slots:
void setCurrentEditSelection( const QgsFeatureIds& fids );

signals:

private slots:
/**
* Changes the currently visible feature within the attribute editor
*/
void on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat );

void previewExpressionBuilder();

void previewColumnChanged( QObject* previewAction );

void editingToggled();

void viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex );

/**
* Will be called periodically, when loading layers from slow data providers.
*
* @param i The number of features already loaded
* @param canceled Set to true to cancel
*/
virtual void progress( int i, bool& cancel );

/**
* Will be called, once all the features are loaded.
* Use e.g. to close a dialog created from {@link progress(int i,bool& cancel )}
*/
virtual void finished();

private:
void initLayerCache(QgsVectorLayer *layer);
void initModels( QgsMapCanvas* mapCanvas );

QgsAttributeTableModel* mMasterModel;
QgsAttributeTableFilterModel* mFilterModel;
QgsFeatureListModel* mFeatureListModel;
QgsAttributeDialog* mAttributeDialog;
QgsCachedFeatureIterator* mFeatureCache;
QSignalMapper* mPreviewActionMapper;
QMenu* mPreviewColumnsMenu;
QgsVectorLayerCache* mLayerCache;
QProgressDialog* mProgressDlg;

QgsDistanceArea mDistanceArea;

friend class TestQgsDualView;
};

class QgsAttributeTableAction : public QAction
{
Q_OBJECT

public:
QgsAttributeTableAction( const QString &name, QgsDualView *dualView, int action, const QModelIndex &fieldIdx ) :
QAction( name, dualView ), mDualView( dualView ), mAction( action ), mFieldIdx( fieldIdx )
{}

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

private:
QgsDualView* mDualView;
int mAction;
QModelIndex mFieldIdx;
};

#endif // QGSFEATURELIST_H
Loading