2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / provider.cpp
blobcdbd3c6b88dcc0a20b67b93c41d54e694a9fbe6a
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"
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 ()
35 Value *
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,
50 (GDestroyNotify)NULL,
51 (GDestroyNotify)event_object_unref);
54 void
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);
74 Value*
75 StylePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
77 Setter *setter = (Setter*)g_hash_table_lookup (style_hash, property);
79 if (!setter)
80 return NULL;
81 else
82 return setter->GetValue (Setter::ConvertedValueProperty);
85 void
86 StylePropertyValueProvider::RecomputePropertyValue (DependencyProperty *prop)
88 Style *style = ((FrameworkElement*)obj)->GetStyle();
89 if (!style)
90 return;
92 DependencyProperty *property = NULL;
93 Value *value = NULL;
94 SetterBaseCollection *setters = style->GetSetters ();
95 if (!setters)
96 return;
98 CollectionIterator *iter = setters->GetIterator ();
99 Value *setterBase;
100 int err;
102 while (iter->Next () && (setterBase = iter->GetCurrent (&err))) {
103 if (err) {
104 // Something bad happened - what to do?
105 delete iter;
106 return;
109 if (!setterBase->Is (Type::SETTER))
110 continue;
112 Setter *setter = setterBase->AsSetter ();
113 if (!(value = setter->GetValue (Setter::PropertyProperty)))
114 continue;
116 if (!(property = value->AsDependencyProperty ()))
117 continue;
119 if (prop == property) {
120 // the hash holds a ref
121 setter->ref ();
122 g_hash_table_insert (style_hash, property, setter);
123 delete iter;
124 return;
128 delete iter;
132 void
133 StylePropertyValueProvider::SealStyle (Style *style)
135 style->Seal ();
137 SetterBaseCollection *setters = style->GetSetters ();
138 if (!setters)
139 return;
141 CollectionIterator *iter = setters->GetIterator ();
142 Value *setterBase;
143 int err;
145 while (iter->Next () && (setterBase = iter->GetCurrent (&err))) {
146 if (err) {
147 // Something bad happened - what to do?
148 delete iter;
149 return;
152 if (!setterBase->Is (Type::SETTER))
153 continue;
155 Setter *setter = setterBase->AsSetter ();
157 DependencyProperty *setter_property;
158 Value *value;
160 if (!(value = setter->GetValue (Setter::PropertyProperty)))
161 continue;
163 if (!(setter_property = value->AsDependencyProperty ()))
164 continue;
166 Value *setter_value;
167 if (!(setter_value = setter->GetValue (Setter::ConvertedValueProperty)))
168 continue;
170 // the hash holds a ref
171 setter->ref ();
172 g_hash_table_insert (style_hash, setter_property, setter);
174 MoonError error;
175 obj->ProviderValueChanged (precedence, setter_property, NULL, setter_value, true, true, &error);
178 delete iter;
183 // InheritedPropertyValueProvider
185 Value*
186 InheritedPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
188 int propertyId = property->GetId ();
190 if (!IsPropertyInherited (propertyId))
191 return NULL;
193 int parentPropertyId = -1;
195 Types *types = Deployment::GetCurrent()->GetTypes();
197 #define INHERIT_CTI_CTI(p) \
198 G_STMT_START { \
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; \
208 } G_STMT_END
211 #define INHERIT_I_T(p) \
212 G_STMT_START { \
213 if (property->GetId () == Inline::p) { \
214 parentPropertyId = TextBlock::p; \
216 } G_STMT_END
218 #define INHERIT_F_F(p) \
219 G_STMT_START { \
220 if (property->GetId () == FrameworkElement::p) { \
221 parentPropertyId = FrameworkElement::p; \
223 } G_STMT_END
225 #define INHERIT_U_U(p) \
226 G_STMT_START { \
227 if (property->GetId () == UIElement::p) { \
228 parentPropertyId = UIElement::p; \
230 } G_STMT_END
232 DependencyObject *parent = NULL;
234 if (types->IsSubclassOf (obj->GetObjectType(), Type::FRAMEWORKELEMENT)) {
235 // we loop up the visual tree
236 parent = ((FrameworkElement*)obj)->GetVisualParent();
237 if (parent) {
238 while (parent) {
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)) {
259 // skip collections
260 DependencyObject *new_parent = obj->GetParent();
261 while (new_parent && !types->IsSubclassOf (new_parent->GetObjectType(), Type::TEXTBLOCK))
262 new_parent = new_parent->GetParent ();
263 parent = new_parent;
265 if (!parent)
266 return NULL;
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);
283 return NULL;
287 bool
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; \
294 } G_STMT_END
296 #define PROP_F(p) G_STMT_START { \
297 if (propertyId == FrameworkElement::p) return true; \
298 } G_STMT_END
300 #define PROP_U(p) G_STMT_START { \
301 if (propertyId == UIElement::p) return true; \
302 } G_STMT_END
304 #define PROP_I(p) G_STMT_START { \
305 if (propertyId == Inline::p) return true; \
306 } G_STMT_END
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);
323 return false;
326 DependencyProperty*
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); \
338 } G_STMT_END
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); \
346 } G_STMT_END
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)
375 return property;
379 if (types->IsSubclassOf (property->GetOwnerType(), Type::UIELEMENT)) {
380 if (types->IsSubclassOf (descendantKind, Type::UIELEMENT)) {
381 if (property->GetId() == UIElement::UseLayoutRoundingProperty)
382 return property;
386 return NULL;
389 void
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);
400 if (!child_property)
401 return;
403 for (int i = 0; i < inlines->GetCount (); i++) {
404 Inline *item = inlines->GetValueAt (i)->AsInline ();
406 MoonError error;
408 item->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
409 old_value, new_value, false, false, &error);
411 if (error.number) {
412 // FIXME: what do we do here? I'm guessing we continue propagating?
417 else {
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());
427 if (!child_property)
428 continue;
430 MoonError error;
432 element->ProviderValueChanged (PropertyPrecedence_Inherited, child_property,
433 old_value, new_value, true, true, &error);
435 if (error.number) {
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); \
461 if (v != NULL) { \
462 element->ProviderValueChanged (PropertyPrecedence_Inherited, property, \
463 NULL, v, \
464 true, true, &error); \
465 SEEN (seen, s); \
468 } G_STMT_END
470 static void
471 walk_tree (Types *types, UIElement *element, guint32 seen)
473 MoonError error;
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);
508 void
509 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (UIElement *subtreeRoot)
511 Types *types = subtreeRoot->GetDeployment ()->GetTypes ();
513 walk_tree (types, subtreeRoot, 0);
517 // DefaultPropertyValueProvider
520 Value *
521 DefaultValuePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
523 return property->GetDefaultValue ();
528 // AutoPropertyValueProvider
531 static gboolean
532 dispose_value (gpointer key, gpointer value, gpointer data)
534 DependencyObject *obj = (DependencyObject *) data;
535 Value *v = (Value *) value;
537 if (!value)
538 return true;
540 // detach from the existing value
541 if (v->Is (Type::DEPENDENCY_OBJECT)) {
542 DependencyObject *dob = v->AsDependencyObject ();
544 if (dob != NULL) {
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);
555 delete v;
557 return true;
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);
572 Value *
573 AutoCreatePropertyValueProvider::GetPropertyValue (DependencyProperty *property)
575 Value *value;
577 if (!property->IsAutoCreated ())
578 return NULL;
580 // return previously set auto value next
581 if ((value = (Value *) g_hash_table_lookup (auto_values, property)))
582 return value;
584 value = (property->GetAutoCreator()) (obj, property);
586 #if SANITY
587 if (!value->Is(property->GetPropertyType()))
588 g_warning ("autocreated value for property '%s' (type=%s) is of incompatible type %s\n",
589 property->GetName(),
590 Type::Find (property->GetPropertyType ())->GetName(),
591 Type::Find (value->GetKind())->GetName());
592 #endif
594 g_hash_table_insert (auto_values, property, value);
596 MoonError error;
597 obj->ProviderValueChanged (precedence, property, NULL, value, false, true, &error);
599 return value;
602 Value *
603 AutoCreatePropertyValueProvider::ReadLocalValue (DependencyProperty *property)
605 return (Value *) g_hash_table_lookup (auto_values, property);
608 void
609 AutoCreatePropertyValueProvider::ClearValue (DependencyProperty *property)
611 g_hash_table_remove (auto_values, property);
614 Value*
615 AutoCreators::default_autocreator (DependencyObject *instance, DependencyProperty *property)
617 Type *type = Type::Find (property->GetPropertyType ());
618 if (!type)
619 return NULL;
621 return Value::CreateUnrefPtr (type->CreateInstance ());
624 #define XAML_FONT_SIZE 14.666666984558105
625 #define XAP_FONT_SIZE 11.0
627 Value *
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);