file.c 8.36 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 14
#include "file.h"

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

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

	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
40 41
file_format_register_open (int priority, const char *desc,
			   FileFormatProbe probe_fn, FileFormatOpen open_fn)
42 43 44 45 46
{
	FileOpener *fo = g_new (FileOpener, 1);

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

48
	fo->priority = priority;
49
	fo->format_description = desc ? g_strdup (desc) : NULL;
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	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
73
			g_list_free_1 (l);
74 75 76
			if (fo->format_description)
				g_free (fo->format_description);
			g_free (fo);
77 78 79 80 81 82 83 84 85 86 87 88 89 90
			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
91 92
file_format_register_save (char *extension, const char *format_description,
			   FileFormatSave save_fn)
93 94 95 96
{
	FileSaver *fs = g_new (FileSaver, 1);

	g_return_if_fail (save_fn != NULL);
97

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

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

/**
 * 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){
121 122
			if (fs == current_saver)
				current_saver = NULL;
123

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

Workbook *
workbook_read (const char *filename)
{
	GList *l;

	g_return_val_if_fail (filename != NULL, NULL);

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

144
		if ((*fo->probe) (filename)){
145
			Workbook *w;
146 147 148 149 150 151 152 153
			w = (*fo->open) (filename);

			if (w)
				return w;
		}
	}
	return NULL;
}
Arturo Espinosa's avatar
Arturo Espinosa committed
154 155 156 157 158 159 160 161

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

162 163 164 165 166 167 168 169 170 171 172 173
/**
 * 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;
174

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
		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;

191 192 193
	if (strcmp (saver->extension, ".gnumeric") == 0){
		if (current_saver == NULL)
			current_saver = saver;
194
		return TRUE;
195
	}
196 197 198 199 200 201 202 203
	return FALSE;
}

static void
fill_save_menu (GtkOptionMenu *omenu, GtkMenu *menu)
{
	GList *l;
	int i;
204

205 206 207 208 209 210
	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);
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		gtk_menu_append (menu, menu_item);

		if (file_saver_is_default_format (fs))
			gtk_option_menu_set_history (omenu, i);

		gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
				    GTK_SIGNAL_FUNC(saver_activate), fs);
	}
}

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));
234

235 236 237 238 239
	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);

	gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
240

241 242 243
	return box;
}

244 245 246 247 248 249 250 251 252 253 254
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
255 256
	g_assert_not_reached ();
	return NULL;
257 258
}

Arturo Espinosa's avatar
Arturo Espinosa committed
259 260 261 262 263
void
workbook_save_as (Workbook *wb)
{
	GtkFileSelection *fsel;
	gboolean accepted = FALSE;
264
	GtkWidget *format_selector;
265

Arturo Espinosa's avatar
Arturo Espinosa committed
266 267 268
	g_return_if_fail (wb != NULL);

	fsel = (GtkFileSelection *)gtk_file_selection_new (_("Save workbook as"));
269

270
	gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
Arturo Espinosa's avatar
Arturo Espinosa committed
271 272
	if (wb->filename)
		gtk_file_selection_set_filename (fsel, wb->filename);
273 274 275 276 277

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

279

Arturo Espinosa's avatar
Arturo Espinosa committed
280 281 282 283 284
	/* 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);
285
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
286

Arturo Espinosa's avatar
Arturo Espinosa committed
287 288 289 290 291 292 293 294 295
	/* 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] != '/'){
Arturo Espinosa's avatar
Arturo Espinosa committed
296
			char *base = g_basename (name);
297

298 299
			current_saver = insure_saver (current_saver);
			if (!current_saver)
300
				gnumeric_notice (wb, GNOME_MESSAGE_BOX_ERROR, _("Sorry, there are no file savers loaded, I cannot save"));
Arturo Espinosa's avatar
Arturo Espinosa committed
301 302 303 304 305
			else {
				if (strchr (base, '.') == NULL){
					name = g_strconcat (name, current_saver->extension, NULL);
				} else
					name = g_strdup (name);
306

Arturo Espinosa's avatar
Arturo Espinosa committed
307
				workbook_set_filename (wb, name);
308

309
				current_saver->save (wb, wb->filename);
310

Arturo Espinosa's avatar
Arturo Espinosa committed
311 312
				g_free (name);
			}
Arturo Espinosa's avatar
Arturo Espinosa committed
313 314 315 316 317 318 319 320 321
		}
	}
	gtk_widget_destroy (GTK_WIDGET (fsel));
}

void
workbook_save (Workbook *wb)
{
	g_return_if_fail (wb != NULL);
322

Arturo Espinosa's avatar
Arturo Espinosa committed
323 324 325 326 327 328 329 330 331
	if (!wb->filename){
		workbook_save_as (wb);
		return;
	}

	gnumericWriteXmlWorkbook (wb, wb->filename);
}

char *
332
dialog_query_load_file (Workbook *wb)
Arturo Espinosa's avatar
Arturo Espinosa committed
333 334
{
	GtkFileSelection *fsel;
335
	gboolean accepted = FALSE;
Arturo Espinosa's avatar
Arturo Espinosa committed
336
	char *result;
337

Arturo Espinosa's avatar
Arturo Espinosa committed
338
	fsel = (GtkFileSelection *) gtk_file_selection_new (_("Load file"));
339
	gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
Arturo Espinosa's avatar
Arturo Espinosa committed
340

341
	gtk_window_set_transient_for (GTK_WINDOW (fsel), GTK_WINDOW (wb->toplevel));
342

Arturo Espinosa's avatar
Arturo Espinosa committed
343 344 345 346 347
	/* 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);
348
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
Arturo Espinosa's avatar
Arturo Espinosa committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

	/* 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;
}