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