diff --git a/python/gui/gui.sip b/python/gui/gui.sip index 8ce5c780413..502f903e7d2 100644 --- a/python/gui/gui.sip +++ b/python/gui/gui.sip @@ -69,6 +69,7 @@ %Include qgssearchquerybuilder.sip %Include qgstextannotationitem.sip %Include qgsvertexmarker.sip +%Include qgsvectorlayertools.sip %Include qgssublayersdialog.sip %Include qgscharacterselectdialog.sip %Include qgscomposerruler.sip diff --git a/python/gui/qgisinterface.sip b/python/gui/qgisinterface.sip index 08a48e1fb7d..f61ce146804 100644 --- a/python/gui/qgisinterface.sip +++ b/python/gui/qgisinterface.sip @@ -484,6 +484,8 @@ class QgisInterface : QObject virtual QDialog* getFeatureForm( QgsVectorLayer *l, QgsFeature &f ) = 0; + virtual QgsVectorLayerTools* vectorLayerTools() = 0; + virtual void preloadForm( QString uifile ) = 0; /** Return vector layers in edit mode diff --git a/python/gui/qgsvectorlayertools.sip b/python/gui/qgsvectorlayertools.sip new file mode 100644 index 00000000000..0c3147c020e --- /dev/null +++ b/python/gui/qgsvectorlayertools.sip @@ -0,0 +1,10 @@ +class QgsVectorLayerTools +{ +%TypeHeaderCode +#include +%End + public: + virtual bool addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues = QgsAttributeMap(), const QgsGeometry& = QgsGeometry() ) = 0; + virtual bool startEditing( QgsVectorLayer* layer ) = 0; + virtual bool stopEditing( QgsVectorLayer* layer, bool allowCancel = true ) = 0; +}; diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index e4ea6dee09a..b232140eddf 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -43,6 +43,7 @@ SET(QGIS_APP_SRCS qgslabelpreview.cpp qgsloadstylefromdbdialog.cpp qgssavestyletodbdialog.cpp + qgsguivectorlayertools.cpp qgsmaptooladdfeature.cpp qgsmaptooladdpart.cpp @@ -99,6 +100,7 @@ SET(QGIS_APP_SRCS qgsrasterlayerproperties.cpp qgstextannotationdialog.cpp qgsshortcutsmanager.cpp + qgsguivectorlayertools.h qgssnappingdialog.cpp qgssvgannotationdialog.cpp qgsundowidget.cpp diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 0da1342a1e5..fb9163c64bd 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -133,6 +133,7 @@ #include "qgshtmlannotationitem.h" #include "qgsgenericprojectionselector.h" #include "qgsgpsinformationwidget.h" +#include "qgsguivectorlayertools.h" #include "qgslabelinggui.h" #include "qgslegend.h" #include "qgslayerorder.h" @@ -574,6 +575,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent, mLogDock->setWidget( mLogViewer ); mLogDock->hide(); + mVectorLayerTools = new QgsGuiVectorLayerTools(); + mInternalClipboard = new QgsClipboard; // create clipboard connect( mInternalClipboard, SIGNAL( changed() ), this, SLOT( clipboardChanged() ) ); mQgisInterface = new QgisAppInterface( this ); // create the interfce diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 4f51039b009..e545729afb3 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -57,9 +57,9 @@ class QgsPoint; class QgsProviderRegistry; class QgsPythonUtils; class QgsRectangle; - class QgsUndoWidget; class QgsVectorLayer; +class QgsVectorLayerTools; class QDomDocument; class QNetworkReply; @@ -242,6 +242,13 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow /** overloaded function used to sort menu entries alphabetically */ QMenu* createPopupMenu(); + /** + * Access the vector layer tools. This will be an instance of {@see QgsGuiVectorLayerTools} + * by default. + * @return The vector layer tools + */ + QgsVectorLayerTools* vectorLayerTools() { return mVectorLayerTools; } + //! Actions to be inserted in menus and toolbars QAction *actionNewProject() { return mActionNewProject; } QAction *actionOpenProject() { return mActionOpenProject; } @@ -1518,6 +1525,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow QgsMessageBar *mInfoBar; QWidget *mMacrosWarn; + QgsVectorLayerTools* mVectorLayerTools; #ifdef HAVE_TOUCH bool gestureEvent( QGestureEvent *event ); void tapAndHoldTriggered( QTapAndHoldGesture *gesture ); diff --git a/src/app/qgsattributetabledialog.cpp b/src/app/qgsattributetabledialog.cpp index 624eef9e70c..a876ff9117b 100644 --- a/src/app/qgsattributetabledialog.cpp +++ b/src/app/qgsattributetabledialog.cpp @@ -80,6 +80,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); context.setDistanceArea( myDa ); + context.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() ); // Initialize dual view mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), QgsFeatureRequest(), context ); diff --git a/src/app/qgsfeatureaction.cpp b/src/app/qgsfeatureaction.cpp index 036f4d7385b..91cacfd8a5e 100644 --- a/src/app/qgsfeatureaction.cpp +++ b/src/app/qgsfeatureaction.cpp @@ -15,16 +15,17 @@ * * ***************************************************************************/ -#include "qgsfeatureaction.h" -#include "qgsvectorlayer.h" -#include "qgsvectordataprovider.h" -#include "qgsidentifyresultsdialog.h" -#include "qgsattributedialog.h" -#include "qgslogger.h" -#include "qgsdistancearea.h" #include "qgisapp.h" -#include "qgsproject.h" +#include "qgsattributedialog.h" +#include "qgsdistancearea.h" +#include "qgsfeatureaction.h" +#include "qgsguivectorlayertools.h" +#include "qgsidentifyresultsdialog.h" +#include "qgslogger.h" #include "qgsmapcanvas.h" +#include "qgsproject.h" +#include "qgsvectordataprovider.h" +#include "qgsvectorlayer.h" #include #include @@ -47,13 +48,18 @@ QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature ) { QgsFeature *f = cloneFeature ? new QgsFeature( mFeature ) : &mFeature; + QgsAttributeEditorContext context; + QgsDistanceArea myDa; myDa.setSourceCrs( mLayer->crs() ); myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() ); myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); - QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature, myDa ); + context.setDistanceArea( myDa ); + context.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() ); + + QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, f, cloneFeature, NULL, true, context ); if ( mLayer->actions()->size() > 0 ) { @@ -138,7 +144,7 @@ bool QgsFeatureAction::editFeature() return res; } -bool QgsFeatureAction::addFeature() +bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes ) { if ( !mLayer || !mLayer->isEditable() ) return false; @@ -154,7 +160,12 @@ bool QgsFeatureAction::addFeature() mFeature.initAttributes( fields.count() ); for ( int idx = 0; idx < fields.count(); ++idx ) { - if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( idx ) ) + if ( defaultAttributes.contains( idx ) ) + { + QgsDebugMsg( QString( "Using specified default %1 for %2" ).arg( defaultAttributes.value( idx ).toString() ).arg( idx ) ); + mFeature.setAttribute( idx, defaultAttributes.value( idx ) ); + } + else if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( idx ) ) { QgsDebugMsg( QString( "reusing %1 for %2" ).arg( mLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) ); mFeature.setAttribute( idx, mLastUsedValues[ mLayer ][idx] ); diff --git a/src/app/qgsfeatureaction.h b/src/app/qgsfeatureaction.h index 8ce71763801..4f8dea416ea 100644 --- a/src/app/qgsfeatureaction.h +++ b/src/app/qgsfeatureaction.h @@ -18,6 +18,7 @@ #define QGSFEATUREACTION_H #include "qgsfeature.h" +#include "qgsvectorlayertools.h" #include #include @@ -33,13 +34,23 @@ class APP_EXPORT QgsFeatureAction : public QAction Q_OBJECT public: - QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *vl, int action, int defaultAttr, QObject *parent ); + QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *vl, int action = -1, int defaultAttr = -1, QObject *parent = NULL ); public slots: void execute(); bool viewFeatureForm( QgsHighlight *h = 0 ); bool editFeature(); - bool addFeature(); + + /** + * Add a new feature to the layer. + * Will set the default values to recently used or provider defaults based on settings + * and override with values in defaultAttributes if provided. + * + * @param defaultAttributes Provide some default attributes here if desired. + * + * @return true if feature was added + */ + bool addFeature( const QgsAttributeMap& defaultAttributes = QgsAttributeMap() ); private: QgsAttributeDialog *newDialog( bool cloneFeature ); diff --git a/src/app/qgsguivectorlayertools.cpp b/src/app/qgsguivectorlayertools.cpp new file mode 100644 index 00000000000..9bbccecbbee --- /dev/null +++ b/src/app/qgsguivectorlayertools.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + qgsfeaturefactory.cpp + -------------------------------------- + Date : 30.5.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 +#include + +#include "qgsguivectorlayertools.h" +#include "qgsvectorlayer.h" +#include "qgsvectordataprovider.h" +#include "qgsmessagebar.h" +#include "qgisapp.h" +#include "qgsapplication.h" +#include "qgsmessageviewer.h" +#include "qgsfeatureaction.h" +#include "qgsmapcanvas.h" +#include "qgsmessagebaritem.h" + + +QgsGuiVectorLayerTools::QgsGuiVectorLayerTools() + : QObject( NULL ) +{} + +bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues, const QgsGeometry& defaultGeometry ) +{ + QgsFeature f; + f.setGeometry( defaultGeometry ); + QgsFeatureAction a( tr( "Add feature" ), f, layer ); + return a.addFeature( defaultValues ); +} + +bool QgsGuiVectorLayerTools::startEditing( QgsVectorLayer* layer ) +{ + if ( !layer ) + { + return false; + } + + bool res = true; + + if ( !layer->isEditable() && !layer->isReadOnly() ) + { + if ( !( layer->dataProvider()->capabilities() & QgsVectorDataProvider::EditingCapabilities ) ) + { + QgisApp::instance()->messageBar()->pushMessage( tr( "Start editing failed" ), + tr( "Provider cannot be opened for editing" ), + QgsMessageBar::INFO, QgisApp::instance()->messageTimeout() ); + return false; + } + + layer->startEditing(); + } + + return res; +} + +bool QgsGuiVectorLayerTools::stopEditing( QgsVectorLayer* layer, bool allowCancel ) +{ + bool res = true; + + if ( layer->isModified() ) + { + QMessageBox::StandardButtons buttons = QMessageBox::Save | QMessageBox::Discard; + if ( allowCancel ) + buttons |= QMessageBox::Cancel; + + switch ( QMessageBox::information( 0, + tr( "Stop editing" ), + tr( "Do you want to save the changes to layer %1?" ).arg( layer->name() ), + buttons ) ) + { + case QMessageBox::Cancel: + res = false; + break; + + case QMessageBox::Save: + if ( !layer->commitChanges() ) + { + commitError( layer ); + // Leave the in-memory editing state alone, + // to give the user a chance to enter different values + // and try the commit again later + res = false; + } + + layer->triggerRepaint(); + break; + + case QMessageBox::Discard: + QgisApp::instance()->mapCanvas()->freeze( true ); + if ( !layer->rollBack() ) + { + QgisApp::instance()->messageBar()->pushMessage( tr( "Error" ), + tr( "Problems during roll back" ), + QgsMessageBar::CRITICAL ); + res = false; + } + QgisApp::instance()->mapCanvas()->freeze( false ); + + layer->triggerRepaint(); + break; + + default: + break; + } + } + else //layer not modified + { + QgisApp::instance()->mapCanvas()->freeze( true ); + layer->rollBack(); + QgisApp::instance()->mapCanvas()->freeze( false ); + res = true; + layer->triggerRepaint(); + } + + return res; +} + +void QgsGuiVectorLayerTools::commitError( QgsVectorLayer* vlayer ) +{ + QgsMessageViewer *mv = new QgsMessageViewer(); + mv->setWindowTitle( tr( "Commit errors" ) ); + mv->setMessageAsPlainText( tr( "Could not commit changes to layer %1" ).arg( vlayer->name() ) + + "\n\n" + + tr( "Errors: %1\n" ).arg( vlayer->commitErrors().join( "\n " ) ) + ); + + QToolButton *showMore = new QToolButton(); + // store pointer to vlayer in data of QAction + QAction *act = new QAction( showMore ); + act->setData( QVariant( QMetaType::QObjectStar, &vlayer ) ); + act->setText( tr( "Show more" ) ); + showMore->setStyleSheet( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ); + showMore->setCursor( Qt::PointingHandCursor ); + showMore->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred ); + showMore->addAction( act ); + showMore->setDefaultAction( act ); + connect( showMore, SIGNAL( triggered( QAction* ) ), mv, SLOT( exec() ) ); + connect( showMore, SIGNAL( triggered( QAction* ) ), showMore, SLOT( deleteLater() ) ); + + // no timeout set, since notice needs attention and is only shown first time layer is labeled + QgsMessageBarItem *errorMsg = new QgsMessageBarItem( + tr( "Commit errors" ), + tr( "Could not commit changes to layer %1" ).arg( vlayer->name() ), + showMore, + QgsMessageBar::WARNING, + 0, + QgisApp::instance()->messageBar() ); + QgisApp::instance()->messageBar()->pushItem( errorMsg ); + +} diff --git a/src/app/qgsguivectorlayertools.h b/src/app/qgsguivectorlayertools.h new file mode 100644 index 00000000000..bf85645145f --- /dev/null +++ b/src/app/qgsguivectorlayertools.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgsfeaturefactory.h + -------------------------------------- + Date : 30.5.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 QGSGUIVECTORLAYERTOOLS_H +#define QGSGUIVECTORLAYERTOOLS_H + +#include "qgsvectorlayertools.h" + +/** + * Implements all the dialogs and actions when editing on a vector layer is toggled + * or a feature is added. + */ + +class QgsGuiVectorLayerTools : public QgsVectorLayerTools, public QObject +{ + public: + QgsGuiVectorLayerTools(); + + /** + * This method should be called, whenever a new feature is added to a layer + * + * @param layer The layer to which the feature should be added + * @param defaultValues Default values for the feature to add + * @param defaultGeometry A default geometry to add to the feature + * + * @return True in case of success, False if the operation failed/was aborted + */ + bool addFeature( QgsVectorLayer *layer, QgsAttributeMap defaultValues, const QgsGeometry &defaultGeometry ); + + /** + * This should be called, whenever a vector layer should be switched to edit mode. If succesful + * the layer is switched to editable and an edit sessions started. + * + * @param layer The layer on which to start an edit session + * + * @return True, if the editing session was started + */ + bool startEditing( QgsVectorLayer* layer ); + + /** + * Should be called, when an editing session is ended and the features should be commited. + * An appropriate dialog asking the user if he wants to save the edits will be shown if + * allowCancel is set to true. + * + * @param layer The layer to commit + * @param allowCancel True if a cancel button should be offered + * + * @return True if successful + */ + bool stopEditing( QgsVectorLayer* layer , bool allowCancel = true ); + + private: + void commitError( QgsVectorLayer* vlayer ); +}; + +#endif // QGSGUIVECTORLAYERTOOLS_H diff --git a/src/gui/qgsattributeeditorcontext.cpp b/src/gui/qgsattributeeditorcontext.cpp index c3d089682e1..86933de5a01 100644 --- a/src/gui/qgsattributeeditorcontext.cpp +++ b/src/gui/qgsattributeeditorcontext.cpp @@ -16,6 +16,7 @@ #include "qgsattributeeditorcontext.h" QgsAttributeEditorContext::QgsAttributeEditorContext() + : mVectorLayerTools( NULL ) { } diff --git a/src/gui/qgsattributeeditorcontext.h b/src/gui/qgsattributeeditorcontext.h index 8bd5e825f62..02fa1ab0f04 100644 --- a/src/gui/qgsattributeeditorcontext.h +++ b/src/gui/qgsattributeeditorcontext.h @@ -21,6 +21,7 @@ #include #include +#include /** @@ -41,6 +42,9 @@ class GUI_EXPORT QgsAttributeEditorContext void setDistanceArea( const QgsDistanceArea& distanceArea ) { mDistanceArea = distanceArea; } inline const QgsDistanceArea& distanceArea() { return mDistanceArea; } + void setVectorLayerTools( QgsVectorLayerTools* vlTools ) { mVectorLayerTools = vlTools; } + QgsVectorLayerTools* vectorLayerTools() { return mVectorLayerTools; } + /** * When copying the context for another layer, call this. * Will adjast the distance area for this layer @@ -49,7 +53,10 @@ class GUI_EXPORT QgsAttributeEditorContext */ void adjustForLayer( QgsVectorLayer* layer ); + private: + QgsVectorLayerTools* mVectorLayerTools; + //! vectorlayer => ( fieldIdx, proxyWidget ) QMap > mProxyWidgets; diff --git a/src/gui/qgsvectorlayertools.h b/src/gui/qgsvectorlayertools.h new file mode 100644 index 00000000000..5cdceacad7b --- /dev/null +++ b/src/gui/qgsvectorlayertools.h @@ -0,0 +1,73 @@ +/*************************************************************************** + QgsAbstractFeatureAction.h + -------------------------------------- + Date : 29.5.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 QGSVECTORLAYERTOOLS_H +#define QGSVECTORLAYERTOOLS_H + +#include "qgsfeature.h" +#include "qgsgeometry.h" + +class QgsVectorLayer; + +/** + * Methods in this class are used to handle basic operations on vector layers. + * With an implementation of this class, parts of the application can ask for + * an operation to be done and the implementation will then take care of it. + * + * Reimplement this class, if you need to have custom checks or GUI elements + * in your application. + * + */ +class GUI_EXPORT QgsVectorLayerTools +{ + public: + QgsVectorLayerTools() + {} + + /** + * This method should/will be called, whenever a new feature will be added to the layer + * + * @param layer The layer to which the feature should be added + * @param defaultValues Default values for the feature to add + * @param defaultGeometry A default geometry to add to the feature + * @return True in case of success, False if the operation failed/was aborted + */ + virtual bool addFeature( QgsVectorLayer* layer, QgsAttributeMap defaultValues = QgsAttributeMap(), const QgsGeometry& defaultGeometry = QgsGeometry() ) = 0; + + + /** + * This will be called, whenever a vector layer should be switched to edit mode. Check the providers + * capability to edit in here. + * If successful layer->startEditing() will be called and true returned. + * + * @param layer The layer on which to start an edit session + * + * @return True, if the editing session was started + */ + virtual bool startEditing( QgsVectorLayer* layer ) = 0; + + /** + * Will be called, when an editing session is ended and the features should be commited. + * Appropriate dialogs should be shown like + * + * @param layer The layer to commit + * @param allowCancel True if a cancel button should be offered + * @return True if successful + */ + virtual bool stopEditing( QgsVectorLayer* layer, bool allowCancel = true ) = 0; + +}; + +#endif // QGSVECTORLAYERTOOLS_H