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