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 }