2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "EOObserver.h"
28 typedef struct _EOObserverList {
29 struct _EOObserverList *next;
30 id<EOObserving> observer;
31 void (*notify)(id, SEL, id);
34 static void mapValRetain(NSMapTable *self, const void *_value);
35 static void mapValRelease(NSMapTable *self, void *_value);
36 static NSString *mapDescribe(NSMapTable *self, const void *_value);
38 const NSMapTableValueCallBacks EOObserverListMapValueCallBacks = {
39 (void (*)(NSMapTable *, const void *))mapValRetain,
40 (void (*)(NSMapTable *, void *))mapValRelease,
41 (NSString *(*)(NSMapTable *, const void *))mapDescribe
44 @implementation NSObject(EOObserver)
47 static Class EOObserverCenterClass = Nil;
48 if (EOObserverCenterClass == Nil)
49 EOObserverCenterClass = [EOObserverCenter class];
51 [EOObserverCenterClass notifyObserversObjectWillChange:self];
54 @end /* NSObject(EOObserver) */
56 @implementation EOObserverCenter
58 static unsigned observerNotificationSuppressCount = 0;
59 static EOObserverList *omniscientObservers = NULL;
60 static NSMapTable *objectToObservers = NULL;
63 if (objectToObservers == NULL) {
64 objectToObservers = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
65 EOObserverListMapValueCallBacks,
70 + (void)notifyObserversObjectWillChange:(id)_object {
71 static id lastObject = nil;
72 register EOObserverList *l;
74 /* check if notifications are suppressed */
75 if (observerNotificationSuppressCount > 0)
78 /* compress notifications for the same object */
79 if (_object == lastObject)
82 /* notify usual observers */
84 for (l = NSMapGet(objectToObservers, _object); l != NULL; l = l->next) {
86 l->notify(l->observer, @selector(objectWillChange:), _object);
88 [l->observer objectWillChange:_object];
91 /* notify omniscient observers */
93 for (l = omniscientObservers; l != NULL; l = l->next) {
95 l->notify(l->observer, @selector(objectWillChange:), _object);
97 [l->observer objectWillChange:_object];
101 + (void)addObserver:(id<EOObserving>)_observer forObject:(id)_object {
102 register EOObserverList *l, *nl;
104 if ((l = NSMapGet(objectToObservers, _object))) {
105 /* check whether the observer is already registered */
107 for (nl = l; nl != NULL; nl = nl->next) {
108 if (nl->observer == _object)
114 nl = malloc(sizeof(EOObserverList));
116 nl = objc_malloc(sizeof(EOObserverList));
118 nl->observer = [_observer retain];
120 [(id)_observer methodForSelector:@selector(objectWillChange:)];
123 /* this is the first observer defined */
125 NSMapInsert(objectToObservers, _object, nl);
129 insert at second position (so that we don't need to remove/add the new
130 entry in table or traverse the list to the end)
137 + (void)removeObserver:(id<EOObserving>)_observer forObject:(id)_object {
138 register EOObserverList *l, *ll, *first;
140 if ((first = NSMapGet(objectToObservers, _object)) == NULL)
141 /* no observers registered for object */
147 if (l->observer == _observer) {
148 /* found matching list entry */
150 /* entry is not the first entry */
152 [l->observer release];
162 entry is the first entry, but there are more than one entries.
163 In this case we copy the second to the first and remove the second,
164 this way we save removing/inserting in the hash table.
166 [l->observer release];
168 l->observer = ll->observer;
169 l->notify = ll->notify;
179 /* entry is the lone entry */
180 NSMapRemove(objectToObservers, _object);
181 [l->observer release];
196 + (NSArray *)observersForObject:(id)_object {
197 EOObserverList *observers;
198 NSMutableArray *result;
200 if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
201 return [NSArray array];
203 result = [NSMutableArray arrayWithCapacity:16];
204 while ((observers)) {
205 if (observers->observer)
206 [result addObject:observers->observer];
207 observers = observers->next;
210 return [[result copy] autorelease];
213 + (id)observerForObject:(id)_object ofClass:(Class)_targetClass {
214 register EOObserverList *observers;
216 if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
219 while ((observers)) {
220 if ([observers->observer class] == _targetClass)
221 return observers->observer;
222 observers = observers->next;
227 + (void)addOmniscientObserver:(id<EOObserving>)_observer {
230 /* first check whether we already added this observer to the list */
232 for (l = omniscientObservers; l != NULL; l = l->next) {
233 if (l->observer == _observer)
238 l = malloc(sizeof(EOObserverList));
240 l = objc_malloc(sizeof(EOObserverList));
242 l->next = omniscientObservers;
243 l->observer = [_observer retain];
244 l->notify = (void*)[(id)_observer methodForSelector:@selector(willChange:)];
246 omniscientObservers = l;
248 + (void)removeOmniscientObserver:(id<EOObserving>)_observer {
249 EOObserverList *l, *ll;
251 /* first check whether we already added this observer to the list */
253 for (l = omniscientObservers, ll = NULL; l != NULL; ) {
254 if (l->observer == _observer) {
257 omniscientObservers = l->next;
261 [l->observer release];
271 /* suppressing notifications */
273 + (void)suppressObserverNotification {
274 observerNotificationSuppressCount++;
276 + (void)enableObserverNotification {
277 observerNotificationSuppressCount--;
280 + (unsigned)observerNotificationSuppressCount {
281 return observerNotificationSuppressCount;
284 @end /* EOObserverCenter */
286 @implementation EODelayedObserverQueue
288 static EODelayedObserverQueue *defaultQueue = nil;
290 + (EODelayedObserverQueue *)defaultObserverQueue {
291 if (defaultQueue == nil)
292 defaultQueue = [[EODelayedObserverQueue alloc] init];
297 [[NSNotificationCenter defaultCenter]
298 addObserver:self selector:@selector(_notify:)
299 name:@"EODelayedNotify" object:self];
304 [[NSNotificationCenter defaultCenter] removeObserver:self];
305 [self->runLoopModes release];
311 - (void)setRunLoopModes:(NSArray *)_modes {
312 ASSIGN(self->runLoopModes, _modes);
314 - (NSArray *)runLoopModes {
315 return self->runLoopModes;
320 static inline void _enqueue(EODelayedObserverQueue *self,
321 EODelayedObserver **list,
322 EODelayedObserver *newEntry)
325 /* first entry in this list */
326 *list = [newEntry retain];
329 EODelayedObserver *e, *le;
331 for (e = *list, le = NULL; e != NULL; e = e->next) {
333 /* already in queue */
338 le->next = [e retain];
342 static inline void _dequeue(EODelayedObserverQueue *self,
343 EODelayedObserver **list,
344 EODelayedObserver *entry)
346 EODelayedObserver *e, *le;
348 for (e = *list, le = NULL; e != NULL; e = e->next) {
359 static inline void _notify(EODelayedObserverQueue *self, EODelayedObserver *list)
362 [list subjectChanged];
369 - (void)enqueueObserver:(EODelayedObserver *)_observer {
370 if (_observer == nil) return;
372 _enqueue(self, &(self->queues[[_observer priority]]), _observer);
374 if (!self->hasObservers) {
375 /* register for ASAP notification */
376 NSNotification *notification;
378 notification = [NSNotification notificationWithName:@"EODelayedNotify"
381 [[NSNotificationQueue defaultQueue]
382 enqueueNotification:notification
383 postingStyle:NSPostASAP
384 coalesceMask:NSNotificationCoalescingOnSender
385 forModes:[self runLoopModes]];
387 self->hasObservers = YES;
390 - (void)dequeueObserver:(EODelayedObserver *)_observer {
391 if (_observer == nil) return;
393 _dequeue(self, &(self->queues[[_observer priority]]), _observer);
398 - (void)notifyObserversUpToPriority:(EOObserverPriority)_lastPriority {
401 for (i = 0; i < _lastPriority; i++)
402 _notify(self, self->queues[i]);
405 - (void)_notify:(NSNotification *)_notification {
406 [self notifyObserversUpToPriority:EOObserverPrioritySixth];
409 @end /* EODelayedObserverQueue */
411 @implementation EODelayedObserver
415 - (EOObserverPriority)priority {
416 return EOObserverPriorityThird;
419 - (EODelayedObserverQueue *)observerQueue {
420 return [EODelayedObserverQueue defaultObserverQueue];
425 - (void)subjectChanged {
426 [self doesNotRecognizeSelector:_cmd];
429 - (void)objectWillChange:(id)_object {
430 [[self observerQueue] enqueueObserver:self];
433 - (void)discardPendingNotification {
434 [[self observerQueue] dequeueObserver:self];
437 @end /* EODelayedObserver */
439 @implementation EOObserverProxy
441 - (id)initWithTarget:(id)_target action:(SEL)_action
442 priority:(EOObserverPriority)_priority
444 if ((self = [super init])) {
445 self->target = [_target retain];
446 self->action = _action;
447 self->priority = _priority;
452 return [self initWithTarget:nil action:NULL priority:EOObserverPriorityThird];
456 [self->target release];
462 - (EOObserverPriority)priority {
463 return self->priority;
468 - (void)subjectChanged {
469 [self->target performSelector:self->action withObject:self];
472 @end /* EOObserverProxy */
474 /* value functions for mapping table */
476 static void mapValRetain(NSMapTable *self, const void *_value) {
479 static void mapValRelease(NSMapTable *self, void *_value) {
483 static NSString *mapDescribe(NSMapTable *self, const void *_value) {