- added WMRemoveFromArrayMatching(array, match, cdata), which will remove the
[wmaker-crm.git] / WINGs / notification.c
blob95bfb83c87a16a81ebe01cba653583a75132f47c
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <string.h>
7 #include "WUtil.h"
10 typedef struct W_Notification {
11 const char *name;
12 void *object;
13 void *clientData;
14 int refCount;
15 } Notification;
18 extern void W_FlushASAPNotificationQueue();
21 const char*
22 WMGetNotificationName(WMNotification *notification)
24 return notification->name;
28 void*
29 WMGetNotificationObject(WMNotification *notification)
31 return notification->object;
35 void*
36 WMGetNotificationClientData(WMNotification *notification)
38 return notification->clientData;
42 WMNotification*
43 WMCreateNotification(const char *name, void *object, void *clientData)
45 Notification *nPtr;
47 nPtr = wmalloc(sizeof(Notification));
49 nPtr->name = name;
50 nPtr->object = object;
51 nPtr->clientData = clientData;
53 nPtr->refCount = 1;
55 return nPtr;
59 void
60 WMReleaseNotification(WMNotification *notification)
62 notification->refCount--;
64 if (notification->refCount < 1) {
65 wfree(notification);
70 WMNotification*
71 WMRetainNotification(WMNotification *notification)
73 notification->refCount++;
75 return notification;
79 /***************** Notification Center *****************/
81 typedef struct NotificationObserver {
82 WMNotificationObserverAction *observerAction;
83 void *observer;
85 const char *name;
86 void *object;
88 struct NotificationObserver *prev; /* for tables */
89 struct NotificationObserver *next;
90 struct NotificationObserver *nextAction; /* for observerTable */
91 } NotificationObserver;
94 typedef struct W_NotificationCenter {
95 WMHashTable *nameTable; /* names -> observer lists */
96 WMHashTable *objectTable; /* object -> observer lists */
97 NotificationObserver *nilList; /* obervers that catch everything */
99 WMHashTable *observerTable; /* observer -> NotificationObserver */
100 } NotificationCenter;
103 /* default (and only) center */
104 static NotificationCenter *notificationCenter = NULL;
107 void
108 W_InitNotificationCenter(void)
110 notificationCenter = wmalloc(sizeof(NotificationCenter));
112 notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
113 notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
114 notificationCenter->nilList = NULL;
116 notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
120 void
121 WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
122 void *observer, const char *name, void *object)
124 NotificationObserver *oRec, *rec;
126 oRec = wmalloc(sizeof(NotificationObserver));
127 oRec->observerAction = observerAction;
128 oRec->observer = observer;
129 oRec->name = name;
130 oRec->object = object;
131 oRec->next = NULL;
132 oRec->prev = NULL;
135 /* put this action in the list of actions for this observer */
136 rec = (NotificationObserver*)WMHashInsert(notificationCenter->observerTable,
137 observer, oRec);
139 if (rec) {
140 /* if this is not the first action for the observer */
141 oRec->nextAction = rec;
142 } else {
143 oRec->nextAction = NULL;
146 if (!name && !object) {
147 /* catch-all */
148 oRec->next = notificationCenter->nilList;
149 if (notificationCenter->nilList) {
150 notificationCenter->nilList->prev = oRec;
152 notificationCenter->nilList = oRec;
153 } else if (!name) {
154 /* any message coming from object */
155 rec = (NotificationObserver*)WMHashInsert(notificationCenter->objectTable,
156 object, oRec);
157 oRec->next = rec;
158 if (rec) {
159 rec->prev = oRec;
161 } else {
162 /* name && (object || !object) */
163 rec = (NotificationObserver*)WMHashInsert(notificationCenter->nameTable,
164 name, oRec);
165 oRec->next = rec;
166 if (rec) {
167 rec->prev = oRec;
173 void
174 WMPostNotification(WMNotification *notification)
176 NotificationObserver *orec, *tmp;
178 WMRetainNotification(notification);
180 /* tell the observers that want to know about a particular message */
181 orec = (NotificationObserver*)WMHashGet(notificationCenter->nameTable,
182 notification->name);
184 while (orec) {
185 tmp = orec->next;
187 if (!orec->object || !notification->object
188 || orec->object == notification->object) {
189 /* tell the observer */
190 if (orec->observerAction) {
191 (*orec->observerAction)(orec->observer, notification);
195 orec = tmp;
198 /* tell the observers that want to know about an object */
199 orec = (NotificationObserver*)WMHashGet(notificationCenter->objectTable,
200 notification->object);
202 while (orec) {
203 tmp = orec->next;
205 /* tell the observer */
206 if (orec->observerAction) {
207 (*orec->observerAction)(orec->observer, notification);
209 orec = tmp;
212 /* tell the catch all observers */
213 orec = notificationCenter->nilList;
214 while (orec) {
215 tmp = orec->next;
217 /* tell the observer */
218 if (orec->observerAction) {
219 (*orec->observerAction)(orec->observer, notification);
221 orec = tmp;
224 WMReleaseNotification(notification);
228 void
229 WMRemoveNotificationObserver(void *observer)
231 NotificationObserver *orec, *tmp, *rec;
233 /* get the list of actions the observer is doing */
234 orec = (NotificationObserver*)WMHashGet(notificationCenter->observerTable,
235 observer);
238 * FOREACH orec IN actionlist for observer
239 * DO
240 * remove from respective lists/tables
241 * free
242 * END
244 while (orec) {
245 tmp = orec->nextAction;
247 if (!orec->name && !orec->object) {
248 /* catch-all */
249 if (notificationCenter->nilList==orec)
250 notificationCenter->nilList = orec->next;
251 } else if (!orec->name) {
252 /* any message coming from object */
253 rec = (NotificationObserver*)WMHashGet(notificationCenter->objectTable,
254 orec->object);
255 if (rec==orec) {
256 /* replace table entry */
257 if (orec->next) {
258 WMHashInsert(notificationCenter->objectTable, orec->object,
259 orec->next);
260 } else {
261 WMHashRemove(notificationCenter->objectTable, orec->object);
264 } else {
265 /* name && (object || !object) */
266 rec = (NotificationObserver*)WMHashGet(notificationCenter->nameTable,
267 orec->name);
268 if (rec==orec) {
269 /* replace table entry */
270 if (orec->next) {
271 WMHashInsert(notificationCenter->nameTable, orec->name,
272 orec->next);
273 } else {
274 WMHashRemove(notificationCenter->nameTable, orec->name);
278 if (orec->prev)
279 orec->prev->next = orec->next;
280 if (orec->next)
281 orec->next->prev = orec->prev;
283 wfree(orec);
285 orec = tmp;
288 WMHashRemove(notificationCenter->observerTable, observer);
292 void
293 WMRemoveNotificationObserverWithName(void *observer, const char *name, void *object)
295 NotificationObserver *orec, *tmp, *rec;
296 NotificationObserver *newList = NULL;
298 /* get the list of actions the observer is doing */
299 orec = (NotificationObserver*)WMHashGet(notificationCenter->observerTable, observer);
301 WMHashRemove(notificationCenter->observerTable, observer);
303 /* rebuild the list of actions for the observer */
305 while (orec) {
306 tmp = orec->nextAction;
307 if (orec->name == name && orec->object == object) {
308 if (!name && !object) {
309 if (notificationCenter->nilList == orec)
310 notificationCenter->nilList = orec->next;
311 } else if (!name) {
312 rec = (NotificationObserver*)WMHashGet(notificationCenter->objectTable, orec->object);
313 if (rec==orec) {
314 assert(rec->prev==NULL);
315 /* replace table entry */
316 if (orec->next) {
317 WMHashInsert(notificationCenter->objectTable,
318 orec->object, orec->next);
319 } else {
320 WMHashRemove(notificationCenter->objectTable,
321 orec->object);
324 } else {
325 rec = (NotificationObserver*)WMHashGet(notificationCenter->nameTable,
326 orec->name);
327 if (rec==orec) {
328 assert(rec->prev==NULL);
329 /* replace table entry */
330 if (orec->next) {
331 WMHashInsert(notificationCenter->nameTable,
332 orec->name, orec->next);
333 } else {
334 WMHashRemove(notificationCenter->nameTable,
335 orec->name);
340 if (orec->prev)
341 orec->prev->next = orec->next;
342 if (orec->next)
343 orec->next->prev = orec->prev;
344 wfree(orec);
345 } else {
346 /* append this action in the new action list */
347 orec->nextAction = NULL;
348 if (!newList) {
349 newList = orec;
350 } else {
351 NotificationObserver *p;
353 p = newList;
354 while (p->nextAction) {
355 p = p->nextAction;
357 p->nextAction = orec;
360 orec = tmp;
363 /* reinsert the list to the table */
364 if (newList) {
365 WMHashInsert(notificationCenter->observerTable, observer, newList);
370 void
371 WMPostNotificationName(const char *name, void *object, void *clientData)
373 WMNotification *notification;
375 notification = WMCreateNotification(name, object, clientData);
377 WMPostNotification(notification);
379 WMReleaseNotification(notification);
384 /**************** Notification Queues ****************/
387 typedef struct W_NotificationQueue {
388 WMArray *asapQueue;
389 WMArray *idleQueue;
391 struct W_NotificationQueue *next;
392 } NotificationQueue;
395 static WMNotificationQueue *notificationQueueList = NULL;
397 /* default queue */
398 static WMNotificationQueue *notificationQueue = NULL;
401 WMNotificationQueue*
402 WMGetDefaultNotificationQueue(void)
404 if (!notificationQueue)
405 notificationQueue = WMCreateNotificationQueue();
407 return notificationQueue;
411 WMNotificationQueue*
412 WMCreateNotificationQueue(void)
414 NotificationQueue *queue;
416 queue = wmalloc(sizeof(NotificationQueue));
418 queue->asapQueue =
419 WMCreateArrayWithDestructor(8, (WMFreeDataProc*)WMReleaseNotification);
420 queue->idleQueue =
421 WMCreateArrayWithDestructor(8, (WMFreeDataProc*)WMReleaseNotification);
422 queue->next = notificationQueueList;
424 notificationQueueList = queue;
426 return queue;
431 void
432 WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
433 WMPostingStyle postingStyle)
435 WMEnqueueCoalesceNotification(queue, notification, postingStyle,
436 WNCOnName|WNCOnSender);
440 #define NOTIF ((WMNotification*)cdata)
441 #define ITEM ((WMNotification*)item)
443 static int
444 matchSenderAndName(void *item, void *cdata)
446 return (NOTIF->object==ITEM->object && strcmp(NOTIF->name, ITEM->name)==0);
450 static int
451 matchSender(void *item, void *cdata)
453 return (NOTIF->object == ITEM->object);
457 static int
458 matchName(void *item, void *cdata)
460 return (strcmp(NOTIF->name, ITEM->name)==0);
463 #undef NOTIF
464 #undef ITEM
467 void
468 WMDequeueNotificationMatching(WMNotificationQueue *queue,
469 WMNotification *notification, unsigned mask)
471 WMMatchDataProc *matchFunc;
473 if ((mask & WNCOnName) && (mask & WNCOnSender))
474 matchFunc = matchSenderAndName;
475 else if (mask & WNCOnName)
476 matchFunc = matchName;
477 else if (mask & WNCOnSender)
478 matchFunc = matchSender;
479 else
480 return;
482 WMRemoveFromArrayMatching(queue->asapQueue, matchFunc, notification);
483 WMRemoveFromArrayMatching(queue->idleQueue, matchFunc, notification);
487 void
488 WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
489 WMNotification *notification,
490 WMPostingStyle postingStyle,
491 unsigned coalesceMask)
493 if (coalesceMask != WNCNone)
494 WMDequeueNotificationMatching(queue, notification, coalesceMask);
496 switch (postingStyle) {
497 case WMPostNow:
498 WMPostNotification(notification);
499 WMReleaseNotification(notification);
500 break;
502 case WMPostASAP:
503 WMAddToArray(queue->asapQueue, notification);
504 break;
506 case WMPostWhenIdle:
507 WMAddToArray(queue->idleQueue, notification);
508 break;
513 void
514 W_FlushASAPNotificationQueue()
516 WMNotificationQueue *queue = notificationQueueList;
518 while (queue) {
519 while (WMGetArrayItemCount(queue->asapQueue)) {
520 WMPostNotification(WMGetFromArray(queue->asapQueue, 0));
521 WMDeleteFromArray(queue->asapQueue, 0);
524 queue = queue->next;
529 void
530 W_FlushIdleNotificationQueue()
532 WMNotificationQueue *queue = notificationQueueList;
534 while (queue) {
535 while (WMGetArrayItemCount(queue->idleQueue)) {
536 WMPostNotification(WMGetFromArray(queue->idleQueue, 0));
537 WMDeleteFromArray(queue->idleQueue, 0);
540 queue = queue->next;