Started css stylesheet import code with stubs
[kworship.git] / kworship / css / KwCssScope.cpp
blob292441f2c17764a012376706ccd406c2f5e49ded
1 /***************************************************************************
2 * This file is part of KWorship. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
20 /**
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)
40 , m_childScopes()
41 , m_name()
42 , m_classes()
43 , m_styleSheets()
44 , m_styles()
46 if (0 != m_parentScope)
48 parent->m_childScopes.insert(this);
52 /// Destructor.
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();
71 // Clean style sheets
73 foreach (KwCssStyleSheet* stylesheet, m_styleSheets)
75 /// @todo reference count
76 delete stylesheet;
82 * DOM filters
85 /// Import the style information from a DOM.
86 void KwCssScope::importStylesFromDom(const QDomElement& element, KwResourceManager* resourceManager)
88 // Classes
89 QDomNodeList elems = element.elementsByTagName("class");
90 for (int i = 0; i < elems.count(); ++i)
92 m_classes += elems.at(i).toElement().text();
95 // Stylesheets
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)
105 recalculateStyles();
108 // Explicit styles
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()));
145 * Main interface
148 /// Add a stylesheet for this scope.
149 void KwCssScope::addStyleSheet(KwCssStyleSheet* styleSheet)
151 m_styleSheets.push_back(styleSheet);
152 recalculateStyles();
155 /// Add a class to this scope.
156 void KwCssScope::addClass(QString className)
158 m_classes += className;
159 recalculateStyles();
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()
172 * cached styles
173 * unmatched rules stylesheet
175 * get parent styles
176 * add classes
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;
189 else
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);
202 // Add local classes
203 m_cachedClasses += m_classes;
205 // process rules
206 bool processRules;
209 processRules = false;
210 QSet<QString> triggerableClasses;
211 KwCssStyleSheet::Iterator it;
212 for (it = m_cachedStyleSheet.getRules().begin(); it != m_cachedStyleSheet.getRules().end();)
214 // Match the keys
215 KwCssStyleRule::KeyList& keys = (*it).getCriteriaKeys();
216 if (!keys.empty() && isKeyMatching(keys.first()))
218 keys.pop_front();
220 // Match the classes
221 KwCssStyleRule::StringSet& classes = (*it).getCriteriaClasses();
222 classes -= m_cachedClasses;
224 if (!classes.empty())
226 triggerableClasses += classes;
227 ++it;
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;
241 processRules = true;
244 // Remove from stylesheet since it's now applied
245 KwCssStyleSheet::Iterator cur = it;
246 ++it;
247 m_cachedStyleSheet.getRules().erase(cur);
249 else
251 ++it;
255 while (processRules);
257 // Apply each style
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();
271 * Accessors
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
283 return m_styles;
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);
300 * Mutators
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;
311 if (0 != parent)
313 m_parentScope->m_childScopes.insert(this);
315 recalculateStyles();