new/changelog fix
[wmaker-crm.git] / WINGs / notification.c
blob0607b44a368ecb5ebdd4e3dd00955ebdabf22640
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <string.h>
7 #include "WUtil.h"
9 #include "llist.h"
11 typedef struct W_Notification {
12 char *name;
13 void *object;
14 void *clientData;
15 int refCount;
16 } Notification;
19 extern void W_FlushASAPNotificationQueue();
22 char*
23 WMGetNotificationName(WMNotification *notification)
25 return notification->name;
29 void*
30 WMGetNotificationObject(WMNotification *notification)
32 return notification->object;
36 void*
37 WMGetNotificationClientData(WMNotification *notification)
39 return notification->clientData;
43 WMNotification*
44 WMCreateNotification(char *name, void *object, void *clientData)
46 Notification *nPtr;
48 nPtr = wmalloc(sizeof(Notification));
50 nPtr->name = name;
51 nPtr->object = object;
52 nPtr->clientData = clientData;
54 nPtr->refCount = 1;
56 return nPtr;
60 void
61 WMReleaseNotification(WMNotification *notification)
63 notification->refCount--;
65 if (notification->refCount < 1) {
66 free(notification);
71 WMNotification*
72 WMRetainNotification(WMNotification *notification)
74 notification->refCount++;
76 return notification;
80 /***************** Notification Center *****************/
82 typedef struct NotificationObserver {
83 WMNotificationObserverAction *observerAction;
84 void *observer;
86 char *name;
87 void *object;
89 struct NotificationObserver *prev; /* for tables */
90 struct NotificationObserver *next;
91 struct NotificationObserver *nextAction; /* for observerTable */
92 } NotificationObserver;
95 typedef struct W_NotificationCenter {
96 WMHashTable *nameTable; /* names -> observer lists */
97 WMHashTable *objectTable; /* object -> observer lists */
98 NotificationObserver *nilList; /* obervers that catch everything */
100 WMHashTable *observerTable; /* observer -> NotificationObserver */
101 } NotificationCenter;
104 /* default (and only) center */
105 static NotificationCenter *notificationCenter = NULL;
108 void
109 W_InitNotificationCenter(void)
111 notificationCenter = wmalloc(sizeof(NotificationCenter));
113 notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
114 notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
115 notificationCenter->nilList = NULL;
117 notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
121 void
122 WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
123 void *observer, char *name, void *object)
125 NotificationObserver *oRec, *rec;
127 oRec = wmalloc(sizeof(NotificationObserver));
128 oRec->observerAction = observerAction;
129 oRec->observer = observer;
130 oRec->name = name;
131 oRec->object = object;
132 oRec->next = NULL;
133 oRec->prev = NULL;
136 /* put this action in the list of actions for this observer */
137 rec = WMHashInsert(notificationCenter->observerTable, 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 = WMHashInsert(notificationCenter->objectTable, object, oRec);
156 oRec->next = rec;
157 if (rec) {
158 rec->prev = oRec;
160 } else {
161 /* name && (object || !object) */
162 rec = WMHashInsert(notificationCenter->nameTable, name, oRec);
163 oRec->next = rec;
164 if (rec) {
165 rec->prev = oRec;
171 void
172 WMPostNotification(WMNotification *notification)
174 NotificationObserver *orec, *tmp;
176 WMRetainNotification(notification);
178 /* tell the observers that want to know about a particular message */
179 orec = WMHashGet(notificationCenter->nameTable, notification->name);
181 while (orec) {
182 tmp = orec->next;
184 if (!orec->object || !notification->object
185 || orec->object == notification->object) {
186 /* tell the observer */
187 if (orec->observerAction) {
188 (*orec->observerAction)(orec->observer, notification);
192 orec = tmp;
195 /* tell the observers that want to know about an object */
196 orec = WMHashGet(notificationCenter->objectTable, notification->object);
198 while (orec) {
199 tmp = orec->next;
201 /* tell the observer */
202 if (orec->observerAction) {
203 (*orec->observerAction)(orec->observer, notification);
205 orec = tmp;
208 /* tell the catch all observers */
209 orec = notificationCenter->nilList;
210 while (orec) {
211 tmp = orec->next;
213 /* tell the observer */
214 if (orec->observerAction) {
215 (*orec->observerAction)(orec->observer, notification);
217 orec = tmp;
220 WMReleaseNotification(notification);
222 W_FlushASAPNotificationQueue();
226 void
227 WMRemoveNotificationObserver(void *observer)
229 NotificationObserver *orec, *tmp, *rec;
231 /* get the list of actions the observer is doing */
232 orec = WMHashGet(notificationCenter->observerTable, observer);
235 * FOREACH orec IN actionlist for observer
236 * DO
237 * remove from respective lists/tables
238 * free
239 * END
241 while (orec) {
242 tmp = orec->nextAction;
244 if (!orec->name && !orec->object) {
245 /* catch-all */
246 if (notificationCenter->nilList==orec)
247 notificationCenter->nilList = orec->next;
248 } else if (!orec->name) {
249 /* any message coming from object */
250 rec = WMHashGet(notificationCenter->objectTable, orec->object);
251 if (rec==orec) {
252 /* replace table entry */
253 if (orec->next) {
254 WMHashInsert(notificationCenter->objectTable, orec->object,
255 orec->next);
256 } else {
257 WMHashRemove(notificationCenter->objectTable, orec->object);
260 } else {
261 /* name && (object || !object) */
262 rec = WMHashGet(notificationCenter->nameTable, orec->name);
263 if (rec==orec) {
264 /* replace table entry */
265 if (orec->next) {
266 WMHashInsert(notificationCenter->nameTable, orec->name,
267 orec->next);
268 } else {
269 WMHashRemove(notificationCenter->nameTable, orec->name);
273 if (orec->prev)
274 orec->prev->next = orec->next;
275 if (orec->next)
276 orec->next->prev = orec->prev;
278 free(orec);
280 orec = tmp;
283 WMHashRemove(notificationCenter->observerTable, observer);
287 void
288 WMRemoveNotificationObserverWithName(void *observer, char *name, void *object)
290 NotificationObserver *orec, *tmp, *rec;
291 NotificationObserver *newList = NULL;
293 /* get the list of actions the observer is doing */
294 orec = WMHashGet(notificationCenter->observerTable, observer);
296 WMHashRemove(notificationCenter->observerTable, observer);
298 /* rebuild the list of actions for the observer */
300 while (orec) {
301 tmp = orec->nextAction;
302 if (orec->name == name && orec->object == object) {
303 if (!name && !object) {
304 if (notificationCenter->nilList == orec)
305 notificationCenter->nilList = orec->next;
306 } else if (!name) {
307 rec = WMHashGet(notificationCenter->objectTable, orec->object);
308 if (rec==orec) {
309 assert(rec->prev==NULL);
310 /* replace table entry */
311 if (orec->next) {
312 WMHashInsert(notificationCenter->objectTable,
313 orec->object, orec->next);
314 } else {
315 WMHashRemove(notificationCenter->objectTable,
316 orec->object);
319 } else {
320 rec = WMHashGet(notificationCenter->nameTable, orec->name);
321 if (rec==orec) {
322 assert(rec->prev==NULL);
323 /* replace table entry */
324 if (orec->next) {
325 WMHashInsert(notificationCenter->nameTable,
326 orec->name, orec->next);
327 } else {
328 WMHashRemove(notificationCenter->nameTable,
329 orec->name);
334 if (orec->prev)
335 orec->prev->next = orec->next;
336 if (orec->next)
337 orec->next->prev = orec->prev;
338 free(orec);
339 } else {
340 /* append this action in the new action list */
341 orec->nextAction = NULL;
342 if (!newList) {
343 newList = orec;
344 } else {
345 NotificationObserver *p;
347 p = newList;
348 while (p->nextAction) {
349 p = p->nextAction;
351 p->nextAction = orec;
354 orec = tmp;
357 /* reinsert the list to the table */
358 if (newList) {
359 WMHashInsert(notificationCenter->observerTable, observer, newList);
364 void
365 WMPostNotificationName(char *name, void *object, void *clientData)
367 WMNotification *notification;
369 notification = WMCreateNotification(name, object, clientData);
371 WMPostNotification(notification);
373 WMReleaseNotification(notification);
378 /**************** Notification Queues ****************/
381 typedef struct W_NotificationQueue {
382 list_t *asapQueue;
383 list_t *idleQueue;
385 struct W_NotificationQueue *next;
386 } NotificationQueue;
389 static WMNotificationQueue *notificationQueueList = NULL;
391 /* default queue */
392 static WMNotificationQueue *notificationQueue = NULL;
395 WMNotificationQueue*
396 WMGetDefaultNotificationQueue(void)
398 if (!notificationQueue)
399 notificationQueue = WMCreateNotificationQueue();
401 return notificationQueue;
405 WMNotificationQueue*
406 WMCreateNotificationQueue(void)
408 NotificationQueue *queue;
410 queue = wmalloc(sizeof(NotificationQueue));
412 queue->asapQueue = NULL;
413 queue->idleQueue = NULL;
414 queue->next = notificationQueueList;
416 notificationQueueList = queue;
418 return queue;
423 void
424 WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
425 WMPostingStyle postingStyle)
427 WMEnqueueCoalesceNotification(queue, notification, postingStyle,
428 WNCOnName|WNCOnSender);
432 static int
433 matchName(void *a, void *b)
435 WMNotification *n1 = (WMNotification*)a;
436 WMNotification *n2 = (WMNotification*)b;
438 return strcmp(n1->name, n2->name);
442 static int
443 matchSender(void *a, void *b)
445 WMNotification *n1 = (WMNotification*)a;
446 WMNotification *n2 = (WMNotification*)b;
448 return (n1->object == n2->object);
452 void
453 WMDequeueNotificationMatching(WMNotificationQueue *queue,
454 WMNotification *notification, unsigned mask)
456 void *n;
458 if (mask & WNCOnName) {
459 while ((n = lfind(notification->name, queue->asapQueue, matchName))) {
460 queue->asapQueue = lremove(queue->asapQueue, n);
461 WMReleaseNotification((WMNotification*)n);
463 while ((n = lfind(notification->name, queue->idleQueue, matchName))) {
464 queue->idleQueue = lremove(queue->idleQueue, n);
465 WMReleaseNotification((WMNotification*)n);
468 if (mask & WNCOnSender) {
469 while ((n = lfind(notification->name, queue->asapQueue, matchSender))) {
470 queue->asapQueue = lremove(queue->asapQueue, n);
471 WMReleaseNotification((WMNotification*)n);
473 while ((n = lfind(notification->name, queue->idleQueue, matchSender))) {
474 queue->idleQueue = lremove(queue->idleQueue, n);
475 WMReleaseNotification((WMNotification*)n);
481 void
482 WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
483 WMNotification *notification,
484 WMPostingStyle postingStyle,
485 unsigned coalesceMask)
487 if (coalesceMask != WNCNone)
488 WMDequeueNotificationMatching(queue, notification, coalesceMask);
490 switch (postingStyle) {
491 case WMPostNow:
492 WMPostNotification(notification);
493 break;
495 case WMPostASAP:
496 queue->asapQueue = lappend(queue->asapQueue,
497 lcons(notification, NULL));
498 break;
500 case WMPostWhenIdle:
501 queue->idleQueue = lappend(queue->idleQueue,
502 lcons(notification, NULL));
503 break;
508 void
509 W_FlushASAPNotificationQueue()
511 WMNotificationQueue *queue = notificationQueueList;
513 while (queue) {
514 while (queue->asapQueue) {
515 WMPostNotification((WMNotification*)lhead(queue->asapQueue));
516 queue->asapQueue = lremovehead(queue->asapQueue);
522 void
523 W_FlushIdleNotificationQueue()
525 WMNotificationQueue *queue = notificationQueueList;
527 while (queue) {
528 while (queue->idleQueue) {
529 WMPostNotification((WMNotification*)lhead(queue->idleQueue));
530 queue->idleQueue = lremovehead(queue->idleQueue);