1__all__ = ('object_property', 'bool_property',
2        'array_property', 'set_property', 'dict_property')
3
4from objc import ivar, selector, _C_ID, _C_NSBOOL, _C_BOOL, NULL, _C_NSUInteger
5from objc import lookUpClass
6import collections
7from copy import copy as copy_func
8import sys
9
10NSSet = lookUpClass('NSSet')
11NSObject = lookUpClass('NSObject')
12
13if sys.version_info[0] == 2:
14    range = xrange
15
16    def _str(value):
17        return value
18
19else: # pragma: no cover (py3k)
20    long = int
21
22    def _str(value):
23        return value.decode('ascii')
24
25
26
27def attrsetter(prop, name, copy):
28    if copy:
29        def func(self, value):
30            if isinstance(value, NSObject):
31                setattr(self, name, value.copy())
32            else:
33                setattr(self, name, copy_func(value))
34    else:
35        def func(self, value):
36            setattr(self, name, value)
37    return func
38
39def attrgetter(name):
40    def func(self):
41        return getattr(self, name)
42    return func
43
44def _return_value(value):
45    def func(self):
46        return value
47
48    return func
49
50def _dynamic_getter(name):
51    def getter(object):
52        m = getattr(object.pyobjc_instanceMethods, name)
53        return m()
54    getter.__name__ = name
55    return getter
56
57def _dynamic_setter(name):
58    def setter(object, value):
59        m = getattr(object.pyobjc_instanceMethods, name)
60        return m(value)
61    setter.__name__ = name
62    return setter
63
64class object_property (object):
65    def __init__(self, name=None,
66            read_only=False, copy=False, dynamic=False,
67            ivar=None, typestr=_C_ID, depends_on=None):
68        self.__created = False
69        self.__inherit = False
70        self._name = name
71        self._typestr = typestr
72        self._ro = read_only
73        self._copy = copy
74        self._dynamic = dynamic
75        self._ivar = ivar
76        self._getter = None
77        self._setter = None
78        self._validate = None
79        if depends_on is None:
80            self._depends_on = None
81        else:
82            self._depends_on = set(depends_on)
83
84        self.__getprop = None
85        self.__setprop = None
86        self.__parent = None
87
88    def _clone(self):
89        if self._depends_on is None:
90            depends = None
91        else:
92            depends = self._depends_on.copy()
93
94        v = type(self)(name=self._name,
95                read_only=self._ro, copy=self._copy, dynamic=self._dynamic,
96                ivar=self._ivar, typestr=self._typestr, depends_on=depends)
97        v.__inherit = True
98
99        v.__getprop = self.__getprop
100        v.__setprop = self.__setprop
101        v.__parent = self
102
103        return v
104
105    def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods):
106        self.__created = True
107        if self._name is None:
108            self._name = name
109
110        if self._ivar is not NULL:
111            if self._ivar is None:
112                ivname = '_' + self._name
113            else:
114                ivname = self._ivar
115
116            if self.__parent is None:
117                ivar_ref = ivar(name=ivname, type=self._typestr)
118                class_dict[ivname] = ivar_ref
119
120        if self._ro:
121            self._setter = None
122
123        else:
124            setterName = b'set' + name[0].upper().encode('latin1') + name[1:].encode('latin1') + b':'
125            signature = b'v@:' + self._typestr
126            if self._setter is None:
127                if self.__inherit:
128                    pass
129
130                elif self._dynamic:
131                    dynSetterName = 'set' + name[0].upper() + name[1:] + '_'
132                    self.__setprop = _dynamic_setter(dynSetterName)
133                    instance_methods.add(setterName)
134
135                else:
136
137                    if self._ivar is NULL:
138                        raise ValueError(
139                            "Cannot create default setter for property "
140                            "without ivar")
141
142                    setprop = selector(
143                        attrsetter(self._name, ivname, self._copy),
144                        selector=setterName,
145                        signature=signature
146                    )
147                    setprop.isHidden = True
148                    instance_methods.add(setprop)
149
150                    # Use dynamic setter to avoid problems when subclassing
151                    self.__setprop = _dynamic_setter(_str(setterName))
152            else:
153                setprop = selector(
154                    self._setter,
155                    selector=setterName,
156                    signature=signature
157                )
158                setprop.isHidden = True
159                instance_methods.add(setprop)
160
161                # Use dynamic setter to avoid problems when subclassing
162                self.__setprop = _dynamic_setter(_str(setterName))
163
164        if self._typestr in (_C_NSBOOL, _C_BOOL):
165            getterName = b'is' + name[0].upper().encode('latin1') + name[1:].encode('latin1')
166        else:
167            getterName = self._name.encode('latin1')
168
169        if self._getter is None:
170            if self.__inherit:
171                pass
172
173            elif self._dynamic:
174                if self._typestr in (_C_NSBOOL, _C_BOOL):
175                    dynGetterName = 'is' + name[0].upper() + name[1:]
176                else:
177                    dynGetterName = self._name
178
179                self.__getprop = _dynamic_getter(dynGetterName)
180                instance_methods.add(getterName)
181
182            else:
183                if self._ivar is NULL:
184                    raise ValueError(
185                        "Cannot create default getter for property without ivar")
186
187                self.__getprop = selector(
188                        attrgetter(ivname),
189                        selector=getterName,
190                        signature=self._typestr + b'@:')
191                self.__getprop.isHidden=True
192                instance_methods.add(self.__getprop)
193
194        else:
195            self.__getprop = getprop = selector(
196                    self._getter,
197                    selector=getterName,
198                    signature=self._typestr + b'@:')
199            getprop.isHidden=True
200            instance_methods.add(getprop)
201            #self.__getprop = _dynamic_getter(getterName)
202
203        if self._validate is not None:
204            selName = b'validate' + self._name[0].upper().encode('latin') + self._name[1:].encode('latin') + b':error:'
205            signature = _C_NSBOOL + b'@:N^@o^@'
206            validate = selector(
207                    self._validate,
208                    selector=selName,
209                    signature=signature)
210            class_dict[validate.selector] = validate
211            instance_methods.add(validate)
212
213        if self._depends_on:
214            if self.__parent is not None:
215                if self.__parent._depends_on:
216                    self._depends_on.update(self.__parent._depends_on.copy())
217
218            self._depends_on = self._depends_on
219
220            affecting = selector(
221                    _return_value(NSSet.setWithArray_(list(self._depends_on))),
222                    selector = b'keyPathsForValuesAffecting' + self._name[0].upper().encode('latin1') + self._name[1:].encode('latin1'),
223                    signature = b'@@:',
224                    isClassMethod=True)
225            class_dict[affecting.selector] = affecting
226            class_methods.add(affecting)
227
228    def __get__(self, object, owner):
229        if object is None:
230            return self
231        return self.__getprop(object)
232
233    def __set__(self, object, value):
234        if self.__setprop is None:
235            raise ValueError("setting read-only property " + self._name)
236
237        return self.__setprop(object, value)
238
239    def __delete__(self, object):
240        raise TypeError("cannot delete property " + self._name)
241
242    def depends_on(self, keypath):
243        if self._depends_on is None:
244            self._depends_on = set()
245        self._depends_on.add(keypath)
246
247    def getter(self, function):
248        if self.__created:
249            v = self._clone()
250            v._getter = function
251            return v
252
253        self._getter = function
254        return self
255
256    def setter(self, function):
257
258        if self.__created:
259            v = self._clone()
260            v._ro = False
261            v._setter = function
262            return v
263
264        if self._ro:
265            raise ValueError("Defining settter for read-only property")
266
267        self._setter = function
268        return self
269
270    def validate(self, function):
271        if self._ro:
272            raise ValueError("Defining validator for read-only property")
273
274        if self.__created:
275            v = self._clone()
276            v._validate = function
277            return v
278
279        self._validate = function
280        return self
281
282class bool_property (object_property):
283    def __init__(self, name=None,
284            read_only=False, copy=False, dynamic=False,
285            ivar=None, typestr=_C_NSBOOL):
286        super(bool_property, self).__init__(
287                name, read_only, copy, dynamic, ivar, typestr)
288
289
290
291
292NSIndexSet = lookUpClass('NSIndexSet')
293NSMutableIndexSet = lookUpClass('NSMutableIndexSet')
294NSKeyValueChangeSetting = 1
295NSKeyValueChangeInsertion = 2
296NSKeyValueChangeRemoval = 3
297NSKeyValueChangeReplacement = 4
298
299
300# Helper function for (not) pickling array_proxy instances
301# NOTE: Don't remove this function, it can be referenced from
302# pickle files.
303def _id(value):
304    return value
305
306# FIXME: split into two: array_proxy and mutable_array_proxy
307class array_proxy (collections.MutableSequence):
308    # XXX: The implemenation should be complete, but is currently not
309    # tested.
310    __slots__ = ('_name', '_parent', '__wrapped', '_ro')
311
312    def __init__(self, name, parent, wrapped, read_only):
313        self._name = name
314        self._parent = parent
315        self._ro = read_only
316        self.__wrapped = wrapped
317
318    @property
319    def _wrapped(self):
320        return self.__wrapped.__getvalue__(self._parent)
321
322    def __indexSetForIndex(self, index):
323        if isinstance(index, slice):
324            result = NSMutableIndexSet.alloc().init()
325            start, stop, step = index.indices(len(self._wrapped))
326            for i in range(start, stop, step):
327                result.addIndex_(i)
328
329            return result
330
331        elif isinstance(index, (int, long)):
332            if index < 0:
333                v = len(self) + index
334                if v < 0:
335                    raise IndexError(index)
336                return NSIndexSet.alloc().initWithIndex_(v)
337
338            else:
339                return NSIndexSet.alloc().initWithIndex_(index)
340
341        else:
342            raise TypeError(index)
343
344
345
346    def __repr__(self):
347        return '<array proxy for property ' + self._name + ' ' + repr(self._wrapped) + '>'
348
349    def __reduce__(self):
350        # Ensure that the proxy itself doesn't get stored
351        # in pickles.
352        return _id, (self._wrapped,)
353
354    def __getattr__(self, name):
355        # Default: just defer to wrapped list
356        return getattr(self._wrapped, name)
357
358    def __len__(self):
359        return self._wrapped.__len__()
360
361    def __getitem__(self, index):
362        return self._wrapped[index]
363
364    def __setitem__(self, index, value):
365        if self._ro:
366            raise ValueError("Property '%s' is read-only"%(self._name,))
367
368        indexes = self.__indexSetForIndex(index)
369        self._parent.willChange_valuesAtIndexes_forKey_(
370                NSKeyValueChangeSetting,
371                indexes, self._name)
372        try:
373            self._wrapped[index] = value
374        finally:
375            self._parent.didChange_valuesAtIndexes_forKey_(
376                NSKeyValueChangeReplacement,
377                indexes, self._name)
378
379    def __delitem__(self, index):
380        if self._ro:
381            raise ValueError("Property '%s' is read-only"%(self._name,))
382
383        indexes = self.__indexSetForIndex(index)
384        self._parent.willChange_valuesAtIndexes_forKey_(
385                NSKeyValueChangeRemoval,
386                indexes, self._name)
387        try:
388            del self._wrapped[index]
389        finally:
390            self._parent.didChange_valuesAtIndexes_forKey_(
391                NSKeyValueChangeRemoval,
392                indexes, self._name)
393
394    def append(self, value):
395        if self._ro:
396            raise ValueError("Property '%s' is read-only"%(self._name,))
397
398        index = len(self)
399        indexes = NSIndexSet.alloc().initWithIndex_(index)
400        self._parent.willChange_valuesAtIndexes_forKey_(
401                NSKeyValueChangeInsertion,
402                indexes, self._name)
403        try:
404            self._wrapped.append(value)
405        finally:
406            self._parent.didChange_valuesAtIndexes_forKey_(
407                NSKeyValueChangeInsertion,
408                indexes, self._name)
409
410    def insert(self, index, value):
411        if self._ro:
412            raise ValueError("Property '%s' is read-only"%(self._name,))
413
414        if isinstance(index, slice):
415            raise TypeError("insert argument 1 is a slice")
416
417        indexes = self.__indexSetForIndex(index)
418        self._parent.willChange_valuesAtIndexes_forKey_(
419                NSKeyValueChangeInsertion,
420                indexes, self._name)
421        try:
422            self._wrapped.insert(index, value)
423        finally:
424            self._parent.didChange_valuesAtIndexes_forKey_(
425                NSKeyValueChangeInsertion,
426                indexes, self._name)
427
428    def pop(self, index=-1):
429        if self._ro:
430            raise ValueError("Property '%s' is read-only"%(self._name,))
431
432        if isinstance(index, slice):
433            raise TypeError("insert argument 1 is a slice")
434
435        indexes = self.__indexSetForIndex(index)
436        self._parent.willChange_valuesAtIndexes_forKey_(
437                NSKeyValueChangeRemoval,
438                indexes, self._name)
439        try:
440            return self._wrapped.pop(index)
441        finally:
442            self._parent.didChange_valuesAtIndexes_forKey_(
443                NSKeyValueChangeRemoval,
444                indexes, self._name)
445
446    def extend(self, values):
447        # XXX: This is suboptimal but correct
448        if self._ro:
449            raise ValueError("Property '%s' is read-only"%(self._name,))
450
451        values = list(values)
452
453        indexes = NSIndexSet.alloc().initWithIndexesInRange_((len(self), len(values)))
454
455        self._parent.willChange_valuesAtIndexes_forKey_(
456                NSKeyValueChangeInsertion,
457                indexes, self._name)
458        try:
459            self._wrapped.extend(values)
460
461        finally:
462            self._parent.didChange_valuesAtIndexes_forKey_(
463                NSKeyValueChangeInsertion,
464                indexes, self._name)
465
466    def __iadd__(self, values):
467        self.extend(values)
468        return self
469
470    def __add__(self, values):
471        return self._wrapped + values
472
473    def __mul__(self, count):
474        return self._wrapped * count
475
476    def __imul__(self, count):
477        if self._ro:
478            raise ValueError("Property '%s' is read-only"%(self._name,))
479        if not isinstance(count, (int, long)):
480            raise TypeError(count)
481
482        indexes = NSIndexSet.alloc().initWithIndexesInRange_((len(self), len(self)*(count-1)))
483        self._parent.willChange_valuesAtIndexes_forKey_(
484                NSKeyValueChangeInsertion,
485                indexes, self._name)
486        try:
487            self._wrapped.__imul__(count)
488        finally:
489            self._parent.didChange_valuesAtIndexes_forKey_(
490                NSKeyValueChangeInsertion,
491                indexes, self._name)
492
493        return self
494
495
496    def __eq__(self, other):
497        if isinstance(other, array_proxy):
498            return self._wrapped == other._wrapped
499
500        else:
501            return self._wrapped == other
502
503    def __ne__(self, other):
504        if isinstance(other, array_proxy):
505            return self._wrapped != other._wrapped
506
507        else:
508            return self._wrapped != other
509
510    def __lt__(self, other):
511        if isinstance(other, array_proxy):
512            return self._wrapped < other._wrapped
513
514        else:
515            return self._wrapped < other
516
517    def __le__(self, other):
518        if isinstance(other, array_proxy):
519            return self._wrapped <= other._wrapped
520
521        else:
522            return self._wrapped <= other
523
524    def __gt__(self, other):
525        if isinstance(other, array_proxy):
526            return self._wrapped > other._wrapped
527
528        else:
529            return self._wrapped > other
530
531    def __ge__(self, other):
532        if isinstance(other, array_proxy):
533            return self._wrapped >= other._wrapped
534
535        else:
536            return self._wrapped >= other
537
538
539    if sys.version_info[0] == 2:
540        def __cmp__(self, other):
541            if isinstance(other, array_proxy):
542                return cmp(self._wrapped, other._wrapped)
543
544            else:
545                return cmp(self._wrapped, other)
546
547    if sys.version_info[0] == 2:
548        def sort(self, cmp=None, key=None, reverse=False):
549            if self._ro:
550                raise ValueError("Property '%s' is read-only"%(self._name,))
551
552            indexes = NSIndexSet.alloc().initWithIndexesInRange_(
553                    (0, len(self._wrapped)))
554            self._parent.willChange_valuesAtIndexes_forKey_(
555                    NSKeyValueChangeReplacement,
556                    indexes, self._name)
557            try:
558                self._wrapped.sort(cmp=cmp, key=key, reverse=reverse)
559            finally:
560                self._parent.didChange_valuesAtIndexes_forKey_(
561                    NSKeyValueChangeReplacement,
562                    indexes, self._name)
563
564    else: # pragma: no cover (py3k)
565        def sort(self, key=None, reverse=False):
566            if self._ro:
567                raise ValueError("Property '%s' is read-only"%(self._name,))
568
569            indexes = NSIndexSet.alloc().initWithIndexesInRange_(
570                    (0, len(self._wrapped)))
571            self._parent.willChange_valuesAtIndexes_forKey_(
572                    NSKeyValueChangeReplacement,
573                    indexes, self._name)
574            try:
575                self._wrapped.sort(key=key, reverse=reverse)
576            finally:
577                self._parent.didChange_valuesAtIndexes_forKey_(
578                    NSKeyValueChangeReplacement,
579                    indexes, self._name)
580
581    def reverse(self):
582        if self._ro:
583            raise ValueError("Property '%s' is read-only"%(self._name,))
584
585        indexes = NSIndexSet.alloc().initWithIndexesInRange_(
586                (0, len(self._wrapped)))
587        self._parent.willChange_valuesAtIndexes_forKey_(
588                NSKeyValueChangeReplacement,
589                indexes, self._name)
590        try:
591            self._wrapped.reverse()
592        finally:
593            self._parent.didChange_valuesAtIndexes_forKey_(
594                NSKeyValueChangeReplacement,
595                indexes, self._name)
596
597def makeArrayAccessors(name):
598
599    def countOf(self):
600        return len(getattr(self, name))
601
602    def objectIn(self, idx):
603        return getattr(self, name)[idx]
604
605    def insert(self, value, idx):
606        getattr(self, name).insert(idx, value)
607
608    def replace(self, idx, value):
609        getattr(self, name)[idx] = value
610
611    def remove(self, idx):
612        del getattr(self, name)[idx]
613
614    return countOf, objectIn, insert, remove, replace
615
616class array_property (object_property):
617    def __init__(self, name=None,
618            read_only=False, copy=True, dynamic=False,
619            ivar=None, depends_on=None):
620        super(array_property, self).__init__(name,
621                read_only=read_only,
622                copy=copy, dynamic=dynamic,
623                ivar=ivar, depends_on=depends_on)
624
625    def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods):
626        super(array_property, self).__pyobjc_class_setup__(name, class_dict, instance_methods, class_methods)
627
628
629        # Insert (Mutable) Indexed Accessors
630        # FIXME: should only do the mutable bits when we're actually a mutable property
631
632        name = self._name
633        Name = name[0].upper() + name[1:]
634
635        countOf, objectIn, insert, remove, replace = makeArrayAccessors(self._name)
636
637        countOf = selector(countOf,
638                selector  = ('countOf%s'%(Name,)).encode('latin1'),
639                signature = _C_NSUInteger + b'@:',
640        )
641        countOf.isHidden = True
642        instance_methods.add(countOf)
643
644        objectIn = selector(objectIn,
645                selector  = ('objectIn%sAtIndex:'%(Name,)).encode('latin1'),
646                signature = b'@@:' + _C_NSUInteger,
647        )
648        objectIn.isHidden = True
649        instance_methods.add(objectIn)
650
651        insert = selector(insert,
652                selector  = ('insertObject:in%sAtIndex:'%(Name,)).encode('latin1'),
653                signature = b'v@:@' + _C_NSUInteger,
654        )
655        insert.isHidden = True
656        instance_methods.add(insert)
657
658        remove = selector(remove,
659                selector  = ('removeObjectFrom%sAtIndex:'%(Name,)).encode('latin1'),
660                signature = b'v@:' + _C_NSUInteger,
661        )
662        remove.isHidden = True
663        instance_methods.add(remove)
664
665        replace = selector(replace,
666                selector  = ('replaceObjectIn%sAtIndex:withObject:'%(Name,)).encode('latin1'),
667                signature = b'v@:' + _C_NSUInteger + b'@',
668        )
669        replace.isHidden = True
670        instance_methods.add(replace)
671
672
673    def __set__(self, object, value):
674        if isinstance(value, array_proxy):
675            if value._name == self._name and value._parent is object:
676                # attr.prop = attr.prop
677                return
678
679        if isinstance(value, array_proxy):
680            value = list(value)
681
682        super(array_property, self).__set__(object, value)
683
684    def __get__(self, object, owner):
685        v = object_property.__get__(self, object, owner)
686        if v is None:
687            v = list()
688            object_property.__set__(self, object, v)
689        return array_proxy(self._name, object, self, self._ro)
690
691    def __getvalue__(self, object):
692        v = object_property.__get__(self, object, None)
693        if v is None:
694            v = list()
695            object_property.__set__(self, object, v)
696        return v
697
698
699NSKeyValueUnionSetMutation = 1
700NSKeyValueMinusSetMutation = 2
701NSKeyValueIntersectSetMutation = 3
702NSKeyValueSetSetMutation = 4
703
704
705class set_proxy (collections.MutableSet):
706    __slots__ = ('_name', '__wrapped', '_parent', '_ro')
707
708    def __init__(self, name, parent, wrapped, read_only):
709        self._name = name
710        self._parent = parent
711        self._ro = read_only
712        self.__wrapped = wrapped
713
714    def __repr__(self):
715        return '<set proxy for property ' + self._name + ' ' + repr(self._wrapped) + '>'
716
717    @property
718    def _wrapped(self):
719        return self.__wrapped.__getvalue__(self._parent)
720
721    def __reduce__(self):
722        # Ensure that the proxy itself doesn't get stored
723        # in pickles.
724        return _id, (self._wrapped,)
725
726    def __getattr__(self, attr):
727        return getattr(self._wrapped, attr)
728
729    def __contains__(self, value):
730        return self._wrapped.__contains__(value)
731
732    def __iter__(self):
733        return self._wrapped.__iter__()
734
735    def __len__(self):
736        return self._wrapped.__len__()
737
738
739    def __eq__(self, other):
740        if isinstance(other, set_proxy):
741            return self._wrapped == other._wrapped
742
743        else:
744            return self._wrapped == other
745
746    def __ne__(self, other):
747        if isinstance(other, set_proxy):
748            return self._wrapped != other._wrapped
749
750        else:
751            return self._wrapped != other
752
753    def __lt__(self, other):
754        if isinstance(other, set_proxy):
755            return self._wrapped < other._wrapped
756
757        else:
758            return self._wrapped < other
759
760    def __le__(self, other):
761        if isinstance(other, set_proxy):
762            return self._wrapped <= other._wrapped
763
764        else:
765            return self._wrapped <= other
766
767    def __gt__(self, other):
768        if isinstance(other, set_proxy):
769            return self._wrapped > other._wrapped
770
771        else:
772            return self._wrapped > other
773
774    def __ge__(self, other):
775        if isinstance(other, set_proxy):
776            return self._wrapped >= other._wrapped
777
778        else:
779            return self._wrapped >= other
780
781
782    if sys.version_info[0] == 2:
783        def __cmp__(self, other):
784            raise TypeError('cannot compare sets using cmp()')
785
786    def add(self, item):
787        if self._ro:
788            raise ValueError("Property '%s' is read-only"%(self._name,))
789
790        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
791                self._name,
792                NSKeyValueUnionSetMutation,
793                set([item]),
794        )
795        try:
796            self._wrapped.add(item)
797        finally:
798            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
799                self._name,
800                NSKeyValueUnionSetMutation,
801                set([item]),
802            )
803
804    def clear(self):
805        if self._ro:
806            raise ValueError("Property '%s' is read-only"%(self._name,))
807
808        object = set(self._wrapped)
809        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
810                self._name,
811                NSKeyValueMinusSetMutation,
812                object
813        )
814        try:
815            self._wrapped.clear()
816        finally:
817            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
818                self._name,
819                NSKeyValueMinusSetMutation,
820                object
821            )
822
823    def difference_update(self, *others):
824        if self._ro:
825            raise ValueError("Property '%s' is read-only"%(self._name,))
826
827        s = set()
828        s.update(*others)
829        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
830                self._name,
831                NSKeyValueMinusSetMutation,
832                s
833        )
834        try:
835            self._wrapped.difference_update(s)
836
837        finally:
838            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
839                self._name,
840                NSKeyValueMinusSetMutation,
841                s
842            )
843
844
845    def discard(self, item):
846        if self._ro:
847            raise ValueError("Property '%s' is read-only"%(self._name,))
848
849        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
850                self._name,
851                NSKeyValueMinusSetMutation,
852                {item}
853        )
854        try:
855            self._wrapped.discard(item)
856
857        finally:
858            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
859                self._name,
860                NSKeyValueMinusSetMutation,
861                {item}
862            )
863
864    def intersection_update(self, other):
865        if self._ro:
866            raise ValueError("Property '%s' is read-only"%(self._name,))
867
868        other = set(other)
869
870        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
871                self._name,
872                NSKeyValueIntersectSetMutation,
873                other
874        )
875        try:
876            self._wrapped.intersection_update(other)
877
878        finally:
879            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
880                self._name,
881                NSKeyValueIntersectSetMutation,
882                other
883            )
884
885    def pop(self):
886        if self._ro:
887            raise ValueError("Property '%s' is read-only"%(self._name,))
888
889        try:
890            v = next(iter(self))
891        except StopIteration:
892            raise KeyError("Empty set")
893
894        self.remove(v)
895        return v
896
897    def remove(self, item):
898        if self._ro:
899            raise ValueError("Property '%s' is read-only"%(self._name,))
900
901        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
902                self._name,
903                NSKeyValueMinusSetMutation,
904                set([item])
905        )
906        try:
907            self._wrapped.remove(item)
908
909        finally:
910            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
911                self._name,
912                NSKeyValueMinusSetMutation,
913                set([item])
914            )
915
916    def symmetric_difference_update(self, other):
917        # NOTE: This method does not call the corresponding method
918        # of the wrapped set to ensure that we generate the right
919        # notifications.
920        if self._ro:
921            raise ValueError("Property '%s' is read-only"%(self._name,))
922
923        other = set(other)
924
925        to_add = set()
926        to_remove = set()
927        for o in other:
928            if o in self:
929                to_remove.add(o)
930            else:
931                to_add.add(o)
932
933        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
934                self._name,
935                NSKeyValueMinusSetMutation,
936                to_remove
937        )
938        try:
939            self._wrapped.difference_update(to_remove)
940
941        finally:
942            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
943                    self._name,
944                    NSKeyValueMinusSetMutation,
945                    to_remove
946            )
947
948        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
949                self._name,
950                NSKeyValueUnionSetMutation,
951                to_add
952        )
953        try:
954            self._wrapped.update(to_add)
955
956        finally:
957            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
958                    self._name,
959                    NSKeyValueUnionSetMutation,
960                    to_add
961            )
962
963    def update(self, *others):
964        if self._ro:
965            raise ValueError("Property '%s' is read-only"%(self._name,))
966
967        s = set()
968        s.update(*others)
969
970        self._parent.willChangeValueForKey_withSetMutation_usingObjects_(
971                self._name,
972                NSKeyValueUnionSetMutation,
973                s
974        )
975        try:
976            self._wrapped.update(s)
977
978        finally:
979            self._parent.didChangeValueForKey_withSetMutation_usingObjects_(
980                self._name,
981                NSKeyValueUnionSetMutation,
982                s
983            )
984
985    def __or__(self, other):
986        return self._wrapped | other
987
988    def __and__(self, other):
989        return self._wrapped & other
990
991    def __xor__(self, other):
992        return self._wrapped ^ other
993
994    def __sub__(self, other):
995        return self._wrapped - other
996
997    def __ior__(self, other):
998        if self._ro:
999            raise ValueError("Property '%s' is read-only"%(self._name,))
1000
1001        self.update(other)
1002        return self
1003
1004    def __isub__(self, other):
1005        if self._ro:
1006            raise ValueError("Property '%s' is read-only"%(self._name,))
1007
1008        self.difference_update(other)
1009        return self
1010
1011    def __ixor__(self, other):
1012        if self._ro:
1013            raise ValueError("Property '%s' is read-only"%(self._name,))
1014
1015        self.symmetric_difference_update(other)
1016        return self
1017
1018    def __iand__(self, other):
1019        if self._ro:
1020            raise ValueError("Property '%s' is read-only"%(self._name,))
1021
1022        self.intersection_update(other)
1023        return self
1024
1025
1026def makeSetAccessors(name):
1027    def countOf(self):
1028        return len(getattr(self, name))
1029
1030    def enumeratorOf(self):
1031        return iter(getattr(self, name))
1032
1033    def memberOf(self, value):
1034        collection =  getattr(self, name)
1035        if value not in collection:
1036            return None
1037
1038        for item in collection:
1039            if item == value:
1040                return item
1041
1042    def add(self, value):
1043        getattr(self, name).add(value)
1044
1045    def remove(self, value):
1046        getattr(self, name).discard(value)
1047
1048    return countOf, enumeratorOf, memberOf, add, remove
1049
1050
1051class set_property (object_property):
1052    def __init__(self, name=None,
1053            read_only=False, copy=True, dynamic=False,
1054            ivar=None, depends_on=None):
1055        super(set_property, self).__init__(name,
1056                read_only=read_only,
1057                copy=copy, dynamic=dynamic,
1058                ivar=ivar, depends_on=depends_on)
1059
1060    def __get__(self, object, owner):
1061        v = object_property.__get__(self, object, owner)
1062        if v is None:
1063            v = set()
1064            object_property.__set__(self, object, v)
1065        return set_proxy(self._name, object, self, self._ro)
1066
1067    def __set__(self, object, value):
1068        if isinstance(value, set_proxy):
1069            if value._name == self._name and value._parent is object:
1070                # attr.prop = attr.prop
1071                return
1072
1073        if isinstance(value, set_proxy):
1074            value = list(value)
1075
1076        super(set_property, self).__set__(object, value)
1077
1078    def __getvalue__(self, object):
1079        v = object_property.__get__(self, object, None)
1080        if v is None:
1081            v = set()
1082            object_property.__set__(self, object, v)
1083        return v
1084
1085    def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods):
1086        super(set_property, self).__pyobjc_class_setup__(name, class_dict, instance_methods, class_methods)
1087
1088        # (Mutable) Unordered Accessors
1089        # FIXME: should only do the mutable bits when we're actually a mutable property
1090
1091        name = self._name
1092        Name = name[0].upper() + name[1:]
1093
1094        countOf, enumeratorOf, memberOf, add, remove = makeSetAccessors(self._name)
1095
1096        countOf = selector(countOf,
1097                selector  = ('countOf%s'%(Name,)).encode('latin1'),
1098                signature = _C_NSUInteger + b'@:',
1099        )
1100        countOf.isHidden = True
1101        instance_methods.add(countOf)
1102
1103        enumeratorOf = selector(enumeratorOf,
1104                selector  = ('enumeratorOf%s'%(Name,)).encode('latin1'),
1105                signature = b'@@:',
1106        )
1107        enumeratorOf.isHidden = True
1108        instance_methods.add(enumeratorOf)
1109
1110        memberOf = selector(memberOf,
1111                selector  = ('memberOf%s:'%(Name,)).encode('latin'),
1112                signature = b'@@:@',
1113        )
1114        memberOf.isHidden = True
1115        instance_methods.add(memberOf)
1116
1117        add1 = selector(add,
1118                selector  = ('add%s:'%(Name,)).encode('latin'),
1119                signature = b'v@:@',
1120        )
1121        add1.isHidden = True
1122        instance_methods.add(add1)
1123
1124        add2 = selector(add,
1125                selector  = ('add%sObject:'%(Name,)).encode('latin1'),
1126                signature = b'v@:@',
1127        )
1128        add2.isHidden = True
1129        instance_methods.add(add2)
1130
1131        remove1 = selector(remove,
1132                selector  = ('remove%s:'%(Name,)).encode('latin1'),
1133                signature = b'v@:@',
1134        )
1135        remove1.isHidden = True
1136        instance_methods.add(remove1)
1137
1138        remove2 = selector(remove,
1139                selector  = ('remove%sObject:'%(Name,)).encode('latin'),
1140                signature = b'v@:@',
1141        )
1142        remove2.isHidden = True
1143        instance_methods.add(remove2)
1144
1145
1146NSMutableDictionary = lookUpClass('NSMutableDictionary')
1147
1148class dict_property (object_property):
1149    def __get__(self, object, owner):
1150        v = object_property.__get__(self, object, owner)
1151        if v is None:
1152            v = NSMutableDictionary.alloc().init()
1153            object_property.__set__(self, object, v)
1154        return object_property.__get__(self, object, owner)
1155