25#include <QPlainTextEdit>
28#include "moc_qgstableeditorwidget.cpp"
31 : QTableWidget( parent )
33 mHeaderMenu =
new QMenu(
this );
34 mCellMenu =
new QMenu(
this );
37 connect(
this, &QgsTableEditorWidget::cellChanged,
this, [
this] {
42 setContextMenuPolicy( Qt::CustomContextMenu );
43 connect(
this, &QWidget::customContextMenuRequested,
this, [
this](
const QPoint &point ) {
47 QAction *mergeCells = mCellMenu->addAction( tr(
"Merge Selected Cells" ) );
52 QAction *splitCells = mCellMenu->addAction( tr(
"Split Selected Cells" ) );
55 if ( !mCellMenu->isEmpty() )
56 mCellMenu->popup( mapToGlobal( point ) );
60 horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
61 connect( horizontalHeader(), &QWidget::customContextMenuRequested,
this, [
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, [
this](
const QPoint &point ) {
103 const int row = verticalHeader()->logicalIndexAt( point.y() );
105 QSet<int> selectedRows;
106 for (
const QModelIndex &index : selectedIndexes() )
108 selectedRows.insert( index.row() );
112 bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
115 if ( selectedIndexes().count() == 1 )
119 isConsecutive =
true;
121 else if ( !selectedRows.contains( row ) )
125 isConsecutive =
true;
128 mHeaderMenu->clear();
131 QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Above",
nullptr, selectedRows.size() ) : tr(
"Insert Row Above" ) );
133 QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Below",
nullptr, selectedRows.size() ) : tr(
"Insert Row Below" ) );
136 QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Delete %n Row(s)",
nullptr, selectedRows.size() ) : tr(
"Delete Row" ) );
139 mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
144 connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex,
this, &QgsTableEditorWidget::updateNumericFormatForIndex );
145 setItemDelegate( delegate );
148 connect(
this, &QTableWidget::cellDoubleClicked,
this, [
this] {
151 d->setWeakEditorMode(
false );
160 qDeleteAll( mNumericFormats );
163void QgsTableEditorWidget::updateNumericFormatForIndex(
const QModelIndex &index )
165 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
169 i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(),
QgsNumericFormatContext() ) );
174void QgsTableEditorWidget::updateHeaders()
182 for (
char c =
'A';
c <=
'Z';
c++ )
184 letters.push_back( QString(
c ) );
187 int len = letters.length();
191 for (
int i = 0; i < 1000; i++ )
197 first = letters.at( fIndex );
207 current += letters.at( index );
208 headers.push_back( current );
214 setHorizontalHeaderLabels( headers );
217 if ( mIncludeHeader )
218 headers << tr(
"Header" );
219 for (
int i = 1; i <= 1000; i++ )
221 headers << QString::number( i );
224 setVerticalHeaderLabels( headers );
227bool QgsTableEditorWidget::collectConsecutiveRowRange(
const QModelIndexList &list,
int &minRow,
int &maxRow )
const
229 QSet<int> includedRows;
230 minRow = std::numeric_limits<int>::max();
232 for (
const QModelIndex &index : list )
234 includedRows.insert( index.row() );
235 minRow = std::min( minRow, index.row() );
236 maxRow = std::max( maxRow, index.row() );
240 for (
int r = minRow + 1; r < maxRow; r++ )
242 if ( !includedRows.contains( r ) )
248bool QgsTableEditorWidget::collectConsecutiveColumnRange(
const QModelIndexList &list,
int &minColumn,
int &maxColumn )
const
250 QSet<int> includedColumns;
251 minColumn = std::numeric_limits<int>::max();
253 for (
const QModelIndex &index : list )
255 includedColumns.insert( index.column() );
256 minColumn = std::min( minColumn, index.column() );
257 maxColumn = std::max( maxColumn, index.column() );
261 for (
int r = minColumn + 1; r < maxColumn; r++ )
263 if ( !includedColumns.contains( r ) )
269QList<int> QgsTableEditorWidget::collectUniqueRows(
const QModelIndexList &list )
const
272 for (
const QModelIndex &index : list )
274 if ( !res.contains( index.row() ) )
277 std::sort( res.begin(), res.end() );
281QList<int> QgsTableEditorWidget::collectUniqueColumns(
const QModelIndexList &list )
const
284 for (
const QModelIndex &index : list )
286 if ( !res.contains( index.column() ) )
287 res << index.column();
289 std::sort( res.begin(), res.end() );
293bool QgsTableEditorWidget::isRectangularSelection(
const QModelIndexList &list )
const
302 QSet<QPair<int, int>> selectedSet;
303 for (
const QModelIndex &index : list )
305 if ( minRow == -1 || index.row() < minRow )
306 minRow = index.row();
307 if ( maxRow == -1 || index.row() > maxRow )
308 maxRow = index.row();
309 if ( minCol == -1 || index.column() < minCol )
310 minCol = index.column();
311 if ( maxCol == -1 || index.column() > maxCol )
312 maxCol = index.column();
313 selectedSet.insert( qMakePair( index.row(), index.column() ) );
317 if ( list.size() != ( maxRow - minRow + 1 ) * ( maxCol - minCol + 1 ) )
321 QSet<QPair<int, int>> expectedSet;
322 for (
int row = minRow; row <= maxRow; ++row )
324 for (
int col = minCol; col <= maxCol; ++col )
326 expectedSet.insert( qMakePair( row, col ) );
329 return selectedSet == expectedSet;
332bool QgsTableEditorWidget::hasMergedCells(
const QModelIndexList &list )
const
334 for (
const QModelIndex &index : list )
336 if ( rowSpan( index.row(), index.column() ) > 1
337 || columnSpan( index.row(), index.column() ) > 1 )
345 switch ( event->key() )
351 QTableWidget::keyPressEvent( event );
352 setCurrentCell( currentRow() + 1, currentColumn() );
363 QTableWidget::keyPressEvent( event );
367 d->setWeakEditorMode(
true );
374 qDeleteAll( mNumericFormats );
375 mNumericFormats.clear();
378 int rowNumber = mIncludeHeader ? 1 : 0;
380 setRowCount( contents.size() + rowNumber );
385 setColumnCount( row.size() );
393 item->setData( CellContent, col.content() );
394 item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
395 item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
396 item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
397 item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
398 item->setData( HorizontalAlignment,
static_cast<int>( col.horizontalAlignment() ) );
399 item->setData( VerticalAlignment,
static_cast<int>( col.verticalAlignment() ) );
400 item->setData( CellProperty, QVariant::fromValue( col.content().value<
QgsProperty>() ) );
403 item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
405 if (
auto *lNumericFormat = col.numericFormat() )
407 mNumericFormats.insert( item, lNumericFormat->clone() );
408 item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
410 setItem( rowNumber, colNumber, item );
412 if ( col.rowSpan() > 1 || col.columnSpan() > 1 )
413 setSpan( rowNumber, colNumber, col.rowSpan(), col.columnSpan() );
425 resizeColumnsToContents();
426 resizeRowsToContents();
435 items.reserve( rowCount() );
437 QSet<QPair<int, int>> spannedCells;
438 for (
int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
441 row.reserve( columnCount() );
442 for (
int c = 0;
c < columnCount();
c++ )
445 if ( QTableWidgetItem *i = item( r,
c ) )
451 cell.
setVerticalAlignment(
static_cast<Qt::Alignment
>( i->data( VerticalAlignment ).toInt() ) );
454 if ( !spannedCells.contains( qMakePair( r,
c ) ) )
456 const int rowsSpan = rowSpan( r,
c );
457 const int colsSpan = columnSpan( r,
c );
458 for (
int spannedRow = r; spannedRow < r + rowsSpan; ++spannedRow )
460 for (
int spannedCol =
c; spannedCol <
c + colsSpan; ++spannedCol )
462 spannedCells.insert( qMakePair( spannedRow, spannedCol ) );
465 cell.
setSpan( rowSpan( r,
c ), columnSpan( r,
c ) );
472 if ( mNumericFormats.value( i ) )
477 row.push_back( cell );
479 items.push_back( row );
487 bool changed =
false;
489 std::unique_ptr<QgsNumericFormat> newFormat( format );
490 const QModelIndexList selection = selectedIndexes();
492 for (
const QModelIndex &index : selection )
494 if ( index.row() == 0 && mIncludeHeader )
497 QTableWidgetItem *i = item( index.row(), index.column() );
500 i =
new QTableWidgetItem();
501 setItem( index.row(), index.column(), i );
503 if ( !mNumericFormats.value( i ) && newFormat )
506 mNumericFormats.insert( i, newFormat->clone() );
508 else if ( mNumericFormats.value( i ) && !newFormat )
511 delete mNumericFormats.value( i );
512 mNumericFormats.remove( i );
514 else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
517 delete mNumericFormats.value( i );
518 mNumericFormats.insert( i, newFormat->clone() );
520 i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
523 if ( changed && !mBlockSignals )
531 const QModelIndexList selection = selectedIndexes();
532 for (
const QModelIndex &index : selection )
534 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
538 f = mNumericFormats.value( i );
541 else if ( ( !f && !mNumericFormats.value( i ) )
542 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
561 const QModelIndexList selection = selectedIndexes();
562 for (
const QModelIndex &index : selection )
564 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
568 f = mNumericFormats.value( i );
571 else if ( ( !f && !mNumericFormats.value( i ) )
572 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
597 const QModelIndexList selection = selectedIndexes();
598 for (
const QModelIndex &index : selection )
600 QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value<QColor>() : QColor();
606 else if ( indexColor ==
c )
618 Qt::Alignment alignment = Qt::AlignLeft;
620 const QModelIndexList selection = selectedIndexes();
621 for (
const QModelIndex &index : selection )
623 Qt::Alignment cellAlign =
static_cast<Qt::Alignment
>( model()->data( index, HorizontalAlignment ).toInt() );
626 alignment = cellAlign;
629 else if ( cellAlign == alignment )
633 return Qt::AlignLeft | Qt::AlignTop;
641 Qt::Alignment alignment = Qt::AlignVCenter;
643 const QModelIndexList selection = selectedIndexes();
644 for (
const QModelIndex &index : selection )
646 Qt::Alignment cellAlign =
static_cast<Qt::Alignment
>( model()->data( index, VerticalAlignment ).toInt() );
649 alignment = cellAlign;
652 else if ( cellAlign == alignment )
656 return Qt::AlignLeft | Qt::AlignTop;
666 const QModelIndexList selection = selectedIndexes();
667 for (
const QModelIndex &index : selection )
672 property = cellProperty;
675 else if ( cellProperty == property )
689 const QModelIndexList selection = selectedIndexes();
690 for (
const QModelIndex &index : selection )
692 if ( !model()->data( index, TextFormat ).isValid() )
698 format = std::move( cellFormat );
701 else if ( cellFormat == format )
713 const QModelIndexList selection = selectedIndexes();
714 for (
const QModelIndex &index : selection )
719 else if ( thisHeight != height )
732 const QModelIndexList selection = selectedIndexes();
733 for (
const QModelIndex &index : selection )
738 else if ( thisWidth != width )
750 for (
int col = 0; col < columnCount(); ++col )
752 double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
753 height = std::max( thisHeight, height );
761 for (
int row = 0; row < rowCount(); ++row )
763 double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
764 width = std::max( thisWidth, width );
771 if ( row == 0 && mIncludeHeader )
774 bool changed =
false;
777 for (
int col = 0; col < columnCount(); ++col )
779 if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
781 if ( i->data( RowHeight ).toDouble() != height )
783 i->setData( RowHeight, height );
789 QTableWidgetItem *newItem =
new QTableWidgetItem();
790 newItem->setData( RowHeight, height );
791 setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
797 if ( changed && !mBlockSignals )
803 bool changed =
false;
805 for (
int row = 0; row < rowCount(); ++row )
807 if ( QTableWidgetItem *i = item( row, col ) )
809 if ( i->data( ColumnWidth ).toDouble() != width )
811 i->setData( ColumnWidth, width );
817 QTableWidgetItem *newItem =
new QTableWidgetItem();
818 newItem->setData( ColumnWidth, width );
819 setItem( row, col, newItem );
824 if ( changed && !mBlockSignals )
830 return collectUniqueRows( selectedIndexes() );
835 return collectUniqueColumns( selectedIndexes() );
840 if ( !mIncludeHeader )
841 return QVariantList();
844 res.reserve( columnCount() );
845 for (
int col = 0; col < columnCount(); ++col )
847 if ( QTableWidgetItem *i = item( 0, col ) )
849 res << i->data( CellContent );
861 if ( !mIncludeHeader )
864 return collectUniqueRows( selectedIndexes() ).contains( 0 );
869 return selectedIndexes().size() > 1
871 && isRectangularSelection( selectedIndexes() );
876 return !selectedIndexes().empty()
878 && hasMergedCells( selectedIndexes() );
883 if ( rowCount() == 0 )
891 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
894 const int rowsToInsert = maxRow - minRow + 1;
895 for (
int i = 0; i < rowsToInsert; ++i )
896 insertRow( maxRow + 1 );
899 if ( !mBlockSignals )
905 if ( rowCount() == 0 )
913 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
916 const int rowsToInsert = maxRow - minRow + 1;
917 for (
int i = 0; i < rowsToInsert; ++i )
921 if ( !mBlockSignals )
927 if ( columnCount() == 0 )
935 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
938 const int columnsToInsert = maxColumn - minColumn + 1;
939 for (
int i = 0; i < columnsToInsert; ++i )
940 insertColumn( minColumn );
943 if ( !mBlockSignals )
949 if ( columnCount() == 0 )
957 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
960 const int columnsToInsert = maxColumn - minColumn + 1;
961 for (
int i = 0; i < columnsToInsert; ++i )
962 insertColumn( maxColumn + 1 );
965 if ( !mBlockSignals )
975 bool changed =
false;
976 for (
int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
978 removeRow( rows.at( i ) );
982 if ( changed && !mBlockSignals )
989 if ( columns.empty() )
992 bool changed =
false;
993 for (
int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
995 removeColumn( columns.at( i ) );
999 if ( !mBlockSignals && changed )
1005 const QModelIndexList s = selectedIndexes();
1006 for (
const QModelIndex &index : s )
1008 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
1014 const QModelIndexList s = selectedIndexes();
1015 for (
const QModelIndex &index : s )
1017 selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
1023 const QModelIndexList selection = selectedIndexes();
1024 bool changed =
false;
1026 for (
const QModelIndex &index : selection )
1028 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1030 i->setText( QString() );
1031 i->setData( CellContent, QVariant() );
1036 if ( changed && !mBlockSignals )
1042 const QModelIndexList selection = selectedIndexes();
1043 bool changed =
false;
1045 for (
const QModelIndex &index : selection )
1047 if ( index.row() == 0 && mIncludeHeader )
1050 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1052 if ( i->data( Qt::ForegroundRole ).value<QColor>() != color )
1054 i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1057 i->setData( TextFormat, QVariant::fromValue( f ) );
1063 QTableWidgetItem *newItem =
new QTableWidgetItem();
1064 newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1067 newItem->setData( TextFormat, QVariant::fromValue( f ) );
1068 setItem( index.row(), index.column(), newItem );
1073 if ( changed && !mBlockSignals )
1079 const QModelIndexList selection = selectedIndexes();
1080 bool changed =
false;
1082 for (
const QModelIndex &index : selection )
1084 if ( index.row() == 0 && mIncludeHeader )
1087 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1089 if ( i->data( PresetBackgroundColorRole ).value<QColor>() != color )
1091 i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1092 i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1098 QTableWidgetItem *newItem =
new QTableWidgetItem();
1099 newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1100 newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1101 setItem( index.row(), index.column(), newItem );
1106 if ( changed && !mBlockSignals )
1112 const QModelIndexList selection = selectedIndexes();
1113 bool changed =
false;
1115 for (
const QModelIndex &index : selection )
1117 if ( index.row() == 0 && mIncludeHeader )
1120 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1122 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1124 i->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1130 QTableWidgetItem *newItem =
new QTableWidgetItem();
1131 newItem->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1132 setItem( index.row(), index.column(), newItem );
1137 if ( changed && !mBlockSignals )
1143 const QModelIndexList selection = selectedIndexes();
1144 bool changed =
false;
1146 for (
const QModelIndex &index : selection )
1148 if ( index.row() == 0 && mIncludeHeader )
1151 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1153 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1155 i->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1161 QTableWidgetItem *newItem =
new QTableWidgetItem();
1162 newItem->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1163 setItem( index.row(), index.column(), newItem );
1168 if ( changed && !mBlockSignals )
1174 const QModelIndexList selection = selectedIndexes();
1175 bool changed =
false;
1177 for (
const QModelIndex &index : selection )
1179 if ( index.row() == 0 && mIncludeHeader )
1182 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1184 if ( i->data( CellProperty ).value<
QgsProperty>() != property )
1188 i->setData( CellProperty, QVariant::fromValue( property ) );
1190 i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1194 i->setData( CellProperty, QVariant() );
1195 i->setText( QString() );
1196 i->setFlags( i->flags() | Qt::ItemIsEditable );
1203 QTableWidgetItem *newItem =
new QTableWidgetItem( property.
asExpression() );
1206 newItem->setData( CellProperty, QVariant::fromValue( property ) );
1207 newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1211 newItem->setData( CellProperty, QVariant() );
1212 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1214 setItem( index.row(), index.column(), newItem );
1219 if ( changed && !mBlockSignals )
1225 const QModelIndexList selection = selectedIndexes();
1226 bool changed =
false;
1228 for (
const QModelIndex &index : selection )
1230 if ( index.row() == 0 && mIncludeHeader )
1233 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1235 i->setData( TextFormat, QVariant::fromValue( format ) );
1236 i->setData( Qt::ForegroundRole, format.
color() );
1241 QTableWidgetItem *newItem =
new QTableWidgetItem();
1242 newItem->setData( TextFormat, QVariant::fromValue( format ) );
1243 newItem->setData( Qt::ForegroundRole, format.
color() );
1244 setItem( index.row(), index.column(), newItem );
1249 if ( changed && !mBlockSignals )
1255 bool changed =
false;
1258 for (
int row : rows )
1260 if ( row == 0 && mIncludeHeader )
1263 for (
int col = 0; col < columnCount(); ++col )
1265 if ( QTableWidgetItem *i = item( row, col ) )
1267 if ( i->data( RowHeight ).toDouble() != height )
1269 i->setData( RowHeight, height );
1275 QTableWidgetItem *newItem =
new QTableWidgetItem();
1276 newItem->setData( RowHeight, height );
1277 setItem( row, col, newItem );
1283 if ( changed && !mBlockSignals )
1289 bool changed =
false;
1292 for (
int col : cols )
1294 for (
int row = 0; row < rowCount(); ++row )
1296 if ( QTableWidgetItem *i = item( row, col ) )
1298 if ( i->data( ColumnWidth ).toDouble() != width )
1300 i->setData( ColumnWidth, width );
1306 QTableWidgetItem *newItem =
new QTableWidgetItem();
1307 newItem->setData( ColumnWidth, width );
1308 setItem( row, col, newItem );
1314 if ( changed && !mBlockSignals )
1320 if ( included == mIncludeHeader )
1323 mIncludeHeader = included;
1325 if ( mIncludeHeader )
1334 if ( !mIncludeHeader )
1339 for (
int col = 0; col < columnCount(); ++col )
1341 if ( QTableWidgetItem *i = item( 0, col ) )
1343 i->setText( headers.value( col ).toString() );
1344 i->setData( CellContent, headers.value( col ) );
1348 QTableWidgetItem *item =
new QTableWidgetItem( headers.value( col ).toString() );
1349 item->setData( CellContent, headers.value( col ) );
1350 setItem( 0, col, item );
1358 const QModelIndexList selection = selectedIndexes();
1359 if ( selection.size() < 2 )
1366 for (
const QModelIndex &index : selection )
1368 if ( minRow == -1 || index.row() < minRow )
1369 minRow = index.row();
1370 if ( maxRow == -1 || index.row() > maxRow )
1371 maxRow = index.row();
1372 if ( minCol == -1 || index.column() < minCol )
1373 minCol = index.column();
1374 if ( maxCol == -1 || index.column() > maxCol )
1375 maxCol = index.column();
1377 QStringList mergedCellText;
1378 for (
int row = minRow; row <= maxRow; ++row )
1380 for (
int col = minCol; col <= maxCol; ++col )
1382 if ( QTableWidgetItem *i = item( row, col ) )
1384 const QString content = i->data( CellContent ).toString();
1385 if ( !content.isEmpty() )
1387 mergedCellText.append( content );
1393 setSpan( minRow, minCol, maxRow - minRow + 1, maxCol - minCol + 1 );
1396 if ( !mergedCellText.isEmpty() )
1398 if ( QTableWidgetItem *i = item( minRow, minCol ) )
1400 i->setText( mergedCellText.join(
' ' ) );
1401 i->setData( CellContent, i->text() );
1405 if ( !mBlockSignals )
1411 const QModelIndexList selection = selectedIndexes();
1412 for (
const QModelIndex &index : selection )
1414 if ( rowSpan( index.row(), index.column() ) > 1 || columnSpan( index.row(), index.column() ) > 1 )
1415 setSpan( index.row(), index.column(), 1, 1 );
1418 if ( !mBlockSignals )
1424QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1425 : QPlainTextEdit( parent )
1428 document()->setDocumentMargin( document()->documentMargin() / 2 );
1430 connect(
this, &QPlainTextEdit::textChanged,
this, &QgsTableEditorTextEdit::resizeToContents );
1431 updateMinimumSize();
1434void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1436 switch ( event->key() )
1439 case Qt::Key_Return:
1441 if ( event->modifiers() & Qt::ControlModifier )
1444 insertPlainText( QString(
'\n' ) );
1460 if ( mWeakEditorMode )
1467 QPlainTextEdit::keyPressEvent( event );
1474 if ( event->modifiers() & Qt::ControlModifier )
1478 insertPlainText( QString(
'\t' ) );
1489 QPlainTextEdit::keyPressEvent( event );
1493void QgsTableEditorTextEdit::updateMinimumSize()
1495 const double tm = document()->documentMargin();
1496 const QMargins cm = contentsMargins();
1497 const int width = tm * 2 + cm.left() + cm.right() + 30;
1498 const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1499 QStyleOptionFrame opt;
1500 initStyleOption( &opt );
1501 const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ),
this );
1502 setMinimumWidth( sizeFromContent.width() );
1503 setMinimumHeight( sizeFromContent.height() );
1506void QgsTableEditorTextEdit::setWeakEditorMode(
bool weakEditorMode )
1508 mWeakEditorMode = weakEditorMode;
1511void QgsTableEditorTextEdit::resizeToContents()
1513 int oldWidth = width();
1514 int oldHeight = height();
1515 if ( mOriginalWidth == -1 )
1516 mOriginalWidth = oldWidth;
1517 if ( mOriginalHeight == -1 )
1518 mOriginalHeight = oldHeight;
1520 if ( QWidget *parent = parentWidget() )
1522 QPoint position = pos();
1523 QFontMetrics fm( font() );
1525 const QStringList lines = toPlainText().split(
'\n' );
1526 int maxTextLineWidth = 0;
1527 int totalTextHeight = 0;
1528 for (
const QString &line : lines )
1530 const QRect bounds = fontMetrics().boundingRect( line );
1531 maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1532 totalTextHeight += fm.height();
1535 int hintWidth = minimumWidth() + maxTextLineWidth;
1536 int hintHeight = minimumHeight() + totalTextHeight;
1537 int parentWidth = parent->width();
1538 int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1539 int maxHeight = parent->height() - position.y();
1540 int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1541 int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1543 if ( mWidgetOwnsGeometry )
1545 setMaximumWidth( newWidth );
1546 setMaximumHeight( newHeight );
1548 if ( isRightToLeft() )
1549 move( position.x() - newWidth + oldWidth, position.y() );
1550 resize( newWidth, newHeight );
1554void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1556 switch ( e->type() )
1558 case QEvent::FontChange:
1559 case QEvent::StyleChange:
1560 case QEvent::ContentsRectChange:
1561 updateMinimumSize();
1566 QPlainTextEdit::changeEvent( e );
1569QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1570 : QStyledItemDelegate( parent )
1574void QgsTableEditorDelegate::setWeakEditorMode(
bool weakEditorMode )
1576 mWeakEditorMode = weakEditorMode;
1579QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex & )
const
1581 QgsTableEditorTextEdit *w =
new QgsTableEditorTextEdit( parent );
1582 w->setWeakEditorMode( mWeakEditorMode );
1584 if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame,
nullptr, w ) )
1585 w->setFrameShape( QFrame::NoFrame );
1586 if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected,
nullptr, w ) )
1587 w->setWidgetOwnsGeometry(
true );
1592void QgsTableEditorDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
1594 QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1595 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1597 if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1599 lineEdit->setPlainText( value.toString() );
1600 lineEdit->selectAll();
1606void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
1608 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1610 const QString text = lineEdit->toPlainText();
1611 if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value<
QgsProperty>().
isActive() )
1613 model->setData( index, text, QgsTableEditorWidget::CellContent );
1614 model->setData( index, text, Qt::DisplayRole );
1615 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.