bag tree finished.. updated code to new bags
[wmaker-crm.git] / WINGs / notification.c
blobc9bb1772ef07d93782d9374ffcd450bf492030f3
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 char *name;
12 void *object;
13 void *clientData;
14 int refCount;
15 } Notification;
18 extern void W_FlushASAPNotificationQueue();
21 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(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 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, 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 = WMHashInsert(notificationCenter->observerTable, observer, oRec);
138 if (rec) {
139 /* if this is not the first action for the observer */
140 oRec->nextAction = rec;
141 } else {
142 oRec->nextAction = NULL;
145 if (!name && !object) {
146 /* catch-all */
147 oRec->next = notificationCenter->nilList;
148 if (notificationCenter->nilList) {
149 notificationCenter->nilList->prev = oRec;
151 notificationCenter->nilList = oRec;
152 } else if (!name) {
153 /* any message coming from object */
154 rec = WMHashInsert(notificationCenter->objectTable, object, oRec);
155 oRec->next = rec;
156 if (rec) {
157 rec->prev = oRec;
159 } else {
160 /* name && (object || !object) */
161 rec = WMHashInsert(notificationCenter->nameTable, name, oRec);
162 oRec->next = rec;
163 if (rec) {
164 rec->prev = oRec;
170 void
171 WMPostNotification(WMNotification *notification)
173 NotificationObserver *orec, *tmp;
175 WMRetainNotification(notification);
177 /* tell the observers that want to know about a particular message */
178 orec = WMHashGet(notificationCenter->nameTable, notification->name);
180 while (orec) {
181 tmp = orec->next;
183 if (!orec->object || !notification->object
184 || orec->object == notification->object) {
185 /* tell the observer */
186 if (orec->observerAction) {
187 (*orec->observerAction)(orec->observer, notification);
191 orec = tmp;
194 /* tell the observers that want to know about an object */
195 orec = WMHashGet(notificationCenter->objectTable, notification->object);
197 while (orec) {
198 tmp = orec->next;
200 /* tell the observer */
201 if (orec->observerAction) {
202 (*orec->observerAction)(orec->observer, notification);
204 orec = tmp;
207 /* tell the catch all observers */
208 orec = notificationCenter->nilList;
209 while (orec) {
210 tmp = orec->next;
212 /* tell the observer */
213 if (orec->observerAction) {
214 (*orec->observerAction)(orec->observer, notification);
216 orec = tmp;
219 WMReleaseNotification(notification);
223 void
224 WMRemoveNotificationObserver(void *observer)
226 NotificationObserver *orec, *tmp, *rec;
228 /* get the list of actions the observer is doing */
229 orec = WMHashGet(notificationCenter->observerTable, observer);
232 * FOREACH orec IN actionlist for observer
233 * DO
234 * remove from respective lists/tables
235 * free
236 * END
238 while (orec) {
239 tmp = orec->nextAction;
241 if (!orec->name && !orec->object) {
242 /* catch-all */
243 if (notificationCenter->nilList==orec)
244 notificationCenter->nilList = orec->next;
245 } else if (!orec->name) {
246 /* any message coming from object */
247 rec = WMHashGet(notificationCenter->objectTable, orec->object);
248 if (rec==orec) {
249 /* replace table entry */
250 if (orec->next) {
251 WMHashInsert(notificationCenter->objectTable, orec->object,
252 orec->next);
253 } else {
254 WMHashRemove(notificationCenter->objectTable, orec->object);
257 } else {
258 /* name && (object || !object) */
259 rec = WMHashGet(notificationCenter->nameTable, orec->name);
260 if (rec==orec) {
261 /* replace table entry */
262 if (orec->next) {
263 WMHashInsert(notificationCenter->nameTable, orec->name,
264 orec->next);
265 } else {
266 WMHashRemove(notificationCenter->nameTable, orec->name);
270 if (orec->prev)
271 orec->prev->next = orec->next;
272 if (orec->next)
273 orec->next->prev = orec->prev;
275 wfree(orec);
277 orec = tmp;
280 WMHashRemove(notificationCenter->observerTable, observer);
284 void
285 WMRemoveNotificationObserverWithName(void *observer, char *name, void *object)
287 NotificationObserver *orec, *tmp, *rec;
288 NotificationObserver *newList = NULL;
290 /* get the list of actions the observer is doing */
291 orec = WMHashGet(notificationCenter->observerTable, observer);
293 WMHashRemove(notificationCenter->observerTable, observer);
295 /* rebuild the list of actions for the observer */
297 while (orec) {
298 tmp = orec->nextAction;
299 if (orec->name == name && orec->object == object) {
300 if (!name && !object) {
301 if (notificationCenter->nilList == orec)
302 notificationCenter->nilList = orec->next;
303 } else if (!name) {
304 rec = WMHashGet(notificationCenter->objectTable, orec->object);
305 if (rec==orec) {
306 assert(rec->prev==NULL);
307 /* replace table entry */
308 if (orec->next) {
309 WMHashInsert(notificationCenter->objectTable,
310 orec->object, orec->next);
311 } else {
312 WMHashRemove(notificationCenter->objectTable,
313 orec->object);
316 } else {
317 rec = WMHashGet(notificationCenter->nameTable, orec->name);
318 if (rec==orec) {
319 assert(rec->prev==NULL);
320 /* replace table entry */
321 if (orec->next) {
322 WMHashInsert(notificationCenter->nameTable,
323 orec->name, orec->next);
324 } else {
325 WMHashRemove(notificationCenter->nameTable,
326 orec->name);
331 if (orec->prev)
332 orec->prev->next = orec->next;
333 if (orec->next)
334 orec->next->prev = orec->prev;
335 wfree(orec);
336 } else {
337 /* append this action in the new action list */
338 orec->nextAction = NULL;
339 if (!newList) {
340 newList = orec;
341 } else {
342 NotificationObserver *p;
344 p = newList;
345 while (p->nextAction) {
346 p = p->nextAction;
348 p->nextAction = orec;
351 orec = tmp;
354 /* reinsert the list to the table */
355 if (newList) {
356 WMHashInsert(notificationCenter->observerTable, observer, newList);
361 void
362 WMPostNotificationName(char *name, void *object, void *clientData)
364 WMNotification *notification;
366 notification = WMCreateNotification(name, object, clientData);
368 WMPostNotification(notification);
370 WMReleaseNotification(notification);
375 /**************** Notification Queues ****************/
378 typedef struct W_NotificationQueue {
379 WMBag *asapQueue;
380 WMBag *idleQueue;
382 struct W_NotificationQueue *next;
383 } NotificationQueue;
386 static WMNotificationQueue *notificationQueueList = NULL;
388 /* default queue */
389 static WMNotificationQueue *notificationQueue = NULL;
392 WMNotificationQueue*
393 WMGetDefaultNotificationQueue(void)
395 if (!notificationQueue)
396 notificationQueue = WMCreateNotificationQueue();
398 return notificationQueue;
402 WMNotificationQueue*
403 WMCreateNotificationQueue(void)
405 NotificationQueue *queue;
407 queue = wmalloc(sizeof(NotificationQueue));
409 queue->asapQueue = WMCreateBag(8);
410 queue->idleQueue = WMCreateBag(8);
411 queue->next = notificationQueueList;
413 notificationQueueList = queue;
415 return queue;
420 void
421 WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
422 WMPostingStyle postingStyle)
424 WMEnqueueCoalesceNotification(queue, notification, postingStyle,
425 WNCOnName|WNCOnSender);
430 void
431 WMDequeueNotificationMatching(WMNotificationQueue *queue,
432 WMNotification *notification, unsigned mask)
434 WMBagIterator i;
435 WMNotification *tmp;
437 if ((mask & WNCOnName) && (mask & WNCOnSender)) {
438 WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
439 if (notification->object == tmp->object &&
440 strcmp(notification->name, tmp->name) == 0) {
441 WMRemoveFromBag(queue->asapQueue, tmp);
442 WMReleaseNotification(tmp);
443 break;
446 WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
447 if (notification->object == tmp->object &&
448 strcmp(notification->name, tmp->name) == 0) {
449 WMRemoveFromBag(queue->idleQueue, tmp);
450 WMReleaseNotification(tmp);
451 break;
454 } else if (mask & WNCOnName) {
455 WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
456 if (strcmp(notification->name, tmp->name) == 0) {
457 WMRemoveFromBag(queue->asapQueue, tmp);
458 WMReleaseNotification(tmp);
459 break;
462 WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
463 if (strcmp(notification->name, tmp->name) == 0) {
464 WMRemoveFromBag(queue->idleQueue, tmp);
465 WMReleaseNotification(tmp);
466 break;
469 } else if (mask & WNCOnSender) {
470 WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
471 if (notification->object == tmp->object) {
472 WMRemoveFromBag(queue->asapQueue, tmp);
473 WMReleaseNotification(tmp);
474 break;
477 WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
478 if (notification->object == tmp->object) {
479 WMRemoveFromBag(queue->idleQueue, tmp);
480 WMReleaseNotification(tmp);
481 break;
488 void
489 WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
490 WMNotification *notification,
491 WMPostingStyle postingStyle,
492 unsigned coalesceMask)
494 if (coalesceMask != WNCNone)
495 WMDequeueNotificationMatching(queue, notification, coalesceMask);
497 switch (postingStyle) {
498 case WMPostNow:
499 WMPostNotification(notification);
500 WMReleaseNotification(notification);
501 break;
503 case WMPostASAP:
504 WMPutInBag(queue->asapQueue, notification);
505 break;
507 case WMPostWhenIdle:
508 WMPutInBag(queue->idleQueue, notification);
509 break;
514 void
515 W_FlushASAPNotificationQueue()
517 WMNotificationQueue *queue = notificationQueueList;
519 while (queue) {
520 while (WMGetBagItemCount(queue->asapQueue)) {
521 WMNotification *tmp = WMGetFromBag(queue->asapQueue, 0);
523 WMPostNotification(tmp);
524 WMReleaseNotification(tmp);
525 WMDeleteFromBag(queue->asapQueue, 0);
528 queue = queue->next;
533 void
534 W_FlushIdleNotificationQueue()
536 WMNotificationQueue *queue = notificationQueueList;
538 while (queue) {
539 while (WMGetBagItemCount(queue->idleQueue)) {
540 WMNotification *tmp = WMGetFromBag(queue->idleQueue, 0);
542 WMPostNotification(tmp);
543 WMReleaseNotification(tmp);
544 WMDeleteFromBag(queue->idleQueue, 0);
547 queue = queue->next;