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