1 // This file is part of CppSheets.
2 //
3 // Copyright 2018 Patrick Flynn <patrick_dev2000@outlook.com>
4 //
5 // CppSheets is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // CppSheets is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with CppSheets. If not, see <https://www.gnu.org/licenses/>.
17 
18 #include "qgstableeditorwidget.h"
19 #include "qgsnumericformat.h"
20 #include <QStringList>
21 #include <QKeyEvent>
22 #include <QHeaderView>
23 #include <QMenu>
24 #include <QPlainTextEdit>
25 
QgsTableEditorWidget(QWidget * parent)26 QgsTableEditorWidget::QgsTableEditorWidget( QWidget *parent )
27   : QTableWidget( parent )
28 {
29   mHeaderMenu = new QMenu( this );
30   setColumnCount( 0 );
31   setRowCount( 0 );
32   connect( this, &QgsTableEditorWidget::cellChanged, this, [ = ]
33   {
34     if ( !mBlockSignals )
35       emit tableChanged();
36   } );
37 
38   horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
39   connect( horizontalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
40   {
41     const int column = horizontalHeader()->logicalIndexAt( point.x() );
42 
43     QSet< int > selectedColumns;
44     for ( const QModelIndex &index : selectedIndexes() )
45     {
46       selectedColumns.insert( index.column() );
47     }
48     int minCol = 0;
49     int maxCol = 0;
50     bool isConsecutive = collectConsecutiveColumnRange( selectedIndexes(), minCol, maxCol );
51 
52     // this is modeled off Libreoffice calc!
53     if ( selectedIndexes().count() == 1 )
54     {
55       // select whole column
56       selectColumn( column );
57       isConsecutive = true;
58     }
59     else if ( !selectedColumns.contains( column ) )
60     {
61       // select whole column
62       selectColumn( column );
63       isConsecutive = true;
64     }
65 
66     mHeaderMenu->clear();
67     if ( isConsecutive )
68     {
69       QAction *insertBefore = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %1 Columns Before" ).arg( selectedColumns.size() ) : tr( "Insert Column Before" ) );
70       connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsBefore );
71       QAction *insertAfter = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %1 Columns After" ).arg( selectedColumns.size() ) : tr( "Insert Column After" ) );
72       connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsAfter );
73     }
74     QAction *deleteSelected = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Delete %1 Columns" ).arg( selectedColumns.size() ) : tr( "Delete Column" ) );
75     connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteColumns );
76 
77     mHeaderMenu->popup( horizontalHeader()->mapToGlobal( point ) );
78   } );
79 
80   verticalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
81   connect( verticalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
82   {
83     const int row = verticalHeader()->logicalIndexAt( point.y() );
84 
85     QSet< int > selectedRows;
86     for ( const QModelIndex &index : selectedIndexes() )
87     {
88       selectedRows.insert( index.row() );
89     }
90     int minRow = 0;
91     int maxRow = 0;
92     bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
93 
94     // this is modeled off Libreoffice calc!
95     if ( selectedIndexes().count() == 1 )
96     {
97       // select whole row
98       selectRow( row );
99       isConsecutive = true;
100     }
101     else if ( !selectedRows.contains( row ) )
102     {
103       // select whole row
104       selectRow( row );
105       isConsecutive = true;
106     }
107 
108     mHeaderMenu->clear();
109     if ( isConsecutive )
110     {
111       QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %1 Rows Above" ).arg( selectedRows.size() ) : tr( "Insert Row Above" ) );
112       connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsAbove );
113       QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %1 Rows Below" ).arg( selectedRows.size() ) : tr( "Insert Row Below" ) );
114       connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsBelow );
115     }
116     QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Delete %1 Rows" ).arg( selectedRows.size() ) : tr( "Delete Row" ) );
117     connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteRows );
118 
119     mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
120   } );
121 
122 
123   QgsTableEditorDelegate *delegate = new QgsTableEditorDelegate( this );
124   connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex, this, &QgsTableEditorWidget::updateNumericFormatForIndex );
125   setItemDelegate( delegate );
126 
127 
128   connect( this, &QTableWidget::cellDoubleClicked, this, [ = ]
129   {
130     if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
131     {
132       d->setWeakEditorMode( false );
133     }
134   } );
135 
136   connect( selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsTableEditorWidget::activeCellChanged );
137 }
138 
~QgsTableEditorWidget()139 QgsTableEditorWidget::~QgsTableEditorWidget()
140 {
141   qDeleteAll( mNumericFormats );
142 }
143 
updateNumericFormatForIndex(const QModelIndex & index)144 void QgsTableEditorWidget::updateNumericFormatForIndex( const QModelIndex &index )
145 {
146   if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
147   {
148     if ( QgsNumericFormat *format = mNumericFormats.value( i ) )
149     {
150       i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(), QgsNumericFormatContext() ) );
151     }
152   }
153 }
154 
updateHeaders()155 void QgsTableEditorWidget::updateHeaders()
156 {
157   QStringList headers;
158   QStringList letters;
159 
160   QString first;
161   QString current;
162 
163   for ( char c = 'A'; c <= 'Z'; c++ )
164   {
165     letters.push_back( QString( c ) );
166   }
167 
168   int len = letters.length();
169   int index = 0;
170   int fIndex = 0;
171 
172   for ( int i = 0; i < 1000; i++ )
173   {
174     if ( index == len )
175     {
176       index = 0;
177 
178       first = letters.at( fIndex );
179       fIndex++;
180 
181       if ( fIndex == len )
182       {
183         fIndex = 0;
184       }
185     }
186 
187     current = first;
188     current += letters.at( index );
189     headers.push_back( current );
190     current.clear();
191 
192     index++;
193   }
194 
195   setHorizontalHeaderLabels( headers );
196 
197   headers.clear();
198   if ( mIncludeHeader )
199     headers << tr( "Header" );
200   for ( int i = 1; i <= 1000; i++ )
201   {
202     headers << QString::number( i );
203   }
204 
205   setVerticalHeaderLabels( headers );
206 }
207 
collectConsecutiveRowRange(const QModelIndexList & list,int & minRow,int & maxRow) const208 bool QgsTableEditorWidget::collectConsecutiveRowRange( const QModelIndexList &list, int &minRow, int &maxRow ) const
209 {
210   QSet< int > includedRows;
211   minRow = std::numeric_limits< int >::max();
212   maxRow = -1;
213   for ( const QModelIndex &index : list )
214   {
215     includedRows.insert( index.row() );
216     minRow = std::min( minRow, index.row() );
217     maxRow = std::max( maxRow, index.row() );
218   }
219 
220   // test that selection is consecutive rows
221   for ( int r = minRow + 1; r < maxRow; r++ )
222   {
223     if ( !includedRows.contains( r ) )
224       return false;
225   }
226   return true;
227 }
228 
collectConsecutiveColumnRange(const QModelIndexList & list,int & minColumn,int & maxColumn) const229 bool QgsTableEditorWidget::collectConsecutiveColumnRange( const QModelIndexList &list, int &minColumn, int &maxColumn ) const
230 {
231   QSet< int > includedColumns;
232   minColumn = std::numeric_limits< int >::max();
233   maxColumn = -1;
234   for ( const QModelIndex &index : list )
235   {
236     includedColumns.insert( index.column() );
237     minColumn = std::min( minColumn, index.column() );
238     maxColumn = std::max( maxColumn, index.column() );
239   }
240 
241   // test that selection is consecutive columns
242   for ( int r = minColumn + 1; r < maxColumn; r++ )
243   {
244     if ( !includedColumns.contains( r ) )
245       return false;
246   }
247   return true;
248 }
249 
collectUniqueRows(const QModelIndexList & list) const250 QList<int> QgsTableEditorWidget::collectUniqueRows( const QModelIndexList &list ) const
251 {
252   QList<int > res;
253   for ( const QModelIndex &index : list )
254   {
255     if ( !res.contains( index.row() ) )
256       res << index.row();
257   }
258   std::sort( res.begin(), res.end() );
259   return res;
260 }
261 
collectUniqueColumns(const QModelIndexList & list) const262 QList<int> QgsTableEditorWidget::collectUniqueColumns( const QModelIndexList &list ) const
263 {
264   QList<int > res;
265   for ( const QModelIndex &index : list )
266   {
267     if ( !res.contains( index.column() ) )
268       res << index.column();
269   }
270   std::sort( res.begin(), res.end() );
271   return res;
272 }
273 
keyPressEvent(QKeyEvent * event)274 void QgsTableEditorWidget::keyPressEvent( QKeyEvent *event )
275 {
276   switch ( event->key() )
277   {
278     case Qt::Key_Enter:
279     case Qt::Key_Return:
280     {
281       //Enter or return keys moves to next row
282       QTableWidget::keyPressEvent( event );
283       setCurrentCell( currentRow() + 1, currentColumn() );
284       break;
285     }
286 
287     case Qt::Key_Delete:
288     {
289       clearSelectedCells();
290       break;
291     }
292 
293     default:
294       QTableWidget::keyPressEvent( event );
295   }
296   if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
297   {
298     d->setWeakEditorMode( true );
299   }
300 }
301 
setTableContents(const QgsTableContents & contents)302 void QgsTableEditorWidget::setTableContents( const QgsTableContents &contents )
303 {
304   mBlockSignals++;
305   qDeleteAll( mNumericFormats );
306   mNumericFormats.clear();
307 
308   QgsNumericFormatContext numericContext;
309   int rowNumber = mIncludeHeader ? 1 : 0;
310   bool first = true;
311   setRowCount( contents.size() + rowNumber );
312   for ( const QgsTableRow &row : contents )
313   {
314     if ( first )
315     {
316       setColumnCount( row.size() );
317       first = false;
318     }
319 
320     int colNumber = 0;
321     for ( const QgsTableCell &col : row )
322     {
323       QTableWidgetItem *item = new QTableWidgetItem( col.content().value< QgsProperty >().isActive() ? col.content().value< QgsProperty >().asExpression() : col.content().toString() );
324       item->setData( CellContent, col.content() ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
325       item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
326       item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
327       item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
328       item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
329       item->setData( HorizontalAlignment, static_cast< int >( col.horizontalAlignment() ) );
330       item->setData( VerticalAlignment, static_cast< int >( col.verticalAlignment() ) );
331       item->setData( CellProperty, QVariant::fromValue( col.content().value< QgsProperty >() ) );
332 
333       if ( col.content().value< QgsProperty >().isActive() )
334         item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
335 
336       if ( auto *lNumericFormat = col.numericFormat() )
337       {
338         mNumericFormats.insert( item, lNumericFormat->clone() );
339         item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
340       }
341       setItem( rowNumber, colNumber, item );
342       colNumber++;
343     }
344     rowNumber++;
345   }
346 
347   mBlockSignals--;
348   updateHeaders();
349 
350   if ( mFirstSet )
351   {
352     resizeColumnsToContents();
353     resizeRowsToContents();
354     mFirstSet = false;
355   }
356   emit tableChanged();
357 }
358 
tableContents() const359 QgsTableContents QgsTableEditorWidget::tableContents() const
360 {
361   QgsTableContents items;
362   items.reserve( rowCount() );
363 
364   for ( int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
365   {
366     QgsTableRow row;
367     row.reserve( columnCount() );
368     for ( int c = 0; c < columnCount(); c++ )
369     {
370       QgsTableCell cell;
371       if ( QTableWidgetItem *i = item( r, c ) )
372       {
373         cell.setContent( i->data( CellProperty ).value< QgsProperty >().isActive() ? i->data( CellProperty ) : i->data( CellContent ) );
374         cell.setBackgroundColor( i->data( PresetBackgroundColorRole ).value< QColor >() );
375         cell.setTextFormat( i->data( TextFormat ).value< QgsTextFormat >() );
376         cell.setHorizontalAlignment( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) );
377         cell.setVerticalAlignment( static_cast< Qt::Alignment >( i->data( VerticalAlignment ).toInt() ) );
378 
379         if ( mNumericFormats.value( i ) )
380         {
381           cell.setNumericFormat( mNumericFormats.value( i )->clone() );
382         }
383       }
384       row.push_back( cell );
385     }
386     items.push_back( row );
387   }
388 
389   return items;
390 }
391 
setSelectionNumericFormat(QgsNumericFormat * format)392 void QgsTableEditorWidget::setSelectionNumericFormat( QgsNumericFormat *format )
393 {
394   bool changed = false;
395   mBlockSignals++;
396   std::unique_ptr< QgsNumericFormat > newFormat( format );
397   const QModelIndexList selection = selectedIndexes();
398   QgsNumericFormatContext numericContext;
399   for ( const QModelIndex &index : selection )
400   {
401     if ( index.row() == 0 && mIncludeHeader )
402       continue;
403 
404     QTableWidgetItem *i = item( index.row(), index.column() );
405     if ( !i )
406     {
407       i = new QTableWidgetItem();
408       setItem( index.row(), index.column(), i );
409     }
410     if ( !mNumericFormats.value( i ) && newFormat )
411     {
412       changed = true;
413       mNumericFormats.insert( i, newFormat->clone() );
414     }
415     else if ( mNumericFormats.value( i ) && !newFormat )
416     {
417       changed = true;
418       delete mNumericFormats.value( i );
419       mNumericFormats.remove( i );
420     }
421     else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
422     {
423       changed = true;
424       delete mNumericFormats.value( i );
425       mNumericFormats.insert( i, newFormat->clone() );
426     }
427     i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
428   }
429   mBlockSignals--;
430   if ( changed && !mBlockSignals )
431     emit tableChanged();
432 }
433 
selectionNumericFormat()434 QgsNumericFormat *QgsTableEditorWidget::selectionNumericFormat()
435 {
436   QgsNumericFormat *f = nullptr;
437   bool first = true;
438   const QModelIndexList selection = selectedIndexes();
439   for ( const QModelIndex &index : selection )
440   {
441     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
442     {
443       if ( first )
444       {
445         f = mNumericFormats.value( i );
446         first = false;
447       }
448       else if ( ( !f && !mNumericFormats.value( i ) )
449                 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
450         continue;
451       else
452       {
453         return nullptr;
454       }
455     }
456     else
457     {
458       return nullptr;
459     }
460   }
461   return f;
462 }
463 
hasMixedSelectionNumericFormat()464 bool QgsTableEditorWidget::hasMixedSelectionNumericFormat()
465 {
466   QgsNumericFormat *f = nullptr;
467   bool first = true;
468   const QModelIndexList selection = selectedIndexes();
469   for ( const QModelIndex &index : selection )
470   {
471     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
472     {
473       if ( first )
474       {
475         f = mNumericFormats.value( i );
476         first = false;
477       }
478       else if ( ( !f && !mNumericFormats.value( i ) )
479                 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
480         continue;
481       else
482       {
483         return true;
484       }
485     }
486     else if ( f )
487     {
488       return true;
489     }
490   }
491   return false;
492 }
493 
selectionForegroundColor()494 QColor QgsTableEditorWidget::selectionForegroundColor()
495 {
496   const QgsTextFormat f = selectionTextFormat();
497   return f.isValid() ? f.color() : QColor();
498 }
499 
selectionBackgroundColor()500 QColor QgsTableEditorWidget::selectionBackgroundColor()
501 {
502   QColor c;
503   bool first = true;
504   const QModelIndexList selection = selectedIndexes();
505   for ( const QModelIndex &index : selection )
506   {
507     QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value< QColor >() : QColor();
508     if ( first )
509     {
510       c = indexColor;
511       first = false;
512     }
513     else if ( indexColor == c )
514       continue;
515     else
516     {
517       return QColor();
518     }
519   }
520   return c;
521 }
522 
selectionHorizontalAlignment()523 Qt::Alignment QgsTableEditorWidget::selectionHorizontalAlignment()
524 {
525   Qt::Alignment alignment = Qt::AlignLeft;
526   bool first = true;
527   const QModelIndexList selection = selectedIndexes();
528   for ( const QModelIndex &index : selection )
529   {
530     Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, HorizontalAlignment ).toInt() );
531     if ( first )
532     {
533       alignment = cellAlign;
534       first = false;
535     }
536     else if ( cellAlign == alignment )
537       continue;
538     else
539     {
540       return Qt::AlignLeft | Qt::AlignTop;
541     }
542   }
543   return alignment;
544 }
545 
selectionVerticalAlignment()546 Qt::Alignment QgsTableEditorWidget::selectionVerticalAlignment()
547 {
548   Qt::Alignment alignment = Qt::AlignVCenter;
549   bool first = true;
550   const QModelIndexList selection = selectedIndexes();
551   for ( const QModelIndex &index : selection )
552   {
553     Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, VerticalAlignment ).toInt() );
554     if ( first )
555     {
556       alignment = cellAlign;
557       first = false;
558     }
559     else if ( cellAlign == alignment )
560       continue;
561     else
562     {
563       return Qt::AlignLeft | Qt::AlignTop;
564     }
565   }
566   return alignment;
567 }
568 
selectionCellProperty()569 QgsProperty QgsTableEditorWidget::selectionCellProperty()
570 {
571   QgsProperty property;
572   bool first = true;
573   const QModelIndexList selection = selectedIndexes();
574   for ( const QModelIndex &index : selection )
575   {
576     const QgsProperty cellProperty = model()->data( index, CellProperty ).value< QgsProperty >();
577     if ( first )
578     {
579       property = cellProperty;
580       first = false;
581     }
582     else if ( cellProperty == property )
583       continue;
584     else
585     {
586       return QgsProperty();
587     }
588   }
589   return property;
590 }
591 
selectionTextFormat()592 QgsTextFormat QgsTableEditorWidget::selectionTextFormat()
593 {
594   QgsTextFormat format;
595   bool first = true;
596   const QModelIndexList selection = selectedIndexes();
597   for ( const QModelIndex &index : selection )
598   {
599     if ( !model()->data( index, TextFormat ).isValid() )
600       return QgsTextFormat();
601 
602     QgsTextFormat cellFormat = model()->data( index, TextFormat ).value< QgsTextFormat >();
603     if ( first )
604     {
605       format = cellFormat;
606       first = false;
607     }
608     else if ( cellFormat == format )
609       continue;
610     else
611       return QgsTextFormat();
612   }
613   return format;
614 }
615 
selectionRowHeight()616 double QgsTableEditorWidget::selectionRowHeight()
617 {
618   double height = 0;
619   bool first = true;
620   const QModelIndexList selection = selectedIndexes();
621   for ( const QModelIndex &index : selection )
622   {
623     double thisHeight = tableRowHeight( index.row() );
624     if ( first )
625       height = thisHeight;
626     else if ( thisHeight != height )
627     {
628       return -1;
629     }
630     first = false;
631   }
632   return height;
633 }
634 
selectionColumnWidth()635 double QgsTableEditorWidget::selectionColumnWidth()
636 {
637   double width = 0;
638   bool first = true;
639   const QModelIndexList selection = selectedIndexes();
640   for ( const QModelIndex &index : selection )
641   {
642     double thisWidth = tableColumnWidth( index.column() );
643     if ( first )
644       width = thisWidth;
645     else if ( thisWidth != width )
646     {
647       return -1;
648     }
649     first = false;
650   }
651   return width;
652 }
653 
tableRowHeight(int row)654 double QgsTableEditorWidget::tableRowHeight( int row )
655 {
656   double height = 0;
657   for ( int col = 0; col < columnCount(); ++col )
658   {
659     double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
660     height = std::max( thisHeight, height );
661   }
662   return height;
663 }
664 
tableColumnWidth(int column)665 double QgsTableEditorWidget::tableColumnWidth( int column )
666 {
667   double width = 0;
668   for ( int row = 0; row < rowCount(); ++row )
669   {
670     double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
671     width = std::max( thisWidth, width );
672   }
673   return width;
674 }
675 
setTableRowHeight(int row,double height)676 void QgsTableEditorWidget::setTableRowHeight( int row, double height )
677 {
678   if ( row == 0 && mIncludeHeader )
679     return;
680 
681   bool changed = false;
682   mBlockSignals++;
683 
684   for ( int col = 0; col < columnCount(); ++col )
685   {
686     if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
687     {
688       if ( i->data( RowHeight ).toDouble() != height )
689       {
690         i->setData( RowHeight, height );
691         changed = true;
692       }
693     }
694     else
695     {
696       QTableWidgetItem *newItem = new QTableWidgetItem();
697       newItem->setData( RowHeight, height );
698       setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
699       changed = true;
700     }
701   }
702 
703   mBlockSignals--;
704   if ( changed && !mBlockSignals )
705     emit tableChanged();
706 }
707 
setTableColumnWidth(int col,double width)708 void QgsTableEditorWidget::setTableColumnWidth( int col, double width )
709 {
710   bool changed = false;
711   mBlockSignals++;
712   for ( int row = 0; row < rowCount(); ++row )
713   {
714     if ( QTableWidgetItem *i = item( row, col ) )
715     {
716       if ( i->data( ColumnWidth ).toDouble() != width )
717       {
718         i->setData( ColumnWidth, width );
719         changed = true;
720       }
721     }
722     else
723     {
724       QTableWidgetItem *newItem = new QTableWidgetItem();
725       newItem->setData( ColumnWidth, width );
726       setItem( row, col, newItem );
727       changed = true;
728     }
729   }
730   mBlockSignals--;
731   if ( changed && !mBlockSignals )
732     emit tableChanged();
733 }
734 
rowsAssociatedWithSelection()735 QList<int> QgsTableEditorWidget::rowsAssociatedWithSelection()
736 {
737   return collectUniqueRows( selectedIndexes() );
738 }
739 
columnsAssociatedWithSelection()740 QList<int> QgsTableEditorWidget::columnsAssociatedWithSelection()
741 {
742   return collectUniqueColumns( selectedIndexes() );
743 }
744 
tableHeaders() const745 QVariantList QgsTableEditorWidget::tableHeaders() const
746 {
747   if ( !mIncludeHeader )
748     return QVariantList();
749 
750   QVariantList res;
751   res.reserve( columnCount() );
752   for ( int col = 0; col < columnCount(); ++col )
753   {
754     if ( QTableWidgetItem *i = item( 0, col ) )
755     {
756       res << i->data( CellContent );
757     }
758     else
759     {
760       res << QVariant();
761     }
762   }
763   return res;
764 }
765 
isHeaderCellSelected()766 bool QgsTableEditorWidget::isHeaderCellSelected()
767 {
768   if ( !mIncludeHeader )
769     return false;
770 
771   return collectUniqueRows( selectedIndexes() ).contains( 0 );
772 }
773 
insertRowsBelow()774 void QgsTableEditorWidget::insertRowsBelow()
775 {
776   if ( rowCount() == 0 )
777   {
778     insertRow( 0 );
779     return;
780   }
781 
782   int minRow = 0;
783   int maxRow = 0;
784   if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
785     return;
786 
787   const int rowsToInsert = maxRow - minRow + 1;
788   for ( int i = 0; i < rowsToInsert; ++i )
789     insertRow( maxRow + 1 );
790 
791   updateHeaders();
792   if ( !mBlockSignals )
793     emit tableChanged();
794 }
795 
insertRowsAbove()796 void QgsTableEditorWidget::insertRowsAbove()
797 {
798   if ( rowCount() == 0 )
799   {
800     insertRow( 0 );
801     return;
802   }
803 
804   int minRow = 0;
805   int maxRow = 0;
806   if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
807     return;
808 
809   const int rowsToInsert = maxRow - minRow + 1;
810   for ( int i = 0; i < rowsToInsert; ++i )
811     insertRow( minRow );
812 
813   updateHeaders();
814   if ( !mBlockSignals )
815     emit tableChanged();
816 }
817 
insertColumnsBefore()818 void QgsTableEditorWidget::insertColumnsBefore()
819 {
820   if ( columnCount() == 0 )
821   {
822     insertColumn( 0 );
823     return;
824   }
825 
826   int minColumn = 0;
827   int maxColumn = 0;
828   if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
829     return;
830 
831   const int columnsToInsert = maxColumn - minColumn + 1;
832   for ( int i = 0; i < columnsToInsert; ++i )
833     insertColumn( minColumn );
834 
835   updateHeaders();
836   if ( !mBlockSignals )
837     emit tableChanged();
838 }
839 
insertColumnsAfter()840 void QgsTableEditorWidget::insertColumnsAfter()
841 {
842   if ( columnCount() == 0 )
843   {
844     insertColumn( 0 );
845     return;
846   }
847 
848   int minColumn = 0;
849   int maxColumn = 0;
850   if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
851     return;
852 
853   const int columnsToInsert = maxColumn - minColumn + 1;
854   for ( int i = 0; i < columnsToInsert; ++i )
855     insertColumn( maxColumn + 1 );
856 
857   updateHeaders();
858   if ( !mBlockSignals )
859     emit tableChanged();
860 }
861 
deleteRows()862 void QgsTableEditorWidget::deleteRows()
863 {
864   const QList< int > rows = rowsAssociatedWithSelection();
865   if ( rows.empty() )
866     return;
867 
868   bool changed = false;
869   for ( int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
870   {
871     removeRow( rows.at( i ) );
872     changed = true;
873   }
874   updateHeaders();
875   if ( changed &&  !mBlockSignals )
876     emit tableChanged();
877 }
878 
deleteColumns()879 void QgsTableEditorWidget::deleteColumns()
880 {
881   const QList< int > columns = columnsAssociatedWithSelection();
882   if ( columns.empty() )
883     return;
884 
885   bool changed = false;
886   for ( int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
887   {
888     removeColumn( columns.at( i ) );
889     changed = true;
890   }
891   updateHeaders();
892   if ( !mBlockSignals && changed )
893     emit tableChanged();
894 }
895 
expandRowSelection()896 void QgsTableEditorWidget::expandRowSelection()
897 {
898   const QModelIndexList s = selectedIndexes();
899   for ( const QModelIndex &index : s )
900   {
901     selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
902   }
903 }
904 
expandColumnSelection()905 void QgsTableEditorWidget::expandColumnSelection()
906 {
907   const QModelIndexList s = selectedIndexes();
908   for ( const QModelIndex &index : s )
909   {
910     selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
911   }
912 }
913 
clearSelectedCells()914 void QgsTableEditorWidget::clearSelectedCells()
915 {
916   const QModelIndexList selection = selectedIndexes();
917   bool changed = false;
918   mBlockSignals++;
919   for ( const QModelIndex &index : selection )
920   {
921     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
922     {
923       i->setText( QString() );
924       i->setData( CellContent, QVariant() );
925       changed = true;
926     }
927   }
928   mBlockSignals--;
929   if ( changed && !mBlockSignals )
930     emit tableChanged();
931 }
932 
setSelectionForegroundColor(const QColor & color)933 void QgsTableEditorWidget::setSelectionForegroundColor( const QColor &color )
934 {
935   const QModelIndexList selection = selectedIndexes();
936   bool changed = false;
937   mBlockSignals++;
938   for ( const QModelIndex &index : selection )
939   {
940     if ( index.row() == 0 && mIncludeHeader )
941       continue;
942 
943     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
944     {
945       if ( i->data( Qt::ForegroundRole ).value< QColor >() != color )
946       {
947         i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
948         QgsTextFormat f = i->data( TextFormat ).value< QgsTextFormat >();
949         f.setColor( color );
950         i->setData( TextFormat, QVariant::fromValue( f ) );
951         changed = true;
952       }
953     }
954     else
955     {
956       QTableWidgetItem *newItem = new QTableWidgetItem();
957       newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
958       QgsTextFormat f;
959       f.setColor( color );
960       newItem->setData( TextFormat, QVariant::fromValue( f ) );
961       setItem( index.row(), index.column(), newItem );
962       changed = true;
963     }
964   }
965   mBlockSignals--;
966   if ( changed && !mBlockSignals )
967     emit tableChanged();
968 }
969 
setSelectionBackgroundColor(const QColor & color)970 void QgsTableEditorWidget::setSelectionBackgroundColor( const QColor &color )
971 {
972   const QModelIndexList selection = selectedIndexes();
973   bool changed = false;
974   mBlockSignals++;
975   for ( const QModelIndex &index : selection )
976   {
977     if ( index.row() == 0 && mIncludeHeader )
978       continue;
979 
980     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
981     {
982       if ( i->data( PresetBackgroundColorRole ).value< QColor >() != color )
983       {
984         i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
985         i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
986         changed = true;
987       }
988     }
989     else
990     {
991       QTableWidgetItem *newItem = new QTableWidgetItem();
992       newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
993       newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
994       setItem( index.row(), index.column(), newItem );
995       changed = true;
996     }
997   }
998   mBlockSignals--;
999   if ( changed && !mBlockSignals )
1000     emit tableChanged();
1001 }
1002 
setSelectionHorizontalAlignment(Qt::Alignment alignment)1003 void QgsTableEditorWidget::setSelectionHorizontalAlignment( Qt::Alignment alignment )
1004 {
1005   const QModelIndexList selection = selectedIndexes();
1006   bool changed = false;
1007   mBlockSignals++;
1008   for ( const QModelIndex &index : selection )
1009   {
1010     if ( index.row() == 0 && mIncludeHeader )
1011       continue;
1012 
1013     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1014     {
1015       if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1016       {
1017         i->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1018         changed = true;
1019       }
1020     }
1021     else
1022     {
1023       QTableWidgetItem *newItem = new QTableWidgetItem();
1024       newItem->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1025       setItem( index.row(), index.column(), newItem );
1026       changed = true;
1027     }
1028   }
1029   mBlockSignals--;
1030   if ( changed && !mBlockSignals )
1031     emit tableChanged();
1032 }
1033 
setSelectionVerticalAlignment(Qt::Alignment alignment)1034 void QgsTableEditorWidget::setSelectionVerticalAlignment( Qt::Alignment alignment )
1035 {
1036   const QModelIndexList selection = selectedIndexes();
1037   bool changed = false;
1038   mBlockSignals++;
1039   for ( const QModelIndex &index : selection )
1040   {
1041     if ( index.row() == 0 && mIncludeHeader )
1042       continue;
1043 
1044     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1045     {
1046       if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1047       {
1048         i->setData( VerticalAlignment, static_cast< int >( alignment ) );
1049         changed = true;
1050       }
1051     }
1052     else
1053     {
1054       QTableWidgetItem *newItem = new QTableWidgetItem();
1055       newItem->setData( VerticalAlignment, static_cast< int >( alignment ) );
1056       setItem( index.row(), index.column(), newItem );
1057       changed = true;
1058     }
1059   }
1060   mBlockSignals--;
1061   if ( changed && !mBlockSignals )
1062     emit tableChanged();
1063 }
1064 
setSelectionCellProperty(const QgsProperty & property)1065 void QgsTableEditorWidget::setSelectionCellProperty( const QgsProperty &property )
1066 {
1067   const QModelIndexList selection = selectedIndexes();
1068   bool changed = false;
1069   mBlockSignals++;
1070   for ( const QModelIndex &index : selection )
1071   {
1072     if ( index.row() == 0 && mIncludeHeader )
1073       continue;
1074 
1075     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1076     {
1077       if ( i->data( CellProperty ).value< QgsProperty >() != property )
1078       {
1079         if ( property.isActive() )
1080         {
1081           i->setData( CellProperty, QVariant::fromValue( property ) );
1082           i->setText( property.asExpression() );
1083           i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1084         }
1085         else
1086         {
1087           i->setData( CellProperty, QVariant() );
1088           i->setText( QString() );
1089           i->setFlags( i->flags() | Qt::ItemIsEditable );
1090         }
1091         changed = true;
1092       }
1093     }
1094     else
1095     {
1096       QTableWidgetItem *newItem = new QTableWidgetItem( property.asExpression() );
1097       if ( property.isActive() )
1098       {
1099         newItem->setData( CellProperty,  QVariant::fromValue( property ) );
1100         newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1101       }
1102       else
1103       {
1104         newItem->setData( CellProperty, QVariant() );
1105         newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1106       }
1107       setItem( index.row(), index.column(), newItem );
1108       changed = true;
1109     }
1110   }
1111   mBlockSignals--;
1112   if ( changed && !mBlockSignals )
1113     emit tableChanged();
1114 }
1115 
setSelectionTextFormat(const QgsTextFormat & format)1116 void QgsTableEditorWidget::setSelectionTextFormat( const QgsTextFormat &format )
1117 {
1118   const QModelIndexList selection = selectedIndexes();
1119   bool changed = false;
1120   mBlockSignals++;
1121   for ( const QModelIndex &index : selection )
1122   {
1123     if ( index.row() == 0 && mIncludeHeader )
1124       continue;
1125 
1126     if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1127     {
1128       i->setData( TextFormat, QVariant::fromValue( format ) );
1129       i->setData( Qt::ForegroundRole, format.color() );
1130       changed = true;
1131     }
1132     else
1133     {
1134       QTableWidgetItem *newItem = new QTableWidgetItem();
1135       newItem->setData( TextFormat, QVariant::fromValue( format ) );
1136       newItem->setData( Qt::ForegroundRole, format.color() );
1137       setItem( index.row(), index.column(), newItem );
1138       changed = true;
1139     }
1140   }
1141   mBlockSignals--;
1142   if ( changed && !mBlockSignals )
1143     emit tableChanged();
1144 }
1145 
setSelectionRowHeight(double height)1146 void QgsTableEditorWidget::setSelectionRowHeight( double height )
1147 {
1148   bool changed = false;
1149   mBlockSignals++;
1150   const QList< int > rows = rowsAssociatedWithSelection();
1151   for ( int row : rows )
1152   {
1153     if ( row == 0 && mIncludeHeader )
1154       continue;
1155 
1156     for ( int col = 0; col < columnCount(); ++col )
1157     {
1158       if ( QTableWidgetItem *i = item( row, col ) )
1159       {
1160         if ( i->data( RowHeight ).toDouble() != height )
1161         {
1162           i->setData( RowHeight, height );
1163           changed = true;
1164         }
1165       }
1166       else
1167       {
1168         QTableWidgetItem *newItem = new QTableWidgetItem();
1169         newItem->setData( RowHeight, height );
1170         setItem( row, col, newItem );
1171         changed = true;
1172       }
1173     }
1174   }
1175   mBlockSignals--;
1176   if ( changed && !mBlockSignals )
1177     emit tableChanged();
1178 }
1179 
setSelectionColumnWidth(double width)1180 void QgsTableEditorWidget::setSelectionColumnWidth( double width )
1181 {
1182   bool changed = false;
1183   mBlockSignals++;
1184   const QList< int > cols = columnsAssociatedWithSelection();
1185   for ( int col : cols )
1186   {
1187     for ( int row = 0; row < rowCount(); ++row )
1188     {
1189       if ( QTableWidgetItem *i = item( row, col ) )
1190       {
1191         if ( i->data( ColumnWidth ).toDouble() != width )
1192         {
1193           i->setData( ColumnWidth, width );
1194           changed = true;
1195         }
1196       }
1197       else
1198       {
1199         QTableWidgetItem *newItem = new QTableWidgetItem();
1200         newItem->setData( ColumnWidth, width );
1201         setItem( row, col, newItem );
1202         changed = true;
1203       }
1204     }
1205   }
1206   mBlockSignals--;
1207   if ( changed && !mBlockSignals )
1208     emit tableChanged();
1209 }
1210 
setIncludeTableHeader(bool included)1211 void QgsTableEditorWidget::setIncludeTableHeader( bool included )
1212 {
1213   if ( included == mIncludeHeader )
1214     return;
1215 
1216   mIncludeHeader = included;
1217 
1218   if ( mIncludeHeader )
1219     insertRow( 0 );
1220   else
1221     removeRow( 0 );
1222   updateHeaders();
1223 }
1224 
setTableHeaders(const QVariantList & headers)1225 void QgsTableEditorWidget::setTableHeaders( const QVariantList &headers )
1226 {
1227   if ( !mIncludeHeader )
1228     return;
1229 
1230   mBlockSignals++;
1231 
1232   for ( int col = 0; col < columnCount(); ++col )
1233   {
1234     if ( QTableWidgetItem *i = item( 0, col ) )
1235     {
1236       i->setText( headers.value( col ).toString() );
1237       i->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1238     }
1239     else
1240     {
1241       QTableWidgetItem *item = new QTableWidgetItem( headers.value( col ).toString() );
1242       item->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1243       setItem( 0, col, item );
1244     }
1245   }
1246   mBlockSignals--;
1247 }
1248 
1249 /// @cond PRIVATE
1250 
QgsTableEditorTextEdit(QWidget * parent)1251 QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1252   : QPlainTextEdit( parent )
1253 {
1254   // narrower default margins
1255   document()->setDocumentMargin( document()->documentMargin() / 2 );
1256 
1257   connect( this, &QPlainTextEdit::textChanged, this, &QgsTableEditorTextEdit::resizeToContents );
1258   updateMinimumSize();
1259 }
1260 
keyPressEvent(QKeyEvent * event)1261 void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1262 {
1263   switch ( event->key() )
1264   {
1265     case Qt::Key_Enter:
1266     case Qt::Key_Return:
1267     {
1268       if ( event->modifiers() & Qt::ControlModifier )
1269       {
1270         // ctrl+enter inserts a line break
1271         insertPlainText( QString( '\n' ) );
1272         resizeToContents();
1273       }
1274       else
1275       {
1276         // closes editor
1277         event->ignore();
1278       }
1279       break;
1280     }
1281 
1282     case Qt::Key_Right:
1283     case Qt::Key_Left:
1284     case Qt::Key_Up:
1285     case Qt::Key_Down:
1286     {
1287       if ( mWeakEditorMode )
1288       {
1289         // close editor and defer to table
1290         event->ignore();
1291       }
1292       else
1293       {
1294         QPlainTextEdit::keyPressEvent( event );
1295       }
1296       break;
1297     }
1298 
1299     case Qt::Key_Tab:
1300     {
1301       if ( event->modifiers() & Qt::ControlModifier )
1302       {
1303         // if tab is pressed then defer to table, unless ctrl modifier is also held
1304         // (emulate spreadsheet behavior)
1305         insertPlainText( QString( '\t' ) );
1306         resizeToContents();
1307       }
1308       else
1309       {
1310         event->ignore();
1311       }
1312       break;
1313     }
1314 
1315     default:
1316       QPlainTextEdit::keyPressEvent( event );
1317   }
1318 }
1319 
updateMinimumSize()1320 void QgsTableEditorTextEdit::updateMinimumSize()
1321 {
1322   const double tm = document()->documentMargin();
1323   const QMargins cm = contentsMargins();
1324   const int width = tm * 2 + cm.left() + cm.right() + 30;
1325   const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1326   QStyleOptionFrame opt;
1327   initStyleOption( &opt );
1328   const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ), this );
1329   setMinimumWidth( sizeFromContent.width() );
1330   setMinimumHeight( sizeFromContent.height() );
1331 }
1332 
setWeakEditorMode(bool weakEditorMode)1333 void QgsTableEditorTextEdit::setWeakEditorMode( bool weakEditorMode )
1334 {
1335   mWeakEditorMode = weakEditorMode;
1336 }
1337 
resizeToContents()1338 void QgsTableEditorTextEdit::resizeToContents()
1339 {
1340   int oldWidth = width();
1341   int oldHeight = height();
1342   if ( mOriginalWidth == -1 )
1343     mOriginalWidth = oldWidth;
1344   if ( mOriginalHeight == -1 )
1345     mOriginalHeight = oldHeight;
1346 
1347   if ( QWidget *parent = parentWidget() )
1348   {
1349     QPoint position = pos();
1350     QFontMetrics fm( font() );
1351 
1352     const QStringList lines = toPlainText().split( '\n' );
1353     int maxTextLineWidth = 0;
1354     int totalTextHeight = 0;
1355     for ( const QString &line : lines )
1356     {
1357       const QRect bounds = fontMetrics().boundingRect( line );
1358       maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1359       totalTextHeight += fm.height();
1360     }
1361 
1362     int hintWidth = minimumWidth() + maxTextLineWidth;
1363     int hintHeight = minimumHeight() + totalTextHeight;
1364     int parentWidth = parent->width();
1365     int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1366     int maxHeight = parent->height() - position.y();
1367     int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1368     int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1369 
1370     if ( mWidgetOwnsGeometry )
1371     {
1372       setMaximumWidth( newWidth );
1373       setMaximumHeight( newHeight );
1374     }
1375     if ( isRightToLeft() )
1376       move( position.x() - newWidth + oldWidth, position.y() );
1377     resize( newWidth, newHeight );
1378   }
1379 }
1380 
changeEvent(QEvent * e)1381 void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1382 {
1383   switch ( e->type() )
1384   {
1385     case QEvent::FontChange:
1386     case QEvent::StyleChange:
1387     case QEvent::ContentsRectChange:
1388       updateMinimumSize();
1389       break;
1390     default:
1391       break;
1392   }
1393   QPlainTextEdit::changeEvent( e );
1394 }
1395 
QgsTableEditorDelegate(QObject * parent)1396 QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1397   : QStyledItemDelegate( parent )
1398 {
1399 
1400 }
1401 
setWeakEditorMode(bool weakEditorMode)1402 void QgsTableEditorDelegate::setWeakEditorMode( bool weakEditorMode )
1403 {
1404   mWeakEditorMode = weakEditorMode;
1405 }
1406 
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex &) const1407 QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const
1408 {
1409   QgsTableEditorTextEdit *w = new QgsTableEditorTextEdit( parent );
1410   w->setWeakEditorMode( mWeakEditorMode );
1411 
1412   if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame, 0, w ) )
1413     w->setFrameShape( QFrame::NoFrame );
1414   if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected, 0, w ) )
1415     w->setWidgetOwnsGeometry( true );
1416 
1417   return w;
1418 }
1419 
setEditorData(QWidget * editor,const QModelIndex & index) const1420 void QgsTableEditorDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
1421 {
1422   QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1423   if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1424   {
1425     if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1426     {
1427       lineEdit->setPlainText( value.toString() );
1428       lineEdit->selectAll();
1429     }
1430   }
1431   mLastIndex = index;
1432 }
1433 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const1434 void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
1435 {
1436   if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1437   {
1438     const QString text = lineEdit->toPlainText();
1439     if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value< QgsProperty >().isActive() )
1440     {
1441       model->setData( index, text, QgsTableEditorWidget::CellContent );
1442       model->setData( index, text, Qt::DisplayRole );
1443       emit updateNumericFormatForIndex( index );
1444     }
1445   }
1446 }
1447 
1448 
1449 ///@endcond
1450 
1451