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"
22 #include "deployment.h"
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 ()
47 if (default_value
!= NULL
)
53 DependencyProperty::Dispose ()
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
)) {
66 default_value
= new Value (k
); /* null */
72 DependencyProperty::GetHashKey ()
75 hash_key
= g_ascii_strdown (name
, -1);
81 DependencyProperty::GetDependencyProperty (Type::Kind type
, const char *name
)
83 return GetDependencyProperty (Type::Find (Deployment::GetCurrent (), type
), name
);
87 DependencyProperty::GetDependencyProperty (Type
*type
, const char *name
)
89 return GetDependencyProperty (type
, name
, true);
93 DependencyProperty::GetDependencyPropertyFull (Type::Kind type
, const char *name
, bool inherits
)
95 DependencyProperty
*property
;
96 Type
*t
= Type::Find (Deployment::GetCurrent (), type
);
101 property
= GetDependencyProperty (t
, name
, inherits
);
103 if (property
== NULL
) {
105 property
= GetDependencyProperty (t
, name
, false);
106 if (property
== NULL
&& t
->HasParent ())
107 return GetDependencyPropertyFull (t
->GetParentType (), name
, inherits
);
114 DependencyProperty::GetDependencyPropertyFull (Type
*type
, const char *name
, bool inherits
)
116 DependencyProperty
*property
;
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
);
133 DependencyProperty::GetDependencyProperty (Type
*type
, const char *name
, bool inherits
)
135 DependencyProperty
*property
= NULL
;
140 property
= type
->LookupProperty (name
);
148 if (!type
->HasParent ())
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);
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 ();
192 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
193 default_value
= NULL
;
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
);
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 ();
208 if (default_value
&& default_value
->GetKind () == Type::INVALID
)
209 default_value
= NULL
;
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 ();
244 DependencyProperty::Validate (DependencyObject
*instance
, Value
*value
, MoonError
*error
)
246 return validator (instance
, this, value
, error
);
250 DependencyProperty::SetPropertyChangedCallback (PropertyChangeHandler changed_callback
)
252 this->changed_callback
= changed_callback
;
256 lookup_type (DependencyObject
*lu
, const char* name
)
258 Type
*t
= Type::Find (lu
->GetDeployment (), name
);
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
))
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
))
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
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
;
318 bool paren_open
= false;
319 bool tick_open
= false;
322 while (inptr
< inend
) {
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.");
337 tick_open
= !tick_open
;
343 // resolve the dependency property
345 DependencyObject
*new_lu
;
347 // make sure that we are getting what we expect
348 if (!(value
= lu
->GetValue (res
)))
351 if (!(new_lu
= value
->AsDependencyObject ()))
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();
363 cloned_value
= lu
->GetValue (res
);
364 g_hash_table_insert (promoted_values
, cloned_value
, cloned_value
);
370 expression_found
= false;
374 // Need to be a little more loving
378 index
= strtol (inptr
, &p
, 10);
379 if (*p
!= ']' || *(p
+ 1) != '.')
385 if (expression_found
) {
386 expression_found
= false;
387 if (!(value
= lu
->GetValue (res
)))
394 if (!(collection
= value
->AsCollection ()))
397 if (!(value
= collection
->GetValueAt (index
)))
400 if (!(lu
= value
->AsDependencyObject ()))
406 bool explicit_type
= false;
407 expression_found
= true;
410 while (inptr
< inend
&& (*inptr
!= '.' || tick_open
) && (!paren_open
|| *inptr
!= ')') && *inptr
!= '[') {
411 if (*inptr
== '\'') {
412 tick_open
= !tick_open
;
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;
434 const char *s
= inptr
;
435 if (*(inptr
-1) == '\'' && !tick_open
) {
438 name
= g_strndup (start
, s
- start
);
439 type
= lookup_type (lu
, name
);
440 explicit_type
= true;
442 type
= lu
->GetType ();
448 while (inptr
< inend
&& (!paren_open
|| *inptr
!= ')') && (*inptr
!= '.' || tick_open
)) {
449 if (*inptr
== '\'') {
450 tick_open
= !tick_open
;
462 type
= lu
->GetType ();
463 explicit_type
= false;
466 if ((*inptr
!= ')' && paren_open
) || !type
)
469 name
= g_strndup (start
, inptr
- start
);
470 if (!(res
= DependencyProperty::GetDependencyProperty (type
, name
)) && lu
)
471 res
= DependencyProperty::GetDependencyProperty (lu
->GetType (), name
);
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
))) {
486 if (res
->IsAttached () && explicit_type
&& !paren_open
)