replaced linked lists with WMBag, added progress indicator
[wmaker-crm.git] / WINGs / notification.c
blob2fd27afa8cf302b063c73126e063b8b6c7e61c4b
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 free(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);
221 W_FlushASAPNotificationQueue();
225 void
226 WMRemoveNotificationObserver(void *observer)
228 NotificationObserver *orec, *tmp, *rec;
230 /* get the list of actions the observer is doing */
231 orec = WMHashGet(notificationCenter->observerTable, observer);
234 * FOREACH orec IN actionlist for observer
235 * DO
236 * remove from respective lists/tables
237 * free
238 * END
240 while (orec) {
241 tmp = orec->nextAction;
243 if (!orec->name && !orec->object) {
244 /* catch-all */
245 if (notificationCenter->nilList==orec)
246 notificationCenter->nilList = orec->next;
247 } else if (!orec->name) {
248 /* any message coming from object */
249 rec = WMHashGet(notificationCenter->objectTable, orec->object);
250 if (rec==orec) {
251 /* replace table entry */
252 if (orec->next) {
253 WMHashInsert(notificationCenter->objectTable, orec->object,
254 orec->next);
255 } else {
256 WMHashRemove(notificationCenter->objectTable, orec->object);
259 } else {
260 /* name && (object || !object) */
261 rec = WMHashGet(notificationCenter->nameTable, orec->name);
262 if (rec==orec) {
263 /* replace table entry */
264 if (orec->next) {
265 WMHashInsert(notificationCenter->nameTable, orec->name,
266 orec->next);
267 } else {
268 WMHashRemove(notificationCenter->nameTable, orec->name);
272 if (orec->prev)
273 orec->prev->next = orec->next;
274 if (orec->next)
275 orec->next->prev = orec->prev;
277 free(orec);
279 orec = tmp;
282 WMHashRemove(notificationCenter->observerTable, observer);
286 void
287 WMRemoveNotificationObserverWithName(void *observer, char *name, void *object)
289 NotificationObserver *orec, *tmp, *rec;
290 NotificationObserver *newList = NULL;
292 /* get the list of actions the observer is doing */
293 orec = WMHashGet(notificationCenter->observerTable, observer);
295 WMHashRemove(notificationCenter->observerTable, observer);
297 /* rebuild the list of actions for the observer */
299 while (orec) {
300 tmp = orec->nextAction;
301 if (orec->name == name && orec->object == object) {
302 if (!name && !object) {
303 if (notificationCenter->nilList == orec)
304 notificationCenter->nilList = orec->next;
305 } else if (!name) {
306 rec = WMHashGet(notificationCenter->objectTable, orec->object);
307 if (rec==orec) {
308 assert(rec->prev==NULL);
309 /* replace table entry */
310 if (orec->next) {
311 WMHashInsert(notificationCenter->objectTable,
312 orec->object, orec->next);
313 } else {
314 WMHashRemove(notificationCenter->objectTable,
315 orec->object);
318 } else {
319 rec = WMHashGet(notificationCenter->nameTable, orec->name);
320 if (rec==orec) {
321 assert(rec->prev==NULL);
322 /* replace table entry */
323 if (orec->next) {
324 WMHashInsert(notificationCenter->nameTable,
325 orec->name, orec->next);
326 } else {
327 WMHashRemove(notificationCenter->nameTable,
328 orec->name);
333 if (orec->prev)
334 orec->prev->next = orec->next;
335 if (orec->next)
336 orec->next->prev = orec->prev;
337 free(orec);
338 } else {
339 /* append this action in the new action list */
340 orec->nextAction = NULL;
341 if (!newList) {
342 newList = orec;
343 } else {
344 NotificationObserver *p;
346 p = newList;
347 while (p->nextAction) {
348 p = p->nextAction;
350 p->nextAction = orec;
353 orec = tmp;
356 /* reinsert the list to the table */
357 if (newList) {
358 WMHashInsert(notificationCenter->observerTable, observer, newList);
363 void
364 WMPostNotificationName(char *name, void *object, void *clientData)
366 WMNotification *notification;
368 notification = WMCreateNotification(name, object, clientData);
370 WMPostNotification(notification);
372 WMReleaseNotification(notification);
377 /**************** Notification Queues ****************/
380 typedef struct W_NotificationQueue {
381 WMBag *asapQueue;
382 WMBag *idleQueue;
384 struct W_NotificationQueue *next;
385 } NotificationQueue;
388 static WMNotificationQueue *notificationQueueList = NULL;
390 /* default queue */
391 static WMNotificationQueue *notificationQueue = NULL;
394 WMNotificationQueue*
395 WMGetDefaultNotificationQueue(void)
397 if (!notificationQueue)
398 notificationQueue = WMCreateNotificationQueue();
400 return notificationQueue;
404 WMNotificationQueue*
405 WMCreateNotificationQueue(void)
407 NotificationQueue *queue;
409 queue = wmalloc(sizeof(NotificationQueue));
411 queue->asapQueue = WMCreateBag(8);
412 queue->idleQueue = WMCreateBag(8);
413 queue->next = notificationQueueList;
415 notificationQueueList = queue;
417 return queue;
422 void
423 WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
424 WMPostingStyle postingStyle)
426 WMEnqueueCoalesceNotification(queue, notification, postingStyle,
427 WNCOnName|WNCOnSender);
432 void
433 WMDequeueNotificationMatching(WMNotificationQueue *queue,
434 WMNotification *notification, unsigned mask)
436 int i;
437 WMNotification *tmp;
439 if (mask & WNCOnName) {
440 for (i = 0; i < WMGetBagItemCount(queue->asapQueue); i++) {
441 tmp = WMGetFromBag(queue->asapQueue, i);
443 if (strcmp(notification->name, tmp) == 0) {
444 WMRemoveFromBag(queue->asapQueue, tmp);
445 WMReleaseNotification(tmp);
446 break;
449 for (i = 0; i < WMGetBagItemCount(queue->idleQueue); i++) {
450 tmp = WMGetFromBag(queue->idleQueue, i);
452 if (strcmp(notification->name, tmp) == 0) {
453 WMRemoveFromBag(queue->idleQueue, tmp);
454 WMReleaseNotification(tmp);
455 break;
459 if (mask & WNCOnSender) {
460 for (i = 0; i < WMGetBagItemCount(queue->asapQueue); i++) {
461 tmp = WMGetFromBag(queue->asapQueue, i);
463 if (notification->object == tmp->object) {
464 WMRemoveFromBag(queue->asapQueue, tmp);
465 WMReleaseNotification(tmp);
466 break;
469 for (i = 0; i < WMGetBagItemCount(queue->idleQueue); i++) {
470 tmp = WMGetFromBag(queue->idleQueue, i);
472 if (notification->object == tmp->object) {
473 WMRemoveFromBag(queue->idleQueue, tmp);
474 WMReleaseNotification(tmp);
475 break;
482 void
483 WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
484 WMNotification *notification,
485 WMPostingStyle postingStyle,
486 unsigned coalesceMask)
488 if (coalesceMask != WNCNone)
489 WMDequeueNotificationMatching(queue, notification, coalesceMask);
491 switch (postingStyle) {
492 case WMPostNow:
493 WMPostNotification(notification);
494 break;
496 case WMPostASAP:
497 WMPutInBag(queue->asapQueue, notification);
498 break;
500 case WMPostWhenIdle:
501 WMPutInBag(queue->idleQueue, notification);
502 break;
507 void
508 W_FlushASAPNotificationQueue()
510 WMNotificationQueue *queue = notificationQueueList;
512 while (queue) {
513 while (WMGetBagItemCount(queue->asapQueue)) {
514 WMNotification *tmp = WMGetFromBag(queue->asapQueue, 0);
516 WMPostNotification(tmp);
517 WMDeleteFromBag(queue->asapQueue, 0);
520 queue = queue->next;
525 void
526 W_FlushIdleNotificationQueue()
528 WMNotificationQueue *queue = notificationQueueList;
530 while (queue) {
531 while (WMGetBagItemCount(queue->idleQueue)) {
532 WMNotification *tmp = WMGetFromBag(queue->idleQueue, 0);
534 WMPostNotification(tmp);
535 WMDeleteFromBag(queue->idleQueue, 0);
538 queue = queue->next;