2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / collection.cpp
blob395b0eea617adfe4243fd237afe748ec62c4cf4e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * collection.cpp: different types of collections
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 <glib.h>
15 #include "cbinding.h"
16 #include "canvas.h"
17 #include "collection.h"
18 #include "geometry.h"
19 #include "transform.h"
20 #include "frameworkelement.h"
21 #include "namescope.h"
22 #include "textblock.h"
23 #include "utils.h"
24 #include "error.h"
25 #include "deployment.h"
26 #include "multiscalesubimage.h"
29 // Collection
32 Collection::Collection ()
34 SetObjectType (Type::COLLECTION);
35 array = g_ptr_array_new ();
36 generation = 0;
39 void
40 Collection::CloneCore (Types* types, DependencyObject* fromObj)
42 DependencyObject::CloneCore (types, fromObj);
44 Collection *c = (Collection*)fromObj;
46 for (guint i = 0; i < c->array->len; i++) {
47 Value *value = Value::Clone ((Value *) c->array->pdata[i]);
48 Add (value);
49 delete value;
53 Collection::~Collection ()
55 g_ptr_array_free (array, true);
58 void
59 Collection::Dispose ()
61 Value *value;
63 for (guint i = 0; i < array->len; i++) {
64 value = (Value *) array->pdata[i];
65 RemovedFromCollection (value);
66 delete value;
68 g_ptr_array_set_size (array, 0);
70 DependencyObject::Dispose ();
73 CollectionIterator *
74 Collection::GetIterator ()
76 return new CollectionIterator (this);
79 int
80 Collection::Add (Value *value)
82 MoonError error;
83 return AddWithError (value, &error);
86 int
87 Collection::Add (Value value)
89 return Add (&value);
92 int
93 Collection::AddWithError (Value *value, MoonError *error)
95 bool rv = InsertWithError (array->len, value, error);
96 return rv ? array->len - 1 : -1;
99 bool
100 Collection::Clear ()
102 EmitChanged (CollectionChangedActionClearing, NULL, NULL, -1);
104 guint len = array->len;
105 Value** vals = new Value*[len];
106 memmove (vals, array->pdata, len * sizeof(Value*));
108 g_ptr_array_set_size (array, 0);
109 generation++;
111 SetCount (0);
113 for (guint i = 0; i < len; i++) {
114 RemovedFromCollection (vals[i]);
115 delete vals[i];
117 delete[] vals;
119 EmitChanged (CollectionChangedActionCleared, NULL, NULL, -1);
121 return true;
124 bool
125 Collection::Contains (Value *value)
127 return IndexOf (value) != -1;
131 Collection::IndexOf (Value *value)
133 Value *v;
135 for (guint i = 0; i < array->len; i++) {
136 v = (Value *) array->pdata[i];
137 if (*v == *value)
138 return i;
141 return -1;
144 bool
145 Collection::Insert (int index, Value value)
147 return Insert (index, &value);
150 bool
151 Collection::InsertWithError (int index, Value *value, MoonError *error)
153 Value *added;
155 // Check that the item can be added to our collection
156 if (!CanAdd (value))
157 return false;
159 // bounds check
160 if (index < 0)
161 return false;
163 int count = GetCount ();
164 if (index > count)
165 index = count;
167 added = new Value (*value);
169 if (AddedToCollection (added, error)) {
170 g_ptr_array_insert (array, index, added);
172 SetCount ((int) array->len);
174 Value *added_copy = new Value (*added);
176 EmitChanged (CollectionChangedActionAdd, added_copy, NULL, index);
178 delete added_copy;
180 return true;
182 else {
183 delete added;
184 return false;
188 bool
189 Collection::Insert (int index, Value *value)
191 MoonError error;
192 return InsertWithError (index, value, &error);
195 bool
196 Collection::Remove (Value value)
198 return Remove (&value);
201 bool
202 Collection::Remove (Value *value)
204 int index;
206 if ((index = IndexOf (value)) == -1)
207 return false;
209 return RemoveAt (index);
212 bool
213 Collection::RemoveAt (int index)
215 Value *value;
217 // check bounds
218 if (index < 0 || (guint) index >= array->len)
219 return false;
221 value = (Value *) array->pdata[index];
223 g_ptr_array_remove_index (array, index);
224 SetCount ((int) array->len);
225 generation++;
227 RemovedFromCollection (value);
229 EmitChanged (CollectionChangedActionRemove, NULL, value, index);
231 delete value;
233 return true;
236 bool
237 Collection::RemoveAtWithError (int index, MoonError *error)
239 // check bounds
240 if (index < 0 || (guint) index >= array->len) {
241 MoonError::FillIn (error, MoonError::ARGUMENT_OUT_OF_RANGE, "");
242 return false;
245 return RemoveAt (index);
248 Value *
249 Collection::GetValueAt (int index)
251 if (index < 0 || (guint) index >= array->len)
252 return NULL;
254 return (Value *) array->pdata[index];
257 Value *
258 Collection::GetValueAtWithError (int index, MoonError *error)
260 // check array bounds
261 if (index < 0 || (guint) index >= array->len) {
262 MoonError::FillIn (error, MoonError::ARGUMENT_OUT_OF_RANGE, "");
263 return NULL;
266 return GetValueAt (index);
269 bool
270 Collection::SetValueAt (int index, Value *value)
272 MoonError error;
273 return SetValueAtWithError (index, value, &error);
276 bool
277 Collection::SetValueAtWithError (int index, Value *value, MoonError *error)
279 Value *added, *removed;
281 // Check that the value can be added to our collection
282 if (!CanAdd (value)) {
283 MoonError::FillIn (error, MoonError::ARGUMENT, "");
284 return false;
287 // check array bounds
288 if (index < 0 || (guint) index >= array->len) {
289 MoonError::FillIn (error, MoonError::ARGUMENT_OUT_OF_RANGE, "");
290 return false;
293 removed = (Value *) array->pdata[index];
294 added = new Value (*value);
296 if (AddedToCollection (added, error)) {
297 array->pdata[index] = added;
299 RemovedFromCollection (removed);
301 EmitChanged (CollectionChangedActionReplace, added, removed, index);
303 delete removed;
305 return true;
307 else
308 return false;
311 void
312 Collection::EmitChanged (CollectionChangedAction action, Value *new_value, Value *old_value, int index)
314 Emit (Collection::ChangedEvent, new CollectionChangedEventArgs (action, new_value, old_value, index));
317 void
318 Collection::EmitItemChanged (DependencyObject *object, DependencyProperty *property, Value *newValue, Value *oldValue)
320 Emit (Collection::ItemChangedEvent, new CollectionItemChangedEventArgs (object, property, oldValue, newValue));
323 bool
324 Collection::CanAdd (Value *value)
326 return value->Is (GetDeployment (), GetElementType ());
331 // DependencyObjectCollection
334 DependencyObjectCollection::DependencyObjectCollection ()
336 SetObjectType (Type::DEPENDENCY_OBJECT_COLLECTION);
339 DependencyObjectCollection::~DependencyObjectCollection ()
343 bool
344 DependencyObjectCollection::AddedToCollection (Value *value, MoonError *error)
346 DependencyObject *obj = value->AsDependencyObject ();
348 DependencyObject *parent = obj->GetParent();
350 // Attach /before/ setting the logical parent
351 // because Storyboard::SetIsAttached () needs to be able to
352 // distinguish between the two cases.
353 obj->SetIsAttached (IsAttached ());
355 if (parent) {
356 if (parent->Is(Type::COLLECTION) && !obj->PermitsMultipleParents ()) {
357 MoonError::FillIn (error, MoonError::INVALID_OPERATION, "Element is already a child of another element.");
358 return false;
361 else {
362 obj->SetParent (this, error);
363 if (error->number)
364 return false;
367 obj->AddPropertyChangeListener (this);
369 bool rv = Collection::AddedToCollection (value, error);
371 if (!rv && parent == NULL) {
372 /* If we set the parent, but the object wasn't added to the collection, make sure we clear the parent */
373 obj->SetParent (NULL, error);
376 return rv;
379 void
380 DependencyObjectCollection::RemovedFromCollection (Value *value)
382 DependencyObject *obj = value->AsDependencyObject ();
384 obj->RemovePropertyChangeListener (this);
385 obj->SetParent (NULL, NULL);
386 obj->SetIsAttached (false);
388 Collection::RemovedFromCollection (value);
391 void
392 DependencyObjectCollection::SetIsAttached (bool attached)
394 if (IsAttached () == attached)
395 return;
397 DependencyObject *obj;
398 Value *value;
400 for (guint i = 0; i < array->len; i++) {
401 value = (Value *) array->pdata[i];
402 obj = value->AsDependencyObject ();
403 obj->SetIsAttached (attached);
406 Collection::SetIsAttached (attached);
409 void
410 DependencyObjectCollection::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
412 EmitItemChanged (obj, subobj_args->GetProperty(), subobj_args->GetNewValue(), subobj_args->GetOldValue());
415 void
416 DependencyObjectCollection::UnregisterAllNamesRootedAt (NameScope *from_ns)
418 DependencyObject *obj;
419 Value *value;
421 Types *types = Deployment::GetCurrent ()->GetTypes ();
422 for (guint i = 0; i < array->len; i++) {
423 value = (Value *) array->pdata[i];
424 obj = value->AsDependencyObject (types);
425 obj->UnregisterAllNamesRootedAt (from_ns);
428 Collection::UnregisterAllNamesRootedAt (from_ns);
431 void
432 DependencyObjectCollection::RegisterAllNamesRootedAt (NameScope *to_ns, MoonError *error)
434 DependencyObject *obj;
435 Value *value;
437 Types *types = Deployment::GetCurrent ()->GetTypes ();
438 for (guint i = 0; i < array->len; i++) {
439 if (error->number)
440 break;
442 value = (Value *) array->pdata[i];
443 obj = value->AsDependencyObject (types);
444 obj->RegisterAllNamesRootedAt (to_ns, error);
447 Collection::RegisterAllNamesRootedAt (to_ns, error);
451 // InlineCollection
454 InlineCollection::InlineCollection ()
456 SetObjectType (Type::INLINE_COLLECTION);
459 InlineCollection::~InlineCollection ()
463 bool
464 InlineCollection::Equals (InlineCollection *inlines)
466 Inline *run0, *run1;
468 if (inlines->array->len != array->len)
469 return false;
471 Types *types = Deployment::GetCurrent ()->GetTypes ();
472 for (guint i = 0; i < array->len; i++) {
473 run1 = ((Value *) inlines->array->pdata[i])->AsInline (types);
474 run0 = ((Value *) array->pdata[i])->AsInline (types);
476 if (!run0->Equals (run1))
477 return false;
480 return true;
485 // UIElementCollection
488 UIElementCollection::UIElementCollection ()
490 SetObjectType (Type::UIELEMENT_COLLECTION);
491 z_sorted = g_ptr_array_new ();
494 UIElementCollection::~UIElementCollection ()
496 g_ptr_array_free (z_sorted, true);
499 static int
500 UIElementZIndexComparer (gconstpointer ui1, gconstpointer ui2)
502 int z1 = Canvas::GetZIndex (*((UIElement **) ui1));
503 int z2 = Canvas::GetZIndex (*((UIElement **) ui2));
505 return z1 - z2;
508 void
509 UIElementCollection::ResortByZIndex ()
511 g_ptr_array_set_size (z_sorted, array->len);
513 if (array->len == 0)
514 return;
516 Types *types = Deployment::GetCurrent ()->GetTypes ();
517 for (guint i = 0; i < array->len; i++)
518 z_sorted->pdata[i] = ((Value *) array->pdata[i])->AsUIElement (types);
520 if (array->len > 1)
521 g_ptr_array_sort (z_sorted, UIElementZIndexComparer);
524 bool
525 UIElementCollection::Clear ()
527 g_ptr_array_set_size (z_sorted, 0);
528 return DependencyObjectCollection::Clear ();
532 // HitTestCollection
535 HitTestCollection::HitTestCollection ()
540 // DoubleCollection
543 DoubleCollection::DoubleCollection ()
545 SetObjectType (Type::DOUBLE_COLLECTION);
548 DoubleCollection::~DoubleCollection ()
552 DoubleCollection *
553 DoubleCollection::FromStr (const char *s)
555 GArray *values = double_garray_from_str (s, 0);
557 if (values->len == 0) {
558 g_array_free (values, true);
559 return NULL;
562 DoubleCollection *doubles = new DoubleCollection ();
563 for (guint i = 0; i < values->len; i ++)
564 doubles->Add (Value (g_array_index (values, double, i)));
565 g_array_free (values, true);
567 return doubles;
571 // Point Collection
574 PointCollection::PointCollection ()
576 SetObjectType (Type::POINT_COLLECTION);
579 PointCollection::~PointCollection ()
583 PointCollection *
584 PointCollection::FromStr (const char *s)
586 int i, j, n = 0;
587 GArray *values = double_garray_from_str (s, 0);
589 n = values->len / 2;
590 if (n == 0 || n % 1 == 1) {
591 g_array_free (values, true);
592 return NULL;
595 PointCollection *points = new PointCollection();
596 for (i = 0, j = 0; j < n; j++) {
597 double x = g_array_index (values, double, i++);
598 double y = g_array_index (values, double, i++);
600 points->Add (Point (x, y));
603 g_array_free (values, true);
604 return points;
608 // Item Collection
611 ItemCollection::ItemCollection ()
613 SetObjectType (Type::ITEM_COLLECTION);
616 ItemCollection::~ItemCollection ()
621 // Trigger Collection
624 TriggerCollection::TriggerCollection ()
626 SetObjectType (Type::TRIGGER_COLLECTION);
629 TriggerCollection::~TriggerCollection ()
634 // TriggerAction Collection
637 TriggerActionCollection::TriggerActionCollection ()
639 SetObjectType (Type::TRIGGERACTION_COLLECTION);
642 TriggerActionCollection::~TriggerActionCollection ()
647 // MultiScaleSubImage Collection
649 MultiScaleSubImageCollection::MultiScaleSubImageCollection ()
651 SetObjectType (Type::MULTISCALESUBIMAGE_COLLECTION);
652 z_sorted = g_ptr_array_new ();
655 MultiScaleSubImageCollection::~MultiScaleSubImageCollection ()
657 g_ptr_array_free (z_sorted, true);
660 static int
661 MultiScaleSubImageZIndexComparer (gconstpointer msisi1, gconstpointer msisi2)
663 int z1 = (*((MultiScaleSubImage**)msisi1))->GetZIndex ();
664 int z2 = (*((MultiScaleSubImage**)msisi2))->GetZIndex ();
666 return z1 - z2;
669 void
670 MultiScaleSubImageCollection::ResortByZIndex ()
672 g_ptr_array_set_size (z_sorted, array->len);
674 if (array->len == 0)
675 return;
677 Types *types = Deployment::GetCurrent ()->GetTypes ();
678 for (guint i = 0; i < array->len; i++)
679 z_sorted->pdata[i] = ((Value *) array->pdata[i])->AsMultiScaleSubImage (types);
681 if (array->len > 1)
682 g_ptr_array_sort (z_sorted, MultiScaleSubImageZIndexComparer);
685 bool
686 MultiScaleSubImageCollection::Clear ()
688 g_ptr_array_set_size (z_sorted, 0);
689 return DependencyObjectCollection::Clear ();
693 // ResourceDictionaryCollection
696 ResourceDictionaryCollection::ResourceDictionaryCollection ()
698 SetObjectType (Type::RESOURCE_DICTIONARY_COLLECTION);
701 ResourceDictionaryCollection::~ResourceDictionaryCollection ()
706 // CollectionIterator
709 CollectionIterator::CollectionIterator (Collection *c)
711 generation = c->Generation ();
712 collection = c;
713 collection->ref ();
714 index = -1;
717 CollectionIterator::~CollectionIterator ()
719 collection->unref ();
722 bool
723 CollectionIterator::Next (MoonError *err)
725 if (generation != collection->Generation ()) {
726 MoonError::FillIn (err, MoonError::INVALID_OPERATION, "The underlying collection has mutated");
727 return false;
730 index++;
732 return index < collection->GetCount ();
735 bool
736 CollectionIterator::Reset ()
738 if (generation != collection->Generation ())
739 return false;
741 index = -1;
743 return true;
746 Value *
747 CollectionIterator::GetCurrent (MoonError *err)
749 if (generation != collection->Generation ()) {
750 MoonError::FillIn (err, MoonError::INVALID_OPERATION, "The underlying collection has mutated");
751 return NULL;
754 if (index < 0 || index >= collection->GetCount ()) {
755 MoonError::FillIn (err, MoonError::INVALID_OPERATION, "Index out of bounds");
756 return NULL;
759 return collection->GetValueAt (index);
762 void
763 CollectionIterator::Destroy (CollectionIterator *iterator)
765 delete iterator;
770 // VisualTreeWalker
773 VisualTreeWalker::VisualTreeWalker (UIElement *obj, VisualTreeWalkerDirection dir, Types *cached)
775 index = 0;
776 collection = NULL;
777 content = obj->GetSubtreeObject ();
778 direction = dir;
779 types = (cached == NULL) ? obj->GetDeployment ()->GetTypes () : cached;
781 if (content != NULL) {
782 if (types->IsSubclassOf (content->GetObjectType (), Type::COLLECTION)) {
783 collection = (Collection *)content;
785 if (!types->IsSubclassOf (content->GetObjectType (), Type::UIELEMENT_COLLECTION))
786 direction = Logical;
789 content->ref ();
793 UIElement *
794 VisualTreeWalker::Step ()
796 UIElement *result = NULL;
798 if (collection) {
799 UIElementCollection *uiecollection = NULL;
800 int count = GetCount ();
802 if (count < 0 || index >= count)
803 return NULL;
805 if (count == 1 && index == 0) {
806 index ++;
807 return collection->GetValueAt (0)->AsUIElement(types);
810 if (direction == ZForward || direction == ZReverse) {
811 uiecollection = (UIElementCollection *)collection;
813 if ((int)uiecollection->z_sorted->len != count) {
814 g_warning ("VisualTreeWalker: unexpectedly got an unsorted UIElementCollection");
815 uiecollection->ResortByZIndex ();
819 switch (direction) {
820 case ZForward:
821 result = (UIElement*)uiecollection->z_sorted->pdata[index];
822 break;
823 case ZReverse:
824 result = (UIElement *)uiecollection->z_sorted->pdata[count - (index + 1)];
825 break;
826 case Logical: {
827 Value *v = collection->GetValueAt (index);
828 result = v == NULL ? NULL : v->AsUIElement (types);
829 break;
831 case LogicalReverse: {
832 Value *v = collection->GetValueAt (count - (index + 1));
833 result = v == NULL ? NULL : v->AsUIElement (types);
834 break;
838 index++;
839 } else {
840 if (index == 0) {
841 index++;
842 result = (UIElement *)content;
843 } else {
844 result = NULL;
848 return result;
852 VisualTreeWalker::GetCount ()
854 if (!content)
855 return 0;
857 if (!collection)
858 return 1;
860 return collection->GetCount ();
863 VisualTreeWalker::~VisualTreeWalker()
865 if (content)
866 content->unref ();
870 class UnsafeUIElementNode : public List::Node {
871 public:
872 UIElement *uielement;
874 UnsafeUIElementNode (UIElement *el) { uielement = el; }
877 DeepTreeWalker::DeepTreeWalker (UIElement *top, VisualTreeWalkerDirection direction, Types *types)
879 walk_list = new List ();
880 walk_list->Append (new UnsafeUIElementNode (top));
881 last = NULL;
882 this->types = types ? types : top->GetDeployment ()->GetTypes ();
883 this->direction = direction;
886 UIElement *
887 DeepTreeWalker::Step ()
889 if (last) {
890 VisualTreeWalker walker (last, direction, types);
891 //VisualTreeWalker walker (last, ZForward, types);
892 UnsafeUIElementNode *prepend = (UnsafeUIElementNode *) walk_list->First ();
893 while (UIElement *child = walker.Step ())
894 walk_list->InsertBefore (new UnsafeUIElementNode (child), prepend);
897 UnsafeUIElementNode *next = (UnsafeUIElementNode*)walk_list->First ();
899 if (!next) {
900 last = NULL;
901 return NULL;
904 UIElement *current = next->uielement;
905 walk_list->Unlink (next);
906 delete next;
907 last = current;
909 return current;
912 void
913 DeepTreeWalker::SkipBranch ()
915 last = NULL;
918 DeepTreeWalker::~DeepTreeWalker ()
920 delete walk_list;
924 // Manual C-Bindings
926 Collection *
927 collection_new (Type::Kind kind)
929 Type *t = Type::Find (Deployment::GetCurrent (), kind);
931 if (!t->IsSubclassOf (Type::COLLECTION)) {
932 g_warning ("create_collection passed non-collection type");
933 return NULL;
936 return (Collection *) t->CreateInstance();