19#include "moc_qgstableeditorwidget.cpp"
25#include <QPlainTextEdit>
28 : QTableWidget( parent )
30 mHeaderMenu =
new QMenu(
this );
31 mCellMenu =
new QMenu(
this );
34 connect(
this, &QgsTableEditorWidget::cellChanged,
this, [=] {
39 setContextMenuPolicy( Qt::CustomContextMenu );
40 connect(
this, &QWidget::customContextMenuRequested,
this, [=](
const QPoint &point ) {
44 QAction *mergeCells = mCellMenu->addAction( tr(
"Merge Selected Cells" ) );
49 QAction *splitCells = mCellMenu->addAction( tr(
"Split Selected Cells" ) );
52 if ( !mCellMenu->isEmpty() )
53 mCellMenu->popup( mapToGlobal( point ) );
57 horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
58 connect( horizontalHeader(), &QWidget::customContextMenuRequested,
this, [=](
const QPoint &point ) {
59 const int column = horizontalHeader()->logicalIndexAt( point.x() );
61 QSet<int> selectedColumns;
62 for (
const QModelIndex &index : selectedIndexes() )
64 selectedColumns.insert( index.column() );
68 bool isConsecutive = collectConsecutiveColumnRange( selectedIndexes(), minCol, maxCol );
71 if ( selectedIndexes().count() == 1 )
74 selectColumn( column );
77 else if ( !selectedColumns.contains( column ) )
80 selectColumn( column );
87 QAction *insertBefore = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Insert %n Column(s) Before",
nullptr, selectedColumns.size() ) : tr(
"Insert Column Before" ) );
89 QAction *insertAfter = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Insert %n Column(s) After",
nullptr, selectedColumns.size() ) : tr(
"Insert Column After" ) );
92 QAction *deleteSelected = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr(
"Delete %n Column(s)",
nullptr, selectedColumns.size() ) : tr(
"Delete Column" ) );
95 mHeaderMenu->popup( horizontalHeader()->mapToGlobal( point ) );
98 verticalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
99 connect( verticalHeader(), &QWidget::customContextMenuRequested,
this, [=](
const QPoint &point ) {
100 const int row = verticalHeader()->logicalIndexAt( point.y() );
102 QSet<int> selectedRows;
103 for (
const QModelIndex &index : selectedIndexes() )
105 selectedRows.insert( index.row() );
109 bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
112 if ( selectedIndexes().count() == 1 )
116 isConsecutive =
true;
118 else if ( !selectedRows.contains( row ) )
122 isConsecutive =
true;
125 mHeaderMenu->clear();
128 QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Above",
nullptr, selectedRows.size() ) : tr(
"Insert Row Above" ) );
130 QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Insert %n Row(s) Below",
nullptr, selectedRows.size() ) : tr(
"Insert Row Below" ) );
133 QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr(
"Delete %n Row(s)",
nullptr, selectedRows.size() ) : tr(
"Delete Row" ) );
136 mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
141 connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex,
this, &QgsTableEditorWidget::updateNumericFormatForIndex );
142 setItemDelegate( delegate );
145 connect(
this, &QTableWidget::cellDoubleClicked,
this, [=] {
148 d->setWeakEditorMode(
false );
157 qDeleteAll( mNumericFormats );
160void QgsTableEditorWidget::updateNumericFormatForIndex(
const QModelIndex &index )
162 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
166 i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(),
QgsNumericFormatContext() ) );
171void QgsTableEditorWidget::updateHeaders()
179 for (
char c =
'A';
c <=
'Z';
c++ )
181 letters.push_back( QString(
c ) );
184 int len = letters.length();
188 for (
int i = 0; i < 1000; i++ )
194 first = letters.at( fIndex );
204 current += letters.at( index );
205 headers.push_back( current );
211 setHorizontalHeaderLabels( headers );
214 if ( mIncludeHeader )
215 headers << tr(
"Header" );
216 for (
int i = 1; i <= 1000; i++ )
218 headers << QString::number( i );
221 setVerticalHeaderLabels( headers );
224bool QgsTableEditorWidget::collectConsecutiveRowRange(
const QModelIndexList &list,
int &minRow,
int &maxRow )
const
226 QSet<int> includedRows;
227 minRow = std::numeric_limits<int>::max();
229 for (
const QModelIndex &index : list )
231 includedRows.insert( index.row() );
232 minRow = std::min( minRow, index.row() );
233 maxRow = std::max( maxRow, index.row() );
237 for (
int r = minRow + 1; r < maxRow; r++ )
239 if ( !includedRows.contains( r ) )
245bool QgsTableEditorWidget::collectConsecutiveColumnRange(
const QModelIndexList &list,
int &minColumn,
int &maxColumn )
const
247 QSet<int> includedColumns;
248 minColumn = std::numeric_limits<int>::max();
250 for (
const QModelIndex &index : list )
252 includedColumns.insert( index.column() );
253 minColumn = std::min( minColumn, index.column() );
254 maxColumn = std::max( maxColumn, index.column() );
258 for (
int r = minColumn + 1; r < maxColumn; r++ )
260 if ( !includedColumns.contains( r ) )
266QList<int> QgsTableEditorWidget::collectUniqueRows(
const QModelIndexList &list )
const
269 for (
const QModelIndex &index : list )
271 if ( !res.contains( index.row() ) )
274 std::sort( res.begin(), res.end() );
278QList<int> QgsTableEditorWidget::collectUniqueColumns(
const QModelIndexList &list )
const
281 for (
const QModelIndex &index : list )
283 if ( !res.contains( index.column() ) )
284 res << index.column();
286 std::sort( res.begin(), res.end() );
290bool QgsTableEditorWidget::isRectangularSelection(
const QModelIndexList &list )
const
299 QSet<QPair<int, int>> selectedSet;
300 for (
const QModelIndex &index : list )
302 if ( minRow == -1 || index.row() < minRow )
303 minRow = index.row();
304 if ( maxRow == -1 || index.row() > maxRow )
305 maxRow = index.row();
306 if ( minCol == -1 || index.column() < minCol )
307 minCol = index.column();
308 if ( maxCol == -1 || index.column() > maxCol )
309 maxCol = index.column();
310 selectedSet.insert( qMakePair( index.row(), index.column() ) );
314 if ( list.size() != ( maxRow - minRow + 1 ) * ( maxCol - minCol + 1 ) )
318 QSet<QPair<int, int>> expectedSet;
319 for (
int row = minRow; row <= maxRow; ++row )
321 for (
int col = minCol; col <= maxCol; ++col )
323 expectedSet.insert( qMakePair( row, col ) );
326 return selectedSet == expectedSet;
329bool QgsTableEditorWidget::hasMergedCells(
const QModelIndexList &list )
const
331 for (
const QModelIndex &index : list )
333 if ( rowSpan( index.row(), index.column() ) > 1
334 || columnSpan( index.row(), index.column() ) > 1 )
342 switch ( event->key() )
348 QTableWidget::keyPressEvent( event );
349 setCurrentCell( currentRow() + 1, currentColumn() );
360 QTableWidget::keyPressEvent( event );
364 d->setWeakEditorMode(
true );
371 qDeleteAll( mNumericFormats );
372 mNumericFormats.clear();
375 int rowNumber = mIncludeHeader ? 1 : 0;
377 setRowCount( contents.size() + rowNumber );
382 setColumnCount( row.size() );
390 item->setData( CellContent, col.content() );
391 item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
392 item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
393 item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
394 item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
395 item->setData( HorizontalAlignment,
static_cast<int>( col.horizontalAlignment() ) );
396 item->setData( VerticalAlignment,
static_cast<int>( col.verticalAlignment() ) );
397 item->setData( CellProperty, QVariant::fromValue( col.content().value<
QgsProperty>() ) );
399 if ( col.content().value<
QgsProperty>().isActive() )
400 item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
402 if (
auto *lNumericFormat = col.numericFormat() )
404 mNumericFormats.insert( item, lNumericFormat->clone() );
405 item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
407 setItem( rowNumber, colNumber, item );
409 if ( col.rowSpan() > 1 || col.columnSpan() > 1 )
410 setSpan( rowNumber, colNumber, col.rowSpan(), col.columnSpan() );
422 resizeColumnsToContents();
423 resizeRowsToContents();
432 items.reserve( rowCount() );
434 QSet<QPair<int, int>> spannedCells;
435 for (
int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
438 row.reserve( columnCount() );
439 for (
int c = 0;
c < columnCount();
c++ )
442 if ( QTableWidgetItem *i = item( r,
c ) )
444 cell.
setContent( i->data( CellProperty ).value<
QgsProperty>().isActive() ? i->data( CellProperty ) : i->data( CellContent ) );
448 cell.
setVerticalAlignment(
static_cast<Qt::Alignment
>( i->data( VerticalAlignment ).toInt() ) );
451 if ( !spannedCells.contains( qMakePair( r,
c ) ) )
453 const int rowsSpan = rowSpan( r,
c );
454 const int colsSpan = columnSpan( r,
c );
455 for (
int spannedRow = r; spannedRow < r + rowsSpan; ++spannedRow )
457 for (
int spannedCol =
c; spannedCol <
c + colsSpan; ++spannedCol )
459 spannedCells.insert( qMakePair( spannedRow, spannedCol ) );
462 cell.
setSpan( rowSpan( r,
c ), columnSpan( r,
c ) );
469 if ( mNumericFormats.value( i ) )
474 row.push_back( cell );
476 items.push_back( row );
484 bool changed =
false;
486 std::unique_ptr<QgsNumericFormat> newFormat( format );
487 const QModelIndexList selection = selectedIndexes();
489 for (
const QModelIndex &index : selection )
491 if ( index.row() == 0 && mIncludeHeader )
494 QTableWidgetItem *i = item( index.row(), index.column() );
497 i =
new QTableWidgetItem();
498 setItem( index.row(), index.column(), i );
500 if ( !mNumericFormats.value( i ) && newFormat )
503 mNumericFormats.insert( i, newFormat->clone() );
505 else if ( mNumericFormats.value( i ) && !newFormat )
508 delete mNumericFormats.value( i );
509 mNumericFormats.remove( i );
511 else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
514 delete mNumericFormats.value( i );
515 mNumericFormats.insert( i, newFormat->clone() );
517 i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
520 if ( changed && !mBlockSignals )
528 const QModelIndexList selection = selectedIndexes();
529 for (
const QModelIndex &index : selection )
531 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
535 f = mNumericFormats.value( i );
538 else if ( ( !f && !mNumericFormats.value( i ) )
539 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
558 const QModelIndexList selection = selectedIndexes();
559 for (
const QModelIndex &index : selection )
561 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
565 f = mNumericFormats.value( i );
568 else if ( ( !f && !mNumericFormats.value( i ) )
569 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
594 const QModelIndexList selection = selectedIndexes();
595 for (
const QModelIndex &index : selection )
597 QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value<QColor>() : QColor();
603 else if ( indexColor ==
c )
615 Qt::Alignment alignment = Qt::AlignLeft;
617 const QModelIndexList selection = selectedIndexes();
618 for (
const QModelIndex &index : selection )
620 Qt::Alignment cellAlign =
static_cast<Qt::Alignment
>( model()->data( index, HorizontalAlignment ).toInt() );
623 alignment = cellAlign;
626 else if ( cellAlign == alignment )
630 return Qt::AlignLeft | Qt::AlignTop;
638 Qt::Alignment alignment = Qt::AlignVCenter;
640 const QModelIndexList selection = selectedIndexes();
641 for (
const QModelIndex &index : selection )
643 Qt::Alignment cellAlign =
static_cast<Qt::Alignment
>( model()->data( index, VerticalAlignment ).toInt() );
646 alignment = cellAlign;
649 else if ( cellAlign == alignment )
653 return Qt::AlignLeft | Qt::AlignTop;
663 const QModelIndexList selection = selectedIndexes();
664 for (
const QModelIndex &index : selection )
669 property = cellProperty;
672 else if ( cellProperty == property )
686 const QModelIndexList selection = selectedIndexes();
687 for (
const QModelIndex &index : selection )
689 if ( !model()->data( index, TextFormat ).isValid() )
698 else if ( cellFormat == format )
710 const QModelIndexList selection = selectedIndexes();
711 for (
const QModelIndex &index : selection )
716 else if ( thisHeight != height )
729 const QModelIndexList selection = selectedIndexes();
730 for (
const QModelIndex &index : selection )
735 else if ( thisWidth != width )
747 for (
int col = 0; col < columnCount(); ++col )
749 double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
750 height = std::max( thisHeight, height );
758 for (
int row = 0; row < rowCount(); ++row )
760 double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
761 width = std::max( thisWidth, width );
768 if ( row == 0 && mIncludeHeader )
771 bool changed =
false;
774 for (
int col = 0; col < columnCount(); ++col )
776 if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
778 if ( i->data( RowHeight ).toDouble() != height )
780 i->setData( RowHeight, height );
786 QTableWidgetItem *newItem =
new QTableWidgetItem();
787 newItem->setData( RowHeight, height );
788 setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
794 if ( changed && !mBlockSignals )
800 bool changed =
false;
802 for (
int row = 0; row < rowCount(); ++row )
804 if ( QTableWidgetItem *i = item( row, col ) )
806 if ( i->data( ColumnWidth ).toDouble() != width )
808 i->setData( ColumnWidth, width );
814 QTableWidgetItem *newItem =
new QTableWidgetItem();
815 newItem->setData( ColumnWidth, width );
816 setItem( row, col, newItem );
821 if ( changed && !mBlockSignals )
827 return collectUniqueRows( selectedIndexes() );
832 return collectUniqueColumns( selectedIndexes() );
837 if ( !mIncludeHeader )
838 return QVariantList();
841 res.reserve( columnCount() );
842 for (
int col = 0; col < columnCount(); ++col )
844 if ( QTableWidgetItem *i = item( 0, col ) )
846 res << i->data( CellContent );
858 if ( !mIncludeHeader )
861 return collectUniqueRows( selectedIndexes() ).contains( 0 );
866 return selectedIndexes().size() > 1
868 && isRectangularSelection( selectedIndexes() );
873 return !selectedIndexes().empty()
875 && hasMergedCells( selectedIndexes() );
880 if ( rowCount() == 0 )
888 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
891 const int rowsToInsert = maxRow - minRow + 1;
892 for (
int i = 0; i < rowsToInsert; ++i )
893 insertRow( maxRow + 1 );
896 if ( !mBlockSignals )
902 if ( rowCount() == 0 )
910 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
913 const int rowsToInsert = maxRow - minRow + 1;
914 for (
int i = 0; i < rowsToInsert; ++i )
918 if ( !mBlockSignals )
924 if ( columnCount() == 0 )
932 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
935 const int columnsToInsert = maxColumn - minColumn + 1;
936 for (
int i = 0; i < columnsToInsert; ++i )
937 insertColumn( minColumn );
940 if ( !mBlockSignals )
946 if ( columnCount() == 0 )
954 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
957 const int columnsToInsert = maxColumn - minColumn + 1;
958 for (
int i = 0; i < columnsToInsert; ++i )
959 insertColumn( maxColumn + 1 );
962 if ( !mBlockSignals )
972 bool changed =
false;
973 for (
int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
975 removeRow( rows.at( i ) );
979 if ( changed && !mBlockSignals )
986 if ( columns.empty() )
989 bool changed =
false;
990 for (
int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
992 removeColumn( columns.at( i ) );
996 if ( !mBlockSignals && changed )
1002 const QModelIndexList s = selectedIndexes();
1003 for (
const QModelIndex &index : s )
1005 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
1011 const QModelIndexList s = selectedIndexes();
1012 for (
const QModelIndex &index : s )
1014 selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
1020 const QModelIndexList selection = selectedIndexes();
1021 bool changed =
false;
1023 for (
const QModelIndex &index : selection )
1025 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1027 i->setText( QString() );
1028 i->setData( CellContent, QVariant() );
1033 if ( changed && !mBlockSignals )
1039 const QModelIndexList selection = selectedIndexes();
1040 bool changed =
false;
1042 for (
const QModelIndex &index : selection )
1044 if ( index.row() == 0 && mIncludeHeader )
1047 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1049 if ( i->data( Qt::ForegroundRole ).value<QColor>() != color )
1051 i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1054 i->setData( TextFormat, QVariant::fromValue( f ) );
1060 QTableWidgetItem *newItem =
new QTableWidgetItem();
1061 newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1064 newItem->setData( TextFormat, QVariant::fromValue( f ) );
1065 setItem( index.row(), index.column(), newItem );
1070 if ( changed && !mBlockSignals )
1076 const QModelIndexList selection = selectedIndexes();
1077 bool changed =
false;
1079 for (
const QModelIndex &index : selection )
1081 if ( index.row() == 0 && mIncludeHeader )
1084 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1086 if ( i->data( PresetBackgroundColorRole ).value<QColor>() != color )
1088 i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1089 i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1095 QTableWidgetItem *newItem =
new QTableWidgetItem();
1096 newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1097 newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1098 setItem( index.row(), index.column(), newItem );
1103 if ( changed && !mBlockSignals )
1109 const QModelIndexList selection = selectedIndexes();
1110 bool changed =
false;
1112 for (
const QModelIndex &index : selection )
1114 if ( index.row() == 0 && mIncludeHeader )
1117 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1119 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1121 i->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1127 QTableWidgetItem *newItem =
new QTableWidgetItem();
1128 newItem->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1129 setItem( index.row(), index.column(), newItem );
1134 if ( changed && !mBlockSignals )
1140 const QModelIndexList selection = selectedIndexes();
1141 bool changed =
false;
1143 for (
const QModelIndex &index : selection )
1145 if ( index.row() == 0 && mIncludeHeader )
1148 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1150 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1152 i->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1158 QTableWidgetItem *newItem =
new QTableWidgetItem();
1159 newItem->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1160 setItem( index.row(), index.column(), newItem );
1165 if ( changed && !mBlockSignals )
1171 const QModelIndexList selection = selectedIndexes();
1172 bool changed =
false;
1174 for (
const QModelIndex &index : selection )
1176 if ( index.row() == 0 && mIncludeHeader )
1179 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1181 if ( i->data( CellProperty ).value<
QgsProperty>() != property )
1185 i->setData( CellProperty, QVariant::fromValue( property ) );
1187 i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1191 i->setData( CellProperty, QVariant() );
1192 i->setText( QString() );
1193 i->setFlags( i->flags() | Qt::ItemIsEditable );
1200 QTableWidgetItem *newItem =
new QTableWidgetItem( property.
asExpression() );
1203 newItem->setData( CellProperty, QVariant::fromValue( property ) );
1204 newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1208 newItem->setData( CellProperty, QVariant() );
1209 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1211 setItem( index.row(), index.column(), newItem );
1216 if ( changed && !mBlockSignals )
1222 const QModelIndexList selection = selectedIndexes();
1223 bool changed =
false;
1225 for (
const QModelIndex &index : selection )
1227 if ( index.row() == 0 && mIncludeHeader )
1230 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1232 i->setData( TextFormat, QVariant::fromValue( format ) );
1233 i->setData( Qt::ForegroundRole, format.
color() );
1238 QTableWidgetItem *newItem =
new QTableWidgetItem();
1239 newItem->setData( TextFormat, QVariant::fromValue( format ) );
1240 newItem->setData( Qt::ForegroundRole, format.
color() );
1241 setItem( index.row(), index.column(), newItem );
1246 if ( changed && !mBlockSignals )
1252 bool changed =
false;
1255 for (
int row : rows )
1257 if ( row == 0 && mIncludeHeader )
1260 for (
int col = 0; col < columnCount(); ++col )
1262 if ( QTableWidgetItem *i = item( row, col ) )
1264 if ( i->data( RowHeight ).toDouble() != height )
1266 i->setData( RowHeight, height );
1272 QTableWidgetItem *newItem =
new QTableWidgetItem();
1273 newItem->setData( RowHeight, height );
1274 setItem( row, col, newItem );
1280 if ( changed && !mBlockSignals )
1286 bool changed =
false;
1289 for (
int col : cols )
1291 for (
int row = 0; row < rowCount(); ++row )
1293 if ( QTableWidgetItem *i = item( row, col ) )
1295 if ( i->data( ColumnWidth ).toDouble() != width )
1297 i->setData( ColumnWidth, width );
1303 QTableWidgetItem *newItem =
new QTableWidgetItem();
1304 newItem->setData( ColumnWidth, width );
1305 setItem( row, col, newItem );
1311 if ( changed && !mBlockSignals )
1317 if ( included == mIncludeHeader )
1320 mIncludeHeader = included;
1322 if ( mIncludeHeader )
1331 if ( !mIncludeHeader )
1336 for (
int col = 0; col < columnCount(); ++col )
1338 if ( QTableWidgetItem *i = item( 0, col ) )
1340 i->setText( headers.value( col ).toString() );
1341 i->setData( CellContent, headers.value( col ) );
1345 QTableWidgetItem *item =
new QTableWidgetItem( headers.value( col ).toString() );
1346 item->setData( CellContent, headers.value( col ) );
1347 setItem( 0, col, item );
1355 const QModelIndexList selection = selectedIndexes();
1356 if ( selection.size() < 2 )
1363 for (
const QModelIndex &index : selection )
1365 if ( minRow == -1 || index.row() < minRow )
1366 minRow = index.row();
1367 if ( maxRow == -1 || index.row() > maxRow )
1368 maxRow = index.row();
1369 if ( minCol == -1 || index.column() < minCol )
1370 minCol = index.column();
1371 if ( maxCol == -1 || index.column() > maxCol )
1372 maxCol = index.column();
1374 QStringList mergedCellText;
1375 for (
int row = minRow; row <= maxRow; ++row )
1377 for (
int col = minCol; col <= maxCol; ++col )
1379 if ( QTableWidgetItem *i = item( row, col ) )
1381 const QString content = i->data( CellContent ).toString();
1382 if ( !content.isEmpty() )
1384 mergedCellText.append( content );
1390 setSpan( minRow, minCol, maxRow - minRow + 1, maxCol - minCol + 1 );
1393 if ( !mergedCellText.isEmpty() )
1395 if ( QTableWidgetItem *i = item( minRow, minCol ) )
1397 i->setText( mergedCellText.join(
' ' ) );
1398 i->setData( CellContent, i->text() );
1402 if ( !mBlockSignals )
1408 const QModelIndexList selection = selectedIndexes();
1409 for (
const QModelIndex &index : selection )
1411 if ( rowSpan( index.row(), index.column() ) > 1 || columnSpan( index.row(), index.column() ) > 1 )
1412 setSpan( index.row(), index.column(), 1, 1 );
1415 if ( !mBlockSignals )
1421QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1422 : QPlainTextEdit( parent )
1425 document()->setDocumentMargin( document()->documentMargin() / 2 );
1427 connect(
this, &QPlainTextEdit::textChanged,
this, &QgsTableEditorTextEdit::resizeToContents );
1428 updateMinimumSize();
1431void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1433 switch ( event->key() )
1436 case Qt::Key_Return:
1438 if ( event->modifiers() & Qt::ControlModifier )
1441 insertPlainText( QString(
'\n' ) );
1457 if ( mWeakEditorMode )
1464 QPlainTextEdit::keyPressEvent( event );
1471 if ( event->modifiers() & Qt::ControlModifier )
1475 insertPlainText( QString(
'\t' ) );
1486 QPlainTextEdit::keyPressEvent( event );
1490void QgsTableEditorTextEdit::updateMinimumSize()
1492 const double tm = document()->documentMargin();
1493 const QMargins cm = contentsMargins();
1494 const int width = tm * 2 + cm.left() + cm.right() + 30;
1495 const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1496 QStyleOptionFrame opt;
1497 initStyleOption( &opt );
1498 const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ),
this );
1499 setMinimumWidth( sizeFromContent.width() );
1500 setMinimumHeight( sizeFromContent.height() );
1503void QgsTableEditorTextEdit::setWeakEditorMode(
bool weakEditorMode )
1505 mWeakEditorMode = weakEditorMode;
1508void QgsTableEditorTextEdit::resizeToContents()
1510 int oldWidth = width();
1511 int oldHeight = height();
1512 if ( mOriginalWidth == -1 )
1513 mOriginalWidth = oldWidth;
1514 if ( mOriginalHeight == -1 )
1515 mOriginalHeight = oldHeight;
1517 if ( QWidget *parent = parentWidget() )
1519 QPoint position = pos();
1520 QFontMetrics fm( font() );
1522 const QStringList lines = toPlainText().split(
'\n' );
1523 int maxTextLineWidth = 0;
1524 int totalTextHeight = 0;
1525 for (
const QString &line : lines )
1527 const QRect bounds = fontMetrics().boundingRect( line );
1528 maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1529 totalTextHeight += fm.height();
1532 int hintWidth = minimumWidth() + maxTextLineWidth;
1533 int hintHeight = minimumHeight() + totalTextHeight;
1534 int parentWidth = parent->width();
1535 int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1536 int maxHeight = parent->height() - position.y();
1537 int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1538 int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1540 if ( mWidgetOwnsGeometry )
1542 setMaximumWidth( newWidth );
1543 setMaximumHeight( newHeight );
1545 if ( isRightToLeft() )
1546 move( position.x() - newWidth + oldWidth, position.y() );
1547 resize( newWidth, newHeight );
1551void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1553 switch ( e->type() )
1555 case QEvent::FontChange:
1556 case QEvent::StyleChange:
1557 case QEvent::ContentsRectChange:
1558 updateMinimumSize();
1563 QPlainTextEdit::changeEvent( e );
1566QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1567 : QStyledItemDelegate( parent )
1571void QgsTableEditorDelegate::setWeakEditorMode(
bool weakEditorMode )
1573 mWeakEditorMode = weakEditorMode;
1576QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex & )
const
1578 QgsTableEditorTextEdit *w =
new QgsTableEditorTextEdit( parent );
1579 w->setWeakEditorMode( mWeakEditorMode );
1581 if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame, 0, w ) )
1582 w->setFrameShape( QFrame::NoFrame );
1583 if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected, 0, w ) )
1584 w->setWidgetOwnsGeometry(
true );
1589void QgsTableEditorDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
1591 QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1592 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1594 if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1596 lineEdit->setPlainText( value.toString() );
1597 lineEdit->selectAll();
1603void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
1605 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1607 const QString text = lineEdit->toPlainText();
1608 if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value<
QgsProperty>().
isActive() )
1610 model->setData( index, text, QgsTableEditorWidget::CellContent );
1611 model->setData( index, text, Qt::DisplayRole );
1612 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.