Styles System Overview

This document is supposed to provide a global picture on Scribus's style system. Central classes are  and its subclasses,   and.

= Style = A  is basically just a structure of properties. Each property can be set to some value or be unset. In the latter case this property is said to be inherited from the 's parent. If the  doesn't have a parent, any unset property is undefined and will return its default value.

That means that for each property  of type   you have the following methods:

T X const;         // Returns the property's value bool isInhX const; // Returns true if the property is *not* set in this style bool isDefX const; // Returns false if the property is neither set in this style nor in any parent void setX(T val);    // Sets the property's value to 'val'; isInhX will return false now. void resetX;       // Sets the property to its defaulot value; isInhX will return true now.

Styles also provide methods which work on the whole style:

void setStyle(const Style& other);        // Sets all properties to the value and inheritance status they have in 'other' void applyStyle(const Style& other);      // Sets all properties which are set in 'other' to the value they have in 'other' void eraseStyle(const Style& other);      // Resets all properties which are set in 'other' and have the same value in 'this' void erase;                             // Resets all properties. bool equivStyle(const Style& other) const; // Returns true if all properties have the same value and inherited status bool operator==(const Style& other) const; // Returns true if equiv(other) returns true and also have the same name.

Styles are never addressed by pointer but always by name. If a style doesn't have a name, it can only be used where it is stored: this is used for direct formatting. The parent is also stored by name. To allow lookup, each  stores a.

= StyleContext = The class  provides one central method:

const Style* resolve(const QString& name) const;

So a  performs the trick of converting a name to a style. A  can also delegate the lookups to other. are only consistent if there is no loop in this delegation chain.

That's basically all you need to know about. are used in the form of  (which is a subclass of  ) and in   (which provides a   for  ).

= StyleSet = This is a container class which stores one type of styles (subclass of ), eg.  . It allows access to styles via name or index:

STYLE& operator[] (int index); const STYLE& operator[] (int index) const; const STYLE& get(const QString& name) const;

It also implements the  method:

const Style* resolve(const QString& name) const;

To insert a new style into a, there are two methods:

STYLE* create(const STYLE& proto) STYLE* append(STYLE* style)

The first one is safer and should be used if possible. The second one takes ownership of the style object and stores the pointer directly in the.

also have a default style which is returned for the empty name (""). It can be set and tested with

void makeDefault(STYLE* def) bool isDefault(const STYLE& style) const

The argument to  should point to an existing   within the.

Now the fun part: there are two highlevel functions which allow proper changes to :

void rename(const QMap& newNames); void redefine(const StyleSet& defs, bool removeUnused=false)

The first one just renames all styles in the set and also updates parent names used in this set (!).

The second is used by the  for editing styles. First, it uses  to copy the existing styles into an empty. This temporary  can be edited at will. If the user chooses to commit,  uses   again to merge the changed data to the original , setting   to   (that will delete any styles that were removed during the edit).

When Scribus imports styles it also uses, this time with   set to. To avoid overwriting styles with the same name one has to use  before calling.

= Updates / Property Propagation = Now to the last and most technical aspect of Styles: updates.

When the value of a property changes in the parent style, any child styles which inherit this property need to be informed about the changed value. For performance reasons Scribus does not look up inherited values recursively but caches the value locally. Each  stores a version, and each   stores the version of its context when it last updated the cached values. You can call  to increase a  's version and   to copy all inherited values to the local cache (this is in fact delegated to   only if the versions of   and   don't match. Don't use   directly).

The update mechanism "pulls" the correct values from the parent styles. There's also a "push" mechanism which can be used to update styles: you can register observers on any  which will be signaled each time the   is invalidated. This is mainly used to invalidate other  which depend on the invalidated one.

That concludes the global picture. There are more details related to  and the way how   are nested, but that's for another time.

= Property Lookup = The lookup procedure for a property X in the style S with parent P and style context C goes something like this in pseudo code:

lookup(X): if (X is set in S): use value set in S else: while (P != ""): if (X is set in P): use value set in P else: P = parent of P if (X is set in default style of C): use value set in default style of C else: use the initial value for X in S

= Simplified UML diagram =