KD Reports API Documentation  2.0
KDReportsAutoTableElement.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Reports library.
4 **
5 ** SPDX-FileCopyrightText: 2007-2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDAB-KDReports OR LicenseRef-KDAB-KDReports-US
8 **
9 ** Licensees holding valid commercial KD Reports licenses may use this file in
10 ** accordance with the KD Reports Commercial License Agreement provided with
11 ** the Software.
12 **
13 ** Contact info@kdab.com if any conditions of this licensing are not clear to you.
14 **
15 ****************************************************************************/
16 
20 #include "KDReportsReport_p.h" // modelForKey
21 #include <QAbstractItemModel>
22 #include <QBitArray>
23 #include <QDateTime>
24 #include <QDebug>
25 #include <QIcon>
26 #include <QTextCursor>
27 #include <QTextTableCell>
28 #include <QUrl>
29 #include <QVector>
30 
31 class KDReports::AutoTableElementPrivate
32 {
33 public:
34  AutoTableElementPrivate()
35  : m_tableModel(nullptr)
36  , m_verticalHeaderVisible(true)
37  , m_horizontalHeaderVisible(true)
38  , m_headerBackground(QColor(218, 218, 218))
39  , m_iconSize(32, 32)
40  {
41  }
42  ~AutoTableElementPrivate() { }
43 
44  void fillCellFromHeaderData(int section, Qt::Orientation orientation, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const;
45  QSize fillTableCell(int row, int column, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const;
46 
47  QAbstractItemModel *m_tableModel;
48  QString m_modelKey;
49  bool m_verticalHeaderVisible;
50  bool m_horizontalHeaderVisible;
51  QBrush m_headerBackground;
52  QSize m_iconSize;
53 };
54 
55 // Helper for fillCellFromHeaderData and fillTableCell
56 class FillCellHelper
57 {
58 public:
59  FillCellHelper(QAbstractItemModel *tableModel, int section, Qt::Orientation orientation, QSize iconSz)
60  : iconSize(iconSz)
61  , cellDecoration(tableModel->headerData(section, orientation, Qt::DecorationRole))
62  , cellFont(tableModel->headerData(section, orientation, Qt::FontRole))
63  , cellText(tableModel->headerData(section, orientation, Qt::DisplayRole).toString())
64  , foreground(tableModel->headerData(section, orientation, Qt::ForegroundRole))
65  , background(tableModel->headerData(section, orientation, Qt::BackgroundRole))
66  , alignment(Qt::Alignment(tableModel->headerData(section, orientation, Qt::TextAlignmentRole).toInt()))
67  , decorationAlignment(tableModel->headerData(section, orientation, KDReports::AutoTableElement::DecorationAlignmentRole))
68  , nonBreakableLines(tableModel->headerData(section, orientation, KDReports::AutoTableElement::NonBreakableLinesRole).toBool())
69  , span(1, 1)
70  {
71  }
72  FillCellHelper(QAbstractItemModel *tableModel, const QModelIndex &index, QSize _span, QSize iconSz)
73  : iconSize(iconSz)
74  , cellDecoration(tableModel->data(index, Qt::DecorationRole))
75  , cellFont(tableModel->data(index, Qt::FontRole))
76  , cellText(displayText(tableModel->data(index, Qt::DisplayRole)))
77  , foreground(tableModel->data(index, Qt::ForegroundRole))
78  , background(tableModel->data(index, Qt::BackgroundRole))
79  , alignment(Qt::Alignment(tableModel->data(index, Qt::TextAlignmentRole).toInt()))
80  , decorationAlignment(tableModel->data(index, KDReports::AutoTableElement::DecorationAlignmentRole))
81  , nonBreakableLines(tableModel->data(index, KDReports::AutoTableElement::NonBreakableLinesRole).toBool())
82  , span(_span)
83  {
84  }
85  void fill(QTextTable *textTable, KDReports::ReportBuilder &builder, QTextDocument &textDoc, QTextTableCell &cell);
86 
87 private:
88  void insertDecoration(KDReports::ReportBuilder &builder, QTextDocument &textDoc);
89  static QString displayText(const QVariant &value);
90 
91  QSize iconSize;
92  QVariant cellDecoration;
93  QVariant cellFont;
94  QString cellText;
95  QVariant foreground;
96  QVariant background;
97  Qt::Alignment alignment;
98  QVariant decorationAlignment;
99  bool nonBreakableLines;
100  QSize span;
101 
102  QTextCursor cellCursor;
103 };
104 
105 static QTextCharFormat::VerticalAlignment toVerticalAlignment(Qt::Alignment alignment)
106 {
107  switch (alignment & Qt::AlignVertical_Mask) {
108  case Qt::AlignTop:
109  return QTextCharFormat::AlignTop;
110  case Qt::AlignBottom:
111  return QTextCharFormat::AlignBottom;
112  case Qt::AlignVCenter:
113  return QTextCharFormat::AlignMiddle;
114  }
115  return QTextCharFormat::AlignNormal;
116 }
117 
118 void FillCellHelper::fill(QTextTable *textTable, KDReports::ReportBuilder &builder, QTextDocument &textDoc, QTextTableCell &cell)
119 {
120  cellCursor = cell.firstCursorPosition();
121  QTextCharFormat cellFormat = cell.format();
122  if (background.canConvert<QBrush>()) {
123  cellFormat.setBackground(qvariant_cast<QBrush>(background));
124  }
125  cellFormat.setVerticalAlignment(toVerticalAlignment(alignment));
126  cell.setFormat(cellFormat);
127 
128  QTextBlockFormat blockFormat = cellCursor.blockFormat();
129  blockFormat.setAlignment(alignment);
130  blockFormat.setNonBreakableLines(nonBreakableLines);
131  builder.setupBlockFormat(blockFormat);
132 
133  cellCursor.setBlockFormat(blockFormat);
134 
135  const bool hasIcon = !cellDecoration.isNull();
136  const bool iconAfterText = decorationAlignment.isValid() && (decorationAlignment.toInt() & Qt::AlignRight);
137  if (hasIcon && !iconAfterText) {
138  insertDecoration(builder, textDoc);
139  }
140 
141  QTextCharFormat charFormat = cellCursor.charFormat();
142  if (cellFont.isValid()) {
143  QFont cellQFont = qvariant_cast<QFont>(cellFont);
144 #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
145  charFormat.setFont(cellQFont, QTextCharFormat::FontPropertiesSpecifiedOnly);
146 #else
147  charFormat.setFont(cellQFont);
148 #endif
149  } else {
150  charFormat.setFont(builder.defaultFont());
151  }
152  if (foreground.canConvert<QBrush>()) {
153  charFormat.setForeground(qvariant_cast<QBrush>(foreground));
154  }
155  cellCursor.setCharFormat(charFormat);
156 
157  if (hasIcon && !iconAfterText) {
158  cellCursor.insertText(QChar::fromLatin1(' ')); // spacing between icon and text
159  }
160 
161  // qDebug() << cellText;
162  if (cellText.startsWith(QLatin1String("<qt>")) || cellText.startsWith(QLatin1String("<html>")))
163  cellCursor.insertHtml(cellText);
164  else
165  cellCursor.insertText(cellText);
166 
167  if (hasIcon && iconAfterText) {
168  cellCursor.insertText(QChar::fromLatin1(' ')); // spacing between icon and text
169  insertDecoration(builder, textDoc);
170  }
171 
172  if (span.width() > 1 || span.height() > 1)
173  textTable->mergeCells(cell.row(), cell.column(), span.height(), span.width());
174 }
175 
176 void FillCellHelper::insertDecoration(KDReports::ReportBuilder &builder, QTextDocument &textDoc)
177 {
178  QImage img = qvariant_cast<QImage>(cellDecoration);
179  if (img.isNull()) {
180  img = qvariant_cast<QIcon>(cellDecoration).pixmap(iconSize).toImage();
181  }
182  if (!img.isNull()) {
183  static int imageNumber = 0;
184  const QString name = QStringLiteral("cell-image%1.png").arg(++imageNumber);
185  textDoc.addResource(QTextDocument::ImageResource, QUrl(name), img);
186  builder.currentDocumentData().addResourceName(name);
187  cellCursor.insertImage(name);
188  }
189 }
190 
191 QString FillCellHelper::displayText(const QVariant &value)
192 {
193  QLocale locale; // in QStyledItemDelegate this is configurable, it comes from QWidget::locale()...
194  QString text;
195  switch (value.userType()) {
196  case QMetaType::Float:
197  case QVariant::Double:
198  text = locale.toString(value.toReal());
199  break;
200  case QVariant::Int:
201  case QVariant::LongLong:
202  text = locale.toString(value.toLongLong());
203  break;
204  case QVariant::UInt:
205  case QVariant::ULongLong:
206  text = locale.toString(value.toULongLong());
207  break;
208  case QVariant::Date:
209  text = locale.toString(value.toDate(), QLocale::ShortFormat);
210  break;
211  case QVariant::Time:
212  text = locale.toString(value.toTime(), QLocale::ShortFormat);
213  break;
214  case QVariant::DateTime:
215  text = locale.toString(value.toDateTime().date(), QLocale::ShortFormat);
216  text += QLatin1Char(' ');
217  text += locale.toString(value.toDateTime().time(), QLocale::ShortFormat);
218  break;
219  default:
220  text = value.toString();
221  break;
222  }
223  return text;
224 }
225 
227 
228 KDReports::AutoTableElement::AutoTableElement(QAbstractItemModel *tableModel)
229  : d(new AutoTableElementPrivate)
230 {
231  d->m_tableModel = tableModel;
232 }
233 
235  : d(new AutoTableElementPrivate)
236 {
237  d->m_tableModel = KDReports::modelForKey(modelKey);
238 }
239 
241  : AbstractTableElement(other)
242  , d(new AutoTableElementPrivate(*other.d))
243 {
244 }
245 
247 {
248  if (&other == this)
249  return *this;
251  *d = *other.d;
252  return *this;
253 }
254 
256 {
257  delete d;
258 }
259 
260 void KDReports::AutoTableElementPrivate::fillCellFromHeaderData(int section, Qt::Orientation orientation, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable,
261  ReportBuilder &builder) const
262 {
263  FillCellHelper helper(m_tableModel, section, orientation, m_iconSize);
264  helper.fill(textTable, builder, textDoc, cell);
265 }
266 
267 QSize KDReports::AutoTableElementPrivate::fillTableCell(int row, int column, QTextTableCell &cell, QTextDocument &textDoc, QTextTable *textTable, ReportBuilder &builder) const
268 {
269  const QModelIndex index = m_tableModel->index(row, column);
270  const QSize span = m_tableModel->span(index);
271  FillCellHelper helper(m_tableModel, index, span, m_iconSize);
272  helper.fill(textTable, builder, textDoc, cell);
273  return span;
274 }
275 
277 {
278  if (!d->m_tableModel) {
279  return;
280  }
281  QTextDocument &textDoc = builder.currentDocument();
282  QTextCursor &textDocCursor = builder.cursor();
283  textDocCursor.beginEditBlock();
284 
285  QTextTableFormat tableFormat;
286  const int headerRowCount = d->m_horizontalHeaderVisible ? 1 : 0;
287  const int headerColumnCount = d->m_verticalHeaderVisible ? 1 : 0;
288  tableFormat.setHeaderRowCount(headerRowCount);
289  tableFormat.setProperty(KDReports::HeaderColumnsProperty, headerColumnCount);
290 
291  tableFormat.setAlignment(textDocCursor.blockFormat().alignment());
292  fillTableFormat(tableFormat, textDocCursor);
293 
294  while (d->m_tableModel->canFetchMore(QModelIndex()))
295  d->m_tableModel->fetchMore(QModelIndex());
296 
297  const int rows = d->m_tableModel->rowCount();
298  const int columns = d->m_tableModel->columnCount();
299 
300  QTextTable *textTable = textDocCursor.insertTable(rows + headerRowCount, columns + headerColumnCount, tableFormat);
301 
302  QTextCharFormat tableHeaderFormat;
303  tableHeaderFormat.setBackground(d->m_headerBackground);
304  // qDebug( "rows = %d, columns = %d", textTable->rows(), textTable->columns() );
305 
306  if (d->m_horizontalHeaderVisible) {
307  for (int column = 0; column < columns; column++) {
308  QTextTableCell cell = textTable->cellAt(0, column + headerColumnCount);
309  Q_ASSERT(cell.isValid());
310  cell.setFormat(tableHeaderFormat);
311  d->fillCellFromHeaderData(column, Qt::Horizontal, cell, textDoc, textTable, builder);
312  }
313  }
314 
315  if (d->m_verticalHeaderVisible) {
316  for (int row = 0; row < rows; row++) {
317  QTextTableCell cell = textTable->cellAt(row + headerRowCount, 0);
318  Q_ASSERT(cell.isValid());
319  cell.setFormat(tableHeaderFormat);
320  d->fillCellFromHeaderData(row, Qt::Vertical, cell, textDoc, textTable, builder);
321  }
322  }
323 
324  QVector<QBitArray> coveredCells;
325  coveredCells.resize(rows);
326  for (int row = 0; row < rows; row++)
327  coveredCells[row].resize(columns);
328 
329  // The normal data
330  for (int row = 0; row < rows; row++) {
331  for (int column = 0; column < columns; column++) {
332  if (coveredCells[row].testBit(column))
333  continue;
334  QTextTableCell cell = textTable->cellAt(row + headerRowCount, column + headerColumnCount);
335  Q_ASSERT(cell.isValid());
336  const QSize span = d->fillTableCell(row, column, cell, textDoc, textTable, builder);
337  if (span.isValid()) {
338  for (int r = row; r < row + span.height() && r < rows; ++r) {
339  for (int c = column; c < column + span.width() && c < columns; ++c) {
340  coveredCells[r].setBit(c);
341  }
342  }
343  }
344  }
345  }
346 
347  textDocCursor.movePosition(QTextCursor::End);
348  textDocCursor.endEditBlock();
349 
350  builder.currentDocumentData().registerAutoTable(textTable, this);
351 }
352 
354 {
355  // never used at the moment
356  return new AutoTableElement(*this);
357 }
358 
360 {
361  d->m_verticalHeaderVisible = visible;
362 }
363 
365 {
366  d->m_horizontalHeaderVisible = visible;
367 }
368 
370 {
371  d->m_headerBackground = brush;
372 }
373 
375 {
376  return d->m_verticalHeaderVisible;
377 }
378 
380 {
381  return d->m_horizontalHeaderVisible;
382 }
383 
385 {
386  d->m_iconSize = iconSize;
387 }
388 
390 {
391  return d->m_iconSize;
392 }
393 
394 QAbstractItemModel *KDReports::AutoTableElement::tableModel() const
395 {
396  return d->m_tableModel;
397 }
398 
399 void KDReports::AutoTableElement::setTableModel(QAbstractItemModel *tableModel)
400 {
401  d->m_tableModel = tableModel;
402 }
403 
404 void KDReports::AutoTableElement::setModelKey(const QString &modelKey)
405 {
406  d->m_tableModel = KDReports::modelForKey(modelKey);
407 }
408 
410 {
411  return d->m_headerBackground;
412 }
KDReports::AutoTableElement::tableModel
QAbstractItemModel * tableModel() const
Definition: KDReportsAutoTableElement.cpp:394
KDReports::TextDocumentData::registerAutoTable
void registerAutoTable(QTextTable *table, const KDReports::AutoTableElement *element)
Definition: KDReportsTextDocumentData.cpp:319
KDReports::ReportBuilder
Definition: KDReportsReportBuilder_p.h:41
KDReports::ReportBuilder::currentDocumentData
TextDocumentData & currentDocumentData()
Definition: KDReportsReportBuilder_p.h:69
KDReports::AutoTableElement::AutoTableElement
AutoTableElement(QAbstractItemModel *tableModel)
Definition: KDReportsAutoTableElement.cpp:228
KDReports::AutoTableElement::clone
Element * clone() const override
Definition: KDReportsAutoTableElement.cpp:353
KDReports::AutoTableElement::setIconSize
void setIconSize(QSize iconSize)
Definition: KDReportsAutoTableElement.cpp:384
KDReports::AutoTableElement::setModelKey
void setModelKey(const QString &modelKey)
Definition: KDReportsAutoTableElement.cpp:404
KDReports::modelForKey
QAbstractItemModel * modelForKey(const QString &key)
Definition: KDReportsReport.cpp:426
KDReports::HeaderColumnsProperty
@ HeaderColumnsProperty
Definition: KDReportsTextDocument_p.h:48
KDReports::AutoTableElement::isHorizontalHeaderVisible
bool isHorizontalHeaderVisible() const
Definition: KDReportsAutoTableElement.cpp:379
KDReportsReport_p.h
KDReports::Element
Definition: KDReportsElement.h:39
KDReports::AutoTableElement::build
void build(ReportBuilder &) const override
Definition: KDReportsAutoTableElement.cpp:276
KDReports::AutoTableElement
Definition: KDReportsAutoTableElement.h:40
KDReports::AutoTableElement::operator=
AutoTableElement & operator=(const AutoTableElement &other)
Definition: KDReportsAutoTableElement.cpp:246
KDReports::AutoTableElement::setTableModel
void setTableModel(QAbstractItemModel *tableModel)
Definition: KDReportsAutoTableElement.cpp:399
KDReports::AutoTableElement::setVerticalHeaderVisible
void setVerticalHeaderVisible(bool visible)
Definition: KDReportsAutoTableElement.cpp:359
KDReportsReportBuilder_p.h
KDReports::AutoTableElement::~AutoTableElement
~AutoTableElement() override
Definition: KDReportsAutoTableElement.cpp:255
KDReports::ReportBuilder::defaultFont
QFont defaultFont() const
Definition: KDReportsReportBuilder_p.h:77
KDReports::AbstractTableElement::operator=
AbstractTableElement & operator=(const AbstractTableElement &other)
Definition: KDReportsAbstractTableElement.cpp:57
KDReports::ReportBuilder::setupBlockFormat
void setupBlockFormat(QTextBlockFormat &blockFormat)
Definition: KDReportsReportBuilder.cpp:205
KDReportsAutoTableElement.h
KDReportsLayoutHelper_p.h
KDReports::AutoTableElement::iconSize
QSize iconSize() const
Definition: KDReportsAutoTableElement.cpp:389
KDReports::ReportBuilder::cursor
QTextCursor & cursor()
Definition: KDReportsReportBuilder_p.h:48
KDReports::TextDocumentData::addResourceName
void addResourceName(const QString &resourceName)
Definition: KDReportsTextDocumentData.cpp:408
KDReports::AutoTableElement::headerBackground
QBrush headerBackground() const
Definition: KDReportsAutoTableElement.cpp:409
KDReports::ReportBuilder::currentDocument
QTextDocument & currentDocument()
Definition: KDReportsReportBuilder_p.h:71
KDReports::AbstractTableElement
Definition: KDReportsAbstractTableElement.h:38
toVerticalAlignment
static QTextCharFormat::VerticalAlignment toVerticalAlignment(Qt::Alignment alignment)
Definition: KDReportsAutoTableElement.cpp:105
KDReports::AutoTableElement::setHeaderBackground
void setHeaderBackground(const QBrush &brush)
Definition: KDReportsAutoTableElement.cpp:369
KDReports::AutoTableElement::setHorizontalHeaderVisible
void setHorizontalHeaderVisible(bool visible)
Definition: KDReportsAutoTableElement.cpp:364
KDReports::AutoTableElement::isVerticalHeaderVisible
bool isVerticalHeaderVisible() const
Definition: KDReportsAutoTableElement.cpp:374
KDReports
Definition: KDReportsAbstractReportLayout_p.h:30

© 2007-2021 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-reports/
Generated on Sat Jan 8 2022 02:38:32 for KD Reports API Documentation by doxygen 1.8.17