File indexing completed on 2024-05-12 16:35:41

0001 /* This file is part of the KDE project
0002    Copyright 1998-2016 The Calligra Team <calligra-devel@kde.org>
0003    Copyright 2016 Tomas Mecir <mecirt@gmail.com>
0004    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0005    Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0006    Copyright 2007 Thorsten Zachmann <zachmann@kde.org>
0007    Copyright 2005-2006 Inge Wallin <inge@lysator.liu.se>
0008    Copyright 2004 Ariya Hidayat <ariya@kde.org>
0009    Copyright 2002-2003 Norbert Andres <nandres@web.de>
0010    Copyright 2000-2002 Laurent Montel <montel@kde.org>
0011    Copyright 2002 John Dailey <dailey@vt.edu>
0012    Copyright 2002 Phillip Mueller <philipp.mueller@gmx.de>
0013    Copyright 2000 Werner Trobin <trobin@kde.org>
0014    Copyright 1999-2000 Simon Hausmann <hausmann@kde.org>
0015    Copyright 1999 David Faure <faure@kde.org>
0016    Copyright 1998-2000 Torben Weis <weis@kde.org>
0017 
0018    This library is free software; you can redistribute it and/or
0019    modify it under the terms of the GNU Library General Public
0020    License as published by the Free Software Foundation; either
0021    version 2 of the License, or (at your option) any later version.
0022 
0023    This library is distributed in the hope that it will be useful,
0024    but WITHOUT ANY WARRANTY; without even the implied warranty of
0025    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0026    Library General Public License for more details.
0027 
0028    You should have received a copy of the GNU Library General Public License
0029    along with this library; see the file COPYING.LIB.  If not, write to
0030    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0031    Boston, MA 02110-1301, USA.
0032 */
0033 
0034 #include "SheetsOdf.h"
0035 #include "SheetsOdfPrivate.h"
0036 
0037 #include <klocale.h>
0038 
0039 #include <KoGenStyle.h>
0040 #include <KoGenStyles.h>
0041 #include <KoOdfStylesReader.h>
0042 #include <KoXmlReader.h>
0043 #include <KoXmlNS.h>
0044 #include <KoOdfGraphicStyles.h>
0045 #include <KoOdfWorkaround.h>
0046 #include <KoStyleStack.h>
0047 #include <KoUnit.h>
0048 
0049 #include "CalculationSettings.h"
0050 #include "Condition.h"
0051 #include "Map.h"
0052 #include "StyleManager.h"
0053 
0054 // This file contains functionality to load/save styles
0055 
0056 namespace Calligra {
0057 namespace Sheets {
0058 
0059 namespace Odf {
0060 
0061     // Single style loading
0062     void loadStyle(Style *style, KoOdfStylesReader& stylesReader, const KoXmlElement& element,
0063                       Conditions& conditions, const StyleManager* styleManager,
0064                       const ValueParser *parser);
0065     void loadCustomStyle(CustomStyle *style, KoOdfStylesReader& stylesReader, const KoXmlElement& xmlstyle,
0066                           const QString& name, Conditions& conditions,
0067                           const StyleManager* styleManager, const ValueParser *parser);
0068 
0069     // Single style saving
0070     void saveStyle(const Style *style, const QSet<Style::Key>& subStyles, KoGenStyle &xmlstyle,
0071                       KoGenStyles &mainStyles, const StyleManager* manager);
0072     QString saveCustomStyle(CustomStyle *style, KoGenStyle& genstyle, KoGenStyles &mainStyles,
0073                              const StyleManager* manager);
0074 
0075     void loadParagraphProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack);
0076     void loadTableCellProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack);
0077     void loadTextProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack);
0078 
0079     /**
0080      * @return the name of the data style (number, currency, percentage, date,
0081      * boolean, text)
0082      */
0083     QString saveStyleNumeric(KoGenStyle &style, KoGenStyles &mainStyles, Format::Type _style,
0084                                        const QString &_prefix, const QString &_postfix, int _precision, const QString& symbol,
0085                                        bool thousandsSep);
0086     QString saveStyleNumericDate(KoGenStyles &mainStyles, Format::Type _style,
0087                                            const QString &_prefix, const QString &_suffix);
0088     QString saveStyleNumericFraction(KoGenStyles &mainStyles, Format::Type _style,
0089             const QString &_prefix, const QString &_suffix);
0090     QString saveStyleNumericTime(KoGenStyles& mainStyles, Format::Type _style,
0091                                            const QString &_prefix, const QString &_suffix);
0092     QString saveStyleNumericCustom(KoGenStyles&mainStyles, Format::Type _style,
0093             const QString &_prefix, const QString &_suffix);
0094     QString saveStyleNumericScientific(KoGenStyles&mainStyles, Format::Type _style,
0095             const QString &_prefix, const QString &_suffix, int _precision, bool thousandsSep);
0096     QString saveStyleNumericPercentage(KoGenStyles&mainStyles, Format::Type _style, int _precision,
0097             const QString &_prefix, const QString &_suffix);
0098     QString saveStyleNumericMoney(KoGenStyles&mainStyles, Format::Type _style,
0099                                             const QString& symbol, int _precision,
0100                                             const QString &_prefix, const QString &_suffix);
0101     QString saveStyleNumericText(KoGenStyles&mainStyles, Format::Type _style, int _precision,
0102                                            const QString &_prefix, const QString &_suffix);
0103     QString saveStyleNumericNumber(KoGenStyles&mainStyles, Format::Type _style, int _precision,
0104             const QString &_prefix, const QString &_suffix, bool thousandsSep);
0105     QString saveBackgroundStyle(KoGenStyles &mainStyles, const QBrush &brush);
0106 
0107     // Helpers
0108     Format::Type dateType(const QString&);
0109     Format::Type timeType(const QString&);
0110     Format::Type fractionType(const QString&);
0111     Format::Type numberType(const QString&);
0112     Currency numberCurrency(const QString&);
0113     QPen decodePen(const QString &border);
0114     QString encodePen(const QPen &pen);
0115     /**
0116      * Returns the name of a color.  This is the same as returned by QColor::name, but an internal cache
0117      * is used to reduce the overhead when asking for the name of the same color.
0118      */
0119     QString colorName(const QColor& color);
0120 }
0121 
0122 void Odf::loadStyleTemplate(StyleManager *styles, KoOdfStylesReader& stylesReader, Map* map)
0123 {
0124     // reset the map of OpenDocument Styles
0125     styles->clearOasisStyles();
0126     QHash<QString, QString> oasisStyles;
0127 
0128     // loading default style first
0129     const KoXmlElement* defStyle = stylesReader.defaultStyle("table-cell");
0130     if (defStyle) {
0131         debugSheetsODF << "StyleManager: Loading default cell style";
0132         Conditions conditions;
0133         loadCustomStyle(styles->defaultStyle(), stylesReader, *defStyle, "Default", conditions, styles, map->parser());
0134         styles->defaultStyle()->setType(Style::BUILTIN);
0135         if (map) {
0136             // Load the default precision to be used, if the (default) cell style
0137             // is set to arbitrary precision.
0138             KoXmlNode n = defStyle->firstChild();
0139             while (!n.isNull()) {
0140                 if (n.isElement() &&
0141                         n.namespaceURI() == KoXmlNS::style &&
0142                         n.localName() == "table-cell-properties") {
0143                     KoXmlElement e = n.toElement();
0144                     if (n.toElement().hasAttributeNS(KoXmlNS::style, "decimal-places")) {
0145                         bool ok;
0146                         const int precision = n.toElement().attributeNS(KoXmlNS::style, "decimal-places").toInt(&ok);
0147                         if (ok && precision > -1) {
0148                             debugSheetsODF << "Default decimal precision:" << precision;
0149                             map->calculationSettings()->setDefaultDecimalPrecision(precision);
0150                         }
0151                     }
0152                 }
0153                 n = n.nextSibling();
0154             }
0155         }
0156     } else
0157         styles->resetDefaultStyle();
0158 
0159     QList<KoXmlElement*> customStyles(stylesReader.customStyles("table-cell").values());
0160     uint nStyles = customStyles.count();
0161     for (unsigned int item = 0; item < nStyles; item++) {
0162         KoXmlElement* styleElem = customStyles[item];
0163         if (!styleElem) continue;
0164 
0165         // assume the name assigned by the application
0166         const QString oasisName = styleElem->attributeNS(KoXmlNS::style, "name", QString());
0167 
0168         // then replace by user-visible one (if any)
0169         const QString name = styleElem->attributeNS(KoXmlNS::style, "display-name", oasisName);
0170         debugSheetsODF << " StyleManager: Loading common cell style:" << oasisName << " (display name:" << name << ")";
0171 
0172         if (!name.isEmpty()) {
0173             // The style's parent name will be set in loadStyle(..).
0174             // After all styles are loaded the pointer to the parent is set.
0175             CustomStyle * style = new CustomStyle(name);
0176 
0177             Conditions conditions;
0178             loadCustomStyle(style, stylesReader, *styleElem, name, conditions, styles, map->parser());
0179             // TODO Stefan: conditions
0180             styles->insertStyle(style);
0181             // insert it into the map sorted the OpenDocument name
0182             styles->defineOasisStyle(oasisName, style->name());  // TODO: is this actually needed?
0183             oasisStyles[oasisName] = style->name();
0184             debugSheetsODF << "Style" << style->name() << ":" << style;
0185         }
0186     }
0187 
0188     // replace all OpenDocument internal parent names by Calligra Sheets' style names
0189     QStringList styleNames = styles->styleNames(false);
0190 
0191     foreach (QString name, styleNames)
0192     {
0193         CustomStyle *style = styles->style (name);
0194 
0195         if (!style->parentName().isNull()) {
0196             const QString parentOdfName = style->parentName();
0197             const CustomStyle* parentStyle = styles->style(oasisStyles.value(parentOdfName));
0198             if (!parentStyle) {
0199                 warnSheetsODF << parentOdfName << " not found.";
0200                 continue;
0201             }
0202             style->setParentName(oasisStyles.value(parentOdfName));
0203             debugSheetsODF << style->name() << " (" << style << ") gets" << style->parentName() << " (" << parentOdfName << ") as parent.";
0204         } else {
0205             style->setParentName("Default");
0206             debugSheetsODF << style->name() << " (" << style << ") has" << style->parentName() << " as parent.";
0207         }
0208     }
0209 }
0210 
0211 Styles Odf::loadAutoStyles(StyleManager *styles, KoOdfStylesReader& stylesReader,
0212                                        QHash<QString, Conditions>& conditionalStyles,
0213                                        const ValueParser *parser)
0214 {
0215     Styles autoStyles;
0216     foreach(KoXmlElement* element, stylesReader.autoStyles("table-cell")) {
0217         if (element->hasAttributeNS(KoXmlNS::style , "name")) {
0218             QString name = element->attributeNS(KoXmlNS::style , "name" , QString());
0219             debugSheetsODF << "StyleManager: Preloading automatic cell style:" << name;
0220             autoStyles.remove(name);
0221             Conditions conditions;
0222             loadStyle(&autoStyles[name], stylesReader, *(element), conditions, styles, parser);
0223             if (!conditions.isEmpty()) {
0224                 debugSheets << "\t\tCONDITIONS";
0225                 conditionalStyles[name] = conditions;
0226             }
0227 
0228             if (element->hasAttributeNS(KoXmlNS::style, "parent-style-name")) {
0229                 const QString parentOdfName = element->attributeNS(KoXmlNS::style, "parent-style-name", QString());
0230                 const CustomStyle* parentStyle = styles->style(styles->openDocumentName(parentOdfName));
0231                 if (!parentStyle) {
0232                     warnSheetsODF << parentOdfName << " not found.";
0233                     continue;
0234                 }
0235                 autoStyles[name].setParentName(parentStyle->name());
0236                 debugSheetsODF << "\t parent-style-name:" << autoStyles[name].parentName();
0237             }
0238         }
0239     }
0240     return autoStyles;
0241 }
0242 
0243 void Odf::saveStyles(StyleManager *manager, KoGenStyles &mainStyles)
0244 {
0245     debugSheetsODF << "StyleManager: Saving default cell style";
0246     KoGenStyle defStyle = KoGenStyle(KoGenStyle::TableCellStyle, "table-cell");
0247     saveStyle(manager->defaultStyle(), defStyle, mainStyles, manager);
0248 
0249     manager->clearOasisStyles();
0250     QStringList styleNames = manager->styleNames(false);
0251 
0252     foreach (QString name, styleNames)
0253     {
0254         CustomStyle *style = manager->style (name);
0255         debugSheetsODF << "StyleManager: Saving common cell style" << name;
0256         KoGenStyle customStyle = KoGenStyle(KoGenStyle::TableCellStyle, "table-cell");
0257         const QString oasisName = saveCustomStyle(style, customStyle, mainStyles, manager);
0258         manager->defineOasisStyle(style->name(), oasisName);
0259     }
0260 }
0261 
0262 
0263 // Single style loading
0264 
0265 void Odf::loadStyle(Style *style, KoOdfStylesReader& stylesReader, const KoXmlElement& element,
0266                   Conditions& conditions, const StyleManager* styleManager,
0267                   const ValueParser *parser)
0268 {
0269     // NOTE Stefan: Do not fill the style stack with the parent styles!
0270     KoStyleStack styleStack;
0271     styleStack.push(element);
0272 
0273     styleStack.setTypeProperties("table-cell");
0274     loadTableCellProperties(style, stylesReader, styleStack);
0275 
0276     styleStack.setTypeProperties("text");
0277     loadTextProperties(style, stylesReader, styleStack);
0278 
0279     styleStack.setTypeProperties("paragraph");
0280     loadParagraphProperties(style, stylesReader, styleStack);
0281 
0282     KoXmlElement e;
0283     forEachElement(e, element) {
0284         if (e.namespaceURI() == KoXmlNS::style && e.localName() == "map")
0285             loadConditions(&conditions, e, parser, styleManager);
0286     }
0287 
0288     loadDataStyle(style, stylesReader, element, conditions, styleManager, parser);
0289 
0290 }
0291 
0292 void Odf::loadCustomStyle(CustomStyle *style, KoOdfStylesReader& stylesReader, const KoXmlElement& xmlstyle,
0293                           const QString& name, Conditions& conditions,
0294                           const StyleManager* styleManager, const ValueParser *parser)
0295 {
0296     style->setName(name);
0297     if (xmlstyle.hasAttributeNS(KoXmlNS::style, "parent-style-name"))
0298         style->setParentName(xmlstyle.attributeNS(KoXmlNS::style, "parent-style-name", QString()));
0299 
0300     style->setType(Style::CUSTOM);
0301 
0302     loadStyle((Style*) style, stylesReader, xmlstyle, conditions, styleManager, parser);
0303 }
0304 
0305 
0306 void Odf::loadDataStyle(Style *style, KoOdfStylesReader& stylesReader, const KoXmlElement& element,
0307                              Conditions& conditions, const StyleManager* styleManager,
0308                              const ValueParser *parser)
0309 {
0310     QString str;
0311     if (element.hasAttributeNS(KoXmlNS::style, "data-style-name")) {
0312         const QString styleName = element.attributeNS(KoXmlNS::style, "data-style-name", QString());
0313         loadDataStyle(style, stylesReader, styleName, conditions, styleManager, parser);
0314     }
0315 
0316 }
0317 
0318 void Odf::loadDataStyle(Style *style, KoOdfStylesReader &stylesReader, const QString &styleName, Conditions &conditions, const StyleManager *styleManager, const ValueParser *parser)
0319 {
0320     if (!stylesReader.dataFormats().contains(styleName)) return;
0321 
0322     QPair<KoOdfNumberStyles::NumericStyleFormat, KoXmlElement*> dataStylePair = stylesReader.dataFormats()[styleName];
0323 
0324     const KoOdfNumberStyles::NumericStyleFormat& dataStyle = dataStylePair.first;
0325     const QList<QPair<QString,QString> > styleMaps = dataStyle.styleMaps;
0326     bool useNewStyle = (styleMaps.count() > 0);
0327     if (useNewStyle) {
0328         style = new Style();
0329         for (QList<QPair<QString,QString> >::const_iterator it = styleMaps.begin(); it != styleMaps.end(); ++it) {
0330             const Conditional c = loadCondition(&conditions, it->first, it->second, QString(), parser);
0331             if (styleManager->style(c.styleName) == 0) {
0332                 CustomStyle* s = new CustomStyle(c.styleName);
0333                 loadDataStyle(s, stylesReader, c.styleName, conditions, styleManager, parser);
0334                 const_cast<StyleManager*>(styleManager)->insertStyle(s);
0335             }
0336         }
0337     }
0338 
0339     KoStyleStack styleStack;
0340     styleStack.push(*dataStylePair.second);
0341     styleStack.setTypeProperties("text");
0342     loadTextProperties(style, stylesReader, styleStack);
0343 
0344     QString tmp = dataStyle.prefix;
0345     if (!tmp.isEmpty()) {
0346         style->setPrefix(tmp);
0347     }
0348     tmp = dataStyle.suffix;
0349     if (!tmp.isEmpty()) {
0350         style->setPostfix(tmp);
0351     }
0352     // determine data formatting
0353     switch (dataStyle.type) {
0354     case KoOdfNumberStyles::Number:
0355         style->setFormatType(Format::Number);
0356         if (!dataStyle.currencySymbol.isEmpty())
0357             style->setCurrency(numberCurrency(dataStyle.currencySymbol));
0358         else
0359             style->setCurrency(numberCurrency(dataStyle.formatStr));
0360         break;
0361     case KoOdfNumberStyles::Scientific:
0362         style->setFormatType(Format::Scientific);
0363         break;
0364     case KoOdfNumberStyles::Currency:
0365         debugSheetsODF << " currency-symbol:" << dataStyle.currencySymbol;
0366         if (!dataStyle.currencySymbol.isEmpty())
0367             style->setCurrency(numberCurrency(dataStyle.currencySymbol));
0368         else
0369             style->setCurrency(numberCurrency(dataStyle.formatStr));
0370         break;
0371     case KoOdfNumberStyles::Percentage:
0372         style->setFormatType(Format::Percentage);
0373         break;
0374     case KoOdfNumberStyles::Fraction:
0375         // determine format of fractions, dates and times by using the
0376         // formatting string
0377         tmp = dataStyle.formatStr;
0378         if (!tmp.isEmpty()) {
0379             style->setFormatType(fractionType(tmp));
0380         }
0381         break;
0382     case KoOdfNumberStyles::Date:
0383         // determine format of fractions, dates and times by using the
0384         // formatting string
0385         tmp = dataStyle.formatStr;
0386         if (!tmp.isEmpty()) {
0387             style->setFormatType(dateType(tmp));
0388         }
0389         break;
0390     case KoOdfNumberStyles::Time:
0391         // determine format of fractions, dates and times by using the
0392         // formatting string
0393         tmp = dataStyle.formatStr;
0394         if (!tmp.isEmpty()) {
0395             style->setFormatType(timeType(tmp));
0396         }
0397         break;
0398     case KoOdfNumberStyles::Boolean:
0399         style->setFormatType(Format::Number);
0400         break;
0401     case KoOdfNumberStyles::Text:
0402         style->setFormatType(Format::Text);
0403         break;
0404     }
0405 
0406     if (dataStyle.precision > -1) {
0407         // special handling for precision
0408         // The Style default (-1) and the storage default (0) differ.
0409         // The maximum is 10. Replace the Style value 0 with -11, which always results
0410         // in a storage value < 0 and is interpreted as Style value 0.
0411         int precision = dataStyle.precision;
0412         if (style->type() == Style::AUTO && precision == 0)
0413             precision = -11;
0414         style->setPrecision(precision);
0415     }
0416 
0417     style->setThousandsSep(dataStyle.thousandsSep);
0418 
0419     style->setCustomFormat(dataStyle.formatStr);
0420 
0421     if (useNewStyle) {
0422         conditions.setDefaultStyle(*style);
0423         delete style;
0424     }
0425 }
0426 
0427 void Odf::loadParagraphProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack)
0428 {
0429     Q_UNUSED(stylesReader);
0430     debugSheetsODF << "\t paragraph-properties";
0431     if (styleStack.hasProperty(KoXmlNS::fo, "text-align")) {
0432         QString str = styleStack.property(KoXmlNS::fo, "text-align");
0433         if (str == "center")
0434             style->setHAlign(Style::Center);
0435         else if (str == "end" || str=="right")
0436             style->setHAlign(Style::Right);
0437         else if (str == "start" || str=="left")
0438             style->setHAlign(Style::Left);
0439         else if (str == "justify")
0440             style->setHAlign(Style::Justified);
0441         else
0442             style->setHAlign(Style::HAlignUndefined);
0443         debugSheetsODF << "\t\t text-align:" << str;
0444     }
0445 
0446     if (styleStack.hasProperty(KoXmlNS::fo, "margin-left")) {
0447         //todo fix me
0448         style->setIndentation(KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "margin-left"), 0.0));
0449     }
0450 }
0451 
0452 void Odf::loadTableCellProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack)
0453 {
0454     QString str;
0455     if (styleStack.hasProperty(KoXmlNS::style, "vertical-align")) {
0456         str = styleStack.property(KoXmlNS::style, "vertical-align");
0457         if (str == "bottom")
0458             style->setVAlign(Style::Bottom);
0459         else if (str == "top")
0460             style->setVAlign(Style::Top);
0461         else if (str == "middle")
0462             style->setVAlign(Style::Middle);
0463         else
0464             style->setVAlign(Style::VAlignUndefined);
0465     }
0466     if (styleStack.property(KoXmlNS::calligra, "vertical-distributed") == "distributed") {
0467         if (style->valign() == Style::Top)
0468             style->setVAlign(Style::VJustified);
0469         else
0470             style->setVAlign(Style::VDistributed);
0471     }
0472     if (styleStack.hasProperty(KoXmlNS::fo, "background-color")) {
0473         str = styleStack.property(KoXmlNS::fo, "background-color");
0474         if (str == "transparent") {
0475             debugSheetsODF << "\t\t fo:background-color: transparent";
0476             style->setBackgroundColor(QColor());
0477         } else {
0478             QColor color(str);
0479             if (color.isValid()) {
0480                 debugSheetsODF << "\t\t fo:background-color:" << color.name();
0481                 style->setBackgroundColor(color);
0482             }
0483         }
0484     }
0485 
0486     if (styleStack.hasProperty(KoXmlNS::fo, "wrap-option") && (styleStack.property(KoXmlNS::fo, "wrap-option") == "wrap")) {
0487         style->setWrapText(true);
0488     }
0489     if (styleStack.hasProperty(KoXmlNS::style, "cell-protect")) {
0490         str = styleStack.property(KoXmlNS::style, "cell-protect");
0491 #ifndef NWORKAROUND_ODF_BUGS
0492         KoOdfWorkaround::fixBadFormulaHiddenForStyleCellProtect(str);
0493 #endif
0494         if (str == "none")
0495             style->setNotProtected(true);
0496         else if (str == "hidden-and-protected")
0497             style->setHideAll(true);
0498         else if (str == "protected formula-hidden" || str == "formula-hidden protected")
0499             style->setHideFormula(true);
0500         else if (str == "formula-hidden") {
0501             style->setNotProtected(true);
0502             style->setHideFormula(true);
0503         }
0504     }
0505     if (styleStack.hasProperty(KoXmlNS::style, "print-content") &&
0506             (styleStack.property(KoXmlNS::style, "print-content") == "false")) {
0507         style->setDontPrintText(true);
0508     }
0509     if (styleStack.hasProperty(KoXmlNS::style, "shrink-to-fit") &&
0510             (styleStack.property(KoXmlNS::style, "shrink-to-fit") == "true")) {
0511         style->setShrinkToFit(true);
0512     }
0513     if (styleStack.hasProperty(KoXmlNS::style, "direction") &&
0514             (styleStack.property(KoXmlNS::style, "direction") == "ttb")) {
0515         style->setVerticalText(true);
0516     }
0517     if (styleStack.hasProperty(KoXmlNS::style, "rotation-angle")) {
0518         bool ok;
0519         int a = styleStack.property(KoXmlNS::style, "rotation-angle").toInt(&ok);
0520         debugSheetsODF << " rotation-angle :" << a;
0521         if (a != 0) {
0522             style->setAngle(-a);
0523         }
0524     }
0525     if (styleStack.hasProperty(KoXmlNS::fo, "border")) {
0526         str = styleStack.property(KoXmlNS::fo, "border");
0527         QPen pen = decodePen(str);
0528         style->setLeftBorderPen(pen);
0529         style->setTopBorderPen(pen);
0530         style->setBottomBorderPen(pen);
0531         style->setRightBorderPen(pen);
0532         debugSheetsODF<<"\t\tfo:border"<<str;
0533     }
0534     if (styleStack.hasProperty(KoXmlNS::fo, "border-left")) {
0535         str = styleStack.property(KoXmlNS::fo, "border-left");
0536         style->setLeftBorderPen(decodePen(str));
0537         debugSheetsODF<<"\t\tfo:border-left"<<str;
0538     }
0539     if (styleStack.hasProperty(KoXmlNS::fo, "border-right")) {
0540         str = styleStack.property(KoXmlNS::fo, "border-right");
0541         style->setRightBorderPen(decodePen(str));
0542         debugSheetsODF<<"\t\tfo:border-right"<<str;
0543     }
0544     if (styleStack.hasProperty(KoXmlNS::fo, "border-top")) {
0545         str = styleStack.property(KoXmlNS::fo, "border-top");
0546         style->setTopBorderPen(decodePen(str));
0547         debugSheetsODF<<"\t\tfo:border-top"<<str;
0548     }
0549     if (styleStack.hasProperty(KoXmlNS::fo, "border-bottom")) {
0550         str = styleStack.property(KoXmlNS::fo, "border-bottom");
0551         style->setBottomBorderPen(decodePen(str));
0552         debugSheetsODF<<"\t\tfo:border-bottom"<<str;
0553     }
0554     if (styleStack.hasProperty(KoXmlNS::style, "diagonal-tl-br")) {
0555         str = styleStack.property(KoXmlNS::style, "diagonal-tl-br");
0556         style->setFallDiagonalPen(decodePen(str));
0557         debugSheetsODF<<"\t\tfo:diagonal-tl-br"<<str;
0558     }
0559     if (styleStack.hasProperty(KoXmlNS::style, "diagonal-bl-tr")) {
0560         str = styleStack.property(KoXmlNS::style, "diagonal-bl-tr");
0561         style->setGoUpDiagonalPen(decodePen(str));
0562         debugSheetsODF<<"\t\tfo:diagonal-bl-tr"<<str;
0563     }
0564 
0565     if (styleStack.hasProperty(KoXmlNS::draw, "style-name") || styleStack.hasProperty(KoXmlNS::calligra, "fill-style-name")) {
0566         QString styleName = styleStack.hasProperty(KoXmlNS::calligra, "fill-style-name") ? styleStack.property(KoXmlNS::calligra, "fill-style-name")
0567                 : styleStack.property(KoXmlNS::draw, "style-name");
0568         debugSheetsODF << " style name :" << styleName;
0569 
0570         const KoXmlElement *xmlstyle = stylesReader.findStyle(styleName, "graphic");
0571         debugSheetsODF << " style :" << style;
0572         if (xmlstyle) {
0573             KoStyleStack drawStyleStack;
0574             drawStyleStack.push(*xmlstyle);
0575             drawStyleStack.setTypeProperties("graphic");
0576             if (drawStyleStack.hasProperty(KoXmlNS::draw, "fill")) {
0577                 const QString fill = drawStyleStack.property(KoXmlNS::draw, "fill");
0578                 debugSheetsODF << " load object gradient fill type :" << fill;
0579 
0580                 if (fill == "solid" || fill == "hatch") {
0581                     debugSheetsODF << " Style ******************************************************";
0582                     style->setBackgroundBrush(KoOdfGraphicStyles::loadOdfFillStyle(drawStyleStack, fill, stylesReader));
0583 
0584                 } else
0585                     debugSheetsODF << " fill style not supported into sheets :" << fill;
0586             }
0587         }
0588     }
0589 }
0590 
0591 void Odf::loadTextProperties(Style *style, KoOdfStylesReader& stylesReader, const KoStyleStack& styleStack)
0592 {
0593     Q_UNUSED(stylesReader);
0594     // fo:font-size="13pt"
0595     // fo:font-style="italic"
0596     // style:text-underline="double"
0597     // style:text-underline-color="font-color"
0598     // fo:font-weight="bold"
0599     debugSheetsODF << "\t text-properties";
0600     if (styleStack.hasProperty(KoXmlNS::fo, "font-family")) {
0601         style->setFontFamily(styleStack.property(KoXmlNS::fo, "font-family"));     // FIXME Stefan: sanity check
0602         debugSheetsODF << "\t\t fo:font-family:" << style->fontFamily();
0603     }
0604     if (styleStack.hasProperty(KoXmlNS::fo, "font-size")) {
0605         style->setFontSize((int) KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "font-size"), 10.0));       // FIXME Stefan: fallback to default
0606         debugSheetsODF << "\t\t fo:font-size:" << style->fontSize();
0607     }
0608     if (styleStack.hasProperty(KoXmlNS::fo, "font-style")) {
0609         if (styleStack.property(KoXmlNS::fo, "font-style") == "italic") {   // "normal", "oblique"
0610             style->setFontItalic(true);
0611             debugSheetsODF << "\t\t fo:font-style:" << "italic";
0612         }
0613     }
0614     if (styleStack.hasProperty(KoXmlNS::fo, "font-weight")) {
0615         if (styleStack.property(KoXmlNS::fo, "font-weight") == "bold") {   // "normal", "100", "200", ...
0616             style->setFontBold(true);
0617             debugSheetsODF << "\t\t fo:font-weight:" << "bold";
0618         }
0619     }
0620     if (styleStack.hasProperty(KoXmlNS::style, "text-underline-style")) {
0621         if (styleStack.property(KoXmlNS::style, "text-underline-style") != "none") {
0622             style->setFontUnderline(true);
0623             debugSheetsODF << "\t\t style:text-underline-style:" << "solid (actually: !none)";
0624         }
0625     }
0626     if (styleStack.hasProperty(KoXmlNS::style, "text-underline-width")) {
0627         //TODO
0628     }
0629     if (styleStack.hasProperty(KoXmlNS::style, "text-underline-color")) {
0630         //TODO
0631     }
0632     if (styleStack.hasProperty(KoXmlNS::fo, "color")) {
0633         QColor color = QColor(styleStack.property(KoXmlNS::fo, "color"));
0634         if (color.isValid()) {
0635             style->setFontColor(color);
0636             debugSheetsODF << "\t\t fo:color:" << color.name();
0637         }
0638     }
0639     if (styleStack.hasProperty(KoXmlNS::style, "text-line-through-style")) {
0640         if (styleStack.property(KoXmlNS::style, "text-line-through-style") != "none"
0641                 /*&& styleStack.property("text-line-through-style")=="solid"*/) {
0642             style->setFontStrikeOut(true);
0643             debugSheetsODF << "\t\t text-line-through-style:" << "solid (actually: !none)";
0644         }
0645     }
0646     if (styleStack.hasProperty(KoXmlNS::style, "font-name")) {
0647         QString fontName = styleStack.property(KoXmlNS::style, "font-name");
0648         debugSheetsODF << "\t\t style:font-name:" << fontName;
0649         const KoXmlElement * xmlstyle = stylesReader.findStyle(fontName);
0650         // TODO: sanity check that it is a font-face style?
0651         debugSheetsODF << "\t\t\t style:" << xmlstyle;
0652         if (xmlstyle) {
0653             style->setFontFamily(xmlstyle->attributeNS(KoXmlNS::svg, "font-family"));
0654             debugSheetsODF << "\t\t\t svg:font-family:" << style->fontFamily();
0655         }
0656     }
0657 }
0658 
0659 
0660 
0661 // Single style saving
0662 
0663 void Odf::saveStyle(const Style *style, const QSet<Style::Key>& keysToStore, KoGenStyle &xmlstyle,
0664                   KoGenStyles &mainStyles, const StyleManager* manager)
0665 {
0666 #ifndef NDEBUG
0667     //if (type() == BUILTIN )
0668     //  debugSheetsStyle <<"BUILTIN";
0669     //else if (type() == CUSTOM )
0670     //  debugSheetsStyle <<"CUSTOM";
0671     //else if (type() == AUTO )
0672     //  debugSheetsStyle <<"AUTO";
0673 #endif
0674 
0675     if (!style->isDefault() && style->hasAttribute(Style::NamedStyleKey)) {
0676         const QString parentName = manager->openDocumentName(style->parentName());
0677         if (!parentName.isEmpty())
0678             xmlstyle.addAttribute("style:parent-style-name", parentName);
0679     }
0680 
0681     if (keysToStore.contains(Style::HorizontalAlignment)) {
0682         QString value;
0683         switch (style->halign()) {
0684         case Style::Center:
0685             value = "center";
0686             break;
0687         case Style::Right:
0688             value = "end";
0689             break;
0690         case Style::Left:
0691             value = "start";
0692             break;
0693         case Style::Justified:
0694             value = "justify";
0695             break;
0696         case Style::HAlignUndefined:
0697             break;
0698         }
0699         if (!value.isEmpty()) {
0700             xmlstyle.addProperty("style:text-align-source", "fix");   // table-cell-properties
0701             xmlstyle.addProperty("fo:text-align", value, KoGenStyle::ParagraphType);
0702         }
0703     }
0704 
0705     if (keysToStore.contains(Style::VerticalAlignment)) {
0706         QString value;
0707         switch (style->valign()) {
0708         case Style::Top:
0709         case Style::VJustified:
0710             value = "top";
0711             break;
0712         case Style::Middle:
0713         case Style::VDistributed:
0714             value = "middle";
0715             break;
0716         case Style::Bottom:
0717             value = "bottom";
0718             break;
0719         case Style::VAlignUndefined:
0720         default:
0721             break;
0722         }
0723         if (!value.isEmpty()) // sanity
0724             xmlstyle.addProperty("style:vertical-align", value);
0725 
0726         if (style->valign() == Style::VJustified || style->valign() == Style::VDistributed)
0727             xmlstyle.addProperty("calligra:vertical-distributed", "distributed");
0728     }
0729 
0730     if (keysToStore.contains(Style::BackgroundColor) && style->backgroundColor().isValid())
0731         xmlstyle.addProperty("fo:background-color", colorName(style->backgroundColor()));
0732 
0733     if (keysToStore.contains(Style::MultiRow) && style->wrapText())
0734         xmlstyle.addProperty("fo:wrap-option", "wrap");
0735 
0736     if (keysToStore.contains(Style::VerticalText) && style->verticalText()) {
0737         xmlstyle.addProperty("style:direction", "ttb");
0738         xmlstyle.addProperty("style:rotation-angle", "0");
0739         xmlstyle.addProperty("style:rotation-align", "none");
0740     }
0741 
0742     if (keysToStore.contains(Style::ShrinkToFit) && style->shrinkToFit())
0743         xmlstyle.addProperty("style:shrink-to-fit", "true");
0744 
0745 #if 0
0746     if (keysToStore.contains(Style::FloatFormat))
0747         format.setAttribute("float", QString::number((int) style->floatFormat()));
0748 
0749     if (keysToStore.contains(Style::FloatColor))
0750         format.setAttribute("floatcolor", QString::number((int)style->floatColor()));
0751 
0752     if (keysToStore.contains(Style::CustomFormat) && !style->customFormat().isEmpty())
0753         format.setAttribute("custom", customFormat());
0754 
0755     if (keysToStore.contains(Style::Format::Type) && style->formatType() == Money) {
0756         format.setAttribute("type", QString::number((int) currency().type));
0757         format.setAttribute("symbol", currency().symbol);
0758     }
0759 #endif
0760     if (keysToStore.contains(Style::Angle) && style->angle() != 0) {
0761         xmlstyle.addProperty("style:rotation-align", "none");
0762         xmlstyle.addProperty("style:rotation-angle", QString::number(-1.0 * style->angle()));
0763     }
0764 
0765     if (keysToStore.contains(Style::Indentation) && style->indentation() != 0.0) {
0766         xmlstyle.addPropertyPt("fo:margin-left", style->indentation(), KoGenStyle::ParagraphType);
0767         //FIXME
0768         //if ( a == HAlignUndefined )
0769         //currentCellStyle.addProperty("fo:text-align", "start" );
0770     }
0771 
0772     if (keysToStore.contains(Style::DontPrintText) && keysToStore.contains(Style::DontPrintText)) // huh? why twice?
0773         xmlstyle.addProperty("style:print-content", "false");
0774 
0775     // protection
0776     bool hideAll = false;
0777     bool hideFormula = false;
0778     bool isNotProtected = false;
0779 
0780     if (keysToStore.contains(Style::NotProtected))
0781         isNotProtected = style->notProtected();
0782 
0783     if (keysToStore.contains(Style::HideAll))
0784         hideAll = style->hideAll();
0785 
0786     if (keysToStore.contains(Style::HideFormula))
0787         hideFormula = style->hideFormula();
0788 
0789     if (hideAll)
0790         xmlstyle.addProperty("style:cell-protect", "hidden-and-protected");
0791     else {
0792         if (isNotProtected && !hideFormula)
0793             xmlstyle.addProperty("style:cell-protect", "none");
0794         else if (isNotProtected && hideFormula)
0795             xmlstyle.addProperty("style:cell-protect", "formula-hidden");
0796         else if (hideFormula)
0797             xmlstyle.addProperty("style:cell-protect", "protected formula-hidden");
0798         else if (keysToStore.contains(Style::NotProtected) && !isNotProtected)
0799             // write out, only if it is explicitly set
0800             xmlstyle.addProperty("style:cell-protect", "protected");
0801     }
0802 
0803     // borders
0804     // NOTE Stefan: QPen api docs:
0805     //              A line width of zero indicates a cosmetic pen. This means
0806     //              that the pen width is always drawn one pixel wide,
0807     //              independent of the transformation set on the painter.
0808     if (keysToStore.contains(Style::LeftPen) && keysToStore.contains(Style::RightPen) &&
0809             keysToStore.contains(Style::TopPen) && keysToStore.contains(Style::BottomPen) &&
0810             (style->leftBorderPen() == style->topBorderPen()) &&
0811             (style->leftBorderPen() == style->rightBorderPen()) &&
0812             (style->leftBorderPen() == style->bottomBorderPen())) {
0813         if (style->leftBorderPen().style() != Qt::NoPen)
0814             xmlstyle.addProperty("fo:border", encodePen(style->leftBorderPen()));
0815     } else {
0816         if (keysToStore.contains(Style::LeftPen) && (style->leftBorderPen().style() != Qt::NoPen))
0817             xmlstyle.addProperty("fo:border-left", encodePen(style->leftBorderPen()));
0818 
0819         if (keysToStore.contains(Style::RightPen) && (style->rightBorderPen().style() != Qt::NoPen))
0820             xmlstyle.addProperty("fo:border-right", encodePen(style->rightBorderPen()));
0821 
0822         if (keysToStore.contains(Style::TopPen) && (style->topBorderPen().style() != Qt::NoPen))
0823             xmlstyle.addProperty("fo:border-top", encodePen(style->topBorderPen()));
0824 
0825         if (keysToStore.contains(Style::BottomPen) && (style->bottomBorderPen().style() != Qt::NoPen))
0826             xmlstyle.addProperty("fo:border-bottom", encodePen(style->bottomBorderPen()));
0827     }
0828     if (keysToStore.contains(Style::FallDiagonalPen) && (style->fallDiagonalPen().style() != Qt::NoPen)) {
0829         xmlstyle.addProperty("style:diagonal-tl-br", encodePen(style->fallDiagonalPen()));
0830     }
0831     if (keysToStore.contains(Style::GoUpDiagonalPen) && (style->goUpDiagonalPen().style() != Qt::NoPen)) {
0832         xmlstyle.addProperty("style:diagonal-bl-tr", encodePen(style->goUpDiagonalPen()));
0833     }
0834 
0835     // font
0836     if (keysToStore.contains(Style::FontFamily)) {   // !fontFamily().isEmpty() == true
0837         xmlstyle.addProperty("fo:font-family", style->fontFamily(), KoGenStyle::TextType);
0838     }
0839     if (keysToStore.contains(Style::FontSize)) {   // fontSize() != 0
0840         xmlstyle.addPropertyPt("fo:font-size", style->fontSize(), KoGenStyle::TextType);
0841     }
0842 
0843     if (keysToStore.contains(Style::FontBold) && style->bold())
0844         xmlstyle.addProperty("fo:font-weight", "bold", KoGenStyle::TextType);
0845 
0846     if (keysToStore.contains(Style::FontItalic) && style->italic())
0847         xmlstyle.addProperty("fo:font-style", "italic", KoGenStyle::TextType);
0848 
0849     if (keysToStore.contains(Style::FontUnderline) && style->underline()) {
0850         //style:text-underline-style="solid" style:text-underline-width="auto"
0851         xmlstyle.addProperty("style:text-underline-style", "solid", KoGenStyle::TextType);
0852         //copy from oo-129
0853         xmlstyle.addProperty("style:text-underline-width", "auto", KoGenStyle::TextType);
0854         xmlstyle.addProperty("style:text-underline-color", "font-color", KoGenStyle::TextType);
0855     }
0856 
0857     if (keysToStore.contains(Style::FontStrike) && style->strikeOut())
0858         xmlstyle.addProperty("style:text-line-through-style", "solid", KoGenStyle::TextType);
0859 
0860     if (keysToStore.contains(Style::FontColor) && style->fontColor().isValid()) {   // always save
0861         xmlstyle.addProperty("fo:color", colorName(style->fontColor()), KoGenStyle::TextType);
0862     }
0863 
0864     //I don't think there is a reason why the background brush should be saved if it is null,
0865     //but remove the check if it causes problems.  -- Robert Knight <robertknight@gmail.com>
0866     if (keysToStore.contains(Style::BackgroundBrush) && (style->backgroundBrush().style() != Qt::NoBrush)) {
0867         QString tmp = saveBackgroundStyle(mainStyles, style->backgroundBrush());
0868         if (!tmp.isEmpty())
0869             xmlstyle.addProperty("calligra:fill-style-name", tmp);
0870     }
0871 
0872     QString _prefix;
0873     QString _postfix;
0874     int _precision = -1;
0875     if (keysToStore.contains(Style::Prefix) && !style->prefix().isEmpty())
0876         _prefix = style->prefix();
0877     if (keysToStore.contains(Style::Postfix) && !style->postfix().isEmpty())
0878         _postfix = style->postfix();
0879     if (keysToStore.contains(Style::Precision) && style->precision() != -1)
0880         _precision = style->precision();
0881     bool _thousandsSep = false;
0882     if (keysToStore.contains(Style::ThousandsSep)) {
0883         _thousandsSep = style->thousandsSep();
0884     }
0885 
0886     QString currencyCode;
0887     if (keysToStore.contains(Style::FormatTypeKey) && style->formatType() == Format::Money) {
0888         currencyCode = style->currency().code();
0889     }
0890 
0891     QString numericStyle = saveStyleNumeric(xmlstyle, mainStyles, style->formatType(),
0892                            _prefix, _postfix, _precision,
0893                            currencyCode, _thousandsSep);
0894     if (!numericStyle.isEmpty())
0895         xmlstyle.addAttribute("style:data-style-name", numericStyle);
0896 }
0897 
0898 QString Odf::saveCustomStyle(CustomStyle *style, KoGenStyle& genstyle, KoGenStyles &mainStyles,
0899                              const StyleManager* manager)
0900 {
0901     Q_ASSERT(!style->name().isEmpty());
0902     // default style does not need display name
0903     if (!style->isDefault())
0904         genstyle.addAttribute("style:display-name", style->name());
0905 
0906     // doing the real work
0907     QSet<Style::Key> keysToStore = style->definedKeys(manager);
0908     saveStyle(style, keysToStore, genstyle, mainStyles, manager);
0909 
0910     if (style->isDefault()) {
0911         genstyle.setDefaultStyle(true);
0912         // don't i18n'ize "Default" in this case
0913         return mainStyles.insert(genstyle, "Default", KoGenStyles::DontAddNumberToName);
0914     }
0915 
0916     // this is a custom style
0917     return mainStyles.insert(genstyle, "custom-style");
0918 }
0919 
0920 QString Odf::saveStyle(const Style *style, KoGenStyle& xmlstyle, KoGenStyles& mainStyles,
0921                        const StyleManager* manager)
0922 {
0923     // list of substyles to store
0924     QSet<Style::Key> keysToStore = style->definedKeys(manager);
0925 
0926     if (style->isDefault()) {
0927         if (xmlstyle.isEmpty()) {
0928             xmlstyle = KoGenStyle(KoGenStyle::TableCellStyle, "table-cell");
0929             xmlstyle.setDefaultStyle(true);
0930             // don't i18n'ize "Default" in this case
0931             return "Default"; // mainStyles.insert( style, "Default", KoGenStyles::DontAddNumberToName );
0932         }
0933         // no attributes to store here
0934         return mainStyles.insert(xmlstyle, "ce");
0935     } else if (style->hasAttribute(Style::NamedStyleKey)) {
0936         // it's not really the parent name in this case
0937 
0938         // no differences and not an automatic style yet?
0939         if (xmlstyle.isEmpty() &&
0940                 (keysToStore.count() == 0 ||
0941                  (keysToStore.count() == 1 && keysToStore.toList().first() == Style::NamedStyleKey))) {
0942             return manager->openDocumentName(style->parentName());
0943         }
0944     }
0945 
0946     // Calligra::Sheets::Style is definitely an OASIS auto style,
0947     // but don't overwrite it, if it already exists
0948     if (xmlstyle.isEmpty())
0949         xmlstyle = KoGenStyle(KoGenStyle::TableCellAutoStyle, "table-cell");
0950 
0951     // doing the real work
0952     saveStyle(style, keysToStore, xmlstyle, mainStyles, manager);
0953     return mainStyles.insert(xmlstyle, "ce");
0954 }
0955 
0956 QString Odf::saveBackgroundStyle(KoGenStyles &mainStyles, const QBrush &brush)
0957 {
0958     KoGenStyle styleobjectauto = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0959     KoOdfGraphicStyles::saveOdfFillStyle(styleobjectauto, mainStyles, brush);
0960     return mainStyles.insert(styleobjectauto, "gr");
0961 }
0962 
0963 QString Odf::saveStyleNumeric(KoGenStyle &style, KoGenStyles &mainStyles,
0964                                    Format::Type _style,
0965                                    const QString &_prefix, const QString &_postfix,
0966                                    int _precision, const QString& symbol,
0967                                    bool thousandsSep)
0968 {
0969 //  debugSheetsODF ;
0970     QString styleName;
0971     QString valueType;
0972     switch (_style) {
0973     case Format::Number:
0974         styleName = saveStyleNumericNumber(mainStyles, _style, _precision, _prefix, _postfix, thousandsSep);
0975         valueType = "float";
0976         break;
0977     case Format::Text:
0978         styleName = saveStyleNumericText(mainStyles, _style, _precision, _prefix, _postfix);
0979         valueType = "string";
0980         break;
0981     case Format::Money:
0982         styleName = saveStyleNumericMoney(mainStyles, _style, symbol, _precision, _prefix, _postfix);
0983         valueType = "currency";
0984         break;
0985     case Format::Percentage:
0986         styleName = saveStyleNumericPercentage(mainStyles, _style, _precision, _prefix, _postfix);
0987         valueType = "percentage";
0988         break;
0989     case Format::Scientific:
0990         styleName = saveStyleNumericScientific(mainStyles, _style, _prefix, _postfix, _precision, thousandsSep);
0991         valueType = "float";
0992         break;
0993     case Format::ShortDate:
0994     case Format::TextDate:
0995         styleName = saveStyleNumericDate(mainStyles, _style, _prefix, _postfix);
0996         valueType = "date";
0997         break;
0998     case Format::Time:
0999     case Format::SecondeTime:
1000     case Format::Time1:
1001     case Format::Time2:
1002     case Format::Time3:
1003     case Format::Time4:
1004     case Format::Time5:
1005     case Format::Time6:
1006     case Format::Time7:
1007     case Format::Time8:
1008         styleName = saveStyleNumericTime(mainStyles, _style, _prefix, _postfix);
1009         valueType = "time";
1010         break;
1011     case Format::fraction_half:
1012     case Format::fraction_quarter:
1013     case Format::fraction_eighth:
1014     case Format::fraction_sixteenth:
1015     case Format::fraction_tenth:
1016     case Format::fraction_hundredth:
1017     case Format::fraction_one_digit:
1018     case Format::fraction_two_digits:
1019     case Format::fraction_three_digits:
1020         styleName = saveStyleNumericFraction(mainStyles, _style, _prefix, _postfix);
1021         valueType = "float";
1022         break;
1023     case Format::Date1:
1024     case Format::Date2:
1025     case Format::Date3:
1026     case Format::Date4:
1027     case Format::Date5:
1028     case Format::Date6:
1029     case Format::Date7:
1030     case Format::Date8:
1031     case Format::Date9:
1032     case Format::Date10:
1033     case Format::Date11:
1034     case Format::Date12:
1035     case Format::Date13:
1036     case Format::Date14:
1037     case Format::Date15:
1038     case Format::Date16:
1039     case Format::Date17:
1040     case Format::Date18:
1041     case Format::Date19:
1042     case Format::Date20:
1043     case Format::Date21:
1044     case Format::Date22:
1045     case Format::Date23:
1046     case Format::Date24:
1047     case Format::Date25:
1048     case Format::Date26:
1049     case Format::Date27:
1050     case Format::Date28:
1051     case Format::Date29:
1052     case Format::Date30:
1053     case Format::Date31:
1054     case Format::Date32:
1055     case Format::Date33:
1056     case Format::Date34:
1057     case Format::Date35:
1058         styleName = saveStyleNumericDate(mainStyles, _style, _prefix, _postfix);
1059         valueType = "date";
1060         break;
1061     case Format::Custom:
1062         styleName = saveStyleNumericCustom(mainStyles, _style, _prefix, _postfix);
1063         break;
1064     case Format::Generic:
1065     case Format::None:
1066         if (_precision > -1 || !_prefix.isEmpty() || !_postfix.isEmpty()) {
1067             styleName = saveStyleNumericNumber(mainStyles, _style, _precision, _prefix, _postfix, thousandsSep);
1068             valueType = "float";
1069         }
1070         break;
1071     case Format::DateTime:
1072     default:
1073         ;
1074     }
1075     if (!styleName.isEmpty()) {
1076         style.addAttribute("style:data-style-name", styleName);
1077     }
1078     return styleName;
1079 }
1080 
1081 QString Odf::saveStyleNumericNumber(KoGenStyles& mainStyles, Format::Type /*_style*/, int _precision,
1082         const QString& _prefix, const QString& _postfix, bool thousandsSep)
1083 {
1084     QString format;
1085     if (_precision == -1)
1086         format = '0';
1087     else {
1088         QString tmp;
1089         for (int i = 0; i < _precision; i++) {
1090             tmp += '0';
1091         }
1092         format = "0." + tmp;
1093     }
1094     return KoOdfNumberStyles::saveOdfNumberStyle(mainStyles, format, _prefix, _postfix, thousandsSep);
1095 }
1096 
1097 QString Odf::saveStyleNumericText(KoGenStyles& /*mainStyles*/, Format::Type /*_style*/, int /*_precision*/,
1098                                        const QString& /*_prefix*/, const QString& /*_postfix*/)
1099 {
1100     return "";
1101 }
1102 
1103 QString Odf::saveStyleNumericMoney(KoGenStyles& mainStyles, Format::Type /*_style*/,
1104                                         const QString& symbol, int _precision,
1105                                         const QString& _prefix, const QString& _postfix)
1106 {
1107     QString format;
1108     if (_precision == -1)
1109         format = '0';
1110     else {
1111         QString tmp;
1112         for (int i = 0; i < _precision; i++) {
1113             tmp += '0';
1114         }
1115         format = "0." + tmp;
1116     }
1117     return KoOdfNumberStyles::saveOdfCurrencyStyle(mainStyles, format, symbol, _prefix, _postfix);
1118 }
1119 
1120 QString Odf::saveStyleNumericPercentage(KoGenStyles&mainStyles, Format::Type /*_style*/, int _precision,
1121         const QString& _prefix, const QString& _postfix)
1122 {
1123     //<number:percentage-style style:name="N106" style:family="data-style">
1124     //<number:number number:decimal-places="6" number:min-integer-digits="1"/>
1125     //<number:text>%</number:text>
1126     //</number:percentage-style>
1127     //TODO add decimal etc.
1128     QString format;
1129     if (_precision == -1)
1130         format = '0';
1131     else {
1132         QString tmp;
1133         for (int i = 0; i < _precision; i++) {
1134             tmp += '0';
1135         }
1136         format = "0." + tmp;
1137     }
1138     return KoOdfNumberStyles::saveOdfPercentageStyle(mainStyles, format, _prefix, _postfix);
1139 }
1140 
1141 
1142 QString Odf::saveStyleNumericScientific(KoGenStyles&mainStyles, Format::Type /*_style*/,
1143         const QString &_prefix, const QString &_suffix, int _precision, bool thousandsSep)
1144 {
1145     //<number:number-style style:name="N60" style:family="data-style">
1146     //  <number:scientific-number number:decimal-places="2" number:min-integer-digits="1" number:min-exponent-digits="3"/>
1147     //</number:number-style>
1148     QString format;
1149     if (_precision == -1)
1150         format = "0E+00";
1151     else {
1152         QString tmp;
1153         for (int i = 0; i < _precision; i++) {
1154             tmp += '0';
1155         }
1156         format = "0." + tmp + "E+00";
1157     }
1158     return KoOdfNumberStyles::saveOdfScientificStyle(mainStyles, format, _prefix, _suffix, thousandsSep);
1159 }
1160 
1161 QString Odf::saveStyleNumericDate(KoGenStyles&mainStyles, Format::Type _style,
1162                                        const QString& _prefix, const QString& _postfix)
1163 {
1164     QString format;
1165     bool locale = false;
1166     switch (_style) {
1167         //TODO fixme use locale of Calligra Sheets and not kglobal
1168     case Format::ShortDate:
1169         format = KLocale::global()->dateFormatShort();
1170         locale = true;
1171         break;
1172     case Format::TextDate:
1173         format = KLocale::global()->dateFormat();
1174         locale = true;
1175         break;
1176     case Format::Date1:
1177         format = "dd-MMM-yy";
1178         break;
1179     case Format::Date2:
1180         format = "dd-MMM-yyyy";
1181         break;
1182     case Format::Date3:
1183         format = "dd-M";
1184         break;
1185     case Format::Date4:
1186         format = "dd-MM";
1187         break;
1188     case Format::Date5:
1189         format = "dd/MM/yy";
1190         break;
1191     case Format::Date6:
1192         format = "dd/MM/yyyy";
1193         break;
1194     case Format::Date7:
1195         format = "MMM-yy";
1196         break;
1197     case Format::Date8:
1198         format = "MMMM-yy";
1199         break;
1200     case Format::Date9:
1201         format = "MMMM-yyyy";
1202         break;
1203     case Format::Date10:
1204         format = "MMMMM-yy";
1205         break;
1206     case Format::Date11:
1207         format = "dd/MMM";
1208         break;
1209     case Format::Date12:
1210         format = "dd/MM";
1211         break;
1212     case Format::Date13:
1213         format = "dd/MMM/yyyy";
1214         break;
1215     case Format::Date14:
1216         format = "yyyy/MMM/dd";
1217         break;
1218     case Format::Date15:
1219         format = "yyyy-MMM-dd";
1220         break;
1221     case Format::Date16:
1222         format = "yyyy/MM/dd";
1223         break;
1224     case Format::Date17:
1225         format = "d MMMM yyyy";
1226         break;
1227     case Format::Date18:
1228         format = "MM/dd/yyyy";
1229         break;
1230     case Format::Date19:
1231         format = "MM/dd/yy";
1232         break;
1233     case Format::Date20:
1234         format = "MMM/dd/yy";
1235         break;
1236     case Format::Date21:
1237         format = "MMM/dd/yyyy";
1238         break;
1239     case Format::Date22:
1240         format = "MMM-yyyy";
1241         break;
1242     case Format::Date23:
1243         format = "yyyy";
1244         break;
1245     case Format::Date24:
1246         format = "yy";
1247         break;
1248     case Format::Date25:
1249         format = "yyyy/MM/dd";
1250         break;
1251     case Format::Date26:
1252         format = "yyyy/MMM/dd";
1253         break;
1254     case Format::Date27:
1255         format = "MMM/yy";
1256         break;
1257     case Format::Date28:
1258         format = "MMM/yyyy";
1259         break;
1260     case Format::Date29:
1261         format = "MMMM/yy";
1262         break;
1263     case Format::Date30:
1264         format = "MMMM/yyyy";
1265         break;
1266     case Format::Date31:
1267         format = "dd-MM";
1268         break;
1269     case Format::Date32:
1270         format = "MM/yy";
1271         break;
1272     case Format::Date33:
1273         format = "MM-yy";
1274         break;
1275     case Format::Date34:
1276         format = "ddd d MMM yyyy";
1277         break;
1278     case Format::Date35:
1279         format = "dddd d MMM yyyy";
1280         break;
1281     default:
1282         debugSheetsODF << "this date format is not defined ! :" << _style;
1283         break;
1284     }
1285     return KoOdfNumberStyles::saveOdfDateStyle(mainStyles, format, locale, _prefix, _postfix);
1286 }
1287 
1288 QString Odf::saveStyleNumericCustom(KoGenStyles& /*mainStyles*/, Format::Type /*_style*/,
1289         const QString& /*_prefix*/, const QString& /*_postfix*/)
1290 {
1291     //TODO
1292     //<number:date-style style:name="N50" style:family="data-style" number:automatic-order="true" number:format-source="language">
1293     //<number:month/>
1294     //<number:text>/</number:text>
1295     //<number:day/>
1296     //<number:text>/</number:text>
1297     //<number:year/>
1298     //<number:text> </number:text>
1299     //<number:hours number:style="long"/>
1300     //<number:text>:</number:text>
1301     //<number:minutes number:style="long"/>
1302     // <number:text> </number:text>
1303     //<number:am-pm/>
1304     //</number:date-style>
1305     return "";
1306 }
1307 
1308 QString Odf::saveStyleNumericTime(KoGenStyles& mainStyles, Format::Type _style,
1309                                        const QString& _prefix, const QString& _postfix)
1310 {
1311     //<number:time-style style:name="N42" style:family="data-style">
1312     //<number:hours number:style="long"/>
1313     //<number:text>:</number:text>
1314     //<number:minutes number:style="long"/>
1315     //<number:text> </number:text>
1316     //<number:am-pm/>
1317     //</number:time-style>
1318 
1319     QString format;
1320     bool locale = false;
1321     //TODO use format
1322     switch (_style) {
1323     case Format::Time: //TODO FIXME
1324         format = "hh:mm:ss";
1325         break;
1326     case Format::SecondeTime: //TODO FIXME
1327         format = "hh:mm";
1328         break;
1329     case Format::Time1:
1330         format = "h:mm AP";
1331         break;
1332     case Format::Time2:
1333         format = "h:mm:ss AP";
1334         break;
1335     case Format::Time3: // 9 h 01 min 28 s
1336         format = "hh \\h mm \\m\\i\\n ss \\s";
1337         break;
1338     case Format::Time4:
1339         format = "hh:mm";
1340         break;
1341     case Format::Time5:
1342         format = "hh:mm:ss";
1343         break;
1344     case Format::Time6:
1345         format = "m:ss";
1346         break;
1347     case Format::Time7:
1348         format = "h:mm:ss";
1349         break;
1350     case Format::Time8:
1351         format = "h:mm";
1352         break;
1353     default:
1354         debugSheetsODF << "time format not defined :" << _style;
1355         break;
1356     }
1357     return KoOdfNumberStyles::saveOdfTimeStyle(mainStyles, format, locale, _prefix, _postfix);
1358 }
1359 
1360 QString Odf::saveStyleNumericFraction(KoGenStyles &mainStyles, Format::Type formatType,
1361         const QString &_prefix, const QString &_suffix)
1362 {
1363     //<number:number-style style:name="N71" style:family="data-style">
1364     //<number:fraction number:min-integer-digits="0" number:min-numerator-digits="2" number:min-denominator-digits="2"/>
1365     //</number:number-style>
1366     QString format;
1367     switch (formatType) {
1368     case Format::fraction_half:
1369         format = "# ?/2";
1370         break;
1371     case Format::fraction_quarter:
1372         format = "# ?/4";
1373         break;
1374     case Format::fraction_eighth:
1375         format = "# ?/8";
1376         break;
1377     case Format::fraction_sixteenth:
1378         format = "# ?/16";
1379         break;
1380     case Format::fraction_tenth:
1381         format = "# ?/10";
1382         break;
1383     case Format::fraction_hundredth:
1384         format = "# ?/100";
1385         break;
1386     case Format::fraction_one_digit:
1387         format = "# ?/?";
1388         break;
1389     case Format::fraction_two_digits:
1390         format = "# \?\?/\?\?";
1391         break;
1392     case Format::fraction_three_digits:
1393         format = "# \?\?\?/\?\?\?";
1394         break;
1395     default:
1396         debugSheetsODF << " fraction format not defined :" << formatType;
1397         break;
1398     }
1399 
1400     return KoOdfNumberStyles::saveOdfFractionStyle(mainStyles, format, _prefix, _suffix);
1401 }
1402 
1403 
1404 // Helpers
1405 
1406 static QString convertDateFormat(const QString& date)
1407 {
1408     QString result = date;
1409     result.replace("%Y", "yyyy");
1410     result.replace("%y", "yy");
1411     result.replace("%n", "M");
1412     result.replace("%m", "MM");
1413     result.replace("%e", "d");
1414     result.replace("%d", "dd");
1415     result.replace("%b", "MMM");
1416     result.replace("%B", "MMMM");
1417     result.replace("%a", "ddd");
1418     result.replace("%A", "dddd");
1419     return result;
1420 }
1421 
1422 Format::Type Odf::dateType(const QString &_f)
1423 {
1424     const QString dateFormatShort = convertDateFormat(KLocale::global()->dateFormatShort());
1425     const QString dateFormat = convertDateFormat(KLocale::global()->dateFormat());
1426     QString _format = _f;
1427     _format.replace(' ', '-');
1428 
1429     if (_format == "d-MMM-yy" || _format == "dd-MMM-yy")
1430         return Format::Date1;
1431     else if (_format == "dd-MMM-yyyy")
1432         return Format::Date2;
1433     else if (_format == "d-MM")
1434         return Format::Date3;
1435     else if (_format == "dd-MM")   //TODO ???????
1436         return Format::Date4;
1437     else if (_format == "dd/MM/yy")
1438         return Format::Date5;
1439     else if (_format == "dd/MM/yyyy")
1440         return Format::Date6;
1441     else if (_format == "MMM-yy")
1442         return Format::Date7;
1443     else if (_format == "MMMM-yy")
1444         return Format::Date8;
1445     else if (_format == "MMMM-yyyy")
1446         return Format::Date9;
1447     else if (_format == "MMMMM-yy" || _format == "X-yy")
1448         return Format::Date10;
1449     else if (_format == "dd/MMM")
1450         return Format::Date11;
1451     else if (_format == "dd/MM")
1452         return Format::Date12;
1453     else if (_format == "dd/MMM/yyyy")
1454         return Format::Date13;
1455     else if (_format == "yyyy/MMM/dd")
1456         return Format::Date14;
1457     else if (_format == "yyyy-MMM-dd")
1458         return Format::Date15;
1459     else if (_format == "yyyy-MM-dd")
1460         return Format::Date16;
1461     else if (_format == "d MMMM yyyy")
1462         return Format::Date17;
1463     else if (_format == "MM/dd/yyyy")
1464         return Format::Date18;
1465     else if (_format == "MM/dd/yy")
1466         return Format::Date19;
1467     else if (_format == "MMM/dd/yy")
1468         return Format::Date20;
1469     else if (_format == "MMM/dd/yyyy")
1470         return Format::Date21;
1471     else if (_format == "MMM-yyyy")
1472         return Format::Date22;
1473     else if (_format == "yyyy")
1474         return Format::Date23;
1475     else if (_format == "yy")
1476         return Format::Date24;
1477     else if (_format == "yyyy/MM/dd")
1478         return Format::Date25;
1479     else if (_format == "yyyy/MMM/dd")
1480         return Format::Date26;
1481     else if (_format == "MMM/yy")
1482         return Format::Date27;
1483     else if (_format == "MMM/yyyy")
1484         return Format::Date28;
1485     else if (_format == "MMMM/yy")
1486         return Format::Date29;
1487     else if (_format == "MMMM/yyyy")
1488         return Format::Date30;
1489     else if (_format == "dd-MM")
1490         return Format::Date31;
1491     else if (_format == "MM/yy")
1492         return Format::Date32;
1493     else if (_format == "MM-yy")
1494         return Format::Date33;
1495     else if (QRegExp("^[d]+[\\s]*[d]{1,2}[\\s]+[M]{1,4}[\\s]+[y]{2,2}$").indexIn(_f) >= 0)
1496         return Format::Date34;
1497     else if (QRegExp("^[d]+[\\s]*[d]{1,2}[\\s]+[M]{1,}[\\s]+[y]{2,4}$").indexIn(_f) >= 0)
1498         return Format::Date35;
1499     else if (_format == dateFormatShort)
1500         return Format::ShortDate;
1501     else if (_format == dateFormat)
1502         return Format::TextDate;
1503     else {
1504         debugSheets << "Unhandled date format=" << _format;
1505         return Format::ShortDate;
1506     }
1507 }
1508 
1509 Format::Type Odf::timeType(const QString &_format)
1510 {
1511     if (_format == "h:mm AP")
1512         return Format::Time1;
1513     else if (_format == "h:mm:ss AP")
1514         return Format::Time2;
1515     else if (_format == "hh \\h mm \\m\\i\\n ss \\s")
1516         return Format::Time3;
1517     else if (_format == "hh:mm")
1518         return Format::Time4;
1519     else if (_format == "hh:mm:ss")
1520         return Format::Time5;
1521     else if (_format == "m:ss")
1522         return Format::Time6;
1523     else if (_format == "h:mm:ss")
1524         return Format::Time7;
1525     else if (_format == "h:mm")
1526         return Format::Time8;
1527     else
1528         return Format::Time;
1529 }
1530 
1531 Currency Odf::numberCurrency(const QString &_format)
1532 {
1533     // Look up if a prefix or postfix is in the currency table,
1534     // return the currency symbol to use for formatting purposes.
1535     if(!_format.isEmpty()) {
1536         QString f = QString(_format.at(0));
1537         Currency currStart = Currency(f);
1538         if (currStart.index() > 1)
1539             return currStart;
1540         f = QString(_format.at(_format.size()-1));
1541         Currency currEnd = Currency(f);
1542         if (currEnd.index() > 1)
1543             return currEnd;
1544     }
1545     return Currency(QString());
1546 }
1547 
1548 Format::Type Odf::fractionType(const QString &_format)
1549 {
1550     if (_format.endsWith(QLatin1String("/2")))
1551         return Format::fraction_half;
1552     else if (_format.endsWith(QLatin1String("/4")))
1553         return Format::fraction_quarter;
1554     else if (_format.endsWith(QLatin1String("/8")))
1555         return Format::fraction_eighth;
1556     else if (_format.endsWith(QLatin1String("/16")))
1557         return Format::fraction_sixteenth;
1558     else if (_format.endsWith(QLatin1String("/10")))
1559         return Format::fraction_tenth;
1560     else if (_format.endsWith(QLatin1String("/100")))
1561         return Format::fraction_hundredth;
1562     else if (_format.endsWith(QLatin1String("/?")))
1563         return Format::fraction_one_digit;
1564     else if (_format.endsWith(QLatin1String("/??")))
1565         return Format::fraction_two_digits;
1566     else if (_format.endsWith(QLatin1String("/???")))
1567         return Format::fraction_three_digits;
1568     else
1569         return Format::fraction_three_digits;
1570 }
1571 
1572 QPen Odf::decodePen(const QString &border)
1573 {
1574     QPen pen;
1575     //string like "0.088cm solid #800000"
1576     if (border.isEmpty() || border == "none" || border == "hidden") { // in fact no border
1577         pen.setStyle(Qt::NoPen);
1578         return pen;
1579     }
1580     //code from koborder, for the moment Calligra Sheets doesn't use koborder
1581     // ## isn't it faster to use QStringList::split than parse it 3 times?
1582     QString _width = border.section(' ', 0, 0);
1583     QByteArray _style = border.section(' ', 1, 1).toLatin1();
1584     QString _color = border.section(' ', 2, 2);
1585 
1586     pen.setWidth((int)(KoUnit::parseValue(_width, 1.0)));
1587 
1588     if (_style == "none")
1589         pen.setStyle(Qt::NoPen);
1590     else if (_style == "solid")
1591         pen.setStyle(Qt::SolidLine);
1592     else if (_style == "dashed")
1593         pen.setStyle(Qt::DashLine);
1594     else if (_style == "dotted")
1595         pen.setStyle(Qt::DotLine);
1596     else if (_style == "dot-dash")
1597         pen.setStyle(Qt::DashDotLine);
1598     else if (_style == "dot-dot-dash")
1599         pen.setStyle(Qt::DashDotDotLine);
1600     else
1601         debugSheets << " style undefined :" << _style;
1602 
1603     if (_color.isEmpty())
1604         pen.setColor(QColor());
1605     else
1606         pen.setColor(QColor(_color));
1607 
1608     return pen;
1609 }
1610 
1611 QString Odf::encodePen(const QPen & pen)
1612 {
1613 //     debugSheets<<"encodePen( const QPen & pen ) :"<<pen;
1614     // NOTE Stefan: QPen api docs:
1615     //              A line width of zero indicates a cosmetic pen. This means
1616     //              that the pen width is always drawn one pixel wide,
1617     //              independent of the transformation set on the painter.
1618     QString s = QString("%1pt ").arg((pen.width() == 0) ? 1 : pen.width());
1619     switch (pen.style()) {
1620     case Qt::NoPen:
1621         return "none";
1622     case Qt::SolidLine:
1623         s += "solid";
1624         break;
1625     case Qt::DashLine:
1626         s += "dashed";
1627         break;
1628     case Qt::DotLine:
1629         s += "dotted";
1630         break;
1631     case Qt::DashDotLine:
1632         s += "dot-dash";
1633         break;
1634     case Qt::DashDotDotLine:
1635         s += "dot-dot-dash";
1636         break;
1637     default: break;
1638     }
1639     //debugSheets << " encodePen :" << s;
1640     if (pen.color().isValid()) {
1641         s += ' ' + colorName(pen.color());
1642     }
1643     return s;
1644 }
1645 
1646 QString Odf::colorName(const QColor& color)
1647 {
1648     static QMap<QRgb, QString> map;
1649     QRgb rgb = color.rgb();
1650     if (!map.contains(rgb)) {
1651         map[rgb] = color.name();
1652         return map[rgb];
1653     } else {
1654         return map[rgb];
1655     }
1656 }
1657 
1658 
1659 
1660 
1661 
1662 
1663 
1664 
1665 }  // Sheets
1666 }  // Calligra
1667