diff --git a/examples/xlsx/conditionalformatting/main.cpp b/examples/xlsx/conditionalformatting/main.cpp index 59908bc..7beab28 100644 --- a/examples/xlsx/conditionalformatting/main.cpp +++ b/examples/xlsx/conditionalformatting/main.cpp @@ -11,7 +11,10 @@ int main() Format hFmt; hFmt.setFontBold(true); xlsx.write("B1", "(-inf,40)", hFmt); - xlsx.write("D1", "[30,70]", hFmt); + xlsx.write("C1", "[30,70]", hFmt); + xlsx.write("D1", "startsWith 2", hFmt); + xlsx.write("E1", "dataBar", hFmt); + xlsx.write("F1", "colorScale", hFmt); for (int row=3; row<22; ++row) { for (int col=2; col<22; ++col) @@ -19,7 +22,7 @@ int main() } //![0] - //![1] + //![cf1] ConditionalFormatting cf1; Format fmt1; fmt1.setFontColor(Qt::green); @@ -27,7 +30,7 @@ int main() cf1.addHighlightCellsRule(ConditionalFormatting::Highlight_LessThan, "40", fmt1); cf1.addRange("B3:B21"); xlsx.addConditionalFormatting(cf1); - //![1] + //![cf1] //![cf2] ConditionalFormatting cf2; @@ -35,7 +38,7 @@ int main() fmt2.setBorderStyle(Format::BorderDotted); fmt2.setBorderColor(Qt::blue); cf2.addHighlightCellsRule(ConditionalFormatting::Highlight_Between, "30", "70", fmt2); - cf2.addRange("D3:D21"); + cf2.addRange("C3:C21"); xlsx.addConditionalFormatting(cf2); //![cf2] @@ -43,11 +46,26 @@ int main() ConditionalFormatting cf3; Format fmt3; fmt3.setFontStrikeOut(true); + fmt3.setFontBold(true); cf3.addHighlightCellsRule(ConditionalFormatting::Highlight_BeginsWith, "2", fmt3); - cf3.addRange("F3:F21"); + cf3.addRange("D3:D21"); xlsx.addConditionalFormatting(cf3); //![cf3] + //![cf4] + ConditionalFormatting cf4; + cf4.addDataBarRule(Qt::blue); + cf4.addRange("E3:E21"); + xlsx.addConditionalFormatting(cf4); + //![cf4] + + //![cf5] + ConditionalFormatting cf5; + cf5.add2ColorScaleRule(Qt::blue, Qt::red); + cf5.addRange("F3:F21"); + xlsx.addConditionalFormatting(cf5); + //![cf5] + //![2] xlsx.save(); //![2] diff --git a/src/xlsx/xlsxconditionalformatting.cpp b/src/xlsx/xlsxconditionalformatting.cpp index ff35cd2..b7a939b 100644 --- a/src/xlsx/xlsxconditionalformatting.cpp +++ b/src/xlsx/xlsxconditionalformatting.cpp @@ -50,6 +50,41 @@ ConditionalFormattingPrivate::~ConditionalFormattingPrivate() } +void ConditionalFormattingPrivate::writeCfVo(const XlsxCfVoData &cfvo, QXmlStreamWriter &writer) const +{ + writer.writeEmptyElement(QStringLiteral("cfvo")); + QString type; + switch(cfvo.type) { + case ConditionalFormatting::VOT_Formula: type=QStringLiteral("formula"); break; + case ConditionalFormatting::VOT_Max: type=QStringLiteral("max"); break; + case ConditionalFormatting::VOT_Min: type=QStringLiteral("min"); break; + case ConditionalFormatting::VOT_Num: type=QStringLiteral("num"); break; + case ConditionalFormatting::VOT_Percent: type=QStringLiteral("percent"); break; + case ConditionalFormatting::VOT_Percentile: type=QStringLiteral("percentile"); break; + default: break; + } + writer.writeAttribute(QStringLiteral("type"), type); + writer.writeAttribute(QStringLiteral("val"), cfvo.value); + if (!cfvo.gte) + writer.writeAttribute(QStringLiteral("gte"), QStringLiteral("0")); +} + +void ConditionalFormattingPrivate::writeColor(const QColor &color, QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("color")); + if (color.isValid()) { + writer.writeAttribute(QStringLiteral("rgb"), QStringLiteral("FF")+color.name().mid(1)); //remove # +// } else if (!themeColor.isEmpty()) { +// QStringList themes = themeColor.split(QLatin1Char(':')); +// writer.writeAttribute(QStringLiteral("theme"), themes[0]); +// if (!themes[1].isEmpty()) +// writer.writeAttribute(QStringLiteral("tint"), themes[1]); + } else { + writer.writeAttribute(QStringLiteral("auto"), QStringLiteral("1")); + } + writer.writeEndElement();//color +} + /*! * \class ConditionalFormatting * \brief Conditional formatting for single cell or ranges @@ -102,7 +137,7 @@ ConditionalFormattingPrivate::~ConditionalFormattingPrivate() \value Highlight_BelowStdDev2 \value Highlight_BelowStdDev3 - \value Highlight_SatisfyFormula + \value Highlight_Expression */ /*! @@ -150,7 +185,7 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const if (format.isEmpty()) return false; - bool skipFormula1 = false; + bool skipFormula = false; QSharedPointer cfRule(new XlsxCfRuleData); if (type >= Highlight_LessThan && type <= Highlight_NotBetween) { @@ -187,7 +222,7 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("RIGHT(%2,LEN(\"%1\"))=\"%1\"").arg(formula1); } cfRule->attrs[XlsxCfRuleData::A_text] = formula1; - skipFormula1 = true; + skipFormula = true; } else if (type == Highlight_TimePeriod) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("timePeriod"); //:Todo @@ -199,19 +234,19 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const } else if (type == Highlight_Errors) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("containsErrors"); cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("ISERROR(%1)"); - skipFormula1 = true; + skipFormula = true; } else if (type == Highlight_NoErrors) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("notContainsErrors"); cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("NOT(ISERROR(%1))"); - skipFormula1 = true; + skipFormula = true; } else if (type == Highlight_Blanks) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("containsBlanks"); cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("LEN(TRIM(%1))=0"); - skipFormula1 = true; + skipFormula = true; } else if (type == Highlight_NoBlanks) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("notContainsBlanks"); cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("LEN(TRIM(%1))>0"); - skipFormula1 = true; + skipFormula = true; } else if (type >= Highlight_Top && type <= Highlight_BottomPercent) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("top10"); if (type == Highlight_Bottom || type == Highlight_BottomPercent) @@ -219,7 +254,7 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const if (type == Highlight_TopPercent || type == Highlight_BottomPercent) cfRule->attrs[XlsxCfRuleData::A_percent] = QStringLiteral("1"); cfRule->attrs[XlsxCfRuleData::A_rank] = !formula1.isEmpty() ? formula1 : QStringLiteral("10"); - skipFormula1 = true; + skipFormula = true; } else if (type >= Highlight_AboveAverage && type <= Highlight_BelowStdDev3) { cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("aboveAverage"); if (type >= Highlight_BelowAverage && type <= Highlight_BelowStdDev3) @@ -232,6 +267,8 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const cfRule->attrs[XlsxCfRuleData::A_stdDev] = QStringLiteral("2"); else if (type == Highlight_AboveStdDev3 || type == Highlight_BelowStdDev3) cfRule->attrs[XlsxCfRuleData::A_stdDev] = QStringLiteral("3"); + } else if (type == Highlight_Expression){ + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("expression"); } else { return false; } @@ -239,11 +276,12 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const cfRule->dxfFormat = format; if (stopIfTrue) cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; - if (!formula1.isEmpty() && !skipFormula1) - cfRule->attrs[XlsxCfRuleData::A_formula1] = formula1.startsWith(QLatin1String("=")) ? formula1.mid(1) : formula1; - if (!formula2.isEmpty()) - cfRule->attrs[XlsxCfRuleData::A_formula2] = formula2.startsWith(QLatin1String("=")) ? formula2.mid(1) : formula2; - + if (!skipFormula) { + if (!formula1.isEmpty()) + cfRule->attrs[XlsxCfRuleData::A_formula1] = formula1.startsWith(QLatin1String("=")) ? formula1.mid(1) : formula1; + if (!formula2.isEmpty()) + cfRule->attrs[XlsxCfRuleData::A_formula2] = formula2.startsWith(QLatin1String("=")) ? formula2.mid(1) : formula2; + } d->cfRules.append(cfRule); return true; } @@ -276,6 +314,99 @@ bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const return addHighlightCellsRule(type, formula, QString(), format, stopIfTrue); } +/*! + * Add a dataBar rule with the given \a color, \a type1, \a val1 + * , \a type2, \a val2, \a showData and \a stopIfTrue. + */ +bool ConditionalFormatting::addDataBarRule(const QColor &color, ValueObjectType type1, const QString &val1, ValueObjectType type2, const QString &val2, bool showData, bool stopIfTrue) +{ + QSharedPointer cfRule(new XlsxCfRuleData); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("dataBar"); + cfRule->attrs[XlsxCfRuleData::A_color1] = color; + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + if (!showData) + cfRule->attrs[XlsxCfRuleData::A_hideData] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + + d->cfRules.append(cfRule); + return true; +} + +/*! + * \overload + * Add a dataBar rule with the given \a color, \a showData and \a stopIfTrue. + */ +bool ConditionalFormatting::addDataBarRule(const QColor &color, bool showData, bool stopIfTrue) +{ + return addDataBarRule(color, VOT_Min, QStringLiteral("0"), VOT_Max, QStringLiteral("0"), showData, stopIfTrue); +} + +/*! + * Add a colorScale rule with the given \a minColor, \a maxColor and \a stopIfTrue. + */ +bool ConditionalFormatting::add2ColorScaleRule(const QColor &minColor, const QColor &maxColor, bool stopIfTrue) +{ + ValueObjectType type1 = VOT_Min; + ValueObjectType type2 = VOT_Max; + QString val1 = QStringLiteral("0"); + QString val2 = QStringLiteral("0"); + + QSharedPointer cfRule(new XlsxCfRuleData); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("colorScale"); + cfRule->attrs[XlsxCfRuleData::A_color1] = minColor; + cfRule->attrs[XlsxCfRuleData::A_color2] = maxColor; + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + + d->cfRules.append(cfRule); + return true; +} + +/*! + * Add a colorScale rule with the given \a minColor, \a midColor, \a maxColor and \a stopIfTrue. + */ +bool ConditionalFormatting::add3ColorScaleRule(const QColor &minColor, const QColor &midColor, const QColor &maxColor, bool stopIfTrue) +{ + ValueObjectType type1 = VOT_Min; + ValueObjectType type2 = VOT_Percent; + ValueObjectType type3 = VOT_Max; + QString val1 = QStringLiteral("0"); + QString val2 = QStringLiteral("50"); + QString val3 = QStringLiteral("0"); + + QSharedPointer cfRule(new XlsxCfRuleData); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("colorScale"); + cfRule->attrs[XlsxCfRuleData::A_color1] = minColor; + cfRule->attrs[XlsxCfRuleData::A_color2] = midColor; + cfRule->attrs[XlsxCfRuleData::A_color3] = maxColor; + + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + XlsxCfVoData cfvo3(type3, val3); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + cfRule->attrs[XlsxCfRuleData::A_cfvo3] = QVariant::fromValue(cfvo3); + + d->cfRules.append(cfRule); + return true; +} + /*! Returns the ranges on which the validation will be applied. */ @@ -371,6 +502,30 @@ bool ConditionalFormatting::saveToXml(QXmlStreamWriter &writer) const if (rule->attrs.contains(XlsxCfRuleData::A_equalAverage)) writer.writeAttribute(QStringLiteral("equalAverage"), rule->attrs[XlsxCfRuleData::A_equalAverage].toString()); + if (rule->attrs[XlsxCfRuleData::A_type] == QLatin1String("dataBar")) { + writer.writeStartElement(QStringLiteral("dataBar")); + if (rule->attrs.contains(XlsxCfRuleData::A_hideData)) + writer.writeAttribute(QStringLiteral("showValue"), QStringLiteral("0")); + d->writeCfVo(rule->attrs[XlsxCfRuleData::A_cfvo1].value(), writer); + d->writeCfVo(rule->attrs[XlsxCfRuleData::A_cfvo2].value(), writer); + d->writeColor(rule->attrs[XlsxCfRuleData::A_color1].value(), writer); + writer.writeEndElement();//dataBar + } else if (rule->attrs[XlsxCfRuleData::A_type] == QLatin1String("colorScale")) { + writer.writeStartElement(QStringLiteral("colorScale")); + d->writeCfVo(rule->attrs[XlsxCfRuleData::A_cfvo1].value(), writer); + d->writeCfVo(rule->attrs[XlsxCfRuleData::A_cfvo2].value(), writer); + if (rule->attrs.contains(XlsxCfRuleData::A_cfvo3)) + d->writeCfVo(rule->attrs[XlsxCfRuleData::A_cfvo3].value(), writer); + + d->writeColor(rule->attrs[XlsxCfRuleData::A_color1].value(), writer); + d->writeColor(rule->attrs[XlsxCfRuleData::A_color2].value(), writer); + if (rule->attrs.contains(XlsxCfRuleData::A_color3)) + d->writeColor(rule->attrs[XlsxCfRuleData::A_color3].value(), writer); + + writer.writeEndElement();//colorScale + } + + if (rule->attrs.contains(XlsxCfRuleData::A_formula1_temp)) { QString startCell = ranges()[0].toString().split(QLatin1Char(':'))[0]; writer.writeTextElement(QStringLiteral("formula"), rule->attrs[XlsxCfRuleData::A_formula1_temp].toString().arg(startCell)); diff --git a/src/xlsx/xlsxconditionalformatting.h b/src/xlsx/xlsxconditionalformatting.h index 9857ea5..0644873 100644 --- a/src/xlsx/xlsxconditionalformatting.h +++ b/src/xlsx/xlsxconditionalformatting.h @@ -32,6 +32,7 @@ class QXmlStreamReader; class QXmlStreamWriter; +class QColor; class ConditionalFormattingTest; QT_BEGIN_NAMESPACE_XLSX @@ -87,6 +88,16 @@ public: Highlight_Expression }; + enum ValueObjectType + { + VOT_Formula, + VOT_Max, + VOT_Min, + VOT_Num, + VOT_Percent, + VOT_Percentile + }; + ConditionalFormatting(); ConditionalFormatting(const ConditionalFormatting &other); ~ConditionalFormatting(); @@ -94,6 +105,10 @@ public: bool addHighlightCellsRule(HighlightRuleType type, const Format &format, bool stopIfTrue=false); bool addHighlightCellsRule(HighlightRuleType type, const QString &formula1, const Format &format, bool stopIfTrue=false); bool addHighlightCellsRule(HighlightRuleType type, const QString &formula1, const QString &formula2, const Format &format, bool stopIfTrue=false); + bool addDataBarRule(const QColor &color, bool showData=true, bool stopIfTrue=false); + bool addDataBarRule(const QColor &color, ValueObjectType type1, const QString &val1, ValueObjectType type2, const QString &val2, bool showData=true, bool stopIfTrue=false); + bool add2ColorScaleRule(const QColor &minColor, const QColor &maxColor, bool stopIfTrue=false); + bool add3ColorScaleRule(const QColor &minColor, const QColor &midColor, const QColor &maxColor, bool stopIfTrue=false); QList ranges() const; diff --git a/src/xlsx/xlsxconditionalformatting_p.h b/src/xlsx/xlsxconditionalformatting_p.h index 495de2c..03cb94b 100644 --- a/src/xlsx/xlsxconditionalformatting_p.h +++ b/src/xlsx/xlsxconditionalformatting_p.h @@ -45,6 +45,24 @@ QT_BEGIN_NAMESPACE_XLSX +class XlsxCfVoData +{ +public: + XlsxCfVoData() + :gte(true) + { + } + + XlsxCfVoData(ConditionalFormatting::ValueObjectType type, const QString &value, bool gte=true) + :type(type), value(value), gte(gte) + { + } + + ConditionalFormatting::ValueObjectType type; + QString value; + bool gte; +}; + class XlsxCfRuleData { public: @@ -67,7 +85,17 @@ public: A_formula1, A_formula2, A_formula3, - A_formula1_temp + A_formula1_temp, + + A_color1, + A_color2, + A_color3, + + A_cfvo1, + A_cfvo2, + A_cfvo3, + + A_hideData }; XlsxCfRuleData() @@ -86,9 +114,14 @@ public: ConditionalFormattingPrivate(const ConditionalFormattingPrivate &other); ~ConditionalFormattingPrivate(); + void writeCfVo(const XlsxCfVoData& cfvo, QXmlStreamWriter &writer) const; + void writeColor(const QColor &color, QXmlStreamWriter &writer) const; + QList >cfRules; QList ranges; }; QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::XlsxCfVoData) #endif // XLSXCONDITIONALFORMATTING_P_H diff --git a/tests/auto/xlsxconditionalformatting/tst_conditionalformattingtest.cpp b/tests/auto/xlsxconditionalformatting/tst_conditionalformattingtest.cpp index 7781fb4..21fde11 100644 --- a/tests/auto/xlsxconditionalformatting/tst_conditionalformattingtest.cpp +++ b/tests/auto/xlsxconditionalformatting/tst_conditionalformattingtest.cpp @@ -19,6 +19,7 @@ public: private Q_SLOTS: void testHighlightRules(); void testHighlightRules_data(); + void testDataBarRules(); }; ConditionalFormattingTest::ConditionalFormattingTest() @@ -78,6 +79,25 @@ void ConditionalFormattingTest::testHighlightRules() QVERIFY(buffer.buffer().contains(result)); } +void ConditionalFormattingTest::testDataBarRules() +{ + ConditionalFormatting cf; + cf.addDataBarRule(Qt::blue); + cf.addRange("C3:C10"); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QXmlStreamWriter writer(&buffer); + cf.saveToXml(writer); + qDebug()<" + "" + "" + "" + ""; + QVERIFY(buffer.buffer().contains(res)); +} + QTEST_APPLESS_MAIN(ConditionalFormattingTest) #include "tst_conditionalformattingtest.moc"