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