]> err.no Git - sope/blob - sope-core/EOControl/EOObserver.m
removed NGJavaScript support
[sope] / sope-core / EOControl / EOObserver.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "EOObserver.h"
24 #include "common.h"
25
26 // THREAD, MT
27
28 typedef struct _EOObserverList {
29   struct _EOObserverList *next;
30   id<EOObserving>        observer;
31   void                   (*notify)(id, SEL, id);
32 } EOObserverList;
33
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);
37
38 const NSMapTableValueCallBacks EOObserverListMapValueCallBacks = {
39   (void (*)(NSMapTable *, const void *))mapValRetain,
40   (void (*)(NSMapTable *, void *))mapValRelease,
41   (NSString *(*)(NSMapTable *, const void *))mapDescribe
42 };
43
44 @implementation NSObject(EOObserver)
45
46 - (void)willChange {
47   static Class EOObserverCenterClass = Nil;
48   if (EOObserverCenterClass == Nil)
49     EOObserverCenterClass = [EOObserverCenter class];
50
51   [EOObserverCenterClass notifyObserversObjectWillChange:self];
52 }
53
54 @end /* NSObject(EOObserver) */
55
56 @implementation EOObserverCenter
57
58 static unsigned       observerNotificationSuppressCount = 0;
59 static EOObserverList *omniscientObservers = NULL;
60 static NSMapTable     *objectToObservers   = NULL;
61
62 + (void)initialize {
63   if (objectToObservers == NULL) {
64     objectToObservers = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
65                                          EOObserverListMapValueCallBacks,
66                                          256);
67   }
68 }
69
70 + (void)notifyObserversObjectWillChange:(id)_object {
71   static id lastObject = nil;
72   register EOObserverList *l;
73
74   /* check if notifications are suppressed */
75   if (observerNotificationSuppressCount > 0)
76     return;
77   
78   /* compress notifications for the same object */
79   if (_object == lastObject)
80     return;
81
82   /* notify usual observers */
83
84   for (l = NSMapGet(objectToObservers, _object); l != NULL; l = l->next) {
85     if (l->notify)
86       l->notify(l->observer, @selector(objectWillChange:), _object);
87     else
88       [l->observer objectWillChange:_object];
89   }
90   
91   /* notify omniscient observers */
92   
93   for (l = omniscientObservers; l != NULL; l = l->next) {
94     if (l->notify)
95       l->notify(l->observer, @selector(objectWillChange:), _object);
96     else
97       [l->observer objectWillChange:_object];
98   }
99 }
100
101 + (void)addObserver:(id<EOObserving>)_observer forObject:(id)_object {
102   register EOObserverList *l, *nl;
103   
104   if ((l = NSMapGet(objectToObservers, _object))) {
105     /* check whether the observer is already registered */
106     
107     for (nl = l; nl != NULL; nl = nl->next) {
108       if (nl->observer == _object)
109         return;
110     }
111   }
112
113 #if NeXT_RUNTIME
114   nl = malloc(sizeof(EOObserverList));
115 #else  
116   nl = objc_malloc(sizeof(EOObserverList));
117 #endif
118   nl->observer = [_observer retain];
119   nl->notify   = (void*)
120     [(id)_observer methodForSelector:@selector(objectWillChange:)];
121
122   if (l == NULL) {
123     /* this is the first observer defined */
124     nl->next = NULL;
125     NSMapInsert(objectToObservers, _object, nl);
126   }
127   else {
128     /*
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)
131     */
132     nl->next = l->next;
133     l->next = nl;
134   }
135 }
136
137 + (void)removeObserver:(id<EOObserving>)_observer forObject:(id)_object {
138   register EOObserverList *l, *ll, *first;
139
140   if ((first = NSMapGet(objectToObservers, _object)) == NULL)
141     /* no observers registered for object */
142     return;
143
144   l  = first;
145   ll = NULL;
146   while (l) {
147     if (l->observer == _observer) {
148       /* found matching list entry */
149       if (l != first) {
150         /* entry is not the first entry */
151         ll->next = l->next;
152         [l->observer release];
153 #if NeXT_RUNTIME
154         free(l);
155 #else    
156         objc_free(l);
157 #endif
158         break;
159       }
160       else if (l->next) {
161         /*
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.
165         */
166         [l->observer release];
167         ll = l->next;
168         l->observer = ll->observer;
169         l->notify   = ll->notify;
170         l->next     = ll->next;
171 #if NeXT_RUNTIME
172         free(ll);
173 #else    
174         objc_free(ll);
175 #endif
176         break;
177       }
178       else {
179         /* entry is the lone entry */
180         NSMapRemove(objectToObservers, _object);
181         [l->observer release];
182 #if NeXT_RUNTIME
183         free(l);
184 #else    
185         objc_free(l);
186 #endif
187         break;
188       }
189     }
190     
191     ll = l;
192     l = ll->next;
193   }
194 }
195
196 + (NSArray *)observersForObject:(id)_object {
197   EOObserverList *observers;
198   NSMutableArray *result;
199   
200   if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
201     return [NSArray array];
202   
203   result = [NSMutableArray arrayWithCapacity:16];
204   while ((observers)) {
205     if (observers->observer)
206       [result addObject:observers->observer];
207     observers = observers->next;
208   }
209   
210   return [[result copy] autorelease];
211 }
212
213 + (id)observerForObject:(id)_object ofClass:(Class)_targetClass {
214   register EOObserverList *observers;
215   
216   if ((observers = NSMapGet(objectToObservers, _object)) == NULL)
217     return nil;
218
219   while ((observers)) {
220     if ([observers->observer class] == _targetClass)
221       return observers->observer;
222     observers = observers->next;
223   }
224   return nil;
225 }
226
227 + (void)addOmniscientObserver:(id<EOObserving>)_observer {
228   EOObserverList *l;
229   
230   /* first check whether we already added this observer to the list */
231   
232   for (l = omniscientObservers; l != NULL; l = l->next) {
233     if (l->observer == _observer)
234       return;
235   }
236
237 #if NeXT_RUNTIME
238   l = malloc(sizeof(EOObserverList));
239 #else  
240   l = objc_malloc(sizeof(EOObserverList));
241 #endif
242   l->next     = omniscientObservers;
243   l->observer = [_observer retain];
244   l->notify   = (void*)[(id)_observer methodForSelector:@selector(willChange:)];
245
246   omniscientObservers = l;
247 }
248 + (void)removeOmniscientObserver:(id<EOObserving>)_observer {
249   EOObserverList *l, *ll;
250   
251   /* first check whether we already added this observer to the list */
252   
253   for (l = omniscientObservers, ll = NULL; l != NULL; ) {
254     if (l->observer == _observer) {
255       /* matched */
256       if (ll == NULL)
257         omniscientObservers = l->next;
258       else
259         ll->next = l->next;
260
261       [l->observer release];
262       objc_free(l);
263       return;
264     }
265
266     ll = l;
267     l = ll->next;
268   }
269 }
270
271 /* suppressing notifications */
272
273 + (void)suppressObserverNotification {
274   observerNotificationSuppressCount++;
275 }
276 + (void)enableObserverNotification {
277   observerNotificationSuppressCount--;
278 }
279
280 + (unsigned)observerNotificationSuppressCount {
281   return observerNotificationSuppressCount;
282 }
283
284 @end /* EOObserverCenter */
285
286 @implementation EODelayedObserverQueue
287
288 static EODelayedObserverQueue *defaultQueue = nil;
289
290 + (EODelayedObserverQueue *)defaultObserverQueue {
291   if (defaultQueue == nil)
292     defaultQueue = [[EODelayedObserverQueue alloc] init];
293   return defaultQueue;
294 }
295
296 - (id)init {
297   [[NSNotificationCenter defaultCenter]
298                          addObserver:self selector:@selector(_notify:)
299                          name:@"EODelayedNotify" object:self];
300   return self;
301 }
302
303 - (void)dealloc {
304   [[NSNotificationCenter defaultCenter] removeObserver:self];
305   [self->runLoopModes release];
306   [super dealloc];
307 }
308
309 /* accessors */
310
311 - (void)setRunLoopModes:(NSArray *)_modes {
312   ASSIGN(self->runLoopModes, _modes);
313 }
314 - (NSArray *)runLoopModes {
315   return self->runLoopModes;
316 }
317
318 /* single queue */
319
320 static inline void _enqueue(EODelayedObserverQueue *self,
321                             EODelayedObserver **list,
322                             EODelayedObserver *newEntry)
323 {
324   if (*list == nil) {
325     /* first entry in this list */
326     *list = [newEntry retain];
327   }
328   else {
329     EODelayedObserver *e, *le;
330
331     for (e = *list, le = NULL; e != NULL; e = e->next) {
332       if (e == newEntry) {
333         /* already in queue */
334         return;
335       }
336       le = e;
337     }
338     le->next = [e retain];
339     e->next  = NULL;
340   }
341 }
342 static inline void _dequeue(EODelayedObserverQueue *self,
343                             EODelayedObserver **list,
344                             EODelayedObserver *entry)
345 {
346   EODelayedObserver *e, *le;
347
348   for (e = *list, le = NULL; e != NULL; e = e->next) {
349     if (e == entry) {
350       /* found entry */
351       le->next = e->next;
352       [e release];
353       return;
354     }
355     le = e;
356   }
357 }
358
359 static inline void _notify(EODelayedObserverQueue *self, EODelayedObserver *list)
360 {
361   while (list) {
362     [list subjectChanged];
363     list = list->next;
364   }
365 }
366
367 /* managing queue */
368
369 - (void)enqueueObserver:(EODelayedObserver *)_observer {
370   if (_observer == nil) return;
371   
372   _enqueue(self, &(self->queues[[_observer priority]]), _observer);
373
374   if (!self->hasObservers) {
375     /* register for ASAP notification */
376     NSNotification *notification;
377     
378     notification = [NSNotification notificationWithName:@"EODelayedNotify"
379                                    object:self];
380     
381     [[NSNotificationQueue defaultQueue]
382                           enqueueNotification:notification
383                           postingStyle:NSPostASAP
384                           coalesceMask:NSNotificationCoalescingOnSender
385                           forModes:[self runLoopModes]];
386     
387     self->hasObservers = YES;
388   }
389 }
390 - (void)dequeueObserver:(EODelayedObserver *)_observer {
391   if (_observer == nil) return;
392   
393   _dequeue(self, &(self->queues[[_observer priority]]), _observer);
394 }
395
396 /* notification */
397
398 - (void)notifyObserversUpToPriority:(EOObserverPriority)_lastPriority {
399   unsigned i;
400   
401   for (i = 0; i < _lastPriority; i++)
402     _notify(self, self->queues[i]);
403 }
404
405 - (void)_notify:(NSNotification *)_notification {
406   [self notifyObserversUpToPriority:EOObserverPrioritySixth];
407 }
408
409 @end /* EODelayedObserverQueue */
410
411 @implementation EODelayedObserver
412
413 /* accessors */
414
415 - (EOObserverPriority)priority {
416   return EOObserverPriorityThird;
417 }
418
419 - (EODelayedObserverQueue *)observerQueue {
420   return [EODelayedObserverQueue defaultObserverQueue];
421 }
422
423 /* notifications */
424
425 - (void)subjectChanged {
426   [self doesNotRecognizeSelector:_cmd];
427 }
428
429 - (void)objectWillChange:(id)_object {
430   [[self observerQueue] enqueueObserver:self];
431 }
432
433 - (void)discardPendingNotification {
434   [[self observerQueue] dequeueObserver:self];
435 }
436
437 @end /* EODelayedObserver */
438
439 @implementation EOObserverProxy
440
441 - (id)initWithTarget:(id)_target action:(SEL)_action
442   priority:(EOObserverPriority)_priority
443 {
444   if ((self = [super init])) {
445     self->target   = [_target retain];
446     self->action   = _action;
447     self->priority = _priority;
448   }
449   return self;
450 }
451 - (id)init {
452   return [self initWithTarget:nil action:NULL priority:EOObserverPriorityThird];
453 }
454
455 - (void)dealloc {
456   [self->target release];
457   [super dealloc];
458 }
459
460 /* accessors */
461
462 - (EOObserverPriority)priority {
463   return self->priority;
464 }
465
466 /* notifications */
467
468 - (void)subjectChanged {
469   [self->target performSelector:self->action withObject:self];
470 }
471
472 @end /* EOObserverProxy */
473
474 /* value functions for mapping table */
475
476 static void mapValRetain(NSMapTable *self, const void *_value) {
477   /* do nothing */
478 }
479 static void mapValRelease(NSMapTable *self, void *_value) {
480   /* do nothing */
481 }
482
483 static NSString *mapDescribe(NSMapTable *self, const void *_value) {
484   return @"";
485 }