Mercurial > clickerconvert
comparison src/xlsx/xlsxworksheet.cpp @ 1:93d3106bb9a4
Add qt xlsx library
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 22 Mar 2016 10:38:08 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:49cd5cc0b072 | 1:93d3106bb9a4 |
---|---|
1 /**************************************************************************** | |
2 ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> | |
3 ** All right reserved. | |
4 ** | |
5 ** Permission is hereby granted, free of charge, to any person obtaining | |
6 ** a copy of this software and associated documentation files (the | |
7 ** "Software"), to deal in the Software without restriction, including | |
8 ** without limitation the rights to use, copy, modify, merge, publish, | |
9 ** distribute, sublicense, and/or sell copies of the Software, and to | |
10 ** permit persons to whom the Software is furnished to do so, subject to | |
11 ** the following conditions: | |
12 ** | |
13 ** The above copyright notice and this permission notice shall be | |
14 ** included in all copies or substantial portions of the Software. | |
15 ** | |
16 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
19 ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
20 ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
21 ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
22 ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
23 ** | |
24 ****************************************************************************/ | |
25 #include "xlsxrichstring.h" | |
26 #include "xlsxcellreference.h" | |
27 #include "xlsxworksheet.h" | |
28 #include "xlsxworksheet_p.h" | |
29 #include "xlsxworkbook.h" | |
30 #include "xlsxformat.h" | |
31 #include "xlsxformat_p.h" | |
32 #include "xlsxutility_p.h" | |
33 #include "xlsxsharedstrings_p.h" | |
34 #include "xlsxdrawing_p.h" | |
35 #include "xlsxstyles_p.h" | |
36 #include "xlsxcell.h" | |
37 #include "xlsxcell_p.h" | |
38 #include "xlsxcellrange.h" | |
39 #include "xlsxconditionalformatting_p.h" | |
40 #include "xlsxdrawinganchor_p.h" | |
41 #include "xlsxchart.h" | |
42 #include "xlsxcellformula.h" | |
43 #include "xlsxcellformula_p.h" | |
44 | |
45 #include <QVariant> | |
46 #include <QDateTime> | |
47 #include <QPoint> | |
48 #include <QFile> | |
49 #include <QUrl> | |
50 #include <QRegularExpression> | |
51 #include <QDebug> | |
52 #include <QBuffer> | |
53 #include <QXmlStreamWriter> | |
54 #include <QXmlStreamReader> | |
55 #include <QTextDocument> | |
56 #include <QDir> | |
57 | |
58 #include <math.h> | |
59 | |
60 QT_BEGIN_NAMESPACE_XLSX | |
61 | |
62 WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag) | |
63 : AbstractSheetPrivate(p, flag) | |
64 , windowProtection(false), showFormulas(false), showGridLines(true), showRowColHeaders(true) | |
65 , showZeros(true), rightToLeft(false), tabSelected(false), showRuler(false) | |
66 , showOutlineSymbols(true), showWhiteSpace(true), urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)")) | |
67 { | |
68 previous_row = 0; | |
69 | |
70 outline_row_level = 0; | |
71 outline_col_level = 0; | |
72 | |
73 default_row_height = 15; | |
74 default_row_zeroed = false; | |
75 } | |
76 | |
77 WorksheetPrivate::~WorksheetPrivate() | |
78 { | |
79 } | |
80 | |
81 /* | |
82 Calculate the "spans" attribute of the <row> tag. This is an | |
83 XLSX optimisation and isn't strictly required. However, it | |
84 makes comparing files easier. The span is the same for each | |
85 block of 16 rows. | |
86 */ | |
87 void WorksheetPrivate::calculateSpans() const | |
88 { | |
89 row_spans.clear(); | |
90 int span_min = XLSX_COLUMN_MAX+1; | |
91 int span_max = -1; | |
92 | |
93 for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) { | |
94 if (cellTable.contains(row_num)) { | |
95 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { | |
96 if (cellTable[row_num].contains(col_num)) { | |
97 if (span_max == -1) { | |
98 span_min = col_num; | |
99 span_max = col_num; | |
100 } else { | |
101 if (col_num < span_min) | |
102 span_min = col_num; | |
103 else if (col_num > span_max) | |
104 span_max = col_num; | |
105 } | |
106 } | |
107 } | |
108 } | |
109 if (comments.contains(row_num)) { | |
110 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { | |
111 if (comments[row_num].contains(col_num)) { | |
112 if (span_max == -1) { | |
113 span_min = col_num; | |
114 span_max = col_num; | |
115 } else { | |
116 if (col_num < span_min) | |
117 span_min = col_num; | |
118 else if (col_num > span_max) | |
119 span_max = col_num; | |
120 } | |
121 } | |
122 } | |
123 } | |
124 | |
125 if (row_num%16 == 0 || row_num == dimension.lastRow()) { | |
126 if (span_max != -1) { | |
127 row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max); | |
128 span_min = XLSX_COLUMN_MAX+1; | |
129 span_max = -1; | |
130 } | |
131 } | |
132 } | |
133 } | |
134 | |
135 | |
136 QString WorksheetPrivate::generateDimensionString() const | |
137 { | |
138 if (!dimension.isValid()) | |
139 return QStringLiteral("A1"); | |
140 else | |
141 return dimension.toString(); | |
142 } | |
143 | |
144 /* | |
145 Check that row and col are valid and store the max and min | |
146 values for use in other methods/elements. The ignore_row / | |
147 ignore_col flags is used to indicate that we wish to perform | |
148 the dimension check without storing the value. The ignore | |
149 flags are use by setRow() and dataValidate. | |
150 */ | |
151 int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col) | |
152 { | |
153 Q_ASSERT_X(row!=0, "checkDimensions", "row should start from 1 instead of 0"); | |
154 Q_ASSERT_X(col!=0, "checkDimensions", "column should start from 1 instead of 0"); | |
155 | |
156 if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1) | |
157 return -1; | |
158 | |
159 if (!ignore_row) { | |
160 if (row < dimension.firstRow() || dimension.firstRow() == -1) dimension.setFirstRow(row); | |
161 if (row > dimension.lastRow()) dimension.setLastRow(row); | |
162 } | |
163 if (!ignore_col) { | |
164 if (col < dimension.firstColumn() || dimension.firstColumn() == -1) dimension.setFirstColumn(col); | |
165 if (col > dimension.lastColumn()) dimension.setLastColumn(col); | |
166 } | |
167 | |
168 return 0; | |
169 } | |
170 | |
171 /*! | |
172 \class Worksheet | |
173 \inmodule QtXlsx | |
174 \brief Represent one worksheet in the workbook. | |
175 */ | |
176 | |
177 /*! | |
178 * \internal | |
179 */ | |
180 Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag) | |
181 :AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag)) | |
182 { | |
183 if (!workbook) //For unit test propose only. Ignore the memery leak. | |
184 d_func()->workbook = new Workbook(flag); | |
185 } | |
186 | |
187 /*! | |
188 * \internal | |
189 * | |
190 * Make a copy of this sheet. | |
191 */ | |
192 | |
193 Worksheet *Worksheet::copy(const QString &distName, int distId) const | |
194 { | |
195 Q_D(const Worksheet); | |
196 Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch); | |
197 WorksheetPrivate *sheet_d = sheet->d_func(); | |
198 | |
199 sheet_d->dimension = d->dimension; | |
200 | |
201 QMapIterator<int, QMap<int, QSharedPointer<Cell> > > it(d->cellTable); | |
202 while (it.hasNext()) { | |
203 it.next(); | |
204 int row = it.key(); | |
205 QMapIterator<int, QSharedPointer<Cell> > it2(it.value()); | |
206 while (it2.hasNext()) { | |
207 it2.next(); | |
208 int col = it2.key(); | |
209 | |
210 QSharedPointer<Cell> cell(new Cell(it2.value().data())); | |
211 cell->d_ptr->parent = sheet; | |
212 | |
213 if (cell->cellType() == Cell::SharedStringType) | |
214 d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString); | |
215 | |
216 sheet_d->cellTable[row][col] = cell; | |
217 } | |
218 } | |
219 | |
220 sheet_d->merges = d->merges; | |
221 // sheet_d->rowsInfo = d->rowsInfo; | |
222 // sheet_d->colsInfo = d->colsInfo; | |
223 // sheet_d->colsInfoHelper = d->colsInfoHelper; | |
224 // sheet_d->dataValidationsList = d->dataValidationsList; | |
225 // sheet_d->conditionalFormattingList = d->conditionalFormattingList; | |
226 | |
227 return sheet; | |
228 } | |
229 | |
230 /*! | |
231 * Destroys this workssheet. | |
232 */ | |
233 Worksheet::~Worksheet() | |
234 { | |
235 } | |
236 | |
237 /*! | |
238 * Returns whether sheet is protected. | |
239 */ | |
240 bool Worksheet::isWindowProtected() const | |
241 { | |
242 Q_D(const Worksheet); | |
243 return d->windowProtection; | |
244 } | |
245 | |
246 /*! | |
247 * Protects/unprotects the sheet based on \a protect. | |
248 */ | |
249 void Worksheet::setWindowProtected(bool protect) | |
250 { | |
251 Q_D(Worksheet); | |
252 d->windowProtection = protect; | |
253 } | |
254 | |
255 /*! | |
256 * Return whether formulas instead of their calculated results shown in cells | |
257 */ | |
258 bool Worksheet::isFormulasVisible() const | |
259 { | |
260 Q_D(const Worksheet); | |
261 return d->showFormulas; | |
262 } | |
263 | |
264 /*! | |
265 * Show formulas in cells instead of their calculated results when \a visible is true. | |
266 */ | |
267 void Worksheet::setFormulasVisible(bool visible) | |
268 { | |
269 Q_D(Worksheet); | |
270 d->showFormulas = visible; | |
271 } | |
272 | |
273 /*! | |
274 * Return whether gridlines is shown or not. | |
275 */ | |
276 bool Worksheet::isGridLinesVisible() const | |
277 { | |
278 Q_D(const Worksheet); | |
279 return d->showGridLines; | |
280 } | |
281 | |
282 /*! | |
283 * Show or hide the gridline based on \a visible | |
284 */ | |
285 void Worksheet::setGridLinesVisible(bool visible) | |
286 { | |
287 Q_D(Worksheet); | |
288 d->showGridLines = visible; | |
289 } | |
290 | |
291 /*! | |
292 * Return whether is row and column headers is vislbe. | |
293 */ | |
294 bool Worksheet::isRowColumnHeadersVisible() const | |
295 { | |
296 Q_D(const Worksheet); | |
297 return d->showRowColHeaders; | |
298 } | |
299 | |
300 /*! | |
301 * Show or hide the row column headers based on \a visible | |
302 */ | |
303 void Worksheet::setRowColumnHeadersVisible(bool visible) | |
304 { | |
305 Q_D(Worksheet); | |
306 d->showRowColHeaders = visible; | |
307 } | |
308 | |
309 | |
310 /*! | |
311 * Return whether the sheet is shown right-to-left or not. | |
312 */ | |
313 bool Worksheet::isRightToLeft() const | |
314 { | |
315 Q_D(const Worksheet); | |
316 return d->rightToLeft; | |
317 } | |
318 | |
319 /*! | |
320 * Enable or disable the right-to-left based on \a enable. | |
321 */ | |
322 void Worksheet::setRightToLeft(bool enable) | |
323 { | |
324 Q_D(Worksheet); | |
325 d->rightToLeft = enable; | |
326 } | |
327 | |
328 /*! | |
329 * Return whether is cells that have zero value show a zero. | |
330 */ | |
331 bool Worksheet::isZerosVisible() const | |
332 { | |
333 Q_D(const Worksheet); | |
334 return d->showZeros; | |
335 } | |
336 | |
337 /*! | |
338 * Show a zero in cells that have zero value if \a visible is true. | |
339 */ | |
340 void Worksheet::setZerosVisible(bool visible) | |
341 { | |
342 Q_D(Worksheet); | |
343 d->showZeros = visible; | |
344 } | |
345 | |
346 /*! | |
347 * Return whether this tab is selected. | |
348 */ | |
349 bool Worksheet::isSelected() const | |
350 { | |
351 Q_D(const Worksheet); | |
352 return d->tabSelected; | |
353 } | |
354 | |
355 /*! | |
356 * Select this sheet if \a select is true. | |
357 */ | |
358 void Worksheet::setSelected(bool select) | |
359 { | |
360 Q_D(Worksheet); | |
361 d->tabSelected = select; | |
362 } | |
363 | |
364 /*! | |
365 * Return whether is ruler is shown. | |
366 */ | |
367 bool Worksheet::isRulerVisible() const | |
368 { | |
369 Q_D(const Worksheet); | |
370 return d->showRuler; | |
371 | |
372 } | |
373 | |
374 /*! | |
375 * Show or hide the ruler based on \a visible. | |
376 */ | |
377 void Worksheet::setRulerVisible(bool visible) | |
378 { | |
379 Q_D(Worksheet); | |
380 d->showRuler = visible; | |
381 | |
382 } | |
383 | |
384 /*! | |
385 * Return whether is outline symbols is shown. | |
386 */ | |
387 bool Worksheet::isOutlineSymbolsVisible() const | |
388 { | |
389 Q_D(const Worksheet); | |
390 return d->showOutlineSymbols; | |
391 } | |
392 | |
393 /*! | |
394 * Show or hide the outline symbols based ib \a visible. | |
395 */ | |
396 void Worksheet::setOutlineSymbolsVisible(bool visible) | |
397 { | |
398 Q_D(Worksheet); | |
399 d->showOutlineSymbols = visible; | |
400 } | |
401 | |
402 /*! | |
403 * Return whether is white space is shown. | |
404 */ | |
405 bool Worksheet::isWhiteSpaceVisible() const | |
406 { | |
407 Q_D(const Worksheet); | |
408 return d->showWhiteSpace; | |
409 } | |
410 | |
411 /*! | |
412 * Show or hide the white space based on \a visible. | |
413 */ | |
414 void Worksheet::setWhiteSpaceVisible(bool visible) | |
415 { | |
416 Q_D(Worksheet); | |
417 d->showWhiteSpace = visible; | |
418 } | |
419 | |
420 /*! | |
421 * Write \a value to cell (\a row, \a column) with the \a format. | |
422 * Both \a row and \a column are all 1-indexed value. | |
423 * | |
424 * Returns true on success. | |
425 */ | |
426 bool Worksheet::write(int row, int column, const QVariant &value, const Format &format) | |
427 { | |
428 Q_D(Worksheet); | |
429 | |
430 if (d->checkDimensions(row, column)) | |
431 return false; | |
432 | |
433 bool ret = true; | |
434 if (value.isNull()) { | |
435 //Blank | |
436 ret = writeBlank(row, column, format); | |
437 } else if (value.userType() == QMetaType::QString) { | |
438 //String | |
439 QString token = value.toString(); | |
440 bool ok; | |
441 | |
442 if (token.startsWith(QLatin1String("="))) { | |
443 //convert to formula | |
444 ret = writeFormula(row, column, CellFormula(token), format); | |
445 } else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern)) { | |
446 //convert to url | |
447 ret = writeHyperlink(row, column, QUrl(token)); | |
448 } else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok)) { | |
449 //Try convert string to number if the flag enabled. | |
450 ret = writeString(row, column, value.toString(), format); | |
451 } else { | |
452 //normal string now | |
453 ret = writeString(row, column, token, format); | |
454 } | |
455 } else if (value.userType() == qMetaTypeId<RichString>()) { | |
456 ret = writeString(row, column, value.value<RichString>(), format); | |
457 } else if (value.userType() == QMetaType::Int || value.userType() == QMetaType::UInt | |
458 || value.userType() == QMetaType::LongLong || value.userType() == QMetaType::ULongLong | |
459 || value.userType() == QMetaType::Double || value.userType() == QMetaType::Float) { | |
460 //Number | |
461 | |
462 ret = writeNumeric(row, column, value.toDouble(), format); | |
463 } else if (value.userType() == QMetaType::Bool) { | |
464 //Bool | |
465 ret = writeBool(row,column, value.toBool(), format); | |
466 } else if (value.userType() == QMetaType::QDateTime || value.userType() == QMetaType::QDate) { | |
467 //DateTime, Date | |
468 // note that, QTime cann't convert to QDateTime | |
469 ret = writeDateTime(row, column, value.toDateTime(), format); | |
470 } else if (value.userType() == QMetaType::QTime) { | |
471 //Time | |
472 ret = writeTime(row, column, value.toTime(), format); | |
473 } else if (value.userType() == QMetaType::QUrl) { | |
474 //Url | |
475 ret = writeHyperlink(row, column, value.toUrl(), format); | |
476 } else { | |
477 //Wrong type | |
478 return false; | |
479 } | |
480 | |
481 return ret; | |
482 } | |
483 | |
484 /*! | |
485 * \overload | |
486 * Write \a value to cell \a row_column with the \a format. | |
487 * Both row and column are all 1-indexed value. | |
488 * Returns true on success. | |
489 */ | |
490 bool Worksheet::write(const CellReference &row_column, const QVariant &value, const Format &format) | |
491 { | |
492 if (!row_column.isValid()) | |
493 return false; | |
494 | |
495 return write(row_column.row(), row_column.column(), value, format); | |
496 } | |
497 | |
498 /*! | |
499 \overload | |
500 Return the contents of the cell \a row_column. | |
501 */ | |
502 QVariant Worksheet::read(const CellReference &row_column) const | |
503 { | |
504 if (!row_column.isValid()) | |
505 return QVariant(); | |
506 | |
507 return read(row_column.row(), row_column.column()); | |
508 } | |
509 | |
510 /*! | |
511 Return the contents of the cell (\a row, \a column). | |
512 */ | |
513 QVariant Worksheet::read(int row, int column) const | |
514 { | |
515 Q_D(const Worksheet); | |
516 | |
517 Cell *cell = cellAt(row, column); | |
518 if (!cell) | |
519 return QVariant(); | |
520 | |
521 if (cell->hasFormula()) { | |
522 if (cell->formula().formulaType() == CellFormula::NormalType) { | |
523 return QVariant(QLatin1String("=")+cell->formula().formulaText()); | |
524 } else if (cell->formula().formulaType() == CellFormula::SharedType) { | |
525 if (!cell->formula().formulaText().isEmpty()) { | |
526 return QVariant(QLatin1String("=")+cell->formula().formulaText()); | |
527 } else { | |
528 const CellFormula &rootFormula = d->sharedFormulaMap[cell->formula().sharedIndex()]; | |
529 CellReference rootCellRef = rootFormula.reference().topLeft(); | |
530 QString rootFormulaText = rootFormula.formulaText(); | |
531 QString newFormulaText = convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column)); | |
532 return QVariant(QLatin1String("=")+newFormulaText); | |
533 } | |
534 } | |
535 } | |
536 | |
537 if (cell->isDateTime()) { | |
538 double val = cell->value().toDouble(); | |
539 QDateTime dt = cell->dateTime(); | |
540 if (val < 1) | |
541 return dt.time(); | |
542 if (fmod(val, 1.0) < 1.0/(1000*60*60*24)) //integer | |
543 return dt.date(); | |
544 return dt; | |
545 } | |
546 | |
547 return cell->value(); | |
548 } | |
549 | |
550 /*! | |
551 * Returns the cell at the given \a row_column. If there | |
552 * is no cell at the specified position, the function returns 0. | |
553 */ | |
554 Cell *Worksheet::cellAt(const CellReference &row_column) const | |
555 { | |
556 if (!row_column.isValid()) | |
557 return 0; | |
558 | |
559 return cellAt(row_column.row(), row_column.column()); | |
560 } | |
561 | |
562 /*! | |
563 * Returns the cell at the given \a row and \a column. If there | |
564 * is no cell at the specified position, the function returns 0. | |
565 */ | |
566 Cell *Worksheet::cellAt(int row, int column) const | |
567 { | |
568 Q_D(const Worksheet); | |
569 if (!d->cellTable.contains(row)) | |
570 return 0; | |
571 if (!d->cellTable[row].contains(column)) | |
572 return 0; | |
573 | |
574 return d->cellTable[row][column].data(); | |
575 } | |
576 | |
577 Format WorksheetPrivate::cellFormat(int row, int col) const | |
578 { | |
579 if (!cellTable.contains(row)) | |
580 return Format(); | |
581 if (!cellTable[row].contains(col)) | |
582 return Format(); | |
583 return cellTable[row][col]->format(); | |
584 } | |
585 | |
586 /*! | |
587 \overload | |
588 Write string \a value to the cell \a row_column with the \a format. | |
589 | |
590 Returns true on success. | |
591 */ | |
592 bool Worksheet::writeString(const CellReference &row_column, const RichString &value, const Format &format) | |
593 { | |
594 if (!row_column.isValid()) | |
595 return false; | |
596 | |
597 return writeString(row_column.row(), row_column.column(), value, format); | |
598 } | |
599 | |
600 /*! | |
601 Write string \a value to the cell (\a row, \a column) with the \a format. | |
602 Returns true on success. | |
603 */ | |
604 bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format) | |
605 { | |
606 Q_D(Worksheet); | |
607 // QString content = value.toPlainString(); | |
608 if (d->checkDimensions(row, column)) | |
609 return false; | |
610 | |
611 // if (content.size() > d->xls_strmax) { | |
612 // content = content.left(d->xls_strmax); | |
613 // error = -2; | |
614 // } | |
615 | |
616 d->sharedStrings()->addSharedString(value); | |
617 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
618 if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid()) | |
619 fmt.mergeFormat(value.fragmentFormat(0)); | |
620 d->workbook->styles()->addXfFormat(fmt); | |
621 QSharedPointer<Cell> cell = QSharedPointer<Cell>(new Cell(value.toPlainString(), Cell::SharedStringType, fmt, this)); | |
622 cell->d_ptr->richString = value; | |
623 d->cellTable[row][column] = cell; | |
624 return true; | |
625 } | |
626 | |
627 /*! | |
628 \overload | |
629 Write string \a value to the cell \a row_column with the \a format. | |
630 */ | |
631 bool Worksheet::writeString(const CellReference &row_column, const QString &value, const Format &format) | |
632 { | |
633 if (!row_column.isValid()) | |
634 return false; | |
635 | |
636 return writeString(row_column.row(), row_column.column(), value, format); | |
637 } | |
638 | |
639 /*! | |
640 \overload | |
641 | |
642 Write string \a value to the cell (\a row, \a column) with the \a format. | |
643 Returns true on success. | |
644 */ | |
645 bool Worksheet::writeString(int row, int column, const QString &value, const Format &format) | |
646 { | |
647 Q_D(Worksheet); | |
648 if (d->checkDimensions(row, column)) | |
649 return false; | |
650 | |
651 RichString rs; | |
652 if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value)) | |
653 rs.setHtml(value); | |
654 else | |
655 rs.addFragment(value, Format()); | |
656 | |
657 return writeString(row, column, rs, format); | |
658 } | |
659 | |
660 /*! | |
661 \overload | |
662 Write string \a value to the cell \a row_column with the \a format | |
663 */ | |
664 bool Worksheet::writeInlineString(const CellReference &row_column, const QString &value, const Format &format) | |
665 { | |
666 if (!row_column.isValid()) | |
667 return false; | |
668 | |
669 return writeInlineString(row_column.row(), row_column.column(), value, format); | |
670 } | |
671 | |
672 /*! | |
673 Write string \a value to the cell (\a row, \a column) with the \a format. | |
674 Returns true on success. | |
675 */ | |
676 bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format) | |
677 { | |
678 Q_D(Worksheet); | |
679 //int error = 0; | |
680 QString content = value; | |
681 if (d->checkDimensions(row, column)) | |
682 return false; | |
683 | |
684 if (value.size() > XLSX_STRING_MAX) { | |
685 content = value.left(XLSX_STRING_MAX); | |
686 //error = -2; | |
687 } | |
688 | |
689 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
690 d->workbook->styles()->addXfFormat(fmt); | |
691 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::InlineStringType, fmt, this)); | |
692 return true; | |
693 } | |
694 | |
695 /*! | |
696 \overload | |
697 Write numeric \a value to the cell \a row_column with the \a format. | |
698 Returns true on success. | |
699 */ | |
700 bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format) | |
701 { | |
702 if (!row_column.isValid()) | |
703 return false; | |
704 | |
705 return writeNumeric(row_column.row(), row_column.column(), value, format); | |
706 } | |
707 | |
708 /*! | |
709 Write numeric \a value to the cell (\a row, \a column) with the \a format. | |
710 Returns true on success. | |
711 */ | |
712 bool Worksheet::writeNumeric(int row, int column, double value, const Format &format) | |
713 { | |
714 Q_D(Worksheet); | |
715 if (d->checkDimensions(row, column)) | |
716 return false; | |
717 | |
718 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
719 d->workbook->styles()->addXfFormat(fmt); | |
720 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this)); | |
721 return true; | |
722 } | |
723 | |
724 /*! | |
725 \overload | |
726 Write \a formula to the cell \a row_column with the \a format and \a result. | |
727 Returns true on success. | |
728 */ | |
729 bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format, double result) | |
730 { | |
731 if (!row_column.isValid()) | |
732 return false; | |
733 | |
734 return writeFormula(row_column.row(), row_column.column(), formula, format, result); | |
735 } | |
736 | |
737 /*! | |
738 Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result. | |
739 Returns true on success. | |
740 */ | |
741 bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result) | |
742 { | |
743 Q_D(Worksheet); | |
744 if (d->checkDimensions(row, column)) | |
745 return false; | |
746 | |
747 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
748 d->workbook->styles()->addXfFormat(fmt); | |
749 | |
750 CellFormula formula = formula_; | |
751 formula.d->ca = true; | |
752 if (formula.formulaType() == CellFormula::SharedType) { | |
753 //Assign proper shared index for shared formula | |
754 int si=0; | |
755 while(d->sharedFormulaMap.contains(si)) | |
756 ++si; | |
757 formula.d->si = si; | |
758 d->sharedFormulaMap[si] = formula; | |
759 } | |
760 | |
761 QSharedPointer<Cell> data = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this)); | |
762 data->d_ptr->formula = formula; | |
763 d->cellTable[row][column] = data; | |
764 | |
765 CellRange range = formula.reference(); | |
766 if (formula.formulaType() == CellFormula::SharedType) { | |
767 CellFormula sf(QString(), CellFormula::SharedType); | |
768 sf.d->si = formula.sharedIndex(); | |
769 for (int r=range.firstRow(); r<=range.lastRow(); ++r) { | |
770 for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) { | |
771 if (!(r==row && c==column)) { | |
772 if(Cell *cell = cellAt(r, c)) { | |
773 cell->d_ptr->formula = sf; | |
774 } else { | |
775 QSharedPointer<Cell> newCell = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this)); | |
776 newCell->d_ptr->formula = sf; | |
777 d->cellTable[r][c] = newCell; | |
778 } | |
779 } | |
780 } | |
781 } | |
782 } else if (formula.formulaType() == CellFormula::SharedType) { | |
783 | |
784 } | |
785 | |
786 return true; | |
787 } | |
788 | |
789 /*! | |
790 \overload | |
791 Write a empty cell \a row_column with the \a format. | |
792 Returns true on success. | |
793 */ | |
794 bool Worksheet::writeBlank(const CellReference &row_column, const Format &format) | |
795 { | |
796 if (!row_column.isValid()) | |
797 return false; | |
798 | |
799 return writeBlank(row_column.row(), row_column.column(), format); | |
800 } | |
801 | |
802 /*! | |
803 Write a empty cell (\a row, \a column) with the \a format. | |
804 Returns true on success. | |
805 */ | |
806 bool Worksheet::writeBlank(int row, int column, const Format &format) | |
807 { | |
808 Q_D(Worksheet); | |
809 if (d->checkDimensions(row, column)) | |
810 return false; | |
811 | |
812 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
813 d->workbook->styles()->addXfFormat(fmt); | |
814 | |
815 //Note: NumberType with an invalid QVariant value means blank. | |
816 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(QVariant(), Cell::NumberType, fmt, this)); | |
817 | |
818 return true; | |
819 } | |
820 /*! | |
821 \overload | |
822 Write a bool \a value to the cell \a row_column with the \a format. | |
823 Returns true on success. | |
824 */ | |
825 bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format) | |
826 { | |
827 if (!row_column.isValid()) | |
828 return false; | |
829 | |
830 return writeBool(row_column.row(), row_column.column(), value, format); | |
831 } | |
832 | |
833 /*! | |
834 Write a bool \a value to the cell (\a row, \a column) with the \a format. | |
835 Returns true on success. | |
836 */ | |
837 bool Worksheet::writeBool(int row, int column, bool value, const Format &format) | |
838 { | |
839 Q_D(Worksheet); | |
840 if (d->checkDimensions(row, column)) | |
841 return false; | |
842 | |
843 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
844 d->workbook->styles()->addXfFormat(fmt); | |
845 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::BooleanType, fmt, this)); | |
846 | |
847 return true; | |
848 } | |
849 /*! | |
850 \overload | |
851 Write a QDateTime \a dt to the cell \a row_column with the \a format. | |
852 Returns true on success. | |
853 */ | |
854 bool Worksheet::writeDateTime(const CellReference &row_column, const QDateTime &dt, const Format &format) | |
855 { | |
856 if (!row_column.isValid()) | |
857 return false; | |
858 | |
859 return writeDateTime(row_column.row(), row_column.column(), dt, format); | |
860 } | |
861 | |
862 /*! | |
863 Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format. | |
864 Returns true on success. | |
865 */ | |
866 bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format) | |
867 { | |
868 Q_D(Worksheet); | |
869 if (d->checkDimensions(row, column)) | |
870 return false; | |
871 | |
872 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
873 if (!fmt.isValid() || !fmt.isDateTimeFormat()) | |
874 fmt.setNumberFormat(d->workbook->defaultDateFormat()); | |
875 d->workbook->styles()->addXfFormat(fmt); | |
876 | |
877 double value = datetimeToNumber(dt, d->workbook->isDate1904()); | |
878 | |
879 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this)); | |
880 | |
881 return true; | |
882 } | |
883 | |
884 /*! | |
885 \overload | |
886 Write a QTime \a t to the cell \a row_column with the \a format. | |
887 Returns true on success. | |
888 */ | |
889 bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format) | |
890 { | |
891 if (!row_column.isValid()) | |
892 return false; | |
893 | |
894 return writeTime(row_column.row(), row_column.column(), t, format); | |
895 } | |
896 | |
897 /*! | |
898 Write a QTime \a t to the cell (\a row, \a column) with the \a format. | |
899 Returns true on success. | |
900 */ | |
901 bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format) | |
902 { | |
903 Q_D(Worksheet); | |
904 if (d->checkDimensions(row, column)) | |
905 return false; | |
906 | |
907 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
908 if (!fmt.isValid() || !fmt.isDateTimeFormat()) | |
909 fmt.setNumberFormat(QStringLiteral("hh:mm:ss")); | |
910 d->workbook->styles()->addXfFormat(fmt); | |
911 | |
912 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(timeToNumber(t), Cell::NumberType, fmt, this)); | |
913 | |
914 return true; | |
915 } | |
916 | |
917 /*! | |
918 \overload | |
919 Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a tip. | |
920 Returns true on success. | |
921 */ | |
922 bool Worksheet::writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format, const QString &display, const QString &tip) | |
923 { | |
924 if (!row_column.isValid()) | |
925 return false; | |
926 | |
927 return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip); | |
928 } | |
929 | |
930 /*! | |
931 Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and \a tip. | |
932 Returns true on success. | |
933 */ | |
934 bool Worksheet::writeHyperlink(int row, int column, const QUrl &url, const Format &format, const QString &display, const QString &tip) | |
935 { | |
936 Q_D(Worksheet); | |
937 if (d->checkDimensions(row, column)) | |
938 return false; | |
939 | |
940 //int error = 0; | |
941 | |
942 QString urlString = url.toString(); | |
943 | |
944 //Generate proper display string | |
945 QString displayString = display.isEmpty() ? urlString : display; | |
946 if (displayString.startsWith(QLatin1String("mailto:"))) | |
947 displayString.replace(QLatin1String("mailto:"), QString()); | |
948 if (displayString.size() > XLSX_STRING_MAX) { | |
949 displayString = displayString.left(XLSX_STRING_MAX); | |
950 //error = -2; | |
951 } | |
952 | |
953 /* | |
954 Location within target. If target is a workbook (or this workbook) | |
955 this shall refer to a sheet and cell or a defined name. Can also | |
956 be an HTML anchor if target is HTML file. | |
957 | |
958 c:\temp\file.xlsx#Sheet!A1 | |
959 http://a.com/aaa.html#aaaaa | |
960 */ | |
961 QString locationString; | |
962 if (url.hasFragment()) { | |
963 locationString = url.fragment(); | |
964 urlString = url.toString(QUrl::RemoveFragment); | |
965 } | |
966 | |
967 Format fmt = format.isValid() ? format : d->cellFormat(row, column); | |
968 //Given a default style for hyperlink | |
969 if (!fmt.isValid()) { | |
970 fmt.setFontColor(Qt::blue); | |
971 fmt.setFontUnderline(Format::FontUnderlineSingle); | |
972 } | |
973 d->workbook->styles()->addXfFormat(fmt); | |
974 | |
975 //Write the hyperlink string as normal string. | |
976 d->sharedStrings()->addSharedString(displayString); | |
977 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(displayString, Cell::SharedStringType, fmt, this)); | |
978 | |
979 //Store the hyperlink data in a separate table | |
980 d->urlTable[row][column] = QSharedPointer<XlsxHyperlinkData>(new XlsxHyperlinkData(XlsxHyperlinkData::External, urlString, locationString, QString(), tip)); | |
981 | |
982 return true; | |
983 } | |
984 | |
985 /*! | |
986 * Add one DataValidation \a validation to the sheet. | |
987 * Returns true on success. | |
988 */ | |
989 bool Worksheet::addDataValidation(const DataValidation &validation) | |
990 { | |
991 Q_D(Worksheet); | |
992 if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None) | |
993 return false; | |
994 | |
995 d->dataValidationsList.append(validation); | |
996 return true; | |
997 } | |
998 | |
999 /*! | |
1000 * Add one ConditionalFormatting \a cf to the sheet. | |
1001 * Returns true on success. | |
1002 */ | |
1003 bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf) | |
1004 { | |
1005 Q_D(Worksheet); | |
1006 if (cf.ranges().isEmpty()) | |
1007 return false; | |
1008 | |
1009 for (int i=0; i<cf.d->cfRules.size(); ++i) { | |
1010 const QSharedPointer<XlsxCfRuleData> &rule = cf.d->cfRules[i]; | |
1011 if (!rule->dxfFormat.isEmpty()) | |
1012 d->workbook->styles()->addDxfFormat(rule->dxfFormat); | |
1013 rule->priority = 1; | |
1014 } | |
1015 d->conditionalFormattingList.append(cf); | |
1016 return true; | |
1017 } | |
1018 | |
1019 /*! | |
1020 * Insert an \a image at the position \a row, \a column | |
1021 * Returns true on success. | |
1022 */ | |
1023 bool Worksheet::insertImage(int row, int column, const QImage &image) | |
1024 { | |
1025 Q_D(Worksheet); | |
1026 | |
1027 if (image.isNull()) | |
1028 return false; | |
1029 | |
1030 if (!d->drawing) | |
1031 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch)); | |
1032 | |
1033 DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture); | |
1034 | |
1035 /* | |
1036 The size are expressed as English Metric Units (EMUs). There are | |
1037 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per | |
1038 pixel | |
1039 */ | |
1040 anchor->from = XlsxMarker(row, column, 0, 0); | |
1041 anchor->ext = QSize(image.width() * 9525, image.height() * 9525); | |
1042 | |
1043 anchor->setObjectPicture(image); | |
1044 return true; | |
1045 } | |
1046 | |
1047 /*! | |
1048 * Creates an chart with the given \a size and insert | |
1049 * at the position \a row, \a column. | |
1050 * The chart will be returned. | |
1051 */ | |
1052 Chart *Worksheet::insertChart(int row, int column, const QSize &size) | |
1053 { | |
1054 Q_D(Worksheet); | |
1055 | |
1056 if (!d->drawing) | |
1057 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch)); | |
1058 | |
1059 DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture); | |
1060 | |
1061 /* | |
1062 The size are expressed as English Metric Units (EMUs). There are | |
1063 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per | |
1064 pixel | |
1065 */ | |
1066 anchor->from = XlsxMarker(row, column, 0, 0); | |
1067 anchor->ext = size * 9525; | |
1068 | |
1069 QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, F_NewFromScratch)); | |
1070 anchor->setObjectGraphicFrame(chart); | |
1071 | |
1072 return chart.data(); | |
1073 } | |
1074 | |
1075 /*! | |
1076 Merge a \a range of cells. The first cell should contain the data and the others should | |
1077 be blank. All cells will be applied the same style if a valid \a format is given. | |
1078 Returns true on success. | |
1079 | |
1080 \note All cells except the top-left one will be cleared. | |
1081 */ | |
1082 bool Worksheet::mergeCells(const CellRange &range, const Format &format) | |
1083 { | |
1084 Q_D(Worksheet); | |
1085 if (range.rowCount() < 2 && range.columnCount() < 2) | |
1086 return false; | |
1087 | |
1088 if (d->checkDimensions(range.firstRow(), range.firstColumn())) | |
1089 return false; | |
1090 | |
1091 if (format.isValid()) | |
1092 d->workbook->styles()->addXfFormat(format); | |
1093 | |
1094 for (int row = range.firstRow(); row <= range.lastRow(); ++row) { | |
1095 for (int col = range.firstColumn(); col <= range.lastColumn(); ++col) { | |
1096 if (row == range.firstRow() && col == range.firstColumn()) { | |
1097 Cell *cell = cellAt(row, col); | |
1098 if (cell) { | |
1099 if (format.isValid()) | |
1100 cell->d_ptr->format = format; | |
1101 } else { | |
1102 writeBlank(row, col, format); | |
1103 } | |
1104 } else { | |
1105 writeBlank(row, col, format); | |
1106 } | |
1107 } | |
1108 } | |
1109 | |
1110 d->merges.append(range); | |
1111 return true; | |
1112 } | |
1113 | |
1114 /*! | |
1115 Unmerge the cells in the \a range. Returns true on success. | |
1116 | |
1117 */ | |
1118 bool Worksheet::unmergeCells(const CellRange &range) | |
1119 { | |
1120 Q_D(Worksheet); | |
1121 if (!d->merges.contains(range)) | |
1122 return false; | |
1123 | |
1124 d->merges.removeOne(range); | |
1125 return true; | |
1126 } | |
1127 | |
1128 /*! | |
1129 Returns all the merged cells. | |
1130 */ | |
1131 QList<CellRange> Worksheet::mergedCells() const | |
1132 { | |
1133 Q_D(const Worksheet); | |
1134 return d->merges; | |
1135 } | |
1136 | |
1137 /*! | |
1138 * \internal | |
1139 */ | |
1140 void Worksheet::saveToXmlFile(QIODevice *device) const | |
1141 { | |
1142 Q_D(const Worksheet); | |
1143 d->relationships->clear(); | |
1144 | |
1145 QXmlStreamWriter writer(device); | |
1146 | |
1147 writer.writeStartDocument(QStringLiteral("1.0"), true); | |
1148 writer.writeStartElement(QStringLiteral("worksheet")); | |
1149 writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); | |
1150 writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); | |
1151 | |
1152 //for Excel 2010 | |
1153 // writer.writeAttribute("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); | |
1154 // writer.writeAttribute("xmlns:x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); | |
1155 // writer.writeAttribute("mc:Ignorable", "x14ac"); | |
1156 | |
1157 writer.writeStartElement(QStringLiteral("dimension")); | |
1158 writer.writeAttribute(QStringLiteral("ref"), d->generateDimensionString()); | |
1159 writer.writeEndElement();//dimension | |
1160 | |
1161 writer.writeStartElement(QStringLiteral("sheetViews")); | |
1162 writer.writeStartElement(QStringLiteral("sheetView")); | |
1163 if (d->windowProtection) | |
1164 writer.writeAttribute(QStringLiteral("windowProtection"), QStringLiteral("1")); | |
1165 if (d->showFormulas) | |
1166 writer.writeAttribute(QStringLiteral("showFormulas"), QStringLiteral("1")); | |
1167 if (!d->showGridLines) | |
1168 writer.writeAttribute(QStringLiteral("showGridLines"), QStringLiteral("0")); | |
1169 if (!d->showRowColHeaders) | |
1170 writer.writeAttribute(QStringLiteral("showRowColHeaders"), QStringLiteral("0")); | |
1171 if (!d->showZeros) | |
1172 writer.writeAttribute(QStringLiteral("showZeros"), QStringLiteral("0")); | |
1173 if (d->rightToLeft) | |
1174 writer.writeAttribute(QStringLiteral("rightToLeft"), QStringLiteral("1")); | |
1175 if (d->tabSelected) | |
1176 writer.writeAttribute(QStringLiteral("tabSelected"), QStringLiteral("1")); | |
1177 if (!d->showRuler) | |
1178 writer.writeAttribute(QStringLiteral("showRuler"), QStringLiteral("0")); | |
1179 if (!d->showOutlineSymbols) | |
1180 writer.writeAttribute(QStringLiteral("showOutlineSymbols"), QStringLiteral("0")); | |
1181 if (!d->showWhiteSpace) | |
1182 writer.writeAttribute(QStringLiteral("showWhiteSpace"), QStringLiteral("0")); | |
1183 writer.writeAttribute(QStringLiteral("workbookViewId"), QStringLiteral("0")); | |
1184 writer.writeEndElement();//sheetView | |
1185 writer.writeEndElement();//sheetViews | |
1186 | |
1187 writer.writeStartElement(QStringLiteral("sheetFormatPr")); | |
1188 writer.writeAttribute(QStringLiteral("defaultRowHeight"), QString::number(d->default_row_height)); | |
1189 if (d->default_row_height != 15) | |
1190 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1")); | |
1191 if (d->default_row_zeroed) | |
1192 writer.writeAttribute(QStringLiteral("zeroHeight"), QStringLiteral("1")); | |
1193 if (d->outline_row_level) | |
1194 writer.writeAttribute(QStringLiteral("outlineLevelRow"), QString::number(d->outline_row_level)); | |
1195 if (d->outline_col_level) | |
1196 writer.writeAttribute(QStringLiteral("outlineLevelCol"), QString::number(d->outline_col_level)); | |
1197 //for Excel 2010 | |
1198 // writer.writeAttribute("x14ac:dyDescent", "0.25"); | |
1199 writer.writeEndElement();//sheetFormatPr | |
1200 | |
1201 if (!d->colsInfo.isEmpty()) { | |
1202 writer.writeStartElement(QStringLiteral("cols")); | |
1203 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(d->colsInfo); | |
1204 while (it.hasNext()) { | |
1205 it.next(); | |
1206 QSharedPointer<XlsxColumnInfo> col_info = it.value(); | |
1207 writer.writeStartElement(QStringLiteral("col")); | |
1208 writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn)); | |
1209 writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn)); | |
1210 if (col_info->width) | |
1211 writer.writeAttribute(QStringLiteral("width"), QString::number(col_info->width, 'g', 15)); | |
1212 if (!col_info->format.isEmpty()) | |
1213 writer.writeAttribute(QStringLiteral("style"), QString::number(col_info->format.xfIndex())); | |
1214 if (col_info->hidden) | |
1215 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1")); | |
1216 if (col_info->width) | |
1217 writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1")); | |
1218 if (col_info->outlineLevel) | |
1219 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(col_info->outlineLevel)); | |
1220 if (col_info->collapsed) | |
1221 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1")); | |
1222 writer.writeEndElement();//col | |
1223 } | |
1224 writer.writeEndElement();//cols | |
1225 } | |
1226 | |
1227 writer.writeStartElement(QStringLiteral("sheetData")); | |
1228 if (d->dimension.isValid()) | |
1229 d->saveXmlSheetData(writer); | |
1230 writer.writeEndElement();//sheetData | |
1231 | |
1232 d->saveXmlMergeCells(writer); | |
1233 foreach (const ConditionalFormatting cf, d->conditionalFormattingList) | |
1234 cf.saveToXml(writer); | |
1235 d->saveXmlDataValidations(writer); | |
1236 d->saveXmlHyperlinks(writer); | |
1237 d->saveXmlDrawings(writer); | |
1238 | |
1239 writer.writeEndElement();//worksheet | |
1240 writer.writeEndDocument(); | |
1241 } | |
1242 | |
1243 void WorksheetPrivate::saveXmlSheetData(QXmlStreamWriter &writer) const | |
1244 { | |
1245 calculateSpans(); | |
1246 for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) { | |
1247 if (!(cellTable.contains(row_num) || comments.contains(row_num) || rowsInfo.contains(row_num))) { | |
1248 //Only process rows with cell data / comments / formatting | |
1249 continue; | |
1250 } | |
1251 | |
1252 int span_index = (row_num-1) / 16; | |
1253 QString span; | |
1254 if (row_spans.contains(span_index)) | |
1255 span = row_spans[span_index]; | |
1256 | |
1257 writer.writeStartElement(QStringLiteral("row")); | |
1258 writer.writeAttribute(QStringLiteral("r"), QString::number(row_num)); | |
1259 | |
1260 if (!span.isEmpty()) | |
1261 writer.writeAttribute(QStringLiteral("spans"), span); | |
1262 | |
1263 if (rowsInfo.contains(row_num)) { | |
1264 QSharedPointer<XlsxRowInfo> rowInfo = rowsInfo[row_num]; | |
1265 if (!rowInfo->format.isEmpty()) { | |
1266 writer.writeAttribute(QStringLiteral("s"), QString::number(rowInfo->format.xfIndex())); | |
1267 writer.writeAttribute(QStringLiteral("customFormat"), QStringLiteral("1")); | |
1268 } | |
1269 //!Todo: support customHeight from info struct | |
1270 //!Todo: where does this magic number '15' come from? | |
1271 if (rowInfo->customHeight) { | |
1272 writer.writeAttribute(QStringLiteral("ht"), QString::number(rowInfo->height)); | |
1273 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1")); | |
1274 } else { | |
1275 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("0")); | |
1276 } | |
1277 | |
1278 if (rowInfo->hidden) | |
1279 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1")); | |
1280 if (rowInfo->outlineLevel > 0) | |
1281 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(rowInfo->outlineLevel)); | |
1282 if (rowInfo->collapsed) | |
1283 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1")); | |
1284 } | |
1285 | |
1286 //Write cell data if row contains filled cells | |
1287 if (cellTable.contains(row_num)) { | |
1288 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { | |
1289 if (cellTable[row_num].contains(col_num)) { | |
1290 saveXmlCellData(writer, row_num, col_num, cellTable[row_num][col_num]); | |
1291 } | |
1292 } | |
1293 } | |
1294 writer.writeEndElement(); //row | |
1295 } | |
1296 } | |
1297 | |
1298 void WorksheetPrivate::saveXmlCellData(QXmlStreamWriter &writer, int row, int col, QSharedPointer<Cell> cell) const | |
1299 { | |
1300 //This is the innermost loop so efficiency is important. | |
1301 QString cell_pos = CellReference(row, col).toString(); | |
1302 | |
1303 writer.writeStartElement(QStringLiteral("c")); | |
1304 writer.writeAttribute(QStringLiteral("r"), cell_pos); | |
1305 | |
1306 //Style used by the cell, row or col | |
1307 if (!cell->format().isEmpty()) | |
1308 writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex())); | |
1309 else if (rowsInfo.contains(row) && !rowsInfo[row]->format.isEmpty()) | |
1310 writer.writeAttribute(QStringLiteral("s"), QString::number(rowsInfo[row]->format.xfIndex())); | |
1311 else if (colsInfoHelper.contains(col) && !colsInfoHelper[col]->format.isEmpty()) | |
1312 writer.writeAttribute(QStringLiteral("s"), QString::number(colsInfoHelper[col]->format.xfIndex())); | |
1313 | |
1314 if (cell->cellType() == Cell::SharedStringType) { | |
1315 int sst_idx; | |
1316 if (cell->isRichString()) | |
1317 sst_idx = sharedStrings()->getSharedStringIndex(cell->d_ptr->richString); | |
1318 else | |
1319 sst_idx = sharedStrings()->getSharedStringIndex(cell->value().toString()); | |
1320 | |
1321 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("s")); | |
1322 writer.writeTextElement(QStringLiteral("v"), QString::number(sst_idx)); | |
1323 } else if (cell->cellType() == Cell::InlineStringType) { | |
1324 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr")); | |
1325 writer.writeStartElement(QStringLiteral("is")); | |
1326 if (cell->isRichString()) { | |
1327 //Rich text string | |
1328 RichString string = cell->d_ptr->richString; | |
1329 for (int i=0; i<string.fragmentCount(); ++i) { | |
1330 writer.writeStartElement(QStringLiteral("r")); | |
1331 if (string.fragmentFormat(i).hasFontData()) { | |
1332 writer.writeStartElement(QStringLiteral("rPr")); | |
1333 //:Todo | |
1334 writer.writeEndElement();// rPr | |
1335 } | |
1336 writer.writeStartElement(QStringLiteral("t")); | |
1337 if (isSpaceReserveNeeded(string.fragmentText(i))) | |
1338 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); | |
1339 writer.writeCharacters(string.fragmentText(i)); | |
1340 writer.writeEndElement();// t | |
1341 writer.writeEndElement(); // r | |
1342 } | |
1343 } else { | |
1344 writer.writeStartElement(QStringLiteral("t")); | |
1345 QString string = cell->value().toString(); | |
1346 if (isSpaceReserveNeeded(string)) | |
1347 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); | |
1348 writer.writeCharacters(string); | |
1349 writer.writeEndElement(); // t | |
1350 } | |
1351 writer.writeEndElement();//is | |
1352 } else if (cell->cellType() == Cell::NumberType){ | |
1353 if (cell->hasFormula()) | |
1354 cell->formula().saveToXml(writer); | |
1355 if (cell->value().isValid()) {//note that, invalid value means 'v' is blank | |
1356 double value = cell->value().toDouble(); | |
1357 writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15)); | |
1358 } | |
1359 } else if (cell->cellType() == Cell::StringType) { | |
1360 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str")); | |
1361 if (cell->hasFormula()) | |
1362 cell->formula().saveToXml(writer); | |
1363 writer.writeTextElement(QStringLiteral("v"), cell->value().toString()); | |
1364 } else if (cell->cellType() == Cell::BooleanType) { | |
1365 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b")); | |
1366 writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0")); | |
1367 } | |
1368 writer.writeEndElement(); //c | |
1369 } | |
1370 | |
1371 void WorksheetPrivate::saveXmlMergeCells(QXmlStreamWriter &writer) const | |
1372 { | |
1373 if (merges.isEmpty()) | |
1374 return; | |
1375 | |
1376 writer.writeStartElement(QStringLiteral("mergeCells")); | |
1377 writer.writeAttribute(QStringLiteral("count"), QString::number(merges.size())); | |
1378 | |
1379 foreach (CellRange range, merges) { | |
1380 writer.writeEmptyElement(QStringLiteral("mergeCell")); | |
1381 writer.writeAttribute(QStringLiteral("ref"), range.toString()); | |
1382 } | |
1383 | |
1384 writer.writeEndElement(); //mergeCells | |
1385 } | |
1386 | |
1387 void WorksheetPrivate::saveXmlDataValidations(QXmlStreamWriter &writer) const | |
1388 { | |
1389 if (dataValidationsList.isEmpty()) | |
1390 return; | |
1391 | |
1392 writer.writeStartElement(QStringLiteral("dataValidations")); | |
1393 writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size())); | |
1394 | |
1395 foreach (DataValidation validation, dataValidationsList) | |
1396 validation.saveToXml(writer); | |
1397 | |
1398 writer.writeEndElement(); //dataValidations | |
1399 } | |
1400 | |
1401 void WorksheetPrivate::saveXmlHyperlinks(QXmlStreamWriter &writer) const | |
1402 { | |
1403 if (urlTable.isEmpty()) | |
1404 return; | |
1405 | |
1406 writer.writeStartElement(QStringLiteral("hyperlinks")); | |
1407 QMapIterator<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > it(urlTable); | |
1408 while (it.hasNext()) { | |
1409 it.next(); | |
1410 int row = it.key(); | |
1411 QMapIterator <int, QSharedPointer<XlsxHyperlinkData> > it2(it.value()); | |
1412 while (it2.hasNext()) { | |
1413 it2.next(); | |
1414 int col = it2.key(); | |
1415 QSharedPointer<XlsxHyperlinkData> data = it2.value(); | |
1416 QString ref = CellReference(row, col).toString(); | |
1417 writer.writeEmptyElement(QStringLiteral("hyperlink")); | |
1418 writer.writeAttribute(QStringLiteral("ref"), ref); | |
1419 if (data->linkType == XlsxHyperlinkData::External) { | |
1420 //Update relationships | |
1421 relationships->addWorksheetRelationship(QStringLiteral("/hyperlink"), data->target, QStringLiteral("External")); | |
1422 | |
1423 writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count())); | |
1424 } | |
1425 | |
1426 if (!data->location.isEmpty()) | |
1427 writer.writeAttribute(QStringLiteral("location"), data->location); | |
1428 if (!data->display.isEmpty()) | |
1429 writer.writeAttribute(QStringLiteral("display"), data->display); | |
1430 if (!data->tooltip.isEmpty()) | |
1431 writer.writeAttribute(QStringLiteral("tooltip"), data->tooltip); | |
1432 } | |
1433 } | |
1434 | |
1435 writer.writeEndElement();//hyperlinks | |
1436 } | |
1437 | |
1438 void WorksheetPrivate::saveXmlDrawings(QXmlStreamWriter &writer) const | |
1439 { | |
1440 if (!drawing) | |
1441 return; | |
1442 | |
1443 int idx = workbook->drawings().indexOf(drawing.data()); | |
1444 relationships->addWorksheetRelationship(QStringLiteral("/drawing"), QStringLiteral("../drawings/drawing%1.xml").arg(idx+1)); | |
1445 | |
1446 writer.writeEmptyElement(QStringLiteral("drawing")); | |
1447 writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count())); | |
1448 } | |
1449 | |
1450 void WorksheetPrivate::splitColsInfo(int colFirst, int colLast) | |
1451 { | |
1452 // Split current columnInfo, for example, if "A:H" has been set, | |
1453 // we are trying to set "B:D", there should be "A", "B:D", "E:H". | |
1454 // This will be more complex if we try to set "C:F" after "B:D". | |
1455 { | |
1456 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo); | |
1457 while (it.hasNext()) { | |
1458 it.next(); | |
1459 QSharedPointer<XlsxColumnInfo> info = it.value(); | |
1460 if (colFirst > info->firstColumn && colFirst <= info->lastColumn) { | |
1461 //split the range, | |
1462 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info)); | |
1463 info->lastColumn = colFirst - 1; | |
1464 info2->firstColumn = colFirst; | |
1465 colsInfo.insert(colFirst, info2); | |
1466 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) | |
1467 colsInfoHelper[c] = info2; | |
1468 | |
1469 break; | |
1470 } | |
1471 } | |
1472 } | |
1473 { | |
1474 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo); | |
1475 while (it.hasNext()) { | |
1476 it.next(); | |
1477 QSharedPointer<XlsxColumnInfo> info = it.value(); | |
1478 if (colLast >= info->firstColumn && colLast < info->lastColumn) { | |
1479 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info)); | |
1480 info->lastColumn = colLast; | |
1481 info2->firstColumn = colLast + 1; | |
1482 colsInfo.insert(colLast + 1, info2); | |
1483 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) | |
1484 colsInfoHelper[c] = info2; | |
1485 | |
1486 break; | |
1487 } | |
1488 } | |
1489 } | |
1490 } | |
1491 | |
1492 bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast) | |
1493 { | |
1494 bool ignore_row = true; | |
1495 bool ignore_col = false; | |
1496 | |
1497 if (colFirst > colLast) | |
1498 return false; | |
1499 | |
1500 if (checkDimensions(1, colLast, ignore_row, ignore_col)) | |
1501 return false; | |
1502 if (checkDimensions(1, colFirst, ignore_row, ignore_col)) | |
1503 return false; | |
1504 | |
1505 return true; | |
1506 } | |
1507 | |
1508 QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast) | |
1509 { | |
1510 splitColsInfo(colFirst, colLast); | |
1511 | |
1512 QList<int> nodes; | |
1513 nodes.append(colFirst); | |
1514 for (int col = colFirst; col <= colLast; ++col) { | |
1515 if (colsInfo.contains(col)) { | |
1516 if (nodes.last() != col) | |
1517 nodes.append(col); | |
1518 int nextCol = colsInfo[col]->lastColumn + 1; | |
1519 if (nextCol <= colLast) | |
1520 nodes.append(nextCol); | |
1521 } | |
1522 } | |
1523 | |
1524 return nodes; | |
1525 } | |
1526 | |
1527 /*! | |
1528 Sets width in characters of a \a range of columns to \a width. | |
1529 Returns true on success. | |
1530 */ | |
1531 bool Worksheet::setColumnWidth(const CellRange &range, double width) | |
1532 { | |
1533 if (!range.isValid()) | |
1534 return false; | |
1535 | |
1536 return setColumnWidth(range.firstColumn(), range.lastColumn(), width); | |
1537 } | |
1538 | |
1539 /*! | |
1540 Sets format property of a \a range of columns to \a format. Columns are 1-indexed. | |
1541 Returns true on success. | |
1542 */ | |
1543 bool Worksheet::setColumnFormat(const CellRange& range, const Format &format) | |
1544 { | |
1545 if (!range.isValid()) | |
1546 return false; | |
1547 | |
1548 return setColumnFormat(range.firstColumn(), range.lastColumn(), format); | |
1549 } | |
1550 | |
1551 /*! | |
1552 Sets hidden property of a \a range of columns to \a hidden. Columns are 1-indexed. | |
1553 Hidden columns are not visible. | |
1554 Returns true on success. | |
1555 */ | |
1556 bool Worksheet::setColumnHidden(const CellRange &range, bool hidden) | |
1557 { | |
1558 if (!range.isValid()) | |
1559 return false; | |
1560 | |
1561 return setColumnHidden(range.firstColumn(), range.lastColumn(), hidden); | |
1562 } | |
1563 | |
1564 /*! | |
1565 Sets width in characters for columns [\a colFirst, \a colLast] to \a width. | |
1566 Columns are 1-indexed. | |
1567 Returns true on success. | |
1568 */ | |
1569 bool Worksheet::setColumnWidth(int colFirst, int colLast, double width) | |
1570 { | |
1571 Q_D(Worksheet); | |
1572 | |
1573 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); | |
1574 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) | |
1575 columnInfo->width = width; | |
1576 | |
1577 return (columnInfoList.count() > 0); | |
1578 } | |
1579 | |
1580 /*! | |
1581 Sets format property of a range of columns [\a colFirst, \a colLast] to \a format. | |
1582 Columns are 1-indexed. | |
1583 Returns true on success. | |
1584 */ | |
1585 bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format) | |
1586 { | |
1587 Q_D(Worksheet); | |
1588 | |
1589 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); | |
1590 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) | |
1591 columnInfo->format = format; | |
1592 | |
1593 if(columnInfoList.count() > 0) { | |
1594 d->workbook->styles()->addXfFormat(format); | |
1595 return true; | |
1596 } | |
1597 | |
1598 return false; | |
1599 } | |
1600 | |
1601 /*! | |
1602 Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden. | |
1603 Columns are 1-indexed. Returns true on success. | |
1604 */ | |
1605 bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden) | |
1606 { | |
1607 Q_D(Worksheet); | |
1608 | |
1609 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); | |
1610 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) | |
1611 columnInfo->hidden = hidden; | |
1612 | |
1613 return (columnInfoList.count() > 0); | |
1614 } | |
1615 | |
1616 /*! | |
1617 Returns width of the \a column in characters of the normal font. Columns are 1-indexed. | |
1618 */ | |
1619 double Worksheet::columnWidth(int column) | |
1620 { | |
1621 Q_D(Worksheet); | |
1622 | |
1623 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); | |
1624 if (columnInfoList.count() == 1) | |
1625 return columnInfoList.at(0)->width ; | |
1626 | |
1627 return d->sheetFormatProps.defaultColWidth; | |
1628 } | |
1629 | |
1630 /*! | |
1631 Returns formatting of the \a column. Columns are 1-indexed. | |
1632 */ | |
1633 Format Worksheet::columnFormat(int column) | |
1634 { | |
1635 Q_D(Worksheet); | |
1636 | |
1637 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); | |
1638 if (columnInfoList.count() == 1) | |
1639 return columnInfoList.at(0)->format; | |
1640 | |
1641 return Format(); | |
1642 } | |
1643 | |
1644 /*! | |
1645 Returns true if \a column is hidden. Columns are 1-indexed. | |
1646 */ | |
1647 bool Worksheet::isColumnHidden(int column) | |
1648 { | |
1649 Q_D(Worksheet); | |
1650 | |
1651 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); | |
1652 if (columnInfoList.count() == 1) | |
1653 return columnInfoList.at(0)->hidden; | |
1654 | |
1655 return false; | |
1656 } | |
1657 | |
1658 /*! | |
1659 Sets the \a height of the rows including and between \a rowFirst and \a rowLast. | |
1660 Row height measured in point size. | |
1661 Rows are 1-indexed. | |
1662 | |
1663 Returns true if success. | |
1664 */ | |
1665 bool Worksheet::setRowHeight(int rowFirst,int rowLast, double height) | |
1666 { | |
1667 Q_D(Worksheet); | |
1668 | |
1669 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); | |
1670 | |
1671 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) { | |
1672 rowInfo->height = height; | |
1673 rowInfo->customHeight = true; | |
1674 } | |
1675 | |
1676 return rowInfoList.count() > 0; | |
1677 } | |
1678 | |
1679 /*! | |
1680 Sets the \a format of the rows including and between \a rowFirst and \a rowLast. | |
1681 Rows are 1-indexed. | |
1682 | |
1683 Returns true if success. | |
1684 */ | |
1685 bool Worksheet::setRowFormat(int rowFirst,int rowLast, const Format &format) | |
1686 { | |
1687 Q_D(Worksheet); | |
1688 | |
1689 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); | |
1690 | |
1691 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) | |
1692 rowInfo->format = format; | |
1693 | |
1694 d->workbook->styles()->addXfFormat(format); | |
1695 return rowInfoList.count() > 0; | |
1696 } | |
1697 | |
1698 /*! | |
1699 Sets the \a hidden proeprty of the rows including and between \a rowFirst and \a rowLast. | |
1700 Rows are 1-indexed. If hidden is true rows will not be visible. | |
1701 | |
1702 Returns true if success. | |
1703 */ | |
1704 bool Worksheet::setRowHidden(int rowFirst,int rowLast, bool hidden) | |
1705 { | |
1706 Q_D(Worksheet); | |
1707 | |
1708 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); | |
1709 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) | |
1710 rowInfo->hidden = hidden; | |
1711 | |
1712 return rowInfoList.count() > 0; | |
1713 } | |
1714 | |
1715 /*! | |
1716 Returns height of \a row in points. | |
1717 */ | |
1718 double Worksheet::rowHeight(int row) | |
1719 { | |
1720 Q_D(Worksheet); | |
1721 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; | |
1722 | |
1723 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) | |
1724 return d->sheetFormatProps.defaultRowHeight; //return default on invalid row | |
1725 | |
1726 | |
1727 return d->rowsInfo[row]->height; | |
1728 } | |
1729 | |
1730 /*! | |
1731 Returns format of \a row. | |
1732 */ | |
1733 Format Worksheet::rowFormat(int row) | |
1734 { | |
1735 Q_D(Worksheet); | |
1736 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; | |
1737 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) | |
1738 return Format(); //return default on invalid row | |
1739 | |
1740 return d->rowsInfo[row]->format; | |
1741 } | |
1742 | |
1743 /*! | |
1744 Returns true if \a row is hidden. | |
1745 */ | |
1746 bool Worksheet::isRowHidden(int row) | |
1747 { | |
1748 Q_D(Worksheet); | |
1749 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; | |
1750 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) | |
1751 return false; //return default on invalid row | |
1752 | |
1753 return d->rowsInfo[row]->hidden; | |
1754 } | |
1755 | |
1756 /*! | |
1757 Groups rows from \a rowFirst to \a rowLast with the given \a collapsed. | |
1758 | |
1759 Returns false if error occurs. | |
1760 */ | |
1761 bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed) | |
1762 { | |
1763 Q_D(Worksheet); | |
1764 | |
1765 for (int row=rowFirst; row<=rowLast; ++row) { | |
1766 if (d->rowsInfo.contains(row)) { | |
1767 d->rowsInfo[row]->outlineLevel += 1; | |
1768 } else { | |
1769 QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo); | |
1770 info->outlineLevel += 1; | |
1771 d->rowsInfo.insert(row, info); | |
1772 } | |
1773 if (collapsed) | |
1774 d->rowsInfo[row]->hidden = true; | |
1775 } | |
1776 if (collapsed) { | |
1777 if (!d->rowsInfo.contains(rowLast+1)) | |
1778 d->rowsInfo.insert(rowLast+1, QSharedPointer<XlsxRowInfo>(new XlsxRowInfo)); | |
1779 d->rowsInfo[rowLast+1]->collapsed = true; | |
1780 } | |
1781 return true; | |
1782 } | |
1783 | |
1784 /*! | |
1785 \overload | |
1786 | |
1787 Groups columns with the given \a range and \a collapsed. | |
1788 */ | |
1789 bool Worksheet::groupColumns(const CellRange &range, bool collapsed) | |
1790 { | |
1791 if (!range.isValid()) | |
1792 return false; | |
1793 | |
1794 return groupColumns(range.firstColumn(), range.lastColumn(), collapsed); | |
1795 } | |
1796 | |
1797 /*! | |
1798 Groups columns from \a colFirst to \a colLast with the given \a collapsed. | |
1799 Returns false if error occurs. | |
1800 */ | |
1801 bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed) | |
1802 { | |
1803 Q_D(Worksheet); | |
1804 | |
1805 d->splitColsInfo(colFirst, colLast); | |
1806 | |
1807 QList<int> nodes; | |
1808 nodes.append(colFirst); | |
1809 for (int col = colFirst; col <= colLast; ++col) { | |
1810 if (d->colsInfo.contains(col)) { | |
1811 if (nodes.last() != col) | |
1812 nodes.append(col); | |
1813 int nextCol = d->colsInfo[col]->lastColumn + 1; | |
1814 if (nextCol <= colLast) | |
1815 nodes.append(nextCol); | |
1816 } | |
1817 } | |
1818 | |
1819 for (int idx = 0; idx < nodes.size(); ++idx) { | |
1820 int colStart = nodes[idx]; | |
1821 if (d->colsInfo.contains(colStart)) { | |
1822 QSharedPointer<XlsxColumnInfo> info = d->colsInfo[colStart]; | |
1823 info->outlineLevel += 1; | |
1824 if (collapsed) | |
1825 info->hidden = true; | |
1826 } else { | |
1827 int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; | |
1828 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd)); | |
1829 info->outlineLevel += 1; | |
1830 d->colsInfo.insert(colFirst, info); | |
1831 if (collapsed) | |
1832 info->hidden = true; | |
1833 for (int c = colStart; c <= colEnd; ++c) | |
1834 d->colsInfoHelper[c] = info; | |
1835 } | |
1836 } | |
1837 | |
1838 if (collapsed) { | |
1839 int col = colLast+1; | |
1840 d->splitColsInfo(col, col); | |
1841 if (d->colsInfo.contains(col)) | |
1842 d->colsInfo[col]->collapsed = true; | |
1843 else { | |
1844 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(col, col)); | |
1845 info->collapsed = true; | |
1846 d->colsInfo.insert(col, info); | |
1847 d->colsInfoHelper[col] = info; | |
1848 } | |
1849 } | |
1850 | |
1851 return false; | |
1852 } | |
1853 | |
1854 /*! | |
1855 Return the range that contains cell data. | |
1856 */ | |
1857 CellRange Worksheet::dimension() const | |
1858 { | |
1859 Q_D(const Worksheet); | |
1860 return d->dimension; | |
1861 } | |
1862 | |
1863 /* | |
1864 Convert the height of a cell from user's units to pixels. If the | |
1865 height hasn't been set by the user we use the default value. If | |
1866 the row is hidden it has a value of zero. | |
1867 */ | |
1868 int WorksheetPrivate::rowPixelsSize(int row) const | |
1869 { | |
1870 double height; | |
1871 if (row_sizes.contains(row)) | |
1872 height = row_sizes[row]; | |
1873 else | |
1874 height = default_row_height; | |
1875 return static_cast<int>(4.0 / 3.0 *height); | |
1876 } | |
1877 | |
1878 /* | |
1879 Convert the width of a cell from user's units to pixels. Excel rounds | |
1880 the column width to the nearest pixel. If the width hasn't been set | |
1881 by the user we use the default value. If the column is hidden it | |
1882 has a value of zero. | |
1883 */ | |
1884 int WorksheetPrivate::colPixelsSize(int col) const | |
1885 { | |
1886 double max_digit_width = 7.0; //For Calabri 11 | |
1887 double padding = 5.0; | |
1888 int pixels = 0; | |
1889 | |
1890 if (col_sizes.contains(col)) { | |
1891 double width = col_sizes[col]; | |
1892 if (width < 1) | |
1893 pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5); | |
1894 else | |
1895 pixels = static_cast<int>(width * max_digit_width + 0.5) + padding; | |
1896 } else { | |
1897 pixels = 64; | |
1898 } | |
1899 return pixels; | |
1900 } | |
1901 | |
1902 void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader) | |
1903 { | |
1904 Q_Q(Worksheet); | |
1905 Q_ASSERT(reader.name() == QLatin1String("sheetData")); | |
1906 | |
1907 while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
1908 if (reader.readNextStartElement()) { | |
1909 if (reader.name() == QLatin1String("row")) { | |
1910 QXmlStreamAttributes attributes = reader.attributes(); | |
1911 | |
1912 if (attributes.hasAttribute(QLatin1String("customFormat")) | |
1913 || attributes.hasAttribute(QLatin1String("customHeight")) | |
1914 || attributes.hasAttribute(QLatin1String("hidden")) | |
1915 || attributes.hasAttribute(QLatin1String("outlineLevel")) | |
1916 || attributes.hasAttribute(QLatin1String("collapsed"))) { | |
1917 | |
1918 QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo); | |
1919 if (attributes.hasAttribute(QLatin1String("customFormat")) && attributes.hasAttribute(QLatin1String("s"))) { | |
1920 int idx = attributes.value(QLatin1String("s")).toString().toInt(); | |
1921 info->format = workbook->styles()->xfFormat(idx); | |
1922 } | |
1923 | |
1924 if (attributes.hasAttribute(QLatin1String("customHeight"))) { | |
1925 info->customHeight = attributes.value(QLatin1String("customHeight")) == QLatin1String("1"); | |
1926 //Row height is only specified when customHeight is set | |
1927 if(attributes.hasAttribute(QLatin1String("ht"))) { | |
1928 info->height = attributes.value(QLatin1String("ht")).toString().toDouble(); | |
1929 } | |
1930 } | |
1931 | |
1932 //both "hidden" and "collapsed" default are false | |
1933 info->hidden = attributes.value(QLatin1String("hidden")) == QLatin1String("1"); | |
1934 info->collapsed = attributes.value(QLatin1String("collapsed")) == QLatin1String("1"); | |
1935 | |
1936 if (attributes.hasAttribute(QLatin1String("outlineLevel"))) | |
1937 info->outlineLevel = attributes.value(QLatin1String("outlineLevel")).toString().toInt(); | |
1938 | |
1939 //"r" is optional too. | |
1940 if (attributes.hasAttribute(QLatin1String("r"))) { | |
1941 int row = attributes.value(QLatin1String("r")).toString().toInt(); | |
1942 rowsInfo[row] = info; | |
1943 } | |
1944 } | |
1945 | |
1946 } else if (reader.name() == QLatin1String("c")) { //Cell | |
1947 QXmlStreamAttributes attributes = reader.attributes(); | |
1948 QString r = attributes.value(QLatin1String("r")).toString(); | |
1949 CellReference pos(r); | |
1950 | |
1951 //get format | |
1952 Format format; | |
1953 if (attributes.hasAttribute(QLatin1String("s"))) { //"s" == style index | |
1954 int idx = attributes.value(QLatin1String("s")).toString().toInt(); | |
1955 format = workbook->styles()->xfFormat(idx); | |
1956 ////Empty format exists in styles xf table of real .xlsx files, see issue #65. | |
1957 //if (!format.isValid()) | |
1958 // qDebug()<<QStringLiteral("<c s=\"%1\">Invalid style index: ").arg(idx)<<idx; | |
1959 } | |
1960 | |
1961 Cell::CellType cellType = Cell::NumberType; | |
1962 if (attributes.hasAttribute(QLatin1String("t"))) { | |
1963 QString typeString = attributes.value(QLatin1String("t")).toString(); | |
1964 if (typeString == QLatin1String("s")) | |
1965 cellType = Cell::SharedStringType; | |
1966 else if (typeString == QLatin1String("inlineStr")) | |
1967 cellType = Cell::InlineStringType; | |
1968 else if (typeString == QLatin1String("str")) | |
1969 cellType = Cell::StringType; | |
1970 else if (typeString == QLatin1String("b")) | |
1971 cellType = Cell::BooleanType; | |
1972 else if (typeString == QLatin1String("e")) | |
1973 cellType = Cell::ErrorType; | |
1974 else | |
1975 cellType = Cell::NumberType; | |
1976 } | |
1977 | |
1978 QSharedPointer<Cell> cell(new Cell(QVariant() ,cellType, format, q)); | |
1979 while (!reader.atEnd() && !(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
1980 if (reader.readNextStartElement()) { | |
1981 if (reader.name() == QLatin1String("f")) { | |
1982 CellFormula &formula = cell->d_func()->formula; | |
1983 formula.loadFromXml(reader); | |
1984 if (formula.formulaType() == CellFormula::SharedType && !formula.formulaText().isEmpty()) { | |
1985 sharedFormulaMap[formula.sharedIndex()] = formula; | |
1986 } | |
1987 } else if (reader.name() == QLatin1String("v")) { | |
1988 QString value = reader.readElementText(); | |
1989 if (cellType == Cell::SharedStringType) { | |
1990 int sst_idx = value.toInt(); | |
1991 sharedStrings()->incRefByStringIndex(sst_idx); | |
1992 RichString rs = sharedStrings()->getSharedString(sst_idx); | |
1993 cell->d_func()->value = rs.toPlainString(); | |
1994 if (rs.isRichString()) | |
1995 cell->d_func()->richString = rs; | |
1996 } else if (cellType == Cell::NumberType) { | |
1997 cell->d_func()->value = value.toDouble(); | |
1998 } else if (cellType == Cell::BooleanType) { | |
1999 cell->d_func()->value = value.toInt() ? true : false; | |
2000 } else { //Cell::ErrorType and Cell::StringType | |
2001 cell->d_func()->value = value; | |
2002 } | |
2003 } else if (reader.name() == QLatin1String("is")) { | |
2004 while (!reader.atEnd() && !(reader.name() == QLatin1String("is") && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2005 if (reader.readNextStartElement()) { | |
2006 //:Todo, add rich text read support | |
2007 if (reader.name() == QLatin1String("t")) { | |
2008 cell->d_func()->value = reader.readElementText(); | |
2009 } | |
2010 } | |
2011 } | |
2012 } else if (reader.name() == QLatin1String("extLst")) { | |
2013 //skip extLst element | |
2014 while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst") | |
2015 && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2016 reader.readNextStartElement(); | |
2017 } | |
2018 } | |
2019 } | |
2020 } | |
2021 cellTable[pos.row()][pos.column()] = cell; | |
2022 } | |
2023 } | |
2024 } | |
2025 } | |
2026 | |
2027 void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader) | |
2028 { | |
2029 Q_ASSERT(reader.name() == QLatin1String("cols")); | |
2030 | |
2031 while (!reader.atEnd() && !(reader.name() == QLatin1String("cols") && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2032 reader.readNextStartElement(); | |
2033 if (reader.tokenType() == QXmlStreamReader::StartElement) { | |
2034 if (reader.name() == QLatin1String("col")) { | |
2035 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo); | |
2036 | |
2037 QXmlStreamAttributes colAttrs = reader.attributes(); | |
2038 int min = colAttrs.value(QLatin1String("min")).toString().toInt(); | |
2039 int max = colAttrs.value(QLatin1String("max")).toString().toInt(); | |
2040 info->firstColumn = min; | |
2041 info->lastColumn = max; | |
2042 | |
2043 //Flag indicating that the column width for the affected column(s) is different from the | |
2044 // default or has been manually set | |
2045 if(colAttrs.hasAttribute(QLatin1String("customWidth"))) { | |
2046 info->customWidth = colAttrs.value(QLatin1String("customWidth")) == QLatin1String("1"); | |
2047 } | |
2048 //Note, node may have "width" without "customWidth" | |
2049 if (colAttrs.hasAttribute(QLatin1String("width"))) { | |
2050 double width = colAttrs.value(QLatin1String("width")).toString().toDouble(); | |
2051 info->width = width; | |
2052 } | |
2053 | |
2054 info->hidden = colAttrs.value(QLatin1String("hidden")) == QLatin1String("1"); | |
2055 info->collapsed = colAttrs.value(QLatin1String("collapsed")) == QLatin1String("1"); | |
2056 | |
2057 if (colAttrs.hasAttribute(QLatin1String("style"))) { | |
2058 int idx = colAttrs.value(QLatin1String("style")).toString().toInt(); | |
2059 info->format = workbook->styles()->xfFormat(idx); | |
2060 } | |
2061 if (colAttrs.hasAttribute(QLatin1String("outlineLevel"))) | |
2062 info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toString().toInt(); | |
2063 | |
2064 colsInfo.insert(min, info); | |
2065 for (int col=min; col<=max; ++col) | |
2066 colsInfoHelper[col] = info; | |
2067 } | |
2068 } | |
2069 } | |
2070 } | |
2071 | |
2072 void WorksheetPrivate::loadXmlMergeCells(QXmlStreamReader &reader) | |
2073 { | |
2074 Q_ASSERT(reader.name() == QLatin1String("mergeCells")); | |
2075 | |
2076 QXmlStreamAttributes attributes = reader.attributes(); | |
2077 int count = attributes.value(QLatin1String("count")).toString().toInt(); | |
2078 | |
2079 while (!reader.atEnd() && !(reader.name() == QLatin1String("mergeCells") && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2080 reader.readNextStartElement(); | |
2081 if (reader.tokenType() == QXmlStreamReader::StartElement) { | |
2082 if (reader.name() == QLatin1String("mergeCell")) { | |
2083 QXmlStreamAttributes attrs = reader.attributes(); | |
2084 QString rangeStr = attrs.value(QLatin1String("ref")).toString(); | |
2085 merges.append(CellRange(rangeStr)); | |
2086 } | |
2087 } | |
2088 } | |
2089 | |
2090 if (merges.size() != count) | |
2091 qDebug("read merge cells error"); | |
2092 } | |
2093 | |
2094 void WorksheetPrivate::loadXmlDataValidations(QXmlStreamReader &reader) | |
2095 { | |
2096 Q_ASSERT(reader.name() == QLatin1String("dataValidations")); | |
2097 QXmlStreamAttributes attributes = reader.attributes(); | |
2098 int count = attributes.value(QLatin1String("count")).toString().toInt(); | |
2099 | |
2100 while (!reader.atEnd() && !(reader.name() == QLatin1String("dataValidations") | |
2101 && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2102 reader.readNextStartElement(); | |
2103 if (reader.tokenType() == QXmlStreamReader::StartElement | |
2104 && reader.name() == QLatin1String("dataValidation")) { | |
2105 dataValidationsList.append(DataValidation::loadFromXml(reader)); | |
2106 } | |
2107 } | |
2108 | |
2109 if (dataValidationsList.size() != count) | |
2110 qDebug("read data validation error"); | |
2111 } | |
2112 | |
2113 void WorksheetPrivate::loadXmlSheetViews(QXmlStreamReader &reader) | |
2114 { | |
2115 Q_ASSERT(reader.name() == QLatin1String("sheetViews")); | |
2116 | |
2117 while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetViews") | |
2118 && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2119 reader.readNextStartElement(); | |
2120 if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("sheetView")) { | |
2121 QXmlStreamAttributes attrs = reader.attributes(); | |
2122 //default false | |
2123 windowProtection = attrs.value(QLatin1String("windowProtection")) == QLatin1String("1"); | |
2124 showFormulas = attrs.value(QLatin1String("showFormulas")) == QLatin1String("1"); | |
2125 rightToLeft = attrs.value(QLatin1String("rightToLeft")) == QLatin1String("1"); | |
2126 tabSelected = attrs.value(QLatin1String("tabSelected")) == QLatin1String("1"); | |
2127 //default true | |
2128 showGridLines = attrs.value(QLatin1String("showGridLines")) != QLatin1String("0"); | |
2129 showRowColHeaders = attrs.value(QLatin1String("showRowColHeaders")) != QLatin1String("0"); | |
2130 showZeros = attrs.value(QLatin1String("showZeros")) != QLatin1String("0"); | |
2131 showRuler = attrs.value(QLatin1String("showRuler")) != QLatin1String("0"); | |
2132 showOutlineSymbols = attrs.value(QLatin1String("showOutlineSymbols")) != QLatin1String("0"); | |
2133 showWhiteSpace = attrs.value(QLatin1String("showWhiteSpace")) != QLatin1String("0"); | |
2134 } | |
2135 } | |
2136 } | |
2137 | |
2138 void WorksheetPrivate::loadXmlSheetFormatProps(QXmlStreamReader &reader) | |
2139 { | |
2140 Q_ASSERT(reader.name() == QLatin1String("sheetFormatPr")); | |
2141 QXmlStreamAttributes attributes = reader.attributes(); | |
2142 XlsxSheetFormatProps formatProps; | |
2143 | |
2144 //Retain default values | |
2145 foreach (QXmlStreamAttribute attrib, attributes) { | |
2146 if(attrib.name() == QLatin1String("baseColWidth") ) { | |
2147 formatProps.baseColWidth = attrib.value().toString().toInt(); | |
2148 } else if(attrib.name() == QLatin1String("customHeight")) { | |
2149 formatProps.customHeight = attrib.value() == QLatin1String("1"); | |
2150 } else if(attrib.name() == QLatin1String("defaultColWidth")) { | |
2151 formatProps.defaultColWidth = attrib.value().toString().toDouble(); | |
2152 } else if(attrib.name() == QLatin1String("defaultRowHeight")) { | |
2153 formatProps.defaultRowHeight = attrib.value().toString().toDouble(); | |
2154 } else if(attrib.name() == QLatin1String("outlineLevelCol")) { | |
2155 formatProps.outlineLevelCol = attrib.value().toString().toInt(); | |
2156 } else if(attrib.name() == QLatin1String("outlineLevelRow")) { | |
2157 formatProps.outlineLevelRow = attrib.value().toString().toInt(); | |
2158 } else if(attrib.name() == QLatin1String("thickBottom")) { | |
2159 formatProps.thickBottom = attrib.value() == QLatin1String("1"); | |
2160 } else if(attrib.name() == QLatin1String("thickTop")) { | |
2161 formatProps.thickTop = attrib.value() == QLatin1String("1"); | |
2162 } else if(attrib.name() == QLatin1String("zeroHeight")) { | |
2163 formatProps.zeroHeight = attrib.value() == QLatin1String("1"); | |
2164 } | |
2165 } | |
2166 | |
2167 if(formatProps.defaultColWidth == 0.0) { //not set | |
2168 formatProps.defaultColWidth = WorksheetPrivate::calculateColWidth(formatProps.baseColWidth); | |
2169 } | |
2170 | |
2171 } | |
2172 double WorksheetPrivate::calculateColWidth(int characters) | |
2173 { | |
2174 //!Todo | |
2175 //Take normal style' font maximum width and add padding and margin pixels | |
2176 return characters + 0.5; | |
2177 } | |
2178 | |
2179 void WorksheetPrivate::loadXmlHyperlinks(QXmlStreamReader &reader) | |
2180 { | |
2181 Q_ASSERT(reader.name() == QLatin1String("hyperlinks")); | |
2182 | |
2183 while (!reader.atEnd() && !(reader.name() == QLatin1String("hyperlinks") | |
2184 && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2185 reader.readNextStartElement(); | |
2186 if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("hyperlink")) { | |
2187 QXmlStreamAttributes attrs = reader.attributes(); | |
2188 CellReference pos(attrs.value(QLatin1String("ref")).toString()); | |
2189 if (pos.isValid()) { //Valid | |
2190 QSharedPointer<XlsxHyperlinkData> link(new XlsxHyperlinkData); | |
2191 link->display = attrs.value(QLatin1String("display")).toString(); | |
2192 link->tooltip = attrs.value(QLatin1String("tooltip")).toString(); | |
2193 link->location = attrs.value(QLatin1String("location")).toString(); | |
2194 | |
2195 if (attrs.hasAttribute(QLatin1String("r:id"))) { | |
2196 link->linkType = XlsxHyperlinkData::External; | |
2197 XlsxRelationship ship = relationships->getRelationshipById(attrs.value(QLatin1String("r:id")).toString()); | |
2198 link->target = ship.target; | |
2199 } else { | |
2200 link->linkType = XlsxHyperlinkData::Internal; | |
2201 } | |
2202 | |
2203 urlTable[pos.row()][pos.column()] = link; | |
2204 } | |
2205 } | |
2206 } | |
2207 } | |
2208 | |
2209 QList <QSharedPointer<XlsxColumnInfo> > WorksheetPrivate::getColumnInfoList(int colFirst, int colLast) | |
2210 { | |
2211 QList <QSharedPointer<XlsxColumnInfo> > columnsInfoList; | |
2212 if(isColumnRangeValid(colFirst,colLast)) | |
2213 { | |
2214 QList<int> nodes = getColumnIndexes(colFirst, colLast); | |
2215 | |
2216 for (int idx = 0; idx < nodes.size(); ++idx) { | |
2217 int colStart = nodes[idx]; | |
2218 if (colsInfo.contains(colStart)) { | |
2219 QSharedPointer<XlsxColumnInfo> info = colsInfo[colStart]; | |
2220 columnsInfoList.append(info); | |
2221 } else { | |
2222 int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; | |
2223 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd)); | |
2224 colsInfo.insert(colFirst, info); | |
2225 columnsInfoList.append(info); | |
2226 for (int c = colStart; c <= colEnd; ++c) | |
2227 colsInfoHelper[c] = info; | |
2228 } | |
2229 } | |
2230 } | |
2231 | |
2232 return columnsInfoList; | |
2233 } | |
2234 | |
2235 QList <QSharedPointer<XlsxRowInfo> > WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast) | |
2236 { | |
2237 QList <QSharedPointer<XlsxRowInfo> > rowInfoList; | |
2238 | |
2239 int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn(); | |
2240 | |
2241 for(int row = rowFirst; row <= rowLast; ++row) { | |
2242 if (checkDimensions(row, min_col, false, true)) | |
2243 continue; | |
2244 | |
2245 QSharedPointer<XlsxRowInfo> rowInfo; | |
2246 if ((rowsInfo[row]).isNull()){ | |
2247 rowsInfo[row] = QSharedPointer<XlsxRowInfo>(new XlsxRowInfo()); | |
2248 } | |
2249 rowInfoList.append(rowsInfo[row]); | |
2250 } | |
2251 | |
2252 return rowInfoList; | |
2253 } | |
2254 | |
2255 bool Worksheet::loadFromXmlFile(QIODevice *device) | |
2256 { | |
2257 Q_D(Worksheet); | |
2258 | |
2259 QXmlStreamReader reader(device); | |
2260 while (!reader.atEnd()) { | |
2261 reader.readNextStartElement(); | |
2262 if (reader.tokenType() == QXmlStreamReader::StartElement) { | |
2263 if (reader.name() == QLatin1String("dimension")) { | |
2264 QXmlStreamAttributes attributes = reader.attributes(); | |
2265 QString range = attributes.value(QLatin1String("ref")).toString(); | |
2266 d->dimension = CellRange(range); | |
2267 } else if (reader.name() == QLatin1String("sheetViews")) { | |
2268 d->loadXmlSheetViews(reader); | |
2269 } else if (reader.name() == QLatin1String("sheetFormatPr")) { | |
2270 d->loadXmlSheetFormatProps(reader); | |
2271 } else if (reader.name() == QLatin1String("cols")) { | |
2272 d->loadXmlColumnsInfo(reader); | |
2273 } else if (reader.name() == QLatin1String("sheetData")) { | |
2274 d->loadXmlSheetData(reader); | |
2275 } else if (reader.name() == QLatin1String("mergeCells")) { | |
2276 d->loadXmlMergeCells(reader); | |
2277 } else if (reader.name() == QLatin1String("dataValidations")) { | |
2278 d->loadXmlDataValidations(reader); | |
2279 } else if (reader.name() == QLatin1String("conditionalFormatting")) { | |
2280 ConditionalFormatting cf; | |
2281 cf.loadFromXml(reader, workbook()->styles()); | |
2282 d->conditionalFormattingList.append(cf); | |
2283 } else if (reader.name() == QLatin1String("hyperlinks")) { | |
2284 d->loadXmlHyperlinks(reader); | |
2285 } else if (reader.name() == QLatin1String("drawing")) { | |
2286 QString rId = reader.attributes().value(QStringLiteral("r:id")).toString(); | |
2287 QString name = d->relationships->getRelationshipById(rId).target; | |
2288 QString path = QDir::cleanPath(splitPath(filePath())[0] + QLatin1String("/") + name); | |
2289 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_LoadFromExists)); | |
2290 d->drawing->setFilePath(path); | |
2291 } else if (reader.name() == QLatin1String("extLst")) { | |
2292 //Todo: add extLst support | |
2293 while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst") | |
2294 && reader.tokenType() == QXmlStreamReader::EndElement)) { | |
2295 reader.readNextStartElement(); | |
2296 } | |
2297 } | |
2298 } | |
2299 } | |
2300 | |
2301 d->validateDimension(); | |
2302 return true; | |
2303 } | |
2304 | |
2305 /* | |
2306 * Documents imported from Google Docs does not contain dimension data. | |
2307 */ | |
2308 void WorksheetPrivate::validateDimension() | |
2309 { | |
2310 if (dimension.isValid() || cellTable.isEmpty()) | |
2311 return; | |
2312 | |
2313 int firstRow = cellTable.constBegin().key(); | |
2314 int lastRow = (cellTable.constEnd()-1).key(); | |
2315 int firstColumn = -1; | |
2316 int lastColumn = -1; | |
2317 | |
2318 for (QMap<int, QMap<int, QSharedPointer<Cell> > >::const_iterator it = cellTable.begin(); it != cellTable.end(); ++it) | |
2319 { | |
2320 Q_ASSERT(!it.value().isEmpty()); | |
2321 | |
2322 if (firstColumn == -1 || it.value().constBegin().key() < firstColumn) | |
2323 firstColumn = it.value().constBegin().key(); | |
2324 | |
2325 if (lastColumn == -1 || (it.value().constEnd()-1).key() > lastColumn) | |
2326 lastColumn = (it.value().constEnd()-1).key(); | |
2327 } | |
2328 | |
2329 CellRange cr(firstRow, firstColumn, lastRow, lastColumn); | |
2330 | |
2331 if (cr.isValid()) | |
2332 dimension = cr; | |
2333 } | |
2334 | |
2335 /*! | |
2336 * \internal | |
2337 * Unit test can use this member to get sharedString object. | |
2338 */ | |
2339 SharedStrings *WorksheetPrivate::sharedStrings() const | |
2340 { | |
2341 return workbook->sharedStrings(); | |
2342 } | |
2343 | |
2344 QT_END_NAMESPACE_XLSX |