Initial commit
[platform/upstream/glib2.0.git] / glib / ghook.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GHook: Callback maintenance functions
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GLib Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 /* 
31  * MT safe
32  */
33
34 #include "config.h"
35
36 #include "glib.h"
37 #include "galias.h"
38
39
40 /* --- functions --- */
41 static void
42 default_finalize_hook (GHookList *hook_list,
43                        GHook     *hook)
44 {
45   GDestroyNotify destroy = hook->destroy;
46
47   if (destroy)
48     {
49       hook->destroy = NULL;
50       destroy (hook->data);
51     }
52 }
53
54 void
55 g_hook_list_init (GHookList *hook_list,
56                   guint      hook_size)
57 {
58   g_return_if_fail (hook_list != NULL);
59   g_return_if_fail (hook_size >= sizeof (GHook));
60   
61   hook_list->seq_id = 1;
62   hook_list->hook_size = hook_size;
63   hook_list->is_setup = TRUE;
64   hook_list->hooks = NULL;
65   hook_list->dummy3 = NULL;
66   hook_list->finalize_hook = default_finalize_hook;
67   hook_list->dummy[0] = NULL;
68   hook_list->dummy[1] = NULL;
69 }
70
71 void
72 g_hook_list_clear (GHookList *hook_list)
73 {
74   g_return_if_fail (hook_list != NULL);
75   
76   if (hook_list->is_setup)
77     {
78       GHook *hook;
79       
80       hook_list->is_setup = FALSE;
81       
82       hook = hook_list->hooks;
83       if (!hook)
84         {
85           /* destroy hook_list->hook_memchunk */
86         }
87       else
88         do
89           {
90             GHook *tmp;
91             
92             g_hook_ref (hook_list, hook);
93             g_hook_destroy_link (hook_list, hook);
94             tmp = hook->next;
95             g_hook_unref (hook_list, hook);
96             hook = tmp;
97           }
98         while (hook);
99     }
100 }
101
102 GHook*
103 g_hook_alloc (GHookList *hook_list)
104 {
105   GHook *hook;
106   
107   g_return_val_if_fail (hook_list != NULL, NULL);
108   g_return_val_if_fail (hook_list->is_setup, NULL);
109   
110   hook = g_slice_alloc0 (hook_list->hook_size);
111   hook->data = NULL;
112   hook->next = NULL;
113   hook->prev = NULL;
114   hook->flags = G_HOOK_FLAG_ACTIVE;
115   hook->ref_count = 0;
116   hook->hook_id = 0;
117   hook->func = NULL;
118   hook->destroy = NULL;
119   
120   return hook;
121 }
122
123 void
124 g_hook_free (GHookList *hook_list,
125              GHook     *hook)
126 {
127   g_return_if_fail (hook_list != NULL);
128   g_return_if_fail (hook_list->is_setup);
129   g_return_if_fail (hook != NULL);
130   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
131   g_return_if_fail (!G_HOOK_IN_CALL (hook));
132
133   if(hook_list->finalize_hook != NULL)
134       hook_list->finalize_hook (hook_list, hook);
135   g_slice_free1 (hook_list->hook_size, hook);
136 }
137
138 void
139 g_hook_destroy_link (GHookList *hook_list,
140                      GHook     *hook)
141 {
142   g_return_if_fail (hook_list != NULL);
143   g_return_if_fail (hook != NULL);
144
145   hook->flags &= ~G_HOOK_FLAG_ACTIVE;
146   if (hook->hook_id)
147     {
148       hook->hook_id = 0;
149       g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
150     }
151 }
152
153 gboolean
154 g_hook_destroy (GHookList   *hook_list,
155                 gulong       hook_id)
156 {
157   GHook *hook;
158   
159   g_return_val_if_fail (hook_list != NULL, FALSE);
160   g_return_val_if_fail (hook_id > 0, FALSE);
161   
162   hook = g_hook_get (hook_list, hook_id);
163   if (hook)
164     {
165       g_hook_destroy_link (hook_list, hook);
166       return TRUE;
167     }
168   
169   return FALSE;
170 }
171
172 void
173 g_hook_unref (GHookList *hook_list,
174               GHook     *hook)
175 {
176   g_return_if_fail (hook_list != NULL);
177   g_return_if_fail (hook != NULL);
178   g_return_if_fail (hook->ref_count > 0);
179   
180   hook->ref_count--;
181   if (!hook->ref_count)
182     {
183       g_return_if_fail (hook->hook_id == 0);
184       g_return_if_fail (!G_HOOK_IN_CALL (hook));
185
186       if (hook->prev)
187         hook->prev->next = hook->next;
188       else
189         hook_list->hooks = hook->next;
190       if (hook->next)
191         {
192           hook->next->prev = hook->prev;
193           hook->next = NULL;
194         }
195       hook->prev = NULL;
196
197       if (!hook_list->is_setup)
198         {
199           hook_list->is_setup = TRUE;
200           g_hook_free (hook_list, hook);
201           hook_list->is_setup = FALSE;
202       
203           if (!hook_list->hooks)
204             {
205               /* destroy hook_list->hook_memchunk */
206             }
207         }
208       else
209         g_hook_free (hook_list, hook);
210     }
211 }
212
213 GHook *
214 g_hook_ref (GHookList *hook_list,
215             GHook     *hook)
216 {
217   g_return_val_if_fail (hook_list != NULL, NULL);
218   g_return_val_if_fail (hook != NULL, NULL);
219   g_return_val_if_fail (hook->ref_count > 0, NULL);
220   
221   hook->ref_count++;
222
223   return hook;
224 }
225
226 void
227 g_hook_prepend (GHookList *hook_list,
228                 GHook     *hook)
229 {
230   g_return_if_fail (hook_list != NULL);
231   
232   g_hook_insert_before (hook_list, hook_list->hooks, hook);
233 }
234
235 void
236 g_hook_insert_before (GHookList *hook_list,
237                       GHook     *sibling,
238                       GHook     *hook)
239 {
240   g_return_if_fail (hook_list != NULL);
241   g_return_if_fail (hook_list->is_setup);
242   g_return_if_fail (hook != NULL);
243   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
244   g_return_if_fail (hook->ref_count == 0);
245   
246   hook->hook_id = hook_list->seq_id++;
247   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
248   
249   if (sibling)
250     {
251       if (sibling->prev)
252         {
253           hook->prev = sibling->prev;
254           hook->prev->next = hook;
255           hook->next = sibling;
256           sibling->prev = hook;
257         }
258       else
259         {
260           hook_list->hooks = hook;
261           hook->next = sibling;
262           sibling->prev = hook;
263         }
264     }
265   else
266     {
267       if (hook_list->hooks)
268         {
269           sibling = hook_list->hooks;
270           while (sibling->next)
271             sibling = sibling->next;
272           hook->prev = sibling;
273           sibling->next = hook;
274         }
275       else
276         hook_list->hooks = hook;
277     }
278 }
279
280 void
281 g_hook_list_invoke (GHookList *hook_list,
282                     gboolean   may_recurse)
283 {
284   GHook *hook;
285   
286   g_return_if_fail (hook_list != NULL);
287   g_return_if_fail (hook_list->is_setup);
288
289   hook = g_hook_first_valid (hook_list, may_recurse);
290   while (hook)
291     {
292       GHookFunc func;
293       gboolean was_in_call;
294       
295       func = (GHookFunc) hook->func;
296       
297       was_in_call = G_HOOK_IN_CALL (hook);
298       hook->flags |= G_HOOK_FLAG_IN_CALL;
299       func (hook->data);
300       if (!was_in_call)
301         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
302       
303       hook = g_hook_next_valid (hook_list, hook, may_recurse);
304     }
305 }
306
307 void
308 g_hook_list_invoke_check (GHookList *hook_list,
309                           gboolean   may_recurse)
310 {
311   GHook *hook;
312   
313   g_return_if_fail (hook_list != NULL);
314   g_return_if_fail (hook_list->is_setup);
315   
316   hook = g_hook_first_valid (hook_list, may_recurse);
317   while (hook)
318     {
319       GHookCheckFunc func;
320       gboolean was_in_call;
321       gboolean need_destroy;
322       
323       func = (GHookCheckFunc) hook->func;
324       
325       was_in_call = G_HOOK_IN_CALL (hook);
326       hook->flags |= G_HOOK_FLAG_IN_CALL;
327       need_destroy = !func (hook->data);
328       if (!was_in_call)
329         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
330       if (need_destroy)
331         g_hook_destroy_link (hook_list, hook);
332       
333       hook = g_hook_next_valid (hook_list, hook, may_recurse);
334     }
335 }
336
337 void
338 g_hook_list_marshal_check (GHookList           *hook_list,
339                            gboolean             may_recurse,
340                            GHookCheckMarshaller marshaller,
341                            gpointer             data)
342 {
343   GHook *hook;
344   
345   g_return_if_fail (hook_list != NULL);
346   g_return_if_fail (hook_list->is_setup);
347   g_return_if_fail (marshaller != NULL);
348   
349   hook = g_hook_first_valid (hook_list, may_recurse);
350   while (hook)
351     {
352       gboolean was_in_call;
353       gboolean need_destroy;
354       
355       was_in_call = G_HOOK_IN_CALL (hook);
356       hook->flags |= G_HOOK_FLAG_IN_CALL;
357       need_destroy = !marshaller (hook, data);
358       if (!was_in_call)
359         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
360       if (need_destroy)
361         g_hook_destroy_link (hook_list, hook);
362       
363       hook = g_hook_next_valid (hook_list, hook, may_recurse);
364     }
365 }
366
367 void
368 g_hook_list_marshal (GHookList               *hook_list,
369                      gboolean                 may_recurse,
370                      GHookMarshaller          marshaller,
371                      gpointer                 data)
372 {
373   GHook *hook;
374   
375   g_return_if_fail (hook_list != NULL);
376   g_return_if_fail (hook_list->is_setup);
377   g_return_if_fail (marshaller != NULL);
378   
379   hook = g_hook_first_valid (hook_list, may_recurse);
380   while (hook)
381     {
382       gboolean was_in_call;
383       
384       was_in_call = G_HOOK_IN_CALL (hook);
385       hook->flags |= G_HOOK_FLAG_IN_CALL;
386       marshaller (hook, data);
387       if (!was_in_call)
388         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
389       
390       hook = g_hook_next_valid (hook_list, hook, may_recurse);
391     }
392 }
393
394 GHook*
395 g_hook_first_valid (GHookList *hook_list,
396                     gboolean   may_be_in_call)
397 {
398   g_return_val_if_fail (hook_list != NULL, NULL);
399   
400   if (hook_list->is_setup)
401     {
402       GHook *hook;
403       
404       hook = hook_list->hooks;
405       if (hook)
406         {
407           g_hook_ref (hook_list, hook);
408           if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
409             return hook;
410           else
411             return g_hook_next_valid (hook_list, hook, may_be_in_call);
412         }
413     }
414   
415   return NULL;
416 }
417
418 GHook*
419 g_hook_next_valid (GHookList *hook_list,
420                    GHook     *hook,
421                    gboolean   may_be_in_call)
422 {
423   GHook *ohook = hook;
424
425   g_return_val_if_fail (hook_list != NULL, NULL);
426
427   if (!hook)
428     return NULL;
429   
430   hook = hook->next;
431   while (hook)
432     {
433       if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
434         {
435           g_hook_ref (hook_list, hook);
436           g_hook_unref (hook_list, ohook);
437           
438           return hook;
439         }
440       hook = hook->next;
441     }
442   g_hook_unref (hook_list, ohook);
443
444   return NULL;
445 }
446
447 GHook*
448 g_hook_get (GHookList *hook_list,
449             gulong     hook_id)
450 {
451   GHook *hook;
452   
453   g_return_val_if_fail (hook_list != NULL, NULL);
454   g_return_val_if_fail (hook_id > 0, NULL);
455   
456   hook = hook_list->hooks;
457   while (hook)
458     {
459       if (hook->hook_id == hook_id)
460         return hook;
461       hook = hook->next;
462     }
463   
464   return NULL;
465 }
466
467 GHook*
468 g_hook_find (GHookList    *hook_list,
469              gboolean      need_valids,
470              GHookFindFunc func,
471              gpointer      data)
472 {
473   GHook *hook;
474   
475   g_return_val_if_fail (hook_list != NULL, NULL);
476   g_return_val_if_fail (func != NULL, NULL);
477   
478   hook = hook_list->hooks;
479   while (hook)
480     {
481       GHook *tmp;
482
483       /* test only non-destroyed hooks */
484       if (!hook->hook_id)
485         {
486           hook = hook->next;
487           continue;
488         }
489       
490       g_hook_ref (hook_list, hook);
491       
492       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
493         {
494           g_hook_unref (hook_list, hook);
495           
496           return hook;
497         }
498
499       tmp = hook->next;
500       g_hook_unref (hook_list, hook);
501       hook = tmp;
502     }
503   
504   return NULL;
505 }
506
507 GHook*
508 g_hook_find_data (GHookList *hook_list,
509                   gboolean   need_valids,
510                   gpointer   data)
511 {
512   GHook *hook;
513   
514   g_return_val_if_fail (hook_list != NULL, NULL);
515   
516   hook = hook_list->hooks;
517   while (hook)
518     {
519       /* test only non-destroyed hooks */
520       if (hook->data == data &&
521           hook->hook_id &&
522           (!need_valids || G_HOOK_ACTIVE (hook)))
523         return hook;
524
525       hook = hook->next;
526     }
527   
528   return NULL;
529 }
530
531 GHook*
532 g_hook_find_func (GHookList *hook_list,
533                   gboolean   need_valids,
534                   gpointer   func)
535 {
536   GHook *hook;
537   
538   g_return_val_if_fail (hook_list != NULL, NULL);
539   g_return_val_if_fail (func != NULL, NULL);
540   
541   hook = hook_list->hooks;
542   while (hook)
543     {
544       /* test only non-destroyed hooks */
545       if (hook->func == func &&
546           hook->hook_id &&
547           (!need_valids || G_HOOK_ACTIVE (hook)))
548         return hook;
549
550       hook = hook->next;
551     }
552   
553   return NULL;
554 }
555
556 GHook*
557 g_hook_find_func_data (GHookList *hook_list,
558                        gboolean   need_valids,
559                        gpointer   func,
560                        gpointer   data)
561 {
562   GHook *hook;
563   
564   g_return_val_if_fail (hook_list != NULL, NULL);
565   g_return_val_if_fail (func != NULL, NULL);
566   
567   hook = hook_list->hooks;
568   while (hook)
569     {
570       /* test only non-destroyed hooks */
571       if (hook->data == data &&
572           hook->func == func &&
573           hook->hook_id &&
574           (!need_valids || G_HOOK_ACTIVE (hook)))
575         return hook;
576
577       hook = hook->next;
578     }
579   
580   return NULL;
581 }
582
583 void
584 g_hook_insert_sorted (GHookList       *hook_list,
585                       GHook           *hook,
586                       GHookCompareFunc func)
587 {
588   GHook *sibling;
589   
590   g_return_if_fail (hook_list != NULL);
591   g_return_if_fail (hook_list->is_setup);
592   g_return_if_fail (hook != NULL);
593   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
594   g_return_if_fail (hook->func != NULL);
595   g_return_if_fail (func != NULL);
596
597   /* first non-destroyed hook */
598   sibling = hook_list->hooks;
599   while (sibling && !sibling->hook_id)
600     sibling = sibling->next;
601   
602   while (sibling)
603     {
604       GHook *tmp;
605       
606       g_hook_ref (hook_list, sibling);
607       if (func (hook, sibling) <= 0 && sibling->hook_id)
608         {
609           g_hook_unref (hook_list, sibling);
610           break;
611         }
612
613       /* next non-destroyed hook */
614       tmp = sibling->next;
615       while (tmp && !tmp->hook_id)
616         tmp = tmp->next;
617
618       g_hook_unref (hook_list, sibling);
619       sibling = tmp;
620     }
621   
622   g_hook_insert_before (hook_list, sibling, hook);
623 }
624
625 gint
626 g_hook_compare_ids (GHook *new_hook,
627                     GHook *sibling)
628 {
629   if (new_hook->hook_id < sibling->hook_id)
630     return -1;
631   else if (new_hook->hook_id > sibling->hook_id)
632     return 1;
633   
634   return 0;
635 }
636
637 #define __G_HOOK_C__
638 #include "galiasdef.c"