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"
22 // LocalPropertyValueProvider
25 LocalPropertyValueProvider::LocalPropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
26 : PropertyValueProvider (obj
, precedence
)
28 // XXX maybe move the "DependencyObject::current_values" hash table here?
31 LocalPropertyValueProvider::~LocalPropertyValueProvider ()
36 LocalPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
38 return (Value
*) g_hash_table_lookup (obj
->GetLocalValues (), property
);
43 // StylePropertyValueProvider
46 StylePropertyValueProvider::StylePropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
47 : PropertyValueProvider (obj
, precedence
)
49 style_hash
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
51 (GDestroyNotify
)event_object_unref
);
55 StylePropertyValueProvider::unlink_converted_value (gpointer key
, gpointer value
, gpointer data
)
57 StylePropertyValueProvider
*provider
= (StylePropertyValueProvider
*)data
;
58 Setter
*s
= (Setter
*)value
;
60 Value
*v
= s
->GetValue(Setter::ConvertedValueProperty
);
61 if (v
->Is(Type::DEPENDENCY_OBJECT
)) {
62 DependencyObject
*dob
= v
->AsDependencyObject();
63 if (dob
->GetParent() == provider
->obj
)
64 dob
->SetParent(NULL
, NULL
);
68 StylePropertyValueProvider::~StylePropertyValueProvider ()
70 g_hash_table_foreach (style_hash
, StylePropertyValueProvider::unlink_converted_value
, this);
71 g_hash_table_destroy (style_hash
);
75 StylePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
77 Setter
*setter
= (Setter
*)g_hash_table_lookup (style_hash
, property
);
82 return setter
->GetValue (Setter::ConvertedValueProperty
);
86 StylePropertyValueProvider::RecomputePropertyValue (DependencyProperty
*prop
)
88 Style
*style
= ((FrameworkElement
*)obj
)->GetStyle();
92 DependencyProperty
*property
= NULL
;
94 SetterBaseCollection
*setters
= style
->GetSetters ();
98 CollectionIterator
*iter
= setters
->GetIterator ();
102 while (iter
->Next () && (setterBase
= iter
->GetCurrent (&err
))) {
104 // Something bad happened - what to do?
109 if (!setterBase
->Is (Type::SETTER
))
112 Setter
*setter
= setterBase
->AsSetter ();
113 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
116 if (!(property
= value
->AsDependencyProperty ()))
119 if (prop
== property
) {
120 // the hash holds a ref
122 g_hash_table_insert (style_hash
, property
, setter
);
133 StylePropertyValueProvider::SealStyle (Style
*style
)
137 SetterBaseCollection
*setters
= style
->GetSetters ();
141 CollectionIterator
*iter
= setters
->GetIterator ();
145 while (iter
->Next () && (setterBase
= iter
->GetCurrent (&err
))) {
147 // Something bad happened - what to do?
152 if (!setterBase
->Is (Type::SETTER
))
155 Setter
*setter
= setterBase
->AsSetter ();
157 DependencyProperty
*setter_property
;
160 if (!(value
= setter
->GetValue (Setter::PropertyProperty
)))
163 if (!(setter_property
= value
->AsDependencyProperty ()))
167 if (!(setter_value
= setter
->GetValue (Setter::ConvertedValueProperty
)))
170 // the hash holds a ref
172 g_hash_table_insert (style_hash
, setter_property
, setter
);
175 obj
->ProviderValueChanged (precedence
, setter_property
, NULL
, setter_value
, true, true, &error
);
183 // InheritedPropertyValueProvider
186 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
188 int propertyId
= property
->GetId ();
190 if (!IsPropertyInherited (propertyId
))
193 int parentPropertyId
= -1;
195 Types
*types
= Deployment::GetCurrent()->GetTypes();
197 #define INHERIT_CTI_CTI(p) \
199 if (property->GetId () == Control::p || \
200 property->GetId () == TextBlock::p || \
201 property->GetId () == Inline::p) { \
203 if (types->IsSubclassOf (parent->GetObjectType(), Type::CONTROL)) \
204 parentPropertyId = Control::p; \
205 else if (types->IsSubclassOf (parent->GetObjectType(), Type::TEXTBLOCK)) \
206 parentPropertyId = TextBlock::p; \
211 #define INHERIT_I_T(p) \
213 if (property->GetId () == Inline::p) { \
214 parentPropertyId = TextBlock::p; \
218 #define INHERIT_F_F(p) \
220 if (property->GetId () == FrameworkElement::p) { \
221 parentPropertyId = FrameworkElement::p; \
225 #define INHERIT_U_U(p) \
227 if (property->GetId () == UIElement::p) { \
228 parentPropertyId = UIElement::p; \
232 DependencyObject
*parent
= NULL
;
234 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::FRAMEWORKELEMENT
)) {
235 // we loop up the visual tree
236 parent
= ((FrameworkElement
*)obj
)->GetVisualParent();
239 INHERIT_CTI_CTI (ForegroundProperty
);
240 INHERIT_CTI_CTI (FontFamilyProperty
);
241 INHERIT_CTI_CTI (FontStretchProperty
);
242 INHERIT_CTI_CTI (FontStyleProperty
);
243 INHERIT_CTI_CTI (FontWeightProperty
);
244 INHERIT_CTI_CTI (FontSizeProperty
);
246 INHERIT_F_F (LanguageProperty
);
247 INHERIT_F_F (DataContextProperty
);
249 INHERIT_U_U (UseLayoutRoundingProperty
);
251 if (parentPropertyId
!= -1)
252 return parent
->GetValue (types
->GetProperty (parentPropertyId
));
254 parent
= ((FrameworkElement
*)parent
)->GetVisualParent();
258 else if (types
->IsSubclassOf (obj
->GetObjectType(), Type::INLINE
)) {
260 DependencyObject
*new_parent
= obj
->GetParent();
261 while (new_parent
&& !types
->IsSubclassOf (new_parent
->GetObjectType(), Type::TEXTBLOCK
))
262 new_parent
= new_parent
->GetParent ();
268 INHERIT_I_T (ForegroundProperty
);
269 INHERIT_I_T (FontFamilyProperty
);
270 INHERIT_I_T (FontStretchProperty
);
271 INHERIT_I_T (FontStyleProperty
);
272 INHERIT_I_T (FontWeightProperty
);
273 INHERIT_I_T (FontSizeProperty
);
275 INHERIT_I_T (LanguageProperty
);
276 INHERIT_I_T (TextDecorationsProperty
);
278 if (parentPropertyId
!= -1) {
279 return parent
->GetValue (parentPropertyId
);
288 InheritedPropertyValueProvider::IsPropertyInherited (int propertyId
)
290 #define PROP_CTI(p) G_STMT_START { \
291 if (propertyId == Control::p) return true; \
292 if (propertyId == TextBlock::p) return true; \
293 if (propertyId == Inline::p) return true; \
296 #define PROP_F(p) G_STMT_START { \
297 if (propertyId == FrameworkElement::p) return true; \
300 #define PROP_U(p) G_STMT_START { \
301 if (propertyId == UIElement::p) return true; \
304 #define PROP_I(p) G_STMT_START { \
305 if (propertyId == Inline::p) return true; \
308 PROP_CTI (ForegroundProperty
);
309 PROP_CTI (FontFamilyProperty
);
310 PROP_CTI (FontStretchProperty
);
311 PROP_CTI (FontStyleProperty
);
312 PROP_CTI (FontWeightProperty
);
313 PROP_CTI (FontSizeProperty
);
315 PROP_U (UseLayoutRoundingProperty
);
317 PROP_F (LanguageProperty
);
318 PROP_F (DataContextProperty
);
320 PROP_I (LanguageProperty
);
321 PROP_I (TextDecorationsProperty
);
327 InheritedPropertyValueProvider::MapPropertyToDescendant (Types
*types
,
328 DependencyProperty
*property
,
329 Type::Kind descendantKind
)
331 #define PROPAGATE_C_CT(p) G_STMT_START { \
332 if (property->GetId() == Control::p) { \
333 if (types->IsSubclassOf (descendantKind, Type::CONTROL)) \
334 return types->GetProperty (Control::p); \
335 else if (types->IsSubclassOf (descendantKind, Type::TEXTBLOCK)) \
336 return types->GetProperty (TextBlock::p); \
340 #define PROPAGATE_T_I(p) G_STMT_START { \
341 if (property->GetId() == TextBlock::p) { \
342 /* we don't need the check here since we can do it once above all the PROPAGATE_I below */ \
343 /*if (types->IsSubclassOf (descendantKind, Type::INLINE))*/ \
344 return types->GetProperty (Inline::p); \
348 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::CONTROL
)) {
349 PROPAGATE_C_CT (ForegroundProperty
);
350 PROPAGATE_C_CT (FontFamilyProperty
);
351 PROPAGATE_C_CT (FontStretchProperty
);
352 PROPAGATE_C_CT (FontStyleProperty
);
353 PROPAGATE_C_CT (FontWeightProperty
);
354 PROPAGATE_C_CT (FontSizeProperty
);
357 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::TEXTBLOCK
)) {
358 if (types
->IsSubclassOf (descendantKind
, Type::INLINE
)) {
359 PROPAGATE_T_I (ForegroundProperty
);
360 PROPAGATE_T_I (FontFamilyProperty
);
361 PROPAGATE_T_I (FontStretchProperty
);
362 PROPAGATE_T_I (FontStyleProperty
);
363 PROPAGATE_T_I (FontWeightProperty
);
364 PROPAGATE_T_I (FontSizeProperty
);
366 PROPAGATE_T_I (LanguageProperty
);
367 PROPAGATE_T_I (TextDecorationsProperty
);
371 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::FRAMEWORKELEMENT
)) {
372 if (types
->IsSubclassOf (descendantKind
, Type::FRAMEWORKELEMENT
)) {
373 if (property
->GetId() == FrameworkElement::LanguageProperty
374 || property
->GetId() == FrameworkElement::DataContextProperty
)
379 if (types
->IsSubclassOf (property
->GetOwnerType(), Type::UIELEMENT
)) {
380 if (types
->IsSubclassOf (descendantKind
, Type::UIELEMENT
)) {
381 if (property
->GetId() == UIElement::UseLayoutRoundingProperty
)
390 InheritedPropertyValueProvider::PropagateInheritedProperty (DependencyObject
*obj
, DependencyProperty
*property
, Value
*old_value
, Value
*new_value
)
392 Types
*types
= obj
->GetDeployment ()->GetTypes ();
394 if (types
->IsSubclassOf (obj
->GetObjectType(), Type::TEXTBLOCK
)) {
395 InlineCollection
*inlines
= ((TextBlock
*)obj
)->GetInlines();
397 // lift this out of the loop since we know all
398 // elements of InlinesProperty will be inlines.
399 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, Type::INLINE
);
403 for (int i
= 0; i
< inlines
->GetCount (); i
++) {
404 Inline
*item
= inlines
->GetValueAt (i
)->AsInline ();
408 item
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
409 old_value
, new_value
, false, false, &error
);
412 // FIXME: what do we do here? I'm guessing we continue propagating?
418 // for inherited properties, we need to walk down the
419 // subtree and call ProviderValueChanged on all
420 // elements that can inherit the property.
421 DeepTreeWalker
walker ((UIElement
*)obj
, types
);
423 walker
.Step (); // skip obj
425 while (UIElement
*element
= walker
.Step ()) {
426 DependencyProperty
*child_property
= MapPropertyToDescendant (types
, property
, element
->GetObjectType());
432 element
->ProviderValueChanged (PropertyPrecedence_Inherited
, child_property
,
433 old_value
, new_value
, true, true, &error
);
436 // FIXME: what do we do here? I'm guessing we continue propagating?
439 walker
.SkipBranch ();
444 #define FOREGROUND_PROP (1<<0)
445 #define FONTFAMILY_PROP (1<<1)
446 #define FONTSTRETCH_PROP (1<<2)
447 #define FONTSTYLE_PROP (1<<3)
448 #define FONTWEIGHT_PROP (1<<4)
449 #define FONTSIZE_PROP (1<<5)
450 #define LANGUAGE_PROP (1<<6)
451 #define DATACONTEXT_PROP (1<<7)
452 #define LAYOUTROUNDING_PROP (1<<8)
454 #define HAS_SEEN(s,p) (((s) & (p))!=0)
455 #define SEEN(s,p) ((s)|=(p))
457 #define PROP_ADD(p,s) G_STMT_START { \
458 if (!HAS_SEEN (seen, s)) { \
459 DependencyProperty *property = types->GetProperty (p); \
460 Value *v = element->GetValue (property, PropertyPrecedence_Inherited, PropertyPrecedence_Inherited); \
462 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
464 true, true, &error); \
471 walk_tree (Types
*types
, UIElement
*element
, guint32 seen
)
475 if (types
->IsSubclassOf (element
->GetObjectType (), Type::CONTROL
)) {
476 PROP_ADD (Control::ForegroundProperty
, FOREGROUND_PROP
);
477 PROP_ADD (Control::FontFamilyProperty
, FONTFAMILY_PROP
);
478 PROP_ADD (Control::FontStretchProperty
, FONTSTRETCH_PROP
);
479 PROP_ADD (Control::FontStyleProperty
, FONTSTYLE_PROP
);
480 PROP_ADD (Control::FontWeightProperty
, FONTWEIGHT_PROP
);
481 PROP_ADD (Control::FontSizeProperty
, FONTSIZE_PROP
);
484 if (types
->IsSubclassOf (element
->GetObjectType (), Type::TEXTBLOCK
)) {
485 PROP_ADD (TextBlock::ForegroundProperty
, FOREGROUND_PROP
);
486 PROP_ADD (TextBlock::FontFamilyProperty
, FONTFAMILY_PROP
);
487 PROP_ADD (TextBlock::FontStretchProperty
, FONTSTRETCH_PROP
);
488 PROP_ADD (TextBlock::FontStyleProperty
, FONTSTYLE_PROP
);
489 PROP_ADD (TextBlock::FontWeightProperty
, FONTWEIGHT_PROP
);
490 PROP_ADD (TextBlock::FontSizeProperty
, FONTSIZE_PROP
);
493 if (types
->IsSubclassOf (element
->GetObjectType (), Type::FRAMEWORKELEMENT
)) {
494 PROP_ADD (FrameworkElement::LanguageProperty
, LANGUAGE_PROP
);
495 PROP_ADD (FrameworkElement::DataContextProperty
, DATACONTEXT_PROP
);
499 PROP_ADD (UIElement::UseLayoutRoundingProperty
, LAYOUTROUNDING_PROP
);
501 VisualTreeWalker
walker ((UIElement
*)element
, Logical
, types
);
503 while (UIElement
*child
= walker
.Step ())
504 walk_tree (types
, child
, seen
);
509 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement
*subtreeRoot
)
511 Types
*types
= subtreeRoot
->GetDeployment ()->GetTypes ();
513 walk_tree (types
, subtreeRoot
, 0);
517 // DefaultPropertyValueProvider
521 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
523 return property
->GetDefaultValue ();
528 // AutoPropertyValueProvider
532 dispose_value (gpointer key
, gpointer value
, gpointer data
)
534 DependencyObject
*obj
= (DependencyObject
*) data
;
535 Value
*v
= (Value
*) value
;
540 // detach from the existing value
541 if (v
->Is (Type::DEPENDENCY_OBJECT
)) {
542 DependencyObject
*dob
= v
->AsDependencyObject ();
545 if (obj
== dob
->GetParent ()) {
546 // unset its logical parent
547 dob
->SetParent (NULL
, NULL
);
550 // unregister from the existing value
551 dob
->RemovePropertyChangeListener (obj
, NULL
);
560 AutoCreatePropertyValueProvider::AutoCreatePropertyValueProvider (DependencyObject
*obj
, PropertyPrecedence precedence
)
561 : PropertyValueProvider (obj
, precedence
)
563 auto_values
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
566 AutoCreatePropertyValueProvider::~AutoCreatePropertyValueProvider ()
568 g_hash_table_foreach_remove (auto_values
, dispose_value
, obj
);
569 g_hash_table_destroy (auto_values
);
573 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
577 if (!property
->IsAutoCreated ())
580 // return previously set auto value next
581 if ((value
= (Value
*) g_hash_table_lookup (auto_values
, property
)))
584 value
= (property
->GetAutoCreator()) (obj
, property
);
587 if (!value
->Is(property
->GetPropertyType()))
588 g_warning ("autocreated value for property '%s' (type=%s) is of incompatible type %s\n",
590 Type::Find (property
->GetPropertyType ())->GetName(),
591 Type::Find (value
->GetKind())->GetName());
594 g_hash_table_insert (auto_values
, property
, value
);
597 obj
->ProviderValueChanged (precedence
, property
, NULL
, value
, false, true, &error
);
603 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty
*property
)
605 return (Value
*) g_hash_table_lookup (auto_values
, property
);
609 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty
*property
)
611 g_hash_table_remove (auto_values
, property
);
615 AutoCreators::default_autocreator (DependencyObject
*instance
, DependencyProperty
*property
)
617 Type
*type
= Type::Find (property
->GetPropertyType ());
621 return Value::CreateUnrefPtr (type
->CreateInstance ());
624 #define XAML_FONT_SIZE 14.666666984558105
625 #define XAP_FONT_SIZE 11.0
628 AutoCreators::CreateDefaultFontSize (DependencyObject
*obj
, DependencyProperty
*property
)
630 Deployment
*deployment
;
632 if ((deployment
= Deployment::GetCurrent ()) && deployment
->IsLoadedFromXap ())
633 return new Value (XAP_FONT_SIZE
);
635 return new Value (XAML_FONT_SIZE
);