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 || columnSpan( index.row(), index.column() ) > 1 )
344 switch ( event->key() )
350 QTableWidget::keyPressEvent( event );
351 setCurrentCell( currentRow() + 1, currentColumn() );
362 QTableWidget::keyPressEvent( event );
366 d->setWeakEditorMode(
true );
373 qDeleteAll( mNumericFormats );
374 mNumericFormats.clear();
377 int rowNumber = mIncludeHeader ? 1 : 0;
379 setRowCount( contents.size() + rowNumber );
384 setColumnCount( row.size() );
392 item->setData( CellContent, col.content() );
393 item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
394 item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
395 item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
396 item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
397 item->setData( HorizontalAlignment,
static_cast<int>( col.horizontalAlignment() ) );
398 item->setData( VerticalAlignment,
static_cast<int>( col.verticalAlignment() ) );
399 item->setData( CellProperty, QVariant::fromValue( col.content().value<
QgsProperty>() ) );
402 item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
404 if (
auto *lNumericFormat = col.numericFormat() )
406 mNumericFormats.insert( item, lNumericFormat->clone() );
407 item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
409 setItem( rowNumber, colNumber, item );
411 if ( col.rowSpan() > 1 || col.columnSpan() > 1 )
412 setSpan( rowNumber, colNumber, col.rowSpan(), col.columnSpan() );
424 resizeColumnsToContents();
425 resizeRowsToContents();
434 items.reserve( rowCount() );
436 QSet<QPair<int, int>> spannedCells;
437 for (
int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
440 row.reserve( columnCount() );
441 for (
int c = 0;
c < columnCount();
c++ )
444 if ( QTableWidgetItem *i = item( r,
c ) )
450 cell.
setVerticalAlignment(
static_cast<Qt::Alignment
>( i->data( VerticalAlignment ).toInt() ) );
453 if ( !spannedCells.contains( qMakePair( r,
c ) ) )
455 const int rowsSpan = rowSpan( r,
c );
456 const int colsSpan = columnSpan( r,
c );
457 for (
int spannedRow = r; spannedRow < r + rowsSpan; ++spannedRow )
459 for (
int spannedCol =
c; spannedCol <
c + colsSpan; ++spannedCol )
461 spannedCells.insert( qMakePair( spannedRow, spannedCol ) );
464 cell.
setSpan( rowSpan( r,
c ), columnSpan( r,
c ) );
471 if ( mNumericFormats.value( i ) )
476 row.push_back( cell );
478 items.push_back( row );
486 bool changed =
false;
488 std::unique_ptr<QgsNumericFormat> newFormat( format );
489 const QModelIndexList selection = selectedIndexes();
491 for (
const QModelIndex &index : selection )
493 if ( index.row() == 0 && mIncludeHeader )
496 QTableWidgetItem *i = item( index.row(), index.column() );
499 i =
new QTableWidgetItem();
500 setItem( index.row(), index.column(), i );
502 if ( !mNumericFormats.value( i ) && newFormat )
505 mNumericFormats.insert( i, newFormat->clone() );
507 else if ( mNumericFormats.value( i ) && !newFormat )
510 delete mNumericFormats.value( i );
511 mNumericFormats.remove( i );
513 else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
516 delete mNumericFormats.value( i );
517 mNumericFormats.insert( i, newFormat->clone() );
519 i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
522 if ( changed && !mBlockSignals )
530 const QModelIndexList selection = selectedIndexes();
531 for (
const QModelIndex &index : selection )
533 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
537 f = mNumericFormats.value( i );
540 else if ( ( !f && !mNumericFormats.value( i ) ) || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
559 const QModelIndexList selection = selectedIndexes();
560 for (
const QModelIndex &index : selection )
562 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
566 f = mNumericFormats.value( i );
569 else if ( ( !f && !mNumericFormats.value( i ) ) || ( 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() )
695 format = std::move( cellFormat );
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 && !
isHeaderCellSelected() && isRectangularSelection( selectedIndexes() );
871 return !selectedIndexes().empty() && !
isHeaderCellSelected() && hasMergedCells( selectedIndexes() );
876 if ( rowCount() == 0 )
884 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
887 const int rowsToInsert = maxRow - minRow + 1;
888 for (
int i = 0; i < rowsToInsert; ++i )
889 insertRow( maxRow + 1 );
892 if ( !mBlockSignals )
898 if ( rowCount() == 0 )
906 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
909 const int rowsToInsert = maxRow - minRow + 1;
910 for (
int i = 0; i < rowsToInsert; ++i )
914 if ( !mBlockSignals )
920 if ( columnCount() == 0 )
928 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
931 const int columnsToInsert = maxColumn - minColumn + 1;
932 for (
int i = 0; i < columnsToInsert; ++i )
933 insertColumn( minColumn );
936 if ( !mBlockSignals )
942 if ( columnCount() == 0 )
950 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
953 const int columnsToInsert = maxColumn - minColumn + 1;
954 for (
int i = 0; i < columnsToInsert; ++i )
955 insertColumn( maxColumn + 1 );
958 if ( !mBlockSignals )
968 bool changed =
false;
969 for (
int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
971 removeRow( rows.at( i ) );
975 if ( changed && !mBlockSignals )
982 if ( columns.empty() )
985 bool changed =
false;
986 for (
int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
988 removeColumn( columns.at( i ) );
992 if ( !mBlockSignals && changed )
998 const QModelIndexList s = selectedIndexes();
999 for (
const QModelIndex &index : s )
1001 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
1007 const QModelIndexList s = selectedIndexes();
1008 for (
const QModelIndex &index : s )
1010 selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
1016 const QModelIndexList selection = selectedIndexes();
1017 bool changed =
false;
1019 for (
const QModelIndex &index : selection )
1021 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1023 i->setText( QString() );
1024 i->setData( CellContent, QVariant() );
1029 if ( changed && !mBlockSignals )
1035 const QModelIndexList selection = selectedIndexes();
1036 bool changed =
false;
1038 for (
const QModelIndex &index : selection )
1040 if ( index.row() == 0 && mIncludeHeader )
1043 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1045 if ( i->data( Qt::ForegroundRole ).value<QColor>() != color )
1047 i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1050 i->setData( TextFormat, QVariant::fromValue( f ) );
1056 QTableWidgetItem *newItem =
new QTableWidgetItem();
1057 newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
1060 newItem->setData( TextFormat, QVariant::fromValue( f ) );
1061 setItem( index.row(), index.column(), newItem );
1066 if ( changed && !mBlockSignals )
1072 const QModelIndexList selection = selectedIndexes();
1073 bool changed =
false;
1075 for (
const QModelIndex &index : selection )
1077 if ( index.row() == 0 && mIncludeHeader )
1080 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1082 if ( i->data( PresetBackgroundColorRole ).value<QColor>() != color )
1084 i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1085 i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1091 QTableWidgetItem *newItem =
new QTableWidgetItem();
1092 newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
1093 newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
1094 setItem( index.row(), index.column(), newItem );
1099 if ( changed && !mBlockSignals )
1105 const QModelIndexList selection = selectedIndexes();
1106 bool changed =
false;
1108 for (
const QModelIndex &index : selection )
1110 if ( index.row() == 0 && mIncludeHeader )
1113 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1115 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1117 i->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1123 QTableWidgetItem *newItem =
new QTableWidgetItem();
1124 newItem->setData( HorizontalAlignment,
static_cast<int>( alignment ) );
1125 setItem( index.row(), index.column(), newItem );
1130 if ( changed && !mBlockSignals )
1136 const QModelIndexList selection = selectedIndexes();
1137 bool changed =
false;
1139 for (
const QModelIndex &index : selection )
1141 if ( index.row() == 0 && mIncludeHeader )
1144 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1146 if (
static_cast<Qt::Alignment
>( i->data( HorizontalAlignment ).toInt() ) != alignment )
1148 i->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1154 QTableWidgetItem *newItem =
new QTableWidgetItem();
1155 newItem->setData( VerticalAlignment,
static_cast<int>( alignment ) );
1156 setItem( index.row(), index.column(), newItem );
1161 if ( changed && !mBlockSignals )
1167 const QModelIndexList selection = selectedIndexes();
1168 bool changed =
false;
1170 for (
const QModelIndex &index : selection )
1172 if ( index.row() == 0 && mIncludeHeader )
1175 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1177 if ( i->data( CellProperty ).value<
QgsProperty>() != property )
1181 i->setData( CellProperty, QVariant::fromValue( property ) );
1183 i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1187 i->setData( CellProperty, QVariant() );
1188 i->setText( QString() );
1189 i->setFlags( i->flags() | Qt::ItemIsEditable );
1196 QTableWidgetItem *newItem =
new QTableWidgetItem( property.
asExpression() );
1199 newItem->setData( CellProperty, QVariant::fromValue( property ) );
1200 newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1204 newItem->setData( CellProperty, QVariant() );
1205 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1207 setItem( index.row(), index.column(), newItem );
1212 if ( changed && !mBlockSignals )
1218 const QModelIndexList selection = selectedIndexes();
1219 bool changed =
false;
1221 for (
const QModelIndex &index : selection )
1223 if ( index.row() == 0 && mIncludeHeader )
1226 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1228 i->setData( TextFormat, QVariant::fromValue( format ) );
1229 i->setData( Qt::ForegroundRole, format.
color() );
1234 QTableWidgetItem *newItem =
new QTableWidgetItem();
1235 newItem->setData( TextFormat, QVariant::fromValue( format ) );
1236 newItem->setData( Qt::ForegroundRole, format.
color() );
1237 setItem( index.row(), index.column(), newItem );
1242 if ( changed && !mBlockSignals )
1248 bool changed =
false;
1251 for (
int row : rows )
1253 if ( row == 0 && mIncludeHeader )
1256 for (
int col = 0; col < columnCount(); ++col )
1258 if ( QTableWidgetItem *i = item( row, col ) )
1260 if ( i->data( RowHeight ).toDouble() != height )
1262 i->setData( RowHeight, height );
1268 QTableWidgetItem *newItem =
new QTableWidgetItem();
1269 newItem->setData( RowHeight, height );
1270 setItem( row, col, newItem );
1276 if ( changed && !mBlockSignals )
1282 bool changed =
false;
1285 for (
int col : cols )
1287 for (
int row = 0; row < rowCount(); ++row )
1289 if ( QTableWidgetItem *i = item( row, col ) )
1291 if ( i->data( ColumnWidth ).toDouble() != width )
1293 i->setData( ColumnWidth, width );
1299 QTableWidgetItem *newItem =
new QTableWidgetItem();
1300 newItem->setData( ColumnWidth, width );
1301 setItem( row, col, newItem );
1307 if ( changed && !mBlockSignals )
1313 if ( included == mIncludeHeader )
1316 mIncludeHeader = included;
1318 if ( mIncludeHeader )
1327 if ( !mIncludeHeader )
1332 for (
int col = 0; col < columnCount(); ++col )
1334 if ( QTableWidgetItem *i = item( 0, col ) )
1336 i->setText( headers.value( col ).toString() );
1337 i->setData( CellContent, headers.value( col ) );
1341 QTableWidgetItem *item =
new QTableWidgetItem( headers.value( col ).toString() );
1342 item->setData( CellContent, headers.value( col ) );
1343 setItem( 0, col, item );
1351 const QModelIndexList selection = selectedIndexes();
1352 if ( selection.size() < 2 )
1359 for (
const QModelIndex &index : selection )
1361 if ( minRow == -1 || index.row() < minRow )
1362 minRow = index.row();
1363 if ( maxRow == -1 || index.row() > maxRow )
1364 maxRow = index.row();
1365 if ( minCol == -1 || index.column() < minCol )
1366 minCol = index.column();
1367 if ( maxCol == -1 || index.column() > maxCol )
1368 maxCol = index.column();
1370 QStringList mergedCellText;
1371 for (
int row = minRow; row <= maxRow; ++row )
1373 for (
int col = minCol; col <= maxCol; ++col )
1375 if ( QTableWidgetItem *i = item( row, col ) )
1377 const QString content = i->data( CellContent ).toString();
1378 if ( !content.isEmpty() )
1380 mergedCellText.append( content );
1386 setSpan( minRow, minCol, maxRow - minRow + 1, maxCol - minCol + 1 );
1389 if ( !mergedCellText.isEmpty() )
1391 if ( QTableWidgetItem *i = item( minRow, minCol ) )
1393 i->setText( mergedCellText.join(
' ' ) );
1394 i->setData( CellContent, i->text() );
1398 if ( !mBlockSignals )
1404 const QModelIndexList selection = selectedIndexes();
1405 for (
const QModelIndex &index : selection )
1407 if ( rowSpan( index.row(), index.column() ) > 1 || columnSpan( index.row(), index.column() ) > 1 )
1408 setSpan( index.row(), index.column(), 1, 1 );
1411 if ( !mBlockSignals )
1417QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1418 : QPlainTextEdit( parent )
1421 document()->setDocumentMargin( document()->documentMargin() / 2 );
1423 connect(
this, &QPlainTextEdit::textChanged,
this, &QgsTableEditorTextEdit::resizeToContents );
1424 updateMinimumSize();
1427void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1429 switch ( event->key() )
1432 case Qt::Key_Return:
1434 if ( event->modifiers() & Qt::ControlModifier )
1437 insertPlainText( QString(
'\n' ) );
1453 if ( mWeakEditorMode )
1460 QPlainTextEdit::keyPressEvent( event );
1467 if ( event->modifiers() & Qt::ControlModifier )
1471 insertPlainText( QString(
'\t' ) );
1482 QPlainTextEdit::keyPressEvent( event );
1486void QgsTableEditorTextEdit::updateMinimumSize()
1488 const double tm = document()->documentMargin();
1489 const QMargins cm = contentsMargins();
1490 const int width = tm * 2 + cm.left() + cm.right() + 30;
1491 const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1492 QStyleOptionFrame opt;
1493 initStyleOption( &opt );
1494 const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ),
this );
1495 setMinimumWidth( sizeFromContent.width() );
1496 setMinimumHeight( sizeFromContent.height() );
1499void QgsTableEditorTextEdit::setWeakEditorMode(
bool weakEditorMode )
1501 mWeakEditorMode = weakEditorMode;
1504void QgsTableEditorTextEdit::resizeToContents()
1506 int oldWidth = width();
1507 int oldHeight = height();
1508 if ( mOriginalWidth == -1 )
1509 mOriginalWidth = oldWidth;
1510 if ( mOriginalHeight == -1 )
1511 mOriginalHeight = oldHeight;
1513 if ( QWidget *parent = parentWidget() )
1515 QPoint position = pos();
1516 QFontMetrics fm( font() );
1518 const QStringList lines = toPlainText().split(
'\n' );
1519 int maxTextLineWidth = 0;
1520 int totalTextHeight = 0;
1521 for (
const QString &line : lines )
1523 const QRect bounds = fontMetrics().boundingRect( line );
1524 maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1525 totalTextHeight += fm.height();
1528 int hintWidth = minimumWidth() + maxTextLineWidth;
1529 int hintHeight = minimumHeight() + totalTextHeight;
1530 int parentWidth = parent->width();
1531 int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1532 int maxHeight = parent->height() - position.y();
1533 int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1534 int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1536 if ( mWidgetOwnsGeometry )
1538 setMaximumWidth( newWidth );
1539 setMaximumHeight( newHeight );
1541 if ( isRightToLeft() )
1542 move( position.x() - newWidth + oldWidth, position.y() );
1543 resize( newWidth, newHeight );
1547void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1549 switch ( e->type() )
1551 case QEvent::FontChange:
1552 case QEvent::StyleChange:
1553 case QEvent::ContentsRectChange:
1554 updateMinimumSize();
1559 QPlainTextEdit::changeEvent( e );
1562QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1563 : QStyledItemDelegate( parent )
1566void QgsTableEditorDelegate::setWeakEditorMode(
bool weakEditorMode )
1568 mWeakEditorMode = weakEditorMode;
1571QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex & )
const
1573 QgsTableEditorTextEdit *w =
new QgsTableEditorTextEdit( parent );
1574 w->setWeakEditorMode( mWeakEditorMode );
1576 if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame,
nullptr, w ) )
1577 w->setFrameShape( QFrame::NoFrame );
1578 if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected,
nullptr, w ) )
1579 w->setWidgetOwnsGeometry(
true );
1584void QgsTableEditorDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
1586 QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1587 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1589 if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1591 lineEdit->setPlainText( value.toString() );
1592 lineEdit->selectAll();
1598void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
1600 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit *>( editor ) )
1602 const QString text = lineEdit->toPlainText();
1603 if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value<
QgsProperty>().
isActive() )
1605 model->setData( index, text, QgsTableEditorWidget::CellContent );
1606 model->setData( index, text, Qt::DisplayRole );
1607 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.