QGIS API Documentation  3.17.0-Master (8af46bc54f)
qgstransactiongroup.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstransactiongroup.cpp - QgsTransactionGroup
3  ---------------------------------------------
4 
5  begin : 15.1.2016
6  Copyright : (C) 2016 Matthias Kuhn
7  Email : matthias at opengis dot ch
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 #include "qgstransactiongroup.h"
17 
18 #include "qgstransaction.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsdatasourceuri.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgslogger.h"
23 
24 #include <QTimer>
25 
27  : QObject( parent )
28 {
29 
30 }
31 
33 {
35  return false;
36 
37  QString connString = QgsTransaction::connectionString( layer->source() );
38  if ( mConnString.isEmpty() )
39  {
40  mConnString = connString;
41  mProviderKey = layer->providerType();
42  }
43  else if ( mConnString != connString || mProviderKey != layer->providerType() )
44  {
45  return false;
46  }
47 
48  mLayers.insert( layer );
49 
50  connect( layer, &QgsVectorLayer::beforeEditingStarted, this, &QgsTransactionGroup::onEditingStarted );
51  connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransactionGroup::onLayerDeleted );
52 
53  return true;
54 }
55 
56 QSet<QgsVectorLayer *> QgsTransactionGroup::layers() const
57 {
58  return mLayers;
59 }
60 
62 {
63  const auto constMLayers = mLayers;
64  for ( QgsVectorLayer *layer : constMLayers )
65  {
66  if ( layer->isModified() )
67  return true;
68  }
69  return false;
70 }
71 
72 void QgsTransactionGroup::onEditingStarted()
73 {
74  if ( mTransaction )
75  return;
76 
77  mTransaction.reset( QgsTransaction::create( mConnString, mProviderKey ) );
78  if ( !mTransaction )
79  return;
80 
81  QString errorMsg;
82  mTransaction->begin( errorMsg );
83 
84  const auto constMLayers = mLayers;
85  for ( QgsVectorLayer *layer : constMLayers )
86  {
87  mTransaction->addLayer( layer );
88  layer->startEditing();
89  connect( layer, &QgsVectorLayer::beforeCommitChanges, this, &QgsTransactionGroup::onBeforeCommitChanges );
90  connect( layer, &QgsVectorLayer::beforeRollBack, this, &QgsTransactionGroup::onRollback );
91  }
92 }
93 
94 void QgsTransactionGroup::onLayerDeleted()
95 {
96  mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
97 }
98 
99 void QgsTransactionGroup::onBeforeCommitChanges( bool stopEditing )
100 {
101  if ( mEditingStopping )
102  return;
103 
104  mEditingStopping = true;
105 
106  const QgsVectorLayer *triggeringLayer = qobject_cast<QgsVectorLayer *>( sender() );
107 
108  QString errMsg;
109  if ( mTransaction->commit( errMsg ) )
110  {
111  const auto constMLayers = mLayers;
112  for ( QgsVectorLayer *layer : constMLayers )
113  {
114  if ( layer != triggeringLayer )
115  {
116  layer->commitChanges( stopEditing );
117  }
118  }
119 
120  if ( stopEditing )
121  {
122  disableTransaction();
123  }
124  else
125  {
126  if ( ! mTransaction->begin( errMsg ) )
127  {
128  QgsDebugMsg( QStringLiteral( "Could not restart a transaction for %1: %2" ).arg( triggeringLayer->name() ).arg( errMsg ) );
129  }
130  }
131 
132  }
133  else
134  {
135  emit commitError( errMsg );
136  restartTransaction( triggeringLayer );
137  }
138  mEditingStopping = false;
139 }
140 
141 void QgsTransactionGroup::onRollback()
142 {
143  if ( mEditingStopping )
144  return;
145 
146  mEditingStopping = true;
147 
148  QgsVectorLayer *triggeringLayer = qobject_cast<QgsVectorLayer *>( sender() );
149 
150  QString errMsg;
151  if ( mTransaction->rollback( errMsg ) )
152  {
153  const auto constMLayers = mLayers;
154  for ( QgsVectorLayer *layer : constMLayers )
155  {
156  if ( layer != triggeringLayer )
157  layer->rollBack();
158  }
159  disableTransaction();
160  }
161  else
162  {
163  restartTransaction( triggeringLayer );
164  }
165  mEditingStopping = false;
166 }
167 
168 void QgsTransactionGroup::disableTransaction()
169 {
170  mTransaction.reset();
171 
172  const auto constMLayers = mLayers;
173  for ( QgsVectorLayer *layer : constMLayers )
174  {
175  disconnect( layer, &QgsVectorLayer::beforeCommitChanges, this, &QgsTransactionGroup::onBeforeCommitChanges );
176  disconnect( layer, &QgsVectorLayer::beforeRollBack, this, &QgsTransactionGroup::onRollback );
177  }
178 }
179 
180 void QgsTransactionGroup::restartTransaction( const QgsVectorLayer *layer )
181 {
182  // Restart editing the calling layer in the next event loop cycle
183  QTimer::singleShot( 0, layer, &QgsVectorLayer::startEditing );
184 }
185 
187 {
188  return mProviderKey;
189 }
190 
192 {
193  return mLayers.isEmpty();
194 }
195 
197 {
198  return mConnString;
199 }
static QgsTransaction * create(const QString &connString, const QString &providerKey)
Create a transaction for the specified connection string connString and provider with providerKey...
void beforeRollBack()
Emitted before changes are rolled back.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString providerType() const
Returns the provider type (provider key) for this layer.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
QSet< QgsVectorLayer * > layers() const
Gets the set of layers currently managed by this transaction group.
QString providerKey() const
Returns the provider key used by this transaction group.
void beforeEditingStarted()
Emitted before editing on this layer is started.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
QgsTransactionGroup(QObject *parent=nullptr)
Constructor for QgsTransactionGroup.
QString source() const
Returns the source for the layer.
void beforeCommitChanges(bool stopEditing)
Emitted before changes are committed to the data provider.
bool isEmpty() const
Returns true if there are no layers in this transaction group.
QString name
Definition: qgsmaplayer.h:87
void commitError(const QString &msg)
Will be emitted whenever there is a commit error.
Represents a vector layer which manages a vector based data sets.
QString connString() const
Returns the connection string used by this transaction group.
bool modified() const
Returns true if any of the layers in this group reports a modification.