1 /* A row in a workspace ... not a widget, part of subcolumn
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 /* Mad detail.
31 #define DEBUG
32 */
33
34 /* Show each row being calculated.
35 #define DEBUG_ROW
36 */
37
38 /* Making and removing links between rows.
39 #define DEBUG_LINK
40 */
41
42 /* Time row recomp.
43 #define DEBUG_TIME_SORT
44 */
45
46 /* Trace create/destroy.
47 #define DEBUG_NEW
48 */
49
50 /* Dirty/clean stuff.
51 #define DEBUG_DIRTY
52 */
53
54 /* Error set/clear.
55 #define DEBUG_ERROR
56 */
57
58 /* Show row recomp order decisions.
59 #define DEBUG_SORT_VERBOSE
60 #define DEBUG_SORT
61 */
62
63 #include "ip.h"
64
65 static HeapmodelClass *parent_class = NULL;
66
67 static void *
row_map_all_sub(Model * model,row_map_fn fn,void * a,void * b,void * c)68 row_map_all_sub( Model *model, row_map_fn fn, void *a, void *b, void *c )
69 {
70 if( IS_ROW( model ) )
71 return( fn( ROW( model ), a, b, c ) );
72
73 return( NULL );
74 }
75
76 static void *
row_map_all(Row * row,row_map_fn fn,void * a,void * b,void * c)77 row_map_all( Row *row, row_map_fn fn, void *a, void *b, void *c )
78 {
79 return( icontainer_map4_all( ICONTAINER( row ),
80 (icontainer_map4_fn) row_map_all_sub, (void *) fn, a, b, c ) );
81 }
82
83 const char *
row_name(Row * row)84 row_name( Row *row )
85 {
86 if( row->sym )
87 return( IOBJECT( row->sym )->name );
88 else
89 return( IOBJECT( row )->name );
90 }
91
92 static Row *
row_get_parent(Row * row)93 row_get_parent( Row *row )
94 {
95 return( HEAPMODEL( row )->row );
96 }
97
98 /* Make a fully-qualified name for a row's symbol ... walk back up the tally
99 * hierarchy. eg. "A1.fred.x" ... produce a name which will find this row from
100 * a local of context.
101 */
102 void
row_qualified_name_relative(Symbol * context,Row * row,VipsBuf * buf)103 row_qualified_name_relative( Symbol *context, Row *row, VipsBuf *buf )
104 {
105 if( !row_get_parent( row ) ) {
106 if( !row->sym )
107 vips_buf_appends( buf, "(null)" );
108 else
109 symbol_qualified_name_relative( context,
110 row->sym, buf );
111 }
112 else {
113 /* Qualify our parents, then do us.
114 */
115 row_qualified_name_relative( context,
116 row_get_parent( row ), buf );
117 vips_buf_appends( buf, "." );
118 vips_buf_appends( buf, row_name( row ) );
119 }
120 }
121
122 /* Make a fully-qualified name for a row's symbol ... walk back up the tally
123 * hierarchy. eg. "A1.fred.x".
124 */
125 void
row_qualified_name(Row * row,VipsBuf * buf)126 row_qualified_name( Row *row, VipsBuf *buf )
127 {
128 if( row->ws )
129 row_qualified_name_relative( row->ws->sym, row, buf );
130 }
131
132 /* Convenience ... print a row name out, identifying by tally heirarchy.
133 */
134 void *
row_name_print(Row * row)135 row_name_print( Row *row )
136 {
137 if( row ) {
138 char txt[100];
139 VipsBuf buf = VIPS_BUF_STATIC( txt );
140
141 row_qualified_name( row, &buf );
142 printf( "%s ", vips_buf_all( &buf ) );
143 }
144 else
145 printf( "(null)" );
146
147 return( NULL );
148 }
149
150 static void *
row_dirty_clear(Row * row)151 row_dirty_clear( Row *row )
152 {
153 #ifdef DEBUG_DIRTY
154 {
155 Row *top_row = row->top_row;
156
157 if( row->dirty )
158 g_assert( g_slist_find( top_row->recomp, row ) );
159 }
160 #endif /*DEBUG_DIRTY*/
161
162 if( row->dirty ) {
163 Row *top_row = row->top_row;
164
165 row->dirty = FALSE;
166 top_row->recomp = g_slist_remove( top_row->recomp, row );
167
168 #ifdef DEBUG_DIRTY
169 printf( "row_dirty_clear: " );
170 row_name_print( row );
171 printf( "\n" );
172 #endif /*DEBUG_DIRTY*/
173
174 iobject_changed( IOBJECT( row ) );
175 }
176
177 return( NULL );
178 }
179
180 /* Set a single row dirty.
181 */
182 static void *
row_dirty_set_single(Row * row,gboolean clear_error)183 row_dirty_set_single( Row *row, gboolean clear_error )
184 {
185 #ifdef DEBUG_DIRTY
186 {
187 Row *top_row = row->top_row;
188
189 if( row->dirty )
190 g_assert( g_slist_find( top_row->recomp, row ) );
191 if( !row->dirty )
192 g_assert( !g_slist_find( top_row->recomp, row ) );
193 }
194 #endif /*DEBUG_DIRTY*/
195
196 if( !row->dirty ) {
197 Row *top_row = row->top_row;
198
199 row->dirty = TRUE;
200 top_row->recomp = g_slist_prepend( top_row->recomp, row );
201
202 iobject_changed( IOBJECT( row ) );
203
204 #ifdef DEBUG_DIRTY
205 printf( "row_dirty_set_single: " );
206 row_name_print( row );
207 printf( " clear_error = %s\n", bool_to_char( clear_error ) );
208 #endif /*DEBUG_DIRTY*/
209
210 /* Make sure error is clear ... we want to recomp.
211 */
212 if( row->expr && clear_error )
213 expr_error_clear( row->expr );
214 }
215
216 return( NULL );
217 }
218
219 static void *
row_dirty_set_sub(Model * model,gboolean clear_error)220 row_dirty_set_sub( Model *model, gboolean clear_error )
221 {
222 if( IS_ROW( model ) ) {
223 Row *row = ROW( model );
224 Rhs *rhs = row->child_rhs;
225
226 g_assert( !rhs || IS_RHS( rhs ) );
227
228 if( rhs && rhs->itext && ITEXT( rhs->itext )->edited )
229 row_dirty_set_single( row, clear_error );
230 else if( rhs && rhs->graphic &&
231 CLASSMODEL( rhs->graphic )->edited )
232 row_dirty_set_single( row, clear_error );
233 }
234
235 return( NULL );
236 }
237
238 /* When we mark a row dirty, we need to mark any subrows with non-default
239 * values dirty too so that they will get a chance to reapply their edits over
240 * the top of the new value we will make for this row.
241 */
242 static void *
row_dirty_set(Row * row,gboolean clear_error)243 row_dirty_set( Row *row, gboolean clear_error )
244 {
245 row_dirty_set_single( row, clear_error );
246
247 return( icontainer_map_all( ICONTAINER( row ),
248 (icontainer_map_fn) row_dirty_set_sub,
249 GINT_TO_POINTER( clear_error ) ) );
250 }
251
252 /* Mark a row as containing an error ... called from expr_error_set()
253 * ... don't call this directly.
254 */
255 void
row_error_set(Row * row)256 row_error_set( Row *row )
257 {
258 if( !row->err ) {
259 Workspace *ws = row->ws;
260 gboolean was_clear = ws->errors == NULL;
261
262 ws->errors = g_slist_prepend( ws->errors, row );
263 row->err = TRUE;
264
265 #ifdef DEBUG_ERROR
266 printf( "row_error_set: " );
267 row_name_print( row );
268 printf( "\n" );
269 #endif /*DEBUG_ERROR*/
270
271 iobject_changed( IOBJECT( row ) );
272
273 /* First error? State change on workspace.
274 */
275 if( was_clear )
276 iobject_changed( IOBJECT( ws ) );
277
278 /* If this is a local row, mark the top row in error too to end
279 * recomp on this tree.
280 */
281 if( row != row->top_row ) {
282 char txt[100];
283 VipsBuf buf = VIPS_BUF_STATIC( txt );
284
285 row_qualified_name( row, &buf );
286
287 error_top( _( "Error in row." ) );
288 /* Elements are name of row, principal error,
289 * secondary error.
290 */
291 error_sub( _( "Error in row %s: %s\n%s" ),
292 vips_buf_all( &buf ),
293 row->expr->error_top,
294 row->expr->error_sub );
295
296 expr_error_set( row->top_row->expr );
297 }
298 }
299 }
300
301 /* Clear error state ... called from expr_error_clear() ... don't call this
302 * directly.
303 */
304 void
row_error_clear(Row * row)305 row_error_clear( Row *row )
306 {
307 if( row->err ) {
308 Workspace *ws = row->ws;
309
310 ws->errors = g_slist_remove( ws->errors, row );
311 row->err = FALSE;
312
313 iobject_changed( IOBJECT( row ) );
314
315 #ifdef DEBUG_ERROR
316 printf( "row_error_clear: " );
317 row_name_print( row );
318 printf( "\n" );
319 #endif /*DEBUG_ERROR*/
320
321 /* Mark our text modified to make sure we reparse and compile.
322 * The code may contain pointers to dead symbols if we were in
323 * error because they were undefined.
324 */
325 if( row->child_rhs && row->child_rhs->itext )
326 heapmodel_set_modified(
327 HEAPMODEL( row->child_rhs->itext ), TRUE );
328
329 /* All errors gone? Ws changed too.
330 */
331 if( !ws->errors )
332 iobject_changed( IOBJECT( ws ) );
333
334 /* Is this a local row? Clear the top row error as well, in
335 * case it's in error because of us.
336 */
337 if( row != row->top_row && row->top_row->expr ) {
338 expr_error_clear( row->top_row->expr );
339 row_dirty_set( row->top_row, TRUE );
340 }
341 }
342 }
343
344 /* Break a dependency.
345 */
346 static void *
row_link_break(Row * parent,Row * child)347 row_link_break( Row *parent, Row *child )
348 {
349 /* Must be there.
350 */
351 g_assert( g_slist_find( parent->children, child ) &&
352 g_slist_find( child->parents, parent ) );
353
354 parent->children = g_slist_remove( parent->children, child );
355 child->parents = g_slist_remove( child->parents, parent );
356
357 #ifdef DEBUG_LINK
358 printf( "row_link_break: breaking link from " );
359 row_name_print( parent );
360 printf( "to " );
361 row_name_print( child );
362 printf( "\n" );
363 #endif /*DEBUG_LINK*/
364
365 return( NULL );
366 }
367
368 static void *
row_link_break_rev(Row * child,Row * parent)369 row_link_break_rev( Row *child, Row *parent )
370 {
371 return( row_link_break( parent, child ) );
372 }
373
374 static void
row_dispose(GObject * gobject)375 row_dispose( GObject *gobject )
376 {
377 Row *row = ROW( gobject );
378
379 #ifdef DEBUG_NEW
380 /* Can't use row_name_print(), we may not have a parent.
381 */
382 printf( "row_dispose: %s", NN( IOBJECT( row )->name ) );
383 if( row->sym )
384 printf( " (%s)", symbol_name( row->sym ) );
385 printf( "\n" );
386 #endif /*DEBUG_NEW*/
387
388 /* Reset state. Also see row_parent_remove().
389 */
390 row_hide_dependents( row );
391 if( row->expr )
392 expr_error_clear( row->expr );
393 if( row->top_col && row->top_col->last_select == row )
394 row->top_col->last_select = NULL;
395 row_deselect( row );
396
397 /* Break all recomp links.
398 */
399 slist_map( row->parents, (SListMapFn) row_link_break, row );
400 slist_map( row->children, (SListMapFn) row_link_break_rev, row );
401 g_assert( !row->parents && !row->children );
402 (void) slist_map( row->recomp, (SListMapFn) row_dirty_clear, NULL );
403 if( row->top_row )
404 row->top_row->recomp_save =
405 g_slist_remove( row->top_row->recomp_save, row );
406 IM_FREEF( g_slist_free, row->recomp_save );
407
408 g_assert( !row->recomp );
409
410 if( row->expr ) {
411 g_assert( row->expr->row == row );
412
413 /* If we're a local row, we will have a private expr
414 * allocated for us. Junk it.
415 */
416 if( row != row->top_row )
417 icontainer_child_remove( ICONTAINER( row->expr ) );
418 else {
419 /* Top-level row, we were zapping the sym's expr.
420 * Break the link to it.
421 */
422 row->expr->row = NULL;
423 row->expr = NULL;
424 }
425 }
426
427 /* Is this a top-level row? Kill the symbol too. Need to do this after
428 * sorting out row->expr, since otherwise killing the symbol will kill
429 * us again in turn.
430 */
431 if( row == row->top_row )
432 IDESTROY( row->sym );
433
434 G_OBJECT_CLASS( parent_class )->dispose( gobject );
435 }
436
437 static void *
row_add_parent_name(Link * link,VipsBuf * buf)438 row_add_parent_name( Link *link, VipsBuf *buf )
439 {
440 Row *row;
441
442 if( link->parent->expr &&
443 (row = link->parent->expr->row) ) {
444 row_qualified_name_relative( link->child, row, buf );
445 vips_buf_appends( buf, " " );
446 }
447
448 return( NULL );
449 }
450
451 static void *
row_add_child_name(Link * link,VipsBuf * buf)452 row_add_child_name( Link *link, VipsBuf *buf )
453 {
454 Row *row;
455
456 if( link->child->expr &&
457 (row = link->child->expr->row) ) {
458 row_qualified_name_relative( link->parent, row, buf );
459 vips_buf_appends( buf, " " );
460 }
461
462 return( NULL );
463 }
464
465 static void *
row_add_dirty_child_name(Link * link,VipsBuf * buf)466 row_add_dirty_child_name( Link *link, VipsBuf *buf )
467 {
468 if( link->child->dirty ) {
469 symbol_qualified_name_relative( link->parent,
470 link->child, buf );
471 vips_buf_appends( buf, " " );
472 }
473
474 return( NULL );
475 }
476
477 static void
row_info(iObject * iobject,VipsBuf * buf)478 row_info( iObject *iobject, VipsBuf *buf )
479 {
480 Row *row = ROW( iobject );
481
482 vips_buf_appends( buf, _( "Name" ) );
483 vips_buf_appends( buf, ": " );
484 row_qualified_name( row, buf );
485 vips_buf_appends( buf, "\n" );
486
487 if( row->expr )
488 iobject_info( IOBJECT( row->expr ), buf );
489 if( row->child_rhs &&
490 row->child_rhs->itext )
491 iobject_info( IOBJECT( row->child_rhs->itext ), buf );
492 if( row->child_rhs &&
493 row->child_rhs->graphic )
494 iobject_info( IOBJECT( row->child_rhs->graphic ), buf );
495 if( row->top_row->sym ) {
496 if( row->top_row->sym->topchildren ) {
497 row_qualified_name( row, buf );
498 vips_buf_appends( buf, " " );
499 /* Expands to eg. "B1 refers to: B2, B3".
500 */
501 vips_buf_appends( buf, _( "refers to" ) );
502 vips_buf_appends( buf, ": " );
503 slist_map_rev( row->top_row->sym->topchildren,
504 (SListMapFn) row_add_child_name, buf );
505 vips_buf_appends( buf, "\n" );
506 }
507 if( row->top_row->sym->topparents ) {
508 row_qualified_name( row, buf );
509 vips_buf_appends( buf, " " );
510 /* Expands to eg. "B1 is referred to by: B2, B3".
511 */
512 vips_buf_appends( buf, _( "is referred to by" ) );
513 vips_buf_appends( buf, ": " );
514 slist_map_rev( row->top_row->sym->topparents,
515 (SListMapFn) row_add_parent_name, buf );
516 vips_buf_appends( buf, "\n" );
517 }
518 }
519 if( row == row->top_row &&
520 row->sym &&
521 row->sym->dirty ) {
522 Symbol *sym = row->sym;
523
524 if( sym->ndirtychildren ) {
525 row_qualified_name( row, buf );
526 vips_buf_appends( buf, " " );
527 vips_buf_appends( buf, _( "is blocked on" ) );
528 vips_buf_appends( buf, ": " );
529 slist_map_rev( sym->topchildren,
530 (SListMapFn) row_add_dirty_child_name, buf );
531 vips_buf_appends( buf, "\n" );
532 }
533 }
534 }
535
536 static Rhs *
row_get_rhs(Row * row)537 row_get_rhs( Row *row )
538 {
539 g_assert( g_slist_length( ICONTAINER( row )->children ) == 1 );
540
541 return( RHS( ICONTAINER( row )->children->data ) );
542 }
543
544 static void
row_child_add(iContainer * parent,iContainer * child,int pos)545 row_child_add( iContainer *parent, iContainer *child, int pos )
546 {
547 Row *row = ROW( parent );
548
549 ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos );
550
551 /* Update our context.
552 */
553 row->child_rhs = row_get_rhs( row );
554 }
555
556 static Subcolumn *
row_get_subcolumn(Row * row)557 row_get_subcolumn( Row *row )
558 {
559 return( SUBCOLUMN( ICONTAINER( row )->parent ) );
560 }
561
562 static Column *
row_get_column(Row * row)563 row_get_column( Row *row )
564 {
565 Subcolumn *scol = row_get_subcolumn( row );
566
567 if( scol )
568 return( scol->top_col );
569 else
570 return( NULL );
571 }
572
573 /* Search back up the widget hierarchy for the base row for this
574 * row ... eg "A7"->expr->row.
575 */
576 static Row *
row_get_root(Row * row)577 row_get_root( Row *row )
578 {
579 Row *enclosing = row_get_parent( row );
580
581 if( !enclosing )
582 return( row );
583 else
584 return( row_get_root( enclosing ) );
585 }
586
587 Workspace *
row_get_workspace(Row * row)588 row_get_workspace( Row *row )
589 {
590 Column *col = row_get_column( row );
591
592 if( col )
593 return( col->ws );
594 else
595 return( NULL );
596 }
597
598 static void
row_parent_add(iContainer * child)599 row_parent_add( iContainer *child )
600 {
601 Row *row = ROW( child );
602
603 g_assert( IS_SUBCOLUMN( child->parent ) );
604
605 ICONTAINER_CLASS( parent_class )->parent_add( child );
606
607 /* Update our context.
608 */
609 row->scol = row_get_subcolumn( row );
610 row->top_col = row_get_column( row );
611 row->ws = row_get_workspace( row );
612 row->top_row = row_get_root( row );
613 }
614
615 static void
row_parent_remove(iContainer * child)616 row_parent_remove( iContainer *child )
617 {
618 Row *row = ROW( child );
619
620 /* Reset the parts of state which touch our parent.
621 */
622 row_dirty_clear( row );
623 row_deselect( row );
624
625 /* Don't clear error ... we may no longer have the link to expr. See
626 * row_dispose() for that.
627 */
628
629 ICONTAINER_CLASS( parent_class )->parent_remove( child );
630 }
631
632 static View *
row_view_new(Model * model,View * parent)633 row_view_new( Model *model, View *parent )
634 {
635 return( rowview_new() );
636 }
637
638 static void
row_scrollto(Model * model,ModelScrollPosition position)639 row_scrollto( Model *model, ModelScrollPosition position )
640 {
641 Row *row = ROW( model );
642 Column *col = row->top_col;
643
644 /* If our column is closed, there won't be a view to scrollto, ouch!
645 * Need to open the column first, then scroll to that column. We can't
646 * scroll to the exact row, since there's no view for it, and won't be
647 * for a while after we hit the idle loop again.
648 */
649 if( !col->open ) {
650 column_set_open( col, TRUE );
651 column_scrollto( col, position );
652 }
653
654 MODEL_CLASS( parent_class )->scrollto( model, position );
655 }
656
657 static gboolean
row_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)658 row_load( Model *model,
659 ModelLoadState *state, Model *parent, xmlNode *xnode )
660 {
661 Row *row = ROW( model );
662 Subcolumn *scol = SUBCOLUMN( parent );
663
664 char name[256];
665
666 g_assert( IS_SUBCOLUMN( parent ) );
667
668 if( !get_sprop( xnode, "name", name, 256 ) )
669 return( FALSE );
670 IM_SETSTR( IOBJECT( row )->name, name );
671
672 #ifdef DEBUG
673 printf( "row_load: loading row %s (xmlNode %p)\n", name, xnode );
674 #endif /*DEBUG*/
675
676 /* Popup is optional (top level only)
677 */
678 (void) get_bprop( xnode, "popup", &row->popup );
679
680 if( scol->is_top ) {
681 Column *col = scol->top_col;
682 Workspace *ws = col->ws;
683
684 Symbol *sym;
685
686 sym = symbol_new( ws->sym->expr->compile, name );
687 symbol_user_init( sym );
688 (void) compile_new_local( sym->expr );
689 row_link_symbol( row, sym, NULL );
690
691 /* We can't symbol_made() here, we've not parsed our value
692 * yet. See below ... we just make sure we're on the recomp
693 * lists.
694 */
695 }
696
697 if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) )
698 return( FALSE );
699
700 /* If we've loaded a complete row system, mark this row plus any
701 * edited subrows dirty, and make sure this sym is dirty too.
702 */
703 if( scol->is_top ) {
704 row_dirty_set( row, TRUE );
705 expr_dirty( row->sym->expr, link_serial_new() );
706 }
707
708 return( TRUE );
709 }
710
711 /* Should we display this row. Non-displayed rows don't have rhs, don't
712 * appear on the screen, and aren't saved. They do have rows though, so their
713 * dependencies are tracked.
714 *
715 * We work off sym rather than row so that we can work before the row is fully
716 * built.
717 */
718 static gboolean
row_is_displayable(Symbol * sym)719 row_is_displayable( Symbol *sym )
720 {
721 if( is_system( sym ) )
722 return( FALSE );
723 if( sym->expr && sym->expr->compile && sym->expr->compile->nparam > 0 )
724 return( FALSE );
725 if( is_super( sym ) && sym->expr ) {
726 Expr *expr = sym->expr;
727 PElement *root = &expr->root;
728
729 /* Empty superclass.
730 */
731 if( PEISELIST( root ) )
732 return( FALSE );
733 }
734
735 return( TRUE );
736 }
737
738 static xmlNode *
row_save(Model * model,xmlNode * xnode)739 row_save( Model *model, xmlNode *xnode )
740 {
741 Row *row = ROW( model );
742
743 xmlNode *xthis;
744
745 /* Don't save system rows, or empty superclasses.
746 */
747 if( row->sym ) {
748 if( !row_is_displayable( row->sym ) )
749 /* Need to return non-NULL for abort with no error.
750 */
751 return( (xmlNode *) -1 );
752 }
753
754 if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
755 return( NULL );
756
757 /* Top-level only.
758 */
759 if( row->top_row == row )
760 if( !set_sprop( xthis, "popup", bool_to_char( row->popup ) ) )
761 return( NULL );
762 if( !set_sprop( xthis, "name", IOBJECT( row )->name ) )
763 return( NULL );
764
765 return( xthis );
766 }
767
768 static void *
row_clear_to_save(Model * model)769 row_clear_to_save( Model *model )
770 {
771 if( IS_ROW( model ) )
772 ROW( model )->to_save = FALSE;
773
774 return( NULL );
775 }
776
777 static void *
row_set_to_save(Row * row)778 row_set_to_save( Row *row )
779 {
780 Row *enclosing;
781
782 if( !row->to_save ) {
783 row->to_save = TRUE;
784
785 /* All peers must be saved. When we reload, we want to keep
786 * row ordering. If we just save modded row, they'll move to
787 * the front of the row list on reload, since they'll be made
788 * first.
789 */
790 icontainer_map( ICONTAINER( row->scol ),
791 (icontainer_map_fn) row_set_to_save, NULL, NULL );
792
793 /* All rows back up to the top level must also be saved.
794 */
795 for( enclosing = row; enclosing != row->top_row;
796 enclosing = row_get_parent( enclosing ) )
797 row_set_to_save( enclosing );
798 }
799
800 return( NULL );
801 }
802
803 static void *
row_calculate_to_save(Model * model)804 row_calculate_to_save( Model *model )
805 {
806 if( IS_ROW( model ) ) {
807 Row *row = ROW( model );
808 Rhs *rhs = row->child_rhs;
809
810 if( row != row->top_row && rhs && !row->to_save ) {
811 if( rhs->itext && ITEXT( rhs->itext )->edited )
812 row_set_to_save( row );
813 else if( rhs->graphic &&
814 CLASSMODEL( rhs->graphic )->edited )
815 row_set_to_save( row );
816 }
817 }
818
819 return( NULL );
820 }
821
822 static gboolean
row_save_test(Model * model)823 row_save_test( Model *model )
824 {
825 Row *row = ROW( model );
826 Workspace *ws = row->ws;
827 Workspacegroup *wsg = workspace_get_workspacegroup( ws );
828
829 gboolean save;
830
831 if( row == row->top_row ) {
832 /* This is a top-level row ... save unless we're in
833 * only-save-selected mode.
834 */
835 if( wsg->save_type == WORKSPACEGROUP_SAVE_SELECTED )
836 save = row->selected;
837 else
838 save = TRUE;
839
840 /* If we're going to save this row, clear all the to_save
841 * flags, then walk the tree working out which bits we will need
842 * to write.
843 */
844 if( save ) {
845 icontainer_map_all( ICONTAINER( row ),
846 (icontainer_map_fn) row_clear_to_save, NULL );
847 icontainer_map_all( ICONTAINER( row ),
848 (icontainer_map_fn) row_calculate_to_save,
849 NULL );
850 }
851 }
852 else
853 save = row->to_save;
854
855 return( save );
856 }
857
858 static void *
row_new_heap(Heapmodel * heapmodel,PElement * root)859 row_new_heap( Heapmodel *heapmodel, PElement *root )
860 {
861 Row *row = ROW( heapmodel );
862 Expr *expr = row->expr;
863
864 #ifdef DEBUG
865 printf( "row_new_heap: " );
866 row_name_print( row );
867 printf( "\n" );
868
869 printf( "row_new_heap: new value is " );
870 pgraph( root );
871
872 printf( "row_new_heap: top value is " );
873 pgraph( &row->top_row->expr->root );
874 #endif /*DEBUG*/
875
876 if( row_is_displayable( row->sym ) ) {
877 /* Hide superclasses whose constructor starts with "_".
878 */
879 if( is_super( row->sym ) && PEISCLASS( root ) &&
880 *IOBJECT( PEGETCLASSCOMPILE( root )->sym )->name ==
881 '_' )
882 model_display( MODEL( row ), FALSE );
883 }
884
885 /* New value ... reset error state.
886 */
887 expr_error_clear( expr );
888 expr->root = *root;
889 expr_new_value( expr );
890
891 if( row->child_rhs &&
892 heapmodel_new_heap( HEAPMODEL( row->child_rhs ), root ) )
893 return( row );
894
895 /* Class display only for non-param classes.
896 */
897 row->is_class = PEISCLASS( root ) && row->sym->type != SYM_PARAM;
898
899 /* Set the default vis level.
900 */
901 if( row->child_rhs && row->child_rhs->vislevel == -1 ) {
902 PElement member;
903 double value;
904 gboolean is_class;
905
906 if( !heap_is_class( root, &is_class ) )
907 return( row );
908
909 /* If it's a class with a vis hint, use that.
910 */
911 if( is_class &&
912 class_get_member( root, MEMBER_VISLEVEL,
913 NULL, &member ) &&
914 heap_get_real( &member, &value ) )
915 rhs_set_vislevel( row->child_rhs, value );
916
917 /* Non-parameter rows get higher vislevel, except for super.
918 */
919 else if( row->sym->type != SYM_PARAM && !is_super( row->sym ) )
920 rhs_set_vislevel( row->child_rhs, 1 );
921 else
922 rhs_set_vislevel( row->child_rhs, 0 );
923 }
924
925 return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) ); }
926
927 static void *
row_update_model(Heapmodel * heapmodel)928 row_update_model( Heapmodel *heapmodel )
929 {
930 Row *row = ROW( heapmodel );
931
932 if( row->expr )
933 expr_new_value( row->expr );
934
935 return( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) );
936 }
937
938 static void
row_class_init(RowClass * class)939 row_class_init( RowClass *class )
940 {
941 GObjectClass *gobject_class = (GObjectClass *) class;
942 iObjectClass *iobject_class = (iObjectClass *) class;
943 iContainerClass *icontainer_class = (iContainerClass *) class;
944 ModelClass *model_class = (ModelClass *) class;
945 HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
946
947 parent_class = g_type_class_peek_parent( class );
948
949 /* Create signals.
950 */
951
952 /* Init methods.
953 */
954 gobject_class->dispose = row_dispose;
955
956 iobject_class->info = row_info;
957
958 icontainer_class->child_add = row_child_add;
959 icontainer_class->parent_add = row_parent_add;
960 icontainer_class->parent_remove = row_parent_remove;
961
962 model_class->view_new = row_view_new;
963 model_class->scrollto = row_scrollto;
964 model_class->load = row_load;
965 model_class->save = row_save;
966 model_class->save_test = row_save_test;
967
968 heapmodel_class->new_heap = row_new_heap;
969 heapmodel_class->update_model = row_update_model;
970
971 /* Static init.
972 */
973 model_register_loadable( MODEL_CLASS( class ) );
974 }
975
976 static void
row_init(Row * row)977 row_init( Row *row )
978 {
979 #ifdef DEBUG
980 printf( "row_init\n" );
981 #endif /*DEBUG*/
982
983 row->scol = NULL;
984 row->child_rhs = NULL;
985 row->top_col = NULL;
986 row->ws = NULL;
987 row->top_row = NULL;
988
989 row->sym = NULL;
990
991 row->expr = NULL;
992 row->err = FALSE;
993
994 row->selected = FALSE;
995 row->is_class = FALSE;
996 row->popup = POPUP_NEW_ROWS;
997 row->to_save = FALSE;
998
999 /* Init recomp stuff.
1000 */
1001 row->parents = NULL;
1002 row->children = NULL;
1003 row->dirty = FALSE;
1004 row->recomp = NULL;
1005 row->recomp_save = NULL;
1006
1007 row->depend = FALSE;
1008
1009 row->show = ROW_SHOW_NONE;
1010 }
1011
1012 GType
row_get_type(void)1013 row_get_type( void )
1014 {
1015 static GType row_type = 0;
1016
1017 if( !row_type ) {
1018 static const GTypeInfo info = {
1019 sizeof( RowClass ),
1020 NULL, /* base_init */
1021 NULL, /* base_finalize */
1022 (GClassInitFunc) row_class_init,
1023 NULL, /* class_finalize */
1024 NULL, /* class_data */
1025 sizeof( Row ),
1026 32, /* n_preallocs */
1027 (GInstanceInitFunc) row_init,
1028 };
1029
1030 row_type = g_type_register_static( TYPE_HEAPMODEL,
1031 "Row", &info, 0 );
1032 }
1033
1034 return( row_type );
1035 }
1036
1037 /* After making a row and adding it to model tree ... attach the symbol and
1038 * value this row displays.
1039 */
1040 void
row_link_symbol(Row * row,Symbol * sym,PElement * root)1041 row_link_symbol( Row *row, Symbol *sym, PElement *root )
1042 {
1043 g_assert( !row->sym );
1044 g_assert( !row->expr );
1045 g_assert( !sym->expr || !sym->expr->row );
1046
1047 row->sym = sym;
1048
1049 /* Code we display/update ... if this is a top-level row, we
1050 * directly change the symbol's expr. If it's a sub-row, we need a
1051 * cloned expr for us to fiddle with.
1052 */
1053 if( is_top( sym ) ) {
1054 row->expr = sym->expr;
1055 g_assert( !row->expr->row );
1056 row->expr->row = row;
1057 }
1058 else {
1059 row->expr = expr_clone( sym );
1060 row->expr->row = row;
1061
1062 if( root ) {
1063 row->expr->root = *root;
1064 expr_new_value( row->expr );
1065 }
1066 }
1067 }
1068
1069 Row *
row_new(Subcolumn * scol,Symbol * sym,PElement * root)1070 row_new( Subcolumn *scol, Symbol *sym, PElement *root )
1071 {
1072 Row *row = g_object_new( TYPE_ROW, NULL );
1073
1074 #ifdef DEBUG_NEW
1075 printf( "row_new: " );
1076 dump_tiny( sym );
1077 printf( "\n" );
1078 #endif /*DEBUG_NEW*/
1079
1080 /* Don't make a display or a RHS for invisible rows.
1081 */
1082 if( !row_is_displayable( sym ) )
1083 MODEL( row )->display = FALSE;
1084 else
1085 (void) rhs_new( row );
1086
1087 iobject_set( IOBJECT( row ), IOBJECT( sym )->name, NULL );
1088 icontainer_child_add( ICONTAINER( scol ), ICONTAINER( row ), -1 );
1089
1090 row_link_symbol( row, sym, root );
1091
1092 return( row );
1093 }
1094
1095 /* Make a dependency. parent is displaying an expression which
1096 * refers to the symbol being displayed by child.
1097 */
1098 static void *
row_link_make(Row * parent,Row * child)1099 row_link_make( Row *parent, Row *child )
1100 {
1101 /* Already a dependency? Don't make a second link.
1102 */
1103 if( g_slist_find( parent->children, child ) )
1104 return( NULL );
1105
1106 /* Don't link to self (harmless, but pointless too).
1107 */
1108 if( parent == child )
1109 return( NULL );
1110
1111 /* New link, each direction.
1112 */
1113 parent->children = g_slist_prepend( parent->children, child );
1114 child->parents = g_slist_prepend( child->parents, parent );
1115
1116 #ifdef DEBUG_LINK
1117 printf( "row_link_make: " );
1118 row_name_print( parent );
1119 printf( "refers to " );
1120 row_name_print( child );
1121 printf( "\n" );
1122 #endif /*DEBUG_LINK*/
1123
1124 return( NULL );
1125 }
1126
1127 static void *
row_link_build4(Expr * child_expr,Row * row)1128 row_link_build4( Expr *child_expr, Row *row )
1129 {
1130 if( child_expr->row && child_expr->row->top_row == row )
1131 return( child_expr->row );
1132
1133 return( NULL );
1134 }
1135
1136 /* Does child have a display in the same tally heirarchy as row? Make a link!
1137 */
1138 static void *
row_link_build3(Symbol * child,Row * row)1139 row_link_build3( Symbol *child, Row *row )
1140 {
1141 Row *child_row;
1142
1143 child_row = (Row *) icontainer_map( ICONTAINER( child ),
1144 (icontainer_map_fn) row_link_build4, row->top_row, NULL );
1145
1146 if( child_row )
1147 (void) row_link_make( row, child_row );
1148
1149 return( NULL );
1150 }
1151
1152 static void *row_link_build2( Expr *expr, Row *row );
1153
1154 static void *
row_link_build2_sym(Symbol * sym,Row * row)1155 row_link_build2_sym( Symbol *sym, Row *row )
1156 {
1157 if( sym->expr && row_link_build2( sym->expr, row ) )
1158 return( row );
1159
1160 return( NULL );
1161 }
1162
1163 static void *
row_link_build2(Expr * expr,Row * row)1164 row_link_build2( Expr *expr, Row *row )
1165 {
1166 /* Make links to anything expr refers to in this tree.
1167 */
1168 if( expr->compile &&
1169 slist_map( expr->compile->children,
1170 (SListMapFn) row_link_build3, row ) )
1171 return( expr );
1172
1173 /* Recurse for any locals of expr.
1174 * Exception:
1175 *
1176 * f = class {
1177 * g = class {
1178 * a = 12;
1179 * }
1180 * }
1181 *
1182 * zero-arg local classes will have rows anyway, so we don't need to
1183 * check inside them for locals, since we'll do them anyway at the top
1184 * level.
1185 *
1186 * zero-arg hidden classes do need to be checked inside though :-(
1187 * since we will only have a row for the top element.
1188 */
1189 if( expr->compile &&
1190 !(is_class( expr->compile ) && expr->compile->nparam == 0 &&
1191 expr->row && MODEL( expr->row )->display) &&
1192 icontainer_map( ICONTAINER( expr->compile ),
1193 (icontainer_map_fn) row_link_build2_sym, row, NULL ) )
1194 return( expr );
1195
1196 return( NULL );
1197 }
1198
1199 /* Scan a row, adding links for any dependencies we find.
1200 */
1201 static void *
row_link_build(Row * row)1202 row_link_build( Row *row )
1203 {
1204 #ifdef DEBUG_LINK
1205 printf( "row_link_build: " );
1206 row_name_print( row );
1207 printf( "\n" );
1208 #endif /*DEBUG_LINK*/
1209
1210 /* Build new recomp list. Only for class displays.
1211 */
1212 if( !row->scol->is_top && row->expr &&
1213 row_link_build2( row->expr, row ) )
1214 return( row );
1215
1216 return( NULL );
1217 }
1218
1219 /* Remove any links on a row.
1220 */
1221 static void *
row_link_destroy(Row * row)1222 row_link_destroy( Row *row )
1223 {
1224 slist_map( row->children,
1225 (SListMapFn) row_link_break_rev, row );
1226
1227 return( NULL );
1228 }
1229
1230 static void *row_dependent_map_sub( Row *row, row_map_fn fn, void *a );
1231
1232 /* Do this row, and any that depend on it.
1233 */
1234 static void *
row_dependent_mark(Row * row,row_map_fn fn,void * a)1235 row_dependent_mark( Row *row, row_map_fn fn, void *a )
1236 {
1237 void *res;
1238
1239 /* Done this one already?
1240 */
1241 if( row->depend )
1242 return( NULL );
1243
1244 row->depend = TRUE;
1245 if( (res = fn( row, a, NULL, NULL )) )
1246 return( res );
1247
1248 return( row_dependent_map_sub( row, fn, a ) );
1249 }
1250
1251 /* Apply to all dependents of row.
1252 */
1253 static void *
row_dependent_map_sub(Row * row,row_map_fn fn,void * a)1254 row_dependent_map_sub( Row *row, row_map_fn fn, void *a )
1255 {
1256 Row *i;
1257 void *res;
1258
1259 /* Things that refer to us.
1260 */
1261 if( (res = slist_map2( row->parents,
1262 (SListMap2Fn) row_dependent_mark, (void *) fn, a )) )
1263 return( res );
1264
1265 /* Things that refer to our enclosing syms ... eg. if A1.fred.x
1266 * changes, we don't want to recalc A1.fred, we do want to recalc
1267 * anything that refers to A1.fred.
1268 */
1269 for( i = row; (i = HEAPMODEL( i )->row); )
1270 if( (res = row_dependent_map_sub( i, fn, a )) )
1271 return( res );
1272
1273 /* We are not going to spot things that refer to this.us :-( we could
1274 * say anything that depends on "this" depends on us, but that's much
1275 * too broad (and much too slow).
1276
1277 FIXME ... could use dynamic dependency stuff to find things
1278 that refer to this.us?
1279 */
1280
1281 return( NULL );
1282 }
1283
1284 static void *
row_dependent_clear(Row * row)1285 row_dependent_clear( Row *row )
1286 {
1287 row->depend = FALSE;
1288
1289 return( NULL );
1290 }
1291
1292 /* Apply a function to all rows in this tree which depend on this row.
1293 */
1294 void *
row_dependent_map(Row * row,row_map_fn fn,void * a)1295 row_dependent_map( Row *row, row_map_fn fn, void *a )
1296 {
1297 /* Clear the flags we use to spot loops.
1298 */
1299 row_map_all( row->top_row,
1300 (row_map_fn) row_dependent_clear, NULL, NULL, NULL );
1301
1302 return( row_dependent_map_sub( row, fn, a ) );
1303 }
1304
1305 /* This row has changed ... mark all dependents (direct and indirect)
1306 * dirty.
1307 */
1308 void *
row_dirty(Row * row,gboolean clear_error)1309 row_dirty( Row *row, gboolean clear_error )
1310 {
1311 (void) row_dirty_set( row, clear_error );
1312 (void) row_dependent_map( row,
1313 (row_map_fn) row_dirty_set, GINT_TO_POINTER( clear_error ) );
1314
1315 return( NULL );
1316 }
1317
1318 /* This tally has changed ... mark all dependents (but not this one!)
1319 * dirty.
1320 */
1321 void *
row_dirty_intrans(Row * row,gboolean clear_error)1322 row_dirty_intrans( Row *row, gboolean clear_error )
1323 {
1324 (void) row_dependent_map( row,
1325 (row_map_fn) row_dirty_set, GINT_TO_POINTER( clear_error ) );
1326
1327 return( NULL );
1328 }
1329
1330 /* Find the 'depth' of a row ... 0 is top level.
1331 */
1332 static int
row_recomp_depth(Row * row)1333 row_recomp_depth( Row *row )
1334 {
1335 if( row == row->top_row )
1336 return( 0 );
1337
1338 return( 1 + row_recomp_depth( row_get_parent( row ) ) );
1339 }
1340
1341 /* Compare func for row recomp sort.
1342 */
1343 static int
row_recomp_sort_func(Row * a,Row * b)1344 row_recomp_sort_func( Row *a, Row *b )
1345 {
1346 int order;
1347 #ifdef DEBUG_TIME_SORT
1348 static GTimer *sort_func_timer = NULL;
1349
1350 if( !sort_func_timer )
1351 sort_func_timer = g_timer_new();
1352
1353 g_timer_reset( sort_func_timer );
1354 #endif /*DEBUG_TIME_SORT*/
1355
1356 #ifdef DEBUG_SORT_VERBOSE
1357 printf( "row_recomp_sort_func: " );
1358 #endif /*DEBUG_SORT_VERBOSE*/
1359
1360 /* If b depends on a, want a first.
1361 */
1362 if( row_dependent_map( a, (row_map_fn) map_equal, b ) ) {
1363 #ifdef DEBUG_SORT_VERBOSE
1364 row_name_print( a );
1365 printf( "before " );
1366 row_name_print( b );
1367 printf( "(2nd depends on 1st)\n" );
1368 #endif /*DEBUG_SORT_VERBOSE*/
1369
1370 order = -1;
1371 }
1372 else if( row_dependent_map( b, (row_map_fn) map_equal, a ) ) {
1373 #ifdef DEBUG_SORT_VERBOSE
1374 row_name_print( b );
1375 printf( "before " );
1376 row_name_print( a );
1377 printf( "(2nd depends on 1st #2)\n" );
1378 #endif /*DEBUG_SORT_VERBOSE*/
1379
1380 order = 1;
1381 }
1382 else {
1383 int adepth = row_recomp_depth( a );
1384 int bdepth = row_recomp_depth( b );
1385
1386 #ifdef DEBUG_SORT_VERBOSE
1387 if( adepth < bdepth ) {
1388 row_name_print( a );
1389 printf( "before " );
1390 row_name_print( b );
1391 printf( "(1st shallower)\n" );
1392 }
1393 else if( bdepth < adepth ) {
1394 row_name_print( b );
1395 printf( "before " );
1396 row_name_print( a );
1397 printf( "(1st shallower)\n" );
1398 }
1399 else {
1400 row_name_print( a );
1401 printf( "and " );
1402 row_name_print( b );
1403 printf( "independent\n" );
1404 }
1405 #endif /*DEBUG_SORT_VERBOSE*/
1406
1407 /* No dependency ... want shallower first.
1408 */
1409 order = adepth - bdepth;
1410 }
1411
1412 #ifdef DEBUG_TIME_SORT
1413 printf( "row_recomp_sort_func: took %gs\n",
1414 g_timer_elapsed( sort_func_timer, NULL ) );
1415 #endif /*DEBUG_TIME_SORT*/
1416
1417 return( order );
1418 }
1419
1420 /* Insert-sort an slist.
1421 */
1422 static GSList *
row_recomp_sort_slist(GSList * old)1423 row_recomp_sort_slist( GSList *old )
1424 {
1425 GSList *new;
1426 GSList *p;
1427
1428 new = NULL;
1429
1430 for( p = old; p; p = p->next ) {
1431 Row *a = (Row *) p->data;
1432 Row *b;
1433 GSList *q;
1434
1435 for( q = new; q; q = q->next ) {
1436 b = (Row *) q->data;
1437
1438 if( row_recomp_sort_func( a, b ) < 0 )
1439 break;
1440 }
1441
1442 if( q ) {
1443 q->data = a;
1444 q->next = g_slist_prepend( q->next, b );
1445 }
1446 else
1447 new = g_slist_append( new, a );
1448 }
1449
1450 g_slist_free( old );
1451
1452 return( new );
1453 }
1454
1455 /* Sort dirties into recomp order.
1456 */
1457 static void
row_recomp_sort(Row * row)1458 row_recomp_sort( Row *row )
1459 {
1460 #ifdef DEBUG_TIME_SORT
1461 static GTimer *sort_timer = NULL;
1462
1463 if( !sort_timer )
1464 sort_timer = g_timer_new();
1465
1466 g_timer_reset( sort_timer );
1467 #endif /*DEBUG_TIME_SORT*/
1468
1469 g_assert( row == row->top_row );
1470
1471 /* Nope, can't use g_slist_sort(). We have a partial order and
1472 * g_slist_sort() uses an algorithm that assumes a full order. Do a
1473 * simple insert-sort, it'll do enough comparisons that we won't miss
1474 * things.
1475
1476 row->recomp = g_slist_sort( row->recomp,
1477 (GCompareFunc) row_recomp_sort_func );
1478
1479 */
1480 row->recomp = row_recomp_sort_slist( row->recomp );
1481
1482 #ifdef DEBUG_TIME_SORT
1483 printf( "row_recomp_sort: took %gs\n",
1484 g_timer_elapsed( sort_timer, NULL ) );
1485 #endif /*DEBUG_TIME_SORT*/
1486
1487 #ifdef DEBUG_SORT
1488 printf( "row_recomp: sorted dirties are: " );
1489 slist_map( row->recomp, (SListMapFn) row_name_print, NULL );
1490 printf( "\n" );
1491 #endif /*DEBUG_SORT*/
1492 }
1493
1494 static gboolean
row_regenerate(Row * row)1495 row_regenerate( Row *row )
1496 {
1497 Expr *expr = row->expr;
1498 PElement base;
1499
1500 /* Regenerate any compiled code.
1501 */
1502 if( expr->compile ) {
1503 PEPOINTE( &base, &expr->compile->base );
1504
1505 if( !PEISNOVAL( &base ) ) {
1506 PElement *root = &expr->root;
1507
1508 if( row == row->top_row ) {
1509 /* Recalcing base of tally display ... not a
1510 * class member, must be a sym with a value.
1511 */
1512 gboolean res;
1513
1514 res = reduce_regenerate( expr, root );
1515 expr_new_value( expr );
1516
1517 if( !res )
1518 return( FALSE );
1519 }
1520 else {
1521 /* Recalcing a member somewhere inside ...
1522 * regen (member this) pair. Get the "this"
1523 * for the enclosing class instance ... the
1524 * top one won't always be right (eg. for
1525 * local classes); the enclosing one should
1526 * be the same as the most enclosing this.
1527 */
1528 Row *this = row->scol->this;
1529 gboolean res;
1530
1531 res = reduce_regenerate_member( expr,
1532 &this->expr->root, root );
1533 expr_new_value( expr );
1534
1535 if( !res )
1536 return( FALSE );
1537 }
1538
1539 /* We may have made a new class instance ... all our
1540 * children need to update their heap pointers.
1541 */
1542 if( heapmodel_new_heap( HEAPMODEL( row ), root ) )
1543 return( FALSE );
1544 }
1545 }
1546
1547 return( TRUE );
1548 }
1549
1550 static gboolean
row_recomp_row(Row * row)1551 row_recomp_row( Row *row )
1552 {
1553 Rhs *rhs = row->child_rhs;
1554
1555 #ifdef DEBUG
1556 printf( "row_recomp_row: " );
1557 row_name_print( row );
1558 printf( "\n" );
1559 #endif /*DEBUG*/
1560
1561 /* Not much we can do.
1562 */
1563 if( !row->expr )
1564 return( TRUE );
1565
1566 /* Clear old error state.
1567 */
1568 expr_error_clear( row->expr );
1569
1570 /* Parse and compile any changes to our text since we last came through.
1571 */
1572 if( rhs && rhs->itext &&
1573 heapmodel_update_heap( HEAPMODEL( rhs->itext ) ) )
1574 return( FALSE );
1575
1576 /* We're about to zap the graph: make sure this tree of rows has a
1577 * private copy.
1578 */
1579 if( !subcolumn_make_private( row->scol ) )
1580 return( FALSE );
1581
1582 /* Regenerate from the expr.
1583 */
1584 if( !row_regenerate( row ) )
1585 return( FALSE );
1586
1587 /* Reapply any graphic mods.
1588 */
1589 if( rhs && rhs->graphic ) {
1590 Classmodel *classmodel = CLASSMODEL( rhs->graphic );
1591
1592 /* If the graphic is non-default, need to set modified to make
1593 * sure we reapply the changes.
1594 */
1595 if( classmodel->edited )
1596 heapmodel_set_modified( HEAPMODEL( classmodel ), TRUE );
1597
1598 if( heapmodel_update_heap( HEAPMODEL( classmodel ) ) )
1599 return( FALSE );
1600 }
1601
1602 progress_update_tick();
1603
1604 return( TRUE );
1605 }
1606
1607 static void
row_recomp_all(Row * top_row)1608 row_recomp_all( Row *top_row )
1609 {
1610 /* Rebuild all dirty rows.
1611 */
1612 while( !top_row->err && top_row->recomp ) {
1613 Row *dirty_row = ROW( top_row->recomp->data );
1614
1615 #ifdef DEBUG_ROW
1616 static GTimer *timer = NULL;
1617
1618 if( !timer )
1619 timer = g_timer_new();
1620 g_timer_reset( timer );
1621 #endif /*DEBUG_ROW*/
1622
1623 #ifdef DEBUG_ROW
1624 printf( "row_recomp_all: starting " );
1625 row_name_print( dirty_row );
1626 printf( "\n" );
1627 #endif /*DEBUG_ROW*/
1628
1629 if( !row_recomp_row( dirty_row ) ) {
1630 /* This will set top_row->err and end the loop.
1631 */
1632 if( dirty_row->expr )
1633 expr_error_set( dirty_row->expr );
1634 }
1635 else
1636 row_dirty_clear( dirty_row );
1637
1638 #ifdef DEBUG_ROW
1639 printf( "\t%gs\n", g_timer_elapsed( timer, NULL ) );
1640 #endif /*DEBUG_ROW*/
1641
1642 #ifdef DEBUG
1643 printf( "row_recomp_all: after row recomp, top value now " );
1644 pgraph( &top_row->expr->root );
1645 #endif /*DEBUG*/
1646 }
1647 }
1648
1649 void
row_recomp(Row * row)1650 row_recomp( Row *row )
1651 {
1652 Row *top_row = row->top_row;
1653
1654 static GTimer *recomp_timer = NULL;
1655
1656 if( !recomp_timer )
1657 recomp_timer = g_timer_new();
1658
1659 g_timer_reset( recomp_timer );
1660
1661 /* Sort dirties into recomp order.
1662 */
1663 row_recomp_sort( top_row );
1664
1665 /* Take a copy of the recomp list for later testing.
1666 */
1667 IM_FREEF( g_slist_free, top_row->recomp_save );
1668 top_row->recomp_save = g_slist_copy( top_row->recomp );
1669
1670 /* Remove all top-level dependencies.
1671 */
1672 symbol_link_destroy( top_row->sym );
1673
1674 /* Remove any row recomp links we have.
1675 */
1676 (void) row_map_all( top_row,
1677 (row_map_fn) row_link_destroy, NULL, NULL, NULL );
1678
1679 /* Rebuild all dirty rows. This may add some dynamic top links.
1680 */
1681 row_recomp_all( top_row );
1682
1683 /* Our workspace may have been closed in a callback: bail out.
1684 */
1685 if( !top_row->sym )
1686 return;
1687
1688 /* Add all static row links. Have to do this after any
1689 * parsing in row_recomp_all().
1690 */
1691 (void) row_map_all( top_row,
1692 (row_map_fn) row_link_build, NULL, NULL, NULL );
1693
1694 /* Remake all static top-level links.
1695 */
1696 (void) symbol_link_build( top_row->sym );
1697
1698 /* Now we know dependencies ... mark everything dirty again. This may
1699 * pick up stuff we missed last time and may change the order we
1700 * recomp rows in.
1701 *
1702 * Be careful not to wipe out any errors we found on this first pass.
1703 */
1704 slist_map( top_row->recomp_save, (SListMapFn) row_dirty, FALSE );
1705
1706 /* Is this topsym still a leaf? We may have discovered an external
1707 * reference to another dirty top-level sym. We can come back here
1708 * later.
1709 */
1710 if( top_row->sym->ndirtychildren != 0 ) {
1711 IM_FREEF( g_slist_free, top_row->recomp_save );
1712 return;
1713 }
1714
1715 /* Sort dirties into recomp order.
1716 */
1717 row_recomp_sort( top_row );
1718
1719 /* Now: if the recomp list is the same as last time, we don't need to
1720 * recalc again.
1721 */
1722 if( slist_equal( top_row->recomp_save, top_row->recomp ) ) {
1723 /* Provided we didn't abandon recomp on an error, we can
1724 * just mark all rows clean.
1725 */
1726 if( !top_row->err )
1727 slist_map( top_row->recomp,
1728 (SListMapFn) row_dirty_clear, NULL );
1729 }
1730 else {
1731 #ifdef DEBUG_DIRTY
1732 printf( "row_recomp: recomp list has changed ... pass 2\n" );
1733 #endif /*DEBUG_DIRTY*/
1734
1735 /* Rebuild all dirty rows in a second pass.
1736 */
1737 row_recomp_all( top_row );
1738
1739 /* Our workspace may have been closed in a callback: bail out.
1740 */
1741 if( !top_row->sym )
1742 return;
1743 }
1744
1745 IM_FREEF( g_slist_free, top_row->recomp_save );
1746
1747 /* The symbol can be cleared as well.
1748 */
1749 if( !top_row->err )
1750 symbol_dirty_clear( top_row->sym );
1751
1752 /* Now we're clean, all models can update from the heap. Rows
1753 * containing errors can have bad pointers in, so careful.
1754 */
1755 if( !top_row->err && icontainer_map_all( ICONTAINER( top_row ),
1756 (icontainer_map_fn) heapmodel_update_model, NULL ) )
1757 expr_error_set( top_row->expr );
1758
1759 if( main_option_profile ) {
1760 char txt[100];
1761 VipsBuf buf = VIPS_BUF_STATIC( txt );
1762 Symbol *context = symbol_get_parent( top_row->ws->sym );
1763
1764 row_qualified_name_relative( context, top_row, &buf );
1765 printf( "%s\t%g\n", vips_buf_all( &buf ),
1766 g_timer_elapsed( recomp_timer, NULL ) );
1767 }
1768
1769 #ifdef DEBUG
1770 printf( "row_recomp: value of " );
1771 row_name_print( top_row );
1772 printf( "is " );
1773 pgraph( &top_row->expr->root );
1774 #endif /*DEBUG*/
1775 }
1776
1777 /* Test, suitable for mapping.
1778 */
1779 void *
row_is_selected(Row * row)1780 row_is_selected( Row *row )
1781 {
1782 if( row->selected )
1783 return( row );
1784
1785 return( NULL );
1786 }
1787
1788 /* Deselect a row.
1789 */
1790 void *
row_deselect(Row * row)1791 row_deselect( Row *row )
1792 {
1793 Workspace *ws = row->ws;
1794
1795 if( !row->selected )
1796 return( NULL );
1797
1798 g_assert( ws && IS_WORKSPACE( ws ) );
1799 g_assert( g_slist_find( ws->selected, row ) );
1800
1801 ws->selected = g_slist_remove( ws->selected, row );
1802 row->selected = FALSE;
1803
1804 /* Hack: if this is a matrix with selected cells, deselect the matrix
1805 * sellection too. We should really have a row method for this I
1806 * guess :-( See also workspace_selected_names_sub().
1807 */
1808 if( row->child_rhs && row->child_rhs->graphic &&
1809 IS_MATRIX( row->child_rhs->graphic ) &&
1810 MATRIX( row->child_rhs->graphic )->selected )
1811 matrix_deselect( MATRIX( row->child_rhs->graphic ) );
1812
1813 iobject_changed( IOBJECT( row ) );
1814 iobject_changed( IOBJECT( ws ) );
1815
1816 return( NULL );
1817 }
1818
1819 /* Select a row.
1820 */
1821 static void
row_select2(Row * row)1822 row_select2( Row *row )
1823 {
1824 if( !row->selected ) {
1825 Workspace *ws = row->ws;
1826
1827 row->selected = TRUE;
1828 ws->selected = g_slist_append( ws->selected, row );
1829
1830 iobject_changed( IOBJECT( row ) );
1831 iobject_changed( IOBJECT( ws ) );
1832 }
1833 }
1834
1835 /* Make sure a row is selected ... used for (eg.) select changed on gktsheet.
1836 * No deselection.
1837 */
1838 void *
row_select_ensure(Row * row)1839 row_select_ensure( Row *row )
1840 {
1841 row_select2( row );
1842
1843 /* Note for extend select.
1844 */
1845 row->top_col->last_select = row;
1846
1847 return( NULL );
1848 }
1849
1850 /* Select a row, deselecting others first.
1851 */
1852 void *
row_select(Row * row)1853 row_select( Row *row )
1854 {
1855 Workspace *ws = row->ws;
1856
1857 workspace_deselect_all( ws );
1858 row_select2( row );
1859
1860 /* Note for extend select.
1861 */
1862 row->top_col->last_select = row;
1863
1864 return( NULL );
1865 }
1866
1867 /* Extend the previous selection.
1868 */
1869 void *
row_select_extend(Row * row)1870 row_select_extend( Row *row )
1871 {
1872 Column *col = row->top_col;
1873 Row *last_select = col->last_select;
1874
1875 /* Range select if there was a previous selection, and it was in the
1876 * same subcolumn.
1877 */
1878 if( last_select && row->scol == last_select->scol ) {
1879 Subcolumn *scol = row->scol;
1880 GSList *rows = ICONTAINER( scol )->children;
1881 int pos = g_slist_index( rows, row );
1882 int pos_last = g_slist_index( rows, last_select );
1883 int step = pos > pos_last ? 1 : -1;
1884 int i;
1885
1886 g_assert( pos != -1 && pos_last != -1 );
1887
1888 for( i = pos_last; i != pos + step; i += step )
1889 row_select2( ROW( g_slist_nth_data( rows, i ) ) );
1890 }
1891 else
1892 row_select2( row );
1893
1894 /* Note for extend select.
1895 */
1896 col->last_select = row;
1897
1898 return( NULL );
1899 }
1900
1901 /* Toggle a selection.
1902 */
1903 void *
row_select_toggle(Row * row)1904 row_select_toggle( Row *row )
1905 {
1906 if( row->selected ) {
1907 row_deselect( row );
1908 row->top_col->last_select = NULL;
1909 }
1910 else {
1911 row_select2( row );
1912 row->top_col->last_select = row;
1913 }
1914
1915 return( NULL );
1916 }
1917
1918 /* Do a select action using a modifier.
1919 */
1920 void
row_select_modifier(Row * row,guint state)1921 row_select_modifier( Row *row, guint state )
1922 {
1923 if( state & GDK_CONTROL_MASK )
1924 row_select_toggle( row );
1925 else if( state & GDK_SHIFT_MASK )
1926 row_select_extend( row );
1927 else
1928 row_select( row );
1929 }
1930
1931 static void
row_set_show(Row * row,RowShowState show)1932 row_set_show( Row *row, RowShowState show )
1933 {
1934 if( row->show != show ) {
1935 row->show = show;
1936 iobject_changed( IOBJECT( row ) );
1937 }
1938 }
1939
1940 static void *
row_show_parent(Link * link,RowShowState show)1941 row_show_parent( Link *link, RowShowState show )
1942 {
1943 if( link->parent->expr && link->parent->expr->row )
1944 row_set_show( link->parent->expr->row, show );
1945
1946 return( NULL );
1947 }
1948
1949 static void *
row_show_child(Link * link,RowShowState show)1950 row_show_child( Link *link, RowShowState show )
1951 {
1952 if( link->child->expr && link->child->expr->row )
1953 row_set_show( link->child->expr->row, show );
1954
1955 return( NULL );
1956 }
1957
1958 void
row_show_dependents(Row * row)1959 row_show_dependents( Row *row )
1960 {
1961 Symbol *topsym = row->top_row->sym;
1962
1963 #ifdef DEBUG
1964 printf( "row_show_dependents: " );
1965 row_name_print( row );
1966 printf( "\n" );
1967 #endif /*DEBUG*/
1968
1969 if( topsym ) {
1970 slist_map( topsym->topparents,
1971 (SListMapFn) row_show_parent,
1972 GUINT_TO_POINTER( ROW_SHOW_PARENT ) );
1973 slist_map( topsym->topchildren,
1974 (SListMapFn) row_show_child,
1975 GUINT_TO_POINTER( ROW_SHOW_CHILD ) );
1976 }
1977 }
1978
1979 void
row_hide_dependents(Row * row)1980 row_hide_dependents( Row *row )
1981 {
1982 Symbol *topsym;
1983
1984 #ifdef DEBUG
1985 printf( "row_hide_dependents: " );
1986 row_name_print( row );
1987 printf( "\n" );
1988 #endif /*DEBUG*/
1989
1990 if( row->top_row && (topsym = row->top_row->sym) ) {
1991 slist_map( topsym->topparents,
1992 (SListMapFn) row_show_parent,
1993 GUINT_TO_POINTER( ROW_SHOW_NONE ) );
1994 slist_map( topsym->topchildren,
1995 (SListMapFn) row_show_child,
1996 GUINT_TO_POINTER( ROW_SHOW_NONE ) );
1997 }
1998 }
1999
2000 /* Set help for a row. Used by rowview and itextview etc. on mouseover.
2001 */
2002 void
row_set_status(Row * row)2003 row_set_status( Row *row )
2004 {
2005 Expr *expr = row->expr;
2006
2007 char txt[MAX_LINELENGTH];
2008 VipsBuf buf = VIPS_BUF_STATIC( txt );
2009
2010 /* No symbol? eg. on load error.
2011 */
2012 if( !expr )
2013 return;
2014
2015 row_qualified_name( row, &buf );
2016
2017 if( expr->err ) {
2018 vips_buf_appends( &buf, ": " );
2019 vips_buf_appends( &buf, expr->error_top );
2020 }
2021 else if( row->child_rhs->itext ) {
2022 iText *itext = ITEXT( row->child_rhs->itext );
2023
2024 vips_buf_appends( &buf, " = " );
2025
2026 if( row->ws &&
2027 row->ws->mode != WORKSPACE_MODE_FORMULA )
2028 vips_buf_appends( &buf, NN( itext->formula ) );
2029 else
2030 vips_buf_appends( &buf, vips_buf_all( &itext->value ) );
2031 }
2032
2033 workspace_set_status( row->ws, "%s", vips_buf_firstline( &buf ) );
2034 }
2035
2036 /* Sub fn of below ... search inside a row hierarcy. Context is (eg.) row
2037 * "A1", path is (eg.) "super.name".
2038 */
2039 static Row *
row_parse_name_row(Row * context,const char * path)2040 row_parse_name_row( Row *context, const char *path )
2041 {
2042 char name[256];
2043 char *tail;
2044 Row *row;
2045 Subcolumn *scol;
2046
2047 #ifdef DEBUG
2048 printf( "row_parse_name_row: \"%s\"\n", path );
2049 #endif /*DEBUG*/
2050
2051 /* Break the name into "thing.tail", where tail could contain other
2052 * "." qualifiers.
2053 */
2054 im_strncpy( name, path, 256 );
2055 if( !(tail = break_token( name, "." )) )
2056 /* Passed empty string.
2057 */
2058 return( context );
2059
2060 /* Needs to be a subcolumn to look inside. We could search the value,
2061 * but it's safer to look inside the model we've built from the value.
2062 */
2063 if( !context->child_rhs ||
2064 !context->child_rhs->scol ||
2065 !(scol = SUBCOLUMN( context->child_rhs->scol )) )
2066 return( NULL );
2067
2068 if( !(row = subcolumn_map( scol,
2069 (row_map_fn) iobject_test_name, name, NULL )) )
2070 return( NULL );
2071
2072 return( row_parse_name_row( row, tail ) );
2073 }
2074
2075 /* Parse a qualified name .. eg. "untitled.A1.name" and find the row. Find
2076 * relative to context. Context is a sym, so we can have workspaceroot etc.
2077 */
2078 Row *
row_parse_name(Symbol * context,const char * path)2079 row_parse_name( Symbol *context, const char *path )
2080 {
2081 char name[256];
2082 char *tail;
2083 Symbol *sym;
2084 Row *row;
2085
2086 #ifdef DEBUG
2087 printf( "row_parse_name: \"%s\"\n", path );
2088 #endif /*DEBUG*/
2089
2090 /* Break the name into "thing.tail", where tail could contain other
2091 * "." qualifiers.
2092 */
2093 im_strncpy( name, path, 256 );
2094 if( !(tail = break_token( name, "." )) ) {
2095 /* Run out of names ... return this row, if we've found one.
2096 */
2097 if( context->expr )
2098 return( context->expr->row );
2099 else
2100 return( NULL );
2101 }
2102
2103 /* Try to look up name in context. For scopes, we can do it
2104 * statically. For other syms, look up in the value of the symbol.
2105 */
2106 switch( context->type ) {
2107 case SYM_WORKSPACE:
2108 case SYM_WORKSPACEROOT:
2109 case SYM_ROOT:
2110 if( !(sym = compile_lookup( context->expr->compile, name )) )
2111 return( NULL );
2112 break;
2113
2114 case SYM_VALUE:
2115 if( !(row = context->expr->row) )
2116 return( NULL );
2117
2118 /* Hand off to the row searcher.
2119 */
2120 return( row_parse_name_row( row, path ) );
2121
2122 case SYM_ZOMBIE:
2123 case SYM_PARAM:
2124 case SYM_EXTERNAL:
2125 case SYM_BUILTIN:
2126 default:
2127 /* How odd.
2128 */
2129 return( NULL );
2130 }
2131
2132 return( row_parse_name( sym, tail ) );
2133 }
2134