1 /***************************************************************************
2 * This file is part of KWorship. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
5 * KWorship is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
10 * KWorship is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with KWorship. If not, write to the Free Software Foundation, *
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
21 * @file KwCssScope.cpp
22 * @brief Cascading style scope.
23 * @author James Hogan <james@albanarts.com>
26 #include "KwCssScope.h"
27 #include "KwCssStyleRule.h"
28 #include "KwCssStyleSheet.h"
30 #include <QDomDocument>
31 #include <QDomElement>
34 * Constructors + destructors
37 /// Primary constructor.
38 KwCssScope::KwCssScope(KwCssScope
* parent
)
39 : m_parentScope(parent
)
46 if (0 != m_parentScope
)
48 parent
->m_childScopes
.insert(this);
53 KwCssScope::~KwCssScope()
55 // Remove from parent's child list.
56 if (0 != m_parentScope
)
58 m_parentScope
->m_childScopes
.remove(this);
61 // Remove from children and recalculate styles.
63 ScopeSet::iterator it
;
64 for (it
= m_childScopes
.begin(); it
!= m_childScopes
.end(); ++it
)
66 (*it
)->m_parentScope
= 0;
67 (*it
)->recalculateStyles();
73 foreach (KwCssStyleSheet
* stylesheet
, m_styleSheets
)
75 /// @todo reference count
85 /// Import the style information from a DOM.
86 void KwCssScope::importStylesFromDom(const QDomElement
& element
, KwResourceManager
* resourceManager
)
89 QDomNodeList elems
= element
.elementsByTagName("class");
90 for (int i
= 0; i
< elems
.count(); ++i
)
92 m_classes
+= elems
.at(i
).toElement().text();
96 elems
= element
.elementsByTagName("sheet");
97 for (int i
= 0; i
< elems
.count(); ++i
)
99 KwCssStyleSheet
* newSheet
= new KwCssStyleSheet
;
100 newSheet
->import(elems
.at(i
).toElement().text());
101 m_styleSheets
.push_back(newSheet
);
103 if (elems
.count() > 0)
109 elems
= element
.elementsByTagName("explicit");
110 for (int i
= 0; i
< elems
.count(); ++i
)
112 m_styles
.import(elems
.at(i
).toElement().text());
116 /// Export the style information to a DOM.
117 void KwCssScope::exportStylesToDom(QDomDocument
& document
, QDomElement
& element
, KwResourceManager
* resourceManager
) const
119 // Start with the classes
120 foreach (QString className
, m_classes
)
122 QDomElement classElem
= document
.createElement("class");
123 element
.appendChild(classElem
);
124 classElem
.appendChild(document
.createTextNode(className
));
127 // Now for the stylesheets
128 foreach (KwCssStyleSheet
* stylesheet
, m_styleSheets
)
130 QDomElement sheetElem
= document
.createElement("sheet");
131 element
.appendChild(sheetElem
);
132 sheetElem
.appendChild(document
.createTextNode(stylesheet
->toString()));
135 // And finally the explicit styles
136 if (!m_styles
.isEmpty())
138 QDomElement explicitElem
= document
.createElement("explicit");
139 element
.appendChild(explicitElem
);
140 explicitElem
.appendChild(document
.createTextNode(m_styles
.toString()));
148 /// Add a stylesheet for this scope.
149 void KwCssScope::addStyleSheet(KwCssStyleSheet
* styleSheet
)
151 m_styleSheets
.push_back(styleSheet
);
155 /// Add a class to this scope.
156 void KwCssScope::addClass(QString className
)
158 m_classes
+= className
;
162 /// Get all the styles included in this scope.
163 const KwCssStyleStates
& KwCssScope::getStyles() const
165 return m_cachedStyles
;
168 /// Recalculate all styles.
169 void KwCssScope::recalculateStyles()
173 * unmatched rules stylesheet
177 * get parent unmatched style rules
178 * go through stylesheets
179 * apply any rules that match in order, removing as we go from local list
180 * apply explicit styles
182 // Get the base styles from the parent
183 if (0 != m_parentScope
)
185 m_cachedStyles
= m_parentScope
->getStyles();
186 m_cachedStyleSheet
= m_parentScope
->m_cachedStyleSheet
;
187 m_cachedClasses
= m_parentScope
->m_cachedClasses
;
191 m_cachedStyles
.clear();
192 m_cachedStyleSheet
.clear();
194 // Add local stylesheets
196 StyleSheetList::iterator it
;
197 for (it
= m_styleSheets
.begin(); it
!= m_styleSheets
.end(); ++it
)
199 m_cachedStyleSheet
.importStyleSheet(*it
);
203 m_cachedClasses
+= m_classes
;
209 processRules
= false;
210 QSet
<QString
> triggerableClasses
;
211 KwCssStyleSheet::Iterator it
;
212 for (it
= m_cachedStyleSheet
.getRules().begin(); it
!= m_cachedStyleSheet
.getRules().end();)
215 KwCssStyleRule::KeyList
& keys
= (*it
).getCriteriaKeys();
216 if (!keys
.empty() && isKeyMatching(keys
.first()))
221 KwCssStyleRule::StringSet
& classes
= (*it
).getCriteriaClasses();
222 classes
-= m_cachedClasses
;
224 if (!classes
.empty())
226 triggerableClasses
+= classes
;
229 else if (keys
.empty())
231 // Nothing left to match, we can apply this style and remove it from the list
232 m_cachedStyles
<< *(*it
).getStyles();
233 QSet
<QString
> classesToApply
= (*it
).getIncludedStyles() - m_cachedClasses
;
234 if (!classesToApply
.empty())
236 m_cachedClasses
+= classesToApply
;
237 QSet
<QString
> classesToTrigger
= triggerableClasses
& classesToApply
;
238 if (!classesToTrigger
.empty())
240 triggerableClasses
-= classesToTrigger
;
244 // Remove from stylesheet since it's now applied
245 KwCssStyleSheet::Iterator cur
= it
;
247 m_cachedStyleSheet
.getRules().erase(cur
);
255 while (processRules
);
258 m_cachedStyles
<< m_styles
;
260 // Recurse to children
262 ScopeSet::iterator it
;
263 for (it
= m_childScopes
.begin(); it
!= m_childScopes
.end(); ++it
)
265 (*it
)->recalculateStyles();
274 /// Find whether the scope is effectively empty.
275 bool KwCssScope::isScopeEmpty() const
277 return m_classes
.isEmpty() && m_styleSheets
.isEmpty() && m_styles
.isEmpty();
280 /// Get explicit styles.
281 const KwCssStyles
& KwCssScope::getExplicitStyles() const
286 /// Get the scope's key.
287 KwCssScopeKey
KwCssScope::getKey() const
289 return KwCssScopeKey(getTypeId(), m_name
);
292 /// Find whether a scope key matches this scope.
293 bool KwCssScope::isKeyMatching(KwCssScopeKey key
) const
295 return (!key
.isTypeSpecified() || key
.getTypeId() == getTypeId())
296 && (!key
.isNameSpecified() || key
.getName() == m_name
);
303 /// Set the parent scope.
304 void KwCssScope::setParentScope(KwCssScope
* parent
)
306 if (0 != m_parentScope
)
308 m_parentScope
->m_childScopes
.remove(this);
310 m_parentScope
= parent
;
313 m_parentScope
->m_childScopes
.insert(this);