2009-12-03 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / provider.cpp
blob31ca6c575e53331544c00ee9210e443596ef546b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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.
8 *
9 */
11 #include <config.h>
13 #include "cbinding.h"
14 #include "runtime.h"
15 #include "provider.h"
16 #include "control.h"
17 #include "frameworkelement.h"
18 #include "textblock.h"
19 #include "style.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 ()
36 Value *
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,
51 (GDestroyNotify)NULL,
52 (GDestroyNotify)event_object_unref);
55 void
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);
75 Value*
76 StylePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
78 Setter *setter = (Setter*)g_hash_table_lookup (style_hash, property);
80 if (!setter)
81 return NULL;
82 else
83 return setter->GetValue (Setter::ConvertedValueProperty);
86 void
87 StylePropertyValueProvider::RecomputePropertyValue (DependencyProperty *prop)
89 Style *style = ((FrameworkElement*)obj)->GetStyle();
90 if (!style)
91 return;
93 DependencyProperty *property = NULL;
94 Value *value = NULL;
95 SetterBaseCollection *setters = style->GetSetters ();
96 if (!setters)
97 return;
99 CollectionIterator *iter = setters->GetIterator ();
100 Value *setterBase;
101 MoonError err;
103 while (iter->Next (&err) && (setterBase = iter->GetCurrent (&err))) {
104 if (!setterBase->Is (obj->GetDeployment (), Type::SETTER))
105 continue;
107 Setter *setter = setterBase->AsSetter ();
108 if (!(value = setter->GetValue (Setter::PropertyProperty)))
109 continue;
111 if (!(property = value->AsDependencyProperty ()))
112 continue;
114 if (prop == property) {
115 // the hash holds a ref
116 setter->ref ();
117 g_hash_table_insert (style_hash, property, setter);
118 delete iter;
119 return;
123 delete iter;
127 void
128 StylePropertyValueProvider::SealStyle (Style *style)
130 style->Seal ();
132 SetterBaseCollection *setters = style->GetSetters ();
133 if (!setters)
134 return;
136 CollectionIterator *iter = setters->GetIterator ();
137 Value *setterBase;
138 MoonError err;
140 while (iter->Next (&err) && (setterBase = iter->GetCurrent (&err))) {
141 if (!setterBase->Is (obj->GetDeployment (), Type::SETTER))
142 continue;
144 Setter *setter = setterBase->AsSetter ();
146 DependencyProperty *setter_property;
147 Value *value;
149 if (!(value = setter->GetValue (Setter::PropertyProperty)))
150 continue;
152 if (!(setter_property = value->AsDependencyProperty ()))
153 continue;
155 Value *setter_value;
156 if (!(setter_value = setter->GetValue (Setter::ConvertedValueProperty)))
157 continue;
159 // the hash holds a ref
160 setter->ref ();
161 g_hash_table_insert (style_hash, setter_property, setter);
163 MoonError error;
164 obj->ProviderValueChanged (precedence, setter_property, NULL, setter_value, true, true, &error);
167 delete iter;
172 // InheritedPropertyValueProvider
174 Value*
175 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
177 int propertyId = property->GetId ();
179 if (!IsPropertyInherited (propertyId))
180 return NULL;
182 int parentPropertyId = -1;
184 Types *types = Deployment::GetCurrent()->GetTypes();
186 #define INHERIT_CTI_CTI(p) \
187 G_STMT_START { \
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; \
197 } G_STMT_END
200 #define INHERIT_I_T(p) \
201 G_STMT_START { \
202 if (property->GetId () == Inline::p) { \
203 parentPropertyId = TextBlock::p; \
205 } G_STMT_END
207 #define INHERIT_F_F(p) \
208 G_STMT_START { \
209 if (property->GetId () == FrameworkElement::p) { \
210 parentPropertyId = FrameworkElement::p; \
212 } G_STMT_END
214 #define INHERIT_U_U(p) \
215 G_STMT_START { \
216 if (property->GetId () == UIElement::p) { \
217 parentPropertyId = UIElement::p; \
219 } G_STMT_END
221 DependencyObject *parent = NULL;
223 if (types->IsSubclassOf (obj->GetObjectType(), Type::FRAMEWORKELEMENT)) {
224 // we loop up the visual tree
225 parent = ((FrameworkElement*)obj)->GetVisualParent();
226 if (parent) {
227 while (parent) {
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)) {
248 // skip collections
249 DependencyObject *new_parent = obj->GetParent();
250 while (new_parent && !types->IsSubclassOf (new_parent->GetObjectType(), Type::TEXTBLOCK))
251 new_parent = new_parent->GetParent ();
252 parent = new_parent;
254 if (!parent)
255 return NULL;
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);
272 return NULL;
276 bool
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; \
283 } G_STMT_END
285 #define PROP_F(p) G_STMT_START { \
286 if (propertyId == FrameworkElement::p) return true; \
287 } G_STMT_END
289 #define PROP_U(p) G_STMT_START { \
290 if (propertyId == UIElement::p) return true; \
291 } G_STMT_END
293 #define PROP_I(p) G_STMT_START { \
294 if (propertyId == Inline::p) return true; \
295 } G_STMT_END
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);
312 return false;
315 DependencyProperty*
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); \
327 } G_STMT_END
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); \
335 } G_STMT_END
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)
364 return property;
368 if (types->IsSubclassOf (property->GetOwnerType(), Type::UIELEMENT)) {
369 if (types->IsSubclassOf (descendantKind, Type::UIELEMENT)) {
370 if (property->GetId() == UIElement::UseLayoutRoundingProperty)
371 return property;
375 return NULL;
378 void
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);
389 if (!child_property)
390 return;
392 for (int i = 0; i < inlines->GetCount (); i++) {
393 Inline *item = inlines->GetValueAt (i)->AsInline ();
395 MoonError error;
397 item->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
398 old_value, new_value, false, false, &error);
400 if (error.number) {
401 // FIXME: what do we do here? I'm guessing we continue propagating?
406 else {
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());
416 if (!child_property)
417 continue;
419 MoonError error;
421 element->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
422 old_value, new_value, true, true, &error);
424 if (error.number) {
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); \
450 if (v != NULL) { \
451 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
452 NULL, v, \
453 true, true, &error); \
454 SEEN (seen, s); \
457 } G_STMT_END
459 static void
460 walk_tree (Types *types, UIElement *element, guint32 seen)
462 MoonError error;
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);
497 void
498 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement *subtreeRoot)
500 Types *types = subtreeRoot->GetDeployment ()->GetTypes ();
502 walk_tree (types, subtreeRoot, 0);
506 // DefaultPropertyValueProvider
509 Value *
510 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
512 return property->GetDefaultValue ();
517 // AutoPropertyValueProvider
520 static gboolean
521 dispose_value (gpointer key, gpointer value, gpointer data)
523 DependencyObject *obj = (DependencyObject *) data;
524 Value *v = (Value *) value;
526 if (!value)
527 return true;
529 // detach from the existing value
530 if (v->Is (obj->GetDeployment (), Type::DEPENDENCY_OBJECT)) {
531 DependencyObject *dob = v->AsDependencyObject ();
533 if (dob != NULL) {
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);
544 delete v;
546 return true;
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);
561 Value *
562 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
564 Value *value;
566 if (!property->IsAutoCreated ())
567 return NULL;
569 // return previously set auto value next
570 if ((value = (Value *) g_hash_table_lookup (auto_values, property)))
571 return value;
573 value = (property->GetAutoCreator()) (obj, property);
575 #if SANITY
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",
579 property->GetName(),
580 Type::Find (deployment, property->GetPropertyType ())->GetName(),
581 Type::Find (deployment, value->GetKind())->GetName());
582 #endif
584 g_hash_table_insert (auto_values, property, value);
586 MoonError error;
587 obj->ProviderValueChanged (precedence, property, NULL, value, false, true, &error);
589 return value;
592 Value *
593 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty *property)
595 return (Value *) g_hash_table_lookup (auto_values, property);
598 void
599 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty *property)
601 g_hash_table_remove (auto_values, property);
604 Value*
605 AutoCreators::default_autocreator (DependencyObject *instance, DependencyProperty *property)
607 Type *type = Type::Find (instance->GetDeployment (), property->GetPropertyType ());
608 if (!type)
609 return NULL;
611 return Value::CreateUnrefPtr (type->CreateInstance ());
614 #define XAML_FONT_SIZE 14.666666984558105
615 #define XAP_FONT_SIZE 11.0
617 Value *
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);