Skip to content
Permalink
Browse files

Move addFeature, startEditing and stopEditing vector layer actions

to an abstract base class QgsVectorLayerTools in the gui library,
so these can be reimplemented for a custom app, but can also be used
from plugins or in the gui library.
  • Loading branch information
m-kuhn committed Oct 4, 2013
1 parent 331f71a commit fb32966932b8a5fae96b302e4f8420e351bd1a85
@@ -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
@@ -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
@@ -0,0 +1,10 @@
class QgsVectorLayerTools
{
%TypeHeaderCode
#include <qgsvectorlayertools.h>
%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;
};
@@ -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
@@ -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
@@ -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 );
@@ -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 );
@@ -15,16 +15,17 @@
* *
***************************************************************************/

#include "qgisapp.h"
#include "qgsattributedialog.h"
#include "qgsdistancearea.h"
#include "qgsfeatureaction.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsguivectorlayertools.h"
#include "qgsidentifyresultsdialog.h"
#include "qgsattributedialog.h"
#include "qgslogger.h"
#include "qgsdistancearea.h"
#include "qgisapp.h"
#include "qgsproject.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"

#include <QPushButton>
#include <QSettings>
@@ -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] );
@@ -18,6 +18,7 @@
#define QGSFEATUREACTION_H

#include "qgsfeature.h"
#include "qgsvectorlayertools.h"

#include <QList>
#include <QPair>
@@ -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 );
@@ -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 <QMessageBox>
#include <QToolButton>

#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 );

}

0 comments on commit fb32966

Please sign in to comment.
You can’t perform that action at this time.