1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dependencyobject.cpp:
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
18 #include "namescope.h"
20 #include "collection.h"
21 #include "dependencyobject.h"
22 #include "textblock.h"
23 #include "timemanager.h"
25 #include "uielement.h"
26 #include "animation.h"
27 #include "deployment.h"
45 lists
= new EventList
[size
];
46 for (int i
= 0; i
< size
; i
++) {
47 lists
[i
].current_token
= 1;
48 lists
[i
].emitting
= 0;
49 lists
[i
].event_list
= new List ();
55 for (int i
= 0; i
< size
; i
++) {
56 delete lists
[i
].event_list
;
67 #define OBJECT_TRACK(x,y) Track((x),(y))
69 #define OBJECT_TRACK(x,y)
72 EventObject::EventObject ()
74 Initialize (NULL
, Type::EVENTOBJECT
);
77 EventObject::EventObject (Type::Kind type
)
79 Initialize (NULL
, type
);
82 EventObject::EventObject (Type::Kind type
, bool multi_threaded_safe
)
84 Initialize (NULL
, type
);
85 if (multi_threaded_safe
)
86 flags
|= (gint32
) MultiThreadedSafe
;
89 EventObject::EventObject (Deployment
*deployment
)
91 Initialize (deployment
, Type::EVENTOBJECT
);
94 EventObject::EventObject (Deployment
*deployment
, Type::Kind type
)
96 Initialize (deployment
, type
);
99 static gint current_id
= 0;
102 EventObject::Initialize (Deployment
*depl
, Type::Kind type
)
105 depl
= Deployment::GetCurrent ();
109 if (deployment
!= NULL
&& this != deployment
)
112 flags
= g_atomic_int_exchange_and_add (¤t_id
, 1);
115 toggleNotifyListener
= NULL
;
118 switch (object_type
) {
120 Track ("Created", "<unknown>");
122 case Type::DEPLOYMENT
:
123 Track ("Created", "Deployment");
126 Track ("Created", depl
->GetTypes ()->Find (object_type
)->GetName ());
130 if (object_type
!= Type::DEPLOYMENT
)
131 Deployment::GetCurrent ()->TrackObjectCreated (this);
135 if (object_type
== Type::INVALID
)
136 g_warning ("EventObject::EventObject (): created object with type: INVALID.\n");
137 if (deployment
== NULL
)
138 g_warning ("EventObject::EventObject (): created object with a null deployment.\n");
142 EventObject::~EventObject()
145 if (object_type
!= Type::DEPLOYMENT
)
146 Deployment::GetCurrent ()->TrackObjectDestroyed (this);
147 Track ("Destroyed", "");
152 g_warning ("EventObject::~EventObject () #%i was deleted before its refcount reached 0 (current refcount: %i)\n", GetId (), refcount
);
158 // We can't unref the deployment in Dispose, it breaks
160 if (deployment
&& this != deployment
) {
161 deployment
->unref ();
166 static pthread_rwlock_t surface_lock
= PTHREAD_RWLOCK_INITIALIZER
;
169 EventObject::SetSurfaceLock ()
173 if ((result
= pthread_rwlock_wrlock (&surface_lock
)) != 0) {
174 printf ("EventObject::SetSurface (%p): Couldn't aquire write lock: %s\n", surface
, strerror (result
));
183 EventObject::SetSurface (Surface
*surface
)
185 if (!Surface::InMainThread () && surface
!= this->surface
) {
186 g_warning ("EventObject::SetSurface (): This method must not be called on any other than the main thread!\n");
188 if (debug_flags
& RUNTIME_DEBUG_DP
)
189 print_stack_trace ();
194 this->surface
= surface
;
198 EventObject::SetSurfaceUnlock ()
200 pthread_rwlock_unlock (&surface_lock
);
204 EventObject::AddTickCallSafe (TickCallHandler handler
, EventObject
*data
)
209 * This code assumes that the surface won't be destructed without setting the surface field on to NULL first.
210 * In other words: if we have a read lock here, the surface won't get destroyed, given that setting
211 * the surface field to NULL will block until we release the read lock.
214 if ((result
= pthread_rwlock_rdlock (&surface_lock
)) != 0) {
215 printf ("EventObject::AddTickCallSafe (): Couldn't aquire read lock: %s\n", strerror (result
));
219 AddTickCallInternal (handler
, data
);
221 pthread_rwlock_unlock (&surface_lock
);
225 EventObject::AddTickCall (TickCallHandler handler
, EventObject
*data
)
227 if (!Surface::InMainThread ()) {
228 g_warning ("EventObject::AddTickCall (): This method must not be called on any other than the main thread! Tick call won't be added.\n");
230 if (debug_flags
& RUNTIME_DEBUG_DP
)
231 print_stack_trace ();
236 AddTickCallInternal (handler
, data
);
240 EventObject::AddTickCallInternal (TickCallHandler handler
, EventObject
*data
)
243 TimeManager
*timemanager
;
245 surface
= GetSurface ();
248 surface
= GetDeployment ()->GetSurface ();
251 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no surface\n");
255 timemanager
= surface
->GetTimeManager ();
258 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no time manager\n");
262 timemanager
->AddTickCall (handler
, data
? data
: this);
266 EventObject::GetDeployment ()
268 if (deployment
== NULL
)
269 g_warning ("EventObject::GetDeployment () should not be reached with a null deployment");
272 if (deployment
!= Deployment::GetCurrent () && Deployment::GetCurrent () != NULL
) {
273 g_warning ("EventObject::GetDeployment () our deployment %p doesn't match Deployment::GetCurrent () %p", deployment
, Deployment::GetCurrent ());
274 // print_stack_trace ();
282 EventObject::SetCurrentDeployment (bool domain
)
284 if (deployment
!= NULL
)
285 Deployment::SetCurrent (deployment
, domain
);
289 EventObject::GetSurface ()
292 Deployment
*current_deployment
= Deployment::GetCurrent ();
293 Application
*current_application
;
294 current_application
= deployment
!= NULL
? deployment
->GetCurrentApplication () : (current_deployment
!= NULL
? current_deployment
->GetCurrentApplication () : NULL
);
296 if (deployment
!= NULL
&& deployment
!= current_deployment
) {
297 printf ("EventObject::GetSurface (): WARNING deployment: %p, Deployment::GetCurrent (): %p type: %s, id: %i\n", deployment
, current_deployment
, GetTypeName (), GET_OBJ_ID (this));
298 print_stack_trace ();
300 // current_application is null in the Surface ctor
301 if (!(current_application
== NULL
|| surface
== NULL
|| current_application
->GetSurface () == surface
))
302 printf ("EventObject::GetSurface (): assert failed: current application: %p, surface: %p, current application's surface: %p\n", current_application
, surface
, current_application
->GetSurface ());
305 return surface
; // When surface have been removed, return the Surface stored on the Deployment.
309 EventObject::Dispose ()
311 if (!IsDisposed ()) {
312 // Dispose can be called multiple times, but Emit only once. When DestroyedEvent was in the dtor,
313 // it could only ever be emitted once, don't change that behaviour.
314 Emit (DestroyedEvent
); // TODO: Rename to DisposedEvent
318 // Remove attached flag and set the disposed flag.
319 flags
= (Flags
) (flags
& ~Attached
);
320 flags
= (Flags
) (flags
| Disposed
);
324 EventObject::IsDisposed ()
326 return (flags
& Disposed
) != 0;
332 int v
= g_atomic_int_exchange_and_add (&refcount
, 1);
335 if (GetObjectType () != object_type
)
336 printf ("EventObject::ref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type
)->GetName ());
338 if (deployment
!= Deployment::GetCurrent ()) {
339 printf ("EventObject::ref (): the type '%s' whose id is %i was created on a deployment (%p) different from the current deployment (%p).\n", GetTypeName (), GET_OBJ_ID (this), deployment
, Deployment::GetCurrent ());
340 // print_stack_trace ();
345 // Here something really bad happened, this object is probably being reffed again because
346 // of some action in the destructor. There is no way to recover from this now, no matter what
347 // we do the code that called ref now will be accessing a deleted object later on, which may or
348 // may not crash. It might very well be an exploitable security problem. Anyways when unref is called, we
349 // have a second delete on the same object, which *will* crash. To make things easier and safer
350 // lets just abort right away.
354 // Due to our mixed usage of Dispose and dtor, currently there are valid cases of reffing
355 // an object with refcount = 0. Use a warning instead of error until the mixed usage is
357 g_warning ("Ref was called on an object with a refcount of 0.\n");
359 } else if (v
== 1 && toggleNotifyListener
) {
360 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
361 toggleNotifyListener
->Invoke (false);
364 OBJECT_TRACK ("Ref", GetTypeName ());
368 EventObject::unref ()
370 // we need to retrieve all instance fields into locals before decreasing the refcount
371 // TODO: do we need some sort of gcc foo (volatile variables, memory barries)
372 // to ensure that gcc does not optimize the fetches below away
373 ToggleNotifyListener
*toggle_listener
= this->toggleNotifyListener
;
375 Deployment
*depl
= this->deployment
? this->deployment
: Deployment::GetCurrent ();
376 const char *type_name
= (depl
== NULL
|| depl
->isDead
) ? NULL
: Type::Find (depl
, GetObjectType ())->GetName ();
380 if (GetObjectType () != object_type
)
381 printf ("EventObject::unref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type
)->GetName ());
384 if (!IsMultiThreadedSafe () && !Surface::InMainThread ()) {
389 int v
= g_atomic_int_exchange_and_add (&refcount
, -1) - 1;
391 // from now on we can't access any instance fields if v > 0
392 // since another thread might have unreffed and caused our destruction
394 if (v
== 0 && events
!= NULL
&& events
->emitting
) {
395 g_atomic_int_exchange_and_add (&refcount
, 1);
400 OBJECT_TRACK ("Unref", type_name
);
403 // here we *can* access instance fields, since we know that we haven't been
404 // desctructed already.
408 if ((flags
& Disposed
) == 0)
409 printf ("EventObject::unref (): the type '%s' (or any of its parent types) forgot to call its base class' Dispose method.\n", GetTypeName ());
412 // We need to check again if the refcount really is zero,
413 // the object might have resurrected in the Dispose.
414 // TODO: we should disallow resurrection, it's not thread-safe
415 // if we got resurrected and unreffed, we'd be deleted by now
416 // in which case we'll double free here.
417 v
= g_atomic_int_get (&refcount
);
421 } else if (v
== 1 && toggle_listener
) {
422 // we know that toggle_listener hasn't been freed, since if it exists, it will have a ref to us which would prevent our destruction
423 // note that the instance field might point to garbage after decreasing the refcount above, so we access the local variable we
424 // retrieved before decreasing the refcount.
425 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
426 toggle_listener
->Invoke (true);
431 g_warning ("EventObject::Unref (): NEGATIVE REFCOUNT id: %i v: %i refcount: %i", GET_OBJ_ID (this), v
, refcount
);
432 print_stack_trace ();
438 EventObject::AddToggleRefNotifier (ToggleNotifyHandler tr
)
440 if (toggleNotifyListener
)
444 toggleNotifyListener
= new ToggleNotifyListener (this, tr
);
448 EventObject::RemoveToggleRefNotifier ()
450 if (!toggleNotifyListener
)
453 delete toggleNotifyListener
;
454 toggleNotifyListener
= NULL
;
459 // Define the ID of the object you want to track
460 // Object creation, destruction and all ref/unrefs
461 // are logged to the console, with a stacktrace.
462 static bool object_id_fetched
= false;
463 static int object_id
= -1;
464 static const char *track_object_type
= NULL
;
465 static bool use_visi_output
= false;
466 static bool track_all
= false;
468 #define OBJECT_TRACK_ID (0)
470 GHashTable
* EventObject::objects_alive
= NULL
;
473 EventObject::Track (const char* done
, const char* typname
)
476 if (!object_id_fetched
) {
477 object_id_fetched
= true;
478 char *sval
= getenv ("MOONLIGHT_OBJECT_TRACK_ID");
480 object_id
= atoi (sval
);
481 track_object_type
= getenv ("MOONLIGHT_OBJECT_TRACK_TYPE");
482 use_visi_output
= (getenv ("MOONLIGHT_OBJECT_TRACK_VISI") != NULL
);
483 track_all
= (getenv ("MOONLIGHT_OBJECT_TRACK_ALL") != NULL
);
487 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done
, typname
, id
, refcount
, deployment
);
489 if (id
== object_id
|| (track_object_type
!= NULL
&& typname
!= NULL
&& strcmp (typname
, track_object_type
) == 0)) {
491 // load the stack trace before we print anything
492 // this way there's a lot smaller chance of
493 // ending up with other output between the first line (tracked object of type...)
494 // and the stack trace when using multiple threads.
495 if (!use_visi_output
)
496 st
= get_stack_trace ();
499 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done
, typname
, id
, refcount
, deployment
);
501 if (!use_visi_output
) {
504 print_reftrace (done
, typname
, refcount
, false);
511 EventObject::GetStackTrace (const char* prefix
)
513 return get_stack_trace_prefix (prefix
);
517 EventObject::PrintStackTrace ()
519 print_stack_trace ();
523 // event handlers for c++
524 class EventClosure
: public List::Node
{
526 EventClosure (EventHandler func
, gpointer data
, GDestroyNotify data_dtor
, int token
) {
529 this->data_dtor
= data_dtor
;
532 pending_removal
= false;
544 GDestroyNotify data_dtor
;
546 bool pending_removal
;
551 EventObject::AddHandler (const char *event_name
, EventHandler handler
, gpointer data
, GDestroyNotify data_dtor
)
553 int id
= GetType()->LookupEvent (event_name
);
556 g_warning ("adding handler to event '%s', which has not been registered\n", event_name
);
560 return AddHandler (id
, handler
, data
, data_dtor
);
564 EventObject::AddHandler (int event_id
, EventHandler handler
, gpointer data
, GDestroyNotify data_dtor
)
566 if (GetType()->GetEventCount() <= 0) {
567 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id
);
572 events
= new EventLists (GetType ()->GetEventCount ());
574 int token
= events
->lists
[event_id
].current_token
++;
576 events
->lists
[event_id
].event_list
->Append (new EventClosure (handler
, data
, data_dtor
, token
));
582 EventObject::AddXamlHandler (const char *event_name
, EventHandler handler
, gpointer data
, GDestroyNotify data_dtor
)
584 int id
= GetType ()->LookupEvent (event_name
);
587 g_warning ("adding xaml handler to event '%s', which has not been registered\n", event_name
);
591 return AddXamlHandler (id
, handler
, data
, data_dtor
);
595 EventObject::AddXamlHandler (int event_id
, EventHandler handler
, gpointer data
, GDestroyNotify data_dtor
)
597 if (GetType ()->GetEventCount () <= 0) {
598 g_warning ("adding xaml handler to event with id %d, which has not been registered\n", event_id
);
603 events
= new EventLists (GetType ()->GetEventCount ());
605 events
->lists
[event_id
].event_list
->Append (new EventClosure (handler
, data
, data_dtor
, 0));
611 EventObject::RemoveHandler (const char *event_name
, EventHandler handler
, gpointer data
)
613 int id
= GetType()->LookupEvent (event_name
);
616 g_warning ("removing handler for event '%s', which has not been registered\n", event_name
);
620 RemoveHandler (id
, handler
, data
);
624 EventObject::RemoveHandler (int event_id
, EventHandler handler
, gpointer data
)
626 if (GetType()->GetEventCount() <= 0) {
627 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id
);
634 EventClosure
*closure
= (EventClosure
*) events
->lists
[event_id
].event_list
->First ();
636 if (closure
->func
== handler
&& closure
->data
== data
) {
637 if (events
->lists
[event_id
].emitting
> 0) {
638 closure
->pending_removal
= true;
640 events
->lists
[event_id
].event_list
->Remove (closure
);
645 closure
= (EventClosure
*) closure
->next
;
650 EventObject::RemoveHandler (int event_id
, int token
)
652 if (GetType()->GetEventCount() <= 0) {
653 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id
);
660 EventClosure
*closure
= (EventClosure
*) events
->lists
[event_id
].event_list
->First ();
662 if (closure
->token
== token
) {
663 if (events
->lists
[event_id
].emitting
> 0) {
664 closure
->pending_removal
= true;
666 events
->lists
[event_id
].event_list
->Remove (closure
);
671 closure
= (EventClosure
*) closure
->next
;
676 EventObject::RemoveAllHandlers (gpointer data
)
681 int count
= GetType ()->GetEventCount ();
683 for (int i
= 0; i
< count
- 1; i
++) {
684 EventClosure
*closure
= (EventClosure
*) events
->lists
[i
].event_list
->First ();
686 if (closure
->data
== data
) {
687 if (events
->lists
[i
].emitting
> 0) {
688 closure
->pending_removal
= true;
690 events
->lists
[i
].event_list
->Remove (closure
);
695 closure
= (EventClosure
*) closure
->next
;
701 EventObject::RemoveMatchingHandlers (int event_id
, bool (*predicate
)(EventHandler cb_handler
, gpointer cb_data
, gpointer data
), gpointer closure
)
703 if (GetType()->GetEventCount() <= 0) {
704 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id
);
711 EventClosure
*c
= (EventClosure
*) events
->lists
[event_id
].event_list
->First ();
713 if (predicate (c
->func
, c
->data
, closure
)) {
714 if (events
->lists
[event_id
].emitting
> 0) {
715 c
->pending_removal
= true;
717 events
->lists
[event_id
].event_list
->Remove (c
);
722 c
= (EventClosure
*) c
->next
;
727 EventObject::GetEventGeneration (int event_id
)
729 if (GetType()->GetEventCount() <= 0) {
730 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id
);
735 events
= new EventLists (GetType ()->GetEventCount ());
737 return events
->lists
[event_id
].current_token
;
741 EventObject::Emit (char *event_name
, EventArgs
*calldata
, bool only_unemitted
, int starting_generation
)
743 Deployment
*deployment
= GetDeployment ();
744 if (deployment
&& deployment
->isDead
)
747 int id
= GetType()->LookupEvent (event_name
);
750 g_warning ("trying to emit event '%s', which has not been registered\n", event_name
);
754 return Emit (id
, calldata
, only_unemitted
);
765 EventObject::EmitCallback (gpointer d
)
767 EmitData
*data
= (EmitData
*) d
;
768 data
->sender
->SetCurrentDeployment ();
769 data
->sender
->Emit (data
->event_id
, data
->calldata
, data
->only_unemitted
);
770 data
->sender
->unref ();
773 Deployment::SetCurrent (NULL
);
779 EventObject::Emit (int event_id
, EventArgs
*calldata
, bool only_unemitted
, int starting_generation
)
784 Deployment
*deployment
= GetDeployment ();
785 if (deployment
&& deployment
->isDead
)
788 if (GetType()->GetEventCount() <= 0 || event_id
>= GetType()->GetEventCount()) {
789 g_warning ("trying to emit event with id %d, which has not been registered\n", event_id
);
795 if (events
== NULL
|| events
->lists
[event_id
].event_list
->IsEmpty ()) {
801 if (!Surface::InMainThread ()) {
802 Surface
*surface
= deployment
? deployment
->GetSurface () : NULL
;
804 if (surface
== NULL
) {
805 printf ("EventObject::Emit (): could not emit event, the deployment %p does not have a surface.\n", deployment
);
809 EmitData
*data
= new EmitData ();
811 data
->sender
->ref ();
812 data
->event_id
= event_id
;
813 data
->calldata
= calldata
;
814 data
->only_unemitted
= only_unemitted
;
815 surface
->GetTimeManager ()->AddTimeout (MOON_PRIORITY_HIGH
, 1, EmitCallback
, data
);
819 EmitContext
* ctx
= StartEmit (event_id
);
821 DoEmit (event_id
, ctx
, calldata
, only_unemitted
, starting_generation
);
826 FinishEmit (event_id
, ctx
);
833 EventClosure
**closures
;
847 EventObject::StartEmit (int event_id
)
849 EmitContext
*ctx
= new EmitContext();
850 EventClosure
*closure
;
852 if (GetType()->GetEventCount() <= 0 || event_id
>= GetType()->GetEventCount()) {
853 g_warning ("trying to start emit with id %d, which has not been registered\n", event_id
);
857 if (events
== NULL
|| events
->lists
[event_id
].event_list
->IsEmpty ()) {
862 events
->lists
[event_id
].emitting
++;
864 ctx
->length
= events
->lists
[event_id
].event_list
->Length();
865 ctx
->closures
= g_new (EventClosure
*, ctx
->length
);
867 /* make a copy of the event list to use for emitting */
868 closure
= (EventClosure
*) events
->lists
[event_id
].event_list
->First ();
869 for (int i
= 0; closure
!= NULL
; i
++) {
870 ctx
->closures
[i
] = closure
;
871 closure
= (EventClosure
*) closure
->next
;
878 EventObject::DoEmit (int event_id
, EmitContext
*ctx
, EventArgs
*calldata
, bool only_unemitted
, int starting_generation
)
880 EventClosure
*xaml_closure
= NULL
;
882 /* emit the events using the copied list */
883 for (int i
= 0; i
< ctx
->length
; i
++) {
884 EventClosure
*closure
= ctx
->closures
[i
];
886 #if FORCE_XAML_HANDLERS_LAST
887 // i thought this was required for drt 234, but it
888 // doesn't seem to be helping it, and it's breaking
889 // other p1 tests. - toshok
890 if (closure
->token
== 0) {
891 xaml_closure
= closure
;
896 if (closure
&& closure
->func
897 && (!only_unemitted
|| closure
->emit_count
== 0)
898 && (starting_generation
== -1 || closure
->token
< starting_generation
)) {
899 closure
->func (this, calldata
, closure
->data
);
900 closure
->emit_count
++;
904 if (xaml_closure
&& xaml_closure
->func
905 && (!only_unemitted
|| xaml_closure
->emit_count
== 0)) {
906 xaml_closure
->func (this, calldata
, xaml_closure
->data
);
907 xaml_closure
->emit_count
++;
910 return ctx
->length
> 0;
914 EventObject::FinishEmit (int event_id
, EmitContext
*ctx
)
916 if (GetType()->GetEventCount() <= 0 || event_id
>= GetType()->GetEventCount()) {
917 g_warning ("trying to finish emit with id %d, which has not been registered\n", event_id
);
921 if (ctx
->length
== 0)
924 events
->lists
[event_id
].emitting
--;
927 if (events
->lists
[event_id
].emitting
== 0) {
928 // Remove closures which are waiting for removal
929 EventClosure
*closure
= (EventClosure
*) events
->lists
[event_id
].event_list
->First ();
930 while (closure
!= NULL
) {
931 EventClosure
*next
= (EventClosure
*) closure
->next
;
932 if (closure
->pending_removal
)
933 events
->lists
[event_id
].event_list
->Remove (closure
);
943 EventObject::unref_delayed ()
947 OBJECT_TRACK ("DelayedUnref", GetTypeName ());
949 // access deployment as long as we have it (until Dispose has been called),
950 // after that access the static deployment.
951 depl
= deployment
? deployment
: Deployment::GetCurrent ();
952 depl
->UnrefDelayed (this);
957 virtual bool Matches (PropertyChangedEventArgs
*args
) = 0;
958 virtual void Invoke (DependencyObject
*sender
, PropertyChangedEventArgs
*args
, MoonError
*error
) = 0;
960 virtual gpointer
GetListener () = 0;
961 virtual gpointer
GetProperty () = 0;
964 class WildcardListener
: public Listener
{
966 WildcardListener (DependencyObject
*obj
, DependencyProperty
*prop
)
972 virtual bool Matches (PropertyChangedEventArgs
*args
) { return true; }
973 virtual void Invoke (DependencyObject
*sender
, PropertyChangedEventArgs
*args
, MoonError
*error
)
975 // FIXME we ignore error here.
976 obj
->OnSubPropertyChanged (prop
, sender
, args
);
979 virtual gpointer
GetListener ()
984 virtual gpointer
GetProperty ()
991 DependencyObject
*obj
;
992 DependencyProperty
*prop
;
995 class CallbackListener
: public Listener
{
997 CallbackListener (DependencyProperty
*prop
, PropertyChangeHandler cb
, gpointer closure
)
1001 this->closure
= closure
;
1004 virtual bool Matches (PropertyChangedEventArgs
*args
)
1006 return prop
== args
->GetProperty ();
1009 virtual void Invoke (DependencyObject
*sender
, PropertyChangedEventArgs
*args
, MoonError
*error
)
1011 cb (sender
, args
, error
, closure
);
1014 virtual gpointer
GetListener ()
1016 return (gpointer
)cb
;
1019 virtual gpointer
GetProperty ()
1025 PropertyChangeHandler cb
;
1026 DependencyProperty
*prop
;
1031 // Registers @listener as a listener on changes to @child_property of this DO.
1034 DependencyObject::AddPropertyChangeListener (DependencyObject
*listener
, DependencyProperty
*child_property
)
1036 listener_list
= g_slist_append (listener_list
, new WildcardListener (listener
, child_property
));
1040 DependencyObject::RemoveListener (gpointer listener
, DependencyProperty
*child_property
)
1043 for (GSList
*l
= listener_list
; l
; l
= next
) {
1045 Listener
*listen
= (Listener
*) l
->data
;
1047 if ((listen
->GetListener() == listener
)
1048 && (child_property
== NULL
|| listen
->GetProperty() == child_property
)) {
1049 listener_list
= g_slist_delete_link (listener_list
, l
);
1056 // Unregisters @container as a listener on changes to @child_property of this DO.
1059 DependencyObject::RemovePropertyChangeListener (DependencyObject
*listener
, DependencyProperty
*child_property
)
1061 RemoveListener (listener
, child_property
);
1065 DependencyObject::AddPropertyChangeHandler (DependencyProperty
*property
, PropertyChangeHandler cb
, gpointer closure
)
1067 listener_list
= g_slist_append (listener_list
, new CallbackListener (property
, cb
, closure
));
1071 DependencyObject::RemovePropertyChangeHandler (DependencyProperty
*property
, PropertyChangeHandler cb
)
1073 RemoveListener ((gpointer
)cb
, property
);
1077 unregister_depobj_values (gpointer key
,
1081 DependencyObject
*this_obj
= (DependencyObject
*)user_data
;
1082 //DependencyProperty *prop = (DependencyProperty*)key;
1083 Value
*v
= (Value
*)value
;
1085 if (v
!= NULL
&& v
->Is (Type::DEPENDENCY_OBJECT
) && v
->AsDependencyObject() != NULL
) {
1086 //printf ("unregistering from property %s\n", prop->name);
1087 DependencyObject
*obj
= v
->AsDependencyObject ();
1088 obj
->RemovePropertyChangeListener (this_obj
);
1089 obj
->SetParent (NULL
, NULL
);
1094 DependencyObject::RemoveAllListeners ()
1096 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1099 g_hash_table_foreach (autocreate
->auto_values
, unregister_depobj_values
, this);
1101 g_hash_table_foreach (local_values
, unregister_depobj_values
, this);
1104 static bool listeners_notified
;
1107 DependencyObject::NotifyListenersOfPropertyChange (PropertyChangedEventArgs
*args
, MoonError
*error
)
1109 g_return_if_fail (args
);
1111 listeners_notified
= true;
1113 for (GSList
*l
= listener_list
; l
!= NULL
; l
= l
->next
){
1114 Listener
*listener
= (Listener
*)l
->data
;
1116 if (listener
->Matches (args
))
1117 listener
->Invoke (this, args
, error
);
1118 if (error
&& error
->number
)
1124 DependencyObject::NotifyListenersOfPropertyChange (int id
, MoonError
*error
)
1128 NotifyListenersOfPropertyChange (GetDeployment ()->GetTypes ()->GetProperty (id
), error
);
1132 DependencyObject::NotifyListenersOfPropertyChange (DependencyProperty
*subproperty
, MoonError
*error
)
1134 // XXX I really think this method should go away. we only use it in
1135 // a couple of places, and it abuses things.
1137 Value
*new_value
= subproperty
? GetValue (subproperty
) : NULL
;
1139 PropertyChangedEventArgs
args (subproperty
, subproperty
->GetId (), NULL
, new_value
);
1141 NotifyListenersOfPropertyChange (&args
, error
);
1145 DependencyObject::IsValueValid (DependencyProperty
* property
, Value
* value
, MoonError
*error
)
1147 if (property
== NULL
) {
1148 MoonError::FillIn (error
, MoonError::ARGUMENT_NULL
, 1001,
1149 "NULL property passed to IsValueValid");
1153 if (value
!= NULL
) {
1154 if (value
->Is (Type::EVENTOBJECT
) && !value
->AsEventObject ()) {
1155 // if it's a null DependencyObject, it doesn't matter what type it is
1159 if (value
->Is (Type::MANAGED
)) {
1160 // This is a big hack, we do no type-checking if we try to set a managed type.
1161 // Given that for the moment we might not have the surface available, we can't
1162 // do any type checks since we can't access types registered on the surface.
1166 if (!Type::IsAssignableFrom (property
->GetPropertyType(), value
->GetKind())) {
1167 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1168 g_strdup_printf ("DependencyObject::SetValue, value cannot be assigned to the "
1169 "property %s::%s (property has type '%s', value has type '%s')",
1170 GetTypeName (), property
->GetName(), Type::Find (property
->GetPropertyType())->GetName (),
1171 Type::Find (value
->GetKind ())->GetName ()));
1175 // In 2.0, property->GetPropertyType() can return
1176 // something greater than Type::LASTTYPE. Only check
1177 // built-in types for null Types registered on the
1178 // managed side has their own check there.
1179 if (!CanPropertyBeSetToNull (property
)) {
1180 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1181 g_strdup_printf ("Can not set a non-nullable scalar type to NULL (property: %s)",
1182 property
->GetName()));
1191 DependencyObject::CanPropertyBeSetToNull (DependencyProperty
* property
)
1193 if (property
->GetPropertyType () > Type::LASTTYPE
)
1196 if (Type::IsSubclassOf (property
->GetPropertyType(), Type::DEPENDENCY_OBJECT
))
1199 if (property
->IsNullable ())
1202 if (Type::IsSubclassOf (property
->GetPropertyType (), Type::STRING
))
1209 DependencyObject::SetValue (int id
, Value
*value
)
1213 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id
), value
);
1217 DependencyObject::SetValue (int id
, Value value
)
1221 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id
), value
);
1225 DependencyObject::SetValue (DependencyProperty
*property
, Value
*value
)
1228 return SetValueWithError (property
, value
, &err
);
1232 DependencyObject::SetValue (DependencyProperty
*property
, Value value
)
1235 return SetValueWithError (property
, &value
, &err
);
1239 DependencyObject::SetValueWithError (DependencyProperty
* property
, Value value
, MoonError
*error
)
1241 return SetValueWithError (property
, &value
, error
);
1245 DependencyObject::SetValueWithErrorImpl (DependencyProperty
*property
, Value
*value
, MoonError
*error
)
1248 MoonError::FillIn (error
, MoonError::UNAUTHORIZED_ACCESS
, g_strdup_printf ("Cannot set value for property '%s' on frozen DependencyObject '%s'", property
->GetName(), GetTypeName()));
1252 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1253 Value
*current_value
;
1256 if (!(current_value
= ReadLocalValue (property
)))
1257 if (property
->IsAutoCreated ())
1258 current_value
= autocreate
->ReadLocalValue (property
);
1260 if (current_value
!= NULL
&& value
!= NULL
) {
1261 equal
= !property
->AlwaysChange() && (*current_value
== *value
);
1263 equal
= (current_value
== NULL
) && (value
== NULL
);
1269 // remove the old value
1270 g_hash_table_remove (local_values
, property
);
1272 if (property
->IsAutoCreated ())
1273 autocreate
->ClearValue (property
);
1275 if (value
&& (!property
->IsAutoCreated () || !value
->Is (Type::DEPENDENCY_OBJECT
) || value
->AsDependencyObject () != NULL
))
1276 new_value
= new Value (*value
);
1280 // replace it with the new value
1282 g_hash_table_insert (local_values
, property
, new_value
);
1284 ProviderValueChanged (PropertyPrecedence_LocalValue
, property
, current_value
, new_value
, true, error
);
1287 delete current_value
;
1294 DependencyObject::SetValueWithError (DependencyProperty
*property
, Value
*value
, MoonError
*error
)
1296 if (!IsValueValid (property
, value
, error
))
1298 if (!property
->Validate (this, value
, error
))
1301 return SetValueWithErrorImpl (property
, value
, error
);
1304 struct RegisterNamesClosure
{
1310 register_depobj_names (gpointer key
,
1314 RegisterNamesClosure
*closure
= (RegisterNamesClosure
*)user_data
;
1315 if (closure
->error
->number
)
1318 Value
*v
= (Value
*)value
;
1320 if (v
!= NULL
&& v
->Is (Type::DEPENDENCY_OBJECT
) && v
->AsDependencyObject() != NULL
) {
1321 DependencyObject
*obj
= v
->AsDependencyObject ();
1322 obj
->RegisterAllNamesRootedAt (closure
->to_ns
, closure
->error
);
1327 DependencyObject::RegisterAllNamesRootedAt (NameScope
*to_ns
, MoonError
*error
)
1329 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1334 bool merge_namescope
= false;
1335 bool register_name
= false;
1336 bool recurse
= false;
1338 NameScope
*this_ns
= NameScope::GetNameScope(this);
1340 if (this_ns
&& this_ns
->GetTemporary()) {
1341 merge_namescope
= true;
1343 else if (!this_ns
) {
1345 register_name
= true;
1347 else if (IsHydratedFromXaml ()) {
1348 register_name
= true;
1352 if (merge_namescope
) {
1353 to_ns
->MergeTemporaryScope (this_ns
, error
);
1354 ClearValue (NameScope::NameScopeProperty
, false);
1357 if (register_name
) {
1358 const char *n
= GetName();
1361 DependencyObject
*o
= to_ns
->FindName (n
);
1364 MoonError::FillIn (error
, MoonError::ARGUMENT
, 2028,
1365 g_strdup_printf ("The name already exists in the tree: %s.",
1371 to_ns
->RegisterName (n
, this);
1377 RegisterNamesClosure closure
;
1378 closure
.to_ns
= to_ns
;
1379 closure
.error
= error
;
1382 g_hash_table_foreach (autocreate
->auto_values
, register_depobj_names
, &closure
);
1384 g_hash_table_foreach (local_values
, register_depobj_names
, &closure
);
1389 unregister_depobj_names (gpointer key
,
1393 NameScope
*from_ns
= (NameScope
*)user_data
;
1394 Value
*v
= (Value
*)value
;
1395 DependencyProperty
*property
= (DependencyProperty
*)key
;
1397 if (property
->GetId() != UIElement::TagProperty
&& v
!= NULL
&& v
->Is (Type::DEPENDENCY_OBJECT
) && v
->AsDependencyObject() != NULL
) {
1398 DependencyObject
*obj
= v
->AsDependencyObject ();
1399 obj
->UnregisterAllNamesRootedAt (from_ns
);
1404 DependencyObject::UnregisterAllNamesRootedAt (NameScope
*from_ns
)
1406 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1407 NameScope
*this_ns
= NameScope::GetNameScope(this);
1408 if (this_ns
&& !this_ns
->GetTemporary())
1411 const char *n
= GetName();
1413 if (n
&& strlen (n
) > 0)
1414 from_ns
->UnregisterName (n
);
1417 g_hash_table_foreach (autocreate
->auto_values
, unregister_depobj_names
, from_ns
);
1419 g_hash_table_foreach (local_values
, unregister_depobj_names
, from_ns
);
1423 DependencyObject::SetName (const char* name
, NameScope
*scope
)
1425 DependencyProperty
*property
= GetDeployment ()->GetTypes ()->GetProperty (NameProperty
);
1427 if (scope
->FindName (name
))
1430 Value
*new_value
= new Value (name
);
1431 SetValue (property
, new_value
);
1432 scope
->RegisterName (name
, this);
1438 DependencyObject::ReadLocalValue (int id
)
1442 return ReadLocalValue (GetDeployment ()->GetTypes ()->GetProperty (id
));
1446 DependencyObject::ReadLocalValue (DependencyProperty
*property
)
1448 return (Value
*) g_hash_table_lookup (local_values
, property
);
1452 DependencyObject::ReadLocalValueWithError (DependencyProperty
*property
, MoonError
*error
)
1454 if (!HasProperty (Type::INVALID
, property
, true)) {
1455 Type
*pt
= Type::Find (property
->GetOwnerType ());
1456 MoonError::FillIn (error
, MoonError::EXCEPTION
, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt
? pt
->GetName () : "<unknown>", property
->GetName (), GetTypeName ()));
1459 return ReadLocalValue (property
);
1463 DependencyObject::GetValueWithError (Type::Kind whatami
, DependencyProperty
*property
, MoonError
*error
)
1465 if (!HasProperty (whatami
, property
, true)) {
1466 Type
*pt
= Type::Find (property
->GetOwnerType ());
1467 MoonError::FillIn (error
, MoonError::EXCEPTION
, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt
? pt
->GetName () : "<unknown>", property
->GetName (), GetTypeName ()));
1470 return GetValue (property
);
1474 DependencyObject::GetValue (int id
)
1478 return GetValue (GetDeployment ()->GetTypes ()->GetProperty (id
));
1482 DependencyObject::GetValue (DependencyProperty
*property
)
1484 return GetValue (property
, PropertyPrecedence_Highest
);
1488 DependencyObject::GetValue (DependencyProperty
*property
, PropertyPrecedence startingAtPrecedence
)
1490 for (int i
= startingAtPrecedence
; i
< PropertyPrecedence_Count
; i
++) {
1493 Value
*value
= providers
[i
]->GetPropertyValue (property
);
1494 if (value
) return value
;
1500 DependencyObject::GetValueSkippingPrecedence (DependencyProperty
*property
, PropertyPrecedence toSkip
)
1502 for (int i
= 0; i
< PropertyPrecedence_Count
; i
++) {
1507 Value
*value
= providers
[i
]->GetPropertyValue (property
);
1508 if (value
) return value
;
1514 DependencyObject::GetValueNoDefault (int id
)
1518 return GetValueNoDefault (GetDeployment ()->GetTypes ()->GetProperty (id
));
1522 DependencyObject::GetValueNoDefault (DependencyProperty
*property
)
1524 Value
*value
= NULL
;
1526 for (int i
= 0; i
< PropertyPrecedence_DefaultValue
; i
++) {
1529 value
= providers
[i
]->GetPropertyValue (property
);
1532 return value
&& !value
->GetIsNull () ? value
: NULL
;
1536 DependencyObject::GetValueNoDefaultWithError (DependencyProperty
*property
, MoonError
*error
)
1538 if (!HasProperty (Type::INVALID
, property
, true)) {
1539 Type
*pt
= Type::Find (property
->GetOwnerType ());
1540 MoonError::FillIn (error
, MoonError::EXCEPTION
, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt
? pt
->GetName () : "<unknown>", property
->GetName (), GetTypeName ()));
1543 return GetValueNoDefault (property
);
1547 DependencyObject::ProviderValueChanged (PropertyPrecedence providerPrecedence
,
1548 DependencyProperty
*property
,
1549 Value
*old_provider_value
, Value
*new_provider_value
,
1550 bool notify_listeners
, MoonError
*error
)
1554 // if they're both NULL, get out of here.
1555 if (!old_provider_value
&& !new_provider_value
)
1558 // first we look for a value higher in precedence for this property
1559 for (p
= providerPrecedence
- 1; p
>= PropertyPrecedence_Highest
; p
--) {
1560 if (providers
[p
] && providers
[p
]->GetPropertyValue (property
)) {
1561 // a provider higher in precedence already has
1562 // a value for this property, so the one
1563 // that's changing isn't visible anyway.
1571 if (!old_provider_value
|| !new_provider_value
) {
1572 Value
*lower_priority_value
= GetValue (property
, (PropertyPrecedence
)(providerPrecedence
+ 1));
1574 if (old_provider_value
== NULL
) {
1575 // we're changing from the old value (from a lower
1576 // priority provider) to @new_provider_value.
1577 old_value
= lower_priority_value
;
1578 new_value
= new_provider_value
;
1580 else if (new_provider_value
== NULL
) {
1581 // we're changing from @old_provider_value to whatever the
1582 // value lower on the priority list is.
1583 old_value
= old_provider_value
;
1584 new_value
= lower_priority_value
;
1588 old_value
= old_provider_value
;
1589 new_value
= new_provider_value
;
1594 if (old_value
!= NULL
&& new_value
!= NULL
) {
1595 equal
= !property
->AlwaysChange() && (*old_value
== *new_value
);
1601 DependencyObject
*old_as_dep
= NULL
;
1602 DependencyObject
*new_as_dep
= NULL
;
1604 // XXX this flag should be part of the DP metadata.
1605 // we also need to audit other "typeof (object)" DP's
1606 // to make sure they set parent when they should (and
1607 // don't when they shouldn't.)
1608 bool setsParent
= !property
->IsCustom ();
1610 if (old_value
&& old_value
->Is (Type::DEPENDENCY_OBJECT
))
1611 old_as_dep
= old_value
->AsDependencyObject ();
1612 if (new_value
&& new_value
->Is (Type::DEPENDENCY_OBJECT
))
1613 new_as_dep
= new_value
->AsDependencyObject ();
1615 if (old_as_dep
&& setsParent
) {
1616 old_as_dep
->SetSurface (NULL
);
1619 old_as_dep
->SetParent (NULL
, NULL
);
1621 // remove ourselves as a target
1622 old_as_dep
->RemoveTarget (this);
1624 // unregister from the existing value
1625 old_as_dep
->RemovePropertyChangeListener (this, property
);
1627 if (old_as_dep
->Is(Type::COLLECTION
)) {
1628 old_as_dep
->RemoveHandler (Collection::ChangedEvent
, collection_changed
, this);
1629 old_as_dep
->RemoveHandler (Collection::ItemChangedEvent
, collection_item_changed
, this);
1633 if (new_as_dep
&& setsParent
) {
1634 new_as_dep
->SetSurface (GetSurface ());
1636 new_as_dep
->SetParent (this, error
);
1640 new_as_dep
->SetResourceBase (GetResourceBase());
1642 if (new_as_dep
->Is(Type::COLLECTION
)) {
1643 new_as_dep
->AddHandler (Collection::ChangedEvent
, collection_changed
, this);
1644 new_as_dep
->AddHandler (Collection::ItemChangedEvent
, collection_item_changed
, this);
1647 // listen for property changes on the new object
1648 new_as_dep
->AddPropertyChangeListener (this, property
);
1650 // add ourselves as a target
1651 new_as_dep
->AddTarget (this);
1654 PropertyChangedEventArgs
args (property
, property
->GetId (), old_value
, new_value
);
1656 // we need to make this optional, as doing it for NameScope
1657 // merging is killing performance (and noone should ever care
1658 // about that property changing)
1659 if (notify_listeners
) {
1660 listeners_notified
= false;
1662 OnPropertyChanged (&args
, error
);
1664 if (!listeners_notified
) {
1665 g_warning ("setting property %s::%s on object of type %s didn't result in listeners being notified",
1666 Type::Find(property
->GetOwnerType())->GetName (), property
->GetName(), GetTypeName ());
1668 g_warning ("the error was: %s", error
->message
);
1671 if (property
&& property
->GetChangedCallback () != NULL
) {
1672 PropertyChangeHandler callback
= property
->GetChangedCallback ();
1673 callback (this, &args
, error
, NULL
);
1680 DependencyObject::ClearValue (int id
, bool notify_listeners
)
1684 ClearValue (GetDeployment ()->GetTypes ()->GetProperty (id
), notify_listeners
);
1688 DependencyObject::ClearValue (DependencyProperty
*property
, bool notify_listeners
)
1690 ClearValue(property
, notify_listeners
, NULL
);
1694 DependencyObject::ClearValue (DependencyProperty
*property
, bool notify_listeners
, MoonError
*error
)
1696 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1697 Value
*old_local_value
;
1699 if (!(old_local_value
= ReadLocalValue (property
)))
1700 if (property
->IsAutoCreated ())
1701 old_local_value
= autocreate
->ReadLocalValue (property
);
1703 if (old_local_value
== NULL
) {
1704 // there wasn't a local value set. don't do anything
1708 // detach from the existing value
1709 if (old_local_value
->Is (Type::DEPENDENCY_OBJECT
)) {
1710 DependencyObject
*dob
= old_local_value
->AsDependencyObject();
1714 dob
->SetParent (NULL
, NULL
);
1716 // unregister from the existing value
1717 dob
->RemovePropertyChangeListener (this, property
);
1718 dob
->SetSurface (NULL
);
1719 if (dob
->Is(Type::COLLECTION
)) {
1720 dob
->RemoveHandler (Collection::ChangedEvent
, collection_changed
, this);
1721 dob
->RemoveHandler (Collection::ItemChangedEvent
, collection_item_changed
, this);
1726 g_hash_table_remove (local_values
, property
);
1728 if (property
->IsAutoCreated ())
1729 autocreate
->ClearValue (property
);
1731 // this is... yeah, it's disgusting
1732 for (int p
= PropertyPrecedence_LocalValue
+ 1; p
< PropertyPrecedence_Count
; p
++) {
1734 providers
[p
]->RecomputePropertyValue (property
);
1737 ProviderValueChanged (PropertyPrecedence_LocalValue
, property
, old_local_value
, NULL
, notify_listeners
, error
);
1739 delete old_local_value
;
1743 DependencyObject::dispose_value (gpointer key
, gpointer value
, gpointer data
)
1745 DependencyObject
*_this
= (DependencyObject
*)data
;
1747 Value
*v
= (Value
*) value
;
1752 // detach from the existing value
1753 if (v
->Is (Type::DEPENDENCY_OBJECT
)){
1754 DependencyObject
*dob
= v
->AsDependencyObject();
1757 if (_this
== dob
->GetParent()) {
1758 // unset its logical parent
1759 dob
->SetParent (NULL
, NULL
);
1762 // unregister from the existing value
1763 dob
->RemovePropertyChangeListener ((DependencyObject
*)data
, NULL
);
1765 if (dob
->Is(Type::COLLECTION
)) {
1766 dob
->RemoveHandler (Collection::ChangedEvent
, collection_changed
, _this
);
1767 dob
->RemoveHandler (Collection::ItemChangedEvent
, collection_item_changed
, _this
);
1772 delete (Value
*) value
;
1778 DependencyObject::collection_changed (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
1780 DependencyObject
*obj
= (DependencyObject
*)closure
;
1781 obj
->OnCollectionChanged ((Collection
*)sender
, (CollectionChangedEventArgs
*)args
);
1785 DependencyObject::collection_item_changed (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
1787 DependencyObject
*obj
= (DependencyObject
*)closure
;
1788 CollectionItemChangedEventArgs
* itemArgs
= (CollectionItemChangedEventArgs
*)args
;
1790 PropertyChangedEventArgs
propChangedArgs (itemArgs
->GetProperty(),
1791 itemArgs
->GetProperty()->GetId (),
1792 itemArgs
->GetOldValue(),
1793 itemArgs
->GetNewValue());
1795 obj
->OnCollectionItemChanged ((Collection
*)sender
,
1796 itemArgs
->GetCollectionItem(),
1800 DependencyObject::DependencyObject ()
1801 : EventObject (Type::DEPENDENCY_OBJECT
)
1806 DependencyObject::DependencyObject (Deployment
*deployment
, Type::Kind object_type
)
1807 : EventObject (deployment
, object_type
)
1812 DependencyObject::DependencyObject (Type::Kind object_type
)
1813 : EventObject (object_type
)
1819 DependencyObject::Initialize ()
1821 providers
= new PropertyValueProvider
*[PropertyPrecedence_Count
];
1823 providers
[PropertyPrecedence_LocalValue
] = new LocalPropertyValueProvider (this, PropertyPrecedence_LocalValue
);
1824 providers
[PropertyPrecedence_DynamicValue
] = NULL
; // subclasses will set this if they need it.
1826 providers
[PropertyPrecedence_LocalStyle
] = NULL
; // this is a frameworkelement specific thing
1827 providers
[PropertyPrecedence_DefaultStyle
] = NULL
; // this is a frameworkelement specific thing
1829 providers
[PropertyPrecedence_Inherited
] = new InheritedPropertyValueProvider (this, PropertyPrecedence_Inherited
);
1830 providers
[PropertyPrecedence_DefaultValue
] = new DefaultValuePropertyValueProvider (this, PropertyPrecedence_DefaultValue
);
1831 providers
[PropertyPrecedence_AutoCreate
] = new AutoCreatePropertyValueProvider (this, PropertyPrecedence_AutoCreate
);
1833 local_values
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1834 listener_list
= NULL
;
1836 is_hydrated
= false;
1838 resource_base
= NULL
;
1839 storage_hash
= NULL
; // Create it on first usage request
1843 DependencyObject::Freeze()
1848 struct CloneClosure
{
1850 DependencyObject
*old_do
;
1851 DependencyObject
*new_do
;
1855 DependencyObject::clone_local_value (DependencyProperty
*key
, Value
*value
, gpointer data
)
1857 CloneClosure
*closure
= (CloneClosure
*)data
;
1859 // don't clone the name property, or we end up with nasty
1860 // duplicate name errors.
1861 if (key
->GetId() == DependencyObject::NameProperty
)
1864 Value
*cv
= Value::Clone (value
, closure
->types
);
1866 closure
->new_do
->SetValue (key
, cv
);
1872 DependencyObject::clone_autocreated_value (DependencyProperty
*key
, Value
*value
, gpointer data
)
1874 CloneClosure
*closure
= (CloneClosure
*)data
;
1876 Value
*old_value
= closure
->old_do
->GetValue (key
, PropertyPrecedence_AutoCreate
);
1878 // this should create the new object
1879 Value
*new_value
= closure
->new_do
->GetValue (key
, PropertyPrecedence_AutoCreate
);
1881 if (old_value
&& !old_value
->GetIsNull() && old_value
->Is (Type::DEPENDENCY_OBJECT
) &&
1882 new_value
&& !new_value
->GetIsNull() && new_value
->Is (Type::DEPENDENCY_OBJECT
)) {
1883 DependencyObject
*new_obj
= new_value
->AsDependencyObject(closure
->types
);
1884 DependencyObject
*old_obj
= old_value
->AsDependencyObject(closure
->types
);
1886 new_obj
->CloneCore (closure
->types
, old_obj
);
1891 DependencyObject::clone_animation_storage (DependencyProperty
*key
, AnimationStorage
*storage
, gpointer data
)
1893 CloneClosure
*closure
= (CloneClosure
*)data
;
1895 // we need to fake an AnimationStorage so that any newly created animations on this clone get the right
1896 AnimationStorage
*new_storage
= new AnimationStorage (storage
->GetClock(), storage
->GetTimeline(),
1897 closure
->new_do
, key
);
1899 new_storage
->FlagAsNonResetable();
1900 new_storage
->DetachTarget ();
1901 new_storage
->SetStopValue (storage
->GetStopValue());
1903 closure
->new_do
->AttachAnimationStorage (key
, new_storage
);
1907 DependencyObject::Clone (Types
*types
)
1909 Type
*t
= types
->Find (GetObjectType());
1911 DependencyObject
*new_do
= t
->CreateInstance();
1914 new_do
->CloneCore (types
, (DependencyObject
*)this); // this cast should be unnecessary. but C++ const behavior sucks.
1920 DependencyObject::CloneCore (Types
*types
, DependencyObject
* fromObj
)
1922 CloneClosure closure
;
1923 closure
.types
= types
;
1924 closure
.old_do
= fromObj
;
1925 closure
.new_do
= this;
1927 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) fromObj
->providers
[PropertyPrecedence_AutoCreate
];
1929 g_hash_table_foreach (autocreate
->auto_values
, (GHFunc
)DependencyObject::clone_autocreated_value
, &closure
);
1930 g_hash_table_foreach (fromObj
->local_values
, (GHFunc
)DependencyObject::clone_local_value
, &closure
);
1931 if (fromObj
->storage_hash
)
1932 g_hash_table_foreach (fromObj
->storage_hash
, (GHFunc
)DependencyObject::clone_animation_storage
, &closure
);
1936 detach_target_func (DependencyProperty
*prop
, AnimationStorage
*storage
, gpointer unused
)
1938 storage
->DetachTarget ();
1942 DependencyObject::~DependencyObject ()
1944 g_hash_table_destroy (local_values
);
1945 local_values
= NULL
;
1948 g_free (resource_base
);
1951 g_hash_table_foreach (storage_hash
, (GHFunc
) detach_target_func
, NULL
);
1952 g_hash_table_destroy (storage_hash
);
1953 storage_hash
= NULL
;
1958 free_listener (gpointer data
, gpointer user_data
)
1960 Listener
* listener
= (Listener
*) data
;
1965 DependencyObject::Dispose ()
1967 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
1969 if (listener_list
!= NULL
) {
1970 g_slist_foreach (listener_list
, free_listener
, NULL
);
1971 g_slist_free (listener_list
);
1972 listener_list
= NULL
;
1975 RemoveAllListeners();
1978 g_hash_table_foreach_remove (autocreate
->auto_values
, dispose_value
, this);
1980 g_hash_table_foreach_remove (local_values
, dispose_value
, this);
1982 for (int i
= 0; i
< PropertyPrecedence_Count
; i
++) {
1983 delete providers
[i
];
1984 providers
[i
] = NULL
;
1987 EventObject::Dispose ();
1991 get_attached_props (gpointer key
, gpointer value
, gpointer user_data
)
1993 DependencyProperty
*prop
= (DependencyProperty
*) key
;
1994 GHashTable
*props
= (GHashTable
*) user_data
;
1996 if (!(g_hash_table_lookup (props
, (gpointer
) prop
->GetHashKey ())))
1997 g_hash_table_insert (props
, (gpointer
) prop
->GetHashKey (), prop
);
2001 hash_keys_to_array (gpointer key
, gpointer value
, gpointer user_data
)
2003 g_ptr_array_add ((GPtrArray
*) user_data
, key
);
2007 hash_values_to_array (gpointer key
, gpointer value
, gpointer user_data
)
2009 g_ptr_array_add ((GPtrArray
*) user_data
, value
);
2012 DependencyProperty
**
2013 DependencyObject::GetProperties (bool only_changed
)
2015 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
2016 DependencyProperty
**props
;
2020 array
= g_ptr_array_new ();
2022 if (!only_changed
) {
2023 // get our class/inherited DependencyProperties
2024 table
= GetType ()->CopyProperties (true);
2026 // find any attached properties that have been set
2027 g_hash_table_foreach (local_values
, get_attached_props
, table
);
2029 // dump them to an array
2030 g_hash_table_foreach (table
, hash_values_to_array
, array
);
2031 g_hash_table_destroy (table
);
2033 g_hash_table_foreach (local_values
, hash_keys_to_array
, array
);
2034 g_hash_table_foreach (autocreate
->auto_values
, hash_keys_to_array
, array
);
2037 g_ptr_array_add (array
, NULL
);
2038 props
= (DependencyProperty
**) array
->pdata
;
2039 g_ptr_array_free (array
, false);
2044 DependencyProperty
*
2045 DependencyObject::GetDependencyProperty (const char *name
)
2047 return DependencyProperty::GetDependencyProperty (GetObjectType (), name
);
2051 DependencyObject::HasProperty (const char *name
, bool inherits
)
2053 return DependencyProperty::GetDependencyProperty (GetObjectType (), name
, inherits
) != NULL
;
2057 DependencyObject::HasProperty (Type::Kind whatami
, DependencyProperty
*property
, bool inherits
)
2059 Type::Kind this_type
= whatami
== Type::INVALID
? GetObjectType () : whatami
;
2061 // TODO: Handle attached properties correctly.
2063 if (property
->IsAttached ())
2067 printf ("DependencyObject::HasProperty (%p, %i (%s), %p (%i %s.%s), %i)..\n",
2069 whatami, Type::Find (whatami)->name,
2070 property, property->GetOwnerType (), Type::Find (property->GetOwnerType ())->name, property->GetName (),
2074 if (property
== NULL
)
2077 if (property
->GetOwnerType () == this_type
)
2083 if (!Type::IsSubclassOf (this_type
, property
->GetOwnerType ())) {
2084 bool is_prop_custom
= property
->IsCustom ();
2085 bool is_owner_custom
= property
->GetOwnerType () > Type::LASTTYPE
;
2086 bool is_this_custom
= this_type
> Type::LASTTYPE
;
2087 bool accept
= false;
2090 // This looks very wrong, but it's what SL seems to do.
2091 if (is_prop_custom
) {
2092 if (!is_owner_custom
&& !is_this_custom
) {
2093 // SL does not throw errors for custom properties defined on a builtin type
2094 // and then used on another (unrelated) builtin type (DO.GetValue usage at least)
2096 } else if (is_owner_custom
) {
2097 // And this is a custom property defined on a custom type and used anywhere.
2108 DependencyObject::FindName (const char *name
)
2110 return FindName (name
, Control::GetIsTemplateItem (this));
2114 DependencyObject::FindName (const char *name
, bool template_item
)
2116 NameScope
*scope
= NameScope::GetNameScope (this);
2118 if (scope
&& (template_item
== scope
->GetIsLocked ()))
2119 return scope
->FindName (name
);
2122 return parent
->FindName (name
, template_item
);
2128 DependencyObject::FindNameScope ()
2130 return FindNameScope (Control::GetIsTemplateItem (this));
2134 DependencyObject::FindNameScope (bool template_namescope
)
2136 NameScope
*scope
= NameScope::GetNameScope (this);
2138 // Only template namescopes are locked (for the moment)
2139 if (scope
&& (template_namescope
== scope
->GetIsLocked ()))
2143 return parent
->FindNameScope (template_namescope
);
2149 DependencyObject::FindName (const char *name
, Type::Kind
*element_kind
)
2151 //printf ("Looking up in %p the string %p\n", obj, name);
2152 //printf (" String: %s\n", name);
2153 DependencyObject
*ret
= FindName (name
);
2158 *element_kind
= ret
->GetObjectType ();
2164 DependencyObject::GetAnimationStorageFor (DependencyProperty
*prop
)
2169 return (AnimationStorage
*) g_hash_table_lookup (storage_hash
, prop
);
2173 DependencyObject::AttachAnimationStorage (DependencyProperty
*prop
, AnimationStorage
*storage
)
2175 // Create hash on first access to save some mem
2177 storage_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
2179 AnimationStorage
*attached_storage
= (AnimationStorage
*) g_hash_table_lookup (storage_hash
, prop
);
2180 if (attached_storage
)
2181 attached_storage
->DetachTarget ();
2183 g_hash_table_insert (storage_hash
, prop
, storage
);
2184 return attached_storage
;
2188 DependencyObject::DetachAnimationStorage (DependencyProperty
*prop
, AnimationStorage
*storage
)
2193 if (g_hash_table_lookup (storage_hash
, prop
) == storage
)
2194 g_hash_table_remove (storage_hash
, prop
);
2198 // A helper debugging routine for C#
2201 dependency_object_get_name (DependencyObject
*obj
)
2203 return obj
->GetName ();
2207 dependency_object_get_object_type (DependencyObject
*obj
)
2209 return obj
->GetObjectType ();
2213 dependency_object_get_type_name (DependencyObject
*obj
)
2215 return obj
->GetTypeName ();
2218 // Used by routines which need to create DO from code
2220 dependency_object_set_name (DependencyObject
*obj
, const char *name
)
2222 obj
->SetValue (DependencyObject::NameProperty
, Value (name
));
2226 set_surface (gpointer key
, gpointer value
, gpointer data
)
2228 Surface
*s
= (Surface
*) data
;
2229 Value
*v
= (Value
*) value
;
2231 if (v
&& v
->Is (Type::DEPENDENCY_OBJECT
)) {
2232 DependencyObject
*dob
= v
->AsDependencyObject();
2234 dob
->SetSurface (s
);
2239 DependencyObject::SetSurface (Surface
*s
)
2241 AutoCreatePropertyValueProvider
*autocreate
= (AutoCreatePropertyValueProvider
*) providers
[PropertyPrecedence_AutoCreate
];
2243 if (GetSurface() == s
)
2246 EventObject::SetSurface (s
);
2249 g_hash_table_foreach (autocreate
->auto_values
, set_surface
, s
);
2251 g_hash_table_foreach (local_values
, set_surface
, s
);
2255 DependencyObject::SetParent (DependencyObject
*parent
, MoonError
*error
)
2257 if (parent
== this->parent
)
2261 // Check for circular families
2262 DependencyObject
*current
= parent
;
2263 while (current
!= NULL
) {
2264 if (current
== this) {
2265 g_warning ("cycle found in logical tree. bailing out");
2268 current
= current
->GetParent ();
2272 if (!this->parent
) {
2274 NameScope
*this_scope
= NameScope::GetNameScope(this);
2275 NameScope
*parent_scope
= parent
->FindNameScope();
2277 if (this_scope
->GetTemporary()) {
2278 // if we have a temporary name scope, merge it into the
2279 // closest one up the hierarchy.
2281 parent_scope
->MergeTemporaryScope (this_scope
, error
);
2282 ClearValue (NameScope::NameScopeProperty
, false);
2286 // there's no parent
2287 // namescope, we don't
2292 // we have a non-temporary scope. we still have to register the name
2293 // of this element (not the ones in the subtree rooted at this element)
2294 // in the new parent scope. we only register the name in the parent scope
2295 // if the element was hydrated, not when it was created from a string.
2296 if (IsHydratedFromXaml()) {
2297 const char *name
= GetName();
2298 if (parent_scope
&& name
&& *name
) {
2299 DependencyObject
*existing_obj
= parent_scope
->FindName (name
);
2300 if (existing_obj
!= this) {
2302 MoonError::FillIn (error
, MoonError::ARGUMENT
, g_strdup_printf ("name `%s' is already registered in new parent namescope.", name
));
2305 parent_scope
->RegisterName (name
, this);
2312 // we don't have a namescope at all,
2313 // we have to iterate over the subtree
2314 // rooted at this object, and merge
2315 // the names into the parent
2319 NameScope
*temp_scope
= new NameScope();
2320 temp_scope
->SetTemporary (true);
2322 RegisterAllNamesRootedAt (temp_scope
, error
);
2324 if (error
->number
) {
2325 temp_scope
->unref ();
2329 parent_scope
->MergeTemporaryScope (temp_scope
, error
);
2331 temp_scope
->unref ();
2338 NameScope
*parent_scope
= this->parent
->FindNameScope ();
2340 UnregisterAllNamesRootedAt (parent_scope
);
2344 if (!error
|| error
->number
== 0)
2345 this->parent
= parent
;
2349 dependency_object_get_value (DependencyObject
*object
, DependencyProperty
*prop
)
2354 return object
->GetValue (prop
);
2358 dependency_object_get_value_no_default (DependencyObject
*object
, DependencyProperty
*prop
)
2363 return object
->GetValueNoDefault (prop
);
2367 dependency_object_set_value (DependencyObject
*object
, DependencyProperty
*prop
, Value
*val
)
2372 object
->SetValue (prop
, val
);
2376 DependencyObject::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
2378 if (DependencyObject::NameProperty
== args
->GetId ()) {
2379 NameScope
*scope
= FindNameScope ();
2380 if (scope
&& args
->GetNewValue()) {
2381 if (args
->GetOldValue ())
2382 scope
->UnregisterName (args
->GetOldValue ()->AsString ());
2383 scope
->RegisterName (args
->GetNewValue()->AsString (), this);
2385 if (IsHydratedFromXaml () && parent
) {
2386 // we also need to update any parent
2387 // namescope about our name change
2389 scope
= parent
->FindNameScope ();
2391 if (args
->GetOldValue ())
2392 scope
->UnregisterName (args
->GetOldValue ()->AsString ());
2393 scope
->RegisterName (args
->GetNewValue()->AsString (), this);
2399 NotifyListenersOfPropertyChange (args
, error
);
2403 DependencyObject::GetContent()
2405 const char *content_property_name
= GetType()->GetContentPropertyName();
2406 if (!content_property_name
)
2409 DependencyProperty
*content_property
= GetDependencyProperty (content_property_name
);
2410 if (!content_property
)
2413 Value
*content_value
= GetValue(content_property
);
2418 return content_value
->AsDependencyObject();