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