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