applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.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        "glib.h"
35
36
37 /* --- defines --- */
38 #define G_HOOKS_PREALLOC        (16)
39
40
41 /* --- functions --- */
42 void
43 g_hook_list_init (GHookList *hook_list,
44                   guint      hook_size)
45 {
46   g_return_if_fail (hook_list != NULL);
47   g_return_if_fail (hook_size >= sizeof (GHook));
48   
49   hook_list->seq_id = 1;
50   hook_list->hook_size = hook_size;
51   hook_list->is_setup = TRUE;
52   hook_list->hooks = NULL;
53   hook_list->hook_memchunk = g_mem_chunk_new ("GHook Memchunk",
54                                               hook_size,
55                                               hook_size * G_HOOKS_PREALLOC,
56                                               G_ALLOC_AND_FREE);
57   hook_list->hook_free = NULL;
58   hook_list->hook_destroy = NULL;
59 }
60
61 void
62 g_hook_list_clear (GHookList *hook_list)
63 {
64   g_return_if_fail (hook_list != NULL);
65   
66   if (hook_list->is_setup)
67     {
68       GHook *hook;
69       
70       hook_list->is_setup = FALSE;
71       
72       hook = hook_list->hooks;
73       if (!hook)
74         {
75           g_mem_chunk_destroy (hook_list->hook_memchunk);
76           hook_list->hook_memchunk = NULL;
77         }
78       else
79         do
80           {
81             GHook *tmp;
82             
83             g_hook_ref (hook_list, hook);
84             g_hook_destroy_link (hook_list, hook);
85             tmp = hook->next;
86             g_hook_unref (hook_list, hook);
87             hook = tmp;
88           }
89         while (hook);
90     }
91 }
92
93 GHook*
94 g_hook_alloc (GHookList *hook_list)
95 {
96   GHook *hook;
97   
98   g_return_val_if_fail (hook_list != NULL, NULL);
99   g_return_val_if_fail (hook_list->is_setup, NULL);
100   
101   hook = g_chunk_new0 (GHook, hook_list->hook_memchunk);
102   hook->data = NULL;
103   hook->next = NULL;
104   hook->prev = NULL;
105   hook->flags = G_HOOK_FLAG_ACTIVE;
106   hook->ref_count = 0;
107   hook->hook_id = 0;
108   hook->func = NULL;
109   hook->destroy = NULL;
110   
111   return hook;
112 }
113
114 void
115 g_hook_free (GHookList *hook_list,
116              GHook     *hook)
117 {
118   g_return_if_fail (hook_list != NULL);
119   g_return_if_fail (hook_list->is_setup);
120   g_return_if_fail (hook != NULL);
121   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
122
123   if (hook_list->hook_free)
124     hook_list->hook_free (hook_list, hook);
125   
126   g_chunk_free (hook, hook_list->hook_memchunk);
127 }
128
129 void
130 g_hook_destroy_link (GHookList *hook_list,
131                      GHook     *hook)
132 {
133   g_return_if_fail (hook_list != NULL);
134   g_return_if_fail (hook != NULL);
135   
136   if (hook->hook_id)
137     {
138       hook->hook_id = 0;
139       hook->flags &= ~G_HOOK_FLAG_ACTIVE;
140       if (hook_list->hook_destroy)
141         {
142           if (hook_list->hook_destroy != G_HOOK_DEFERRED_DESTROY)
143             hook_list->hook_destroy (hook_list, hook);
144         }
145       else if (hook->destroy)
146         {
147           hook->destroy (hook->data);
148           hook->data = NULL;
149           hook->func = NULL;
150           hook->destroy = NULL;
151         }
152       g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
153     }
154 }
155
156 gboolean
157 g_hook_destroy (GHookList   *hook_list,
158                 guint        hook_id)
159 {
160   GHook *hook;
161   
162   g_return_val_if_fail (hook_list != NULL, FALSE);
163   g_return_val_if_fail (hook_id > 0, FALSE);
164   
165   hook = g_hook_get (hook_list, hook_id);
166   if (hook)
167     {
168       g_hook_destroy_link (hook_list, hook);
169       return TRUE;
170     }
171   
172   return FALSE;
173 }
174
175 void
176 g_hook_unref (GHookList *hook_list,
177               GHook     *hook)
178 {
179   g_return_if_fail (hook_list != NULL);
180   g_return_if_fail (hook_list->hook_memchunk != NULL);
181   g_return_if_fail (hook != NULL);
182   g_return_if_fail (hook->ref_count > 0);
183   
184   hook->ref_count--;
185   if (!hook->ref_count)
186     {
187       g_return_if_fail (hook->hook_id == 0);
188       g_return_if_fail (!G_HOOK_IN_CALL (hook));
189       
190       if (hook->prev)
191         hook->prev->next = hook->next;
192       else
193         hook_list->hooks = hook->next;
194       if (hook->next)
195         {
196           hook->next->prev = hook->prev;
197           hook->next = NULL;
198         }
199       hook->prev = NULL;
200
201       if (!hook_list->is_setup)
202         {
203           hook_list->is_setup = TRUE;
204           g_hook_free (hook_list, hook);
205           hook_list->is_setup = FALSE;
206       
207           if (!hook_list->hooks)
208             {
209               g_mem_chunk_destroy (hook_list->hook_memchunk);
210               hook_list->hook_memchunk = NULL;
211             }
212         }
213       else
214         g_hook_free (hook_list, hook);
215     }
216 }
217
218 void
219 g_hook_ref (GHookList *hook_list,
220             GHook     *hook)
221 {
222   g_return_if_fail (hook_list != NULL);
223   g_return_if_fail (hook != NULL);
224   g_return_if_fail (hook->ref_count > 0);
225   
226   hook->ref_count++;
227 }
228
229 void
230 g_hook_prepend (GHookList *hook_list,
231                 GHook     *hook)
232 {
233   g_return_if_fail (hook_list != NULL);
234   
235   g_hook_insert_before (hook_list, hook_list->hooks, hook);
236 }
237
238 void
239 g_hook_insert_before (GHookList *hook_list,
240                       GHook     *sibling,
241                       GHook     *hook)
242 {
243   g_return_if_fail (hook_list != NULL);
244   g_return_if_fail (hook_list->is_setup);
245   g_return_if_fail (hook != NULL);
246   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
247   g_return_if_fail (hook->func != NULL);
248   
249   hook->hook_id = hook_list->seq_id++;
250   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
251   
252   if (sibling)
253     {
254       if (sibling->prev)
255         {
256           hook->prev = sibling->prev;
257           hook->prev->next = hook;
258           hook->next = sibling;
259           sibling->prev = hook;
260         }
261       else
262         {
263           hook_list->hooks = hook;
264           hook->next = sibling;
265           sibling->prev = hook;
266         }
267     }
268   else
269     {
270       if (hook_list->hooks)
271         {
272           sibling = hook_list->hooks;
273           while (sibling->next)
274             sibling = sibling->next;
275           hook->prev = sibling;
276           sibling->next = hook;
277         }
278       else
279         hook_list->hooks = hook;
280     }
281 }
282
283 void
284 g_hook_list_invoke (GHookList *hook_list,
285                     gboolean   may_recurse)
286 {
287   GHook *hook;
288   
289   g_return_if_fail (hook_list != NULL);
290   g_return_if_fail (hook_list->is_setup);
291
292   hook = g_hook_first_valid (hook_list, may_recurse);
293   while (hook)
294     {
295       GHookFunc func;
296       gboolean was_in_call;
297       
298       func = (GHookFunc) hook->func;
299       
300       was_in_call = G_HOOK_IN_CALL (hook);
301       hook->flags |= G_HOOK_FLAG_IN_CALL;
302       func (hook->data);
303       if (!was_in_call)
304         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
305       
306       hook = g_hook_next_valid (hook_list, hook, may_recurse);
307     }
308 }
309
310 void
311 g_hook_list_invoke_check (GHookList *hook_list,
312                           gboolean   may_recurse)
313 {
314   GHook *hook;
315   
316   g_return_if_fail (hook_list != NULL);
317   g_return_if_fail (hook_list->is_setup);
318   
319   hook = g_hook_first_valid (hook_list, may_recurse);
320   while (hook)
321     {
322       GHookCheckFunc func;
323       gboolean was_in_call;
324       gboolean need_destroy;
325       
326       func = (GHookCheckFunc) hook->func;
327       
328       was_in_call = G_HOOK_IN_CALL (hook);
329       hook->flags |= G_HOOK_FLAG_IN_CALL;
330       need_destroy = !func (hook->data);
331       if (!was_in_call)
332         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
333       if (need_destroy)
334         g_hook_destroy_link (hook_list, hook);
335       
336       hook = g_hook_next_valid (hook_list, hook, may_recurse);
337     }
338 }
339
340 void
341 g_hook_list_marshal_check (GHookList           *hook_list,
342                            gboolean             may_recurse,
343                            GHookCheckMarshaller marshaller,
344                            gpointer             data)
345 {
346   GHook *hook;
347   
348   g_return_if_fail (hook_list != NULL);
349   g_return_if_fail (hook_list->is_setup);
350   g_return_if_fail (marshaller != NULL);
351   
352   hook = g_hook_first_valid (hook_list, may_recurse);
353   while (hook)
354     {
355       gboolean was_in_call;
356       gboolean need_destroy;
357       
358       was_in_call = G_HOOK_IN_CALL (hook);
359       hook->flags |= G_HOOK_FLAG_IN_CALL;
360       need_destroy = !marshaller (hook, data);
361       if (!was_in_call)
362         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
363       if (need_destroy)
364         g_hook_destroy_link (hook_list, hook);
365       
366       hook = g_hook_next_valid (hook_list, hook, may_recurse);
367     }
368 }
369
370 void
371 g_hook_list_marshal (GHookList               *hook_list,
372                      gboolean                 may_recurse,
373                      GHookMarshaller          marshaller,
374                      gpointer                 data)
375 {
376   GHook *hook;
377   
378   g_return_if_fail (hook_list != NULL);
379   g_return_if_fail (hook_list->is_setup);
380   g_return_if_fail (marshaller != NULL);
381   
382   hook = g_hook_first_valid (hook_list, may_recurse);
383   while (hook)
384     {
385       gboolean was_in_call;
386       
387       was_in_call = G_HOOK_IN_CALL (hook);
388       hook->flags |= G_HOOK_FLAG_IN_CALL;
389       marshaller (hook, data);
390       if (!was_in_call)
391         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
392       
393       hook = g_hook_next_valid (hook_list, hook, may_recurse);
394     }
395 }
396
397 GHook*
398 g_hook_first_valid (GHookList *hook_list,
399                     gboolean   may_be_in_call)
400 {
401   g_return_val_if_fail (hook_list != NULL, NULL);
402   
403   if (hook_list->is_setup)
404     {
405       GHook *hook;
406       
407       hook = hook_list->hooks;
408       if (hook)
409         {
410           g_hook_ref (hook_list, hook);
411           if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
412             return hook;
413           else
414             return g_hook_next_valid (hook_list, hook, may_be_in_call);
415         }
416     }
417   
418   return NULL;
419 }
420
421 GHook*
422 g_hook_next_valid (GHookList *hook_list,
423                    GHook     *hook,
424                    gboolean   may_be_in_call)
425 {
426   GHook *ohook = hook;
427
428   g_return_val_if_fail (hook_list != NULL, NULL);
429
430   if (!hook)
431     return NULL;
432   
433   hook = hook->next;
434   while (hook)
435     {
436       if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
437         {
438           g_hook_ref (hook_list, hook);
439           g_hook_unref (hook_list, ohook);
440           
441           return hook;
442         }
443       hook = hook->next;
444     }
445   g_hook_unref (hook_list, ohook);
446
447   return NULL;
448 }
449
450 GHook*
451 g_hook_get (GHookList *hook_list,
452             guint      hook_id)
453 {
454   GHook *hook;
455   
456   g_return_val_if_fail (hook_list != NULL, NULL);
457   g_return_val_if_fail (hook_id > 0, NULL);
458   
459   hook = hook_list->hooks;
460   while (hook)
461     {
462       if (hook->hook_id == hook_id)
463         return hook;
464       hook = hook->next;
465     }
466   
467   return NULL;
468 }
469
470 GHook*
471 g_hook_find (GHookList    *hook_list,
472              gboolean      need_valids,
473              GHookFindFunc func,
474              gpointer      data)
475 {
476   GHook *hook;
477   
478   g_return_val_if_fail (hook_list != NULL, NULL);
479   g_return_val_if_fail (func != NULL, NULL);
480   
481   hook = hook_list->hooks;
482   while (hook)
483     {
484       GHook *tmp;
485
486       /* test only non-destroyed hooks */
487       if (!hook->hook_id)
488         {
489           hook = hook->next;
490           continue;
491         }
492       
493       g_hook_ref (hook_list, hook);
494       
495       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
496         {
497           g_hook_unref (hook_list, hook);
498           
499           return hook;
500         }
501
502       tmp = hook->next;
503       g_hook_unref (hook_list, hook);
504       hook = tmp;
505     }
506   
507   return NULL;
508 }
509
510 GHook*
511 g_hook_find_data (GHookList *hook_list,
512                   gboolean   need_valids,
513                   gpointer   data)
514 {
515   GHook *hook;
516   
517   g_return_val_if_fail (hook_list != NULL, NULL);
518   
519   hook = hook_list->hooks;
520   while (hook)
521     {
522       /* test only non-destroyed hooks */
523       if (hook->data == data &&
524           hook->hook_id &&
525           (!need_valids || G_HOOK_ACTIVE (hook)))
526         return hook;
527
528       hook = hook->next;
529     }
530   
531   return NULL;
532 }
533
534 GHook*
535 g_hook_find_func (GHookList *hook_list,
536                   gboolean   need_valids,
537                   gpointer   func)
538 {
539   GHook *hook;
540   
541   g_return_val_if_fail (hook_list != NULL, NULL);
542   g_return_val_if_fail (func != NULL, NULL);
543   
544   hook = hook_list->hooks;
545   while (hook)
546     {
547       /* test only non-destroyed hooks */
548       if (hook->func == func &&
549           hook->hook_id &&
550           (!need_valids || G_HOOK_ACTIVE (hook)))
551         return hook;
552
553       hook = hook->next;
554     }
555   
556   return NULL;
557 }
558
559 GHook*
560 g_hook_find_func_data (GHookList *hook_list,
561                        gboolean   need_valids,
562                        gpointer   func,
563                        gpointer   data)
564 {
565   GHook *hook;
566   
567   g_return_val_if_fail (hook_list != NULL, NULL);
568   g_return_val_if_fail (func != NULL, NULL);
569   
570   hook = hook_list->hooks;
571   while (hook)
572     {
573       /* test only non-destroyed hooks */
574       if (hook->data == data &&
575           hook->func == func &&
576           hook->hook_id &&
577           (!need_valids || G_HOOK_ACTIVE (hook)))
578         return hook;
579
580       hook = hook->next;
581     }
582   
583   return NULL;
584 }
585
586 void
587 g_hook_insert_sorted (GHookList       *hook_list,
588                       GHook           *hook,
589                       GHookCompareFunc func)
590 {
591   GHook *sibling;
592   
593   g_return_if_fail (hook_list != NULL);
594   g_return_if_fail (hook_list->is_setup);
595   g_return_if_fail (hook != NULL);
596   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
597   g_return_if_fail (hook->func != NULL);
598   g_return_if_fail (func != NULL);
599
600   /* first non-destroyed hook */
601   sibling = hook_list->hooks;
602   while (sibling && !sibling->hook_id)
603     sibling = sibling->next;
604   
605   while (sibling)
606     {
607       GHook *tmp;
608       
609       g_hook_ref (hook_list, sibling);
610       if (func (hook, sibling) <= 0 && sibling->hook_id)
611         {
612           g_hook_unref (hook_list, sibling);
613           break;
614         }
615
616       /* next non-destroyed hook */
617       tmp = sibling->next;
618       while (tmp && !tmp->hook_id)
619         tmp = tmp->next;
620
621       g_hook_unref (hook_list, sibling);
622       sibling = tmp;
623     }
624   
625   g_hook_insert_before (hook_list, sibling, hook);
626 }
627
628 gint
629 g_hook_compare_ids (GHook *new_hook,
630                     GHook *sibling)
631 {
632   return ((glong) new_hook->hook_id) - ((glong) sibling->hook_id);
633 }