2018-01-08 20:45:52 +01:00
# include " CrudModel.h "
2018-01-09 20:39:43 +01:00
# include "ASyncWindow.h"
2018-01-08 20:45:52 +01:00
# include "OpenDatabase.h"
# include "PgDatabaseCatalog.h"
2018-04-08 09:02:22 +02:00
# include "PgAttribute.h"
2018-01-08 20:45:52 +01:00
# include "PgAttributeContainer.h"
# include "PgConstraintContainer.h"
2018-01-09 20:39:43 +01:00
# include "GlobalIoService.h"
# include "SqlFormattingUtils.h"
# include "WorkManager.h"
2018-02-05 21:42:54 +01:00
# include "Pgsql_oids.h"
2018-01-09 20:39:43 +01:00
# include <QtConcurrent>
# include <QFuture>
# include <QFutureWatcher>
# include "Pgsql_oids.h"
2018-02-05 21:42:54 +01:00
# include "Pgsql_Params.h"
2018-01-08 20:45:52 +01:00
# include <string>
2018-12-08 13:55:43 +01:00
# include "ScopeGuard.h"
2018-01-08 20:45:52 +01:00
2018-01-09 20:39:43 +01:00
CrudModel : : CrudModel ( ASyncWindow * async_window )
: m_asyncWindow ( async_window )
, m_dbConn ( * getGlobalAsioIoService ( ) )
{
2018-02-05 21:42:54 +01:00
qDebug ( " CrudModel created " ) ;
2018-01-09 20:39:43 +01:00
connect ( & m_dbConn , & ASyncDBConnection : : onStateChanged , this , & CrudModel : : connectionStateChanged ) ;
}
2018-01-08 20:45:52 +01:00
CrudModel : : ~ CrudModel ( )
{
m_dbConn . closeConnection ( ) ;
}
/*
* Strategy
* when ordered by primary key , offset and limit work very quickly so we can get away with not loading
* everything .
*/
2018-01-09 20:39:43 +01:00
void CrudModel : : setConfig ( std : : shared_ptr < OpenDatabase > db , const PgClass & table )
2018-01-08 20:45:52 +01:00
{
m_database = db ;
m_table = table ;
2018-11-25 19:45:06 +01:00
m_primaryKey = db - > catalog ( ) - > constraints ( ) - > getPrimaryForRelation ( table . oid ( ) ) ;
2018-01-08 20:45:52 +01:00
//cat->attributes()->getColumnsForRelation()
2018-01-09 20:39:43 +01:00
callLoadData = true ;
2018-02-18 07:15:43 +01:00
auto dbconfig = m_database - > config ( ) ;
m_dbConn . setupConnection ( dbconfig ) ;
2018-01-09 20:39:43 +01:00
}
QVariant CrudModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
{
QVariant r ;
if ( role = = Qt : : DisplayRole ) {
if ( orientation = = Qt : : Horizontal ) {
QString s ( m_roData - > getColName ( section ) ) ;
s + = " \n " ;
2018-11-25 09:05:01 +01:00
s + = getTypeDisplayString ( * m_database - > catalog ( ) , getType ( section ) ) ;
2018-01-09 20:39:43 +01:00
r = s ;
}
else {
r = QString : : number ( section + 1 ) ;
}
}
return r ;
}
// Basic functionality:
2018-02-05 21:42:54 +01:00
int CrudModel : : rowCount ( const QModelIndex & /*parent*/ ) const
2018-01-09 20:39:43 +01:00
{
2018-02-18 08:37:59 +01:00
// int row_count = m_roData ? m_roData->rows() : 0;
2018-01-09 20:39:43 +01:00
2018-02-18 08:37:59 +01:00
// todo there will be rownumbers that are not in m_roData
// ++row_count; // one empty new row at the end
return m_rowCount ;
2018-01-09 20:39:43 +01:00
}
2018-02-05 21:42:54 +01:00
int CrudModel : : columnCount ( const QModelIndex & /*parent*/ ) const
2018-01-09 20:39:43 +01:00
{
2018-02-18 08:37:59 +01:00
int col_count = m_roData ? m_roData - > cols ( ) : 0 ;
return col_count ;
2018-01-09 20:39:43 +01:00
}
Oid CrudModel : : getType ( int column ) const
{
return m_roData ? m_roData - > type ( column ) : InvalidOid ;
}
2018-02-05 21:42:54 +01:00
CrudModel : : Value CrudModel : : getData ( const QModelIndex & index ) const
2018-01-09 20:39:43 +01:00
{
2018-02-05 21:42:54 +01:00
Value value ;
2018-01-09 20:39:43 +01:00
if ( m_roData ) {
2018-04-08 09:02:22 +02:00
int grid_row = index . row ( ) ;
2018-01-09 20:39:43 +01:00
int col = index . column ( ) ;
2018-02-05 21:42:54 +01:00
2018-04-08 09:02:22 +02:00
auto row_mapping = m_rowMapping [ grid_row ] ;
2018-02-18 08:37:59 +01:00
const int last_row = rowCount ( ) - 1 ;
2018-02-05 21:42:54 +01:00
//Oid o = m_roData->type(col);
// First see if we have buffered editted values that still need saving
2018-11-08 21:50:49 +01:00
std : : optional < Value > val ;
2018-04-08 09:02:22 +02:00
if ( row_mapping . pending ) {
val = m_pendingRowList . getValue ( col , row_mapping . rowKey ) ;
}
if ( row_mapping . modified & & ! val & & grid_row < last_row ) { // last_row should never be in modified list, when it is put in modified list a new last row should be created
2018-02-05 21:42:54 +01:00
// No pending save have a look if we have modified saved data in the modified list
2018-04-08 09:02:22 +02:00
auto find_res = m_modifiedRowList . find ( row_mapping . rowKey ) ;
if ( find_res ! = m_modifiedRowList . end ( ) ) {
2018-02-05 21:42:54 +01:00
val = find_res - > second . data ( ) [ col ] ;
2018-04-08 09:02:22 +02:00
}
2018-01-09 20:39:43 +01:00
}
2018-02-05 21:42:54 +01:00
//Value value;
// If we did not have pending or modified data
2018-04-08 09:02:22 +02:00
if ( ! val & & row_mapping . rowKey < m_roData - > rows ( ) ) {
2018-02-05 21:42:54 +01:00
// Then we are going to read the original data.
2018-04-08 09:02:22 +02:00
if ( ! m_roData - > null ( col , row_mapping . rowKey ) ) {
value = std : : string ( m_roData - > val ( col , row_mapping . rowKey ) ) ;
}
2018-01-09 20:39:43 +01:00
}
2018-02-05 21:42:54 +01:00
else {
2018-04-08 09:02:22 +02:00
if ( val ) {
2018-02-18 08:37:59 +01:00
value = * val ;
2018-04-08 09:02:22 +02:00
}
2018-02-05 21:42:54 +01:00
}
2018-01-09 20:39:43 +01:00
}
2018-02-05 21:42:54 +01:00
return value ;
2018-01-09 20:39:43 +01:00
}
QVariant CrudModel : : data ( const QModelIndex & index , int role ) const
{
2018-01-15 13:30:30 +01:00
QVariant v ;
2018-02-05 21:42:54 +01:00
if ( role = = Qt : : EditRole ) {
auto value = getData ( index ) ;
if ( value ) {
2018-02-18 07:15:43 +01:00
QString s = QString : : fromUtf8 ( value - > c_str ( ) ) ;
v = s ;
2018-02-05 21:42:54 +01:00
}
}
else if ( role = = Qt : : DisplayRole ) {
auto value = getData ( index ) ;
if ( value ) {
Oid o = m_roData - > type ( index . column ( ) ) ;
if ( o = = Pgsql : : bool_oid ) {
2018-02-18 08:32:38 +01:00
v = * value = = " t " ; //s = (s == "t") ? "TRUE" : "FALSE";
2018-02-05 21:42:54 +01:00
}
else {
QString s = QString : : fromUtf8 ( value - > c_str ( ) ) ;
if ( s . length ( ) > 256 ) {
s . truncate ( 256 ) ;
}
v = s ;
}
}
2018-01-09 20:39:43 +01:00
}
2018-01-15 13:30:30 +01:00
else if ( role = = Qt : : UserRole ) {
v = getType ( index . column ( ) ) ;
2018-01-09 20:39:43 +01:00
}
2018-01-15 13:30:30 +01:00
return v ;
2018-01-09 20:39:43 +01:00
}
void CrudModel : : loadData ( )
{
2018-11-25 19:45:06 +01:00
QString table_name = m_table - > fullyQualifiedQuotedObjectName ( ) ; // genFQTableName(*m_database->catalog(), *m_table);
2018-01-09 20:39:43 +01:00
std : : string q = " SELECT * FROM " ;
2018-11-10 11:37:17 +01:00
q + = std : : string ( table_name . toUtf8 ( ) . data ( ) ) ;
2018-01-09 20:39:43 +01:00
m_dbConn . send ( q , [ this ] ( Expected < std : : shared_ptr < Pgsql : : Result > > res , qint64 ) {
if ( res . valid ( ) ) {
auto dbres = res . get ( ) ;
if ( dbres & & * dbres ) {
m_asyncWindow - > QueueTask ( [ this , dbres ] ( ) { loadIntoModel ( dbres ) ; } ) ;
}
}
else {
// emit onQueryError();
}
} ) ;
}
void CrudModel : : loadIntoModel ( std : : shared_ptr < Pgsql : : Result > data )
{
beginResetModel ( ) ;
2018-11-14 19:17:29 +01:00
m_pendingRowList . clear ( ) ;
m_modifiedRowList . clear ( ) ;
2018-01-09 20:39:43 +01:00
m_roData = data ;
2018-04-08 09:02:22 +02:00
lastRowKey = data - > rows ( ) ;
m_rowCount = data - > rows ( ) ;
initRowMapping ( ) ;
appendNewRow ( ) ;
2018-01-09 20:39:43 +01:00
endResetModel ( ) ;
}
2018-04-08 09:02:22 +02:00
void CrudModel : : initRowMapping ( )
{
m_rowMapping . resize ( m_rowCount ) ;
for ( int i = 0 ; i < m_rowCount ; + + i )
2018-12-15 11:19:32 +01:00
m_rowMapping [ i ] = RowMapping { i } ;
2018-04-08 09:02:22 +02:00
}
2018-01-09 20:39:43 +01:00
void CrudModel : : connectionStateChanged ( ASyncDBConnection : : State state )
{
switch ( state ) {
case ASyncDBConnection : : State : : NotConnected :
break ;
case ASyncDBConnection : : State : : Connecting :
break ;
case ASyncDBConnection : : State : : Connected :
if ( callLoadData ) {
callLoadData = false ;
loadData ( ) ;
}
break ;
case ASyncDBConnection : : State : : QuerySend :
break ;
case ASyncDBConnection : : State : : CancelSend :
break ;
case ASyncDBConnection : : State : : Terminating :
break ;
}
}
Qt : : ItemFlags CrudModel : : flags ( const QModelIndex & ) const
{
2018-02-18 12:10:09 +01:00
Qt : : ItemFlags flags = Qt : : ItemIsSelectable + Qt : : ItemIsEnabled ;
if ( m_primaryKey ) {
2018-04-08 09:02:22 +02:00
flags | = Qt : : ItemIsEditable ;
2018-02-18 12:10:09 +01:00
}
return flags ;
2018-01-08 20:45:52 +01:00
}
2018-02-05 21:42:54 +01:00
bool CrudModel : : setData ( const QModelIndex & index , const QVariant & value , int role )
{
if ( role = = Qt : : EditRole ) {
2018-04-08 09:02:22 +02:00
int grid_row = index . row ( ) ;
2018-02-05 21:42:54 +01:00
int col = index . column ( ) ;
2018-04-08 09:02:22 +02:00
auto & row_mapping = m_rowMapping [ grid_row ] ;
row_mapping . pending = true ;
2018-02-05 21:42:54 +01:00
Value val ;
2018-02-18 08:37:59 +01:00
std : : string s = value . toString ( ) . toUtf8 ( ) . data ( ) ;
if ( ! s . empty ( ) ) {
if ( s = = " '' " )
s . clear ( ) ;
val = s ;
}
2018-04-08 09:02:22 +02:00
m_pendingRowList . setValue ( col , row_mapping . rowKey , val ) ;
2018-02-05 21:42:54 +01:00
emit dataChanged ( index , index , QVector < int > ( ) < < role ) ;
return true ;
}
return false ;
}
const CrudModel : : ModifiedRow * CrudModel : : getModifiedRow ( int row ) const
{
auto iter = m_modifiedRowList . find ( row ) ;
if ( iter = = m_modifiedRowList . end ( ) )
return nullptr ;
else
return & iter - > second ;
}
CrudModel : : PKeyValues CrudModel : : getPKeyForRow ( int row ) const
{
PKeyValues values ;
2018-12-08 08:47:02 +01:00
values . reserve ( m_primaryKey - > key . size ( ) ) ;
2018-02-05 21:42:54 +01:00
auto mod_row = getModifiedRow ( row ) ;
if ( mod_row ) {
for ( auto attnum : m_primaryKey - > key ) {
2018-12-08 13:55:43 +01:00
const int col = attNumToCol ( attnum ) ;
2018-02-05 21:42:54 +01:00
values . push_back ( * ( mod_row - > data ( ) [ col ] ) ) ;
}
}
2018-02-18 08:37:59 +01:00
else if ( row < m_roData - > rows ( ) ) {
2018-02-05 21:42:54 +01:00
for ( auto attnum : m_primaryKey - > key ) {
2018-12-08 13:55:43 +01:00
int col = attNumToCol ( attnum ) ;
2018-02-05 21:42:54 +01:00
values . push_back ( m_roData - > get ( col , row ) . c_str ( ) ) ;
}
}
return values ;
}
2018-12-08 13:55:43 +01:00
Pgsql : : Params CrudModel : : getPKeyParamsForRow ( int row ) const
{
Pgsql : : Params params ;
auto mod_row = getModifiedRow ( row ) ;
for ( auto attnum : m_primaryKey - > key ) {
const int col = attNumToCol ( attnum ) ;
Oid t = getType ( col ) ;
std : : string s ;
if ( mod_row ) {
s = * ( mod_row - > data ( ) [ col ] ) ;
}
else if ( row < m_roData - > rows ( ) ) {
s = m_roData - > get ( col , row ) . c_str ( ) ;
}
params . add ( s , t ) ;
}
return params ;
}
2018-02-05 21:42:54 +01:00
QString CrudModel : : columnName ( int col ) const
{
return m_roData - > getColName ( col ) ;
}
2018-02-18 08:37:59 +01:00
std : : tuple < QString , Pgsql : : Params > CrudModel : : createUpdateQuery ( const PKeyValues & pkey_values , const PendingRow & pending_row )
{
Pgsql : : Params params ;
auto data = pending_row . data ( ) ;
2018-11-25 19:45:06 +01:00
QString table_name = m_table - > fullyQualifiedQuotedObjectName ( ) ; //genFQTableName(*m_database->catalog(), *m_table);
2018-02-18 08:37:59 +01:00
QString buffer ;
QTextStream q ( & buffer ) ;
q < < " UPDATE " < < table_name < < " AS d \n SET " ;
int param = 0 ;
for ( auto e : data ) {
if ( param > 0 )
q < < " , " ;
q < < quoteIdent ( columnName ( e . first ) ) < < " =$ " < < + + param ;
// Add value to paramlist
params . add ( e . second , getType ( e . first ) ) ;
}
q < < " \n WHERE " ;
int i = 0 ;
for ( auto attnum : m_primaryKey - > key ) {
2018-12-08 13:55:43 +01:00
int col = attNumToCol ( attnum ) ;
2018-02-18 08:37:59 +01:00
if ( i > 0 )
q < < " AND " ;
q < < quoteIdent ( columnName ( col ) ) < < " =$ " < < + + param ;
params . add ( pkey_values [ i ] . c_str ( ) , getType ( col ) ) ;
+ + i ;
}
q < < " \n RETURNING * " ;
q . flush ( ) ;
return { buffer , params } ;
}
std : : tuple < QString , Pgsql : : Params > CrudModel : : createInsertQuery ( const PendingRow & pending_row )
{
Pgsql : : Params params ;
auto data = pending_row . data ( ) ;
2018-11-25 19:45:06 +01:00
QString table_name = m_table - > fullyQualifiedQuotedObjectName ( ) ; // genFQTableName(*m_database->catalog(), *m_table);
2018-02-18 08:37:59 +01:00
QString buffer ;
QTextStream q ( & buffer ) ;
2018-04-08 09:02:22 +02:00
q < < " INSERT INTO " < < table_name < < " ( " ;
2018-11-25 19:45:06 +01:00
auto columns = m_database - > catalog ( ) - > attributes ( ) - > getColumnsForRelation ( m_table - > oid ( ) ) ;
2018-04-08 09:02:22 +02:00
bool first = true ;
for ( auto e : data ) {
int num = e . first + 1 ;
auto find_res = std : : find_if ( columns . begin ( ) , columns . end ( ) ,
[ num ] ( const auto & elem ) - > bool { return num = = elem . num ; } ) ;
if ( find_res ! = columns . end ( ) ) {
if ( first ) first = false ;
else q < < " , " ;
q < < find_res - > name ;
}
}
q < < " ) VALUES ($1 " ;
2018-02-18 08:37:59 +01:00
for ( size_t p = 2 ; p < = data . size ( ) ; + + p )
q < < " ,$ " < < p ;
q < < " ) RETURNING * " ;
for ( auto e : data ) {
// Add value to paramlist
2018-04-08 09:02:22 +02:00
2018-02-18 08:37:59 +01:00
params . add ( e . second , getType ( e . first ) ) ;
}
q . flush ( ) ;
return { buffer , params } ;
}
2018-02-05 21:42:54 +01:00
2018-04-08 09:02:22 +02:00
std : : tuple < QString , Pgsql : : Params > CrudModel : : createDeleteStatement ( const PKeyValues & pkey_values )
{
Pgsql : : Params params ;
2018-12-08 13:55:43 +01:00
size_t i = 0 ;
for ( auto attnum : m_primaryKey - > key ) {
const int col = attNumToCol ( attnum ) ;
params . add ( pkey_values [ i ] . c_str ( ) , getType ( col ) ) ;
+ + i ;
}
return { createDeleteStatement ( ) , params } ;
}
QString CrudModel : : createDeleteStatement ( ) const
{
Pgsql : : Params params ;
QString table_name = m_table - > fullyQualifiedQuotedObjectName ( ) ;
2018-04-08 09:02:22 +02:00
QString buffer ;
QTextStream q ( & buffer ) ;
q < < " DELETE FROM " < < table_name ;
q < < " \n WHERE " ;
2018-12-08 13:55:43 +01:00
int i = 0 ;
2018-04-08 09:02:22 +02:00
for ( auto attnum : m_primaryKey - > key ) {
2018-12-08 13:55:43 +01:00
const int col = attNumToCol ( attnum ) ;
2018-04-08 09:02:22 +02:00
if ( i > 0 )
q < < " AND " ;
2018-12-08 13:55:43 +01:00
q < < quoteIdent ( columnName ( col ) ) < < " =$ " < < + + i ;
2018-04-08 09:02:22 +02:00
}
q . flush ( ) ;
2018-12-08 13:55:43 +01:00
return buffer ;
2018-04-08 09:02:22 +02:00
}
2018-02-18 07:15:43 +01:00
std : : tuple < bool , CrudModel : : ModifiedRow > CrudModel : : updateRow ( const PendingRow & pending_row )
2018-02-05 21:42:54 +01:00
{
auto data = pending_row . data ( ) ;
2018-02-18 08:37:59 +01:00
2018-02-05 21:42:54 +01:00
if ( ! data . empty ( ) ) {
2018-02-18 07:15:43 +01:00
auto pkey_values = getPKeyForRow ( pending_row . row ( ) ) ;
2018-02-18 08:37:59 +01:00
QString buffer ;
Pgsql : : Params params ;
if ( pkey_values . empty ( ) ) {
std : : tie ( buffer , params ) = createInsertQuery ( pending_row ) ;
2018-02-05 21:42:54 +01:00
}
2018-02-18 08:37:59 +01:00
else {
std : : tie ( buffer , params ) = createUpdateQuery ( pkey_values , pending_row ) ;
}
2018-02-05 21:42:54 +01:00
2018-02-18 07:15:43 +01:00
int row_number = pending_row . row ( ) ;
Pgsql : : Connection db_update_conn ;
auto dbconfig = m_database - > config ( ) ;
bool res = db_update_conn . connect ( dbconfig . getKeywords ( ) , dbconfig . getValues ( ) , false ) ;
if ( res ) {
auto result = db_update_conn . queryParam ( buffer , params ) ;
if ( result & & result . rows ( ) = = 1 ) {
std : : vector < Value > values ;
auto row = * result . begin ( ) ;
for ( auto v : row ) {
if ( v . null ( ) )
values . push_back ( Value ( ) ) ;
else
values . push_back ( std : : string ( v . c_str ( ) ) ) ;
}
2018-02-05 21:42:54 +01:00
2018-02-18 07:15:43 +01:00
ModifiedRow modified_row ( row_number , values ) ;
2018-02-05 21:42:54 +01:00
2018-02-18 07:15:43 +01:00
return { true , modified_row } ;
}
}
2018-02-05 21:42:54 +01:00
2018-02-18 07:15:43 +01:00
}
return { false , { } } ;
2018-02-05 21:42:54 +01:00
}
bool CrudModel : : savePendingChanges ( )
{
2018-02-18 07:15:43 +01:00
while ( ! m_pendingRowList . m_rows . empty ( ) ) {
auto iter = m_pendingRowList . m_rows . begin ( ) ;
auto [ ok , modified_row ] = updateRow ( iter - > second ) ;
if ( ok ) {
2018-04-08 09:02:22 +02:00
int rowKey = iter - > first ;
m_modifiedRowList . insert_or_assign ( rowKey , modified_row ) ;
2018-02-18 07:15:43 +01:00
m_pendingRowList . m_rows . erase ( iter ) ;
2018-02-18 08:37:59 +01:00
2018-04-08 09:02:22 +02:00
auto iter = std : : find_if ( m_rowMapping . begin ( ) , m_rowMapping . end ( ) ,
[ rowKey ] ( const RowMapping & rhs ) - > auto { return rhs . rowKey = = rowKey ; } ) ;
if ( iter ! = m_rowMapping . end ( ) ) {
iter - > modified = true ;
iter - > pending = false ;
int row = iter - m_rowMapping . begin ( ) ;
if ( row = = m_rowCount - 1 )
appendNewRow ( ) ;
}
2018-02-18 08:37:59 +01:00
}
else {
return false ;
2018-02-18 07:15:43 +01:00
}
2018-02-05 21:42:54 +01:00
}
return true ;
}
bool CrudModel : : submit ( )
{
return savePendingChanges ( ) ;
}
void CrudModel : : revert ( )
{
}
2018-02-18 08:37:59 +01:00
void CrudModel : : appendNewRow ( )
{
int row = m_rowCount ;
beginInsertRows ( QModelIndex ( ) , row , row ) ;
2018-04-08 09:02:22 +02:00
m_rowMapping . emplace_back ( allocNewRowKey ( ) ) ;
2018-02-18 08:37:59 +01:00
+ + m_rowCount ;
endInsertRows ( ) ;
}
2018-04-08 09:02:22 +02:00
void CrudModel : : removeRows ( )
{
// determine selection
// remove selected rows
}
bool CrudModel : : removeRows ( int row , int count , const QModelIndex & parent )
{
if ( m_rowMapping . empty ( ) ) return false ;
// When removing rows there is no direct mapping anymore between the rows in the grid
// and the rows in m_roData
// Therefor we need an indirection to keep track of which rows are visible and which
// grid row maps to what data row. Maybe we can also keep track where the current data
// of the row is located original data, pending data or modified data
// 1. Get PKEY and remove that row from table
2018-12-08 13:55:43 +01:00
Pgsql : : Connection db_update_conn ;
auto dbconfig = m_database - > config ( ) ;
bool res = db_update_conn . connect ( dbconfig . getKeywords ( ) , dbconfig . getValues ( ) , false ) ;
if ( ! res ) {
return false ;
}
2018-04-08 09:02:22 +02:00
2018-12-08 13:55:43 +01:00
// First delete rows in table
QString delete_statement = createDeleteStatement ( ) ;
db_update_conn . query ( " BEGIN; " ) ;
for ( int current_row = row ; current_row < row + count ; + + current_row ) {
auto & & mapping = m_rowMapping [ static_cast < size_t > ( current_row ) ] ;
auto params = getPKeyParamsForRow ( mapping . rowKey ) ;
if ( ! params . empty ( ) ) {
// Execute DELETE
auto result = db_update_conn . queryParam ( delete_statement , params ) ;
}
}
db_update_conn . query ( " COMMIT; " ) ;
// Then from model
{
beginRemoveRows ( parent , row , row ) ;
SCOPE_EXIT { endRemoveRows ( ) ; } ;
for ( int current_row = row ; current_row < row + count ; + + current_row ) {
auto & & mapping = m_rowMapping [ static_cast < size_t > ( current_row ) ] ;
// if the row is in modified it can be removed from modified
if ( mapping . modified ) {
m_modifiedRowList . erase ( mapping . rowKey ) ;
2018-04-08 09:02:22 +02:00
}
2018-12-08 13:55:43 +01:00
/// \todo can it be pending? should be removed if it is.
2018-04-08 09:02:22 +02:00
}
2018-12-08 13:55:43 +01:00
// remove the rows from m_rowMapping
auto first = m_rowMapping . begin ( ) + row ;
m_rowMapping . erase ( first , first + count ) ;
2018-04-08 09:02:22 +02:00
}
2018-12-08 13:55:43 +01:00
return true ;
2018-04-08 09:02:22 +02:00
}