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(KwCssSchema
* schema
, KwCssScope
* parent
)
40 , m_parentScope(parent
)
47 if (0 != m_parentScope
)
49 parent
->m_childScopes
.insert(this);
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();
74 foreach (KwCssStyleSheet
* stylesheet
, m_styleSheets
)
76 /// @todo reference count
86 /// Import the style information from a DOM.
87 void KwCssScope::importStylesFromDom(const QDomElement
& element
, KwResourceManager
* resourceManager
)
90 QDomNodeList elems
= element
.elementsByTagName("class");
91 for (int i
= 0; i
< elems
.count(); ++i
)
93 m_classes
+= elems
.at(i
).toElement().text();
97 elems
= element
.elementsByTagName("sheet");
98 for (int i
= 0; i
< elems
.count(); ++i
)
100 KwCssStyleSheet
* newSheet
= new KwCssStyleSheet
;
101 newSheet
->import(m_schema
, elems
.at(i
).toElement().text());
102 m_styleSheets
.push_back(newSheet
);
104 if (elems
.count() > 0)
110 elems
= element
.elementsByTagName("explicit");
111 for (int i
= 0; i
< elems
.count(); ++i
)
113 QString styles
= elems
.at(i
).toElement().text();
114 int last
= m_styles
.import(m_schema
, styles
);
115 /// @todo Check it got to the end of the styles
119 /// Export the style information to a DOM.
120 void KwCssScope::exportStylesToDom(QDomDocument
& document
, QDomElement
& element
, KwResourceManager
* resourceManager
) const
122 // Start with the classes
123 foreach (QString className
, m_classes
)
125 QDomElement classElem
= document
.createElement("class");
126 element
.appendChild(classElem
);
127 classElem
.appendChild(document
.createTextNode(className
));
130 // Now for the stylesheets
131 foreach (KwCssStyleSheet
* stylesheet
, m_styleSheets
)
133 QDomElement sheetElem
= document
.createElement("sheet");
134 element
.appendChild(sheetElem
);
135 sheetElem
.appendChild(document
.createTextNode(stylesheet
->toString()));
138 // And finally the explicit styles
139 if (!m_styles
.isEmpty())
141 QDomElement explicitElem
= document
.createElement("explicit");
142 element
.appendChild(explicitElem
);
143 explicitElem
.appendChild(document
.createTextNode(m_styles
.toString()));
151 /// Add a stylesheet for this scope.
152 void KwCssScope::addStyleSheet(KwCssStyleSheet
* styleSheet
)
154 m_styleSheets
.push_back(styleSheet
);
158 /// Add a class to this scope.
159 void KwCssScope::addClass(QString className
)
161 m_classes
+= className
;
165 /// Get all the styles included in this scope.
166 const KwCssStyleStates
& KwCssScope::getStyles() const
168 return m_cachedStyles
;
171 /// Recalculate all styles.
172 void KwCssScope::recalculateStyles()
176 * unmatched rules stylesheet
180 * get parent unmatched style rules
181 * go through stylesheets
182 * apply any rules that match in order, removing as we go from local list
183 * apply explicit styles
185 // Get the base styles from the parent
186 if (0 != m_parentScope
)
188 m_cachedStyles
= m_parentScope
->getStyles();
189 m_cachedStyleSheet
= m_parentScope
->m_cachedStyleSheet
;
190 m_cachedClasses
= m_parentScope
->m_cachedClasses
;
194 m_cachedStyles
.clear();
195 m_cachedStyleSheet
.clear();
197 // Add local stylesheets
199 StyleSheetList::iterator it
;
200 for (it
= m_styleSheets
.begin(); it
!= m_styleSheets
.end(); ++it
)
202 m_cachedStyleSheet
.importStyleSheet(*it
);
206 m_cachedClasses
+= m_classes
;
212 processRules
= false;
213 QSet
<QString
> triggerableClasses
;
214 KwCssStyleSheet::Iterator it
;
215 for (it
= m_cachedStyleSheet
.getRules().begin(); it
!= m_cachedStyleSheet
.getRules().end();)
218 KwCssStyleRule::KeyList
& keys
= (*it
).getCriteriaKeys();
219 if (!keys
.empty() && isKeyMatching(keys
.first()))
224 KwCssStyleRule::StringSet
& classes
= (*it
).getCriteriaClasses();
225 classes
-= m_cachedClasses
;
227 if (!classes
.empty())
229 triggerableClasses
+= classes
;
232 else if (keys
.empty())
234 // Nothing left to match, we can apply this style and remove it from the list
235 m_cachedStyles
<< *(*it
).getStyles();
236 QSet
<QString
> classesToApply
= (*it
).getIncludedStyles() - m_cachedClasses
;
237 if (!classesToApply
.empty())
239 m_cachedClasses
+= classesToApply
;
240 QSet
<QString
> classesToTrigger
= triggerableClasses
& classesToApply
;
241 if (!classesToTrigger
.empty())
243 triggerableClasses
-= classesToTrigger
;
247 // Remove from stylesheet since it's now applied
248 KwCssStyleSheet::Iterator cur
= it
;
250 m_cachedStyleSheet
.getRules().erase(cur
);
258 while (processRules
);
261 m_cachedStyles
<< m_styles
;
263 // Recurse to children
265 ScopeSet::iterator it
;
266 for (it
= m_childScopes
.begin(); it
!= m_childScopes
.end(); ++it
)
268 (*it
)->recalculateStyles();
277 /// Find whether the scope is effectively empty.
278 bool KwCssScope::isScopeEmpty() const
280 return m_classes
.isEmpty() && m_styleSheets
.isEmpty() && m_styles
.isEmpty();
283 /// Get explicit styles.
284 const KwCssStyles
& KwCssScope::getExplicitStyles() const
289 /// Get the scope's key.
290 KwCssScopeKey
KwCssScope::getKey() const
292 return KwCssScopeKey(getTypeId(), m_name
);
295 /// Find whether a scope key matches this scope.
296 bool KwCssScope::isKeyMatching(KwCssScopeKey key
) const
298 return (!key
.isTypeSpecified() || key
.getTypeId() == getTypeId())
299 && (!key
.isNameSpecified() || key
.getName() == m_name
);
306 /// Set the parent scope.
307 void KwCssScope::setParentScope(KwCssScope
* parent
)
309 if (0 != m_parentScope
)
311 m_parentScope
->m_childScopes
.remove(this);
313 m_parentScope
= parent
;
316 m_parentScope
->m_childScopes
.insert(this);