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
)
89 Q_UNUSED(resourceManager
);
91 QDomNodeList elems
= element
.elementsByTagName("class");
92 for (int i
= 0; i
< elems
.count(); ++i
)
94 m_classes
+= elems
.at(i
).toElement().text();
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)
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()));
153 /// Add a stylesheet for this scope.
154 void KwCssScope::addStyleSheet(KwCssStyleSheet
* styleSheet
)
156 m_styleSheets
.push_back(styleSheet
);
160 /// Add a class to this scope.
161 void KwCssScope::addClass(QString className
)
163 m_classes
+= className
;
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()
178 * unmatched rules stylesheet
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
;
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
);
208 m_cachedClasses
+= m_classes
;
214 processRules
= false;
215 QSet
<QString
> triggerableClasses
;
216 KwCssStyleSheet::Iterator it
;
217 for (it
= m_cachedStyleSheet
.getRules().begin(); it
!= m_cachedStyleSheet
.getRules().end();)
220 KwCssStyleRule::KeyList
& keys
= (*it
).getCriteriaKeys();
221 if (!keys
.empty() && isKeyMatching(keys
.first()))
226 KwCssStyleRule::StringSet
& classes
= (*it
).getCriteriaClasses();
227 classes
-= m_cachedClasses
;
229 if (!classes
.empty())
231 triggerableClasses
+= classes
;
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
;
249 // Remove from stylesheet since it's now applied
250 KwCssStyleSheet::Iterator cur
= it
;
252 m_cachedStyleSheet
.getRules().erase(cur
);
260 while (processRules
);
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();
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
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
);
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
;
318 m_parentScope
->m_childScopes
.insert(this);