diff --git a/src/libs/models/reportgenerator/ReportGeneratorOdt.cpp b/src/libs/models/reportgenerator/ReportGeneratorOdt.cpp index e2a4e6eb..127cade6 100644 --- a/src/libs/models/reportgenerator/ReportGeneratorOdt.cpp +++ b/src/libs/models/reportgenerator/ReportGeneratorOdt.cpp @@ -1,1337 +1,1338 @@ /* This file is part of the KDE project Copyright (C) 2017 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "planmodels_export.h" #include "ReportGeneratorOdt.h" #include "kptproject.h" #include "kptschedule.h" #include "kptnodeitemmodel.h" #include "kpttaskstatusmodel.h" #include "kptitemmodelbase.h" #include "kptnodechartmodel.h" #include "kptschedulemodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HeaderRole Qt::UserRole + 543 const QLoggingCategory &PLANRG_LOG() { static const QLoggingCategory category("calligra.plan.reportodt"); return category; } #define dbgRG qCDebug(PLANRG_LOG)<(p->sourceModel())) { ChartItemModel *c = qobject_cast(p->sourceModel()); if (c) { return c; } } return 0; } bool startsWith(const QStringList &keys, const QString &key) { - for (const QString k : keys) { + for (const QString &k : keys) { if (key.startsWith(k)) { return true; } } return false; } QStringList trimmed(const QStringList &lst) { QStringList rlst; for (const QString &s : lst) { QString r = s.trimmed(); if (!r.isEmpty()) { rlst << r; } } return rlst; } bool addDataToFile(QByteArray &buffer, const QString &destName, KoStore &to) { QBuffer file(&buffer); if (!file.open(QIODevice::ReadOnly)) { dbgRG<<"Failed to open buffer"; return false; } if (!to.open(destName)) { dbgRG<<"Failed to open file for writing:"< 0; total += block) { data.resize(block); if (to.write(data) != block) { dbgRG<<"Failed to write block of data"; return false; } data.resize(8*1024); } to.close(); file.close(); return true; } QAbstractItemModel *translationModel() { - QStandardItemModel *model = new QStandardItemModel(); - QMap names; - names["Project"] = i18n("Project"); - names["Manager"] = i18n("Manager"); - names["Schedule"] = i18n("Schedule"); - names["BCWS"] = xi18nc("@title:column Budgeted Cost of Work Scheduled", "BCWS"); - names["BCWP"] = xi18nc("@title:column Budgeted Cost of Work Performed", "BCWP"); - names["ACWP"] = xi18nc("@title:column Actual Cost of Work Performed", "ACWP"); - names["SPI"] = xi18nc("@title:column Schedule Performance Index", "SPI"); - names["CPI"] = xi18nc("@title:column Cost Performance Index", "CPI"); + QList > names; + names << QPair("Project", i18n("Project")) + << QPair("Manager", i18n("Manager")) + << QPair("Schedule", i18n("Schedule")) + << QPair("BCWS", xi18nc("@title:column Budgeted Cost of Work Scheduled", "BCWS")) + << QPair("BCWP", xi18nc("@title:column Budgeted Cost of Work Performed", "BCWP")) + << QPair("ACWP", xi18nc("@title:column Actual Cost of Work Performed", "ACWP")) + << QPair("SPI", xi18nc("@title:column Schedule Performance Index", "SPI")) + << QPair("CPI", xi18nc("@title:column Cost Performance Index", "CPI")); + QStandardItemModel *model = new QStandardItemModel(); for (int column = 0; column < names.count(); ++column) { - model->setHeaderData(column, Qt::Horizontal, names.values().at(column)); - model->setHeaderData(column, Qt::Horizontal, names.keys().at(column), HeaderRole); + model->setHeaderData(column, Qt::Horizontal, names.at(column).first, HeaderRole); + model->setHeaderData(column, Qt::Horizontal, names.at(column).second); } return model; } QAbstractItemModel *projectModel() { QList > names; names << QPair("Name", i18n("Name")) << QPair("Manager", i18n("Manager")) << QPair("BCWS Cost", i18nc("Cost based Budgeted Cost of Work Scheduled", "BCWS Cost")) << QPair("BCWP Cost", i18nc("Cost based Budgeted Cost of Work Performed", "BCWP Cost")) << QPair("ACWP Cost", i18nc("Cost based Actual Cost of Work Performed", "ACWP Cost")) << QPair("SPI Cost", i18nc("Cost based Schedule Performance Index", "SPI Cost")) << QPair("CPI Cost", i18nc("Cost based Cost Performance Index", "CPI Cost")) << QPair("BCWS Effort", i18nc("Effort based Budgeted Cost of Work Scheduled", "BCWS Effort")) << QPair("BCWP Effort", i18nc("Effort based Budgeted Cost of Work Performed", "BCWP Effort")) << QPair("ACWP Effort", i18nc("Effort based Actual Cost of Work Performed", "ACWP Effort")) << QPair("SPI Effort", i18nc("Effort based Schedule Performance Index", "SPI Effort")) << QPair("CPI Effort", i18nc("Effort based Cost Performance Index", "CPI Effort")); QStandardItemModel *model = new QStandardItemModel(0, names.count()); for (int column = 0; column < names.count(); ++column) { model->setHeaderData(column, Qt::Horizontal, names.at(column).first, HeaderRole); model->setHeaderData(column, Qt::Horizontal, names.at(column).second); } return model; } void initProjectModel(QAbstractItemModel *model, Project *project, ScheduleManager *sm) { if (model->rowCount() == 0) { model->insertRow(0); } QModelIndex idx = model->index(0, 0); model->setData(idx, project->name()); idx = model->index(0, 1); model->setData(idx, project->leader()); PerformanceDataCurrentDateModel m(0); m.setProject(project); m.setScheduleManager(sm); m.setNodes(QList() << project); int col = 2; // column of BCWS Cost for (int r = 0; r < 2; ++r) { for (int c = 0; c < 5; ++c) { idx = model->index(0, col++); QModelIndex cidx = m.index(r, c); model->setData(idx, cidx.data()); } } } QAbstractItemModel *scheduleModel() { ScheduleItemModel m; QStandardItemModel *model = new QStandardItemModel(0, m.columnCount()); for (int c = 0; c < m.columnCount(); ++c) { model->setHeaderData(c, Qt::Horizontal, m.headerData(c, Qt::Horizontal)); model->setHeaderData(c, Qt::Horizontal, m.headerData(c, Qt::Horizontal, Qt::EditRole), HeaderRole); } return model; } void initScheduleModel(QAbstractItemModel *model, Project *project, ScheduleManager *sm) { ScheduleItemModel m; m.setProject(project); QModelIndex idx = m.index(sm); if (idx.isValid()) { if (model->rowCount() == 0) { model->insertRow(0); } for (QModelIndex i = idx; i.isValid(); i = i.sibling(i.row(), i.column() + 1)) { QModelIndex midx = model->index(0, i.column()); model->setData(midx, i.data()); dbgRGVariable<headerData(midx.column(), Qt::Horizontal, HeaderRole).toString()<<'='<rowCount()<columnCount(); } //-------------------------------------- ReportGeneratorOdt::ReportGeneratorOdt() : ReportGenerator() , m_templateStore(0) { m_keys = QStringList() << "table" << "chart"; m_variables = QStringList() << "project" << "schedule"; m_basemodels << new NodeItemModel() << new TaskStatusItemModel() << new ChartItemModel() << new ScheduleItemModel(); m_datamodels["tasks"] = m_basemodels.at(0); m_headerrole["tasks"] = Qt::EditRole; m_datamodels["taskstatus"] = m_basemodels.at(1); m_headerrole["taskstatus"] = Qt::EditRole; m_datamodels["chart.project"] = m_basemodels.at(2); m_headerrole["chart.project"] = Qt::EditRole; m_datamodels["projects"] = projectModel(); m_headerrole["projects"] = HeaderRole; m_datamodels["schedules"] = m_basemodels.at(3); m_headerrole["schedules"] = Qt::EditRole; m_datamodels["project"] = projectModel(); m_headerrole["project"] = HeaderRole; m_datamodels["schedule"] = scheduleModel(); m_headerrole["schedule"] = HeaderRole; m_datamodels["tr"] = translationModel(); m_headerrole["tr"] = HeaderRole; } ReportGeneratorOdt::~ReportGeneratorOdt() { - for (QAbstractItemModel *m : m_datamodels) { + for (QAbstractItemModel *m : m_datamodels) { // clazy:exclude=range-loop if (!m_basemodels.contains(qobject_cast(m))) { delete m; } } qDeleteAll(m_basemodels); close(); } bool ReportGeneratorOdt::open() { m_lastError.clear(); if (m_templateFile.isEmpty()) { m_lastError = i18n("Missing report template file"); return false; } if (m_reportFile.isEmpty()) { m_lastError = i18n("Missing report result file"); return false; } if (m_templateStore) { m_lastError = i18n("Report generator is already open"); return false; } m_templateStore = KoStore::createStore(m_templateFile, KoStore::Read); if (!m_templateStore) { dbgRG<<"Failed to open store:"<setProject(m_project); m->setScheduleManager(m_manager); if (qobject_cast(m)) { qobject_cast(m)->setNodes(QList() << m_project); dbgRGChart<<"chart:"<urlOfStore(); KoOdfReadStore reader(m_templateStore); if (!reader.loadAndParse(m_lastError)) { dbgRG<<"Failed to loadAndParse:"<endElement(); // office:document-content writer->endDocument(); if (!outStore->addDataToFile(buffer.buffer(), "content.xml")) { dbgRG<<"Failed to open 'content.xml' for writing"; m_lastError = i18n("Failed to write to store: %1", QString("content.xml")); delete writer; delete outStore; return false; } buffer.close(); if (m_manifestfiles.contains("styles.xml")) { dbgRG << endl << "---- treat styles.xml (for master-page headers/footers) ----" << endl; QBuffer buffer2; KoXmlWriter *styles = createOasisXmlWriter(reader, &buffer2, "styles.xml", "office:document-styles"); if (!styles) { dbgRG<<"Failed to create styles.xml writer"; return false; } KoXmlDocument stylesDoc; if (!reader.loadAndParse("styles.xml", stylesDoc, m_lastError)) { debugPlan<<"Failed to read styles.xml"<endElement(); // office:document-styles styles->endDocument(); if (!outStore->addDataToFile(buffer2.buffer(), "styles.xml")) { dbgRG<<"Failed to open 'styles.xml' for writing"; m_lastError = i18n("Failed to write to store: %1", QString("styles.xml")); delete writer; delete outStore; } m_manifestfiles.removeAt(m_manifestfiles.indexOf("styles.xml")); } dbgRG << endl << "---- treat the embedded files ----" << endl; treatEmbededObjects(reader, *outStore); dbgRG << endl << "---- copy rest of files ----" << endl; - for (const QString &f : m_manifestfiles) { - copyFile(*reader.store(), *outStore, f); + for (int i = 0; i < m_manifestfiles.count(); ++i) { + copyFile(*reader.store(), *outStore, m_manifestfiles.at(i)); } if (!outStore->finalize()) { dbgRG<<"Failed to write store:"<urlOfStore(); m_lastError = i18n("Failed to write report file: %1", outStore->urlOfStore().path()); delete writer; delete outStore; return false; } delete writer; delete outStore; dbgRG<<"finished"; return true; } bool ReportGeneratorOdt::handleTextP(KoXmlWriter &writer, const KoXmlElement &textp) { Q_UNUSED(writer); dbgRG<<"Check:"<type + '.' + field->dataName; field->setModel(dataModel(modelName), m_headerrole[modelName]); dbgRGChart<<"Found chart:"<seqNr = -1; dbgRGTable<type != "table") { return false; } dbgRGTable; UserField *field = m_userfields[m_activefields.last()]; field->seqNr = 0; // we are in header row return false; } bool ReportGeneratorOdt::treatTableRow(KoXmlWriter &writer, const KoXmlElement &rowElement) { if (m_activefields.isEmpty() || m_userfields[m_activefields.last()]->type != "table") { return false; } UserField *field = m_userfields[m_activefields.last()]; dbgRGTable<seqNr; if (field->seqNr == -1) { // there is no header row, so start with data rows directly field->seqNr = 1; } if (field->seqNr == 0) { // header row writer.startElement("table:table-row"); writeElementAttributes(writer, rowElement); writeChildElements(writer, rowElement); writer.endElement(); field->seqNr = 1; // next is first row } else { dbgRGTable<<" add rows:"<rowCount(); for (field->begin(); field->next();) { writer.startElement("table:table-row"); writeElementAttributes(writer, rowElement); writeChildElements(writer, rowElement); writer.endElement(); } } return true; } ReportGeneratorOdt::UserField *ReportGeneratorOdt::findUserField(const KoXmlElement &decl) const { UserField *field = 0; QString name = decl.attributeNS(KoXmlNS::text, "name"); // eg: table1 or table1.Type or project.name or tr.bcws field = m_userfields.value(name); // if Variable or Translation if (!field) { QStringList lst = name.split('.'); - for (const QString k : m_userfields.keys()) { - if (lst.first().startsWith(k)) { - field = m_userfields[k]; + QMap::const_iterator it; + for (it = m_userfields.constBegin(); it != m_userfields.constEnd(); ++it) { + if (lst.first().startsWith(it.key())) { + field = it.value(); break; } } } return field; } void ReportGeneratorOdt::treatUserFieldGet(KoXmlWriter &writer, const KoXmlElement &e) { dbgRG<variant() == UserField::Rows) { QString name = e.attributeNS(KoXmlNS::text, "name"); // eg: table1.type QString value = e.text(); // eg: type or name QString data = field->data(value); writer.addTextNode(data); dbgRGTable<<"rows:"<variant() == UserField::Header) { QString name = e.attributeNS(KoXmlNS::text, "name"); // eg: table1.type QString value = e.text(); // eg: type or BCWS Cost QString data = field->headerData(value); writer.addTextNode(data); dbgRGTable<<"header row:"<variant() == UserField::Variable) { QString name = e.attributeNS(KoXmlNS::text, "name"); // eg: project.name dbgRGVariable<<"variable:"<columns.value(0)<data(field->columns.value(0)); writer.addTextNode(field->data(field->columns.value(0))); // a variable has only one column } else if (field->variant() == UserField::Translation) { QString name = e.attributeNS(KoXmlNS::text, "name"); // eg: tr.bcws QString data = i18n(field->dataName.toLatin1()); if (field->headerNames.contains(field->dataName)) { data = field->data(field->dataName); } dbgRGTr<<"translation:"<dataName<<'='<name = name; m_userfields[field->name] = field; field->dataName = value; field->seqNr = -3; field->columns << value; field->setModel(dataModel("tr"), m_headerrole["tr"]); dbgRGTr<<" added translation"<name<dataName; } else if (m_variables.contains(tags.first())) { Q_ASSERT(tags.count() >= 2); Q_ASSERT(!m_userfields.contains(tags.first())); UserField *field = new UserField(); field->name = name; m_userfields[field->name] = field; field->dataName = tags.first(); field->seqNr = -2; QStringList vl = value.split(';'); if (!vl.isEmpty()) { field->columns << vl.takeFirst(); field->properties = vl; } field->setModel(dataModel(field->dataName), m_headerrole[field->dataName]); dbgRGVariable<<" added variable"<name<columns<properties; } else { - for (const QString k : m_keys) { + for (const QString &k : m_keys) { // clazy:exclude=range-loop const QString vname = tags.first(); if (!vname.startsWith(k)) { continue; } if (tags.count() == 1) { // this is the main definition (eg: name: "table1", value: "tasks ...") if (!m_userfields.contains(vname)) { m_userfields[vname] = new UserField(); } UserField *field = m_userfields[vname]; field->name = vname; field->type = k; QStringList vl = trimmed(value.toLower().split(';', QString::SkipEmptyParts)); field->dataName = vl.takeFirst(); field->properties += vl; field->setModel(dataModel(field->dataName), m_headerrole[field->dataName]); if (k == "chart") { dbgRGChart<<" "<<"added tag:"<properties; } else { dbgRG<<" "<<"added tag:"<properties; } } else { // this is the fields column definitions (eg: name: "table1.type" value: "") if (!m_userfields.contains(vname)) { m_userfields[vname] = new UserField(); } UserField *field = m_userfields[vname]; field->name = vname; field->columns << value.trimmed().toLower(); dbgRG<<" "<<"added column:"<name<columns; } } } } writer.startElement("text:user-field-decls"); writeElementAttributes(writer, decls); writeChartElements(writer, decls); writer.endElement(); } void ReportGeneratorOdt::writeElementAttributes(KoXmlWriter &writer, const KoXmlElement &element, const QStringList &exclude) { - for (const QPair &a : element.attributeFullNames()) { + for (const QPair &a : element.attributeFullNames()) { // clazy:exclude=range-loop QString prefix = KoXmlNS::nsURI2NS(a.first); if (prefix.isEmpty()) { dbgRG<<" Skipping unknown namespace:"<"<hasFile("META-INF/manifest.xml")) { dbgRG<<"No manifest file"; return 0; } KoXmlDocument manifest; if (!reader.loadAndParse("META-INF/manifest.xml", manifest, m_lastError)) { dbgRG<<"Failed to read manifest:"<hasFile("mimetype")) { if (!copyFile(*reader.store(), *out, "mimetype")) { m_lastError = i18n("Failed to load manifest file"); delete out; return 0; } } if (!copyFile(*reader.store(), *out, "META-INF/manifest.xml")) { m_lastError = i18n("Failed to write manifest file"); delete out; return 0; } KoXmlElement e; forEachElement(e, manifest.documentElement()) { dbgRG<hasFile(fileName); if (reader.store()->isOpen()) { reader.store()->close(); } if (!reader.store()->open(fileName)) { dbgRG << "Entry " << fileName << " not found!"; m_lastError = xi18nc("@info", "Failed to open file %1 from store.", fileName); return 0; } if (!reader.store()->device()->isOpen()) { reader.store()->device()->open(QIODevice::ReadOnly); } KoXmlWriter *writer = 0; QXmlStreamReader xml(reader.store()->device()); xml.setNamespaceProcessing(true); while (!xml.atEnd()) { xml.readNext(); if (xml.tokenType() == QXmlStreamReader::StartElement && !xml.namespaceDeclarations().isEmpty()) { writer = new KoXmlWriter(buffer); writer->startDocument(rootElementName); writer->startElement(rootElementName); writer->addAttribute("xmlns:calligra", KoXmlNS::calligra); // Note: windows needs this type of iteration QXmlStreamNamespaceDeclarations dcl = xml.namespaceDeclarations(); for (int ns = 0; ns < dcl.count(); ++ns) { dbgRGTmp<<"add namespace:"<<("xmlns:" + dcl[ns].prefix())<addAttribute(("xmlns:" + dcl[ns].prefix()).toLatin1(), dcl[ns].namespaceUri().toUtf8()); } QXmlStreamAttributes attr = xml.attributes(); for (int a = 0; a < attr.count(); ++a) { dbgRGTmp<<"add attribute:"<addAttribute(attr[a].qualifiedName().toLatin1(), attr[a].value().toUtf8()); } } } reader.store()->close(); if (!writer) { dbgRG<<"Failed to find a start elemet with namespace declarations in"<%1", fileName); } return writer; } void ReportGeneratorOdt::treatEmbededObjects(KoOdfReadStore &reader, KoStore &outStore) { dbgRGChart; {QMap::const_iterator it; for (it = m_embededcharts.constBegin(); it != m_embededcharts.constEnd(); ++it) { treatChart(reader, outStore, it.key(), it.value()); }} {QMap::const_iterator it; for (it = m_embededgantts.constBegin(); it != m_embededgantts.constEnd(); ++it) { treatGantt(reader, outStore, it.key(), it.value()); }} } void ReportGeneratorOdt::treatChart(KoOdfReadStore &reader, KoStore &outStore, const QString &name, const QString &dir) { dbgRGChart<dataName == "project") { ChartItemModel *m = findChartItemModel(field->model); if (m) { m->setNodes(QList() << m_project); } if (field->properties.isEmpty()) { // default: take all - for (const QString &c : field->headerNames) { + for (const QString &c : field->headerNames) { // clazy:exclude=range-loop field->columns << c; } } else { QStringList values; - for (const QString &p : field->properties) { + for (const QString &p : field->properties) { // clazy:exclude=range-loop if (p.startsWith("values")) { QStringList vl = p.split("="); Q_ASSERT(vl.count() > 1); Q_ASSERT(vl.at(0) == "values"); - for (const QString &v : vl.at(1).split(',')) { + for (const QString &v : vl.at(1).split(',')) { // clazy:exclude=range-loop values << v.toLower().trimmed(); } } } field->columns = values; } dbgRGChart<endElement(); // office:document-content writer->endDocument(); dbgRGChart<toString(); if (!outStore.addDataToFile(buffer.buffer(), file)) { dbgRGChart<<"Failed to open"<hasLabels = e.attributeNS(KoXmlNS::chart, "data-source-has-labels"); writer.startElement(tag.constData()); writeElementAttributes(writer, e); writeChartElements(writer, e); writer.endElement(); } else if (tag == "chart:categories") { // assume this is x-axis UserField *field = m_userfields[m_activefields.last()]; int columns = field->columnCount(); if (columns > 0) { writer.startElement(tag.constData()); writeElementAttributes(writer, e, QStringList() << "table:cell-range-address"); int startRow = 1; if (field->hasLabels == "both" || field->hasLabels == "primary-x") { ++startRow; } QString end = QString("local-table.$A$%1:$A$%2").arg(startRow).arg(startRow+field->rowCount()-1); writer.addAttribute("table:cell-range-address", end); writeChartElements(writer, e); writer.endElement(); } } else if (tag == "chart:series") { UserField *field = m_userfields[m_activefields.last()]; int columns = field->columnCount(); if (columns > 0 && field->serieNr < columns) { int startRow = 1; if (field->hasLabels == "both" || field->hasLabels == "primary-x") { ++startRow; } char startColumn = 'A'; if (field->hasLabels == "both" || field->hasLabels == "primary-y") { ++startColumn; } writer.startElement(tag.constData()); writeElementAttributes(writer, e, QStringList() << "chart:values-cell-range-address"<<"chart:label-cell-address"); QChar c(startColumn + field->serieNr); QString lab = QString("local-table.$%1$1").arg(c); writer.addAttribute("chart:values-cell-address", lab); QString end = QString("local-table.$%1$%2:$%1$%3").arg(c).arg(startRow).arg(startRow+field->rowCount()-1); writer.addAttribute("chart:values-cell-range-address", end); writer.startElement("chart:data-point"); writer.addAttribute("chart:repeated", field->rowCount()); writer.endElement(); writer.endElement(); ++field->serieNr; } } else if (tag == "chart:data-point") { // do nothing handled under chart:series } else if (tag == "table:table-header-columns") { writer.startElement(tag.constData()); writer.startElement("table:table-column"); // just an empty tag writer.endElement(); writer.endElement(); } else if (tag == "table:table-columns") { writer.startElement(tag.constData()); writeChartElements(writer, e); writer.endElement(); } else if (tag == "table:table-column") { int columns = m_userfields[m_activefields.last()]->columnCount(); writer.startElement(tag.constData()); writer.addAttribute("table:number-columns-repeated", columns); writer.endElement(); } else if (tag == "table:table-header-rows") { writer.startElement(tag.constData()); writer.startElement("table:table-row"); // first column not used, just an empty cell writer.startElement("table:table-cell"); writer.startElement("text:p"); writer.endElement(); writer.endElement(); // write legends UserField *field = m_userfields[m_activefields.last()]; - for (const QString &name : field->columns) { + for (const QString &name : field->columns) { // clazy:exclude=range-loop QString value = field->headerData(name); writer.startElement("table:table-cell"); writer.addAttribute("office:value-type", "string"); writer.startElement("text:p"); writer.addTextNode(value); writer.endElement(); writer.endElement(); } writer.endElement(); writer.endElement(); } else if (tag == "table:table-rows") { writer.startElement(tag.constData()); UserField *field = m_userfields[m_activefields.last()]; int columns = field->columnCount(); if (columns > 0) { int rows = field->model.rowCount(); for (int r = 0; r < rows; ++r) { writer.startElement("table:table-row"); // first x-axis labels QDate date = QDate(field->model.headerData(r, Qt::Vertical, Qt::EditRole).toDate()); // NOTE: 1899-12-30 is the reference date, but could it depend on style? int day = QDate(1899, 12, 30).daysTo(date); writer.startElement("table:table-cell"); writer.addAttribute("office:value", day); writer.addAttribute("office:value-type", "float"); writer.startElement("text:p"); writer.addTextNode(QString::number(day)); writer.endElement(); writer.endElement(); // then the data - for (const QString &name : field->columns) { + for (const QString &name : field->columns) { // clazy:exclude=range-loop QVariant value = field->model.index(r, field->column(name)).data(); writer.startElement("table:table-cell"); writer.addAttribute("office:value-type", "float"); writer.addAttribute("office:value", value.toDouble()); writer.startElement("text:p"); writer.addTextNode(QString::number(value.toDouble())); writer.endElement(); writer.endElement(); } writer.endElement(); } } writer.endElement(); } else if (tag == "chart:legend") { writer.startElement(tag.constData()); if (!e.hasAttributeNS(KoXmlNS::chart, "legend-align") && e.hasAttributeNS(KoXmlNS::chart, "legend-position")) { // lowriter does not specify this attribute // If legend-position is start, end, top or bottom // we need to have legend-align == center so that words // repositions the legend correctly QStringList lst = QStringList() << "start" << "end" << "top" << "bottom"; if (lst.contains(e.attributeNS(KoXmlNS::chart, "legend-position"))) { writer.addAttribute("chart:legend-align", "center"); dbgRGChart<<"adding legend-align"; } } writeElementAttributes(writer, e); writeChartElements(writer, e); writer.endElement(); } else { writer.startElement(tag.constData()); writeElementAttributes(writer, e); writeChartElements(writer, e); writer.endElement(); } } } void ReportGeneratorOdt::listChildNodes(const QDomNode &parent) { QDomNodeList lst = parent.childNodes(); for (int i = 0; i < lst.count(); ++i) { if (lst.at(i).isElement()) { QDomElement e = lst.at(i).toElement(); dbgRG<<"Element:"<model.setSourceModel(model); for (int c = 0; c < this->model.columnCount(); ++c) { headerNames << this->model.headerData(c, Qt::Horizontal, role).toString().toLower(); } } int ReportGeneratorOdt::UserField::column(const QString &columnName) const { QStringList l = columnName.split('.'); int c = l.isEmpty() ? -1 : headerNames.indexOf(l.last().toLower()); dbgRGTable<<" column:"<column(column), currentIndex.parent()); } else { idx = model.index(0, this->column(column)); } QString s = "No data"; if (idx.isValid()) { s = model.data(idx).toString(); } dbgRG< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPTVIEWBASE_H #define KPTVIEWBASE_H #include "planui_export.h" #include "kptitemmodelbase.h" #include "ui_kptprintingheaderfooter.h" #include #include #include #include //#include #include #include #include #include #include #include class QMetaEnum; class QAbstractItemModel; class QDomElement; class QModelIndex; class KoDocument; class KoPrintJob; class KoPart; /// The main namespace namespace KPlato { class Project; class Node; class Resource; class ResourceGroup; class Relation; class Calendar; class ViewBase; class TreeViewBase; class DoubleTreeViewBase; //------------------ class PLANUI_EXPORT DockWidget : public QDockWidget { Q_OBJECT public: DockWidget( ViewBase *v, const QString &identity, const QString &title ); void activate( KoMainWindow *mainWindow ); void deactivate( KoMainWindow *mainWindow ); bool shown() const; bool saveXml( QDomElement &context ) const; void loadXml( const KoXmlElement &context ); const ViewBase *view; /// The view this docker belongs to QString id; /// Docker identity Qt::DockWidgetArea location; /// The area the docker should go when visible bool editor; /// Editor dockers will not be shown in read only mode public Q_SLOTS: void setShown( bool show ); void setLocation( Qt::DockWidgetArea area ); private: bool m_shown; /// The dockers visibility when the view is active }; //------------------ class PLANUI_EXPORT PrintingOptions { public: PrintingOptions() { headerOptions.group = true; headerOptions.project = Qt::Checked; headerOptions.date = Qt::Checked; headerOptions.manager = Qt::Checked; headerOptions.page = Qt::Checked; - + footerOptions.group = false; footerOptions.project = Qt::Checked; footerOptions.date = Qt::Checked; footerOptions.manager = Qt::Checked; footerOptions.page = Qt::Checked; } ~PrintingOptions() {} bool loadXml( KoXmlElement &element ); void saveXml( QDomElement &element ) const; struct Data { bool group; Qt::CheckState project; Qt::CheckState date; Qt::CheckState manager; Qt::CheckState page; }; struct Data headerOptions; struct Data footerOptions; }; //------------------ class PLANUI_EXPORT PrintingHeaderFooter : public QWidget, public Ui::PrintingHeaderFooter { Q_OBJECT public: explicit PrintingHeaderFooter( const PrintingOptions &opt, QWidget *parent = 0 ); ~PrintingHeaderFooter(); - + void setOptions( const PrintingOptions &options ); PrintingOptions options() const; Q_SIGNALS: void changed(const KPlato::PrintingOptions&); protected Q_SLOTS: void slotChanged(); private: PrintingOptions m_options; }; //------------------ class PLANUI_EXPORT PrintingDialog : public KoPrintingDialog { Q_OBJECT public: explicit PrintingDialog(ViewBase *view); ~PrintingDialog(); virtual QList createOptionWidgets() const; // virtual QList shapesOnPage(int); QRect headerRect() const; QRect footerRect() const; void paintHeaderFooter( QPainter &p, const PrintingOptions &options, int pageNumber, const Project &project ); - + PrintingOptions printingOptions() const; - + QWidget *createPageLayoutWidget() const; QAbstractPrintDialog::PrintDialogOptions printDialogOptions() const; Q_SIGNALS: void changed(const KPlato::PrintingOptions &opt); void changed(); - + public Q_SLOTS: void setPrintingOptions(const KPlato::PrintingOptions &opt); void setPrinterPageLayout( const KoPageLayout &pagelayout ); void startPrinting(KoPrintJob::RemovePolicy removePolicy = DoNotDelete) override; protected: virtual void paint( QPainter &p, const PrintingOptions::Data &options, const QRect &rect, int pageNumber, const Project &project ); int headerFooterHeight( const PrintingOptions::Data &options ) const; void drawRect( QPainter &p, const QRect &r, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge ); protected: ViewBase *m_view; PrintingHeaderFooter *m_widget; int m_textheight; }; class PLANUI_EXPORT ViewActionLists { public: ViewActionLists() : actionOptions( 0 ) {} virtual ~ViewActionLists() {} /// Returns the list of action lists that shall be plugged/unplugged virtual QStringList actionListNames() const { return m_actionListMap.keys(); } /// Returns the list of actions associated with the action list name virtual QList actionList( const QString &name ) const { return m_actionListMap[name]; } /// Add an action to the specified action list void addAction( const QString &list, QAction *action ) { m_actionListMap[list].append( action ); } virtual QList viewlistActionList() const { return m_viewlistActionList; } void addViewlistAction( QAction *action ) { m_viewlistActionList.append( action ); } QList contextActionList() const { return m_contextActionList; } void addContextAction( QAction *action ) { m_contextActionList.append( action ); } protected: /// List of all menu/toolbar actions (used for plug/unplug) QMap > m_actionListMap; /// List of actions that will be shown in the viewlist context menu QList m_viewlistActionList; - + /// List of actions that will be shown in the views header context menu QList m_contextActionList; - + // View options context menu QAction *actionOptions; }; -/** +/** ViewBase is the baseclass of all sub-views to View. */ class PLANUI_EXPORT ViewBase : public KoView, public ViewActionLists { Q_OBJECT public: enum OptionTypes { OptionExpand = 1, OptionCollapse = 2, OptionPrint = 4, OptionPrintPreview = 8, OptionPrintPdf = 16, OptionPrintConfig = 32, OptionViewConfig = 64, OptionAll = 0xffff }; /// Constructor ViewBase(KoPart *part, KoDocument *doc, QWidget *parent); /// Destructor virtual ~ViewBase(); /// Return the part (document) this view handles KoDocument *part() const; /// Return the page layout used for printing this view virtual KoPageLayout pageLayout() const; /// Return the type of view this is (class name) QString viewType() const { return metaObject()->className(); } - + /// Returns true if this view or any child widget has focus bool isActive() const; - + /// Set the project this view shall handle. virtual void setProject( Project *project ); /// Return the project virtual Project *project() const { return m_proj; } /// Return the schedule manager virtual ScheduleManager *scheduleManager() const { return m_schedulemanager; } /// Draw data from current part / project virtual void draw() {} /// Draw data from project. virtual void draw(Project &/*project*/) {} /// Draw changed data from project. virtual void drawChanges(Project &project) { draw(project); } /// Set readWrite mode virtual void updateReadWrite( bool ); bool isReadWrite() const { return m_readWrite; } /// Reimplement if your view handles nodes virtual Node* currentNode() const { return 0; } /// Reimplement if your view handles resources virtual Resource* currentResource() const { return 0; } /// Reimplement if your view handles resource groups virtual ResourceGroup* currentResourceGroup() const { return 0; } /// Reimplement if your view handles calendars virtual Calendar* currentCalendar() const { return 0; } /// Reimplement if your view handles relations virtual Relation *currentRelation() const { return 0; } /// Reimplement if your view handles zoom // virtual KoZoomController *zoomController() const { return 0; } /// Loads context info (printer settings) into this view. virtual bool loadContext( const KoXmlElement &context ); /// Save context info (printer settings) from this view. virtual void saveContext( QDomElement &context ) const; virtual KoPrintJob *createPrintJob(); PrintingOptions printingOptions() const { return m_printingOptions; } static QWidget *createPageLayoutWidget( ViewBase *view ); static PrintingHeaderFooter *createHeaderFooterWidget( ViewBase *view ); void addAction( const QString &list, QAction *action ) { ViewActionLists::addAction( list, action ); } virtual void createDockers() {} void addDocker( DockWidget *ds ); QList dockers() const; DockWidget *findDocker( const QString &id ) const; public Q_SLOTS: void setPrintingOptions(const KPlato::PrintingOptions &opt) { m_printingOptions = opt; } /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); virtual void setScheduleManager(KPlato::ScheduleManager *sm) { m_schedulemanager = sm; } void slotUpdateReadWrite( bool ); virtual void slotHeaderContextMenuRequested( const QPoint &pos ); virtual void slotEditCopy() {} virtual void slotEditCut() {} virtual void slotEditPaste() {} virtual void slotRefreshView() {} - + void setPageLayout( const KoPageLayout &layout ); Q_SIGNALS: /// Emitted when the gui has been activated or deactivated void guiActivated(KPlato::ViewBase*, bool); /// Request for a context menu popup void requestPopupMenu( const QString&, const QPoint & ); - + /// Emitted when options are modified void optionsModified(); void projectChanged(KPlato::Project *project); void readWriteChanged( bool ); void expandAll(); void collapseAll(); void openDocument(const QUrl &url); protected Q_SLOTS: virtual void slotOptions() {} virtual void slotOptionsFinished( int result ); protected: void createOptionActions(int actions); - + bool m_readWrite; PrintingOptions m_printingOptions; - + Project *m_proj; ScheduleManager *m_schedulemanager; - + KoPageLayout m_pagelayout; QList m_dockers; }; //------------------ class PLANUI_EXPORT TreeViewPrintingDialog : public PrintingDialog { Q_OBJECT public: TreeViewPrintingDialog( ViewBase *view, TreeViewBase *treeview, Project *project = 0 ); ~TreeViewPrintingDialog() {} virtual int documentFirstPage() const { return 1; } virtual int documentLastPage() const; QList createOptionWidgets() const; protected: virtual void printPage( int pageNumber, QPainter &painter ); int firstRow( int page ) const; private: TreeViewBase *m_tree; Project *m_project; int m_firstRow; }; //----------------- class PLANUI_EXPORT TreeViewBase : public QTreeView { Q_OBJECT public: explicit TreeViewBase( QWidget *parent = 0 ); void setReadWrite( bool rw ); virtual void createItemDelegates( ItemModelBase *model ); void setArrowKeyNavigation( bool on ) { m_arrowKeyNavigation = on; } bool arrowKeyNavigation() const { return m_arrowKeyNavigation; } /// Move move to first visual QModelIndex firstColumn( int row, const QModelIndex &parent ); /// Move move to last visual QModelIndex lastColumn( int row, const QModelIndex &parent ); /// Move from @p current to next item QModelIndex nextColumn( const QModelIndex ¤t ); /// Move from @p current to next item QModelIndex previousColumn( const QModelIndex ¤t ); /// Move to first editable index in @p row with @p parent QModelIndex firstEditable( int row, const QModelIndex &parent ); /// Move to last editable index in @p row with @p parent QModelIndex lastEditable( int row, const QModelIndex &parent ); void setAcceptDropsOnView( bool mode ) { m_acceptDropsOnView = mode; } virtual void setModel( QAbstractItemModel *model ); virtual void setSelectionModel( QItemSelectionModel *model ); void setStretchLastSection( bool ); void mapToSection( int column, int section ); int section( int col ) const; void setColumnsHidden( const QList &list ); /// Loads context info into this view. Reimplement. virtual bool loadContext(const QMetaEnum &map, const KoXmlElement &element, bool expand = true); /// Save context info from this view. Reimplement. virtual void saveContext(const QMetaEnum &map, QDomElement &context , bool expand = true) const; /** Reimplemented to fix qt bug 160083: Doesn't scroll horizontally. - + Scroll the contents of the tree view until the given model item \a index is visible. The \a hint parameter specifies more precisely where the item should be located after the operation. If any of the parents of the model item are collapsed, they will be expanded to ensure that the model item is visible. */ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible); void setDefaultColumns( const QList &lst ) { m_defaultColumns = lst; } QList defaultColumns() const { return m_defaultColumns; } KoPrintJob *createPrintJob( ViewBase *parent ); QModelIndex firstVisibleIndex( const QModelIndex &idx ) const; ItemModelBase *itemModel() const; void setContextMenuIndex(const QModelIndex &idx); void loadExpanded(const KoXmlElement &element); void saveExpanded(QDomElement &element, const QModelIndex &parent = QModelIndex()) const; void expandRecursivly(QDomElement element, const QModelIndex &parent = QModelIndex()); void doExpand(QDomDocument &doc); public Q_SLOTS: void slotExpand(); void slotCollapse(); Q_SIGNALS: /// Context menu requested from viewport at global position @p pos void contextMenuRequested( const QModelIndex&, const QPoint &pos, const QModelIndexList& ); /// Context menu requested from header at global position @p pos void headerContextMenuRequested( const QPoint &pos ); void moveAfterLastColumn( const QModelIndex & ); void moveBeforeFirstColumn( const QModelIndex & ); void editAfterLastColumn( const QModelIndex & ); void editBeforeFirstColumn( const QModelIndex & ); void dropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); protected: void keyPressEvent(QKeyEvent *event); void mousePressEvent( QMouseEvent *event ); + virtual void focusInEvent(QFocusEvent *event); /** Reimplemented from QTreeView to make tab/backtab in editor work reasonably well. Move the cursor in the way described by \a cursorAction, *not* using the information provided by the button \a modifiers. */ QModelIndex moveCursor( CursorAction cursorAction, Qt::KeyboardModifiers modifiers ); /// Move cursor from @p index in direction @p cursorAction. @p modifiers is not used. QModelIndex moveCursor( const QModelIndex &index, CursorAction cursorAction, Qt::KeyboardModifiers = Qt::NoModifier ); /// Move from @p index to next editable item, in direction @p cursorAction. QModelIndex moveToEditable( const QModelIndex &index, CursorAction cursorAction ); - + void contextMenuEvent ( QContextMenuEvent * event ); void dragMoveEvent(QDragMoveEvent *event); void dropEvent( QDropEvent *e ); void updateSelection( const QModelIndex &oldidx, const QModelIndex &newidx, QKeyEvent *event ); void expandRecursive(const QModelIndex &parent, bool xpand); -protected Q_SLOTS: - /// Close the @p editor, using sender()->endEditHint(). - /// Use @p hint if sender is not of type ItemDelegate. - virtual void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); - - virtual void slotCurrentChanged ( const QModelIndex & current, const QModelIndex & previous ); - void slotHeaderContextMenuRequested( const QPoint& ); - //Copied from QAbstractItemView inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const { switch (selectionBehavior()) { case QAbstractItemView::SelectRows: return QItemSelectionModel::Rows; case QAbstractItemView::SelectColumns: return QItemSelectionModel::Columns; case QAbstractItemView::SelectItems: default: return QItemSelectionModel::NoUpdate; } } +protected Q_SLOTS: + /// Close the @p editor, using sender()->endEditHint(). + /// Use @p hint if sender is not of type ItemDelegate. + virtual void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); + + virtual void slotCurrentChanged ( const QModelIndex & current, const QModelIndex & previous ); + void slotHeaderContextMenuRequested( const QPoint& ); + void doContextExpanded(); void doExpanded(); protected: - virtual void focusInEvent(QFocusEvent *event); - bool m_arrowKeyNavigation; bool m_acceptDropsOnView; QList m_hideList; bool m_readWrite; QList m_defaultColumns; QPersistentModelIndex m_contextMenuIndex; QDomDocument m_loadContextDoc; QDomDocument m_expandDoc; }; //------------------ class PLANUI_EXPORT DoubleTreeViewPrintingDialog : public PrintingDialog { Q_OBJECT public: DoubleTreeViewPrintingDialog( ViewBase *view, DoubleTreeViewBase *treeview, Project *project ); ~DoubleTreeViewPrintingDialog() {} virtual int documentFirstPage() const { return 1; } virtual int documentLastPage() const; QList createOptionWidgets() const; protected: virtual void printPage( int pageNumber, QPainter &painter ); - + int firstRow( int page ) const; - + private: DoubleTreeViewBase *m_tree; Project *m_project; int m_firstRow; }; class PLANUI_EXPORT DoubleTreeViewBase : public QSplitter { Q_OBJECT public: explicit DoubleTreeViewBase( QWidget *parent ); DoubleTreeViewBase( bool mode, QWidget *parent ); ~DoubleTreeViewBase(); void setReadWrite( bool rw ); void closePersistentEditor( const QModelIndex &index ); void setModel( QAbstractItemModel *model ); QAbstractItemModel *model() const; void setArrowKeyNavigation( bool on ) { m_arrowKeyNavigation = on; } bool arrowKeyNavigation() const { return m_arrowKeyNavigation; } QItemSelectionModel *selectionModel() const { return m_selectionmodel; } void setSelectionModel( QItemSelectionModel *model ); void setSelectionMode( QAbstractItemView::SelectionMode mode ); void setSelectionBehavior( QAbstractItemView::SelectionBehavior mode ); virtual void createItemDelegates( ItemModelBase *model ); void setItemDelegateForColumn( int col, QAbstractItemDelegate * delegate ); void setEditTriggers ( QAbstractItemView::EditTriggers ); QAbstractItemView::EditTriggers editTriggers() const; void setAcceptDrops( bool ); void setAcceptDropsOnView( bool ); void setDropIndicatorShown( bool ); void setDragDropMode( QAbstractItemView::DragDropMode mode ); void setDragDropOverwriteMode( bool mode ); void setDragEnabled ( bool mode ); void setDefaultDropAction( Qt::DropAction action ); void setStretchLastSection( bool ); - + /// Hide columns in the @p hideList, show all other columns. /// If the hideList.last() == -1, the rest of the columns are hidden. void hideColumns( TreeViewBase *view, const QList &hideList ); void hideColumns( const QList &masterList, const QList &slaveList = QList() ); void hideColumn( int col ) { - m_leftview->hideColumn( col ); + m_leftview->hideColumn( col ); if ( m_rightview ) m_rightview->hideColumn( col ); } - void showColumn( int col ) { + void showColumn( int col ) { if ( col == 0 || m_rightview == 0 ) m_leftview->showColumn( col ); else m_rightview->showColumn( col ); } bool isColumnHidden( int col ) const { return m_rightview ? m_rightview->isColumnHidden( col ) : m_leftview->isColumnHidden( col ); } TreeViewBase *masterView() const { return m_leftview; } TreeViewBase *slaveView() const { return m_rightview; } /// Loads context info into this view. Reimplement. virtual bool loadContext( const QMetaEnum &map, const KoXmlElement &element ); /// Save context info from this view. Reimplement. virtual void saveContext( const QMetaEnum &map, QDomElement &context ) const; - + void setViewSplitMode( bool split ); bool isViewSplit() const { return m_mode; } QAction *actionSplitView() const { return m_actionSplitView; } - + void setRootIsDecorated ( bool show ); KoPrintJob *createPrintJob( ViewBase *parent ); void setStretchFactors(); - + QModelIndex indexAt( const QPoint &pos ) const; void setParentsExpanded( const QModelIndex &idx, bool expanded ); void setSortingEnabled( bool on ) { m_leftview->setSortingEnabled( on ); m_rightview->setSortingEnabled( on ); } void sortByColumn( int col, Qt::SortOrder order = Qt::AscendingOrder ) { - if ( ! m_leftview->isColumnHidden( col ) || + if ( ! m_leftview->isColumnHidden( col ) || ! m_rightview->isVisible() || m_rightview->isColumnHidden( col ) ) { m_leftview->sortByColumn( col, order ); } else { m_rightview->sortByColumn( col, order ); } } void setContextMenuIndex(const QModelIndex &idx); Q_SIGNALS: /// Context menu requested from the viewport, pointer over @p index at global position @p pos void contextMenuRequested( const QModelIndex &index, const QPoint& pos, const QModelIndexList& ); /// Context menu requested from master- or slave header at global position @p pos void headerContextMenuRequested( const QPoint &pos ); /// Context menu requested from master header at global position @p pos void masterHeaderContextMenuRequested( const QPoint &pos ); /// Context menu requested from slave header at global position @p pos void slaveHeaderContextMenuRequested( const QPoint &pos ); void currentChanged ( const QModelIndex & current, const QModelIndex & previous ); void selectionChanged( const QModelIndexList& ); void dropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); public Q_SLOTS: void edit( const QModelIndex &index ); void slotExpand(); void slotCollapse(); protected Q_SLOTS: void slotSelectionChanged( const QItemSelection &sel, const QItemSelection & ); void slotToRightView( const QModelIndex &index ); void slotToLeftView( const QModelIndex &index ); void slotEditToRightView( const QModelIndex &index ); void slotEditToLeftView( const QModelIndex &index ); void slotRightHeaderContextMenuRequested( const QPoint &pos ); void slotLeftHeaderContextMenuRequested( const QPoint &pos ); void slotLeftSortIndicatorChanged( int logicalIndex, Qt::SortOrder order ); void slotRightSortIndicatorChanged( int logicalIndex, Qt::SortOrder order ); protected: void init(); QList expandColumnList( const QList &lst ) const; protected: TreeViewBase *m_leftview; TreeViewBase *m_rightview; QItemSelectionModel *m_selectionmodel; bool m_arrowKeyNavigation; bool m_readWrite; bool m_mode; - + QAction *m_actionSplitView; }; } // namespace KPlato #endif