Warning, file /office/calligra/libs/odf/KoGenStyle.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004-2006 David Faure <faure@kde.org> 0003 Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org> 0004 Copyright (C) 2010 Jarosław Staniek <staniek@kde.org> 0005 Copyright (C) 2011 Pierre Ducroquet <pinaraf@pinaraf.info> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 #include "KoGenStyle.h" 0023 #include "KoGenStyles.h" 0024 0025 #include <QTextLength> 0026 0027 #include <KoXmlWriter.h> 0028 0029 #include <float.h> 0030 0031 #include <OdfDebug.h> 0032 0033 // Returns -1, 0 (equal) or 1 0034 static int compareMap(const QMap<QString, QString>& map1, const QMap<QString, QString>& map2) 0035 { 0036 QMap<QString, QString>::const_iterator it = map1.constBegin(); 0037 QMap<QString, QString>::const_iterator oit = map2.constBegin(); 0038 for (; it != map1.constEnd(); ++it, ++oit) { // both maps have been checked for size already 0039 if (it.key() != oit.key()) 0040 return it.key() < oit.key() ? -1 : + 1; 0041 if (it.value() != oit.value()) 0042 return it.value() < oit.value() ? -1 : + 1; 0043 } 0044 return 0; // equal 0045 } 0046 0047 0048 KoGenStyle::KoGenStyle(Type type, const char* familyName, 0049 const QString& parentName) 0050 : m_type(type), m_familyName(familyName), m_parentName(parentName), 0051 m_autoStyleInStylesDotXml(false), m_defaultStyle(false) 0052 { 0053 switch (type) { 0054 case TextStyle: 0055 case TextAutoStyle: 0056 m_propertyType = TextType; 0057 break; 0058 case ParagraphStyle: 0059 case ParagraphAutoStyle: 0060 m_propertyType = ParagraphType; 0061 break; 0062 case GraphicStyle: 0063 case GraphicAutoStyle: 0064 m_propertyType = GraphicType; 0065 break; 0066 case SectionStyle: 0067 case SectionAutoStyle: 0068 m_propertyType = SectionType; 0069 break; 0070 case RubyStyle: 0071 case RubyAutoStyle: 0072 m_propertyType = RubyType; 0073 break; 0074 case TableStyle: 0075 case TableAutoStyle: 0076 m_propertyType = TableType; 0077 break; 0078 case TableColumnStyle: 0079 case TableColumnAutoStyle: 0080 m_propertyType = TableColumnType; 0081 break; 0082 case TableRowStyle: 0083 case TableRowAutoStyle: 0084 m_propertyType = TableRowType; 0085 break; 0086 case TableCellStyle: 0087 case TableCellAutoStyle: 0088 m_propertyType = TableCellType; 0089 break; 0090 case PresentationStyle: 0091 case PresentationAutoStyle: 0092 m_propertyType = PresentationType; 0093 break; 0094 case DrawingPageStyle: 0095 case DrawingPageAutoStyle: 0096 m_propertyType = DrawingPageType; 0097 break; 0098 case ChartStyle: 0099 case ChartAutoStyle: 0100 m_propertyType = ChartType; 0101 break; 0102 default: 0103 m_propertyType = DefaultType; 0104 break; 0105 } 0106 } 0107 0108 KoGenStyle::~KoGenStyle() 0109 { 0110 } 0111 0112 /* 0113 * The order of this list is important; e.g. a graphic-properties must 0114 * precede a text-properties always. See the Relax NG to check the order. 0115 */ 0116 static const KoGenStyle::PropertyType s_propertyTypes[] = { 0117 KoGenStyle::DefaultType, 0118 KoGenStyle::SectionType, 0119 KoGenStyle::RubyType, 0120 KoGenStyle::TableType, 0121 KoGenStyle::TableColumnType, 0122 KoGenStyle::TableRowType, 0123 KoGenStyle::TableCellType, 0124 KoGenStyle::DrawingPageType, 0125 KoGenStyle::ChartType, 0126 KoGenStyle::GraphicType, 0127 KoGenStyle::ParagraphType, 0128 KoGenStyle::TextType, 0129 }; 0130 0131 static const char* const s_propertyNames[] = { 0132 0, 0133 "style:section-properties", 0134 "style:ruby-properties", 0135 "style:table-properties", 0136 "style:table-column-properties", 0137 "style:table-row-properties", 0138 "style:table-cell-properties", 0139 "style:drawing-page-properties", 0140 "style:chart-properties", 0141 "style:graphic-properties", 0142 "style:paragraph-properties", 0143 "style:text-properties" 0144 }; 0145 0146 static const int s_propertyNamesCount = sizeof(s_propertyNames) / sizeof(*s_propertyNames); 0147 0148 static KoGenStyle::PropertyType propertyTypeByElementName(const char* propertiesElementName) 0149 { 0150 for (int i = 0; i < s_propertyNamesCount; ++i) { 0151 if (qstrcmp(s_propertyNames[i], propertiesElementName) == 0) { 0152 return s_propertyTypes[i]; 0153 } 0154 } 0155 return KoGenStyle::DefaultType; 0156 } 0157 0158 void KoGenStyle::writeStyleProperties(KoXmlWriter* writer, PropertyType type, 0159 const KoGenStyle* parentStyle) const 0160 { 0161 const char* elementName = 0; 0162 for (int i=0; i<s_propertyNamesCount; ++i) { 0163 if (s_propertyTypes[i] == type) { 0164 elementName = s_propertyNames[i]; 0165 } 0166 } 0167 Q_ASSERT(elementName); 0168 const StyleMap& map = m_properties[type]; 0169 const StyleMap& mapChild = m_childProperties[type]; 0170 if (!map.isEmpty() || !mapChild.isEmpty()) { 0171 writer->startElement(elementName); 0172 QMap<QString, QString>::const_iterator it = map.constBegin(); 0173 const QMap<QString, QString>::const_iterator end = map.constEnd(); 0174 for (; it != end; ++it) { 0175 if (!parentStyle || parentStyle->property(it.key(), type) != it.value()) 0176 writer->addAttribute(it.key().toUtf8(), it.value().toUtf8()); 0177 } 0178 QMap<QString, QString>::const_iterator itChild = mapChild.constBegin(); 0179 const QMap<QString, QString>::const_iterator endChild = mapChild.constEnd(); 0180 for (; itChild != endChild; ++itChild) { 0181 if (!parentStyle || parentStyle->childProperty(itChild.key(), type) != itChild.value()) 0182 writer->addCompleteElement(itChild.value().toUtf8()); 0183 } 0184 writer->endElement(); 0185 } 0186 } 0187 0188 void KoGenStyle::writeStyle(KoXmlWriter* writer, const KoGenStyles& styles, const char* elementName, const QString& name, const char* propertiesElementName, bool closeElement, bool drawElement) const 0189 { 0190 //debugOdf <<"writing out style" << name <<" display-name=" << m_attributes["style:display-name"] <<" family=" << m_familyName; 0191 writer->startElement(elementName); 0192 const KoGenStyle* parentStyle = 0; 0193 if (!m_defaultStyle) { 0194 if (!drawElement) 0195 writer->addAttribute("style:name", name); 0196 else 0197 writer->addAttribute("draw:name", name); 0198 if (!m_parentName.isEmpty()) { 0199 Q_ASSERT(!m_familyName.isEmpty()); 0200 parentStyle = styles.style(m_parentName, m_familyName); 0201 if (parentStyle && m_familyName.isEmpty()) { 0202 // get family from parent style, just in case 0203 // Note: this is saving code, don't convert to attributeNS! 0204 const_cast<KoGenStyle *>(this)-> 0205 m_familyName = parentStyle->attribute("style:family").toLatin1(); 0206 //debugOdf <<"Got familyname" << m_familyName <<" from parent"; 0207 } 0208 if (parentStyle && !parentStyle->isDefaultStyle()) 0209 writer->addAttribute("style:parent-style-name", m_parentName); 0210 } 0211 } else { // default-style 0212 Q_ASSERT(qstrcmp(elementName, "style:default-style") == 0); 0213 Q_ASSERT(m_parentName.isEmpty()); 0214 } 0215 if (!m_familyName.isEmpty()) 0216 const_cast<KoGenStyle *>(this)-> 0217 addAttribute("style:family", QString::fromLatin1(m_familyName)); 0218 else { 0219 if (qstrcmp(elementName, "style:style") == 0) 0220 warnOdf << "User style " << name << " is without family - invalid. m_type=" << m_type; 0221 } 0222 0223 #if 0 // #ifndef NDEBUG 0224 debugOdf << "style:" << name; 0225 printDebug(); 0226 if (parentStyle) { 0227 debugOdf << " parent:" << m_parentName; 0228 parentStyle->printDebug(); 0229 } 0230 #endif 0231 0232 // Write attributes [which differ from the parent style] 0233 // We only look at the direct parent style because we assume 0234 // that styles are fully specified, i.e. the inheritance is 0235 // only in the final file, not in the caller's code. 0236 QMap<QString, QString>::const_iterator it = m_attributes.constBegin(); 0237 for (; it != m_attributes.constEnd(); ++it) { 0238 bool writeit = true; 0239 if (parentStyle && it.key() != "style:family" // always write the family out 0240 && parentStyle->attribute(it.key()) == it.value()) 0241 writeit = false; 0242 if (writeit) 0243 writer->addAttribute(it.key().toUtf8(), it.value().toUtf8()); 0244 } 0245 bool createPropertiesTag = propertiesElementName && propertiesElementName[0] != '\0'; 0246 KoGenStyle::PropertyType i = KoGenStyle::DefaultType; 0247 KoGenStyle::PropertyType defaultPropertyType = KoGenStyle::DefaultType; 0248 if (createPropertiesTag) 0249 defaultPropertyType = propertyTypeByElementName(propertiesElementName); 0250 if (!m_properties[i].isEmpty() || 0251 !m_childProperties[defaultPropertyType].isEmpty() || 0252 !m_properties[defaultPropertyType].isEmpty()) { 0253 if (createPropertiesTag) 0254 writer->startElement(propertiesElementName); // e.g. paragraph-properties 0255 it = m_properties[i].constBegin(); 0256 for (; it != m_properties[i].constEnd(); ++it) { 0257 if (!parentStyle || parentStyle->property(it.key(), i) != it.value()) 0258 writer->addAttribute(it.key().toUtf8(), it.value().toUtf8()); 0259 } 0260 //write the explicitly-defined properties that are the same type as the default, 0261 //but only if defaultPropertyType is Text, Paragraph, or GraphicType 0262 if (defaultPropertyType != 0) { 0263 it = m_properties[defaultPropertyType].constBegin(); 0264 for (; it != m_properties[defaultPropertyType].constEnd(); ++it) { 0265 if (!parentStyle || parentStyle->property(it .key(), defaultPropertyType) != it.value()) 0266 writer->addAttribute(it.key().toUtf8(), it.value().toUtf8()); 0267 } 0268 } 0269 //write child elements of the properties elements 0270 it = m_childProperties[defaultPropertyType].constBegin(); 0271 for (; it != m_childProperties[defaultPropertyType].constEnd(); ++it) { 0272 if (!parentStyle || parentStyle->childProperty(it.key(), defaultPropertyType) != it.value()) { 0273 writer->addCompleteElement(it.value().toUtf8()); 0274 } 0275 } 0276 if (createPropertiesTag) 0277 writer->endElement(); 0278 } 0279 0280 // now write out any other properties elements 0281 //start with i=1 to skip the defaultType that we already took care of 0282 for (int i = 1; i < s_propertyNamesCount; ++i) { 0283 //skip any properties that are the same as the defaultType 0284 if (s_propertyTypes[i] != defaultPropertyType) { 0285 writeStyleProperties(writer, s_propertyTypes[i], parentStyle); 0286 } 0287 } 0288 0289 //write child elements that aren't in any of the properties elements 0290 i = KoGenStyle::StyleChildElement; 0291 it = m_properties[i].constBegin(); 0292 for (; it != m_properties[i].constEnd(); ++it) { 0293 if (!parentStyle || parentStyle->property(it.key(), i) != it.value()) { 0294 writer->addCompleteElement(it.value().toUtf8()); 0295 } 0296 } 0297 0298 // And now the style maps 0299 for (int i = 0; i < m_maps.count(); ++i) { 0300 bool writeit = true; 0301 if (parentStyle && compareMap(m_maps[i], parentStyle->m_maps[i]) == 0) 0302 writeit = false; 0303 if (writeit) { 0304 writer->startElement("style:map"); 0305 QMap<QString, QString>::const_iterator it = m_maps[i].constBegin(); 0306 for (; it != m_maps[i].constEnd(); ++it) { 0307 writer->addAttribute(it.key().toUtf8(), it.value().toUtf8()); 0308 } 0309 writer->endElement(); // style:map 0310 } 0311 } 0312 if (closeElement) 0313 writer->endElement(); 0314 } 0315 0316 void KoGenStyle::addPropertyPt(const QString& propName, qreal propValue, PropertyType type) 0317 { 0318 if (type == DefaultType) { 0319 type = m_propertyType; 0320 } 0321 QString str; 0322 str.setNum(propValue, 'f', DBL_DIG); 0323 str += "pt"; 0324 m_properties[type].insert(propName, str); 0325 } 0326 0327 void KoGenStyle::addPropertyLength(const QString& propName, const QTextLength &propValue, PropertyType type) 0328 { 0329 if (type == DefaultType) { 0330 type = m_propertyType; 0331 } 0332 if (propValue.type() == QTextLength::FixedLength) { 0333 return addPropertyPt(propName, propValue.rawValue(), type); 0334 } else { 0335 QString str; 0336 str.setNum((int) propValue.rawValue()); 0337 str += '%'; 0338 m_properties[type].insert(propName, str); 0339 } 0340 } 0341 0342 void KoGenStyle::addAttributePt(const QString& attrName, qreal attrValue) 0343 { 0344 QString str; 0345 str.setNum(attrValue, 'f', DBL_DIG); 0346 str += "pt"; 0347 m_attributes.insert(attrName, str); 0348 } 0349 0350 void KoGenStyle::addAttributePercent(const QString &attrName, qreal value) 0351 { 0352 QByteArray str; 0353 str.setNum(value, 'f', FLT_DIG); 0354 str += '%'; 0355 addAttribute(attrName, str.data()); 0356 } 0357 0358 void KoGenStyle::addAttributePercent(const QString &attrName, int value) 0359 { 0360 QByteArray str; 0361 str.setNum(value); 0362 str += '%'; 0363 addAttribute(attrName, str.data()); 0364 } 0365 0366 void KoGenStyle::addStyleMap(const QMap<QString, QString>& styleMap) 0367 { 0368 // check, if already present 0369 for (int i = 0 ; i < m_maps.count() ; ++i) { 0370 if (m_maps[i].count() == styleMap.count()) { 0371 int comp = compareMap(m_maps[i], styleMap); 0372 if (comp == 0) 0373 return; 0374 } 0375 } 0376 m_maps.append(styleMap); 0377 } 0378 0379 0380 #ifndef NDEBUG 0381 void KoGenStyle::printDebug() const 0382 { 0383 int i = DefaultType; 0384 debugOdf << m_properties[i].count() << " properties."; 0385 for (QMap<QString, QString>::ConstIterator it = m_properties[i].constBegin(); it != m_properties[i].constEnd(); ++it) { 0386 debugOdf << "" << it.key() << " =" << it.value(); 0387 } 0388 i = TextType; 0389 debugOdf << m_properties[i].count() << " text properties."; 0390 for (QMap<QString, QString>::ConstIterator it = m_properties[i].constBegin(); it != m_properties[i].constEnd(); ++it) { 0391 debugOdf << "" << it.key() << " =" << it.value(); 0392 } 0393 i = ParagraphType; 0394 debugOdf << m_properties[i].count() << " paragraph properties."; 0395 for (QMap<QString, QString>::ConstIterator it = m_properties[i].constBegin(); it != m_properties[i].constEnd(); ++it) { 0396 debugOdf << "" << it.key() << " =" << it.value(); 0397 } 0398 i = TextType; 0399 debugOdf << m_childProperties[i].count() << " text child elements."; 0400 for (QMap<QString, QString>::ConstIterator it = m_childProperties[i].constBegin(); it != m_childProperties[i].constEnd(); ++it) { 0401 debugOdf << "" << it.key() << " =" << it.value(); 0402 } 0403 i = ParagraphType; 0404 debugOdf << m_childProperties[i].count() << " paragraph child elements."; 0405 for (QMap<QString, QString>::ConstIterator it = m_childProperties[i].constBegin(); it != m_childProperties[i].constEnd(); ++it) { 0406 debugOdf << "" << it.key() << " =" << it.value(); 0407 } 0408 debugOdf << m_attributes.count() << " attributes."; 0409 for (QMap<QString, QString>::ConstIterator it = m_attributes.constBegin(); it != m_attributes.constEnd(); ++it) { 0410 debugOdf << "" << it.key() << " =" << it.value(); 0411 } 0412 debugOdf << m_maps.count() << " maps."; 0413 for (int i = 0; i < m_maps.count(); ++i) { 0414 debugOdf << "map" << i << ":"; 0415 for (QMap<QString, QString>::ConstIterator it = m_maps[i].constBegin(); it != m_maps[i].constEnd(); ++it) { 0416 debugOdf << "" << it.key() << " =" << it.value(); 0417 } 0418 } 0419 debugOdf; 0420 } 0421 #endif 0422 0423 bool KoGenStyle::operator<(const KoGenStyle &other) const 0424 { 0425 if (m_type != other.m_type) return m_type < other.m_type; 0426 if (m_parentName != other.m_parentName) return m_parentName < other.m_parentName; 0427 if (m_familyName != other.m_familyName) return m_familyName < other.m_familyName; 0428 if (m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml) return m_autoStyleInStylesDotXml; 0429 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0430 if (m_properties[i].count() != other.m_properties[i].count()) { 0431 return m_properties[i].count() < other.m_properties[i].count(); 0432 } 0433 if (m_childProperties[i].count() != other.m_childProperties[i].count()) { 0434 return m_childProperties[i].count() < other.m_childProperties[i].count(); 0435 } 0436 } 0437 if (m_attributes.count() != other.m_attributes.count()) return m_attributes.count() < other.m_attributes.count(); 0438 if (m_maps.count() != other.m_maps.count()) return m_maps.count() < other.m_maps.count(); 0439 // Same number of properties and attributes, no other choice than iterating 0440 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0441 int comp = compareMap(m_properties[i], other.m_properties[i]); 0442 if (comp != 0) 0443 return comp < 0; 0444 } 0445 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0446 int comp = compareMap(m_childProperties[i], other.m_childProperties[i]); 0447 if (comp != 0) 0448 return comp < 0; 0449 } 0450 int comp = compareMap(m_attributes, other.m_attributes); 0451 if (comp != 0) 0452 return comp < 0; 0453 for (int i = 0 ; i < m_maps.count() ; ++i) { 0454 int comp = compareMap(m_maps[i], other.m_maps[i]); 0455 if (comp != 0) 0456 return comp < 0; 0457 } 0458 return false; 0459 } 0460 0461 bool KoGenStyle::operator==(const KoGenStyle &other) const 0462 { 0463 if (m_type != other.m_type) return false; 0464 if (m_parentName != other.m_parentName) return false; 0465 if (m_familyName != other.m_familyName) return false; 0466 if (m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml) return false; 0467 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0468 if (m_properties[i].count() != other.m_properties[i].count()) { 0469 return false; 0470 } 0471 if (m_childProperties[i].count() != other.m_childProperties[i].count()) { 0472 return false; 0473 } 0474 } 0475 if (m_attributes.count() != other.m_attributes.count()) return false; 0476 if (m_maps.count() != other.m_maps.count()) return false; 0477 // Same number of properties and attributes, no other choice than iterating 0478 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0479 int comp = compareMap(m_properties[i], other.m_properties[i]); 0480 if (comp != 0) 0481 return false; 0482 } 0483 for (uint i = 0 ; i <= LastPropertyType; ++i) { 0484 int comp = compareMap(m_childProperties[i], other.m_childProperties[i]); 0485 if (comp != 0) 0486 return false; 0487 } 0488 int comp = compareMap(m_attributes, other.m_attributes); 0489 if (comp != 0) 0490 return false; 0491 for (int i = 0 ; i < m_maps.count() ; ++i) { 0492 int comp = compareMap(m_maps[i], other.m_maps[i]); 0493 if (comp != 0) 0494 return false; 0495 } 0496 return true; 0497 } 0498 0499 bool KoGenStyle::isEmpty() const 0500 { 0501 if (!m_attributes.isEmpty() || ! m_maps.isEmpty()) 0502 return false; 0503 for (uint i = 0 ; i <= LastPropertyType; ++i) 0504 if (! m_properties[i].isEmpty()) 0505 return false; 0506 return true; 0507 } 0508 0509 void KoGenStyle::copyPropertiesFromStyle(const KoGenStyle &sourceStyle, KoGenStyle &targetStyle, PropertyType type) 0510 { 0511 if (type == DefaultType) { 0512 type = sourceStyle.m_propertyType; 0513 } 0514 0515 const StyleMap& map = sourceStyle.m_properties[type]; 0516 if (!map.isEmpty()) { 0517 QMap<QString, QString>::const_iterator it = map.constBegin(); 0518 const QMap<QString, QString>::const_iterator end = map.constEnd(); 0519 for (; it != end; ++it) { 0520 targetStyle.addProperty(it.key(), it.value(), type); 0521 } 0522 } 0523 }