in plugin/:
[moon.git] / src / dependencyproperty.cpp
blob37eac7b6bd9f5bd333d4bdc12d121c41ec37b113
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"
24 * DependencyProperty
26 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)
28 this->owner_type = owner_type;
29 this->hash_key = NULL;
30 this->name = g_strdup (name);
31 this->default_value = default_value;
32 this->property_type = property_type;
33 this->is_nullable = false;
34 this->is_attached = attached;
35 this->is_readonly = readonly;
36 this->always_change = always_change;
37 this->changed_callback = changed_callback;
38 this->validator = validator ? validator : Validators::default_validator;
39 this->autocreator = autocreator;
40 this->is_custom = is_custom;
43 DependencyProperty::~DependencyProperty ()
45 g_free (name);
46 if (default_value != NULL)
47 delete default_value;
48 g_free (hash_key);
51 void
52 DependencyProperty::Dispose ()
54 /*
55 * We want to clear out any refs the default_value might have, but we still
56 * need a default value, since we depend on not returning null for the
57 * default value in some places. So if the current default value is an
58 * EventObject, delete it (clears out the ref) and create a new one with
59 * the same type and null value.
61 if (default_value != NULL) {
62 Type::Kind k = default_value->GetKind ();
63 if (Type::IsSubclassOf (k, Type::EVENTOBJECT)) {
64 delete default_value;
65 default_value = new Value (k); /* null */
70 const char *
71 DependencyProperty::GetHashKey ()
73 if (hash_key == NULL)
74 hash_key = g_ascii_strdown (name, -1);
76 return hash_key;
79 DependencyProperty *
80 DependencyProperty::GetDependencyProperty (Type::Kind type, const char *name)
82 return GetDependencyProperty (type, name, true);
85 DependencyProperty *
86 DependencyProperty::GetDependencyProperty (Type::Kind type, const char *name, bool inherits)
88 return GetDependencyProperty (Type::Find (type), name, inherits);
91 DependencyProperty *
92 DependencyProperty::GetDependencyPropertyFull (Type::Kind type, const char *name, bool inherits)
94 DependencyProperty *property;
96 property = GetDependencyProperty (type, name, inherits);
98 if (property == NULL) {
99 Type *t = Type::Find (type);
100 if (t == NULL)
101 return NULL;
103 property = GetDependencyProperty (t, name, false);
104 if (property == NULL && t->GetParent () != Type::INVALID)
105 return GetDependencyPropertyFull (t->GetParent (), name, inherits);
108 return property;
111 DependencyProperty *
112 DependencyProperty::GetDependencyProperty (Type *type, const char *name, bool inherits)
114 DependencyProperty *property = NULL;
116 if (type == NULL)
117 return NULL;
119 property = type->LookupProperty (name);
121 if (property)
122 return property;
124 if (!inherits)
125 return NULL;
127 if (type->GetParent () == Type::INVALID)
128 return NULL;
130 return GetDependencyProperty (Type::Find (type->GetParent ()), name, inherits);
134 // Use this for values that can be null
137 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Type::Kind vtype)
139 return RegisterFull (types, type, name, is_custom, NULL, vtype, false, false, false, NULL, NULL, NULL, false);
143 // DependencyObject takes ownership of the Value * for default_value
146 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value)
148 g_return_val_if_fail (default_value != NULL, NULL);
150 return RegisterFull (types, type, name, is_custom, default_value, default_value->GetKind (), false, false, false, NULL, NULL, NULL, false);
154 // DependencyObject takes ownership of the Value * for default_value
155 // This overload can be used to set the type of the property to a different type
156 // than the default value (the default value can for instance be a SolidColorBrush
157 // while the property type can be a Brush).
160 DependencyProperty::Register (Types *types, Type::Kind type, const char *name, bool is_custom, Value *default_value, Type::Kind vtype)
162 return RegisterFull (types, type, name, is_custom, default_value, vtype, false, false, false, NULL, NULL, NULL, false);
165 DependencyProperty *
166 DependencyProperty::RegisterCoreProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
168 Types *types = Deployment::GetCurrent ()->GetTypes ();
169 int id;
171 if (default_value && default_value->GetKind () == Type::INVALID)
172 default_value = NULL;
173 else
174 default_value = new Value (*default_value);
176 id = DependencyProperty::RegisterFull (types, owner_type, name, false, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
178 return types->GetProperty (id);
181 DependencyProperty *
182 DependencyProperty::RegisterCustomProperty (const char *name, Type::Kind property_type, Type::Kind owner_type, Value *default_value, bool attached, bool readonly, PropertyChangeHandler callback)
184 Types *types = Deployment::GetCurrent ()->GetTypes ();
185 int id;
187 if (default_value && default_value->GetKind () == Type::INVALID)
188 default_value = NULL;
189 else
190 default_value = new Value (*default_value);
192 id = DependencyProperty::RegisterFull (types, owner_type, name, true, default_value, property_type, attached, readonly, false, callback, NULL, NULL, false);
194 return types->GetProperty (id);
198 // Register the dependency property that belongs to @type with the name @name
199 // The default value is @default_value (if provided) and the type that can be
200 // stored in the dependency property is of type @vtype
203 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)
205 DependencyProperty *property;
207 g_return_val_if_fail (types != NULL, NULL);
208 g_return_val_if_fail (type != Type::INVALID, NULL);
209 g_return_val_if_fail (name != NULL, NULL);
211 if (!is_custom && default_value && types->IsSubclassOf (default_value->GetKind (), Type::DEPENDENCY_OBJECT))
212 default_value->AsDependencyObject ()->Freeze();
214 property = new DependencyProperty (type, name, default_value, vtype, attached, readonly, always_change, changed_callback, validator, autocreator, is_custom);
215 property->is_nullable = is_nullable;
217 types->AddProperty (property);
219 return property->GetId ();
222 bool
223 DependencyProperty::Validate (DependencyObject *instance, Value *value, MoonError *error)
225 return validator (instance, this, value, error);
228 void
229 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback)
231 this->changed_callback = changed_callback;
234 Type *
235 lookup_type (DependencyObject *lu, const char* name)
237 Type *t = Type::Find (name);
239 if (t)
240 return t;
243 // If we are dealing with a managed type and we don't have the full namespace
244 // we just verify that the type name matches the lookup type.
247 const char *tname = strchr (name, ':');
248 if (!tname || ! *(++tname))
249 return NULL;
251 const char *luname = lu->GetTypeName ();
252 int lulen = strlen (luname);
253 int tlen = strlen (tname);
255 if (lulen < tlen || strcmp (luname + lulen - tlen, tname))
256 return NULL;
258 return lu->GetType ();
262 // Everything inside of a ( ) resolves to a DependencyProperty, if there is a
263 // '.' after the property, we get the object, and continue resolving from there
264 // if there is a [n] after the property, we convert the property to a collection
265 // and grab the nth item.
267 // Dependency properties can be specified as (PropertyName) of the current object
268 // or they can be specified as (DependencyObject.PropertyName).
270 // Returns NULL on any error
272 DependencyProperty *
273 resolve_property_path (DependencyObject **o, PropertyPath *propertypath, GHashTable *promoted_values)
275 g_return_val_if_fail (o != NULL, NULL);
276 g_return_val_if_fail (propertypath != NULL, NULL);
277 g_return_val_if_fail (propertypath->path != NULL || propertypath->property != NULL, NULL);
279 if (propertypath->property)
280 return propertypath->property;
282 const char *path = propertypath->path;
283 if (propertypath->expanded_path)
284 path = propertypath->expanded_path;
286 const char *inend = path + strlen (path);
287 register const char *inptr = path;
288 const char *start, *prop = path;
289 bool expression_found = false;
290 DependencyProperty *res = NULL;
291 DependencyObject *lu = *o;
292 Collection *collection;
293 char *p, *name = NULL;
294 Value *value;
295 Type *type = NULL;
296 int index;
297 bool paren_open = false;
298 bool tick_open = false;
299 bool cloned = false;
301 while (inptr < inend) {
302 switch (*inptr++) {
303 case '(':
304 paren_open = true;
305 break;
306 case ')':
307 paren_open = false;
308 break;
309 case '\'':
310 // Ticks are only legal in expanded paths, so we should just fail here
311 if (!propertypath->expanded_path) {
312 g_warning ("The ' character is not legal in property paths.");
313 break;
316 tick_open = !tick_open;
317 break;
318 case '.':
319 if (tick_open)
320 continue;
322 // resolve the dependency property
323 if (res) {
324 DependencyObject *new_lu;
326 // make sure that we are getting what we expect
327 if (!(value = lu->GetValue (res)))
328 goto error;
330 if (!(new_lu = value->AsDependencyObject ()))
331 goto error;
333 if (!cloned && !g_hash_table_lookup (promoted_values, value) && !value->Is (Type::UIELEMENT)) {
334 // we need to clone the value here so that we deep copy any
335 // DO subclasses (such as brushes, etc) that we're promoting
336 // from a shared space (Styles, default values)
337 Value *cloned_value = Value::Clone (value);
338 lu->SetValue (res, cloned_value);
339 new_lu = cloned_value->AsDependencyObject();
340 delete cloned_value;
342 cloned_value = lu->GetValue (res);
343 g_hash_table_insert (promoted_values, cloned_value, cloned_value);
346 lu = new_lu;
349 expression_found = false;
350 prop = inptr;
351 break;
352 case '[':
353 // Need to be a little more loving
354 if (*inptr == '\0')
355 break;
357 index = strtol (inptr, &p, 10);
358 if (*p != ']' || *(p + 1) != '.')
359 break;
361 inptr = p + 2;
362 prop = inptr;
364 if (expression_found) {
365 expression_found = false;
366 if (!(value = lu->GetValue (res)))
367 goto error;
370 if (!(collection = value->AsCollection ()))
371 goto error;
373 if (!(value = collection->GetValueAt (index)))
374 goto error;
376 if (!(lu = value->AsDependencyObject ()))
377 goto error;
379 break;
381 default:
382 bool explicit_type = false;
383 expression_found = true;
384 start = inptr - 1;
386 while (inptr < inend && (*inptr != '.' || tick_open) && (!paren_open || *inptr != ')') && *inptr != '[') {
387 if (*inptr == '\'') {
388 tick_open = !tick_open;
389 if (!tick_open) {
390 inptr++;
391 break;
394 inptr++;
397 if (inptr == start)
398 goto error;
400 if (*inptr == '.') {
401 // we found a type name, now we need to find the property name
402 if ((inptr - start) == 11 && !g_ascii_strncasecmp (start, "TextElement", 11)) {
403 // Some Beta versions of Blend had a bug where they would save the TextBlock
404 // properties as TextElement instead. Since Silverlight 1.0 works around this
405 // bug, we should too. Fixes http://silverlight.timovil.com and
406 // http://election.msn.com/podium08.aspx.
407 type = Type::Find ("TextBlock");
408 explicit_type = true;
409 } else {
410 const char *s = inptr;
411 if (*(inptr -1) == '\'' && !tick_open) {
412 s = inptr - 1;
414 name = g_strndup (start, s - start);
415 type = lookup_type (lu, name);
416 explicit_type = true;
417 g_free (name);
420 inptr++;
421 start = inptr;
422 while (inptr < inend && (!paren_open || *inptr != ')') && (*inptr != '.' || tick_open)) {
423 if (*inptr == '\'') {
424 tick_open = !tick_open;
425 if (!tick_open) {
426 inptr++;
427 break;
430 inptr++;
433 if (inptr == start)
434 goto error;
435 } else {
436 type = Type::Find (lu->GetObjectType ());
437 explicit_type = false;
440 if ((*inptr != ')' && paren_open) || !type)
441 goto error;
443 name = g_strndup (start, inptr - start);
444 if (!(res = DependencyProperty::GetDependencyProperty (type->GetKind (), name)) && lu)
445 res = DependencyProperty::GetDependencyProperty (lu->GetType ()->GetKind (), name);
447 if (!res) {
448 g_free (name);
449 goto error;
452 if (!res->IsAttached () && !lu->Is (type->GetKind ())) {
453 // We try to be gracefull here and do something smart...
454 if (!(res = DependencyProperty::GetDependencyProperty (lu->GetObjectType (), name))) {
455 g_free (name);
456 goto error;
460 if (res->IsAttached () && explicit_type && !paren_open)
461 goto error;
463 g_free (name);
464 break;
468 *o = lu;
469 return res;
471 error:
472 *o = NULL;
473 return NULL;