QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgstableeditorwidget.cpp
Go to the documentation of this file.
1 // This file is part of CppSheets.
2 //
3 // Copyright 2018 Patrick Flynn <[email protected]>
4 //
5 // CppSheets is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // CppSheets is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with CppSheets. If not, see <https://www.gnu.org/licenses/>.
17 
18 #include "qgstableeditorwidget.h"
19 #include "qgsnumericformat.h"
20 #include <QStringList>
21 #include <QKeyEvent>
22 #include <QHeaderView>
23 #include <QMenu>
24 #include <QPlainTextEdit>
25 
27  : QTableWidget( parent )
28 {
29  mHeaderMenu = new QMenu( this );
30  setColumnCount( 0 );
31  setRowCount( 0 );
32  connect( this, &QgsTableEditorWidget::cellChanged, this, [ = ]
33  {
34  if ( !mBlockSignals )
35  emit tableChanged();
36  } );
37 
38  horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
39  connect( horizontalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
40  {
41  const int column = horizontalHeader()->logicalIndexAt( point.x() );
42 
43  QSet< int > selectedColumns;
44  for ( const QModelIndex &index : selectedIndexes() )
45  {
46  selectedColumns.insert( index.column() );
47  }
48  int minCol = 0;
49  int maxCol = 0;
50  bool isConsecutive = collectConsecutiveColumnRange( selectedIndexes(), minCol, maxCol );
51 
52  // this is modeled off Libreoffice calc!
53  if ( selectedIndexes().count() == 1 )
54  {
55  // select whole column
56  selectColumn( column );
57  isConsecutive = true;
58  }
59  else if ( !selectedColumns.contains( column ) )
60  {
61  // select whole column
62  selectColumn( column );
63  isConsecutive = true;
64  }
65 
66  mHeaderMenu->clear();
67  if ( isConsecutive )
68  {
69  QAction *insertBefore = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %1 Columns Before" ).arg( selectedColumns.size() ) : tr( "Insert Column Before" ) );
70  connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsBefore );
71  QAction *insertAfter = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %1 Columns After" ).arg( selectedColumns.size() ) : tr( "Insert Column After" ) );
72  connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsAfter );
73  }
74  QAction *deleteSelected = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Delete %1 Columns" ).arg( selectedColumns.size() ) : tr( "Delete Column" ) );
75  connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteColumns );
76 
77  mHeaderMenu->popup( horizontalHeader()->mapToGlobal( point ) );
78  } );
79 
80  verticalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
81  connect( verticalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
82  {
83  const int row = verticalHeader()->logicalIndexAt( point.y() );
84 
85  QSet< int > selectedRows;
86  for ( const QModelIndex &index : selectedIndexes() )
87  {
88  selectedRows.insert( index.row() );
89  }
90  int minRow = 0;
91  int maxRow = 0;
92  bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
93 
94  // this is modeled off Libreoffice calc!
95  if ( selectedIndexes().count() == 1 )
96  {
97  // select whole row
98  selectRow( row );
99  isConsecutive = true;
100  }
101  else if ( !selectedRows.contains( row ) )
102  {
103  // select whole row
104  selectRow( row );
105  isConsecutive = true;
106  }
107 
108  mHeaderMenu->clear();
109  if ( isConsecutive )
110  {
111  QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %1 Rows Above" ).arg( selectedRows.size() ) : tr( "Insert Row Above" ) );
112  connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsAbove );
113  QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %1 Rows Below" ).arg( selectedRows.size() ) : tr( "Insert Row Below" ) );
114  connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsBelow );
115  }
116  QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Delete %1 Rows" ).arg( selectedRows.size() ) : tr( "Delete Row" ) );
117  connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteRows );
118 
119  mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
120  } );
121 
122 
123  QgsTableEditorDelegate *delegate = new QgsTableEditorDelegate( this );
124  connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex, this, &QgsTableEditorWidget::updateNumericFormatForIndex );
125  setItemDelegate( delegate );
126 
127 
128  connect( this, &QTableWidget::cellDoubleClicked, this, [ = ]
129  {
130  if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
131  {
132  d->setWeakEditorMode( false );
133  }
134  } );
135 
136  connect( selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsTableEditorWidget::activeCellChanged );
137 }
138 
140 {
141  qDeleteAll( mNumericFormats );
142 }
143 
144 void QgsTableEditorWidget::updateNumericFormatForIndex( const QModelIndex &index )
145 {
146  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
147  {
148  if ( QgsNumericFormat *format = mNumericFormats.value( i ) )
149  {
150  i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(), QgsNumericFormatContext() ) );
151  }
152  }
153 }
154 
155 void QgsTableEditorWidget::updateHeaders()
156 {
157  QStringList headers;
158  QStringList letters;
159 
160  QString first;
161  QString current;
162 
163  for ( char c = 'A'; c <= 'Z'; c++ )
164  {
165  letters.push_back( QString( c ) );
166  }
167 
168  int len = letters.length();
169  int index = 0;
170  int fIndex = 0;
171 
172  for ( int i = 0; i < 1000; i++ )
173  {
174  if ( index == len )
175  {
176  index = 0;
177 
178  first = letters.at( fIndex );
179  fIndex++;
180 
181  if ( fIndex == len )
182  {
183  fIndex = 0;
184  }
185  }
186 
187  current = first;
188  current += letters.at( index );
189  headers.push_back( current );
190  current.clear();
191 
192  index++;
193  }
194 
195  setHorizontalHeaderLabels( headers );
196 
197  headers.clear();
198  if ( mIncludeHeader )
199  headers << tr( "Header" );
200  for ( int i = 1; i <= 1000; i++ )
201  {
202  headers << QString::number( i );
203  }
204 
205  setVerticalHeaderLabels( headers );
206 }
207 
208 bool QgsTableEditorWidget::collectConsecutiveRowRange( const QModelIndexList &list, int &minRow, int &maxRow ) const
209 {
210  QSet< int > includedRows;
211  minRow = std::numeric_limits< int >::max();
212  maxRow = -1;
213  for ( const QModelIndex &index : list )
214  {
215  includedRows.insert( index.row() );
216  minRow = std::min( minRow, index.row() );
217  maxRow = std::max( maxRow, index.row() );
218  }
219 
220  // test that selection is consecutive rows
221  for ( int r = minRow + 1; r < maxRow; r++ )
222  {
223  if ( !includedRows.contains( r ) )
224  return false;
225  }
226  return true;
227 }
228 
229 bool QgsTableEditorWidget::collectConsecutiveColumnRange( const QModelIndexList &list, int &minColumn, int &maxColumn ) const
230 {
231  QSet< int > includedColumns;
232  minColumn = std::numeric_limits< int >::max();
233  maxColumn = -1;
234  for ( const QModelIndex &index : list )
235  {
236  includedColumns.insert( index.column() );
237  minColumn = std::min( minColumn, index.column() );
238  maxColumn = std::max( maxColumn, index.column() );
239  }
240 
241  // test that selection is consecutive columns
242  for ( int r = minColumn + 1; r < maxColumn; r++ )
243  {
244  if ( !includedColumns.contains( r ) )
245  return false;
246  }
247  return true;
248 }
249 
250 QList<int> QgsTableEditorWidget::collectUniqueRows( const QModelIndexList &list ) const
251 {
252  QList<int > res;
253  for ( const QModelIndex &index : list )
254  {
255  if ( !res.contains( index.row() ) )
256  res << index.row();
257  }
258  std::sort( res.begin(), res.end() );
259  return res;
260 }
261 
262 QList<int> QgsTableEditorWidget::collectUniqueColumns( const QModelIndexList &list ) const
263 {
264  QList<int > res;
265  for ( const QModelIndex &index : list )
266  {
267  if ( !res.contains( index.column() ) )
268  res << index.column();
269  }
270  std::sort( res.begin(), res.end() );
271  return res;
272 }
273 
274 void QgsTableEditorWidget::keyPressEvent( QKeyEvent *event )
275 {
276  switch ( event->key() )
277  {
278  case Qt::Key_Enter:
279  case Qt::Key_Return:
280  {
281  //Enter or return keys moves to next row
282  QTableWidget::keyPressEvent( event );
283  setCurrentCell( currentRow() + 1, currentColumn() );
284  break;
285  }
286 
287  case Qt::Key_Delete:
288  {
290  break;
291  }
292 
293  default:
294  QTableWidget::keyPressEvent( event );
295  }
296  if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
297  {
298  d->setWeakEditorMode( true );
299  }
300 }
301 
303 {
304  mBlockSignals++;
305  qDeleteAll( mNumericFormats );
306  mNumericFormats.clear();
307 
308  QgsNumericFormatContext numericContext;
309  int rowNumber = mIncludeHeader ? 1 : 0;
310  bool first = true;
311  setRowCount( contents.size() + rowNumber );
312  for ( const QgsTableRow &row : contents )
313  {
314  if ( first )
315  {
316  setColumnCount( row.size() );
317  first = false;
318  }
319 
320  int colNumber = 0;
321  for ( const QgsTableCell &col : row )
322  {
323  QTableWidgetItem *item = new QTableWidgetItem( col.content().value< QgsProperty >().isActive() ? col.content().value< QgsProperty >().asExpression() : col.content().toString() );
324  item->setData( CellContent, col.content() ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
325  item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
326  item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
327  item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
328  item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
329  item->setData( HorizontalAlignment, static_cast< int >( col.horizontalAlignment() ) );
330  item->setData( VerticalAlignment, static_cast< int >( col.verticalAlignment() ) );
331  item->setData( CellProperty, QVariant::fromValue( col.content().value< QgsProperty >() ) );
332 
333  if ( col.content().value< QgsProperty >().isActive() )
334  item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
335 
336  if ( auto *lNumericFormat = col.numericFormat() )
337  {
338  mNumericFormats.insert( item, lNumericFormat->clone() );
339  item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
340  }
341  setItem( rowNumber, colNumber, item );
342  colNumber++;
343  }
344  rowNumber++;
345  }
346 
347  mBlockSignals--;
348  updateHeaders();
349 
350  if ( mFirstSet )
351  {
352  resizeColumnsToContents();
353  resizeRowsToContents();
354  mFirstSet = false;
355  }
356  emit tableChanged();
357 }
358 
360 {
361  QgsTableContents items;
362  items.reserve( rowCount() );
363 
364  for ( int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
365  {
366  QgsTableRow row;
367  row.reserve( columnCount() );
368  for ( int c = 0; c < columnCount(); c++ )
369  {
370  QgsTableCell cell;
371  if ( QTableWidgetItem *i = item( r, c ) )
372  {
373  cell.setContent( i->data( CellProperty ).value< QgsProperty >().isActive() ? i->data( CellProperty ) : i->data( CellContent ) );
374  cell.setBackgroundColor( i->data( PresetBackgroundColorRole ).value< QColor >() );
375  cell.setTextFormat( i->data( TextFormat ).value< QgsTextFormat >() );
376  cell.setHorizontalAlignment( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) );
377  cell.setVerticalAlignment( static_cast< Qt::Alignment >( i->data( VerticalAlignment ).toInt() ) );
378 
379  if ( mNumericFormats.value( i ) )
380  {
381  cell.setNumericFormat( mNumericFormats.value( i )->clone() );
382  }
383  }
384  row.push_back( cell );
385  }
386  items.push_back( row );
387  }
388 
389  return items;
390 }
391 
393 {
394  bool changed = false;
395  mBlockSignals++;
396  std::unique_ptr< QgsNumericFormat > newFormat( format );
397  const QModelIndexList selection = selectedIndexes();
398  QgsNumericFormatContext numericContext;
399  for ( const QModelIndex &index : selection )
400  {
401  if ( index.row() == 0 && mIncludeHeader )
402  continue;
403 
404  QTableWidgetItem *i = item( index.row(), index.column() );
405  if ( !i )
406  {
407  i = new QTableWidgetItem();
408  setItem( index.row(), index.column(), i );
409  }
410  if ( !mNumericFormats.value( i ) && newFormat )
411  {
412  changed = true;
413  mNumericFormats.insert( i, newFormat->clone() );
414  }
415  else if ( mNumericFormats.value( i ) && !newFormat )
416  {
417  changed = true;
418  delete mNumericFormats.value( i );
419  mNumericFormats.remove( i );
420  }
421  else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
422  {
423  changed = true;
424  delete mNumericFormats.value( i );
425  mNumericFormats.insert( i, newFormat->clone() );
426  }
427  i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
428  }
429  mBlockSignals--;
430  if ( changed && !mBlockSignals )
431  emit tableChanged();
432 }
433 
435 {
436  QgsNumericFormat *f = nullptr;
437  bool first = true;
438  const QModelIndexList selection = selectedIndexes();
439  for ( const QModelIndex &index : selection )
440  {
441  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
442  {
443  if ( first )
444  {
445  f = mNumericFormats.value( i );
446  first = false;
447  }
448  else if ( ( !f && !mNumericFormats.value( i ) )
449  || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
450  continue;
451  else
452  {
453  return nullptr;
454  }
455  }
456  else
457  {
458  return nullptr;
459  }
460  }
461  return f;
462 }
463 
465 {
466  QgsNumericFormat *f = nullptr;
467  bool first = true;
468  const QModelIndexList selection = selectedIndexes();
469  for ( const QModelIndex &index : selection )
470  {
471  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
472  {
473  if ( first )
474  {
475  f = mNumericFormats.value( i );
476  first = false;
477  }
478  else if ( ( !f && !mNumericFormats.value( i ) )
479  || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
480  continue;
481  else
482  {
483  return true;
484  }
485  }
486  else if ( f )
487  {
488  return true;
489  }
490  }
491  return false;
492 }
493 
495 {
497  return f.isValid() ? f.color() : QColor();
498 }
499 
501 {
502  QColor c;
503  bool first = true;
504  const QModelIndexList selection = selectedIndexes();
505  for ( const QModelIndex &index : selection )
506  {
507  QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value< QColor >() : QColor();
508  if ( first )
509  {
510  c = indexColor;
511  first = false;
512  }
513  else if ( indexColor == c )
514  continue;
515  else
516  {
517  return QColor();
518  }
519  }
520  return c;
521 }
522 
524 {
525  Qt::Alignment alignment = Qt::AlignLeft;
526  bool first = true;
527  const QModelIndexList selection = selectedIndexes();
528  for ( const QModelIndex &index : selection )
529  {
530  Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, HorizontalAlignment ).toInt() );
531  if ( first )
532  {
533  alignment = cellAlign;
534  first = false;
535  }
536  else if ( cellAlign == alignment )
537  continue;
538  else
539  {
540  return Qt::AlignLeft | Qt::AlignTop;
541  }
542  }
543  return alignment;
544 }
545 
547 {
548  Qt::Alignment alignment = Qt::AlignVCenter;
549  bool first = true;
550  const QModelIndexList selection = selectedIndexes();
551  for ( const QModelIndex &index : selection )
552  {
553  Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, VerticalAlignment ).toInt() );
554  if ( first )
555  {
556  alignment = cellAlign;
557  first = false;
558  }
559  else if ( cellAlign == alignment )
560  continue;
561  else
562  {
563  return Qt::AlignLeft | Qt::AlignTop;
564  }
565  }
566  return alignment;
567 }
568 
570 {
571  QgsProperty property;
572  bool first = true;
573  const QModelIndexList selection = selectedIndexes();
574  for ( const QModelIndex &index : selection )
575  {
576  const QgsProperty cellProperty = model()->data( index, CellProperty ).value< QgsProperty >();
577  if ( first )
578  {
579  property = cellProperty;
580  first = false;
581  }
582  else if ( cellProperty == property )
583  continue;
584  else
585  {
586  return QgsProperty();
587  }
588  }
589  return property;
590 }
591 
593 {
594  QgsTextFormat format;
595  bool first = true;
596  const QModelIndexList selection = selectedIndexes();
597  for ( const QModelIndex &index : selection )
598  {
599  if ( !model()->data( index, TextFormat ).isValid() )
600  return QgsTextFormat();
601 
602  QgsTextFormat cellFormat = model()->data( index, TextFormat ).value< QgsTextFormat >();
603  if ( first )
604  {
605  format = cellFormat;
606  first = false;
607  }
608  else if ( cellFormat == format )
609  continue;
610  else
611  return QgsTextFormat();
612  }
613  return format;
614 }
615 
617 {
618  double height = 0;
619  bool first = true;
620  const QModelIndexList selection = selectedIndexes();
621  for ( const QModelIndex &index : selection )
622  {
623  double thisHeight = tableRowHeight( index.row() );
624  if ( first )
625  height = thisHeight;
626  else if ( thisHeight != height )
627  {
628  return -1;
629  }
630  first = false;
631  }
632  return height;
633 }
634 
636 {
637  double width = 0;
638  bool first = true;
639  const QModelIndexList selection = selectedIndexes();
640  for ( const QModelIndex &index : selection )
641  {
642  double thisWidth = tableColumnWidth( index.column() );
643  if ( first )
644  width = thisWidth;
645  else if ( thisWidth != width )
646  {
647  return -1;
648  }
649  first = false;
650  }
651  return width;
652 }
653 
655 {
656  double height = 0;
657  for ( int col = 0; col < columnCount(); ++col )
658  {
659  double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
660  height = std::max( thisHeight, height );
661  }
662  return height;
663 }
664 
666 {
667  double width = 0;
668  for ( int row = 0; row < rowCount(); ++row )
669  {
670  double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
671  width = std::max( thisWidth, width );
672  }
673  return width;
674 }
675 
676 void QgsTableEditorWidget::setTableRowHeight( int row, double height )
677 {
678  if ( row == 0 && mIncludeHeader )
679  return;
680 
681  bool changed = false;
682  mBlockSignals++;
683 
684  for ( int col = 0; col < columnCount(); ++col )
685  {
686  if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
687  {
688  if ( i->data( RowHeight ).toDouble() != height )
689  {
690  i->setData( RowHeight, height );
691  changed = true;
692  }
693  }
694  else
695  {
696  QTableWidgetItem *newItem = new QTableWidgetItem();
697  newItem->setData( RowHeight, height );
698  setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
699  changed = true;
700  }
701  }
702 
703  mBlockSignals--;
704  if ( changed && !mBlockSignals )
705  emit tableChanged();
706 }
707 
708 void QgsTableEditorWidget::setTableColumnWidth( int col, double width )
709 {
710  bool changed = false;
711  mBlockSignals++;
712  for ( int row = 0; row < rowCount(); ++row )
713  {
714  if ( QTableWidgetItem *i = item( row, col ) )
715  {
716  if ( i->data( ColumnWidth ).toDouble() != width )
717  {
718  i->setData( ColumnWidth, width );
719  changed = true;
720  }
721  }
722  else
723  {
724  QTableWidgetItem *newItem = new QTableWidgetItem();
725  newItem->setData( ColumnWidth, width );
726  setItem( row, col, newItem );
727  changed = true;
728  }
729  }
730  mBlockSignals--;
731  if ( changed && !mBlockSignals )
732  emit tableChanged();
733 }
734 
736 {
737  return collectUniqueRows( selectedIndexes() );
738 }
739 
741 {
742  return collectUniqueColumns( selectedIndexes() );
743 }
744 
746 {
747  if ( !mIncludeHeader )
748  return QVariantList();
749 
750  QVariantList res;
751  res.reserve( columnCount() );
752  for ( int col = 0; col < columnCount(); ++col )
753  {
754  if ( QTableWidgetItem *i = item( 0, col ) )
755  {
756  res << i->data( CellContent );
757  }
758  else
759  {
760  res << QVariant();
761  }
762  }
763  return res;
764 }
765 
767 {
768  if ( !mIncludeHeader )
769  return false;
770 
771  return collectUniqueRows( selectedIndexes() ).contains( 0 );
772 }
773 
775 {
776  if ( rowCount() == 0 )
777  {
778  insertRow( 0 );
779  return;
780  }
781 
782  int minRow = 0;
783  int maxRow = 0;
784  if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
785  return;
786 
787  const int rowsToInsert = maxRow - minRow + 1;
788  for ( int i = 0; i < rowsToInsert; ++i )
789  insertRow( maxRow + 1 );
790 
791  updateHeaders();
792  if ( !mBlockSignals )
793  emit tableChanged();
794 }
795 
797 {
798  if ( rowCount() == 0 )
799  {
800  insertRow( 0 );
801  return;
802  }
803 
804  int minRow = 0;
805  int maxRow = 0;
806  if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
807  return;
808 
809  const int rowsToInsert = maxRow - minRow + 1;
810  for ( int i = 0; i < rowsToInsert; ++i )
811  insertRow( minRow );
812 
813  updateHeaders();
814  if ( !mBlockSignals )
815  emit tableChanged();
816 }
817 
819 {
820  if ( columnCount() == 0 )
821  {
822  insertColumn( 0 );
823  return;
824  }
825 
826  int minColumn = 0;
827  int maxColumn = 0;
828  if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
829  return;
830 
831  const int columnsToInsert = maxColumn - minColumn + 1;
832  for ( int i = 0; i < columnsToInsert; ++i )
833  insertColumn( minColumn );
834 
835  updateHeaders();
836  if ( !mBlockSignals )
837  emit tableChanged();
838 }
839 
841 {
842  if ( columnCount() == 0 )
843  {
844  insertColumn( 0 );
845  return;
846  }
847 
848  int minColumn = 0;
849  int maxColumn = 0;
850  if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
851  return;
852 
853  const int columnsToInsert = maxColumn - minColumn + 1;
854  for ( int i = 0; i < columnsToInsert; ++i )
855  insertColumn( maxColumn + 1 );
856 
857  updateHeaders();
858  if ( !mBlockSignals )
859  emit tableChanged();
860 }
861 
863 {
864  const QList< int > rows = rowsAssociatedWithSelection();
865  if ( rows.empty() )
866  return;
867 
868  bool changed = false;
869  for ( int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
870  {
871  removeRow( rows.at( i ) );
872  changed = true;
873  }
874  updateHeaders();
875  if ( changed && !mBlockSignals )
876  emit tableChanged();
877 }
878 
880 {
881  const QList< int > columns = columnsAssociatedWithSelection();
882  if ( columns.empty() )
883  return;
884 
885  bool changed = false;
886  for ( int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
887  {
888  removeColumn( columns.at( i ) );
889  changed = true;
890  }
891  updateHeaders();
892  if ( !mBlockSignals && changed )
893  emit tableChanged();
894 }
895 
897 {
898  const QModelIndexList s = selectedIndexes();
899  for ( const QModelIndex &index : s )
900  {
901  selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
902  }
903 }
904 
906 {
907  const QModelIndexList s = selectedIndexes();
908  for ( const QModelIndex &index : s )
909  {
910  selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
911  }
912 }
913 
915 {
916  const QModelIndexList selection = selectedIndexes();
917  bool changed = false;
918  mBlockSignals++;
919  for ( const QModelIndex &index : selection )
920  {
921  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
922  {
923  i->setText( QString() );
924  i->setData( CellContent, QVariant() );
925  changed = true;
926  }
927  }
928  mBlockSignals--;
929  if ( changed && !mBlockSignals )
930  emit tableChanged();
931 }
932 
934 {
935  const QModelIndexList selection = selectedIndexes();
936  bool changed = false;
937  mBlockSignals++;
938  for ( const QModelIndex &index : selection )
939  {
940  if ( index.row() == 0 && mIncludeHeader )
941  continue;
942 
943  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
944  {
945  if ( i->data( Qt::ForegroundRole ).value< QColor >() != color )
946  {
947  i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
948  QgsTextFormat f = i->data( TextFormat ).value< QgsTextFormat >();
949  f.setColor( color );
950  i->setData( TextFormat, QVariant::fromValue( f ) );
951  changed = true;
952  }
953  }
954  else
955  {
956  QTableWidgetItem *newItem = new QTableWidgetItem();
957  newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
958  QgsTextFormat f;
959  f.setColor( color );
960  newItem->setData( TextFormat, QVariant::fromValue( f ) );
961  setItem( index.row(), index.column(), newItem );
962  changed = true;
963  }
964  }
965  mBlockSignals--;
966  if ( changed && !mBlockSignals )
967  emit tableChanged();
968 }
969 
971 {
972  const QModelIndexList selection = selectedIndexes();
973  bool changed = false;
974  mBlockSignals++;
975  for ( const QModelIndex &index : selection )
976  {
977  if ( index.row() == 0 && mIncludeHeader )
978  continue;
979 
980  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
981  {
982  if ( i->data( PresetBackgroundColorRole ).value< QColor >() != color )
983  {
984  i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
985  i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
986  changed = true;
987  }
988  }
989  else
990  {
991  QTableWidgetItem *newItem = new QTableWidgetItem();
992  newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
993  newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
994  setItem( index.row(), index.column(), newItem );
995  changed = true;
996  }
997  }
998  mBlockSignals--;
999  if ( changed && !mBlockSignals )
1000  emit tableChanged();
1001 }
1002 
1004 {
1005  const QModelIndexList selection = selectedIndexes();
1006  bool changed = false;
1007  mBlockSignals++;
1008  for ( const QModelIndex &index : selection )
1009  {
1010  if ( index.row() == 0 && mIncludeHeader )
1011  continue;
1012 
1013  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1014  {
1015  if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1016  {
1017  i->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1018  changed = true;
1019  }
1020  }
1021  else
1022  {
1023  QTableWidgetItem *newItem = new QTableWidgetItem();
1024  newItem->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1025  setItem( index.row(), index.column(), newItem );
1026  changed = true;
1027  }
1028  }
1029  mBlockSignals--;
1030  if ( changed && !mBlockSignals )
1031  emit tableChanged();
1032 }
1033 
1035 {
1036  const QModelIndexList selection = selectedIndexes();
1037  bool changed = false;
1038  mBlockSignals++;
1039  for ( const QModelIndex &index : selection )
1040  {
1041  if ( index.row() == 0 && mIncludeHeader )
1042  continue;
1043 
1044  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1045  {
1046  if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1047  {
1048  i->setData( VerticalAlignment, static_cast< int >( alignment ) );
1049  changed = true;
1050  }
1051  }
1052  else
1053  {
1054  QTableWidgetItem *newItem = new QTableWidgetItem();
1055  newItem->setData( VerticalAlignment, static_cast< int >( alignment ) );
1056  setItem( index.row(), index.column(), newItem );
1057  changed = true;
1058  }
1059  }
1060  mBlockSignals--;
1061  if ( changed && !mBlockSignals )
1062  emit tableChanged();
1063 }
1064 
1066 {
1067  const QModelIndexList selection = selectedIndexes();
1068  bool changed = false;
1069  mBlockSignals++;
1070  for ( const QModelIndex &index : selection )
1071  {
1072  if ( index.row() == 0 && mIncludeHeader )
1073  continue;
1074 
1075  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1076  {
1077  if ( i->data( CellProperty ).value< QgsProperty >() != property )
1078  {
1079  if ( property.isActive() )
1080  {
1081  i->setData( CellProperty, QVariant::fromValue( property ) );
1082  i->setText( property.asExpression() );
1083  i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1084  }
1085  else
1086  {
1087  i->setData( CellProperty, QVariant() );
1088  i->setText( QString() );
1089  i->setFlags( i->flags() | Qt::ItemIsEditable );
1090  }
1091  changed = true;
1092  }
1093  }
1094  else
1095  {
1096  QTableWidgetItem *newItem = new QTableWidgetItem( property.asExpression() );
1097  if ( property.isActive() )
1098  {
1099  newItem->setData( CellProperty, QVariant::fromValue( property ) );
1100  newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1101  }
1102  else
1103  {
1104  newItem->setData( CellProperty, QVariant() );
1105  newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1106  }
1107  setItem( index.row(), index.column(), newItem );
1108  changed = true;
1109  }
1110  }
1111  mBlockSignals--;
1112  if ( changed && !mBlockSignals )
1113  emit tableChanged();
1114 }
1115 
1117 {
1118  const QModelIndexList selection = selectedIndexes();
1119  bool changed = false;
1120  mBlockSignals++;
1121  for ( const QModelIndex &index : selection )
1122  {
1123  if ( index.row() == 0 && mIncludeHeader )
1124  continue;
1125 
1126  if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1127  {
1128  i->setData( TextFormat, QVariant::fromValue( format ) );
1129  i->setData( Qt::ForegroundRole, format.color() );
1130  changed = true;
1131  }
1132  else
1133  {
1134  QTableWidgetItem *newItem = new QTableWidgetItem();
1135  newItem->setData( TextFormat, QVariant::fromValue( format ) );
1136  newItem->setData( Qt::ForegroundRole, format.color() );
1137  setItem( index.row(), index.column(), newItem );
1138  changed = true;
1139  }
1140  }
1141  mBlockSignals--;
1142  if ( changed && !mBlockSignals )
1143  emit tableChanged();
1144 }
1145 
1147 {
1148  bool changed = false;
1149  mBlockSignals++;
1150  const QList< int > rows = rowsAssociatedWithSelection();
1151  for ( int row : rows )
1152  {
1153  if ( row == 0 && mIncludeHeader )
1154  continue;
1155 
1156  for ( int col = 0; col < columnCount(); ++col )
1157  {
1158  if ( QTableWidgetItem *i = item( row, col ) )
1159  {
1160  if ( i->data( RowHeight ).toDouble() != height )
1161  {
1162  i->setData( RowHeight, height );
1163  changed = true;
1164  }
1165  }
1166  else
1167  {
1168  QTableWidgetItem *newItem = new QTableWidgetItem();
1169  newItem->setData( RowHeight, height );
1170  setItem( row, col, newItem );
1171  changed = true;
1172  }
1173  }
1174  }
1175  mBlockSignals--;
1176  if ( changed && !mBlockSignals )
1177  emit tableChanged();
1178 }
1179 
1181 {
1182  bool changed = false;
1183  mBlockSignals++;
1184  const QList< int > cols = columnsAssociatedWithSelection();
1185  for ( int col : cols )
1186  {
1187  for ( int row = 0; row < rowCount(); ++row )
1188  {
1189  if ( QTableWidgetItem *i = item( row, col ) )
1190  {
1191  if ( i->data( ColumnWidth ).toDouble() != width )
1192  {
1193  i->setData( ColumnWidth, width );
1194  changed = true;
1195  }
1196  }
1197  else
1198  {
1199  QTableWidgetItem *newItem = new QTableWidgetItem();
1200  newItem->setData( ColumnWidth, width );
1201  setItem( row, col, newItem );
1202  changed = true;
1203  }
1204  }
1205  }
1206  mBlockSignals--;
1207  if ( changed && !mBlockSignals )
1208  emit tableChanged();
1209 }
1210 
1212 {
1213  if ( included == mIncludeHeader )
1214  return;
1215 
1216  mIncludeHeader = included;
1217 
1218  if ( mIncludeHeader )
1219  insertRow( 0 );
1220  else
1221  removeRow( 0 );
1222  updateHeaders();
1223 }
1224 
1225 void QgsTableEditorWidget::setTableHeaders( const QVariantList &headers )
1226 {
1227  if ( !mIncludeHeader )
1228  return;
1229 
1230  mBlockSignals++;
1231 
1232  for ( int col = 0; col < columnCount(); ++col )
1233  {
1234  if ( QTableWidgetItem *i = item( 0, col ) )
1235  {
1236  i->setText( headers.value( col ).toString() );
1237  i->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1238  }
1239  else
1240  {
1241  QTableWidgetItem *item = new QTableWidgetItem( headers.value( col ).toString() );
1242  item->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1243  setItem( 0, col, item );
1244  }
1245  }
1246  mBlockSignals--;
1247 }
1248 
1250 
1251 QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1252  : QPlainTextEdit( parent )
1253 {
1254  // narrower default margins
1255  document()->setDocumentMargin( document()->documentMargin() / 2 );
1256 
1257  connect( this, &QPlainTextEdit::textChanged, this, &QgsTableEditorTextEdit::resizeToContents );
1258  updateMinimumSize();
1259 }
1260 
1261 void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1262 {
1263  switch ( event->key() )
1264  {
1265  case Qt::Key_Enter:
1266  case Qt::Key_Return:
1267  {
1268  if ( event->modifiers() & Qt::ControlModifier )
1269  {
1270  // ctrl+enter inserts a line break
1271  insertPlainText( QString( '\n' ) );
1272  resizeToContents();
1273  }
1274  else
1275  {
1276  // closes editor
1277  event->ignore();
1278  }
1279  break;
1280  }
1281 
1282  case Qt::Key_Right:
1283  case Qt::Key_Left:
1284  case Qt::Key_Up:
1285  case Qt::Key_Down:
1286  {
1287  if ( mWeakEditorMode )
1288  {
1289  // close editor and defer to table
1290  event->ignore();
1291  }
1292  else
1293  {
1294  QPlainTextEdit::keyPressEvent( event );
1295  }
1296  break;
1297  }
1298 
1299  case Qt::Key_Tab:
1300  {
1301  if ( event->modifiers() & Qt::ControlModifier )
1302  {
1303  // if tab is pressed then defer to table, unless ctrl modifier is also held
1304  // (emulate spreadsheet behavior)
1305  insertPlainText( QString( '\t' ) );
1306  resizeToContents();
1307  }
1308  else
1309  {
1310  event->ignore();
1311  }
1312  break;
1313  }
1314 
1315  default:
1316  QPlainTextEdit::keyPressEvent( event );
1317  }
1318 }
1319 
1320 void QgsTableEditorTextEdit::updateMinimumSize()
1321 {
1322  const double tm = document()->documentMargin();
1323  const QMargins cm = contentsMargins();
1324  const int width = tm * 2 + cm.left() + cm.right() + 30;
1325  const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1326  QStyleOptionFrame opt;
1327  initStyleOption( &opt );
1328  const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ), this );
1329  setMinimumWidth( sizeFromContent.width() );
1330  setMinimumHeight( sizeFromContent.height() );
1331 }
1332 
1333 void QgsTableEditorTextEdit::setWeakEditorMode( bool weakEditorMode )
1334 {
1335  mWeakEditorMode = weakEditorMode;
1336 }
1337 
1338 void QgsTableEditorTextEdit::resizeToContents()
1339 {
1340  int oldWidth = width();
1341  int oldHeight = height();
1342  if ( mOriginalWidth == -1 )
1343  mOriginalWidth = oldWidth;
1344  if ( mOriginalHeight == -1 )
1345  mOriginalHeight = oldHeight;
1346 
1347  if ( QWidget *parent = parentWidget() )
1348  {
1349  QPoint position = pos();
1350  QFontMetrics fm( font() );
1351 
1352  const QStringList lines = toPlainText().split( '\n' );
1353  int maxTextLineWidth = 0;
1354  int totalTextHeight = 0;
1355  for ( const QString &line : lines )
1356  {
1357  const QRect bounds = fontMetrics().boundingRect( line );
1358  maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1359  totalTextHeight += fm.height();
1360  }
1361 
1362  int hintWidth = minimumWidth() + maxTextLineWidth;
1363  int hintHeight = minimumHeight() + totalTextHeight;
1364  int parentWidth = parent->width();
1365  int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1366  int maxHeight = parent->height() - position.y();
1367  int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1368  int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1369 
1370  if ( mWidgetOwnsGeometry )
1371  {
1372  setMaximumWidth( newWidth );
1373  setMaximumHeight( newHeight );
1374  }
1375  if ( isRightToLeft() )
1376  move( position.x() - newWidth + oldWidth, position.y() );
1377  resize( newWidth, newHeight );
1378  }
1379 }
1380 
1381 void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1382 {
1383  switch ( e->type() )
1384  {
1385  case QEvent::FontChange:
1386  case QEvent::StyleChange:
1387  case QEvent::ContentsRectChange:
1388  updateMinimumSize();
1389  break;
1390  default:
1391  break;
1392  }
1393  QPlainTextEdit::changeEvent( e );
1394 }
1395 
1396 QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1397  : QStyledItemDelegate( parent )
1398 {
1399 
1400 }
1401 
1402 void QgsTableEditorDelegate::setWeakEditorMode( bool weakEditorMode )
1403 {
1404  mWeakEditorMode = weakEditorMode;
1405 }
1406 
1407 QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const
1408 {
1409  QgsTableEditorTextEdit *w = new QgsTableEditorTextEdit( parent );
1410  w->setWeakEditorMode( mWeakEditorMode );
1411 
1412  if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame, 0, w ) )
1413  w->setFrameShape( QFrame::NoFrame );
1414  if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected, 0, w ) )
1415  w->setWidgetOwnsGeometry( true );
1416 
1417  return w;
1418 }
1419 
1420 void QgsTableEditorDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
1421 {
1422  QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1423  if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1424  {
1425  if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1426  {
1427  lineEdit->setPlainText( value.toString() );
1428  lineEdit->selectAll();
1429  }
1430  }
1431  mLastIndex = index;
1432 }
1433 
1434 void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
1435 {
1436  if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1437  {
1438  const QString text = lineEdit->toPlainText();
1439  if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value< QgsProperty >().isActive() )
1440  {
1441  model->setData( index, text, QgsTableEditorWidget::CellContent );
1442  model->setData( index, text, Qt::DisplayRole );
1443  emit updateNumericFormatForIndex( index );
1444  }
1445  }
1446 }
1447 
1448 
1450 
A context for numeric formats.
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
A store for object properties.
Definition: qgsproperty.h:232
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.
Definition: qgstablecell.h:36
void setHorizontalAlignment(Qt::Alignment alignment)
Sets the horizontal alignment for text in 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.
Definition: qgstablecell.h:80
void setTextFormat(const QgsTextFormat &format)
Sets the cell's text format.
Definition: qgstablecell.h:116
void setContent(const QVariant &content)
Sets the cell's content.
Definition: qgstablecell.h:64
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used for numbers in the cell, or nullptr if no specific format is set.
void setSelectionVerticalAlignment(Qt::Alignment alignment)
Sets the vertical alignment for the currently selected cells.
Q_DECL_DEPRECATED void setSelectionForegroundColor(const QColor &color)
Sets the foreground color for the currently selected cells.
QgsProperty selectionCellProperty()
Returns the QgsProperty used for the contents of the currently selected cells.
QVariantList tableHeaders() const
Returns the table header values.
void setSelectionTextFormat(const QgsTextFormat &format)
Sets the text format for the selected cells.
void setTableHeaders(const QVariantList &headers)
Sets the table headers.
bool isHeaderCellSelected()
Returns true if any header cells are selected.
void setSelectionBackgroundColor(const QColor &color)
Sets the background color for the currently selected cells.
void activeCellChanged()
Emitted whenever the active (or selected) cell changes in the widget.
Qt::Alignment selectionHorizontalAlignment()
Returns the horizontal alignment for the currently selected cells.
QgsTextFormat selectionTextFormat()
Returns the text format for the currently selected cells.
void insertColumnsAfter()
Inserts new columns after the current selection.
double selectionRowHeight()
Returns the height (in millimeters) of the rows associated with the current selection,...
void clearSelectedCells()
Clears the contents of the currently selected cells.
bool hasMixedSelectionNumericFormat()
Returns true if the current selection has a mix of numeric formats.
QList< int > rowsAssociatedWithSelection()
Returns a list of the rows associated with the current table selected cells.
QList< int > columnsAssociatedWithSelection()
Returns a list of the columns associated with the current table selected cells.
void deleteRows()
Deletes all rows associated with the current selected cells.
void setSelectionHorizontalAlignment(Qt::Alignment alignment)
Sets the horizontal alignment for the currently selected cells.
friend class QgsTableEditorDelegate
void setSelectionRowHeight(double height)
Sets the row height (in millimeters) for the currently selected rows, or 0 for automatic row height.
void insertRowsBelow()
Inserts new rows below the current selection.
double tableRowHeight(int row)
Returns the configured row height for the specified row, or 0 if an automatic height should be used f...
QColor selectionBackgroundColor()
Returns the background color for the currently selected cells.
QgsTableEditorWidget(QWidget *parent=nullptr)
Constructor for QgsTableEditorWidget with the specified parent widget.
Qt::Alignment selectionVerticalAlignment()
Returns the horizontal alignment for the currently selected cells.
void insertRowsAbove()
Inserts new rows above the current selection.
void setTableColumnWidth(int column, double width)
Sets the configured column width for the specified column.
void setSelectionCellProperty(const QgsProperty &property)
Sets the cell contents QgsProperty for the currently selected cells.
void setSelectionColumnWidth(double height)
Sets the column width (in millimeters) for the currently selected columns, or 0 for automatic column ...
double selectionColumnWidth()
Returns the width (in millimeters) of the columns associated with the current selection,...
void expandRowSelection()
Expands out the selection to include whole rows associated with the current selected cells.
double tableColumnWidth(int column)
Returns the configured column width for the specified column, or 0 if an automatic width should be us...
void setIncludeTableHeader(bool included)
Sets whether the table includes a header row.
QgsNumericFormat * selectionNumericFormat()
Returns the numeric format used for the currently selected cells, or nullptr if the selection has no ...
void setTableRowHeight(int row, double height)
Sets the configured row height for the specified row.
void keyPressEvent(QKeyEvent *event) override
QgsTableContents tableContents() const
Returns the current contents of the editor widget table.
void deleteColumns()
Deletes all columns associated with the current selected cells.
void setTableContents(const QgsTableContents &contents)
Sets the contents to show in the editor widget.
void tableChanged()
Emitted whenever the table contents are changed.
void insertColumnsBefore()
Inserts new columns before the current selection.
void setSelectionNumericFormat(QgsNumericFormat *format)
Sets the numeric format to use for the currently selected cells.
void expandColumnSelection()
Expands out the selection to include whole columns associated with the current selected cells.
Q_DECL_DEPRECATED QColor selectionForegroundColor()
Returns the foreground color for the currently selected cells.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
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.
Definition: qgstablecell.h:220
QVector< QgsTableCell > QgsTableRow
A row of table cells.
Definition: qgstablecell.h:211