in plugin/:
[moon.git] / src / dependencyobject.cpp
blob9a36225843017cbd5648f5c6b4af32e0672e930b
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 List *context_stack;
88 EventClosure *onevent;
89 List *event_list;
92 class EventLists {
93 public:
94 int size;
95 int emitting;
96 EventList *lists;
98 EventLists (int n)
100 size = n;
101 emitting = 0;
102 lists = new EventList [size];
103 for (int i = 0; i < size; i++) {
104 lists [i].current_token = 1;
105 lists [i].context_stack = new List();
106 lists [i].onevent = NULL;
107 lists [i].event_list = new List ();
111 ~EventLists ()
113 for (int i = 0; i < size; i++) {
114 delete lists [i].event_list;
115 delete lists [i].onevent;
116 delete lists [i].context_stack;
118 delete [] lists;
123 * EventObject
126 #if OBJECT_TRACKING
127 #define OBJECT_TRACK(x,y) Track((x),(y))
128 #else
129 #define OBJECT_TRACK(x,y)
130 #endif
132 EventObject::EventObject ()
134 Initialize (NULL, Type::EVENTOBJECT);
137 EventObject::EventObject (Type::Kind type)
139 Initialize (NULL, type);
142 EventObject::EventObject (Type::Kind type, bool multi_threaded_safe)
144 Initialize (NULL, type);
145 if (multi_threaded_safe)
146 flags |= (gint32) MultiThreadedSafe;
149 EventObject::EventObject (Deployment *deployment)
151 Initialize (deployment, Type::EVENTOBJECT);
154 EventObject::EventObject (Deployment *deployment, Type::Kind type)
156 Initialize (deployment, type);
159 static gint current_id = 0;
161 void
162 EventObject::Initialize (Deployment *depl, Type::Kind type)
164 if (depl == NULL)
165 depl = Deployment::GetCurrent ();
167 object_type = type;
168 deployment = depl;
169 if (deployment != NULL && this != deployment)
170 deployment->ref ();
171 surface = NULL;
172 flags = g_atomic_int_exchange_and_add (&current_id, 1);
173 refcount = 1;
174 events = NULL;
175 toggleNotifyListener = NULL;
177 #if OBJECT_TRACKING
178 switch (object_type) {
179 case Type::INVALID:
180 Track ("Created", "<unknown>");
181 break;
182 case Type::DEPLOYMENT:
183 Track ("Created", "Deployment");
184 break;
185 default:
186 Track ("Created", depl->GetTypes ()->Find (object_type)->GetName ());
187 break;
190 if (object_type != Type::DEPLOYMENT)
191 Deployment::GetCurrent ()->TrackObjectCreated (this);
192 #endif
194 #if SANITY
195 if (object_type == Type::INVALID)
196 g_warning ("EventObject::EventObject (): created object with type: INVALID.\n");
197 if (deployment == NULL)
198 g_warning ("EventObject::EventObject (): created object with a null deployment.\n");
199 #endif
202 EventObject::~EventObject()
204 #if OBJECT_TRACKING
205 if (object_type != Type::DEPLOYMENT)
206 Deployment::GetCurrent ()->TrackObjectDestroyed (this);
207 Track ("Destroyed", "");
208 #endif
210 #if SANITY
211 if (refcount != 0) {
212 g_warning ("EventObject::~EventObject () #%i was deleted before its refcount reached 0 (current refcount: %i)\n", GetId (), refcount);
214 #endif
216 delete events;
218 // We can't unref the deployment in Dispose, it breaks
219 // object tracking.
220 if (deployment && this != deployment) {
221 deployment->unref ();
222 deployment = NULL;
226 static pthread_rwlock_t surface_lock = PTHREAD_RWLOCK_INITIALIZER;
228 bool
229 EventObject::SetSurfaceLock ()
231 int result;
233 if ((result = pthread_rwlock_wrlock (&surface_lock)) != 0) {
234 printf ("EventObject::SetSurface (%p): Couldn't aquire write lock: %s\n", surface, strerror (result));
235 return false;
238 return true;
242 void
243 EventObject::SetSurface (Surface *surface)
245 if (!Surface::InMainThread () && surface != this->surface) {
246 g_warning ("EventObject::SetSurface (): This method must not be called on any other than the main thread!\n");
247 #if DEBUG
248 if (debug_flags & RUNTIME_DEBUG_DP)
249 print_stack_trace ();
250 #endif
251 return;
254 this->surface = surface;
257 void
258 EventObject::SetSurfaceUnlock ()
260 pthread_rwlock_unlock (&surface_lock);
263 void
264 EventObject::AddTickCallSafe (TickCallHandler handler, EventObject *data)
266 int result;
269 * This code assumes that the surface won't be destructed without setting the surface field on to NULL first.
270 * In other words: if we have a read lock here, the surface won't get destroyed, given that setting
271 * the surface field to NULL will block until we release the read lock.
274 if ((result = pthread_rwlock_rdlock (&surface_lock)) != 0) {
275 printf ("EventObject::AddTickCallSafe (): Couldn't aquire read lock: %s\n", strerror (result));
276 return;
279 AddTickCallInternal (handler, data);
281 pthread_rwlock_unlock (&surface_lock);
284 void
285 EventObject::AddTickCall (TickCallHandler handler, EventObject *data)
287 if (!Surface::InMainThread ()) {
288 g_warning ("EventObject::AddTickCall (): This method must not be called on any other than the main thread! Tick call won't be added.\n");
289 #if DEBUG
290 if (debug_flags & RUNTIME_DEBUG_DP)
291 print_stack_trace ();
292 #endif
293 return;
296 AddTickCallInternal (handler, data);
299 void
300 EventObject::AddTickCallInternal (TickCallHandler handler, EventObject *data)
302 Surface *surface;
303 TimeManager *timemanager;
305 surface = GetSurface ();
307 if (surface == NULL)
308 surface = GetDeployment ()->GetSurface ();
310 if (!surface) {
311 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no surface\n");
312 return;
315 timemanager = surface->GetTimeManager ();
317 if (!timemanager) {
318 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no time manager\n");
319 return;
322 timemanager->AddTickCall (handler, data ? data : this);
325 Deployment *
326 EventObject::GetDeployment ()
328 if (deployment == NULL)
329 g_warning ("EventObject::GetDeployment () should not be reached with a null deployment");
331 #if SANITY
332 if (deployment != Deployment::GetCurrent () && Deployment::GetCurrent () != NULL) {
333 g_warning ("EventObject::GetDeployment () our deployment %p doesn't match Deployment::GetCurrent () %p", deployment, Deployment::GetCurrent ());
334 // print_stack_trace ();
336 #endif
338 return deployment;
341 void
342 EventObject::SetCurrentDeployment (bool domain, bool register_thread)
344 if (deployment != NULL) {
345 if (register_thread)
346 Deployment::RegisterThread (deployment);
347 Deployment::SetCurrent (deployment, domain);
351 Surface *
352 EventObject::GetSurface ()
354 #if 0
355 Deployment *current_deployment = Deployment::GetCurrent ();
356 Application *current_application;
357 current_application = deployment != NULL ? deployment->GetCurrentApplication () : (current_deployment != NULL ? current_deployment->GetCurrentApplication () : NULL);
359 if (deployment != NULL && deployment != current_deployment) {
360 printf ("EventObject::GetSurface (): WARNING deployment: %p, Deployment::GetCurrent (): %p type: %s, id: %i\n", deployment, current_deployment, GetTypeName (), GET_OBJ_ID (this));
361 print_stack_trace ();
363 // current_application is null in the Surface ctor
364 if (!(current_application == NULL || surface == NULL || current_application->GetSurface () == surface))
365 printf ("EventObject::GetSurface (): assert failed: current application: %p, surface: %p, current application's surface: %p\n", current_application, surface, current_application->GetSurface ());
366 #endif
368 return surface; // When surface have been removed, return the Surface stored on the Deployment.
371 void
372 EventObject::Dispose ()
374 if (!IsDisposed () && Surface::InMainThread ()) {
375 // Dispose can be called multiple times, but Emit only once. When DestroyedEvent was in the dtor,
376 // it could only ever be emitted once, don't change that behaviour.
377 Emit (DestroyedEvent); // TODO: Rename to DisposedEvent
380 SetSurface (NULL);
381 // Remove attached flag and set the disposed flag.
382 flags = (Flags) (flags & ~Attached);
383 flags = (Flags) (flags | Disposed);
386 bool
387 EventObject::IsDisposed ()
389 return (flags & Disposed) != 0;
392 void
393 EventObject::ref ()
395 int v = g_atomic_int_exchange_and_add (&refcount, 1);
397 #if DEBUG
398 if (GetObjectType () != object_type)
399 printf ("EventObject::ref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
401 if (deployment != Deployment::GetCurrent ()) {
402 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 ());
403 // print_stack_trace ();
405 #endif
407 if (v == 0) {
408 // Here something really bad happened, this object is probably being reffed again because
409 // of some action in the destructor. There is no way to recover from this now, no matter what
410 // we do the code that called ref now will be accessing a deleted object later on, which may or
411 // may not crash. It might very well be an exploitable security problem. Anyways when unref is called, we
412 // have a second delete on the same object, which *will* crash. To make things easier and safer
413 // lets just abort right away.
414 #if OBJECT_TRACKING
415 PrintStackTrace ();
416 #endif
417 // Due to our mixed usage of Dispose and dtor, currently there are valid cases of reffing
418 // an object with refcount = 0. Use a warning instead of error until the mixed usage is
419 // gone.
420 g_warning ("Ref was called on an object with a refcount of 0.\n");
422 } else if (v == 1 && toggleNotifyListener) {
423 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
424 toggleNotifyListener->Invoke (false);
427 OBJECT_TRACK ("Ref", GetTypeName ());
430 void
431 EventObject::unref ()
433 // we need to retrieve all instance fields into locals before decreasing the refcount
434 // TODO: do we need some sort of gcc foo (volatile variables, memory barries)
435 // to ensure that gcc does not optimize the fetches below away
436 ToggleNotifyListener *toggle_listener = this->toggleNotifyListener;
437 #if OBJECT_TRACKING
438 Deployment *depl = this->deployment ? this->deployment : Deployment::GetCurrent ();
439 const char *type_name = depl == NULL ? NULL : Type::Find (depl, GetObjectType ())->GetName ();
440 #endif
442 #if SANITY
443 if (GetObjectType () != object_type)
444 printf ("EventObject::unref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
445 #endif
447 if (!IsMultiThreadedSafe () && !Surface::InMainThread ()) {
448 unref_delayed ();
449 return;
452 int v = g_atomic_int_exchange_and_add (&refcount, -1) - 1;
454 // from now on we can't access any instance fields if v > 0
455 // since another thread might have unreffed and caused our destruction
457 if (v == 0 && events != NULL && events->emitting) {
458 g_atomic_int_exchange_and_add (&refcount, 1);
459 unref_delayed ();
460 return;
463 OBJECT_TRACK ("Unref", type_name);
465 if (v == 0) {
466 // here we *can* access instance fields, since we know that we haven't been
467 // desctructed already.
468 Dispose ();
470 #if SANITY
471 if ((flags & Disposed) == 0)
472 printf ("EventObject::unref (): the type '%s' (or any of its parent types) forgot to call its base class' Dispose method.\n", GetTypeName ());
473 #endif
475 // We need to check again if the refcount really is zero,
476 // the object might have resurrected in the Dispose.
477 // TODO: we should disallow resurrection, it's not thread-safe
478 // if we got resurrected and unreffed, we'd be deleted by now
479 // in which case we'll double free here.
480 v = g_atomic_int_get (&refcount);
481 if (v == 0)
482 delete this;
484 } else if (v == 1 && toggle_listener) {
485 // 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
486 // note that the instance field might point to garbage after decreasing the refcount above, so we access the local variable we
487 // retrieved before decreasing the refcount.
488 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
489 toggle_listener->Invoke (true);
492 #if SANITY
493 if (v < 0) {
494 g_warning ("EventObject::Unref (): NEGATIVE REFCOUNT id: %i v: %i refcount: %i", GET_OBJ_ID (this), v, refcount);
495 print_stack_trace ();
497 #endif
500 void
501 EventObject::AddToggleRefNotifier (ToggleNotifyHandler tr)
503 if (toggleNotifyListener)
504 return;
506 this->ref ();
507 toggleNotifyListener = new ToggleNotifyListener (this, tr);
510 void
511 EventObject::RemoveToggleRefNotifier ()
513 if (!toggleNotifyListener)
514 return;
516 delete toggleNotifyListener;
517 toggleNotifyListener = NULL;
518 this->unref ();
521 #if OBJECT_TRACKING
522 // Define the ID of the object you want to track
523 // Object creation, destruction and all ref/unrefs
524 // are logged to the console, with a stacktrace.
525 static bool object_id_fetched = false;
526 static int object_id = -1;
527 static const char *track_object_type = NULL;
528 static bool use_visi_output = false;
529 static bool track_all = false;
531 #define OBJECT_TRACK_ID (0)
533 GHashTable* EventObject::objects_alive = NULL;
535 void
536 EventObject::Track (const char* done, const char* typname)
538 int id = GetId ();
539 if (!object_id_fetched) {
540 object_id_fetched = true;
541 char *sval = getenv ("MOONLIGHT_OBJECT_TRACK_ID");
542 if (sval)
543 object_id = atoi (sval);
544 track_object_type = getenv ("MOONLIGHT_OBJECT_TRACK_TYPE");
545 use_visi_output = (getenv ("MOONLIGHT_OBJECT_TRACK_VISI") != NULL);
546 track_all = (getenv ("MOONLIGHT_OBJECT_TRACK_ALL") != NULL);
549 if (track_all)
550 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
552 if (id == object_id || (track_object_type != NULL && typname != NULL && strcmp (typname, track_object_type) == 0)) {
553 char *st = NULL;
554 // load the stack trace before we print anything
555 // this way there's a lot smaller chance of
556 // ending up with other output between the first line (tracked object of type...)
557 // and the stack trace when using multiple threads.
558 if (!use_visi_output)
559 st = get_stack_trace ();
561 if (!track_all)
562 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
564 if (!use_visi_output) {
565 printf("%s", st);
566 } else {
567 print_reftrace (done, typname, refcount, false);
569 g_free (st);
573 char *
574 EventObject::GetStackTrace (const char* prefix)
576 return get_stack_trace_prefix (prefix);
579 void
580 EventObject::PrintStackTrace ()
582 print_stack_trace ();
584 #endif
587 EventObject::AddHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
589 int id = GetType()->LookupEvent (event_name);
591 if (id == -1) {
592 g_warning ("adding handler to event '%s', which has not been registered\n", event_name);
593 return -1;
596 return AddHandler (id, handler, data, data_dtor);
600 EventObject::AddHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
602 if (GetType()->GetEventCount() <= 0) {
603 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
604 return -1;
607 if (events == NULL)
608 events = new EventLists (GetType ()->GetEventCount ());
610 int token = events->lists [event_id].current_token++;
612 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, token));
614 return token;
617 void
618 EventObject::AddOnEventHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
620 if (GetType()->GetEventCount() <= event_id) {
621 g_warning ("adding OnEvent handler to event with id %d, which has not been registered\n", event_id);
622 return;
625 if (events == NULL)
626 events = new EventLists (GetType ()->GetEventCount ());
628 events->lists [event_id].onevent = new EventClosure (handler, data, data_dtor, 0);
631 void
632 EventObject::RemoveOnEventHandler (int event_id, EventHandler handler, gpointer data)
634 if (GetType()->GetEventCount() <= event_id) {
635 g_warning ("adding OnEvent handler to event with id %d, which has not been registered\n", event_id);
636 return;
639 if (events == NULL)
640 events = new EventLists (GetType ()->GetEventCount ());
642 if (events->lists [event_id].onevent) {
643 // FIXME check handler + data
644 delete events->lists [event_id].onevent;
645 events->lists [event_id].onevent = NULL;
650 EventObject::AddXamlHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
652 int id = GetType ()->LookupEvent (event_name);
654 if (id == -1) {
655 g_warning ("adding xaml handler to event '%s', which has not been registered\n", event_name);
656 return -1;
659 return AddXamlHandler (id, handler, data, data_dtor);
663 EventObject::AddXamlHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
665 if (GetType ()->GetEventCount () <= 0) {
666 g_warning ("adding xaml handler to event with id %d, which has not been registered\n", event_id);
667 return -1;
670 if (events == NULL)
671 events = new EventLists (GetType ()->GetEventCount ());
673 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, 0));
675 return 0;
678 void
679 EventObject::RemoveHandler (const char *event_name, EventHandler handler, gpointer data)
681 int id = GetType()->LookupEvent (event_name);
683 if (id == -1) {
684 g_warning ("removing handler for event '%s', which has not been registered\n", event_name);
685 return;
688 RemoveHandler (id, handler, data);
691 void
692 EventObject::RemoveHandler (int event_id, EventHandler handler, gpointer data)
694 if (GetType()->GetEventCount() <= 0) {
695 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
696 return;
699 if (events == NULL)
700 events = new EventLists (GetType ()->GetEventCount ());
702 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
703 while (closure) {
704 if (closure->func == handler && closure->data == data) {
705 if (!events->lists [event_id].context_stack->IsEmpty()) {
706 closure->pending_removal = true;
707 } else {
708 events->lists [event_id].event_list->Remove (closure);
710 break;
713 closure = (EventClosure *) closure->next;
717 void
718 EventObject::RemoveHandler (int event_id, int token)
720 if (GetType()->GetEventCount() <= 0) {
721 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
722 return;
725 if (events == NULL)
726 events = new EventLists (GetType ()->GetEventCount ());
728 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
729 while (closure) {
730 if (closure->token == token) {
731 if (!events->lists [event_id].context_stack->IsEmpty()) {
732 closure->pending_removal = true;
733 } else {
734 events->lists [event_id].event_list->Remove (closure);
736 break;
739 closure = (EventClosure *) closure->next;
743 void
744 EventObject::RemoveAllHandlers (gpointer data)
746 if (events == NULL)
747 events = new EventLists (GetType ()->GetEventCount ());
749 int count = GetType ()->GetEventCount ();
751 for (int i = 0; i < count - 1; i++) {
752 EventClosure *closure = (EventClosure *) events->lists [i].event_list->First ();
753 while (closure) {
754 if (closure->data == data) {
755 if (!events->lists [i].context_stack->IsEmpty()) {
756 closure->pending_removal = true;
757 } else {
758 events->lists [i].event_list->Remove (closure);
760 break;
763 closure = (EventClosure *) closure->next;
768 void
769 EventObject::RemoveMatchingHandlers (int event_id, bool (*predicate)(EventHandler cb_handler, gpointer cb_data, gpointer data), gpointer closure)
771 if (GetType()->GetEventCount() <= 0) {
772 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
773 return;
776 if (events == NULL)
777 events = new EventLists (GetType ()->GetEventCount ());
779 EventClosure *c = (EventClosure *) events->lists [event_id].event_list->First ();
780 while (c) {
781 if (predicate (c->func, c->data, closure)) {
782 if (!events->lists [event_id].context_stack->IsEmpty()) {
783 c->pending_removal = true;
784 } else {
785 events->lists [event_id].event_list->Remove (c);
787 break;
790 c = (EventClosure *) c->next;
795 EventObject::GetEventGeneration (int event_id)
797 if (GetType()->GetEventCount() <= 0) {
798 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
799 return -1;
802 if (events == NULL)
803 events = new EventLists (GetType ()->GetEventCount ());
805 return events->lists [event_id].current_token;
808 bool
809 EventObject::CanEmitEvents ()
811 if (IsDisposed ())
812 return false;
814 if (deployment == NULL)
815 return false; /* how did this happen? */
817 if (deployment == this)
818 return true; /* Deployment::ShuttingDownEvent and Deployment::AppDomainUnloadedEvent */
820 if (deployment->IsShuttingDown ())
821 return false; /* don't emit events after we've started shutting down (except the two events on Deployment, which are used for proper shutdown) */
823 return true;
826 bool
827 EventObject::EmitAsync (const char *event_name, EventArgs *calldata, bool only_unemitted)
829 int event_id;
831 if (!CanEmitEvents ()) {
832 if (calldata)
833 calldata->unref ();
834 return false;
837 if ((event_id = GetType()->LookupEvent (event_name)) == -1) {
838 g_warning ("trying to emit event '%s', which has not been registered\n", event_name);
839 if (calldata)
840 calldata->unref ();
841 return false;
844 return EmitAsync (event_id, calldata, only_unemitted);
847 bool
848 EventObject::Emit (const char *event_name, EventArgs *calldata, bool only_unemitted, int starting_generation)
850 if (!CanEmitEvents ()) {
851 if (calldata)
852 calldata->unref ();
853 return false;
856 int id = GetType()->LookupEvent (event_name);
858 if (id == -1) {
859 g_warning ("trying to emit event '%s', which has not been registered\n", event_name);
860 if (calldata)
861 calldata->unref ();
862 return false;
865 return Emit (id, calldata, only_unemitted, starting_generation);
868 class AsyncEventClosure : public EventObject {
869 public:
870 EventObject *sender;
871 EventArgs *args;
872 bool unemitted;
873 int generation;
874 int event_id;
876 AsyncEventClosure (EventObject *sender, int event_id, EventArgs *args, bool unemitted, int generation)
878 this->sender = sender;
879 this->event_id = event_id;
880 this->args = args;
881 this->unemitted = unemitted;
882 this->generation = generation;
884 sender->ref ();
887 protected:
888 virtual ~AsyncEventClosure ()
890 sender->unref ();
894 void
895 EventObject::emit_async (EventObject *calldata)
897 AsyncEventClosure *async = (AsyncEventClosure *) calldata;
899 async->sender->Emit (async->event_id, async->args, async->unemitted, async->generation);
901 async->unref ();
904 bool
905 EventObject::EmitAsync (int event_id, EventArgs *calldata, bool only_unemitted)
907 if (!CanEmitEvents ()) {
908 if (calldata)
909 calldata->unref ();
910 return false;
913 if (events == NULL)
914 events = new EventLists (GetType ()->GetEventCount ());
916 AddTickCall (EventObject::emit_async, new AsyncEventClosure (this, event_id, calldata, only_unemitted, GetEventGeneration (event_id)));
918 return true;
921 struct EmitData {
922 EventObject *sender;
923 int event_id;
924 EventArgs *calldata;
925 bool only_unemitted;
928 gboolean
929 EventObject::EmitCallback (gpointer d)
931 EmitData *data = (EmitData *) d;
932 data->sender->SetCurrentDeployment ();
933 data->sender->Emit (data->event_id, data->calldata, data->only_unemitted);
934 data->sender->unref ();
935 delete data;
936 #if SANITY
937 Deployment::SetCurrent (NULL);
938 #endif
939 return FALSE;
942 bool
943 EventObject::Emit (int event_id, EventArgs *calldata, bool only_unemitted, int starting_generation)
945 if (!CanEmitEvents ()) {
946 if (calldata)
947 calldata->unref ();
948 return false;
951 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
952 g_warning ("trying to emit event with id %d, which has not been registered\n", event_id);
953 if (calldata)
954 calldata->unref ();
955 return false;
958 if (events == NULL)
959 events = new EventLists (GetType ()->GetEventCount ());
961 if (events->lists [event_id].event_list->IsEmpty () && events->lists [event_id].onevent == NULL) {
962 if (calldata)
963 calldata->unref ();
964 return false;
967 if (!Surface::InMainThread ()) {
968 Surface *surface = deployment ? deployment->GetSurface () : NULL;
970 if (surface == NULL) {
971 printf ("EventObject::Emit (): could not emit event, the deployment %p does not have a surface.\n", deployment);
972 return false;
975 EmitData *data = new EmitData ();
976 data->sender = this;
977 data->sender->ref ();
978 data->event_id = event_id;
979 data->calldata = calldata;
980 data->only_unemitted = only_unemitted;
981 surface->GetTimeManager ()->AddTimeout (MOON_PRIORITY_HIGH, 1, EmitCallback, data);
982 return false;
985 EmitContext* ctx = StartEmit (event_id, only_unemitted, starting_generation);
986 DoEmit (event_id, calldata);
988 FinishEmit (event_id, ctx);
990 return true;
993 EmitContext*
994 EventObject::StartEmit (int event_id, bool only_unemitted, int starting_generation)
996 if (events == NULL)
997 events = new EventLists (GetType ()->GetEventCount ());
999 EmitContext *ctx = new EmitContext();
1000 ctx->only_unemitted = only_unemitted;
1001 ctx->starting_generation = starting_generation;
1002 EventClosure *closure;
1004 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
1005 g_warning ("trying to start emit with id %d, which has not been registered\n", event_id);
1006 return ctx;
1009 events->emitting++;
1011 events->lists [event_id].context_stack->Prepend (new EmitContextNode (ctx));
1013 if (events->lists [event_id].event_list->IsEmpty ())
1014 return ctx;
1016 ctx->length = events->lists [event_id].event_list->Length();
1017 ctx->closures = g_new (EventClosure*, ctx->length);
1019 /* make a copy of the event list to use for emitting */
1020 closure = (EventClosure *) events->lists [event_id].event_list->First ();
1021 for (int i = 0; closure != NULL; i ++) {
1022 ctx->closures[i] = closure->pending_removal ? NULL : closure;
1023 closure = (EventClosure *) closure->next;
1026 return ctx;
1029 bool
1030 EventObject::DoEmit (int event_id, EventArgs *calldata)
1032 if (events->lists [event_id].context_stack->IsEmpty ()) {
1033 g_warning ("DoEmit called with no EmitContexts");
1034 return false;
1037 EmitContext *ctx = ((EmitContextNode*)events->lists [event_id].context_stack->First())->GetEmitContext();
1039 if (events->lists [event_id].onevent) {
1040 EventClosure *closure = events->lists [event_id].onevent;
1041 closure->func (this, calldata, closure->data);
1043 else {
1044 DoEmitCurrentContext (event_id, calldata);
1047 if (calldata)
1048 calldata->unref ();
1050 return ctx->length > 0;
1053 void
1054 EventObject::DoEmitCurrentContext (int event_id, EventArgs *calldata)
1056 if (events->lists [event_id].context_stack->IsEmpty()) {
1057 g_warning ("DoEmitCurrentContext called with no EmitContexts");
1058 return;
1061 EmitContext *ctx = ((EmitContextNode*)events->lists [event_id].context_stack->First())->GetEmitContext();
1063 /* emit the events using the copied list in the context*/
1064 for (int i = 0; i < ctx->length; i++) {
1065 if (calldata && calldata->Is (Type::ROUTEDEVENTARGS)) {
1066 RoutedEventArgs *rea = (RoutedEventArgs*)calldata;
1067 if (rea->GetHandled ())
1068 break;
1071 EventClosure *closure = ctx->closures[i];
1073 if (closure && closure->func
1074 && (!ctx->only_unemitted || closure->emit_count == 0)
1075 && (ctx->starting_generation == -1 || closure->token < ctx->starting_generation)) {
1076 closure->func (this, calldata, closure->data);
1078 closure->emit_count ++;
1083 void
1084 EventObject::FinishEmit (int event_id, EmitContext *ctx)
1086 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
1087 g_warning ("trying to finish emit with id %d, which has not been registered\n", event_id);
1088 return;
1091 if (events->lists [event_id].context_stack->IsEmpty()) {
1092 g_warning ("FinishEmit called with no EmitContexts");
1093 return;
1096 EmitContextNode *first_node = (EmitContextNode*)events->lists [event_id].context_stack->First();
1097 EmitContext *first_ctx = first_node->GetEmitContext();
1099 if (first_ctx != ctx) {
1100 g_warning ("FinishEmit called out of order");
1101 return;
1104 events->lists [event_id].context_stack->Unlink (first_node);
1106 delete first_node;
1107 events->emitting--;
1109 if (events->lists [event_id].context_stack->IsEmpty ()) {
1110 // Remove closures which are waiting for removal
1111 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
1112 while (closure != NULL) {
1113 EventClosure *next = (EventClosure *) closure->next;
1114 if (closure->pending_removal)
1115 events->lists [event_id].event_list->Remove (closure);
1116 closure = next;
1121 void
1122 EventObject::unref_delayed ()
1124 Deployment *depl;
1126 OBJECT_TRACK ("DelayedUnref", GetTypeName ());
1128 // access deployment as long as we have it (until Dispose has been called),
1129 // after that access the static deployment.
1130 depl = deployment ? deployment : Deployment::GetCurrent ();
1131 depl->UnrefDelayed (this);
1134 class Listener {
1135 public:
1136 virtual bool Matches (PropertyChangedEventArgs *args) = 0;
1137 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error) = 0;
1139 virtual gpointer GetListener () = 0;
1140 virtual gpointer GetProperty () = 0;
1143 class WildcardListener : public Listener {
1144 public:
1145 WildcardListener (DependencyObject *obj, DependencyProperty *prop)
1147 this->obj = obj;
1148 this->prop = prop;
1151 virtual bool Matches (PropertyChangedEventArgs *args) { return true; }
1152 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
1154 // FIXME we ignore error here.
1155 obj->OnSubPropertyChanged (prop, sender, args);
1158 virtual gpointer GetListener ()
1160 return obj;
1163 virtual gpointer GetProperty ()
1165 return prop;
1168 private:
1170 DependencyObject *obj;
1171 DependencyProperty *prop;
1174 class CallbackListener : public Listener {
1175 public:
1176 CallbackListener (DependencyProperty *prop, PropertyChangeHandler cb, gpointer closure)
1178 this->prop = prop;
1179 this->cb = cb;
1180 this->closure = closure;
1183 virtual bool Matches (PropertyChangedEventArgs *args)
1185 return prop == args->GetProperty ();
1188 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
1190 cb (sender, args, error, closure);
1193 virtual gpointer GetListener ()
1195 return (gpointer)cb;
1198 virtual gpointer GetProperty ()
1200 return prop;
1203 private:
1204 PropertyChangeHandler cb;
1205 DependencyProperty *prop;
1206 gpointer closure;
1210 // Registers @listener as a listener on changes to @child_property of this DO.
1212 void
1213 DependencyObject::AddPropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1215 listener_list = g_slist_append (listener_list, new WildcardListener (listener, child_property));
1218 void
1219 DependencyObject::RemoveListener (gpointer listener, DependencyProperty *child_property)
1221 GSList *next;
1222 for (GSList *l = listener_list; l; l = next) {
1223 next = l->next;
1224 Listener *listen = (Listener *) l->data;
1226 if ((listen->GetListener() == listener)
1227 && (child_property == NULL || listen->GetProperty() == child_property)) {
1228 listener_list = g_slist_delete_link (listener_list, l);
1229 delete listen;
1235 // Unregisters @container as a listener on changes to @child_property of this DO.
1237 void
1238 DependencyObject::RemovePropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1240 RemoveListener (listener, child_property);
1243 void
1244 DependencyObject::AddPropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb, gpointer closure)
1246 listener_list = g_slist_append (listener_list, new CallbackListener (property, cb, closure));
1249 void
1250 DependencyObject::RemovePropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb)
1252 RemoveListener ((gpointer)cb, property);
1255 static void
1256 unregister_depobj_values (gpointer key,
1257 gpointer value,
1258 gpointer user_data)
1260 DependencyObject *this_obj = (DependencyObject*)user_data;
1261 //DependencyProperty *prop = (DependencyProperty*)key;
1262 Value *v = (Value*)value;
1264 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1265 //printf ("unregistering from property %s\n", prop->name);
1266 DependencyObject *obj = v->AsDependencyObject ();
1267 obj->RemovePropertyChangeListener (this_obj);
1268 obj->SetParent (NULL, NULL);
1272 void
1273 DependencyObject::RemoveAllListeners ()
1275 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1277 if (autocreate)
1278 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_values, this);
1280 g_hash_table_foreach (local_values, unregister_depobj_values, this);
1283 static bool listeners_notified;
1285 void
1286 DependencyObject::NotifyListenersOfPropertyChange (PropertyChangedEventArgs *args, MoonError *error)
1288 g_return_if_fail (args);
1290 listeners_notified = true;
1292 for (GSList *l = listener_list; l != NULL; l = l->next){
1293 Listener *listener = (Listener*)l->data;
1295 if (listener->Matches (args))
1296 listener->Invoke (this, args, error);
1297 if (error && error->number)
1298 break;
1302 void
1303 DependencyObject::NotifyListenersOfPropertyChange (int id, MoonError *error)
1305 if (IsDisposed ())
1306 return;
1307 NotifyListenersOfPropertyChange (GetDeployment ()->GetTypes ()->GetProperty (id), error);
1310 void
1311 DependencyObject::NotifyListenersOfPropertyChange (DependencyProperty *subproperty, MoonError *error)
1313 // XXX I really think this method should go away. we only use it in
1314 // a couple of places, and it abuses things.
1316 Value *new_value = subproperty ? GetValue (subproperty) : NULL;
1318 PropertyChangedEventArgs *args = new PropertyChangedEventArgs (subproperty, subproperty->GetId (), NULL, new_value);
1320 NotifyListenersOfPropertyChange (args, error);
1322 args->unref ();
1325 bool
1326 DependencyObject::IsValueValid (DependencyProperty* property, Value* value, MoonError *error)
1328 if (property == NULL) {
1329 MoonError::FillIn (error, MoonError::ARGUMENT_NULL, 1001,
1330 "NULL property passed to IsValueValid");
1331 return false;
1334 if (value != NULL) {
1335 if (value->Is (Type::EVENTOBJECT) && !value->AsEventObject ()) {
1336 // if it's a null DependencyObject, it doesn't matter what type it is
1337 return true;
1340 if (value->Is (Type::MANAGED)) {
1341 // This is a big hack, we do no type-checking if we try to set a managed type.
1342 // Given that for the moment we might not have the surface available, we can't
1343 // do any type checks since we can't access types registered on the surface.
1344 return true;
1347 if (!Type::IsAssignableFrom (property->GetPropertyType(), value->GetKind())) {
1348 char *error_msg = g_strdup_printf ("DependencyObject::SetValue, value cannot be assigned to the "
1349 "property %s::%s (property has type '%s', value has type '%s')",
1350 GetTypeName (), property->GetName(), Type::Find (property->GetPropertyType())->GetName (),
1351 Type::Find (value->GetKind ())->GetName ());
1352 MoonError::FillIn (error, MoonError::ARGUMENT, 1001, error_msg);
1353 g_free (error_msg);
1354 return false;
1356 } else {
1357 // In 2.0, property->GetPropertyType() can return
1358 // something greater than Type::LASTTYPE. Only check
1359 // built-in types for null Types registered on the
1360 // managed side has their own check there.
1361 if (!CanPropertyBeSetToNull (property)) {
1362 char *error_msg = g_strdup_printf ("Can not set a non-nullable scalar type to NULL (property: %s)",
1363 property->GetName());
1364 MoonError::FillIn (error, MoonError::ARGUMENT, 1001, error_msg);
1365 g_free (error_msg);
1366 return false;
1370 return true;
1373 bool
1374 DependencyObject::CanPropertyBeSetToNull (DependencyProperty* property)
1376 if (property->GetPropertyType () > Type::LASTTYPE)
1377 return true;
1379 if (Type::IsSubclassOf (property->GetPropertyType(), Type::DEPENDENCY_OBJECT))
1380 return true;
1382 if (property->IsNullable ())
1383 return true;
1385 if (Type::IsSubclassOf (property->GetPropertyType (), Type::STRING))
1386 return true;
1388 return false;
1391 bool
1392 DependencyObject::SetValue (int id, Value *value)
1394 if (IsDisposed ())
1395 return false;
1396 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1399 bool
1400 DependencyObject::SetValue (int id, Value value)
1402 if (IsDisposed ())
1403 return false;
1404 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1407 bool
1408 DependencyObject::SetValue (DependencyProperty *property, Value *value)
1410 MoonError err;
1411 return SetValueWithError (property, value, &err);
1414 bool
1415 DependencyObject::SetValue (DependencyProperty *property, Value value)
1417 MoonError err;
1418 return SetValueWithError (property, &value, &err);
1421 bool
1422 DependencyObject::SetValueWithError (DependencyProperty* property, Value value, MoonError *error)
1424 return SetValueWithError (property, &value, error);
1427 bool
1428 DependencyObject::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
1430 if (is_frozen) {
1431 char *error_msg = g_strdup_printf ("Cannot set value for property '%s' on frozen DependencyObject '%s'", property->GetName(), GetTypeName());
1432 MoonError::FillIn (error, MoonError::UNAUTHORIZED_ACCESS, error_msg);
1433 g_free (error_msg);
1434 return false;
1437 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1438 Value *current_value;
1439 bool equal = false;
1441 if (!(current_value = ReadLocalValue (property)))
1442 if (property->IsAutoCreated ())
1443 current_value = autocreate->ReadLocalValue (property);
1445 if (current_value != NULL && value != NULL) {
1446 equal = !property->AlwaysChange() && (*current_value == *value);
1447 } else {
1448 equal = (current_value == NULL) && (value == NULL);
1451 if (!equal) {
1452 Value *new_value;
1454 // remove the old value
1455 g_hash_table_remove (local_values, property);
1457 if (property->IsAutoCreated ())
1458 autocreate->ClearValue (property);
1460 if (value && (!property->IsAutoCreated () || !value->Is (Type::DEPENDENCY_OBJECT) || value->AsDependencyObject () != NULL))
1461 new_value = new Value (*value);
1462 else
1463 new_value = NULL;
1465 // replace it with the new value
1466 if (new_value)
1467 g_hash_table_insert (local_values, property, new_value);
1469 ProviderValueChanged (PropertyPrecedence_LocalValue, property, current_value, new_value, true, true, error);
1471 if (current_value)
1472 delete current_value;
1475 return true;
1478 bool
1479 DependencyObject::SetValueWithError (DependencyProperty *property, Value *value, MoonError *error)
1481 if (!IsValueValid (property, value, error))
1482 return false;
1483 if (!property->Validate (this, value, error))
1484 return false;
1486 return SetValueWithErrorImpl (property, value, error);
1489 struct RegisterNamesClosure {
1490 NameScope *to_ns;
1491 MoonError *error;
1494 static void
1495 register_depobj_names (gpointer key,
1496 gpointer value,
1497 gpointer user_data)
1499 RegisterNamesClosure *closure = (RegisterNamesClosure*)user_data;
1500 if (closure->error->number)
1501 return;
1503 Value *v = (Value*)value;
1505 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1506 DependencyObject *obj = v->AsDependencyObject ();
1507 obj->RegisterAllNamesRootedAt (closure->to_ns, closure->error);
1511 void
1512 DependencyObject::RegisterAllNamesRootedAt (NameScope *to_ns, MoonError *error)
1514 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1516 if (error->number)
1517 return;
1519 bool merge_namescope = false;
1520 bool register_name = false;
1521 bool recurse = false;
1523 NameScope *this_ns = NameScope::GetNameScope(this);
1525 if (this_ns && this_ns->GetTemporary()) {
1526 merge_namescope = true;
1528 else if (!this_ns) {
1529 recurse = true;
1530 register_name = true;
1532 else if (IsHydratedFromXaml ()) {
1533 register_name = true;
1537 if (merge_namescope) {
1538 to_ns->MergeTemporaryScope (this_ns, error);
1539 ClearValue (NameScope::NameScopeProperty, false);
1542 if (register_name) {
1543 const char *n = GetName();
1545 if (n && *n) {
1546 DependencyObject *o = to_ns->FindName (n);
1547 if (o) {
1548 if (o != this) {
1549 char *error_msg = g_strdup_printf ("The name already exists in the tree: %s.", n);
1550 MoonError::FillIn (error, MoonError::ARGUMENT, 2028, error_msg);
1551 g_free (error_msg);
1552 return;
1555 else {
1556 to_ns->RegisterName (n, this);
1561 if (recurse) {
1562 RegisterNamesClosure closure;
1563 closure.to_ns = to_ns;
1564 closure.error = error;
1566 if (autocreate)
1567 g_hash_table_foreach (autocreate->auto_values, register_depobj_names, &closure);
1569 g_hash_table_foreach (local_values, register_depobj_names, &closure);
1573 static void
1574 unregister_depobj_names (gpointer key,
1575 gpointer value,
1576 gpointer user_data)
1578 NameScope *from_ns = (NameScope*)user_data;
1579 Value *v = (Value*)value;
1580 DependencyProperty *property = (DependencyProperty*)key;
1582 if (property->GetId() != UIElement::TagProperty && v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1583 DependencyObject *obj = v->AsDependencyObject ();
1584 obj->UnregisterAllNamesRootedAt (from_ns);
1588 void
1589 DependencyObject::UnregisterAllNamesRootedAt (NameScope *from_ns)
1591 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1593 NameScope *this_ns = NameScope::GetNameScope(this);
1594 if (IsHydratedFromXaml () || this_ns == NULL || this_ns->GetTemporary ()) {
1595 // Unregister in the parent scope
1597 const char *n = GetName();
1599 if (n && strlen (n) > 0)
1600 from_ns->UnregisterName (n);
1603 if (this_ns && !this_ns->GetTemporary())
1604 return;
1606 if (autocreate)
1607 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_names, from_ns);
1609 g_hash_table_foreach (local_values, unregister_depobj_names, from_ns);
1612 bool
1613 DependencyObject::SetName (const char* name, NameScope *scope)
1615 DependencyProperty *property = GetDeployment ()->GetTypes ()->GetProperty (NameProperty);
1617 if (scope->FindName (name))
1618 return false;
1620 Value *new_value = new Value (name);
1621 SetValue (property, new_value);
1622 scope->RegisterName (name, this);
1624 return true;
1627 Value *
1628 DependencyObject::ReadLocalValue (int id)
1630 if (IsDisposed ())
1631 return NULL;
1632 return ReadLocalValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1635 Value *
1636 DependencyObject::ReadLocalValue (DependencyProperty *property)
1638 return (Value *) g_hash_table_lookup (local_values, property);
1641 Value *
1642 DependencyObject::ReadLocalValueWithError (DependencyProperty *property, MoonError *error)
1644 if (!HasProperty (Type::INVALID, property, true)) {
1645 Type *pt = Type::Find (property->GetOwnerType ());
1646 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 ());
1647 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1648 g_free (error_msg);
1649 return NULL;
1651 return ReadLocalValue (property);
1654 Value *
1655 DependencyObject::GetValueWithError (Type::Kind whatami, DependencyProperty *property, MoonError *error)
1657 if (!HasProperty (whatami, property, true)) {
1658 Type *pt = Type::Find (property->GetOwnerType ());
1659 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 ());
1660 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1661 g_free (error_msg);
1662 return NULL;
1664 return GetValue (property);
1667 Value *
1668 DependencyObject::GetValue (int id)
1670 if (IsDisposed ())
1671 return NULL;
1672 return GetValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1675 Value *
1676 DependencyObject::GetValue (DependencyProperty *property)
1678 return GetValue (property, PropertyPrecedence_Highest, PropertyPrecedence_Lowest);
1681 Value *
1682 DependencyObject::GetValue (DependencyProperty *property, PropertyPrecedence startingAtPrecedence)
1684 return GetValue (property, startingAtPrecedence, PropertyPrecedence_Lowest);
1687 Value *
1688 DependencyObject::GetValue (DependencyProperty *property, PropertyPrecedence startingAtPrecedence, PropertyPrecedence endingAtPrecedence)
1690 for (int i = startingAtPrecedence; i <= endingAtPrecedence; i ++) {
1691 if (!providers[i])
1692 continue;
1693 Value *value = providers[i]->GetPropertyValue (property);
1694 if (value) return value;
1696 return NULL;
1699 Value *
1700 DependencyObject::GetValueNoDefault (int id)
1702 if (IsDisposed ())
1703 return NULL;
1704 return GetValueNoDefault (GetDeployment ()->GetTypes ()->GetProperty (id));
1707 Value *
1708 DependencyObject::GetValueNoDefault (DependencyProperty *property)
1710 Value *value = NULL;
1712 for (int i = 0; i < PropertyPrecedence_DefaultValue; i ++) {
1713 if (!providers[i])
1714 continue;
1715 value = providers[i]->GetPropertyValue (property);
1716 if (value) break;
1718 return value && !value->GetIsNull () ? value : NULL;
1721 Value *
1722 DependencyObject::GetValueNoDefaultWithError (DependencyProperty *property, MoonError *error)
1724 if (!HasProperty (Type::INVALID, property, true)) {
1725 Type *pt = Type::Find (property->GetOwnerType ());
1726 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 ());
1727 MoonError::FillIn (error, MoonError::EXCEPTION, error_msg);
1728 g_free (error_msg);
1729 return NULL;
1731 return GetValueNoDefault (property);
1734 void
1735 DependencyObject::ProviderValueChanged (PropertyPrecedence providerPrecedence,
1736 DependencyProperty *property,
1737 Value *old_provider_value, Value *new_provider_value,
1738 bool notify_listeners, bool set_parent, MoonError *error)
1740 int p;
1742 // first we look for a value higher in precedence for this property
1743 for (p = providerPrecedence - 1; p >= PropertyPrecedence_Highest; p --) {
1744 if (providers[p] && providers[p]->GetPropertyValue (property)) {
1745 // a provider higher in precedence already has
1746 // a value for this property, so the one
1747 // that's changing isn't visible anyway.
1748 return;
1752 Value *old_value;
1753 Value *new_value;
1755 if (!old_provider_value || !new_provider_value) {
1756 Value *lower_priority_value = GetValue (property, (PropertyPrecedence)(providerPrecedence + 1));
1758 if (new_provider_value == NULL) {
1759 // we're changing from @old_provider_value to whatever the
1760 // value lower on the priority list is.
1761 old_value = old_provider_value;
1762 new_value = lower_priority_value;
1764 else if (old_provider_value == NULL) {
1765 // we're changing from the old value (from a lower
1766 // priority provider) to @new_provider_value.
1767 old_value = lower_priority_value;
1768 new_value = new_provider_value;
1771 else {
1772 old_value = old_provider_value;
1773 new_value = new_provider_value;
1776 bool equal = false;
1778 if (old_value != NULL && new_value != NULL) {
1779 equal = !property->AlwaysChange() && (*old_value == *new_value);
1780 } else {
1781 equal = false;
1784 if (!equal) {
1785 DependencyObject *old_as_dep = NULL;
1786 DependencyObject *new_as_dep = NULL;
1788 // XXX this flag should be part of the DP metadata.
1789 // we also need to audit other "typeof (object)" DP's
1790 // to make sure they set parent when they should (and
1791 // don't when they shouldn't.)
1792 bool setsParent = set_parent && !property->IsCustom ();
1794 if (old_value && old_value->Is (Type::DEPENDENCY_OBJECT))
1795 old_as_dep = old_value->AsDependencyObject ();
1796 if (new_value && new_value->Is (Type::DEPENDENCY_OBJECT))
1797 new_as_dep = new_value->AsDependencyObject ();
1799 if (old_as_dep && setsParent) {
1800 old_as_dep->SetSurface (NULL);
1802 // unset its parent
1803 old_as_dep->SetParent (NULL, NULL);
1805 // remove ourselves as a target
1806 old_as_dep->RemoveTarget (this);
1808 // unregister from the existing value
1809 old_as_dep->RemovePropertyChangeListener (this, property);
1811 if (old_as_dep->Is(Type::COLLECTION)) {
1812 old_as_dep->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1813 old_as_dep->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1817 if (new_as_dep && setsParent) {
1818 new_as_dep->SetSurface (GetSurface ());
1820 new_as_dep->SetParent (this, error);
1821 if (error->number)
1822 return;
1824 new_as_dep->SetResourceBase (GetResourceBase());
1826 if (new_as_dep->Is(Type::COLLECTION)) {
1827 new_as_dep->AddHandler (Collection::ChangedEvent, collection_changed, this);
1828 new_as_dep->AddHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1831 // listen for property changes on the new object
1832 new_as_dep->AddPropertyChangeListener (this, property);
1834 // add ourselves as a target
1835 new_as_dep->AddTarget (this);
1838 // we need to make this optional, as doing it for NameScope
1839 // merging is killing performance (and noone should ever care
1840 // about that property changing)
1841 if (notify_listeners) {
1842 Value *old_value_copy = old_value == NULL ? NULL : new Value (*old_value);
1843 Value *new_value_copy = new_value == NULL ? NULL : new Value (*new_value);
1845 PropertyChangedEventArgs *args = new PropertyChangedEventArgs (property, property->GetId (), old_value_copy, new_value_copy);
1847 listeners_notified = false;
1849 OnPropertyChanged (args, error);
1851 if (!listeners_notified) {
1852 g_warning ("setting property %s::%s on object of type %s didn't result in listeners being notified",
1853 Type::Find(property->GetOwnerType())->GetName (), property->GetName(), GetTypeName ());
1854 if (error->number)
1855 g_warning ("the error was: %s", error->message);
1858 if (property && property->GetChangedCallback () != NULL) {
1859 PropertyChangeHandler callback = property->GetChangedCallback ();
1860 callback (this, args, error, NULL);
1864 if (InheritedPropertyValueProvider::IsPropertyInherited (property->GetId ()))
1865 InheritedPropertyValueProvider::PropagateInheritedProperty (this, property, old_value_copy, new_value_copy);
1867 args->unref ();
1869 delete old_value_copy;
1870 delete new_value_copy;
1875 void
1876 DependencyObject::ClearValue (int id, bool notify_listeners)
1878 if (IsDisposed ())
1879 return;
1880 ClearValue (GetDeployment ()->GetTypes ()->GetProperty (id), notify_listeners);
1883 void
1884 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners)
1886 ClearValue(property, notify_listeners, NULL);
1889 void
1890 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners, MoonError *error)
1892 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1893 Value *old_local_value;
1895 if (!(old_local_value = ReadLocalValue (property)))
1896 if (property->IsAutoCreated ())
1897 old_local_value = autocreate->ReadLocalValue (property);
1899 // detach from the existing value
1900 if (old_local_value != NULL && old_local_value->Is (Type::DEPENDENCY_OBJECT)) {
1901 DependencyObject *dob = old_local_value->AsDependencyObject();
1903 if (dob != NULL) {
1904 // unset its parent
1905 dob->SetParent (NULL, NULL);
1907 // unregister from the existing value
1908 dob->RemovePropertyChangeListener (this, property);
1909 dob->SetSurface (NULL);
1910 if (dob->Is(Type::COLLECTION)) {
1911 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1912 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1917 g_hash_table_remove (local_values, property);
1919 if (property->IsAutoCreated ())
1920 autocreate->ClearValue (property);
1922 // this is... yeah, it's disgusting
1923 for (int p = PropertyPrecedence_LocalValue + 1; p < PropertyPrecedence_Count; p ++) {
1924 if (providers[p])
1925 providers[p]->RecomputePropertyValue (property);
1928 ProviderValueChanged (PropertyPrecedence_LocalValue, property, old_local_value, NULL, notify_listeners, true, error);
1930 delete old_local_value;
1933 gboolean
1934 DependencyObject::dispose_value (gpointer key, gpointer value, gpointer data)
1936 DependencyObject *_this = (DependencyObject*)data;
1938 Value *v = (Value *) value;
1940 if (!value)
1941 return TRUE;
1943 // detach from the existing value
1944 if (v->Is (Type::DEPENDENCY_OBJECT)){
1945 DependencyObject *dob = v->AsDependencyObject();
1947 if (dob != NULL) {
1948 if (_this == dob->GetParent()) {
1949 // unset its logical parent
1950 dob->SetParent (NULL, NULL);
1953 // unregister from the existing value
1954 dob->RemovePropertyChangeListener ((DependencyObject*)data, NULL);
1956 if (dob->Is(Type::COLLECTION)) {
1957 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, _this);
1958 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, _this);
1963 delete (Value *) value;
1965 return TRUE;
1968 void
1969 DependencyObject::collection_changed (EventObject *sender, EventArgs *args, gpointer closure)
1971 DependencyObject *obj = (DependencyObject*)closure;
1972 obj->OnCollectionChanged ((Collection*)sender, (CollectionChangedEventArgs*)args);
1975 void
1976 DependencyObject::collection_item_changed (EventObject *sender, EventArgs *args, gpointer closure)
1978 DependencyObject *obj = (DependencyObject*)closure;
1979 CollectionItemChangedEventArgs* itemArgs = (CollectionItemChangedEventArgs*)args;
1981 PropertyChangedEventArgs *propChangedArgs = new PropertyChangedEventArgs (itemArgs->GetProperty(),
1982 itemArgs->GetProperty()->GetId (),
1983 itemArgs->GetOldValue(),
1984 itemArgs->GetNewValue());
1986 obj->OnCollectionItemChanged ((Collection*)sender,
1987 itemArgs->GetCollectionItem(),
1988 propChangedArgs);
1990 propChangedArgs->unref ();
1993 DependencyObject::DependencyObject ()
1994 : EventObject (Type::DEPENDENCY_OBJECT)
1996 Initialize ();
1999 DependencyObject::DependencyObject (Deployment *deployment, Type::Kind object_type)
2000 : EventObject (deployment, object_type)
2002 Initialize ();
2005 DependencyObject::DependencyObject (Type::Kind object_type)
2006 : EventObject (object_type)
2008 Initialize ();
2011 void
2012 DependencyObject::Initialize ()
2014 providers = new PropertyValueProvider*[PropertyPrecedence_Count];
2016 providers[PropertyPrecedence_LocalValue] = new LocalPropertyValueProvider (this, PropertyPrecedence_LocalValue);
2017 providers[PropertyPrecedence_DynamicValue] = NULL; // subclasses will set this if they need it.
2019 providers[PropertyPrecedence_LocalStyle] = NULL; // this is a frameworkelement specific thing
2020 providers[PropertyPrecedence_DefaultStyle] = NULL; // this is a frameworkelement specific thing
2022 providers[PropertyPrecedence_Inherited] = new InheritedPropertyValueProvider (this, PropertyPrecedence_Inherited);
2023 providers[PropertyPrecedence_DefaultValue] = new DefaultValuePropertyValueProvider (this, PropertyPrecedence_DefaultValue);
2024 providers[PropertyPrecedence_AutoCreate] = new AutoCreatePropertyValueProvider (this, PropertyPrecedence_AutoCreate);
2026 local_values = g_hash_table_new (g_direct_hash, g_direct_equal);
2027 listener_list = NULL;
2028 parent = NULL;
2029 is_hydrated = false;
2030 is_frozen = false;
2031 resource_base = NULL;
2032 storage_hash = NULL; // Create it on first usage request
2035 void
2036 DependencyObject::Freeze()
2038 is_frozen = true;
2041 struct CloneClosure {
2042 Types *types;
2043 DependencyObject *old_do;
2044 DependencyObject *new_do;
2047 void
2048 DependencyObject::clone_local_value (DependencyProperty *key, Value *value, gpointer data)
2050 CloneClosure *closure = (CloneClosure*)data;
2052 // don't clone the name property, or we end up with nasty
2053 // duplicate name errors.
2054 if (key->GetId() == DependencyObject::NameProperty)
2055 return;
2057 Value *cv = Value::Clone (value, closure->types);
2059 closure->new_do->SetValue (key, cv);
2061 delete cv;
2064 void
2065 DependencyObject::clone_autocreated_value (DependencyProperty *key, Value *value, gpointer data)
2067 CloneClosure *closure = (CloneClosure*)data;
2069 Value *old_value = closure->old_do->GetValue (key, PropertyPrecedence_AutoCreate);
2071 // this should create the new object
2072 Value *new_value = closure->new_do->GetValue (key, PropertyPrecedence_AutoCreate);
2074 if (old_value && !old_value->GetIsNull() && old_value->Is (Type::DEPENDENCY_OBJECT) &&
2075 new_value && !new_value->GetIsNull() && new_value->Is (Type::DEPENDENCY_OBJECT)) {
2076 DependencyObject *new_obj = new_value->AsDependencyObject(closure->types);
2077 DependencyObject *old_obj = old_value->AsDependencyObject(closure->types);
2079 new_obj->CloneCore (closure->types, old_obj);
2083 void
2084 DependencyObject::clone_animation_storage_list (DependencyProperty *key, List *list, gpointer data)
2086 if (!list || list->IsEmpty ())
2087 return;
2088 DependencyObject *d = (DependencyObject*)data;
2089 d->CloneAnimationStorageList (key, list);
2092 void
2093 DependencyObject::CloneAnimationStorageList (DependencyProperty *key, List *list)
2096 List *newlist = new List();
2098 AnimationStorage::Node *node = (AnimationStorage::Node *) list->First ();
2099 while (node) {
2100 node->storage->SwitchTarget (this);
2101 newlist->Append (node->Clone ());
2102 node = (AnimationStorage::Node*)node->next;
2104 list->Clear (true);
2105 if (!storage_hash)
2106 storage_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2107 g_hash_table_insert (storage_hash, key, newlist);
2110 DependencyObject*
2111 DependencyObject::Clone (Types *types)
2113 Type *t = types->Find (GetObjectType());
2115 DependencyObject *new_do = t->CreateInstance();
2117 if (new_do)
2118 new_do->CloneCore (types, (DependencyObject*)this); // this cast should be unnecessary. but C++ const behavior sucks.
2120 return new_do;
2123 void
2124 DependencyObject::CloneCore (Types *types, DependencyObject* fromObj)
2126 CloneClosure closure;
2127 closure.types = types;
2128 closure.old_do = fromObj;
2129 closure.new_do = this;
2131 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) fromObj->providers[PropertyPrecedence_AutoCreate];
2133 g_hash_table_foreach (autocreate->auto_values, (GHFunc)DependencyObject::clone_autocreated_value, &closure);
2134 g_hash_table_foreach (fromObj->local_values, (GHFunc)DependencyObject::clone_local_value, &closure);
2135 if (fromObj->storage_hash) {
2136 g_hash_table_foreach (fromObj->storage_hash, (GHFunc)DependencyObject::clone_animation_storage_list, this);
2140 static void
2141 clear_storage_list (DependencyProperty *key, List *list, gpointer unused)
2143 List::Node *node = list->First ();
2144 while (node) {
2145 delete ((AnimationStorage::Node*)node)->storage;
2146 node = node->next;
2148 delete list;
2151 DependencyObject::~DependencyObject ()
2153 g_hash_table_destroy (local_values);
2154 local_values = NULL;
2155 delete[] providers;
2156 providers = NULL;
2157 g_free (resource_base);
2160 static void
2161 free_listener (gpointer data, gpointer user_data)
2163 Listener* listener = (Listener*) data;
2164 delete listener;
2167 void
2168 DependencyObject::Dispose ()
2170 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2172 if (listener_list != NULL) {
2173 g_slist_foreach (listener_list, free_listener, NULL);
2174 g_slist_free (listener_list);
2175 listener_list = NULL;
2178 RemoveAllListeners();
2180 if (autocreate)
2181 g_hash_table_foreach_remove (autocreate->auto_values, dispose_value, this);
2183 g_hash_table_foreach_remove (local_values, dispose_value, this);
2185 for (int i = 0; i < PropertyPrecedence_Count; i ++) {
2186 delete providers[i];
2187 providers [i] = NULL;
2190 if (storage_hash) {
2191 GHashTable *tmphash = storage_hash; // animation storages may call back to DetachAnimationStorage
2192 storage_hash = NULL;
2193 g_hash_table_foreach (tmphash, (GHFunc)clear_storage_list, NULL);
2194 g_hash_table_destroy (tmphash);
2197 EventObject::Dispose ();
2200 static void
2201 get_attached_props (gpointer key, gpointer value, gpointer user_data)
2203 DependencyProperty *prop = (DependencyProperty *) key;
2204 GHashTable *props = (GHashTable *) user_data;
2206 if (!(g_hash_table_lookup (props, (gpointer) prop->GetHashKey ())))
2207 g_hash_table_insert (props, (gpointer) prop->GetHashKey (), prop);
2210 static void
2211 hash_keys_to_array (gpointer key, gpointer value, gpointer user_data)
2213 g_ptr_array_add ((GPtrArray *) user_data, key);
2216 static void
2217 hash_values_to_array (gpointer key, gpointer value, gpointer user_data)
2219 g_ptr_array_add ((GPtrArray *) user_data, value);
2222 DependencyProperty **
2223 DependencyObject::GetProperties (bool only_changed)
2225 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2226 DependencyProperty **props;
2227 GHashTable *table;
2228 GPtrArray *array;
2230 array = g_ptr_array_new ();
2232 if (!only_changed) {
2233 // get our class/inherited DependencyProperties
2234 table = GetType ()->CopyProperties (true);
2236 // find any attached properties that have been set
2237 g_hash_table_foreach (local_values, get_attached_props, table);
2239 // dump them to an array
2240 g_hash_table_foreach (table, hash_values_to_array, array);
2241 g_hash_table_destroy (table);
2242 } else {
2243 g_hash_table_foreach (local_values, hash_keys_to_array, array);
2244 g_hash_table_foreach (autocreate->auto_values, hash_keys_to_array, array);
2247 g_ptr_array_add (array, NULL);
2248 props = (DependencyProperty **) array->pdata;
2249 g_ptr_array_free (array, false);
2251 return props;
2254 DependencyProperty *
2255 DependencyObject::GetDependencyProperty (const char *name)
2257 return DependencyProperty::GetDependencyProperty (GetObjectType (), name);
2260 bool
2261 DependencyObject::HasProperty (const char *name, bool inherits)
2263 return DependencyProperty::GetDependencyProperty (GetObjectType (), name, inherits) != NULL;
2266 bool
2267 DependencyObject::HasProperty (Type::Kind whatami, DependencyProperty *property, bool inherits)
2269 Type::Kind this_type = whatami == Type::INVALID ? GetObjectType () : whatami;
2271 // TODO: Handle attached properties correctly.
2273 if (property->IsAttached ())
2274 return true;
2277 printf ("DependencyObject::HasProperty (%p, %i (%s), %p (%i %s.%s), %i)..\n",
2279 whatami, Type::Find (whatami)->name,
2280 property, property->GetOwnerType (), Type::Find (property->GetOwnerType ())->name, property->GetName (),
2281 inherits);
2284 if (property == NULL)
2285 return false;
2287 if (property->GetOwnerType () == this_type)
2288 return true;
2290 if (!inherits)
2291 return false;
2293 if (!Type::IsSubclassOf (this_type, property->GetOwnerType ())) {
2294 bool is_prop_custom = property->IsCustom ();
2295 bool is_owner_custom = property->GetOwnerType () > Type::LASTTYPE;
2296 bool is_this_custom = this_type > Type::LASTTYPE;
2297 bool accept = false;
2299 // Yuck.
2300 // This looks very wrong, but it's what SL seems to do.
2301 if (is_prop_custom) {
2302 if (!is_owner_custom && !is_this_custom) {
2303 // SL does not throw errors for custom properties defined on a builtin type
2304 // and then used on another (unrelated) builtin type (DO.GetValue usage at least)
2305 accept = true;
2306 } else if (is_owner_custom) {
2307 // And this is a custom property defined on a custom type and used anywhere.
2308 accept = true;
2311 return accept;
2314 return true;
2317 DependencyObject *
2318 DependencyObject::FindName (const char *name)
2320 return FindName (name, Control::GetIsTemplateItem (this));
2323 DependencyObject *
2324 DependencyObject::FindName (const char *name, bool template_item)
2326 NameScope *scope = NameScope::GetNameScope (this);
2328 if (scope && (template_item == scope->GetIsLocked ()))
2329 return scope->FindName (name);
2331 if (parent)
2332 return parent->FindName (name, template_item);
2334 return NULL;
2337 NameScope *
2338 DependencyObject::FindNameScope ()
2340 return FindNameScope (Control::GetIsTemplateItem (this));
2343 NameScope*
2344 DependencyObject::FindNameScope (bool template_namescope)
2346 NameScope *scope = NameScope::GetNameScope (this);
2348 // Only template namescopes are locked (for the moment)
2349 if (scope && (template_namescope == scope->GetIsLocked ()))
2350 return scope;
2352 if (parent)
2353 return parent->FindNameScope (template_namescope);
2355 return NULL;
2358 DependencyObject *
2359 DependencyObject::FindName (const char *name, Type::Kind *element_kind)
2361 //printf ("Looking up in %p the string %p\n", obj, name);
2362 //printf (" String: %s\n", name);
2363 DependencyObject *ret = FindName (name);
2365 if (ret == NULL)
2366 return NULL;
2368 *element_kind = ret->GetObjectType ();
2370 return ret;
2373 AnimationStorage*
2374 DependencyObject::GetAnimationStorageFor (DependencyProperty *prop)
2376 if (!storage_hash)
2377 return NULL;
2379 List *list = (List*) g_hash_table_lookup (storage_hash, prop);
2380 if (!list || !list->IsEmpty ())
2381 return NULL;
2383 return ((AnimationStorage::Node *) list->Last())->storage;
2386 AnimationStorage*
2387 DependencyObject::AttachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2389 AnimationStorage* attached_storage = NULL;
2390 // Create hash on first access to save some mem
2391 if (!storage_hash)
2392 storage_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2394 List *list = (List*) g_hash_table_lookup (storage_hash, prop);
2395 if (!list) {
2396 list = new List();
2397 g_hash_table_insert (storage_hash, prop, list);
2399 else if (!list->IsEmpty ()) {
2400 attached_storage = ((AnimationStorage::Node*) list->Last())->storage;
2401 attached_storage->Disable ();
2404 list->Append (new AnimationStorage::Node (prop, storage));
2405 return attached_storage;
2408 void
2409 DependencyObject::DetachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2411 if (!storage_hash)
2412 return;
2414 List *list = (List *) g_hash_table_lookup (storage_hash, prop);
2415 if (!list || list->IsEmpty ())
2416 return;
2418 // if the storage to be removed is the last one, activate the previous one (if any)
2419 if (((AnimationStorage::Node*)list->Last ())->storage == storage) {
2420 list->Remove (list->Last ());
2421 if (!list->IsEmpty ()) {
2422 ((AnimationStorage::Node*)list->Last ())->storage->Enable ();
2425 } else { // if there's more than one storage, that means the storage that was added after
2426 // the one we're removing is configured to reset back to the value of the previous
2427 // storage, so update the stop value so it resets back to the proper value
2428 List::Node *node = list->First ();
2429 while (node) {
2430 if (((AnimationStorage::Node*)node)->storage == storage) {
2431 List::Node *remove = node;
2432 node = node->next;
2433 ((AnimationStorage::Node*)node)->storage->SetStopValue (storage->GetResetValue ());
2434 list->Remove (remove);
2435 break;
2437 node = node->next;
2443 // A helper debugging routine for C#
2445 const char *
2446 dependency_object_get_name (DependencyObject *obj)
2448 return obj->GetName ();
2451 Type::Kind
2452 dependency_object_get_object_type (DependencyObject *obj)
2454 return obj->GetObjectType ();
2457 const char *
2458 dependency_object_get_type_name (DependencyObject *obj)
2460 return obj->GetTypeName ();
2463 // Used by routines which need to create DO from code
2464 void
2465 dependency_object_set_name (DependencyObject *obj, const char *name)
2467 obj->SetValue (DependencyObject::NameProperty, Value (name));
2470 static void
2471 set_surface (gpointer key, gpointer value, gpointer data)
2473 Surface *s = (Surface *) data;
2474 Value *v = (Value *) value;
2476 if (v && v->Is (Type::DEPENDENCY_OBJECT)) {
2477 DependencyObject *dob = v->AsDependencyObject();
2478 if (dob)
2479 dob->SetSurface (s);
2483 void
2484 DependencyObject::SetSurface (Surface *s)
2486 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2488 if (GetSurface() == s)
2489 return;
2491 EventObject::SetSurface (s);
2493 if (autocreate)
2494 g_hash_table_foreach (autocreate->auto_values, set_surface, s);
2496 g_hash_table_foreach (local_values, set_surface, s);
2499 void
2500 DependencyObject::SetParent (DependencyObject *parent, MoonError *error)
2502 if (parent == this->parent)
2503 return;
2505 #if DEBUG
2506 // Check for circular families
2507 DependencyObject *current = parent;
2508 while (current != NULL) {
2509 if (current == this) {
2510 g_warning ("cycle found in logical tree. bailing out");
2511 return;
2513 current = current->GetParent ();
2515 #endif
2517 if (!this->parent) {
2518 if (parent) {
2519 NameScope *this_scope = NameScope::GetNameScope(this);
2520 NameScope *parent_scope = parent->FindNameScope();
2521 if (this_scope) {
2522 if (this_scope->GetTemporary()) {
2523 // if we have a temporary name scope, merge it into the
2524 // closest one up the hierarchy.
2525 if (parent_scope) {
2526 parent_scope->MergeTemporaryScope (this_scope, error);
2527 ClearValue (NameScope::NameScopeProperty, false);
2529 else {
2530 // oddly enough, if
2531 // there's no parent
2532 // namescope, we don't
2533 // do anything
2536 else {
2537 // we have a non-temporary scope. we still have to register the name
2538 // of this element (not the ones in the subtree rooted at this element)
2539 // in the new parent scope. we only register the name in the parent scope
2540 // if the element was hydrated, not when it was created from a string.
2541 if (IsHydratedFromXaml()) {
2542 const char *name = GetName();
2543 if (parent_scope && name && *name) {
2544 DependencyObject *existing_obj = parent_scope->FindName (name);
2545 if (existing_obj != this) {
2546 if (existing_obj) {
2547 char *error_msg = g_strdup_printf ("name `%s' is already registered in new parent namescope.", name);
2548 MoonError::FillIn (error, MoonError::ARGUMENT, error_msg);
2549 return;
2551 parent_scope->RegisterName (name, this);
2557 else {
2558 // we don't have a namescope at all,
2559 // we have to iterate over the subtree
2560 // rooted at this object, and merge
2561 // the names into the parent
2562 // namescope.
2564 if (parent_scope) {
2565 NameScope *temp_scope = new NameScope();
2566 temp_scope->SetTemporary (true);
2568 RegisterAllNamesRootedAt (temp_scope, error);
2570 if (error->number) {
2571 temp_scope->unref ();
2572 return;
2575 parent_scope->MergeTemporaryScope (temp_scope, error);
2577 temp_scope->unref ();
2582 else {
2583 if (!parent) {
2584 NameScope *parent_scope = this->parent->FindNameScope ();
2585 if (parent_scope)
2586 UnregisterAllNamesRootedAt (parent_scope);
2590 if (!error || error->number == 0)
2591 this->parent = parent;
2594 Value *
2595 dependency_object_get_value (DependencyObject *object, DependencyProperty *prop)
2597 if (object == NULL)
2598 return NULL;
2600 return object->GetValue (prop);
2603 Value *
2604 dependency_object_get_value_no_default (DependencyObject *object, DependencyProperty *prop)
2606 if (object == NULL)
2607 return NULL;
2609 return object->GetValueNoDefault (prop);
2612 void
2613 dependency_object_set_value (DependencyObject *object, DependencyProperty *prop, Value *val)
2615 if (object == NULL)
2616 return;
2618 object->SetValue (prop, val);
2621 void
2622 DependencyObject::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
2624 if (DependencyObject::NameProperty == args->GetId ()) {
2625 NameScope *scope = FindNameScope ();
2626 if (scope && args->GetNewValue()) {
2627 if (args->GetOldValue ())
2628 scope->UnregisterName (args->GetOldValue ()->AsString ());
2629 scope->RegisterName (args->GetNewValue()->AsString (), this);
2631 if (IsHydratedFromXaml () && parent) {
2632 // we also need to update any parent
2633 // namescope about our name change
2635 scope = parent->FindNameScope ();
2636 if (scope) {
2637 if (args->GetOldValue ())
2638 scope->UnregisterName (args->GetOldValue ()->AsString ());
2639 scope->RegisterName (args->GetNewValue()->AsString (), this);
2645 NotifyListenersOfPropertyChange (args, error);
2648 DependencyObject*
2649 DependencyObject::GetContent()
2651 const char *content_property_name = GetType()->GetContentPropertyName();
2652 if (!content_property_name)
2653 return NULL;
2655 DependencyProperty *content_property = GetDependencyProperty (content_property_name);
2656 if (!content_property)
2657 return NULL;
2659 Value *content_value = GetValue(content_property);
2661 if (!content_value)
2662 return NULL;
2664 return content_value->AsDependencyObject();