1 /***************************************************************************
2     qgsgrassprovider.cpp -  Data provider for GRASS format
3                              -------------------
4     begin                : March, 2004
5     copyright            : (C) 2004 by Gary E.Sherman, Radim Blazek
6     email                : sherman@mrcc.com, blazek@itc.it
7  ***************************************************************************/
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 
17 #include <cstring>
18 #include <vector>
19 #include <cfloat>
20 
21 #include <QString>
22 #include <QDateTime>
23 #include <QElapsedTimer>
24 
25 #include "qgis.h"
26 #include "qgsdataprovider.h"
27 #include "qgsfeature.h"
28 #include "qgsfields.h"
29 #include "qgslinestring.h"
30 #include "qgspoint.h"
31 #include "qgspolygon.h"
32 #include "qgsrectangle.h"
33 #include "qgsvectorlayer.h"
34 #include "qgsvectorlayereditbuffer.h"
35 
36 #include "qgsgrass.h"
37 #include "qgsgrassprovider.h"
38 #include "qgsgrassfeatureiterator.h"
39 #include "qgsgrassvector.h"
40 #include "qgsgrassundocommand.h"
41 
42 #include "qgsapplication.h"
43 #include "qgscoordinatereferencesystem.h"
44 #include "qgslogger.h"
45 
46 #include <QByteArray>
47 #include <QDir>
48 #include <QFile>
49 #include <QFileInfo>
50 #include <QMessageBox>
51 #include <QTextCodec>
52 
53 #ifdef _MSC_VER
54 // enable grass prototypes
55 #define __STDC__ 1
56 #endif
57 
58 extern "C"
59 {
60 #include <grass/version.h>
61 #if defined(_MSC_VER) && defined(M_PI_4)
62 #undef M_PI_4 //avoid redefinition warning
63 #endif
64 #if defined(PROJ_VERSION_MAJOR) && PROJ_VERSION_MAJOR>=6 && PROJ_VERSION_MAJOR<8
65 #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
66 #endif
67 #include <grass/gprojects.h>
68 #include <grass/gis.h>
69 #include <grass/dbmi.h>
70 #include <grass/vector.h>
71 }
72 
73 #ifdef _MSC_VER
74 #undef __STDC__
75 #endif
76 
77 // Vect_rewrite_line and Vect_delete_line use int in GRASS 6 and off_t in GRASS 7 for line id argument.
78 // off_t size is not specified by C, POSIX specifies it as signed integer and its size depends on compiler.
79 // In OSGeo4W 32bit the off_t size is 8 bytes in GRASS (32bit, compiled with MinGW, 'g.version -g' prints build_off_t_size=8 )
80 // and 4 bytes QGIS (32bit, compiled with MSVC), which is causing crashes.
81 // The problem with off_t size was also reported for custom build of QGIS on Xubuntu 14.04 LTS using GRASS 7.0.1-2~ubuntu14.04.1.
82 // See: https://lists.osgeo.org/pipermail/grass-dev/2015-June/075539.html
83 //      https://lists.osgeo.org/pipermail/grass-dev/2015-September/076338.html
84 // TODO: get real off_t size from GRASS, probably contribute a patch which will save 'g.version -g' to a header file during compilation
85 #ifdef Q_OS_WIN
86 typedef qint64 grass_off_t;
87 #else
88 #if defined(GRASS_OFF_T_SIZE) && GRASS_OFF_T_SIZE == 4
89 typedef qint32 grass_off_t;
90 #elif defined(GRASS_OFF_T_SIZE) && GRASS_OFF_T_SIZE == 8
91 typedef qint64 grass_off_t;
92 #else
93 typedef off_t grass_off_t; // GRASS_OFF_T_SIZE undefined, default to off_t
94 #endif
95 #endif
96 typedef grass_off_t Vect_rewrite_line_function_type( struct Map_info *, grass_off_t, int, const struct line_pnts *, const struct line_cats * );
97 typedef int Vect_delete_line_function_type( struct Map_info *, grass_off_t );
98 Vect_rewrite_line_function_type *Vect_rewrite_line_function_pointer = ( Vect_rewrite_line_function_type * )Vect_rewrite_line;
99 Vect_delete_line_function_type *Vect_delete_line_function_pointer = ( Vect_delete_line_function_type * )Vect_delete_line;
100 
101 static QString GRASS_KEY = QStringLiteral( "grass" );
102 
103 int QgsGrassProvider::sLastType = -9999;
104 int QgsGrassProvider::sEditedCount = 0;
105 
QgsGrassProvider(const QString & uri)106 QgsGrassProvider::QgsGrassProvider( const QString &uri )
107   : QgsVectorDataProvider( uri )
108 {
109   QgsDebugMsg( "uri = " + uri );
110 
111   mValid = false;
112   if ( !QgsGrass::init() )
113   {
114     appendError( QgsGrass::initError() );
115     return;
116   }
117 
118   QElapsedTimer time;
119   time.start();
120 
121   mPoints = Vect_new_line_struct();
122   mCats = Vect_new_cats_struct();
123 
124   // Parse URI
125   QDir dir( uri );   // it is not a directory in fact
126   QString myURI = dir.path();  // no dupl '/'
127 
128   mLayerName = dir.dirName();
129   myURI = myURI.left( dir.path().lastIndexOf( '/' ) );
130   dir = QDir( myURI );
131   QString mapName = dir.dirName();
132   dir.cdUp();
133   QString mapset = dir.dirName();
134   dir.cdUp();
135   QString location = dir.dirName();
136   dir.cdUp();
137   QString gisdbase = dir.path();
138 
139   mGrassObject = QgsGrassObject( gisdbase, location, mapset, mapName );
140   QgsDebugMsg( "mGrassObject = " + mGrassObject.toString() + " mLayerName = " + mLayerName );
141 
142   /* Parse Layer, supported layers <field>_point, <field>_line, <field>_area
143   *  Layer is opened even if it is empty (has no features)
144   */
145   if ( mLayerName.compare( QLatin1String( "boundary" ) ) == 0 ) // currently not used
146   {
147     mLayerType = Boundary;
148     mGrassType = GV_BOUNDARY;
149   }
150   else if ( mLayerName.compare( QLatin1String( "centroid" ) ) == 0 ) // currently not used
151   {
152     mLayerType = Centroid;
153     mGrassType = GV_CENTROID;
154   }
155   else if ( mLayerName == QLatin1String( "topo_point" ) )
156   {
157     mLayerType = TopoPoint;
158     mGrassType = GV_POINTS;
159   }
160   else if ( mLayerName == QLatin1String( "topo_line" ) )
161   {
162     mLayerType = TopoLine;
163     mGrassType = GV_LINES;
164   }
165   else if ( mLayerName == QLatin1String( "topo_node" ) )
166   {
167     mLayerType = TopoNode;
168     mGrassType = 0;
169   }
170   else
171   {
172     mLayerField = grassLayer( mLayerName );
173     if ( mLayerField == -1 )
174     {
175       QgsDebugMsg( QString( "Invalid layer name, no underscore found: %1" ).arg( mLayerName ) );
176       return;
177     }
178 
179     mGrassType = grassLayerType( mLayerName );
180 
181     if ( mGrassType == GV_POINT )
182     {
183       mLayerType = Point;
184     }
185     else if ( mGrassType == GV_LINES )
186     {
187       mLayerType = Line;
188     }
189     else if ( mGrassType == GV_FACE )
190     {
191       mLayerType = Face;
192     }
193     else if ( mGrassType == GV_AREA )
194     {
195       mLayerType = Polygon;
196     }
197     else
198     {
199       QgsDebugMsg( QString( "Invalid layer name, wrong type: %1" ).arg( mLayerName ) );
200       return;
201     }
202 
203   }
204   QgsDebugMsg( QString( "mLayerField: %1" ).arg( mLayerField ) );
205   QgsDebugMsg( QString( "mLayerType: %1" ).arg( mLayerType ) );
206 
207   if ( mLayerType == Boundary || mLayerType == Centroid )
208   {
209     QgsDebugMsg( "Layer type not supported." );
210     return;
211   }
212 
213   // Set QGIS type
214   switch ( mLayerType )
215   {
216     case Point:
217     case Centroid:
218     case TopoPoint:
219     case TopoNode:
220       mQgisType = QgsWkbTypes::Point;
221       break;
222     case Line:
223     case Boundary:
224     case TopoLine:
225       mQgisType = QgsWkbTypes::LineString;
226       break;
227     case Polygon:
228     case Face:
229       mQgisType = QgsWkbTypes::Polygon;
230       break;
231   }
232 
233   if ( !openLayer() )
234   {
235     QgsDebugMsg( "Cannot open layer" );
236     return;
237   }
238 
239   loadMapInfo();
240   setTopoFields();
241 
242   connect( mLayer->map(),
243            &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
244 
245   // TODO: types according to database
246   setNativeTypes( QList<NativeType>()
247                   << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, -1, -1, -1, -1 )
248                   << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
249                   << QgsVectorDataProvider::NativeType( tr( "Text" ), QStringLiteral( "text" ), QVariant::String )
250                   // TODO:
251                   // << QgsVectorDataProvider::NativeType( tr( "Date" ), "date", QVariant::Date, 8, 8 );
252                 );
253 
254   // Assign default encoding
255   if ( !textEncoding() )
256     QgsVectorDataProvider::setEncoding( QStringLiteral( "UTF-8" ) );
257 
258   mValid = true;
259 
260   QgsDebugMsg( QString( "New GRASS layer opened, time (ms): %1" ).arg( time.elapsed() ) );
261 }
262 
~QgsGrassProvider()263 QgsGrassProvider::~QgsGrassProvider()
264 {
265   if ( mLayer )
266   {
267     mLayer->close();
268   }
269   if ( mPoints )
270   {
271     Vect_destroy_line_struct( mPoints );
272   }
273   if ( mCats )
274   {
275     Vect_destroy_cats_struct( mCats );
276   }
277 }
278 
capabilities() const279 QgsVectorDataProvider::Capabilities QgsGrassProvider::capabilities() const
280 {
281   // Because of bug in GRASS https://trac.osgeo.org/grass/ticket/2775 it is not possible
282   // close db drivers in random order on Unix and probably Mac -> disable editing if another layer is edited
283 #ifndef Q_OS_WIN
284   if ( sEditedCount > 0 && !mEditBuffer )
285   {
286     return nullptr;
287   }
288 #endif
289   // for now, only one map may be edited at time
290   if ( mEditBuffer || ( mLayer && mLayer->map() && !mLayer->map()->isEdited() ) )
291   {
292     return AddFeatures | DeleteFeatures | ChangeGeometries | AddAttributes | DeleteAttributes | ChangeAttributeValues;
293   }
294   return nullptr;
295 }
296 
openLayer()297 bool QgsGrassProvider::openLayer()
298 {
299   // the map may be invalid (e.g. wrong uri or open failed)
300   QgsGrassVectorMap *vectorMap = QgsGrassVectorMapStore::instance()->openMap( mGrassObject );
301   if ( !vectorMap ) // should not happen
302   {
303     QgsDebugMsg( "Cannot open map" );
304     return false;
305   }
306   if ( !vectorMap->isValid() ) // may happen
307   {
308     QgsDebugMsg( "vectorMap is not valid" );
309     return false;
310   }
311 
312   mLayer = vectorMap->openLayer( mLayerField );
313 
314   if ( !mLayer ) // should not happen
315   {
316     QgsDebugMsg( "Cannot open layer" );
317     return false;
318   }
319   if ( !mLayer->map() || !mLayer->map()->map() ) // should not happen
320   {
321     QgsDebugMsg( "map is null" );
322     return false;
323   }
324   mMapVersion = mLayer->map()->version();
325   return true;
326 }
327 
loadMapInfo()328 void QgsGrassProvider::loadMapInfo()
329 {
330   // Getting the total number of features in the layer
331   int cidxFieldIndex = -1;
332   mNumberFeatures = 0;
333   if ( mLayerType == TopoPoint )
334   {
335     mNumberFeatures = Vect_get_num_primitives( map(), GV_POINTS );
336   }
337   else if ( mLayerType == TopoLine )
338   {
339     mNumberFeatures = Vect_get_num_primitives( map(), GV_LINES );
340   }
341   else if ( mLayerType == TopoNode )
342   {
343     mNumberFeatures = Vect_get_num_nodes( map() );
344   }
345   else
346   {
347     if ( mLayerField >= 0 )
348     {
349       cidxFieldIndex = Vect_cidx_get_field_index( map(), mLayerField );
350       if ( cidxFieldIndex >= 0 )
351       {
352         mNumberFeatures = Vect_cidx_get_type_count( map(), mLayerField, mGrassType );
353       }
354     }
355     else
356     {
357       // TODO nofield layers
358       mNumberFeatures = 0;
359     }
360   }
361   QgsDebugMsg( QString( "mNumberFeatures = %1 cidxFieldIndex = %2" ).arg( mNumberFeatures ).arg( cidxFieldIndex ) );
362 }
363 
update()364 void QgsGrassProvider::update()
365 {
366 
367   mValid = false;
368 
369   if ( mLayer )
370   {
371     mLayer->close();
372     mLayer = nullptr;
373   }
374 
375   if ( !openLayer() )
376   {
377     QgsDebugMsg( "Cannot open layer" );
378     return;
379   }
380 
381   loadMapInfo();
382 
383   mValid = true;
384 }
385 
featureSource() const386 QgsAbstractFeatureSource *QgsGrassProvider::featureSource() const
387 {
388   const_cast<QgsGrassProvider *>( this )->ensureUpdated();
389   return new QgsGrassFeatureSource( this );
390 }
391 
392 
storageType() const393 QString QgsGrassProvider::storageType() const
394 {
395   return QStringLiteral( "GRASS (Geographic Resources Analysis and Support System) file" );
396 }
397 
398 
399 
getFeatures(const QgsFeatureRequest & request) const400 QgsFeatureIterator QgsGrassProvider::getFeatures( const QgsFeatureRequest &request ) const
401 {
402   if ( !mValid )
403   {
404     return QgsFeatureIterator();
405   }
406 
407   // check if outdated and update if necessary
408   ensureUpdated();
409 
410   QgsGrassFeatureSource *source = new QgsGrassFeatureSource( this );
411   QgsGrassFeatureIterator *iterator = new QgsGrassFeatureIterator( source, true, request );
412   return QgsFeatureIterator( iterator );
413 }
414 
extent() const415 QgsRectangle QgsGrassProvider::extent() const
416 {
417   if ( isValid() )
418   {
419     bound_box box;
420     Vect_get_map_box( map(), &box );
421     return QgsRectangle( box.W, box.S, box.E, box.N );
422   }
423   return QgsRectangle();
424 }
425 
426 /**
427 * Return the feature type
428 */
wkbType() const429 QgsWkbTypes::Type QgsGrassProvider::wkbType() const
430 {
431   return mQgisType;
432 }
433 
featureCount() const434 long QgsGrassProvider::featureCount() const
435 {
436   return mNumberFeatures;
437 }
438 
fields() const439 QgsFields QgsGrassProvider::fields() const
440 {
441   if ( isTopoType() )
442   {
443     return mTopoFields;
444   }
445   else
446   {
447     // Original fields must be returned during editing because edit buffer updates fields by indices
448     if ( mEditBuffer )
449     {
450       return mLayer->fields();
451     }
452     else
453     {
454       return mLayer->tableFields();
455     }
456   }
457 }
458 
keyField()459 int QgsGrassProvider::keyField()
460 {
461   return mLayer->keyColumn();
462 }
463 
minimumValue(int index) const464 QVariant QgsGrassProvider::minimumValue( int index ) const
465 {
466   if ( isValid() )
467   {
468     return mLayer->minMax().value( index ).first;
469   }
470   return QVariant();
471 }
472 
473 
maxValue(int index)474 QVariant QgsGrassProvider::maxValue( int index )
475 {
476   if ( isValid() )
477   {
478     return mLayer->minMax().value( index ).second;
479   }
480   return QVariant();
481 }
482 
isValid() const483 bool QgsGrassProvider::isValid() const
484 {
485   bool valid = mValid && mLayer && mLayer->map() && mLayer->map()->map();
486   QgsDebugMsg( QString( "valid = %1" ).arg( valid ) );
487   return valid;
488 }
489 
crs() const490 QgsCoordinateReferenceSystem QgsGrassProvider::crs() const
491 {
492   QString error;
493   return QgsGrass::crs( mGrassObject.gisdbase(), mGrassObject.location(), error );
494 }
495 
grassLayer()496 int QgsGrassProvider::grassLayer()
497 {
498   return mLayerField;
499 }
500 
grassLayer(const QString & name)501 int QgsGrassProvider::grassLayer( const QString &name )
502 {
503   // Get field number
504   int pos = name.indexOf( '_' );
505 
506   if ( pos == -1 )
507   {
508     return -1;
509   }
510 
511   return name.leftRef( pos ).toInt();
512 }
513 
grassLayerType(const QString & name)514 int QgsGrassProvider::grassLayerType( const QString &name )
515 {
516   int pos = name.indexOf( '_' );
517 
518   if ( pos == -1 )
519   {
520     return -1;
521   }
522 
523   QString ts = name.right( name.length() - pos - 1 );
524   if ( ts.compare( QLatin1String( "point" ) ) == 0 )
525   {
526     return GV_POINT; // ?! centroids may be points
527   }
528   else if ( ts.compare( QLatin1String( "line" ) ) == 0 )
529   {
530     return GV_LINES;
531   }
532   else if ( ts.compare( QLatin1String( "face" ) ) == 0 )
533   {
534     return GV_FACE;
535   }
536   else if ( ts.compare( QLatin1String( "polygon" ) ) == 0 )
537   {
538     return GV_AREA;
539   }
540 
541   return -1;
542 }
543 
onDataChanged()544 void QgsGrassProvider::onDataChanged()
545 {
546   update();
547   emit dataChanged();
548 }
549 
550 //-----------------------------------------  Edit -------------------------------------------------------
551 
isGrassEditable(void)552 bool QgsGrassProvider::isGrassEditable( void )
553 {
554 
555   if ( !isValid() )
556     return false;
557 
558   /* Check if current user is owner of mapset */
559   if ( G_mapset_permissions2( mGrassObject.gisdbase().toUtf8().constData(), mGrassObject.location().toUtf8().constData(), mGrassObject.mapset().toUtf8().constData() ) != 1 )
560     return false;
561 
562   // TODO: check format? (cannot edit OGR layers)
563 
564   return true;
565 }
566 
isEdited(void)567 bool QgsGrassProvider::isEdited( void )
568 {
569   QgsDebugMsgLevel( "entered", 3 );
570   return ( mEditBuffer );
571 }
572 
freeze()573 void QgsGrassProvider::freeze()
574 {
575 
576   if ( !isValid() )
577   {
578     return;
579   }
580 
581   mValid = false;
582 
583   if ( mLayer )
584   {
585     mLayer->close();
586     mLayer->map()->close(); // closes all iterators, blocking
587     mLayer = nullptr;
588   }
589 }
590 
thaw()591 void QgsGrassProvider::thaw()
592 {
593 
594   if ( !openLayer() )
595   {
596     QgsDebugMsg( "Cannot open layer" );
597     return;
598   }
599 
600   loadMapInfo();
601 
602   mValid = true;
603 }
604 
closeEdit(bool newMap,QgsVectorLayer * vectorLayer)605 bool QgsGrassProvider::closeEdit( bool newMap, QgsVectorLayer *vectorLayer )
606 {
607 
608   if ( !isValid() )
609   {
610     QgsDebugMsg( "not valid" );
611     return false;
612   }
613 
614   mEditBuffer = nullptr;
615   mEditLayer = nullptr;
616   // drivers must be closed in reversed order in which were opened
617   // TODO: close driver order for simultaneous editing of multiple vector maps
618   for ( int i = mOtherEditLayers.size() - 1; i >= 0; i-- )
619   {
620     QgsGrassVectorMapLayer *layer = mOtherEditLayers[i];
621     layer->closeEdit();
622     mLayer->map()->closeLayer( layer );
623   }
624   mOtherEditLayers.clear();
625   mLayer->closeEdit();
626   if ( mLayer->map()->closeEdit( newMap ) )
627   {
628     loadMapInfo();
629     if ( vectorLayer )
630     {
631       vectorLayer->updateFields();
632     }
633     connect( mLayer->map(), &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
634     emit fullExtentCalculated();
635     sEditedCount--;
636     return true;
637   }
638   return false;
639 }
640 
ensureUpdated() const641 void QgsGrassProvider::ensureUpdated() const
642 {
643   // TODO
644 #if 0
645   int mapId = mLayers[mLayerId].mapId;
646   if ( mapOutdated( mapId ) )
647   {
648     updateMap( mapId );
649   }
650   if ( map()Version < map()s[mapId].version )
651   {
652     update();
653   }
654   if ( attributesOutdated( mapId ) )
655   {
656     loadAttributes( mLayers[mLayerId] );
657   }
658 #endif
659 }
660 
numLines(void)661 int QgsGrassProvider::numLines( void )
662 {
663 
664   //return ( Vect_get_num_lines( map() ) );
665   return mLayer->map()->numLines();
666 }
667 
numNodes(void)668 int QgsGrassProvider::numNodes( void )
669 {
670 
671   return ( Vect_get_num_nodes( map() ) );
672 }
673 
readLine(struct line_pnts * Points,struct line_cats * Cats,int line)674 int QgsGrassProvider::readLine( struct line_pnts *Points, struct line_cats *Cats, int line )
675 {
676   QgsDebugMsgLevel( "entered", 3 );
677 
678   if ( Points )
679   {
680     Vect_reset_line( Points );
681   }
682 
683   if ( Cats )
684   {
685     Vect_reset_cats( Cats );
686   }
687 
688   if ( !map() )
689   {
690     return -1;
691   }
692 
693   if ( !Vect_line_alive( map(), line ) )
694   {
695     return -1;
696   }
697 
698   int type = -1;
699   G_TRY
700   {
701     type = Vect_read_line( map(), mPoints, mCats, line );
702   }
703   G_CATCH( QgsGrass::Exception & e )
704   {
705     QgsDebugMsg( QString( "Cannot read line : %1" ).arg( e.what() ) );
706   }
707   return type;
708 }
709 
nodeCoor(int node,double * x,double * y)710 bool QgsGrassProvider::nodeCoor( int node, double *x, double *y )
711 {
712   QgsDebugMsgLevel( "entered", 3 );
713 
714   if ( !Vect_node_alive( map(), node ) )
715   {
716     *x = 0.0;
717     *y = 0.0;
718     return false;
719   }
720 
721   Vect_get_node_coor( map(), node, x, y, nullptr );
722   return true;
723 }
724 
lineNodes(int line,int * node1,int * node2)725 bool QgsGrassProvider::lineNodes( int line, int *node1, int *node2 )
726 {
727   QgsDebugMsgLevel( "entered", 3 );
728 
729   if ( !Vect_line_alive( map(), line ) )
730   {
731     *node1 = 0;
732     *node2 = 0;
733     return false;
734   }
735 
736   /* points don't have topology in GRASS >= 7 */
737   *node1 = 0;
738   *node2 = 0;
739   return true;
740 }
741 
writeLine(int type,struct line_pnts * Points,struct line_cats * Cats)742 int QgsGrassProvider::writeLine( int type, struct line_pnts *Points, struct line_cats *Cats )
743 {
744   QgsDebugMsg( QString( "n_points = %1 n_cats = %2" ).arg( Points->n_points ).arg( Cats->n_cats ) );
745 
746   if ( !isEdited() )
747     return -1;
748 
749   return ( ( int ) Vect_write_line( map(), type, Points, Cats ) );
750 }
751 
rewriteLine(int oldLid,int type,struct line_pnts * Points,struct line_cats * Cats)752 int QgsGrassProvider::rewriteLine( int oldLid, int type, struct line_pnts *Points, struct line_cats *Cats )
753 {
754   QgsDebugMsg( QString( "n_points = %1 n_cats = %2" ).arg( Points->n_points ).arg( Cats->n_cats ) );
755 
756   if ( !map() || !isEdited() )
757   {
758     return -1;
759   }
760 
761   int newLid = -1;
762   G_TRY
763   {
764     newLid = Vect_rewrite_line_function_pointer( map(), oldLid, type, Points, Cats );
765 
766     // oldLids are mapping to the very first, original version (used by undo)
767     int oldestLid = oldLid;
768     if ( mLayer->map()->oldLids().contains( oldLid ) ) // if it was changed already
769     {
770       oldestLid = mLayer->map()->oldLids().value( oldLid );
771     }
772 
773     QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2 newLine = %3 numLines = %4" )
774                  .arg( oldLid ).arg( oldestLid ).arg( newLid ).arg( mLayer->map()->numLines() ) );
775     QgsDebugMsg( QString( "oldLids : %1 -> %2" ).arg( newLid ).arg( oldestLid ) );
776     mLayer->map()->oldLids()[newLid] = oldestLid;
777     QgsDebugMsg( QString( "newLids : %1 -> %2" ).arg( oldestLid ).arg( newLid ) );
778     mLayer->map()->newLids()[oldestLid] = newLid;
779   }
780   G_CATCH( QgsGrass::Exception & e )
781   {
782     QgsDebugMsg( QString( "Cannot write line : %1" ).arg( e.what() ) );
783   }
784   return newLid;
785 }
786 
787 
deleteLine(int line)788 int QgsGrassProvider::deleteLine( int line )
789 {
790 
791   if ( !isEdited() )
792     return -1;
793 
794   return ( Vect_delete_line_function_pointer( map(), line ) );
795 }
796 
findLine(double x,double y,int type,double threshold)797 int QgsGrassProvider::findLine( double x, double y, int type, double threshold )
798 {
799   QgsDebugMsgLevel( "entered", 3 );
800 
801   return ( Vect_find_line( map(), x, y, 0, type, threshold, 0, 0 ) );
802 }
803 
findNode(double x,double y,double threshold)804 int QgsGrassProvider::findNode( double x, double y, double threshold )
805 {
806   return ( Vect_find_node( map(), x, y, 0, threshold, 0 ) );
807 }
808 
lineAreas(int line,int * left,int * right)809 bool QgsGrassProvider::lineAreas( int line, int *left, int *right )
810 {
811   QgsDebugMsgLevel( "entered", 3 );
812 
813   if ( !Vect_line_alive( map(), line ) )
814   {
815     *left = 0;
816     *right = 0;
817     return false;
818   }
819 
820   Vect_get_line_areas( map(), line, left, right );
821   return true;
822 }
823 
isleArea(int isle)824 int QgsGrassProvider::isleArea( int isle )
825 {
826   QgsDebugMsgLevel( "entered", 3 );
827 
828   if ( !Vect_isle_alive( map(), isle ) )
829   {
830     return 0;
831   }
832 
833   return ( Vect_get_isle_area( map(), isle ) );
834 }
835 
centroidArea(int centroid)836 int QgsGrassProvider::centroidArea( int centroid )
837 {
838   QgsDebugMsgLevel( "entered", 3 );
839 
840   if ( !Vect_line_alive( map(), centroid ) )
841   {
842     return 0;
843   }
844 
845   return ( Vect_get_centroid_area( map(), centroid ) );
846 }
847 
nodeNLines(int node)848 int QgsGrassProvider::nodeNLines( int node )
849 {
850   QgsDebugMsgLevel( "entered", 3 );
851 
852   if ( !Vect_node_alive( map(), node ) )
853   {
854     return 0;
855   }
856 
857   return ( Vect_get_node_n_lines( map(), node ) );
858 }
859 
nodeLine(int node,int idx)860 int QgsGrassProvider::nodeLine( int node, int idx )
861 {
862   QgsDebugMsgLevel( "entered", 3 );
863 
864   if ( !Vect_node_alive( map(), node ) )
865   {
866     return 0;
867   }
868 
869   return ( Vect_get_node_line( map(), node, idx ) );
870 }
871 
lineAlive(int line)872 int QgsGrassProvider::lineAlive( int line )
873 {
874   QgsDebugMsgLevel( "entered", 3 );
875 
876   return ( Vect_line_alive( map(), line ) );
877 }
878 
nodeAlive(int node)879 int QgsGrassProvider::nodeAlive( int node )
880 {
881   QgsDebugMsgLevel( "QgsGrassProvider::nodeAlive", 3 );
882 
883   return ( Vect_node_alive( map(), node ) );
884 }
885 
numUpdatedLines(void)886 int QgsGrassProvider::numUpdatedLines( void )
887 {
888   QgsDebugMsg( QString( "numUpdatedLines = %1" ).arg( Vect_get_num_updated_lines( map() ) ) );
889 
890   return ( Vect_get_num_updated_lines( map() ) );
891 }
892 
numUpdatedNodes(void)893 int QgsGrassProvider::numUpdatedNodes( void )
894 {
895   QgsDebugMsg( QString( "numUpdatedNodes = %1" ).arg( Vect_get_num_updated_nodes( map() ) ) );
896 
897   return ( Vect_get_num_updated_nodes( map() ) );
898 }
899 
updatedLine(int idx)900 int QgsGrassProvider::updatedLine( int idx )
901 {
902   QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
903   QgsDebugMsg( QString( "  updatedLine = %1" ).arg( Vect_get_updated_line( map(), idx ) ) );
904 
905   return ( Vect_get_updated_line( map(), idx ) );
906 }
907 
updatedNode(int idx)908 int QgsGrassProvider::updatedNode( int idx )
909 {
910   QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
911   QgsDebugMsg( QString( "  updatedNode = %1" ).arg( Vect_get_updated_node( map(), idx ) ) );
912 
913   return ( Vect_get_updated_node( map(), idx ) );
914 }
915 
916 // ------------------ Attributes -------------------------------------------------
917 
key(int field)918 QString QgsGrassProvider::key( int field )
919 {
920   QgsDebugMsg( QString( "field = %1" ).arg( field ) );
921 
922   struct field_info *fi = Vect_get_field( map(), field ); // should work also with field = 0
923   if ( !fi )
924   {
925     QgsDebugMsg( "No field info -> no attributes" );
926     return QString();
927   }
928 
929   return QString::fromUtf8( fi->key );
930 }
931 
attributes(int field,int cat)932 QgsAttributeMap *QgsGrassProvider::attributes( int field, int cat )
933 {
934   QgsDebugMsg( QString( "field = %1 cat = %2" ).arg( field ).arg( cat ) );
935 
936   QgsAttributeMap *att = new QgsAttributeMap;
937 
938   struct  field_info *fi = Vect_get_field( map(), field ); // should work also with field = 0
939 
940   // Read attributes
941   if ( !fi )
942   {
943     QgsDebugMsg( "No field info -> no attributes" );
944     return att;
945   }
946 
947   QgsDebugMsg( "Field info found -> open database" );
948   setMapset();
949   dbDriver *driver = db_start_driver_open_database( fi->driver, fi->database );
950 
951   if ( !driver )
952   {
953     QgsDebugMsg( QString( "Cannot open database %1 by driver %2" ).arg( fi->database, fi->driver ) );
954     return att;
955   }
956 
957   QgsDebugMsg( "Database opened -> read attributes" );
958 
959   dbString dbstr;
960   db_init_string( &dbstr );
961   QString query = QStringLiteral( "select * from %1 where %2=%3" ).arg( fi->table, fi->key ).arg( cat );
962   db_set_string( &dbstr, query.toUtf8().constData() );
963 
964   QgsDebugMsg( QString( "SQL: %1" ).arg( db_get_string( &dbstr ) ) );
965 
966   dbCursor databaseCursor;
967   if ( db_open_select_cursor( driver, &dbstr, &databaseCursor, DB_SCROLL ) != DB_OK )
968   {
969     db_close_database_shutdown_driver( driver );
970     QgsDebugMsg( "Cannot select attributes from table" );
971     return att;
972   }
973 
974   int nRecords = db_get_num_rows( &databaseCursor );
975   QgsDebugMsg( QString( "Number of records: %1" ).arg( nRecords ) );
976 
977   if ( nRecords < 1 )
978   {
979     db_close_database_shutdown_driver( driver );
980     QgsDebugMsg( "No DB record" );
981     return att;
982   }
983 
984   dbTable  *databaseTable = db_get_cursor_table( &databaseCursor );
985   int nColumns = db_get_table_number_of_columns( databaseTable );
986 
987   int more;
988   if ( db_fetch( &databaseCursor, DB_NEXT, &more ) != DB_OK )
989   {
990     db_close_database_shutdown_driver( driver );
991     QgsDebugMsg( "Cannot fetch DB record" );
992     return att;
993   }
994 
995   // Read columns' description
996   for ( int i = 0; i < nColumns; i++ )
997   {
998     dbColumn *column = db_get_table_column( databaseTable, i );
999     db_convert_column_value_to_string( column, &dbstr );
1000 
1001     QString v = textEncoding()->toUnicode( db_get_string( &dbstr ) );
1002     QgsDebugMsg( QString( "Value: %1" ).arg( v ) );
1003     att->insert( i, QVariant( v ) );
1004   }
1005 
1006   db_close_cursor( &databaseCursor );
1007   db_close_database_shutdown_driver( driver );
1008   db_free_string( &dbstr );
1009 
1010   return att;
1011 }
1012 
1013 
1014 
numDbLinks(void)1015 int QgsGrassProvider::numDbLinks( void )
1016 {
1017 
1018   return ( Vect_get_num_dblinks( map() ) );
1019 }
1020 
dbLinkField(int link)1021 int QgsGrassProvider::dbLinkField( int link )
1022 {
1023 
1024   struct  field_info *fi = Vect_get_dblink( map(), link );
1025 
1026   if ( !fi )
1027     return 0;
1028 
1029   return ( fi->number );
1030 }
1031 
isTopoType() const1032 bool QgsGrassProvider::isTopoType() const
1033 {
1034   return isTopoType( mLayerType );
1035 }
1036 
isTopoType(int layerType)1037 bool QgsGrassProvider::isTopoType( int layerType )
1038 {
1039   return layerType == TopoPoint || layerType == TopoLine || layerType == TopoNode;
1040 }
1041 
setTopoFields()1042 void QgsGrassProvider::setTopoFields()
1043 {
1044   mTopoFields.append( QgsField( QStringLiteral( "id" ), QVariant::Int ) );
1045 
1046   if ( mLayerType == TopoPoint )
1047   {
1048     mTopoFields.append( QgsField( QStringLiteral( "type" ), QVariant::String ) );
1049     mTopoFields.append( QgsField( QStringLiteral( "node" ), QVariant::Int ) );
1050   }
1051   else if ( mLayerType == TopoLine )
1052   {
1053     mTopoFields.append( QgsField( QStringLiteral( "type" ), QVariant::String ) );
1054     mTopoFields.append( QgsField( QStringLiteral( "node1" ), QVariant::Int ) );
1055     mTopoFields.append( QgsField( QStringLiteral( "node2" ), QVariant::Int ) );
1056     mTopoFields.append( QgsField( QStringLiteral( "left" ), QVariant::Int ) );
1057     mTopoFields.append( QgsField( QStringLiteral( "right" ), QVariant::Int ) );
1058   }
1059   else if ( mLayerType == TopoNode )
1060   {
1061     mTopoFields.append( QgsField( QStringLiteral( "lines" ), QVariant::String ) );
1062   }
1063 }
1064 
startEditing(QgsVectorLayer * vectorLayer)1065 void QgsGrassProvider::startEditing( QgsVectorLayer *vectorLayer )
1066 {
1067   QgsDebugMsg( "uri = " +  dataSourceUri() );
1068   if ( !vectorLayer || !vectorLayer->editBuffer() )
1069   {
1070     QgsDebugMsg( "vector or buffer is null" );
1071     return;
1072   }
1073   mEditLayer = vectorLayer;
1074   if ( !isValid() || !isGrassEditable() )
1075   {
1076     QgsDebugMsg( "not valid or not editable" );
1077     return;
1078   }
1079   if ( mEditBuffer )
1080   {
1081     QgsDebugMsg( "already edited" );
1082     return;
1083   }
1084 
1085   // disconnect dataChanged() because the changes are done here and we know about them
1086   disconnect( mLayer->map(), &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
1087   mLayer->map()->startEdit();
1088   mLayer->startEdit();
1089 
1090   mEditBuffer = vectorLayer->editBuffer();
1091   connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureAdded, this, &QgsGrassProvider::onFeatureAdded );
1092   connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureDeleted, this, &QgsGrassProvider::onFeatureDeleted );
1093   connect( mEditBuffer, &QgsVectorLayerEditBuffer::geometryChanged, this, &QgsGrassProvider::onGeometryChanged );
1094   connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeValueChanged, this, &QgsGrassProvider::onAttributeValueChanged );
1095   connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeAdded, this, &QgsGrassProvider::onAttributeAdded );
1096   connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeDeleted, this, &QgsGrassProvider::onAttributeDeleted );
1097   connect( vectorLayer, &QgsVectorLayer::beforeCommitChanges, this, &QgsGrassProvider::onBeforeCommitChanges );
1098   connect( vectorLayer, &QgsVectorLayer::beforeRollBack, this, &QgsGrassProvider::onBeforeRollBack );
1099   connect( vectorLayer, &QgsVectorLayer::editingStopped, this, &QgsGrassProvider::onEditingStopped );
1100 
1101   connect( vectorLayer->undoStack(), &QUndoStack::indexChanged, this, &QgsGrassProvider::onUndoIndexChanged );
1102 
1103   // let qgis know (attribute table etc.) that we added topo symbol field
1104   vectorLayer->updateFields();
1105   mEditLayerFields = vectorLayer->fields();
1106 
1107   // TODO: enable cats editing once all consequences are implemented
1108   // disable cat and topo symbol editing
1109   QgsEditFormConfig formConfig = vectorLayer->editFormConfig();
1110   formConfig.setReadOnly( mLayer->keyColumn(), true );
1111   formConfig.setReadOnly( mLayer->fields().size() - 1, true );
1112   vectorLayer->setEditFormConfig( formConfig );
1113 
1114   sEditedCount++;
1115 
1116   QgsDebugMsg( "edit started" );
1117 }
1118 
setPoints(struct line_pnts * points,const QgsAbstractGeometry * geometry)1119 void QgsGrassProvider::setPoints( struct line_pnts *points, const QgsAbstractGeometry *geometry )
1120 {
1121   if ( !points )
1122   {
1123     return;
1124   }
1125   Vect_reset_line( points );
1126   if ( !geometry )
1127   {
1128     return;
1129   }
1130   if ( geometry->wkbType() == QgsWkbTypes::Point || geometry->wkbType() == QgsWkbTypes::PointZ )
1131   {
1132     const QgsPoint *point = dynamic_cast<const QgsPoint *>( geometry );
1133     if ( point )
1134     {
1135       Vect_append_point( points, point->x(), point->y(), point->z() );
1136       QgsDebugMsg( QString( "x = %1 y = %2" ).arg( point->x() ).arg( point->y() ) );
1137     }
1138   }
1139   else if ( geometry->wkbType() == QgsWkbTypes::LineString || geometry->wkbType() == QgsWkbTypes::LineStringZ )
1140   {
1141     const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( geometry );
1142     if ( lineString )
1143     {
1144       for ( int i = 0; i < lineString->numPoints(); i++ )
1145       {
1146         QgsPoint point = lineString->pointN( i );
1147         Vect_append_point( points, point.x(), point.y(), point.z() );
1148       }
1149     }
1150   }
1151   else if ( geometry->wkbType() == QgsWkbTypes::Polygon || geometry->wkbType() == QgsWkbTypes::PolygonZ )
1152   {
1153     const QgsPolygon *polygon = dynamic_cast<const QgsPolygon *>( geometry );
1154     if ( polygon && polygon->exteriorRing() )
1155     {
1156       QgsLineString *lineString = polygon->exteriorRing()->curveToLine();
1157       if ( lineString )
1158       {
1159         for ( int i = 0; i < lineString->numPoints(); i++ )
1160         {
1161           QgsPoint point = lineString->pointN( i );
1162           Vect_append_point( points, point.x(), point.y(), point.z() );
1163         }
1164       }
1165     }
1166   }
1167   else
1168   {
1169     QgsDebugMsg( "unknown type : " + geometry->geometryType() );
1170   }
1171 }
1172 
onFeatureAdded(QgsFeatureId fid)1173 void QgsGrassProvider::onFeatureAdded( QgsFeatureId fid )
1174 {
1175   // fid is negative for new features
1176 
1177   int lid = QgsGrassFeatureIterator::lidFromFid( fid );
1178   int cat = QgsGrassFeatureIterator::catFromFid( fid );
1179 
1180   QgsDebugMsg( QString( "fid = %1 lid = %2 cat = %3" ).arg( fid ).arg( lid ).arg( cat ) );
1181 
1182   Vect_reset_cats( mCats );
1183   int realLine = 0; // number of line to be rewritten if exists
1184   int newCat = 0;
1185 
1186   // TODO: get also old type if it is feature previously deleted
1187   int type = 0;
1188 
1189   if ( FID_IS_NEW( fid ) )
1190   {
1191     if ( mNewFeatureType == QgsGrassProvider::sLastType )
1192     {
1193       type = mLastType;
1194       QgsDebugMsg( QString( "use mLastType = %1" ).arg( mLastType ) );
1195     }
1196     else
1197     {
1198       type = mNewFeatureType == GV_AREA ? GV_BOUNDARY : mNewFeatureType;
1199     }
1200     // geometry
1201     const QgsAbstractGeometry *geometry = nullptr;
1202     if ( !mEditBuffer->isFeatureAdded( fid ) )
1203     {
1204 #ifdef QGISDEBUG
1205       QgsDebugMsg( "the feature is missing in buffer addedFeatures :" );
1206 
1207       const auto constKeys = mEditBuffer->addedFeatures().keys();
1208       for ( QgsFeatureId id : constKeys )
1209       {
1210         QgsDebugMsg( QString( "addedFeatures : id = %1" ).arg( id ) );
1211       }
1212 #endif
1213       return;
1214     }
1215     QgsFeature feature = mEditBuffer->addedFeatures().value( fid );
1216     QgsGeometry featureGeometry = feature.geometry();
1217     geometry = featureGeometry.constGet();
1218     if ( !geometry )
1219     {
1220       QgsDebugMsg( "geometry is null" );
1221       return;
1222     }
1223 
1224     setPoints( mPoints, geometry );
1225 
1226     QgsFeatureMap &addedFeatures = mEditBuffer->mAddedFeatures;
1227 
1228     // change polygon to linestring
1229     QgsWkbTypes::Type wkbType = QgsWkbTypes::flatType( geometry->wkbType() );
1230     if ( wkbType == QgsWkbTypes::Polygon )
1231     {
1232       QgsGeometry addedFeatureGeom = addedFeatures[fid].geometry();
1233       const QgsPolygon *polygon = dynamic_cast<const QgsPolygon *>( addedFeatureGeom.constGet() );
1234       if ( polygon )
1235       {
1236         QgsLineString *lineString = polygon->exteriorRing()->curveToLine();
1237         addedFeatures[fid].setGeometry( QgsGeometry( lineString ) );
1238       }
1239       // TODO: create also centroid and add it to undo
1240     }
1241 
1242     // add new category
1243     // TODO: add category also to boundary if user defined at least one attribute?
1244     if ( type != GV_BOUNDARY )
1245     {
1246       // TODO: redo of deleted new features - save new cats somewhere,
1247       // resetting fid probably is not possible because it is stored in undo commands and used in buffer maps
1248 
1249       // It may be that user manually entered cat value
1250       QgsFeatureMap &addedFeatures = mEditBuffer->mAddedFeatures;
1251       QgsFeature &feature = addedFeatures[fid];
1252       int catIndex = feature.fields().indexFromName( mLayer->keyColumnName() );
1253       if ( catIndex != -1 )
1254       {
1255         QVariant userCatVariant = feature.attributes().value( catIndex );
1256         if ( !userCatVariant.isNull() )
1257         {
1258           newCat = userCatVariant.toInt();
1259           QgsDebugMsg( QString( "user defined newCat = %1" ).arg( newCat ) );
1260         }
1261       }
1262       if ( newCat == 0 )
1263       {
1264         newCat = getNewCat();
1265       }
1266       QgsDebugMsg( QString( "newCat = %1" ).arg( newCat ) );
1267       Vect_cat_set( mCats, mLayerField, newCat );
1268 
1269       if ( mLayer->hasTable() )
1270       {
1271         addedFeatures[fid].setAttribute( mLayer->keyColumn(), newCat );
1272       }
1273       else
1274       {
1275         addedFeatures[fid].setAttribute( 0, newCat );
1276       }
1277       mLayer->map()->newCats()[fid] = newCat;
1278       QgsDebugMsg( QString( "newCats[%1] = %2" ).arg( fid ).arg( newCat ) );
1279 
1280       // Currently neither entering new cat nor changing existing cat is allowed
1281 #if 0
1282       // There may be other new features with the same cat which we have to update
1283       const auto constKeys = addedFeatures.keys();
1284       for ( QgsFeatureId addedFid : constKeys )
1285       {
1286         if ( addedFid == fid )
1287         {
1288           continue;
1289         }
1290         int addedCat = mLayer->map()->newCats().value( addedFid ); // it should always exist
1291         QgsDebugMsg( QString( "addedFid = %1 addedCat = %2" ).arg( addedFid ).arg( addedCat ) );
1292         if ( addedCat == newCat )
1293         {
1294           QgsFeature addedFeature = addedFeatures[addedFid];
1295           // TODO: better to update form mLayer->attributes() ?
1296           for ( int i = 0; i < feature.fields()->size(); i++ )
1297           {
1298             if ( feature.fields()->field( i ).name() == QgsGrassVectorMap::topoSymbolFieldName() )
1299             {
1300               continue;
1301             }
1302             if ( feature.attributes().at( i ).isNull() )
1303             {
1304               continue;
1305             }
1306             addedFeature.setAttribute( i, feature.attributes().at( i ) );
1307           }
1308           addedFeatures[addedFid] = addedFeature;
1309         }
1310       }
1311 
1312       // Update all changed attributes
1313       QgsChangedAttributesMap &changedAttributes = const_cast<QgsChangedAttributesMap &>( mEditBuffer->changedAttributeValues() );
1314       const auto constKeys = changedAttributes.keys();
1315       for ( QgsFeatureId changedFid : constKeys )
1316       {
1317         int changedCat = QgsGrassFeatureIterator::catFromFid( changedFid );
1318         int realChangedCat = changedCat;
1319         if ( mLayer->map()->newCats().contains( changedFid ) )
1320         {
1321           realChangedCat = mLayer->map()->newCats().value( changedFid );
1322         }
1323         QgsDebugMsg( QString( "changedFid = %1 changedCat = %2 realChangedCat = %3" )
1324                      .arg( changedFid ).arg( changedCat ).arg( realChangedCat ) );
1325         if ( realChangedCat == newCat )
1326         {
1327           QgsAttributeMap attributeMap = changedAttributes[changedFid];
1328           const auto constKeys = attributeMap.keys();
1329           for ( int index : constKeys )
1330           {
1331             attributeMap[index] = feature.attributes().value( index );
1332           }
1333           changedAttributes[changedFid] = attributeMap;
1334         }
1335       }
1336 #endif
1337 
1338       if ( mLayer->hasTable() )
1339       {
1340         QString error;
1341         // The record may exist if cat is manually defined by user (currently editing of cat column is disabled )
1342         bool recordExists = mLayer->recordExists( newCat, error );
1343         if ( !error.isEmpty() )
1344         {
1345           QgsGrass::warning( error );
1346         }
1347         else
1348         {
1349           error.clear();
1350           if ( !recordExists )
1351           {
1352             QgsDebugMsg( "record does not exist" );
1353             if ( mLayer->attributes().contains( newCat ) )
1354             {
1355               QgsDebugMsg( "attributes exist -> reinsert" );
1356               mLayer->reinsertAttributes( newCat, error );
1357             }
1358             else
1359             {
1360               mLayer->insertAttributes( newCat, feature, error );
1361             }
1362           }
1363           else
1364           {
1365             // Currently disabled
1366 #if 0
1367             // Manual entry of cat is not currently allowed
1368             // TODO: open warning dialog?
1369             // For now we are expecting that user knows what he is doing.
1370             // We update existing record by non null values and set feature null values to existing values
1371             mLayer->updateAttributes( newCat, feature, error ); // also updates feature by existing non null attributes
1372 #endif
1373           }
1374           if ( !error.isEmpty() )
1375           {
1376             QgsGrass::warning( error );
1377           }
1378         }
1379       }
1380 
1381       // update table
1382       emit dataChanged();
1383     }
1384   }
1385   else
1386   {
1387     QgsDebugMsg( "undo of old deleted feature" );
1388     int oldLid = lid;
1389     realLine = oldLid;
1390     int realCat = cat;
1391     int layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1392     if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1393     {
1394       realLine = mLayer->map()->newLids().value( oldLid );
1395     }
1396     if ( mLayer->map()->newCats().contains( fid ) )
1397     {
1398       realCat = mLayer->map()->newCats().value( fid );
1399     }
1400     QgsDebugMsg( QString( "fid = %1 lid = %2 realLine = %3 cat = %4 realCat = %5 layerField = %6" )
1401                  .arg( fid ).arg( lid ).arg( realLine ).arg( cat ).arg( realCat ).arg( layerField ) );
1402 
1403     if ( realLine > 0 )
1404     {
1405       QgsDebugMsg( QString( "reading realLine = %1" ).arg( realLine ) );
1406       int realType = readLine( mPoints, mCats, realLine );
1407       if ( realType > 0 )
1408       {
1409         QgsDebugMsg( QString( "the line exists realType = %1, add the cat to that line" ).arg( realType ) );
1410         type = realType;
1411       }
1412       else // should not happen
1413       {
1414         QgsDebugMsg( "cannot read realLine" );
1415         return;
1416       }
1417     }
1418     else
1419     {
1420       QgsDebugMsg( QString( "the line does not exist -> restore old geometry" ) );
1421       const QgsAbstractGeometry *geometry = nullptr;
1422 
1423       // If it is not new feature, we should have the geometry in oldGeometries
1424       if ( mLayer->map()->oldGeometries().contains( lid ) )
1425       {
1426         geometry = mLayer->map()->oldGeometries().value( lid );
1427         type = mLayer->map()->oldTypes().value( lid );
1428       }
1429       else
1430       {
1431         QgsDebugMsg( "geometry of old, previously deleted feature not found" );
1432         return;
1433       }
1434       if ( !geometry )
1435       {
1436         QgsDebugMsg( "geometry is null" );
1437         return;
1438       }
1439       setPoints( mPoints, geometry );
1440     }
1441 
1442     QgsDebugMsg( QString( "layerField = %1 realCat = %2" ).arg( layerField ).arg( realCat ) );
1443     if ( realCat > 0 && layerField > 0 )
1444     {
1445       Vect_cat_set( mCats, layerField, realCat );
1446 
1447       // restore attributes
1448       QgsGrassVectorMapLayer *layer = mLayer;
1449       if ( layerField != mLayer->field() )
1450       {
1451         layer = otherEditLayer( layerField );
1452       }
1453       if ( !layer )
1454       {
1455         QgsDebugMsg( "Cannot get layer" );
1456       }
1457       else
1458       {
1459         QString error;
1460         bool recordExists = layer->recordExists( realCat, error );
1461         QgsDebugMsg( QString( "recordExists = %1 error = %2" ).arg( recordExists ).arg( error ) );
1462         if ( !recordExists && error.isEmpty() )
1463         {
1464           QgsDebugMsg( "record does not exist -> restore attributes" );
1465           error.clear();
1466           layer->reinsertAttributes( realCat, error );
1467           if ( !error.isEmpty() )
1468           {
1469             QgsGrass::warning( tr( "Cannot restore record with cat %1" ).arg( realCat ) );
1470           }
1471         }
1472       }
1473     }
1474   }
1475 
1476   QgsDebugMsg( QString( "type = %1 mPoints->n_points = %2" ).arg( type ).arg( mPoints->n_points ) );
1477   if ( type > 0 && mPoints->n_points > 0 )
1478   {
1479     int newLid = 0;
1480     mLayer->map()->lockReadWrite();
1481     if ( realLine > 0 )
1482     {
1483       newLid = rewriteLine( realLine, type, mPoints, mCats );
1484     }
1485     else
1486     {
1487       // TODO: use writeLine() and move setting oldLids, newLids there
1488       newLid = Vect_write_line( map(), type, mPoints, mCats );
1489       // fid may be new (negative) or old, if this is delete undo
1490       int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1491 
1492       mLayer->map()->oldLids()[newLid] = oldLid;
1493       mLayer->map()->newLids()[oldLid] = newLid;
1494       QgsDebugMsg( QString( "oldLid = %1 newLine = %2" ).arg( oldLid ).arg( newLid ) );
1495 
1496       QgsDebugMsg( QString( "oldLids : %1 -> %2" ).arg( newLid ).arg( oldLid ) );
1497       mLayer->map()->oldLids()[newLid] = oldLid;
1498       QgsDebugMsg( QString( "newLids : %1 -> %2" ).arg( oldLid ).arg( newLid ) );
1499       mLayer->map()->newLids()[oldLid] = newLid;
1500     }
1501     mLayer->map()->unlockReadWrite();
1502 
1503     QgsDebugMsg( QString( "newLine = %1" ).arg( newLid ) );
1504 
1505     setAddedFeaturesSymbol();
1506   }
1507   QgsDebugMsg( QString( "mLayer->cidxFieldIndex() = %1 cidxFieldNumCats() = %2" )
1508                .arg( mLayer->cidxFieldIndex() ).arg( mLayer->cidxFieldNumCats() ) );
1509 }
1510 
onFeatureDeleted(QgsFeatureId fid)1511 void QgsGrassProvider::onFeatureDeleted( QgsFeatureId fid )
1512 {
1513   QgsDebugMsg( QString( "fid = %1" ).arg( fid ) );
1514 
1515   int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1516   int cat = QgsGrassFeatureIterator::catFromFid( fid );
1517   int layerField = 0;
1518   if ( FID_IS_NEW( fid ) )
1519   {
1520     layerField = mLayerField;
1521   }
1522   else
1523   {
1524     layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1525   }
1526 
1527   int realLine = oldLid;
1528   int realCat = cat;
1529   if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1530   {
1531     realLine = mLayer->map()->newLids().value( oldLid );
1532   }
1533   if ( mLayer->map()->newCats().contains( fid ) )
1534   {
1535     realCat = mLayer->map()->newCats().value( fid );
1536   }
1537 
1538   QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3 cat = %4 realCat = %5 layerField = %6" )
1539                .arg( fid ).arg( oldLid ).arg( realLine ).arg( cat ).arg( realCat ).arg( layerField ) );
1540 
1541   int type = 0;
1542   mLayer->map()->lockReadWrite();
1543   G_TRY
1544   {
1545     int type = readLine( mPoints, mCats, realLine );
1546     if ( type <= 0 )
1547     {
1548       QgsDebugMsg( "cannot read line" );
1549     }
1550     else
1551     {
1552       // store only the first original geometry if it is not new feature, changed geometries are stored in the buffer
1553       if ( oldLid > 0 && !mLayer->map()->oldGeometries().contains( oldLid ) )
1554       {
1555         QgsAbstractGeometry *geometry = mLayer->map()->lineGeometry( oldLid );
1556         if ( geometry )
1557         {
1558           QgsDebugMsg( QString( "save old geometry of oldLid = %1" ).arg( oldLid ) );
1559           mLayer->map()->oldGeometries().insert( oldLid, geometry );
1560           mLayer->map()->oldTypes().insert( oldLid, type );
1561         }
1562         else
1563         {
1564           QgsDebugMsg( QString( "cannot read geometry of oldLid = %1" ).arg( oldLid ) );
1565         }
1566       }
1567 
1568       if ( realCat > 0 && layerField > 0 )
1569       {
1570         if ( Vect_field_cat_del( mCats, layerField, realCat ) == 0 )
1571         {
1572           // should not happen
1573           QgsDebugMsg( "the line does not have old category" );
1574         }
1575       }
1576       QgsDebugMsg( QString( "mCats->n_cats = %1" ).arg( mCats->n_cats ) );
1577       if ( mCats->n_cats > 0 )
1578       {
1579         QgsDebugMsg( "the line has more cats -> rewrite" );
1580         int newLid = rewriteLine( realLine, type, mPoints, mCats );
1581         Q_UNUSED( newLid )
1582       }
1583       else
1584       {
1585         QgsDebugMsg( "no more cats on the line -> delete" );
1586 
1587         Vect_delete_line_function_pointer( map(), realLine );
1588         // oldLids are mapping to the very first, original version (used by undo)
1589         int oldestLid = oldLid;
1590         if ( mLayer->map()->oldLids().contains( oldLid ) )
1591         {
1592           oldestLid = mLayer->map()->oldLids().value( oldLid );
1593         }
1594         QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2" ).arg( oldLid ).arg( oldestLid ) );
1595         QgsDebugMsg( QString( "newLids : %1 -> 0" ).arg( oldestLid ) );
1596         mLayer->map()->newLids()[oldestLid] = 0;
1597       }
1598 
1599       // Delete record if orphan
1600       if ( realCat > 0 && layerField > 0 )
1601       {
1602         QgsGrassVectorMapLayer *layer = mLayer;
1603         if ( layerField != mLayer->field() )
1604         {
1605           layer = otherEditLayer( layerField );
1606         }
1607         if ( !layer )
1608         {
1609           QgsDebugMsg( "Cannot get layer" );
1610         }
1611         else
1612         {
1613           QString error;
1614           bool orphan = layer->isOrphan( realCat, error );
1615           QgsDebugMsg( QString( "orphan = %1 error = %2" ).arg( orphan ).arg( error ) );
1616           if ( orphan && error.isEmpty() )
1617           {
1618             QgsDebugMsg( QString( "realCat = %1 is orphan -> delete record" ).arg( realCat ) );
1619             error.clear();
1620             layer->deleteAttribute( realCat, error );
1621             if ( !error.isEmpty() )
1622             {
1623               QgsGrass::warning( tr( "Cannot delete orphan record with cat %1" ).arg( realCat ) );
1624             }
1625           }
1626         }
1627       }
1628     }
1629   }
1630   G_CATCH( QgsGrass::Exception & e )
1631   {
1632     QgsDebugMsg( QString( "Cannot rewrite/delete line : %1" ).arg( e.what() ) );
1633   }
1634   mLayer->map()->unlockReadWrite();
1635 
1636   if ( type == GV_BOUNDARY || type == GV_CENTROID )
1637   {
1638     setAddedFeaturesSymbol();
1639   }
1640 }
1641 
onGeometryChanged(QgsFeatureId fid,const QgsGeometry & geom)1642 void QgsGrassProvider::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1643 {
1644   int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1645   int realLine = oldLid;
1646   if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1647   {
1648     realLine = mLayer->map()->newLids().value( oldLid );
1649   }
1650   QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3" ).arg( fid ).arg( oldLid ).arg( realLine ) );
1651 
1652   int type = readLine( mPoints, mCats, realLine );
1653   QgsDebugMsg( QString( "type = %1 n_points = %2" ).arg( type ).arg( mPoints->n_points ) );
1654   if ( type < 1 ) // error
1655   {
1656     return;
1657   }
1658   mLastType = type;
1659 
1660   // store only the first original geometry if it is not new feature, changed geometries are stored in the buffer
1661   if ( oldLid > 0 && !mLayer->map()->oldGeometries().contains( oldLid ) )
1662   {
1663     QgsAbstractGeometry *geometry = mLayer->map()->lineGeometry( oldLid );
1664     if ( geometry )
1665     {
1666       QgsDebugMsg( QString( "save old geometry of oldLid = %1" ).arg( oldLid ) );
1667       mLayer->map()->oldGeometries().insert( oldLid, geometry );
1668       mLayer->map()->oldTypes().insert( oldLid, type );
1669     }
1670     else
1671     {
1672       QgsDebugMsg( QString( "cannot read geometry of oldLid = %1" ).arg( oldLid ) );
1673     }
1674   }
1675 
1676   setPoints( mPoints, geom.constGet() );
1677 
1678   mLayer->map()->lockReadWrite();
1679   // Vect_rewrite_line may delete/write the line with a new id
1680   int newLid = rewriteLine( realLine, type, mPoints, mCats );
1681   Q_UNUSED( newLid )
1682 
1683   mLayer->map()->unlockReadWrite();
1684 
1685   if ( type == GV_BOUNDARY || type == GV_CENTROID )
1686   {
1687     setAddedFeaturesSymbol();
1688   }
1689 }
1690 
onAttributeValueChanged(QgsFeatureId fid,int idx,const QVariant & value)1691 void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1692 {
1693   QgsDebugMsg( QString( "fid = %1 idx = %2 value = %3" ).arg( fid ).arg( idx ).arg( value.toString() ) );
1694 
1695   int layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1696   int cat = QgsGrassFeatureIterator::catFromFid( fid );
1697   QgsDebugMsg( QString( "layerField = %1" ).arg( layerField ) );
1698 
1699   if ( !FID_IS_NEW( fid ) && ( layerField > 0 && layerField != mLayerField ) )
1700   {
1701     QgsDebugMsg( "changing attributes in different layer is not allowed" );
1702     // reset the value
1703     QgsChangedAttributesMap &changedAttributes = mEditBuffer->mChangedAttributeValues;
1704     if ( idx == mLayer->keyColumn() )
1705     {
1706       // should not happen because cat field is not editable
1707       changedAttributes[fid][idx] = cat;
1708     }
1709     else
1710     {
1711       changedAttributes[fid][idx] = QgsGrassFeatureIterator::nonEditableValue( layerField );
1712     }
1713     // update table
1714     // TODO: This would be too slow with bulk updates (field calculator for example), causing update
1715     // of the whole table after each change. How to update single row?
1716     //emit dataChanged();
1717     return;
1718   }
1719 
1720   int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1721 
1722   int realLine = oldLid;
1723   int realCat = cat;
1724   if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1725   {
1726     realLine = mLayer->map()->newLids().value( oldLid );
1727   }
1728   if ( mLayer->map()->newCats().contains( fid ) )
1729   {
1730     realCat = mLayer->map()->newCats().value( fid );
1731   }
1732   QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3 cat = %4 realCat = %5" )
1733                .arg( fid ).arg( oldLid ).arg( realLine ).arg( cat ).arg( realCat ) );
1734 
1735   // index is for current fields
1736   if ( idx < 0 || idx > mEditLayer->fields().size() )
1737   {
1738     QgsDebugMsg( "index out of range" );
1739     return;
1740   }
1741   QgsField field = mEditLayer->fields().at( idx );
1742 
1743   QgsDebugMsg( "field.name() = " + field.name() + " keyColumnName() = " + mLayer->keyColumnName() );
1744   // TODO: Changing existing category is currently disabled (read only widget set on layer)
1745   //       because it makes it all too complicated
1746   if ( field.name() == mLayer->keyColumnName() )
1747   {
1748     // user changed category -> rewrite line
1749     QgsDebugMsg( "cat changed -> rewrite line" );
1750     int type = readLine( mPoints, mCats, realLine );
1751     if ( type <= 0 )
1752     {
1753       QgsDebugMsg( "cannot read line" );
1754     }
1755     else
1756     {
1757       if ( Vect_field_cat_del( mCats, mLayerField, realCat ) == 0 )
1758       {
1759         // should not happen
1760         QgsDebugMsg( "the line does not have old category" );
1761       }
1762 
1763       int newCat = value.toInt();
1764       QgsDebugMsg( QString( "realCat = %1 newCat = %2" ).arg( realCat ).arg( newCat ) );
1765       if ( newCat == 0 )
1766       {
1767         QgsDebugMsg( "new category is 0" );
1768         // TODO: remove category from line?
1769       }
1770       else
1771       {
1772         Vect_cat_set( mCats, mLayerField, newCat );
1773         mLayer->map()->lockReadWrite();
1774         int newLid = rewriteLine( realLine, type, mPoints, mCats );
1775         Q_UNUSED( newLid )
1776         mLayer->map()->newCats()[fid] = newCat;
1777 
1778         // TODO: - store the new cat somewhere for cats mapping
1779         //       - insert record if does not exist
1780         //       - update feature attributes by existing record?
1781 
1782         mLayer->map()->unlockReadWrite();
1783       }
1784     }
1785   }
1786   else
1787   {
1788     int undoIndex = mEditLayer->undoStack()->index();
1789     QgsDebugMsg( QString( "undoIndex = %1" ).arg( undoIndex ) );
1790     if ( realCat > 0 )
1791     {
1792       QString error;
1793       bool recordExists = mLayer->recordExists( realCat, error );
1794       if ( !error.isEmpty() )
1795       {
1796         QgsGrass::warning( error );
1797       }
1798       error.clear();
1799       mLayer->changeAttributeValue( realCat, field, value, error );
1800       if ( !error.isEmpty() )
1801       {
1802         QgsGrass::warning( error );
1803       }
1804       if ( !recordExists )
1805       {
1806         mLayer->map()->undoCommands()[undoIndex]
1807             << new QgsGrassUndoCommandChangeAttribute( this, fid, realLine, mLayerField, realCat, false, true );
1808       }
1809     }
1810     else
1811     {
1812       int newCat = getNewCat();
1813       QgsDebugMsg( QString( "no cat -> add new cat %1 to line" ).arg( newCat ) );
1814       int type = readLine( mPoints, mCats, realLine );
1815       if ( type <= 0 )
1816       {
1817         QgsDebugMsg( "cannot read line" );
1818       }
1819       else
1820       {
1821         Vect_cat_set( mCats, mLayerField, newCat );
1822         mLayer->map()->lockReadWrite();
1823         int newLid = rewriteLine( realLine, type, mPoints, mCats );
1824         Q_UNUSED( newLid )
1825         mLayer->map()->newCats()[fid] = newCat;
1826 
1827         QString error;
1828         bool recordExists = mLayer->recordExists( newCat, error );
1829         if ( !error.isEmpty() )
1830         {
1831           QgsGrass::warning( error );
1832         }
1833         error.clear();
1834         // it does insert new record if it doesn't exist
1835         mLayer->changeAttributeValue( newCat, field, value, error );
1836         if ( !error.isEmpty() )
1837         {
1838           QgsGrass::warning( error );
1839         }
1840 
1841         mLayer->map()->undoCommands()[undoIndex]
1842             << new QgsGrassUndoCommandChangeAttribute( this, fid, newLid, mLayerField, newCat, true, !recordExists );
1843 
1844         mLayer->map()->unlockReadWrite();
1845       }
1846     }
1847   }
1848 }
1849 
onAttributeAdded(int idx)1850 void QgsGrassProvider::onAttributeAdded( int idx )
1851 {
1852   QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
1853   if ( idx < 0 || idx >= mEditLayer->fields().size() )
1854   {
1855     QgsDebugMsg( "index out of range" );
1856   }
1857   QString error;
1858   mLayer->addColumn( mEditLayer->fields().at( idx ), error );
1859   if ( !error.isEmpty() )
1860   {
1861     QgsDebugMsg( error );
1862     QgsGrass::warning( error );
1863     // TODO: remove the column somehow from the layer/buffer - undo?
1864   }
1865   else
1866   {
1867     mEditLayerFields = mEditLayer->fields();
1868     emit fieldsChanged();
1869   }
1870 }
1871 
onAttributeDeleted(int idx)1872 void QgsGrassProvider::onAttributeDeleted( int idx )
1873 {
1874   QgsDebugMsg( QString( "idx = %1 mEditLayerFields.size() = %2" ).arg( idx ).arg( mEditLayerFields.size() ) );
1875   // The field was already removed from mEditLayer->fields() -> using stored last version of fields
1876   if ( idx < 0 || idx >= mEditLayerFields.size() )
1877   {
1878     QgsDebugMsg( "index out of range" );
1879     return;
1880   }
1881   QgsField deletedField = mEditLayerFields.at( idx );
1882   QgsDebugMsg( QString( "deletedField.name() = %1" ).arg( deletedField.name() ) );
1883 
1884   QString error;
1885   mLayer->deleteColumn( deletedField, error );
1886   if ( !error.isEmpty() )
1887   {
1888     QgsDebugMsg( error );
1889     QgsGrass::warning( error );
1890     // TODO: get back the column somehow to the layer/buffer - undo?
1891   }
1892   else
1893   {
1894     mEditLayerFields = mEditLayer->fields();
1895     emit fieldsChanged();
1896   }
1897 }
1898 
setAddedFeaturesSymbol()1899 void QgsGrassProvider::setAddedFeaturesSymbol()
1900 {
1901   if ( !mEditBuffer )
1902   {
1903     return;
1904   }
1905   QgsFeatureMap &features = mEditBuffer->mAddedFeatures;
1906   for ( auto it = features.constBegin(); it != features.constEnd(); ++it )
1907   {
1908     QgsFeature feature = it.value();
1909     if ( !feature.hasGeometry() )
1910     {
1911       continue;
1912     }
1913     int lid = QgsGrassFeatureIterator::lidFromFid( it.key() );
1914     int realLid = lid;
1915     if ( mLayer->map()->newLids().contains( lid ) )
1916     {
1917       realLid = mLayer->map()->newLids().value( lid );
1918     }
1919     QgsDebugMsg( QString( "fid = %1 lid = %2 realLid = %3" ).arg( it.key() ).arg( lid ).arg( realLid ) );
1920     QgsGrassVectorMap::TopoSymbol symbol = mLayer->map()->topoSymbol( realLid );
1921     // the feature may be without fields and set attribute by name does not work
1922     int index = mLayer->fields().indexFromName( QgsGrassVectorMap::topoSymbolFieldName() );
1923     feature.setAttribute( index, symbol );
1924     features[it.key()] = feature;
1925   }
1926 }
1927 
onUndoIndexChanged(int currentIndex)1928 void QgsGrassProvider::onUndoIndexChanged( int currentIndex )
1929 {
1930   QgsDebugMsg( QString( "currentIndex = %1" ).arg( currentIndex ) );
1931   // multiple commands maybe undone with single undoIndexChanged signal
1932   QList<int> indexes = mLayer->map()->undoCommands().keys();
1933   std::sort( indexes.begin(), indexes.end() );
1934   for ( int i = indexes.size() - 1; i >= 0; i-- )
1935   {
1936     int index = indexes[i];
1937     if ( index < currentIndex )
1938     {
1939       break;
1940     }
1941     QgsDebugMsg( QString( "index = %1" ).arg( index ) );
1942     if ( mLayer->map()->undoCommands().contains( index ) )
1943     {
1944       QgsDebugMsg( QString( "%1 undo commands" ).arg( mLayer->map()->undoCommands()[index].size() ) );
1945 
1946       for ( int j = 0; j < mLayer->map()->undoCommands()[index].size(); j++ )
1947       {
1948         mLayer->map()->undoCommands()[index][j]->undo();
1949         delete mLayer->map()->undoCommands()[index][j];
1950       }
1951       mLayer->map()->undoCommands().remove( index );
1952     }
1953   }
1954 }
1955 
addAttributes(const QList<QgsField> & attributes)1956 bool QgsGrassProvider::addAttributes( const QList<QgsField> &attributes )
1957 {
1958   Q_UNUSED( attributes )
1959   // update fields because QgsVectorLayerEditBuffer::commitChanges() checks old /new fields count
1960   mLayer->updateFields();
1961   return true;
1962 }
1963 
deleteAttributes(const QgsAttributeIds & attributes)1964 bool QgsGrassProvider::deleteAttributes( const QgsAttributeIds &attributes )
1965 {
1966   Q_UNUSED( attributes )
1967   // update fields because QgsVectorLayerEditBuffer::commitChanges() checks old /new fields count
1968   mLayer->updateFields();
1969   return true;
1970 }
1971 
onBeforeCommitChanges()1972 void QgsGrassProvider::onBeforeCommitChanges()
1973 {
1974   mLayer->map()->clearUndoCommands();
1975 }
1976 
onBeforeRollBack()1977 void QgsGrassProvider::onBeforeRollBack()
1978 {
1979 }
1980 
onEditingStopped()1981 void QgsGrassProvider::onEditingStopped()
1982 {
1983   QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( sender() );
1984   closeEdit( false, vectorLayer );
1985 }
1986 
1987 // -------------------------------------------------------------------------------
1988 
cidxGetNumFields()1989 int QgsGrassProvider::cidxGetNumFields()
1990 {
1991   return ( Vect_cidx_get_num_fields( map() ) );
1992 }
1993 
cidxGetFieldNumber(int idx)1994 int QgsGrassProvider::cidxGetFieldNumber( int idx )
1995 {
1996   if ( idx < 0 || idx >= cidxGetNumFields() )
1997   {
1998     QgsDebugMsg( QString( "idx %1 out of range (0,%2)" ).arg( idx ).arg( cidxGetNumFields() - 1 ) );
1999     return 0;
2000   }
2001   return ( Vect_cidx_get_field_number( map(), idx ) );
2002 }
2003 
cidxGetMaxCat(int idx)2004 int QgsGrassProvider::cidxGetMaxCat( int idx )
2005 {
2006   QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
2007   if ( idx < 0 || idx >= cidxGetNumFields() )
2008   {
2009     QgsDebugMsg( QString( "idx %1 out of range (0,%2)" ).arg( idx ).arg( cidxGetNumFields() - 1 ) );
2010     return 0;
2011   }
2012 
2013   int ncats = Vect_cidx_get_num_cats_by_index( map(), idx );
2014   QgsDebugMsg( QString( "ncats = %1" ).arg( ncats ) );
2015 
2016   if ( ncats == 0 )
2017   {
2018     return 0;
2019   }
2020 
2021   int cat, type, id;
2022   Vect_cidx_get_cat_by_index( map(), idx, ncats - 1, &cat, &type, &id );
2023 
2024   return cat;
2025 }
2026 
getNewCat()2027 int QgsGrassProvider::getNewCat()
2028 {
2029   QgsDebugMsg( QString( "get new cat for cidxFieldIndex() = %1" ).arg( mLayer->cidxFieldIndex() ) );
2030   if ( mLayer->cidxFieldIndex() == -1 )
2031   {
2032     // No features with this field yet in map
2033     return 1;
2034   }
2035   else
2036   {
2037     return cidxGetMaxCat( mLayer->cidxFieldIndex() ) + 1;
2038   }
2039 }
2040 
openLayer() const2041 QgsGrassVectorMapLayer *QgsGrassProvider::openLayer() const
2042 {
2043   return mLayer->map()->openLayer( mLayerField );
2044 }
2045 
map() const2046 struct Map_info *QgsGrassProvider::map() const
2047 {
2048   Q_ASSERT( mLayer );
2049   // cppcheck-suppress assertWithSideEffect
2050   Q_ASSERT( mLayer->map() );
2051   // cppcheck-suppress assertWithSideEffect
2052   Q_ASSERT( mLayer->map()->map() );
2053   return mLayer->map()->map();
2054 }
2055 
setMapset()2056 void QgsGrassProvider::setMapset()
2057 {
2058   QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
2059 }
2060 
otherEditLayer(int layerField)2061 QgsGrassVectorMapLayer *QgsGrassProvider::otherEditLayer( int layerField )
2062 {
2063   const auto constMOtherEditLayers = mOtherEditLayers;
2064   for ( QgsGrassVectorMapLayer *layer : constMOtherEditLayers )
2065   {
2066     if ( layer->field() == layerField )
2067     {
2068       return layer;
2069     }
2070   }
2071   QgsGrassVectorMapLayer *layer = mLayer->map()->openLayer( layerField );
2072   if ( layer )
2073   {
2074     layer->startEdit();
2075     mOtherEditLayers << layer;
2076   }
2077   return layer;
2078 }
2079 
name() const2080 QString QgsGrassProvider::name() const
2081 {
2082   return GRASS_KEY;
2083 }
2084 
description() const2085 QString QgsGrassProvider::description() const
2086 {
2087   return tr( "GRASS %1 vector provider" ).arg( GRASS_VERSION_MAJOR );
2088 }
2089 
2090