1 /* a 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 /*
31 #define DEBUG
32 */
33
34 #include "ip.h"
35
36 static HeapmodelClass *parent_class = NULL;
37
38 static gboolean
subcolumn_row_pred_none(Row * row)39 subcolumn_row_pred_none( Row *row )
40 {
41 return( FALSE );
42 }
43
44 /* No params, no super.
45 */
46 static gboolean
subcolumn_row_pred_members(Row * row)47 subcolumn_row_pred_members( Row *row )
48 {
49 if( row->sym &&
50 is_system( row->sym ) )
51 return( FALSE );
52
53 if( row->sym &&
54 is_super( row->sym ) )
55 return( FALSE );
56
57 if( row->sym &&
58 row->sym->type == SYM_PARAM )
59 return( FALSE );
60
61 return( TRUE );
62 }
63
64 static gboolean
subcolumn_row_pred_params(Row * row)65 subcolumn_row_pred_params( Row *row )
66 {
67 return( row->sym &&
68 row->sym->type == SYM_PARAM );
69 }
70
71 /* Everything but empty superclasses.
72 */
73 static gboolean
subcolumn_row_pred_super(Row * row)74 subcolumn_row_pred_super( Row *row )
75 {
76 if( row->sym &&
77 is_super( row->sym ) &&
78 PEISELIST( &row->expr->root ) )
79 return( FALSE );
80
81 return( TRUE );
82 }
83
84 /* Array of these guys control member visibility, scol->vislevel indexes this
85 * array, one of preds from vislevel down has to be TRUE for the row to be
86 * visible.
87 */
88 const SubcolumnVisibility subcolumn_visibility[] = {
89 { "none", subcolumn_row_pred_none },
90 { "members", subcolumn_row_pred_members },
91 { "params", subcolumn_row_pred_params },
92 { "super", subcolumn_row_pred_super }
93 };
94 const int subcolumn_nvisibility = IM_NUMBER( subcolumn_visibility );
95
96 /* Map down a Subcolumn.
97 */
98 void *
subcolumn_map(Subcolumn * scol,row_map_fn fn,void * a,void * b)99 subcolumn_map( Subcolumn *scol, row_map_fn fn, void *a, void *b )
100 {
101 return( icontainer_map( ICONTAINER( scol ),
102 (icontainer_map_fn) fn, a, b ) );
103 }
104
105 static void
subcolumn_dispose(GObject * gobject)106 subcolumn_dispose( GObject *gobject )
107 {
108 Subcolumn *scol;
109
110 #ifdef DEBUG
111 printf( "subcolumn_dispose\n" );
112 #endif /*DEBUG*/
113
114 g_return_if_fail( gobject != NULL );
115 g_return_if_fail( IS_SUBCOLUMN( gobject ) );
116
117 scol = SUBCOLUMN( gobject );
118
119 scol->col = NULL;
120 scol->scol = NULL;
121 scol->top_col = NULL;
122
123 heap_unregister_element( reduce_context->heap, &scol->base );
124 scol->base.type = ELEMENT_NOVAL;
125 scol->base.ele = (void *) 13;
126
127 scol->this = NULL;
128 scol->super = NULL;
129
130 G_OBJECT_CLASS( parent_class )->dispose( gobject );
131 }
132
133 /* Stuff we track during class instance display update.
134 */
135 typedef struct {
136 Subcolumn *scol; /* Enclosing column */
137 GSList *notused; /* List of row we've not used */
138 } ClassRefreshInfo;
139
140 /* Test for row represents a sym.
141 */
142 static void *
subcolumn_test_sym(Row * row,Symbol * sym)143 subcolumn_test_sym( Row *row, Symbol *sym )
144 {
145 if( row->sym == sym )
146 return( row );
147
148 return( NULL );
149 }
150
151 /* Test for row has a zombie of the same name.
152 */
153 static void *
subcolumn_test_row_name(Row * row,Symbol * sym)154 subcolumn_test_row_name( Row *row, Symbol *sym )
155 {
156 if( !row->sym &&
157 strcmp( IOBJECT( row )->name, IOBJECT( sym )->name ) == 0 )
158 return( row );
159
160 return( NULL );
161 }
162
163 /* Refresh one line of a subcolumn.
164 */
165 static void
subcolumn_class_new_heap_sub(ClassRefreshInfo * cri,Symbol * sym,PElement * value)166 subcolumn_class_new_heap_sub( ClassRefreshInfo *cri,
167 Symbol *sym, PElement *value )
168 {
169 Row *row;
170
171 #ifdef DEBUG
172 char txt[200];
173 VipsBuf buf = VIPS_BUF_STATIC( txt );
174
175 symbol_qualified_name( sym, &buf );
176 printf( "subcolumn_class_new_heap_sub: %s\n", vips_buf_all( &buf ) );
177 #endif /*DEBUG*/
178
179 /* Do we have a row for this symbol?
180 */
181 if( (row = (Row *) slist_map( cri->notused,
182 (SListMapFn) subcolumn_test_sym, sym )) ) {
183 /* Update it.
184 */
185 if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
186 expr_error_set( row->expr );
187
188 cri->notused = g_slist_remove( cri->notused, row );
189 }
190 else if( (row = (Row *) slist_map( cri->notused,
191 (SListMapFn) subcolumn_test_row_name, sym )) ) {
192 /* There's a blank row of the same name, left for us by XML
193 * load. Update the row with the correct symbol.
194 */
195 row_link_symbol( row, sym, NULL );
196 if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
197 expr_error_set( row->expr );
198
199 cri->notused = g_slist_remove( cri->notused, row );
200 }
201 else {
202 row = row_new( cri->scol, sym, value );
203 if( heapmodel_new_heap( HEAPMODEL( row ), value ) )
204 expr_error_set( row->expr );
205 }
206 }
207
208 #ifdef DEBUG
209 static void *
subcolumn_class_dump_tiny_row(Row * row)210 subcolumn_class_dump_tiny_row( Row *row )
211 {
212 row_name_print( row );
213 printf( " " );
214
215 return( NULL );
216 }
217 #endif /*DEBUG*/
218
219 /* A new scrap of heap for a subcolumn.
220 */
221 static gboolean
subcolumn_class_new_heap(Subcolumn * scol,PElement * root)222 subcolumn_class_new_heap( Subcolumn *scol, PElement *root )
223 {
224 PElement instance = *root;
225
226 Expr *expr;
227 Row *row;
228 gboolean result;
229 PElement base, member;
230 HeapNode *p;
231 ClassRefreshInfo cri;
232
233 /* Must be a class display.
234 */
235 g_assert( !scol->is_top );
236 row = HEAPMODEL( scol )->row;
237 expr = row->expr;
238
239 #ifdef DEBUG
240 printf( "subcolumn_class_new_heap: " );
241 row_name_print( row );
242 printf( "\n" );
243 #endif /*DEBUG*/
244
245 /* Can loop here for some recursive classes.
246
247 FIXME
248
249 if( mainw_countdown_animate( 99 ) )
250 return( FALSE );
251 */
252
253 /* No displays for system rows.
254 */
255 if( row->sym &&
256 is_system( row->sym ) )
257 return( TRUE );
258
259 /* If we are the top of a class instance display, get a new serial.
260 * As we recurse down refreshing our contents, this should stop
261 * circular structures looping the browser.
262
263 FIXME ... clear flags for a whole class, then do a complete
264 redisplay? more reliable, but even slower :-(
265
266 */
267 if( scol->scol->is_top )
268 heap_serial_new( reduce_context->heap );
269
270 /* Is it a class with a typecheck member? Go through
271 * that. Do an isclass first to force eval.
272 */
273 if( !heap_is_class( &instance, &result ) )
274 return( FALSE );
275 if( result &&
276 class_get_member( &instance, MEMBER_CHECK, NULL, &member ) ) {
277 #ifdef DEBUG
278 printf( "subcolumn_class_new_heap: invoking arg checker\n" );
279 #endif
280
281 /* Force eval of the typecheck member.
282 */
283 if( !heap_is_class( &member, &result ) || !result )
284 return( FALSE );
285 }
286
287 /* Have we already displayed this class?
288 */
289 if( (PEGETVAL( &instance )->flgs & FLAG_SERIAL) ==
290 reduce_context->heap->serial ) {
291 /*
292
293 FIXME ... display something here? "circular"?
294
295 */
296 return( TRUE );
297 }
298 SETSERIAL( PEGETVAL( &instance )->flgs, reduce_context->heap->serial );
299
300 /* Note the heap root ... if this is the top of a row tree, then we
301 * clone the class and use that private copy.
302 */
303 PEPOINTE( &base, &(SUBCOLUMN( scol ))->base );
304 PEPUTPE( &base, &instance );
305 PEPUTPE( &expr->root, &base );
306
307 /* Init rebuild params. We make a list of all the existing
308 * row objects for this class display, and every time we
309 * manage to reuse one of them, we knock it off the list. At the
310 * end, remove all unused rows.
311 */
312 cri.scol = scol;
313 cri.notused = g_slist_copy( ICONTAINER( scol )->children );
314
315 #ifdef DEBUG
316 printf( "subcolumn_class_new_heap: existing rows: " );
317 icontainer_map( ICONTAINER( scol ),
318 (icontainer_map_fn) subcolumn_class_dump_tiny_row, NULL, NULL );
319 printf( "\n" );
320 #endif /*DEBUG*/
321
322 /* Loop along the members, updating row entries.
323 */
324 PEGETCLASSMEMBER( &member, &base );
325
326 if( PEISNODE( &member ) )
327 for( p = PEGETVAL( &member ); p; p = GETRIGHT( p ) ) {
328 PElement s, v;
329 HeapNode *hn;
330 Symbol *sym;
331
332 /* Get the sym/value pair.
333 */
334 hn = GETLEFT( p );
335 PEPOINTLEFT( hn, &s );
336 PEPOINTRIGHT( hn, &v );
337 sym = SYMBOL( PEGETSYMREF( &s ) );
338
339 /* We don't make rows for the default constructor, or
340 * for ".name". These things don't change, so there's
341 * no point (and the default constructor has no text
342 * equivalent anyway).
343 */
344 if( strcmp( IOBJECT( sym )->name, MEMBER_NAME ) == 0 )
345 continue;
346 if( is_member( sym ) &&
347 strcmp( IOBJECT( sym )->name,
348 IOBJECT( symbol_get_parent( sym ) )->name ) ==
349 0 )
350 continue;
351
352 /* Display!
353 */
354 subcolumn_class_new_heap_sub( &cri, sym, &v );
355 }
356
357 /* Remove all the rows we've not used.
358 */
359 slist_map( cri.notused, (SListMapFn) iobject_destroy, NULL );
360 IM_FREEF( g_slist_free, cri.notused );
361
362 return( TRUE );
363 }
364
365 static void *
subcolumn_new_heap(Heapmodel * heapmodel,PElement * root)366 subcolumn_new_heap( Heapmodel *heapmodel, PElement *root )
367 {
368 Subcolumn *scol = SUBCOLUMN( heapmodel );
369
370 /* New heap for a class display? CLear known_private, we've no idea
371 * where this heap came from.
372 */
373 if( scol == scol->top_scol )
374 scol->known_private = FALSE;
375
376 /* A bunch of locals? Update them all.
377 */
378 if( !scol->is_top && !subcolumn_class_new_heap( scol, root ) )
379 return( scol );
380
381 return( HEAPMODEL_CLASS( parent_class )->new_heap( heapmodel, root ) );
382 }
383
384 static void
subcolumn_child_add(iContainer * parent,iContainer * child,int pos)385 subcolumn_child_add( iContainer *parent, iContainer *child, int pos )
386 {
387 Subcolumn *scol = SUBCOLUMN( parent );
388 Row *row = ROW( child );
389
390 /* May not have a symbol yet during ws load.
391 *
392 * Can't use is_this()/is_super(), not everything has been built yet.
393 * We don't do this often, so strcmp() it.
394 */
395 const char *name =
396 row->sym ? IOBJECT( row->sym )->name : IOBJECT( row )->name;
397
398 if( strcmp( name, MEMBER_THIS ) == 0 )
399 scol->this = row;
400
401 if( strcmp( name, MEMBER_SUPER ) == 0 )
402 scol->super = row;
403
404 ICONTAINER_CLASS( parent_class )->child_add( parent, child, pos );
405 }
406
407 static void
subcolumn_child_remove(iContainer * parent,iContainer * child)408 subcolumn_child_remove( iContainer *parent, iContainer *child )
409 {
410 Subcolumn *scol = SUBCOLUMN( parent );
411 Row *row = ROW( child );
412
413 ICONTAINER_CLASS( parent_class )->child_remove( parent, child );
414
415 if( scol->this == row )
416 scol->this = NULL;
417 if( scol->super == row )
418 scol->super = NULL;
419 }
420
421 /* If this is a top-level subcolumn, get the enclosing column.
422 */
423 static Column *
subcolumn_get_column(Subcolumn * scol)424 subcolumn_get_column( Subcolumn *scol )
425 {
426 g_assert( scol->is_top );
427
428 return( COLUMN( ICONTAINER( scol )->parent ) );
429 }
430
431 /* If this is a nested subcolumn, get the enclosing subcolumn.
432 */
433 static Subcolumn *
subcolumn_get_subcolumn(Subcolumn * scol)434 subcolumn_get_subcolumn( Subcolumn *scol )
435 {
436 Rhs *rhs;
437 Row *row;
438 Subcolumn *escol;
439
440 g_assert( !scol->is_top );
441
442 rhs = HEAPMODEL( scol )->rhs;
443 row = HEAPMODEL( rhs )->row;
444 escol = row->scol;
445
446 return( escol );
447 }
448
449 /* Return the enclosing column for a Subcolumn.
450 */
451 static Column *
subcolumn_get_top_column(Subcolumn * scol)452 subcolumn_get_top_column( Subcolumn *scol )
453 {
454 if( !scol->is_top )
455 return( subcolumn_get_top_column(
456 subcolumn_get_subcolumn( scol ) ) );
457
458 return( subcolumn_get_column( scol ) );
459 }
460
461 /* Return the enclosing subcolumn ... but not the is_top one. Ie. the enclosing
462 * subcolumn which has the base for this class tree.
463 */
464 static Subcolumn *
subcolumn_get_top_subcolumn(Subcolumn * scol)465 subcolumn_get_top_subcolumn( Subcolumn *scol )
466 {
467 Subcolumn *enclosing;
468
469 if( scol->is_top )
470 return( NULL );
471
472 enclosing = subcolumn_get_subcolumn( scol );
473 if( enclosing->is_top )
474 return( scol );
475 else
476 return( subcolumn_get_top_subcolumn( enclosing ) );
477 }
478
479 static void
subcolumn_parent_add(iContainer * child)480 subcolumn_parent_add( iContainer *child )
481 {
482 Subcolumn *scol = SUBCOLUMN( child );
483
484 ICONTAINER_CLASS( parent_class )->parent_add( child );
485
486 g_assert( IS_COLUMN( child->parent ) || IS_RHS( child->parent ) );
487 g_assert( !IS_COLUMN( child->parent ) ||
488 g_slist_length( child->parent->children ) == 1 );
489
490 scol->is_top = IS_COLUMN( child->parent );
491
492 /* For sub-columns, default to nothing visible.
493 */
494 if( !scol->is_top )
495 scol->vislevel = 0;
496
497 /* Update context pointers.
498 */
499 if( scol->is_top )
500 scol->col = subcolumn_get_column( scol );
501 else
502 scol->col = NULL;
503
504 if( !scol->is_top )
505 scol->scol = subcolumn_get_subcolumn( scol );
506 else
507 scol->scol = NULL;
508
509 scol->top_col = subcolumn_get_top_column( scol );
510 scol->top_scol = subcolumn_get_top_subcolumn( scol );
511
512 /* Top level subcolumns default to display on, others to display off.
513 */
514 MODEL( scol )->display = scol->is_top;
515 }
516
517 static View *
subcolumn_view_new(Model * model,View * parent)518 subcolumn_view_new( Model *model, View *parent )
519 {
520 return( subcolumnview_new() );
521 }
522
523 static void
subcolumn_display(Model * model,gboolean display)524 subcolumn_display( Model *model, gboolean display )
525 {
526 /*
527 printf( "subcolumn_display: " );
528 row_name_print( HEAPMODEL( model )->row );
529 printf( " %d\n", display );
530 */
531
532 MODEL_CLASS( parent_class )->display( model, display );
533 }
534
535 static gboolean
subcolumn_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)536 subcolumn_load( Model *model,
537 ModelLoadState *state, Model *parent, xmlNode *xnode )
538 {
539 Subcolumn *scol = SUBCOLUMN( model );
540
541 g_assert( IS_COLUMN( parent ) || IS_RHS( parent ) );
542
543 if( !get_iprop( xnode, "vislevel", &scol->vislevel ) )
544 return( FALSE );
545
546 if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) )
547 return( FALSE );
548
549 return( TRUE );
550 }
551
552 static xmlNode *
subcolumn_save(Model * model,xmlNode * xnode)553 subcolumn_save( Model *model, xmlNode *xnode )
554 {
555 Subcolumn *scol = SUBCOLUMN( model );
556
557 xmlNode *xthis;
558
559 if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
560 return( NULL );
561
562 if( !set_iprop( xthis, "vislevel", scol->vislevel ) )
563 return( NULL );
564
565 return( xthis );
566 }
567
568 static void
subcolumn_class_init(SubcolumnClass * class)569 subcolumn_class_init( SubcolumnClass *class )
570 {
571 GObjectClass *gobject_class = (GObjectClass *) class;
572 iContainerClass *icontainer_class = (iContainerClass *) class;
573 ModelClass *model_class = (ModelClass *) class;
574 HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
575
576 parent_class = g_type_class_peek_parent( class );
577
578 /* Create signals.
579 */
580
581 /* Init methods.
582 */
583 gobject_class->dispose = subcolumn_dispose;
584
585 icontainer_class->child_add = subcolumn_child_add;
586 icontainer_class->child_remove = subcolumn_child_remove;
587 icontainer_class->parent_add = subcolumn_parent_add;
588
589 model_class->view_new = subcolumn_view_new;
590 model_class->display = subcolumn_display;
591 model_class->load = subcolumn_load;
592 model_class->save = subcolumn_save;
593
594 heapmodel_class->new_heap = subcolumn_new_heap;
595
596 /* Static init.
597 */
598 model_register_loadable( MODEL_CLASS( class ) );
599 }
600
601 static void
subcolumn_init(Subcolumn * scol)602 subcolumn_init( Subcolumn *scol )
603 {
604 #ifdef DEBUG
605 printf( "subcolumn_init\n" );
606 #endif /*DEBUG*/
607
608 scol->col = NULL;
609 scol->scol = NULL;
610 scol->top_col = NULL;
611
612 scol->vislevel = subcolumn_nvisibility - 1;
613
614 scol->base.type = ELEMENT_NOVAL;
615 scol->base.ele = (void *) 14;
616 heap_register_element( reduce_context->heap, &scol->base );
617 scol->known_private = FALSE;
618
619 scol->this = NULL;
620 scol->super = NULL;
621 }
622
623 GType
subcolumn_get_type(void)624 subcolumn_get_type( void )
625 {
626 static GType subcolumn_type = 0;
627
628 if( !subcolumn_type ) {
629 static const GTypeInfo info = {
630 sizeof( SubcolumnClass ),
631 NULL, /* base_init */
632 NULL, /* base_finalize */
633 (GClassInitFunc) subcolumn_class_init,
634 NULL, /* class_finalize */
635 NULL, /* class_data */
636 sizeof( Subcolumn ),
637 32, /* n_preallocs */
638 (GInstanceInitFunc) subcolumn_init,
639 };
640
641 subcolumn_type = g_type_register_static( TYPE_HEAPMODEL,
642 "Subcolumn", &info, 0 );
643 }
644
645 return( subcolumn_type );
646 }
647
648 static void
subcolumn_link(Subcolumn * scol,Rhs * rhs,Column * col)649 subcolumn_link( Subcolumn *scol, Rhs *rhs, Column *col )
650 {
651 g_assert( rhs == NULL || col == NULL );
652
653 /* parent_add() sets is_top for us.
654 */
655 if( rhs )
656 icontainer_child_add( ICONTAINER( rhs ),
657 ICONTAINER( scol ), -1 );
658 else
659 icontainer_child_add( ICONTAINER( col ),
660 ICONTAINER( scol ), -1 );
661 }
662
663 Subcolumn *
subcolumn_new(Rhs * rhs,Column * col)664 subcolumn_new( Rhs *rhs, Column *col )
665 {
666 Subcolumn *scol;
667
668 scol = SUBCOLUMN( g_object_new( TYPE_SUBCOLUMN, NULL ) );
669 subcolumn_link( scol, rhs, col );
670
671 return( scol );
672 }
673
674 void
subcolumn_set_vislevel(Subcolumn * scol,int vislevel)675 subcolumn_set_vislevel( Subcolumn *scol, int vislevel )
676 {
677 scol->vislevel = IM_CLIP( 0, vislevel, subcolumn_nvisibility - 1 );
678
679 #ifdef DEBUG
680 printf( "subcolumn_set_vislevel: %d\n", scol->vislevel );
681 #endif /*DEBUG*/
682
683 iobject_changed( IOBJECT( scol ) );
684 }
685
686 /* Make sure we have a private copy of the graph for this tree of stuff.
687 */
688 gboolean
subcolumn_make_private(Subcolumn * scol)689 subcolumn_make_private( Subcolumn *scol )
690 {
691 Subcolumn *top_scol = scol->top_scol;
692 PElement base;
693
694 if( !top_scol || top_scol->known_private )
695 return( TRUE );
696
697 #ifdef DEBUG
698 {
699 Row *row = HEAPMODEL( top_scol )->row;
700
701 printf( "subcolumn_make_private: cloning " );
702 row_name_print( row );
703 printf( "\n" );
704 }
705 #endif /*DEBUG*/
706
707 /* Clone from the class args and rebuild our tree.
708 */
709 PEPOINTE( &base, &top_scol->base );
710 if( !class_clone_args( reduce_context->heap, &base, &base ) ||
711 heapmodel_new_heap( HEAPMODEL( top_scol ), &base ) )
712 return( FALSE );
713
714 top_scol->known_private = TRUE;
715
716 return( TRUE );
717 }
718
719