file.c 9.55 KB
Newer Older
Arturo Espinosa's avatar
Arturo Espinosa committed
1 2 3 4 5 6 7 8 9
/*
 * file.c: File loading and saving routines
 *
 * Author:
 *   Miguel de Icaza (miguel@kernel.org)
 */
#include <config.h>
#include <gnome.h>
#include "gnumeric.h"
10
#include "gnumeric-util.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
11 12
#include "dialogs.h"
#include "xml-io.h"
13
#include "file.h"
14
#include <locale.h>
15

16 17 18
static GList *gnumeric_file_savers = NULL;
static GList *gnumeric_file_openers = NULL;
static FileSaver *current_saver = NULL;
19 20 21 22

static gint
file_priority_sort (gconstpointer a, gconstpointer b)
{
23 24
	const FileOpener *fa = (const FileOpener *)a;
	const FileOpener *fb = (const FileOpener *)b;
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

	return fb->priority - fa->priority;
}

/**
 * file_format_register_open:
 * @priority: The priority at which this file open handler is registered
 * @desc: a description of this file format
 * @probe_fn: A routine that would probe if the file is of a given type.
 * @open_fn: A routine that would load the code
 *
 * The priority is used to give it a higher precendence to a format.
 * The higher the priority, the sooner it will be tried, gnumeric registers
 * its XML-based format at priority 50.
 */
void
41 42
file_format_register_open (int priority, const char *desc,
			   FileFormatProbe probe_fn, FileFormatOpen open_fn)
43 44 45 46 47
{
	FileOpener *fo = g_new (FileOpener, 1);

	g_return_if_fail (probe_fn != NULL);
	g_return_if_fail (open_fn != NULL);
48

49
	fo->priority = priority;
50
	fo->format_description = desc ? g_strdup (desc) : NULL;
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	fo->probe = probe_fn;
	fo->open  = open_fn;

	gnumeric_file_openers = g_list_insert_sorted (gnumeric_file_openers, fo, file_priority_sort);
}

/**
 * file_format_unregister_open:
 * @probe: The routine that was used to probe
 * @open:  The routine that was used to open
 *
 * This function is used to remove a registered format opener from gnumeric
 */
void
file_format_unregister_open (FileFormatProbe probe, FileFormatOpen open)
{
	GList *l;

	for (l = gnumeric_file_openers; l; l = l->next){
		FileOpener *fo = l->data;

		if (fo->probe == probe && fo->open == open){
			gnumeric_file_openers = g_list_remove_link (gnumeric_file_openers, l);
Morten Welinder's avatar
Morten Welinder committed
74
			g_list_free_1 (l);
75 76 77
			if (fo->format_description)
				g_free (fo->format_description);
			g_free (fo);
78 79 80 81 82 83 84 85 86 87 88 89 90 91
			return;
		}
	}
}

/**
 * file_format_register_save:
 * @extension: An extension that is usually used by this format
 * @format_description: A description of this format
 * @save_fn: A function that should be used to save
 *
 * This routine registers a file format save routine with Gnumeric
 */
void
92 93
file_format_register_save (char *extension, const char *format_description,
			   FileFormatSave save_fn)
94 95 96 97
{
	FileSaver *fs = g_new (FileSaver, 1);

	g_return_if_fail (save_fn != NULL);
98

99
	fs->extension = extension;
100 101
	fs->format_description =
		format_description ? g_strdup (format_description) : NULL;
102 103
	fs->save = save_fn;

Michael Meeks's avatar
Michael Meeks committed
104
	gnumeric_file_savers = g_list_append (gnumeric_file_savers, fs);
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
}

/**
 * file_format_unregister_save:
 * @probe: The routine that was used to save
 *
 * This function is used to remove a registered format saver from gnumeric
 */
void
file_format_unregister_save (FileFormatSave save)
{
	GList *l;

	for (l = gnumeric_file_savers; l; l = l->next){
		FileSaver *fs = l->data;

		if (fs->save == save){
122 123
			if (fs == current_saver)
				current_saver = NULL;
124

125
			gnumeric_file_savers = g_list_remove_link (gnumeric_file_savers, l);
Morten Welinder's avatar
Morten Welinder committed
126
			g_list_free_1 (l);
127 128 129
			if (fs->format_description)
				g_free (fs->format_description);
			g_free (fs);
130 131 132 133 134 135 136 137 138
			return;
		}
	}
}

Workbook *
workbook_read (const char *filename)
{
	GList *l;
139 140
	char *oldlocale;
	Workbook *w = NULL;
141 142 143

	g_return_val_if_fail (filename != NULL, NULL);

144 145 146
	/* Files are expected to be in standard C format.  */
	oldlocale = g_strdup (setlocale (LC_NUMERIC, "C"));

147
	for (l = gnumeric_file_openers; l; l = l->next){
148 149
		const FileOpener *fo = l->data;

150 151 152
		if ((*fo->probe) (filename)){
			w = (*fo->open) (filename);

153 154
			if (w) {
				workbook_mark_clean (w);
155
				break;
156
			}
157 158
		}
	}
159 160 161 162

	setlocale (LC_NUMERIC, oldlocale);
	g_free (oldlocale);
	return w;
163
}
Arturo Espinosa's avatar
Arturo Espinosa committed
164 165 166 167 168 169 170 171

static void
set_ok (GtkWidget *widget, gboolean *dialog_result)
{
	*dialog_result = TRUE;
	gtk_main_quit ();
}

172 173 174 175 176 177 178 179 180 181 182 183
/**
 * saver_activate:
 *
 * Callback routine to choose the current file saver
 */
static void
saver_activate (GtkMenuItem *item, FileSaver *saver)
{
	GList *l;

	for (l = gnumeric_file_savers; l; l = l->next){
		FileSaver *fs = l->data;
184

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
		if (fs == saver)
			current_saver = saver;
	}
}

/**
 * file_saver_is_default_format:
 *
 * Returns TRUE if @saver is the default file save format
 */
static gboolean
file_saver_is_default_format (FileSaver *saver)
{
	if (current_saver == saver)
		return TRUE;
200 201 202
	
	if (current_saver == NULL)
		if (strcmp (saver->extension, ".gnumeric") == 0) {
203
			current_saver = saver;
204 205 206
			return TRUE;
		}

207 208 209 210 211 212 213
	return FALSE;
}

static void
fill_save_menu (GtkOptionMenu *omenu, GtkMenu *menu)
{
	GList *l;
214
	int i, selected=-1;
215

216 217 218 219 220 221
	for (i = 0, l = gnumeric_file_savers; l; l = l->next, i++){
		GtkWidget *menu_item;
		FileSaver *fs = l->data;

		menu_item = gtk_menu_item_new_with_label (fs->format_description);
		gtk_widget_show (menu_item);
222

223 224 225
		gtk_menu_append (menu, menu_item);

		if (file_saver_is_default_format (fs))
226
			selected = i;
227 228 229 230

		gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
				    GTK_SIGNAL_FUNC(saver_activate), fs);
	}
231 232 233 234

	gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), GTK_WIDGET (menu));
	if (selected > 0)
		gtk_option_menu_set_history (omenu, selected);
235 236 237 238 239 240 241 242 243 244 245 246 247 248
}

static GtkWidget *
make_format_chooser (void)
{
	GtkWidget *box, *label;
	GtkWidget *omenu, *menu;

	box = gtk_hbox_new (0, GNOME_PAD);
	label = gtk_label_new (_("File format:"));
	omenu = gtk_option_menu_new ();
	menu = gtk_menu_new ();

	fill_save_menu (GTK_OPTION_MENU (omenu), GTK_MENU (menu));
249

250 251 252 253 254 255 256
	gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, GNOME_PAD);
	gtk_box_pack_start (GTK_BOX (box), omenu, FALSE, TRUE, GNOME_PAD);
	gtk_widget_show_all (box);

	return box;
}

257 258 259 260 261 262 263 264 265 266 267
static FileSaver *
insure_saver (FileSaver *current)
{
	GList *l;

	if (current)
		return current;

	for (l = gnumeric_file_savers; l; l = l->next){
		return l->data;
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
268 269
	g_assert_not_reached ();
	return NULL;
270 271
}

272 273 274 275 276 277 278
static guint
file_dialog_delete_event (GtkWidget *widget, GdkEventAny *event)
{
	gtk_main_quit ();
	return TRUE;
}

Arturo Espinosa's avatar
Arturo Espinosa committed
279 280 281 282 283
void
workbook_save_as (Workbook *wb)
{
	GtkFileSelection *fsel;
	gboolean accepted = FALSE;
284
	GtkWidget *format_selector;
285

Arturo Espinosa's avatar
Arturo Espinosa committed
286 287
	g_return_if_fail (wb != NULL);

288
	fsel = GTK_FILE_SELECTION (gtk_file_selection_new (_("Save workbook as")));
289

290
	gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
Arturo Espinosa's avatar
Arturo Espinosa committed
291 292
	if (wb->filename)
		gtk_file_selection_set_filename (fsel, wb->filename);
293 294 295 296 297

	/* Choose the format */
	format_selector = make_format_chooser ();
	gtk_box_pack_start (GTK_BOX (fsel->action_area), format_selector,
			    FALSE, TRUE, 0);
298

Arturo Espinosa's avatar
Arturo Espinosa committed
299 300 301 302 303
	/* Connect the signals for Ok and Cancel */
	gtk_signal_connect (GTK_OBJECT (fsel->ok_button), "clicked",
			    GTK_SIGNAL_FUNC (set_ok), &accepted);
	gtk_signal_connect (GTK_OBJECT (fsel->cancel_button), "clicked",
			    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
304
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
305

306 307 308 309 310 311
	/*
	 * Make sure that we quit the main loop if the window is destroyed 
	 */
	gtk_signal_connect (GTK_OBJECT (fsel), "delete_event",
			    GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
									   
Arturo Espinosa's avatar
Arturo Espinosa committed
312
	/* Run the dialog */
313

Arturo Espinosa's avatar
Arturo Espinosa committed
314 315 316 317 318 319 320 321
	gtk_widget_show (GTK_WIDGET (fsel));
	gtk_grab_add (GTK_WIDGET (fsel));
	gtk_main ();

	if (accepted){
		char *name = gtk_file_selection_get_filename (fsel);

		if (name [strlen (name)-1] != '/'){
Arturo Espinosa's avatar
Arturo Espinosa committed
322
			char *base = g_basename (name);
323

324 325
			current_saver = insure_saver (current_saver);
			if (!current_saver)
326 327
				gnumeric_notice (wb, GNOME_MESSAGE_BOX_ERROR,
						 _("Sorry, there are no file savers loaded, I cannot save"));
Arturo Espinosa's avatar
Arturo Espinosa committed
328
			else {
329 330
				char *oldlocale;

Arturo Espinosa's avatar
Arturo Espinosa committed
331 332 333 334
				if (strchr (base, '.') == NULL){
					name = g_strconcat (name, current_saver->extension, NULL);
				} else
					name = g_strdup (name);
335

Arturo Espinosa's avatar
Arturo Espinosa committed
336
				workbook_set_filename (wb, name);
337

338 339 340
				/* Files are expected to be in standard C format.  */
				oldlocale = g_strdup (setlocale (LC_NUMERIC, "C"));

341
				current_saver->save (wb, wb->filename);
342

343 344 345
				setlocale (LC_NUMERIC, oldlocale);
				g_free (oldlocale);

Arturo Espinosa's avatar
Arturo Espinosa committed
346 347
				g_free (name);
			}
Arturo Espinosa's avatar
Arturo Espinosa committed
348 349 350 351 352 353 354 355
		}
	}
	gtk_widget_destroy (GTK_WIDGET (fsel));
}

void
workbook_save (Workbook *wb)
{
356 357
	char *oldlocale;

Arturo Espinosa's avatar
Arturo Espinosa committed
358
	g_return_if_fail (wb != NULL);
359

Arturo Espinosa's avatar
Arturo Espinosa committed
360 361 362 363 364
	if (!wb->filename){
		workbook_save_as (wb);
		return;
	}

365 366
	/* Files are expected to be in standard C format.  */
	oldlocale = g_strdup (setlocale (LC_NUMERIC, "C"));
367
	gnumeric_xml_write_workbook (wb, wb->filename);
368 369 370

	setlocale (LC_NUMERIC, oldlocale);
	g_free (oldlocale);
Arturo Espinosa's avatar
Arturo Espinosa committed
371 372 373
}

char *
374
dialog_query_load_file (Workbook *wb)
Arturo Espinosa's avatar
Arturo Espinosa committed
375 376
{
	GtkFileSelection *fsel;
377
	gboolean accepted = FALSE;
Arturo Espinosa's avatar
Arturo Espinosa committed
378
	char *result;
379

380
	fsel = GTK_FILE_SELECTION (gtk_file_selection_new (_("Load file")));
381
	gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
Arturo Espinosa's avatar
Arturo Espinosa committed
382

383
	gtk_window_set_transient_for (GTK_WINDOW (fsel), GTK_WINDOW (wb->toplevel));
384

Arturo Espinosa's avatar
Arturo Espinosa committed
385 386 387 388 389
	/* Connect the signals for Ok and Cancel */
	gtk_signal_connect (GTK_OBJECT (fsel->ok_button), "clicked",
			    GTK_SIGNAL_FUNC (set_ok), &accepted);
	gtk_signal_connect (GTK_OBJECT (fsel->cancel_button), "clicked",
			    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
390
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
Arturo Espinosa's avatar
Arturo Espinosa committed
391

392 393 394 395 396 397
	/*
	 * Make sure that we quit the main loop if the window is destroyed 
	 */
	gtk_signal_connect (GTK_OBJECT (fsel), "delete_event",
			    GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);

Arturo Espinosa's avatar
Arturo Espinosa committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
	/* Run the dialog */
	gtk_widget_show (GTK_WIDGET (fsel));
	gtk_grab_add (GTK_WIDGET (fsel));
	gtk_main ();

	if (accepted){
		char *name = gtk_file_selection_get_filename (fsel);

		if (name [strlen (name)-1] == '/')
			result = NULL;
		else
			result = g_strdup (name);
	} else
		result = NULL;

	gtk_widget_destroy (GTK_WIDGET (fsel));

	return result;
}