moved old ChangeLog to ChangeLog.pre-1-2, and started new one.
[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 Library 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  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library 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-1999.  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 != NULL);
181   g_return_if_fail (hook->ref_count > 0);
182   
183   hook->ref_count--;
184   if (!hook->ref_count)
185     {
186       g_return_if_fail (hook->hook_id == 0);
187       g_return_if_fail (!G_HOOK_IN_CALL (hook));
188       
189       if (hook->prev)
190         hook->prev->next = hook->next;
191       else
192         hook_list->hooks = hook->next;
193       if (hook->next)
194         {
195           hook->next->prev = hook->prev;
196           hook->next = NULL;
197         }
198       hook->prev = NULL;
199       
200       g_hook_free (hook_list, hook);
201       
202       if (!hook_list->hooks &&
203           !hook_list->is_setup)
204         {
205           g_mem_chunk_destroy (hook_list->hook_memchunk);
206           hook_list->hook_memchunk = NULL;
207         }
208     }
209 }
210
211 void
212 g_hook_ref (GHookList *hook_list,
213             GHook     *hook)
214 {
215   g_return_if_fail (hook_list != NULL);
216   g_return_if_fail (hook != NULL);
217   g_return_if_fail (hook->ref_count > 0);
218   
219   hook->ref_count++;
220 }
221
222 void
223 g_hook_prepend (GHookList *hook_list,
224                 GHook     *hook)
225 {
226   g_return_if_fail (hook_list != NULL);
227   
228   g_hook_insert_before (hook_list, hook_list->hooks, hook);
229 }
230
231 void
232 g_hook_insert_before (GHookList *hook_list,
233                       GHook     *sibling,
234                       GHook     *hook)
235 {
236   g_return_if_fail (hook_list != NULL);
237   g_return_if_fail (hook_list->is_setup);
238   g_return_if_fail (hook != NULL);
239   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
240   g_return_if_fail (hook->func != NULL);
241   
242   hook->hook_id = hook_list->seq_id++;
243   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
244   
245   if (sibling)
246     {
247       if (sibling->prev)
248         {
249           hook->prev = sibling->prev;
250           hook->prev->next = hook;
251           hook->next = sibling;
252           sibling->prev = hook;
253         }
254       else
255         {
256           hook_list->hooks = hook;
257           hook->next = sibling;
258           sibling->prev = hook;
259         }
260     }
261   else
262     {
263       if (hook_list->hooks)
264         {
265           sibling = hook_list->hooks;
266           while (sibling->next)
267             sibling = sibling->next;
268           hook->prev = sibling;
269           sibling->next = hook;
270         }
271       else
272         hook_list->hooks = hook;
273     }
274 }
275
276 void
277 g_hook_list_invoke (GHookList *hook_list,
278                     gboolean   may_recurse)
279 {
280   GHook *hook;
281   
282   g_return_if_fail (hook_list != NULL);
283   g_return_if_fail (hook_list->is_setup);
284
285   hook = g_hook_first_valid (hook_list, may_recurse);
286   while (hook)
287     {
288       GHookFunc func;
289       gboolean was_in_call;
290       
291       func = (GHookFunc) hook->func;
292       
293       was_in_call = G_HOOK_IN_CALL (hook);
294       hook->flags |= G_HOOK_FLAG_IN_CALL;
295       func (hook->data);
296       if (!was_in_call)
297         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
298       
299       hook = g_hook_next_valid (hook_list, hook, may_recurse);
300     }
301 }
302
303 void
304 g_hook_list_invoke_check (GHookList *hook_list,
305                           gboolean   may_recurse)
306 {
307   GHook *hook;
308   
309   g_return_if_fail (hook_list != NULL);
310   g_return_if_fail (hook_list->is_setup);
311   
312   hook = g_hook_first_valid (hook_list, may_recurse);
313   while (hook)
314     {
315       GHookCheckFunc func;
316       gboolean was_in_call;
317       gboolean need_destroy;
318       
319       func = (GHookCheckFunc) hook->func;
320       
321       was_in_call = G_HOOK_IN_CALL (hook);
322       hook->flags |= G_HOOK_FLAG_IN_CALL;
323       need_destroy = !func (hook->data);
324       if (!was_in_call)
325         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
326       if (need_destroy)
327         g_hook_destroy_link (hook_list, hook);
328       
329       hook = g_hook_next_valid (hook_list, hook, may_recurse);
330     }
331 }
332
333 void
334 g_hook_list_marshal_check (GHookList           *hook_list,
335                            gboolean             may_recurse,
336                            GHookCheckMarshaller marshaller,
337                            gpointer             data)
338 {
339   GHook *hook;
340   
341   g_return_if_fail (hook_list != NULL);
342   g_return_if_fail (hook_list->is_setup);
343   g_return_if_fail (marshaller != NULL);
344   
345   hook = g_hook_first_valid (hook_list, may_recurse);
346   while (hook)
347     {
348       gboolean was_in_call;
349       gboolean need_destroy;
350       
351       was_in_call = G_HOOK_IN_CALL (hook);
352       hook->flags |= G_HOOK_FLAG_IN_CALL;
353       need_destroy = !marshaller (hook, data);
354       if (!was_in_call)
355         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
356       if (need_destroy)
357         g_hook_destroy_link (hook_list, hook);
358       
359       hook = g_hook_next_valid (hook_list, hook, may_recurse);
360     }
361 }
362
363 void
364 g_hook_list_marshal (GHookList               *hook_list,
365                      gboolean                 may_recurse,
366                      GHookMarshaller          marshaller,
367                      gpointer                 data)
368 {
369   GHook *hook;
370   
371   g_return_if_fail (hook_list != NULL);
372   g_return_if_fail (hook_list->is_setup);
373   g_return_if_fail (marshaller != NULL);
374   
375   hook = g_hook_first_valid (hook_list, may_recurse);
376   while (hook)
377     {
378       gboolean was_in_call;
379       
380       was_in_call = G_HOOK_IN_CALL (hook);
381       hook->flags |= G_HOOK_FLAG_IN_CALL;
382       marshaller (hook, data);
383       if (!was_in_call)
384         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
385       
386       hook = g_hook_next_valid (hook_list, hook, may_recurse);
387     }
388 }
389
390 GHook*
391 g_hook_first_valid (GHookList *hook_list,
392                     gboolean   may_be_in_call)
393 {
394   g_return_val_if_fail (hook_list != NULL, NULL);
395   
396   if (hook_list->is_setup)
397     {
398       GHook *hook;
399       
400       hook = hook_list->hooks;
401       if (hook)
402         {
403           g_hook_ref (hook_list, hook);
404           if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
405             return hook;
406           else
407             return g_hook_next_valid (hook_list, hook, may_be_in_call);
408         }
409     }
410   
411   return NULL;
412 }
413
414 GHook*
415 g_hook_next_valid (GHookList *hook_list,
416                    GHook     *hook,
417                    gboolean   may_be_in_call)
418 {
419   GHook *ohook = hook;
420
421   g_return_val_if_fail (hook_list != NULL, NULL);
422
423   if (!hook)
424     return NULL;
425   
426   hook = hook->next;
427   while (hook)
428     {
429       if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
430         {
431           g_hook_ref (hook_list, hook);
432           g_hook_unref (hook_list, ohook);
433           
434           return hook;
435         }
436       hook = hook->next;
437     }
438   g_hook_unref (hook_list, ohook);
439
440   return NULL;
441 }
442
443 GHook*
444 g_hook_get (GHookList *hook_list,
445             guint      hook_id)
446 {
447   GHook *hook;
448   
449   g_return_val_if_fail (hook_list != NULL, NULL);
450   g_return_val_if_fail (hook_id > 0, NULL);
451   
452   hook = hook_list->hooks;
453   while (hook)
454     {
455       if (hook->hook_id == hook_id)
456         return hook;
457       hook = hook->next;
458     }
459   
460   return NULL;
461 }
462
463 GHook*
464 g_hook_find (GHookList    *hook_list,
465              gboolean      need_valids,
466              GHookFindFunc func,
467              gpointer      data)
468 {
469   GHook *hook;
470   
471   g_return_val_if_fail (hook_list != NULL, NULL);
472   g_return_val_if_fail (func != NULL, NULL);
473   
474   hook = hook_list->hooks;
475   while (hook)
476     {
477       GHook *tmp;
478
479       /* test only non-destroyed hooks */
480       if (!hook->hook_id)
481         {
482           hook = hook->next;
483           continue;
484         }
485       
486       g_hook_ref (hook_list, hook);
487       
488       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
489         {
490           g_hook_unref (hook_list, hook);
491           
492           return hook;
493         }
494
495       tmp = hook->next;
496       g_hook_unref (hook_list, hook);
497       hook = tmp;
498     }
499   
500   return NULL;
501 }
502
503 GHook*
504 g_hook_find_data (GHookList *hook_list,
505                   gboolean   need_valids,
506                   gpointer   data)
507 {
508   GHook *hook;
509   
510   g_return_val_if_fail (hook_list != NULL, NULL);
511   
512   hook = hook_list->hooks;
513   while (hook)
514     {
515       /* test only non-destroyed hooks */
516       if (hook->data == data &&
517           hook->hook_id &&
518           (!need_valids || G_HOOK_ACTIVE (hook)))
519         return hook;
520
521       hook = hook->next;
522     }
523   
524   return NULL;
525 }
526
527 GHook*
528 g_hook_find_func (GHookList *hook_list,
529                   gboolean   need_valids,
530                   gpointer   func)
531 {
532   GHook *hook;
533   
534   g_return_val_if_fail (hook_list != NULL, NULL);
535   g_return_val_if_fail (func != NULL, NULL);
536   
537   hook = hook_list->hooks;
538   while (hook)
539     {
540       /* test only non-destroyed hooks */
541       if (hook->func == func &&
542           hook->hook_id &&
543           (!need_valids || G_HOOK_ACTIVE (hook)))
544         return hook;
545
546       hook = hook->next;
547     }
548   
549   return NULL;
550 }
551
552 GHook*
553 g_hook_find_func_data (GHookList *hook_list,
554                        gboolean   need_valids,
555                        gpointer   func,
556                        gpointer   data)
557 {
558   GHook *hook;
559   
560   g_return_val_if_fail (hook_list != NULL, NULL);
561   g_return_val_if_fail (func != NULL, NULL);
562   
563   hook = hook_list->hooks;
564   while (hook)
565     {
566       /* test only non-destroyed hooks */
567       if (hook->data == data &&
568           hook->func == func &&
569           hook->hook_id &&
570           (!need_valids || G_HOOK_ACTIVE (hook)))
571         return hook;
572
573       hook = hook->next;
574     }
575   
576   return NULL;
577 }
578
579 void
580 g_hook_insert_sorted (GHookList       *hook_list,
581                       GHook           *hook,
582                       GHookCompareFunc func)
583 {
584   GHook *sibling;
585   
586   g_return_if_fail (hook_list != NULL);
587   g_return_if_fail (hook_list->is_setup);
588   g_return_if_fail (hook != NULL);
589   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
590   g_return_if_fail (hook->func != NULL);
591   g_return_if_fail (func != NULL);
592
593   /* first non-destroyed hook */
594   sibling = hook_list->hooks;
595   while (sibling && !sibling->hook_id)
596     sibling = sibling->next;
597   
598   while (sibling)
599     {
600       GHook *tmp;
601       
602       g_hook_ref (hook_list, sibling);
603       if (func (hook, sibling) <= 0 && sibling->hook_id)
604         {
605           g_hook_unref (hook_list, sibling);
606           break;
607         }
608
609       /* next non-destroyed hook */
610       tmp = sibling->next;
611       while (tmp && !tmp->hook_id)
612         tmp = tmp->next;
613
614       g_hook_unref (hook_list, sibling);
615       sibling = tmp;
616     }
617   
618   g_hook_insert_before (hook_list, sibling, hook);
619 }
620
621 gint
622 g_hook_compare_ids (GHook *new_hook,
623                     GHook *sibling)
624 {
625   return ((glong) new_hook->hook_id) - ((glong) sibling->hook_id);
626 }