lib

KoGenStyles.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2004-2006 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17*/
18
19#include "KoGenStyles.h"
20#include <KoXmlWriter.h>
21#include <float.h>
22#include <kdebug.h>
23
24KoGenStyles::KoGenStyles()
25{
26}
27
28KoGenStyles::~KoGenStyles()
29{
30}
31
32TQString KoGenStyles::lookup( const KoGenStyle& style, const TQString& name, int flags )
33{
34 StyleMap::iterator it = m_styleMap.find( style );
35 if ( it == m_styleMap.end() ) {
36 // Not found, try if this style is in fact equal to its parent (the find above
37 // wouldn't have found it, due to m_parentName being set).
38 if ( !style.parentName().isEmpty() ) {
39 KoGenStyle testStyle( style );
40 const KoGenStyle* parentStyle = this->style( style.parentName() ); // ## linear search
41 if( !parentStyle ) {
42 kdDebug(30003) << "KoGenStyles::lookup(" << name << "): parent style '" << style.parentName() << "' not found in collection" << endl;
43 } else {
44 if ( testStyle.m_familyName != parentStyle->m_familyName )
45 {
46 kdWarning(30003) << "KoGenStyles::lookup(" << name << ", family=" << testStyle.m_familyName << ") parent style '" << style.parentName() << "' has a different family: " << parentStyle->m_familyName << endl;
47 }
48
49 testStyle.m_parentName = parentStyle->m_parentName;
50 // Exclude the type from the comparison. It's ok for an auto style
51 // to have a user style as parent; they can still be identical
52 testStyle.m_type = parentStyle->m_type;
53 // Also it's ok to not have the display name of the parent style
54 // in the auto style
55 TQMap<TQString, TQString>::const_iterator it = parentStyle->m_attributes.find( "style:display-name" );
56 if ( it != parentStyle->m_attributes.end() )
57 testStyle.addAttribute( "style:display-name", *it );
58
59 if ( *parentStyle == testStyle )
60 return style.parentName();
61 }
62 }
63
64 TQString styleName( name );
65 if ( styleName.isEmpty() ) {
66 styleName = 'A'; // for "auto".
67 flags &= ~DontForceNumbering; // i.e. force numbering
68 }
69 styleName = makeUniqueName( styleName, flags );
70 if ( style.autoStyleInStylesDotXml() )
71 m_autoStylesInStylesDotXml.insert( styleName, true /*unused*/ );
72 else
73 m_styleNames.insert( styleName, true /*unused*/ );
74 it = m_styleMap.insert( style, styleName );
75 NamedStyle s;
76 s.style = &it.key();
77 s.name = styleName;
78 m_styleArray.append( s );
79 }
80 return it.data();
81}
82
83TQString KoGenStyles::makeUniqueName( const TQString& base, int flags ) const
84{
85 // If this name is not used yet, and numbering isn't forced, then the given name is ok.
86 if ( ( flags & DontForceNumbering )
87 && m_autoStylesInStylesDotXml.find( base ) == m_autoStylesInStylesDotXml.end()
88 && m_styleNames.find( base ) == m_styleNames.end() )
89 return base;
90 int num = 1;
91 TQString name;
92 do {
93 name = base;
94 name += TQString::number( num++ );
95 } while ( m_autoStylesInStylesDotXml.find( name ) != m_autoStylesInStylesDotXml.end()
96 || m_styleNames.find( name ) != m_styleNames.end() );
97 return name;
98}
99
100TQValueList<KoGenStyles::NamedStyle> KoGenStyles::styles( int type, bool markedForStylesXml ) const
101{
102 TQValueList<KoGenStyles::NamedStyle> lst;
103 const NameMap& nameMap = markedForStylesXml ? m_autoStylesInStylesDotXml : m_styleNames;
104 StyleArray::const_iterator it = m_styleArray.begin();
105 const StyleArray::const_iterator end = m_styleArray.end();
106 for ( ; it != end ; ++it ) {
107 // Look up if it's marked for styles.xml or not by looking up in the corresponding style map.
108 if ( (*it).style->type() == type && nameMap.find((*it).name) != nameMap.end() ) {
109 lst.append( *it );
110 }
111 }
112 return lst;
113}
114
115const KoGenStyle* KoGenStyles::style( const TQString& name ) const
116{
117 StyleArray::const_iterator it = m_styleArray.begin();
118 const StyleArray::const_iterator end = m_styleArray.end();
119 for ( ; it != end ; ++it ) {
120 if ( (*it).name == name )
121 return (*it).style;
122 }
123 return 0;
124}
125
127{
128 return const_cast<KoGenStyle *>( style( name ) );
129}
130
131void KoGenStyles::markStyleForStylesXml( const TQString& name )
132{
133 Q_ASSERT( m_styleNames.find( name ) != m_styleNames.end() );
134 m_styleNames.remove( name );
135 m_autoStylesInStylesDotXml.insert( name, true );
136 styleForModification( name )->setAutoStyleInStylesDotXml( true );
137}
138
140{
141 kdDebug() << "Style array:" << endl;
142 StyleArray::const_iterator it = m_styleArray.begin();
143 const StyleArray::const_iterator end = m_styleArray.end();
144 for ( ; it != end ; ++it ) {
145 kdDebug() << (*it).name << endl;
146 }
147 for ( NameMap::const_iterator it = m_styleNames.begin(); it != m_styleNames.end(); ++it ) {
148 kdDebug() << "style: " << it.key() << endl;
149 }
150 for ( NameMap::const_iterator it = m_autoStylesInStylesDotXml.begin(); it != m_autoStylesInStylesDotXml.end(); ++it ) {
151 kdDebug() << "auto style for style.xml: " << it.key() << endl;
152 const KoGenStyle* s = style( it.key() );
153 Q_ASSERT( s );
154 Q_ASSERT( s->autoStyleInStylesDotXml() );
155 }
156}
157
158// Returns -1, 0 (equal) or 1
159static int compareMap( const TQMap<TQString, TQString>& map1, const TQMap<TQString, TQString>& map2 )
160{
161 TQMap<TQString, TQString>::const_iterator it = map1.begin();
162 TQMap<TQString, TQString>::const_iterator oit = map2.begin();
163 for ( ; it != map1.end(); ++it, ++oit ) { // both maps have been checked for size already
164 if ( it.key() != oit.key() )
165 return it.key() < oit.key() ? -1 : +1;
166 if ( it.data() != oit.data() )
167 return it.data() < oit.data() ? -1 : +1;
168 }
169 return 0; // equal
170}
171
173
174
176 const TQString& parentName )
177 : m_type( type ), m_familyName( familyName ), m_parentName( parentName ),
178 m_autoStyleInStylesDotXml( false ), m_defaultStyle( false )
179{
180}
181
182KoGenStyle::~KoGenStyle()
183{
184}
185
186void KoGenStyle::writeStyleProperties( KoXmlWriter* writer, PropertyType i,
187 const char* elementName, const KoGenStyle* parentStyle ) const
188{
189 if ( !m_properties[i].isEmpty() ) {
190 writer->startElement( elementName );
191 TQMap<TQString, TQString>::const_iterator it = m_properties[i].begin();
192 const TQMap<TQString, TQString>::const_iterator end = m_properties[i].end();
193 for ( ; it != end; ++it ) {
194 if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
195 writer->addAttribute( it.key().utf8(), it.data().utf8() );
196 }
197 writer->endElement();
198 }
199}
200
201void KoGenStyle::writeStyle( KoXmlWriter* writer, KoGenStyles& styles, const char* elementName, const TQString& name, const char* propertiesElementName, bool closeElement, bool drawElement ) const
202{
203 //kdDebug(30003) << "writing out style " << name << " display-name=" << m_attributes["style:display-name"] << " family=" << m_familyName << endl;
204 writer->startElement( elementName );
205 const KoGenStyle* parentStyle = 0;
206 if ( !m_defaultStyle ) {
207 if ( !drawElement )
208 writer->addAttribute( "style:name", name );
209 else
210 writer->addAttribute( "draw:name", name );
211 if ( !m_parentName.isEmpty() ) {
212 parentStyle = styles.style( m_parentName );
213 if ( parentStyle && m_familyName.isEmpty() ) {
214 // get family from parent style, just in case
215 // Note: this is saving code, don't convert to attributeNS!
216 const_cast<KoGenStyle *>( this )->
217 m_familyName = parentStyle->attribute( "style:family" ).latin1();
218 //kdDebug(30003) << "Got familyname " << m_familyName << " from parent" << endl;
219 }
220 writer->addAttribute( "style:parent-style-name", m_parentName );
221 }
222 } else { // default-style
223 Q_ASSERT( qstrcmp( elementName, "style:default-style" ) == 0 );
224 Q_ASSERT( m_parentName.isEmpty() );
225 }
226 if ( !m_familyName.isEmpty() )
227 const_cast<KoGenStyle *>( this )->
228 addAttribute( "style:family", TQString::fromLatin1( m_familyName ) );
229 else {
230 if ( qstrcmp( elementName, "style:style" ) == 0 )
231 kdWarning(30003) << "User style " << name << " is without family - invalid. m_type=" << m_type << endl;
232 }
233
234#if 0 // #ifndef NDEBUG
235 kdDebug() << "style: " << name << endl;
236 printDebug();
237 if ( parentStyle ) {
238 kdDebug() << " parent: " << m_parentName << endl;
239 parentStyle->printDebug();
240 }
241#endif
242
243 // Write attributes [which differ from the parent style]
244 // We only look at the direct parent style because we assume
245 // that styles are fully specified, i.e. the inheritance is
246 // only in the final file, not in the caller's code.
247 TQMap<TQString, TQString>::const_iterator it = m_attributes.begin();
248 for ( ; it != m_attributes.end(); ++it ) {
249 bool writeit = true;
250 if ( parentStyle && it.key() != "style:family" // always write the family out
251 && parentStyle->attribute( it.key() ) == it.data() )
252 writeit = false;
253 if ( writeit )
254 writer->addAttribute( it.key().utf8(), it.data().utf8() );
255 }
256 bool createPropertiesTag = propertiesElementName && propertiesElementName[0] != '\0';
258 if ( !m_properties[i].isEmpty() ||
259 !m_properties[KoGenStyle::ChildElement].isEmpty() ) {
260 if ( createPropertiesTag )
261 writer->startElement( propertiesElementName ); // e.g. paragraph-properties
262 it = m_properties[i].begin();
263 for ( ; it != m_properties[i].end(); ++it ) {
264 if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
265 writer->addAttribute( it.key().utf8(), it.data().utf8() );
266 }
267 i = KoGenStyle::ChildElement;
268 it = m_properties[i].begin();
269 for ( ; it != m_properties[i].end(); ++it ) {
270 if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() ) {
271 writer->addCompleteElement( it.data().utf8() );
272 }
273 }
274 if ( createPropertiesTag )
275 writer->endElement();
276 }
277 writeStyleProperties( writer, KoGenStyle::GraphicType, "style:graphic-properties", parentStyle );
278 writeStyleProperties( writer, KoGenStyle::ParagraphType, "style:paragraph-properties", parentStyle );
279 writeStyleProperties( writer, KoGenStyle::TextType, "style:text-properties", parentStyle );
280
281 // And now the style maps
282 for ( uint i = 0; i < m_maps.count(); ++i ) {
283 bool writeit = true;
284 if ( parentStyle && compareMap( m_maps[i], parentStyle->m_maps[i] ) == 0 )
285 writeit = false;
286 if ( writeit ) {
287 writer->startElement( "style:map" );
288 TQMap<TQString, TQString>::const_iterator it = m_maps[i].begin();
289 for ( ; it != m_maps[i].end(); ++it ) {
290 writer->addAttribute( it.key().utf8(), it.data().utf8() );
291 }
292 writer->endElement(); // style:map
293 }
294 }
295 if ( closeElement )
296 writer->endElement();
297}
298
299void KoGenStyle::addPropertyPt( const TQString& propName, double propValue, PropertyType type )
300{
301 TQString str;
302 str.setNum( propValue, 'g', DBL_DIG );
303 str += "pt";
304 m_properties[type].insert( propName, str );
305}
306
307void KoGenStyle::addAttributePt( const TQString& attrName, double attrValue )
308{
309 TQString str;
310 str.setNum( attrValue, 'g', DBL_DIG );
311 str += "pt";
312 m_attributes.insert( attrName, str );
313}
314
315#ifndef NDEBUG
316void KoGenStyle::printDebug() const
317{
318 int i = DefaultType;
319 kdDebug() << m_properties[i].count() << " properties." << endl;
320 for( TQMap<TQString,TQString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
321 kdDebug() << " " << it.key() << " = " << it.data() << endl;
322 }
323 i = TextType;
324 kdDebug() << m_properties[i].count() << " text properties." << endl;
325 for( TQMap<TQString,TQString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
326 kdDebug() << " " << it.key() << " = " << it.data() << endl;
327 }
328 i = ParagraphType;
329 kdDebug() << m_properties[i].count() << " paragraph properties." << endl;
330 for( TQMap<TQString,TQString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
331 kdDebug() << " " << it.key() << " = " << it.data() << endl;
332 }
333 i = ChildElement;
334 kdDebug() << m_properties[i].count() << " child elements." << endl;
335 for( TQMap<TQString,TQString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
336 kdDebug() << " " << it.key() << " = " << it.data() << endl;
337 }
338 kdDebug() << m_attributes.count() << " attributes." << endl;
339 for( TQMap<TQString,TQString>::ConstIterator it = m_attributes.begin(); it != m_attributes.end(); ++it ) {
340 kdDebug() << " " << it.key() << " = " << it.data() << endl;
341 }
342 kdDebug() << m_maps.count() << " maps." << endl;
343 for ( uint i = 0; i < m_maps.count(); ++i ) {
344 kdDebug() << "map " << i << ":" << endl;
345 for( TQMap<TQString,TQString>::ConstIterator it = m_maps[i].begin(); it != m_maps[i].end(); ++it ) {
346 kdDebug() << " " << it.key() << " = " << it.data() << endl;
347 }
348 }
349 kdDebug() << endl;
350}
351#endif
352
353bool KoGenStyle::operator<( const KoGenStyle &other ) const
354{
355 if ( m_type != other.m_type ) return m_type < other.m_type;
356 if ( m_parentName != other.m_parentName ) return m_parentName < other.m_parentName;
357 if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return m_autoStyleInStylesDotXml;
358 for ( uint i = 0 ; i < N_NumTypes ; ++i )
359 if ( m_properties[i].count() != other.m_properties[i].count() )
360 return m_properties[i].count() < other.m_properties[i].count();
361 if ( m_attributes.count() != other.m_attributes.count() ) return m_attributes.count() < other.m_attributes.count();
362 if ( m_maps.count() != other.m_maps.count() ) return m_maps.count() < other.m_maps.count();
363 // Same number of properties and attributes, no other choice than iterating
364 for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
365 int comp = compareMap( m_properties[i], other.m_properties[i] );
366 if ( comp != 0 )
367 return comp < 0;
368 }
369 int comp = compareMap( m_attributes, other.m_attributes );
370 if ( comp != 0 )
371 return comp < 0;
372 for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
373 int comp = compareMap( m_maps[i], other.m_maps[i] );
374 if ( comp != 0 )
375 return comp < 0;
376 }
377 return false;
378}
379
380bool KoGenStyle::operator==( const KoGenStyle &other ) const
381{
382 if ( m_type != other.m_type ) return false;
383 if ( m_parentName != other.m_parentName ) return false;
384 if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return false;
385 for ( uint i = 0 ; i < N_NumTypes ; ++i )
386 if ( m_properties[i].count() != other.m_properties[i].count() )
387 return false;
388 if ( m_attributes.count() != other.m_attributes.count() ) return false;
389 if ( m_maps.count() != other.m_maps.count() ) return false;
390 // Same number of properties and attributes, no other choice than iterating
391 for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
392 int comp = compareMap( m_properties[i], other.m_properties[i] );
393 if ( comp != 0 )
394 return false;
395 }
396 int comp = compareMap( m_attributes, other.m_attributes );
397 if ( comp != 0 )
398 return false;
399 for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
400 int comp = compareMap( m_maps[i], other.m_maps[i] );
401 if ( comp != 0 )
402 return false;
403 }
404 return true;
405}
A generic style, i.e.
KoGenStyle(int type=0, const char *familyName=0, const TQString &parentName=TQString())
Start the definition of a new style.
bool operator<(const KoGenStyle &other) const
TQMap requires a complete sorting order.
const char * familyName() const
Return the family name.
bool isEmpty() const
void writeStyle(KoXmlWriter *writer, KoGenStyles &styles, const char *elementName, const TQString &name, const char *propertiesElementName, bool closeElement=true, bool drawElement=false) const
Write the definition of this style to writer, using the OASIS format.
bool operator==(const KoGenStyle &other) const
Not needed for TQMap, but can still be useful.
void addAttributePt(const TQString &attrName, double attrValue)
Add an attribute which represents a distance, measured in pt The number is written out with the highe...
PropertyType
The types of properties.
@ DefaultType
DefaultType depends on family: e.g.
@ GraphicType
GraphicType is always graphic-properties.
@ TextType
TextType is always text-properties.
@ ParagraphType
ParagraphType is always paragraph-properties.
TQString parentName() const
Return the name of style's parent, if set.
int type() const
Return the type of this style, as set in the constructor.
void addPropertyPt(const TQString &propName, double propValue, PropertyType type=DefaultType)
Add a property which represents a distance, measured in pt The number is written out with the highest...
bool autoStyleInStylesDotXml() const
void addAttribute(const TQString &attrName, const TQString &attrValue)
Add an attribute to the style The difference between property and attributes is a bit oasis-format-sp...
void dump()
Outputs debug information.
const StyleMap & styles() const
Return the entire collection of styles Use this for saving the styles.
KoGenStyle * styleForModification(const TQString &name)
void markStyleForStylesXml(const TQString &name)
Mark a given automatic style as being needed in styles.xml.
const KoGenStyle * style(const TQString &name) const
TQString lookup(const KoGenStyle &style, const TQString &name=TQString(), int flags=NoFlag)
Look up a style in the collection, inserting it if necessary.
A class for writing out XML (to any TQIODevice), with a special attention on performance.
Definition KoXmlWriter.h:37
void startElement(const char *tagName, bool indentInside=true)
Start a new element, as a child of the current element.
void endElement()
Terminate the current element.
void addCompleteElement(const char *cstr)
This is quite a special-purpose method, not for everyday use.
void addAttribute(const char *attrName, const TQString &value)
Overloaded version of addAttribute( const char*, const char* ), which is a bit slower because it need...
Definition KoXmlWriter.h:76
const char * name(StdAction id)