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"
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 ()
45 if (default_value
!= NULL
)
51 DependencyProperty::GetHashKey ()
54 hash_key
= g_ascii_strdown (name
, -1);
60 DependencyProperty::GetDependencyProperty (Type::Kind type
, const char *name
)
62 return GetDependencyProperty (type
, name
, true);
66 DependencyProperty::GetDependencyProperty (Type::Kind type
, const char *name
, bool inherits
)
68 return GetDependencyProperty (Type::Find (type
), name
, inherits
);
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
);
83 property
= GetDependencyProperty (t
, name
, false);
84 if (property
== NULL
&& t
->GetParent () != Type::INVALID
)
85 return GetDependencyPropertyFull (t
->GetParent (), name
, inherits
);
92 DependencyProperty::GetDependencyProperty (Type
*type
, const char *name
, bool inherits
)
94 DependencyProperty
*property
= NULL
;
99 property
= type
->LookupProperty (name
);
107 if (type
->GetParent () == Type::INVALID
)
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);
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 ();
151 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
152 default_value
= NULL
;
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
);
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 ();
167 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
168 default_value
= NULL
;
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 ();
203 DependencyProperty::Validate (DependencyObject
*instance
, Value
*value
, MoonError
*error
)
205 return validator (instance
, this, value
, error
);
209 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback
)
211 this->changed_callback
= changed_callback
;
215 lookup_type (DependencyObject
*lu
, const char* name
)
217 Type
*t
= Type::Find (name
);
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
))
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
))
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
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
;
277 bool paren_open
= false;
278 bool tick_open
= false;
281 while (inptr
< inend
) {
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.");
296 tick_open
= !tick_open
;
302 // resolve the dependency property
304 DependencyObject
*new_lu
;
306 // make sure that we are getting what we expect
307 if (!(value
= lu
->GetValue (res
)))
310 if (!(new_lu
= value
->AsDependencyObject ()))
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();
322 cloned_value
= lu
->GetValue (res
);
323 g_hash_table_insert (promoted_values
, cloned_value
, cloned_value
);
329 expression_found
= false;
333 // Need to be a little more loving
337 index
= strtol (inptr
, &p
, 10);
338 if (*p
!= ']' || *(p
+ 1) != '.')
344 if (expression_found
) {
345 expression_found
= false;
346 if (!(value
= lu
->GetValue (res
)))
350 if (!(collection
= value
->AsCollection ()))
353 if (!(value
= collection
->GetValueAt (index
)))
356 if (!(lu
= value
->AsDependencyObject ()))
362 expression_found
= true;
365 while (inptr
< inend
&& (*inptr
!= '.' || tick_open
) && (!paren_open
|| *inptr
!= ')') && *inptr
!= '[') {
366 if (*inptr
== '\'') {
367 tick_open
= !tick_open
;
380 // we found a type name, now we need to find the property name
381 if ((inptr
- start
) == 11 && !g_ascii_strncasecmp (start
, "TextElement", 11)) {
382 // Some Beta versions of Blend had a bug where they would save the TextBlock
383 // properties as TextElement instead. Since Silverlight 1.0 works around this
384 // bug, we should too. Fixes http://silverlight.timovil.com and
385 // http://election.msn.com/podium08.aspx.
386 type
= Type::Find ("TextBlock");
388 const char *s
= inptr
;
389 if (*(inptr
-1) == '\'' && !tick_open
) {
392 name
= g_strndup (start
, s
- start
);
393 type
= lookup_type (lu
, name
);
399 while (inptr
< inend
&& (!paren_open
|| *inptr
!= ')') && (*inptr
!= '.' || tick_open
)) {
400 if (*inptr
== '\'') {
401 tick_open
= !tick_open
;
413 type
= Type::Find (lu
->GetObjectType ());
416 if ((*inptr
!= ')' && paren_open
) || !type
)
419 name
= g_strndup (start
, inptr
- start
);
420 if (!(res
= DependencyProperty::GetDependencyProperty (type
->GetKind (), name
))) {
425 if (!res
->IsAttached () && !lu
->Is (type
->GetKind ())) {
426 // We try to be gracefull here and do something smart...
427 if (!(res
= DependencyProperty::GetDependencyProperty (lu
->GetObjectType (), name
))) {
433 if (res
->IsAttached () && !paren_open
)