1 /* abstract base class for items which can form a row in a tally
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
32 #define DEBUG_VIEWCHILD
33  */
34 
35 /* Time each refresh
36 #define DEBUG_TIME
37  */
38 
39 #include "ip.h"
40 
41 static vObjectClass *parent_class = NULL;
42 
43 static GSList *view_scannable = NULL;
44 
45 static GSList *view_resettable = NULL;
46 
47 void
view_scannable_register(View * view)48 view_scannable_register( View *view )
49 {
50 	/* Must have a scan method.
51 	 */
52 	g_assert( VIEW_GET_CLASS( view )->scan );
53 
54 	if( !view->scannable ) {
55 		view_scannable = g_slist_prepend( view_scannable, view );
56 		view->scannable = TRUE;
57 	}
58 }
59 
60 void
view_scannable_unregister(View * view)61 view_scannable_unregister( View *view )
62 {
63 	if( view->scannable ) {
64 		view_scannable = g_slist_remove( view_scannable, view );
65 		view->scannable = FALSE;
66 	}
67 }
68 
69 gboolean
view_scan_all(void)70 view_scan_all( void )
71 {
72 	if( slist_map( view_scannable, (SListMapFn) view_scan, NULL ) )
73 		return( FALSE );
74 
75 	view_reset_all();
76 
77 	return( TRUE );
78 }
79 
80 void
view_resettable_register(View * view)81 view_resettable_register( View *view )
82 {
83 	/* Must have a reset method.
84 	 */
85 	g_assert( VIEW_GET_CLASS( view )->reset );
86 
87 	if( !view->resettable ) {
88 		view_resettable = g_slist_prepend( view_resettable, view );
89 		view->resettable = TRUE;
90 	}
91 }
92 
93 void
view_resettable_unregister(View * view)94 view_resettable_unregister( View *view )
95 {
96 	if( view->resettable ) {
97 		view_resettable = g_slist_remove( view_resettable, view );
98 		view->resettable = FALSE;
99 	}
100 }
101 
102 void
view_reset_all(void)103 view_reset_all( void )
104 {
105 	(void) slist_map( view_resettable, (SListMapFn) view_reset, NULL );
106 }
107 
108 /* Should a viewchild be displayed? If model->display is true, also give the
109  * enclosing view a chance to filter.
110  */
111 static gboolean
view_viewchild_display(ViewChild * viewchild)112 view_viewchild_display( ViewChild *viewchild )
113 {
114 	View *parent_view = viewchild->parent_view;
115 	Model *child_model = viewchild->child_model;
116 	ViewClass *parent_view_class = VIEW_GET_CLASS( parent_view );
117 
118 	if( child_model->display && parent_view_class->display )
119 		return( parent_view_class->display( parent_view,
120 			child_model ) );
121 
122 	return( child_model->display );
123 }
124 
125 /* One of the children of the model we watch has changed ... create or destroy
126  * the child view as required.
127  */
128 static void
view_viewchild_changed(Model * model,ViewChild * viewchild)129 view_viewchild_changed( Model *model, ViewChild *viewchild )
130 {
131 	gboolean display = view_viewchild_display( viewchild );
132 	View *child = viewchild->child_view;
133 
134 	if( !display && child ) {
135 #ifdef DEBUG_VIEWCHILD
136 		printf( "view_viewchild_changed: %s \"%s\", removing view\n",
137 			G_OBJECT_TYPE_NAME( model ),
138 			NN( IOBJECT( model )->name ) );
139 
140 		printf( "view_viewchild_changed: %s\n",
141 			G_OBJECT_TYPE_NAME( child ) );
142 #endif /*DEBUG_VIEWCHILD*/
143 
144 		DESTROY_GTK( child );
145 	}
146 	else if( display && !child ) {
147 #ifdef DEBUG_VIEWCHILD
148 		printf( "view_viewchild_changed: %s \"%s\", adding view\n",
149 			G_OBJECT_TYPE_NAME( model ),
150 			NN( IOBJECT( model )->name ) );
151 #endif /*DEBUG_VIEWCHILD*/
152 
153 		model_view_new( model, viewchild->parent_view );
154 	}
155 }
156 
157 static ViewChild *
view_viewchild_new(View * parent_view,Model * child_model)158 view_viewchild_new( View *parent_view, Model *child_model )
159 {
160 	ViewChild *viewchild;
161 
162 #ifdef DEBUG_VIEWCHILD
163 	printf( "view_viewchild_new: view \"%s\" watching %s \"%s\"\n",
164 		G_OBJECT_TYPE_NAME( parent_view ),
165 		G_OBJECT_TYPE_NAME( child_model ),
166 		NN( IOBJECT( child_model )->name ) );
167 #endif /*DEBUG_VIEWCHILD*/
168 
169 	if( !(viewchild = INEW( NULL, ViewChild )) )
170 		return( NULL );
171 
172 	viewchild->parent_view = parent_view;
173 	viewchild->child_model = child_model;
174 	viewchild->child_model_changed_sid =
175 		g_signal_connect( child_model, "changed",
176 			G_CALLBACK( view_viewchild_changed ), viewchild );
177 	viewchild->child_view = NULL;
178 
179 	parent_view->managed =
180 		g_slist_append( parent_view->managed, viewchild );
181 
182 	return( viewchild );
183 }
184 
185 static void *
view_viewchild_destroy(ViewChild * viewchild)186 view_viewchild_destroy( ViewChild *viewchild )
187 {
188 	View *parent_view = viewchild->parent_view;
189 	View *child_view = viewchild->child_view;
190 
191 #ifdef DEBUG_VIEWCHILD
192 	printf( "view_viewchild_destroy: view %s watching model %s\n",
193 		G_OBJECT_TYPE_NAME( viewchild->parent_view ),
194 		G_OBJECT_TYPE_NAME( viewchild->child_model ) );
195 #endif /*DEBUG_VIEWCHILD*/
196 
197 	if( child_view ) {
198 		g_assert( child_view->parent == parent_view );
199 		child_view->parent = NULL;
200 	}
201 	FREESID( viewchild->child_model_changed_sid, viewchild->child_model );
202 	parent_view->managed =
203 		g_slist_remove( parent_view->managed, viewchild );
204 
205 	im_free( viewchild );
206 
207 	return( NULL );
208 }
209 
210 static void *
view_viewchild_test_child_model(ViewChild * viewchild,Model * child_model)211 view_viewchild_test_child_model( ViewChild *viewchild, Model *child_model )
212 {
213 #ifdef DEBUG
214 	printf( "view_viewchild_test_child_model: model %s \"%s\"\n",
215 		G_OBJECT_TYPE_NAME( child_model ),
216 		NN( IOBJECT( child_model )->name ) );
217 #endif /*DEBUG*/
218 
219 	if( viewchild->child_model == child_model )
220 		return( viewchild );
221 
222 	return( NULL );
223 }
224 
225 /* Do we have a model?
226  */
227 gboolean
view_hasmodel(View * view)228 view_hasmodel( View *view )
229 {
230 	return( VOBJECT( view )->iobject != NULL );
231 }
232 
233 void *
view_model_test(View * view,Model * model)234 view_model_test( View *view, Model *model )
235 {
236 	if( VOBJECT( view )->iobject == IOBJECT( model ) )
237 		return( view );
238 
239 	return( NULL );
240 }
241 
242 /* Link to enclosing model and view.
243  */
244 void
view_link(View * view,Model * model,View * parent)245 view_link( View *view, Model *model, View *parent )
246 {
247 	VIEW_GET_CLASS( view )->link( view, model, parent );
248 }
249 
250 /* Add a child.
251  */
252 void
view_child_add(View * parent,View * child)253 view_child_add( View *parent, View *child )
254 {
255 	VIEW_GET_CLASS( parent )->child_add( parent, child );
256 }
257 
258 /* Remove a child.
259  */
260 void
view_child_remove(View * child)261 view_child_remove( View *child )
262 {
263 	View *parent = child->parent;
264 
265 	VIEW_GET_CLASS( parent )->child_remove( parent, child );
266 }
267 
268 /* Child needs repositioning.
269  */
270 void
view_child_position(View * child)271 view_child_position( View *child )
272 {
273 	View *parent = child->parent;
274 
275 	VIEW_GET_CLASS( parent )->child_position( parent, child );
276 }
277 
278 /* Pop child to front of stacking order.
279  */
280 void
view_child_front(View * child)281 view_child_front( View *child )
282 {
283 	View *parent = child->parent;
284 
285 	if( parent )
286 		VIEW_GET_CLASS( parent )->child_front( parent, child );
287 }
288 
289 /* Break link to model.
290  */
291 void
view_unlink(View * view)292 view_unlink( View *view )
293 {
294 	g_assert( view != NULL );
295 	g_assert( VOBJECT( view )->iobject != NULL );
296 	g_assert( IS_VIEW( view ) && IS_MODEL( VOBJECT( view )->iobject ) );
297 
298 	FREESID( view->pos_changed_sid, VOBJECT( view )->iobject );
299 	FREESID( view->scrollto_sid, VOBJECT( view )->iobject );
300 	FREESID( view->layout_sid, VOBJECT( view )->iobject );
301 	FREESID( view->reset_sid, VOBJECT( view )->iobject );
302 	FREESID( view->front_sid, VOBJECT( view )->iobject );
303 	FREESID( view->child_add_sid, VOBJECT( view )->iobject );
304 	FREESID( view->child_remove_sid, VOBJECT( view )->iobject );
305 	FREESID( view->child_detach_sid, VOBJECT( view )->iobject );
306 	FREESID( view->child_attach_sid, VOBJECT( view )->iobject );
307 }
308 
309 static void
view_destroy(GtkObject * object)310 view_destroy( GtkObject *object )
311 {
312 	View *view;
313 
314 	g_return_if_fail( object != NULL );
315 	g_return_if_fail( IS_VIEW( object ) );
316 
317 	view = VIEW( object );
318 
319 #ifdef DEBUG
320 	printf( "view_destroy: \"%s\"\n", G_OBJECT_TYPE_NAME( object ) );
321 #endif /*DEBUG*/
322 
323 	/* We're probably changing the size of our enclosing column.
324 	 */
325 	view_resize( view );
326 
327 	if( view->scannable )
328 		view_scannable_unregister( view );
329 	if( view->resettable )
330 		view_resettable_unregister( view );
331 
332 	if( VOBJECT( view )->iobject )
333 		view_unlink( view );
334 
335 	if( view->parent )
336 		view_child_remove( view );
337 
338 	slist_map( view->managed,
339 		(SListMapFn) view_viewchild_destroy, NULL );
340 
341 	GTK_OBJECT_CLASS( parent_class )->destroy( object );
342 }
343 
344 static void
view_finalize(GObject * gobject)345 view_finalize( GObject *gobject )
346 {
347 #ifdef DEBUG
348 	printf( "view_finalize: \"%s\"\n", G_OBJECT_TYPE_NAME( gobject ) );
349 #endif /*DEBUG*/
350 
351 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
352 }
353 
354 /* Called for model pos_changed signal ... queue a refresh.
355  */
356 static void
view_model_pos_changed(Model * model,View * view)357 view_model_pos_changed( Model *model, View *view )
358 {
359         g_assert( IS_MODEL( model ) );
360         g_assert( IS_VIEW( view ) );
361 
362 #ifdef DEBUG
363         printf( "view_model_pos_changed: %s %s \"%s\"\n",
364                 G_OBJECT_TYPE_NAME( view ),
365                 G_OBJECT_TYPE_NAME( model ),
366                 NN( IOBJECT( model )->name ) );
367 #endif /*DEBUG*/
368 
369         vobject_refresh_queue( VOBJECT( view ) );
370 }
371 
372 /* Called for model scrollto signal ... try scrolling.
373  */
374 static void
view_model_scrollto(Model * model,ModelScrollPosition position,View * view)375 view_model_scrollto( Model *model, ModelScrollPosition position, View *view )
376 {
377 	g_assert( IS_MODEL( model ) );
378 	g_assert( IS_VIEW( view ) );
379 
380 #ifdef DEBUG
381 	printf( "view_model_scrollto: %s\n", IOBJECT( model )->name );
382 #endif /*DEBUG*/
383 
384 	view_scrollto( view, position );
385 }
386 
387 /* Called for model layout signal ... try to lay out children.
388  */
389 static void
view_model_layout(Model * model,View * view)390 view_model_layout( Model *model, View *view )
391 {
392 	g_assert( IS_MODEL( model ) );
393 	g_assert( IS_VIEW( view ) );
394 
395 #ifdef DEBUG
396 	printf( "view_model_layout: %s\n", IOBJECT( model )->name );
397 #endif /*DEBUG*/
398 
399 	view_layout( view );
400 }
401 
402 /* Called for model reset signal ... try resetting.
403  */
404 static void
view_model_reset(Model * model,View * view)405 view_model_reset( Model *model, View *view )
406 {
407 	g_assert( IS_MODEL( model ) );
408 	g_assert( IS_VIEW( view ) );
409 
410 #ifdef DEBUG
411 	printf( "view_model_reset: %s\n", IOBJECT( model )->name );
412 #endif /*DEBUG*/
413 
414 	view_reset( view );
415 }
416 
417 /* Called for model front signal ... bring view to front.
418  */
419 static void
view_model_front(Model * model,View * view)420 view_model_front( Model *model, View *view )
421 {
422 	g_assert( IS_MODEL( model ) );
423 	g_assert( IS_VIEW( view ) );
424 
425 #ifdef DEBUG
426 	printf( "view_model_front: model %s \"%s\"\n",
427 		G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) );
428 	printf( "\tview %s\n", G_OBJECT_TYPE_NAME( view ) );
429 #endif /*DEBUG*/
430 
431 	view_child_front( view );
432 }
433 
434 /* Called for model child_add signal ... start watching that child.
435  */
436 static void
view_model_child_add(Model * parent,Model * child,int pos,View * parent_view)437 view_model_child_add( Model *parent, Model *child, int pos, View *parent_view )
438 {
439 	ViewChild *viewchild;
440 
441 #ifdef DEBUG
442 	printf( "view_model_child_add: parent %s \"%s\"\n",
443 		G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) );
444 #endif /*DEBUG*/
445 
446 	g_assert( IS_MODEL( parent ) );
447 	g_assert( IS_MODEL( child ) );
448 	g_assert( IS_VIEW( parent_view ) );
449 
450 #ifdef DEBUG
451 	viewchild = slist_map( parent_view->managed,
452 		(SListMapFn) view_viewchild_test_child_model, child );
453 	g_assert( !viewchild );
454 #endif /*DEBUG*/
455 
456 	viewchild = view_viewchild_new( parent_view, child );
457 	view_viewchild_changed( child, viewchild );
458 }
459 
460 /* Called for model child_remove signal ... stop watching that child. child
461  * may have been finalized already.
462  */
463 static void
view_model_child_remove(iContainer * parent,iContainer * child,View * parent_view)464 view_model_child_remove( iContainer *parent, iContainer *child,
465 	View *parent_view )
466 {
467 	ViewChild *viewchild;
468 
469 #ifdef DEBUG
470 {
471 	printf( "view_model_child_remove: child %s \"%s\"; "
472 		"parent %s \"%s\"\n",
473 		G_OBJECT_TYPE_NAME( child ), NN( IOBJECT( child )->name ),
474 		G_OBJECT_TYPE_NAME( parent ), NN( IOBJECT( parent )->name ) );
475 
476 	printf( "view_model_child_remove: parent_view = view of %s \"%s\"\n",
477 		G_OBJECT_TYPE_NAME( VOBJECT( parent_view )->iobject ),
478 		NN( IOBJECT( VOBJECT( parent_view )->iobject )->name ) );
479 }
480 #endif /*DEBUG*/
481 
482 	viewchild = slist_map( parent_view->managed,
483 		(SListMapFn) view_viewchild_test_child_model, child );
484 
485 	g_assert( viewchild );
486 
487 	(void) view_viewchild_destroy( viewchild );
488 }
489 
490 /* Called for model parent_detach signal ... remove the viewchild for this
491  * child. child_attach will build a new one.
492  */
493 static void
view_model_child_detach(iContainer * old_parent,iContainer * child,View * old_parent_view)494 view_model_child_detach( iContainer *old_parent, iContainer *child,
495 	View *old_parent_view )
496 {
497 	ViewChild *viewchild;
498 
499 #ifdef DEBUG
500 {
501 	printf( "view_model_child_detach: child %s \"%s\"; "
502 		"old_parent %s \"%s\"\n",
503 		G_OBJECT_TYPE_NAME( child ),
504 		NN( IOBJECT( child )->name ),
505 		G_OBJECT_TYPE_NAME( old_parent ),
506 		NN( IOBJECT( old_parent )->name ) );
507 
508 	printf( "view_model_child_detach: old_parent_view = "
509 			"view of %s \"%s\"\n",
510 		G_OBJECT_TYPE_NAME( VOBJECT( old_parent_view )->iobject ),
511 		NN( IOBJECT( VOBJECT( old_parent_view )->iobject )->name ) );
512 }
513 #endif /*DEBUG*/
514 
515 	viewchild = slist_map( old_parent_view->managed,
516 		(SListMapFn) view_viewchild_test_child_model, child );
517 
518 	g_assert( viewchild );
519 	g_assert( !child->temp_view );
520 
521 	child->temp_view = viewchild->child_view;
522 
523 	(void) view_viewchild_destroy( viewchild );
524 }
525 
526 /* Called for model child_attach signal ... make a new viewchild on the new
527  * parent view.
528  */
529 static void
view_model_child_attach(iContainer * new_parent,iContainer * child,int pos,View * new_parent_view)530 view_model_child_attach( iContainer *new_parent, iContainer *child, int pos,
531 	View *new_parent_view )
532 {
533 	ViewChild *viewchild;
534 
535 	g_assert( !slist_map( new_parent_view->managed,
536 		(SListMapFn) view_viewchild_test_child_model, child ) );
537 
538 	viewchild = view_viewchild_new( new_parent_view, MODEL( child ) );
539 
540 	g_assert( child->temp_view && IS_VIEW( child->temp_view ) );
541 	viewchild->child_view = child->temp_view;
542 	child->temp_view = NULL;
543 
544 	viewchild->child_view->parent = new_parent_view;
545 }
546 
547 static void *
view_real_link_sub(Model * child_model,View * parent_view)548 view_real_link_sub( Model *child_model, View *parent_view )
549 {
550 	ViewChild *viewchild;
551 
552 	viewchild = view_viewchild_new( parent_view, child_model );
553 	view_viewchild_changed( child_model, viewchild );
554 
555 	return( NULL );
556 }
557 
558 /* Link to model and to enclosing view.
559  */
560 static void
view_real_link(View * view,Model * model,View * parent_view)561 view_real_link( View *view, Model *model, View *parent_view )
562 {
563 	g_assert( view != NULL );
564 	g_assert( IS_VIEW( view ) && IS_MODEL( model ) );
565 	g_assert( !VOBJECT( view )->iobject );
566 
567 #ifdef DEBUG
568 	printf( "view_real_link: linking %s to model %s \"%s\"\n",
569 		G_OBJECT_TYPE_NAME( view ),
570 		G_OBJECT_TYPE_NAME( model ), NN( IOBJECT( model )->name ) );
571 #endif /*DEBUG*/
572 
573 	vobject_link( VOBJECT( view ), IOBJECT( model ) );
574 	if( parent_view )
575 		view_child_add( parent_view, view );
576 
577 	view->pos_changed_sid = g_signal_connect( model, "pos_changed",
578 		G_CALLBACK( view_model_pos_changed ), view );
579 	view->scrollto_sid = g_signal_connect( model, "scrollto",
580 		G_CALLBACK( view_model_scrollto ), view );
581 	view->layout_sid = g_signal_connect( model, "layout",
582 		G_CALLBACK( view_model_layout ), view );
583 	view->reset_sid = g_signal_connect( model, "reset",
584 		G_CALLBACK( view_model_reset ), view );
585 	view->front_sid = g_signal_connect( model, "front",
586 		G_CALLBACK( view_model_front ), view );
587 	view->child_add_sid = g_signal_connect( model, "child_add",
588 		G_CALLBACK( view_model_child_add ), view );
589 	view->child_remove_sid = g_signal_connect( model, "child_remove",
590 		G_CALLBACK( view_model_child_remove ), view );
591 	view->child_detach_sid = g_signal_connect( model, "child_detach",
592 		G_CALLBACK( view_model_child_detach ), view );
593 	view->child_attach_sid = g_signal_connect( model, "child_attach",
594 		G_CALLBACK( view_model_child_attach ), view );
595 
596 	icontainer_map( ICONTAINER( model ),
597 		(icontainer_map_fn) view_real_link_sub, view, NULL );
598 
599 	gtk_widget_show( GTK_WIDGET( view ) );
600 }
601 
602 static void
view_real_child_add(View * parent,View * child)603 view_real_child_add( View *parent, View *child )
604 {
605 	ViewChild *viewchild;
606 
607 	g_assert( IS_VIEW( parent ) && IS_VIEW( child ) );
608 	g_assert( child->parent == NULL );
609 
610 #ifdef DEBUG
611 	printf( "view_real_child_add: parent %p %s, child %p %s\n",
612 		parent,
613 		G_OBJECT_TYPE_NAME( parent ),
614 		child,
615 		G_OBJECT_TYPE_NAME( child ) );
616 #endif /*DEBUG*/
617 
618 	viewchild = slist_map( parent->managed,
619 		(SListMapFn) view_viewchild_test_child_model,
620 		VOBJECT( child)->iobject );
621 
622 	g_assert( viewchild );
623 	g_assert( viewchild->child_view == NULL );
624 
625 	/* Not all views are true widgets (ie. get _ref()'s and _sink()'d by a
626 	 * parent in gtk_container()). Ref and sink ourselves to ensure that
627 	 * even these odd views get unfloated. See also
628 	 * view_real_child_remove(). Affects the tool/toolkit views, and
629 	 * rowview at least.
630 	 */
631 	child->parent = parent;
632 	viewchild->child_view = child;
633 	g_object_ref( GTK_OBJECT( child ) );
634 	gtk_object_sink( GTK_OBJECT( child ) );
635 }
636 
637 static void
view_real_child_remove(View * parent,View * child)638 view_real_child_remove( View *parent, View *child )
639 {
640 	ViewChild *viewchild;
641 
642 #ifdef DEBUG
643 	printf( "view_real_child_remove: parent %s, child %s\n",
644 		G_OBJECT_TYPE_NAME( parent ), G_OBJECT_TYPE_NAME( child ) );
645 #endif /*DEBUG*/
646 
647 	viewchild = slist_map( parent->managed,
648 		(SListMapFn) view_viewchild_test_child_model,
649 		VOBJECT( child )->iobject );
650 
651 	/* Can have floating views which are not part of the viewchild system.
652 	 */
653 	if( viewchild &&
654 		viewchild->child_view == child ) {
655 		viewchild->child_view = NULL;
656 		g_object_unref( G_OBJECT( child ) );
657 	}
658 
659 	child->parent = NULL;
660 }
661 
662 static void
view_real_child_position(View * parent,View * child)663 view_real_child_position( View *parent, View *child )
664 {
665 }
666 
667 static void
view_real_child_front(View * parent,View * child)668 view_real_child_front( View *parent, View *child )
669 {
670 }
671 
672 static void
view_real_reset(View * view)673 view_real_reset( View *view )
674 {
675 	view_resettable_unregister( view );
676 }
677 
678 static void *
view_real_scan(View * view)679 view_real_scan( View *view )
680 {
681 	Model *model = MODEL( VOBJECT( view )->iobject );
682 	Heapmodel *heapmodel;
683 
684 	view_scannable_unregister( view );
685 
686 	/* If we've changed something in this model, mark it for recomp.
687 	 */
688 	if( model && IS_HEAPMODEL( model ) &&
689 		(heapmodel = HEAPMODEL( model ))->modified &&
690 		heapmodel->row ) {
691 		Expr *expr = heapmodel->row->expr;
692 
693 		if( expr )
694 			(void) expr_dirty( expr, link_serial_new() );
695 	}
696 
697 	return( NULL );
698 }
699 
700 static void
view_class_init(ViewClass * class)701 view_class_init( ViewClass *class )
702 {
703 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
704 	GtkObjectClass *object_class = (GtkObjectClass*) class;
705 
706 	parent_class = g_type_class_peek_parent( class );
707 
708 	gobject_class->finalize = view_finalize;
709 
710 	object_class->destroy = view_destroy;
711 
712 	/* Create signals.
713 	 */
714 
715 	/* Init default methods.
716 	 */
717 	class->link = view_real_link;
718 	class->child_add = view_real_child_add;
719 	class->child_remove = view_real_child_remove;
720 	class->child_position = view_real_child_position;
721 	class->child_front = view_real_child_front;
722 	class->display = NULL;
723 
724 	class->reset = view_real_reset;
725 	class->scan = view_real_scan;
726 	class->scrollto = NULL;
727 	class->layout = NULL;
728 }
729 
730 static void
view_init(View * view)731 view_init( View *view )
732 {
733 	/* Init our instance fields.
734 	 */
735 	view->pos_changed_sid = 0;
736 	view->scrollto_sid = 0;
737 	view->layout_sid = 0;
738 	view->reset_sid = 0;
739 	view->front_sid = 0;
740 	view->child_add_sid = 0;
741 	view->child_remove_sid = 0;
742 	view->child_detach_sid = 0;
743 	view->child_attach_sid = 0;
744 
745 	view->parent = NULL;
746 
747 	view->scannable = FALSE;
748 	view->resettable = FALSE;
749 }
750 
751 GtkType
view_get_type(void)752 view_get_type( void )
753 {
754 	static GtkType view_type = 0;
755 
756 	if( !view_type ) {
757 		static const GtkTypeInfo view_info = {
758 			"View",
759 			sizeof( View ),
760 			sizeof( ViewClass ),
761 			(GtkClassInitFunc) view_class_init,
762 			(GtkObjectInitFunc) view_init,
763 			/* reserved_1 */ NULL,
764 			/* reserved_2 */ NULL,
765 			(GtkClassInitFunc) NULL,
766 		};
767 
768 		view_type = gtk_type_unique( TYPE_VOBJECT, &view_info );
769 	}
770 
771 	return( view_type );
772 }
773 
774 /* Trigger the reset method for a view.
775  */
776 void *
view_reset(View * view)777 view_reset( View *view )
778 {
779 	ViewClass *view_class = VIEW_GET_CLASS( view );
780 
781 	if( view_class->reset )
782 		view_class->reset( view );
783 
784 	return( NULL );
785 }
786 
787 /* Trigger the scan method for a view.
788  */
789 void *
view_scan(View * view)790 view_scan( View *view )
791 {
792 	ViewClass *view_class = VIEW_GET_CLASS( view );
793 
794 	if( view_class->scan )
795 		return( view_class->scan( view ) );
796 
797 	return( NULL );
798 }
799 
800 /* Trigger the scrollto method for a view.
801  */
802 void *
view_scrollto(View * view,ModelScrollPosition position)803 view_scrollto( View *view, ModelScrollPosition position )
804 {
805 	ViewClass *view_class = VIEW_GET_CLASS( view );
806 
807 	if( view_class->scrollto )
808 		view_class->scrollto( view, position );
809 
810 	return( NULL );
811 }
812 
813 /* Trigger the layout method for a view.
814  */
815 void *
view_layout(View * view)816 view_layout( View *view )
817 {
818 	ViewClass *view_class = VIEW_GET_CLASS( view );
819 
820 	if( view_class->layout )
821 		view_class->layout( view );
822 
823 	return( NULL );
824 }
825 
826 static void *
view_map_sub(ViewChild * viewchild,view_map_fn fn,void * a,void * b)827 view_map_sub( ViewChild *viewchild, view_map_fn fn, void *a, void *b )
828 {
829 	if( viewchild->child_view )
830 		return( fn( viewchild->child_view, a, b ) );
831 
832 	return( NULL );
833 }
834 
835 /* Map over a view's children.
836  */
837 void *
view_map(View * view,view_map_fn fn,void * a,void * b)838 view_map( View *view, view_map_fn fn, void *a, void *b )
839 {
840 	return( slist_map3( view->managed,
841 		(SListMap3Fn) view_map_sub, (void *) fn, a, b ) );
842 }
843 
844 /* Apply a function to view, and to all it's children.
845  */
846 void *
view_map_all(View * view,view_map_fn fn,void * a)847 view_map_all( View *view, view_map_fn fn, void *a )
848 {
849 	View *result;
850 
851 	if( (result = fn( view, a, NULL )) )
852 		return( result );
853 
854 	return( view_map( view,
855 		(view_map_fn) view_map_all, (void *) fn, a ) );
856 }
857 
858 void
view_save_as_cb(GtkWidget * menu,GtkWidget * host,View * view)859 view_save_as_cb( GtkWidget *menu, GtkWidget *host, View *view )
860 {
861 	Model *model = MODEL( VOBJECT( view )->iobject );
862 
863 	if( IS_FILEMODEL( model ) ) {
864 		iWindow *iwnd = IWINDOW( view_get_toplevel( view ) );
865 
866 		filemodel_inter_saveas( iwnd, FILEMODEL( model ) );
867 	}
868 }
869 
870 void
view_save_cb(GtkWidget * menu,GtkWidget * host,View * view)871 view_save_cb( GtkWidget *menu, GtkWidget *host, View *view )
872 {
873 	Model *model = MODEL( VOBJECT( view )->iobject );
874 
875 	if( IS_FILEMODEL( model ) ) {
876 		iWindow *iwnd = IWINDOW( view_get_toplevel( view ) );
877 
878 		filemodel_inter_save( iwnd, FILEMODEL( model ) );
879 	}
880 }
881 
882 void
view_close_cb(GtkWidget * menu,GtkWidget * host,View * view)883 view_close_cb( GtkWidget *menu, GtkWidget *host, View *view )
884 {
885 	Model *model = MODEL( VOBJECT( view )->iobject );
886 
887 	if( IS_FILEMODEL( model ) ) {
888 		iWindow *iwnd = IWINDOW( view_get_toplevel( view ) );
889 
890 		filemodel_inter_savenclose( iwnd, FILEMODEL( model ) );
891 	}
892 }
893 
894 /* Callback for "activate" on a view.
895  */
896 void
view_activate_cb(View * view)897 view_activate_cb( View *view )
898 {
899 	view_scannable_register( view );
900 	symbol_recalculate_all();
901 }
902 
903 /* Callback for "changed" on a view.
904  */
905 void
view_changed_cb(View * view)906 view_changed_cb( View *view )
907 {
908 	/* Make sure it's on the scannable list.
909 	 */
910 	view_scannable_register( view );
911 }
912 
913 void
view_not_implemented_cb(GtkWidget * menu,GtkWidget * host,View * view)914 view_not_implemented_cb( GtkWidget *menu, GtkWidget *host, View *view )
915 {
916 	error_top( _( "Not implemented." ) );
917 	iwindow_alert( GTK_WIDGET( view ), GTK_MESSAGE_ERROR );
918 }
919 
920 GtkWidget *
view_get_toplevel(View * view)921 view_get_toplevel( View *view )
922 {
923 	while( IS_VIEW( view ) && view->parent )
924 		view = view->parent;
925 
926 	return( gtk_widget_get_toplevel( GTK_WIDGET( view ) ) );
927 }
928 
929 Columnview *
view_get_columnview(View * child)930 view_get_columnview( View *child )
931 {
932 	View *view;
933 
934 	for( view = child; view && !IS_COLUMNVIEW( view ); view = view->parent )
935 		;
936 
937 	if( !view )
938 		return( NULL );
939 
940 	return( COLUMNVIEW( view ) );
941 }
942 
943 /* A view has changed size ... rethink the enclosing column geo. Helps table
944  * to not break.
945  */
946 void *
view_resize(View * view)947 view_resize( View *view )
948 {
949 	Columnview *cview = view_get_columnview( view );
950 
951 	if( cview )
952 		gtk_widget_queue_resize( GTK_WIDGET( cview ) );
953 
954 	return( NULL );
955 }
956