1#!/usr/bin/env python
2"""
3Experimental code, attempting to work around Apple's KVO hacks.
4"""
5
6from Foundation import *
7import objc
8from sets import Set
9import sys
10
11_kvoclassed = {}
12
13def dumpClass(cls):
14    print "DUMP", cls
15    print cls.__bases__
16    #for k, v in cls.__dict__.items():
17    #    print k, v
18    print "-"*40
19
20
21def toKVOClass(orig, new):
22    if new in _kvoclassed:
23        return new
24    origdct = dict(orig.__dict__)
25    origset = Set(origdct)
26    newset = Set(new.__dict__)
27    # Merge in non-methods from the dict
28    for key in (origset - newset):
29        value = origdct[key]
30        if isinstance(value, objc.selector):
31            continue
32        setattr(new, key, value)
33    # Remember the original class,
34    # KVO wants to go back to NSObject!
35    _kvoclassed[new] = orig
36    return new
37
38def fromKVOClass(new):
39    return _kvoclassed[new]
40
41class FooClass(NSObject):
42    _kvc_bar = None
43    outlet = objc.IBOutlet('outlet')
44
45    def XXaddObserver_forKeyPath_options_context_(self, observer, keyPath, options, context):
46        print 'addObserver_forKeyPath_options_context_', observer, keyPath, options, context
47        orig = FooClass #type(self)
48        super(orig, self).addObserver_forKeyPath_options_context_(observer, keyPath, options, context)
49        new = self.class__()
50        print orig, type(self), new
51        if orig is not new:
52            print "class changed!!"
53            self.__class__ = toKVOClass(orig, new)
54
55    def XXremoveObserver_forKeyPath_(self, observer, keyPath):
56        print 'removeObserver_forKeyPath_', observer, keyPath
57        orig = type(self)
58        super(orig, self).removeObserver_forKeyPath_(observer, keyPath)
59        new = self.class__()
60        print orig, type(self), new
61        if orig is not new:
62            print "class changed!!"
63            self.__class__ = fromKVOClass(orig)
64
65    def setBar_(self, bar):
66        print 'setBar_ ->', bar
67        print self, type(self), self.class__()
68        #print '->', bar
69        self._kvc_bar = bar
70    setBar_ = objc.accessor(setBar_)
71
72    def bar(self):
73        print 'bar', self._kvc_bar
74        #print self, type(self), self.class__()
75        return self._kvc_bar
76    bar = objc.accessor(bar)
77
78class FooObserver(NSObject):
79    def observeValueForKeyPath_ofObject_change_context_(self, keyPath, obj, change, context):
80        print '[[[[[]]]]] observeValueForKeyPath_ofObject_change_context_', keyPath, obj, change, context
81        dumpClass(type(obj))
82
83    def willChangeValueForKey_(self, key):
84        print '[[[[[]]]]] willChangeValueForKey_', key
85
86foo = FooClass.alloc().init()
87fooObserver = FooObserver.alloc().init()
88
89print
90print
91print "***** unobserved set"
92print foo.bar()
93foo.setBar_(u'0shw00t')
94print foo.bar()
95
96print
97print
98print "***** observing, setting three times"
99print "foo's ISA:", foo.pyobjc_ISA
100print "Adding an observer"
101foo.addObserver_forKeyPath_options_context_(fooObserver, u'bar',  (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 0)
102print "foo's ISA:", foo.pyobjc_ISA
103#foo.removeObserver_forKeyPath_(fooObserver, u'bar');sys.exit(1)
104print foo.bar()
105foo.setBar_(u'1w00t')
106print foo.bar()
107foo.setBar_(u'2sw00t')
108print foo.bar()
109foo.setBar_(u'3shw00t')
110print foo.bar()
111
112print
113print
114print "***** removing the observer and setting twice (unobserved)"
115foo.removeObserver_forKeyPath_(fooObserver, u'bar')
116print foo.bar()
117foo.setBar_(u'4sw00t')
118print foo.bar()
119foo.setBar_(u'5w00t')
120print foo.bar()
121
122print foo.__class__
123