2009-12-01 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / dependencyproperty.cpp
blobe50e48f5b24edf7236c17a2d7a343d0b6ab9836d
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"
21 #include "eventargs.h"
22 #include "deployment.h"
25 * DependencyProperty
27 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)
29 this->owner_type = owner_type;
30 this->hash_key = NULL;
31 this->name = g_strdup (name);
32 this->default_value = default_value;
33 this->property_type = property_type;
34 this->is_nullable = false;
35 this->is_attached = attached;
36 this->is_readonly = readonly;
37 this->always_change = always_change;
38 this->changed_callback = changed_callback;
39 this->validator = validator ? validator : Validators::default_validator;
40 this->autocreator = autocreator;
41 this->is_custom = is_custom;
44 DependencyProperty::~DependencyProperty ()
46 g_free (name);
47 if (default_value != NULL)
48 delete default_value;
49 g_free (hash_key);
52 void
53 DependencyProperty::Dispose ()
55 /*
56 * We want to clear out any refs the default_value might have, but we still
57 * need a default value, since we depend on not returning null for the
58 * default value in some places. So if the current default value is an
59 * EventObject, delete it (clears out the ref) and create a new one with
60 * the same type and null value.
62 if (default_value != NULL) {
63 Type::Kind k = default_value->GetKind ();
64 if (Type::IsSubclassOf (Deployment::GetCurrent (), k, Type::EVENTOBJECT)) {
65 delete default_value;
66 default_value = new Value (k); /* null */
71 const char *
72 DependencyProperty::GetHashKey ()
74 if (hash_key == NULL)
75 hash_key = g_ascii_strdown (name, -1);
77 return hash_key;
80 DependencyProperty *
81 DependencyProperty::GetDependencyProperty (Type::Kind type, const char *name)
83 return GetDependencyProperty (Type::Find (Deployment::GetCurrent (), type), name);
86 DependencyProperty *
87 DependencyProperty::GetDependencyProperty (Type *type, const char *name)
89 return GetDependencyProperty (type, name, true);
92 DependencyProperty *
93 DependencyProperty::GetDependencyPropertyFull (Type::Kind type, const char *name, bool inherits)
95 DependencyProperty *property;
96 Type *t = Type::Find (Deployment::GetCurrent (), type);
98 if (t == NULL)
99 return NULL;
101 property = GetDependencyProperty (t, name, inherits);
103 if (property == NULL) {
104 if (inherits)
105 property = GetDependencyProperty (t, name, false);
106 if (property == NULL && t->HasParent ())
107 return GetDependencyPropertyFull (t->GetParentType (), name, inherits);
110 return property;
113 DependencyProperty *
114 DependencyProperty::GetDependencyPropertyFull (Type *type, const char *name, bool inherits)
116 DependencyProperty *property;
118 if (type == NULL)
119 return NULL;
121 property = GetDependencyProperty (type, name, inherits);
123 if (property == NULL) {
124 property = GetDependencyProperty (type, name, false);
125 if (property == NULL && type->HasParent ())
126 return GetDependencyPropertyFull (type->GetParentType (), name, inherits);
129 return property;
132 DependencyProperty *
133 DependencyProperty::GetDependencyProperty (Type *type, const char *name, bool inherits)
135 DependencyProperty *property = NULL;
137 if (type == NULL)
138 return NULL;
140 property = type->LookupProperty (name);
142 if (property)
143 return property;
145 if (!inherits)
146 return NULL;
148 if (!type->HasParent ())
149 return NULL;
151 return GetDependencyProperty (type->GetParentType (), name, inherits);
155 // Use this for values that can be null
158 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Type::Kind vtype)
160 return RegisterFull (types, type, name, is_custom, NULL, vtype, false, false, false, NULL, NULL, NULL, false);
164 // DependencyObject takes ownership of the Value * for default_value
167 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value)
169 g_return_val_if_fail (default_value != NULL, NULL);
171 return RegisterFull (types, type, name, is_custom, default_value, default_value->GetKind (), false, false, false, NULL, NULL, NULL, false);
175 // DependencyObject takes ownership of the Value * for default_value
176 // This overload can be used to set the type of the property to a different type
177 // than the default value (the default value can for instance be a SolidColorBrush
178 // while the property type can be a Brush).
181 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value, Type::Kind vtype)
183 return RegisterFull (types, type, name, is_custom, default_value, vtype, false, false, false, NULL, NULL, NULL, false);
186 DependencyProperty *
187 DependencyProperty::RegisterCoreProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
189 Types *types = Deployment::GetCurrent ()->GetTypes ();
190 int id;
192 if (default_value && default_value->GetKind () == Type::INVALID)
193 default_value = NULL;
194 else
195 default_value = new Value (*default_value);
197 id = DependencyProperty::RegisterFull (types, owner_type, name, false, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
199 return types->GetProperty (id);
202 DependencyProperty *
203 DependencyProperty::RegisterCustomProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
205 Types *types = Deployment::GetCurrent ()->GetTypes ();
206 int id;
208 if (default_value && default_value->GetKind () == Type::INVALID)
209 default_value = NULL;
210 else
211 default_value = new Value (*default_value);
213 id = DependencyProperty::RegisterFull (types, owner_type, name, true, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
215 return types->GetProperty (id);
219 // Register the dependency property that belongs to @type with the name @name
220 // The default value is @default_value (if provided) and the type that can be
221 // stored in the dependency property is of type @vtype
224 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)
226 DependencyProperty *property;
228 g_return_val_if_fail (types != NULL, NULL);
229 g_return_val_if_fail (type != Type::INVALID, NULL);
230 g_return_val_if_fail (name != NULL, NULL);
232 if (!is_custom && default_value && types->IsSubclassOf (default_value->GetKind (), Type::DEPENDENCY_OBJECT))
233 default_value->AsDependencyObject ()->Freeze();
235 property = new DependencyProperty (type, name, default_value, vtype, attached, readonly, always_change, changed_callback, validator, autocreator, is_custom);
236 property->is_nullable = is_nullable;
238 types->AddProperty (property);
240 return property->GetId ();
243 bool
244 DependencyProperty::Validate (DependencyObject *instance, Value *value, MoonError *error)
246 return validator (instance, this, value, error);
249 void
250 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback)
252 this->changed_callback = changed_callback;
255 Type *
256 lookup_type (DependencyObject *lu, const char* name)
258 Type *t = Type::Find (lu->GetDeployment (), name);
260 if (t)
261 return t;
264 // If we are dealing with a managed type and we don't have the full namespace
265 // we just verify that the type name matches the lookup type.
268 const char *tname = strchr (name, ':');
269 if (!tname || ! *(++tname))
270 return NULL;
272 const char *luname = lu->GetTypeName ();
273 int lulen = strlen (luname);
274 int tlen = strlen (tname);
276 if (lulen < tlen || strcmp (luname + lulen - tlen, tname))
277 return NULL;
279 return lu->GetType ();
283 // Everything inside of a ( ) resolves to a DependencyProperty, if there is a
284 // '.' after the property, we get the object, and continue resolving from there
285 // if there is a [n] after the property, we convert the property to a collection
286 // and grab the nth item.
288 // Dependency properties can be specified as (PropertyName) of the current object
289 // or they can be specified as (DependencyObject.PropertyName).
291 // Returns NULL on any error
293 DependencyProperty *
294 resolve_property_path (DependencyObject **o, PropertyPath *propertypath, GHashTable *promoted_values)
296 g_return_val_if_fail (o != NULL, NULL);
297 g_return_val_if_fail (propertypath != NULL, NULL);
298 g_return_val_if_fail (propertypath->path != NULL || propertypath->property != NULL, NULL);
300 if (propertypath->property)
301 return propertypath->property;
303 const char *path = propertypath->path;
304 if (propertypath->expanded_path)
305 path = propertypath->expanded_path;
307 const char *inend = path + strlen (path);
308 register const char *inptr = path;
309 const char *start, *prop = path;
310 bool expression_found = false;
311 DependencyProperty *res = NULL;
312 DependencyObject *lu = *o;
313 Collection *collection;
314 char *p, *name = NULL;
315 Value *value = NULL;
316 Type *type = NULL;
317 int index;
318 bool paren_open = false;
319 bool tick_open = false;
320 bool cloned = false;
322 while (inptr < inend) {
323 switch (*inptr++) {
324 case '(':
325 paren_open = true;
326 break;
327 case ')':
328 paren_open = false;
329 break;
330 case '\'':
331 // Ticks are only legal in expanded paths, so we should just fail here
332 if (!propertypath->expanded_path) {
333 g_warning ("The ' character is not legal in property paths.");
334 break;
337 tick_open = !tick_open;
338 break;
339 case '.':
340 if (tick_open)
341 continue;
343 // resolve the dependency property
344 if (res) {
345 DependencyObject *new_lu;
347 // make sure that we are getting what we expect
348 if (!(value = lu->GetValue (res)))
349 goto error;
351 if (!(new_lu = value->AsDependencyObject ()))
352 goto error;
354 if (!cloned && !g_hash_table_lookup (promoted_values, value) && !value->Is (lu->GetDeployment (), Type::UIELEMENT)) {
355 // we need to clone the value here so that we deep copy any
356 // DO subclasses (such as brushes, etc) that we're promoting
357 // from a shared space (Styles, default values)
358 Value *cloned_value = Value::Clone (value);
359 lu->SetValue (res, cloned_value);
360 new_lu = cloned_value->AsDependencyObject();
361 delete cloned_value;
363 cloned_value = lu->GetValue (res);
364 g_hash_table_insert (promoted_values, cloned_value, cloned_value);
367 lu = new_lu;
370 expression_found = false;
371 prop = inptr;
372 break;
373 case '[':
374 // Need to be a little more loving
375 if (*inptr == '\0')
376 break;
378 index = strtol (inptr, &p, 10);
379 if (*p != ']' || *(p + 1) != '.')
380 break;
382 inptr = p + 2;
383 prop = inptr;
385 if (expression_found) {
386 expression_found = false;
387 if (!(value = lu->GetValue (res)))
388 goto error;
391 if (value == NULL)
392 goto error;
394 if (!(collection = value->AsCollection ()))
395 goto error;
397 if (!(value = collection->GetValueAt (index)))
398 goto error;
400 if (!(lu = value->AsDependencyObject ()))
401 goto error;
403 break;
405 default:
406 bool explicit_type = false;
407 expression_found = true;
408 start = inptr - 1;
410 while (inptr < inend && (*inptr != '.' || tick_open) && (!paren_open || *inptr != ')') && *inptr != '[') {
411 if (*inptr == '\'') {
412 tick_open = !tick_open;
413 if (!tick_open) {
414 inptr++;
415 break;
418 inptr++;
421 if (inptr == start)
422 goto error;
424 if (*inptr == '.') {
425 // we found a type name, now we need to find the property name
426 if ((inptr - start) == 11 && !g_ascii_strncasecmp (start, "TextElement", 11)) {
427 // Some Beta versions of Blend had a bug where they would save the TextBlock
428 // properties as TextElement instead. Since Silverlight 1.0 works around this
429 // bug, we should too. Fixes http://silverlight.timovil.com and
430 // http://election.msn.com/podium08.aspx.
431 type = Type::Find (lu->GetDeployment (), "TextBlock");
432 explicit_type = true;
433 } else {
434 const char *s = inptr;
435 if (*(inptr -1) == '\'' && !tick_open) {
436 s = inptr - 1;
438 name = g_strndup (start, s - start);
439 type = lookup_type (lu, name);
440 explicit_type = true;
441 if (!type)
442 type = lu->GetType ();
443 g_free (name);
446 inptr++;
447 start = inptr;
448 while (inptr < inend && (!paren_open || *inptr != ')') && (*inptr != '.' || tick_open)) {
449 if (*inptr == '\'') {
450 tick_open = !tick_open;
451 if (!tick_open) {
452 inptr++;
453 break;
456 inptr++;
459 if (inptr == start)
460 goto error;
461 } else {
462 type = lu->GetType ();
463 explicit_type = false;
466 if ((*inptr != ')' && paren_open) || !type)
467 goto error;
469 name = g_strndup (start, inptr - start);
470 if (!(res = DependencyProperty::GetDependencyProperty (type, name)) && lu)
471 res = DependencyProperty::GetDependencyProperty (lu->GetType (), name);
473 if (!res) {
474 g_free (name);
475 goto error;
478 if (!res->IsAttached () && !lu->Is (type->GetKind ())) {
479 // We try to be gracefull here and do something smart...
480 if (!(res = DependencyProperty::GetDependencyProperty (lu->GetType (), name))) {
481 g_free (name);
482 goto error;
486 if (res->IsAttached () && explicit_type && !paren_open)
487 goto error;
489 g_free (name);
490 break;
494 *o = lu;
495 return res;
497 error:
498 *o = NULL;
499 return NULL;