summaryrefslogtreecommitdiffstats
path: root/lib/kofficecore/KoGenStyles.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kofficecore/KoGenStyles.h')
-rw-r--r--lib/kofficecore/KoGenStyles.h463
1 files changed, 463 insertions, 0 deletions
diff --git a/lib/kofficecore/KoGenStyles.h b/lib/kofficecore/KoGenStyles.h
new file mode 100644
index 00000000..ad05d6e0
--- /dev/null
+++ b/lib/kofficecore/KoGenStyles.h
@@ -0,0 +1,463 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <[email protected]>
+
+ 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.
+*/
+
+#ifndef KOGENSTYLES_H
+#define KOGENSTYLES_H
+
+#include <qdict.h>
+#include <qmap.h>
+#include <qvaluevector.h>
+
+#include <koffice_export.h>
+
+class KoXmlWriter;
+class KoGenStyle;
+
+/**
+ * @brief Repository of styles used during saving of OASIS/OOo file.
+ *
+ * Each instance of KoGenStyles is a collection of styles whose names
+ * are in the same "namespace".
+ * This means there should be one instance for all styles in <office:styles>,
+ * and automatic-styles, another instance for number formats, another
+ * one for draw styles, and another one for list styles.
+ *
+ * "Style" in this context only means "a collection of properties".
+ * The "Gen" means both "Generic" and "Generated" :)
+ *
+ * The basic design principle is the flyweight pattern: if you need
+ * a style with the same properties from two different places, you
+ * get the same object. Here it means rather you get the same name for the style,
+ * and it will get saved only once to the file.
+ *
+ * KoGenStyles features sharing, creation on demand, and name generation.
+ * Since this is used for saving only, it doesn't feature refcounting, nor
+ * removal of individual styles.
+ *
+ * NOTE: the use of KoGenStyles isn't mandatory, of course. If the application
+ * is already designed with user and automatic styles in mind for a given
+ * set of properties, it can go ahead and save all styles directly (after
+ * ensuring they have unique names).
+ *
+ * @author David Faure <[email protected]>
+ */
+class KOFFICECORE_EXPORT KoGenStyles
+{
+public:
+ KoGenStyles();
+ ~KoGenStyles();
+
+ /**
+ * Those are flags for the lookup() call.
+ *
+ * By default, the generated style names will look like "name1", "name2".
+ * If DontForceNumbering is set, the first name that will be tried is "name", and only if
+ * that one exists, then "name1" is tried. Set DontForceNumbering if the name given as
+ * argument is supposed to be the full style name.
+ *
+ */
+ enum Flags { // bitfield
+ NoFlag = 0,
+ ForceNumbering = 0, // it's the default anyway
+ DontForceNumbering = 1
+ };
+ // KDE4 TODO: use QFlags and change the arg type in lookup
+
+ /**
+ * Look up a style in the collection, inserting it if necessary.
+ * This assigns a name to the style and returns it.
+ *
+ * @param style the style to look up.
+ * @param name proposed (base) name for the style. Note that with the OASIS format,
+ * the style name is never shown to the user (there's a separate display-name
+ * attribute for that). So there are little reasons to use named styles anyway.
+ * But this attribute can be used for clarity of the files.
+ * If this name is already in use (for another style), then a number is appended
+ * to it until unique.
+ * @param flags see Flags
+ *
+ * @return the name for this style
+ * @todo ### rename lookup to insert
+ */
+ QString lookup( const KoGenStyle& style, const QString& name = QString::null, int flags = NoFlag );
+
+ typedef QMap<KoGenStyle, QString> StyleMap;
+ /**
+ * Return the entire collection of styles
+ * Use this for saving the styles
+ */
+ const StyleMap& styles() const { return m_styleMap; }
+
+ struct NamedStyle {
+ const KoGenStyle* style; ///< @note owned by the collection
+ QString name;
+ };
+ /**
+ * Return all styles of a given type
+ * Use this for saving the styles
+ *
+ * @param type the style type, see the KoGenStyle constructor
+ * @param markedForStylesXml if true, return only style marked for styles.xml,
+ * otherwise only those NOT marked for styles.xml.
+ * @see lookup
+ */
+ QValueList<NamedStyle> styles( int type, bool markedForStylesXml = false ) const;
+
+ /**
+ * @return an existing style by name
+ */
+ const KoGenStyle* style( const QString& name ) const;
+
+ /**
+ * @return an existing style by name, which can be modified.
+ * @warning This is DANGEROUS.
+ * It basically defeats the purpose of lookup()!
+ * Only do this if you know for sure no other 'user' of that style will
+ * be affected.
+ */
+ KoGenStyle* styleForModification( const QString& name );
+
+ /**
+ * Mark a given automatic style as being needed in styles.xml.
+ * For instance styles used by headers and footers need to go there, since
+ * they are saved in styles.xml, and styles.xml must be independent from content.xml.
+ *
+ * Equivalent to using KoGenStyle::setAutoStyleInStylesDotXml() but this can be done after lookup.
+ *
+ * This operation can't be undone; once styles are promoted they can't go back
+ * to being content.xml-only.
+ *
+ * @see styles, KoGenStyle::setAutoStyleInStylesDotXml
+ */
+ void markStyleForStylesXml( const QString& name );
+
+ /**
+ * Outputs debug information
+ */
+ void dump();
+
+private:
+ QString makeUniqueName( const QString& base, int flags ) const;
+
+ /// style definition -> name
+ StyleMap m_styleMap;
+
+ /// Map with the style name as key.
+ /// This map is mainly used to check for name uniqueness
+ /// The value of the bool doesn't matter.
+ typedef QMap<QString, bool> NameMap; // KDE4: QSet
+ NameMap m_styleNames;
+ NameMap m_autoStylesInStylesDotXml;
+
+ /// List of styles (used to preserve ordering)
+ typedef QValueVector<NamedStyle> StyleArray;
+ StyleArray m_styleArray;
+
+ class Private;
+ Private *d;
+};
+
+/**
+ * A generic style, i.e. basically a collection of properties and a name.
+ * Instances of KoGenStyle can either be held in the KoGenStyles collection,
+ * or created (e.g. on the stack) and given to KoGenStyles::lookup.
+ *
+ * @author David Faure <[email protected]>
+ */
+class KOFFICECORE_EXPORT KoGenStyle
+{
+public:
+ /**
+ * Possible values for the "type" argument of the KoGenStyle constructor.
+ * Those values can be extended by applications (starting at number 20),
+ * it's for their own consumption anyway.
+ * (The reason for having the very common ones here, is to make it possible to
+ * use them from libkotext).
+ */
+ enum { STYLE_PAGELAYOUT = 0,
+ STYLE_USER = 1,
+ STYLE_AUTO = 2,
+ STYLE_MASTER = 3,
+ STYLE_LIST = 4,
+ STYLE_AUTO_LIST = 5,
+ STYLE_NUMERIC_NUMBER = 6,
+ STYLE_NUMERIC_DATE = 7,
+ STYLE_NUMERIC_TIME = 8,
+ STYLE_NUMERIC_FRACTION = 9,
+ STYLE_NUMERIC_PERCENTAGE = 10,
+ STYLE_NUMERIC_SCIENTIFIC = 11,
+ STYLE_NUMERIC_CURRENCY = 12,
+ STYLE_NUMERIC_TEXT = 13,
+ STYLE_HATCH = 14,
+ STYLE_GRAPHICAUTO = 15};
+
+ /**
+ * Start the definition of a new style. Its name will be set later by KoGenStyles::lookup(),
+ * but first you must define its properties and attributes.
+ *
+ * @param type this is a hook for the application to categorize styles
+ * See the STYLE_* enum. Ignored when writing out the style.
+ *
+ * @param familyName The value for style:family, e.g. text, paragraph, graphic etc.
+ * The family is for style:style elements only; number styles and list styles don't have one.
+ *
+ * @param parentName If set, name of the parent style from which this one inherits.
+ */
+ explicit KoGenStyle( int type = 0, const char* familyName = 0,
+ const QString& parentName = QString::null );
+ ~KoGenStyle();
+
+ /*
+ * setAutoStyleInStylesDotXml(true) marks a given automatic style as being needed in styles.xml.
+ * For instance styles used by headers and footers need to go there, since
+ * they are saved in styles.xml, and styles.xml must be independent from content.xml.
+ *
+ * The application should use KoGenStyles::styles( type, true ) in order to retrieve
+ * those styles and save them separately.
+ */
+ void setAutoStyleInStylesDotXml( bool b ) { m_autoStyleInStylesDotXml = b; }
+ /// @return the value passed to setAutoStyleInStylesDotXml; false by default
+ bool autoStyleInStylesDotXml() const { return m_autoStyleInStylesDotXml; }
+
+ /*
+ * setDefaultStyle(true) marks a given style as being the default style.
+ * This means we expect that you will call writeStyle( ...,"style:default-style"),
+ * and its name will be ommitted in the output.
+ */
+ void setDefaultStyle( bool b ) { m_defaultStyle = b; }
+ /// @return the value passed to setDefaultStyle; false by default
+ bool isDefaultStyle() const { return m_defaultStyle; }
+
+ /// Return the type of this style, as set in the constructor
+ int type() const { return m_type; }
+
+ /// Return the family name
+ const char* familyName() const { return m_familyName.data(); }
+
+ /// Return the name of style's parent, if set
+ QString parentName() const { return m_parentName; }
+
+ /**
+ * @brief The types of properties
+ *
+ * Simple styles only write one foo-properties tag, in which case they can just use DefaultType.
+ * However a given style might want to write several kinds of properties, in which case it would
+ * need to use other property types than the default one.
+ *
+ * For instance this style:
+ * @code
+ * <style:style style:family="chart">
+ * <style:chart-properties .../>
+ * <style:graphic-properties .../>
+ * <style:text-properties .../>
+ * </style:style>
+ * @endcode
+ * would use DefaultType for chart-properties (and would pass "style:chart-properties" to writeStyle(),
+ * and would use GraphicType and TextType.
+ */
+ enum PropertyType
+ {
+ /**
+ * DefaultType depends on family: e.g. paragraph-properties if family=paragraph
+ * or on the type of style (e.g. page-layout -> page-layout-properties).
+ * (In fact that tag name is the one passed to writeStyle)
+ */
+ DefaultType = 0,
+ /// TextType is always text-properties.
+ TextType,
+ /// ParagraphType is always paragraph-properties.
+ ParagraphType,
+ /// GraphicType is always graphic-properties.
+ GraphicType,
+ Reserved1, ///< @internal for binary compatible extensions
+ Reserved2, ///< @internal for binary compatible extensions
+ ChildElement, ///< @internal
+ N_NumTypes ///< @internal - warning, adding items here affects binary compatibility (size of KoGenStyle)
+ };
+
+ /// Add a property to the style
+ void addProperty( const QString& propName, const QString& propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, propValue );
+ }
+ /// Overloaded version of addProperty that takes a char*, usually for "..."
+ void addProperty( const QString& propName, const char* propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, QString::fromUtf8( propValue ) );
+ }
+ /// Overloaded version of addProperty that converts an int to a string
+ void addProperty( const QString& propName, int propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, QString::number( propValue ) );
+ }
+ /// Overloaded version of addProperty that converts a bool to a string (false/true)
+ void addProperty( const QString& propName, bool propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, propValue ? "true" : "false" );
+ }
+
+ /**
+ * Add a property which represents a distance, measured in pt
+ * The number is written out with the highest possible precision
+ * (unlike QString::number and setNum, which default to 6 digits),
+ * and the unit name ("pt") is appended to it.
+ */
+ void addPropertyPt( const QString& propName, double propValue, PropertyType type = DefaultType );
+
+ /**
+ * Add an attribute to the style
+ * The difference between property and attributes is a bit oasis-format-specific:
+ * attributes are for the style element itself, and properties are in the style:properties child element
+ */
+ void addAttribute( const QString& attrName, const QString& attrValue ) {
+ m_attributes.insert( attrName, attrValue );
+ }
+ /// Overloaded version of addAttribute that takes a char*, usually for "..."
+ void addAttribute( const QString& attrName, const char* attrValue ) {
+ m_attributes.insert( attrName, QString::fromUtf8( attrValue ) );
+ }
+ /// Overloaded version of addAttribute that converts an int to a string
+ void addAttribute( const QString& attrName, int attrValue ) {
+ m_attributes.insert( attrName, QString::number( attrValue ) );
+ }
+
+ /// Overloaded version of addAttribute that converts a bool to a string
+ void addAttribute( const QString& attrName, bool attrValue ) {
+ m_attributes.insert( attrName, attrValue ? "true" : "false" );
+ }
+
+ /**
+ * Add an attribute which represents a distance, measured in pt
+ * The number is written out with the highest possible precision
+ * (unlike QString::number and setNum, which default to 6 digits),
+ * and the unit name ("pt") is appended to it.
+ */
+ void addAttributePt( const QString& attrName, double attrValue );
+
+ /**
+ * @brief Add a child element to the style properties.
+ *
+ * What is meant here is that the contents of the QString
+ * will be written out literally. This means you should use
+ * KoXmlWriter to generate it:
+ * @code
+ * QBuffer buffer;
+ * buffer.open( IO_WriteOnly );
+ * KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ * elementWriter.startElement( "..." );
+ * ...
+ * elementWriter.endElement();
+ * QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ * gs.addChildElement( "...", elementContents );
+ * @endcode
+ *
+ * The value of @p elementName isn't used, except that it must be unique.
+ */
+ void addChildElement( const QString& elementName, const QString& elementContents ) {
+ m_properties[ChildElement].insert( elementName, elementContents );
+ }
+
+ /**
+ * @brief Add a style:map to the style.
+ * @param styleMap the attributes for the map, associated as (name,value).
+ */
+ void addStyleMap( const QMap<QString, QString>& styleMap ) {
+ m_maps.append( styleMap );
+ }
+
+ /**
+ * @return true if the style has no attributes, no properties, no style map etc.
+ * This can be used by applications which do not save all attributes unconditionally,
+ * but only those that differ from the parent. But note that lookup() can't find this out...
+ */
+ bool isEmpty() const {
+ if ( !m_attributes.isEmpty() || ! m_maps.isEmpty() )
+ return false;
+ for ( uint i = 0 ; i < N_NumTypes ; ++i )
+ if ( ! m_properties[i].isEmpty() )
+ return false;
+ return true;
+ }
+
+ /**
+ * Write the definition of this style to @p writer, using the OASIS format.
+ * @param writer the KoXmlWriter in which @p elementName will be created and filled in
+ * @param styles the styles collection, used to look up the parent style
+ * @param elementName the name of the XML element, e.g. "style:style". Don't forget to
+ * pass style:default-style if isDefaultStyle().
+ * @param name must come from the collection. It will be ignored if isDefaultStyle() is true.
+ * @param propertiesElementName the name of the XML element with the style properties,
+ * e.g. "style:text-properties". Can be 0 in special cases where there should be no such item,
+ * in which case the attributes and elements are added under the style itself.
+ * @param closeElement set it to false to be able to add more child elements to the style element
+ * @param drawElement set it to true to add "draw:name" (used for gradient/hatch style) otherwise add "style:name"
+ */
+ void writeStyle( KoXmlWriter* writer, KoGenStyles& styles, const char* elementName, const QString& name,
+ const char* propertiesElementName, bool closeElement = true, bool drawElement = false ) const;
+
+ /**
+ * QMap requires a complete sorting order.
+ * Another solution would have been a qdict and a key() here, a la KoTextFormat,
+ * but the key was difficult to generate.
+ * Solutions with only a hash value (not representative of the whole data)
+ * require us to write a hashtable by hand....
+ */
+ bool operator<( const KoGenStyle &other ) const;
+
+ /// Not needed for QMap, but can still be useful
+ bool operator==( const KoGenStyle &other ) const;
+
+private:
+ QString property( const QString& propName, PropertyType type ) const {
+ QMap<QString, QString>::const_iterator it = m_properties[type].find( propName );
+ if ( it != m_properties[type].end() )
+ return it.data();
+ return QString::null;
+ }
+
+ QString attribute( const QString& propName ) const {
+ QMap<QString, QString>::const_iterator it = m_attributes.find( propName );
+ if ( it != m_attributes.end() )
+ return it.data();
+ return QString::null;
+ }
+
+ void writeStyleProperties( KoXmlWriter* writer, PropertyType i,
+ const char* elementName, const KoGenStyle* parentStyle ) const;
+
+#ifndef NDEBUG
+ void printDebug() const;
+#endif
+
+private:
+ // Note that the copy constructor and assignment operator are allowed.
+ // Better not use pointers below!
+ int m_type;
+ QCString m_familyName;
+ QString m_parentName;
+ /// We use QMaps since they provide automatic sorting on the key (important for unicity!)
+ QMap<QString, QString> m_properties[N_NumTypes];
+ QMap<QString, QString> m_attributes;
+ typedef QMap<QString, QString> StyleMap;
+ QValueVector<StyleMap> m_maps; // we can't really sort the maps between themselves...
+
+ bool m_autoStyleInStylesDotXml;
+ bool m_defaultStyle;
+ short m_unused2;
+
+ // For lookup
+ friend class KoGenStyles;
+};
+
+#endif /* KOGENSTYLES_H */