2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "EOObserver.h"
27 typedef struct _EOObserverList {
28 struct _EOObserverList *next;
29 id<EOObserving> observer;
30 void (*notify)(id, SEL, id);
33 static void mapValRetain(NSMapTable *self, const void *_value);
34 static void mapValRelease(NSMapTable *self, void *_value);
35 static NSString *mapDescribe(NSMapTable *self, const void *_value);
37 const NSMapTableValueCallBacks EOObserverListMapValueCallBacks = {
38 (void (*)(NSMapTable *, const void *))mapValRetain,
39 (void (*)(NSMapTable *, void *))mapValRelease,
40 (NSString *(*)(NSMapTable *, const void *))mapDescribe
43 @implementation NSObject(EOObserver)
46 static Class EOObserverCenterClass = Nil;
47 if (EOObserverCenterClass == Nil)
48 EOObserverCenterClass = [EOObserverCenter class];
50 [EOObserverCenterClass notifyObserversObjectWillChange:self];
53 @end /* NSObject(EOObserver) */
55 @implementation EOObserverCenter
57 static unsigned observerNotificationSuppressCount = 0;
58 static EOObserverList *omniscientObservers = NULL;
59 static NSMapTable *objectToObservers = NULL;
62 if (objectToObservers == NULL) {
63 objectToObservers = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
64 EOObserverListMapValueCallBacks,
69 + (void)notifyObserversObjectWillChange:(id)_object {
70 static id lastObject = nil;
71 register EOObserverList *l;
73 /* check if notifications are suppressed */
74 if (observerNotificationSuppressCount > 0)
77 /* compress notifications for the same object */
78 if (_object == lastObject)
81 /* notify usual observers */
83 for (l = NSMapGet(objectToObservers, _object); l != NULL; l = l->next) {
85 l->notify(l->observer, @selector(objectWillChange:), _object);
87 [l->observer objectWillChange:_object];
90 /* notify omniscient observers */
92 for (l = omniscientObservers; l != NULL; l = l->next) {
94 l->notify(l->observer, @selector(objectWillChange:), _object);
96 [l->observer objectWillChange:_object];
100 + (void)addObserver:(id<EOObserving>)_observer forObject:(id)_object {
101 register EOObserverList *l, *nl;
103 if ((l = NSMapGet(objectToObservers, _object))) {
104 /* check whether the observer is already registered */
106 for (nl = l; nl != NULL; nl = nl->next) {
107 if (nl->observer == _object)
113 nl = malloc(sizeof(EOObserverList));
115 nl = objc_malloc(sizeof(EOObserverList));
117 nl->observer = [_observer retain];
119 [(id)_observer methodForSelector:@selector(objectWillChange:)];
122 /* this is the first observer defined */
124 NSMapInsert(objectToObservers, _object, nl);
128 insert at second position (so that we don't need to remove/add the new
129 entry in table or traverse the list to the end)
136 + (void)removeObserver:(id<EOObserving>)_observer forObject:(id)_object {
137 register EOObserverList *l, *ll, *first;
139 if ((first = NSMapGet(objectToObservers, _object)) == NULL)
140 /* no observers registered for object */
146 if (l->observer == _observer) {
147 /* found matching list entry */
149 /* entry is not the first entry */
151 [l->observer release];
161 entry is the first entry, but there are more than one entries.
162 In this case we copy the second to the first and remove the second,
163 this way we save removing/inserting in the hash table.
165 [l->observer release];
167 l->observer = ll->observer;
168 l->notify = ll->notify;
178 /* entry is the lone entry */
179 NSMapRemove(objectToObservers, _object);
180 [l->observer release];
195 + (NSArray *)observersForObject:(id)_object {
196 EOObserverList *observers;
197 NSMutableArray *result;
199 if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
200 return [NSArray array];
202 result = [NSMutableArray arrayWithCapacity:16];
203 while ((observers)) {
204 if (observers->observer)
205 [result addObject:observers->observer];
206 observers = observers->next;
209 return [[result copy] autorelease];
212 + (id)observerForObject:(id)_object ofClass:(Class)_targetClass {
213 register EOObserverList *observers;
215 if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
218 while ((observers)) {
219 if ([observers->observer class] == _targetClass)
220 return observers->observer;
221 observers = observers->next;
226 + (void)addOmniscientObserver:(id<EOObserving>)_observer {
229 /* first check whether we already added this observer to the list */
231 for (l = omniscientObservers; l != NULL; l = l->next) {
232 if (l->observer == _observer)
237 l = malloc(sizeof(EOObserverList));
239 l = objc_malloc(sizeof(EOObserverList));
241 l->next = omniscientObservers;
242 l->observer = [_observer retain];
243 l->notify = (void*)[(id)_observer methodForSelector:@selector(willChange:)];
245 omniscientObservers = l;
247 + (void)removeOmniscientObserver:(id<EOObserving>)_observer {
248 EOObserverList *l, *ll;
250 /* first check whether we already added this observer to the list */
252 for (l = omniscientObservers, ll = NULL; l != NULL; ) {
253 if (l->observer == _observer) {
256 omniscientObservers = l->next;
260 [l->observer release];
270 /* suppressing notifications */
272 + (void)suppressObserverNotification {
273 observerNotificationSuppressCount++;
275 + (void)enableObserverNotification {
276 observerNotificationSuppressCount--;
279 + (unsigned)observerNotificationSuppressCount {
280 return observerNotificationSuppressCount;
283 @end /* EOObserverCenter */
285 @implementation EODelayedObserverQueue
287 static EODelayedObserverQueue *defaultQueue = nil;
289 + (EODelayedObserverQueue *)defaultObserverQueue {
290 if (defaultQueue == nil)
291 defaultQueue = [[EODelayedObserverQueue alloc] init];
296 [[NSNotificationCenter defaultCenter]
297 addObserver:self selector:@selector(_notify:)
298 name:@"EODelayedNotify" object:self];
303 [[NSNotificationCenter defaultCenter] removeObserver:self];
304 [self->runLoopModes release];
310 - (void)setRunLoopModes:(NSArray *)_modes {
311 ASSIGN(self->runLoopModes, _modes);
313 - (NSArray *)runLoopModes {
314 return self->runLoopModes;
319 static inline void _enqueue(EODelayedObserverQueue *self,
320 EODelayedObserver **list,
321 EODelayedObserver *newEntry)
324 /* first entry in this list */
325 *list = [newEntry retain];
328 EODelayedObserver *e, *le;
330 for (e = *list, le = NULL; e != NULL; e = e->next) {
332 /* already in queue */
337 le->next = [e retain];
341 static inline void _dequeue(EODelayedObserverQueue *self,
342 EODelayedObserver **list,
343 EODelayedObserver *entry)
345 EODelayedObserver *e, *le;
347 for (e = *list, le = NULL; e != NULL; e = e->next) {
358 static inline void _notify(EODelayedObserverQueue *self, EODelayedObserver *list)
361 [list subjectChanged];
368 - (void)enqueueObserver:(EODelayedObserver *)_observer {
369 if (_observer == nil) return;
371 _enqueue(self, &(self->queues[[_observer priority]]), _observer);
373 if (!self->hasObservers) {
374 /* register for ASAP notification */
375 NSNotification *notification;
377 notification = [NSNotification notificationWithName:@"EODelayedNotify"
380 [[NSNotificationQueue defaultQueue]
381 enqueueNotification:notification
382 postingStyle:NSPostASAP
383 coalesceMask:NSNotificationCoalescingOnSender
384 forModes:[self runLoopModes]];
386 self->hasObservers = YES;
389 - (void)dequeueObserver:(EODelayedObserver *)_observer {
390 if (_observer == nil) return;
392 _dequeue(self, &(self->queues[[_observer priority]]), _observer);
397 - (void)notifyObserversUpToPriority:(EOObserverPriority)_lastPriority {
400 for (i = 0; i < _lastPriority; i++)
401 _notify(self, self->queues[i]);
404 - (void)_notify:(NSNotification *)_notification {
405 [self notifyObserversUpToPriority:EOObserverPrioritySixth];
408 @end /* EODelayedObserverQueue */
410 @implementation EODelayedObserver
414 - (EOObserverPriority)priority {
415 return EOObserverPriorityThird;
418 - (EODelayedObserverQueue *)observerQueue {
419 return [EODelayedObserverQueue defaultObserverQueue];
424 - (void)subjectChanged {
425 [self doesNotRecognizeSelector:_cmd];
428 - (void)objectWillChange:(id)_object {
429 [[self observerQueue] enqueueObserver:self];
432 - (void)discardPendingNotification {
433 [[self observerQueue] dequeueObserver:self];
436 @end /* EODelayedObserver */
438 @implementation EOObserverProxy
440 - (id)initWithTarget:(id)_target action:(SEL)_action
441 priority:(EOObserverPriority)_priority
443 if ((self = [super init])) {
444 self->target = [_target retain];
445 self->action = _action;
446 self->priority = _priority;
451 return [self initWithTarget:nil action:NULL priority:EOObserverPriorityThird];
455 [self->target release];
461 - (EOObserverPriority)priority {
462 return self->priority;
467 - (void)subjectChanged {
468 [self->target performSelector:self->action withObject:self];
471 @end /* EOObserverProxy */
473 /* value functions for mapping table */
475 static void mapValRetain(NSMapTable *self, const void *_value) {
478 static void mapValRelease(NSMapTable *self, void *_value) {
482 static NSString *mapDescribe(NSMapTable *self, const void *_value) {