/* This file is part of the KDE project
Copyright ( C ) 2004 - 2006 David Faure < faure @ kde . org >
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation .
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Library General Public License for more details .
You should have received a copy of the GNU Library General Public License
along with this library ; see the file COPYING . LIB . If not , write to
the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# include "KoGenStyles.h"
# include <KoXmlWriter.h>
# include <float.h>
# include <kdebug.h>
KoGenStyles : : KoGenStyles ( )
{
}
KoGenStyles : : ~ KoGenStyles ( )
{
}
TQString KoGenStyles : : lookup ( const KoGenStyle & style , const TQString & name , int flags )
{
StyleMap : : iterator it = m_styleMap . find ( style ) ;
if ( it = = m_styleMap . end ( ) ) {
// Not found, try if this style is in fact equal to its parent (the find above
// wouldn't have found it, due to m_parentName being set).
if ( ! style . parentName ( ) . isEmpty ( ) ) {
KoGenStyle testStyle ( style ) ;
const KoGenStyle * parentStyle = this - > style ( style . parentName ( ) ) ; // ## linear search
if ( ! parentStyle ) {
kdDebug ( 30003 ) < < " KoGenStyles::lookup( " < < name < < " ): parent style ' " < < style . parentName ( ) < < " ' not found in collection " < < endl ;
} else {
if ( testStyle . m_familyName ! = parentStyle - > m_familyName )
{
kdWarning ( 30003 ) < < " KoGenStyles::lookup( " < < name < < " , family= " < < testStyle . m_familyName < < " ) parent style ' " < < style . parentName ( ) < < " ' has a different family: " < < parentStyle - > m_familyName < < endl ;
}
testStyle . m_parentName = parentStyle - > m_parentName ;
// Exclude the type from the comparison. It's ok for an auto style
// to have a user style as parent; they can still be identical
testStyle . m_type = parentStyle - > m_type ;
// Also it's ok to not have the display name of the parent style
// in the auto style
TQMap < TQString , TQString > : : const_iterator it = parentStyle - > m_attributes . find ( " style:display-name " ) ;
if ( it ! = parentStyle - > m_attributes . end ( ) )
testStyle . addAttribute ( " style:display-name " , * it ) ;
if ( * parentStyle = = testStyle )
return style . parentName ( ) ;
}
}
TQString styleName ( name ) ;
if ( styleName . isEmpty ( ) ) {
styleName = ' A ' ; // for "auto".
flags & = ~ DontForceNumbering ; // i.e. force numbering
}
styleName = makeUniqueName ( styleName , flags ) ;
if ( style . autoStyleInStylesDotXml ( ) )
m_autoStylesInStylesDotXml . insert ( styleName , true /*unused*/ ) ;
else
m_styleNames . insert ( styleName , true /*unused*/ ) ;
it = m_styleMap . insert ( style , styleName ) ;
NamedStyle s ;
s . style = & it . key ( ) ;
s . name = styleName ;
m_styleArray . append ( s ) ;
}
return it . data ( ) ;
}
TQString KoGenStyles : : makeUniqueName ( const TQString & base , int flags ) const
{
// If this name is not used yet, and numbering isn't forced, then the given name is ok.
if ( ( flags & DontForceNumbering )
& & m_autoStylesInStylesDotXml . find ( base ) = = m_autoStylesInStylesDotXml . end ( )
& & m_styleNames . find ( base ) = = m_styleNames . end ( ) )
return base ;
int num = 1 ;
TQString name ;
do {
name = base ;
name + = TQString : : number ( num + + ) ;
} while ( m_autoStylesInStylesDotXml . find ( name ) ! = m_autoStylesInStylesDotXml . end ( )
| | m_styleNames . find ( name ) ! = m_styleNames . end ( ) ) ;
return name ;
}
TQValueList < KoGenStyles : : NamedStyle > KoGenStyles : : styles ( int type , bool markedForStylesXml ) const
{
TQValueList < KoGenStyles : : NamedStyle > lst ;
const NameMap & nameMap = markedForStylesXml ? m_autoStylesInStylesDotXml : m_styleNames ;
StyleArray : : const_iterator it = m_styleArray . begin ( ) ;
const StyleArray : : const_iterator end = m_styleArray . end ( ) ;
for ( ; it ! = end ; + + it ) {
// Look up if it's marked for styles.xml or not by looking up in the corresponding style map.
if ( ( * it ) . style - > type ( ) = = type & & nameMap . find ( ( * it ) . name ) ! = nameMap . end ( ) ) {
lst . append ( * it ) ;
}
}
return lst ;
}
const KoGenStyle * KoGenStyles : : style ( const TQString & name ) const
{
StyleArray : : const_iterator it = m_styleArray . begin ( ) ;
const StyleArray : : const_iterator end = m_styleArray . end ( ) ;
for ( ; it ! = end ; + + it ) {
if ( ( * it ) . name = = name )
return ( * it ) . style ;
}
return 0 ;
}
KoGenStyle * KoGenStyles : : styleForModification ( const TQString & name )
{
return const_cast < KoGenStyle * > ( style ( name ) ) ;
}
void KoGenStyles : : markStyleForStylesXml ( const TQString & name )
{
Q_ASSERT ( m_styleNames . find ( name ) ! = m_styleNames . end ( ) ) ;
m_styleNames . remove ( name ) ;
m_autoStylesInStylesDotXml . insert ( name , true ) ;
styleForModification ( name ) - > setAutoStyleInStylesDotXml ( true ) ;
}
void KoGenStyles : : dump ( )
{
kdDebug ( ) < < " Style array: " < < endl ;
StyleArray : : const_iterator it = m_styleArray . begin ( ) ;
const StyleArray : : const_iterator end = m_styleArray . end ( ) ;
for ( ; it ! = end ; + + it ) {
kdDebug ( ) < < ( * it ) . name < < endl ;
}
for ( NameMap : : const_iterator it = m_styleNames . begin ( ) ; it ! = m_styleNames . end ( ) ; + + it ) {
kdDebug ( ) < < " style: " < < it . key ( ) < < endl ;
}
for ( NameMap : : const_iterator it = m_autoStylesInStylesDotXml . begin ( ) ; it ! = m_autoStylesInStylesDotXml . end ( ) ; + + it ) {
kdDebug ( ) < < " auto style for style.xml: " < < it . key ( ) < < endl ;
const KoGenStyle * s = style ( it . key ( ) ) ;
Q_ASSERT ( s ) ;
Q_ASSERT ( s - > autoStyleInStylesDotXml ( ) ) ;
}
}
// Returns -1, 0 (equal) or 1
static int compareMap ( const TQMap < TQString , TQString > & map1 , const TQMap < TQString , TQString > & map2 )
{
TQMap < TQString , TQString > : : const_iterator it = map1 . begin ( ) ;
TQMap < TQString , TQString > : : const_iterator oit = map2 . begin ( ) ;
for ( ; it ! = map1 . end ( ) ; + + it , + + oit ) { // both maps have been checked for size already
if ( it . key ( ) ! = oit . key ( ) )
return it . key ( ) < oit . key ( ) ? - 1 : + 1 ;
if ( it . data ( ) ! = oit . data ( ) )
return it . data ( ) < oit . data ( ) ? - 1 : + 1 ;
}
return 0 ; // equal
}
////
KoGenStyle : : KoGenStyle ( int type , const char * familyName ,
const TQString & parentName )
: m_type ( type ) , m_familyName ( familyName ) , m_parentName ( parentName ) ,
m_autoStyleInStylesDotXml ( false ) , m_defaultStyle ( false )
{
}
KoGenStyle : : ~ KoGenStyle ( )
{
}
void KoGenStyle : : writeStyleProperties ( KoXmlWriter * writer , PropertyType i ,
const char * elementName , const KoGenStyle * parentStyle ) const
{
if ( ! m_properties [ i ] . isEmpty ( ) ) {
writer - > startElement ( elementName ) ;
TQMap < TQString , TQString > : : const_iterator it = m_properties [ i ] . begin ( ) ;
const TQMap < TQString , TQString > : : const_iterator end = m_properties [ i ] . end ( ) ;
for ( ; it ! = end ; + + it ) {
if ( ! parentStyle | | parentStyle - > property ( it . key ( ) , i ) ! = it . data ( ) )
writer - > addAttribute ( it . key ( ) . utf8 ( ) , it . data ( ) . utf8 ( ) ) ;
}
writer - > endElement ( ) ;
}
}
void KoGenStyle : : writeStyle ( KoXmlWriter * writer , KoGenStyles & styles , const char * elementName , const TQString & name , const char * propertiesElementName , bool closeElement , bool drawElement ) const
{
//kdDebug(30003) << "writing out style " << name << " display-name=" << m_attributes["style:display-name"] << " family=" << m_familyName << endl;
writer - > startElement ( elementName ) ;
const KoGenStyle * parentStyle = 0 ;
if ( ! m_defaultStyle ) {
if ( ! drawElement )
writer - > addAttribute ( " style:name " , name ) ;
else
writer - > addAttribute ( " draw:name " , name ) ;
if ( ! m_parentName . isEmpty ( ) ) {
parentStyle = styles . style ( m_parentName ) ;
if ( parentStyle & & m_familyName . isEmpty ( ) ) {
// get family from parent style, just in case
// Note: this is saving code, don't convert to attributeNS!
const_cast < KoGenStyle * > ( this ) - >
m_familyName = parentStyle - > attribute ( " style:family " ) . latin1 ( ) ;
//kdDebug(30003) << "Got familyname " << m_familyName << " from parent" << endl;
}
writer - > addAttribute ( " style:parent-style-name " , m_parentName ) ;
}
} else { // default-style
Q_ASSERT ( qstrcmp ( elementName , " style:default-style " ) = = 0 ) ;
Q_ASSERT ( m_parentName . isEmpty ( ) ) ;
}
if ( ! m_familyName . isEmpty ( ) )
const_cast < KoGenStyle * > ( this ) - >
addAttribute ( " style:family " , TQString : : fromLatin1 ( m_familyName ) ) ;
else {
if ( qstrcmp ( elementName , " style:style " ) = = 0 )
kdWarning ( 30003 ) < < " User style " < < name < < " is without family - invalid. m_type= " < < m_type < < endl ;
}
#if 0 // #ifndef NDEBUG
kdDebug ( ) < < " style: " < < name < < endl ;
printDebug ( ) ;
if ( parentStyle ) {
kdDebug ( ) < < " parent: " < < m_parentName < < endl ;
parentStyle - > printDebug ( ) ;
}
# endif
// Write attributes [which differ from the parent style]
// We only look at the direct parent style because we assume
// that styles are fully specified, i.e. the inheritance is
// only in the final file, not in the caller's code.
TQMap < TQString , TQString > : : const_iterator it = m_attributes . begin ( ) ;
for ( ; it ! = m_attributes . end ( ) ; + + it ) {
bool writeit = true ;
if ( parentStyle & & it . key ( ) ! = " style:family " // always write the family out
& & parentStyle - > attribute ( it . key ( ) ) = = it . data ( ) )
writeit = false ;
if ( writeit )
writer - > addAttribute ( it . key ( ) . utf8 ( ) , it . data ( ) . utf8 ( ) ) ;
}
bool createPropertiesTag = propertiesElementName & & propertiesElementName [ 0 ] ! = ' \0 ' ;
KoGenStyle : : PropertyType i = KoGenStyle : : DefaultType ;
if ( ! m_properties [ i ] . isEmpty ( ) | |
! m_properties [ KoGenStyle : : ChildElement ] . isEmpty ( ) ) {
if ( createPropertiesTag )
writer - > startElement ( propertiesElementName ) ; // e.g. paragraph-properties
it = m_properties [ i ] . begin ( ) ;
for ( ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
if ( ! parentStyle | | parentStyle - > property ( it . key ( ) , i ) ! = it . data ( ) )
writer - > addAttribute ( it . key ( ) . utf8 ( ) , it . data ( ) . utf8 ( ) ) ;
}
i = KoGenStyle : : ChildElement ;
it = m_properties [ i ] . begin ( ) ;
for ( ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
if ( ! parentStyle | | parentStyle - > property ( it . key ( ) , i ) ! = it . data ( ) ) {
writer - > addCompleteElement ( it . data ( ) . utf8 ( ) ) ;
}
}
if ( createPropertiesTag )
writer - > endElement ( ) ;
}
writeStyleProperties ( writer , KoGenStyle : : GraphicType , " style:graphic-properties " , parentStyle ) ;
writeStyleProperties ( writer , KoGenStyle : : ParagraphType , " style:paragraph-properties " , parentStyle ) ;
writeStyleProperties ( writer , KoGenStyle : : TextType , " style:text-properties " , parentStyle ) ;
// And now the style maps
for ( uint i = 0 ; i < m_maps . count ( ) ; + + i ) {
bool writeit = true ;
if ( parentStyle & & compareMap ( m_maps [ i ] , parentStyle - > m_maps [ i ] ) = = 0 )
writeit = false ;
if ( writeit ) {
writer - > startElement ( " style:map " ) ;
TQMap < TQString , TQString > : : const_iterator it = m_maps [ i ] . begin ( ) ;
for ( ; it ! = m_maps [ i ] . end ( ) ; + + it ) {
writer - > addAttribute ( it . key ( ) . utf8 ( ) , it . data ( ) . utf8 ( ) ) ;
}
writer - > endElement ( ) ; // style:map
}
}
if ( closeElement )
writer - > endElement ( ) ;
}
void KoGenStyle : : addPropertyPt ( const TQString & propName , double propValue , PropertyType type )
{
TQString str ;
str . setNum ( propValue , ' g ' , DBL_DIG ) ;
str + = " pt " ;
m_properties [ type ] . insert ( propName , str ) ;
}
void KoGenStyle : : addAttributePt ( const TQString & attrName , double attrValue )
{
TQString str ;
str . setNum ( attrValue , ' g ' , DBL_DIG ) ;
str + = " pt " ;
m_attributes . insert ( attrName , str ) ;
}
# ifndef NDEBUG
void KoGenStyle : : printDebug ( ) const
{
int i = DefaultType ;
kdDebug ( ) < < m_properties [ i ] . count ( ) < < " properties. " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_properties [ i ] . begin ( ) ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
i = TextType ;
kdDebug ( ) < < m_properties [ i ] . count ( ) < < " text properties. " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_properties [ i ] . begin ( ) ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
i = ParagraphType ;
kdDebug ( ) < < m_properties [ i ] . count ( ) < < " paragraph properties. " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_properties [ i ] . begin ( ) ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
i = ChildElement ;
kdDebug ( ) < < m_properties [ i ] . count ( ) < < " child elements. " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_properties [ i ] . begin ( ) ; it ! = m_properties [ i ] . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
kdDebug ( ) < < m_attributes . count ( ) < < " attributes. " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_attributes . begin ( ) ; it ! = m_attributes . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
kdDebug ( ) < < m_maps . count ( ) < < " maps. " < < endl ;
for ( uint i = 0 ; i < m_maps . count ( ) ; + + i ) {
kdDebug ( ) < < " map " < < i < < " : " < < endl ;
for ( TQMap < TQString , TQString > : : ConstIterator it = m_maps [ i ] . begin ( ) ; it ! = m_maps [ i ] . end ( ) ; + + it ) {
kdDebug ( ) < < " " < < it . key ( ) < < " = " < < it . data ( ) < < endl ;
}
}
kdDebug ( ) < < endl ;
}
# endif
bool KoGenStyle : : operator < ( const KoGenStyle & other ) const
{
if ( m_type ! = other . m_type ) return m_type < other . m_type ;
if ( m_parentName ! = other . m_parentName ) return m_parentName < other . m_parentName ;
if ( m_autoStyleInStylesDotXml ! = other . m_autoStyleInStylesDotXml ) return m_autoStyleInStylesDotXml ;
for ( uint i = 0 ; i < N_NumTypes ; + + i )
if ( m_properties [ i ] . count ( ) ! = other . m_properties [ i ] . count ( ) )
return m_properties [ i ] . count ( ) < other . m_properties [ i ] . count ( ) ;
if ( m_attributes . count ( ) ! = other . m_attributes . count ( ) ) return m_attributes . count ( ) < other . m_attributes . count ( ) ;
if ( m_maps . count ( ) ! = other . m_maps . count ( ) ) return m_maps . count ( ) < other . m_maps . count ( ) ;
// Same number of properties and attributes, no other choice than iterating
for ( uint i = 0 ; i < N_NumTypes ; + + i ) {
int comp = compareMap ( m_properties [ i ] , other . m_properties [ i ] ) ;
if ( comp ! = 0 )
return comp < 0 ;
}
int comp = compareMap ( m_attributes , other . m_attributes ) ;
if ( comp ! = 0 )
return comp < 0 ;
for ( uint i = 0 ; i < m_maps . count ( ) ; + + i ) {
int comp = compareMap ( m_maps [ i ] , other . m_maps [ i ] ) ;
if ( comp ! = 0 )
return comp < 0 ;
}
return false ;
}
bool KoGenStyle : : operator = = ( const KoGenStyle & other ) const
{
if ( m_type ! = other . m_type ) return false ;
if ( m_parentName ! = other . m_parentName ) return false ;
if ( m_autoStyleInStylesDotXml ! = other . m_autoStyleInStylesDotXml ) return false ;
for ( uint i = 0 ; i < N_NumTypes ; + + i )
if ( m_properties [ i ] . count ( ) ! = other . m_properties [ i ] . count ( ) )
return false ;
if ( m_attributes . count ( ) ! = other . m_attributes . count ( ) ) return false ;
if ( m_maps . count ( ) ! = other . m_maps . count ( ) ) return false ;
// Same number of properties and attributes, no other choice than iterating
for ( uint i = 0 ; i < N_NumTypes ; + + i ) {
int comp = compareMap ( m_properties [ i ] , other . m_properties [ i ] ) ;
if ( comp ! = 0 )
return false ;
}
int comp = compareMap ( m_attributes , other . m_attributes ) ;
if ( comp ! = 0 )
return false ;
for ( uint i = 0 ; i < m_maps . count ( ) ; + + i ) {
int comp = compareMap ( m_maps [ i ] , other . m_maps [ i ] ) ;
if ( comp ! = 0 )
return false ;
}
return true ;
}