1 /* an ip region class object in a workspace
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 /*
31 #define DEBUG
32  */
33 
34 #include "ip.h"
35 
36 static iImageClass *parent_class = NULL;
37 
38 void
iregion_instance_destroy(iRegionInstance * instance)39 iregion_instance_destroy( iRegionInstance *instance )
40 {
41 	instance->image_class.type = ELEMENT_NOVAL;
42 	instance->image_class.ele = (void *) 8;
43 	MANAGED_UNREF( instance->ii );
44 	instance->classmodel = NULL;
45 	instance->iregiongroup = NULL;
46 	heap_unregister_element( reduce_context->heap, &instance->image_class );
47 }
48 
49 void
iregion_instance_init(iRegionInstance * instance,Classmodel * classmodel)50 iregion_instance_init( iRegionInstance *instance, Classmodel *classmodel )
51 {
52 	instance->image_class.type = ELEMENT_NOVAL;
53 	instance->image_class.ele = (void *) 9;
54 	instance->ii = NULL;
55 	instance->area.left = 0;
56 	instance->area.top = 0;
57 	instance->area.width = 0;
58 	instance->area.height = 0;
59 	instance->classmodel = classmodel;
60 	instance->iregiongroup = NULL;
61 
62 	heap_register_element( reduce_context->heap, &instance->image_class );
63 }
64 
65 gboolean
iregion_instance_update(iRegionInstance * instance,PElement * root)66 iregion_instance_update( iRegionInstance *instance, PElement *root )
67 {
68 	PElement image;
69 	PElement image_class;
70 	Imageinfo *value;
71 	int left, top, width, height;
72 
73 	if( !class_get_member_class( root, MEMBER_IMAGE, "Image", &image ) ||
74 		!class_get_member_image( &image, MEMBER_VALUE, &value ) ||
75 		!class_get_member_int( root, MEMBER_LEFT, &left ) ||
76 		!class_get_member_int( root, MEMBER_TOP, &top ) ||
77 		!class_get_member_int( root, MEMBER_WIDTH, &width ) ||
78 		!class_get_member_int( root, MEMBER_HEIGHT, &height ) )
79 		return( FALSE );
80 
81 	instance->area.left = left;
82 	instance->area.top = top;
83 	instance->area.width = width;
84 	instance->area.height = height;
85 
86 	MANAGED_UNREF( instance->ii );
87 	instance->ii = value;
88 	MANAGED_REF( value );
89 
90 	PEPOINTE( &image_class, &instance->image_class );
91 	PEPUTPE( &image_class, &image );
92 
93 	return( TRUE );
94 }
95 
96 static void
iregion_finalize(GObject * gobject)97 iregion_finalize( GObject *gobject )
98 {
99 	iRegion *iregion;
100 
101 #ifdef DEBUG
102 	printf( "iregion_finalize\n" );
103 #endif /*DEBUG*/
104 
105 	g_return_if_fail( gobject != NULL );
106 	g_return_if_fail( IS_IREGION( gobject ) );
107 
108 	iregion = IREGION( gobject );
109 
110 	/* My instance finalize stuff.
111 	 */
112 	iregion_instance_destroy( &iregion->instance );
113 
114 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
115 }
116 
117 static void *
iregion_generate_caption_sub(iImage * iimage,iRegion * iregion,gboolean * first)118 iregion_generate_caption_sub( iImage *iimage,
119 	iRegion *iregion, gboolean *first )
120 {
121 	iImage *our_iimage = IIMAGE( iregion );
122 	Workspace *ws = HEAPMODEL( iregion )->row->ws;
123 	Row *row = HEAPMODEL( iimage )->row;
124 
125 	/* Supress this name in the caption if it's a superclass. If this
126 	 * thing is on a super, it's on the subclass too ... not helpful to
127 	 * have it twice.
128 	 */
129 	if( row->sym &&
130 		!is_super( row->sym ) ) {
131 		if( *first )
132 			*first = FALSE;
133 		else
134 			vips_buf_appends( &our_iimage->caption_buffer, ", " );
135 
136 		row_qualified_name_relative( ws->sym,
137 			row, &our_iimage->caption_buffer );
138 	}
139 
140 	return( NULL );
141 }
142 
143 static const char *
iregion_generate_caption(iObject * iobject)144 iregion_generate_caption( iObject *iobject )
145 {
146 	iRegion *iregion = IREGION( iobject );
147 	iImage *iimage = IIMAGE( iregion );
148 	const int nimages = g_slist_length( CLASSMODEL( iregion )->iimages );
149 	VipsBuf *buf = &iimage->caption_buffer;
150 	gboolean first;
151 
152 	vips_buf_rewind( buf );
153 	heapmodel_name( HEAPMODEL( iregion ), buf );
154 	vips_buf_appendf( buf, " " );
155 	/* Expands to (eg.) "Region on A1 at (10, 10), size (50, 50)"
156 	 */
157 	vips_buf_appendf( buf, _( "on" ) );
158 	vips_buf_appendf( buf, " " );
159 	if( nimages > 1 )
160 		vips_buf_appendf( buf, "[" );
161 	first = TRUE;
162 	slist_map2( CLASSMODEL( iregion )->iimages,
163 		(SListMap2Fn) iregion_generate_caption_sub, iregion, &first );
164 	if( nimages > 1 )
165 		vips_buf_appendf( buf, "]" );
166 	vips_buf_appendf( buf, " " );
167 	vips_buf_appendf( buf, _( "at (%d, %d), size (%d, %d)" ),
168 		iregion->instance.area.left, iregion->instance.area.top,
169 		iregion->instance.area.width, iregion->instance.area.height );
170 
171 	return( vips_buf_all( buf ) );
172 }
173 
174 static void
iregion_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)175 iregion_done_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
176 {
177 	Classmodel *classmodel = CLASSMODEL( client );
178 	iRegionInstance *instance = classmodel_get_instance( classmodel );
179 	Stringset *ss = STRINGSET( iwnd );
180 	Rect area;
181 
182 	StringsetChild *left = stringset_child_get( ss, _( "Left" ) );
183 	StringsetChild *top = stringset_child_get( ss, _( "Top" ) );
184 	StringsetChild *width = stringset_child_get( ss, _( "Width" ) );
185 	StringsetChild *height = stringset_child_get( ss, _( "Height" ) );
186 
187 	if( !get_geditable_int( left->entry, &area.left ) ||
188 		!get_geditable_int( top->entry, &area.top ) ||
189 		!get_geditable_int( width->entry, &area.width ) ||
190 		!get_geditable_int( height->entry, &area.height ) ) {
191 		nfn( sys, IWINDOW_ERROR );
192 		return;
193 	}
194 
195 	if( instance ) {
196 		instance->area = area;
197 		classmodel_update( classmodel );
198 		symbol_recalculate_all();
199 	}
200 
201 	nfn( sys, IWINDOW_YES );
202 }
203 
204 static View *
iregion_view_new(Model * model,View * parent)205 iregion_view_new( Model *model, View *parent )
206 {
207 	return( iregionview_new() );
208 }
209 
210 /* Pop up a iregion edit box. Shared with iarrow.c.
211  */
212 void
iregion_edit(GtkWidget * parent,Model * model)213 iregion_edit( GtkWidget *parent, Model *model )
214 {
215 	Classmodel *classmodel = CLASSMODEL( model );
216 	iRegionInstance *instance = classmodel_get_instance( classmodel );
217 	GtkWidget *ss = stringset_new();
218 
219 	if( instance ) {
220 		char txt[256];
221 
222 		im_snprintf( txt, 256, "%d", instance->area.left );
223 		stringset_child_new( STRINGSET( ss ),
224 			_( "Left" ), txt, _( "Left edge of region" ) );
225 		im_snprintf( txt, 256, "%d", instance->area.top );
226 		stringset_child_new( STRINGSET( ss ),
227 			_( "Top" ), txt, _( "Top edge of region" ) );
228 		im_snprintf( txt, 256, "%d", instance->area.width );
229 		stringset_child_new( STRINGSET( ss ),
230 			_( "Width" ), txt, _( "Width of region" ) );
231 		im_snprintf( txt, 256, "%d", instance->area.height );
232 		stringset_child_new( STRINGSET( ss ),
233 			_( "Height" ), txt, _( "Height of region" ) );
234 	}
235 
236 	iwindow_set_title( IWINDOW( ss ), _( "Edit %s %s" ),
237 		IOBJECT_GET_CLASS_NAME( model ),
238 		IOBJECT( HEAPMODEL( model )->row )->name );
239 	idialog_set_callbacks( IDIALOG( ss ),
240 		iwindow_true_cb, NULL, NULL, classmodel );
241 	idialog_add_ok( IDIALOG( ss ),
242 		iregion_done_cb, _( "Set %s" ),
243 		IOBJECT_GET_CLASS_NAME( model ) );
244 	iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( parent ) );
245 	idialog_set_iobject( IDIALOG( ss ), IOBJECT( model ) );
246 	idialog_set_pinup( IDIALOG( ss ), TRUE );
247 	iwindow_build( IWINDOW( ss ) );
248 
249 	gtk_widget_show( ss );
250 }
251 
252 /* Shared with iarrow.c.
253  */
254 void
iregion_parent_add(iContainer * child)255 iregion_parent_add( iContainer *child )
256 {
257 	ICONTAINER_CLASS( parent_class )->parent_add( child );
258 
259 	/* Now we're all linked up, make a child model to handle client
260 	 * displays on imageviews.
261 	 */
262 	(void) iregiongroup_new( CLASSMODEL( child ) );
263 }
264 
265 /* Shared with iarrow.c.
266  */
267 xmlNode *
iregion_save(Model * model,xmlNode * xnode)268 iregion_save( Model *model, xmlNode *xnode )
269 {
270 	/* Get our parent class. We can't just use the global parent_class,
271 	 * since due to our lame MI scheme, this method may be called for
272 	 * iarrow/ipoint etc. as well as iregion ... look up dynamically.
273 	 */
274 	gpointer parent_class = PARENT_CLASS_DYNAMIC( model );
275 
276 	iRegionInstance *instance =
277 		classmodel_get_instance( CLASSMODEL( model ) );
278 
279 	xmlNode *xthis;
280 
281 	if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
282 		return( NULL );
283 
284 	if( instance && CLASSMODEL( model )->edited ) {
285 		Rect *area = &instance->area;
286 
287 		if( !set_iprop( xthis, "left", area->left ) ||
288 			!set_iprop( xthis, "top", area->top ) ||
289 			!set_iprop( xthis, "width", area->width ) ||
290 			!set_iprop( xthis, "height", area->height ) )
291 			return( NULL );
292 	}
293 
294 	return( xthis );
295 }
296 
297 /* Shared with iarrow.c.
298  */
299 gboolean
iregion_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)300 iregion_load( Model *model,
301 	ModelLoadState *state, Model *parent, xmlNode *xnode )
302 {
303 	gpointer parent_class = PARENT_CLASS_DYNAMIC( model );
304 	iRegionInstance *instance =
305 		classmodel_get_instance( CLASSMODEL( model ) );
306 
307 	g_assert( IS_RHS( parent ) );
308 
309 	if( instance ) {
310 		Rect *area = &instance->area;
311 
312 		if( get_iprop( xnode, "left", &area->left ) &&
313 			get_iprop( xnode, "top", &area->top ) &&
314 			get_iprop( xnode, "width", &area->width ) &&
315 			get_iprop( xnode, "height", &area->height ) )
316 			classmodel_set_edited( CLASSMODEL( model ), TRUE );
317 	}
318 
319 	return( MODEL_CLASS( parent_class )->load( model,
320 		state, parent, xnode ) );
321 }
322 
323 /* Need to implement _update_heap(), as not all model fields are directly
324  * editable ... some are set only from expr. See also iimage.c. Shared with
325  * iarrow.c.
326  */
327 void *
iregion_update_heap(Heapmodel * heapmodel)328 iregion_update_heap( Heapmodel *heapmodel )
329 {
330 	gpointer parent_class = PARENT_CLASS_DYNAMIC( heapmodel );
331 	iRegionInstance *instance =
332 		classmodel_get_instance( CLASSMODEL( heapmodel ) );
333 	Expr *expr = heapmodel->row->expr;
334 
335 	Rect area;
336 	PElement pe;
337 
338 	if( instance ) {
339 		/* Save any model fields that may have been set by _load() and
340 		 * which might be zapped by _get_instance().
341 		 */
342 		area = instance->area;
343 
344 		/* Look for the base instance and update from that.
345 		 */
346 		if( !class_get_exact( &expr->root,
347 			IOBJECT( heapmodel )->name, &pe ) )
348 			return( FALSE );
349 		if( !iregion_instance_update( instance, &pe ) )
350 			return( heapmodel );
351 
352 		/* Restore model fields from _load().
353 		 */
354 		instance->area = area;
355 	}
356 
357 	/* Classmodel _update_heap() will do _instance_new() from the fixed up
358 	 * model.
359 	 */
360 	return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) );
361 }
362 
363 static void *
iregion_update_model(Heapmodel * heapmodel)364 iregion_update_model( Heapmodel *heapmodel )
365 {
366 	iRegion *iregion = IREGION( heapmodel );
367 
368 	if( HEAPMODEL_CLASS( parent_class )->update_model( heapmodel ) )
369 		return( heapmodel );
370 
371 	/* Update who-has-displays-on-what stuff.
372 	 */
373 	classmodel_iimage_update( CLASSMODEL( iregion ), iregion->instance.ii );
374 
375 	/* Make sure the caption is regenerated.
376 	 */
377 	iobject_changed( IOBJECT( heapmodel ) );
378 
379 	return( NULL );
380 }
381 
382 /* Update iRegion from heap. Shared with iarrow.c.
383  */
384 gboolean
iregion_class_get(Classmodel * classmodel,PElement * root)385 iregion_class_get( Classmodel *classmodel, PElement *root )
386 {
387 	gpointer parent_class = PARENT_CLASS_DYNAMIC( classmodel );
388 	iRegionInstance *instance = classmodel_get_instance( classmodel );
389 
390 #ifdef DEBUG
391 	printf( "iregion_class_get: " );
392 	row_name_print( HEAPMODEL( classmodel )->row );
393 	printf( "\n" );
394 #endif /*DEBUG*/
395 
396 	if( instance && !iregion_instance_update( instance, root ) )
397 		return( FALSE );
398 
399 	return( CLASSMODEL_CLASS( parent_class )->class_get(
400 		classmodel, root ) );
401 }
402 
403 /* Make a new "fn value" application. Shared with iarrow.c.
404  */
405 gboolean
iregion_class_new(Classmodel * classmodel,PElement * fn,PElement * out)406 iregion_class_new( Classmodel *classmodel, PElement *fn, PElement *out )
407 {
408 	Heap *heap = reduce_context->heap;
409 	iRegionInstance *instance = classmodel_get_instance( classmodel );
410 
411 	PElement rhs;
412 
413 #ifdef DEBUG
414 	printf( "iregion_class_new\n" );
415 #endif /*DEBUG*/
416 
417 	/* Make application nodes.
418 	 */
419 	if( instance ) {
420 		heap_appl_init( out, fn );
421 		if( !heap_appl_add( heap, out, &rhs ) ||
422 			!heap_element_new( heap,
423 				&instance->image_class, &rhs ) ||
424 			!heap_appl_add( heap, out, &rhs ) ||
425 			!heap_real_new( heap, instance->area.left, &rhs ) ||
426 			!heap_appl_add( heap, out, &rhs ) ||
427 			!heap_real_new( heap, instance->area.top, &rhs ) ||
428 			!heap_appl_add( heap, out, &rhs ) ||
429 			!heap_real_new( heap, instance->area.width, &rhs ) ||
430 			!heap_appl_add( heap, out, &rhs ) ||
431 			!heap_real_new( heap, instance->area.height, &rhs ) )
432 			return( FALSE );
433 	}
434 
435 	return( TRUE );
436 }
437 
438 static void *
iregion_get_instance(Classmodel * classmodel)439 iregion_get_instance( Classmodel *classmodel )
440 {
441 	iRegion *iregion = IREGION( classmodel );
442 
443 	return( &iregion->instance );
444 }
445 
446 static void
iregion_class_init(iRegionClass * class)447 iregion_class_init( iRegionClass *class )
448 {
449 	GObjectClass *gobject_class = (GObjectClass *) class;
450 	iObjectClass *iobject_class = (iObjectClass *) class;
451 	iContainerClass *icontainer_class = (iContainerClass *) class;
452 	ModelClass *model_class = (ModelClass *) class;
453 	HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
454 	ClassmodelClass *classmodel_class = (ClassmodelClass *) class;
455 
456 	parent_class = g_type_class_peek_parent( class );
457 
458 	/* Create signals.
459 	 */
460 
461 	/* Init methods.
462 	 */
463 	gobject_class->finalize = iregion_finalize;
464 
465 	iobject_class->user_name = _( "Region" );
466 	iobject_class->generate_caption = iregion_generate_caption;
467 
468 	icontainer_class->parent_add = iregion_parent_add;
469 
470 	model_class->view_new = iregion_view_new;
471 	model_class->edit = iregion_edit;
472 	model_class->save = iregion_save;
473 	model_class->load = iregion_load;
474 
475 	heapmodel_class->update_heap = iregion_update_heap;
476 	heapmodel_class->update_model = iregion_update_model;
477 
478 	classmodel_class->class_get = iregion_class_get;
479 	classmodel_class->class_new = iregion_class_new;
480 	classmodel_class->get_instance = iregion_get_instance;
481 
482 	/* Static init.
483 	 */
484 	model_register_loadable( MODEL_CLASS( class ) );
485 }
486 
487 static void
iregion_init(iRegion * iregion)488 iregion_init( iRegion *iregion )
489 {
490 	iregion_instance_init( &iregion->instance, CLASSMODEL( iregion ) );
491 
492 	iobject_set( IOBJECT( iregion ), CLASS_REGION, NULL );
493 }
494 
495 GType
iregion_get_type(void)496 iregion_get_type( void )
497 {
498 	static GType type = 0;
499 
500 	if( !type ) {
501 		static const GTypeInfo info = {
502 			sizeof( iRegionClass ),
503 			NULL,           /* base_init */
504 			NULL,           /* base_finalize */
505 			(GClassInitFunc) iregion_class_init,
506 			NULL,           /* class_finalize */
507 			NULL,           /* class_data */
508 			sizeof( iRegion ),
509 			32,             /* n_preallocs */
510 			(GInstanceInitFunc) iregion_init,
511 		};
512 
513 		type = g_type_register_static( TYPE_IIMAGE,
514 			"iRegion", &info, 0 );
515 	}
516 
517 	return( type );
518 }
519