Merge branch 'loaded-branch'
[moon.git] / src / dependencyobject.cpp
blob96ad0aa25966501edc72818fcb391919d07afd3f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dependencyobject.cpp:
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
8 *
9 */
11 #include <config.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <pthread.h>
17 #include "debug.h"
18 #include "namescope.h"
19 #include "list.h"
20 #include "collection.h"
21 #include "dependencyobject.h"
22 #include "textblock.h"
23 #include "timemanager.h"
24 #include "runtime.h"
25 #include "uielement.h"
26 #include "animation.h"
27 #include "deployment.h"
29 // event handlers for c++
30 class EventClosure : public List::Node {
31 public:
32 EventClosure (EventHandler func, gpointer data, GDestroyNotify data_dtor, int token) {
33 this->func = func;
34 this->data = data;
35 this->data_dtor = data_dtor;
36 this->token = token;
38 pending_removal = false;
39 emit_count = 0;
42 ~EventClosure ()
44 if (data_dtor)
45 data_dtor (data);
48 EventHandler func;
49 gpointer data;
50 GDestroyNotify data_dtor;
51 int token;
52 bool pending_removal;
53 int emit_count;
56 struct EmitContext {
57 int length;
58 bool only_unemitted;
59 int starting_generation;
60 EventClosure **closures;
62 EmitContext()
64 length = 0;
65 closures = NULL;
67 ~EmitContext()
69 g_free (closures);
73 class EmitContextNode : public List::Node {
74 public:
75 EmitContextNode (EmitContext *ctx) : ctx (ctx) { }
77 virtual ~EmitContextNode () { delete ctx; }
79 EmitContext *GetEmitContext () { return ctx; }
81 private:
82 EmitContext *ctx;
85 struct EventList {
86 int current_token;
87 int last_foreach_generation;
88 List *context_stack;
89 EventClosure *onevent;
90 List *event_list;
93 class EventLists {
94 public:
95 int size;
96 int emitting;
97 EventList *lists;
99 EventLists (int n)
101 size = n;
102 emitting = 0;
103 lists = new EventList [size];
104 for (int i = 0; i < size; i++) {
105 lists [i].current_token = 1;
106 lists [i].last_foreach_generation = -1;
107 lists [i].context_stack = new List();
108 lists [i].onevent = NULL;
109 lists [i].event_list = new List ();
113 ~EventLists ()
115 for (int i = 0; i < size; i++) {
116 delete lists [i].event_list;
117 delete lists [i].onevent;
118 delete lists [i].context_stack;
120 delete [] lists;
125 * EventObject
128 #if OBJECT_TRACKING
129 #define OBJECT_TRACK(x,y) Track((x),(y))
130 #else
131 #define OBJECT_TRACK(x,y)
132 #endif
134 EventObject::EventObject ()
136 Initialize (NULL, Type::EVENTOBJECT);
139 EventObject::EventObject (Type::Kind type)
141 Initialize (NULL, type);
144 EventObject::EventObject (Type::Kind type, bool multi_threaded_safe)
146 Initialize (NULL, type);
147 if (multi_threaded_safe)
148 flags |= (gint32) MultiThreadedSafe;
151 EventObject::EventObject (Deployment *deployment)
153 Initialize (deployment, Type::EVENTOBJECT);
156 EventObject::EventObject (Deployment *deployment, Type::Kind type)
158 Initialize (deployment, type);
161 static gint current_id = 0;
163 void
164 EventObject::Initialize (Deployment *depl, Type::Kind type)
166 if (depl == NULL)
167 depl = Deployment::GetCurrent ();
169 object_type = type;
170 deployment = depl;
171 if (deployment != NULL && this != deployment)
172 deployment->ref ();
173 surface = NULL;
174 flags = g_atomic_int_exchange_and_add (&current_id, 1);
175 refcount = 1;
176 events = NULL;
177 toggleNotifyListener = NULL;
179 #if OBJECT_TRACKING
180 switch (object_type) {
181 case Type::INVALID:
182 Track ("Created", "<unknown>");
183 break;
184 case Type::DEPLOYMENT:
185 Track ("Created", "Deployment");
186 break;
187 default:
188 Track ("Created", depl->GetTypes ()->Find (object_type)->GetName ());
189 break;
192 if (object_type != Type::DEPLOYMENT)
193 Deployment::GetCurrent ()->TrackObjectCreated (this);
194 #endif
196 #if SANITY
197 if (object_type == Type::INVALID)
198 g_warning ("EventObject::EventObject (): created object with type: INVALID.\n");
199 if (deployment == NULL)
200 g_warning ("EventObject::EventObject (): created object with a null deployment.\n");
201 #endif
204 EventObject::~EventObject()
206 #if OBJECT_TRACKING
207 if (object_type != Type::DEPLOYMENT)
208 Deployment::GetCurrent ()->TrackObjectDestroyed (this);
209 Track ("Destroyed", "");
210 #endif
212 #if SANITY
213 if (refcount != 0) {
214 g_warning ("EventObject::~EventObject () #%i was deleted before its refcount reached 0 (current refcount: %i)\n", GetId (), refcount);
216 #endif
218 delete events;
220 // We can't unref the deployment in Dispose, it breaks
221 // object tracking.
222 if (deployment && this != deployment) {
223 deployment->unref ();
224 deployment = NULL;
228 static pthread_rwlock_t surface_lock = PTHREAD_RWLOCK_INITIALIZER;
230 bool
231 EventObject::SetSurfaceLock ()
233 int result;
235 if ((result = pthread_rwlock_wrlock (&surface_lock)) != 0) {
236 printf ("EventObject::SetSurface (%p): Couldn't aquire write lock: %s\n", surface, strerror (result));
237 return false;
240 return true;
244 void
245 EventObject::SetSurface (Surface *surface)
247 if (!Surface::InMainThread () && surface != this->surface) {
248 g_warning ("EventObject::SetSurface (): This method must not be called on any other than the main thread!\n");
249 #if DEBUG
250 if (debug_flags & RUNTIME_DEBUG_DP)
251 print_stack_trace ();
252 #endif
253 return;
256 this->surface = surface;
259 void
260 EventObject::SetSurfaceUnlock ()
262 pthread_rwlock_unlock (&surface_lock);
265 void
266 EventObject::AddTickCallSafe (TickCallHandler handler, EventObject *data)
268 int result;
271 * This code assumes that the surface won't be destructed without setting the surface field on to NULL first.
272 * In other words: if we have a read lock here, the surface won't get destroyed, given that setting
273 * the surface field to NULL will block until we release the read lock.
276 if ((result = pthread_rwlock_rdlock (&surface_lock)) != 0) {
277 printf ("EventObject::AddTickCallSafe (): Couldn't aquire read lock: %s\n", strerror (result));
278 return;
281 AddTickCallInternal (handler, data);
283 pthread_rwlock_unlock (&surface_lock);
286 void
287 EventObject::AddTickCall (TickCallHandler handler, EventObject *data)
289 if (!Surface::InMainThread ()) {
290 g_warning ("EventObject::AddTickCall (): This method must not be called on any other than the main thread! Tick call won't be added.\n");
291 #if DEBUG
292 if (debug_flags & RUNTIME_DEBUG_DP)
293 print_stack_trace ();
294 #endif
295 return;
298 AddTickCallInternal (handler, data);
301 void
302 EventObject::AddTickCallInternal (TickCallHandler handler, EventObject *data)
304 Surface *surface;
305 TimeManager *timemanager;
307 surface = GetSurface ();
309 if (surface == NULL)
310 surface = GetDeployment ()->GetSurface ();
312 if (!surface) {
313 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no surface\n");
314 return;
317 timemanager = surface->GetTimeManager ();
319 if (!timemanager) {
320 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no time manager\n");
321 return;
324 timemanager->AddTickCall (handler, data ? data : this);
327 Deployment *
328 EventObject::GetDeployment ()
330 if (deployment == NULL)
331 g_warning ("EventObject::GetDeployment () should not be reached with a null deployment");
333 #if SANITY
334 if (deployment != Deployment::GetCurrent () && Deployment::GetCurrent () != NULL) {
335 g_warning ("EventObject::GetDeployment () our deployment %p doesn't match Deployment::GetCurrent () %p", deployment, Deployment::GetCurrent ());
336 // print_stack_trace ();
338 #endif
340 return deployment;
343 void
344 EventObject::SetCurrentDeployment (bool domain, bool register_thread)
346 if (deployment != NULL) {
347 if (register_thread)
348 Deployment::RegisterThread (deployment);
349 Deployment::SetCurrent (deployment, domain);
353 Surface *
354 EventObject::GetSurface ()
356 #if 0
357 Deployment *current_deployment = Deployment::GetCurrent ();
358 Application *current_application;
359 current_application = deployment != NULL ? deployment->GetCurrentApplication () : (current_deployment != NULL ? current_deployment->GetCurrentApplication () : NULL);
361 if (deployment != NULL && deployment != current_deployment) {
362 printf ("EventObject::GetSurface (): WARNING deployment: %p, Deployment::GetCurrent (): %p type: %s, id: %i\n", deployment, current_deployment, GetTypeName (), GET_OBJ_ID (this));
363 print_stack_trace ();
365 // current_application is null in the Surface ctor
366 if (!(current_application == NULL || surface == NULL || current_application->GetSurface () == surface))
367 printf ("EventObject::GetSurface (): assert failed: current application: %p, surface: %p, current application's surface: %p\n", current_application, surface, current_application->GetSurface ());
368 #endif
370 return surface; // When surface have been removed, return the Surface stored on the Deployment.
373 void
374 EventObject::Dispose ()
376 if (!IsDisposed () && Surface::InMainThread ()) {
377 // Dispose can be called multiple times, but Emit only once. When DestroyedEvent was in the dtor,
378 // it could only ever be emitted once, don't change that behaviour.
379 Emit (DestroyedEvent); // TODO: Rename to DisposedEvent
382 SetSurface (NULL);
383 // Remove attached flag and set the disposed flag.
384 flags = (Flags) (flags & ~Attached);
385 flags = (Flags) (flags | Disposed);
388 bool
389 EventObject::IsDisposed ()
391 return (flags & Disposed) != 0;
394 void
395 EventObject::ref ()
397 int v = g_atomic_int_exchange_and_add (&refcount, 1);
399 #if DEBUG
400 if (GetObjectType () != object_type)
401 printf ("EventObject::ref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
403 if (deployment != Deployment::GetCurrent ()) {
404 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 ());
405 // print_stack_trace ();
407 #endif
409 if (v == 0) {
410 // Here something really bad happened, this object is probably being reffed again because
411 // of some action in the destructor. There is no way to recover from this now, no matter what
412 // we do the code that called ref now will be accessing a deleted object later on, which may or
413 // may not crash. It might very well be an exploitable security problem. Anyways when unref is called, we
414 // have a second delete on the same object, which *will* crash. To make things easier and safer
415 // lets just abort right away.
416 #if OBJECT_TRACKING
417 PrintStackTrace ();
418 #endif
419 // Due to our mixed usage of Dispose and dtor, currently there are valid cases of reffing
420 // an object with refcount = 0. Use a warning instead of error until the mixed usage is
421 // gone.
422 g_warning ("Ref was called on an object with a refcount of 0.\n");
424 } else if (v == 1 && toggleNotifyListener) {
425 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
426 toggleNotifyListener->Invoke (false);
429 OBJECT_TRACK ("Ref", GetTypeName ());
432 void
433 EventObject::unref ()
435 // we need to retrieve all instance fields into locals before decreasing the refcount
436 // TODO: do we need some sort of gcc foo (volatile variables, memory barries)
437 // to ensure that gcc does not optimize the fetches below away
438 ToggleNotifyListener *toggle_listener = this->toggleNotifyListener;
439 #if OBJECT_TRACKING
440 Deployment *depl = this->deployment ? this->deployment : Deployment::GetCurrent ();
441 const char *type_name = depl == NULL ? NULL : Type::Find (depl, GetObjectType ())->GetName ();
442 #endif
444 #if SANITY
445 if (GetObjectType () != object_type)
446 printf ("EventObject::unref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
447 #endif
449 if (!IsMultiThreadedSafe () && !Surface::InMainThread ()) {
450 unref_delayed ();
451 return;
454 int v = g_atomic_int_exchange_and_add (&refcount, -1) - 1;
456 // from now on we can't access any instance fields if v > 0
457 // since another thread might have unreffed and caused our destruction
459 if (v == 0 && events != NULL && events->emitting) {
460 g_atomic_int_exchange_and_add (&refcount, 1);
461 unref_delayed ();
462 return;
465 OBJECT_TRACK ("Unref", type_name);
467 if (v == 0) {
468 // here we *can* access instance fields, since we know that we haven't been
469 // desctructed already.
470 Dispose ();
472 #if SANITY
473 if ((flags & Disposed) == 0)
474 printf ("EventObject::unref (): the type '%s' (or any of its parent types) forgot to call its base class' Dispose method.\n", GetTypeName ());
475 #endif
477 // We need to check again if the refcount really is zero,
478 // the object might have resurrected in the Dispose.
479 // TODO: we should disallow resurrection, it's not thread-safe
480 // if we got resurrected and unreffed, we'd be deleted by now
481 // in which case we'll double free here.
482 v = g_atomic_int_get (&refcount);
483 if (v == 0)
484 delete this;
486 } else if (v == 1 && toggle_listener) {
487 // 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
488 // note that the instance field might point to garbage after decreasing the refcount above, so we access the local variable we
489 // retrieved before decreasing the refcount.
490 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
491 toggle_listener->Invoke (true);
494 #if SANITY
495 if (v < 0) {
496 g_warning ("EventObject::Unref (): NEGATIVE REFCOUNT id: %i v: %i refcount: %i", GET_OBJ_ID (this), v, refcount);
497 print_stack_trace ();
499 #endif
502 void
503 EventObject::AddToggleRefNotifier (ToggleNotifyHandler tr)
505 if (toggleNotifyListener)
506 return;
508 this->ref ();
509 toggleNotifyListener = new ToggleNotifyListener (this, tr);
512 void
513 EventObject::RemoveToggleRefNotifier ()
515 if (!toggleNotifyListener)
516 return;
518 delete toggleNotifyListener;
519 toggleNotifyListener = NULL;
520 this->unref ();
523 #if OBJECT_TRACKING
524 // Define the ID of the object you want to track
525 // Object creation, destruction and all ref/unrefs
526 // are logged to the console, with a stacktrace.
527 static bool object_id_fetched = false;
528 static int object_id = -1;
529 static const char *track_object_type = NULL;
530 static bool use_visi_output = false;
531 static bool track_all = false;
533 #define OBJECT_TRACK_ID (0)
535 GHashTable* EventObject::objects_alive = NULL;
537 void
538 EventObject::Track (const char* done, const char* typname)
540 int id = GetId ();
541 if (!object_id_fetched) {
542 object_id_fetched = true;
543 char *sval = getenv ("MOONLIGHT_OBJECT_TRACK_ID");
544 if (sval)
545 object_id = atoi (sval);
546 track_object_type = getenv ("MOONLIGHT_OBJECT_TRACK_TYPE");
547 use_visi_output = (getenv ("MOONLIGHT_OBJECT_TRACK_VISI") != NULL);
548 track_all = (getenv ("MOONLIGHT_OBJECT_TRACK_ALL") != NULL);
551 if (track_all)
552 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
554 if (id == object_id || (track_object_type != NULL && typname != NULL && strcmp (typname, track_object_type) == 0)) {
555 char *st = NULL;
556 // load the stack trace before we print anything
557 // this way there's a lot smaller chance of
558 // ending up with other output between the first line (tracked object of type...)
559 // and the stack trace when using multiple threads.
560 if (!use_visi_output)
561 st = get_stack_trace ();
563 if (!track_all)
564 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
566 if (!use_visi_output) {
567 printf("%s", st);
568 } else {
569 print_reftrace (done, typname, refcount, false);
571 g_free (st);
575 char *
576 EventObject::GetStackTrace (const char* prefix)
578 return get_stack_trace_prefix (prefix);
581 void
582 EventObject::PrintStackTrace ()
584 print_stack_trace ();
586 #endif
589 EventObject::AddHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
591 int id = GetType()->LookupEvent (event_name);
593 if (id == -1) {
594 g_warning ("adding handler to event '%s', which has not been registered\n", event_name);
595 return -1;
598 return AddHandler (id, handler, data, data_dtor);
602 EventObject::AddHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
604 if (GetType()->GetEventCount() <= 0) {
605 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
606 return -1;
609 if (events == NULL)
610 events = new EventLists (GetType ()->GetEventCount ());
612 int token = events->lists [event_id].current_token++;
614 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, token));
616 return token;
619 void
620 EventObject::AddOnEventHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
622 if (GetType()->GetEventCount() <= event_id) {
623 g_warning ("adding OnEvent handler to event with id %d, which has not been registered\n", event_id);
624 return;
627 if (events == NULL)
628 events = new EventLists (GetType ()->GetEventCount ());
630 events->lists [event_id].onevent = new EventClosure (handler, data, data_dtor, 0);
633 void
634 EventObject::RemoveOnEventHandler (int event_id, EventHandler handler, gpointer data)
636 if (GetType()->GetEventCount() <= event_id) {
637 g_warning ("adding OnEvent handler to event with id %d, which has not been registered\n", event_id);
638 return;
641 if (events == NULL)
642 events = new EventLists (GetType ()->GetEventCount ());
644 if (events->lists [event_id].onevent) {
645 // FIXME check handler + data
646 delete events->lists [event_id].onevent;
647 events->lists [event_id].onevent = NULL;
652 EventObject::AddXamlHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
654 int id = GetType ()->LookupEvent (event_name);
656 if (id == -1) {
657 g_warning ("adding xaml handler to event '%s', which has not been registered\n", event_name);
658 return -1;
661 return AddXamlHandler (id, handler, data, data_dtor);
665 EventObject::AddXamlHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
667 if (GetType ()->GetEventCount () <= 0) {
668 g_warning ("adding xaml handler to event with id %d, which has not been registered\n", event_id);
669 return -1;
672 if (events == NULL)
673 events = new EventLists (GetType ()->GetEventCount ());
675 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, 0));
677 return 0;
680 void
681 EventObject::RemoveHandler (const char *event_name, EventHandler handler, gpointer data)
683 int id = GetType()->LookupEvent (event_name);
685 if (id == -1) {
686 g_warning ("removing handler for event '%s', which has not been registered\n", event_name);
687 return;
690 RemoveHandler (id, handler, data);
694 EventObject::RemoveHandler (int event_id, EventHandler handler, gpointer data)
696 int token = -1;
697 if (GetType()->GetEventCount() <= 0) {
698 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
699 return -1;
702 if (events == NULL)
703 events = new EventLists (GetType ()->GetEventCount ());
705 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
706 while (closure) {
707 if (closure->func == handler && closure->data == data) {
708 token = closure->token;
709 if (!events->lists [event_id].context_stack->IsEmpty()) {
710 closure->pending_removal = true;
711 } else {
712 events->lists [event_id].event_list->Remove (closure);
714 break;
717 closure = (EventClosure *) closure->next;
719 return token;
722 void
723 EventObject::RemoveHandler (int event_id, int token)
725 if (GetType()->GetEventCount() <= 0) {
726 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
727 return;
730 if (events == NULL)
731 events = new EventLists (GetType ()->GetEventCount ());
733 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
734 while (closure) {
735 if (closure->token == token) {
736 if (!events->lists [event_id].context_stack->IsEmpty()) {
737 closure->pending_removal = true;
738 } else {
739 events->lists [event_id].event_list->Remove (closure);
741 break;
744 closure = (EventClosure *) closure->next;
748 void
749 EventObject::RemoveAllHandlers (gpointer data)
751 if (events == NULL)
752 events = new EventLists (GetType ()->GetEventCount ());
754 int count = GetType ()->GetEventCount ();
756 for (int i = 0; i < count - 1; i++) {
757 EventClosure *closure = (EventClosure *) events->lists [i].event_list->First ();
758 while (closure) {
759 if (closure->data == data) {
760 if (!events->lists [i].context_stack->IsEmpty()) {
761 closure->pending_removal = true;
762 } else {
763 events->lists [i].event_list->Remove (closure);
765 break;
768 closure = (EventClosure *) closure->next;
773 void
774 EventObject::RemoveMatchingHandlers (int event_id, bool (*predicate)(EventHandler cb_handler, gpointer cb_data, gpointer data), gpointer closure)
776 if (GetType()->GetEventCount() <= 0) {
777 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
778 return;
781 if (events == NULL)
782 events = new EventLists (GetType ()->GetEventCount ());
784 EventClosure *c = (EventClosure *) events->lists [event_id].event_list->First ();
785 while (c) {
786 if (predicate (c->func, c->data, closure)) {
787 if (!events->lists [event_id].context_stack->IsEmpty()) {
788 c->pending_removal = true;
789 } else {
790 events->lists [event_id].event_list->Remove (c);
792 break;
795 c = (EventClosure *) c->next;
799 void
800 EventObject::ForeachHandler (int event_id, bool only_new, HandlerMethod m, gpointer closure)
802 if (GetType()->GetEventCount() <= 0)
803 return;
805 if (events == NULL)
806 events = new EventLists (GetType ()->GetEventCount ());
808 EventClosure *event_closure = (EventClosure *) events->lists [event_id].event_list->First ();
809 while (event_closure) {
810 if (!only_new || event_closure->token >= events->lists [event_id].last_foreach_generation)
811 (*m) (this, event_closure->func, event_closure->data, closure);
812 event_closure = (EventClosure *) event_closure->next;
815 events->lists [event_id].last_foreach_generation = GetEventGeneration (event_id);
818 void
819 EventObject::ClearForeachGeneration (int event_id)
821 if (GetType()->GetEventCount() <= 0)
822 return;
824 if (events == NULL)
825 events = new EventLists (GetType ()->GetEventCount ());
827 events->lists [event_id].last_foreach_generation = -1;
830 void
831 EventObject::ForHandler (int event_id, int token, HandlerMethod m, gpointer closure)
833 if (GetType()->GetEventCount() <= 0)
834 return;
836 if (events == NULL)
837 events = new EventLists (GetType ()->GetEventCount ());
839 EventClosure *event_closure = (EventClosure *) events->lists [event_id].event_list->First ();
840 while (event_closure) {
841 if (event_closure->token == token) {
842 (*m) (this, event_closure->func, event_closure->data, closure);
843 break;
845 event_closure = (EventClosure *) event_closure->next;
849 bool
850 EventObject::HasHandlers (int event_id, int newer_than_generation)
852 if (GetType()->GetEventCount() <= 0)
853 return false;
855 if (events == NULL)
856 events = new EventLists (GetType ()->GetEventCount ());
858 EventClosure *event_closure = (EventClosure *) events->lists [event_id].event_list->First ();
859 while (event_closure) {
860 if (newer_than_generation != -1 || event_closure->token >= newer_than_generation)
861 return true;
862 event_closure = (EventClosure *) event_closure->next;
864 return false;
868 EventObject::GetEventGeneration (int event_id)
870 if (GetType()->GetEventCount() <= 0) {
871 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
872 return -1;
875 if (events == NULL)
876 events = new EventLists (GetType ()->GetEventCount ());
878 return events->lists [event_id].current_token;
881 bool
882 EventObject::CanEmitEvents (int event_id)
884 if (IsDisposed ())
885 return false;
887 if (deployment == NULL)
888 return false; /* how did this happen? */
890 if (deployment == this)
891 return true; /* Deployment::ShuttingDownEvent and Deployment::AppDomainUnloadedEvent */
893 if (event_id == DestroyedEvent) {
894 /* We need to allow this one too, namescopes listen to it to ensure
895 * that they detach from objects that are destroyed. Managed code
896 * doesn't listen to this event, so it's safe. */
897 return true;
900 if (deployment->IsShuttingDown ()) {
901 /* Don't emit events after we've started shutting down, we might have
902 * managed event handlers, which could crash since the appdomain could
903 * have been unloaded */
904 return false;
907 return true;
910 bool
911 EventObject::EmitAsync (const char *event_name, EventArgs *calldata, bool only_unemitted)
913 int event_id;
915 if ((event_id = GetType ()->LookupEvent (GetDeployment (), event_name)) == -1) {
916 g_warning ("trying to emit event '%s', which has not been registered\n", event_name);
917 if (calldata)
918 calldata->unref ();
919 return false;
922 if (!CanEmitEvents (event_id)) {
923 if (calldata)
924 calldata->unref ();
925 return false;
928 return EmitAsync (event_id, calldata, only_unemitted);
931 bool
932 EventObject::Emit (const char *event_name, EventArgs *calldata, bool only_unemitted, int starting_generation)
934 int id = GetType ()->LookupEvent (GetDeployment (), event_name);
936 if (id == -1) {
937 g_warning ("trying to emit event '%s', which has not been registered\n", event_name);
938 if (calldata)
939 calldata->unref ();
940 return false;
943 if (!CanEmitEvents (id)) {
944 if (calldata)
945 calldata->unref ();
946 return false;
949 return Emit (id, calldata, only_unemitted, starting_generation);
952 class AsyncEventClosure : public EventObject {
953 public:
954 EventObject *sender;
955 EventArgs *args;
956 bool unemitted;
957 int generation;
958 int event_id;
960 AsyncEventClosure (EventObject *sender, int event_id, EventArgs *args, bool unemitted, int generation)
962 this->sender = sender;
963 this->event_id = event_id;
964 this->args = args;
965 this->unemitted = unemitted;
966 this->generation = generation;
968 sender->ref ();
971 protected:
972 virtual ~AsyncEventClosure ()
974 sender->unref ();
978 void
979 EventObject::emit_async (EventObject *calldata)
981 AsyncEventClosure *async = (AsyncEventClosure *) calldata;
983 async->sender->Emit (async->event_id, async->args, async->unemitted, async->generation);
985 async->unref ();
988 bool
989 EventObject::EmitAsync (int event_id, EventArgs *calldata, bool only_unemitted)
991 if (!CanEmitEvents (event_id)) {
992 if (calldata)
993 calldata->unref ();
994 return false;
997 if (events == NULL)
998 events = new EventLists (GetType ()->GetEventCount ());
1000 AddTickCall (EventObject::emit_async, new AsyncEventClosure (this, event_id, calldata, only_unemitted, GetEventGeneration (event_id)));
1002 return true;
1005 struct EmitData {
1006 EventObject *sender;
1007 int event_id;
1008 EventArgs *calldata;
1009 bool only_unemitted;
1012 gboolean
1013 EventObject::EmitCallback (gpointer d)
1015 EmitData *data = (EmitData *) d;
1016 data->sender->SetCurrentDeployment ();
1017 data->sender->Emit (data->event_id, data->calldata, data->only_unemitted);
1018 data->sender->unref ();
1019 delete data;
1020 #if SANITY
1021 Deployment::SetCurrent (NULL);
1022 #endif
1023 return FALSE;
1026 bool
1027 EventObject::Emit (int event_id, EventArgs *calldata, bool only_unemitted, int starting_generation)
1029 if (!CanEmitEvents (event_id)) {
1030 if (calldata)
1031 calldata->unref ();
1032 return false;
1035 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
1036 g_warning ("trying to emit event with id %d, which has not been registered\n", event_id);
1037 if (calldata)
1038 calldata->unref ();
1039 return false;
1042 if (events == NULL)
1043 events = new EventLists (GetType ()->GetEventCount ());
1045 if (events->lists [event_id].event_list->IsEmpty () && events->lists [event_id].onevent == NULL) {
1046 if (calldata)
1047 calldata->unref ();
1048 return false;
1051 if (!Surface::InMainThread ()) {
1052 Surface *surface = deployment ? deployment->GetSurface () : NULL;
1054 if (surface == NULL) {
1055 printf ("EventObject::Emit (): could not emit event, the deployment %p does not have a surface.\n", deployment);
1056 return false;
1059 EmitData *data = new EmitData ();
1060 data->sender = this;
1061 data->sender->ref ();
1062 data->event_id = event_id;
1063 data->calldata = calldata;
1064 data->only_unemitted = only_unemitted;
1065 surface->GetTimeManager ()->AddTimeout (MOON_PRIORITY_HIGH, 1, EmitCallback, data);
1066 return false;
1069 EmitContext* ctx = StartEmit (event_id, only_unemitted, starting_generation);
1070 DoEmit (event_id, calldata);
1072 FinishEmit (event_id, ctx);
1074 return true;
1077 EmitContext*
1078 EventObject::StartEmit (int event_id, bool only_unemitted, int starting_generation)
1080 if (events == NULL)
1081 events = new EventLists (GetType ()->GetEventCount ());
1083 EmitContext *ctx = new EmitContext();
1084 ctx->only_unemitted = only_unemitted;
1085 ctx->starting_generation = starting_generation;
1086 EventClosure *closure;
1088 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
1089 g_warning ("trying to start emit with id %d, which has not been registered\n", event_id);
1090 return ctx;
1093 events->emitting++;
1095 events->lists [event_id].context_stack->Prepend (new EmitContextNode (ctx));
1097 if (events->lists [event_id].event_list->IsEmpty ())
1098 return ctx;
1100 ctx->length = events->lists [event_id].event_list->Length();
1101 ctx->closures = g_new (EventClosure*, ctx->length);
1103 /* make a copy of the event list to use for emitting */
1104 closure = (EventClosure *) events->lists [event_id].event_list->First ();
1105 for (int i = 0; closure != NULL; i ++) {
1106 ctx->closures[i] = closure->pending_removal ? NULL : closure;
1107 closure = (EventClosure *) closure->next;
1110 return ctx;
1113 bool
1114 EventObject::DoEmit (int event_id, EventArgs *calldata)
1116 if (events->lists [event_id].context_stack->IsEmpty ()) {
1117 g_warning ("DoEmit called with no EmitContexts");
1118 return false;
1121 EmitContext *ctx = ((EmitContextNode*)events->lists [event_id].context_stack->First())->GetEmitContext();
1123 if (events->lists [event_id].onevent) {
1124 EventClosure *closure = events->lists [event_id].onevent;
1125 closure->func (this, calldata, closure->data);
1127 else {
1128 DoEmitCurrentContext (event_id, calldata);
1131 if (calldata)
1132 calldata->unref ();
1134 return ctx->length > 0;
1137 void
1138 EventObject::DoEmitCurrentContext (int event_id, EventArgs *calldata)
1140 if (events->lists [event_id].context_stack->IsEmpty()) {
1141 g_warning ("DoEmitCurrentContext called with no EmitContexts");
1142 return;
1145 EmitContext *ctx = ((EmitContextNode*)events->lists [event_id].context_stack->First())->GetEmitContext();
1147 /* emit the events using the copied list in the context*/
1148 for (int i = 0; i < ctx->length; i++) {
1149 if (calldata && calldata->Is (Type::ROUTEDEVENTARGS)) {
1150 RoutedEventArgs *rea = (RoutedEventArgs*)calldata;
1151 if (rea->GetHandled ())
1152 break;
1155 EventClosure *closure = ctx->closures[i];
1157 if (closure && closure->func
1158 && (!ctx->only_unemitted || closure->emit_count == 0)
1159 && (ctx->starting_generation == -1 || closure->token < ctx->starting_generation)) {
1160 closure->func (this, calldata, closure->data);
1162 closure->emit_count ++;
1167 void
1168 EventObject::FinishEmit (int event_id, EmitContext *ctx)
1170 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
1171 g_warning ("trying to finish emit with id %d, which has not been registered\n", event_id);
1172 return;
1175 if (events->lists [event_id].context_stack->IsEmpty()) {
1176 g_warning ("FinishEmit called with no EmitContexts");
1177 return;
1180 EmitContextNode *first_node = (EmitContextNode*)events->lists [event_id].context_stack->First();
1181 EmitContext *first_ctx = first_node->GetEmitContext();
1183 if (first_ctx != ctx) {
1184 g_warning ("FinishEmit called out of order");
1185 return;
1188 events->lists [event_id].context_stack->Unlink (first_node);
1190 delete first_node;
1191 events->emitting--;
1193 if (events->lists [event_id].context_stack->IsEmpty ()) {
1194 // Remove closures which are waiting for removal
1195 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
1196 while (closure != NULL) {
1197 EventClosure *next = (EventClosure *) closure->next;
1198 if (closure->pending_removal)
1199 events->lists [event_id].event_list->Remove (closure);
1200 closure = next;
1205 void
1206 EventObject::unref_delayed ()
1208 Deployment *depl;
1210 OBJECT_TRACK ("DelayedUnref", GetTypeName ());
1212 // access deployment as long as we have it (until Dispose has been called),
1213 // after that access the static deployment.
1214 depl = deployment ? deployment : Deployment::GetCurrent ();
1215 depl->UnrefDelayed (this);
1218 class Listener {
1219 public:
1220 virtual bool Matches (PropertyChangedEventArgs *args) = 0;
1221 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error) = 0;
1223 virtual gpointer GetListener () = 0;
1224 virtual gpointer GetProperty () = 0;
1227 class WildcardListener : public Listener {
1228 public:
1229 WildcardListener (DependencyObject *obj, DependencyProperty *prop)
1231 this->obj = obj;
1232 this->prop = prop;
1235 virtual bool Matches (PropertyChangedEventArgs *args) { return true; }
1236 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
1238 // FIXME we ignore error here.
1239 obj->OnSubPropertyChanged (prop, sender, args);
1242 virtual gpointer GetListener ()
1244 return obj;
1247 virtual gpointer GetProperty ()
1249 return prop;
1252 private:
1254 DependencyObject *obj;
1255 DependencyProperty *prop;
1258 class CallbackListener : public Listener {
1259 public:
1260 CallbackListener (DependencyProperty *prop, PropertyChangeHandler cb, gpointer closure)
1262 this->prop = prop;
1263 this->cb = cb;
1264 this->closure = closure;
1267 virtual bool Matches (PropertyChangedEventArgs *args)
1269 return prop == args->GetProperty ();
1272 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
1274 cb (sender, args, error, closure);
1277 virtual gpointer GetListener ()
1279 return (gpointer)cb;
1282 virtual gpointer GetProperty ()
1284 return prop;
1287 private:
1288 PropertyChangeHandler cb;
1289 DependencyProperty *prop;
1290 gpointer closure;
1294 // Registers @listener as a listener on changes to @child_property of this DO.
1296 void
1297 DependencyObject::AddPropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1299 listener_list = g_slist_append (listener_list, new WildcardListener (listener, child_property));
1302 void
1303 DependencyObject::RemoveListener (gpointer listener, DependencyProperty *child_property)
1305 GSList *next;
1306 for (GSList *l = listener_list; l; l = next) {
1307 next = l->next;
1308 Listener *listen = (Listener *) l->data;
1310 if ((listen->GetListener() == listener)
1311 && (child_property == NULL || listen->GetProperty() == child_property)) {
1312 listener_list = g_slist_delete_link (listener_list, l);
1313 delete listen;
1319 // Unregisters @container as a listener on changes to @child_property of this DO.
1321 void
1322 DependencyObject::RemovePropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1324 RemoveListener (listener, child_property);
1327 void
1328 DependencyObject::AddPropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb, gpointer closure)
1330 listener_list = g_slist_append (listener_list, new CallbackListener (property, cb, closure));
1333 void
1334 DependencyObject::RemovePropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb)
1336 RemoveListener ((gpointer)cb, property);
1339 static void
1340 unregister_depobj_values (gpointer key,
1341 gpointer value,
1342 gpointer user_data)
1344 DependencyObject *this_obj = (DependencyObject*)user_data;
1345 //DependencyProperty *prop = (DependencyProperty*)key;
1346 Value *v = (Value*)value;
1348 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1349 //printf ("unregistering from property %s\n", prop->name);
1350 DependencyObject *obj = v->AsDependencyObject ();
1351 obj->RemovePropertyChangeListener (this_obj);
1352 obj->SetParent (NULL, NULL);
1356 void
1357 DependencyObject::RemoveAllListeners ()
1359 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1361 if (autocreate)
1362 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_values, this);
1364 g_hash_table_foreach (local_values, unregister_depobj_values, this);
1367 static bool listeners_notified;
1369 void
1370 DependencyObject::NotifyListenersOfPropertyChange (PropertyChangedEventArgs *args, MoonError *error)
1372 g_return_if_fail (args);
1374 listeners_notified = true;
1376 for (GSList *l = listener_list; l != NULL; l = l->next){
1377 Listener *listener = (Listener*)l->data;
1379 if (listener->Matches (args))
1380 listener->Invoke (this, args, error);
1381 if (error && error->number)
1382 break;
1386 void
1387 DependencyObject::NotifyListenersOfPropertyChange (int id, MoonError *error)
1389 if (IsDisposed ())
1390 return;
1391 NotifyListenersOfPropertyChange (GetDeployment ()->GetTypes ()->GetProperty (id), error);
1394 void
1395 DependencyObject::NotifyListenersOfPropertyChange (DependencyProperty *subproperty, MoonError *error)
1397 // XXX I really think this method should go away. we only use it in
1398 // a couple of places, and it abuses things.
1400 Value *new_value = subproperty ? GetValue (subproperty) : NULL;
1402 PropertyChangedEventArgs *args = new PropertyChangedEventArgs (subproperty, subproperty->GetId (), NULL, new_value);
1404 NotifyListenersOfPropertyChange (args, error);
1406 args->unref ();
1409 bool
1410 DependencyObject::IsValueValid (DependencyProperty* property, Value* value, MoonError *error)
1412 if (property == NULL) {
1413 MoonError::FillIn (error, MoonError::ARGUMENT_NULL, 1001,
1414 "NULL property passed to IsValueValid");
1415 return false;
1418 if (value != NULL) {
1419 if (value->Is (Type::EVENTOBJECT) && !value->AsEventObject ()) {
1420 // if it's a null DependencyObject, it doesn't matter what type it is
1421 return true;
1424 if (value->Is (Type::MANAGED)) {
1425 // This is a big hack, we do no type-checking if we try to set a managed type.
1426 // Given that for the moment we might not have the surface available, we can't
1427 // do any type checks since we can't access types registered on the surface.
1428 return true;
1431 if (!Type::IsAssignableFrom (property->GetPropertyType(), value->GetKind())) {
1432 char *error_msg = g_strdup_printf ("DependencyObject::SetValue, value cannot be assigned to the "
1433 "property %s::%s (property has type '%s', value has type '%s')",
1434 GetTypeName (), property->GetName(), Type::Find (property->GetPropertyType())->GetName (),
1435 Type::Find (value->GetKind ())->GetName ());
1436 MoonError::FillIn (error, MoonError::ARGUMENT, 1001, error_msg);
1437 g_free (error_msg);
1438 return false;
1440 } else {
1441 // In 2.0, property->GetPropertyType() can return
1442 // something greater than Type::LASTTYPE. Only check
1443 // built-in types for null Types registered on the
1444 // managed side has their own check there.
1445 if (!CanPropertyBeSetToNull (property)) {
1446 char *error_msg = g_strdup_printf ("Can not set a non-nullable scalar type to NULL (property: %s)",
1447 property->GetName());
1448 MoonError::FillIn (error, MoonError::ARGUMENT, 1001, error_msg);
1449 g_free (error_msg);
1450 return false;
1454 return true;
1457 bool
1458 DependencyObject::CanPropertyBeSetToNull (DependencyProperty* property)
1460 if (property->GetPropertyType () > Type::LASTTYPE)
1461 return true;
1463 if (Type::IsSubclassOf (property->GetPropertyType(), Type::DEPENDENCY_OBJECT))
1464 return true;
1466 if (property->IsNullable ())
1467 return true;
1469 if (Type::IsSubclassOf (property->GetPropertyType (), Type::STRING))
1470 return true;
1472 return false;
1475 bool
1476 DependencyObject::SetValue (int id, Value *value)
1478 if (IsDisposed ())
1479 return false;
1480 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1483 bool
1484 DependencyObject::SetValue (int id, Value value)
1486 if (IsDisposed ())
1487 return false;
1488 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1491 bool
1492 DependencyObject::SetValue (DependencyProperty *property, Value *value)
1494 MoonError err;
1495 return SetValueWithError (property, value, &err);
1498 bool
1499 DependencyObject::SetValue (DependencyProperty *property, Value value)
1501 MoonError err;
1502 return SetValueWithError (property, &value, &err);
1505 bool
1506 DependencyObject::SetValueWithError (DependencyProperty* property, Value value, MoonError *error)
1508 return SetValueWithError (property, &value, error);
1511 bool
1512 DependencyObject::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
1514 if (is_frozen) {
1515 char *error_msg = g_strdup_printf ("Cannot set value for property '%s' on frozen DependencyObject '%s'", property->GetName(), GetTypeName());
1516 MoonError::FillIn (error, MoonError::UNAUTHORIZED_ACCESS, error_msg);
1517 g_free (error_msg);
1518 return false;
1521 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1522 Value *current_value;
1523 bool equal = false;
1525 if (!(current_value = ReadLocalValue (property)))
1526 if (property->IsAutoCreated ())
1527 current_value = autocreate->ReadLocalValue (property);
1529 if (current_value != NULL && value != NULL) {
1530 equal = !property->AlwaysChange() && (*current_value == *value);
1531 } else {
1532 equal = (current_value == NULL) && (value == NULL);
1535 if (!equal) {
1536 Value *new_value;
1538 // remove the old value
1539 g_hash_table_remove (local_values, property);
1541 if (property->IsAutoCreated ())
1542 autocreate->ClearValue (property);
1544 if (value && (!property->IsAutoCreated () || !value->Is (Type::DEPENDENCY_OBJECT) || value->AsDependencyObject () != NULL))
1545 new_value = new Value (*value);
1546 else
1547 new_value = NULL;
1549 // replace it with the new value
1550 if (new_value)
1551 g_hash_table_insert (local_values, property, new_value);
1553 ProviderValueChanged (PropertyPrecedence_LocalValue, property, current_value, new_value, true, true, error);
1555 if (current_value)
1556 delete current_value;
1559 return true;
1562 bool
1563 DependencyObject::SetValueWithError (DependencyProperty *property, Value *value, MoonError *error)
1565 if (!IsValueValid (property, value, error))
1566 return false;
1567 if (!property->Validate (this, value, error))
1568 return false;
1570 return SetValueWithErrorImpl (property, value, error);
1573 struct RegisterNamesClosure {
1574 NameScope *to_ns;
1575 MoonError *error;
1578 static void
1579 register_depobj_names (gpointer key,
1580 gpointer value,
1581 gpointer user_data)
1583 RegisterNamesClosure *closure = (RegisterNamesClosure*)user_data;
1584 if (closure->error->number)
1585 return;
1587 Value *v = (Value*)value;
1589 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1590 DependencyObject *obj = v->AsDependencyObject ();
1591 obj->RegisterAllNamesRootedAt (closure->to_ns, closure->error);
1595 void
1596 DependencyObject::RegisterAllNamesRootedAt (NameScope *to_ns, MoonError *error)
1598 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1600 if (error->number)
1601 return;
1603 bool merge_namescope = false;
1604 bool register_name = false;
1605 bool recurse = false;
1607 NameScope *this_ns = NameScope::GetNameScope(this);
1609 if (this_ns && this_ns->GetTemporary()) {
1610 merge_namescope = true;
1612 else if (!this_ns) {
1613 recurse = true;
1614 register_name = true;
1616 else if (IsHydratedFromXaml ()) {
1617 register_name = true;
1621 if (merge_namescope) {
1622 to_ns->MergeTemporaryScope (this_ns, error);
1623 ClearValue (NameScope::NameScopeProperty, false);
1626 if (register_name) {
1627 const char *n = GetName();
1629 if (n && *n) {
1630 DependencyObject *o = to_ns->FindName (n);
1631 if (o) {
1632 if (o != this) {
1633 char *error_msg = g_strdup_printf ("The name already exists in the tree: %s.", n);
1634 MoonError::FillIn (error, MoonError::ARGUMENT, 2028, error_msg);
1635 g_free (error_msg);
1636 return;
1639 else {
1640 to_ns->RegisterName (n, this);
1645 if (recurse) {
1646 RegisterNamesClosure closure;
1647 closure.to_ns = to_ns;
1648 closure.error = error;
1650 if (autocreate)
1651 g_hash_table_foreach (autocreate->auto_values, register_depobj_names, &closure);
1653 g_hash_table_foreach (local_values, register_depobj_names, &closure);
1657 static void
1658 unregister_depobj_names (gpointer key,
1659 gpointer value,
1660 gpointer user_data)
1662 NameScope *from_ns = (NameScope*)user_data;
1663 Value *v = (Value*)value;
1664 DependencyProperty *property = (DependencyProperty*)key;
1666 if (property->GetId() != UIElement::TagProperty && v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1667 DependencyObject *obj = v->AsDependencyObject ();
1668 obj->UnregisterAllNamesRootedAt (from_ns);
1672 void
1673 DependencyObject::UnregisterAllNamesRootedAt (NameScope *from_ns)
1675 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1677 NameScope *this_ns = NameScope::GetNameScope(this);
1678 if (IsHydratedFromXaml () || this_ns == NULL || this_ns->GetTemporary ()) {
1679 // Unregister in the parent scope
1681 const char *n = GetName();
1683 if (n && strlen (n) > 0)
1684 from_ns->UnregisterName (n);
1687 if (this_ns && !this_ns->GetTemporary())
1688 return;
1690 if (autocreate)
1691 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_names, from_ns);
1693 g_hash_table_foreach (local_values, unregister_depobj_names, from_ns);
1696 bool
1697 DependencyObject::SetName (const char* name, NameScope *scope)
1699 DependencyProperty *property = GetDeployment ()->GetTypes ()->GetProperty (NameProperty);
1701 if (scope->FindName (name))
1702 return false;
1704 Value *new_value = new Value (name);
1705 SetValue (property, new_value);
1706 scope->RegisterName (name, this);
1708 return true;
1711 Value *
1712 DependencyObject::ReadLocalValue (int id)
1714 if (IsDisposed ())
1715 return NULL;
1716 return ReadLocalValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1719 Value *
1720 DependencyObject::ReadLocalValue (DependencyProperty *property)
1722 return (Value *) g_hash_table_lookup (local_values, property);
1725 Value *
1726 DependencyObject::ReadLocalValueWithError (DependencyProperty *property, MoonError *error)
1728 if (!HasProperty (Type::INVALID, property, true)) {
1729 Type *pt = Type::Find (property->GetOwnerType ());
1730 char *error_msg = g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ());
1731 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1732 g_free (error_msg);
1733 return NULL;
1735 return ReadLocalValue (property);
1738 Value *
1739 DependencyObject::GetValueWithError (Type::Kind whatami, DependencyProperty *property, MoonError *error)
1741 if (!HasProperty (whatami, property, true)) {
1742 Type *pt = Type::Find (property->GetOwnerType ());
1743 char *error_msg = g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ());
1744 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1745 g_free (error_msg);
1746 return NULL;
1748 return GetValue (property);
1751 Value *
1752 DependencyObject::GetValue (int id)
1754 if (IsDisposed ())
1755 return NULL;
1756 return GetValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1759 Value *
1760 DependencyObject::GetValue (DependencyProperty *property)
1762 return GetValue (property, PropertyPrecedence_Highest, PropertyPrecedence_Lowest);
1765 Value *
1766 DependencyObject::GetValue (DependencyProperty *property, PropertyPrecedence startingAtPrecedence)
1768 return GetValue (property, startingAtPrecedence, PropertyPrecedence_Lowest);
1771 Value *
1772 DependencyObject::GetValue (DependencyProperty *property, PropertyPrecedence startingAtPrecedence, PropertyPrecedence endingAtPrecedence)
1774 for (int i = startingAtPrecedence; i <= endingAtPrecedence; i ++) {
1775 if (!providers[i])
1776 continue;
1777 Value *value = providers[i]->GetPropertyValue (property);
1778 if (value) return value;
1780 return NULL;
1783 Value *
1784 DependencyObject::GetValueNoDefault (int id)
1786 if (IsDisposed ())
1787 return NULL;
1788 return GetValueNoDefault (GetDeployment ()->GetTypes ()->GetProperty (id));
1791 Value *
1792 DependencyObject::GetValueNoDefault (DependencyProperty *property)
1794 Value *value = NULL;
1796 for (int i = 0; i < PropertyPrecedence_DefaultValue; i ++) {
1797 if (!providers[i])
1798 continue;
1799 value = providers[i]->GetPropertyValue (property);
1800 if (value) break;
1802 return value && !value->GetIsNull () ? value : NULL;
1805 Value *
1806 DependencyObject::GetValueNoDefaultWithError (DependencyProperty *property, MoonError *error)
1808 if (!HasProperty (Type::INVALID, property, true)) {
1809 Type *pt = Type::Find (property->GetOwnerType ());
1810 char *error_msg = g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ());
1811 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1812 g_free (error_msg);
1813 return NULL;
1815 return GetValueNoDefault (property);
1818 void
1819 DependencyObject::ProviderValueChanged (PropertyPrecedence providerPrecedence,
1820 DependencyProperty *property,
1821 Value *old_provider_value, Value *new_provider_value,
1822 bool notify_listeners, bool set_parent, MoonError *error)
1824 int p;
1826 // first we look for a value higher in precedence for this property
1827 for (p = providerPrecedence - 1; p >= PropertyPrecedence_Highest; p --) {
1828 if (providers[p] && providers[p]->GetPropertyValue (property)) {
1829 // a provider higher in precedence already has
1830 // a value for this property, so the one
1831 // that's changing isn't visible anyway.
1832 return;
1836 Value *old_value;
1837 Value *new_value;
1839 if (!old_provider_value || !new_provider_value) {
1840 Value *lower_priority_value = GetValue (property, (PropertyPrecedence)(providerPrecedence + 1));
1842 if (new_provider_value == NULL) {
1843 // we're changing from @old_provider_value to whatever the
1844 // value lower on the priority list is.
1845 old_value = old_provider_value;
1846 new_value = lower_priority_value;
1848 else if (old_provider_value == NULL) {
1849 // we're changing from the old value (from a lower
1850 // priority provider) to @new_provider_value.
1851 old_value = lower_priority_value;
1852 new_value = new_provider_value;
1855 else {
1856 old_value = old_provider_value;
1857 new_value = new_provider_value;
1860 bool equal = false;
1862 if (old_value != NULL && new_value != NULL) {
1863 equal = !property->AlwaysChange() && (*old_value == *new_value);
1864 } else {
1865 equal = false;
1868 if (!equal) {
1869 DependencyObject *old_as_dep = NULL;
1870 DependencyObject *new_as_dep = NULL;
1872 // XXX this flag should be part of the DP metadata.
1873 // we also need to audit other "typeof (object)" DP's
1874 // to make sure they set parent when they should (and
1875 // don't when they shouldn't.)
1876 bool setsParent = set_parent && !property->IsCustom ();
1878 if (old_value && old_value->Is (Type::DEPENDENCY_OBJECT))
1879 old_as_dep = old_value->AsDependencyObject ();
1880 if (new_value && new_value->Is (Type::DEPENDENCY_OBJECT))
1881 new_as_dep = new_value->AsDependencyObject ();
1883 if (old_as_dep && setsParent) {
1884 old_as_dep->SetSurface (NULL);
1886 // unset its parent
1887 old_as_dep->SetParent (NULL, NULL);
1889 // remove ourselves as a target
1890 old_as_dep->RemoveTarget (this);
1892 // unregister from the existing value
1893 old_as_dep->RemovePropertyChangeListener (this, property);
1895 if (old_as_dep->Is(Type::COLLECTION)) {
1896 old_as_dep->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1897 old_as_dep->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1901 if (new_as_dep && setsParent) {
1902 new_as_dep->SetSurface (GetSurface ());
1904 new_as_dep->SetParent (this, error);
1905 if (error->number)
1906 return;
1908 new_as_dep->SetResourceBase (GetResourceBase());
1910 if (new_as_dep->Is(Type::COLLECTION)) {
1911 new_as_dep->AddHandler (Collection::ChangedEvent, collection_changed, this);
1912 new_as_dep->AddHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1915 // listen for property changes on the new object
1916 new_as_dep->AddPropertyChangeListener (this, property);
1918 // add ourselves as a target
1919 new_as_dep->AddTarget (this);
1922 // we need to make this optional, as doing it for NameScope
1923 // merging is killing performance (and noone should ever care
1924 // about that property changing)
1925 if (notify_listeners) {
1926 Value *old_value_copy = old_value == NULL ? NULL : new Value (*old_value);
1927 Value *new_value_copy = new_value == NULL ? NULL : new Value (*new_value);
1929 PropertyChangedEventArgs *args = new PropertyChangedEventArgs (property, property->GetId (), old_value_copy, new_value_copy);
1931 listeners_notified = false;
1933 OnPropertyChanged (args, error);
1935 if (!listeners_notified) {
1936 g_warning ("setting property %s::%s on object of type %s didn't result in listeners being notified",
1937 Type::Find(property->GetOwnerType())->GetName (), property->GetName(), GetTypeName ());
1938 if (error->number)
1939 g_warning ("the error was: %s", error->message);
1942 if (property && property->GetChangedCallback () != NULL) {
1943 PropertyChangeHandler callback = property->GetChangedCallback ();
1944 callback (this, args, error, NULL);
1948 if (InheritedPropertyValueProvider::IsPropertyInherited (property->GetId ()))
1949 InheritedPropertyValueProvider::PropagateInheritedProperty (this, property, old_value_copy, new_value_copy);
1951 args->unref ();
1953 delete old_value_copy;
1954 delete new_value_copy;
1959 void
1960 DependencyObject::ClearValue (int id, bool notify_listeners)
1962 if (IsDisposed ())
1963 return;
1964 ClearValue (GetDeployment ()->GetTypes ()->GetProperty (id), notify_listeners);
1967 void
1968 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners)
1970 ClearValue(property, notify_listeners, NULL);
1973 void
1974 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners, MoonError *error)
1976 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1977 Value *old_local_value;
1979 if (!(old_local_value = ReadLocalValue (property)))
1980 if (property->IsAutoCreated ())
1981 old_local_value = autocreate->ReadLocalValue (property);
1983 // detach from the existing value
1984 if (old_local_value != NULL && old_local_value->Is (Type::DEPENDENCY_OBJECT)) {
1985 DependencyObject *dob = old_local_value->AsDependencyObject();
1987 if (dob != NULL) {
1988 // unset its parent
1989 dob->SetParent (NULL, NULL);
1991 // unregister from the existing value
1992 dob->RemovePropertyChangeListener (this, property);
1993 dob->SetSurface (NULL);
1994 if (dob->Is(Type::COLLECTION)) {
1995 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1996 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
2001 g_hash_table_remove (local_values, property);
2003 if (property->IsAutoCreated ())
2004 autocreate->ClearValue (property);
2006 // this is... yeah, it's disgusting
2007 for (int p = PropertyPrecedence_LocalValue + 1; p < PropertyPrecedence_Count; p ++) {
2008 if (providers[p])
2009 providers[p]->RecomputePropertyValue (property);
2012 ProviderValueChanged (PropertyPrecedence_LocalValue, property, old_local_value, NULL, notify_listeners, true, error);
2014 delete old_local_value;
2017 gboolean
2018 DependencyObject::dispose_value (gpointer key, gpointer value, gpointer data)
2020 DependencyObject *_this = (DependencyObject*)data;
2022 Value *v = (Value *) value;
2024 if (!value)
2025 return TRUE;
2027 // detach from the existing value
2028 if (v->Is (Type::DEPENDENCY_OBJECT)){
2029 DependencyObject *dob = v->AsDependencyObject();
2031 if (dob != NULL) {
2032 if (_this == dob->GetParent()) {
2033 // unset its logical parent
2034 dob->SetParent (NULL, NULL);
2037 // unregister from the existing value
2038 dob->RemovePropertyChangeListener ((DependencyObject*)data, NULL);
2040 if (dob->Is(Type::COLLECTION)) {
2041 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, _this);
2042 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, _this);
2047 delete (Value *) value;
2049 return TRUE;
2052 void
2053 DependencyObject::collection_changed (EventObject *sender, EventArgs *args, gpointer closure)
2055 DependencyObject *obj = (DependencyObject*)closure;
2056 obj->OnCollectionChanged ((Collection*)sender, (CollectionChangedEventArgs*)args);
2059 void
2060 DependencyObject::collection_item_changed (EventObject *sender, EventArgs *args, gpointer closure)
2062 DependencyObject *obj = (DependencyObject*)closure;
2063 CollectionItemChangedEventArgs* itemArgs = (CollectionItemChangedEventArgs*)args;
2065 PropertyChangedEventArgs *propChangedArgs = new PropertyChangedEventArgs (itemArgs->GetProperty(),
2066 itemArgs->GetProperty()->GetId (),
2067 itemArgs->GetOldValue(),
2068 itemArgs->GetNewValue());
2070 obj->OnCollectionItemChanged ((Collection*)sender,
2071 itemArgs->GetCollectionItem(),
2072 propChangedArgs);
2074 propChangedArgs->unref ();
2077 DependencyObject::DependencyObject ()
2078 : EventObject (Type::DEPENDENCY_OBJECT)
2080 Initialize ();
2083 DependencyObject::DependencyObject (Deployment *deployment, Type::Kind object_type)
2084 : EventObject (deployment, object_type)
2086 Initialize ();
2089 DependencyObject::DependencyObject (Type::Kind object_type)
2090 : EventObject (object_type)
2092 Initialize ();
2095 void
2096 DependencyObject::Initialize ()
2098 providers = new PropertyValueProvider*[PropertyPrecedence_Count];
2100 providers[PropertyPrecedence_LocalValue] = new LocalPropertyValueProvider (this, PropertyPrecedence_LocalValue);
2101 providers[PropertyPrecedence_DynamicValue] = NULL; // subclasses will set this if they need it.
2103 providers[PropertyPrecedence_LocalStyle] = NULL; // this is a frameworkelement specific thing
2104 providers[PropertyPrecedence_DefaultStyle] = NULL; // this is a frameworkelement specific thing
2106 providers[PropertyPrecedence_Inherited] = new InheritedPropertyValueProvider (this, PropertyPrecedence_Inherited);
2107 providers[PropertyPrecedence_DefaultValue] = new DefaultValuePropertyValueProvider (this, PropertyPrecedence_DefaultValue);
2108 providers[PropertyPrecedence_AutoCreate] = new AutoCreatePropertyValueProvider (this, PropertyPrecedence_AutoCreate);
2110 local_values = g_hash_table_new (g_direct_hash, g_direct_equal);
2111 listener_list = NULL;
2112 parent = NULL;
2113 is_hydrated = false;
2114 is_frozen = false;
2115 resource_base = NULL;
2116 storage_hash = NULL; // Create it on first usage request
2119 void
2120 DependencyObject::Freeze()
2122 is_frozen = true;
2125 struct CloneClosure {
2126 Types *types;
2127 DependencyObject *old_do;
2128 DependencyObject *new_do;
2131 void
2132 DependencyObject::clone_local_value (DependencyProperty *key, Value *value, gpointer data)
2134 CloneClosure *closure = (CloneClosure*)data;
2136 // don't clone the name property, or we end up with nasty
2137 // duplicate name errors.
2138 if (key->GetId() == DependencyObject::NameProperty)
2139 return;
2141 Value *cv = Value::Clone (value, closure->types);
2143 closure->new_do->SetValue (key, cv);
2145 delete cv;
2148 void
2149 DependencyObject::clone_autocreated_value (DependencyProperty *key, Value *value, gpointer data)
2151 CloneClosure *closure = (CloneClosure*)data;
2153 Value *old_value = closure->old_do->GetValue (key, PropertyPrecedence_AutoCreate);
2155 // this should create the new object
2156 Value *new_value = closure->new_do->GetValue (key, PropertyPrecedence_AutoCreate);
2158 if (old_value && !old_value->GetIsNull() && old_value->Is (Type::DEPENDENCY_OBJECT) &&
2159 new_value && !new_value->GetIsNull() && new_value->Is (Type::DEPENDENCY_OBJECT)) {
2160 DependencyObject *new_obj = new_value->AsDependencyObject(closure->types);
2161 DependencyObject *old_obj = old_value->AsDependencyObject(closure->types);
2163 new_obj->CloneCore (closure->types, old_obj);
2167 void
2168 DependencyObject::clone_animation_storage_list (DependencyProperty *key, List *list, gpointer data)
2170 if (!list || list->IsEmpty ())
2171 return;
2172 DependencyObject *d = (DependencyObject*)data;
2173 d->CloneAnimationStorageList (key, list);
2176 void
2177 DependencyObject::CloneAnimationStorageList (DependencyProperty *key, List *list)
2180 List *newlist = new List();
2182 AnimationStorage::Node *node = (AnimationStorage::Node *) list->First ();
2183 while (node) {
2184 node->storage->SwitchTarget (this);
2185 newlist->Append (node->Clone ());
2186 node = (AnimationStorage::Node*)node->next;
2188 list->Clear (true);
2189 if (!storage_hash)
2190 storage_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2191 g_hash_table_insert (storage_hash, key, newlist);
2194 DependencyObject*
2195 DependencyObject::Clone (Types *types)
2197 Type *t = types->Find (GetObjectType());
2199 DependencyObject *new_do = t->CreateInstance();
2201 if (new_do)
2202 new_do->CloneCore (types, (DependencyObject*)this); // this cast should be unnecessary. but C++ const behavior sucks.
2204 return new_do;
2207 void
2208 DependencyObject::CloneCore (Types *types, DependencyObject* fromObj)
2210 CloneClosure closure;
2211 closure.types = types;
2212 closure.old_do = fromObj;
2213 closure.new_do = this;
2215 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) fromObj->providers[PropertyPrecedence_AutoCreate];
2217 g_hash_table_foreach (autocreate->auto_values, (GHFunc)DependencyObject::clone_autocreated_value, &closure);
2218 g_hash_table_foreach (fromObj->local_values, (GHFunc)DependencyObject::clone_local_value, &closure);
2219 if (fromObj->storage_hash) {
2220 g_hash_table_foreach (fromObj->storage_hash, (GHFunc)DependencyObject::clone_animation_storage_list, this);
2224 static void
2225 clear_storage_list (DependencyProperty *key, List *list, gpointer unused)
2227 List::Node *node = list->First ();
2228 while (node) {
2229 delete ((AnimationStorage::Node*)node)->storage;
2230 node = node->next;
2232 delete list;
2235 DependencyObject::~DependencyObject ()
2237 g_hash_table_destroy (local_values);
2238 local_values = NULL;
2239 delete[] providers;
2240 providers = NULL;
2241 g_free (resource_base);
2244 static void
2245 free_listener (gpointer data, gpointer user_data)
2247 Listener* listener = (Listener*) data;
2248 delete listener;
2251 void
2252 DependencyObject::Dispose ()
2254 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2256 if (listener_list != NULL) {
2257 g_slist_foreach (listener_list, free_listener, NULL);
2258 g_slist_free (listener_list);
2259 listener_list = NULL;
2262 RemoveAllListeners();
2264 if (autocreate)
2265 g_hash_table_foreach_remove (autocreate->auto_values, dispose_value, this);
2267 g_hash_table_foreach_remove (local_values, dispose_value, this);
2269 for (int i = 0; i < PropertyPrecedence_Count; i ++) {
2270 delete providers[i];
2271 providers [i] = NULL;
2274 if (storage_hash) {
2275 GHashTable *tmphash = storage_hash; // animation storages may call back to DetachAnimationStorage
2276 storage_hash = NULL;
2277 g_hash_table_foreach (tmphash, (GHFunc)clear_storage_list, NULL);
2278 g_hash_table_destroy (tmphash);
2281 EventObject::Dispose ();
2284 static void
2285 get_attached_props (gpointer key, gpointer value, gpointer user_data)
2287 DependencyProperty *prop = (DependencyProperty *) key;
2288 GHashTable *props = (GHashTable *) user_data;
2290 if (!(g_hash_table_lookup (props, (gpointer) prop->GetHashKey ())))
2291 g_hash_table_insert (props, (gpointer) prop->GetHashKey (), prop);
2294 static void
2295 hash_keys_to_array (gpointer key, gpointer value, gpointer user_data)
2297 g_ptr_array_add ((GPtrArray *) user_data, key);
2300 static void
2301 hash_values_to_array (gpointer key, gpointer value, gpointer user_data)
2303 g_ptr_array_add ((GPtrArray *) user_data, value);
2306 DependencyProperty **
2307 DependencyObject::GetProperties (bool only_changed)
2309 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2310 DependencyProperty **props;
2311 GHashTable *table;
2312 GPtrArray *array;
2314 array = g_ptr_array_new ();
2316 if (!only_changed) {
2317 // get our class/inherited DependencyProperties
2318 table = GetType ()->CopyProperties (true);
2320 // find any attached properties that have been set
2321 g_hash_table_foreach (local_values, get_attached_props, table);
2323 // dump them to an array
2324 g_hash_table_foreach (table, hash_values_to_array, array);
2325 g_hash_table_destroy (table);
2326 } else {
2327 g_hash_table_foreach (local_values, hash_keys_to_array, array);
2328 g_hash_table_foreach (autocreate->auto_values, hash_keys_to_array, array);
2331 g_ptr_array_add (array, NULL);
2332 props = (DependencyProperty **) array->pdata;
2333 g_ptr_array_free (array, false);
2335 return props;
2338 DependencyProperty *
2339 DependencyObject::GetDependencyProperty (const char *name)
2341 return DependencyProperty::GetDependencyProperty (GetObjectType (), name);
2344 bool
2345 DependencyObject::HasProperty (const char *name, bool inherits)
2347 return DependencyProperty::GetDependencyProperty (GetObjectType (), name, inherits) != NULL;
2350 bool
2351 DependencyObject::HasProperty (Type::Kind whatami, DependencyProperty *property, bool inherits)
2353 Type::Kind this_type = whatami == Type::INVALID ? GetObjectType () : whatami;
2355 // TODO: Handle attached properties correctly.
2357 if (property->IsAttached ())
2358 return true;
2361 printf ("DependencyObject::HasProperty (%p, %i (%s), %p (%i %s.%s), %i)..\n",
2363 whatami, Type::Find (whatami)->name,
2364 property, property->GetOwnerType (), Type::Find (property->GetOwnerType ())->name, property->GetName (),
2365 inherits);
2368 if (property == NULL)
2369 return false;
2371 if (property->GetOwnerType () == this_type)
2372 return true;
2374 if (!inherits)
2375 return false;
2377 if (!Type::IsSubclassOf (this_type, property->GetOwnerType ())) {
2378 bool is_prop_custom = property->IsCustom ();
2379 bool is_owner_custom = property->GetOwnerType () > Type::LASTTYPE;
2380 bool is_this_custom = this_type > Type::LASTTYPE;
2381 bool accept = false;
2383 // Yuck.
2384 // This looks very wrong, but it's what SL seems to do.
2385 if (is_prop_custom) {
2386 if (!is_owner_custom && !is_this_custom) {
2387 // SL does not throw errors for custom properties defined on a builtin type
2388 // and then used on another (unrelated) builtin type (DO.GetValue usage at least)
2389 accept = true;
2390 } else if (is_owner_custom) {
2391 // And this is a custom property defined on a custom type and used anywhere.
2392 accept = true;
2395 return accept;
2398 return true;
2401 DependencyObject *
2402 DependencyObject::FindName (const char *name)
2404 return FindName (name, Control::GetIsTemplateItem (this));
2407 DependencyObject *
2408 DependencyObject::FindName (const char *name, bool template_item)
2410 NameScope *scope = NameScope::GetNameScope (this);
2412 if (scope && (template_item == scope->GetIsLocked ()))
2413 return scope->FindName (name);
2415 if (parent)
2416 return parent->FindName (name, template_item);
2418 return NULL;
2421 NameScope *
2422 DependencyObject::FindNameScope ()
2424 return FindNameScope (Control::GetIsTemplateItem (this));
2427 NameScope*
2428 DependencyObject::FindNameScope (bool template_namescope)
2430 NameScope *scope = NameScope::GetNameScope (this);
2432 // Only template namescopes are locked (for the moment)
2433 if (scope && (template_namescope == scope->GetIsLocked ()))
2434 return scope;
2436 if (parent)
2437 return parent->FindNameScope (template_namescope);
2439 return NULL;
2442 DependencyObject *
2443 DependencyObject::FindName (const char *name, Type::Kind *element_kind)
2445 //printf ("Looking up in %p the string %p\n", obj, name);
2446 //printf (" String: %s\n", name);
2447 DependencyObject *ret = FindName (name);
2449 if (ret == NULL)
2450 return NULL;
2452 *element_kind = ret->GetObjectType ();
2454 return ret;
2457 AnimationStorage*
2458 DependencyObject::GetAnimationStorageFor (DependencyProperty *prop)
2460 if (!storage_hash)
2461 return NULL;
2463 List *list = (List*) g_hash_table_lookup (storage_hash, prop);
2464 if (!list || !list->IsEmpty ())
2465 return NULL;
2467 return ((AnimationStorage::Node *) list->Last())->storage;
2470 AnimationStorage*
2471 DependencyObject::AttachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2473 AnimationStorage* attached_storage = NULL;
2474 // Create hash on first access to save some mem
2475 if (!storage_hash)
2476 storage_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2478 List *list = (List*) g_hash_table_lookup (storage_hash, prop);
2479 if (!list) {
2480 list = new List();
2481 g_hash_table_insert (storage_hash, prop, list);
2483 else if (!list->IsEmpty ()) {
2484 attached_storage = ((AnimationStorage::Node*) list->Last())->storage;
2485 attached_storage->Disable ();
2488 list->Append (new AnimationStorage::Node (prop, storage));
2489 return attached_storage;
2492 void
2493 DependencyObject::DetachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2495 if (!storage_hash)
2496 return;
2498 List *list = (List *) g_hash_table_lookup (storage_hash, prop);
2499 if (!list || list->IsEmpty ())
2500 return;
2502 // if the storage to be removed is the last one, activate the previous one (if any)
2503 if (((AnimationStorage::Node*)list->Last ())->storage == storage) {
2504 list->Remove (list->Last ());
2505 if (!list->IsEmpty ()) {
2506 ((AnimationStorage::Node*)list->Last ())->storage->Enable ();
2509 } else { // if there's more than one storage, that means the storage that was added after
2510 // the one we're removing is configured to reset back to the value of the previous
2511 // storage, so update the stop value so it resets back to the proper value
2512 List::Node *node = list->First ();
2513 while (node) {
2514 if (((AnimationStorage::Node*)node)->storage == storage) {
2515 List::Node *remove = node;
2516 node = node->next;
2517 ((AnimationStorage::Node*)node)->storage->SetStopValue (storage->GetResetValue ());
2518 list->Remove (remove);
2519 break;
2521 node = node->next;
2527 // A helper debugging routine for C#
2529 const char *
2530 dependency_object_get_name (DependencyObject *obj)
2532 return obj->GetName ();
2535 Type::Kind
2536 dependency_object_get_object_type (DependencyObject *obj)
2538 return obj->GetObjectType ();
2541 const char *
2542 dependency_object_get_type_name (DependencyObject *obj)
2544 return obj->GetTypeName ();
2547 // Used by routines which need to create DO from code
2548 void
2549 dependency_object_set_name (DependencyObject *obj, const char *name)
2551 obj->SetValue (DependencyObject::NameProperty, Value (name));
2554 static void
2555 set_surface (gpointer key, gpointer value, gpointer data)
2557 Surface *s = (Surface *) data;
2558 Value *v = (Value *) value;
2560 if (v && v->Is (Type::DEPENDENCY_OBJECT)) {
2561 DependencyObject *dob = v->AsDependencyObject();
2562 if (dob)
2563 dob->SetSurface (s);
2567 void
2568 DependencyObject::SetSurface (Surface *s)
2570 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2572 if (GetSurface() == s)
2573 return;
2575 EventObject::SetSurface (s);
2577 if (autocreate)
2578 g_hash_table_foreach (autocreate->auto_values, set_surface, s);
2580 g_hash_table_foreach (local_values, set_surface, s);
2583 void
2584 DependencyObject::SetParent (DependencyObject *parent, MoonError *error)
2586 if (parent == this->parent)
2587 return;
2589 #if DEBUG
2590 // Check for circular families
2591 DependencyObject *current = parent;
2592 while (current != NULL) {
2593 if (current == this) {
2594 g_warning ("cycle found in logical tree. bailing out");
2595 return;
2597 current = current->GetParent ();
2599 #endif
2601 if (!this->parent) {
2602 if (parent) {
2603 NameScope *this_scope = NameScope::GetNameScope(this);
2604 NameScope *parent_scope = parent->FindNameScope();
2605 if (this_scope) {
2606 if (this_scope->GetTemporary()) {
2607 // if we have a temporary name scope, merge it into the
2608 // closest one up the hierarchy.
2609 if (parent_scope) {
2610 parent_scope->MergeTemporaryScope (this_scope, error);
2611 ClearValue (NameScope::NameScopeProperty, false);
2613 else {
2614 // oddly enough, if
2615 // there's no parent
2616 // namescope, we don't
2617 // do anything
2620 else {
2621 // we have a non-temporary scope. we still have to register the name
2622 // of this element (not the ones in the subtree rooted at this element)
2623 // in the new parent scope. we only register the name in the parent scope
2624 // if the element was hydrated, not when it was created from a string.
2625 if (IsHydratedFromXaml()) {
2626 const char *name = GetName();
2627 if (parent_scope && name && *name) {
2628 DependencyObject *existing_obj = parent_scope->FindName (name);
2629 if (existing_obj != this) {
2630 if (existing_obj) {
2631 char *error_msg = g_strdup_printf ("name `%s' is already registered in new parent namescope.", name);
2632 MoonError::FillIn (error, MoonError::ARGUMENT, error_msg);
2633 return;
2635 parent_scope->RegisterName (name, this);
2641 else {
2642 // we don't have a namescope at all,
2643 // we have to iterate over the subtree
2644 // rooted at this object, and merge
2645 // the names into the parent
2646 // namescope.
2648 if (parent_scope) {
2649 NameScope *temp_scope = new NameScope();
2650 temp_scope->SetTemporary (true);
2652 RegisterAllNamesRootedAt (temp_scope, error);
2654 if (error->number) {
2655 temp_scope->unref ();
2656 return;
2659 parent_scope->MergeTemporaryScope (temp_scope, error);
2661 temp_scope->unref ();
2666 else {
2667 if (!parent) {
2668 NameScope *parent_scope = this->parent->FindNameScope ();
2669 if (parent_scope)
2670 UnregisterAllNamesRootedAt (parent_scope);
2674 if (!error || error->number == 0)
2675 this->parent = parent;
2678 Value *
2679 dependency_object_get_value (DependencyObject *object, DependencyProperty *prop)
2681 if (object == NULL)
2682 return NULL;
2684 return object->GetValue (prop);
2687 Value *
2688 dependency_object_get_value_no_default (DependencyObject *object, DependencyProperty *prop)
2690 if (object == NULL)
2691 return NULL;
2693 return object->GetValueNoDefault (prop);
2696 void
2697 dependency_object_set_value (DependencyObject *object, DependencyProperty *prop, Value *val)
2699 if (object == NULL)
2700 return;
2702 object->SetValue (prop, val);
2705 void
2706 DependencyObject::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
2708 if (DependencyObject::NameProperty == args->GetId ()) {
2709 NameScope *scope = FindNameScope ();
2710 if (scope && args->GetNewValue()) {
2711 if (args->GetOldValue ())
2712 scope->UnregisterName (args->GetOldValue ()->AsString ());
2713 scope->RegisterName (args->GetNewValue()->AsString (), this);
2715 if (IsHydratedFromXaml () && parent) {
2716 // we also need to update any parent
2717 // namescope about our name change
2719 scope = parent->FindNameScope ();
2720 if (scope) {
2721 if (args->GetOldValue ())
2722 scope->UnregisterName (args->GetOldValue ()->AsString ());
2723 scope->RegisterName (args->GetNewValue()->AsString (), this);
2729 NotifyListenersOfPropertyChange (args, error);
2732 DependencyObject*
2733 DependencyObject::GetContent()
2735 const char *content_property_name = GetType()->GetContentPropertyName();
2736 if (!content_property_name)
2737 return NULL;
2739 DependencyProperty *content_property = GetDependencyProperty (content_property_name);
2740 if (!content_property)
2741 return NULL;
2743 Value *content_value = GetValue(content_property);
2745 if (!content_value)
2746 return NULL;
2748 return content_value->AsDependencyObject();