add the 2.1-bootstrap dir to MONO_PATH when running smcs
[moon.git] / src / dependencyobject.cpp
blob33b03ca280ce033e2cc73a18736da9ae460b0b27
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 struct EventList {
30 int current_token;
31 int emitting;
32 List *event_list;
35 class EventLists {
36 public:
37 int size;
38 int emitting;
39 EventList *lists;
41 EventLists (int n)
43 size = n;
44 emitting = 0;
45 lists = new EventList [size];
46 for (int i = 0; i < size; i++) {
47 lists [i].current_token = 1;
48 lists [i].emitting = 0;
49 lists [i].event_list = new List ();
53 ~EventLists ()
55 for (int i = 0; i < size; i++) {
56 delete lists [i].event_list;
58 delete [] lists;
63 * EventObject
66 #if OBJECT_TRACKING
67 #define OBJECT_TRACK(x,y) Track((x),(y))
68 #else
69 #define OBJECT_TRACK(x,y)
70 #endif
72 EventObject::EventObject ()
74 Initialize (NULL, Type::EVENTOBJECT);
77 EventObject::EventObject (Type::Kind type)
79 Initialize (NULL, type);
82 EventObject::EventObject (Type::Kind type, bool multi_threaded_safe)
84 Initialize (NULL, type);
85 if (multi_threaded_safe)
86 flags |= (gint32) MultiThreadedSafe;
89 EventObject::EventObject (Deployment *deployment)
91 Initialize (deployment, Type::EVENTOBJECT);
94 EventObject::EventObject (Deployment *deployment, Type::Kind type)
96 Initialize (deployment, type);
99 static gint current_id = 0;
101 void
102 EventObject::Initialize (Deployment *depl, Type::Kind type)
104 if (depl == NULL)
105 depl = Deployment::GetCurrent ();
107 object_type = type;
108 deployment = depl;
109 if (deployment != NULL && this != deployment)
110 deployment->ref ();
111 surface = NULL;
112 flags = g_atomic_int_exchange_and_add (&current_id, 1);
113 refcount = 1;
114 events = NULL;
115 toggleNotifyListener = NULL;
117 #if OBJECT_TRACKING
118 switch (object_type) {
119 case Type::INVALID:
120 Track ("Created", "<unknown>");
121 break;
122 case Type::DEPLOYMENT:
123 Track ("Created", "Deployment");
124 break;
125 default:
126 Track ("Created", depl->GetTypes ()->Find (object_type)->GetName ());
127 break;
130 if (object_type != Type::DEPLOYMENT)
131 Deployment::GetCurrent ()->TrackObjectCreated (this);
132 #endif
134 #if SANITY
135 if (object_type == Type::INVALID)
136 g_warning ("EventObject::EventObject (): created object with type: INVALID.\n");
137 if (deployment == NULL)
138 g_warning ("EventObject::EventObject (): created object with a null deployment.\n");
139 #endif
142 EventObject::~EventObject()
144 #if OBJECT_TRACKING
145 if (object_type != Type::DEPLOYMENT)
146 Deployment::GetCurrent ()->TrackObjectDestroyed (this);
147 Track ("Destroyed", "");
148 #endif
150 #if SANITY
151 if (refcount != 0) {
152 g_warning ("EventObject::~EventObject () #%i was deleted before its refcount reached 0 (current refcount: %i)\n", GetId (), refcount);
154 #endif
156 delete events;
158 // We can't unref the deployment in Dispose, it breaks
159 // object tracking.
160 if (deployment && this != deployment) {
161 deployment->unref ();
162 deployment = NULL;
166 static pthread_rwlock_t surface_lock = PTHREAD_RWLOCK_INITIALIZER;
168 bool
169 EventObject::SetSurfaceLock ()
171 int result;
173 if ((result = pthread_rwlock_wrlock (&surface_lock)) != 0) {
174 printf ("EventObject::SetSurface (%p): Couldn't aquire write lock: %s\n", surface, strerror (result));
175 return false;
178 return true;
182 void
183 EventObject::SetSurface (Surface *surface)
185 if (!Surface::InMainThread () && surface != this->surface) {
186 g_warning ("EventObject::SetSurface (): This method must not be called on any other than the main thread!\n");
187 #if DEBUG
188 if (debug_flags & RUNTIME_DEBUG_DP)
189 print_stack_trace ();
190 #endif
191 return;
194 this->surface = surface;
197 void
198 EventObject::SetSurfaceUnlock ()
200 pthread_rwlock_unlock (&surface_lock);
203 void
204 EventObject::AddTickCallSafe (TickCallHandler handler, EventObject *data)
206 int result;
209 * This code assumes that the surface won't be destructed without setting the surface field on to NULL first.
210 * In other words: if we have a read lock here, the surface won't get destroyed, given that setting
211 * the surface field to NULL will block until we release the read lock.
214 if ((result = pthread_rwlock_rdlock (&surface_lock)) != 0) {
215 printf ("EventObject::AddTickCallSafe (): Couldn't aquire read lock: %s\n", strerror (result));
216 return;
219 AddTickCallInternal (handler, data);
221 pthread_rwlock_unlock (&surface_lock);
224 void
225 EventObject::AddTickCall (TickCallHandler handler, EventObject *data)
227 if (!Surface::InMainThread ()) {
228 g_warning ("EventObject::AddTickCall (): This method must not be called on any other than the main thread! Tick call won't be added.\n");
229 #if DEBUG
230 if (debug_flags & RUNTIME_DEBUG_DP)
231 print_stack_trace ();
232 #endif
233 return;
236 AddTickCallInternal (handler, data);
239 void
240 EventObject::AddTickCallInternal (TickCallHandler handler, EventObject *data)
242 Surface *surface;
243 TimeManager *timemanager;
245 surface = GetSurface ();
247 if (surface == NULL)
248 surface = GetDeployment ()->GetSurface ();
250 if (!surface) {
251 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no surface\n");
252 return;
255 timemanager = surface->GetTimeManager ();
257 if (!timemanager) {
258 LOG_DP ("EventObject::AddTickCall (): Could not add tick call, no time manager\n");
259 return;
262 timemanager->AddTickCall (handler, data ? data : this);
265 Deployment *
266 EventObject::GetDeployment ()
268 if (deployment == NULL)
269 g_warning ("EventObject::GetDeployment () should not be reached with a null deployment");
271 #if SANITY
272 if (deployment != Deployment::GetCurrent () && Deployment::GetCurrent () != NULL) {
273 g_warning ("EventObject::GetDeployment () our deployment %p doesn't match Deployment::GetCurrent () %p", deployment, Deployment::GetCurrent ());
274 // print_stack_trace ();
276 #endif
278 return deployment;
281 void
282 EventObject::SetCurrentDeployment (bool domain)
284 if (deployment != NULL)
285 Deployment::SetCurrent (deployment, domain);
288 Surface *
289 EventObject::GetSurface ()
291 #if 0
292 Deployment *current_deployment = Deployment::GetCurrent ();
293 Application *current_application;
294 current_application = deployment != NULL ? deployment->GetCurrentApplication () : (current_deployment != NULL ? current_deployment->GetCurrentApplication () : NULL);
296 if (deployment != NULL && deployment != current_deployment) {
297 printf ("EventObject::GetSurface (): WARNING deployment: %p, Deployment::GetCurrent (): %p type: %s, id: %i\n", deployment, current_deployment, GetTypeName (), GET_OBJ_ID (this));
298 print_stack_trace ();
300 // current_application is null in the Surface ctor
301 if (!(current_application == NULL || surface == NULL || current_application->GetSurface () == surface))
302 printf ("EventObject::GetSurface (): assert failed: current application: %p, surface: %p, current application's surface: %p\n", current_application, surface, current_application->GetSurface ());
303 #endif
305 return surface; // When surface have been removed, return the Surface stored on the Deployment.
308 void
309 EventObject::Dispose ()
311 if (!IsDisposed ()) {
312 // Dispose can be called multiple times, but Emit only once. When DestroyedEvent was in the dtor,
313 // it could only ever be emitted once, don't change that behaviour.
314 Emit (DestroyedEvent); // TODO: Rename to DisposedEvent
317 SetSurface (NULL);
318 // Remove attached flag and set the disposed flag.
319 flags = (Flags) (flags & ~Attached);
320 flags = (Flags) (flags | Disposed);
323 bool
324 EventObject::IsDisposed ()
326 return (flags & Disposed) != 0;
329 void
330 EventObject::ref ()
332 int v = g_atomic_int_exchange_and_add (&refcount, 1);
334 #if DEBUG
335 if (GetObjectType () != object_type)
336 printf ("EventObject::ref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
338 if (deployment != Deployment::GetCurrent ()) {
339 printf ("EventObject::ref (): the type '%s' whose id is %i was created on a deployment (%p) different from the current deployment (%p).\n", GetTypeName (), GET_OBJ_ID (this), deployment, Deployment::GetCurrent ());
340 // print_stack_trace ();
342 #endif
344 if (v == 0) {
345 // Here something really bad happened, this object is probably being reffed again because
346 // of some action in the destructor. There is no way to recover from this now, no matter what
347 // we do the code that called ref now will be accessing a deleted object later on, which may or
348 // may not crash. It might very well be an exploitable security problem. Anyways when unref is called, we
349 // have a second delete on the same object, which *will* crash. To make things easier and safer
350 // lets just abort right away.
351 #if OBJECT_TRACKING
352 PrintStackTrace ();
353 #endif
354 // Due to our mixed usage of Dispose and dtor, currently there are valid cases of reffing
355 // an object with refcount = 0. Use a warning instead of error until the mixed usage is
356 // gone.
357 g_warning ("Ref was called on an object with a refcount of 0.\n");
359 } else if (v == 1 && toggleNotifyListener) {
360 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
361 toggleNotifyListener->Invoke (false);
364 OBJECT_TRACK ("Ref", GetTypeName ());
367 void
368 EventObject::unref ()
370 // we need to retrieve all instance fields into locals before decreasing the refcount
371 // TODO: do we need some sort of gcc foo (volatile variables, memory barries)
372 // to ensure that gcc does not optimize the fetches below away
373 ToggleNotifyListener *toggle_listener = this->toggleNotifyListener;
374 #if OBJECT_TRACKING
375 Deployment *depl = this->deployment ? this->deployment : Deployment::GetCurrent ();
376 const char *type_name = (depl == NULL || depl->isDead) ? NULL : Type::Find (depl, GetObjectType ())->GetName ();
377 #endif
379 #if SANITY
380 if (GetObjectType () != object_type)
381 printf ("EventObject::unref (): the type '%s' did not call SetObjectType, object_type is '%s'\n", Type::Find (GetObjectType ())->GetName (), Type::Find (object_type)->GetName ());
382 #endif
384 if (!IsMultiThreadedSafe () && !Surface::InMainThread ()) {
385 unref_delayed ();
386 return;
389 int v = g_atomic_int_exchange_and_add (&refcount, -1) - 1;
391 // from now on we can't access any instance fields if v > 0
392 // since another thread might have unreffed and caused our destruction
394 if (v == 0 && events != NULL && events->emitting) {
395 g_atomic_int_exchange_and_add (&refcount, 1);
396 unref_delayed ();
397 return;
400 OBJECT_TRACK ("Unref", type_name);
402 if (v == 0) {
403 // here we *can* access instance fields, since we know that we haven't been
404 // desctructed already.
405 Dispose ();
407 #if SANITY
408 if ((flags & Disposed) == 0)
409 printf ("EventObject::unref (): the type '%s' (or any of its parent types) forgot to call its base class' Dispose method.\n", GetTypeName ());
410 #endif
412 // We need to check again if the refcount really is zero,
413 // the object might have resurrected in the Dispose.
414 // TODO: we should disallow resurrection, it's not thread-safe
415 // if we got resurrected and unreffed, we'd be deleted by now
416 // in which case we'll double free here.
417 v = g_atomic_int_get (&refcount);
418 if (v == 0)
419 delete this;
421 } else if (v == 1 && toggle_listener) {
422 // we know that toggle_listener hasn't been freed, since if it exists, it will have a ref to us which would prevent our destruction
423 // note that the instance field might point to garbage after decreasing the refcount above, so we access the local variable we
424 // retrieved before decreasing the refcount.
425 if (getenv ("MOONLIGHT_ENABLE_TOGGLEREF"))
426 toggle_listener->Invoke (true);
429 #if SANITY
430 if (v < 0) {
431 g_warning ("EventObject::Unref (): NEGATIVE REFCOUNT id: %i v: %i refcount: %i", GET_OBJ_ID (this), v, refcount);
432 print_stack_trace ();
434 #endif
437 void
438 EventObject::AddToggleRefNotifier (ToggleNotifyHandler tr)
440 if (toggleNotifyListener)
441 return;
443 this->ref ();
444 toggleNotifyListener = new ToggleNotifyListener (this, tr);
447 void
448 EventObject::RemoveToggleRefNotifier ()
450 if (!toggleNotifyListener)
451 return;
453 delete toggleNotifyListener;
454 toggleNotifyListener = NULL;
455 this->unref ();
458 #if OBJECT_TRACKING
459 // Define the ID of the object you want to track
460 // Object creation, destruction and all ref/unrefs
461 // are logged to the console, with a stacktrace.
462 static bool object_id_fetched = false;
463 static int object_id = -1;
464 static const char *track_object_type = NULL;
465 static bool use_visi_output = false;
466 static bool track_all = false;
468 #define OBJECT_TRACK_ID (0)
470 GHashTable* EventObject::objects_alive = NULL;
472 void
473 EventObject::Track (const char* done, const char* typname)
475 int id = GetId ();
476 if (!object_id_fetched) {
477 object_id_fetched = true;
478 char *sval = getenv ("MOONLIGHT_OBJECT_TRACK_ID");
479 if (sval)
480 object_id = atoi (sval);
481 track_object_type = getenv ("MOONLIGHT_OBJECT_TRACK_TYPE");
482 use_visi_output = (getenv ("MOONLIGHT_OBJECT_TRACK_VISI") != NULL);
483 track_all = (getenv ("MOONLIGHT_OBJECT_TRACK_ALL") != NULL);
486 if (track_all)
487 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
489 if (id == object_id || (track_object_type != NULL && typname != NULL && strcmp (typname, track_object_type) == 0)) {
490 char *st = NULL;
491 // load the stack trace before we print anything
492 // this way there's a lot smaller chance of
493 // ending up with other output between the first line (tracked object of type...)
494 // and the stack trace when using multiple threads.
495 if (!use_visi_output)
496 st = get_stack_trace ();
498 if (!track_all)
499 printf ("%p\t%s tracked object of type '%s': %i, current refcount: %i deployment: %p\n", this, done, typname, id, refcount, deployment);
501 if (!use_visi_output) {
502 printf("%s", st);
503 } else {
504 print_reftrace (done, typname, refcount, false);
506 g_free (st);
510 char *
511 EventObject::GetStackTrace (const char* prefix)
513 return get_stack_trace_prefix (prefix);
516 void
517 EventObject::PrintStackTrace ()
519 print_stack_trace ();
521 #endif
523 // event handlers for c++
524 class EventClosure : public List::Node {
525 public:
526 EventClosure (EventHandler func, gpointer data, GDestroyNotify data_dtor, int token) {
527 this->func = func;
528 this->data = data;
529 this->data_dtor = data_dtor;
530 this->token = token;
532 pending_removal = false;
533 emit_count = 0;
536 ~EventClosure ()
538 if (data_dtor)
539 data_dtor (data);
542 EventHandler func;
543 gpointer data;
544 GDestroyNotify data_dtor;
545 int token;
546 bool pending_removal;
547 int emit_count;
551 EventObject::AddHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
553 int id = GetType()->LookupEvent (event_name);
555 if (id == -1) {
556 g_warning ("adding handler to event '%s', which has not been registered\n", event_name);
557 return -1;
560 return AddHandler (id, handler, data, data_dtor);
564 EventObject::AddHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
566 if (GetType()->GetEventCount() <= 0) {
567 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
568 return -1;
571 if (events == NULL)
572 events = new EventLists (GetType ()->GetEventCount ());
574 int token = events->lists [event_id].current_token++;
576 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, token));
578 return token;
582 EventObject::AddXamlHandler (const char *event_name, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
584 int id = GetType ()->LookupEvent (event_name);
586 if (id == -1) {
587 g_warning ("adding xaml handler to event '%s', which has not been registered\n", event_name);
588 return -1;
591 return AddXamlHandler (id, handler, data, data_dtor);
595 EventObject::AddXamlHandler (int event_id, EventHandler handler, gpointer data, GDestroyNotify data_dtor)
597 if (GetType ()->GetEventCount () <= 0) {
598 g_warning ("adding xaml handler to event with id %d, which has not been registered\n", event_id);
599 return -1;
602 if (events == NULL)
603 events = new EventLists (GetType ()->GetEventCount ());
605 events->lists [event_id].event_list->Append (new EventClosure (handler, data, data_dtor, 0));
607 return 0;
610 void
611 EventObject::RemoveHandler (const char *event_name, EventHandler handler, gpointer data)
613 int id = GetType()->LookupEvent (event_name);
615 if (id == -1) {
616 g_warning ("removing handler for event '%s', which has not been registered\n", event_name);
617 return;
620 RemoveHandler (id, handler, data);
623 void
624 EventObject::RemoveHandler (int event_id, EventHandler handler, gpointer data)
626 if (GetType()->GetEventCount() <= 0) {
627 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
628 return;
631 if (events == NULL)
632 return;
634 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
635 while (closure) {
636 if (closure->func == handler && closure->data == data) {
637 if (events->lists [event_id].emitting > 0) {
638 closure->pending_removal = true;
639 } else {
640 events->lists [event_id].event_list->Remove (closure);
642 break;
645 closure = (EventClosure *) closure->next;
649 void
650 EventObject::RemoveHandler (int event_id, int token)
652 if (GetType()->GetEventCount() <= 0) {
653 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
654 return;
657 if (events == NULL)
658 return;
660 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
661 while (closure) {
662 if (closure->token == token) {
663 if (events->lists [event_id].emitting > 0) {
664 closure->pending_removal = true;
665 } else {
666 events->lists [event_id].event_list->Remove (closure);
668 break;
671 closure = (EventClosure *) closure->next;
675 void
676 EventObject::RemoveAllHandlers (gpointer data)
678 if (events == NULL)
679 return;
681 int count = GetType ()->GetEventCount ();
683 for (int i = 0; i < count - 1; i++) {
684 EventClosure *closure = (EventClosure *) events->lists [i].event_list->First ();
685 while (closure) {
686 if (closure->data == data) {
687 if (events->lists [i].emitting > 0) {
688 closure->pending_removal = true;
689 } else {
690 events->lists [i].event_list->Remove (closure);
692 break;
695 closure = (EventClosure *) closure->next;
700 void
701 EventObject::RemoveMatchingHandlers (int event_id, bool (*predicate)(EventHandler cb_handler, gpointer cb_data, gpointer data), gpointer closure)
703 if (GetType()->GetEventCount() <= 0) {
704 g_warning ("removing handler for event with id %d, which has not been registered\n", event_id);
705 return;
708 if (events == NULL)
709 return;
711 EventClosure *c = (EventClosure *) events->lists [event_id].event_list->First ();
712 while (c) {
713 if (predicate (c->func, c->data, closure)) {
714 if (events->lists [event_id].emitting > 0) {
715 c->pending_removal = true;
716 } else {
717 events->lists [event_id].event_list->Remove (c);
719 break;
722 c = (EventClosure *) c->next;
727 EventObject::GetEventGeneration (int event_id)
729 if (GetType()->GetEventCount() <= 0) {
730 g_warning ("adding handler to event with id %d, which has not been registered\n", event_id);
731 return -1;
734 if (events == NULL)
735 events = new EventLists (GetType ()->GetEventCount ());
737 return events->lists [event_id].current_token;
740 bool
741 EventObject::Emit (char *event_name, EventArgs *calldata, bool only_unemitted, int starting_generation)
743 Deployment *deployment = GetDeployment ();
744 if (deployment && deployment->isDead)
745 return false;
747 int id = GetType()->LookupEvent (event_name);
749 if (id == -1) {
750 g_warning ("trying to emit event '%s', which has not been registered\n", event_name);
751 return false;
754 return Emit (id, calldata, only_unemitted);
757 struct EmitData {
758 EventObject *sender;
759 int event_id;
760 EventArgs *calldata;
761 bool only_unemitted;
764 gboolean
765 EventObject::EmitCallback (gpointer d)
767 EmitData *data = (EmitData *) d;
768 data->sender->SetCurrentDeployment ();
769 data->sender->Emit (data->event_id, data->calldata, data->only_unemitted);
770 data->sender->unref ();
771 delete data;
772 #if SANITY
773 Deployment::SetCurrent (NULL);
774 #endif
775 return FALSE;
778 bool
779 EventObject::Emit (int event_id, EventArgs *calldata, bool only_unemitted, int starting_generation)
781 if (IsDisposed ())
782 return false;
784 Deployment *deployment = GetDeployment ();
785 if (deployment && deployment->isDead)
786 return false;
788 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
789 g_warning ("trying to emit event with id %d, which has not been registered\n", event_id);
790 if (calldata)
791 calldata->unref ();
792 return false;
795 if (events == NULL || events->lists [event_id].event_list->IsEmpty ()) {
796 if (calldata)
797 calldata->unref ();
798 return false;
801 if (!Surface::InMainThread ()) {
802 Surface *surface = deployment ? deployment->GetSurface () : NULL;
804 if (surface == NULL) {
805 printf ("EventObject::Emit (): could not emit event, the deployment %p does not have a surface.\n", deployment);
806 return false;
809 EmitData *data = new EmitData ();
810 data->sender = this;
811 data->sender->ref ();
812 data->event_id = event_id;
813 data->calldata = calldata;
814 data->only_unemitted = only_unemitted;
815 surface->GetTimeManager ()->AddTimeout (MOON_PRIORITY_HIGH, 1, EmitCallback, data);
816 return false;
819 EmitContext* ctx = StartEmit (event_id);
821 DoEmit (event_id, ctx, calldata, only_unemitted, starting_generation);
823 if (calldata)
824 calldata->unref ();
826 FinishEmit (event_id, ctx);
828 return true;
831 struct EmitContext {
832 int length;
833 EventClosure **closures;
835 EmitContext()
837 length = 0;
838 closures = NULL;
840 ~EmitContext()
842 g_free (closures);
846 EmitContext*
847 EventObject::StartEmit (int event_id)
849 EmitContext *ctx = new EmitContext();
850 EventClosure *closure;
852 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
853 g_warning ("trying to start emit with id %d, which has not been registered\n", event_id);
854 return ctx;
857 if (events == NULL || events->lists [event_id].event_list->IsEmpty ()) {
858 return ctx;
861 events->emitting++;
862 events->lists [event_id].emitting++;
864 ctx->length = events->lists [event_id].event_list->Length();
865 ctx->closures = g_new (EventClosure*, ctx->length);
867 /* make a copy of the event list to use for emitting */
868 closure = (EventClosure *) events->lists [event_id].event_list->First ();
869 for (int i = 0; closure != NULL; i++) {
870 ctx->closures [i] = closure;
871 closure = (EventClosure *) closure->next;
874 return ctx;
877 bool
878 EventObject::DoEmit (int event_id, EmitContext *ctx, EventArgs *calldata, bool only_unemitted, int starting_generation)
880 EventClosure *xaml_closure = NULL;
882 /* emit the events using the copied list */
883 for (int i = 0; i < ctx->length; i++) {
884 EventClosure *closure = ctx->closures[i];
886 #if FORCE_XAML_HANDLERS_LAST
887 // i thought this was required for drt 234, but it
888 // doesn't seem to be helping it, and it's breaking
889 // other p1 tests. - toshok
890 if (closure->token == 0) {
891 xaml_closure = closure;
892 continue;
894 #endif
896 if (closure && closure->func
897 && (!only_unemitted || closure->emit_count == 0)
898 && (starting_generation == -1 || closure->token < starting_generation)) {
899 closure->func (this, calldata, closure->data);
900 closure->emit_count ++;
904 if (xaml_closure && xaml_closure->func
905 && (!only_unemitted || xaml_closure->emit_count == 0)) {
906 xaml_closure->func (this, calldata, xaml_closure->data);
907 xaml_closure->emit_count ++;
910 return ctx->length > 0;
913 void
914 EventObject::FinishEmit (int event_id, EmitContext *ctx)
916 if (GetType()->GetEventCount() <= 0 || event_id >= GetType()->GetEventCount()) {
917 g_warning ("trying to finish emit with id %d, which has not been registered\n", event_id);
918 goto delete_ctx;
921 if (ctx->length == 0)
922 goto delete_ctx;
924 events->lists [event_id].emitting--;
925 events->emitting--;
927 if (events->lists [event_id].emitting == 0) {
928 // Remove closures which are waiting for removal
929 EventClosure *closure = (EventClosure *) events->lists [event_id].event_list->First ();
930 while (closure != NULL) {
931 EventClosure *next = (EventClosure *) closure->next;
932 if (closure->pending_removal)
933 events->lists [event_id].event_list->Remove (closure);
934 closure = next;
938 delete_ctx:
939 delete ctx;
942 void
943 EventObject::unref_delayed ()
945 Deployment *depl;
947 OBJECT_TRACK ("DelayedUnref", GetTypeName ());
949 // access deployment as long as we have it (until Dispose has been called),
950 // after that access the static deployment.
951 depl = deployment ? deployment : Deployment::GetCurrent ();
952 depl->UnrefDelayed (this);
955 class Listener {
956 public:
957 virtual bool Matches (PropertyChangedEventArgs *args) = 0;
958 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error) = 0;
960 virtual gpointer GetListener () = 0;
961 virtual gpointer GetProperty () = 0;
964 class WildcardListener : public Listener {
965 public:
966 WildcardListener (DependencyObject *obj, DependencyProperty *prop)
968 this->obj = obj;
969 this->prop = prop;
972 virtual bool Matches (PropertyChangedEventArgs *args) { return true; }
973 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
975 // FIXME we ignore error here.
976 obj->OnSubPropertyChanged (prop, sender, args);
979 virtual gpointer GetListener ()
981 return obj;
984 virtual gpointer GetProperty ()
986 return prop;
989 private:
991 DependencyObject *obj;
992 DependencyProperty *prop;
995 class CallbackListener : public Listener {
996 public:
997 CallbackListener (DependencyProperty *prop, PropertyChangeHandler cb, gpointer closure)
999 this->prop = prop;
1000 this->cb = cb;
1001 this->closure = closure;
1004 virtual bool Matches (PropertyChangedEventArgs *args)
1006 return prop == args->GetProperty ();
1009 virtual void Invoke (DependencyObject *sender, PropertyChangedEventArgs *args, MoonError *error)
1011 cb (sender, args, error, closure);
1014 virtual gpointer GetListener ()
1016 return (gpointer)cb;
1019 virtual gpointer GetProperty ()
1021 return prop;
1024 private:
1025 PropertyChangeHandler cb;
1026 DependencyProperty *prop;
1027 gpointer closure;
1031 // Registers @listener as a listener on changes to @child_property of this DO.
1033 void
1034 DependencyObject::AddPropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1036 listener_list = g_slist_append (listener_list, new WildcardListener (listener, child_property));
1039 void
1040 DependencyObject::RemoveListener (gpointer listener, DependencyProperty *child_property)
1042 GSList *next;
1043 for (GSList *l = listener_list; l; l = next) {
1044 next = l->next;
1045 Listener *listen = (Listener *) l->data;
1047 if ((listen->GetListener() == listener)
1048 && (child_property == NULL || listen->GetProperty() == child_property)) {
1049 listener_list = g_slist_delete_link (listener_list, l);
1050 delete listen;
1056 // Unregisters @container as a listener on changes to @child_property of this DO.
1058 void
1059 DependencyObject::RemovePropertyChangeListener (DependencyObject *listener, DependencyProperty *child_property)
1061 RemoveListener (listener, child_property);
1064 void
1065 DependencyObject::AddPropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb, gpointer closure)
1067 listener_list = g_slist_append (listener_list, new CallbackListener (property, cb, closure));
1070 void
1071 DependencyObject::RemovePropertyChangeHandler (DependencyProperty *property, PropertyChangeHandler cb)
1073 RemoveListener ((gpointer)cb, property);
1076 static void
1077 unregister_depobj_values (gpointer key,
1078 gpointer value,
1079 gpointer user_data)
1081 DependencyObject *this_obj = (DependencyObject*)user_data;
1082 //DependencyProperty *prop = (DependencyProperty*)key;
1083 Value *v = (Value*)value;
1085 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1086 //printf ("unregistering from property %s\n", prop->name);
1087 DependencyObject *obj = v->AsDependencyObject ();
1088 obj->RemovePropertyChangeListener (this_obj);
1089 obj->SetParent (NULL, NULL);
1093 void
1094 DependencyObject::RemoveAllListeners ()
1096 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1098 if (autocreate)
1099 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_values, this);
1101 g_hash_table_foreach (local_values, unregister_depobj_values, this);
1104 static bool listeners_notified;
1106 void
1107 DependencyObject::NotifyListenersOfPropertyChange (PropertyChangedEventArgs *args, MoonError *error)
1109 g_return_if_fail (args);
1111 listeners_notified = true;
1113 for (GSList *l = listener_list; l != NULL; l = l->next){
1114 Listener *listener = (Listener*)l->data;
1116 if (listener->Matches (args))
1117 listener->Invoke (this, args, error);
1118 if (error && error->number)
1119 break;
1123 void
1124 DependencyObject::NotifyListenersOfPropertyChange (int id, MoonError *error)
1126 if (IsDisposed ())
1127 return;
1128 NotifyListenersOfPropertyChange (GetDeployment ()->GetTypes ()->GetProperty (id), error);
1131 void
1132 DependencyObject::NotifyListenersOfPropertyChange (DependencyProperty *subproperty, MoonError *error)
1134 // XXX I really think this method should go away. we only use it in
1135 // a couple of places, and it abuses things.
1137 Value *new_value = subproperty ? GetValue (subproperty) : NULL;
1139 PropertyChangedEventArgs args (subproperty, subproperty->GetId (), NULL, new_value);
1141 NotifyListenersOfPropertyChange (&args, error);
1144 bool
1145 DependencyObject::IsValueValid (DependencyProperty* property, Value* value, MoonError *error)
1147 if (property == NULL) {
1148 MoonError::FillIn (error, MoonError::ARGUMENT_NULL, 1001,
1149 "NULL property passed to IsValueValid");
1150 return false;
1153 if (value != NULL) {
1154 if (value->Is (Type::EVENTOBJECT) && !value->AsEventObject ()) {
1155 // if it's a null DependencyObject, it doesn't matter what type it is
1156 return true;
1159 if (value->Is (Type::MANAGED)) {
1160 // This is a big hack, we do no type-checking if we try to set a managed type.
1161 // Given that for the moment we might not have the surface available, we can't
1162 // do any type checks since we can't access types registered on the surface.
1163 return true;
1166 if (!Type::IsAssignableFrom (property->GetPropertyType(), value->GetKind())) {
1167 MoonError::FillIn (error, MoonError::ARGUMENT, 1001,
1168 g_strdup_printf ("DependencyObject::SetValue, value cannot be assigned to the "
1169 "property %s::%s (property has type '%s', value has type '%s')",
1170 GetTypeName (), property->GetName(), Type::Find (property->GetPropertyType())->GetName (),
1171 Type::Find (value->GetKind ())->GetName ()));
1172 return false;
1174 } else {
1175 // In 2.0, property->GetPropertyType() can return
1176 // something greater than Type::LASTTYPE. Only check
1177 // built-in types for null Types registered on the
1178 // managed side has their own check there.
1179 if (!CanPropertyBeSetToNull (property)) {
1180 MoonError::FillIn (error, MoonError::ARGUMENT, 1001,
1181 g_strdup_printf ("Can not set a non-nullable scalar type to NULL (property: %s)",
1182 property->GetName()));
1183 return false;
1187 return true;
1190 bool
1191 DependencyObject::CanPropertyBeSetToNull (DependencyProperty* property)
1193 if (property->GetPropertyType () > Type::LASTTYPE)
1194 return true;
1196 if (Type::IsSubclassOf (property->GetPropertyType(), Type::DEPENDENCY_OBJECT))
1197 return true;
1199 if (property->IsNullable ())
1200 return true;
1202 if (Type::IsSubclassOf (property->GetPropertyType (), Type::STRING))
1203 return true;
1205 return false;
1208 bool
1209 DependencyObject::SetValue (int id, Value *value)
1211 if (IsDisposed ())
1212 return false;
1213 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1216 bool
1217 DependencyObject::SetValue (int id, Value value)
1219 if (IsDisposed ())
1220 return false;
1221 return SetValue (GetDeployment ()->GetTypes ()->GetProperty (id), value);
1224 bool
1225 DependencyObject::SetValue (DependencyProperty *property, Value *value)
1227 MoonError err;
1228 return SetValueWithError (property, value, &err);
1231 bool
1232 DependencyObject::SetValue (DependencyProperty *property, Value value)
1234 MoonError err;
1235 return SetValueWithError (property, &value, &err);
1238 bool
1239 DependencyObject::SetValueWithError (DependencyProperty* property, Value value, MoonError *error)
1241 return SetValueWithError (property, &value, error);
1244 bool
1245 DependencyObject::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
1247 if (is_frozen) {
1248 MoonError::FillIn (error, MoonError::UNAUTHORIZED_ACCESS, g_strdup_printf ("Cannot set value for property '%s' on frozen DependencyObject '%s'", property->GetName(), GetTypeName()));
1249 return false;
1252 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1253 Value *current_value;
1254 bool equal = false;
1256 if (!(current_value = ReadLocalValue (property)))
1257 if (property->IsAutoCreated ())
1258 current_value = autocreate->ReadLocalValue (property);
1260 if (current_value != NULL && value != NULL) {
1261 equal = !property->AlwaysChange() && (*current_value == *value);
1262 } else {
1263 equal = (current_value == NULL) && (value == NULL);
1266 if (!equal) {
1267 Value *new_value;
1269 // remove the old value
1270 g_hash_table_remove (local_values, property);
1272 if (property->IsAutoCreated ())
1273 autocreate->ClearValue (property);
1275 if (value && (!property->IsAutoCreated () || !value->Is (Type::DEPENDENCY_OBJECT) || value->AsDependencyObject () != NULL))
1276 new_value = new Value (*value);
1277 else
1278 new_value = NULL;
1280 // replace it with the new value
1281 if (new_value)
1282 g_hash_table_insert (local_values, property, new_value);
1284 ProviderValueChanged (PropertyPrecedence_LocalValue, property, current_value, new_value, true, error);
1286 if (current_value)
1287 delete current_value;
1290 return true;
1293 bool
1294 DependencyObject::SetValueWithError (DependencyProperty *property, Value *value, MoonError *error)
1296 if (!IsValueValid (property, value, error))
1297 return false;
1298 if (!property->Validate (this, value, error))
1299 return false;
1301 return SetValueWithErrorImpl (property, value, error);
1304 struct RegisterNamesClosure {
1305 NameScope *to_ns;
1306 MoonError *error;
1309 static void
1310 register_depobj_names (gpointer key,
1311 gpointer value,
1312 gpointer user_data)
1314 RegisterNamesClosure *closure = (RegisterNamesClosure*)user_data;
1315 if (closure->error->number)
1316 return;
1318 Value *v = (Value*)value;
1320 if (v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1321 DependencyObject *obj = v->AsDependencyObject ();
1322 obj->RegisterAllNamesRootedAt (closure->to_ns, closure->error);
1326 void
1327 DependencyObject::RegisterAllNamesRootedAt (NameScope *to_ns, MoonError *error)
1329 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1331 if (error->number)
1332 return;
1334 bool merge_namescope = false;
1335 bool register_name = false;
1336 bool recurse = false;
1338 NameScope *this_ns = NameScope::GetNameScope(this);
1340 if (this_ns && this_ns->GetTemporary()) {
1341 merge_namescope = true;
1343 else if (!this_ns) {
1344 recurse = true;
1345 register_name = true;
1347 else if (IsHydratedFromXaml ()) {
1348 register_name = true;
1352 if (merge_namescope) {
1353 to_ns->MergeTemporaryScope (this_ns, error);
1354 ClearValue (NameScope::NameScopeProperty, false);
1357 if (register_name) {
1358 const char *n = GetName();
1360 if (n && *n) {
1361 DependencyObject *o = to_ns->FindName (n);
1362 if (o) {
1363 if (o != this) {
1364 MoonError::FillIn (error, MoonError::ARGUMENT, 2028,
1365 g_strdup_printf ("The name already exists in the tree: %s.",
1366 n));
1367 return;
1370 else {
1371 to_ns->RegisterName (n, this);
1376 if (recurse) {
1377 RegisterNamesClosure closure;
1378 closure.to_ns = to_ns;
1379 closure.error = error;
1381 if (autocreate)
1382 g_hash_table_foreach (autocreate->auto_values, register_depobj_names, &closure);
1384 g_hash_table_foreach (local_values, register_depobj_names, &closure);
1388 static void
1389 unregister_depobj_names (gpointer key,
1390 gpointer value,
1391 gpointer user_data)
1393 NameScope *from_ns = (NameScope*)user_data;
1394 Value *v = (Value*)value;
1395 DependencyProperty *property = (DependencyProperty*)key;
1397 if (property->GetId() != UIElement::TagProperty && v != NULL && v->Is (Type::DEPENDENCY_OBJECT) && v->AsDependencyObject() != NULL) {
1398 DependencyObject *obj = v->AsDependencyObject ();
1399 obj->UnregisterAllNamesRootedAt (from_ns);
1403 void
1404 DependencyObject::UnregisterAllNamesRootedAt (NameScope *from_ns)
1406 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1407 NameScope *this_ns = NameScope::GetNameScope(this);
1408 if (this_ns && !this_ns->GetTemporary())
1409 return;
1411 const char *n = GetName();
1413 if (n && strlen (n) > 0)
1414 from_ns->UnregisterName (n);
1416 if (autocreate)
1417 g_hash_table_foreach (autocreate->auto_values, unregister_depobj_names, from_ns);
1419 g_hash_table_foreach (local_values, unregister_depobj_names, from_ns);
1422 bool
1423 DependencyObject::SetName (const char* name, NameScope *scope)
1425 DependencyProperty *property = GetDeployment ()->GetTypes ()->GetProperty (NameProperty);
1427 if (scope->FindName (name))
1428 return false;
1430 Value *new_value = new Value (name);
1431 SetValue (property, new_value);
1432 scope->RegisterName (name, this);
1434 return true;
1437 Value *
1438 DependencyObject::ReadLocalValue (int id)
1440 if (IsDisposed ())
1441 return NULL;
1442 return ReadLocalValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1445 Value *
1446 DependencyObject::ReadLocalValue (DependencyProperty *property)
1448 return (Value *) g_hash_table_lookup (local_values, property);
1451 Value *
1452 DependencyObject::ReadLocalValueWithError (DependencyProperty *property, MoonError *error)
1454 if (!HasProperty (Type::INVALID, property, true)) {
1455 Type *pt = Type::Find (property->GetOwnerType ());
1456 MoonError::FillIn (error, MoonError::EXCEPTION, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ()));
1457 return NULL;
1459 return ReadLocalValue (property);
1462 Value *
1463 DependencyObject::GetValueWithError (Type::Kind whatami, DependencyProperty *property, MoonError *error)
1465 if (!HasProperty (whatami, property, true)) {
1466 Type *pt = Type::Find (property->GetOwnerType ());
1467 MoonError::FillIn (error, MoonError::EXCEPTION, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ()));
1468 return NULL;
1470 return GetValue (property);
1473 Value *
1474 DependencyObject::GetValue (int id)
1476 if (IsDisposed ())
1477 return NULL;
1478 return GetValue (GetDeployment ()->GetTypes ()->GetProperty (id));
1481 Value *
1482 DependencyObject::GetValue (DependencyProperty *property)
1484 return GetValue (property, PropertyPrecedence_Highest);
1487 Value *
1488 DependencyObject::GetValue (DependencyProperty *property, PropertyPrecedence startingAtPrecedence)
1490 for (int i = startingAtPrecedence; i < PropertyPrecedence_Count; i ++) {
1491 if (!providers[i])
1492 continue;
1493 Value *value = providers[i]->GetPropertyValue (property);
1494 if (value) return value;
1496 return NULL;
1499 Value *
1500 DependencyObject::GetValueSkippingPrecedence (DependencyProperty *property, PropertyPrecedence toSkip)
1502 for (int i = 0; i < PropertyPrecedence_Count; i ++) {
1503 if (i == toSkip)
1504 continue;
1505 if (!providers[i])
1506 continue;
1507 Value *value = providers[i]->GetPropertyValue (property);
1508 if (value) return value;
1510 return NULL;
1513 Value *
1514 DependencyObject::GetValueNoDefault (int id)
1516 if (IsDisposed ())
1517 return NULL;
1518 return GetValueNoDefault (GetDeployment ()->GetTypes ()->GetProperty (id));
1521 Value *
1522 DependencyObject::GetValueNoDefault (DependencyProperty *property)
1524 Value *value = NULL;
1526 for (int i = 0; i < PropertyPrecedence_DefaultValue; i ++) {
1527 if (!providers[i])
1528 continue;
1529 value = providers[i]->GetPropertyValue (property);
1530 if (value) break;
1532 return value && !value->GetIsNull () ? value : NULL;
1535 Value *
1536 DependencyObject::GetValueNoDefaultWithError (DependencyProperty *property, MoonError *error)
1538 if (!HasProperty (Type::INVALID, property, true)) {
1539 Type *pt = Type::Find (property->GetOwnerType ());
1540 MoonError::FillIn (error, MoonError::EXCEPTION, g_strdup_printf ("Cannot get the DependencyProperty %s.%s on an object of type %s", pt ? pt->GetName () : "<unknown>", property->GetName (), GetTypeName ()));
1541 return NULL;
1543 return GetValueNoDefault (property);
1546 void
1547 DependencyObject::ProviderValueChanged (PropertyPrecedence providerPrecedence,
1548 DependencyProperty *property,
1549 Value *old_provider_value, Value *new_provider_value,
1550 bool notify_listeners, MoonError *error)
1552 int p;
1554 // if they're both NULL, get out of here.
1555 if (!old_provider_value && !new_provider_value)
1556 return;
1558 // first we look for a value higher in precedence for this property
1559 for (p = providerPrecedence - 1; p >= PropertyPrecedence_Highest; p --) {
1560 if (providers[p] && providers[p]->GetPropertyValue (property)) {
1561 // a provider higher in precedence already has
1562 // a value for this property, so the one
1563 // that's changing isn't visible anyway.
1564 return;
1568 Value *old_value;
1569 Value *new_value;
1571 if (!old_provider_value || !new_provider_value) {
1572 Value *lower_priority_value = GetValue (property, (PropertyPrecedence)(providerPrecedence + 1));
1574 if (old_provider_value == NULL) {
1575 // we're changing from the old value (from a lower
1576 // priority provider) to @new_provider_value.
1577 old_value = lower_priority_value;
1578 new_value = new_provider_value;
1580 else if (new_provider_value == NULL) {
1581 // we're changing from @old_provider_value to whatever the
1582 // value lower on the priority list is.
1583 old_value = old_provider_value;
1584 new_value = lower_priority_value;
1587 else {
1588 old_value = old_provider_value;
1589 new_value = new_provider_value;
1592 bool equal = false;
1594 if (old_value != NULL && new_value != NULL) {
1595 equal = !property->AlwaysChange() && (*old_value == *new_value);
1596 } else {
1597 equal = false;
1600 if (!equal) {
1601 DependencyObject *old_as_dep = NULL;
1602 DependencyObject *new_as_dep = NULL;
1604 // XXX this flag should be part of the DP metadata.
1605 // we also need to audit other "typeof (object)" DP's
1606 // to make sure they set parent when they should (and
1607 // don't when they shouldn't.)
1608 bool setsParent = !property->IsCustom ();
1610 if (old_value && old_value->Is (Type::DEPENDENCY_OBJECT))
1611 old_as_dep = old_value->AsDependencyObject ();
1612 if (new_value && new_value->Is (Type::DEPENDENCY_OBJECT))
1613 new_as_dep = new_value->AsDependencyObject ();
1615 if (old_as_dep && setsParent) {
1616 old_as_dep->SetSurface (NULL);
1618 // unset its parent
1619 old_as_dep->SetParent (NULL, NULL);
1621 // remove ourselves as a target
1622 old_as_dep->RemoveTarget (this);
1624 // unregister from the existing value
1625 old_as_dep->RemovePropertyChangeListener (this, property);
1627 if (old_as_dep->Is(Type::COLLECTION)) {
1628 old_as_dep->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1629 old_as_dep->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1633 if (new_as_dep && setsParent) {
1634 new_as_dep->SetSurface (GetSurface ());
1636 new_as_dep->SetParent (this, error);
1637 if (error->number)
1638 return;
1640 new_as_dep->SetResourceBase (GetResourceBase());
1642 if (new_as_dep->Is(Type::COLLECTION)) {
1643 new_as_dep->AddHandler (Collection::ChangedEvent, collection_changed, this);
1644 new_as_dep->AddHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1647 // listen for property changes on the new object
1648 new_as_dep->AddPropertyChangeListener (this, property);
1650 // add ourselves as a target
1651 new_as_dep->AddTarget (this);
1654 PropertyChangedEventArgs args (property, property->GetId (), old_value, new_value);
1656 // we need to make this optional, as doing it for NameScope
1657 // merging is killing performance (and noone should ever care
1658 // about that property changing)
1659 if (notify_listeners) {
1660 listeners_notified = false;
1662 OnPropertyChanged (&args, error);
1664 if (!listeners_notified) {
1665 g_warning ("setting property %s::%s on object of type %s didn't result in listeners being notified",
1666 Type::Find(property->GetOwnerType())->GetName (), property->GetName(), GetTypeName ());
1667 if (error->number)
1668 g_warning ("the error was: %s", error->message);
1671 if (property && property->GetChangedCallback () != NULL) {
1672 PropertyChangeHandler callback = property->GetChangedCallback ();
1673 callback (this, &args, error, NULL);
1679 void
1680 DependencyObject::ClearValue (int id, bool notify_listeners)
1682 if (IsDisposed ())
1683 return;
1684 ClearValue (GetDeployment ()->GetTypes ()->GetProperty (id), notify_listeners);
1687 void
1688 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners)
1690 ClearValue(property, notify_listeners, NULL);
1693 void
1694 DependencyObject::ClearValue (DependencyProperty *property, bool notify_listeners, MoonError *error)
1696 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1697 Value *old_local_value;
1699 if (!(old_local_value = ReadLocalValue (property)))
1700 if (property->IsAutoCreated ())
1701 old_local_value = autocreate->ReadLocalValue (property);
1703 if (old_local_value == NULL) {
1704 // there wasn't a local value set. don't do anything
1705 return;
1708 // detach from the existing value
1709 if (old_local_value->Is (Type::DEPENDENCY_OBJECT)) {
1710 DependencyObject *dob = old_local_value->AsDependencyObject();
1712 if (dob != NULL) {
1713 // unset its parent
1714 dob->SetParent (NULL, NULL);
1716 // unregister from the existing value
1717 dob->RemovePropertyChangeListener (this, property);
1718 dob->SetSurface (NULL);
1719 if (dob->Is(Type::COLLECTION)) {
1720 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, this);
1721 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, this);
1726 g_hash_table_remove (local_values, property);
1728 if (property->IsAutoCreated ())
1729 autocreate->ClearValue (property);
1731 // this is... yeah, it's disgusting
1732 for (int p = PropertyPrecedence_LocalValue + 1; p < PropertyPrecedence_Count; p ++) {
1733 if (providers[p])
1734 providers[p]->RecomputePropertyValue (property);
1737 ProviderValueChanged (PropertyPrecedence_LocalValue, property, old_local_value, NULL, notify_listeners, error);
1739 delete old_local_value;
1742 gboolean
1743 DependencyObject::dispose_value (gpointer key, gpointer value, gpointer data)
1745 DependencyObject *_this = (DependencyObject*)data;
1747 Value *v = (Value *) value;
1749 if (!value)
1750 return TRUE;
1752 // detach from the existing value
1753 if (v->Is (Type::DEPENDENCY_OBJECT)){
1754 DependencyObject *dob = v->AsDependencyObject();
1756 if (dob != NULL) {
1757 if (_this == dob->GetParent()) {
1758 // unset its logical parent
1759 dob->SetParent (NULL, NULL);
1762 // unregister from the existing value
1763 dob->RemovePropertyChangeListener ((DependencyObject*)data, NULL);
1765 if (dob->Is(Type::COLLECTION)) {
1766 dob->RemoveHandler (Collection::ChangedEvent, collection_changed, _this);
1767 dob->RemoveHandler (Collection::ItemChangedEvent, collection_item_changed, _this);
1772 delete (Value *) value;
1774 return TRUE;
1777 void
1778 DependencyObject::collection_changed (EventObject *sender, EventArgs *args, gpointer closure)
1780 DependencyObject *obj = (DependencyObject*)closure;
1781 obj->OnCollectionChanged ((Collection*)sender, (CollectionChangedEventArgs*)args);
1784 void
1785 DependencyObject::collection_item_changed (EventObject *sender, EventArgs *args, gpointer closure)
1787 DependencyObject *obj = (DependencyObject*)closure;
1788 CollectionItemChangedEventArgs* itemArgs = (CollectionItemChangedEventArgs*)args;
1790 PropertyChangedEventArgs propChangedArgs (itemArgs->GetProperty(),
1791 itemArgs->GetProperty()->GetId (),
1792 itemArgs->GetOldValue(),
1793 itemArgs->GetNewValue());
1795 obj->OnCollectionItemChanged ((Collection*)sender,
1796 itemArgs->GetCollectionItem(),
1797 &propChangedArgs);
1800 DependencyObject::DependencyObject ()
1801 : EventObject (Type::DEPENDENCY_OBJECT)
1803 Initialize ();
1806 DependencyObject::DependencyObject (Deployment *deployment, Type::Kind object_type)
1807 : EventObject (deployment, object_type)
1809 Initialize ();
1812 DependencyObject::DependencyObject (Type::Kind object_type)
1813 : EventObject (object_type)
1815 Initialize ();
1818 void
1819 DependencyObject::Initialize ()
1821 providers = new PropertyValueProvider*[PropertyPrecedence_Count];
1823 providers[PropertyPrecedence_LocalValue] = new LocalPropertyValueProvider (this, PropertyPrecedence_LocalValue);
1824 providers[PropertyPrecedence_DynamicValue] = NULL; // subclasses will set this if they need it.
1826 providers[PropertyPrecedence_LocalStyle] = NULL; // this is a frameworkelement specific thing
1827 providers[PropertyPrecedence_DefaultStyle] = NULL; // this is a frameworkelement specific thing
1829 providers[PropertyPrecedence_Inherited] = new InheritedPropertyValueProvider (this, PropertyPrecedence_Inherited);
1830 providers[PropertyPrecedence_DefaultValue] = new DefaultValuePropertyValueProvider (this, PropertyPrecedence_DefaultValue);
1831 providers[PropertyPrecedence_AutoCreate] = new AutoCreatePropertyValueProvider (this, PropertyPrecedence_AutoCreate);
1833 local_values = g_hash_table_new (g_direct_hash, g_direct_equal);
1834 listener_list = NULL;
1835 parent = NULL;
1836 is_hydrated = false;
1837 is_frozen = false;
1838 resource_base = NULL;
1839 storage_hash = NULL; // Create it on first usage request
1842 void
1843 DependencyObject::Freeze()
1845 is_frozen = true;
1848 struct CloneClosure {
1849 Types *types;
1850 DependencyObject *old_do;
1851 DependencyObject *new_do;
1854 void
1855 DependencyObject::clone_local_value (DependencyProperty *key, Value *value, gpointer data)
1857 CloneClosure *closure = (CloneClosure*)data;
1859 // don't clone the name property, or we end up with nasty
1860 // duplicate name errors.
1861 if (key->GetId() == DependencyObject::NameProperty)
1862 return;
1864 Value *cv = Value::Clone (value, closure->types);
1866 closure->new_do->SetValue (key, cv);
1868 delete cv;
1871 void
1872 DependencyObject::clone_autocreated_value (DependencyProperty *key, Value *value, gpointer data)
1874 CloneClosure *closure = (CloneClosure*)data;
1876 Value *old_value = closure->old_do->GetValue (key, PropertyPrecedence_AutoCreate);
1878 // this should create the new object
1879 Value *new_value = closure->new_do->GetValue (key, PropertyPrecedence_AutoCreate);
1881 if (old_value && !old_value->GetIsNull() && old_value->Is (Type::DEPENDENCY_OBJECT) &&
1882 new_value && !new_value->GetIsNull() && new_value->Is (Type::DEPENDENCY_OBJECT)) {
1883 DependencyObject *new_obj = new_value->AsDependencyObject(closure->types);
1884 DependencyObject *old_obj = old_value->AsDependencyObject(closure->types);
1886 new_obj->CloneCore (closure->types, old_obj);
1890 void
1891 DependencyObject::clone_animation_storage (DependencyProperty *key, AnimationStorage *storage, gpointer data)
1893 CloneClosure *closure = (CloneClosure*)data;
1895 // we need to fake an AnimationStorage so that any newly created animations on this clone get the right
1896 AnimationStorage *new_storage = new AnimationStorage (storage->GetClock(), storage->GetTimeline(),
1897 closure->new_do, key);
1899 new_storage->FlagAsNonResetable();
1900 new_storage->DetachTarget ();
1901 new_storage->SetStopValue (storage->GetStopValue());
1903 closure->new_do->AttachAnimationStorage (key, new_storage);
1906 DependencyObject*
1907 DependencyObject::Clone (Types *types)
1909 Type *t = types->Find (GetObjectType());
1911 DependencyObject *new_do = t->CreateInstance();
1913 if (new_do)
1914 new_do->CloneCore (types, (DependencyObject*)this); // this cast should be unnecessary. but C++ const behavior sucks.
1916 return new_do;
1919 void
1920 DependencyObject::CloneCore (Types *types, DependencyObject* fromObj)
1922 CloneClosure closure;
1923 closure.types = types;
1924 closure.old_do = fromObj;
1925 closure.new_do = this;
1927 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) fromObj->providers[PropertyPrecedence_AutoCreate];
1929 g_hash_table_foreach (autocreate->auto_values, (GHFunc)DependencyObject::clone_autocreated_value, &closure);
1930 g_hash_table_foreach (fromObj->local_values, (GHFunc)DependencyObject::clone_local_value, &closure);
1931 if (fromObj->storage_hash)
1932 g_hash_table_foreach (fromObj->storage_hash, (GHFunc)DependencyObject::clone_animation_storage, &closure);
1935 static void
1936 detach_target_func (DependencyProperty *prop, AnimationStorage *storage, gpointer unused)
1938 storage->DetachTarget ();
1939 delete storage;
1942 DependencyObject::~DependencyObject ()
1944 g_hash_table_destroy (local_values);
1945 local_values = NULL;
1946 delete[] providers;
1947 providers = NULL;
1948 g_free (resource_base);
1950 if (storage_hash) {
1951 g_hash_table_foreach (storage_hash, (GHFunc) detach_target_func, NULL);
1952 g_hash_table_destroy (storage_hash);
1953 storage_hash = NULL;
1957 static void
1958 free_listener (gpointer data, gpointer user_data)
1960 Listener* listener = (Listener*) data;
1961 delete listener;
1964 void
1965 DependencyObject::Dispose ()
1967 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
1969 if (listener_list != NULL) {
1970 g_slist_foreach (listener_list, free_listener, NULL);
1971 g_slist_free (listener_list);
1972 listener_list = NULL;
1975 RemoveAllListeners();
1977 if (autocreate)
1978 g_hash_table_foreach_remove (autocreate->auto_values, dispose_value, this);
1980 g_hash_table_foreach_remove (local_values, dispose_value, this);
1982 for (int i = 0; i < PropertyPrecedence_Count; i ++) {
1983 delete providers[i];
1984 providers [i] = NULL;
1987 EventObject::Dispose ();
1990 static void
1991 get_attached_props (gpointer key, gpointer value, gpointer user_data)
1993 DependencyProperty *prop = (DependencyProperty *) key;
1994 GHashTable *props = (GHashTable *) user_data;
1996 if (!(g_hash_table_lookup (props, (gpointer) prop->GetHashKey ())))
1997 g_hash_table_insert (props, (gpointer) prop->GetHashKey (), prop);
2000 static void
2001 hash_keys_to_array (gpointer key, gpointer value, gpointer user_data)
2003 g_ptr_array_add ((GPtrArray *) user_data, key);
2006 static void
2007 hash_values_to_array (gpointer key, gpointer value, gpointer user_data)
2009 g_ptr_array_add ((GPtrArray *) user_data, value);
2012 DependencyProperty **
2013 DependencyObject::GetProperties (bool only_changed)
2015 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2016 DependencyProperty **props;
2017 GHashTable *table;
2018 GPtrArray *array;
2020 array = g_ptr_array_new ();
2022 if (!only_changed) {
2023 // get our class/inherited DependencyProperties
2024 table = GetType ()->CopyProperties (true);
2026 // find any attached properties that have been set
2027 g_hash_table_foreach (local_values, get_attached_props, table);
2029 // dump them to an array
2030 g_hash_table_foreach (table, hash_values_to_array, array);
2031 g_hash_table_destroy (table);
2032 } else {
2033 g_hash_table_foreach (local_values, hash_keys_to_array, array);
2034 g_hash_table_foreach (autocreate->auto_values, hash_keys_to_array, array);
2037 g_ptr_array_add (array, NULL);
2038 props = (DependencyProperty **) array->pdata;
2039 g_ptr_array_free (array, false);
2041 return props;
2044 DependencyProperty *
2045 DependencyObject::GetDependencyProperty (const char *name)
2047 return DependencyProperty::GetDependencyProperty (GetObjectType (), name);
2050 bool
2051 DependencyObject::HasProperty (const char *name, bool inherits)
2053 return DependencyProperty::GetDependencyProperty (GetObjectType (), name, inherits) != NULL;
2056 bool
2057 DependencyObject::HasProperty (Type::Kind whatami, DependencyProperty *property, bool inherits)
2059 Type::Kind this_type = whatami == Type::INVALID ? GetObjectType () : whatami;
2061 // TODO: Handle attached properties correctly.
2063 if (property->IsAttached ())
2064 return true;
2067 printf ("DependencyObject::HasProperty (%p, %i (%s), %p (%i %s.%s), %i)..\n",
2069 whatami, Type::Find (whatami)->name,
2070 property, property->GetOwnerType (), Type::Find (property->GetOwnerType ())->name, property->GetName (),
2071 inherits);
2074 if (property == NULL)
2075 return false;
2077 if (property->GetOwnerType () == this_type)
2078 return true;
2080 if (!inherits)
2081 return false;
2083 if (!Type::IsSubclassOf (this_type, property->GetOwnerType ())) {
2084 bool is_prop_custom = property->IsCustom ();
2085 bool is_owner_custom = property->GetOwnerType () > Type::LASTTYPE;
2086 bool is_this_custom = this_type > Type::LASTTYPE;
2087 bool accept = false;
2089 // Yuck.
2090 // This looks very wrong, but it's what SL seems to do.
2091 if (is_prop_custom) {
2092 if (!is_owner_custom && !is_this_custom) {
2093 // SL does not throw errors for custom properties defined on a builtin type
2094 // and then used on another (unrelated) builtin type (DO.GetValue usage at least)
2095 accept = true;
2096 } else if (is_owner_custom) {
2097 // And this is a custom property defined on a custom type and used anywhere.
2098 accept = true;
2101 return accept;
2104 return true;
2107 DependencyObject *
2108 DependencyObject::FindName (const char *name)
2110 return FindName (name, Control::GetIsTemplateItem (this));
2113 DependencyObject *
2114 DependencyObject::FindName (const char *name, bool template_item)
2116 NameScope *scope = NameScope::GetNameScope (this);
2118 if (scope && (template_item == scope->GetIsLocked ()))
2119 return scope->FindName (name);
2121 if (parent)
2122 return parent->FindName (name, template_item);
2124 return NULL;
2127 NameScope *
2128 DependencyObject::FindNameScope ()
2130 return FindNameScope (Control::GetIsTemplateItem (this));
2133 NameScope*
2134 DependencyObject::FindNameScope (bool template_namescope)
2136 NameScope *scope = NameScope::GetNameScope (this);
2138 // Only template namescopes are locked (for the moment)
2139 if (scope && (template_namescope == scope->GetIsLocked ()))
2140 return scope;
2142 if (parent)
2143 return parent->FindNameScope (template_namescope);
2145 return NULL;
2148 DependencyObject *
2149 DependencyObject::FindName (const char *name, Type::Kind *element_kind)
2151 //printf ("Looking up in %p the string %p\n", obj, name);
2152 //printf (" String: %s\n", name);
2153 DependencyObject *ret = FindName (name);
2155 if (ret == NULL)
2156 return NULL;
2158 *element_kind = ret->GetObjectType ();
2160 return ret;
2163 AnimationStorage*
2164 DependencyObject::GetAnimationStorageFor (DependencyProperty *prop)
2166 if (!storage_hash)
2167 return NULL;
2169 return (AnimationStorage *) g_hash_table_lookup (storage_hash, prop);
2172 AnimationStorage*
2173 DependencyObject::AttachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2175 // Create hash on first access to save some mem
2176 if (!storage_hash)
2177 storage_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2179 AnimationStorage *attached_storage = (AnimationStorage *) g_hash_table_lookup (storage_hash, prop);
2180 if (attached_storage)
2181 attached_storage->DetachTarget ();
2183 g_hash_table_insert (storage_hash, prop, storage);
2184 return attached_storage;
2187 void
2188 DependencyObject::DetachAnimationStorage (DependencyProperty *prop, AnimationStorage *storage)
2190 if (!storage_hash)
2191 return;
2193 if (g_hash_table_lookup (storage_hash, prop) == storage)
2194 g_hash_table_remove (storage_hash, prop);
2198 // A helper debugging routine for C#
2200 const char *
2201 dependency_object_get_name (DependencyObject *obj)
2203 return obj->GetName ();
2206 Type::Kind
2207 dependency_object_get_object_type (DependencyObject *obj)
2209 return obj->GetObjectType ();
2212 const char *
2213 dependency_object_get_type_name (DependencyObject *obj)
2215 return obj->GetTypeName ();
2218 // Used by routines which need to create DO from code
2219 void
2220 dependency_object_set_name (DependencyObject *obj, const char *name)
2222 obj->SetValue (DependencyObject::NameProperty, Value (name));
2225 static void
2226 set_surface (gpointer key, gpointer value, gpointer data)
2228 Surface *s = (Surface *) data;
2229 Value *v = (Value *) value;
2231 if (v && v->Is (Type::DEPENDENCY_OBJECT)) {
2232 DependencyObject *dob = v->AsDependencyObject();
2233 if (dob)
2234 dob->SetSurface (s);
2238 void
2239 DependencyObject::SetSurface (Surface *s)
2241 AutoCreatePropertyValueProvider *autocreate = (AutoCreatePropertyValueProvider *) providers[PropertyPrecedence_AutoCreate];
2243 if (GetSurface() == s)
2244 return;
2246 EventObject::SetSurface (s);
2248 if (autocreate)
2249 g_hash_table_foreach (autocreate->auto_values, set_surface, s);
2251 g_hash_table_foreach (local_values, set_surface, s);
2254 void
2255 DependencyObject::SetParent (DependencyObject *parent, MoonError *error)
2257 if (parent == this->parent)
2258 return;
2260 #if DEBUG
2261 // Check for circular families
2262 DependencyObject *current = parent;
2263 while (current != NULL) {
2264 if (current == this) {
2265 g_warning ("cycle found in logical tree. bailing out");
2266 return;
2268 current = current->GetParent ();
2270 #endif
2272 if (!this->parent) {
2273 if (parent) {
2274 NameScope *this_scope = NameScope::GetNameScope(this);
2275 NameScope *parent_scope = parent->FindNameScope();
2276 if (this_scope) {
2277 if (this_scope->GetTemporary()) {
2278 // if we have a temporary name scope, merge it into the
2279 // closest one up the hierarchy.
2280 if (parent_scope) {
2281 parent_scope->MergeTemporaryScope (this_scope, error);
2282 ClearValue (NameScope::NameScopeProperty, false);
2284 else {
2285 // oddly enough, if
2286 // there's no parent
2287 // namescope, we don't
2288 // do anything
2291 else {
2292 // we have a non-temporary scope. we still have to register the name
2293 // of this element (not the ones in the subtree rooted at this element)
2294 // in the new parent scope. we only register the name in the parent scope
2295 // if the element was hydrated, not when it was created from a string.
2296 if (IsHydratedFromXaml()) {
2297 const char *name = GetName();
2298 if (parent_scope && name && *name) {
2299 DependencyObject *existing_obj = parent_scope->FindName (name);
2300 if (existing_obj != this) {
2301 if (existing_obj) {
2302 MoonError::FillIn (error, MoonError::ARGUMENT, g_strdup_printf ("name `%s' is already registered in new parent namescope.", name));
2303 return;
2305 parent_scope->RegisterName (name, this);
2311 else {
2312 // we don't have a namescope at all,
2313 // we have to iterate over the subtree
2314 // rooted at this object, and merge
2315 // the names into the parent
2316 // namescope.
2318 if (parent_scope) {
2319 NameScope *temp_scope = new NameScope();
2320 temp_scope->SetTemporary (true);
2322 RegisterAllNamesRootedAt (temp_scope, error);
2324 if (error->number) {
2325 temp_scope->unref ();
2326 return;
2329 parent_scope->MergeTemporaryScope (temp_scope, error);
2331 temp_scope->unref ();
2336 else {
2337 if (!parent) {
2338 NameScope *parent_scope = this->parent->FindNameScope ();
2339 if (parent_scope)
2340 UnregisterAllNamesRootedAt (parent_scope);
2344 if (!error || error->number == 0)
2345 this->parent = parent;
2348 Value *
2349 dependency_object_get_value (DependencyObject *object, DependencyProperty *prop)
2351 if (object == NULL)
2352 return NULL;
2354 return object->GetValue (prop);
2357 Value *
2358 dependency_object_get_value_no_default (DependencyObject *object, DependencyProperty *prop)
2360 if (object == NULL)
2361 return NULL;
2363 return object->GetValueNoDefault (prop);
2366 void
2367 dependency_object_set_value (DependencyObject *object, DependencyProperty *prop, Value *val)
2369 if (object == NULL)
2370 return;
2372 object->SetValue (prop, val);
2375 void
2376 DependencyObject::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
2378 if (DependencyObject::NameProperty == args->GetId ()) {
2379 NameScope *scope = FindNameScope ();
2380 if (scope && args->GetNewValue()) {
2381 if (args->GetOldValue ())
2382 scope->UnregisterName (args->GetOldValue ()->AsString ());
2383 scope->RegisterName (args->GetNewValue()->AsString (), this);
2385 if (IsHydratedFromXaml () && parent) {
2386 // we also need to update any parent
2387 // namescope about our name change
2389 scope = parent->FindNameScope ();
2390 if (scope) {
2391 if (args->GetOldValue ())
2392 scope->UnregisterName (args->GetOldValue ()->AsString ());
2393 scope->RegisterName (args->GetNewValue()->AsString (), this);
2399 NotifyListenersOfPropertyChange (args, error);
2402 DependencyObject*
2403 DependencyObject::GetContent()
2405 const char *content_property_name = GetType()->GetContentPropertyName();
2406 if (!content_property_name)
2407 return NULL;
2409 DependencyProperty *content_property = GetDependencyProperty (content_property_name);
2410 if (!content_property)
2411 return NULL;
2413 Value *content_value = GetValue(content_property);
2415 if (!content_value)
2416 return NULL;
2418 return content_value->AsDependencyObject();