1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
17 #include "dependencyproperty.h"
18 #include "animation.h"
20 #include "validators.h"
21 #include "eventargs.h"
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 ()
46 if (default_value
!= NULL
)
52 DependencyProperty::Dispose ()
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
)) {
65 default_value
= new Value (k
); /* null */
71 DependencyProperty::GetHashKey ()
74 hash_key
= g_ascii_strdown (name
, -1);
80 DependencyProperty::GetDependencyProperty (Type::Kind type
, const char *name
)
82 return GetDependencyProperty (type
, name
, true);
86 DependencyProperty::GetDependencyProperty (Type::Kind type
, const char *name
, bool inherits
)
88 return GetDependencyProperty (Type::Find (type
), name
, inherits
);
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
);
103 property
= GetDependencyProperty (t
, name
, false);
104 if (property
== NULL
&& t
->GetParent () != Type::INVALID
)
105 return GetDependencyPropertyFull (t
->GetParent (), name
, inherits
);
112 DependencyProperty::GetDependencyProperty (Type
*type
, const char *name
, bool inherits
)
114 DependencyProperty
*property
= NULL
;
119 property
= type
->LookupProperty (name
);
127 if (type
->GetParent () == Type::INVALID
)
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);
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 ();
171 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
172 default_value
= NULL
;
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
);
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 ();
187 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
188 default_value
= NULL
;
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 ();
223 DependencyProperty::Validate (DependencyObject
*instance
, Value
*value
, MoonError
*error
)
225 return validator (instance
, this, value
, error
);
229 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback
)
231 this->changed_callback
= changed_callback
;
235 lookup_type (DependencyObject
*lu
, const char* name
)
237 Type
*t
= Type::Find (name
);
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
))
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
))
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
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
;
297 bool paren_open
= false;
298 bool tick_open
= false;
301 while (inptr
< inend
) {
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.");
316 tick_open
= !tick_open
;
322 // resolve the dependency property
324 DependencyObject
*new_lu
;
326 // make sure that we are getting what we expect
327 if (!(value
= lu
->GetValue (res
)))
330 if (!(new_lu
= value
->AsDependencyObject ()))
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();
342 cloned_value
= lu
->GetValue (res
);
343 g_hash_table_insert (promoted_values
, cloned_value
, cloned_value
);
349 expression_found
= false;
353 // Need to be a little more loving
357 index
= strtol (inptr
, &p
, 10);
358 if (*p
!= ']' || *(p
+ 1) != '.')
364 if (expression_found
) {
365 expression_found
= false;
366 if (!(value
= lu
->GetValue (res
)))
370 if (!(collection
= value
->AsCollection ()))
373 if (!(value
= collection
->GetValueAt (index
)))
376 if (!(lu
= value
->AsDependencyObject ()))
382 bool explicit_type
= false;
383 expression_found
= true;
386 while (inptr
< inend
&& (*inptr
!= '.' || tick_open
) && (!paren_open
|| *inptr
!= ')') && *inptr
!= '[') {
387 if (*inptr
== '\'') {
388 tick_open
= !tick_open
;
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;
410 const char *s
= inptr
;
411 if (*(inptr
-1) == '\'' && !tick_open
) {
414 name
= g_strndup (start
, s
- start
);
415 type
= lookup_type (lu
, name
);
416 explicit_type
= true;
422 while (inptr
< inend
&& (!paren_open
|| *inptr
!= ')') && (*inptr
!= '.' || tick_open
)) {
423 if (*inptr
== '\'') {
424 tick_open
= !tick_open
;
436 type
= Type::Find (lu
->GetObjectType ());
437 explicit_type
= false;
440 if ((*inptr
!= ')' && paren_open
) || !type
)
443 name
= g_strndup (start
, inptr
- start
);
444 if (!(res
= DependencyProperty::GetDependencyProperty (type
->GetKind (), name
)) && lu
)
445 res
= DependencyProperty::GetDependencyProperty (lu
->GetType ()->GetKind (), name
);
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
))) {
460 if (res
->IsAttached () && explicit_type
&& !paren_open
)