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