File indexing completed on 2025-03-23 03:32:51

0001 // xlsxworkbook.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QXmlStreamWriter>
0005 #include <QXmlStreamReader>
0006 #include <QFile>
0007 #include <QBuffer>
0008 #include <QDir>
0009 #include <QtDebug>
0010 
0011 #include "xlsxworkbook.h"
0012 #include "xlsxworkbook_p.h"
0013 #include "xlsxsharedstrings_p.h"
0014 #include "xlsxworksheet.h"
0015 #include "xlsxchartsheet.h"
0016 #include "xlsxstyles_p.h"
0017 #include "xlsxformat.h"
0018 #include "xlsxworksheet_p.h"
0019 #include "xlsxformat_p.h"
0020 #include "xlsxmediafile_p.h"
0021 #include "xlsxutility_p.h"
0022 #include "xlsxchart.h"
0023 
0024 QT_BEGIN_NAMESPACE_XLSX
0025 
0026 WorkbookPrivate::WorkbookPrivate(Workbook *q, Workbook::CreateFlag flag) :
0027     AbstractOOXmlFilePrivate(q, flag)
0028 {
0029     sharedStrings = QSharedPointer<SharedStrings> (new SharedStrings(flag));
0030     styles = QSharedPointer<Styles>(new Styles(flag));
0031     theme = QSharedPointer<Theme>(new Theme(flag));
0032 
0033     x_window = 240;
0034     y_window = 15;
0035     window_width = 16095;
0036     window_height = 9660;
0037 
0038     strings_to_numbers_enabled = false;
0039     strings_to_hyperlinks_enabled = true;
0040     html_to_richstring_enabled = false;
0041     date1904 = false;
0042     defaultDateFormat = QStringLiteral("yyyy-mm-dd");
0043     activesheetIndex = 0;
0044     firstsheet = 0;
0045     table_count = 0;
0046 
0047     last_worksheet_index = 0;
0048     last_chartsheet_index = 0;
0049     last_sheet_id = 0;
0050 }
0051 
0052 Workbook::Workbook(CreateFlag flag)
0053     : AbstractOOXmlFile(new WorkbookPrivate(this, flag))
0054 {
0055 
0056 }
0057 
0058 Workbook::~Workbook()
0059 {
0060 }
0061 
0062 bool Workbook::isDate1904() const
0063 {
0064     Q_D(const Workbook);
0065     return d->date1904;
0066 }
0067 
0068 /*!
0069   Excel for Windows uses a default epoch of 1900 and Excel
0070   for Mac uses an epoch of 1904. However, Excel on either
0071   platform will convert automatically between one system
0072   and the other. Qt Xlsx stores dates in the 1900 format
0073   by default.
0074 
0075   \note This function should be called before any date/time
0076   has been written.
0077 */
0078 void Workbook::setDate1904(bool date1904)
0079 {
0080     Q_D(Workbook);
0081     d->date1904 = date1904;
0082 }
0083 
0084 /*
0085   Enable the worksheet.write() method to convert strings
0086   to numbers, where possible, using float() in order to avoid
0087   an Excel warning about "Numbers Stored as Text".
0088 
0089   The default is false
0090  */
0091 void Workbook::setStringsToNumbersEnabled(bool enable)
0092 {
0093     Q_D(Workbook);
0094     d->strings_to_numbers_enabled = enable;
0095 }
0096 
0097 bool Workbook::isStringsToNumbersEnabled() const
0098 {
0099     Q_D(const Workbook);
0100     return d->strings_to_numbers_enabled;
0101 }
0102 
0103 void Workbook::setStringsToHyperlinksEnabled(bool enable)
0104 {
0105     Q_D(Workbook);
0106     d->strings_to_hyperlinks_enabled = enable;
0107 }
0108 
0109 bool Workbook::isStringsToHyperlinksEnabled() const
0110 {
0111     Q_D(const Workbook);
0112     return d->strings_to_hyperlinks_enabled;
0113 }
0114 
0115 void Workbook::setHtmlToRichStringEnabled(bool enable)
0116 {
0117     Q_D(Workbook);
0118     d->html_to_richstring_enabled = enable;
0119 }
0120 
0121 bool Workbook::isHtmlToRichStringEnabled() const
0122 {
0123     Q_D(const Workbook);
0124     return d->html_to_richstring_enabled;
0125 }
0126 
0127 QString Workbook::defaultDateFormat() const
0128 {
0129     Q_D(const Workbook);
0130     return d->defaultDateFormat;
0131 }
0132 
0133 void Workbook::setDefaultDateFormat(const QString &format)
0134 {
0135     Q_D(Workbook);
0136     d->defaultDateFormat = format;
0137 }
0138 
0139 /*!
0140  * \brief Create a defined name in the workbook.
0141  * \param name The defined name
0142  * \param formula The cell or range that the defined name refers to.
0143  * \param comment
0144  * \param scope The name of one worksheet, or empty which means golbal scope.
0145  * \return Return false if the name invalid.
0146  */
0147 bool Workbook::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope)
0148 {
0149     Q_D(Workbook);
0150 
0151     //Remove the = sign from the formula if it exists.
0152     QString formulaString = formula;
0153     if (formulaString.startsWith(QLatin1Char('=')))
0154         formulaString = formula.mid(1);
0155 
0156     int id=-1;
0157     if (!scope.isEmpty()) {
0158         for (int i=0; i<d->sheets.size(); ++i) {
0159             if (d->sheets[i]->sheetName() == scope) {
0160                 id = d->sheets[i]->sheetId();
0161                 break;
0162             }
0163         }
0164     }
0165 
0166     d->definedNamesList.append(XlsxDefineNameData(name, formulaString, comment, id));
0167     return true;
0168 }
0169 
0170 AbstractSheet *Workbook::addSheet(const QString &name, AbstractSheet::SheetType type)
0171 {
0172     Q_D(Workbook);
0173     return insertSheet(d->sheets.size(), name, type);
0174 }
0175 
0176 /*!
0177  * \internal
0178  */
0179 QStringList Workbook::worksheetNames() const
0180 {
0181     Q_D(const Workbook);
0182     return d->sheetNames;
0183 }
0184 
0185 /*!
0186  * \internal
0187  * Used only when load the xlsx file!!
0188  */
0189 AbstractSheet *Workbook::addSheet(const QString &name, int sheetId, AbstractSheet::SheetType type)
0190 {
0191     Q_D(Workbook);
0192     if (sheetId > d->last_sheet_id)
0193         d->last_sheet_id = sheetId;
0194 
0195     AbstractSheet *sheet = nullptr;
0196     if (type == AbstractSheet::ST_WorkSheet)
0197     {
0198         // create work sheet (value sheet)
0199         sheet = new Worksheet(name, sheetId, this, F_LoadFromExists);
0200     }
0201     else if (type == AbstractSheet::ST_ChartSheet)
0202     {
0203         // create chart sheet
0204         sheet = new Chartsheet(name, sheetId, this, F_LoadFromExists);
0205     }
0206     else
0207     {
0208         qWarning("unsupported sheet type.");
0209         Q_ASSERT(false);
0210     }
0211 
0212     d->sheets.append(QSharedPointer<AbstractSheet>(sheet));
0213     d->sheetNames.append(name);
0214 
0215     return sheet;
0216 }
0217 
0218 AbstractSheet *Workbook::insertSheet(int index, const QString &name, AbstractSheet::SheetType type)
0219 {
0220     Q_D(Workbook);
0221     QString sheetName = createSafeSheetName(name);
0222     if(index > d->last_sheet_id){
0223         //User tries to insert, where no sheet has gone before.
0224         return nullptr;
0225     }
0226     if (!sheetName.isEmpty()) {
0227         //If user given an already in-used name, we should not continue any more!
0228         if (d->sheetNames.contains(sheetName))
0229             return nullptr;
0230     } else {
0231         if (type == AbstractSheet::ST_WorkSheet) {
0232             do {
0233                 ++d->last_worksheet_index;
0234                 sheetName = QStringLiteral("Sheet%1").arg(d->last_worksheet_index);
0235             } while (d->sheetNames.contains(sheetName));
0236         } else if (type == AbstractSheet::ST_ChartSheet) {
0237             do {
0238                 ++d->last_chartsheet_index;
0239                 sheetName = QStringLiteral("Chart%1").arg(d->last_chartsheet_index);
0240             } while (d->sheetNames.contains(sheetName));
0241         } else {
0242             qWarning("unsupported sheet type.");
0243             return nullptr;
0244         }
0245     }
0246 
0247     ++d->last_sheet_id;
0248 
0249     AbstractSheet *sheet = nullptr;
0250     if ( type == AbstractSheet::ST_WorkSheet )
0251     {
0252         sheet = new Worksheet(sheetName, d->last_sheet_id, this, F_NewFromScratch);
0253     }
0254     else if ( type == AbstractSheet::ST_ChartSheet )
0255     {
0256         sheet = new Chartsheet(sheetName, d->last_sheet_id, this, F_NewFromScratch);
0257     }
0258     else
0259     {
0260         qWarning("unsupported sheet type.");
0261         Q_ASSERT(false);
0262     }
0263 
0264     d->sheets.insert(index, QSharedPointer<AbstractSheet>(sheet));
0265     d->sheetNames.insert(index, sheetName);
0266     d->activesheetIndex = index;
0267 
0268     return sheet;
0269 }
0270 
0271 /*!
0272  * Returns current active worksheet.
0273  */
0274 AbstractSheet *Workbook::activeSheet() const
0275 {
0276     Q_D(const Workbook);
0277     if (d->sheets.isEmpty())
0278         const_cast<Workbook*>(this)->addSheet();
0279     return d->sheets[d->activesheetIndex].data();
0280 }
0281 
0282 bool Workbook::setActiveSheet(int index)
0283 {
0284     Q_D(Workbook);
0285     if (index < 0 || index >= d->sheets.size()) {
0286         //warning
0287         return false;
0288     }
0289     d->activesheetIndex = index;
0290     return true;
0291 }
0292 
0293 /*!
0294  * Rename the worksheet at the \a index to \a newName.
0295  */
0296 bool Workbook::renameSheet(int index, const QString &newName)
0297 {
0298     Q_D(Workbook);
0299     QString name = createSafeSheetName(newName);
0300     if (index < 0 || index >= d->sheets.size())
0301         return false;
0302 
0303     //If user given an already in-used name, return false
0304     for (int i=0; i<d->sheets.size(); ++i) {
0305         if (d->sheets[i]->sheetName() == name)
0306             return false;
0307     }
0308 
0309     d->sheets[index]->setSheetName(name);
0310     d->sheetNames[index] = name;
0311     return true;
0312 }
0313 
0314 /*!
0315  * Remove the worksheet at pos \a index.
0316  */
0317 bool Workbook::deleteSheet(int index)
0318 {
0319     Q_D(Workbook);
0320     if (d->sheets.size() <= 1)
0321         return false;
0322     if (index < 0 || index >= d->sheets.size())
0323         return false;
0324     d->sheets.removeAt(index);
0325     d->sheetNames.removeAt(index);
0326     return true;
0327 }
0328 
0329 /*!
0330  * Moves the worksheet form \a srcIndex to \a distIndex.
0331  */
0332 bool Workbook::moveSheet(int srcIndex, int distIndex)
0333 {
0334     Q_D(Workbook);
0335     if (srcIndex == distIndex)
0336         return false;
0337 
0338     if (srcIndex < 0 || srcIndex >= d->sheets.size())
0339         return false;
0340 
0341     QSharedPointer<AbstractSheet> sheet = d->sheets.takeAt(srcIndex);
0342     d->sheetNames.takeAt(srcIndex);
0343     if (distIndex >= 0 || distIndex <= d->sheets.size()) {
0344         d->sheets.insert(distIndex, sheet);
0345         d->sheetNames.insert(distIndex, sheet->sheetName());
0346     } else {
0347         d->sheets.append(sheet);
0348         d->sheetNames.append(sheet->sheetName());
0349     }
0350     return true;
0351 }
0352 
0353 bool Workbook::copySheet(int index, const QString &newName)
0354 {
0355     Q_D(Workbook);
0356     if (index < 0 || index >= d->sheets.size())
0357         return false;
0358 
0359     QString worksheetName = createSafeSheetName(newName);
0360     if (!newName.isEmpty()) {
0361         //If user given an already in-used name, we should not continue any more!
0362         if (d->sheetNames.contains(newName))
0363             return false;
0364     } else {
0365         int copy_index = 1;
0366         do {
0367             ++copy_index;
0368             worksheetName = QStringLiteral("%1(%2)").arg(d->sheets[index]->sheetName()).arg(copy_index);
0369         } while (d->sheetNames.contains(worksheetName));
0370     }
0371 
0372     ++d->last_sheet_id;
0373     AbstractSheet *sheet = d->sheets[index]->copy(worksheetName, d->last_sheet_id);
0374     d->sheets.append(QSharedPointer<AbstractSheet> (sheet));
0375     d->sheetNames.append(sheet->sheetName());
0376 
0377     return true; // #162
0378 }
0379 
0380 /*!
0381  * Returns count of worksheets.
0382  */
0383 int Workbook::sheetCount() const
0384 {
0385     Q_D(const Workbook);
0386     return d->sheets.count();
0387 }
0388 
0389 /*!
0390  * Returns the sheet object at index \a sheetIndex.
0391  */
0392 AbstractSheet *Workbook::sheet(int index) const
0393 {
0394     Q_D(const Workbook);
0395     if (index < 0 || index >= d->sheets.size())
0396         return nullptr;
0397     return d->sheets.at(index).data();
0398 }
0399 
0400 SharedStrings *Workbook::sharedStrings() const
0401 {
0402     Q_D(const Workbook);
0403     return d->sharedStrings.data();
0404 }
0405 
0406 Styles *Workbook::styles()
0407 {
0408     Q_D(Workbook);
0409     return d->styles.data();
0410 }
0411 
0412 Theme *Workbook::theme()
0413 {
0414     Q_D(Workbook);
0415     return d->theme.data();
0416 }
0417 
0418 /*!
0419  * \internal
0420  *
0421  * Unlike media files, drawing file is a property of the sheet.
0422  */
0423 QList<Drawing *> Workbook::drawings()
0424 {
0425     Q_D(Workbook);
0426     QList<Drawing *> ds;
0427     for (int i=0; i<d->sheets.size(); ++i) {
0428         QSharedPointer<AbstractSheet> sheet = d->sheets[i];
0429         if (sheet->drawing())
0430         ds.append(sheet->drawing());
0431     }
0432 
0433     return ds;
0434 }
0435 
0436 /*!
0437  * \internal
0438  */
0439 QList<QSharedPointer<AbstractSheet> > Workbook::getSheetsByTypes(AbstractSheet::SheetType type) const
0440 {
0441     Q_D(const Workbook);
0442     QList<QSharedPointer<AbstractSheet> > list;
0443     for (int i=0; i<d->sheets.size(); ++i) {
0444         if (d->sheets[i]->sheetType() == type)
0445             list.append(d->sheets[i]);
0446     }
0447     return list;
0448 }
0449 
0450 void Workbook::saveToXmlFile(QIODevice *device) const
0451 {
0452     Q_D(const Workbook);
0453     d->relationships->clear();
0454     if (d->sheets.isEmpty())
0455         const_cast<Workbook *>(this)->addSheet();
0456 
0457     QXmlStreamWriter writer(device);
0458 
0459     writer.writeStartDocument(QStringLiteral("1.0"), true);
0460     writer.writeStartElement(QStringLiteral("workbook"));
0461     writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
0462     writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
0463 
0464     writer.writeEmptyElement(QStringLiteral("fileVersion"));
0465     writer.writeAttribute(QStringLiteral("appName"), QStringLiteral("xl"));
0466     writer.writeAttribute(QStringLiteral("lastEdited"), QStringLiteral("4"));
0467     writer.writeAttribute(QStringLiteral("lowestEdited"), QStringLiteral("4"));
0468     writer.writeAttribute(QStringLiteral("rupBuild"), QStringLiteral("4505"));
0469 //    writer.writeAttribute(QStringLiteral("codeName"), QStringLiteral("{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}"));
0470 
0471     writer.writeEmptyElement(QStringLiteral("workbookPr"));
0472     if (d->date1904)
0473         writer.writeAttribute(QStringLiteral("date1904"), QStringLiteral("1"));
0474     writer.writeAttribute(QStringLiteral("defaultThemeVersion"), QStringLiteral("124226"));
0475 
0476     writer.writeStartElement(QStringLiteral("bookViews"));
0477     writer.writeEmptyElement(QStringLiteral("workbookView"));
0478     writer.writeAttribute(QStringLiteral("xWindow"), QString::number(d->x_window));
0479     writer.writeAttribute(QStringLiteral("yWindow"), QString::number(d->y_window));
0480     writer.writeAttribute(QStringLiteral("windowWidth"), QString::number(d->window_width));
0481     writer.writeAttribute(QStringLiteral("windowHeight"), QString::number(d->window_height));
0482     //Store the firstSheet when it isn't the default
0483     //For example, when "the first sheet 0 is hidden", the first sheet will be 1
0484     if (d->firstsheet > 0)
0485         writer.writeAttribute(QStringLiteral("firstSheet"), QString::number(d->firstsheet + 1));
0486     //Store the activeTab when it isn't the first sheet
0487     if (d->activesheetIndex > 0)
0488         writer.writeAttribute(QStringLiteral("activeTab"), QString::number(d->activesheetIndex));
0489     writer.writeEndElement();//bookViews
0490 
0491     writer.writeStartElement(QStringLiteral("sheets"));
0492     int worksheetIndex = 0;
0493     int chartsheetIndex = 0;
0494     for (int i=0; i<d->sheets.size(); ++i) {
0495         QSharedPointer<AbstractSheet> sheet = d->sheets[i];
0496         writer.writeEmptyElement(QStringLiteral("sheet"));
0497         writer.writeAttribute(QStringLiteral("name"), sheet->sheetName());
0498         writer.writeAttribute(QStringLiteral("sheetId"), QString::number(sheet->sheetId()));
0499         if (sheet->sheetState() == AbstractSheet::SS_Hidden)
0500             writer.writeAttribute(QStringLiteral("state"), QStringLiteral("hidden"));
0501         else if (sheet->sheetState() == AbstractSheet::SS_VeryHidden)
0502             writer.writeAttribute(QStringLiteral("state"), QStringLiteral("veryHidden"));
0503 
0504         if (sheet->sheetType() == AbstractSheet::ST_WorkSheet)
0505             d->relationships->addDocumentRelationship(QStringLiteral("/worksheet"), QStringLiteral("worksheets/sheet%1.xml").arg(++worksheetIndex));
0506         else
0507             d->relationships->addDocumentRelationship(QStringLiteral("/chartsheet"), QStringLiteral("chartsheets/sheet%1.xml").arg(++chartsheetIndex));
0508 
0509         writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(d->relationships->count()));
0510     }
0511     writer.writeEndElement();//sheets
0512 
0513     if (d->externalLinks.size() > 0) {
0514         writer.writeStartElement(QStringLiteral("externalReferences"));
0515         for (int i=0; i<d->externalLinks.size(); ++i) {
0516             writer.writeEmptyElement(QStringLiteral("externalReference"));
0517             d->relationships->addDocumentRelationship(QStringLiteral("/externalLink"), QStringLiteral("externalLinks/externalLink%1.xml").arg(i+1));
0518             writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(d->relationships->count()));
0519         }
0520         writer.writeEndElement();//externalReferences
0521     }
0522 
0523     if (!d->definedNamesList.isEmpty()) {
0524         writer.writeStartElement(QStringLiteral("definedNames"));
0525         for (const XlsxDefineNameData &data : d->definedNamesList) {
0526             writer.writeStartElement(QStringLiteral("definedName"));
0527             writer.writeAttribute(QStringLiteral("name"), data.name);
0528             if (!data.comment.isEmpty())
0529                 writer.writeAttribute(QStringLiteral("comment"), data.comment);
0530             if (data.sheetId != -1) {
0531                 //find the local index of the sheet.
0532                 for (int i=0; i<d->sheets.size(); ++i) {
0533                     if (d->sheets[i]->sheetId() == data.sheetId) {
0534                         writer.writeAttribute(QStringLiteral("localSheetId"), QString::number(i));
0535                         break;
0536                     }
0537                 }
0538             }
0539             writer.writeCharacters(data.formula);
0540             writer.writeEndElement();//definedName
0541         }
0542         writer.writeEndElement();//definedNames
0543     }
0544 
0545     writer.writeStartElement(QStringLiteral("calcPr"));
0546     writer.writeAttribute(QStringLiteral("calcId"), QStringLiteral("124519"));
0547     writer.writeEndElement(); //calcPr
0548 
0549     writer.writeEndElement();//workbook
0550     writer.writeEndDocument();
0551 
0552     d->relationships->addDocumentRelationship(QStringLiteral("/theme"), QStringLiteral("theme/theme1.xml"));
0553     d->relationships->addDocumentRelationship(QStringLiteral("/styles"), QStringLiteral("styles.xml"));
0554     if (!sharedStrings()->isEmpty())
0555         d->relationships->addDocumentRelationship(QStringLiteral("/sharedStrings"), QStringLiteral("sharedStrings.xml"));
0556 }
0557 
0558 bool Workbook::loadFromXmlFile(QIODevice *device)
0559 {
0560     Q_D(Workbook);
0561 
0562     QXmlStreamReader reader(device);
0563     while (!reader.atEnd())
0564     {
0565          QXmlStreamReader::TokenType token = reader.readNext();
0566          if (token == QXmlStreamReader::StartElement)
0567          {
0568              if (reader.name() == QLatin1String("sheet"))
0569              {
0570                  QXmlStreamAttributes attributes = reader.attributes();
0571 
0572                  const auto& name = attributes.value(QLatin1String("name")).toString();
0573 
0574                  int sheetId = attributes.value(QLatin1String("sheetId")).toInt();
0575 
0576                  const auto& rId = attributes.value(QLatin1String("r:id")).toString();
0577 
0578                  const auto& stateString = attributes.value(QLatin1String("state"));
0579 
0580                  AbstractSheet::SheetState state = AbstractSheet::SS_Visible;
0581                  if (stateString == QLatin1String("hidden"))
0582                      state = AbstractSheet::SS_Hidden;
0583                  else if (stateString == QLatin1String("veryHidden"))
0584                      state = AbstractSheet::SS_VeryHidden;
0585 
0586                  XlsxRelationship relationship = d->relationships->getRelationshipById(rId);
0587 
0588                  AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet;
0589                  if (relationship.type.endsWith(QLatin1String("/worksheet")))
0590                  {
0591                      type = AbstractSheet::ST_WorkSheet;
0592                  }
0593                  else if (relationship.type.endsWith(QLatin1String("/chartsheet")))
0594                  {
0595                      type = AbstractSheet::ST_ChartSheet;
0596                  }
0597                  else if (relationship.type.endsWith(QLatin1String("/dialogsheet")))
0598                  {
0599                      type = AbstractSheet::ST_DialogSheet;
0600                  }
0601                  else if (relationship.type.endsWith(QLatin1String("/xlMacrosheet")))
0602                  {
0603                      type = AbstractSheet::ST_MacroSheet;
0604                  }
0605                  else
0606                  {
0607                      qWarning() << "unknown sheet type : " << relationship.type ;
0608                  }
0609 
0610                  AbstractSheet *sheet = addSheet(name, sheetId, type);
0611                  sheet->setSheetState(state);
0612                  QString strFilePath = filePath();
0613 
0614                  // const QString fullPath = QDir::cleanPath(splitPath(strFilePath).constFirst() + QLatin1String("/") + relationship.target);
0615                  const auto parts = splitPath(strFilePath);
0616                  QString fullPath = QDir::cleanPath(parts.first() + QLatin1String("/") + relationship.target);
0617 
0618                  sheet->setFilePath(fullPath);
0619              }
0620              else if (reader.name() == QLatin1String("workbookPr"))
0621              {
0622                 QXmlStreamAttributes attrs = reader.attributes();
0623                 if (attrs.hasAttribute(QLatin1String("date1904")))
0624                     d->date1904 = true;
0625              }
0626              else if (reader.name() == QLatin1String("bookviews"))
0627              {
0628                 while (!(reader.name() == QLatin1String("bookviews") &&
0629                          reader.tokenType() == QXmlStreamReader::EndElement))
0630                 {
0631                     reader.readNextStartElement();
0632                     if (reader.tokenType() == QXmlStreamReader::StartElement)
0633                     {
0634                         if (reader.name() == QLatin1String("workbookView"))
0635                         {
0636                             QXmlStreamAttributes attrs = reader.attributes();
0637                             if (attrs.hasAttribute(QLatin1String("xWindow")))
0638                                 d->x_window = attrs.value(QLatin1String("xWindow")).toInt();
0639                             if (attrs.hasAttribute(QLatin1String("yWindow")))
0640                                 d->y_window = attrs.value(QLatin1String("yWindow")).toInt();
0641                             if (attrs.hasAttribute(QLatin1String("windowWidth")))
0642                                 d->window_width = attrs.value(QLatin1String("windowWidth")).toInt();
0643                             if (attrs.hasAttribute(QLatin1String("windowHeight")))
0644                                 d->window_height = attrs.value(QLatin1String("windowHeight")).toInt();
0645                             if (attrs.hasAttribute(QLatin1String("firstSheet")))
0646                                 d->firstsheet = attrs.value(QLatin1String("firstSheet")).toInt();
0647                             if (attrs.hasAttribute(QLatin1String("activeTab")))
0648                                 d->activesheetIndex = attrs.value(QLatin1String("activeTab")).toInt();
0649                         }
0650                     }
0651                 }
0652              }
0653              else if (reader.name() == QLatin1String("externalReference"))
0654              {
0655                  QXmlStreamAttributes attributes = reader.attributes();
0656                  const QString rId = attributes.value(QLatin1String("r:id")).toString();
0657                  XlsxRelationship relationship = d->relationships->getRelationshipById(rId);
0658 
0659                  QSharedPointer<SimpleOOXmlFile> link(new SimpleOOXmlFile(F_LoadFromExists));
0660 
0661                  const auto parts = splitPath(filePath());
0662                  QString fullPath = QDir::cleanPath(parts.first() + QLatin1String("/") + relationship.target);
0663 
0664                  link->setFilePath(fullPath);
0665                  d->externalLinks.append(link);
0666              } else if (reader.name() == QLatin1String("definedName")) {
0667                  QXmlStreamAttributes attrs = reader.attributes();
0668                  XlsxDefineNameData data;
0669 
0670                  data.name = attrs.value(QLatin1String("name")).toString();
0671                  if (attrs.hasAttribute(QLatin1String("comment")))
0672                      data.comment = attrs.value(QLatin1String("comment")).toString();
0673                  if (attrs.hasAttribute(QLatin1String("localSheetId"))) {
0674                      int localId = attrs.value(QLatin1String("localSheetId")).toInt();
0675                      int sheetId = d->sheets.at(localId)->sheetId();
0676                      data.sheetId = sheetId;
0677                  }
0678                  data.formula = reader.readElementText();
0679                  d->definedNamesList.append(data);
0680              }
0681          }
0682     }
0683     return true;
0684 }
0685 
0686 /*!
0687  * \internal
0688  */
0689 QList<std::shared_ptr<MediaFile> > Workbook::mediaFiles() const
0690 {
0691     Q_D(const Workbook);
0692 
0693     return d->mediaFiles;
0694 }
0695 
0696 /*!
0697  * \internal
0698  */
0699 void Workbook::addMediaFile(std::shared_ptr<MediaFile> media, bool force)
0700 {
0701     Q_D(Workbook);
0702 
0703     if (!force)
0704     {
0705         for (int i=0; i<d->mediaFiles.size(); ++i)
0706         {
0707             if (d->mediaFiles[i]->hashKey() == media->hashKey())
0708             {
0709                 media->setIndex(i);
0710                 return;
0711             }
0712         }
0713     }
0714 
0715     media->setIndex(d->mediaFiles.size());
0716     d->mediaFiles.append(media);
0717 }
0718 
0719 /*!
0720  * \internal
0721  */
0722 QList<QSharedPointer<Chart> > Workbook::chartFiles() const
0723 {
0724     Q_D(const Workbook);
0725 
0726     return d->chartFiles;
0727 }
0728 
0729 /*!
0730  * \internal
0731  */
0732 void Workbook::addChartFile(QSharedPointer<Chart> chart)
0733 {
0734     Q_D(Workbook);
0735 
0736     if (!d->chartFiles.contains(chart))
0737         d->chartFiles.append(chart);
0738 }
0739 
0740 QT_END_NAMESPACE_XLSX