1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * provider.cpp: an api for PropertyValue providers (for property inheritance)
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
17 #include "frameworkelement.h"
18 #include "textblock.h"
20 #include "deployment.h"
23 // LocalPropertyValueProvider
26 LocalPropertyValueProvider::LocalPropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
27 : PropertyValueProvider (obj
, precedence
)
29 // XXX maybe move the "DependencyObject::current_values" hash table here?
32 LocalPropertyValueProvider::~LocalPropertyValueProvider ()
37 LocalPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
39 return (Value
*) g_hash_table_lookup (obj
->GetLocalValues (), property
);
44 // StylePropertyValueProvider
47 StylePropertyValueProvider::StylePropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
48 : PropertyValueProvider (obj
, precedence
)
50 style_hash
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
52 (GDestroyNotify
)event_object_unref
);
56 StylePropertyValueProvider::unlink_converted_value (gpointer key
, gpointer value
, gpointer data
)
58 StylePropertyValueProvider
*provider
= (StylePropertyValueProvider
*)data
;
59 Setter
*s
= (Setter
*)value
;
61 Value
*v
= s
->GetValue(Setter::ConvertedValueProperty
);
62 if (v
->Is(Type::DEPENDENCY_OBJECT
)) {
63 DependencyObject
*dob
= v
->AsDependencyObject();
64 if (dob
->GetParent() == provider
->obj
)
65 dob
->SetParent(NULL
, NULL
);
69 StylePropertyValueProvider::~StylePropertyValueProvider ()
71 g_hash_table_foreach (style_hash
, StylePropertyValueProvider::unlink_converted_value
, this);
72 g_hash_table_destroy (style_hash
);
76 StylePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
78 Setter
*setter
= (Setter
*)g_hash_table_lookup (style_hash
, property
);
83 return setter
->GetValue (Setter::ConvertedValueProperty
);
87 StylePropertyValueProvider::RecomputePropertyValue (DependencyProperty
*prop
)
89 Style
*style
= ((FrameworkElement
*)obj
)->GetStyle();
93 DependencyProperty
*property
= NULL
;
95 SetterBaseCollection
*setters
= style
->GetSetters ();
99 CollectionIterator
*iter
= setters
->GetIterator ();
103 while (iter
->Next () && (setterBase
= iter
->GetCurrent (&err
))) {
105 // Something bad happened - what to do?
110 if (!setterBase
->Is (Type::SETTER
))
113 Setter
*setter
= setterBase
->AsSetter ();
114 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
117 if (!(property
= value
->AsDependencyProperty ()))
120 if (prop
== property
) {
121 // the hash holds a ref
123 g_hash_table_insert (style_hash
, property
, setter
);
134 StylePropertyValueProvider::SealStyle (Style
*style
)
138 SetterBaseCollection
*setters
= style
->GetSetters ();
142 CollectionIterator
*iter
= setters
->GetIterator ();
146 while (iter
->Next () && (setterBase
= iter
->GetCurrent (&err
))) {
148 // Something bad happened - what to do?
153 if (!setterBase
->Is (Type::SETTER
))
156 Setter
*setter
= setterBase
->AsSetter ();
158 DependencyProperty
*setter_property
;
161 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
164 if (!(setter_property
= value
->AsDependencyProperty ()))
168 if (!(setter_value
= setter
->GetValue (Setter::ConvertedValueProperty
)))
171 // the hash holds a ref
173 g_hash_table_insert (style_hash
, setter_property
, setter
);
176 obj
->ProviderValueChanged (precedence
, setter_property
, NULL
, setter_value
, true, true, &error
);
184 // InheritedPropertyValueProvider
187 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
189 int propertyId
= property
->GetId ();
191 if (!IsPropertyInherited (propertyId
))
194 int parentPropertyId
= -1;
196 Types
*types
= Deployment::GetCurrent()->GetTypes();
198 #define INHERIT_CTI_CTI(p) \
200 if (property->GetId () == Control::p || \
201 property->GetId () == TextBlock::p || \
202 property->GetId () == Inline::p) { \
204 if (types->IsSubclassOf (parent->GetObjectType(), Type::CONTROL)) \
205 parentPropertyId = Control::p; \
206 else if (types->IsSubclassOf (parent->GetObjectType(), Type::TEXTBLOCK)) \
207 parentPropertyId = TextBlock::p; \
212 #define INHERIT_I_T(p) \
214 if (property->GetId () == Inline::p) { \
215 parentPropertyId = TextBlock::p; \
219 #define INHERIT_F_F(p) \
221 if (property->GetId () == FrameworkElement::p) { \
222 parentPropertyId = FrameworkElement::p; \
226 #define INHERIT_U_U(p) \
228 if (property->GetId () == UIElement::p) { \
229 parentPropertyId = UIElement::p; \
233 DependencyObject
*parent
= NULL
;
235 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::FRAMEWORKELEMENT
)) {
236 // we loop up the visual tree
237 parent
= ((FrameworkElement
*)obj
)->GetVisualParent();
240 INHERIT_CTI_CTI (ForegroundProperty
);
241 INHERIT_CTI_CTI (FontFamilyProperty
);
242 INHERIT_CTI_CTI (FontStretchProperty
);
243 INHERIT_CTI_CTI (FontStyleProperty
);
244 INHERIT_CTI_CTI (FontWeightProperty
);
245 INHERIT_CTI_CTI (FontSizeProperty
);
247 INHERIT_F_F (LanguageProperty
);
248 INHERIT_F_F (DataContextProperty
);
250 INHERIT_U_U (UseLayoutRoundingProperty
);
252 if (parentPropertyId
!= -1)
253 return parent
->GetValue (types
->GetProperty (parentPropertyId
));
255 parent
= ((FrameworkElement
*)parent
)->GetVisualParent();
259 else if (types
->IsSubclassOf (obj
->GetObjectType(), Type::INLINE
)) {
261 DependencyObject
*new_parent
= obj
->GetParent();
262 while (new_parent
&& !types
->IsSubclassOf (new_parent
->GetObjectType(), Type::TEXTBLOCK
))
263 new_parent
= new_parent
->GetParent ();
269 INHERIT_I_T (ForegroundProperty
);
270 INHERIT_I_T (FontFamilyProperty
);
271 INHERIT_I_T (FontStretchProperty
);
272 INHERIT_I_T (FontStyleProperty
);
273 INHERIT_I_T (FontWeightProperty
);
274 INHERIT_I_T (FontSizeProperty
);
276 INHERIT_I_T (LanguageProperty
);
277 INHERIT_I_T (TextDecorationsProperty
);
279 if (parentPropertyId
!= -1) {
280 return parent
->GetValue (parentPropertyId
);
289 InheritedPropertyValueProvider::IsPropertyInherited (int propertyId
)
291 #define PROP_CTI(p) G_STMT_START { \
292 if (propertyId == Control::p) return true; \
293 if (propertyId == TextBlock::p) return true; \
294 if (propertyId == Inline::p) return true; \
297 #define PROP_F(p) G_STMT_START { \
298 if (propertyId == FrameworkElement::p) return true; \
301 #define PROP_U(p) G_STMT_START { \
302 if (propertyId == UIElement::p) return true; \
305 #define PROP_I(p) G_STMT_START { \
306 if (propertyId == Inline::p) return true; \
309 PROP_CTI (ForegroundProperty
);
310 PROP_CTI (FontFamilyProperty
);
311 PROP_CTI (FontStretchProperty
);
312 PROP_CTI (FontStyleProperty
);
313 PROP_CTI (FontWeightProperty
);
314 PROP_CTI (FontSizeProperty
);
316 PROP_U (UseLayoutRoundingProperty
);
318 PROP_F (LanguageProperty
);
319 PROP_F (DataContextProperty
);
321 PROP_I (LanguageProperty
);
322 PROP_I (TextDecorationsProperty
);
328 InheritedPropertyValueProvider::MapPropertyToDescendant (Types
*types
,
329 DependencyProperty
*property
,
330 Type::Kind descendantKind
)
332 #define PROPAGATE_C_CT(p) G_STMT_START { \
333 if (property->GetId() == Control::p) { \
334 if (types->IsSubclassOf (descendantKind, Type::CONTROL)) \
335 return types->GetProperty (Control::p); \
336 else if (types->IsSubclassOf (descendantKind, Type::TEXTBLOCK)) \
337 return types->GetProperty (TextBlock::p); \
341 #define PROPAGATE_T_I(p) G_STMT_START { \
342 if (property->GetId() == TextBlock::p) { \
343 /* we don't need the check here since we can do it once above all the PROPAGATE_I below */ \
344 /*if (types->IsSubclassOf (descendantKind, Type::INLINE))*/ \
345 return types->GetProperty (Inline::p); \
349 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::CONTROL
)) {
350 PROPAGATE_C_CT (ForegroundProperty
);
351 PROPAGATE_C_CT (FontFamilyProperty
);
352 PROPAGATE_C_CT (FontStretchProperty
);
353 PROPAGATE_C_CT (FontStyleProperty
);
354 PROPAGATE_C_CT (FontWeightProperty
);
355 PROPAGATE_C_CT (FontSizeProperty
);
358 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::TEXTBLOCK
)) {
359 if (types
->IsSubclassOf (descendantKind
, Type::INLINE
)) {
360 PROPAGATE_T_I (ForegroundProperty
);
361 PROPAGATE_T_I (FontFamilyProperty
);
362 PROPAGATE_T_I (FontStretchProperty
);
363 PROPAGATE_T_I (FontStyleProperty
);
364 PROPAGATE_T_I (FontWeightProperty
);
365 PROPAGATE_T_I (FontSizeProperty
);
367 PROPAGATE_T_I (LanguageProperty
);
368 PROPAGATE_T_I (TextDecorationsProperty
);
372 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::FRAMEWORKELEMENT
)) {
373 if (types
->IsSubclassOf (descendantKind
, Type::FRAMEWORKELEMENT
)) {
374 if (property
->GetId() == FrameworkElement::LanguageProperty
375 || property
->GetId() == FrameworkElement::DataContextProperty
)
380 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::UIELEMENT
)) {
381 if (types
->IsSubclassOf (descendantKind
, Type::UIELEMENT
)) {
382 if (property
->GetId() == UIElement::UseLayoutRoundingProperty
)
391 InheritedPropertyValueProvider::PropagateInheritedProperty (DependencyObject
*obj
, DependencyProperty
*property
, Value
*old_value
, Value
*new_value
)
393 Types
*types
= obj
->GetDeployment ()->GetTypes ();
395 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::TEXTBLOCK
)) {
396 InlineCollection
*inlines
= ((TextBlock
*)obj
)->GetInlines();
398 // lift this out of the loop since we know all
399 // elements of InlinesProperty will be inlines.
400 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, Type::INLINE
);
404 for (int i
= 0; i
< inlines
->GetCount (); i
++) {
405 Inline
*item
= inlines
->GetValueAt (i
)->AsInline ();
409 item
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
410 old_value
, new_value
, false, false, &error
);
413 // FIXME: what do we do here? I'm guessing we continue propagating?
419 // for inherited properties, we need to walk down the
420 // subtree and call ProviderValueChanged on all
421 // elements that can inherit the property.
422 DeepTreeWalker
walker ((UIElement
*)obj
, types
);
424 walker
.Step (); // skip obj
426 while (UIElement
*element
= walker
.Step ()) {
427 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, element
->GetObjectType());
433 element
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
434 old_value
, new_value
, true, true, &error
);
437 // FIXME: what do we do here? I'm guessing we continue propagating?
440 walker
.SkipBranch ();
445 #define FOREGROUND_PROP (1<<0)
446 #define FONTFAMILY_PROP (1<<1)
447 #define FONTSTRETCH_PROP (1<<2)
448 #define FONTSTYLE_PROP (1<<3)
449 #define FONTWEIGHT_PROP (1<<4)
450 #define FONTSIZE_PROP (1<<5)
451 #define LANGUAGE_PROP (1<<6)
452 #define DATACONTEXT_PROP (1<<7)
453 #define LAYOUTROUNDING_PROP (1<<8)
455 #define HAS_SEEN(s,p) (((s) & (p))!=0)
456 #define SEEN(s,p) ((s)|=(p))
458 #define PROP_ADD(p,s) G_STMT_START { \
459 if (!HAS_SEEN (seen, s)) { \
460 DependencyProperty *property = types->GetProperty (p); \
461 Value *v = element->GetValue (property, PropertyPrecedence_Inherited, PropertyPrecedence_Inherited); \
463 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
465 true, true, &error); \
472 walk_tree (Types
*types
, UIElement
*element
, guint32 seen
)
476 if (types
->IsSubclassOf (element
->GetObjectType (), Type::CONTROL
)) {
477 PROP_ADD (Control::ForegroundProperty
, FOREGROUND_PROP
);
478 PROP_ADD (Control::FontFamilyProperty
, FONTFAMILY_PROP
);
479 PROP_ADD (Control::FontStretchProperty
, FONTSTRETCH_PROP
);
480 PROP_ADD (Control::FontStyleProperty
, FONTSTYLE_PROP
);
481 PROP_ADD (Control::FontWeightProperty
, FONTWEIGHT_PROP
);
482 PROP_ADD (Control::FontSizeProperty
, FONTSIZE_PROP
);
485 if (types
->IsSubclassOf (element
->GetObjectType (), Type::TEXTBLOCK
)) {
486 PROP_ADD (TextBlock::ForegroundProperty
, FOREGROUND_PROP
);
487 PROP_ADD (TextBlock::FontFamilyProperty
, FONTFAMILY_PROP
);
488 PROP_ADD (TextBlock::FontStretchProperty
, FONTSTRETCH_PROP
);
489 PROP_ADD (TextBlock::FontStyleProperty
, FONTSTYLE_PROP
);
490 PROP_ADD (TextBlock::FontWeightProperty
, FONTWEIGHT_PROP
);
491 PROP_ADD (TextBlock::FontSizeProperty
, FONTSIZE_PROP
);
494 if (types
->IsSubclassOf (element
->GetObjectType (), Type::FRAMEWORKELEMENT
)) {
495 PROP_ADD (FrameworkElement::LanguageProperty
, LANGUAGE_PROP
);
496 PROP_ADD (FrameworkElement::DataContextProperty
, DATACONTEXT_PROP
);
500 PROP_ADD (UIElement::UseLayoutRoundingProperty
, LAYOUTROUNDING_PROP
);
502 VisualTreeWalker
walker ((UIElement
*)element
, Logical
, types
);
504 while (UIElement
*child
= walker
.Step ())
505 walk_tree (types
, child
, seen
);
510 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement
*subtreeRoot
)
512 Types
*types
= subtreeRoot
->GetDeployment ()->GetTypes ();
514 walk_tree (types
, subtreeRoot
, 0);
518 // DefaultPropertyValueProvider
522 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
524 return property
->GetDefaultValue ();
529 // AutoPropertyValueProvider
533 dispose_value (gpointer key
, gpointer value
, gpointer data
)
535 DependencyObject
*obj
= (DependencyObject
*) data
;
536 Value
*v
= (Value
*) value
;
541 // detach from the existing value
542 if (v
->Is (Type::DEPENDENCY_OBJECT
)) {
543 DependencyObject
*dob
= v
->AsDependencyObject ();
546 if (obj
== dob
->GetParent ()) {
547 // unset its logical parent
548 dob
->SetParent (NULL
, NULL
);
551 // unregister from the existing value
552 dob
->RemovePropertyChangeListener (obj
, NULL
);
561 AutoCreatePropertyValueProvider::AutoCreatePropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
562 : PropertyValueProvider (obj
, precedence
)
564 auto_values
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
567 AutoCreatePropertyValueProvider::~AutoCreatePropertyValueProvider ()
569 g_hash_table_foreach_remove (auto_values
, dispose_value
, obj
);
570 g_hash_table_destroy (auto_values
);
574 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
578 if (!property
->IsAutoCreated ())
581 // return previously set auto value next
582 if ((value
= (Value
*) g_hash_table_lookup (auto_values
, property
)))
585 value
= (property
->GetAutoCreator()) (obj
, property
);
588 if (!value
->Is(property
->GetPropertyType()))
589 g_warning ("autocreated value for property '%s' (type=%s) is of incompatible type %s\n",
591 Type::Find (property
->GetPropertyType ())->GetName(),
592 Type::Find (value
->GetKind())->GetName());
595 g_hash_table_insert (auto_values
, property
, value
);
598 obj
->ProviderValueChanged (precedence
, property
, NULL
, value
, false, true, &error
);
604 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty
*property
)
606 return (Value
*) g_hash_table_lookup (auto_values
, property
);
610 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty
*property
)
612 g_hash_table_remove (auto_values
, property
);
616 AutoCreators::default_autocreator (DependencyObject
*instance
, DependencyProperty
*property
)
618 Type
*type
= Type::Find (property
->GetPropertyType ());
622 return Value::CreateUnrefPtr (type
->CreateInstance ());
625 #define XAML_FONT_SIZE 14.666666984558105
626 #define XAP_FONT_SIZE 11.0
629 AutoCreators::CreateDefaultFontSize (DependencyObject
*obj
, DependencyProperty
*property
)
631 Deployment
*deployment
;
633 if ((deployment
= Deployment::GetCurrent ()) && deployment
->IsLoadedFromXap ())
634 return new Value (XAP_FONT_SIZE
);
636 return new Value (XAML_FONT_SIZE
);