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