2009-10-20 Chris Toshok <toshok@ximian.com>
[moon.git] / src / provider.cpp
blobfe23bfeb2fc7b2ee67a3bfa3e6f12ed613ead86a
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(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 int err;
103 while (iter->Next () && (setterBase = iter->GetCurrent (&err))) {
104 if (err) {
105 // Something bad happened - what to do?
106 delete iter;
107 return;
110 if (!setterBase->Is (Type::SETTER))
111 continue;
113 Setter *setter = setterBase->AsSetter ();
114 if (!(value = setter->GetValue (Setter::PropertyProperty)))
115 continue;
117 if (!(property = value->AsDependencyProperty ()))
118 continue;
120 if (prop == property) {
121 // the hash holds a ref
122 setter->ref ();
123 g_hash_table_insert (style_hash, property, setter);
124 delete iter;
125 return;
129 delete iter;
133 void
134 StylePropertyValueProvider::SealStyle (Style *style)
136 style->Seal ();
138 SetterBaseCollection *setters = style->GetSetters ();
139 if (!setters)
140 return;
142 CollectionIterator *iter = setters->GetIterator ();
143 Value *setterBase;
144 int err;
146 while (iter->Next () && (setterBase = iter->GetCurrent (&err))) {
147 if (err) {
148 // Something bad happened - what to do?
149 delete iter;
150 return;
153 if (!setterBase->Is (Type::SETTER))
154 continue;
156 Setter *setter = setterBase->AsSetter ();
158 DependencyProperty *setter_property;
159 Value *value;
161 if (!(value = setter->GetValue (Setter::PropertyProperty)))
162 continue;
164 if (!(setter_property = value->AsDependencyProperty ()))
165 continue;
167 Value *setter_value;
168 if (!(setter_value = setter->GetValue (Setter::ConvertedValueProperty)))
169 continue;
171 // the hash holds a ref
172 setter->ref ();
173 g_hash_table_insert (style_hash, setter_property, setter);
175 MoonError error;
176 obj->ProviderValueChanged (precedence, setter_property, NULL, setter_value, true, true, &error);
179 delete iter;
184 // InheritedPropertyValueProvider
186 Value*
187 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
189 int propertyId = property->GetId ();
191 if (!IsPropertyInherited (propertyId))
192 return NULL;
194 int parentPropertyId = -1;
196 Types *types = Deployment::GetCurrent()->GetTypes();
198 #define INHERIT_CTI_CTI(p) \
199 G_STMT_START { \
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; \
209 } G_STMT_END
212 #define INHERIT_I_T(p) \
213 G_STMT_START { \
214 if (property->GetId () == Inline::p) { \
215 parentPropertyId = TextBlock::p; \
217 } G_STMT_END
219 #define INHERIT_F_F(p) \
220 G_STMT_START { \
221 if (property->GetId () == FrameworkElement::p) { \
222 parentPropertyId = FrameworkElement::p; \
224 } G_STMT_END
226 #define INHERIT_U_U(p) \
227 G_STMT_START { \
228 if (property->GetId () == UIElement::p) { \
229 parentPropertyId = UIElement::p; \
231 } G_STMT_END
233 DependencyObject *parent = NULL;
235 if (types->IsSubclassOf (obj->GetObjectType(), Type::FRAMEWORKELEMENT)) {
236 // we loop up the visual tree
237 parent = ((FrameworkElement*)obj)->GetVisualParent();
238 if (parent) {
239 while (parent) {
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)) {
260 // skip collections
261 DependencyObject *new_parent = obj->GetParent();
262 while (new_parent && !types->IsSubclassOf (new_parent->GetObjectType(), Type::TEXTBLOCK))
263 new_parent = new_parent->GetParent ();
264 parent = new_parent;
266 if (!parent)
267 return NULL;
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);
284 return NULL;
288 bool
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; \
295 } G_STMT_END
297 #define PROP_F(p) G_STMT_START { \
298 if (propertyId == FrameworkElement::p) return true; \
299 } G_STMT_END
301 #define PROP_U(p) G_STMT_START { \
302 if (propertyId == UIElement::p) return true; \
303 } G_STMT_END
305 #define PROP_I(p) G_STMT_START { \
306 if (propertyId == Inline::p) return true; \
307 } G_STMT_END
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);
324 return false;
327 DependencyProperty*
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); \
339 } G_STMT_END
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); \
347 } G_STMT_END
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)
376 return property;
380 if (types->IsSubclassOf (property->GetOwnerType(), Type::UIELEMENT)) {
381 if (types->IsSubclassOf (descendantKind, Type::UIELEMENT)) {
382 if (property->GetId() == UIElement::UseLayoutRoundingProperty)
383 return property;
387 return NULL;
390 void
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);
401 if (!child_property)
402 return;
404 for (int i = 0; i < inlines->GetCount (); i++) {
405 Inline *item = inlines->GetValueAt (i)->AsInline ();
407 MoonError error;
409 item->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
410 old_value, new_value, false, false, &error);
412 if (error.number) {
413 // FIXME: what do we do here? I'm guessing we continue propagating?
418 else {
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());
428 if (!child_property)
429 continue;
431 MoonError error;
433 element->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
434 old_value, new_value, true, true, &error);
436 if (error.number) {
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); \
462 if (v != NULL) { \
463 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
464 NULL, v, \
465 true, true, &error); \
466 SEEN (seen, s); \
469 } G_STMT_END
471 static void
472 walk_tree (Types *types, UIElement *element, guint32 seen)
474 MoonError error;
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);
509 void
510 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement *subtreeRoot)
512 Types *types = subtreeRoot->GetDeployment ()->GetTypes ();
514 walk_tree (types, subtreeRoot, 0);
518 // DefaultPropertyValueProvider
521 Value *
522 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
524 return property->GetDefaultValue ();
529 // AutoPropertyValueProvider
532 static gboolean
533 dispose_value (gpointer key, gpointer value, gpointer data)
535 DependencyObject *obj = (DependencyObject *) data;
536 Value *v = (Value *) value;
538 if (!value)
539 return true;
541 // detach from the existing value
542 if (v->Is (Type::DEPENDENCY_OBJECT)) {
543 DependencyObject *dob = v->AsDependencyObject ();
545 if (dob != NULL) {
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);
556 delete v;
558 return true;
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);
573 Value *
574 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
576 Value *value;
578 if (!property->IsAutoCreated ())
579 return NULL;
581 // return previously set auto value next
582 if ((value = (Value *) g_hash_table_lookup (auto_values, property)))
583 return value;
585 value = (property->GetAutoCreator()) (obj, property);
587 #if SANITY
588 if (!value->Is(property->GetPropertyType()))
589 g_warning ("autocreated value for property '%s' (type=%s) is of incompatible type %s\n",
590 property->GetName(),
591 Type::Find (property->GetPropertyType ())->GetName(),
592 Type::Find (value->GetKind())->GetName());
593 #endif
595 g_hash_table_insert (auto_values, property, value);
597 MoonError error;
598 obj->ProviderValueChanged (precedence, property, NULL, value, false, true, &error);
600 return value;
603 Value *
604 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty *property)
606 return (Value *) g_hash_table_lookup (auto_values, property);
609 void
610 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty *property)
612 g_hash_table_remove (auto_values, property);
615 Value*
616 AutoCreators::default_autocreator (DependencyObject *instance, DependencyProperty *property)
618 Type *type = Type::Find (property->GetPropertyType ());
619 if (!type)
620 return NULL;
622 return Value::CreateUnrefPtr (type->CreateInstance ());
625 #define XAML_FONT_SIZE 14.666666984558105
626 #define XAP_FONT_SIZE 11.0
628 Value *
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);