ElementStyle.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /*
  2. * This source file is part of libRocket, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://www.librocket.com
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. */
  27. #include "precompiled.h"
  28. #include "ElementStyle.h"
  29. #include <algorithm>
  30. #include <Rocket/Core/ElementDocument.h>
  31. #include <Rocket/Core/ElementUtilities.h>
  32. #include <Rocket/Core/Log.h>
  33. #include <Rocket/Core/Property.h>
  34. #include <Rocket/Core/PropertyDefinition.h>
  35. #include <Rocket/Core/PropertyDictionary.h>
  36. #include <Rocket/Core/StyleSheetSpecification.h>
  37. #include "ElementBackground.h"
  38. #include "ElementBorder.h"
  39. #include "ElementDecoration.h"
  40. #include "ElementDefinition.h"
  41. #include "FontFaceHandle.h"
  42. namespace Rocket {
  43. namespace Core {
  44. ElementStyle::ElementStyle(Element* _element)
  45. {
  46. local_properties = NULL;
  47. definition = NULL;
  48. element = _element;
  49. definition_dirty = true;
  50. child_definition_dirty = true;
  51. }
  52. ElementStyle::~ElementStyle()
  53. {
  54. if (local_properties != NULL)
  55. delete local_properties;
  56. if (definition != NULL)
  57. definition->RemoveReference();
  58. }
  59. // Returns the element's definition, updating if necessary.
  60. const ElementDefinition* ElementStyle::GetDefinition()
  61. {
  62. if (definition_dirty)
  63. {
  64. UpdateDefinition();
  65. }
  66. return definition;
  67. }
  68. void ElementStyle::UpdateDefinition()
  69. {
  70. if (definition_dirty)
  71. {
  72. definition_dirty = false;
  73. ElementDefinition* new_definition = NULL;
  74. const StyleSheet* style_sheet = GetStyleSheet();
  75. if (style_sheet != NULL)
  76. {
  77. new_definition = style_sheet->GetElementDefinition(element);
  78. }
  79. // Switch the property definitions if the definition has changed.
  80. if (new_definition != definition || new_definition == NULL)
  81. {
  82. PropertyNameList properties;
  83. if (definition != NULL)
  84. {
  85. definition->GetDefinedProperties(properties, pseudo_classes);
  86. definition->RemoveReference();
  87. }
  88. definition = new_definition;
  89. if (definition != NULL)
  90. definition->GetDefinedProperties(properties, pseudo_classes);
  91. DirtyProperties(properties);
  92. element->GetElementDecoration()->ReloadDecorators();
  93. }
  94. else if (new_definition != NULL)
  95. {
  96. new_definition->RemoveReference();
  97. }
  98. }
  99. if (child_definition_dirty)
  100. {
  101. for (int i = 0; i < element->GetNumChildren(true); i++)
  102. {
  103. element->GetChild(i)->GetStyle()->UpdateDefinition();
  104. }
  105. child_definition_dirty = false;
  106. }
  107. }
  108. // Sets or removes a pseudo-class on the element.
  109. void ElementStyle::SetPseudoClass(const String& pseudo_class, bool activate)
  110. {
  111. size_t num_pseudo_classes = pseudo_classes.size();
  112. if (activate)
  113. pseudo_classes.insert(pseudo_class);
  114. else
  115. pseudo_classes.erase(pseudo_class);
  116. if (pseudo_classes.size() != num_pseudo_classes)
  117. {
  118. element->GetElementDecoration()->DirtyDecorators();
  119. const ElementDefinition* definition = element->GetDefinition();
  120. if (definition != NULL)
  121. {
  122. PropertyNameList properties;
  123. definition->GetDefinedProperties(properties, pseudo_classes, pseudo_class);
  124. DirtyProperties(properties);
  125. switch (definition->GetPseudoClassVolatility(pseudo_class))
  126. {
  127. case ElementDefinition::FONT_VOLATILE:
  128. element->DirtyFont();
  129. break;
  130. case ElementDefinition::STRUCTURE_VOLATILE:
  131. DirtyChildDefinitions();
  132. break;
  133. default:
  134. break;
  135. }
  136. }
  137. }
  138. }
  139. // Checks if a specific pseudo-class has been set on the element.
  140. bool ElementStyle::IsPseudoClassSet(const String& pseudo_class) const
  141. {
  142. return (pseudo_classes.find(pseudo_class) != pseudo_classes.end());
  143. }
  144. const PseudoClassList& ElementStyle::GetActivePseudoClasses() const
  145. {
  146. return pseudo_classes;
  147. }
  148. // Sets or removes a class on the element.
  149. void ElementStyle::SetClass(const String& class_name, bool activate)
  150. {
  151. StringList::iterator class_location = std::find(classes.begin(), classes.end(), class_name);
  152. if (activate)
  153. {
  154. if (class_location == classes.end())
  155. {
  156. classes.push_back(class_name);
  157. DirtyDefinition();
  158. }
  159. }
  160. else
  161. {
  162. if (class_location != classes.end())
  163. {
  164. classes.erase(class_location);
  165. DirtyDefinition();
  166. }
  167. }
  168. }
  169. // Checks if a class is set on the element.
  170. bool ElementStyle::IsClassSet(const String& class_name) const
  171. {
  172. return std::find(classes.begin(), classes.end(), class_name) != classes.end();
  173. }
  174. // Specifies the entire list of classes for this element. This will replace any others specified.
  175. void ElementStyle::SetClassNames(const String& class_names)
  176. {
  177. classes.clear();
  178. StringUtilities::ExpandString(classes, class_names, ' ');
  179. DirtyDefinition();
  180. }
  181. // Returns the list of classes specified for this element.
  182. String ElementStyle::GetClassNames() const
  183. {
  184. String class_names;
  185. for (size_t i = 0; i < classes.size(); i++)
  186. {
  187. if (i != 0)
  188. {
  189. class_names.Append(" ");
  190. }
  191. class_names.Append(classes[i]);
  192. }
  193. return class_names;
  194. }
  195. // Sets a local property override on the element.
  196. bool ElementStyle::SetProperty(const String& name, const String& value)
  197. {
  198. if (local_properties == NULL)
  199. local_properties = new PropertyDictionary();
  200. if (StyleSheetSpecification::ParsePropertyDeclaration(*local_properties, name, value))
  201. {
  202. DirtyProperty(name);
  203. return true;
  204. }
  205. else
  206. {
  207. Log::Message(Log::LT_WARNING, "Syntax error parsing inline property declaration '%s: %s;'.", name.CString(), value.CString());
  208. return false;
  209. }
  210. }
  211. // Sets a local property override on the element to a pre-parsed value.
  212. bool ElementStyle::SetProperty(const String& name, const Property& property)
  213. {
  214. Property new_property = property;
  215. new_property.definition = StyleSheetSpecification::GetProperty(name);
  216. if (new_property.definition == NULL)
  217. return false;
  218. if (local_properties == NULL)
  219. local_properties = new PropertyDictionary();
  220. local_properties->SetProperty(name, new_property);
  221. DirtyProperty(name);
  222. return true;
  223. }
  224. // Removes a local property override on the element.
  225. void ElementStyle::RemoveProperty(const String& name)
  226. {
  227. if (local_properties == NULL)
  228. return;
  229. if (local_properties->GetProperty(name) != NULL)
  230. {
  231. local_properties->RemoveProperty(name);
  232. DirtyProperty(name);
  233. }
  234. }
  235. // Returns one of this element's properties.
  236. const Property* ElementStyle::GetProperty(const String& name)
  237. {
  238. const Property* local_property = GetLocalProperty(name);
  239. if (local_property != NULL)
  240. return local_property;
  241. // Fetch the property specification.
  242. const PropertyDefinition* property = StyleSheetSpecification::GetProperty(name);
  243. if (property == NULL)
  244. return NULL;
  245. // If we can inherit this property, return our parent's property.
  246. if (property->IsInherited())
  247. {
  248. Element* parent = element->GetParentNode();
  249. if (parent != NULL)
  250. {
  251. return parent->GetProperty(name);
  252. }
  253. }
  254. // No property available! Return the default value.
  255. return property->GetDefaultValue();
  256. }
  257. // Returns one of this element's properties.
  258. const Property* ElementStyle::GetLocalProperty(const String& name)
  259. {
  260. // Check for overriding local properties.
  261. if (local_properties != NULL)
  262. {
  263. const Property* property = local_properties->GetProperty(name);
  264. if (property != NULL)
  265. return property;
  266. }
  267. // Check for a property defined in an RCSS rule.
  268. if (definition != NULL)
  269. return definition->GetProperty(name, pseudo_classes);
  270. return NULL;
  271. }
  272. // Resolves one of this element's properties.
  273. float ElementStyle::ResolveProperty(const String& name, float base_value)
  274. {
  275. const Property* property = GetProperty(name);
  276. if (!property)
  277. {
  278. ROCKET_ERROR;
  279. return 0.0f;
  280. }
  281. if (property->unit & Property::RELATIVE_UNIT)
  282. {
  283. // The calculated value of the font-size property is inherited, so we need to check if this
  284. // is an inherited property. If so, then we return our parent's font size instead.
  285. if (name == FONT_SIZE)
  286. {
  287. Rocket::Core::Element* parent = element->GetParentNode();
  288. if (parent == NULL)
  289. return 0;
  290. if (GetLocalProperty(FONT_SIZE) == NULL)
  291. return parent->ResolveProperty(FONT_SIZE, 0);
  292. // The base value for font size is always the height of *this* element's parent's font.
  293. base_value = parent->ResolveProperty(FONT_SIZE, 0);
  294. }
  295. if (property->unit & Property::PERCENT)
  296. return base_value * property->value.Get< float >() * 0.01f;
  297. else if (property->unit & Property::EM)
  298. {
  299. // If an em-relative font size is specified, it is expressed relative to the parent's
  300. // font height.
  301. if (name == FONT_SIZE)
  302. return property->value.Get< float >() * base_value;
  303. else
  304. return property->value.Get< float >() * ElementUtilities::GetFontSize(element);
  305. }
  306. }
  307. if (property->unit & Property::NUMBER || property->unit & Property::PX)
  308. {
  309. return property->value.Get< float >();
  310. }
  311. // We're not a numeric property; return 0.
  312. return 0.0f;
  313. }
  314. // Iterates over the properties defined on the element.
  315. bool ElementStyle::IterateProperties(int& index, PseudoClassList& property_pseudo_classes, String& name, const Property*& property)
  316. {
  317. // First check for locally defined properties.
  318. if (local_properties != NULL)
  319. {
  320. if (index < local_properties->GetNumProperties())
  321. {
  322. PropertyMap::const_iterator i = local_properties->GetProperties().begin();
  323. for (int count = 0; count < index; ++count)
  324. ++i;
  325. name = (*i).first;
  326. property = &((*i).second);
  327. property_pseudo_classes.clear();
  328. ++index;
  329. return true;
  330. }
  331. }
  332. const ElementDefinition* definition = GetDefinition();
  333. if (definition != NULL)
  334. {
  335. int index_offset = 0;
  336. if (local_properties != NULL)
  337. index_offset = local_properties->GetNumProperties();
  338. // Offset the index to be relative to the definition before we start indexing. When we do get a property back,
  339. // check that it hasn't been overridden by the element's local properties; if so, continue on to the next one.
  340. index -= index_offset;
  341. while (definition->IterateProperties(index, pseudo_classes, property_pseudo_classes, name, property))
  342. {
  343. if (local_properties == NULL ||
  344. local_properties->GetProperty(name) == NULL)
  345. {
  346. index += index_offset;
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. return false;
  353. }
  354. // Returns the active style sheet for this element. This may be NULL.
  355. StyleSheet* ElementStyle::GetStyleSheet() const
  356. {
  357. ElementDocument* document = element->GetOwnerDocument();
  358. if (document != NULL)
  359. return document->GetStyleSheet();
  360. return NULL;
  361. }
  362. void ElementStyle::DirtyDefinition()
  363. {
  364. definition_dirty = true;
  365. DirtyChildDefinitions();
  366. // Dirty the child definition update the element tree
  367. Element* parent = element->GetParentNode();
  368. while (parent)
  369. {
  370. parent->GetStyle()->child_definition_dirty = true;
  371. parent = parent->GetParentNode();
  372. }
  373. }
  374. void ElementStyle::DirtyChildDefinitions()
  375. {
  376. for (int i = 0; i < element->GetNumChildren(true); i++)
  377. element->GetChild(i)->GetStyle()->DirtyDefinition();
  378. }
  379. // Dirties every property.
  380. void ElementStyle::DirtyProperties()
  381. {
  382. PropertyNameList properties;
  383. StyleSheetSpecification::GetRegisteredProperties(properties);
  384. DirtyProperties(properties);
  385. }
  386. // Dirties em-relative properties.
  387. void ElementStyle::DirtyEmProperties()
  388. {
  389. PropertyNameList properties;
  390. StyleSheetSpecification::GetRegisteredProperties(properties);
  391. // Check if any of these are currently em-relative. If so, dirty them.
  392. PropertyNameList em_properties;
  393. for (PropertyNameList::iterator list_iterator = properties.begin(); list_iterator != properties.end(); ++list_iterator)
  394. {
  395. // Skip font-size; this is relative to our parent's em, not ours.
  396. if (*list_iterator == FONT_SIZE)
  397. continue;
  398. // Get this element from this element. If this is em-relative, then add it to the list to
  399. // dirty.
  400. if (element->GetProperty(*list_iterator)->unit == Property::EM)
  401. em_properties.insert(*list_iterator);
  402. }
  403. if (!em_properties.empty())
  404. DirtyProperties(em_properties);
  405. // Now dirty all of our descendant's font-size properties that are relative to ems.
  406. int num_children = element->GetNumChildren(true);
  407. for (int i = 0; i < num_children; ++i)
  408. element->GetChild(i)->GetStyle()->DirtyInheritedEmProperties();
  409. }
  410. // Dirties font-size on child elements if appropriate.
  411. void ElementStyle::DirtyInheritedEmProperties()
  412. {
  413. const Property* font_size = element->GetLocalProperty(FONT_SIZE);
  414. if (font_size == NULL)
  415. {
  416. int num_children = element->GetNumChildren(true);
  417. for (int i = 0; i < num_children; ++i)
  418. element->GetChild(i)->GetStyle()->DirtyInheritedEmProperties();
  419. }
  420. else
  421. {
  422. if (font_size->unit & Property::RELATIVE_UNIT)
  423. DirtyProperty(FONT_SIZE);
  424. }
  425. }
  426. // Sets a single property as dirty.
  427. void ElementStyle::DirtyProperty(const String& property)
  428. {
  429. PropertyNameList properties;
  430. properties.insert(String(property));
  431. DirtyProperties(properties);
  432. }
  433. // Sets a list of properties as dirty.
  434. void ElementStyle::DirtyProperties(const PropertyNameList& properties)
  435. {
  436. if (properties.empty())
  437. return;
  438. PropertyNameList inherited_properties;
  439. for (PropertyNameList::const_iterator i = properties.begin(); i != properties.end(); ++i)
  440. {
  441. // If this property is an inherited property, then push it into the list to be passed onto our children.
  442. const PropertyDefinition* property = StyleSheetSpecification::GetProperty(*i);
  443. if (property != NULL &&
  444. property->IsInherited())
  445. inherited_properties.insert(*i);
  446. }
  447. // Pass the list of those properties that are inherited onto our children.
  448. if (!inherited_properties.empty())
  449. {
  450. for (int i = 0; i < element->GetNumChildren(true); i++)
  451. element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
  452. }
  453. // And send the event.
  454. element->OnPropertyChange(properties);
  455. }
  456. // Sets a list of our potentially inherited properties as dirtied by an ancestor.
  457. void ElementStyle::DirtyInheritedProperties(const PropertyNameList& properties)
  458. {
  459. PropertyNameList inherited_properties;
  460. for (PropertyNameList::const_iterator i = properties.begin(); i != properties.end(); ++i)
  461. {
  462. if (GetLocalProperty((*i)) == NULL)
  463. inherited_properties.insert(*i);
  464. }
  465. if (inherited_properties.empty())
  466. return;
  467. // Pass the list of those properties that this element doesn't override onto our children.
  468. for (int i = 0; i < element->GetNumChildren(true); i++)
  469. element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
  470. element->OnPropertyChange(properties);
  471. }
  472. }
  473. }