// xlsxworksheet.cpp
#include <QtGlobal>
#include <QVariant>
#include <QDateTime>
#include <QDate>
#include <QTime>
#include <QPoint>
#include <QFile>
#include <QUrl>
#include <QDebug>
#include <QBuffer>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QTextDocument>
#include <QDir>
#include <QMapIterator>
#include <QMap>
#include <cmath>
#include "xlsxrichstring.h"
#include "xlsxcellreference.h"
#include "xlsxworksheet.h"
#include "xlsxworksheet_p.h"
#include "xlsxworkbook.h"
#include "xlsxformat.h"
#include "xlsxformat_p.h"
#include "xlsxutility_p.h"
#include "xlsxsharedstrings_p.h"
#include "xlsxdrawing_p.h"
#include "xlsxstyles_p.h"
#include "xlsxcell.h"
#include "xlsxcell_p.h"
#include "xlsxcellrange.h"
#include "xlsxconditionalformatting_p.h"
#include "xlsxdrawinganchor_p.h"
#include "xlsxchart.h"
#include "xlsxcellformula.h"
#include "xlsxcellformula_p.h"
#include "xlsxcelllocation.h"
QT_BEGIN_NAMESPACE_XLSX
WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag)
: AbstractSheetPrivate(p, flag),
windowProtection(false),
showFormulas(false),
showGridLines(true),
showRowColHeaders(true),
showZeros(true),
rightToLeft(false),
tabSelected(false),
showRuler(false),
showOutlineSymbols(true),
showWhiteSpace(true),
urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)"))
{
previous_row = 0;
outline_row_level = 0;
outline_col_level = 0;
default_row_height = 15;
default_row_zeroed = false;
}
WorksheetPrivate::~WorksheetPrivate()
{
}
/*
Calculate the "spans" attribute of the <row> tag. This is an
XLSX optimisation and isn't strictly required. However, it
makes comparing files easier. The span is the same for each
block of 16 rows.
*/
void WorksheetPrivate::calculateSpans() const
{
row_spans.clear();
int span_min = XLSX_COLUMN_MAX+1;
int span_max = -1;
for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
auto it = cellTable.constFind(row_num);
if (it != cellTable.constEnd()) {
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
if (it->contains(col_num)) {
if (span_max == -1) {
span_min = col_num;
span_max = col_num;
} else {
if (col_num < span_min)
span_min = col_num;
else if (col_num > span_max)
span_max = col_num;
}
}
}
}
auto cIt = comments.constFind(row_num);
if (cIt != comments.constEnd()) {
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
if (cIt->contains(col_num)) {
if (span_max == -1) {
span_min = col_num;
span_max = col_num;
} else {
if (col_num < span_min)
span_min = col_num;
else if (col_num > span_max)
span_max = col_num;
}
}
}
}
if (row_num%16 == 0 || row_num == dimension.lastRow()) {
if (span_max != -1) {
row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max);
span_min = XLSX_COLUMN_MAX+1;
span_max = -1;
}
}
}
}
QString WorksheetPrivate::generateDimensionString() const
{
if (!dimension.isValid())
return QStringLiteral("A1");
else
return dimension.toString();
}
/*
Check that row and col are valid and store the max and min
values for use in other methods/elements. The ignore_row /
ignore_col flags is used to indicate that we wish to perform
the dimension check without storing the value. The ignore
flags are use by setRow() and dataValidate.
*/
int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col)
{
Q_ASSERT_X(row!=0, "checkDimensions", "row should start from 1 instead of 0");
Q_ASSERT_X(col!=0, "checkDimensions", "column should start from 1 instead of 0");
if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1)
return -1;
if (!ignore_row) {
if (row < dimension.firstRow() || dimension.firstRow() == -1) dimension.setFirstRow(row);
if (row > dimension.lastRow()) dimension.setLastRow(row);
}
if (!ignore_col) {
if (col < dimension.firstColumn() || dimension.firstColumn() == -1) dimension.setFirstColumn(col);
if (col > dimension.lastColumn()) dimension.setLastColumn(col);
}
return 0;
}
/*!
\class Worksheet
\inmodule QtXlsx
\brief Represent one worksheet in the workbook.
*/
/*!
* \internal
*/
Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag)
:AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag))
{
if (!workbook) //For unit test propose only. Ignore the memery leak.
d_func()->workbook = new Workbook(flag);
}
/*!
* \internal
*
* Make a copy of this sheet.
*/
Worksheet *Worksheet::copy(const QString &distName, int distId) const
{
Q_D(const Worksheet);
Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch);
WorksheetPrivate *sheet_d = sheet->d_func();
sheet_d->dimension = d->dimension;
QMapIterator<int, QMap<int, QSharedPointer<Cell> > > it(d->cellTable);
while (it.hasNext())
{
it.next();
int row = it.key();
QMapIterator<int, QSharedPointer<Cell> > it2(it.value());
while (it2.hasNext())
{
it2.next();
int col = it2.key();
QSharedPointer<Cell> cell(new Cell(it2.value().data()));
cell->d_ptr->parent = sheet;
if (cell->cellType() == Cell::SharedStringType)
d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString);
sheet_d->cellTable[row][col] = cell;
}
}
sheet_d->merges = d->merges;
// sheet_d->rowsInfo = d->rowsInfo;
// sheet_d->colsInfo = d->colsInfo;
// sheet_d->colsInfoHelper = d->colsInfoHelper;
// sheet_d->dataValidationsList = d->dataValidationsList;
// sheet_d->conditionalFormattingList = d->conditionalFormattingList;
return sheet;
}
/*!
* Destroys this workssheet.
*/
Worksheet::~Worksheet()
{
}
/*!
* Returns whether sheet is protected.
*/
bool Worksheet::isWindowProtected() const
{
Q_D(const Worksheet);
return d->windowProtection;
}
/*!
* Protects/unprotects the sheet based on \a protect.
*/
void Worksheet::setWindowProtected(bool protect)
{
Q_D(Worksheet);
d->windowProtection = protect;
}
/*!
* Return whether formulas instead of their calculated results shown in cells
*/
bool Worksheet::isFormulasVisible() const
{
Q_D(const Worksheet);
return d->showFormulas;
}
/*!
* Show formulas in cells instead of their calculated results when \a visible is true.
*/
void Worksheet::setFormulasVisible(bool visible)
{
Q_D(Worksheet);
d->showFormulas = visible;
}
/*!
* Return whether gridlines is shown or not.
*/
bool Worksheet::isGridLinesVisible() const
{
Q_D(const Worksheet);
return d->showGridLines;
}
/*!
* Show or hide the gridline based on \a visible
*/
void Worksheet::setGridLinesVisible(bool visible)
{
Q_D(Worksheet);
d->showGridLines = visible;
}
/*!
* Return whether is row and column headers is vislbe.
*/
bool Worksheet::isRowColumnHeadersVisible() const
{
Q_D(const Worksheet);
return d->showRowColHeaders;
}
/*!
* Show or hide the row column headers based on \a visible
*/
void Worksheet::setRowColumnHeadersVisible(bool visible)
{
Q_D(Worksheet);
d->showRowColHeaders = visible;
}
/*!
* Return whether the sheet is shown right-to-left or not.
*/
bool Worksheet::isRightToLeft() const
{
Q_D(const Worksheet);
return d->rightToLeft;
}
/*!
* Enable or disable the right-to-left based on \a enable.
*/
void Worksheet::setRightToLeft(bool enable)
{
Q_D(Worksheet);
d->rightToLeft = enable;
}
/*!
* Return whether is cells that have zero value show a zero.
*/
bool Worksheet::isZerosVisible() const
{
Q_D(const Worksheet);
return d->showZeros;
}
/*!
* Show a zero in cells that have zero value if \a visible is true.
*/
void Worksheet::setZerosVisible(bool visible)
{
Q_D(Worksheet);
d->showZeros = visible;
}
/*!
* Return whether this tab is selected.
*/
bool Worksheet