ElementStyle.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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. while (parent != NULL)
  250. {
  251. const Property* parent_property = parent->style->GetLocalProperty(name);
  252. if (parent_property)
  253. return parent_property;
  254. parent = parent->GetParentNode();
  255. }
  256. }
  257. // No property available! Return the default value.
  258. return property->GetDefaultValue();
  259. }
  260. // Returns one of this element's properties.
  261. const Property* ElementStyle::GetLocalProperty(const String& name)
  262. {
  263. // Check for overriding local properties.
  264. if (local_properties != NULL)
  265. {
  266. const Property* property = local_properties->GetProperty(name);
  267. if (property != NULL)
  268. return property;
  269. }
  270. // Check for a property defined in an RCSS rule.
  271. if (definition != NULL)
  272. return definition->GetProperty(name, pseudo_classes);
  273. return NULL;
  274. }
  275. // Resolves one of this element's properties.
  276. float ElementStyle::ResolveProperty(const String& name, float base_value)
  277. {
  278. const Property* property = GetProperty(name);
  279. if (!property)
  280. {
  281. ROCKET_ERROR;
  282. return 0.0f;
  283. }
  284. if (property->unit & Property::RELATIVE_UNIT)
  285. {
  286. // The calculated value of the font-size property is inherited, so we need to check if this
  287. // is an inherited property. If so, then we return our parent's font size instead.
  288. if (name == FONT_SIZE)
  289. {
  290. Rocket::Core::Element* parent = element->GetParentNode();
  291. if (parent == NULL)
  292. return 0;
  293. if (GetLocalProperty(FONT_SIZE) == NULL)
  294. return parent->ResolveProperty(FONT_SIZE, 0);
  295. // The base value for font size is always the height of *this* element's parent's font.
  296. base_value = parent->ResolveProperty(FONT_SIZE, 0);
  297. }
  298. if (property->unit & Property::PERCENT)
  299. return base_value * property->value.Get< float >() * 0.01f;
  300. else if (property->unit & Property::EM)
  301. {
  302. // If an em-relative font size is specified, it is expressed relative to the parent's
  303. // font height.
  304. if (name == FONT_SIZE)
  305. return property->value.Get< float >() * base_value;
  306. else
  307. return property->value.Get< float >() * ElementUtilities::GetFontSize(element);
  308. }
  309. }
  310. if (property->unit & Property::NUMBER || property->unit & Property::PX)
  311. {
  312. return property->value.Get< float >();
  313. }
  314. // Values based on pixels-per-inch.
  315. if (property->unit & Property::PPI_UNIT)
  316. {
  317. float inch = property->value.Get< float >() * element->GetRenderInterface()->GetPixelsPerInch();
  318. if (property->unit & Property::IN) // inch
  319. return inch;
  320. if (property->unit & Property::CM) // centimeter
  321. return inch / 2.54f;
  322. if (property->unit & Property::MM) // millimeter
  323. return inch / 25.4f;
  324. if (property->unit & Property::PT) // point
  325. return inch / 72.0f;
  326. if (property->unit & Property::PC) // pica
  327. return inch / 6.0f;
  328. }
  329. // We're not a numeric property; return 0.
  330. return 0.0f;
  331. }
  332. // Iterates over the properties defined on the element.
  333. bool ElementStyle::IterateProperties(int& index, PseudoClassList& property_pseudo_classes, String& name, const Property*& property)
  334. {
  335. // First check for locally defined properties.
  336. if (local_properties != NULL)
  337. {
  338. if (index < local_properties->GetNumProperties())
  339. {
  340. PropertyMap::const_iterator i = local_properties->GetProperties().begin();
  341. for (int count = 0; count < index; ++count)
  342. ++i;
  343. name = (*i).first;
  344. property = &((*i).second);
  345. property_pseudo_classes.clear();
  346. ++index;
  347. return true;
  348. }
  349. }
  350. const ElementDefinition* definition = GetDefinition();
  351. if (definition != NULL)
  352. {
  353. int index_offset = 0;
  354. if (local_properties != NULL)
  355. index_offset = local_properties->GetNumProperties();
  356. // Offset the index to be relative to the definition before we start indexing. When we do get a property back,
  357. // check that it hasn't been overridden by the element's local properties; if so, continue on to the next one.
  358. index -= index_offset;
  359. while (definition->IterateProperties(index, pseudo_classes, property_pseudo_classes, name, property))
  360. {
  361. if (local_properties == NULL ||
  362. local_properties->GetProperty(name) == NULL)
  363. {
  364. index += index_offset;
  365. return true;
  366. }
  367. }
  368. return false;
  369. }
  370. return false;
  371. }
  372. // Returns the active style sheet for this element. This may be NULL.
  373. StyleSheet* ElementStyle::GetStyleSheet() const
  374. {
  375. ElementDocument* document = element->GetOwnerDocument();
  376. if (document != NULL)
  377. return document->GetStyleSheet();
  378. return NULL;
  379. }
  380. void ElementStyle::DirtyDefinition()
  381. {
  382. definition_dirty = true;
  383. DirtyChildDefinitions();
  384. // Dirty the child definition update the element tree
  385. Element* parent = element->GetParentNode();
  386. while (parent)
  387. {
  388. parent->GetStyle()->child_definition_dirty = true;
  389. parent = parent->GetParentNode();
  390. }
  391. }
  392. void ElementStyle::DirtyChildDefinitions()
  393. {
  394. for (int i = 0; i < element->GetNumChildren(true); i++)
  395. element->GetChild(i)->GetStyle()->DirtyDefinition();
  396. }
  397. // Dirties every property.
  398. void ElementStyle::DirtyProperties()
  399. {
  400. PropertyNameList properties;
  401. StyleSheetSpecification::GetRegisteredProperties(properties);
  402. DirtyProperties(properties);
  403. }
  404. // Dirties em-relative properties.
  405. void ElementStyle::DirtyEmProperties()
  406. {
  407. PropertyNameList properties;
  408. StyleSheetSpecification::GetRegisteredProperties(properties);
  409. // Check if any of these are currently em-relative. If so, dirty them.
  410. PropertyNameList em_properties;
  411. for (PropertyNameList::iterator list_iterator = properties.begin(); list_iterator != properties.end(); ++list_iterator)
  412. {
  413. // Skip font-size; this is relative to our parent's em, not ours.
  414. if (*list_iterator == FONT_SIZE)
  415. continue;
  416. // Get this element from this element. If this is em-relative, then add it to the list to
  417. // dirty.
  418. if (element->GetProperty(*list_iterator)->unit == Property::EM)
  419. em_properties.insert(*list_iterator);
  420. }
  421. if (!em_properties.empty())
  422. DirtyProperties(em_properties);
  423. // Now dirty all of our descendant's font-size properties that are relative to ems.
  424. int num_children = element->GetNumChildren(true);
  425. for (int i = 0; i < num_children; ++i)
  426. element->GetChild(i)->GetStyle()->DirtyInheritedEmProperties();
  427. }
  428. // Dirties font-size on child elements if appropriate.
  429. void ElementStyle::DirtyInheritedEmProperties()
  430. {
  431. const Property* font_size = element->GetLocalProperty(FONT_SIZE);
  432. if (font_size == NULL)
  433. {
  434. int num_children = element->GetNumChildren(true);
  435. for (int i = 0; i < num_children; ++i)
  436. element->GetChild(i)->GetStyle()->DirtyInheritedEmProperties();
  437. }
  438. else
  439. {
  440. if (font_size->unit & Property::RELATIVE_UNIT)
  441. DirtyProperty(FONT_SIZE);
  442. }
  443. }
  444. // Sets a single property as dirty.
  445. void ElementStyle::DirtyProperty(const String& property)
  446. {
  447. PropertyNameList properties;
  448. properties.insert(String(property));
  449. DirtyProperties(properties);
  450. }
  451. // Sets a list of properties as dirty.
  452. void ElementStyle::DirtyProperties(const PropertyNameList& properties)
  453. {
  454. if (properties.empty())
  455. return;
  456. PropertyNameList inherited_properties;
  457. for (PropertyNameList::const_iterator i = properties.begin(); i != properties.end(); ++i)
  458. {
  459. // If this property is an inherited property, then push it into the list to be passed onto our children.
  460. const PropertyDefinition* property = StyleSheetSpecification::GetProperty(*i);
  461. if (property != NULL &&
  462. property->IsInherited())
  463. inherited_properties.insert(*i);
  464. }
  465. // Pass the list of those properties that are inherited onto our children.
  466. if (!inherited_properties.empty())
  467. {
  468. for (int i = 0; i < element->GetNumChildren(true); i++)
  469. element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
  470. }
  471. // And send the event.
  472. element->OnPropertyChange(properties);
  473. }
  474. // Sets a list of our potentially inherited properties as dirtied by an ancestor.
  475. void ElementStyle::DirtyInheritedProperties(const PropertyNameList& properties)
  476. {
  477. PropertyNameList inherited_properties;
  478. for (PropertyNameList::const_iterator i = properties.begin(); i != properties.end(); ++i)
  479. {
  480. if (GetLocalProperty((*i)) == NULL)
  481. inherited_properties.insert(*i);
  482. }
  483. if (inherited_properties.empty())
  484. return;
  485. // Pass the list of those properties that this element doesn't override onto our children.
  486. for (int i = 0; i < element->GetNumChildren(true); i++)
  487. element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
  488. element->OnPropertyChange(properties);
  489. }
  490. }
  491. }