1 /* abstract base class for things which form the filemodel half of a
2 * filemodel/view pair
3 */
4
5 /*
6
7 Copyright (C) 1991-2003 The National Gallery
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 */
24
25 /*
26
27 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
28
29 */
30
31 /*
32 #define DEBUG
33 */
34
35 /* Don't compress save files.
36
37 FIXME ... some prebuilt libxml2s on win32 don't support libz
38 compression, so don't turn this off
39
40 */
41 #define DEBUG_SAVEFILE
42
43 #include "ip.h"
44
45 static ModelClass *parent_class = NULL;
46
47 static GSList *filemodel_registered = NULL;
48
49 /* Register a file model. Registered models are part of the "xxx has been
50 * modified, save before quit?" check.
51 */
52 void
filemodel_register(Filemodel * filemodel)53 filemodel_register( Filemodel *filemodel )
54 {
55 if( !filemodel->registered ) {
56 filemodel->registered = TRUE;
57 filemodel_registered = g_slist_prepend( filemodel_registered,
58 filemodel );
59
60 #ifdef DEBUG
61 printf( "filemodel_register: %s \"%s\" (%p)\n",
62 G_OBJECT_TYPE_NAME( filemodel ),
63 IOBJECT( filemodel )->name,
64 filemodel );
65 #endif /*DEBUG*/
66 }
67 }
68
69 void
filemodel_unregister(Filemodel * filemodel)70 filemodel_unregister( Filemodel *filemodel )
71 {
72 if( filemodel->registered ) {
73 filemodel->registered = FALSE;
74 filemodel_registered = g_slist_remove( filemodel_registered,
75 filemodel );
76
77 #ifdef DEBUG
78 printf( "filemodel_unregister: %s \"%s\" (%p)\n",
79 G_OBJECT_TYPE_NAME( filemodel ),
80 IOBJECT( filemodel )->name,
81 filemodel );
82 #endif /*DEBUG*/
83 }
84 }
85
86 /* Trigger the top_load method for a filemodel.
87 */
88 void *
filemodel_top_load(Filemodel * filemodel,ModelLoadState * state,Model * parent,xmlNode * xnode)89 filemodel_top_load( Filemodel *filemodel,
90 ModelLoadState *state, Model *parent, xmlNode *xnode )
91 {
92 FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel );
93
94 if( filemodel_class->top_load ) {
95 if( !filemodel_class->top_load( filemodel, state,
96 parent, xnode ) )
97 return( filemodel );
98 }
99 else {
100 error_top( _( "Not implemented." ) );
101 error_sub( _( "_%s() not implemented for class \"%s\"." ),
102 "top_load",
103 G_OBJECT_CLASS_NAME( filemodel_class ) );
104
105 return( filemodel );
106 }
107
108 return( NULL );
109 }
110
111 /* Trigger the set_modified method for a filemodel.
112 */
113 void
filemodel_set_modified(Filemodel * filemodel,gboolean modified)114 filemodel_set_modified( Filemodel *filemodel, gboolean modified )
115 {
116 FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel );
117
118 if( filemodel_class->set_modified )
119 filemodel_class->set_modified( filemodel, modified );
120 }
121
122 void
filemodel_set_window_hint(Filemodel * filemodel,iWindow * iwnd)123 filemodel_set_window_hint( Filemodel *filemodel, iWindow *iwnd )
124 {
125 /* This can be called repeatedly if objects are moved between windows.
126 */
127 filemodel->window_hint = iwnd;
128 }
129
130 iWindow *
filemodel_get_window_hint(Filemodel * filemodel)131 filemodel_get_window_hint( Filemodel *filemodel )
132 {
133 if( filemodel->window_hint )
134 return( filemodel->window_hint );
135 else
136 return( IWINDOW( mainw_pick_one() ) );
137 }
138
139 gboolean
filemodel_top_save(Filemodel * filemodel,const char * filename)140 filemodel_top_save( Filemodel *filemodel, const char *filename )
141 {
142 FilemodelClass *filemodel_class = FILEMODEL_GET_CLASS( filemodel );
143
144 if( filemodel_class->top_save ) {
145 char *old_filename;
146 int result;
147
148 /* We must always have the new filename in the save file or
149 * auto path rewriting will get confused on reload.
150 *
151 * Equally, we must not change the filename on the model, in
152 * case this save is not something initiated by the user, for
153 * example, an auto-backup of the workspace.
154 *
155 * Save and restore the filename. Our caller must set the
156 * final filename, if required (after save-as, for example).
157 */
158 old_filename = g_strdup( filemodel->filename );
159 filemodel_set_filename( filemodel, filename );
160
161 result = filemodel_class->top_save( filemodel, filename );
162
163 filemodel_set_filename( filemodel, old_filename );
164 g_free( old_filename );
165
166 return( result );
167 }
168 else {
169 error_top( _( "Not implemented." ) );
170 error_sub( _( "_%s() not implemented for class \"%s\"." ),
171 "top_save",
172 G_OBJECT_CLASS_NAME( filemodel_class ) );
173
174 return( FALSE );
175 }
176 }
177
178 static void
filemodel_info(iObject * iobject,VipsBuf * buf)179 filemodel_info( iObject *iobject, VipsBuf *buf )
180 {
181 Filemodel *filemodel = FILEMODEL( iobject );
182
183 IOBJECT_CLASS( parent_class )->info( iobject, buf );
184
185 vips_buf_appendf( buf, "filename = \"%s\"\n",
186 NN( filemodel->filename ) );
187 vips_buf_appendf( buf, "modified = \"%s\"\n",
188 bool_to_char( filemodel->modified ) );
189 vips_buf_appendf( buf, "registered = \"%s\"\n",
190 bool_to_char( filemodel->registered ) );
191 vips_buf_appendf( buf, "auto_load = \"%s\"\n",
192 bool_to_char( filemodel->auto_load ) );
193 }
194
195 /* filename can be NULL for unset.
196 */
197 void
filemodel_set_filename(Filemodel * filemodel,const char * filename)198 filemodel_set_filename( Filemodel *filemodel, const char *filename )
199 {
200 if( filemodel->filename != filename ) {
201 char buf[FILENAME_MAX];
202
203 /* We want to keep the absolute, compact form of the filename
204 * inside the object so we don't get a dependency on CWD.
205 */
206 if( filename ) {
207 im_strncpy( buf, filename, FILENAME_MAX );
208 path_compact( buf );
209 filename = buf;
210 }
211
212 IM_SETSTR( filemodel->filename, filename );
213 iobject_changed( IOBJECT( filemodel ) );
214 }
215 }
216
217 static void
filemodel_finalize(GObject * gobject)218 filemodel_finalize( GObject *gobject )
219 {
220 Filemodel *filemodel;
221
222 g_return_if_fail( gobject != NULL );
223 g_return_if_fail( IS_FILEMODEL( gobject ) );
224
225 filemodel = FILEMODEL( gobject );
226
227 #ifdef DEBUG
228 printf( "filemodel_finalize: %s \"%s\" (%s)\n",
229 G_OBJECT_TYPE_NAME( filemodel ),
230 NN( IOBJECT( filemodel )->name ),
231 NN( filemodel->filename ) );
232 #endif /*DEBUG*/
233
234 IM_FREE( filemodel->filename );
235
236 G_OBJECT_CLASS( parent_class )->finalize( gobject );
237 }
238
239 static void
filemodel_dispose(GObject * gobject)240 filemodel_dispose( GObject *gobject )
241 {
242 Filemodel *filemodel;
243
244 g_return_if_fail( gobject != NULL );
245 g_return_if_fail( IS_FILEMODEL( gobject ) );
246
247 filemodel = FILEMODEL( gobject );
248
249 #ifdef DEBUG
250 printf( "filemodel_dispose: %s \"%s\" (%s)\n",
251 G_OBJECT_TYPE_NAME( filemodel ),
252 NN( IOBJECT( filemodel )->name ),
253 NN( filemodel->filename ) );
254 #endif /*DEBUG*/
255
256 filemodel_unregister( filemodel );
257
258 G_OBJECT_CLASS( parent_class )->dispose( gobject );
259 }
260
261 static xmlNode *
filemodel_save(Model * model,xmlNode * xnode)262 filemodel_save( Model *model, xmlNode *xnode )
263 {
264 Filemodel *filemodel = FILEMODEL( model );
265 xmlNode *xthis;
266
267 if( !(xthis = MODEL_CLASS( parent_class )->save( model, xnode )) )
268 return( NULL );
269
270 if( !set_sprop( xthis, "filename", filemodel->filename ) )
271 return( NULL );
272
273 return( xthis );
274 }
275
276 static gboolean
filemodel_load(Model * model,ModelLoadState * state,Model * parent,xmlNode * xnode)277 filemodel_load( Model *model,
278 ModelLoadState *state, Model *parent, xmlNode *xnode )
279 {
280 Filemodel *filemodel = FILEMODEL( model );
281
282 char buf[MAX_STRSIZE];
283
284 if( get_sprop( xnode, "filename", buf, MAX_STRSIZE ) )
285 filemodel_set_filename( filemodel, buf );
286
287 if( !MODEL_CLASS( parent_class )->load( model, state, parent, xnode ) )
288 return( FALSE );
289
290 return( TRUE );
291 }
292
293 static gboolean
filemodel_real_top_load(Filemodel * filemodel,ModelLoadState * state,Model * parent,xmlNode * xnode)294 filemodel_real_top_load( Filemodel *filemodel,
295 ModelLoadState *state, Model *parent, xmlNode *xnode )
296 {
297 return( TRUE );
298 }
299
300 static void
filemodel_real_set_modified(Filemodel * filemodel,gboolean modified)301 filemodel_real_set_modified( Filemodel *filemodel, gboolean modified )
302 {
303 if( filemodel->modified != modified ) {
304 #ifdef DEBUG
305 printf( "filemodel_real_set_modified: %s \"%s\" (%s) %s\n",
306 G_OBJECT_TYPE_NAME( filemodel ),
307 NN( IOBJECT( filemodel )->name ),
308 NN( filemodel->filename ),
309 bool_to_char( modified ) );
310 #endif /*DEBUG*/
311
312 filemodel->modified = modified;
313
314 iobject_changed( IOBJECT( filemodel ) );
315 }
316 }
317
318 static int
filemodel_xml_save_format_file(const char * filename,xmlDoc * doc)319 filemodel_xml_save_format_file( const char *filename, xmlDoc *doc )
320 {
321 return( xmlSaveFormatFile( filename, doc, 1 ) == -1 );
322 }
323
324 /* Save to filemodel->filename.
325 */
326 static gboolean
filemodel_top_save_xml(Filemodel * filemodel,const char * filename)327 filemodel_top_save_xml( Filemodel *filemodel, const char *filename )
328 {
329 xmlDoc *xdoc;
330 char namespace[256];
331
332 if( !(xdoc = xmlNewDoc( (xmlChar *) "1.0" )) ) {
333 error_top( _( "XML library error." ) );
334 error_sub( _( "model_save_filename: xmlNewDoc() failed" ) );
335 return( FALSE );
336 }
337
338 #ifndef DEBUG_SAVEFILE
339 xmlSetDocCompressMode( xdoc, 1 );
340 #endif /*!DEBUG_SAVEFILE*/
341
342 im_snprintf( namespace, 256, "%s/%d.%d.%d",
343 NAMESPACE,
344 filemodel->major, filemodel->minor, filemodel->micro );
345 if( !(xdoc->children = xmlNewDocNode( xdoc,
346 NULL, (xmlChar *) "root", NULL )) ||
347 !set_sprop( xdoc->children, "xmlns", namespace ) ) {
348 error_top( _( "XML library error." ) );
349 error_sub( _( "model_save_filename: xmlNewDocNode() failed" ) );
350 xmlFreeDoc( xdoc );
351 return( FALSE );
352 }
353
354 column_set_offset( filemodel->x_off, filemodel->y_off );
355 if( model_save( MODEL( filemodel ), xdoc->children ) ) {
356 xmlFreeDoc( xdoc );
357 return( FALSE );
358 }
359
360 if( calli_string_filename(
361 (calli_string_fn) filemodel_xml_save_format_file,
362 filename, xdoc, NULL, NULL ) ) {
363 error_top( _( "Save failed." ) );
364 error_sub( _( "Save of %s \"%s\" to file \"%s\" failed.\n%s" ),
365 IOBJECT_GET_CLASS_NAME( filemodel ),
366 NN( IOBJECT( filemodel )->name ),
367 NN( filename ),
368 g_strerror( errno ) );
369 xmlFreeDoc( xdoc );
370
371 return( FALSE );
372 }
373
374 xmlFreeDoc( xdoc );
375
376 return( TRUE );
377 }
378
379 static gboolean
filemodel_top_save_text(Filemodel * filemodel,const char * filename)380 filemodel_top_save_text( Filemodel *filemodel, const char *filename )
381 {
382 iOpenFile *of;
383
384 if( !(of = ifile_open_write( "%s", filename )) )
385 return( FALSE );
386
387 column_set_offset( filemodel->x_off, filemodel->y_off );
388 if( model_save_text( MODEL( filemodel ), of ) ) {
389 ifile_close( of );
390 return( FALSE );
391 }
392 ifile_close( of );
393
394 return( TRUE );
395 }
396
397 static gboolean
filemodel_real_top_save(Filemodel * filemodel,const char * filename)398 filemodel_real_top_save( Filemodel *filemodel, const char *filename )
399 {
400 ModelClass *model_class = MODEL_GET_CLASS( filemodel );
401
402 #ifdef DEBUG
403 printf( "filemodel_real_top_save: save %s \"%s\" to file \"%s\"\n",
404 G_OBJECT_TYPE_NAME( filemodel ),
405 NN( IOBJECT( filemodel )->name ),
406 filename );
407 #endif /*DEBUG*/
408
409 if( model_class->save_text ) {
410 if( !filemodel_top_save_text( filemodel, filename ) )
411 return( FALSE );
412 }
413 else if( model_class->save ) {
414 if( !filemodel_top_save_xml( filemodel, filename ) )
415 return( FALSE );
416 }
417 else {
418 error_top( _( "Not implemented." ) );
419 error_sub( _( "filemodel_real_top_save: no save method" ) );
420 return( FALSE );
421 }
422
423 return( TRUE );
424 }
425
426 static void
filemodel_class_init(FilemodelClass * class)427 filemodel_class_init( FilemodelClass *class )
428 {
429 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
430 iObjectClass *iobject_class = IOBJECT_CLASS( class );
431 ModelClass *model_class = (ModelClass*) class;
432
433 parent_class = g_type_class_peek_parent( class );
434
435 gobject_class->finalize = filemodel_finalize;
436 gobject_class->dispose = filemodel_dispose;
437
438 iobject_class->info = filemodel_info;
439
440 model_class->save = filemodel_save;
441 model_class->load = filemodel_load;
442
443 class->top_load = filemodel_real_top_load;
444 class->set_modified = filemodel_real_set_modified;
445 class->top_save = filemodel_real_top_save;
446
447 /* NULL isn't an allowed value -- this gets overridden by our
448 * subclasses.
449 */
450 class->filetype = NULL;
451 class->filetype_pref = NULL;
452 }
453
454 static void
filemodel_init(Filemodel * filemodel)455 filemodel_init( Filemodel *filemodel )
456 {
457 /* Init our instance fields.
458 */
459 filemodel->filename = NULL;
460 filemodel->modified = FALSE;
461 filemodel->registered = FALSE;
462 filemodel->auto_load = FALSE;
463 filemodel->x_off = 0;
464 filemodel->y_off = 0;
465
466 /* Default version.
467 */
468 filemodel->versioned = FALSE;
469 filemodel->major = MAJOR_VERSION;
470 filemodel->minor = MINOR_VERSION;
471 filemodel->micro = MICRO_VERSION;
472
473 filemodel->window_hint = NULL;
474 }
475
476 GtkType
filemodel_get_type(void)477 filemodel_get_type( void )
478 {
479 static GtkType filemodel_type = 0;
480
481 if( !filemodel_type ) {
482 static const GTypeInfo info = {
483 sizeof( FilemodelClass ),
484 NULL, /* base_init */
485 NULL, /* base_finalize */
486 (GClassInitFunc) filemodel_class_init,
487 NULL, /* class_finalize */
488 NULL, /* class_data */
489 sizeof( Filemodel ),
490 32, /* n_preallocs */
491 (GInstanceInitFunc) filemodel_init,
492 };
493
494 filemodel_type = g_type_register_static( TYPE_MODEL,
495 "Filemodel", &info, 0 );
496 }
497
498 return( filemodel_type );
499 }
500
501 void
filemodel_set_offset(Filemodel * filemodel,int x_off,int y_off)502 filemodel_set_offset( Filemodel *filemodel, int x_off, int y_off )
503 {
504 #ifdef DEBUG
505 printf( "filemodel_set_offset: %s \"%s\" %d x %d\n",
506 G_OBJECT_TYPE_NAME( filemodel ),
507 NN( IOBJECT( filemodel )->name ),
508 x_off, y_off );
509 #endif /*DEBUG*/
510
511 filemodel->x_off = x_off;
512 filemodel->y_off = y_off;
513 }
514
515 static gboolean
filemodel_load_all_xml(Filemodel * filemodel,Model * parent,ModelLoadState * state)516 filemodel_load_all_xml( Filemodel *filemodel,
517 Model *parent, ModelLoadState *state )
518 {
519 xmlNode *xnode;
520
521 /* Check the root element for type/version compatibility.
522 */
523 if( !(xnode = xmlDocGetRootElement( state->xdoc )) ||
524 !xnode->nsDef ||
525 !is_prefix( NAMESPACE, (char *) xnode->nsDef->href ) ) {
526 error_top( _( "Load failed." ) );
527 error_sub( _( "Can't load XML file \"%s\", "
528 "it's not a %s save file." ),
529 state->filename, PACKAGE );
530 return( FALSE );
531 }
532 if( sscanf( (char *) xnode->nsDef->href + strlen( NAMESPACE ) + 1,
533 "%d.%d.%d",
534 &state->major, &state->minor, &state->micro ) != 3 ) {
535 error_top( _( "Load failed." ) );
536 error_sub( _( "Can't load XML file \"%s\", "
537 "unable to extract version information from "
538 "namespace." ), state->filename );
539 return( FALSE );
540 }
541
542 #ifdef DEBUG
543 printf( "filemodel_load_all_xml: major = %d, minor = %d, micro = %d\n",
544 state->major, state->minor, state->micro );
545 #endif /*DEBUG*/
546
547 if( filemodel_top_load( filemodel, state, parent, xnode ) )
548 return( FALSE );
549
550 return( TRUE );
551 }
552
553 static gboolean
filemodel_load_all_xml_file(Filemodel * filemodel,Model * parent,const char * filename,const char * filename_user)554 filemodel_load_all_xml_file( Filemodel *filemodel, Model *parent,
555 const char *filename, const char *filename_user )
556 {
557 ModelLoadState *state;
558
559 if( !(state = model_loadstate_new( filename, filename_user )) )
560 return( FALSE );
561 if( !filemodel_load_all_xml( filemodel, parent, state ) ) {
562 model_loadstate_destroy( state );
563 return( FALSE );
564 }
565 model_loadstate_destroy( state );
566
567 return( TRUE );
568 }
569
570 static gboolean
filemodel_load_all_xml_openfile(Filemodel * filemodel,Model * parent,iOpenFile * of)571 filemodel_load_all_xml_openfile( Filemodel *filemodel,
572 Model *parent, iOpenFile *of )
573 {
574 ModelLoadState *state;
575
576 if( !(state = model_loadstate_new_openfile( of )) )
577 return( FALSE );
578 if( !filemodel_load_all_xml( filemodel, parent, state ) ) {
579 model_loadstate_destroy( state );
580 return( FALSE );
581 }
582 model_loadstate_destroy( state );
583
584 return( TRUE );
585 }
586
587 static gboolean
filemodel_load_all_text(Filemodel * filemodel,Model * parent,const char * filename,const char * filename_user)588 filemodel_load_all_text( Filemodel *filemodel, Model *parent,
589 const char *filename, const char *filename_user )
590 {
591 iOpenFile *of;
592
593 if( !(of = ifile_open_read( "%s", filename )) )
594 return( FALSE );
595
596 if( model_load_text( MODEL( filemodel ), parent, of ) ) {
597 ifile_close( of );
598 return( FALSE );
599 }
600 ifile_close( of );
601
602 return( TRUE );
603 }
604
605 /* Load filename into filemodel ... can mean merge as well as init.
606 *
607 * We load from @filename. If @filename_user is non-NULL, that's the filename
608 * we should record in the model.
609 */
610 gboolean
filemodel_load_all(Filemodel * filemodel,Model * parent,const char * filename,const char * filename_user)611 filemodel_load_all( Filemodel *filemodel, Model *parent,
612 const char *filename, const char *filename_user )
613 {
614 ModelClass *model_class = MODEL_GET_CLASS( filemodel );
615 const char *tname = G_OBJECT_CLASS_NAME( model_class );
616
617 #ifdef DEBUG
618 printf( "filemodel_load_all: load file \"%s\" into parent %s \"%s\"\n",
619 filename,
620 G_OBJECT_TYPE_NAME( parent ),
621 NN( IOBJECT( parent )->name ) );
622 #endif /*DEBUG*/
623
624 if( model_class->load_text ) {
625 if( !filemodel_load_all_text( filemodel, parent,
626 filename, filename_user ) )
627 return( FALSE );
628 }
629 else if( model_class->load ) {
630 if( !filemodel_load_all_xml_file( filemodel, parent,
631 filename, filename_user ) )
632 return( FALSE );
633 }
634 else {
635 error_top( _( "Not implemented." ) );
636 error_sub( _( "_%s() not implemented for class \"%s\"." ),
637 "load", tname );
638 return( FALSE );
639 }
640
641 /* Don't recomp here, we may be loading a bunch of interdependent
642 * files.
643 */
644
645 return( TRUE );
646 }
647
648 /* Load iOpenFile into filemodel ... can mean merge as well as init.
649 */
650 gboolean
filemodel_load_all_openfile(Filemodel * filemodel,Model * parent,iOpenFile * of)651 filemodel_load_all_openfile( Filemodel *filemodel, Model *parent,
652 iOpenFile *of )
653 {
654 ModelClass *model_class = MODEL_GET_CLASS( filemodel );
655 const char *tname = G_OBJECT_CLASS_NAME( model_class );
656
657 #ifdef DEBUG
658 printf( "filemodel_load_all_openfile: load \"%s\" "
659 "into parent %s \"%s\"\n",
660 of->fname,
661 G_OBJECT_TYPE_NAME( parent ),
662 NN( IOBJECT( parent )->name ) );
663 #endif /*DEBUG*/
664
665 if( model_class->load_text ) {
666 if( model_load_text( MODEL( filemodel ), parent, of ) )
667 return( FALSE );
668 }
669 else if( model_class->load ) {
670 if( !filemodel_load_all_xml_openfile( filemodel, parent, of ) )
671 return( FALSE );
672 }
673 else {
674 error_top( _( "Not implemented." ) );
675 error_sub( _( "_%s() not implemented for class \"%s\"." ),
676 "load", tname );
677 return( FALSE );
678 }
679
680 /* Don't recomp here, we may be loading a bunch of interdependent
681 * files.
682 */
683
684 return( TRUE );
685 }
686
687 /* Interactive stuff ... save first.
688 */
689
690 static void
filemodel_inter_saveas_sub_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)691 filemodel_inter_saveas_sub_cb( iWindow *iwnd,
692 void *client, iWindowNotifyFn nfn, void *sys )
693 {
694 Filesel *filesel = FILESEL( iwnd );
695 Filemodel *filemodel = FILEMODEL( client );
696 char *filename;
697
698 if( (filename = filesel_get_filename( filesel )) ) {
699 if( filemodel_top_save( filemodel, filename ) ) {
700 filemodel_set_filename( filemodel, filename );
701 filemodel_set_modified( filemodel, FALSE );
702 nfn( sys, IWINDOW_YES );
703 }
704 else
705 nfn( sys, IWINDOW_ERROR );
706
707 g_free( filename );
708 }
709 else
710 nfn( sys, IWINDOW_ERROR );
711 }
712
713 static void
filemodel_inter_saveas_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)714 filemodel_inter_saveas_cb( iWindow *iwnd, void *client,
715 iWindowNotifyFn nfn, void *sys )
716 {
717 Filemodel *filemodel = FILEMODEL( client );
718 FilemodelClass *class = FILEMODEL_GET_CLASS( filemodel );
719
720 Filesel *filesel = FILESEL( filesel_new() );
721
722 /* Expands to (eg.) "Save Column A2".
723 */
724 iwindow_set_title( IWINDOW( filesel ), _( "Save %s %s" ),
725 IOBJECT_GET_CLASS_NAME( filemodel ),
726 NN( IOBJECT( filemodel )->name ) );
727 filesel_set_flags( filesel, FALSE, TRUE );
728 filesel_set_filetype( filesel,
729 class->filetype,
730 watch_int_get( main_watchgroup, class->filetype_pref, 0 ) );
731 iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) );
732 filesel_set_done( filesel, filemodel_inter_saveas_sub_cb, filemodel );
733 idialog_set_notify( IDIALOG( filesel ), nfn, sys );
734 iwindow_build( IWINDOW( filesel ) );
735 if( filemodel->filename )
736 filesel_set_filename( filesel, filemodel->filename );
737
738 gtk_widget_show( GTK_WIDGET( filesel ) );
739 }
740
741 void
filemodel_inter_saveas(iWindow * parent,Filemodel * filemodel)742 filemodel_inter_saveas( iWindow *parent, Filemodel *filemodel )
743 {
744 filemodel_inter_saveas_cb( parent, filemodel,
745 iwindow_notify_null, NULL );
746 }
747
748 void
filemodel_inter_save(iWindow * parent,Filemodel * filemodel)749 filemodel_inter_save( iWindow *parent, Filemodel *filemodel )
750 {
751 if( filemodel->filename ) {
752 if( !filemodel_top_save( filemodel, filemodel->filename ) )
753 iwindow_alert( GTK_WIDGET( parent ),
754 GTK_MESSAGE_ERROR );
755 else
756 filemodel_set_modified( filemodel, FALSE );
757 }
758 else
759 filemodel_inter_saveas( parent, filemodel );
760 }
761
762 /* Now "empty" ... do an 'are you sure' check if modified has been set.
763 */
764
765 static void
filemodel_inter_empty_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)766 filemodel_inter_empty_cb( iWindow *iwnd, void *client,
767 iWindowNotifyFn nfn, void *sys )
768 {
769 Filemodel *filemodel = FILEMODEL( client );
770
771 (void) model_empty( MODEL( filemodel ) );
772 filemodel_set_modified( filemodel, FALSE );
773
774 nfn( sys, IWINDOW_YES );
775 }
776
777 static void
filemodel_inter_savenempty_ok_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)778 filemodel_inter_savenempty_ok_cb( iWindow *iwnd, void *client,
779 iWindowNotifyFn nfn, void *sys )
780 {
781 iWindowSusp *susp = iwindow_susp_new( filemodel_inter_empty_cb,
782 iwnd, client, nfn, sys );
783
784 filemodel_inter_saveas_cb( iwnd, client, iwindow_susp_comp, susp );
785 }
786
787 void
filemodel_inter_savenempty_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)788 filemodel_inter_savenempty_cb( iWindow *iwnd, void *client,
789 iWindowNotifyFn nfn, void *sys )
790 {
791 Filemodel *filemodel = FILEMODEL( client );
792 const char *tname = IOBJECT_GET_CLASS_NAME( filemodel );
793
794 if( filemodel->modified ) {
795 if( filemodel->filename )
796 box_savenosave( GTK_WIDGET( iwnd ),
797 filemodel_inter_savenempty_ok_cb,
798 filemodel_inter_empty_cb, filemodel,
799 nfn, sys,
800 _( "Object has been modified." ),
801 _( "%s has been modified since you "
802 "loaded it from file \"%s\".\n\n"
803 "Do you want to save your changes?" ),
804 tname,
805 NN( filemodel->filename ) );
806 else
807 box_savenosave( GTK_WIDGET( iwnd ),
808 filemodel_inter_savenempty_ok_cb,
809 filemodel_inter_empty_cb, filemodel,
810 nfn, sys,
811 _( "Object has been modified." ),
812 _( "%s has been modified. "
813 "Do you want to save your changes?" ),
814 tname );
815 }
816 else
817 filemodel_inter_empty_cb( NULL, filemodel, nfn, sys );
818 }
819
820 void
filemodel_inter_savenempty(iWindow * parent,Filemodel * filemodel)821 filemodel_inter_savenempty( iWindow *parent, Filemodel *filemodel )
822 {
823 filemodel_inter_savenempty_cb( parent, filemodel,
824 iwindow_notify_null, NULL );
825 }
826
827 /* Now "close" ... easy: just savenempty, then destroy.
828 */
829
830 static void
filemodel_inter_close_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)831 filemodel_inter_close_cb( iWindow *iwnd, void *client,
832 iWindowNotifyFn nfn, void *sys )
833 {
834 Filemodel *filemodel = FILEMODEL( client );
835
836 iwindow_kill( filemodel_get_window_hint( filemodel ) );
837
838 nfn( sys, IWINDOW_YES );
839 }
840
841 void
filemodel_inter_savenclose_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)842 filemodel_inter_savenclose_cb( iWindow *iwnd, void *client,
843 iWindowNotifyFn nfn, void *sys )
844 {
845 iWindowSusp *susp = iwindow_susp_new( filemodel_inter_close_cb,
846 iwnd, client, nfn, sys );
847
848 filemodel_inter_savenempty_cb( iwnd, client, iwindow_susp_comp, susp );
849 }
850
851 void
filemodel_inter_savenclose(iWindow * parent,Filemodel * filemodel)852 filemodel_inter_savenclose( iWindow *parent, Filemodel *filemodel )
853 {
854 filemodel_inter_savenclose_cb( parent, filemodel,
855 iwindow_notify_null, NULL );
856 }
857
858 /* Now "load" ... add stuff to a model from a file.
859 */
860
861 static void
filemodel_inter_load_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)862 filemodel_inter_load_cb( iWindow *iwnd,
863 void *client, iWindowNotifyFn nfn, void *sys )
864 {
865 Filesel *filesel = FILESEL( iwnd );
866 Filemodel *filemodel = FILEMODEL( client );
867 iContainer *parent = ICONTAINER( filemodel )->parent;
868 char *filename;
869
870 if( (filename = filesel_get_filename( filesel )) ) {
871 filemodel_set_filename( filemodel, filename );
872
873 if( filemodel_load_all( filemodel, MODEL( parent ),
874 filename, NULL ) )
875 nfn( sys, IWINDOW_YES );
876 else
877 nfn( sys, IWINDOW_ERROR );
878
879 g_free( filename );
880 }
881 else
882 nfn( sys, IWINDOW_ERROR );
883 }
884
885 static void
filemodel_inter_loadas_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)886 filemodel_inter_loadas_cb( iWindow *iwnd, void *client,
887 iWindowNotifyFn nfn, void *sys )
888 {
889 Filemodel *filemodel = FILEMODEL( client );
890 FilemodelClass *class = FILEMODEL_GET_CLASS( filemodel );
891
892 Filesel *filesel = FILESEL( filesel_new() );
893
894 iwindow_set_title( IWINDOW( filesel ), "Load %s",
895 IOBJECT_GET_CLASS_NAME( filemodel ) );
896 filesel_set_flags( filesel, FALSE, TRUE );
897 filesel_set_filetype( filesel,
898 class->filetype,
899 watch_int_get( main_watchgroup, class->filetype_pref, 0 ) );
900 iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( iwnd ) );
901 filesel_set_done( filesel, filemodel_inter_load_cb, filemodel );
902 idialog_set_notify( IDIALOG( filesel ), nfn, sys );
903 iwindow_build( IWINDOW( filesel ) );
904 if( filemodel->filename )
905 filesel_set_filename( filesel, filemodel->filename );
906
907 gtk_widget_show( GTK_WIDGET( filesel ) );
908 }
909
910 void
filemodel_inter_loadas(iWindow * parent,Filemodel * filemodel)911 filemodel_inter_loadas( iWindow *parent, Filemodel *filemodel )
912 {
913 filemodel_inter_loadas_cb( parent, filemodel,
914 iwindow_notify_null, NULL );
915 }
916
917 /* Finally "replace" ... empty, then load.
918 */
919
920 static void
filemodel_inter_replace_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)921 filemodel_inter_replace_cb( iWindow *iwnd, void *client,
922 iWindowNotifyFn nfn, void *sys )
923 {
924 iWindowSusp *susp = iwindow_susp_new( filemodel_inter_loadas_cb,
925 iwnd, client, nfn, sys );
926
927 filemodel_inter_savenempty_cb( iwnd, client, iwindow_susp_comp, susp );
928 }
929
930 void
filemodel_inter_replace(iWindow * parent,Filemodel * filemodel)931 filemodel_inter_replace( iWindow *parent, Filemodel *filemodel )
932 {
933 filemodel_inter_replace_cb( parent, filemodel,
934 iwindow_notify_null, NULL );
935 }
936
937 /* Close all registered filemodels.
938 */
939
940 /* The first registered, modified filemodel the user hasn't said "ok!!! ffs"
941 * to.
942 */
943 static Filemodel *
filemodel_inter_close_get_filemodel(void)944 filemodel_inter_close_get_filemodel( void )
945 {
946 GSList *p;
947
948 for( p = filemodel_registered; p; p = p->next ) {
949 Filemodel *filemodel = FILEMODEL( p->data );
950
951 if( filemodel->modified )
952 return( filemodel );
953 }
954
955 return( NULL );
956 }
957
958 void
filemodel_inter_close_registered_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)959 filemodel_inter_close_registered_cb( iWindow *iwnd, void *client,
960 iWindowNotifyFn nfn, void *sys )
961 {
962 Filemodel *filemodel;
963
964 if( (filemodel = filemodel_inter_close_get_filemodel()) ) {
965 iWindowSusp *susp = iwindow_susp_new(
966 filemodel_inter_close_registered_cb,
967 iwnd, client, nfn, sys );
968
969 filemodel_inter_savenclose_cb(
970 filemodel_get_window_hint( filemodel ), filemodel,
971 iwindow_susp_comp, susp );
972 }
973 else
974 nfn( sys, IWINDOW_YES );
975 }
976
977 /* Mark something as having been loaded (or made) during startup. If we loaded
978 * from one of the system areas, zap the filename so that we will save to the
979 * user's area on changes.
980 */
981 void
filemodel_set_auto_load(Filemodel * filemodel)982 filemodel_set_auto_load( Filemodel *filemodel )
983 {
984 filemodel->auto_load = TRUE;
985
986 /*
987
988 FIXME ... not very futureproof
989
990 */
991 if( filemodel->filename &&
992 strstr( filemodel->filename,
993 "share" G_DIR_SEPARATOR_S PACKAGE ) ) {
994 char *p = strrchr( filemodel->filename, G_DIR_SEPARATOR );
995 char buf[FILENAME_MAX];
996
997 g_assert( p );
998
999 im_snprintf( buf, FILENAME_MAX, "$SAVEDIR" G_DIR_SEPARATOR_S
1000 "start" G_DIR_SEPARATOR_S "%s", p + 1 );
1001 filemodel_set_filename( filemodel, buf );
1002 }
1003 }
1004