1 /* a text item in a workspace
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 */
33
34 #include "ip.h"
35
36 static HeapmodelClass *parent_class = NULL;
37
38 static void
itext_finalize(GObject * gobject)39 itext_finalize( GObject *gobject )
40 {
41 iText *itext;
42
43 g_return_if_fail( gobject != NULL );
44 g_return_if_fail( IS_ITEXT( gobject ) );
45
46 itext = ITEXT( gobject );
47
48 #ifdef DEBUG
49 printf( "itext_destroy\n" );
50 #endif /*DEBUG*/
51
52 /* My instance destroy stuff.
53 */
54 IM_FREE( itext->formula );
55 IM_FREE( itext->formula_default );
56 vips_buf_destroy( &itext->value );
57 vips_buf_destroy( &itext->decompile );
58
59 G_OBJECT_CLASS( parent_class )->finalize( gobject );
60 }
61
62 static void
itext_info(iObject * iobject,VipsBuf * buf)63 itext_info( iObject *iobject, VipsBuf *buf )
64 {
65 iText *itext = ITEXT( iobject );
66
67 vips_buf_appends( buf, _( "Formula" ) );
68 vips_buf_appendf( buf, ": %s\n", NN( itext->formula ) );
69 }
70
71 /* Fwd ref this.
72 */
73 static gboolean itext_add_element( VipsBuf *buf,
74 PElement *base, gboolean top, gboolean bracket );
75
76 /* Sub-fn of below, callback for list print. Eval and print the item into
77 * the buffer, separating with commas as required.
78 */
79 static void *
itext_add_list(PElement * base,VipsBuf * buf,gboolean * first)80 itext_add_list( PElement *base, VipsBuf *buf, gboolean *first )
81 {
82 Reduce *rc = reduce_context;
83
84 if( *first )
85 *first = FALSE;
86 else
87 vips_buf_appends( buf, ", " );
88
89 /* Reduce the head, and print.
90 */
91 if( !reduce_pelement( rc, reduce_spine, base ) )
92 return( base );
93 if( !itext_add_element( buf, base, FALSE, FALSE ) )
94 return( base );
95
96 /* Buffer full? Abort list print.
97 */
98 if( buf->full )
99 return( base );
100
101 return( NULL );
102 }
103
104 /* Sub-fn of below, callback for string print. Print the chars into the
105 * buffer.
106 */
107 static void *
itext_add_string(PElement * base,VipsBuf * buf)108 itext_add_string( PElement *base, VipsBuf *buf )
109 {
110 Reduce *rc = reduce_context;
111
112 /* Reduce the head, and add the char.
113 */
114 if( !reduce_pelement( rc, reduce_spine, base ) )
115 return( base );
116 if( PEISCHAR( base ) )
117 /* Don't escape chars in string mode.
118 */
119 vips_buf_appendf( buf, "%c", PEGETCHAR( base ) );
120 else {
121 /* Help! Fall back to ordinary item print.
122 */
123 vips_buf_appends( buf, ", " );
124 if( !itext_add_element( buf, base, FALSE, FALSE ) )
125 return( base );
126 }
127
128 /* Buffer full? Abort string print.
129 */
130 if( buf->full )
131 return( base );
132
133 return( NULL );
134 }
135
136 /* Print a char ... we need to escape \n etc.
137 */
138 static void
itext_add_char(int ch,VipsBuf * buf)139 itext_add_char( int ch, VipsBuf *buf )
140 {
141 char in[2];
142 char out[3];
143
144 in[0] = ch;
145 in[1] = '\0';
146 my_strecpy( out, in, FALSE );
147
148 vips_buf_appends( buf, out );
149 }
150
151 /* Print a complex.
152 */
153 static void
itext_add_complex(double rp,double ip,VipsBuf * buf)154 itext_add_complex( double rp, double ip, VipsBuf *buf )
155 {
156 if( PRINT_CARTESIAN )
157 vips_buf_appendf( buf, "(%.12g, %.12g)", rp, ip );
158 else {
159 if( rp == 0 ) {
160 if( ip == 0 )
161 vips_buf_appendf( buf, "0" );
162 else
163 vips_buf_appendf( buf, "%.12gj", ip );
164 }
165 else if( ip == 0 )
166 vips_buf_appendf( buf, "%.12g", rp );
167 else
168 vips_buf_appendf( buf, "%.12g + %.12gj", rp, ip );
169
170 }
171 }
172
173 /* Try to decompile.
174 */
175 static gboolean
itext_decompile_element(VipsBuf * buf,PElement * base,gboolean top)176 itext_decompile_element( VipsBuf *buf, PElement *base, gboolean top )
177 {
178 Reduce *rc = reduce_context;
179 gboolean result;
180
181 /* Set the value label for a tally entry.
182 */
183 if( PEISNOVAL( base ) )
184 vips_buf_appends( buf, _( "no value" ) );
185 else if( PEISREAL( base ) )
186 vips_buf_appendf( buf, "%g", PEGETREAL( base ) );
187 else if( PEISBOOL( base ) )
188 vips_buf_appends( buf, bool_to_char( PEGETBOOL( base ) ) );
189 else if( PEISCHAR( base ) ) {
190 vips_buf_appends( buf, "'" );
191 itext_add_char( (int) PEGETCHAR( base ), buf );
192 vips_buf_appends( buf, "'" );
193 }
194 else if( PEISCOMPLEX( base ) )
195 itext_add_complex( PEGETREALPART( base ), PEGETIMAGPART( base ),
196 buf );
197 else if( PEISMANAGEDSTRING( base ) ) {
198 Managedstring *managedstring = PEGETMANAGEDSTRING( base );
199
200 vips_buf_appendf( buf, "\"%s\"", managedstring->string );
201 }
202 else if( PEISELIST( base ) ) {
203 vips_buf_appends( buf, "[ ]" );
204 }
205 else if( !heap_is_string( base, &result ) )
206 /* Eval error.
207 */
208 return( FALSE );
209 else if( result ) {
210 vips_buf_appends( buf, "\"" );
211 if( heap_map_list( base,
212 (heap_map_list_fn) itext_add_string, buf, NULL ) )
213 return( FALSE );
214 vips_buf_appends( buf, "\"" );
215 }
216 else if( PEISLIST( base ) ) {
217 gboolean first = TRUE;
218
219 vips_buf_appends( buf, "[" );
220 if( heap_map_list( base,
221 (heap_map_list_fn) itext_add_list, buf, &first ) )
222 return( FALSE );
223 vips_buf_appends( buf, "]" );
224 }
225 else if( PEISIMAGE( base ) ) {
226 Imageinfo *ii = PEGETII( base );
227
228 if( !top )
229 vips_buf_appends( buf, "(" );
230
231 if( ii && IOBJECT( ii )->name )
232 vips_buf_appendf( buf, "vips_image \"%s\"",
233 IOBJECT( ii )->name );
234 else
235 vips_buf_appendf( buf, "vips_image <unknown>" );
236
237 if( !top )
238 vips_buf_appends( buf, ")" );
239 }
240 else if( PEISMANAGED( base ) ) {
241 Managed *managed;
242
243 if( !(managed = PEGETMANAGED( base )) )
244 vips_buf_appendf( buf, "<NULL managed>" );
245 else {
246 vips_buf_appendf( buf, "<%s ",
247 G_OBJECT_TYPE_NAME( managed ) );
248 iobject_info( IOBJECT( managed ), buf );
249 vips_buf_appends( buf, ">" );
250 }
251 }
252 else if( PEISCLASS( base ) ) {
253 Compile *compile = PEGETCLASSCOMPILE( base );
254 PElement params;
255 int i;
256
257 if( !top )
258 vips_buf_appends( buf, "(" );
259
260 symbol_qualified_name( compile->sym, buf );
261
262 /* Skip over the secrets, then decompile all the args.
263 */
264 PEGETCLASSSECRET( ¶ms, base );
265 for( i = 0; i < compile->nsecret; i++ ) {
266 HeapNode *hn = PEGETVAL( ¶ms );
267
268 PEPOINTRIGHT( hn, ¶ms );
269 }
270
271 for( i = 0; i < compile->nparam; i++ ) {
272 HeapNode *hn = PEGETVAL( ¶ms );
273 HeapNode *sv = GETLEFT( hn );
274 PElement value;
275
276 PEPOINTRIGHT( sv, &value );
277 vips_buf_appends( buf, " " );
278 if( !itext_decompile_element( buf, &value, FALSE ) )
279 return( FALSE );
280
281 PEPOINTRIGHT( hn, ¶ms );
282 }
283
284 if( !top )
285 vips_buf_appends( buf, ")" );
286
287 }
288 else if( PEISSYMREF( base ) )
289 vips_buf_appends( buf, IOBJECT( PEGETSYMREF( base ) )->name );
290 else if( PEISTAG( base ) )
291 vips_buf_appends( buf, PEGETTAG( base ) );
292 else
293 graph_pelement( rc->heap, buf, base, TRACE_FUNCTIONS );
294
295 return( TRUE );
296 }
297
298 /* Little wrapper ... used for formatting error messages, etc. FALSE for eval
299 * error.
300 */
301 static gboolean
itext_decompile(Reduce * rc,VipsBuf * buf,PElement * root)302 itext_decompile( Reduce *rc, VipsBuf *buf, PElement *root )
303 {
304 /* Evaluate and print off values.
305 */
306 if( !reduce_pelement( rc, reduce_spine, root ) )
307 return( FALSE );
308
309 if( !itext_decompile_element( buf, root, TRUE ) && !buf->full )
310 /* Tally eval failed, and buffer is not full ... must
311 * have been an eval error.
312 */
313 return( FALSE );
314
315 return( TRUE );
316 }
317
318 /* Print function for computed values. top is TRUE only for the very top level
319 * output. bracket means we should bracket compound expressions.
320 */
321 static gboolean
itext_add_element(VipsBuf * buf,PElement * base,gboolean top,gboolean bracket)322 itext_add_element( VipsBuf *buf, PElement *base,
323 gboolean top, gboolean bracket )
324 {
325 gboolean result;
326
327 /* Set the value label for a tally entry.
328 */
329 if( PEISNOVAL( base ) )
330 vips_buf_appends( buf, _( "no value" ) );
331 else if( PEISREAL( base ) )
332 vips_buf_appendf( buf, "%.7g", PEGETREAL( base ) );
333 else if( PEISBOOL( base ) )
334 vips_buf_appends( buf, bool_to_char( PEGETBOOL( base ) ) );
335 else if( PEISCHAR( base ) ) {
336 vips_buf_appends( buf, "'" );
337 itext_add_char( (int) PEGETCHAR( base ), buf );
338 vips_buf_appends( buf, "'" );
339 }
340 else if( PEISCOMPLEX( base ) ) {
341 itext_add_complex( PEGETREALPART( base ), PEGETIMAGPART( base ),
342 buf );
343 }
344 else if( PEISMANAGEDSTRING( base ) ) {
345 Managedstring *managedstring = PEGETMANAGEDSTRING( base );
346
347 if( !top )
348 vips_buf_appends( buf, "\"" );
349 vips_buf_appends( buf, managedstring->string );
350 if( !top )
351 vips_buf_appends( buf, "\"" );
352 }
353 else if( PEISELIST( base ) ) {
354 vips_buf_appends( buf, "[ ]" );
355 }
356 else if( !heap_is_string( base, &result ) )
357 /* Eval error.
358 */
359 return( FALSE );
360 else if( result ) {
361 /* Only generate quotes for non-top-level string objects.
362 */
363 if( !top )
364 vips_buf_appends( buf, "\"" );
365
366 /* Print string contents.
367 */
368 if( heap_map_list( base,
369 (heap_map_list_fn) itext_add_string, buf, NULL ) )
370 return( FALSE );
371
372 if( !top )
373 vips_buf_appends( buf, "\"" );
374 }
375 else if( PEISLIST( base ) ) {
376 gboolean first = TRUE;
377
378 vips_buf_appends( buf, "[" );
379 if( heap_map_list( base,
380 (heap_map_list_fn) itext_add_list, buf, &first ) )
381 return( FALSE );
382 vips_buf_appends( buf, "]" );
383 }
384 else if( PEISIMAGE( base ) ) {
385 vips_buf_appendf( buf, "<" );
386 vips_buf_appendi( buf, imageinfo_get( FALSE, PEGETII( base ) ) );
387 vips_buf_appendf( buf, ">" );
388 }
389 else if( PEISMANAGED( base ) ) {
390 Managed *managed = PEGETMANAGED( base );
391
392 vips_buf_appends( buf, "<" );
393 iobject_info( IOBJECT( managed ), buf );
394 vips_buf_appends( buf, ">" );
395 }
396 else if( PEISCLASS( base ) ) {
397 Compile *compile = PEGETCLASSCOMPILE( base );
398 PElement params;
399 int i;
400
401 if( bracket && compile->nparam )
402 vips_buf_appends( buf, "(" );
403
404 /* Name.
405 */
406 symbol_qualified_name( compile->sym, buf );
407
408 /* Skip over the secrets, then value-ize all the args.
409 */
410 PEGETCLASSSECRET( ¶ms, base );
411 for( i = 0; i < compile->nsecret; i++ ) {
412 HeapNode *hn = PEGETVAL( ¶ms );
413
414 PEPOINTRIGHT( hn, ¶ms );
415 }
416
417 for( i = 0; i < compile->nparam; i++ ) {
418 HeapNode *hn = PEGETVAL( ¶ms );
419 HeapNode *sv = GETLEFT( hn );
420 PElement value;
421
422 PEPOINTRIGHT( sv, &value );
423 vips_buf_appends( buf, " " );
424 if( !itext_add_element( buf, &value, FALSE, TRUE ) )
425 return( FALSE );
426
427 PEPOINTRIGHT( hn, ¶ms );
428 }
429
430 if( bracket && compile->nparam )
431 vips_buf_appends( buf, ")" );
432 }
433 else if( PEISSYMREF( base ) ) {
434 Symbol *sym = PEGETSYMREF( base );
435
436 if( is_scope( sym ) ) {
437 vips_buf_appendf( buf, "<scope '" );
438 symbol_qualified_name( sym, buf );
439 vips_buf_appendf( buf, "'>" );
440 }
441 else {
442 vips_buf_appendf( buf, "<reference to symbol '" ),
443 symbol_qualified_name( sym, buf );
444 vips_buf_appendf( buf, "'>" );
445 }
446 }
447 else if( PEISTAG( base ) )
448 vips_buf_appendf( buf, ".%s", PEGETTAG( base ) );
449 else {
450 vips_buf_appendf( buf, "<" );
451 vips_buf_appends( buf, _( "function" ) );
452 vips_buf_appendf( buf, ">" );
453 }
454
455 return( TRUE );
456 }
457
458 /* Little wrapper ... used for formatting error messages, etc. FALSE for eval
459 * error.
460 */
461 gboolean
itext_value(Reduce * rc,VipsBuf * buf,PElement * root)462 itext_value( Reduce *rc, VipsBuf *buf, PElement *root )
463 {
464 /* Evaluate and print off values.
465 */
466 if( !reduce_pelement( rc, reduce_spine, root ) )
467 return( FALSE );
468
469 if( !itext_add_element( buf, root, TRUE, FALSE ) && !buf->full )
470 /* Tally eval failed, and buffer is not full ... must
471 * have been an eval error.
472 */
473 return( FALSE );
474
475 return( TRUE );
476 }
477
478 /* Same, but everror on eval fail.
479 */
480 void
itext_value_ev(Reduce * rc,VipsBuf * buf,PElement * root)481 itext_value_ev( Reduce *rc, VipsBuf *buf, PElement *root )
482 {
483 if( !itext_value( rc, buf, root ) )
484 reduce_throw( rc );
485 }
486
487 /* Decompile an Expr.
488 */
489 static gboolean
itext_make_decompiled_string(Expr * expr,VipsBuf * buf)490 itext_make_decompiled_string( Expr *expr, VipsBuf *buf )
491 {
492 /* Old error on this expression?
493 */
494 if( expr->err ) {
495 expr_error_get( expr );
496 return( FALSE );
497 }
498
499 /* Dirty? We can't print dirty values, since we might have pointers
500 * to deleted symbols in the heap (if we are dirty because one of our
501 * parents has been deleted).
502
503 FIXME ... this seem a bit restrictive :-( ... could just
504 block reads of symbol pointers instead?
505
506 */
507 if( expr->sym->dirty ) {
508 vips_buf_appendf( buf, _( "Dirty value" ) );
509 return( TRUE );
510 }
511
512 /* Evaluate and print off values.
513 */
514 if( !itext_decompile( reduce_context, buf, &expr->root ) )
515 return( FALSE );
516
517 return( TRUE );
518 }
519
520 /* Make a value string from an Expr.
521 */
522 gboolean
itext_make_value_string(Expr * expr,VipsBuf * buf)523 itext_make_value_string( Expr *expr, VipsBuf *buf )
524 {
525 /* Old error on this expression?
526 */
527 if( expr->err ) {
528 expr_error_get( expr );
529 return( FALSE );
530 }
531
532 /* Dirty? We can't print dirty values, since we might have pointers
533 * to deleted symbols in the heap (if we are dirty because one of our
534 * parents has been deleted).
535
536 FIXME ... this seem a bit restrictive :-( ... could just
537 block reads of symbol pointers instead?
538
539 */
540 if( expr->sym->dirty ) {
541 vips_buf_appendf( buf, _( "Dirty value" ) );
542 return( TRUE );
543 }
544
545 /* Evaluate and print off values.
546 */
547 if( !itext_value( reduce_context, buf, &expr->root ) )
548 return( FALSE );
549
550 return( TRUE );
551 }
552
553 static void *
itext_update_model(Heapmodel * heapmodel)554 itext_update_model( Heapmodel *heapmodel )
555 {
556 iText *itext = ITEXT( heapmodel );
557 Row *row = HEAPMODEL( itext )->row;
558 Expr *expr = row->expr;
559
560 #ifdef DEBUG
561 printf( "itext_update_model: " );
562 row_name_print( row );
563 if( row->sym &&
564 row->sym->dirty )
565 printf( " (dirty)" );
566 printf( "\n" );
567 #endif /*DEBUG*/
568
569 vips_buf_set_dynamic( &itext->value, LINELENGTH );
570 vips_buf_set_dynamic( &itext->decompile, LINELENGTH );
571 if( expr ) {
572 if( !itext_make_value_string( expr, &itext->value ) ||
573 !itext_make_decompiled_string( expr,
574 &itext->decompile ) )
575 expr_error_set( expr );
576 }
577
578 #ifdef DEBUG
579 printf( "itext_update_model: " );
580 row_name_print( row );
581 printf( " has value: %s\n", vips_buf_all( &itext->value ) );
582 #endif /*DEBUG*/
583
584 /* If this is a non-edited row, update the source.
585 */
586 if( !itext->edited || row == row->top_row ) {
587 const char *new_formula;
588
589 if( expr && expr->compile && expr->compile->rhstext )
590 new_formula = expr->compile->rhstext;
591 else
592 new_formula = vips_buf_all( &itext->decompile );
593
594 IM_SETSTR( itext->formula_default, new_formula );
595
596 /* Don't use itext_set_formula(), as we don't want to set
597 * _modified or recomp.
598 */
599 IM_SETSTR( itext->formula, itext->formula_default );
600 }
601
602 return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) );
603 }
604
605 /* Build param lists.
606 */
607 static void *
itext_update_heap_sub(Symbol * sym,VipsBuf * buf)608 itext_update_heap_sub( Symbol *sym, VipsBuf *buf )
609 {
610 vips_buf_appendf( buf, "%s ", IOBJECT( sym )->name );
611
612 return( NULL );
613 }
614
615 /* heapmodel->modified is set ... parse, compile, and mark for recomp.
616 */
617 static void *
itext_update_heap(Heapmodel * heapmodel)618 itext_update_heap( Heapmodel *heapmodel )
619 {
620 iText *itext = ITEXT( heapmodel );
621 Row *row = heapmodel->row;
622 Expr *expr = row->expr;
623
624 #ifdef DEBUG
625 printf( "itext_update_heap: " );
626 row_name_print( HEAPMODEL( itext )->row );
627 printf( "\n" );
628 #endif /*DEBUG*/
629
630 /* We can have no modified text, but come here anyway. For example, we
631 * could try eval, find an error due to an undefined symbol, and have
632 * to retry later. Clearing the row error later will mark us modified,
633 * even though we have no text of our own.
634 */
635 if( itext->formula ) {
636 char txt[MAX_STRSIZE];
637 VipsBuf buf = VIPS_BUF_STATIC( txt );
638 ParseRhsSyntax syntax;
639
640 if( row->sym &&
641 is_super( row->sym ) ) {
642 /* A super member ... special syntax.
643 */
644 vips_buf_appendf( &buf, "%s", itext->formula );
645
646 syntax = PARSE_SUPER;
647 }
648 else {
649 /* Build a new params + '=' + rhs string.
650 */
651 if( expr->compile )
652 (void) slist_map( expr->compile->param,
653 (SListMapFn) itext_update_heap_sub,
654 &buf );
655 vips_buf_appendf( &buf, "= %s;", itext->formula );
656
657 syntax = PARSE_PARAMS;
658 }
659
660 /* Parse and compile.
661 */
662 expr_error_clear( expr );
663 attach_input_string( vips_buf_all( &buf ) );
664 if( !parse_rhs( expr, syntax ) ) {
665 expr_error_set( expr );
666 return( heapmodel );
667 }
668 }
669
670 /* Mark for recomp.
671 */
672 (void) expr_dirty( expr, link_serial_new() );
673
674 return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) );
675 }
676
677 static void *
itext_clear_edited(Heapmodel * heapmodel)678 itext_clear_edited( Heapmodel *heapmodel )
679 {
680 iText *itext = ITEXT( heapmodel );
681
682 #ifdef DEBUG
683 printf( "itext_clear_edited: " );
684 row_name_print( HEAPMODEL( itext )->row );
685 printf( "\n" );
686 #endif /*DEBUG*/
687
688 if( itext->edited ) {
689 itext_set_edited( itext, FALSE );
690
691 /*
692
693 FIXME ... formula_default is not always set for cloned
694 edited rows! fix this properly
695
696 */
697 if( itext->formula_default )
698 itext_set_formula( itext, itext->formula_default );
699 else
700 printf( "itext_clear_edited: FIXME!\n" );
701
702 if( heapmodel->row->expr )
703 expr_dirty( heapmodel->row->expr, link_serial_new() );
704
705 /* Don't clear HEAPMODEL( itext )->modified, we want to make
706 * sure we re-parse and compile the default value to break any
707 * old links we might have.
708 */
709 }
710
711 return( HEAPMODEL_CLASS( parent_class )->clear_edited( heapmodel ) );
712 }
713
714 static void
itext_parent_add(iContainer * child)715 itext_parent_add( iContainer *child )
716 {
717 iText *itext = ITEXT( child );
718 Row *row;
719
720 g_assert( IS_RHS( child->parent ) );
721
722 ICONTAINER_CLASS( parent_class )->parent_add( child );
723
724 row = HEAPMODEL( itext )->row;
725
726 #ifdef DEBUG
727 printf( "itext_new: " );
728 row_name_print( row );
729 printf( "\n" );
730 #endif /*DEBUG*/
731
732 /* Top rows default to edited.
733 */
734 if( row == row->top_row )
735 itext->edited = TRUE;
736 }
737
738 static gboolean
itext_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)739 itext_load( Model *model,
740 ModelLoadState *state, Model *parent, xmlNode *xnode )
741 {
742 iText *itext = ITEXT( model );
743
744 char formula[MAX_STRSIZE];
745 char formula2[MAX_STRSIZE];
746
747 g_assert( IS_RHS( parent ) );
748
749 if( get_sprop( xnode, "formula", formula, MAX_STRSIZE ) ) {
750 model_loadstate_rewrite( state, formula, formula2 );
751 itext_set_formula( itext, formula2 );
752 itext_set_edited( itext, TRUE );
753 }
754
755 return( MODEL_CLASS( parent_class )->load( model,
756 state, parent, xnode ) );
757 }
758
759 static View *
itext_view_new(Model * model,View * parent)760 itext_view_new( Model *model, View *parent )
761 {
762 return( itextview_new() );
763 }
764
765 static xmlNode *
itext_save(Model * model,xmlNode * xnode)766 itext_save( Model *model, xmlNode *xnode )
767 {
768 iText *itext = ITEXT( model );
769 Row *row = HEAPMODEL( model )->row;
770
771 xmlNode *xthis;
772
773 if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
774 return( NULL );
775
776 if( itext->edited || row->top_row == row )
777 if( !set_sprop( xthis, "formula", itext->formula ) )
778 return( NULL );
779
780 return( xthis );
781 }
782
783 static void
itext_class_init(iTextClass * class)784 itext_class_init( iTextClass *class )
785 {
786 GObjectClass *gobject_class = (GObjectClass *) class;
787 iObjectClass *iobject_class = (iObjectClass *) class;
788 iContainerClass *icontainer_class = (iContainerClass *) class;
789 ModelClass *model_class = (ModelClass *) class;
790 HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
791
792 parent_class = g_type_class_peek_parent( class );
793
794 /* Create signals.
795 */
796
797 /* Init methods.
798 */
799 gobject_class->finalize = itext_finalize;
800
801 iobject_class->info = itext_info;
802
803 icontainer_class->parent_add = itext_parent_add;
804
805 model_class->view_new = itext_view_new;
806 model_class->save = itext_save;
807 model_class->load = itext_load;
808
809 heapmodel_class->update_model = itext_update_model;
810 heapmodel_class->update_heap = itext_update_heap;
811 heapmodel_class->clear_edited = itext_clear_edited;
812
813 /* Static init.
814 */
815 model_register_loadable( MODEL_CLASS( class ) );
816 }
817
818 static void
itext_init(iText * itext)819 itext_init( iText *itext )
820 {
821 Model *model = MODEL( itext );
822
823 model->display = FALSE;
824
825 itext->formula = NULL;
826 itext->formula_default = NULL;
827 vips_buf_init( &itext->value );
828 vips_buf_init( &itext->decompile );
829 vips_buf_set_dynamic( &itext->value, LINELENGTH );
830 vips_buf_set_dynamic( &itext->decompile, LINELENGTH );
831 itext->edited = FALSE;
832
833 /* Some defaults changed in _parent_add() above.
834 */
835 }
836
837 GType
itext_get_type(void)838 itext_get_type( void )
839 {
840 static GType type = 0;
841
842 if( !type ) {
843 static const GTypeInfo info = {
844 sizeof( iTextClass ),
845 NULL, /* base_init */
846 NULL, /* base_finalize */
847 (GClassInitFunc) itext_class_init,
848 NULL, /* class_finalize */
849 NULL, /* class_data */
850 sizeof( iText ),
851 32, /* n_preallocs */
852 (GInstanceInitFunc) itext_init,
853 };
854
855 type = g_type_register_static( TYPE_HEAPMODEL,
856 "iText", &info, 0 );
857 }
858
859 return( type );
860 }
861
862 iText *
itext_new(Rhs * rhs)863 itext_new( Rhs *rhs )
864 {
865 iText *itext;
866
867 itext = ITEXT( g_object_new( TYPE_ITEXT, NULL ) );
868 icontainer_child_add( ICONTAINER( rhs ), ICONTAINER( itext ), -1 );
869
870 return( itext );
871 }
872
873 void
itext_set_edited(iText * itext,gboolean edited)874 itext_set_edited( iText *itext, gboolean edited )
875 {
876 Heapmodel *heapmodel = HEAPMODEL( itext );
877
878 if( itext->edited != edited ) {
879 #ifdef DEBUG
880 printf( "itext_set_edited: " );
881 row_name_print( heapmodel->row );
882 printf( " %s\n", bool_to_char( edited ) );
883 #endif /*DEBUG*/
884
885 itext->edited = edited;
886 iobject_changed( IOBJECT( itext ) );
887 }
888
889 if( edited )
890 heapmodel_set_modified( heapmodel, TRUE );
891 }
892
893 gboolean
itext_set_formula(iText * itext,const char * formula)894 itext_set_formula( iText *itext, const char *formula )
895 {
896 if( !itext->formula || strcmp( itext->formula, formula ) != 0 ) {
897 #ifdef DEBUG
898 printf( "itext_set_formula: " );
899 row_name_print( HEAPMODEL( itext )->row );
900 printf( " \"%s\"\n", formula );
901 #endif /*DEBUG*/
902
903 IM_SETSTR( itext->formula, formula );
904
905 heapmodel_set_modified( HEAPMODEL( itext ), TRUE );
906
907 iobject_changed( IOBJECT( itext ) );
908
909 return( TRUE );
910 }
911
912 return( FALSE );
913 }
914