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, [
this] {
 
   39  setContextMenuPolicy( Qt::CustomContextMenu );
 
   40  connect( 
this, &QWidget::customContextMenuRequested, 
this, [
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, [
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, [
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, [
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.