24#include <QPlainTextEdit>
27 : QTableWidget( parent )
29 mHeaderMenu =
new QMenu(
this );
30 mCellMenu =
new QMenu(
this );
33 connect(
this, &QgsTableEditorWidget::cellChanged,
this, [ = ]
39 setContextMenuPolicy( Qt::CustomContextMenu );
40 connect(
this, &QWidget::customContextMenuRequested,
this, [ = ](
const QPoint & point )
46 QAction *mergeCells = mCellMenu->addAction( tr(
"Merge Selected Cells" ) );
51 QAction *splitCells = mCellMenu->addAction( tr(
"Split Selected Cells" ) );
54 if ( !mCellMenu->isEmpty() )
55 mCellMenu->popup( mapToGlobal( point ) );
59 horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
60 connect( horizontalHeader(), &QWidget::customContextMenuRequested,
this, [ = ](
const QPoint & point )
62 const int column = horizontalHeader()->logicalIndexAt( point.x() );
64 QSet< int > selectedColumns;
65 for (
const QModelIndex &index : selectedIndexes() )
67 selectedColumns.insert( index.column() );
71 bool isConsecutive = collectConsecutiveColumnRange( selectedIndexes(), minCol, maxCol );
74 if ( selectedIndexes().count() == 1 )
77 selectColumn( column );
80 else if ( !selectedColumns.contains( column ) )
83 selectColumn( column );
90 QAction *insertBefore = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Insert %n Column(s) Before",
nullptr, selectedColumns.size() ) : tr(
"Insert Column Before" ) );
92 QAction *insertAfter = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Insert %n Column(s) After",
nullptr, selectedColumns.size() ) : tr(
"Insert Column After" ) );
95 QAction *deleteSelected = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Delete %n Column(s)",
nullptr, selectedColumns.size() ) : tr(
"Delete Column" ) );
98 mHeaderMenu->popup( horizontalHeader()->mapToGlobal( point ) );
101 verticalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
102 connect( verticalHeader(), &QWidget::customContextMenuRequested,
this, [ = ](
const QPoint & point )
104 const int row = verticalHeader()->logicalIndexAt( point.y() );
106 QSet< int > selectedRows;
107 for (
const QModelIndex &index : selectedIndexes() )
109 selectedRows.insert( index.row() );
113 bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
116 if ( selectedIndexes().count() == 1 )
120 isConsecutive =
true;
122 else if ( !selectedRows.contains( row ) )
126 isConsecutive =
true;
129 mHeaderMenu->clear();
132 QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Above",
nullptr, selectedRows.size() ) : tr(
"Insert Row Above" ) );
134 QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Below",
nullptr, selectedRows.size() ) : tr(
"Insert Row Below" ) );
137 QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Delete %n Row(s)",
nullptr, selectedRows.size() ) : tr(
"Delete Row" ) );
140 mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
145 connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex,
this, &QgsTableEditorWidget::updateNumericFormatForIndex );
146 setItemDelegate( delegate );
149 connect(
this, &QTableWidget::cellDoubleClicked,
this, [ = ]
153 d->setWeakEditorMode(
false );
162 qDeleteAll( mNumericFormats );
165void QgsTableEditorWidget::updateNumericFormatForIndex(
const QModelIndex &index )
167 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
171 i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(),
QgsNumericFormatContext() ) );
176void QgsTableEditorWidget::updateHeaders()
184 for (
char c =
'A';
c <=
'Z';
c++ )
186 letters.push_back( QString(
c ) );
189 int len = letters.length();
193 for (
int i = 0; i < 1000; i++ )
199 first = letters.at( fIndex );
209 current += letters.at( index );
210 headers.push_back( current );
216 setHorizontalHeaderLabels( headers );
219 if ( mIncludeHeader )
220 headers << tr(
"Header" );
221 for (
int i = 1; i <= 1000; i++ )
223 headers << QString::number( i );
226 setVerticalHeaderLabels( headers );
229bool QgsTableEditorWidget::collectConsecutiveRowRange(
const QModelIndexList &list,
int &minRow,
int &maxRow )
const
231 QSet< int > includedRows;
232 minRow = std::numeric_limits< int >::max();
234 for (
const QModelIndex &index : list )
236 includedRows.insert( index.row() );
237 minRow = std::min( minRow, index.row() );
238 maxRow = std::max( maxRow, index.row() );
242 for (
int r = minRow + 1; r < maxRow; r++ )
244 if ( !includedRows.contains( r ) )
250bool QgsTableEditorWidget::collectConsecutiveColumnRange(
const QModelIndexList &list,
int &minColumn,
int &maxColumn )
const
252 QSet< int > includedColumns;
253 minColumn = std::numeric_limits< int >::max();
255 for (
const QModelIndex &index : list )
257 includedColumns.insert( index.column() );
258 minColumn = std::min( minColumn, index.column() );
259 maxColumn = std::max( maxColumn, index.column() );
263 for (
int r = minColumn + 1; r < maxColumn; r++ )
265 if ( !includedColumns.contains( r ) )
271QList<int> QgsTableEditorWidget::collectUniqueRows(
const QModelIndexList &list )
const
274 for (
const QModelIndex &index : list )
276 if ( !res.contains( index.row() ) )
279 std::sort( res.begin(), res.end() );
283QList<int> QgsTableEditorWidget::collectUniqueColumns(
const QModelIndexList &list )
const
286 for (
const QModelIndex &index : list )
288 if ( !res.contains( index.column() ) )
289 res << index.column();
291 std::sort( res.begin(), res.end() );
295bool QgsTableEditorWidget::isRectangularSelection(
const QModelIndexList &list )
const
304 QSet< QPair< int, int > > selectedSet;
305 for (
const QModelIndex &index : list )
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() ) );
319 if ( list.size() != ( maxRow - minRow + 1 ) * ( maxCol - minCol + 1 ) )
323 QSet< QPair< int, int > > expectedSet;
324 for (
int row = minRow; row <= maxRow; ++row )
326 for (
int col = minCol; col <= maxCol; ++col )
328 expectedSet.insert( qMakePair( row, col ) );
331 return selectedSet == expectedSet;
334bool QgsTableEditorWidget::hasMergedCells(
const QModelIndexList &list )
const
336 for (
const QModelIndex &index : list )
338 if ( rowSpan( index.row(), index.column() ) > 1
339 || columnSpan( index.row(), index.column() ) > 1 )
347 switch ( event->key() )
353 QTableWidget::keyPressEvent( event );
354 setCurrentCell( currentRow() + 1, currentColumn() );
365 QTableWidget::keyPressEvent( event );
369 d->setWeakEditorMode(
true );
376 qDeleteAll( mNumericFormats );
377 mNumericFormats.clear();
380 int rowNumber = mIncludeHeader ? 1 : 0;
382 setRowCount( contents.size() + rowNumber );
387 setColumnCount( row.size() );
395 item->setData( CellContent, col.content() );
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 >() ) );
404 if ( col.content().value<
QgsProperty >().isActive() )
405 item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
407 if (
auto *lNumericFormat = col.numericFormat() )
409 mNumericFormats.insert( item, lNumericFormat->clone() );
410 item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
412 setItem( rowNumber, colNumber, item );
414 if ( col.rowSpan() > 1 || col.columnSpan() > 1 )
415 setSpan( rowNumber, colNumber, col.rowSpan(), col.columnSpan() );
427 resizeColumnsToContents();
428 resizeRowsToContents();
437 items.reserve( rowCount() );
439 QSet< QPair< int, int > > spannedCells;
440 for (
int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
443 row.reserve( columnCount() );
444 for (
int c = 0;
c < columnCount();
c++ )
447 if ( QTableWidgetItem *i = item( r,
c ) )
449 cell.
setContent( i->data( CellProperty ).value<
QgsProperty >().isActive() ? i->data( CellProperty ) : i->data( CellContent ) );
453 cell.
setVerticalAlignment(
static_cast< Qt::Alignment
>( i->data( VerticalAlignment ).toInt() ) );
456 if ( !spannedCells.contains( qMakePair( r,
c ) ) )
458 const int rowsSpan = rowSpan( r,
c );
459 const int colsSpan = columnSpan( r,
c );
460 for (
int spannedRow = r; spannedRow < r + rowsSpan; ++spannedRow )
462 for (
int spannedCol =
c; spannedCol <
c + colsSpan; ++spannedCol )
464 spannedCells.insert( qMakePair( spannedRow, spannedCol ) );
467 cell.
setSpan( rowSpan( r,
c ), columnSpan( r,
c ) );
474 if ( mNumericFormats.value( i ) )
479 row.push_back( cell );
481 items.push_back( row );
489 bool changed =
false;
491 std::unique_ptr< QgsNumericFormat > newFormat( format );
492 const QModelIndexList selection = selectedIndexes();
494 for (
const QModelIndex &index : selection )
496 if ( index.row() == 0 && mIncludeHeader )
499 QTableWidgetItem *i = item( index.row(), index.column() );
502 i =
new QTableWidgetItem();
503 setItem( index.row(), index.column(), i );
505 if ( !mNumericFormats.value( i ) && newFormat )
508 mNumericFormats.insert( i, newFormat->clone() );
510 else if ( mNumericFormats.value( i ) && !newFormat )
513 delete mNumericFormats.value( i );
514 mNumericFormats.remove( i );
516 else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
519 delete mNumericFormats.value( i );
520 mNumericFormats.insert( i, newFormat->clone() );
522 i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
525 if ( changed && !mBlockSignals )
533 const QModelIndexList selection = selectedIndexes();
534 for (
const QModelIndex &index : selection )
536 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
540 f = mNumericFormats.value( i );
543 else if ( ( !f && !mNumericFormats.value( i ) )
544 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
563 const QModelIndexList selection = selectedIndexes();
564 for (
const QModelIndex &index : selection )
566 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
570 f = mNumericFormats.value( i );
573 else if ( ( !f && !mNumericFormats.value( i ) )
574 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
599 const QModelIndexList selection = selectedIndexes();
600 for (
const QModelIndex &index : selection )
602 QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value< QColor >() : QColor();
608 else if ( indexColor ==
c )
620 Qt::Alignment alignment = Qt::AlignLeft;
622 const QModelIndexList selection = selectedIndexes();
623 for (
const QModelIndex &index : selection )
625 Qt::Alignment cellAlign =
static_cast< Qt::Alignment
>( model()->data( index, HorizontalAlignment ).toInt() );
628 alignment = cellAlign;
631 else if ( cellAlign == alignment )
635 return Qt::AlignLeft | Qt::AlignTop;
643 Qt::Alignment alignment = Qt::AlignVCenter;
645 const QModelIndexList selection = selectedIndexes();
646 for (
const QModelIndex &index : selection )
648 Qt::Alignment cellAlign =
static_cast< Qt::Alignment
>( model()->data( index, VerticalAlignment ).toInt() );
651 alignment = cellAlign;
654 else if ( cellAlign == alignment )
658 return Qt::AlignLeft | Qt::AlignTop;
668 const QModelIndexList selection = selectedIndexes();
669 for (
const QModelIndex &index : selection )
674 property = cellProperty;
677 else if ( cellProperty == property )
691 const QModelIndexList selection = selectedIndexes();
692 for (
const QModelIndex &index : selection )
694 if ( !model()->data( index, TextFormat ).isValid() )
703 else if ( cellFormat == format )
715 const QModelIndexList selection = selectedIndexes();
716 for (
const QModelIndex &index : selection )
721 else if ( thisHeight != height )
734 const QModelIndexList selection = selectedIndexes();
735 for (
const QModelIndex &index : selection )
740 else if ( thisWidth != width )
752 for (
int col = 0; col < columnCount(); ++col )
754 double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
755 height = std::max( thisHeight, height );
763 for (
int row = 0; row < rowCount(); ++row )
765 double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
766 width = std::max( thisWidth, width );
773 if ( row == 0 && mIncludeHeader )
776 bool changed =
false;
779 for (
int col = 0; col < columnCount(); ++col )
781 if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
783 if ( i->data( RowHeight ).toDouble() != height )
785 i->setData( RowHeight, height );
791 QTableWidgetItem *newItem =
new QTableWidgetItem();
792 newItem->setData( RowHeight, height );
793 setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
799 if ( changed && !mBlockSignals )
805 bool changed =
false;
807 for (
int row = 0; row < rowCount(); ++row )
809 if ( QTableWidgetItem *i = item( row, col ) )
811 if ( i->data( ColumnWidth ).toDouble() != width )
813 i->setData( ColumnWidth, width );
819 QTableWidgetItem *newItem =
new QTableWidgetItem();
820 newItem->setData( ColumnWidth, width );
821 setItem( row, col, newItem );
826 if ( changed && !mBlockSignals )
832 return collectUniqueRows( selectedIndexes() );
837 return collectUniqueColumns( selectedIndexes() );
842 if ( !mIncludeHeader )
843 return QVariantList();
846 res.reserve( columnCount() );
847 for (
int col = 0; col < columnCount(); ++col )
849 if ( QTableWidgetItem *i = item( 0, col ) )
851 res << i->data( CellContent );
863 if ( !mIncludeHeader )
866 return collectUniqueRows( selectedIndexes() ).contains( 0 );
871 return selectedIndexes().size() > 1
873 && isRectangularSelection( selectedIndexes() );
878 return !selectedIndexes().empty()
880 && hasMergedCells( selectedIndexes() );
885 if ( rowCount() == 0 )
893 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
896 const int rowsToInsert = maxRow - minRow + 1;
897 for (
int i = 0; i < rowsToInsert; ++i )
898 insertRow( maxRow + 1 );
901 if ( !mBlockSignals )
907 if ( rowCount() == 0 )
915 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
918 const int rowsToInsert = maxRow - minRow + 1;
919 for (
int i = 0; i < rowsToInsert; ++i )
923 if ( !mBlockSignals )
929 if ( columnCount() == 0 )
937 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
940 const int columnsToInsert = maxColumn - minColumn + 1;
941 for (
int i = 0; i < columnsToInsert; ++i )
942 insertColumn( minColumn );
945 if ( !mBlockSignals )
951 if ( columnCount() == 0 )
959 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
962 const int columnsToInsert = maxColumn - minColumn + 1;
963 for (
int i = 0; i < columnsToInsert; ++i )
964 insertColumn( maxColumn + 1 );
967 if ( !mBlockSignals )
977 bool changed =
false;
978 for (
int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
980 removeRow( rows.at( i ) );
984 if ( changed && !mBlockSignals )
991 if ( columns.empty() )
994 bool changed =
false;
995 for (
int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
997 removeColumn( columns.at( i ) );
1001 if ( !mBlockSignals && changed )
1007 const QModelIndexList s = selectedIndexes();
1008 for (
const QModelIndex &index : s )
1010 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
1016 const QModelIndexList s = selectedIndexes();
1017 for (
const QModelIndex &index : s )
1019 selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
1025 const QModelIndexList selection = selectedIndexes();
1026 bool changed =
false;
1028 for (
const QModelIndex &index : selection )
1030 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1032 i->setText( QString() );
1033 i->setData( CellContent, QVariant() );
1038 if ( changed && !mBlockSignals )
1044 const QModelIndexList selection = selectedIndexes();
1045 bool changed =
false;
1047 for (
const QModelIndex &index : selection )
1049 if ( index.row() == 0 && mIncludeHeader )
1052 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1054 if ( i->data( Qt::ForegroundRole ).value< QColor >() != color )
1056 i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1059 i->setData( TextFormat, QVariant::fromValue( f ) );
1065 QTableWidgetItem *newItem =
new QTableWidgetItem();
1066 newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1069 newItem->setData( TextFormat, QVariant::fromValue( f ) );
1070 setItem( index.row(), index.column(), newItem );
1075 if ( changed && !mBlockSignals )
1081 const QModelIndexList selection = selectedIndexes();
1082 bool changed =
false;
1084 for (
const QModelIndex &index : selection )
1086 if ( index.row() == 0 && mIncludeHeader )
1089 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1091 if ( i->data( PresetBackgroundColorRole ).value< QColor >() != color )
1093 i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1094 i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
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 );
1108 if ( changed && !mBlockSignals )
1114 const QModelIndexList selection = selectedIndexes();
1115 bool changed =
false;
1117 for (
const QModelIndex &index : selection )
1119 if ( index.row() == 0 && mIncludeHeader )
1122 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1124 if (
static_cast< Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1126 i->setData( HorizontalAlignment,
static_cast< int >( alignment ) );
1132 QTableWidgetItem *newItem =
new QTableWidgetItem();
1133 newItem->setData( HorizontalAlignment,
static_cast< int >( alignment ) );
1134 setItem( index.row(), index.column(), newItem );
1139 if ( changed && !mBlockSignals )
1145 const QModelIndexList selection = selectedIndexes();
1146 bool changed =
false;
1148 for (
const QModelIndex &index : selection )
1150 if ( index.row() == 0 && mIncludeHeader )
1153 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1155 if (
static_cast< Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1157 i->setData( VerticalAlignment,
static_cast< int >( alignment ) );
1163 QTableWidgetItem *newItem =
new QTableWidgetItem();
1164 newItem->setData( VerticalAlignment,
static_cast< int >( alignment ) );
1165 setItem( index.row(), index.column(), newItem );
1170 if ( changed && !mBlockSignals )
1176 const QModelIndexList selection = selectedIndexes();
1177 bool changed =
false;
1179 for (
const QModelIndex &index : selection )
1181 if ( index.row() == 0 && mIncludeHeader )
1184 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1186 if ( i->data( CellProperty ).value<
QgsProperty >() != property )
1190 i->setData( CellProperty, QVariant::fromValue( property ) );
1192 i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1196 i->setData( CellProperty, QVariant() );
1197 i->setText( QString() );
1198 i->setFlags( i->flags() | Qt::ItemIsEditable );
1205 QTableWidgetItem *newItem =
new QTableWidgetItem( property.
asExpression() );
1208 newItem->setData( CellProperty, QVariant::fromValue( property ) );
1209 newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1213 newItem->setData( CellProperty, QVariant() );
1214 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1216 setItem( index.row(), index.column(), newItem );
1221 if ( changed && !mBlockSignals )
1227 const QModelIndexList selection = selectedIndexes();
1228 bool changed =
false;
1230 for (
const QModelIndex &index : selection )
1232 if ( index.row() == 0 && mIncludeHeader )
1235 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1237 i->setData( TextFormat, QVariant::fromValue( format ) );
1238 i->setData( Qt::ForegroundRole, format.
color() );
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 );
1251 if ( changed && !mBlockSignals )
1257 bool changed =
false;
1260 for (
int row : rows )
1262 if ( row == 0 && mIncludeHeader )
1265 for (
int col = 0; col < columnCount(); ++col )
1267 if ( QTableWidgetItem *i = item( row, col ) )
1269 if ( i->data( RowHeight ).toDouble() != height )
1271 i->setData( RowHeight, height );
1277 QTableWidgetItem *newItem =
new QTableWidgetItem();
1278 newItem->setData( RowHeight, height );
1279 setItem( row, col, newItem );
1285 if ( changed && !mBlockSignals )
1291 bool changed =
false;
1294 for (
int col : cols )
1296 for (
int row = 0; row < rowCount(); ++row )
1298 if ( QTableWidgetItem *i = item( row, col ) )
1300 if ( i->data( ColumnWidth ).toDouble() != width )
1302 i->setData( ColumnWidth, width );
1308 QTableWidgetItem *newItem =
new QTableWidgetItem();
1309 newItem->setData( ColumnWidth, width );
1310 setItem( row, col, newItem );
1316 if ( changed && !mBlockSignals )
1322 if ( included == mIncludeHeader )
1325 mIncludeHeader = included;
1327 if ( mIncludeHeader )
1336 if ( !mIncludeHeader )
1341 for (
int col = 0; col < columnCount(); ++col )
1343 if ( QTableWidgetItem *i = item( 0, col ) )
1345 i->setText( headers.value( col ).toString() );
1346 i->setData( CellContent, headers.value( col ) );
1350 QTableWidgetItem *item =
new QTableWidgetItem( headers.value( col ).toString() );
1351 item->setData( CellContent, headers.value( col ) );
1352 setItem( 0, col, item );
1360 const QModelIndexList selection = selectedIndexes();
1361 if ( selection.size() < 2 )
1368 for (
const QModelIndex &index : selection )
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();
1380 setSpan( minRow, minCol, maxRow - minRow + 1, maxCol - minCol + 1 );
1382 if ( !mBlockSignals )
1388 const QModelIndexList selection = selectedIndexes();
1389 for (
const QModelIndex &index : selection )
1391 if ( rowSpan( index.row(), index.column() ) > 1 ||
1392 columnSpan( index.row(), index.column() ) > 1 )
1393 setSpan( index.row(), index.column(), 1, 1 );
1396 if ( !mBlockSignals )
1402QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1403 : QPlainTextEdit( parent )
1406 document()->setDocumentMargin( document()->documentMargin() / 2 );
1408 connect(
this, &QPlainTextEdit::textChanged,
this, &QgsTableEditorTextEdit::resizeToContents );
1409 updateMinimumSize();
1412void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1414 switch ( event->key() )
1417 case Qt::Key_Return:
1419 if ( event->modifiers() & Qt::ControlModifier )
1422 insertPlainText( QString(
'\n' ) );
1438 if ( mWeakEditorMode )
1445 QPlainTextEdit::keyPressEvent( event );
1452 if ( event->modifiers() & Qt::ControlModifier )
1456 insertPlainText( QString(
'\t' ) );
1467 QPlainTextEdit::keyPressEvent( event );
1471void QgsTableEditorTextEdit::updateMinimumSize()
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() );
1484void QgsTableEditorTextEdit::setWeakEditorMode(
bool weakEditorMode )
1486 mWeakEditorMode = weakEditorMode;
1489void QgsTableEditorTextEdit::resizeToContents()
1491 int oldWidth = width();
1492 int oldHeight = height();
1493 if ( mOriginalWidth == -1 )
1494 mOriginalWidth = oldWidth;
1495 if ( mOriginalHeight == -1 )
1496 mOriginalHeight = oldHeight;
1498 if ( QWidget *parent = parentWidget() )
1500 QPoint position = pos();
1501 QFontMetrics fm( font() );
1503 const QStringList lines = toPlainText().split(
'\n' );
1504 int maxTextLineWidth = 0;
1505 int totalTextHeight = 0;
1506 for (
const QString &line : lines )
1508 const QRect bounds = fontMetrics().boundingRect( line );
1509 maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1510 totalTextHeight += fm.height();
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 );
1521 if ( mWidgetOwnsGeometry )
1523 setMaximumWidth( newWidth );
1524 setMaximumHeight( newHeight );
1526 if ( isRightToLeft() )
1527 move( position.x() - newWidth + oldWidth, position.y() );
1528 resize( newWidth, newHeight );
1532void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1534 switch ( e->type() )
1536 case QEvent::FontChange:
1537 case QEvent::StyleChange:
1538 case QEvent::ContentsRectChange:
1539 updateMinimumSize();
1544 QPlainTextEdit::changeEvent( e );
1547QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1548 : QStyledItemDelegate( parent )
1553void QgsTableEditorDelegate::setWeakEditorMode(
bool weakEditorMode )
1555 mWeakEditorMode = weakEditorMode;
1558QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex & )
const
1560 QgsTableEditorTextEdit *w =
new QgsTableEditorTextEdit( parent );
1561 w->setWeakEditorMode( mWeakEditorMode );
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 );
1571void QgsTableEditorDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
1573 QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1574 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1576 if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1578 lineEdit->setPlainText( value.toString() );
1579 lineEdit->selectAll();
1585void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
1587 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1589 const QString text = lineEdit->toPlainText();
1590 if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value<
QgsProperty >().
isActive() )
1592 model->setData( index, text, QgsTableEditorWidget::CellContent );
1593 model->setData( index, text, Qt::DisplayRole );
1594 emit updateNumericFormatForIndex( index );
A context for numeric formats.
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.
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.