1 /* like a heapmodel, but we represent a class in the heap
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 #include "ip.h"
31 
32 /*
33 #define DEBUG
34  */
35 
36 static HeapmodelClass *parent_class = NULL;
37 
38 void
image_value_init(ImageValue * image,Classmodel * classmodel)39 image_value_init( ImageValue *image, Classmodel *classmodel )
40 {
41 	image->ii = NULL;
42 	image->file_changed_sid = 0;
43 	image->classmodel = classmodel;
44 }
45 
46 void
image_value_destroy(ImageValue * image)47 image_value_destroy( ImageValue *image )
48 {
49 	FREESID( image->file_changed_sid, image->ii );
50 	MANAGED_UNREF( image->ii );
51 }
52 
53 static void
image_value_file_changed_cb(Imageinfo * ii,ImageValue * image)54 image_value_file_changed_cb( Imageinfo *ii, ImageValue *image )
55 {
56 #ifdef DEBUG
57 	printf( "image_value_file_changed_cb: " );
58 	iobject_print( IOBJECT( image->classmodel ) );
59 #endif /*DEBUG*/
60 
61 	if( CALC_RELOAD ) {
62 		Row *row = HEAPMODEL( image->classmodel )->row;
63 
64 		(void) expr_dirty( row->expr, link_serial_new() );
65 		symbol_recalculate_all();
66 	}
67 }
68 
69 void
image_value_set(ImageValue * image,Imageinfo * ii)70 image_value_set( ImageValue *image, Imageinfo *ii )
71 {
72 	image_value_destroy( image );
73 
74 	image->ii = ii;
75 
76 	if( ii ) {
77 		MANAGED_REF( image->ii );
78 		image->file_changed_sid = g_signal_connect(
79 			G_OBJECT( image->ii ), "file_changed",
80 			G_CALLBACK( image_value_file_changed_cb ), image );
81 	}
82 
83 #ifdef DEBUG
84 	printf( "iimage_instance_update: ii = %p\n", ii );
85 #endif /*DEBUG*/
86 }
87 
88 /* Generate a descriptive name for an imagevalue. Used by plot.c etc. as well.
89  */
90 void
image_value_caption(ImageValue * value,VipsBuf * buf)91 image_value_caption( ImageValue *value, VipsBuf *buf )
92 {
93 	Imageinfo *ii = value->ii;
94 	Classmodel *classmodel = value->classmodel;
95 
96 	/* Show the filename if this ii came from a file, otherwise show
97 	 * the class.
98 	 */
99 	if( ii && imageinfo_is_from_file( ii ) && classmodel->filename )
100 		vips_buf_appends( buf, im_skip_dir( classmodel->filename ) );
101 	else if( !heapmodel_name( HEAPMODEL( classmodel ), buf ) )
102 		/* Only if there's no value, I think.
103 		 */
104 		vips_buf_appends( buf, CLASS_IMAGE );
105 }
106 
107 void *
classmodel_get_instance(Classmodel * classmodel)108 classmodel_get_instance( Classmodel *classmodel )
109 {
110 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
111 
112 	if( class && class->get_instance )
113 		return( class->get_instance( classmodel ) );
114 
115 	return( NULL );
116 }
117 
118 static void
classmodel_graphic_save_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)119 classmodel_graphic_save_cb( iWindow *iwnd,
120 	void *client, iWindowNotifyFn nfn, void *sys )
121 {
122 	Filesel *filesel = FILESEL( iwnd );
123 	Classmodel *classmodel = CLASSMODEL( client );
124 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
125 	char *filename;
126 
127 	if( (filename = filesel_get_filename( filesel )) ) {
128 		if( class->graphic_save( classmodel,
129 			GTK_WIDGET( iwnd ), filename ) ) {
130 			IM_SETSTR( classmodel->filename, filename );
131 			iobject_changed( IOBJECT( classmodel ) );
132 
133 			nfn( sys, IWINDOW_YES );
134 		}
135 		else
136 			nfn( sys, IWINDOW_ERROR );
137 
138 		g_free( filename );
139 	}
140 	else
141 		nfn( sys, IWINDOW_ERROR );
142 }
143 
144 void
classmodel_graphic_save(Classmodel * classmodel,GtkWidget * parent)145 classmodel_graphic_save( Classmodel *classmodel, GtkWidget *parent )
146 {
147 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
148 	GtkWidget *filesel;
149 	char txt[100];
150 	VipsBuf buf = VIPS_BUF_STATIC( txt );
151 
152 	if( !class->graphic_save ) {
153 		error_top( _( "Not implemented." ) );
154 		error_sub( _( "_%s() method not implemented for %s." ),
155 			"graphic_save", IOBJECT_GET_CLASS_NAME( classmodel ) );
156 		iwindow_alert( parent, GTK_MESSAGE_ERROR );
157 		return;
158 	}
159 
160 	filesel = filesel_new();
161 	row_qualified_name( HEAPMODEL( classmodel )->row, &buf );
162 	iwindow_set_title( IWINDOW( filesel ), _( "Save %s \"%s\"" ),
163 		IOBJECT_GET_CLASS_NAME( classmodel ), vips_buf_all( &buf ) );
164 	filesel_set_flags( FILESEL( filesel ), TRUE, TRUE );
165 	filesel_set_filetype( FILESEL( filesel ),
166 		class->filetype,
167 		watch_int_get( main_watchgroup, class->filetype_pref, 0 ) );
168 	filesel_set_filetype_pref( FILESEL( filesel ), class->filetype_pref );
169 	iwindow_set_parent( IWINDOW( filesel ), parent );
170 	idialog_set_iobject( IDIALOG( filesel ), IOBJECT( classmodel ) );
171 	filesel_set_done( FILESEL( filesel ),
172 		classmodel_graphic_save_cb, classmodel );
173 	iwindow_build( IWINDOW( filesel ) );
174 
175 	if( classmodel->filename )
176 		filesel_set_filename( FILESEL( filesel ),
177 			classmodel->filename );
178 
179 	gtk_widget_show( GTK_WIDGET( filesel ) );
180 }
181 
182 static void
classmodel_graphic_replace_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)183 classmodel_graphic_replace_cb( iWindow *iwnd,
184 	void *client, iWindowNotifyFn nfn, void *sys )
185 {
186 	Filesel *filesel = FILESEL( iwnd );
187 	Classmodel *classmodel = CLASSMODEL( client );
188 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
189 	char *filename;
190 
191 	if( (filename = filesel_get_filename( filesel )) ) {
192 		if( class->graphic_replace( classmodel,
193 			GTK_WIDGET( iwnd ), filename ) ) {
194 			/* Make sure client stays alive through the
195 			 * recalculate.
196 			 */
197 			g_object_ref( G_OBJECT( classmodel ) );
198 
199 			symbol_recalculate_all();
200 			IM_SETSTR( classmodel->filename, filename );
201 			iobject_changed( IOBJECT( classmodel ) );
202 
203 			g_object_unref( G_OBJECT( classmodel ) );
204 
205 			nfn( sys, IWINDOW_YES );
206 		}
207 		else
208 			nfn( sys, IWINDOW_ERROR );
209 
210 		g_free( filename );
211 	}
212 	else
213 		nfn( sys, IWINDOW_ERROR );
214 }
215 
216 void
classmodel_graphic_replace(Classmodel * classmodel,GtkWidget * parent)217 classmodel_graphic_replace( Classmodel *classmodel, GtkWidget *parent )
218 {
219 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
220 	GtkWidget *filesel;
221 	char txt[100];
222 	VipsBuf buf = VIPS_BUF_STATIC( txt );
223 
224 	if( !class->graphic_replace ) {
225 		error_top( _( "Not implemented." ) );
226 		error_sub( _( "_%s() method not implemented for %s." ),
227 			"graphic_replace",
228 			IOBJECT_GET_CLASS_NAME( classmodel ) );
229 		iwindow_alert( parent, GTK_MESSAGE_ERROR );
230 		return;
231 	}
232 
233 	row_qualified_name( HEAPMODEL( classmodel )->row, &buf );
234 	filesel = filesel_new();
235 	iwindow_set_title( IWINDOW( filesel ), _( "Replace %s \"%s\"" ),
236 		IOBJECT_GET_CLASS_NAME( classmodel ), vips_buf_all( &buf ) );
237 	filesel_set_flags( FILESEL( filesel ), TRUE, FALSE );
238 	filesel_set_filetype( FILESEL( filesel ),
239 		class->filetype,
240 		watch_int_get( main_watchgroup, class->filetype_pref, 0 ) );
241 	filesel_set_filetype_pref( FILESEL( filesel ), class->filetype_pref );
242 	iwindow_set_parent( IWINDOW( filesel ), parent );
243 	idialog_set_iobject( IDIALOG( filesel ), IOBJECT( classmodel ) );
244 	filesel_set_done( FILESEL( filesel ),
245 		classmodel_graphic_replace_cb, classmodel );
246 	iwindow_build( IWINDOW( filesel ) );
247 
248 	if( classmodel->filename )
249 		filesel_set_filename( FILESEL( filesel ),
250 			classmodel->filename );
251 
252 	gtk_widget_show( GTK_WIDGET( filesel ) );
253 }
254 
255 /* Make and break links between classmodels and the iimages displaying them.
256  */
257 static void
classmodel_iimage_link(Classmodel * classmodel,iImage * iimage)258 classmodel_iimage_link( Classmodel *classmodel, iImage *iimage )
259 {
260 	if( !g_slist_find( classmodel->iimages, iimage ) ) {
261 #ifdef DEBUG
262 		printf( "classmodel_iimage_link: linking " );
263 		row_name_print( HEAPMODEL( classmodel )->row );
264 		printf( " to " );
265 		row_name_print( HEAPMODEL( iimage )->row );
266 		printf( "\n" );
267 #endif /*DEBUG*/
268 
269 		iimage->classmodels =
270 			g_slist_prepend( iimage->classmodels, classmodel );
271 		classmodel->iimages =
272 			g_slist_prepend( classmodel->iimages, iimage );
273 	}
274 }
275 
276 void *
classmodel_iimage_unlink(Classmodel * classmodel,iImage * iimage)277 classmodel_iimage_unlink( Classmodel *classmodel, iImage *iimage )
278 {
279 	if( g_slist_find( classmodel->iimages, iimage ) ) {
280 #ifdef DEBUG
281 		printf( "classmodel_iimage_unlink: unlinking " );
282 		row_name_print( HEAPMODEL( classmodel )->row );
283 		printf( " from " );
284 		row_name_print( HEAPMODEL( iimage )->row );
285 		printf( "\n" );
286 #endif /*DEBUG*/
287 
288 		iimage->classmodels =
289 			g_slist_remove( iimage->classmodels, classmodel );
290 		classmodel->iimages =
291 			g_slist_remove( classmodel->iimages, iimage );
292 	}
293 
294 	return( NULL );
295 }
296 
297 static void *
classmodel_iimage_unlink_rev(iImage * iimage,Classmodel * classmodel)298 classmodel_iimage_unlink_rev( iImage *iimage, Classmodel *classmodel )
299 {
300 	return( classmodel_iimage_unlink( classmodel, iimage ) );
301 }
302 
303 typedef struct {
304 	Classmodel *classmodel;
305 	Imageinfo *ii;
306 } ClassmodelSearch;
307 
308 static void *
classmodel_iimage_expr_model(Model * model,ClassmodelSearch * parms)309 classmodel_iimage_expr_model( Model *model, ClassmodelSearch *parms )
310 {
311 	/* Look for iimages which aren't super ... ie. if this is a class
312 	 * derived from Image, display on the derived class, not on the
313 	 * superclass.
314 	 */
315 	if( IS_IIMAGE( model ) &&
316 		HEAPMODEL( model )->row->sym &&
317 		!is_super( HEAPMODEL( model )->row->sym ) &&
318 		!is_this( HEAPMODEL( model )->row->sym ) ) {
319 		iImage *iimage = IIMAGE( model );
320 
321 		if( iimage->value.ii == parms->ii )
322 			classmodel_iimage_link( parms->classmodel, iimage );
323 	}
324 
325 	return( NULL );
326 }
327 
328 /* This classmodel is defined on an Imageinfo recorded as having been the value
329  * of expr ... find an associated iImage, and link to that.
330  */
331 static void *
classmodel_iimage_expr(Expr * expr,ClassmodelSearch * parms)332 classmodel_iimage_expr( Expr *expr, ClassmodelSearch *parms )
333 {
334 	if( expr->row ) {
335 #ifdef DEBUG
336 		printf( "classmodel_iimage_expr: starting for " );
337 		row_name_print( expr->row );
338 		printf( "\n" );
339 #endif /*DEBUG*/
340 
341 		/* Search this part of the tally for an iImage with ii as its
342 		 * derived value, and link to us.
343 		 */
344 		(void) icontainer_map_all( ICONTAINER( expr->row->top_row ),
345 			(icontainer_map_fn) classmodel_iimage_expr_model,
346 			parms );
347 	}
348 
349 	return( NULL );
350 }
351 
352 /* classmodel is defined on ii ... update all the classmodel->iimage links.
353  */
354 void
classmodel_iimage_update(Classmodel * classmodel,Imageinfo * ii)355 classmodel_iimage_update( Classmodel *classmodel, Imageinfo *ii )
356 {
357 	ClassmodelSearch parms;
358 
359 	parms.classmodel = classmodel;
360 	parms.ii = ii;
361 	slist_map( classmodel->iimages,
362 		(SListMapFn) classmodel_iimage_unlink_rev, classmodel );
363 
364 	/* Don't make links for supers/this.
365 	 */
366 	if( HEAPMODEL( classmodel )->row->sym &&
367 		!is_super( HEAPMODEL( classmodel )->row->sym ) &&
368 		!is_this( HEAPMODEL( classmodel )->row->sym ) ) {
369 #ifdef DEBUG
370 		printf( "classmodel_iimage_update: " );
371 		row_name_print( HEAPMODEL( classmodel )->row );
372 		printf( " is defined on ii \"%s\" ... searching for client "
373 			"displays\n", ii->im->filename );
374 #endif /*DEBUG*/
375 		slist_map( imageinfo_expr_which( ii ),
376 			(SListMapFn) classmodel_iimage_expr, &parms );
377 	}
378 }
379 
380 static gboolean
381 classmodel_class_member_new( Classmodel *classmodel,
382 	ClassmodelMember *m, Heap *heap, PElement *out );
383 
384 static gboolean
classmodel_dict_new(Classmodel * classmodel,ClassmodelMember * options,int noptions,Heap * heap,PElement * out)385 classmodel_dict_new( Classmodel *classmodel,
386 	ClassmodelMember *options, int noptions, Heap *heap, PElement *out )
387 {
388 	PElement list = *out;
389 	int i;
390 
391 	/* Make first RHS ... the end of the list.
392 	 */
393 	heap_list_init( &list );
394 
395 	for( i = 0; i < noptions; i++ ) {
396 		PElement pair, key, value;
397 
398 		if( !heap_list_add( heap, &list, &pair ) ||
399 			!heap_list_add( heap, &pair, &key ) ||
400 			!heap_list_add( heap, &pair, &value ) ||
401 			!heap_managedstring_new( heap,
402 				options[i].member_name, &key ) ||
403 			!classmodel_class_member_new( classmodel,
404 				&options[i], heap, &value ) )
405 			return( FALSE );
406 
407 		(void) heap_list_next( &list );
408 	}
409 
410 	return( TRUE );
411 }
412 
413 static gboolean
classmodel_class_member_new(Classmodel * classmodel,ClassmodelMember * m,Heap * heap,PElement * out)414 classmodel_class_member_new( Classmodel *classmodel,
415 	ClassmodelMember *m, Heap *heap, PElement *out )
416 {
417 	switch( m->type ) {
418 	case CLASSMODEL_MEMBER_INT:
419 	case CLASSMODEL_MEMBER_ENUM:
420 		if( !heap_real_new( heap,
421 			G_STRUCT_MEMBER( int, classmodel, m->offset ),
422 			out ) )
423 			return( FALSE );
424 		break;
425 
426 	case CLASSMODEL_MEMBER_BOOLEAN:
427 		if( !heap_bool_new( heap,
428 			G_STRUCT_MEMBER( gboolean, classmodel, m->offset ),
429 			out ) )
430 			return( FALSE );
431 		break;
432 
433 	case CLASSMODEL_MEMBER_DOUBLE:
434 		if( !heap_real_new( heap,
435 			G_STRUCT_MEMBER( double, classmodel, m->offset ),
436 			out ) )
437 			return( FALSE );
438 		break;
439 
440 	case CLASSMODEL_MEMBER_STRING:
441 		if( !heap_managedstring_new( heap,
442 			G_STRUCT_MEMBER( char *, classmodel, m->offset ),
443 			out ) )
444 			return( FALSE );
445 		break;
446 
447 	case CLASSMODEL_MEMBER_STRING_LIST:
448 		if( !heap_lstring_new( heap,
449 			G_STRUCT_MEMBER( GSList *, classmodel, m->offset ),
450 			out ) )
451 			return( FALSE );
452 		break;
453 
454 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
455 		if( !heap_realvec_new( heap, m->extent,
456 			&G_STRUCT_MEMBER( double, classmodel, m->offset ),
457 			out ) )
458 			return( FALSE );
459 		break;
460 
461 	case CLASSMODEL_MEMBER_MATRIX:
462 	{
463 		MatrixValue *value =
464 			&G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset );
465 
466 		if( !heap_matrix_new( heap,
467 			value->width, value->height, value->coeff, out ) )
468 			return( FALSE );
469 		break;
470 	}
471 
472 	case CLASSMODEL_MEMBER_OPTIONS:
473 		if( !classmodel_dict_new( classmodel,
474 			(ClassmodelMember *) m->details, m->extent,
475 			heap, out ) )
476 			return( FALSE );
477 		break;
478 
479 	case CLASSMODEL_MEMBER_IMAGE:
480 	{
481 		ImageValue *value =
482 			&G_STRUCT_MEMBER( ImageValue, classmodel, m->offset );
483 
484 		PEPUTP( out, ELEMENT_MANAGED, value->ii );
485 		break;
486 	}
487 
488 	default:
489 		g_assert( 0 );
490 	}
491 
492 	return( TRUE );
493 }
494 
495 /* Trigger the class_new method for a classmodel ... look for a constructor:
496  * try CLASS_edit, then if that's not defined, try CLASS. Eg.
497  *	A1.Scale_edit from to value
498  * if Scale_edit is not defined, try
499  *	A1.Scale from to value
500  */
501 static gboolean
classmodel_class_instance_new(Classmodel * classmodel)502 classmodel_class_instance_new( Classmodel *classmodel )
503 {
504 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
505 	Row *row = HEAPMODEL( classmodel )->row;
506 	PElement *root = &row->expr->root;
507 	const char *cname = IOBJECT( classmodel )->name;
508 	Reduce *rc = reduce_context;
509 	Heap *heap = rc->heap;
510 
511 	char cname_new[256];
512 	PElement fn;
513 
514 #ifdef DEBUG
515 	printf( "classmodel_class_instance_new: " );
516 	row_name_print( HEAPMODEL( classmodel )->row );
517 	printf( "\n" );
518 #endif /*DEBUG*/
519 
520 	/* Find and build.
521 	 */
522 	im_snprintf( cname_new, 256, "%s_edit", cname );
523 	if( !class_get_member( root, cname_new, NULL, &fn ) ) {
524 		if( !class_get_member( root, cname, NULL, &fn ) )
525 			return( FALSE );
526 	}
527 
528 	if( class->class_new ) {
529 		if( !class->class_new( classmodel, &fn, root ) )
530 			return( FALSE );
531 	}
532 	else {
533 		int i;
534 		PElement rhs;
535 
536 		heap_appl_init( root, &fn );
537 
538 		for( i = 0; i < class->n_members; i++ ) {
539 			if( !heap_appl_add( heap, root, &rhs ) )
540 				return( FALSE );
541 
542 			if( !classmodel_class_member_new( classmodel,
543 				&class->members[i], heap, &rhs ) )
544 				return( FALSE );
545 		}
546 	}
547 
548 	/* Reduce to base type.
549 	 */
550 	if( !reduce_pelement( rc, reduce_spine, root ) )
551 		return( FALSE );
552 
553 	/* We have a new heap struct ... tell everyone to get new pointers.
554 	 */
555 	if( heapmodel_new_heap( HEAPMODEL( row ), root ) )
556 		return( FALSE );
557 
558 	return( TRUE );
559 }
560 
561 static void
classmodel_dispose(GObject * gobject)562 classmodel_dispose( GObject *gobject )
563 {
564 	Classmodel *classmodel;
565 
566 	g_return_if_fail( gobject != NULL );
567 	g_return_if_fail( IS_CLASSMODEL( gobject ) );
568 
569 	classmodel = CLASSMODEL( gobject );
570 
571 	/* My instance destroy stuff.
572 	 */
573 	slist_map( classmodel->iimages,
574 		(SListMapFn) classmodel_iimage_unlink_rev, classmodel );
575 	IM_FREE( classmodel->filename );
576 
577 	G_OBJECT_CLASS( parent_class )->dispose( gobject );
578 }
579 
580 /* We don't want subclases like Group to have an _info() method, since it
581  * will appear in tooltips and the Container _info() is rather annoying.
582  *
583  * Things like iImage define an _info() with useful stuff in.
584  */
585 static void
classmodel_info(iObject * iobject,VipsBuf * buf)586 classmodel_info( iObject *iobject, VipsBuf *buf )
587 {
588 }
589 
590 static void
classmodel_parent_add(iContainer * child)591 classmodel_parent_add( iContainer *child )
592 {
593 	g_assert( IS_CLASSMODEL( child ) );
594 
595 	ICONTAINER_CLASS( parent_class )->parent_add( child );
596 }
597 
598 /* How many widgets we allow for member automation edit.
599  */
600 #define MAX_WIDGETS (10)
601 
602 /* Widgets for classmodel edit.
603  */
604 typedef struct _ClassmodelEdit {
605 	iDialog *idlg;
606 
607 	Classmodel *classmodel;
608 
609 	GtkWidget *widgets[MAX_WIDGETS];
610 } ClassmodelEdit;
611 
612 static gboolean
classmodel_done_member(Classmodel * classmodel,ClassmodelMember * m,GtkWidget * widget)613 classmodel_done_member( Classmodel *classmodel,
614 	ClassmodelMember *m, GtkWidget *widget )
615 {
616 	char txt[256];
617 
618 	switch( m->type ) {
619 	case CLASSMODEL_MEMBER_INT:
620 	case CLASSMODEL_MEMBER_ENUM:
621 		break;
622 
623 	case CLASSMODEL_MEMBER_BOOLEAN:
624 		G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) =
625 			GTK_TOGGLE_BUTTON( widget )->active;
626 		break;
627 
628 	case CLASSMODEL_MEMBER_DOUBLE:
629 		if( !get_geditable_double( widget,
630 			&G_STRUCT_MEMBER( double, classmodel, m->offset ) ) )
631 			return( FALSE );
632 		break;
633 
634 	case CLASSMODEL_MEMBER_STRING:
635 		get_geditable_string( widget, txt, 256 );
636 		IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ),
637 			txt );
638 		break;
639 
640 	case CLASSMODEL_MEMBER_STRING_LIST:
641 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
642 	case CLASSMODEL_MEMBER_MATRIX:
643 	case CLASSMODEL_MEMBER_OPTIONS:
644 	case CLASSMODEL_MEMBER_IMAGE:
645 		break;
646 
647 	default:
648 		g_assert( 0 );
649 	}
650 
651 	return( TRUE );
652 }
653 
654 /* Done button hit.
655  */
656 static void
classmodel_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)657 classmodel_done_cb( iWindow *iwnd, void *client,
658 	iWindowNotifyFn nfn, void *sys )
659 {
660 	ClassmodelEdit *eds = (ClassmodelEdit *) client;
661 	Classmodel *classmodel = eds->classmodel;
662 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
663 	int i;
664 
665 	for( i = 0; i < class->n_members; i++ )
666 		if( !classmodel_done_member( classmodel,
667 			&class->members[i], eds->widgets[i] ) ) {
668 			nfn( sys, IWINDOW_ERROR );
669 			return;
670 		}
671 
672 	/* Rebuild object.
673 	 */
674 	classmodel_update( classmodel );
675 	symbol_recalculate_all();
676 
677 	nfn( sys, IWINDOW_YES );
678 }
679 
680 static GtkWidget *
classmodel_buildedit_member(Classmodel * classmodel,ClassmodelMember * m,iDialog * idlg,GtkWidget * vb,GtkSizeGroup * group)681 classmodel_buildedit_member( Classmodel *classmodel,
682 	ClassmodelMember *m, iDialog *idlg, GtkWidget *vb, GtkSizeGroup *group )
683 {
684 	GtkWidget *widget;
685 
686 	widget = NULL;
687 
688 	switch( m->type ) {
689 	case CLASSMODEL_MEMBER_INT:
690 	case CLASSMODEL_MEMBER_ENUM:
691 		break;
692 
693 	case CLASSMODEL_MEMBER_BOOLEAN:
694 		widget = build_gtoggle( vb, _( m->user_name ) );
695 		gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ),
696 			G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) );
697 		set_tooltip( widget, _( "Set boolean value here" ) );
698 		break;
699 
700 	case CLASSMODEL_MEMBER_DOUBLE:
701 		widget = build_glabeltext4( vb, group, _( m->user_name ) );
702 		idialog_init_entry( idlg, widget,
703 			_( "Enter a floating point number here" ),
704 			"%g",
705 			G_STRUCT_MEMBER( double, classmodel, m->offset ) );
706 		break;
707 
708 	case CLASSMODEL_MEMBER_STRING:
709 		widget = build_glabeltext4( vb, group, _( m->user_name ) );
710 		idialog_init_entry( idlg, widget, _( "Enter a string here" ),
711 			"%s",
712 			G_STRUCT_MEMBER( char *, classmodel, m->offset ) );
713 		break;
714 
715 	case CLASSMODEL_MEMBER_STRING_LIST:
716 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
717 	case CLASSMODEL_MEMBER_MATRIX:
718 	case CLASSMODEL_MEMBER_OPTIONS:
719 	case CLASSMODEL_MEMBER_IMAGE:
720 		break;
721 
722 	default:
723 		g_assert( 0 );
724 	}
725 
726 	return( widget );
727 }
728 
729 /* Build the insides of edit.
730  */
731 static void
classmodel_buildedit(iDialog * idlg,GtkWidget * vb,ClassmodelEdit * eds)732 classmodel_buildedit( iDialog *idlg, GtkWidget *vb, ClassmodelEdit *eds )
733 {
734 	Classmodel *classmodel = eds->classmodel;
735 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
736 	GtkSizeGroup *group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
737 	int i;
738 
739 	for( i = 0; i < class->n_members; i++ )
740 		eds->widgets[i] = classmodel_buildedit_member( classmodel,
741 			&class->members[i], idlg, vb, group );
742 
743         gtk_widget_show_all( vb );
744 
745 	g_object_unref( group );
746 }
747 
748 static void
classmodel_edit(GtkWidget * parent,Model * model)749 classmodel_edit( GtkWidget *parent, Model *model )
750 {
751 	Classmodel *classmodel = CLASSMODEL( model );
752 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
753 
754 	if( class->n_members ) {
755 		GtkWidget *idlg;
756 		ClassmodelEdit *eds = INEW( NULL, ClassmodelEdit );
757 
758 		eds->classmodel = classmodel;
759 
760 		idlg = idialog_new();
761 		/* Expands to eg. "Edit Toggle A1".
762 		 */
763 		iwindow_set_title( IWINDOW( idlg ), _( "Edit %s %s" ),
764 			IOBJECT_GET_CLASS_NAME( model ),
765 			IOBJECT( HEAPMODEL( model )->row )->name );
766 		idialog_set_build( IDIALOG( idlg ),
767 			(iWindowBuildFn) classmodel_buildedit, eds,
768 			NULL, NULL );
769 		idialog_set_callbacks( IDIALOG( idlg ),
770 			iwindow_true_cb, NULL, idialog_free_client, eds );
771 		/* Expands to eg. "Set Toggle".
772 		 */
773 		idialog_add_ok( IDIALOG( idlg ),
774 			classmodel_done_cb, _( "Set %s" ),
775 			IOBJECT_GET_CLASS_NAME( classmodel ) );
776 		iwindow_set_parent( IWINDOW( idlg ), parent );
777 		idialog_set_iobject( IDIALOG( idlg ), IOBJECT( classmodel ) );
778 		iwindow_build( IWINDOW( idlg ) );
779 
780 		gtk_widget_show( GTK_WIDGET( idlg ) );
781 	}
782 }
783 
784 static gboolean
classmodel_save_member(Classmodel * classmodel,ClassmodelMember * m,xmlNode * xthis)785 classmodel_save_member( Classmodel *classmodel,
786 	ClassmodelMember *m, xmlNode *xthis )
787 {
788 	int i;
789 
790 	switch( m->type ) {
791 	case CLASSMODEL_MEMBER_INT:
792 	case CLASSMODEL_MEMBER_ENUM:
793 		if( !set_iprop( xthis, m->save_name,
794 			G_STRUCT_MEMBER( int, classmodel, m->offset ) ) )
795 			return( FALSE );
796 		break;
797 
798 	case CLASSMODEL_MEMBER_BOOLEAN:
799 		if( !set_sprop( xthis, m->save_name, bool_to_char(
800 			G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) ) )
801 			return( FALSE );
802 		break;
803 
804 	case CLASSMODEL_MEMBER_DOUBLE:
805 		if( !set_dprop( xthis, m->save_name,
806 			G_STRUCT_MEMBER( double, classmodel, m->offset ) ) )
807 			return( FALSE );
808 		break;
809 
810 	case CLASSMODEL_MEMBER_STRING:
811 		if( !set_sprop( xthis, m->save_name,
812 			G_STRUCT_MEMBER( char *, classmodel, m->offset ) ) )
813 			return( FALSE );
814 		break;
815 
816 	case CLASSMODEL_MEMBER_STRING_LIST:
817 		if( !set_slprop( xthis, m->save_name,
818 			G_STRUCT_MEMBER( GSList *, classmodel, m->offset ) ) )
819 			return( FALSE );
820 		break;
821 
822 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
823 		for( i = 0; i < m->extent; i++ ) {
824 			char buf[256];
825 
826 			im_snprintf( buf, 256, "%s%d", m->save_name, i );
827 			if( !set_dprop( xthis, buf, (&G_STRUCT_MEMBER( double,
828 				classmodel, m->offset ))[i] ) )
829 				return( FALSE );
830 		}
831 		break;
832 
833 	case CLASSMODEL_MEMBER_MATRIX:
834 	{
835 		MatrixValue *value =
836 			&G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset );
837 		const int n = value->width * value->height;
838 
839 		if( !set_dlprop( xthis, "value", value->coeff, n ) ||
840 			!set_iprop( xthis, "width", value->width ) ||
841 			!set_iprop( xthis, "height", value->height ) )
842 			return( FALSE );
843 
844 		break;
845 	}
846 
847 	case CLASSMODEL_MEMBER_OPTIONS:
848 		for( i = 0; i < m->extent; i++ ) {
849 			ClassmodelMember *options =
850 				(ClassmodelMember *) m->details;
851 
852 			if( !classmodel_save_member( classmodel,
853 				&options[i], xthis ) )
854 				return( FALSE );
855 		}
856 
857 		break;
858 
859 	case CLASSMODEL_MEMBER_IMAGE:
860 		break;
861 
862 	default:
863 		g_assert( 0 );
864 	}
865 
866 	return( TRUE );
867 }
868 
869 static xmlNode *
classmodel_save(Model * model,xmlNode * xnode)870 classmodel_save( Model *model, xmlNode *xnode )
871 {
872 	Classmodel *classmodel = CLASSMODEL( model );
873 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
874 	xmlNode *xthis;
875 	int i;
876 
877 #ifdef DEBUG
878 	printf( "classmodel_save: " );
879 	row_name_print( HEAPMODEL( classmodel )->row );
880 	printf( "\n" );
881 #endif /*DEBUG*/
882 
883 	if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
884 		return( NULL );
885 
886 	if( classmodel->edited )
887 		for( i = 0; i < class->n_members; i++ )
888 			if( !classmodel_save_member( classmodel,
889 				&class->members[i], xthis ) )
890 				return( NULL );
891 
892 	return( xthis );
893 }
894 
895 static gboolean
classmodel_load_member(Classmodel * classmodel,ClassmodelMember * m,xmlNode * xthis)896 classmodel_load_member( Classmodel *classmodel,
897 	ClassmodelMember *m, xmlNode *xthis )
898 {
899 	char buf[MAX_STRSIZE];
900 	gboolean found;
901 	int i;
902 
903 	found = FALSE;
904 
905 	switch( m->type ) {
906 	case CLASSMODEL_MEMBER_INT:
907 		if( get_iprop( xthis, m->save_name,
908 			&G_STRUCT_MEMBER( int, classmodel, m->offset ) ) )
909 			found = TRUE;
910 		break;
911 
912 	case CLASSMODEL_MEMBER_ENUM:
913 	{
914 		int v;
915 
916 		if( get_iprop( xthis, m->save_name, &v ) ) {
917 			v = IM_CLIP( 0, v, m->extent );
918 			G_STRUCT_MEMBER( int, classmodel, m->offset ) = v;
919 			found = TRUE;
920 		}
921 		break;
922 	}
923 
924 	case CLASSMODEL_MEMBER_BOOLEAN:
925 		if( get_bprop( xthis, m->save_name,
926 			&G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) )
927 			found = TRUE;
928 		break;
929 
930 	case CLASSMODEL_MEMBER_DOUBLE:
931 		if( get_dprop( xthis, m->save_name,
932 			&G_STRUCT_MEMBER( double, classmodel, m->offset ) ) )
933 			found = TRUE;
934 		break;
935 
936 	case CLASSMODEL_MEMBER_STRING:
937 		if( get_sprop( xthis, m->save_name, buf, MAX_STRSIZE ) ) {
938 			IM_SETSTR( G_STRUCT_MEMBER( char *,
939 				classmodel, m->offset ), buf );
940 			found = TRUE;
941 		}
942 
943 		/* Nasty: before member automation, we used to always
944 		 * save/load caption, as a member of model. Now caption is
945 		 * only present if the class has it as a automated member.
946 		 * Plus some classes used to not support captions (eg. Scale).
947 		 * So: caption can be missing, even if it should be there. Set
948 		 * a fall-back value.
949 		 */
950 		if( !found && strcmp( m->save_name, "caption" ) == 0 ) {
951 			IM_SETSTR( G_STRUCT_MEMBER( char *,
952 				classmodel, m->offset ), "" );
953 			found = TRUE;
954 		}
955 		break;
956 
957 	case CLASSMODEL_MEMBER_STRING_LIST:
958 	{
959 		GSList *slist;
960 		GSList **member =
961 			&G_STRUCT_MEMBER( GSList *, classmodel, m->offset );
962 
963 		if( get_slprop( xthis, m->member_name, &slist ) ) {
964 			IM_FREEF( slist_free_all, *member );
965 			*member = slist;
966 			found = TRUE;
967 		}
968 
969 		break;
970 	}
971 
972 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
973 		for( i = 0; i < m->extent; i++ ) {
974 			im_snprintf( buf, MAX_STRSIZE,
975 				"%s%d", m->save_name, i );
976 			if( get_dprop( xthis, buf,
977 				&((&G_STRUCT_MEMBER( double,
978 					classmodel, m->offset ))[i]) ) )
979 				found = TRUE;
980 		}
981 		break;
982 
983 	case CLASSMODEL_MEMBER_MATRIX:
984 	{
985 		MatrixValue *value =
986 			&G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset );
987 
988 		if( get_dlprop( xthis, "value", &value->coeff ) &&
989 			get_iprop( xthis, "width", &value->width ) &&
990 			get_iprop( xthis, "height", &value->height ) )
991 			found = TRUE;
992 
993 		break;
994 	}
995 
996 	case CLASSMODEL_MEMBER_OPTIONS:
997 		for( i = 0; i < m->extent; i++ ) {
998 			ClassmodelMember *options =
999 				(ClassmodelMember *) m->details;
1000 
1001 			if( !classmodel_load_member( classmodel,
1002 				&options[i], xthis ) )
1003 				return( FALSE );
1004 		}
1005 
1006 		break;
1007 
1008 	case CLASSMODEL_MEMBER_IMAGE:
1009 		break;
1010 
1011 	default:
1012 		g_assert( 0 );
1013 	}
1014 
1015 	return( found );
1016 }
1017 
1018 static gboolean
classmodel_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xthis)1019 classmodel_load( Model *model,
1020 	ModelLoadState *state, Model *parent, xmlNode *xthis )
1021 {
1022 	Classmodel *classmodel = CLASSMODEL( model );
1023 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
1024 
1025 #ifdef DEBUG
1026 	printf( "classmodel_load: " );
1027 	row_name_print( HEAPMODEL( classmodel )->row );
1028 	printf( "\n" );
1029 #endif /*DEBUG*/
1030 
1031 	/* Only for classes with member automation.
1032 	 */
1033 	if( class->n_members ) {
1034 		gboolean all_found;
1035 		int i;
1036 
1037 		/* Before we mark the graphic as edited, insist all
1038 		 * members have values set. This can be important in
1039 		 * compatibility mode, where the old nip might not have
1040 		 * supported all the members we have.
1041 		 */
1042 		all_found = TRUE;
1043 		for( i = 0; i < class->n_members; i++ )
1044 			all_found &= classmodel_load_member( classmodel,
1045 				&class->members[i], xthis );
1046 		if( all_found )
1047 			classmodel_set_edited( CLASSMODEL( model ), TRUE );
1048 	}
1049 
1050 	return( MODEL_CLASS( parent_class )->load( model,
1051 		state, parent, xthis ) );
1052 }
1053 
1054 static gboolean
1055 classmodel_get_item( Classmodel *classmodel,
1056 	ClassmodelMember *m, PElement *value );
1057 
1058 static void *
classmodel_parse_option(const char * key,PElement * value,Classmodel * classmodel,ClassmodelMember * m)1059 classmodel_parse_option( const char *key, PElement *value,
1060 	Classmodel *classmodel, ClassmodelMember *m )
1061 {
1062 	ClassmodelMember *options = (ClassmodelMember *) m->details;
1063 	int noptions = m->extent;
1064 	int i;
1065 
1066 	for( i = 0; i < noptions; i++ )
1067 		if( strcmp( key, options[i].member_name ) == 0 )
1068 			break;
1069 	if( i == noptions ) {
1070 		error_top( _( "Unknown option." ) );
1071 		error_sub( _( "Option \"%s\" not known." ), key );
1072 
1073 		return( value );
1074 	}
1075 
1076 	if( !classmodel_get_item( classmodel, &options[i], value ) )
1077 		return( value );
1078 
1079 	return( NULL );
1080 }
1081 
1082 static gboolean
classmodel_get_item(Classmodel * classmodel,ClassmodelMember * m,PElement * value)1083 classmodel_get_item( Classmodel *classmodel,
1084 	ClassmodelMember *m, PElement *value )
1085 {
1086 	char buf[MAX_STRSIZE];
1087 	double vec[3];
1088 	int l;
1089 	int i;
1090 	double d;
1091 
1092 	switch( m->type ) {
1093 	case CLASSMODEL_MEMBER_INT:
1094 		if( !heap_get_real( value, &d ) )
1095 			return( FALSE );
1096 		G_STRUCT_MEMBER( int, classmodel, m->offset ) = d;
1097 		break;
1098 
1099 	case CLASSMODEL_MEMBER_ENUM:
1100 		if( !heap_get_real( value, &d ) )
1101 			return( FALSE );
1102 		d = IM_CLIP( 0, d, m->extent );
1103 		G_STRUCT_MEMBER( int, classmodel, m->offset ) = d;
1104 		break;
1105 
1106 	case CLASSMODEL_MEMBER_BOOLEAN:
1107 		if( !heap_get_bool( value,
1108 			&G_STRUCT_MEMBER( gboolean, classmodel, m->offset ) ) )
1109 			return( FALSE );
1110 		break;
1111 
1112 	case CLASSMODEL_MEMBER_DOUBLE:
1113 		if( !heap_get_real( value,
1114 			&G_STRUCT_MEMBER( double, classmodel, m->offset ) ) )
1115 			return( FALSE );
1116 		break;
1117 
1118 	case CLASSMODEL_MEMBER_STRING:
1119 		if( !heap_get_string( value, buf, MAX_STRSIZE ) )
1120 			return( FALSE );
1121 		IM_SETSTR( G_STRUCT_MEMBER( char *, classmodel, m->offset ),
1122 			buf );
1123 		break;
1124 
1125 	case CLASSMODEL_MEMBER_STRING_LIST:
1126 	{
1127 		GSList *slist;
1128 		GSList **member =
1129 			&G_STRUCT_MEMBER( GSList *, classmodel, m->offset );
1130 
1131 		if( !heap_get_lstring( value, &slist ) )
1132 			return( FALSE );
1133 
1134 		IM_FREEF( slist_free_all, *member );
1135 		*member = slist;
1136 
1137 		break;
1138 	}
1139 
1140 	case CLASSMODEL_MEMBER_REALVEC_FIXED:
1141 		g_assert( m->extent < 4 );
1142 
1143 		if( (l = heap_get_realvec( value, vec, m->extent )) < 0 )
1144 			return( FALSE );
1145 		if( l != m->extent ) {
1146 			error_top( _( "Bad value." ) );
1147 			error_sub( _( "%d band value only" ), m->extent );
1148 			return( FALSE );
1149 		}
1150 		for( i = 0; i < m->extent; i++ )
1151 			(&G_STRUCT_MEMBER( double, classmodel, m->offset ))[i] =
1152 				vec[i];
1153 		break;
1154 
1155 	case CLASSMODEL_MEMBER_MATRIX:
1156 	{
1157 		MatrixValue *matrix =
1158 			&G_STRUCT_MEMBER( MatrixValue, classmodel, m->offset );
1159 		int w, h;
1160 
1161 		if( !heap_get_matrix_size( value, &w, &h ) ||
1162 			!matrix_value_resize( matrix, w, h ) ||
1163 			!heap_get_matrix( value,
1164 				matrix->coeff, matrix->width * matrix->height,
1165 				&w, &h ) )
1166 			return( FALSE );
1167 
1168 		break;
1169 	}
1170 
1171 	case CLASSMODEL_MEMBER_OPTIONS:
1172 		/* If there are optional fields, we have to have a reset
1173 		 * method for clearing the ones we don't use.
1174 		 */
1175 		g_assert( CLASSMODEL_GET_CLASS( classmodel )->reset );
1176 
1177 		if( heap_map_dict( value,
1178 			(heap_map_dict_fn) classmodel_parse_option,
1179 			classmodel, m ) )
1180 			return( FALSE );
1181 
1182 		break;
1183 
1184 	case CLASSMODEL_MEMBER_IMAGE:
1185 	{
1186 		ImageValue *image =
1187 			&G_STRUCT_MEMBER( ImageValue, classmodel, m->offset );
1188 		Imageinfo *ii;
1189 
1190 		g_assert( image->classmodel == classmodel );
1191 
1192 		if( !heap_get_image( value, &ii ) )
1193 			return( FALSE );
1194 		image_value_set( image, ii );
1195 
1196 		break;
1197 	}
1198 
1199 	default:
1200 		g_assert( 0 );
1201 	}
1202 
1203 	return( TRUE );
1204 }
1205 
1206 static gboolean
classmodel_update_model_member(Classmodel * classmodel,ClassmodelMember * m,PElement * root)1207 classmodel_update_model_member( Classmodel *classmodel,
1208 	ClassmodelMember *m, PElement *root )
1209 {
1210 	PElement value;
1211 
1212 	if( !class_get_member( root, m->member_name, NULL, &value ) )
1213 		return( FALSE );
1214 
1215 #ifdef DEBUG
1216 	printf( "classmodel_update_model_member: setting %s = ",
1217 		m->member_name );
1218 	pgraph( &value );
1219 #endif /*DEBUG*/
1220 
1221 	if( !classmodel_get_item( classmodel, m, &value ) )
1222 		return( FALSE );
1223 
1224 	return( TRUE );
1225 }
1226 
1227 /* Update all members from the heap. Also used from graph_export_image.
1228  */
1229 gboolean
classmodel_update_members(Classmodel * classmodel,PElement * root)1230 classmodel_update_members( Classmodel *classmodel, PElement *root )
1231 {
1232 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
1233 
1234 	int i;
1235 
1236 	for( i = 0; i < class->n_members; i++ )
1237 		if( !classmodel_update_model_member( classmodel,
1238 			&class->members[i], root ) )
1239 			return( FALSE );
1240 
1241 	if( class->class_get &&
1242 		!class->class_get( classmodel, root ) )
1243 		return( FALSE );
1244 
1245 	return( TRUE );
1246 }
1247 
1248 static void *
classmodel_update_model(Heapmodel * heapmodel)1249 classmodel_update_model( Heapmodel *heapmodel )
1250 {
1251 	Classmodel *classmodel = CLASSMODEL( heapmodel );
1252 	ClassmodelClass *class = CLASSMODEL_GET_CLASS( classmodel );
1253 
1254 #ifdef DEBUG
1255 	printf( "classmodel_update_model: " );
1256 	row_name_print( heapmodel->row );
1257 	printf( "\n" );
1258 #endif /*DEBUG*/
1259 
1260 	/* If necessary, reset model to default.
1261 	 */
1262 	if( class->reset )
1263 		class->reset( classmodel );
1264 
1265 	if( heapmodel->row &&
1266 		heapmodel->row->expr ) {
1267 		Expr *expr = heapmodel->row->expr;
1268 
1269 		if( !heapmodel->modified )
1270 			if( !classmodel_update_members( classmodel,
1271 				&expr->root ) )
1272 				return( classmodel );
1273 	}
1274 
1275 	return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) );
1276 }
1277 
1278 static void *
classmodel_update_heap(Heapmodel * heapmodel)1279 classmodel_update_heap( Heapmodel *heapmodel )
1280 {
1281 	Classmodel *classmodel = CLASSMODEL( heapmodel );
1282 
1283 #ifdef DEBUG
1284 	printf( "classmodel_update_heap: " );
1285 	row_name_print( HEAPMODEL( classmodel )->row );
1286 	printf( "\n" );
1287 #endif /*DEBUG*/
1288 
1289 	/* Nasty: classmodel_class_instance_new() can (indirectly) destroy us.
1290 	 * Wrap a _ref()/_unref() pair around it to make sure we stay alive.
1291 	 */
1292 	g_object_ref( G_OBJECT( heapmodel ) );
1293 
1294 	/* Build a new instance from the model.
1295 	 */
1296 	if( !classmodel_class_instance_new( classmodel ) ) {
1297 		g_object_unref( G_OBJECT( heapmodel ) );
1298 		return( heapmodel );
1299 	}
1300 
1301 	if( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) ) {
1302 		g_object_unref( G_OBJECT( heapmodel ) );
1303 		return( heapmodel );
1304 	}
1305 
1306 	g_object_unref( G_OBJECT( heapmodel ) );
1307 
1308 	return( NULL );
1309 }
1310 
1311 static void *
classmodel_clear_edited(Heapmodel * heapmodel)1312 classmodel_clear_edited( Heapmodel *heapmodel )
1313 {
1314 	Classmodel *classmodel = CLASSMODEL( heapmodel );
1315 
1316 	classmodel_set_edited( classmodel, FALSE );
1317 
1318 	return( HEAPMODEL_CLASS( parent_class )->clear_edited( heapmodel ) );
1319 }
1320 
1321 static gboolean
classmodel_real_class_get(Classmodel * classmodel,PElement * root)1322 classmodel_real_class_get( Classmodel *classmodel, PElement *root )
1323 {
1324 	return( TRUE );
1325 }
1326 
1327 static void
classmodel_class_init(ClassmodelClass * class)1328 classmodel_class_init( ClassmodelClass *class )
1329 {
1330 	GObjectClass *gobject_class = (GObjectClass *) class;
1331 	iObjectClass *iobject_class = (iObjectClass *) class;
1332 	iContainerClass *icontainer_class = (iContainerClass *) class;
1333 	ModelClass *model_class = (ModelClass *) class;
1334 	HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
1335 	ClassmodelClass *classmodel_class = (ClassmodelClass *) class;
1336 
1337 	parent_class = g_type_class_peek_parent( class );
1338 
1339 	/* Init methods.
1340 	 */
1341 	gobject_class->dispose = classmodel_dispose;
1342 
1343 	iobject_class->info = classmodel_info;
1344 
1345 	icontainer_class->parent_add = classmodel_parent_add;
1346 
1347 	model_class->edit = classmodel_edit;
1348 	model_class->save = classmodel_save;
1349 	model_class->load = classmodel_load;
1350 
1351 	heapmodel_class->update_model = classmodel_update_model;
1352 	heapmodel_class->update_heap = classmodel_update_heap;
1353 	heapmodel_class->clear_edited = classmodel_clear_edited;
1354 
1355 	classmodel_class->get_instance = NULL;
1356 
1357 	classmodel_class->class_get = classmodel_real_class_get;
1358 	classmodel_class->class_new = NULL;
1359 
1360 	classmodel_class->graphic_save = NULL;
1361 	classmodel_class->graphic_replace = NULL;
1362 
1363 	classmodel_class->filetype = filesel_type_any;
1364 	classmodel_class->filetype_pref = NULL;
1365 
1366 	classmodel_class->members = NULL;
1367 	classmodel_class->n_members = 0;
1368 }
1369 
1370 static void
classmodel_init(Classmodel * classmodel)1371 classmodel_init( Classmodel *classmodel )
1372 {
1373 	Model *model = MODEL( classmodel );
1374 
1375 	model->display = FALSE;
1376 
1377         classmodel->edited = FALSE;
1378 
1379         classmodel->iimages = NULL;
1380         classmodel->views = NULL;
1381 
1382         classmodel->filename = NULL;
1383 }
1384 
1385 GType
classmodel_get_type(void)1386 classmodel_get_type( void )
1387 {
1388 	static GType type = 0;
1389 
1390 	if( !type ) {
1391 		static const GTypeInfo info = {
1392 			sizeof( ClassmodelClass ),
1393 			NULL,           /* base_init */
1394 			NULL,           /* base_finalize */
1395 			(GClassInitFunc) classmodel_class_init,
1396 			NULL,           /* class_finalize */
1397 			NULL,           /* class_data */
1398 			sizeof( Classmodel ),
1399 			32,             /* n_preallocs */
1400 			(GInstanceInitFunc) classmodel_init,
1401 		};
1402 
1403 		type = g_type_register_static( TYPE_HEAPMODEL,
1404 			"Classmodel", &info, 0 );
1405 	}
1406 
1407 	return( type );
1408 }
1409 
1410 void
classmodel_set_edited(Classmodel * classmodel,gboolean edited)1411 classmodel_set_edited( Classmodel *classmodel, gboolean edited )
1412 {
1413 	if( classmodel->edited != edited ) {
1414 #ifdef DEBUG
1415 		printf( "classmodel_set_edited: " );
1416 		row_name_print( HEAPMODEL( classmodel )->row );
1417 		printf( " %s\n", bool_to_char( edited ) );
1418 #endif /*DEBUG*/
1419 
1420 		classmodel->edited = edited;
1421 		iobject_changed( IOBJECT( classmodel ) );
1422 
1423 		if( HEAPMODEL( classmodel )->row &&
1424 			HEAPMODEL( classmodel )->row->expr )
1425 			expr_dirty( HEAPMODEL( classmodel )->row->expr,
1426 				link_serial_new() );
1427 	}
1428 
1429 	/* Mark eds for application.
1430 	 */
1431 	if( edited )
1432 		heapmodel_set_modified( HEAPMODEL( classmodel ), TRUE );
1433 }
1434 
1435 /* The model has changed: mark for recomp.
1436  */
1437 void
classmodel_update(Classmodel * classmodel)1438 classmodel_update( Classmodel *classmodel )
1439 {
1440 	Row *row = HEAPMODEL( classmodel )->row;
1441 
1442 	/* Eg. for no symol on load.
1443 	 */
1444 	if( !row->expr )
1445 		return;
1446 
1447 #ifdef DEBUG
1448 	printf( "classmodel_update: " );
1449 	row_name_print( HEAPMODEL( classmodel )->row );
1450 	printf( "\n" );
1451 #endif /*DEBUG*/
1452 
1453 	/* classmodel_update_heap() will rebuild us on recomp.
1454 	 */
1455 	classmodel_set_edited( classmodel, TRUE );
1456 	expr_dirty( row->expr, link_serial_new() );
1457 	workspace_set_modified( row->ws, TRUE );
1458 }
1459 
1460 /* Make a new classmodel subtype (eg. TYPE_PATHNAME) and link it on.
1461  */
1462 Classmodel *
classmodel_new_classmodel(GType type,Rhs * rhs)1463 classmodel_new_classmodel( GType type, Rhs *rhs )
1464 {
1465 	Classmodel *classmodel;
1466 
1467 	classmodel = g_object_new( type, NULL );
1468 	icontainer_child_add( ICONTAINER( rhs ), ICONTAINER( classmodel ), -1 );
1469 
1470 	return( classmodel );
1471 }
1472