2009-08-26 Chris Toshok <toshok@ximian.com>
[moon.git] / src / dependencyproperty.cpp
blobca21a7a86537b2f15c60946e1d20dafad18d087c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dependencypropery.h:
5 * Copyright 2007-2008 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 <stdio.h>
14 #include <stdlib.h>
15 #include <pthread.h>
17 #include "dependencyproperty.h"
18 #include "animation.h"
19 #include "runtime.h"
20 #include "validators.h"
23 * DependencyProperty
25 DependencyProperty::DependencyProperty (Type::Kind owner_type, const char *name, Value *default_value, Type::Kind property_type, bool attached, bool readonly, bool always_change, PropertyChangeHandler changed_callback, ValueValidator *validator, AutoCreator* autocreator, bool is_custom)
27 this->owner_type = owner_type;
28 this->hash_key = NULL;
29 this->name = g_strdup (name);
30 this->default_value = default_value;
31 this->property_type = property_type;
32 this->is_nullable = false;
33 this->is_attached = attached;
34 this->is_readonly = readonly;
35 this->always_change = always_change;
36 this->changed_callback = changed_callback;
37 this->validator = validator ? validator : Validators::default_validator;
38 this->autocreator = autocreator;
39 this->is_custom = is_custom;
42 DependencyProperty::~DependencyProperty ()
44 g_free (name);
45 if (default_value != NULL)
46 delete default_value;
47 g_free (hash_key);
50 const char *
51 DependencyProperty::GetHashKey ()
53 if (hash_key == NULL)
54 hash_key = g_ascii_strdown (name, -1);
56 return hash_key;
59 DependencyProperty *
60 DependencyProperty::GetDependencyProperty (Type::Kind type, const char *name)
62 return GetDependencyProperty (type, name, true);
65 DependencyProperty *
66 DependencyProperty::GetDependencyProperty (Type::Kind type, const char *name, bool inherits)
68 return GetDependencyProperty (Type::Find (type), name, inherits);
71 DependencyProperty *
72 DependencyProperty::GetDependencyPropertyFull (Type::Kind type, const char *name, bool inherits)
74 DependencyProperty *property;
76 property = GetDependencyProperty (type, name, inherits);
78 if (property == NULL) {
79 Type *t = Type::Find (type);
80 if (t == NULL)
81 return NULL;
83 property = GetDependencyProperty (t, name, false);
84 if (property == NULL && t->GetParent () != Type::INVALID)
85 return GetDependencyPropertyFull (t->GetParent (), name, inherits);
88 return property;
91 DependencyProperty *
92 DependencyProperty::GetDependencyProperty (Type *type, const char *name, bool inherits)
94 DependencyProperty *property = NULL;
96 if (type == NULL)
97 return NULL;
99 property = type->LookupProperty (name);
101 if (property)
102 return property;
104 if (!inherits)
105 return NULL;
107 if (type->GetParent () == Type::INVALID)
108 return NULL;
110 return GetDependencyProperty (Type::Find (type->GetParent ()), name, inherits);
114 // Use this for values that can be null
117 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Type::Kind vtype)
119 return RegisterFull (types, type, name, is_custom, NULL, vtype, false, false, false, NULL, NULL, NULL, false);
123 // DependencyObject takes ownership of the Value * for default_value
126 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value)
128 g_return_val_if_fail (default_value != NULL, NULL);
130 return RegisterFull (types, type, name, is_custom, default_value, default_value->GetKind (), false, false, false, NULL, NULL, NULL, false);
134 // DependencyObject takes ownership of the Value * for default_value
135 // This overload can be used to set the type of the property to a different type
136 // than the default value (the default value can for instance be a SolidColorBrush
137 // while the property type can be a Brush).
140 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value, Type::Kind vtype)
142 return RegisterFull (types, type, name, is_custom, default_value, vtype, false, false, false, NULL, NULL, NULL, false);
145 DependencyProperty *
146 DependencyProperty::RegisterCoreProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
148 Types *types = Deployment::GetCurrent ()->GetTypes ();
149 int id;
151 if (default_value && default_value->GetKind () == Type::INVALID)
152 default_value = NULL;
153 else
154 default_value = new Value (*default_value);
156 id = DependencyProperty::RegisterFull (types, owner_type, name, false, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
158 return types->GetProperty (id);
161 DependencyProperty *
162 DependencyProperty::RegisterCustomProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
164 Types *types = Deployment::GetCurrent ()->GetTypes ();
165 int id;
167 if (default_value && default_value->GetKind () == Type::INVALID)
168 default_value = NULL;
169 else
170 default_value = new Value (*default_value);
172 id = DependencyProperty::RegisterFull (types, owner_type, name, true, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
174 return types->GetProperty (id);
178 // Register the dependency property that belongs to @type with the name @name
179 // The default value is @default_value (if provided) and the type that can be
180 // stored in the dependency property is of type @vtype
183 DependencyProperty::RegisterFull (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value, Type::Kind vtype, bool attached, bool readonly, bool always_change, PropertyChangeHandler changed_callback, ValueValidator *validator, AutoCreator* autocreator, bool is_nullable)
185 DependencyProperty *property;
187 g_return_val_if_fail (types != NULL, NULL);
188 g_return_val_if_fail (type != Type::INVALID, NULL);
189 g_return_val_if_fail (name != NULL, NULL);
191 if (!is_custom && default_value && types->IsSubclassOf (default_value->GetKind (), Type::DEPENDENCY_OBJECT))
192 default_value->AsDependencyObject ()->Freeze();
194 property = new DependencyProperty (type, name, default_value, vtype, attached, readonly, always_change, changed_callback, validator, autocreator, is_custom);
195 property->is_nullable = is_nullable;
197 types->AddProperty (property);
199 return property->GetId ();
202 bool
203 DependencyProperty::Validate (DependencyObject *instance, Value *value, MoonError *error)
205 return validator (instance, this, value, error);
208 void
209 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback)
211 this->changed_callback = changed_callback;
214 Type *
215 lookup_type (DependencyObject *lu, const char* name)
217 Type *t = Type::Find (name);
219 if (t)
220 return t;
223 // If we are dealing with a managed type and we don't have the full namespace
224 // we just verify that the type name matches the lookup type.
227 const char *tname = strchr (name, ':');
228 if (!tname || ! *(++tname))
229 return NULL;
231 const char *luname = lu->GetTypeName ();
232 int lulen = strlen (luname);
233 int tlen = strlen (tname);
235 if (lulen < tlen || strcmp (luname + lulen - tlen, tname))
236 return NULL;
238 return lu->GetType ();
242 // Everything inside of a ( ) resolves to a DependencyProperty, if there is a
243 // '.' after the property, we get the object, and continue resolving from there
244 // if there is a [n] after the property, we convert the property to a collection
245 // and grab the nth item.
247 // Dependency properties can be specified as (PropertyName) of the current object
248 // or they can be specified as (DependencyObject.PropertyName).
250 // Returns NULL on any error
252 DependencyProperty *
253 resolve_property_path (DependencyObject **o, PropertyPath *propertypath, GHashTable *promoted_values)
255 g_return_val_if_fail (o != NULL, NULL);
256 g_return_val_if_fail (propertypath != NULL, NULL);
257 g_return_val_if_fail (propertypath->path != NULL || propertypath->property != NULL, NULL);
259 if (propertypath->property)
260 return propertypath->property;
262 const char *path = propertypath->path;
263 if (propertypath->expanded_path)
264 path = propertypath->expanded_path;
266 const char *inend = path + strlen (path);
267 register const char *inptr = path;
268 const char *start, *prop = path;
269 bool expression_found = false;
270 DependencyProperty *res = NULL;
271 DependencyObject *lu = *o;
272 Collection *collection;
273 char *p, *name = NULL;
274 Value *value;
275 Type *type = NULL;
276 int index;
277 bool paren_open = false;
278 bool tick_open = false;
279 bool cloned = false;
281 while (inptr < inend) {
282 switch (*inptr++) {
283 case '(':
284 paren_open = true;
285 break;
286 case ')':
287 paren_open = false;
288 break;
289 case '\'':
290 // Ticks are only legal in expanded paths, so we should just fail here
291 if (!propertypath->expanded_path) {
292 g_warning ("The ' character is not legal in property paths.");
293 break;
296 tick_open = !tick_open;
297 break;
298 case '.':
299 if (tick_open)
300 continue;
302 // resolve the dependency property
303 if (res) {
304 DependencyObject *new_lu;
306 // make sure that we are getting what we expect
307 if (!(value = lu->GetValue (res)))
308 goto error;
310 if (!(new_lu = value->AsDependencyObject ()))
311 goto error;
313 if (!cloned && !g_hash_table_lookup (promoted_values, value) && !value->Is (Type::UIELEMENT)) {
314 // we need to clone the value here so that we deep copy any
315 // DO subclasses (such as brushes, etc) that we're promoting
316 // from a shared space (Styles, default values)
317 Value *cloned_value = Value::Clone (value);
318 lu->SetValue (res, cloned_value);
319 new_lu = cloned_value->AsDependencyObject();
320 delete cloned_value;
322 cloned_value = lu->GetValue (res);
323 g_hash_table_insert (promoted_values, cloned_value, cloned_value);
326 lu = new_lu;
329 expression_found = false;
330 prop = inptr;
331 break;
332 case '[':
333 // Need to be a little more loving
334 if (*inptr == '\0')
335 break;
337 index = strtol (inptr, &p, 10);
338 if (*p != ']' || *(p + 1) != '.')
339 break;
341 inptr = p + 2;
342 prop = inptr;
344 if (expression_found) {
345 expression_found = false;
346 if (!(value = lu->GetValue (res)))
347 goto error;
350 if (!(collection = value->AsCollection ()))
351 goto error;
353 if (!(value = collection->GetValueAt (index)))
354 goto error;
356 if (!(lu = value->AsDependencyObject ()))
357 goto error;
359 break;
361 default:
362 bool explicit_type = false;
363 expression_found = true;
364 start = inptr - 1;
366 while (inptr < inend && (*inptr != '.' || tick_open) && (!paren_open || *inptr != ')') && *inptr != '[') {
367 if (*inptr == '\'') {
368 tick_open = !tick_open;
369 if (!tick_open) {
370 inptr++;
371 break;
374 inptr++;
377 if (inptr == start)
378 goto error;
380 if (*inptr == '.') {
381 // we found a type name, now we need to find the property name
382 if ((inptr - start) == 11 && !g_ascii_strncasecmp (start, "TextElement", 11)) {
383 // Some Beta versions of Blend had a bug where they would save the TextBlock
384 // properties as TextElement instead. Since Silverlight 1.0 works around this
385 // bug, we should too. Fixes http://silverlight.timovil.com and
386 // http://election.msn.com/podium08.aspx.
387 type = Type::Find ("TextBlock");
388 explicit_type = true;
389 } else {
390 const char *s = inptr;
391 if (*(inptr -1) == '\'' && !tick_open) {
392 s = inptr - 1;
394 name = g_strndup (start, s - start);
395 type = lookup_type (lu, name);
396 explicit_type = true;
397 g_free (name);
400 inptr++;
401 start = inptr;
402 while (inptr < inend && (!paren_open || *inptr != ')') && (*inptr != '.' || tick_open)) {
403 if (*inptr == '\'') {
404 tick_open = !tick_open;
405 if (!tick_open) {
406 inptr++;
407 break;
410 inptr++;
413 if (inptr == start)
414 goto error;
415 } else {
416 type = Type::Find (lu->GetObjectType ());
417 explicit_type = false;
420 if ((*inptr != ')' && paren_open) || !type)
421 goto error;
423 name = g_strndup (start, inptr - start);
424 if (!(res = DependencyProperty::GetDependencyProperty (type->GetKind (), name))) {
425 g_free (name);
426 goto error;
429 if (!res->IsAttached () && !lu->Is (type->GetKind ())) {
430 // We try to be gracefull here and do something smart...
431 if (!(res = DependencyProperty::GetDependencyProperty (lu->GetObjectType (), name))) {
432 g_free (name);
433 goto error;
437 if (res->IsAttached () && explicit_type && !paren_open)
438 goto error;
440 g_free (name);
441 break;
445 *o = lu;
446 return res;
448 error:
449 *o = NULL;
450 return NULL;