mirror of
https://github.com/qgis/QGIS.git
synced 2025-11-22 00:14:55 -05:00
789 lines
27 KiB
C++
789 lines
27 KiB
C++
/***************************************************************************
|
|
QgsBookmarks.cpp - Spatial Bookmarks
|
|
-------------------
|
|
begin : 2005-04-23
|
|
copyright : (C) 2005 Gary Sherman
|
|
email : sherman at mrcc dot com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
#include "qgisapp.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsbookmarks.h"
|
|
#include "qgsmapcanvas.h"
|
|
#include "qgsproject.h"
|
|
#include "qgsmessagelog.h"
|
|
#include "qgslogger.h"
|
|
#include "qgssettings.h"
|
|
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QMessageBox>
|
|
#include <QSqlError>
|
|
#include <QSqlQuery>
|
|
#include <QSqlRecord>
|
|
#include <QModelIndex>
|
|
#include <QDoubleSpinBox>
|
|
#include <QAbstractTableModel>
|
|
#include <QToolButton>
|
|
|
|
|
|
const int QgsDoubleSpinBoxBookmarksDelegate::DECIMAL_PLACES = 6;
|
|
|
|
QgsBookmarks::QgsBookmarks( QWidget *parent )
|
|
: QgsDockWidget( parent )
|
|
|
|
{
|
|
setupUi( this );
|
|
connect( lstBookmarks, &QTreeView::doubleClicked, this, &QgsBookmarks::lstBookmarks_doubleClicked );
|
|
restorePosition();
|
|
|
|
bookmarksDockContents->layout()->setMargin( 0 );
|
|
bookmarksDockContents->layout()->setContentsMargins( 0, 0, 0, 0 );
|
|
static_cast< QGridLayout * >( bookmarksDockContents->layout() )->setVerticalSpacing( 0 );
|
|
|
|
QToolButton *btnImpExp = new QToolButton;
|
|
btnImpExp->setAutoRaise( true );
|
|
btnImpExp->setToolTip( tr( "Import/Export Bookmarks" ) );
|
|
btnImpExp->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharing.svg" ) ) );
|
|
btnImpExp->setPopupMode( QToolButton::InstantPopup );
|
|
|
|
QMenu *share = new QMenu( this );
|
|
QAction *btnExport = share->addAction( tr( "&Export" ) );
|
|
QAction *btnImport = share->addAction( tr( "&Import" ) );
|
|
btnExport->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingExport.svg" ) ) );
|
|
btnImport->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingImport.svg" ) ) );
|
|
connect( btnExport, &QAction::triggered, this, &QgsBookmarks::exportToXml );
|
|
connect( btnImport, &QAction::triggered, this, &QgsBookmarks::importFromXml );
|
|
btnImpExp->setMenu( share );
|
|
|
|
connect( actionAdd, &QAction::triggered, this, &QgsBookmarks::addClicked );
|
|
connect( actionDelete, &QAction::triggered, this, &QgsBookmarks::deleteClicked );
|
|
connect( actionZoomTo, &QAction::triggered, this, &QgsBookmarks::zoomToBookmark );
|
|
|
|
mBookmarkToolbar->addWidget( btnImpExp );
|
|
|
|
// open the database
|
|
QSqlDatabase db = QSqlDatabase::addDatabase( QStringLiteral( "QSQLITE" ), QStringLiteral( "bookmarks" ) );
|
|
db.setDatabaseName( QgsApplication::qgisUserDatabaseFilePath() );
|
|
if ( ! db.open() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Error" ),
|
|
tr( "Unable to open bookmarks database.\nDatabase: %1\nDriver: %2\nDatabase: %3" )
|
|
.arg( QgsApplication::qgisUserDatabaseFilePath(),
|
|
db.lastError().driverText(),
|
|
db.lastError().databaseText() )
|
|
);
|
|
deleteLater();
|
|
return;
|
|
}
|
|
|
|
// Do not set parent or the destructor will try to write on the log viewer (and crash QGIS)
|
|
mQgisModel = new QSqlTableModel( nullptr, db );
|
|
mQgisModel->setTable( QStringLiteral( "tbl_bookmarks" ) );
|
|
mQgisModel->setSort( 0, Qt::AscendingOrder );
|
|
mQgisModel->select();
|
|
mQgisModel->setEditStrategy( QSqlTableModel::OnFieldChange );
|
|
|
|
// set better headers then column names from table
|
|
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" ) );
|
|
|
|
mProjectModel = new QgsProjectBookmarksTableModel( this );
|
|
mModel = new QgsMergedBookmarksTableModel( *mQgisModel, *mProjectModel, lstBookmarks, this );
|
|
|
|
mProxyModel = new QgsBookmarksProxyModel( this );
|
|
mProxyModel->setSourceModel( mModel );
|
|
|
|
lstBookmarks->setModel( mProxyModel );
|
|
lstBookmarks->setItemDelegate( new QgsDoubleSpinBoxBookmarksDelegate( this ) );
|
|
|
|
connect( mModel, &QgsMergedBookmarksTableModel::layoutChanged, mProxyModel, &QgsBookmarksProxyModel::_resetModel );
|
|
|
|
QgsSettings settings;
|
|
lstBookmarks->header()->restoreState( settings.value( QStringLiteral( "Windows/Bookmarks/headerstate" ) ).toByteArray() );
|
|
|
|
#ifndef QGISDEBUG
|
|
lstBookmarks->setColumnHidden( 0, true );
|
|
#endif
|
|
}
|
|
|
|
QgsBookmarks::~QgsBookmarks()
|
|
{
|
|
delete mQgisModel;
|
|
QSqlDatabase::removeDatabase( QStringLiteral( "bookmarks" ) );
|
|
saveWindowLocation();
|
|
}
|
|
|
|
void QgsBookmarks::restorePosition()
|
|
{
|
|
QgsSettings settings;
|
|
restoreGeometry( settings.value( QStringLiteral( "Windows/Bookmarks/geometry" ) ).toByteArray() );
|
|
}
|
|
|
|
void QgsBookmarks::saveWindowLocation()
|
|
{
|
|
QgsSettings settings;
|
|
settings.setValue( QStringLiteral( "Windows/Bookmarks/geometry" ), saveGeometry() );
|
|
settings.setValue( QStringLiteral( "Windows/Bookmarks/headerstate" ), lstBookmarks->header()->saveState() );
|
|
}
|
|
|
|
void QgsBookmarks::addClicked()
|
|
{
|
|
Q_ASSERT( mModel );
|
|
Q_ASSERT( mQgisModel );
|
|
|
|
QgsMapCanvas *canvas = QgisApp::instance()->mapCanvas();
|
|
Q_ASSERT( canvas );
|
|
|
|
QString projStr( QLatin1String( "" ) );
|
|
if ( QgsProject::instance() )
|
|
{
|
|
if ( !QgsProject::instance()->title().isEmpty() )
|
|
{
|
|
projStr = QgsProject::instance()->title();
|
|
}
|
|
else if ( !QgsProject::instance()->fileName().isEmpty() )
|
|
{
|
|
QFileInfo fi( QgsProject::instance()->fileName() );
|
|
projStr = fi.exists() ? fi.fileName() : QLatin1String( "" );
|
|
}
|
|
}
|
|
|
|
QSqlRecord record = mQgisModel->record();
|
|
record.setValue( 1, QVariant( tr( "New bookmark" ) ) );
|
|
record.setValue( 2, QVariant( projStr ) );
|
|
record.setValue( 3, QVariant( canvas->extent().xMinimum() ) );
|
|
record.setValue( 4, QVariant( canvas->extent().yMinimum() ) );
|
|
record.setValue( 5, QVariant( canvas->extent().xMaximum() ) );
|
|
record.setValue( 6, QVariant( canvas->extent().yMaximum() ) );
|
|
record.setValue( 7, QVariant::fromValue( canvas->mapSettings().destinationCrs().srsid() ) );
|
|
|
|
if ( mQgisModel->insertRecord( -1, record ) )
|
|
{
|
|
mQgisModel->setSort( 0, Qt::AscendingOrder );
|
|
mQgisModel->select();
|
|
QModelIndex newIdx = mProxyModel->mapFromSource( mModel->index( mQgisModel->rowCount() - 1, 1 ) );
|
|
// Edit new bookmark title
|
|
lstBookmarks->scrollTo( newIdx );
|
|
lstBookmarks->setCurrentIndex( newIdx );
|
|
lstBookmarks->edit( newIdx );
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning( this, tr( "Error" ), tr( "Unable to create the bookmark.\nDriver: %1\nDatabase: %2" )
|
|
.arg( mQgisModel->database().lastError().driverText(),
|
|
mQgisModel->database().lastError().databaseText() ) );
|
|
}
|
|
}
|
|
|
|
void QgsBookmarks::deleteClicked()
|
|
{
|
|
QList<int> rows;
|
|
Q_FOREACH ( const QModelIndex &idx, lstBookmarks->selectionModel()->selectedIndexes() )
|
|
{
|
|
if ( idx.column() == 1 )
|
|
{
|
|
rows << idx.row();
|
|
}
|
|
}
|
|
|
|
if ( rows.isEmpty() )
|
|
return;
|
|
|
|
// make sure the user really wants to delete these bookmarks
|
|
if ( QMessageBox::Cancel == QMessageBox::information( this, tr( "Delete Bookmarks" ),
|
|
tr( "Are you sure you want to delete %n bookmark(s)?", "number of rows", rows.size() ),
|
|
QMessageBox::Ok | QMessageBox::Cancel ) )
|
|
return;
|
|
|
|
int i = 0;
|
|
Q_FOREACH ( int row, rows )
|
|
{
|
|
mModel->removeRow( row - i );
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void QgsBookmarks::lstBookmarks_doubleClicked( const QModelIndex &index )
|
|
{
|
|
Q_UNUSED( index );
|
|
zoomToBookmark();
|
|
}
|
|
|
|
void QgsBookmarks::zoomToBookmark()
|
|
{
|
|
QModelIndex index = lstBookmarks->currentIndex();
|
|
if ( !index.isValid() )
|
|
return;
|
|
|
|
double xmin = index.sibling( index.row(), 3 ).data().toDouble();
|
|
double ymin = index.sibling( index.row(), 4 ).data().toDouble();
|
|
double xmax = index.sibling( index.row(), 5 ).data().toDouble();
|
|
double ymax = index.sibling( index.row(), 6 ).data().toDouble();
|
|
int srid = index.sibling( index.row(), 7 ).data().toInt();
|
|
|
|
QgsRectangle rect = QgsRectangle( xmin, ymin, xmax, ymax );
|
|
|
|
// backwards compatibility, older version had -1 in the srid column
|
|
if ( srid > 0 &&
|
|
srid != QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs().srsid() )
|
|
{
|
|
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem::fromSrsId( srid ),
|
|
QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() );
|
|
rect = ct.transform( rect );
|
|
if ( rect.isEmpty() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Empty Extent" ), tr( "Reprojected extent is empty." ) );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// set the extent to the bookmark and refresh
|
|
QgisApp::instance()->setExtent( rect );
|
|
QgisApp::instance()->mapCanvas()->refresh();
|
|
}
|
|
|
|
void QgsBookmarks::importFromXml()
|
|
{
|
|
QgsSettings settings;
|
|
|
|
QString lastUsedDir = settings.value( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QDir::homePath() ).toString();
|
|
QString fileName = QFileDialog::getOpenFileName( this, tr( "Import Bookmarks" ), lastUsedDir,
|
|
tr( "XML files (*.xml *XML)" ) );
|
|
if ( fileName.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QFile f( fileName );
|
|
if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QDomDocument doc;
|
|
if ( !doc.setContent( &f ) )
|
|
{
|
|
return;
|
|
}
|
|
f.close();
|
|
|
|
QDomElement docElem = doc.documentElement();
|
|
QDomNodeList nodeList = docElem.elementsByTagName( QStringLiteral( "bookmark" ) );
|
|
|
|
Q_ASSERT( mModel );
|
|
|
|
QString queries;
|
|
|
|
for ( int i = 0; i < nodeList.count(); i++ )
|
|
{
|
|
QDomNode bookmark = nodeList.at( i );
|
|
QDomElement name = bookmark.firstChildElement( QStringLiteral( "name" ) );
|
|
QDomElement prjname = bookmark.firstChildElement( QStringLiteral( "project" ) );
|
|
QDomElement xmin = bookmark.firstChildElement( QStringLiteral( "xmin" ) );
|
|
QDomElement ymin = bookmark.firstChildElement( QStringLiteral( "ymin" ) );
|
|
QDomElement xmax = bookmark.firstChildElement( QStringLiteral( "xmax" ) );
|
|
QDomElement ymax = bookmark.firstChildElement( QStringLiteral( "ymax" ) );
|
|
QDomElement srid = bookmark.firstChildElement( QStringLiteral( "sr_id" ) );
|
|
|
|
queries += "INSERT INTO tbl_bookmarks(bookmark_id,name,project_name,xmin,ymin,xmax,ymax,projection_srid)"
|
|
" VALUES (NULL,"
|
|
"'" + name.text() + "',"
|
|
"'" + prjname.text() + "',"
|
|
+ xmin.text() + ','
|
|
+ ymin.text() + ','
|
|
+ xmax.text() + ','
|
|
+ ymax.text() + ','
|
|
+ srid.text() + ");";
|
|
}
|
|
|
|
QStringList queriesList = queries.split( ';' );
|
|
QSqlQuery query( mQgisModel->database() );
|
|
|
|
Q_FOREACH ( const QString &queryTxt, queriesList )
|
|
{
|
|
if ( queryTxt.trimmed().isEmpty() )
|
|
{
|
|
continue;
|
|
}
|
|
if ( !query.exec( queryTxt ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Error" ), tr( "Unable to create the bookmark.\nDriver: %1\nDatabase: %2" )
|
|
.arg( query.lastError().driverText(),
|
|
query.lastError().databaseText() ) );
|
|
}
|
|
query.finish();
|
|
}
|
|
mQgisModel->setSort( 0, Qt::AscendingOrder );
|
|
mQgisModel->select();
|
|
}
|
|
|
|
void QgsBookmarks::exportToXml()
|
|
{
|
|
QgsSettings settings;
|
|
|
|
QString lastUsedDir = settings.value( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QDir::homePath() ).toString();
|
|
QString fileName = QFileDialog::getSaveFileName( this, tr( "Export Bookmarks" ), lastUsedDir,
|
|
tr( "XML files (*.xml *.XML)" ) );
|
|
if ( fileName.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ensure the user never omitted the extension from the file name
|
|
if ( !fileName.endsWith( QLatin1String( ".xml" ), Qt::CaseInsensitive ) )
|
|
{
|
|
fileName += QLatin1String( ".xml" );
|
|
}
|
|
|
|
QDomDocument doc( QStringLiteral( "qgis_bookmarks" ) );
|
|
QDomElement root = doc.createElement( QStringLiteral( "qgis_bookmarks" ) );
|
|
doc.appendChild( root );
|
|
|
|
int rowCount = mModel->rowCount();
|
|
int colCount = mModel->columnCount() - 1; // exclude virtual "In project" column
|
|
|
|
QList<QString> headerList;
|
|
headerList
|
|
<< QStringLiteral( "id" )
|
|
<< QStringLiteral( "name" )
|
|
<< QStringLiteral( "project" )
|
|
<< QStringLiteral( "xmin" )
|
|
<< QStringLiteral( "ymin" )
|
|
<< QStringLiteral( "xmax" )
|
|
<< QStringLiteral( "ymax" )
|
|
<< QStringLiteral( "sr_id" );
|
|
|
|
for ( int i = 0; i < rowCount; ++i )
|
|
{
|
|
QDomElement bookmark = doc.createElement( QStringLiteral( "bookmark" ) );
|
|
root.appendChild( bookmark );
|
|
for ( int j = 0; j < colCount; j++ )
|
|
{
|
|
QModelIndex idx = mModel->index( i, j );
|
|
if ( idx.isValid() )
|
|
{
|
|
QString value = idx.data( Qt::DisplayRole ).toString();
|
|
QDomText idText = doc.createTextNode( value );
|
|
QString header = headerList.at( j );
|
|
QDomElement id = doc.createElement( header );
|
|
id.appendChild( idText );
|
|
bookmark.appendChild( id );
|
|
}
|
|
}
|
|
}
|
|
|
|
QFile f( fileName );
|
|
if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
|
|
{
|
|
f.close();
|
|
return;
|
|
}
|
|
|
|
QTextStream out( &f );
|
|
out.setCodec( "UTF - 8" );
|
|
doc.save( out, 2 );
|
|
f.close();
|
|
|
|
settings.setValue( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QFileInfo( fileName ).path() );
|
|
}
|
|
|
|
QgsProjectBookmarksTableModel::QgsProjectBookmarksTableModel( QObject *parent )
|
|
: QAbstractTableModel( parent )
|
|
{
|
|
connect(
|
|
QgisApp::instance(), &QgisApp::projectRead,
|
|
this, &QgsProjectBookmarksTableModel::projectRead );
|
|
connect(
|
|
QgisApp::instance(), &QgisApp::newProject,
|
|
this, &QgsProjectBookmarksTableModel::projectRead );
|
|
}
|
|
|
|
int QgsProjectBookmarksTableModel::rowCount( const QModelIndex &parent ) const
|
|
{
|
|
Q_UNUSED( parent );
|
|
return QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ) );
|
|
}
|
|
|
|
int QgsProjectBookmarksTableModel::columnCount( const QModelIndex &parent ) const
|
|
{
|
|
Q_UNUSED( parent );
|
|
return 8;
|
|
}
|
|
|
|
QVariant QgsProjectBookmarksTableModel::data( const QModelIndex &index, int role ) const
|
|
{
|
|
if ( role != Qt::DisplayRole && role != Qt::EditRole )
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
switch ( index.column() )
|
|
{
|
|
case 1:
|
|
return QgsProject::instance()->readEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( index.row() ) );
|
|
case 2:
|
|
return QgsProject::instance()->readEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Project" ).arg( index.row() ) );
|
|
case 3:
|
|
return QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinX" ).arg( index.row() ) );
|
|
case 4:
|
|
return QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinY" ).arg( index.row() ) );
|
|
case 5:
|
|
return QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxX" ).arg( index.row() ) );
|
|
case 6:
|
|
return QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxY" ).arg( index.row() ) );
|
|
case 7:
|
|
return QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/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( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( index.row() ), value.toString() );
|
|
return true;
|
|
case 2:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Project" ).arg( index.row() ), value.toString() );
|
|
return true;
|
|
case 3:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinX" ).arg( index.row() ), value.toDouble() );
|
|
return true;
|
|
case 4:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinY" ).arg( index.row() ), value.toDouble() );
|
|
return true;
|
|
case 5:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxX" ).arg( index.row() ), value.toDouble() );
|
|
return true;
|
|
case 6:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxY" ).arg( index.row() ), value.toDouble() );
|
|
return true;
|
|
case 7:
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/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( parent );
|
|
beginInsertRows( parent, row, row + count );
|
|
|
|
bool result = QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ), QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ) ) + count );
|
|
endInsertRows();
|
|
return result;
|
|
}
|
|
|
|
bool QgsProjectBookmarksTableModel::removeRows( int row, int count, const QModelIndex &parent )
|
|
{
|
|
Q_UNUSED( parent );
|
|
beginRemoveRows( parent, row, row + count );
|
|
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( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1" ).arg( newRow ) );
|
|
}
|
|
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ), QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ) ) - count );
|
|
endRemoveRows();
|
|
return true;
|
|
}
|
|
|
|
void QgsProjectBookmarksTableModel::projectRead()
|
|
{
|
|
emit layoutChanged();
|
|
}
|
|
|
|
|
|
QgsMergedBookmarksTableModel::QgsMergedBookmarksTableModel( QAbstractTableModel &qgisTableModel, QAbstractTableModel &projectTableModel, QTreeView *treeView, QObject *parent )
|
|
: QAbstractTableModel( parent )
|
|
, mQgisTableModel( qgisTableModel )
|
|
, mProjectTableModel( projectTableModel )
|
|
, mTreeView( treeView )
|
|
{
|
|
|
|
connect(
|
|
&mQgisTableModel, &QAbstractTableModel::layoutChanged,
|
|
this, &QgsMergedBookmarksTableModel::allLayoutChanged );
|
|
|
|
connect(
|
|
&mQgisTableModel, &QAbstractTableModel::rowsInserted,
|
|
this, &QgsMergedBookmarksTableModel::allLayoutChanged );
|
|
|
|
connect(
|
|
&mQgisTableModel, &QAbstractTableModel::rowsRemoved,
|
|
this, &QgsMergedBookmarksTableModel::allLayoutChanged );
|
|
|
|
connect(
|
|
&projectTableModel, &QAbstractTableModel::layoutChanged,
|
|
this, &QgsMergedBookmarksTableModel::allLayoutChanged );
|
|
|
|
connect(
|
|
&projectTableModel, &QAbstractTableModel::rowsInserted,
|
|
this, &QgsMergedBookmarksTableModel::allLayoutChanged );
|
|
|
|
connect(
|
|
&projectTableModel, &QAbstractTableModel::rowsRemoved,
|
|
this, &QgsMergedBookmarksTableModel::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
|
|
{
|
|
QVariant value;
|
|
// Is it checkbox column?
|
|
if ( index.column() == mQgisTableModel.columnCount() && role == Qt::CheckStateRole )
|
|
{
|
|
value = index.row() < mQgisTableModel.rowCount() ? Qt::Unchecked : Qt::Checked;
|
|
}
|
|
else
|
|
{
|
|
// Is it a SQLite stored entry ?
|
|
if ( index.row() < mQgisTableModel.rowCount() )
|
|
{
|
|
value = mQgisTableModel.data( index, role );
|
|
}
|
|
else // ... it is a project stored bookmark
|
|
{
|
|
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
|
{
|
|
value = mProjectTableModel.data( this->index( index.row() - mQgisTableModel.rowCount(), index.column() ), role );
|
|
}
|
|
else // Default roles from base model
|
|
{
|
|
value = mQgisTableModel.data( this->index( 0, index.column() ), role );
|
|
}
|
|
}
|
|
// Is it the projection column ?
|
|
if ( ( role == Qt::DisplayRole || role == Qt::EditRole ) && index.column( ) == mQgisTableModel.columnCount() - 1 )
|
|
{
|
|
value = QgsCoordinateReferenceSystem::fromSrsId( value.toInt( ) ).authid( );
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
bool QgsMergedBookmarksTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
|
|
{
|
|
// last column triggers a move from QGIS to project bookmark
|
|
if ( index.column() == mQgisTableModel.columnCount() )
|
|
{
|
|
if ( index.row() < mQgisTableModel.rowCount() )
|
|
{
|
|
// Move from SQLite storage to project
|
|
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
|
|
{
|
|
//Move from project to SQLite storage
|
|
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.row() < 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
|
|
{
|
|
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
if ( index.column() == mQgisTableModel.columnCount() )
|
|
{
|
|
if ( !projectAvailable() )
|
|
{
|
|
return Qt::ItemIsSelectable;
|
|
}
|
|
flags |= Qt::ItemIsUserCheckable;
|
|
}
|
|
else
|
|
{
|
|
// Skip projection: not editable!
|
|
if ( index.column() != mQgisTableModel.columnCount() - 1 )
|
|
flags |= Qt::ItemIsEditable;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
bool QgsMergedBookmarksTableModel::removeRows( int row, int count, const QModelIndex &parent )
|
|
{
|
|
Q_ASSERT( count == 1 );
|
|
bool result;
|
|
beginRemoveRows( parent, row, row + count );
|
|
if ( row < mQgisTableModel.rowCount() )
|
|
{
|
|
QSqlTableModel *qgisModel = static_cast<QSqlTableModel *>( &mQgisTableModel );
|
|
Q_ASSERT( qgisModel );
|
|
result = qgisModel->removeRows( row, count, parent );
|
|
qgisModel->select();
|
|
}
|
|
else
|
|
{
|
|
result = mProjectTableModel.removeRows( row - mQgisTableModel.rowCount(), count, parent );
|
|
}
|
|
endRemoveRows();
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool QgsMergedBookmarksTableModel::projectAvailable() const
|
|
{
|
|
return ! QgsProject::instance()->fileName().isEmpty();
|
|
}
|
|
|
|
void QgsMergedBookmarksTableModel::moveBookmark( QAbstractTableModel &modelFrom, QAbstractTableModel &modelTo, int row )
|
|
{
|
|
emit beginResetModel();
|
|
QSqlTableModel *qgisModel = dynamic_cast<QSqlTableModel *>( &modelTo );
|
|
if ( !qgisModel )
|
|
{
|
|
modelTo.insertRow( -1 );
|
|
for ( int column = 1 ; column < modelFrom.columnCount() ; column++ )
|
|
{
|
|
Q_ASSERT( modelTo.index( modelTo.rowCount() - 1, column ).isValid( ) );
|
|
modelTo.setData(
|
|
modelTo.index( modelTo.rowCount() - 1, column ),
|
|
modelFrom.data( modelFrom.index( row, column ) ) );
|
|
}
|
|
qgisModel = dynamic_cast<QSqlTableModel *>( &modelFrom );
|
|
Q_ASSERT( qgisModel );
|
|
qgisModel->removeRows( row, 1 );
|
|
qgisModel->select();
|
|
}
|
|
else
|
|
{
|
|
QSqlRecord record = qgisModel->record();
|
|
record.setValue( 1, modelFrom.data( modelFrom.index( row, 1 ) ).toString() );
|
|
record.setValue( 2, modelFrom.data( modelFrom.index( row, 2 ) ).toString() );
|
|
record.setValue( 3, modelFrom.data( modelFrom.index( row, 3 ) ).toDouble() );
|
|
record.setValue( 4, modelFrom.data( modelFrom.index( row, 4 ) ).toDouble() );
|
|
record.setValue( 5, modelFrom.data( modelFrom.index( row, 5 ) ).toDouble() );
|
|
record.setValue( 6, modelFrom.data( modelFrom.index( row, 6 ) ).toDouble() );
|
|
record.setValue( 7, modelFrom.data( modelFrom.index( row, 7 ) ).toInt() );
|
|
|
|
if ( ! qgisModel->insertRecord( -1, record ) )
|
|
{
|
|
QgsDebugMsg( QString( "Could not move bookmark: %1" )
|
|
.arg( qgisModel->database().lastError().text() ) );
|
|
return;
|
|
}
|
|
qgisModel->setSort( 0, Qt::AscendingOrder );
|
|
qgisModel->select();
|
|
modelFrom.removeRows( row, 1 );
|
|
}
|
|
emit endResetModel();
|
|
emit layoutChanged();
|
|
}
|
|
|
|
|
|
QgsBookmarksProxyModel::QgsBookmarksProxyModel( QObject *parent ):
|
|
QSortFilterProxyModel( parent )
|
|
{
|
|
|
|
}
|
|
|
|
QVariant QgsBookmarksProxyModel::headerData( int section, Qt::Orientation orientation, int role ) const
|
|
{
|
|
return sourceModel()->headerData( section, orientation, role );
|
|
}
|
|
|
|
QgsDoubleSpinBoxBookmarksDelegate::QgsDoubleSpinBoxBookmarksDelegate( QObject *parent )
|
|
: QStyledItemDelegate( parent )
|
|
{
|
|
|
|
}
|
|
|
|
QString QgsDoubleSpinBoxBookmarksDelegate::displayText( const QVariant &value, const QLocale &locale ) const
|
|
{
|
|
if ( value.userType() == QVariant::Double )
|
|
{
|
|
return locale.toString( value.toDouble(), 'f', QgsDoubleSpinBoxBookmarksDelegate::DECIMAL_PLACES );
|
|
}
|
|
else
|
|
{
|
|
return QStyledItemDelegate::displayText( value, locale );
|
|
}
|
|
}
|
|
|
|
QWidget *QgsDoubleSpinBoxBookmarksDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
|
{
|
|
QWidget *widget = QStyledItemDelegate::createEditor( parent, option, index );
|
|
QDoubleSpinBox *spinbox = qobject_cast<QDoubleSpinBox *>( widget );
|
|
if ( spinbox )
|
|
spinbox->setDecimals( QgsDoubleSpinBoxBookmarksDelegate::DECIMAL_PLACES );
|
|
return widget;
|
|
}
|