Database: sqlite default to appdata kworship.db
[kworship.git] / kworship / css / KwCssScope.cpp
blobd8950fa85d9404fcce9108854a1e5326ebbc6a31
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(KwCssSchema* schema, KwCssScope* parent)
39 : m_schema(schema)
40 , m_parentScope(parent)
41 , m_childScopes()
42 , m_name()
43 , m_classes()
44 , m_styleSheets()
45 , m_styles()
47 if (0 != m_parentScope)
49 parent->m_childScopes.insert(this);
53 /// Destructor.
54 KwCssScope::~KwCssScope()
56 // Remove from parent's child list.
57 if (0 != m_parentScope)
59 m_parentScope->m_childScopes.remove(this);
62 // Remove from children and recalculate styles.
64 ScopeSet::iterator it;
65 for (it = m_childScopes.begin(); it != m_childScopes.end(); ++it)
67 (*it)->m_parentScope = 0;
68 (*it)->recalculateStyles();
72 // Clean style sheets
74 foreach (KwCssStyleSheet* stylesheet, m_styleSheets)
76 /// @todo reference count
77 delete stylesheet;
83 * DOM filters
86 /// Import the style information from a DOM.
87 void KwCssScope::importStylesFromDom(const QDomElement& element, KwResourceManager* resourceManager)
89 Q_UNUSED(resourceManager);
90 // Classes
91 QDomNodeList elems = element.elementsByTagName("class");
92 for (int i = 0; i < elems.count(); ++i)
94 m_classes += elems.at(i).toElement().text();
97 // Stylesheets
98 elems = element.elementsByTagName("sheet");
99 for (int i = 0; i < elems.count(); ++i)
101 KwCssStyleSheet* newSheet = new KwCssStyleSheet;
102 newSheet->import(m_schema, elems.at(i).toElement().text());
103 m_styleSheets.push_back(newSheet);
105 if (elems.count() > 0)
107 recalculateStyles();
110 // Explicit styles
111 elems = element.elementsByTagName("explicit");
112 for (int i = 0; i < elems.count(); ++i)
114 QString styles = elems.at(i).toElement().text();
115 int last = m_styles.import(m_schema, styles);
116 /// @todo Check it got to the end of the styles
120 /// Export the style information to a DOM.
121 void KwCssScope::exportStylesToDom(QDomDocument& document, QDomElement& element, KwResourceManager* resourceManager) const
123 Q_UNUSED(resourceManager);
124 // Start with the classes
125 foreach (QString className, m_classes)
127 QDomElement classElem = document.createElement("class");
128 element.appendChild(classElem);
129 classElem.appendChild(document.createTextNode(className));
132 // Now for the stylesheets
133 foreach (KwCssStyleSheet* stylesheet, m_styleSheets)
135 QDomElement sheetElem = document.createElement("sheet");
136 element.appendChild(sheetElem);
137 sheetElem.appendChild(document.createTextNode(stylesheet->toString()));
140 // And finally the explicit styles
141 if (!m_styles.isEmpty())
143 QDomElement explicitElem = document.createElement("explicit");
144 element.appendChild(explicitElem);
145 explicitElem.appendChild(document.createTextNode(m_styles.toString()));
150 * Main interface
153 /// Add a stylesheet for this scope.
154 void KwCssScope::addStyleSheet(KwCssStyleSheet* styleSheet)
156 m_styleSheets.push_back(styleSheet);
157 recalculateStyles();
160 /// Add a class to this scope.
161 void KwCssScope::addClass(QString className)
163 m_classes += className;
164 recalculateStyles();
167 /// Get all the styles included in this scope.
168 const KwCssStyleStates& KwCssScope::getStyles() const
170 return m_cachedStyles;
173 /// Recalculate all styles.
174 void KwCssScope::recalculateStyles()
177 * cached styles
178 * unmatched rules stylesheet
180 * get parent styles
181 * add classes
182 * get parent unmatched style rules
183 * go through stylesheets
184 * apply any rules that match in order, removing as we go from local list
185 * apply explicit styles
187 // Get the base styles from the parent
188 if (0 != m_parentScope)
190 m_cachedStyles = m_parentScope->getStyles();
191 m_cachedStyleSheet = m_parentScope->m_cachedStyleSheet;
192 m_cachedClasses = m_parentScope->m_cachedClasses;
194 else
196 m_cachedStyles.clear();
197 m_cachedStyleSheet.clear();
199 // Add local stylesheets
201 StyleSheetList::iterator it;
202 for (it = m_styleSheets.begin(); it != m_styleSheets.end(); ++it)
204 m_cachedStyleSheet.importStyleSheet(*it);
207 // Add local classes
208 m_cachedClasses += m_classes;
210 // process rules
211 bool processRules;
214 processRules = false;
215 QSet<QString> triggerableClasses;
216 KwCssStyleSheet::Iterator it;
217 for (it = m_cachedStyleSheet.getRules().begin(); it != m_cachedStyleSheet.getRules().end();)
219 // Match the keys
220 KwCssStyleRule::KeyList& keys = (*it).getCriteriaKeys();
221 if (!keys.empty() && isKeyMatching(keys.first()))
223 keys.pop_front();
225 // Match the classes
226 KwCssStyleRule::StringSet& classes = (*it).getCriteriaClasses();
227 classes -= m_cachedClasses;
229 if (!classes.empty())
231 triggerableClasses += classes;
232 ++it;
234 else if (keys.empty())
236 // Nothing left to match, we can apply this style and remove it from the list
237 m_cachedStyles << *(*it).getStyles();
238 QSet<QString> classesToApply = (*it).getIncludedStyles() - m_cachedClasses;
239 if (!classesToApply.empty())
241 m_cachedClasses += classesToApply;
242 QSet<QString> classesToTrigger = triggerableClasses & classesToApply;
243 if (!classesToTrigger.empty())
245 triggerableClasses -= classesToTrigger;
246 processRules = true;
249 // Remove from stylesheet since it's now applied
250 KwCssStyleSheet::Iterator cur = it;
251 ++it;
252 m_cachedStyleSheet.getRules().erase(cur);
254 else
256 ++it;
260 while (processRules);
262 // Apply each style
263 m_cachedStyles << m_styles;
265 // Recurse to children
267 ScopeSet::iterator it;
268 for (it = m_childScopes.begin(); it != m_childScopes.end(); ++it)
270 (*it)->recalculateStyles();
276 * Accessors
279 /// Find whether the scope is effectively empty.
280 bool KwCssScope::isScopeEmpty() const
282 return m_classes.isEmpty() && m_styleSheets.isEmpty() && m_styles.isEmpty();
285 /// Get explicit styles.
286 const KwCssStyles& KwCssScope::getExplicitStyles() const
288 return m_styles;
291 /// Get the scope's key.
292 KwCssScopeKey KwCssScope::getKey() const
294 return KwCssScopeKey(getTypeId(), m_name);
297 /// Find whether a scope key matches this scope.
298 bool KwCssScope::isKeyMatching(KwCssScopeKey key) const
300 return (!key.isTypeSpecified() || key.getTypeId() == getTypeId())
301 && (!key.isNameSpecified() || key.getName() == m_name);
305 * Mutators
308 /// Set the parent scope.
309 void KwCssScope::setParentScope(KwCssScope* parent)
311 if (0 != m_parentScope)
313 m_parentScope->m_childScopes.remove(this);
315 m_parentScope = parent;
316 if (0 != parent)
318 m_parentScope->m_childScopes.insert(this);
320 recalculateStyles();