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