1 /* an image 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 ClassmodelClass *parent_class = NULL;
37 
38 static void
iimage_dispose(GObject * gobject)39 iimage_dispose( GObject *gobject )
40 {
41 	iImage *iimage;
42 
43 #ifdef DEBUG
44 	printf( "iimage_dispose %p\n", gobject );
45 #endif /*DEBUG*/
46 
47 	g_return_if_fail( gobject != NULL );
48 	g_return_if_fail( IS_IIMAGE( gobject ) );
49 
50 	iimage = IIMAGE( gobject );
51 
52 	slist_map( iimage->classmodels,
53 		(SListMapFn) classmodel_iimage_unlink, iimage );
54 	g_assert( !iimage->classmodels );
55 
56 	G_OBJECT_CLASS( parent_class )->dispose( gobject );
57 }
58 
59 static void
iimage_finalize(GObject * gobject)60 iimage_finalize( GObject *gobject )
61 {
62 	iImage *iimage;
63 
64 #ifdef DEBUG
65 	printf( "iimage_finalize\n" );
66 #endif /*DEBUG*/
67 
68 	g_return_if_fail( gobject != NULL );
69 	g_return_if_fail( IS_IIMAGE( gobject ) );
70 
71 	iimage = IIMAGE( gobject );
72 
73 	image_value_destroy( &iimage->value );
74 	IM_FREEF( g_slist_free, iimage->views );
75 	vips_buf_destroy( &iimage->caption_buffer );
76 
77 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
78 }
79 
80 /* Return the main caption.
81  */
82 static const char *
iimage_generate_caption(iObject * iobject)83 iimage_generate_caption( iObject *iobject )
84 {
85 	iImage *iimage = IIMAGE( iobject );
86 	Imageinfo *ii = iimage->value.ii;
87 	VipsBuf *buf = &iimage->caption_buffer;
88 
89 	vips_buf_rewind( buf );
90 
91 	image_value_caption( &iimage->value, buf );
92 
93 	if( ii ) {
94 		vips_buf_appends( buf, ", " );
95 		iobject_info( IOBJECT( iimage->value.ii ), buf );
96 	}
97 
98 	return( vips_buf_all( buf ) );
99 }
100 
101 static void
iimage_info(iObject * iobject,VipsBuf * buf)102 iimage_info( iObject *iobject, VipsBuf *buf )
103 {
104 	iImage *iimage = IIMAGE( iobject );
105 	Imageinfo *ii = iimage->value.ii;
106 	IMAGE *im;
107 
108 	if( ii && (im = imageinfo_get( FALSE, ii )) ) {
109 		char *filename;
110 
111 		if( im_header_get_typeof( im, ORIGINAL_FILENAME ) != 0 ) {
112 			if( !im_header_string( im,
113 				ORIGINAL_FILENAME, &filename ) ) {
114 				vips_buf_appends( buf,
115 					_( "Original filename" ) );
116 				vips_buf_appendf( buf, ": %s\n", filename );
117 			}
118 		}
119 	}
120 }
121 
122 static View *
iimage_view_new(Model * model,View * parent)123 iimage_view_new( Model *model, View *parent )
124 {
125 	return( iimageview_new() );
126 }
127 
128 static void
iimage_edit(GtkWidget * parent,Model * model)129 iimage_edit( GtkWidget *parent, Model *model )
130 {
131         iImage *iimage = IIMAGE( model );
132 
133 	if( iimage->value.ii )
134 		(void) imageview_new( iimage, parent );
135 }
136 
137 void
iimage_header(GtkWidget * parent,Model * model)138 iimage_header( GtkWidget *parent, Model *model )
139 {
140         iImage *iimage = IIMAGE( model );
141 	Row *row = HEAPMODEL( iimage )->row;
142 	Workspace *ws = row_get_workspace( row );
143 
144 	GtkWidget *imageheader;
145 	char txt[512];
146 	VipsBuf buf = VIPS_BUF_STATIC( txt );
147 
148 	imageheader = imageheader_new( iimage );
149 	row_qualified_name_relative( ws->sym, row, &buf );
150 	iwindow_set_title( IWINDOW( imageheader ),
151 		_( "Header for \"%s\"" ), vips_buf_all( &buf ) );
152 	idialog_set_callbacks( IDIALOG( imageheader ), NULL, NULL, NULL, NULL );
153 	idialog_add_ok( IDIALOG( imageheader ), iwindow_true_cb, _( "OK" ) );
154 	iwindow_set_parent( IWINDOW( imageheader ), parent );
155 	idialog_set_iobject( IDIALOG( imageheader ), IOBJECT( iimage ) );
156 	iwindow_build( IWINDOW( imageheader ) );
157 
158 	gtk_widget_show( imageheader );
159 }
160 
161 static xmlNode *
iimage_save(Model * model,xmlNode * xnode)162 iimage_save( Model *model, xmlNode *xnode )
163 {
164 	iImage *iimage = IIMAGE( model );
165 	xmlNode *xthis;
166 
167 	if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
168 		return( NULL );
169 
170 	/* We always rebuild the value from the expr ... don't save.
171 	 */
172 	if( !set_iprop( xthis, "image_left", iimage->image_left ) ||
173 		!set_iprop( xthis, "image_top", iimage->image_top ) ||
174 		!set_iprop( xthis, "image_mag", iimage->image_mag ) ||
175 		!set_sprop( xthis, "show_status",
176 			bool_to_char( iimage->show_status ) ) ||
177 		!set_sprop( xthis, "show_paintbox",
178 			bool_to_char( iimage->show_paintbox ) ) ||
179 		!set_sprop( xthis, "show_convert",
180 			bool_to_char( iimage->show_convert ) ) ||
181 		!set_sprop( xthis, "show_rulers",
182 			bool_to_char( iimage->show_rulers ) ) ||
183 		!set_dprop( xthis, "scale", iimage->scale ) ||
184 		!set_dprop( xthis, "offset", iimage->offset ) ||
185 		!set_sprop( xthis, "falsecolour",
186 			bool_to_char( iimage->falsecolour ) ) ||
187 		!set_sprop( xthis, "type", bool_to_char( iimage->type ) ) )
188 		return( NULL );
189 
190 	return( xthis );
191 }
192 
193 static gboolean
iimage_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)194 iimage_load( Model *model,
195 	ModelLoadState *state, Model *parent, xmlNode *xnode )
196 {
197         iImage *iimage = IIMAGE( model );
198 
199 	g_assert( IS_RHS( parent ) );
200 
201 	(void) get_iprop( xnode, "image_left", &iimage->image_left );
202 	(void) get_iprop( xnode, "image_top", &iimage->image_top );
203 	(void) get_iprop( xnode, "image_mag", &iimage->image_mag );
204 	(void) get_bprop( xnode, "show_status", &iimage->show_status );
205 	(void) get_bprop( xnode, "show_paintbox", &iimage->show_paintbox );
206 	(void) get_bprop( xnode, "show_convert", &iimage->show_convert );
207 	(void) get_bprop( xnode, "show_rulers", &iimage->show_rulers );
208 	(void) get_dprop( xnode, "scale", &iimage->scale );
209 	(void) get_dprop( xnode, "offset", &iimage->offset );
210 	(void) get_bprop( xnode, "falsecolour", &iimage->falsecolour );
211 	(void) get_bprop( xnode, "type", &iimage->type );
212 
213 	return( MODEL_CLASS( parent_class )->load( model,
214 		state, parent, xnode ) );
215 }
216 
217 /* Need to implement _update_heap(), as not all model fields are directly
218  * editable ... some are set only from expr. See also iregion.c.
219  */
220 static void *
iimage_update_heap(Heapmodel * heapmodel)221 iimage_update_heap( Heapmodel *heapmodel )
222 {
223 	Expr *expr = heapmodel->row->expr;
224         iImage *iimage = IIMAGE( heapmodel );
225 	ImageValue *value = &iimage->value;
226 
227 	PElement pe;
228 	Imageinfo *ii;
229 
230 #ifdef DEBUG
231 	printf( "iimage_update_heap: " );
232 	row_name_print( HEAPMODEL( iimage )->row );
233 	printf( "\n" );
234 #endif /*DEBUG*/
235 
236 	/* Read the heap into the model, over the top of the unapplied edits.
237 	 */
238 	if( !class_get_exact( &expr->root, IOBJECT( heapmodel )->name, &pe ) )
239 		return( FALSE );
240 	if( !class_get_member_image( &pe, MEMBER_VALUE, &ii ) )
241 		return( FALSE );
242 	image_value_set( value, ii );
243 
244 	IM_FREE( CLASSMODEL( iimage )->filename );
245 
246         if( value->ii && imageinfo_is_from_file( value->ii ) )
247                 IM_SETSTR( CLASSMODEL( iimage )->filename,
248                         IOBJECT( value->ii )->name );
249 
250 	/* Classmodel _update_heap() will do _instance_new() from the fixed up
251 	 * model.
252 	 */
253 	return( HEAPMODEL_CLASS( parent_class )->update_heap( heapmodel ) );
254 }
255 
256 /* Update iImage from heap.
257  */
258 static gboolean
iimage_class_get(Classmodel * classmodel,PElement * root)259 iimage_class_get( Classmodel *classmodel, PElement *root )
260 {
261         iImage *iimage = IIMAGE( classmodel );
262 	ImageValue *value = &iimage->value;
263 
264 	Imageinfo *ii;
265 
266 #ifdef DEBUG
267 	printf( "iimage_class_get: " );
268 	row_name_print( HEAPMODEL( iimage )->row );
269 	printf( "\n" );
270 #endif /*DEBUG*/
271 
272 	if( !class_get_member_image( root, MEMBER_VALUE, &ii ) )
273 		return( FALSE );
274 	image_value_set( value, ii );
275 
276 	/* Try to update the filename for this row ... get from the meta if we
277 	 * can.
278 	 */
279 	IM_FREE( classmodel->filename );
280         if( ii ) {
281 		IMAGE *im;
282 		char *filename;
283 
284 		if( (im = imageinfo_get( FALSE, ii )) &&
285 			im_header_get_typeof( im, ORIGINAL_FILENAME ) != 0 ) {
286 			if( im_header_string( im,
287 				ORIGINAL_FILENAME, &filename ) )
288 				return( FALSE );
289 		}
290 		else if( imageinfo_is_from_file( ii ) )
291 			filename = IOBJECT( ii )->name;
292 		else
293 			filename = NULL;
294 
295 		IM_SETSTR( classmodel->filename, filename );
296 	}
297 
298 	return( CLASSMODEL_CLASS( parent_class )->class_get(
299 		classmodel, root ) );
300 }
301 
302 /* Make a new "fn value" application.
303  */
304 static gboolean
iimage_class_new(Classmodel * classmodel,PElement * fn,PElement * out)305 iimage_class_new( Classmodel *classmodel, PElement *fn, PElement *out )
306 {
307 	Heap *heap = reduce_context->heap;
308         iImage *iimage = IIMAGE( classmodel );
309 	ImageValue *value = &iimage->value;
310 
311 	PElement rhs;
312 
313 #ifdef DEBUG
314 	printf( "iimage_class_new: " );
315 	row_name_print( HEAPMODEL( iimage )->row );
316 	printf( "\n" );
317 #endif /*DEBUG*/
318 
319 	/* Make application nodes.
320 	 */
321 	heap_appl_init( out, fn );
322 	if( !heap_appl_add( heap, out, &rhs ) )
323 		return( FALSE );
324 
325 	PEPUTP( &rhs, ELEMENT_MANAGED, value->ii );
326 
327 	return( TRUE );
328 }
329 
330 static gboolean
iimage_graphic_save(Classmodel * classmodel,GtkWidget * parent,const char * filename)331 iimage_graphic_save( Classmodel *classmodel,
332 	GtkWidget *parent, const char *filename )
333 {
334 	iImage *iimage = IIMAGE( classmodel );
335 	ImageValue *value = &iimage->value;
336 	char buf[FILENAME_MAX];
337 
338 	/* Can't happen nested-ly, so a static is OK.
339 	 */
340 	static GTimer *timer = NULL;
341 
342 	/* We don't want $VAR etc. in the filename we pass down to the file
343 	 * ops.
344 	 */
345 	im_strncpy( buf, filename, FILENAME_MAX );
346 	path_expand( buf );
347 
348 	/* Append the mode string. This needs an expanded filename.
349 	 */
350 	filesel_add_mode( buf );
351 
352 	if( !timer )
353 		timer = g_timer_new();
354 	g_timer_reset( timer );
355 
356 	if( value->ii )
357 		if( !imageinfo_write( value->ii, buf ) )
358 			return( FALSE );
359 
360 	mainw_recent_add( &mainw_recent_image, filename );
361 
362 	if( main_option_time_save ) {
363 		double elapsed;
364 
365 		elapsed = g_timer_elapsed( timer, NULL );
366 		error_top( _( "Save timer." ) );
367 		error_sub( _( "Image save took %g seconds." ), elapsed );
368 
369 		return( FALSE );
370 	}
371 
372 	return( TRUE );
373 }
374 
375 gboolean
iimage_replace(iImage * iimage,const char * filename)376 iimage_replace( iImage *iimage, const char *filename )
377 {
378 	Row *row = HEAPMODEL( iimage )->row;
379 	iText *itext = ITEXT( HEAPMODEL( iimage )->rhs->itext );
380 	char txt[MAX_STRSIZE];
381 	VipsBuf buf = VIPS_BUF_STATIC( txt );
382 
383 	vips_buf_appends( &buf, "Image_file \"" );
384 	vips_buf_appendsc( &buf, TRUE, filename );
385 	vips_buf_appends( &buf, "\"" );
386 
387 	if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) {
388 		itext_set_edited( itext, TRUE );
389 		workspace_set_modified( row->ws, TRUE );
390 		(void) expr_dirty( row->expr, link_serial_new() );
391 
392 		mainw_recent_add( &mainw_recent_image, filename );
393 	}
394 
395 	return( TRUE );
396 }
397 
398 static gboolean
iimage_graphic_replace(Classmodel * classmodel,GtkWidget * parent,const char * filename)399 iimage_graphic_replace( Classmodel *classmodel,
400 	GtkWidget *parent, const char *filename )
401 {
402 	return( iimage_replace( IIMAGE( classmodel ), filename ) );
403 }
404 
405 static void
iimage_class_init(iImageClass * class)406 iimage_class_init( iImageClass *class )
407 {
408 	GObjectClass *gobject_class = (GObjectClass *) class;
409 	iObjectClass *iobject_class = (iObjectClass *) class;
410 	ModelClass *model_class = (ModelClass *) class;
411 	HeapmodelClass *heapmodel_class = (HeapmodelClass *) class;
412 	ClassmodelClass *classmodel_class = (ClassmodelClass *) class;
413 
414 	parent_class = g_type_class_peek_parent( class );
415 
416 	/* Create signals.
417 	 */
418 
419 	/* Init methods.
420 	 */
421 	gobject_class->dispose = iimage_dispose;
422 	gobject_class->finalize = iimage_finalize;
423 
424 	iobject_class->user_name = _( "Image" );
425 	iobject_class->generate_caption = iimage_generate_caption;
426 	iobject_class->info = iimage_info;
427 
428 	model_class->view_new = iimage_view_new;
429 	model_class->edit = iimage_edit;
430 	model_class->header = iimage_header;
431 	model_class->save = iimage_save;
432 	model_class->load = iimage_load;
433 
434 	heapmodel_class->update_heap = iimage_update_heap;
435 
436 	classmodel_class->class_get = iimage_class_get;
437 	classmodel_class->class_new = iimage_class_new;
438 
439 	classmodel_class->graphic_save = iimage_graphic_save;
440 	classmodel_class->graphic_replace = iimage_graphic_replace;
441 
442 	classmodel_class->filetype = filesel_type_image;
443 	classmodel_class->filetype_pref = "IMAGE_FILE_TYPE";
444 
445 	/* Static init.
446 	 */
447 	model_register_loadable( MODEL_CLASS( class ) );
448 }
449 
450 static void
iimage_init(iImage * iimage)451 iimage_init( iImage *iimage )
452 {
453 	image_value_init( &iimage->value, CLASSMODEL( iimage ) );
454 
455 	iimage->classmodels = NULL;
456 
457 	iimage->views = NULL;
458 
459 	iimage->image_left = 0;
460 	iimage->image_top = 0;
461 	iimage->image_mag = 0;
462 
463 	iimage->show_status = FALSE;
464 	iimage->show_paintbox = FALSE;
465 	iimage->show_convert = FALSE;
466 	iimage->show_rulers = FALSE;
467 
468 	iimage->scale = 0.0;
469 	iimage->offset = 0.0;
470 	iimage->falsecolour = FALSE;
471 	iimage->type = TRUE;
472 
473 	vips_buf_init_dynamic( &iimage->caption_buffer, MAX_LINELENGTH );
474 
475 	iobject_set( IOBJECT( iimage ), CLASS_IMAGE, NULL );
476 }
477 
478 GtkType
iimage_get_type(void)479 iimage_get_type( void )
480 {
481 	static GType type = 0;
482 
483 	if( !type ) {
484 		static const GTypeInfo info = {
485 			sizeof( iImageClass ),
486 			NULL,           /* base_init */
487 			NULL,           /* base_finalize */
488 			(GClassInitFunc) iimage_class_init,
489 			NULL,           /* class_finalize */
490 			NULL,           /* class_data */
491 			sizeof( iImage ),
492 			32,             /* n_preallocs */
493 			(GInstanceInitFunc) iimage_init,
494 		};
495 
496 		type = g_type_register_static( TYPE_CLASSMODEL,
497 			"iImage", &info, 0 );
498 	}
499 
500 	return( type );
501 }
502