lib

KoGenStyles.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004-2006 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  * Boston, MA 02110-1301, USA.
00017 */
00018 
00019 #include "KoGenStyles.h"
00020 #include <KoXmlWriter.h>
00021 #include <float.h>
00022 #include <kdebug.h>
00023 
00024 KoGenStyles::KoGenStyles()
00025 {
00026 }
00027 
00028 KoGenStyles::~KoGenStyles()
00029 {
00030 }
00031 
00032 QString KoGenStyles::lookup( const KoGenStyle& style, const QString& name, int flags )
00033 {
00034     StyleMap::iterator it = m_styleMap.find( style );
00035     if ( it == m_styleMap.end() ) {
00036         // Not found, try if this style is in fact equal to its parent (the find above
00037         // wouldn't have found it, due to m_parentName being set).
00038         if ( !style.parentName().isEmpty() ) {
00039             KoGenStyle testStyle( style );
00040             const KoGenStyle* parentStyle = this->style( style.parentName() ); // ## linear search
00041             if( !parentStyle ) {
00042                 kdDebug(30003) << "KoGenStyles::lookup(" << name << "): parent style '" << style.parentName() << "' not found in collection" << endl;
00043             } else {
00044                 if ( testStyle.m_familyName != parentStyle->m_familyName )
00045                 {
00046                     kdWarning(30003) << "KoGenStyles::lookup(" << name << ", family=" << testStyle.m_familyName << ") parent style '" << style.parentName() << "' has a different family: " << parentStyle->m_familyName << endl;
00047                 }
00048 
00049                 testStyle.m_parentName = parentStyle->m_parentName;
00050                 // Exclude the type from the comparison. It's ok for an auto style
00051                 // to have a user style as parent; they can still be identical
00052                 testStyle.m_type = parentStyle->m_type;
00053                 // Also it's ok to not have the display name of the parent style
00054                 // in the auto style
00055                 QMap<QString, QString>::const_iterator it = parentStyle->m_attributes.find( "style:display-name" );
00056                 if ( it != parentStyle->m_attributes.end() )
00057                     testStyle.addAttribute( "style:display-name", *it );
00058 
00059                 if ( *parentStyle == testStyle )
00060                     return style.parentName();
00061             }
00062         }
00063 
00064         QString styleName( name );
00065         if ( styleName.isEmpty() ) {
00066             styleName = 'A'; // for "auto".
00067             flags &= ~DontForceNumbering; // i.e. force numbering
00068         }
00069         styleName = makeUniqueName( styleName, flags );
00070         if ( style.autoStyleInStylesDotXml() )
00071             m_autoStylesInStylesDotXml.insert( styleName, true /*unused*/ );
00072         else
00073             m_styleNames.insert( styleName, true /*unused*/ );
00074         it = m_styleMap.insert( style, styleName );
00075         NamedStyle s;
00076         s.style = &it.key();
00077         s.name = styleName;
00078         m_styleArray.append( s );
00079     }
00080     return it.data();
00081 }
00082 
00083 QString KoGenStyles::makeUniqueName( const QString& base, int flags ) const
00084 {
00085     // If this name is not used yet, and numbering isn't forced, then the given name is ok.
00086     if ( ( flags & DontForceNumbering )
00087          && m_autoStylesInStylesDotXml.find( base ) == m_autoStylesInStylesDotXml.end()
00088          && m_styleNames.find( base ) == m_styleNames.end() )
00089         return base;
00090     int num = 1;
00091     QString name;
00092     do {
00093         name = base;
00094         name += QString::number( num++ );
00095     } while ( m_autoStylesInStylesDotXml.find( name ) != m_autoStylesInStylesDotXml.end()
00096               || m_styleNames.find( name ) != m_styleNames.end() );
00097     return name;
00098 }
00099 
00100 QValueList<KoGenStyles::NamedStyle> KoGenStyles::styles( int type, bool markedForStylesXml ) const
00101 {
00102     QValueList<KoGenStyles::NamedStyle> lst;
00103     const NameMap& nameMap = markedForStylesXml ? m_autoStylesInStylesDotXml : m_styleNames;
00104     StyleArray::const_iterator it = m_styleArray.begin();
00105     const StyleArray::const_iterator end = m_styleArray.end();
00106     for ( ; it != end ; ++it ) {
00107         // Look up if it's marked for styles.xml or not by looking up in the corresponding style map.
00108         if ( (*it).style->type() == type && nameMap.find((*it).name) != nameMap.end() ) {
00109             lst.append( *it );
00110         }
00111     }
00112     return lst;
00113 }
00114 
00115 const KoGenStyle* KoGenStyles::style( const QString& name ) const
00116 {
00117     StyleArray::const_iterator it = m_styleArray.begin();
00118     const StyleArray::const_iterator end = m_styleArray.end();
00119     for ( ; it != end ; ++it ) {
00120         if ( (*it).name == name )
00121             return (*it).style;
00122     }
00123     return 0;
00124 }
00125 
00126 KoGenStyle* KoGenStyles::styleForModification( const QString& name )
00127 {
00128     return const_cast<KoGenStyle *>( style( name ) );
00129 }
00130 
00131 void KoGenStyles::markStyleForStylesXml( const QString& name )
00132 {
00133     Q_ASSERT( m_styleNames.find( name ) != m_styleNames.end() );
00134     m_styleNames.remove( name );
00135     m_autoStylesInStylesDotXml.insert( name, true );
00136     styleForModification( name )->setAutoStyleInStylesDotXml( true );
00137 }
00138 
00139 void KoGenStyles::dump()
00140 {
00141     kdDebug() << "Style array:" << endl;
00142     StyleArray::const_iterator it = m_styleArray.begin();
00143     const StyleArray::const_iterator end = m_styleArray.end();
00144     for ( ; it != end ; ++it ) {
00145         kdDebug() << (*it).name << endl;
00146     }
00147     for ( NameMap::const_iterator it = m_styleNames.begin(); it != m_styleNames.end(); ++it ) {
00148         kdDebug() << "style: " << it.key() << endl;
00149     }
00150     for ( NameMap::const_iterator it = m_autoStylesInStylesDotXml.begin(); it != m_autoStylesInStylesDotXml.end(); ++it ) {
00151         kdDebug() << "auto style for style.xml: " << it.key() << endl;
00152         const KoGenStyle* s = style( it.key() );
00153         Q_ASSERT( s );
00154         Q_ASSERT( s->autoStyleInStylesDotXml() );
00155     }
00156 }
00157 
00158 // Returns -1, 0 (equal) or 1
00159 static int compareMap( const QMap<QString, QString>& map1, const QMap<QString, QString>& map2 )
00160 {
00161     QMap<QString, QString>::const_iterator it = map1.begin();
00162     QMap<QString, QString>::const_iterator oit = map2.begin();
00163     for ( ; it != map1.end(); ++it, ++oit ) { // both maps have been checked for size already
00164         if ( it.key() != oit.key() )
00165             return it.key() < oit.key() ? -1 : +1;
00166         if ( it.data() != oit.data() )
00167             return it.data() < oit.data() ? -1 : +1;
00168     }
00169     return 0; // equal
00170 }
00171 
00173 
00174 
00175 KoGenStyle::KoGenStyle( int type, const char* familyName,
00176                         const QString& parentName )
00177     : m_type( type ), m_familyName( familyName ), m_parentName( parentName ),
00178       m_autoStyleInStylesDotXml( false ), m_defaultStyle( false )
00179 {
00180 }
00181 
00182 KoGenStyle::~KoGenStyle()
00183 {
00184 }
00185 
00186 void KoGenStyle::writeStyleProperties( KoXmlWriter* writer, PropertyType i,
00187                                        const char* elementName, const KoGenStyle* parentStyle ) const
00188 {
00189     if ( !m_properties[i].isEmpty() ) {
00190         writer->startElement( elementName );
00191         QMap<QString, QString>::const_iterator it = m_properties[i].begin();
00192         const QMap<QString, QString>::const_iterator end = m_properties[i].end();
00193         for ( ; it != end; ++it ) {
00194             if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
00195                 writer->addAttribute( it.key().utf8(), it.data().utf8() );
00196         }
00197         writer->endElement();
00198     }
00199 }
00200 
00201 void KoGenStyle::writeStyle( KoXmlWriter* writer, KoGenStyles& styles, const char* elementName, const QString& name, const char* propertiesElementName, bool closeElement, bool drawElement ) const
00202 {
00203     //kdDebug(30003) << "writing out style " << name << " display-name=" << m_attributes["style:display-name"] << " family=" << m_familyName << endl;
00204     writer->startElement( elementName );
00205     const KoGenStyle* parentStyle = 0;
00206     if ( !m_defaultStyle ) {
00207         if ( !drawElement )
00208             writer->addAttribute( "style:name", name );
00209         else
00210             writer->addAttribute( "draw:name", name );
00211         if ( !m_parentName.isEmpty() ) {
00212             parentStyle = styles.style( m_parentName );
00213             if ( parentStyle && m_familyName.isEmpty() ) {
00214                 // get family from parent style, just in case
00215                 // Note: this is saving code, don't convert to attributeNS!
00216                 const_cast<KoGenStyle *>( this )->
00217                     m_familyName = parentStyle->attribute( "style:family" ).latin1();
00218                 //kdDebug(30003) << "Got familyname " << m_familyName << " from parent" << endl;
00219             }
00220             writer->addAttribute( "style:parent-style-name", m_parentName );
00221         }
00222     } else { // default-style
00223         Q_ASSERT( qstrcmp( elementName, "style:default-style" ) == 0 );
00224         Q_ASSERT( m_parentName.isEmpty() );
00225     }
00226     if ( !m_familyName.isEmpty() )
00227         const_cast<KoGenStyle *>( this )->
00228             addAttribute( "style:family", QString::fromLatin1( m_familyName ) );
00229     else {
00230         if ( qstrcmp( elementName, "style:style" ) == 0 )
00231             kdWarning(30003) << "User style " << name << " is without family - invalid. m_type=" << m_type << endl;
00232     }
00233 
00234 #if 0 // #ifndef NDEBUG
00235     kdDebug() << "style: " << name << endl;
00236     printDebug();
00237     if ( parentStyle ) {
00238         kdDebug() << " parent: " << m_parentName << endl;
00239         parentStyle->printDebug();
00240     }
00241 #endif
00242 
00243     // Write attributes [which differ from the parent style]
00244     // We only look at the direct parent style because we assume
00245     // that styles are fully specified, i.e. the inheritance is
00246     // only in the final file, not in the caller's code.
00247     QMap<QString, QString>::const_iterator it = m_attributes.begin();
00248     for ( ; it != m_attributes.end(); ++it ) {
00249         bool writeit = true;
00250         if ( parentStyle && it.key() != "style:family" // always write the family out
00251              && parentStyle->attribute( it.key() ) == it.data() )
00252             writeit = false;
00253         if ( writeit )
00254             writer->addAttribute( it.key().utf8(), it.data().utf8() );
00255     }
00256     bool createPropertiesTag = propertiesElementName && propertiesElementName[0] != '\0';
00257     KoGenStyle::PropertyType i = KoGenStyle::DefaultType;
00258     if ( !m_properties[i].isEmpty() ||
00259          !m_properties[KoGenStyle::ChildElement].isEmpty() ) {
00260         if ( createPropertiesTag )
00261             writer->startElement( propertiesElementName ); // e.g. paragraph-properties
00262         it = m_properties[i].begin();
00263         for ( ; it != m_properties[i].end(); ++it ) {
00264             if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
00265                 writer->addAttribute( it.key().utf8(), it.data().utf8() );
00266         }
00267         i = KoGenStyle::ChildElement;
00268         it = m_properties[i].begin();
00269         for ( ; it != m_properties[i].end(); ++it ) {
00270             if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() ) {
00271                 writer->addCompleteElement( it.data().utf8() );
00272             }
00273         }
00274         if ( createPropertiesTag )
00275             writer->endElement();
00276     }
00277     writeStyleProperties( writer, KoGenStyle::GraphicType, "style:graphic-properties", parentStyle );
00278     writeStyleProperties( writer, KoGenStyle::TextType, "style:text-properties", parentStyle );
00279     writeStyleProperties( writer, KoGenStyle::ParagraphType, "style:paragraph-properties", parentStyle );
00280 
00281     // And now the style maps
00282     for ( uint i = 0; i < m_maps.count(); ++i ) {
00283         bool writeit = true;
00284         if ( parentStyle && compareMap( m_maps[i], parentStyle->m_maps[i] ) == 0 )
00285             writeit = false;
00286         if ( writeit ) {
00287             writer->startElement( "style:map" );
00288             QMap<QString, QString>::const_iterator it = m_maps[i].begin();
00289             for ( ; it != m_maps[i].end(); ++it ) {
00290                 writer->addAttribute( it.key().utf8(), it.data().utf8() );
00291             }
00292             writer->endElement(); // style:map
00293         }
00294     }
00295     if ( closeElement )
00296         writer->endElement();
00297 }
00298 
00299 void KoGenStyle::addPropertyPt( const QString& propName, double propValue, PropertyType type )
00300 {
00301     QString str;
00302     str.setNum( propValue, 'g', DBL_DIG );
00303     str += "pt";
00304     m_properties[type].insert( propName, str );
00305 }
00306 
00307 void KoGenStyle::addAttributePt( const QString& attrName, double attrValue )
00308 {
00309     QString str;
00310     str.setNum( attrValue, 'g', DBL_DIG );
00311     str += "pt";
00312     m_attributes.insert( attrName, str );
00313 }
00314 
00315 #ifndef NDEBUG
00316 void KoGenStyle::printDebug() const
00317 {
00318     int i = DefaultType;
00319     kdDebug() << m_properties[i].count() << " properties." << endl;
00320     for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
00321         kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00322     }
00323     i = TextType;
00324     kdDebug() << m_properties[i].count() << " text properties." << endl;
00325     for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
00326         kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00327     }
00328     i = ParagraphType;
00329     kdDebug() << m_properties[i].count() << " paragraph properties." << endl;
00330     for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
00331         kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00332     }
00333     i = ChildElement;
00334     kdDebug() << m_properties[i].count() << " child elements." << endl;
00335     for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
00336         kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00337     }
00338     kdDebug() << m_attributes.count() << " attributes." << endl;
00339     for( QMap<QString,QString>::ConstIterator it = m_attributes.begin(); it != m_attributes.end(); ++it ) {
00340         kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00341     }
00342     kdDebug() << m_maps.count() << " maps." << endl;
00343     for ( uint i = 0; i < m_maps.count(); ++i ) {
00344         kdDebug() << "map " << i << ":" << endl;
00345         for( QMap<QString,QString>::ConstIterator it = m_maps[i].begin(); it != m_maps[i].end(); ++it ) {
00346             kdDebug() << "     " << it.key() << " = " << it.data() << endl;
00347         }
00348     }
00349     kdDebug() << endl;
00350 }
00351 #endif
00352 
00353 bool KoGenStyle::operator<( const KoGenStyle &other ) const
00354 {
00355     if ( m_type != other.m_type ) return m_type < other.m_type;
00356     if ( m_parentName != other.m_parentName ) return m_parentName < other.m_parentName;
00357     if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return m_autoStyleInStylesDotXml;
00358     for ( uint i = 0 ; i < N_NumTypes ; ++i )
00359         if ( m_properties[i].count() != other.m_properties[i].count() )
00360             return m_properties[i].count() < other.m_properties[i].count();
00361     if ( m_attributes.count() != other.m_attributes.count() ) return m_attributes.count() < other.m_attributes.count();
00362     if ( m_maps.count() != other.m_maps.count() ) return m_maps.count() < other.m_maps.count();
00363     // Same number of properties and attributes, no other choice than iterating
00364     for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
00365         int comp = compareMap( m_properties[i], other.m_properties[i] );
00366         if ( comp != 0 )
00367             return comp < 0;
00368     }
00369     int comp = compareMap( m_attributes, other.m_attributes );
00370     if ( comp != 0 )
00371         return comp < 0;
00372     for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
00373         int comp = compareMap( m_maps[i], other.m_maps[i] );
00374         if ( comp != 0 )
00375             return comp < 0;
00376     }
00377     return false;
00378 }
00379 
00380 bool KoGenStyle::operator==( const KoGenStyle &other ) const
00381 {
00382     if ( m_type != other.m_type ) return false;
00383     if ( m_parentName != other.m_parentName ) return false;
00384     if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return false;
00385     for ( uint i = 0 ; i < N_NumTypes ; ++i )
00386         if ( m_properties[i].count() != other.m_properties[i].count() )
00387             return false;
00388     if ( m_attributes.count() != other.m_attributes.count() ) return false;
00389     if ( m_maps.count() != other.m_maps.count() ) return false;
00390     // Same number of properties and attributes, no other choice than iterating
00391     for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
00392         int comp = compareMap( m_properties[i], other.m_properties[i] );
00393         if ( comp != 0 )
00394             return false;
00395     }
00396     int comp = compareMap( m_attributes, other.m_attributes );
00397     if ( comp != 0 )
00398         return false;
00399     for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
00400         int comp = compareMap( m_maps[i], other.m_maps[i] );
00401         if ( comp != 0 )
00402             return false;
00403     }
00404     return true;
00405 }
KDE Home | KDE Accessibility Home | Description of Access Keys