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(s
->GetDeployment (), 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 (&err
) && (setterBase
= iter
->GetCurrent (&err
))) {
104 if (!setterBase
->Is (obj
->GetDeployment (), Type::SETTER
))
107 Setter
*setter
= setterBase
->AsSetter ();
108 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
111 if (!(property
= value
->AsDependencyProperty ()))
114 if (prop
== property
) {
115 // the hash holds a ref
117 g_hash_table_insert (style_hash
, property
, setter
);
128 StylePropertyValueProvider::SealStyle (Style
*style
)
132 SetterBaseCollection
*setters
= style
->GetSetters ();
136 CollectionIterator
*iter
= setters
->GetIterator ();
140 while (iter
->Next (&err
) && (setterBase
= iter
->GetCurrent (&err
))) {
141 if (!setterBase
->Is (obj
->GetDeployment (), Type::SETTER
))
144 Setter
*setter
= setterBase
->AsSetter ();
146 DependencyProperty
*setter_property
;
149 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
152 if (!(setter_property
= value
->AsDependencyProperty ()))
156 if (!(setter_value
= setter
->GetValue (Setter::ConvertedValueProperty
)))
159 // the hash holds a ref
161 g_hash_table_insert (style_hash
, setter_property
, setter
);
164 obj
->ProviderValueChanged (precedence
, setter_property
, NULL
, setter_value
, true, true, &error
);
172 // InheritedPropertyValueProvider
175 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
177 int propertyId
= property
->GetId ();
179 if (!IsPropertyInherited (propertyId
))
182 int parentPropertyId
= -1;
184 Types
*types
= Deployment::GetCurrent()->GetTypes();
186 #define INHERIT_CTI_CTI(p) \
188 if (property->GetId () == Control::p || \
189 property->GetId () == TextBlock::p || \
190 property->GetId () == Inline::p) { \
192 if (types->IsSubclassOf (parent->GetObjectType(), Type::CONTROL)) \
193 parentPropertyId = Control::p; \
194 else if (types->IsSubclassOf (parent->GetObjectType(), Type::TEXTBLOCK)) \
195 parentPropertyId = TextBlock::p; \
200 #define INHERIT_I_T(p) \
202 if (property->GetId () == Inline::p) { \
203 parentPropertyId = TextBlock::p; \
207 #define INHERIT_F_F(p) \
209 if (property->GetId () == FrameworkElement::p) { \
210 parentPropertyId = FrameworkElement::p; \
214 #define INHERIT_U_U(p) \
216 if (property->GetId () == UIElement::p) { \
217 parentPropertyId = UIElement::p; \
221 DependencyObject
*parent
= NULL
;
223 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::FRAMEWORKELEMENT
)) {
224 // we loop up the visual tree
225 parent
= ((FrameworkElement
*)obj
)->GetVisualParent();
228 INHERIT_CTI_CTI (ForegroundProperty
);
229 INHERIT_CTI_CTI (FontFamilyProperty
);
230 INHERIT_CTI_CTI (FontStretchProperty
);
231 INHERIT_CTI_CTI (FontStyleProperty
);
232 INHERIT_CTI_CTI (FontWeightProperty
);
233 INHERIT_CTI_CTI (FontSizeProperty
);
235 INHERIT_F_F (LanguageProperty
);
236 INHERIT_F_F (DataContextProperty
);
238 INHERIT_U_U (UseLayoutRoundingProperty
);
240 if (parentPropertyId
!= -1)
241 return parent
->GetValue (types
->GetProperty (parentPropertyId
));
243 parent
= ((FrameworkElement
*)parent
)->GetVisualParent();
247 else if (types
->IsSubclassOf (obj
->GetObjectType(), Type::INLINE
)) {
249 DependencyObject
*new_parent
= obj
->GetParent();
250 while (new_parent
&& !types
->IsSubclassOf (new_parent
->GetObjectType(), Type::TEXTBLOCK
))
251 new_parent
= new_parent
->GetParent ();
257 INHERIT_I_T (ForegroundProperty
);
258 INHERIT_I_T (FontFamilyProperty
);
259 INHERIT_I_T (FontStretchProperty
);
260 INHERIT_I_T (FontStyleProperty
);
261 INHERIT_I_T (FontWeightProperty
);
262 INHERIT_I_T (FontSizeProperty
);
264 INHERIT_I_T (LanguageProperty
);
265 INHERIT_I_T (TextDecorationsProperty
);
267 if (parentPropertyId
!= -1) {
268 return parent
->GetValue (parentPropertyId
);
277 InheritedPropertyValueProvider::IsPropertyInherited (int propertyId
)
279 #define PROP_CTI(p) G_STMT_START { \
280 if (propertyId == Control::p) return true; \
281 if (propertyId == TextBlock::p) return true; \
282 if (propertyId == Inline::p) return true; \
285 #define PROP_F(p) G_STMT_START { \
286 if (propertyId == FrameworkElement::p) return true; \
289 #define PROP_U(p) G_STMT_START { \
290 if (propertyId == UIElement::p) return true; \
293 #define PROP_I(p) G_STMT_START { \
294 if (propertyId == Inline::p) return true; \
297 PROP_CTI (ForegroundProperty
);
298 PROP_CTI (FontFamilyProperty
);
299 PROP_CTI (FontStretchProperty
);
300 PROP_CTI (FontStyleProperty
);
301 PROP_CTI (FontWeightProperty
);
302 PROP_CTI (FontSizeProperty
);
304 PROP_U (UseLayoutRoundingProperty
);
306 PROP_F (LanguageProperty
);
307 PROP_F (DataContextProperty
);
309 PROP_I (LanguageProperty
);
310 PROP_I (TextDecorationsProperty
);
316 InheritedPropertyValueProvider::MapPropertyToDescendant (Types
*types
,
317 DependencyProperty
*property
,
318 Type::Kind descendantKind
)
320 #define PROPAGATE_C_CT(p) G_STMT_START { \
321 if (property->GetId() == Control::p) { \
322 if (types->IsSubclassOf (descendantKind, Type::CONTROL)) \
323 return types->GetProperty (Control::p); \
324 else if (types->IsSubclassOf (descendantKind, Type::TEXTBLOCK)) \
325 return types->GetProperty (TextBlock::p); \
329 #define PROPAGATE_T_I(p) G_STMT_START { \
330 if (property->GetId() == TextBlock::p) { \
331 /* we don't need the check here since we can do it once above all the PROPAGATE_I below */ \
332 /*if (types->IsSubclassOf (descendantKind, Type::INLINE))*/ \
333 return types->GetProperty (Inline::p); \
337 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::CONTROL
)) {
338 PROPAGATE_C_CT (ForegroundProperty
);
339 PROPAGATE_C_CT (FontFamilyProperty
);
340 PROPAGATE_C_CT (FontStretchProperty
);
341 PROPAGATE_C_CT (FontStyleProperty
);
342 PROPAGATE_C_CT (FontWeightProperty
);
343 PROPAGATE_C_CT (FontSizeProperty
);
346 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::TEXTBLOCK
)) {
347 if (types
->IsSubclassOf (descendantKind
, Type::INLINE
)) {
348 PROPAGATE_T_I (ForegroundProperty
);
349 PROPAGATE_T_I (FontFamilyProperty
);
350 PROPAGATE_T_I (FontStretchProperty
);
351 PROPAGATE_T_I (FontStyleProperty
);
352 PROPAGATE_T_I (FontWeightProperty
);
353 PROPAGATE_T_I (FontSizeProperty
);
355 PROPAGATE_T_I (LanguageProperty
);
356 PROPAGATE_T_I (TextDecorationsProperty
);
360 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::FRAMEWORKELEMENT
)) {
361 if (types
->IsSubclassOf (descendantKind
, Type::FRAMEWORKELEMENT
)) {
362 if (property
->GetId() == FrameworkElement::LanguageProperty
363 || property
->GetId() == FrameworkElement::DataContextProperty
)
368 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::UIELEMENT
)) {
369 if (types
->IsSubclassOf (descendantKind
, Type::UIELEMENT
)) {
370 if (property
->GetId() == UIElement::UseLayoutRoundingProperty
)
379 InheritedPropertyValueProvider::PropagateInheritedProperty (DependencyObject
*obj
, DependencyProperty
*property
, Value
*old_value
, Value
*new_value
)
381 Types
*types
= obj
->GetDeployment ()->GetTypes ();
383 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::TEXTBLOCK
)) {
384 InlineCollection
*inlines
= ((TextBlock
*)obj
)->GetInlines();
386 // lift this out of the loop since we know all
387 // elements of InlinesProperty will be inlines.
388 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, Type::INLINE
);
392 for (int i
= 0; i
< inlines
->GetCount (); i
++) {
393 Inline
*item
= inlines
->GetValueAt (i
)->AsInline ();
397 item
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
398 old_value
, new_value
, false, false, &error
);
401 // FIXME: what do we do here? I'm guessing we continue propagating?
407 // for inherited properties, we need to walk down the
408 // subtree and call ProviderValueChanged on all
409 // elements that can inherit the property.
410 DeepTreeWalker
walker ((UIElement
*)obj
, Logical
, types
);
412 walker
.Step (); // skip obj
414 while (UIElement
*element
= walker
.Step ()) {
415 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, element
->GetObjectType());
421 element
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
422 old_value
, new_value
, true, true, &error
);
425 // FIXME: what do we do here? I'm guessing we continue propagating?
428 walker
.SkipBranch ();
433 #define FOREGROUND_PROP (1<<0)
434 #define FONTFAMILY_PROP (1<<1)
435 #define FONTSTRETCH_PROP (1<<2)
436 #define FONTSTYLE_PROP (1<<3)
437 #define FONTWEIGHT_PROP (1<<4)
438 #define FONTSIZE_PROP (1<<5)
439 #define LANGUAGE_PROP (1<<6)
440 #define DATACONTEXT_PROP (1<<7)
441 #define LAYOUTROUNDING_PROP (1<<8)
443 #define HAS_SEEN(s,p) (((s) & (p))!=0)
444 #define SEEN(s,p) ((s)|=(p))
446 #define PROP_ADD(p,s) G_STMT_START { \
447 if (!HAS_SEEN (seen, s)) { \
448 DependencyProperty *property = types->GetProperty (p); \
449 Value *v = element->GetValue (property, PropertyPrecedence_Inherited, PropertyPrecedence_Inherited); \
451 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
453 true, true, &error); \
460 walk_tree (Types
*types
, UIElement
*element
, guint32 seen
)
464 if (types
->IsSubclassOf (element
->GetObjectType (), Type::CONTROL
)) {
465 PROP_ADD (Control::ForegroundProperty
, FOREGROUND_PROP
);
466 PROP_ADD (Control::FontFamilyProperty
, FONTFAMILY_PROP
);
467 PROP_ADD (Control::FontStretchProperty
, FONTSTRETCH_PROP
);
468 PROP_ADD (Control::FontStyleProperty
, FONTSTYLE_PROP
);
469 PROP_ADD (Control::FontWeightProperty
, FONTWEIGHT_PROP
);
470 PROP_ADD (Control::FontSizeProperty
, FONTSIZE_PROP
);
473 if (types
->IsSubclassOf (element
->GetObjectType (), Type::TEXTBLOCK
)) {
474 PROP_ADD (TextBlock::ForegroundProperty
, FOREGROUND_PROP
);
475 PROP_ADD (TextBlock::FontFamilyProperty
, FONTFAMILY_PROP
);
476 PROP_ADD (TextBlock::FontStretchProperty
, FONTSTRETCH_PROP
);
477 PROP_ADD (TextBlock::FontStyleProperty
, FONTSTYLE_PROP
);
478 PROP_ADD (TextBlock::FontWeightProperty
, FONTWEIGHT_PROP
);
479 PROP_ADD (TextBlock::FontSizeProperty
, FONTSIZE_PROP
);
482 if (types
->IsSubclassOf (element
->GetObjectType (), Type::FRAMEWORKELEMENT
)) {
483 PROP_ADD (FrameworkElement::LanguageProperty
, LANGUAGE_PROP
);
484 PROP_ADD (FrameworkElement::DataContextProperty
, DATACONTEXT_PROP
);
488 PROP_ADD (UIElement::UseLayoutRoundingProperty
, LAYOUTROUNDING_PROP
);
490 VisualTreeWalker
walker ((UIElement
*)element
, Logical
, types
);
492 while (UIElement
*child
= walker
.Step ())
493 walk_tree (types
, child
, seen
);
498 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement
*subtreeRoot
)
500 Types
*types
= subtreeRoot
->GetDeployment ()->GetTypes ();
502 walk_tree (types
, subtreeRoot
, 0);
506 // DefaultPropertyValueProvider
510 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
512 return property
->GetDefaultValue ();
517 // AutoPropertyValueProvider
521 dispose_value (gpointer key
, gpointer value
, gpointer data
)
523 DependencyObject
*obj
= (DependencyObject
*) data
;
524 Value
*v
= (Value
*) value
;
529 // detach from the existing value
530 if (v
->Is (obj
->GetDeployment (), Type::DEPENDENCY_OBJECT
)) {
531 DependencyObject
*dob
= v
->AsDependencyObject ();
534 if (obj
== dob
->GetParent ()) {
535 // unset its logical parent
536 dob
->SetParent (NULL
, NULL
);
539 // unregister from the existing value
540 dob
->RemovePropertyChangeListener (obj
, NULL
);
549 AutoCreatePropertyValueProvider::AutoCreatePropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
550 : PropertyValueProvider (obj
, precedence
)
552 auto_values
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
555 AutoCreatePropertyValueProvider::~AutoCreatePropertyValueProvider ()
557 g_hash_table_foreach_remove (auto_values
, dispose_value
, obj
);
558 g_hash_table_destroy (auto_values
);
562 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
566 if (!property
->IsAutoCreated ())
569 // return previously set auto value next
570 if ((value
= (Value
*) g_hash_table_lookup (auto_values
, property
)))
573 value
= (property
->GetAutoCreator()) (obj
, property
);
576 Deployment
*deployment
= Deployment::GetCurrent ();
577 if (!value
->Is(deployment
, property
->GetPropertyType()))
578 g_warning ("autocreated value for property '%s' (type=%s) is of incompatible type %s\n",
580 Type::Find (deployment
, property
->GetPropertyType ())->GetName(),
581 Type::Find (deployment
, value
->GetKind())->GetName());
584 g_hash_table_insert (auto_values
, property
, value
);
587 obj
->ProviderValueChanged (precedence
, property
, NULL
, value
, false, true, &error
);
593 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty
*property
)
595 return (Value
*) g_hash_table_lookup (auto_values
, property
);
599 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty
*property
)
601 g_hash_table_remove (auto_values
, property
);
605 AutoCreators::default_autocreator (DependencyObject
*instance
, DependencyProperty
*property
)
607 Type
*type
= Type::Find (instance
->GetDeployment (), property
->GetPropertyType ());
611 return Value::CreateUnrefPtr (type
->CreateInstance ());
614 #define XAML_FONT_SIZE 14.666666984558105
615 #define XAP_FONT_SIZE 11.0
618 AutoCreators::CreateDefaultFontSize (DependencyObject
*obj
, DependencyProperty
*property
)
620 Deployment
*deployment
;
622 if ((deployment
= Deployment::GetCurrent ()) && deployment
->IsLoadedFromXap ())
623 return new Value (XAP_FONT_SIZE
);
625 return new Value (XAML_FONT_SIZE
);