1 /* abstract base class for containers
2 */
3
4 /*
5
6 Copyright (C) 1991-2003 The National Gallery
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 */
23
24 /*
25
26 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27
28 */
29
30 /*
31 #define DEBUG_SANITY
32 #define DEBUG_VERBOSE
33 #define DEBUG
34 */
35
36 #include "ip.h"
37
38 /* Our signals.
39 */
40 enum {
41 SIG_POS_CHANGED, /* Member has moved */
42 SIG_CHILD_ADD, /* iContainer is about to gain a child */
43 SIG_CHILD_REMOVE, /* iContainer is about to loose a child */
44 SIG_CURRENT, /* Make child current of parent */
45 SIG_CHILD_DETACH, /* Used as a pair to do reparent */
46 SIG_CHILD_ATTACH,
47 SIG_LAST
48 };
49
50 static iObjectClass *parent_class = NULL;
51
52 static guint icontainer_signals[SIG_LAST] = { 0 };
53
54 int
icontainer_get_n_children(iContainer * icontainer)55 icontainer_get_n_children( iContainer *icontainer )
56 {
57 return( g_slist_length( icontainer->children ) );
58 }
59
60 iContainer *
icontainer_get_nth_child(iContainer * icontainer,int n)61 icontainer_get_nth_child( iContainer *icontainer, int n )
62 {
63 return( ICONTAINER( g_slist_nth_data( icontainer->children, n ) ) );
64 }
65
66 GSList *
icontainer_get_children(iContainer * icontainer)67 icontainer_get_children( iContainer *icontainer )
68 {
69 return( g_slist_copy( icontainer->children ) );
70 }
71
72 void *
icontainer_map(iContainer * icontainer,icontainer_map_fn fn,void * a,void * b)73 icontainer_map( iContainer *icontainer, icontainer_map_fn fn, void *a, void *b )
74 {
75 return( slist_map2( icontainer->children, (SListMap2Fn) fn, a, b ) );
76 }
77
78 void *
icontainer_map3(iContainer * icontainer,icontainer_map3_fn fn,void * a,void * b,void * c)79 icontainer_map3( iContainer *icontainer,
80 icontainer_map3_fn fn, void *a, void *b, void *c )
81 {
82 return( slist_map3( icontainer->children, (SListMap3Fn) fn, a, b, c ) );
83 }
84
85 void *
icontainer_map4(iContainer * icontainer,icontainer_map4_fn fn,void * a,void * b,void * c,void * d)86 icontainer_map4( iContainer *icontainer,
87 icontainer_map4_fn fn, void *a, void *b, void *c, void *d )
88 {
89 return( slist_map4( icontainer->children,
90 (SListMap4Fn) fn, a, b, c, d ) );
91 }
92
93 void *
icontainer_map5(iContainer * icontainer,icontainer_map5_fn fn,void * a,void * b,void * c,void * d,void * e)94 icontainer_map5( iContainer *icontainer,
95 icontainer_map5_fn fn, void *a, void *b, void *c, void *d, void *e )
96 {
97 return( slist_map5( icontainer->children,
98 (SListMap5Fn) fn, a, b, c, d, e ) );
99 }
100
101 /* Map in reverse order.
102 */
103 void *
icontainer_map_rev(iContainer * icontainer,icontainer_map_fn fn,void * a,void * b)104 icontainer_map_rev( iContainer *icontainer,
105 icontainer_map_fn fn, void *a, void *b )
106 {
107 return( slist_map2_rev( icontainer->children,
108 (SListMap2Fn) fn, a, b ) );
109 }
110
111 /* Apply a function to a tree of icontainers, bottom up.
112 */
113 void *
icontainer_map_all(iContainer * icontainer,icontainer_map_fn fn,void * a)114 icontainer_map_all( iContainer *icontainer, icontainer_map_fn fn, void *a )
115 {
116 iContainer *result;
117
118 if( (result = icontainer_map( icontainer,
119 (icontainer_map_fn) icontainer_map_all, (void *) fn, a )) )
120 return( result );
121
122 return( fn( icontainer, a, NULL ) );
123 }
124
125 void *
icontainer_map2_all(iContainer * icontainer,icontainer_map_fn fn,void * a,void * b)126 icontainer_map2_all( iContainer *icontainer,
127 icontainer_map_fn fn, void *a, void *b )
128 {
129 iContainer *result;
130
131 if( (result = icontainer_map3( icontainer,
132 (icontainer_map3_fn) icontainer_map2_all, (void *) fn, a, b )) )
133 return( result );
134
135 return( fn( icontainer, a, b ) );
136 }
137
138 void *
icontainer_map3_all(iContainer * icontainer,icontainer_map3_fn fn,void * a,void * b,void * c)139 icontainer_map3_all( iContainer *icontainer,
140 icontainer_map3_fn fn, void *a, void *b, void *c )
141 {
142 iContainer *result;
143
144 if( (result = icontainer_map4( icontainer,
145 (icontainer_map4_fn) icontainer_map3_all,
146 (void *) fn, a, b, c )) )
147 return( result );
148
149 return( fn( icontainer, a, b, c ) );
150 }
151
152 void *
icontainer_map4_all(iContainer * icontainer,icontainer_map4_fn fn,void * a,void * b,void * c,void * d)153 icontainer_map4_all( iContainer *icontainer,
154 icontainer_map4_fn fn, void *a, void *b, void *c, void *d )
155 {
156 iContainer *result;
157
158 if( (result = icontainer_map5( icontainer,
159 (icontainer_map5_fn) icontainer_map4_all,
160 (void *) fn, a, b, c, d )) )
161 return( result );
162
163 return( fn( icontainer, a, b, c, d ) );
164 }
165
166 /* Apply a function to the children of a icontainer.
167 */
168 void *
icontainer_map_all_intrans(iContainer * icontainer,icontainer_map_fn fn,void * a)169 icontainer_map_all_intrans( iContainer *icontainer,
170 icontainer_map_fn fn, void *a )
171 {
172 return( icontainer_map( icontainer,
173 (icontainer_map_fn) icontainer_map_all, (void *) fn, a ) );
174 }
175
176 static void *
icontainer_sanity_child(iContainer * child,iContainer * parent)177 icontainer_sanity_child( iContainer *child, iContainer *parent )
178 {
179 g_assert( IS_ICONTAINER( child ) );
180 g_assert( IS_ICONTAINER( parent ) );
181 g_assert( child->parent == parent );
182 g_assert( child->pos >= 0 );
183 g_assert( g_slist_find( parent->children, child ) );
184
185 if( parent->child_hash )
186 g_assert( g_hash_table_lookup( parent->child_hash,
187 IOBJECT( child )->name ) );
188
189 return( NULL );
190 }
191
192 void
icontainer_sanity(iContainer * icontainer)193 icontainer_sanity( iContainer *icontainer )
194 {
195 g_assert( IS_ICONTAINER( icontainer ) );
196
197 if( icontainer->parent )
198 icontainer_sanity_child( icontainer, icontainer->parent );
199 icontainer_map( icontainer,
200 (icontainer_map_fn) icontainer_sanity_child, icontainer, NULL );
201 }
202
203 static gint
icontainer_pos_compare(iContainer * a,iContainer * b)204 icontainer_pos_compare( iContainer *a, iContainer *b )
205 {
206 return( a->pos - b->pos );
207 }
208
209 void
icontainer_pos_sort(iContainer * icontainer)210 icontainer_pos_sort( iContainer *icontainer )
211 {
212 icontainer->children = g_slist_sort( icontainer->children,
213 (GCompareFunc) icontainer_pos_compare );
214 iobject_changed( IOBJECT( icontainer ) );
215 }
216
217 static void *
icontainer_pos_last_sub(iContainer * icontainer,int * max)218 icontainer_pos_last_sub( iContainer *icontainer, int *max )
219 {
220 if( icontainer->pos > *max )
221 *max = icontainer->pos;
222
223 return( NULL );
224 }
225
226 int
icontainer_pos_last(iContainer * icontainer)227 icontainer_pos_last( iContainer *icontainer )
228 {
229 int max = -1;
230
231 icontainer_map( icontainer,
232 (icontainer_map_fn) icontainer_pos_last_sub, &max, NULL );
233
234 return( max );
235 }
236
237 static void *
icontainer_pos_changed(iContainer * icontainer)238 icontainer_pos_changed( iContainer *icontainer )
239 {
240 #ifdef DEBUG
241 printf( "icontainer_pos_changed: " );
242 iobject_print( IOBJECT( icontainer ) );
243 #endif /*DEBUG*/
244
245 g_signal_emit( G_OBJECT( icontainer ),
246 icontainer_signals[SIG_POS_CHANGED], 0 );
247
248 return( NULL );
249 }
250
251 static void *
icontainer_pos_renumber_sub(iContainer * icontainer,int * n,GSList ** changed)252 icontainer_pos_renumber_sub( iContainer *icontainer, int *n, GSList **changed )
253 {
254 if( icontainer->pos != *n ) {
255 icontainer->pos = *n;
256 *changed = g_slist_prepend( *changed, icontainer );
257 }
258
259 *n += 1;
260
261 return( NULL );
262 }
263
264 #ifdef DEBUG_VERBOSE
265 static void *
icontainer_print_element(iContainer * element,int * n)266 icontainer_print_element( iContainer *element, int *n )
267 {
268 printf( "\t%3d) pos = %d ", *n, element->pos );
269 iobject_print( IOBJECT( element ) );
270 *n += 1;
271
272 return( NULL );
273 }
274 #endif /*DEBUG_VERBOSE*/
275
276 void
icontainer_pos_renumber(iContainer * icontainer)277 icontainer_pos_renumber( iContainer *icontainer )
278 {
279 int n = 0;
280 GSList *changed;
281
282 #ifdef DEBUG_VERBOSE
283 {
284 int i;
285
286 printf( "icontainer_pos_renumber: " );
287 iobject_print( IOBJECT( icontainer ) );
288 printf( "\tbefore:\n" );
289 i = 0;
290 icontainer_map( icontainer,
291 (icontainer_map_fn) icontainer_print_element, &i, NULL );
292 }
293 #endif /*DEBUG_VERBOSE*/
294
295 changed = NULL;
296 icontainer_map( icontainer,
297 (icontainer_map_fn) icontainer_pos_renumber_sub, &n, &changed );
298
299 /* Tell all the children that have been renumbered.
300 */
301 #ifdef DEBUG_VERBOSE
302 if( g_slist_length( changed ) > 1 ) {
303 printf( "icontainer_pos_renumber: renumbering %d children! ",
304 g_slist_length( changed ) );
305 iobject_print( IOBJECT( icontainer ) );
306 }
307 #endif /*DEBUG_VERBOSE*/
308 slist_map( changed,
309 (SListMapFn) icontainer_pos_changed, NULL );
310 g_slist_free( changed );
311 iobject_changed( IOBJECT( icontainer ) );
312
313 #ifdef DEBUG_VERBOSE
314 {
315 int i;
316
317 printf( "icontainer_pos_renumber: " );
318 iobject_print( IOBJECT( icontainer ) );
319 printf( "\tafter:\n" );
320 i = 0;
321 icontainer_map( icontainer,
322 (icontainer_map_fn) icontainer_print_element, &i, NULL );
323 }
324 #endif /*DEBUG_VERBOSE*/
325 }
326
327 gint
icontainer_name_compare(iContainer * a,iContainer * b)328 icontainer_name_compare( iContainer *a, iContainer *b )
329 {
330 return( strcasecmp( IOBJECT( a )->name, IOBJECT( b )->name ) );
331 }
332
333 void
icontainer_custom_sort(iContainer * icontainer,GCompareFunc fn)334 icontainer_custom_sort( iContainer *icontainer, GCompareFunc fn )
335 {
336 icontainer->children = g_slist_sort( icontainer->children, fn );
337 icontainer_pos_renumber( icontainer );
338 iobject_changed( IOBJECT( icontainer ) );
339 }
340
341 /* Add a child.
342 */
343 void
icontainer_child_add(iContainer * parent,iContainer * child,int pos)344 icontainer_child_add( iContainer *parent, iContainer *child, int pos )
345 {
346 g_assert( IS_ICONTAINER( parent ) );
347 g_assert( IS_ICONTAINER( child ) );
348
349 #ifdef DEBUG_SANITY
350 icontainer_sanity( parent );
351 icontainer_sanity( child );
352 #endif /*DEBUG_SANITY*/
353
354 g_signal_emit( G_OBJECT( parent ),
355 icontainer_signals[SIG_CHILD_ADD], 0, child, pos );
356
357 #ifdef DEBUG_SANITY
358 icontainer_sanity( parent );
359 icontainer_sanity( child );
360 #endif /*DEBUG_SANITY*/
361 }
362
363 /* Add a child before another child. after == NULL means append.
364 */
365 void
icontainer_child_add_before(iContainer * parent,iContainer * child,iContainer * before)366 icontainer_child_add_before( iContainer *parent,
367 iContainer *child, iContainer *before )
368 {
369 int pos;
370
371 g_assert( !before || IS_ICONTAINER( before ) );
372 g_assert( !before || before->parent == parent );
373
374 pos = g_slist_index( parent->children, before );
375 icontainer_child_add( parent, child, pos );
376 }
377
378 /* pos == 0 ... move to start
379 * pos == -1 ... move to end
380 * pos == n ... move before sibling at position n
381 */
382 void
icontainer_child_move(iContainer * child,int pos)383 icontainer_child_move( iContainer *child, int pos )
384 {
385 iContainer *parent = child->parent;
386
387 parent->children = g_slist_remove( parent->children, child );
388
389 if( pos >= 0 )
390 parent->children = g_slist_insert( parent->children,
391 child, pos );
392 else
393 parent->children = g_slist_append( parent->children, child );
394
395 icontainer_pos_renumber( parent );
396 iobject_changed( IOBJECT( child ) );
397 }
398
399 void *
icontainer_child_remove(iContainer * child)400 icontainer_child_remove( iContainer *child )
401 {
402 iContainer *parent;
403
404 if( (parent = child->parent) ) {
405 g_assert( ICONTAINER_IS_CHILD( parent, child ) );
406
407 #ifdef DEBUG
408 printf( "icontainer_child_remove: (child %p)\n", child );
409 printf( "\tchild: %s \"%s\"\n",
410 G_OBJECT_TYPE_NAME( child ),
411 NN( IOBJECT( child )->name ) );
412 #endif /*DEBUG*/
413
414 #ifdef DEBUG_SANITY
415 icontainer_sanity( parent );
416 icontainer_sanity( child );
417 #endif /*DEBUG_SANITY*/
418
419 g_signal_emit( G_OBJECT( parent ),
420 icontainer_signals[SIG_CHILD_REMOVE], 0, child );
421
422 #ifdef DEBUG_SANITY
423 icontainer_sanity( parent );
424 #endif /*DEBUG_SANITY*/
425 }
426
427 return( NULL );
428 }
429
430 void
icontainer_current(iContainer * parent,iContainer * child)431 icontainer_current( iContainer *parent, iContainer *child )
432 {
433 g_assert( parent );
434 g_assert( !child || ICONTAINER_IS_CHILD( parent, child ) );
435
436 if( parent->current == child )
437 return;
438
439 #ifdef DEBUG
440 printf( "icontainer_current: (child %p)\n", child );
441 printf( "\tchild: %s \"%s\"\n",
442 G_OBJECT_TYPE_NAME( child ),
443 NN( IOBJECT( child )->name ) );
444 #endif /*DEBUG*/
445
446 #ifdef DEBUG_SANITY
447 icontainer_sanity( parent );
448 if( child )
449 icontainer_sanity( child );
450 #endif /*DEBUG_SANITY*/
451
452 g_signal_emit( G_OBJECT( parent ),
453 icontainer_signals[SIG_CURRENT], 0, child );
454
455 #ifdef DEBUG_SANITY
456 icontainer_sanity( parent );
457 if( child )
458 icontainer_sanity( child );
459 #endif /*DEBUG_SANITY*/
460 }
461
462 iContainer *
icontainer_next(iContainer * parent)463 icontainer_next( iContainer *parent )
464 {
465 iContainer *child;
466 int i;
467
468 if( !parent->children )
469 return( NULL );
470
471 if( !parent->current )
472 i = 0;
473 else
474 i = g_slist_index( parent->children, parent->current ) + 1;
475
476 if( !(child = g_slist_nth_data( parent->children, i )) )
477 child = ICONTAINER( parent->children->data );
478
479 icontainer_current( parent, child );
480
481 return( child );
482 }
483
484 void
icontainer_reparent(iContainer * parent,iContainer * child,int pos)485 icontainer_reparent( iContainer *parent, iContainer *child, int pos )
486 {
487 iContainer *old_parent = child->parent;
488
489 g_assert( parent );
490 g_assert( child );
491
492 #ifdef DEBUG_SANITY
493 icontainer_sanity( old_parent );
494 icontainer_sanity( parent );
495 icontainer_sanity( child );
496 #endif /*DEBUG_SANITY*/
497
498 /* These must always happen as a pair.
499 */
500 g_signal_emit( G_OBJECT( old_parent ),
501 icontainer_signals[SIG_CHILD_DETACH], 0, child );
502 g_signal_emit( G_OBJECT( parent ),
503 icontainer_signals[SIG_CHILD_ATTACH], 0, child, pos );
504
505 icontainer_pos_renumber( parent );
506 iobject_changed( IOBJECT( parent ) );
507 iobject_changed( IOBJECT( old_parent ) );
508 iobject_changed( IOBJECT( child ) );
509
510 #ifdef DEBUG_SANITY
511 icontainer_sanity( old_parent );
512 icontainer_sanity( parent );
513 icontainer_sanity( child );
514 #endif /*DEBUG_SANITY*/
515 }
516
517 static void
icontainer_dispose(GObject * gobject)518 icontainer_dispose( GObject *gobject )
519 {
520 iContainer *icontainer;
521
522 g_return_if_fail( gobject != NULL );
523 g_return_if_fail( IS_ICONTAINER( gobject ) );
524
525 icontainer = ICONTAINER( gobject );
526
527 #ifdef DEBUG
528 printf( "icontainer_dispose: (%p) %s \"%s\"\n",
529 icontainer,
530 G_OBJECT_TYPE_NAME( icontainer ),
531 NN( IOBJECT( icontainer )->name ) );
532 #endif /*DEBUG*/
533
534 icontainer_map( icontainer,
535 (icontainer_map_fn) icontainer_child_remove, NULL, NULL );
536 icontainer_child_remove( icontainer );
537
538 G_OBJECT_CLASS( parent_class )->dispose( gobject );
539 }
540
541 static void
icontainer_finalize(GObject * gobject)542 icontainer_finalize( GObject *gobject )
543 {
544 iContainer *icontainer;
545
546 g_return_if_fail( gobject != NULL );
547 g_return_if_fail( IS_ICONTAINER( gobject ) );
548
549 icontainer = ICONTAINER( gobject );
550
551 IM_FREEF( g_hash_table_destroy, icontainer->child_hash );
552
553 G_OBJECT_CLASS( parent_class )->finalize( gobject );
554 }
555
556 static void
icontainer_info(iObject * iobject,VipsBuf * buf)557 icontainer_info( iObject *iobject, VipsBuf *buf )
558 {
559 iContainer *icontainer = ICONTAINER( iobject );
560
561 vips_buf_appendf( buf, "pos = \"%d\"\n", icontainer->pos );
562
563 IOBJECT_CLASS( parent_class )->info( iobject, buf );
564 }
565
566 static void
icontainer_real_pos_changed(iContainer * icontainer)567 icontainer_real_pos_changed( iContainer *icontainer )
568 {
569 }
570
571 static void
icontainer_link(iContainer * parent,iContainer * child,int pos)572 icontainer_link( iContainer *parent, iContainer *child, int pos )
573 {
574 if( pos >= 0 )
575 parent->children = g_slist_insert( parent->children,
576 child, pos );
577 else
578 parent->children = g_slist_append( parent->children, child );
579 child->parent = parent;
580 child->pos = pos;
581 if( parent->child_hash ) {
582 g_assert( !g_hash_table_lookup( parent->child_hash,
583 IOBJECT( child )->name ) );
584
585 g_hash_table_insert( parent->child_hash,
586 IOBJECT( child )->name, child );
587 }
588 }
589
590 static void
icontainer_real_child_add(iContainer * parent,iContainer * child,int pos)591 icontainer_real_child_add( iContainer *parent, iContainer *child, int pos )
592 {
593 iContainerClass *icontainer_class = ICONTAINER_GET_CLASS( child );
594
595 g_assert( IS_ICONTAINER( parent ) && IS_ICONTAINER( child ) );
596 g_assert( child->parent == NULL );
597
598 #ifdef DEBUG
599 printf( "icontainer_real_child_add:\n\tparent " );
600 iobject_print( IOBJECT( parent ) );
601 printf( "\tchild " );
602 iobject_print( IOBJECT( child ) );
603 printf( "\tpos = %d\n", pos );
604 #endif /*DEBUG*/
605
606 icontainer_link( parent, child, pos );
607
608 g_object_ref( G_OBJECT( child ) );
609 iobject_sink( IOBJECT( child ) );
610
611 /* Renumber to get all the pos set.
612 */
613 icontainer_pos_renumber( parent );
614 iobject_changed( IOBJECT( child ) );
615
616 /* We've made the link ... trigger the parent_add() on the child.
617 */
618 icontainer_class->parent_add( child );
619
620 #ifdef DEBUG_VERBOSE
621 printf( "icontainer_real_child_add: " );
622 iobject_print( IOBJECT( parent ) );
623 #endif /*DEBUG_VERBOSE*/
624 }
625
626 static void
icontainer_unlink(iContainer * child)627 icontainer_unlink( iContainer *child )
628 {
629 iContainer *parent = child->parent;
630
631 parent->children = g_slist_remove( parent->children, child );
632 child->parent = NULL;
633 if( parent->child_hash ) {
634 g_assert( g_hash_table_lookup( parent->child_hash,
635 IOBJECT( child )->name ) );
636
637 g_hash_table_remove( parent->child_hash,
638 IOBJECT( child )->name );
639 }
640 }
641
642 static void
icontainer_real_child_remove(iContainer * parent,iContainer * child)643 icontainer_real_child_remove( iContainer *parent, iContainer *child )
644 {
645 iContainerClass *icontainer_child_class = ICONTAINER_GET_CLASS( child );
646
647 g_assert( IS_ICONTAINER( parent ) && IS_ICONTAINER( child ) );
648
649 #ifdef DEBUG
650 printf( "icontainer_real_child_remove: parent %s \"%s\"; "
651 "child %s \"%s\"\n",
652 G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ),
653 G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ) );
654 #endif /*DEBUG*/
655
656 if( parent->current == child )
657 icontainer_current( parent, NULL );
658
659 /* We're about to break the link ... trigger the parent_remove() on
660 * the child.
661 */
662 icontainer_child_class->parent_remove( child );
663
664 icontainer_unlink( child );
665
666 UNREF( child );
667
668 iobject_changed( IOBJECT( parent ) );
669 }
670
671 static void
icontainer_real_parent_add(iContainer * child)672 icontainer_real_parent_add( iContainer *child )
673 {
674 #ifdef DEBUG
675 printf( "icontainer_real_parent_add: child %s \"%s\"; "
676 "parent %s \"%s\"\n",
677 G_OBJECT_TYPE_NAME( child ),
678 NN( IOBJECT( child )->name ),
679 G_OBJECT_TYPE_NAME( child->parent ),
680 NN( IOBJECT( child->parent )->name ) );
681 #endif /*DEBUG*/
682 }
683
684 static void
icontainer_real_parent_remove(iContainer * child)685 icontainer_real_parent_remove( iContainer *child )
686 {
687 #ifdef DEBUG
688 {
689 iContainer *parent = child->parent;
690
691 printf( "icontainer_real_parent_remove: child %s \"%s\"; "
692 "parent %s \"%s\"\n",
693 G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ),
694 G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) );
695 }
696 #endif /*DEBUG*/
697 }
698
699 static void
icontainer_real_current(iContainer * parent,iContainer * child)700 icontainer_real_current( iContainer *parent, iContainer *child )
701 {
702 iContainer *old_current;
703
704 g_assert( IS_ICONTAINER( parent ) );
705 g_assert( !child || IS_ICONTAINER( child ) );
706 g_assert( !child || ICONTAINER_IS_CHILD( parent, child ) );
707
708 #ifdef DEBUG
709 printf( "icontainer_real_current: parent %s \"%s\"; "
710 "child %s \"%s\"\n",
711 G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ),
712 child ? G_OBJECT_TYPE_NAME( child ) : "NULL",
713 child ? NN( IOBJECT( child )->name ) : "NULL" );
714 #endif /*DEBUG*/
715
716 old_current = parent->current;
717 parent->current = child;
718
719 if( old_current != child ) {
720 if( old_current )
721 iobject_changed( IOBJECT( old_current ) );
722 if( child )
723 iobject_changed( IOBJECT( child ) );
724 iobject_changed( IOBJECT( parent ) );
725 }
726
727 if( child )
728 model_front( MODEL( child ) );
729 }
730
731 static void
icontainer_real_child_detach(iContainer * parent,iContainer * child)732 icontainer_real_child_detach( iContainer *parent, iContainer *child )
733 {
734 g_assert( IS_ICONTAINER( parent ) );
735 g_assert( IS_ICONTAINER( child ) );
736 g_assert( child->parent != NULL );
737 g_assert( ICONTAINER_IS_CHILD( parent, child ) );
738
739 icontainer_unlink( child );
740 }
741
742 static void
icontainer_real_child_attach(iContainer * parent,iContainer * child,int pos)743 icontainer_real_child_attach( iContainer *parent, iContainer *child, int pos )
744 {
745 g_assert( IS_ICONTAINER( parent ) );
746 g_assert( IS_ICONTAINER( child ) );
747 g_assert( child->parent == NULL );
748
749 icontainer_link( parent, child, pos );
750 }
751
752 static void
icontainer_class_init(iContainerClass * class)753 icontainer_class_init( iContainerClass *class )
754 {
755 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
756 iObjectClass *iobject_class = IOBJECT_CLASS( class );
757
758 parent_class = g_type_class_peek_parent( class );
759
760 gobject_class->dispose = icontainer_dispose;
761 gobject_class->finalize = icontainer_finalize;
762
763 iobject_class->info = icontainer_info;
764
765 class->pos_changed = icontainer_real_pos_changed;
766 class->child_add = icontainer_real_child_add;
767 class->child_remove = icontainer_real_child_remove;
768 class->parent_add = icontainer_real_parent_add;
769 class->parent_remove = icontainer_real_parent_remove;
770 class->current = icontainer_real_current;
771 class->child_detach = icontainer_real_child_detach;
772 class->child_attach = icontainer_real_child_attach;
773
774 /* Create signals.
775 */
776 icontainer_signals[SIG_POS_CHANGED] = g_signal_new( "pos_changed",
777 G_OBJECT_CLASS_TYPE( gobject_class ),
778 G_SIGNAL_RUN_FIRST,
779 G_STRUCT_OFFSET( iContainerClass, pos_changed ),
780 NULL, NULL,
781 g_cclosure_marshal_VOID__VOID,
782 G_TYPE_NONE, 0 );
783
784 icontainer_signals[SIG_CHILD_ADD] = g_signal_new( "child_add",
785 G_OBJECT_CLASS_TYPE( gobject_class ),
786 G_SIGNAL_RUN_FIRST,
787 G_STRUCT_OFFSET( iContainerClass, child_add ),
788 NULL, NULL,
789 nip_VOID__OBJECT_INT,
790 G_TYPE_NONE, 2,
791 TYPE_ICONTAINER, GTK_TYPE_INT );
792
793 icontainer_signals[SIG_CHILD_REMOVE] = g_signal_new( "child_remove",
794 G_OBJECT_CLASS_TYPE( gobject_class ),
795 G_SIGNAL_RUN_LAST,
796 G_STRUCT_OFFSET( iContainerClass, child_remove ),
797 NULL, NULL,
798 g_cclosure_marshal_VOID__OBJECT,
799 G_TYPE_NONE, 1,
800 TYPE_ICONTAINER );
801
802 icontainer_signals[SIG_CURRENT] = g_signal_new( "current",
803 G_OBJECT_CLASS_TYPE( gobject_class ),
804 G_SIGNAL_RUN_LAST,
805 G_STRUCT_OFFSET( iContainerClass, current ),
806 NULL, NULL,
807 g_cclosure_marshal_VOID__OBJECT,
808 G_TYPE_NONE, 1,
809 TYPE_ICONTAINER );
810
811 icontainer_signals[SIG_CHILD_DETACH] = g_signal_new( "child_detach",
812 G_OBJECT_CLASS_TYPE( gobject_class ),
813 G_SIGNAL_RUN_LAST,
814 G_STRUCT_OFFSET( iContainerClass, child_detach ),
815 NULL, NULL,
816 g_cclosure_marshal_VOID__OBJECT,
817 G_TYPE_NONE, 1,
818 TYPE_ICONTAINER );
819
820 icontainer_signals[SIG_CHILD_ATTACH] = g_signal_new( "child_attach",
821 G_OBJECT_CLASS_TYPE( gobject_class ),
822 G_SIGNAL_RUN_FIRST,
823 G_STRUCT_OFFSET( iContainerClass, child_attach ),
824 NULL, NULL,
825 nip_VOID__OBJECT_INT,
826 G_TYPE_NONE, 2,
827 TYPE_ICONTAINER, GTK_TYPE_INT );
828
829 #ifdef DEBUG_SANITY
830 printf( "*** DEBUG_SANITY is on ... expect slowness\n" );
831 #endif /*DEBUG_SANITY*/
832 }
833
834 static void
icontainer_init(iContainer * icontainer)835 icontainer_init( iContainer *icontainer )
836 {
837 /* Init our instance fields.
838 */
839 icontainer->children = NULL;
840 icontainer->pos = -1;
841 icontainer->parent = NULL;
842 icontainer->child_hash = NULL;
843 }
844
845 GType
icontainer_get_type(void)846 icontainer_get_type( void )
847 {
848 static GType type = 0;
849
850 if( !type ) {
851 static const GTypeInfo info = {
852 sizeof( iContainerClass ),
853 NULL, /* base_init */
854 NULL, /* base_finalize */
855 (GClassInitFunc) icontainer_class_init,
856 NULL, /* class_finalize */
857 NULL, /* class_data */
858 sizeof( iContainer ),
859 32, /* n_preallocs */
860 (GInstanceInitFunc) icontainer_init,
861 };
862
863 type = g_type_register_static( TYPE_IOBJECT,
864 "iContainer", &info, 0 );
865 }
866
867 return( type );
868 }
869
870 /* Put the container into lookup-by-child-name mode.
871 */
872 void
icontainer_set_hash(iContainer * icontainer)873 icontainer_set_hash( iContainer *icontainer )
874 {
875 /* Can only do this once just after startup, and before there are any
876 * children.
877 */
878 g_assert( !icontainer->children );
879 g_assert( !icontainer->child_hash );
880
881 icontainer->child_hash = g_hash_table_new( g_str_hash, g_str_equal );
882 }
883
884 iContainer *
icontainer_child_lookup(iContainer * parent,const char * name)885 icontainer_child_lookup( iContainer *parent, const char *name )
886 {
887 g_assert( parent->child_hash );
888
889 return( ICONTAINER( g_hash_table_lookup( parent->child_hash, name ) ) );
890 }
891