From 11a5a69e34ab688a3ce1014f99036823b8fe2a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 11 May 2016 01:01:58 +0200 Subject: [PATCH] Be able to store the bookmarks in the project (#2661) --- src/app/qgsbookmarks.cpp | 381 +++++++++++++++++++++++++++++++++++---- src/app/qgsbookmarks.h | 83 ++++++++- 2 files changed, 430 insertions(+), 34 deletions(-) diff --git a/src/app/qgsbookmarks.cpp b/src/app/qgsbookmarks.cpp index 1715001189f..d31f0fa0817 100644 --- a/src/app/qgsbookmarks.cpp +++ b/src/app/qgsbookmarks.cpp @@ -30,7 +30,8 @@ #include #include #include -#include +#include +#include QgsBookmarks::QgsBookmarks( QWidget *parent ) : QDockWidget( parent ) @@ -75,23 +76,26 @@ QgsBookmarks::QgsBookmarks( QWidget *parent ) : QDockWidget( parent ) return; } - QSqlTableModel *model = new QSqlTableModel( this, db ); - model->setTable( "tbl_bookmarks" ); - model->setSort( 0, Qt::AscendingOrder ); - model->setEditStrategy( QSqlTableModel::OnFieldChange ); - model->select(); + mQgisModel = new QSqlTableModel( this, db ); + mQgisModel->setTable( "tbl_bookmarks" ); + mQgisModel->setSort( 0, Qt::AscendingOrder ); + mQgisModel->select(); + mQgisModel->setEditStrategy( QSqlTableModel::OnFieldChange ); // set better headers then column names from table - model->setHeaderData( 0, Qt::Horizontal, tr( "ID" ) ); - model->setHeaderData( 1, Qt::Horizontal, tr( "Name" ) ); - model->setHeaderData( 2, Qt::Horizontal, tr( "Project" ) ); - model->setHeaderData( 3, Qt::Horizontal, tr( "xMin" ) ); - model->setHeaderData( 4, Qt::Horizontal, tr( "yMin" ) ); - model->setHeaderData( 5, Qt::Horizontal, tr( "xMax" ) ); - model->setHeaderData( 6, Qt::Horizontal, tr( "yMax" ) ); - model->setHeaderData( 7, Qt::Horizontal, tr( "SRID" ) ); + mQgisModel->setHeaderData( 0, Qt::Horizontal, tr( "ID" ) ); + mQgisModel->setHeaderData( 1, Qt::Horizontal, tr( "Name" ) ); + mQgisModel->setHeaderData( 2, Qt::Horizontal, tr( "Project" ) ); + mQgisModel->setHeaderData( 3, Qt::Horizontal, tr( "xMin" ) ); + mQgisModel->setHeaderData( 4, Qt::Horizontal, tr( "yMin" ) ); + mQgisModel->setHeaderData( 5, Qt::Horizontal, tr( "xMax" ) ); + mQgisModel->setHeaderData( 6, Qt::Horizontal, tr( "yMax" ) ); + mQgisModel->setHeaderData( 7, Qt::Horizontal, tr( "SRID" ) ); - lstBookmarks->setModel( model ); + mProjectModel = new QgsProjectBookmarksTableModel(); + mModel.reset( new QgsMergedBookmarksTableModel( *mQgisModel, *mProjectModel, lstBookmarks ) ); + + lstBookmarks->setModel( mModel.data() ); QSettings settings; lstBookmarks->header()->restoreState( settings.value( "/Windows/Bookmarks/headerstate" ).toByteArray() ); @@ -103,7 +107,8 @@ QgsBookmarks::QgsBookmarks( QWidget *parent ) : QDockWidget( parent ) QgsBookmarks::~QgsBookmarks() { - delete lstBookmarks->model(); + delete mQgisModel; + delete mProjectModel; QSqlDatabase::removeDatabase( "bookmarks" ); saveWindowLocation(); } @@ -123,15 +128,15 @@ void QgsBookmarks::saveWindowLocation() void QgsBookmarks::addClicked() { - QSqlTableModel *model = qobject_cast( lstBookmarks->model() ); - Q_ASSERT( model ); + Q_ASSERT( mModel ); + Q_ASSERT( mQgisModel ); QgsMapCanvas *canvas = QgisApp::instance()->mapCanvas(); Q_ASSERT( canvas ); QSqlQuery query( "INSERT INTO tbl_bookmarks(bookmark_id,name,project_name,xmin,ymin,xmax,ymax,projection_srid)" " VALUES (NULL,:name,:project_name,:xmin,:xmax,:ymin,:ymax,:projection_srid)", - model->database() ); + mQgisModel->database() ); QString projStr( "" ); if ( QgsProject::instance() ) @@ -156,11 +161,11 @@ void QgsBookmarks::addClicked() query.bindValue( ":projection_srid", QVariant::fromValue( canvas->mapSettings().destinationCrs().srsid() ) ); if ( query.exec() ) { - model->setSort( 0, Qt::AscendingOrder ); - model->select(); - lstBookmarks->scrollToBottom(); - lstBookmarks->setCurrentIndex( model->index( model->rowCount() - 1, 1 ) ); - lstBookmarks->edit( model->index( model->rowCount() - 1, 1 ) ); + mQgisModel->setSort( 0, Qt::AscendingOrder ); + mQgisModel->select(); + lstBookmarks->scrollTo( mModel->index( mQgisModel->rowCount() - 1, 1 ) ); + lstBookmarks->setCurrentIndex( mModel->index( mQgisModel->rowCount() - 1, 1 ) ); + lstBookmarks->edit( mModel->index( mQgisModel->rowCount() - 1, 1 ) ); } else { @@ -193,7 +198,7 @@ void QgsBookmarks::deleteClicked() int i = 0; Q_FOREACH ( int row, rows ) { - lstBookmarks->model()->removeRow( row - i ); + mModel->removeRow( row - i ); i++; } } @@ -265,8 +270,7 @@ void QgsBookmarks::importFromXML() QDomElement docElem = doc.documentElement(); QDomNodeList nodeList = docElem.elementsByTagName( "bookmark" ); - QSqlTableModel *model = qobject_cast( lstBookmarks->model() ); - Q_ASSERT( model ); + Q_ASSERT( mModel ); QString queries; @@ -293,7 +297,7 @@ void QgsBookmarks::importFromXML() } QStringList queriesList = queries.split( ';' ); - QSqlQuery query( model->database() ); + QSqlQuery query( mQgisModel->database() ); Q_FOREACH ( const QString& queryTxt, queriesList ) { @@ -309,8 +313,8 @@ void QgsBookmarks::importFromXML() } query.finish(); } - model->setSort( 0, Qt::AscendingOrder ); - model->select(); + mQgisModel->setSort( 0, Qt::AscendingOrder ); + mQgisModel->select(); } void QgsBookmarks::exportToXML() @@ -335,8 +339,8 @@ void QgsBookmarks::exportToXML() QDomElement root = doc.createElement( "qgis_bookmarks" ); doc.appendChild( root ); - int rowCount = lstBookmarks->model()->rowCount(); - int colCount = lstBookmarks->model()->columnCount(); + int rowCount = mModel->rowCount(); + int colCount = mModel->columnCount(); QList headerList; headerList << "id" << "name" << "project" << "xmin" @@ -348,7 +352,7 @@ void QgsBookmarks::exportToXML() root.appendChild( bookmark ); for ( int j = 0; j < colCount; j++ ) { - QModelIndex idx = lstBookmarks->model()->index( i, j ); + QModelIndex idx = mModel->index( i, j ); if ( idx.isValid() ) { QString value = idx.data( Qt::DisplayRole ).toString(); @@ -375,3 +379,314 @@ void QgsBookmarks::exportToXML() settings.setValue( "/Windows/Bookmarks/LastUsedDirectory", QFileInfo( fileName ).path() ); } + + +QgsProjectBookmarksTableModel::QgsProjectBookmarksTableModel() +{ + QObject::connect( + QgisApp::instance(), SIGNAL( projectRead() ), + this, SLOT( projectRead() ) ); +} + +int QgsProjectBookmarksTableModel::rowCount( const QModelIndex& parent ) const +{ + Q_UNUSED( parent ); + + return QgsProject::instance()->readNumEntry( "Bookmarks", "/count" ); +} + +int QgsProjectBookmarksTableModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED( parent ); + + return 8; +} + +QVariant QgsProjectBookmarksTableModel::data( const QModelIndex& index, int role ) const +{ + Q_UNUSED( role ); + Q_ASSERT( role == Qt::DisplayRole ); + + switch ( index.column() ) + { + case 1: + return QgsProject::instance()->readEntry( "Bookmarks", QString( "/Row-%1/Name" ).arg( index.row() ) ); + case 2: + return QgsProject::instance()->readEntry( "Bookmarks", QString( "/Row-%1/Project" ).arg( index.row() ) ); + case 3: + return QgsProject::instance()->readDoubleEntry( "Bookmarks", QString( "/Row-%1/MinX" ).arg( index.row() ) ); + case 4: + return QgsProject::instance()->readDoubleEntry( "Bookmarks", QString( "/Row-%1/MinY" ).arg( index.row() ) ); + case 5: + return QgsProject::instance()->readDoubleEntry( "Bookmarks", QString( "/Row-%1/MaxX" ).arg( index.row() ) ); + case 6: + return QgsProject::instance()->readDoubleEntry( "Bookmarks", QString( "/Row-%1/MaxY" ).arg( index.row() ) ); + case 7: + return QgsProject::instance()->readNumEntry( "Bookmarks", QString( "/Row-%1/SRID" ).arg( index.row() ) ); + default: + return QVariant(); + } +} + +bool QgsProjectBookmarksTableModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + Q_UNUSED( role ); + Q_ASSERT( role == Qt::EditRole ); + + switch ( index.column() ) + { + case 1: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/Name" ).arg( index.row() ), value.value() ); + return true; + case 2: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/Project" ).arg( index.row() ), value.value() ); + return true; + case 3: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/MinX" ).arg( index.row() ), value.toDouble() ); + return true; + case 4: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/MinY" ).arg( index.row() ), value.toDouble() ); + return true; + case 5: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/MaxX" ).arg( index.row() ), value.toDouble() ); + return true; + case 6: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/MaxY" ).arg( index.row() ), value.toDouble() ); + return true; + case 7: + QgsProject::instance()->writeEntry( "Bookmarks", QString( "/Row-%1/SRID" ).arg( index.row() ), value.toInt() ); + return true; + default: + return false; + } +} + +bool QgsProjectBookmarksTableModel::insertRows( int row, int count, const QModelIndex& parent ) +{ + Q_UNUSED( row ); + Q_UNUSED( parent ); + + return QgsProject::instance()->writeEntry( "Bookmarks", "/count", QgsProject::instance()->readNumEntry( "Bookmarks", "/count" ) + count ); +} + +bool QgsProjectBookmarksTableModel::removeRows( int row, int count, const QModelIndex& parent ) +{ + Q_UNUSED( parent ); + + for ( int newRow = row ; newRow < rowCount() - count ; newRow++ ) + { + for ( int column = 0 ; column < columnCount() ; column++ ) + { + setData( index( newRow, column ), data( index( newRow + count, column ) ) ); + } + } + for ( int newRow = rowCount() - count ; newRow < rowCount() ; newRow++ ) + { + QgsProject::instance()->removeEntry( "Bookmarks", QString( "/Row-%1" ).arg( newRow ) ); + } + + QgsProject::instance()->writeEntry( "Bookmarks", "/count", QgsProject::instance()->readNumEntry( "Bookmarks", "/count" ) - count ); + + return true; +} + +QgsMergedBookmarksTableModel::QgsMergedBookmarksTableModel( QAbstractTableModel& qgisTableModel, QAbstractTableModel& projectTableModel, QTreeView* treeView ): + mQgisTableModel( qgisTableModel ), + mProjectTableModel( projectTableModel ), + mTreeView( treeView ) +{ + QObject::connect( + QgisApp::instance(), SIGNAL( projectRead() ), + this, SLOT( projectRead() ) ); + + QObject::connect( + &mQgisTableModel, SIGNAL( layoutChanged() ), + this, SLOT( allLayoutChanged() ) ); + QObject::connect( + &mQgisTableModel, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), + this, SLOT( qgisDataChanged( const QModelIndex&, const QModelIndex& ) ) ); + QObject::connect( + &mQgisTableModel, SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), + this, SLOT( allLayoutChanged() ) ); + QObject::connect( + &mQgisTableModel, SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( allLayoutChanged() ) ); + + QObject::connect( + &projectTableModel, SIGNAL( layoutChanged() ), + this, SLOT( allLayoutChanged() ) ); + QObject::connect( + &projectTableModel, SIGNAL( projectDataChanged( const QModelIndex&, const QModelIndex& ) ), + this, SLOT( dataChanged( const QModelIndex&, const QModelIndex& ) ) ); + QObject::connect( + &projectTableModel, SIGNAL( projectRowsInserted( const QModelIndex&, int, int ) ), + this, SLOT( allLayoutChanged() ) ); + QObject::connect( + &projectTableModel, SIGNAL( ProjectRowsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( allLayoutChanged() ) ); +} + +int QgsMergedBookmarksTableModel::rowCount( const QModelIndex& parent ) const +{ + return mQgisTableModel.rowCount( parent ) + mProjectTableModel.rowCount( parent ); +} + +int QgsMergedBookmarksTableModel::columnCount( const QModelIndex& parent ) const +{ + return mQgisTableModel.columnCount( parent ) + 1; +} + +QVariant QgsMergedBookmarksTableModel::data( const QModelIndex& index, int role ) const +{ + // is project or application + if ( index.column() == mQgisTableModel.columnCount() ) + { + if ( role == Qt::CheckStateRole ) + { + return index.row() < mQgisTableModel.rowCount() ? Qt::Unchecked : Qt::Checked; + } + else + { + return QVariant(); + } + } + if ( index.row() < mQgisTableModel.rowCount() ) + { + return mQgisTableModel.data( index, role ); + } + else + { + if ( role == Qt::DisplayRole || role == Qt::EditRole ) + { + return mProjectTableModel.data( this->index( index.row() - mQgisTableModel.rowCount(), index.column() ), role ); + } + else + { + return mQgisTableModel.data( this->index( 0, index.column() ), role ); + } + } +} + +bool QgsMergedBookmarksTableModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + // is project or QGIS + if ( index.column() == mQgisTableModel.columnCount() ) + { + if ( index.row() < mQgisTableModel.rowCount() ) + { + moveBookmark( mQgisTableModel, mProjectTableModel, index.row() ); + mTreeView->scrollTo( this->index( rowCount() - 1, 1 ) ); + mTreeView->setCurrentIndex( this->index( rowCount() - 1, 1 ) ); + mTreeView->selectionModel()->select( this->index( rowCount() - 1, 1 ), QItemSelectionModel::Rows ); + } + else + { + moveBookmark( mProjectTableModel, mQgisTableModel, index.row() - mQgisTableModel.rowCount() ); + mTreeView->scrollTo( this->index( mQgisTableModel.rowCount() - 1, 1 ) ); + mTreeView->setCurrentIndex( this->index( mQgisTableModel.rowCount() - 1, 1 ) ); + mTreeView->selectionModel()->select( this->index( mQgisTableModel.rowCount() - 1, 1 ), QItemSelectionModel::Rows ); + } + return true; + } + if ( index.column() < mQgisTableModel.rowCount() ) + { + return mQgisTableModel.setData( index, value, role ); + } + else + { + return mProjectTableModel.setData( this->index( index.row() - mQgisTableModel.rowCount(), index.column() ), value, role ); + } +} + +Qt::ItemFlags QgsMergedBookmarksTableModel::flags( const QModelIndex& index ) const +{ + Q_UNUSED( index ); + + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if ( index.column() == mQgisTableModel.columnCount() ) + { + if ( !mProjectOpen ) + { + return Qt::ItemIsSelectable; + } + flags |= Qt::ItemIsUserCheckable; + } + else + { + flags |= Qt::ItemIsEditable; + } + return flags; +} + +bool QgsMergedBookmarksTableModel::removeRows( int row, int count, const QModelIndex& parent ) +{ + Q_ASSERT( count == 1 ); + + if ( row < mQgisTableModel.rowCount() ) + { + return mQgisTableModel.removeRows( row, count, parent ); + } + else + { + return mProjectTableModel.removeRows( row - mQgisTableModel.rowCount(), count, parent ); + } +} + +QVariant QgsMergedBookmarksTableModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( section == mQgisTableModel.columnCount() ) + { + if ( role == Qt::DisplayRole ) + { + return QVariant( tr( "In Project" ) ); + } + else + { + return mQgisTableModel.headerData( 0, orientation, role ); + } + } + else + { + return mQgisTableModel.headerData( section, orientation, role ); + } +} + +QAbstractTableModel* QgsMergedBookmarksTableModel::qgisModel() +{ + return &mQgisTableModel; +} + +void QgsMergedBookmarksTableModel::moveBookmark( QAbstractTableModel& modelFrom, QAbstractTableModel& modelTo, int row ) +{ + QSqlTableModel* qgisModel = dynamic_cast( &modelTo ); + if ( qgisModel == NULL ) + { + modelTo.insertRow( -1 ); + for ( int column = 1 ; column < modelFrom.columnCount() ; column++ ) + { + modelTo.setData( + modelTo.index( modelTo.rowCount() - 1, column ), + modelFrom.data( modelFrom.index( row, column ) ) ); + } + } + else + { + QSqlQuery query( "INSERT INTO tbl_bookmarks(bookmark_id,name,project_name,xmin,ymin,xmax,ymax,projection_srid)" + " VALUES (NULL,:name,:project_name,:xmin,:xmax,:ymin,:ymax,:projection_srid)", + qgisModel->database() ); + + query.bindValue( ":name", modelFrom.data( modelFrom.index( row, 1 ) ).value() ); + query.bindValue( ":project_name", modelFrom.data( modelFrom.index( row, 2 ) ).value() ); + query.bindValue( ":xmin", modelFrom.data( modelFrom.index( row, 3 ) ).toDouble() ); + query.bindValue( ":ymin", modelFrom.data( modelFrom.index( row, 4 ) ).toDouble() ); + query.bindValue( ":xmax", modelFrom.data( modelFrom.index( row, 5 ) ).toDouble() ); + query.bindValue( ":ymax", modelFrom.data( modelFrom.index( row, 6 ) ).toDouble() ); + query.bindValue( ":projection_srid", modelFrom.data( modelFrom.index( row, 7 ) ).toInt() ); + + query.exec(); + qgisModel->setSort( 0, Qt::AscendingOrder ); + qgisModel->select(); + } + + modelFrom.removeRow( row ); +} diff --git a/src/app/qgsbookmarks.h b/src/app/qgsbookmarks.h index 5e242c3e102..a5a3804972f 100644 --- a/src/app/qgsbookmarks.h +++ b/src/app/qgsbookmarks.h @@ -18,10 +18,88 @@ #define QGSBOOKMARKS_H #include +#include +#include #include "ui_qgsbookmarksbase.h" #include "qgscontexthelp.h" + +/* + * Model for project bookmarks + */ +class QgsProjectBookmarksTableModel: public QAbstractTableModel +{ + Q_OBJECT + + public: + + QgsProjectBookmarksTableModel(); + + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; + + int columnCount( const QModelIndex& parent = QModelIndex() ) const override; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + + bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override; + + bool insertRows( int row, int count, const QModelIndex& parent = QModelIndex() ) override; + + bool removeRows( int row, int count, const QModelIndex& parent = QModelIndex() ) override; + + private slots: + void projectRead() { emit layoutChanged(); }; +}; + +/* + * Model that merge the QGIS and project model + */ +class QgsMergedBookmarksTableModel: public QAbstractTableModel +{ + Q_OBJECT + + public: + + QgsMergedBookmarksTableModel( QAbstractTableModel& qgisTableModel, QAbstractTableModel& projectTableModel, QTreeView* treeView ); + + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; + + int columnCount( const QModelIndex& parent = QModelIndex() ) const override; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + + bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override; + Qt::ItemFlags flags( const QModelIndex& index ) const override; + + bool removeRows( int row, int count, const QModelIndex& parent = QModelIndex() ) override; + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + QAbstractTableModel* qgisModel(); + + private: + QAbstractTableModel& mQgisTableModel; + QAbstractTableModel& mProjectTableModel; + QTreeView* mTreeView; + bool mProjectOpen; + + void moveBookmark( QAbstractTableModel& modelFrom, QAbstractTableModel& modelTo, int row ); + + private slots: + void projectRead() { mProjectOpen = true; }; + void allLayoutChanged() { emit layoutChanged(); }; + void qgisDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) + { + emit dataChanged( topLeft, bottomRight ); + }; + void projectDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) + { + emit dataChanged( + index( topLeft.row() + mQgisTableModel.rowCount(), topLeft.column() ), + index( bottomRight.row() + mQgisTableModel.rowCount(), bottomRight.column() ) ); + }; +}; + + class APP_EXPORT QgsBookmarks : public QDockWidget, private Ui::QgsBookmarksBase { Q_OBJECT @@ -43,6 +121,10 @@ class APP_EXPORT QgsBookmarks : public QDockWidget, private Ui::QgsBookmarksBase void on_actionHelp_triggered() { QgsContextHelp::run( metaObject()->className() ); } private: + QSqlTableModel* mQgisModel; + QgsProjectBookmarksTableModel* mProjectModel; + QScopedPointer mModel; + void saveWindowLocation(); void restorePosition(); @@ -50,4 +132,3 @@ class APP_EXPORT QgsBookmarks : public QDockWidget, private Ui::QgsBookmarksBase #endif // QGSBOOKMARKS_H -