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