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