[evolution-patches] Implementation for the RDF-format (save-calendar plugin)



Hi there,

I've tested a few calendars to export them to the RDF-format. It looks
like the generated result is correct using this version.

More information about this RDF-format: 

	Website:
	http://www.w3.org/2002/12/cal/

	Schema:
	http://www.w3.org/2002/12/cal/ical
	http://www.w3.org/2002/12/cal/ical.rdf
	http://www.w3.org/2002/12/cal/ical.n3

	Test data:
	http://www.w3.org/2002/12/cal/test/


[freax lort save-calendar]$ tar cvf rdfformat-01.tar *
ChangeLog
csv-format.c
format-handler.h
ical-format.c
Makefile.am
org-gnome-save-calendar.eplug.in
rdf-format.c
save-calendar.c
[freax lort save-calendar]$

[freax lort save-calendar]$ cvs diff -u > patch.diff
cvs server: Diffing .
[freax lort save-calendar]$ pwd
/home/freax/cvs/gnome/evolution/plugins/save-calendar
[freax lort save-calendar]$


Please review.


-- 
Philip Van Hoof, Software Developer @ Cronos
home: me at freax dot org
gnome: pvanhoof at gnome dot org
work: philip dot vanhoof at cronos dot be
junk: philip dot vanhoof at gmail dot com
http://www.freax.be, http://www.freax.eu.org
? csv-format.c
? format-handler.h
? ical-format.c
? patch.diff
? rdf-format.c
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/plugins/save-calendar/ChangeLog,v
retrieving revision 1.6
diff -u -r1.6 ChangeLog
Binary files /tmp/cvstOFfsD and ChangeLog differ
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/plugins/save-calendar/Makefile.am,v
retrieving revision 1.2
diff -u -r1.2 Makefile.am
--- Makefile.am	1 Nov 2004 18:39:17 -0000	1.2
+++ Makefile.am	20 Dec 2004 09:12:08 -0000
@@ -7,7 +7,7 @@
 plugin_DATA = org-gnome-save-calendar.eplug
 plugin_LTLIBRARIES = liborg-gnome-save-calendar.la
 
-liborg_gnome_save_calendar_la_SOURCES = save-calendar.c
+liborg_gnome_save_calendar_la_SOURCES = save-calendar.c ical-format.c csv-format.c rdf-format.c
 liborg_gnome_save_calendar_la_LDFLAGS = -module -avoid-version
 
-EXTRA_DIST = org-gnome-save-calendar.eplug.in
\ No newline at end of file
+EXTRA_DIST = org-gnome-save-calendar.eplug.in
Index: save-calendar.c
===================================================================
RCS file: /cvs/gnome/evolution/plugins/save-calendar/save-calendar.c,v
retrieving revision 1.4
diff -u -r1.4 save-calendar.c
--- save-calendar.c	8 Dec 2004 11:33:07 -0000	1.4
+++ save-calendar.c	20 Dec 2004 09:12:08 -0000
@@ -44,6 +44,7 @@
 #include <libgnomevfs/gnome-vfs.h>
 #include <string.h>
 
+#include "format-handler.h"
 
 enum {  /* GtkComboBox enum */
 	DEST_NAME_COLUMN,
@@ -51,643 +52,200 @@
 	N_DEST_COLUMNS
 
 };
-enum { /* CSV helper enum */
-	ECALCOMPONENTTEXT,
-	ECALCOMPONENTATTENDEE,
-	CONSTCHAR
-};
 
-typedef struct _CsvConfig CsvConfig;
-struct _CsvConfig {
-	gchar *newline;
-	gchar *quote;
-	gchar *delimiter;
-	gboolean header;
-};
 void org_gnome_save_calendar (EPlugin *ep, ECalPopupTargetSource *target);
 void org_gnome_save_tasks (EPlugin *ep, ECalPopupTargetSource *target);
-static gboolean string_needsquotes (const char *value, CsvConfig *config);
-
-static void
-display_error_message (GtkWidget *parent, GError *error)
-{
-	GtkWidget *dialog;
-
-	dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
-					 error->message);
-	gtk_dialog_run (GTK_DIALOG (dialog));
-	gtk_widget_destroy (dialog);
-}
-
-
-/* Some helpers for the csv stuff */
-static GString *
-add_list_to_csv (GString *line, GSList *list_in, CsvConfig *config, gint type)
-{
-
-	/* 
-	 * This one will write 'ECalComponentText' and 'const char' GSLists. It will
-	 * put quotes around the complete written value if there's was only one value
-	 * but it required having quotes and if there was more than one value (in which
-	 * case delimiters are used to separate them, hence the need for the quotes).
-	 */
-
-	if (list_in) {
-		gboolean needquotes = FALSE;
-		GSList *list = list_in;
-		GString *tmp = NULL;
-		gint cnt=0;
-		while (list) {
-			const char *str = NULL;
-			if (cnt == 0)
-				tmp = g_string_new ("");
-			if (cnt > 0)
-				needquotes = TRUE;
-			switch (type) {
-			case ECALCOMPONENTATTENDEE:
-				str = ((ECalComponentAttendee*)list->data)->value;
-				break;
-			case ECALCOMPONENTTEXT:
-				str = ((ECalComponentText*)list->data)->value;
-				break;
-			case CONSTCHAR:
-			default:
-				str = list->data;
-				break;
-			}
-			if (!needquotes)
-				needquotes = string_needsquotes (str, config);
-			if (str)
-				tmp = g_string_append (tmp, (const gchar*)str);
-			list = g_slist_next (list); cnt++;
-			if (list)
-				tmp = g_string_append (tmp, config->delimiter);
-		}
-	
-		if (needquotes)
-			line = g_string_append (line, config->quote);
-		line = g_string_append_len (line, tmp->str, tmp->len);
-		g_string_free (tmp, TRUE);
-		if (needquotes)
-			line = g_string_append (line, config->quote);
-	}
-
-	line = g_string_append (line, config->delimiter);
-	return line;
-}
-
-static GString *
-add_nummeric_to_csv (GString *line, gint *nummeric, CsvConfig *config)
-{
-
-	/* 
-	 * This one will write {-1}..{00}..{01}..{99}
-	 * it prepends a 0 if it's < 10 and > -1
-	 */
-
-	if (nummeric)
-		g_string_append_printf  (line, "%s%d", (*nummeric<10 && *nummeric>-1)?"0":"", *nummeric);
-
-	line = g_string_append (line, config->delimiter);
-	return line;
-}
-
-static GString *
-add_time_to_csv (GString *line, icaltimetype *time, CsvConfig *config)
-{
-	/*
-	 * Perhaps we should check for quotes, delimiter and newlines in the
-	 * resulting string: The translators can put it there!
-	 *
-	 * Or perhaps we shouldn't make this translatable?
-	 * Or perhaps there is a library-function to do this?
-	 */
-
-	if (time) {
-		g_string_append_printf (line, _("%s%d/%s%d/%s%d %s%d:%s%d:%s%d"), 
-					(time->month < 10)?"0":"", time->month, 
-					(time->day < 10)?"0":"", time->day, 
-					(time->year < 10)?"0":"", time->year, 
-					(time->hour < 10)?"0":"", time->hour, 
-					(time->minute < 10)?"0":"", time->minute, 
-					(time->second < 10)?"0":"", time->second);
-	}
-
-	line = g_string_append (line, config->delimiter);
-	return line;
-}
-
-static gboolean
-string_needsquotes (const char *value, CsvConfig *config)
-{
-
-	/* This is the actual need for quotes-checker */
-
-	/* 
-	 * These are the simple substring-checks 
-	 *
-	 * Example: {Mom, can you please do that for me?}
-	 * Will be written as {"Mom, can you please do that for me?"}
-	 */
-
-	gboolean needquotes = strstr (value, config->delimiter) ? TRUE:FALSE;
-
-	if (!needquotes) {
-		needquotes = strstr (value, config->newline) ? TRUE:FALSE;
-		if (!needquotes) 
-			needquotes = strstr (value, config->quote) ? TRUE:FALSE;
-	}
-
-
-	/* 
-	 * If the special-char is char+onespace (so like {, } {" }, {\n }) and it occurs
-	 * the value that is going to be written
-	 * 
-	 * In this case we don't trust the user . . . and are going to quote the string
-	 * just to play save -- Quoting is always allowed in the CSV format. If you can
-	 * avoid it, it's better to do so since a lot applications don't support CSV
-	 * correctly! --.
-	 *
-	 * Example: {Mom,can you please do that for me?}
-	 * This example will be written as {"Mom,can you please do that for me?"} because
-	 * there's a {,} behind {Mom} and the delimiter is {, } (so we searched only the
-	 * first character of {, } and didn't trust the user).
-	 */	
-
-
-	if (!needquotes) {
-		gint len = strlen (config->delimiter);
-		if ((len == 2) && (config->delimiter[1] = ' ')) {
-			needquotes = strchr (value, config->delimiter[0])?TRUE:FALSE;
-			if (!needquotes) {
-				gint len = strlen (config->newline);
-				if ((len == 2) && (config->newline[1] = ' ')) {
-					needquotes = strchr (value, config->newline[0])?TRUE:FALSE;
-					if (!needquotes) {
-						gint len = strlen (config->quote);
-						if ((len == 2) && (config->quote[1] = ' ')) {
-							needquotes = strchr 
-								(value, config->quote[0])?TRUE:FALSE;
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return needquotes;
-}
-
-static GString *
-add_string_to_csv (GString *line, const char *value, CsvConfig *config)
-{
-	/* Will add a string to the record and will check for the need for quotes */
-
-	if ((value) && (strlen(value)>0)) {
-		gboolean needquotes = string_needsquotes (value, config);
-
-		if (needquotes)
-			line = g_string_append (line, config->quote);
-		line = g_string_append (line, (const gchar*)value);
-		if (needquotes)
-			line = g_string_append (line, config->quote);
-	}
-	line = g_string_append (line, config->delimiter);
-	return line;
-}
-
-static void
-do_save_calendar_csv (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri, gpointer data)
-{
-
-	/* 
-	 * According to some documentation about CSV, newlines 'are' allowed 
-	 * in CSV-files. But you 'do' have to put the value between quotes.
-	 * The helper 'string_needsquotes' will check for that 
-	 *
-	 * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
-	 * http://www.creativyst.com/cgi-bin/Prod/15/eg/csv2xml.pl
-	 */
-
-	ESource *primary_source;
-	ECal *source_client;
-	GError *error = NULL;
-	GList *objects=NULL;
-	GnomeVFSResult result;
-	GnomeVFSHandle *handle;
-	GnomeVFSURI *uri;
-	GString *line = NULL;
-	CsvConfig *config = data;
- 
-	primary_source = e_source_selector_peek_primary_selection (target->selector);
-
-	if (!dest_uri)
-		return;
-
-	/* open source client */
-	source_client = e_cal_new (primary_source, type);
-	if (!e_cal_open (source_client, TRUE, &error)) {
-		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
-		g_object_unref (source_client);
-		g_error_free (error);
-		return;
-	}
-
-	uri = gnome_vfs_uri_new (dest_uri);
-	result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
-	if (result != GNOME_VFS_OK) {
-		gnome_vfs_create (&handle, dest_uri, GNOME_VFS_OPEN_WRITE, TRUE, GNOME_VFS_PERM_USER_ALL);
-		result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
-	}
-
-	if (result == GNOME_VFS_OK && e_cal_get_object_list_as_comp (source_client, "#t", &objects, NULL)) {
-
-		if (config->header) {
-			line = g_string_new ("");
-			g_string_append_printf (line, _("Uid%sSummary%sDescription List%sCategories List%s"
-							"Comment List%sCompleted%sCreated%sContact List%s"
-							"Start%sEnd%sDue%sPercent Done%sPriority%sUrl%s"
-							"Attendees List%sLocation%sModified%s"),
-						config->delimiter, config->delimiter, config->delimiter, config->delimiter, 
-						config->delimiter, config->delimiter, config->delimiter, config->delimiter, 
-						config->delimiter, config->delimiter, config->delimiter, config->delimiter, 
-						config->delimiter, config->delimiter, config->delimiter, config->delimiter, 
-						config->newline);
 
-			gnome_vfs_write (handle, line->str, line->len, NULL);
-			g_string_free (line, TRUE);
-		}
-
-	
-		while (objects != NULL) {
-			ECalComponent *comp = objects->data;
-			gchar *delimiter_temp = NULL;
-			const char *temp_constchar;
-			GSList *temp_list;
-			ECalComponentDateTime temp_dt;
-			struct icaltimetype *temp_time;
-			int *temp_int;
-			ECalComponentText temp_comptext; 
-
-			line = g_string_new ("");
-
-			/* Getting the stuff */
-			e_cal_component_get_uid (comp, &temp_constchar);
-			line = add_string_to_csv (line, temp_constchar, config);
-
-			e_cal_component_get_summary (comp, &temp_comptext);
-			line = add_string_to_csv (line, &temp_comptext?temp_comptext.value:NULL, config);
-
-			e_cal_component_get_description_list (comp, &temp_list);
-			line = add_list_to_csv (line, temp_list, config, ECALCOMPONENTTEXT);
-			if (temp_list)
-				e_cal_component_free_text_list (temp_list);
-
-			e_cal_component_get_categories_list (comp, &temp_list);
-			line = add_list_to_csv (line, temp_list, config, CONSTCHAR);
-			if (temp_list)
-				e_cal_component_free_categories_list (temp_list);
-
-			e_cal_component_get_comment_list (comp, &temp_list);
-			line = add_list_to_csv (line, temp_list, config, ECALCOMPONENTTEXT);
-			if (temp_list)
-				e_cal_component_free_text_list (temp_list);
-
-			e_cal_component_get_completed (comp, &temp_time);
-			line = add_time_to_csv (line, temp_time, config);
-			if (temp_time)
-				e_cal_component_free_icaltimetype (temp_time);
-
-			e_cal_component_get_created (comp, &temp_time);
-			line = add_time_to_csv (line, temp_time, config);
-			if (temp_time)
-				e_cal_component_free_icaltimetype (temp_time);
-
-			e_cal_component_get_contact_list (comp, &temp_list);
-			line = add_list_to_csv (line, temp_list, config, ECALCOMPONENTTEXT);
-			if (temp_list)
-				e_cal_component_free_text_list (temp_list);
-
-			e_cal_component_get_dtstart (comp, &temp_dt);
-			line = add_time_to_csv (line, temp_dt.value ? temp_dt.value : NULL, config);
-			if (temp_dt.value)
-				e_cal_component_free_datetime (&temp_dt);
-
-			e_cal_component_get_dtend (comp, &temp_dt);
-			line = add_time_to_csv (line, temp_dt.value ? temp_dt.value : NULL, config);
-			if (temp_dt.value)
-				e_cal_component_free_datetime (&temp_dt);
-
-			e_cal_component_get_due (comp, &temp_dt);
-			line = add_time_to_csv (line, temp_dt.value ? temp_dt.value : NULL, config);
-			if (temp_dt.value)
-				e_cal_component_free_datetime (&temp_dt);
-
-			e_cal_component_get_percent (comp, &temp_int);
-			line = add_nummeric_to_csv (line, temp_int, config);
-
-			e_cal_component_get_priority (comp, &temp_int);
-			line = add_nummeric_to_csv (line, temp_int, config);
-
-			e_cal_component_get_url (comp, &temp_constchar);
-			line = add_string_to_csv (line, temp_constchar, config);
-
-			if (e_cal_component_has_attendees (comp)) {
-				e_cal_component_get_attendee_list (comp, &temp_list);
-				line = add_list_to_csv (line, temp_list, config, ECALCOMPONENTATTENDEE);
-				if (temp_list)
-					e_cal_component_free_attendee_list (temp_list);
-			} else {
-				line = add_list_to_csv (line, NULL, config, ECALCOMPONENTATTENDEE);
-			}
-
-			e_cal_component_get_location (comp, &temp_constchar);
-			line = add_string_to_csv (line, temp_constchar, config);
-
-			e_cal_component_get_last_modified (comp, &temp_time);
-
-			/* Append a newline (record delimiter) */
-			delimiter_temp = config->delimiter;
-			config->delimiter = config->newline;
- 
-			line = add_time_to_csv (line, temp_time, config);
-
-			/* And restore for the next record */
-			config->delimiter = delimiter_temp;
-
-			/* Important note!
-			 * The documentation is not requiring this!
-			 *
-			 * if (temp_time) e_cal_component_free_icaltimetype (temp_time);
-			 *
-			 * Please uncomment and fix documentation if untrue
-			 * http://www.gnome.org/projects/evolution/developer-doc/libecal/ECalComponent.html
-			 *	#e-cal-component-get-last-modified
-			 */
-			gnome_vfs_write (handle, line->str, line->len, NULL);
 
-			/* It's written, so we can free it */
-			g_string_free (line, TRUE);
-
-			objects = g_list_next (objects);
-		}
-
-		gnome_vfs_close (handle);
-	}
-	g_object_unref (source_client);
-}
-
-static void
-do_save_calendar_ical (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri, gpointer data)
+static void 
+extra_widget_foreach_hide (GtkWidget *widget, gpointer data)
 {
-	ESource *primary_source;
-	ECal *source_client, *dest_client;
-	GError *error = NULL;
-
-	primary_source = e_source_selector_peek_primary_selection (target->selector);
-
-	if (!dest_uri)
-		return;
-
-	/* open source client */
-	source_client = e_cal_new (primary_source, type);
-	if (!e_cal_open (source_client, TRUE, &error)) {
-		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
-		g_object_unref (source_client);
-		g_error_free (error);
-		return;
-	}
-
-	/* open destination client */
-	error = NULL;
-	dest_client = e_cal_new_from_uri (dest_uri, type);
-	if (e_cal_open (dest_client, FALSE, &error)) {
-		GList *objects;
-
-		if (e_cal_get_object_list (source_client, "#t", &objects, NULL)) {
-			while (objects != NULL) {
-				icalcomponent *icalcomp = objects->data;
-
-				/* FIXME: deal with additions/modifications */
-
-				/* FIXME: This stores a directory with one file in it, the user expects only a file */
-
-				/* FIXME: It would be nice if this ical-handler would use gnome-vfs rather than e_cal_* */
-
-				error = NULL;
-				if (!e_cal_create_object (dest_client, icalcomp, NULL, &error)) {
-					display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
-					g_error_free (error);
-				}
-
-				/* remove item from the list */
-				objects = g_list_remove (objects, icalcomp);
-				icalcomponent_free (icalcomp);
-			}
-		}
-	} else {
-		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
-		g_error_free (error);
-	}
-
-	/* terminate */
-	g_object_unref (source_client);
-	g_object_unref (dest_client);
+	if (widget != data)
+		gtk_widget_hide (widget);
 }
 
-
 static void 
 on_type_combobox_changed (GtkComboBox *combobox, gpointer data)
 {
-	void (*handler) (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri, gpointer data);
+	FormatHandler *handler = NULL;
+	GtkWidget *extra_widget = data;
 	GtkTreeIter iter;
-	GtkWidget *csv_options = data;
 	GtkTreeModel *model = gtk_combo_box_get_model (combobox);
 
+	gtk_container_foreach (GTK_CONTAINER (extra_widget), 
+		extra_widget_foreach_hide, combobox);
+
 	gtk_combo_box_get_active_iter (combobox, &iter);
 
 	gtk_tree_model_get (model, &iter, 
 		DEST_HANDLER, &handler, -1);
 
-	if (handler == do_save_calendar_csv) {
-		gtk_widget_show (csv_options);
-	} else {
-		gtk_widget_hide (csv_options);
-	}
+
+	if (handler->options_widget)
+	{
+		gtk_widget_show (handler->options_widget);
+	} 
+
 }
 
-/* Convert what the user types to what he probably means */
-static gchar *
-userstring_to_systemstring (const gchar *userstring)
+static void 
+format_handlers_foreach_free (gpointer data, gpointer user_data)
 {
-	const gchar *text = userstring;
-	gint i=0, len = strlen(text);
-	GString *str = g_string_new ("");
-	gchar *retval = NULL;
-
-	while (i < len) {
-		if (text[i] == '\\') {
-			switch (text[i+1]) {
-			case 'n':
-				str = g_string_append_c (str, '\n');
-				i++;
-				break;
-			case '\\':
-				str = g_string_append_c (str, '\\');
-				i++;
-				break; 
-			case 'r':
-				str = g_string_append_c (str, '\r');
-				i++;		
-				break;
-			case 't':
-                                str = g_string_append_c (str, '\t'); 
-                                i++;
-                                break;
-			}
-		} else {
-			str = g_string_append_c (str, text[i]);
-		}
+	FormatHandler *handler = data;
 
-		i++;
-	}
+	if (handler->options_widget)
+		gtk_widget_destroy (handler->options_widget);
 
-	retval = str->str;
-	g_string_free (str, FALSE);
+	if (handler->data)
+		g_free (handler->data);
 
-	return retval; 
+	g_free (data);
 }
 
 static void 
 ask_destination_and_save (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type)
 {
-	void (*handler) (EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri, gpointer data);
-	gpointer data = NULL;
-	CsvConfig *config = NULL;
+	FormatHandler *handler = NULL;
 
 	GtkWidget *extra_widget = gtk_vbox_new (FALSE, 0);
 	GtkComboBox *combo = GTK_COMBO_BOX(gtk_combo_box_new ());
-	GtkTreeModel *model = GTK_TREE_MODEL (gtk_list_store_new (N_DEST_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER));
+	GtkTreeModel *model = GTK_TREE_MODEL (gtk_list_store_new 
+		(N_DEST_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER));
 	GtkCellRenderer *renderer=NULL;
 	GtkListStore *store = GTK_LIST_STORE (model);
 	GtkTreeIter iter;
 	GtkWidget *dialog = NULL;
 	char *dest_uri = NULL;
-	GtkWidget *header_check = gtk_check_button_new_with_label (_("Prepend a header")),
-		*delimiter_entry = gtk_entry_new (),
-		*newline_entry = gtk_entry_new (),
-		*quote_entry = gtk_entry_new (),
-		*table = gtk_table_new (4, 2, FALSE), *label = NULL,
-		*csv_options = gtk_expander_new (_("Advanced options for the CSV format")),
-		*vbox = gtk_vbox_new (FALSE, 0);
+	gboolean proceed = FALSE;
+
+	GList *format_handlers = NULL;
+
+	/* The available formathandlers */
+	format_handlers = g_list_append (format_handlers, 
+		ical_format_handler_new ());
+	format_handlers = g_list_append (format_handlers, 
+		csv_format_handler_new ());
+	format_handlers = g_list_append (format_handlers, 
+		rdf_format_handler_new ());
+
 
 	/* The Type GtkComboBox */
+	gtk_box_pack_start (GTK_BOX (extra_widget), GTK_WIDGET (combo), 
+		TRUE, TRUE, 0);
 	gtk_combo_box_set_model (combo, model);
+
+	gtk_list_store_clear (store);
 	renderer = gtk_cell_renderer_text_new ();
 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), 
-					renderer, "text", DEST_NAME_COLUMN, NULL);
+			renderer, "text", DEST_NAME_COLUMN, NULL);
 
-	gtk_box_pack_start (GTK_BOX (extra_widget), GTK_WIDGET (combo), TRUE, TRUE, 0);
-	gtk_list_store_clear (store);
-	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, DEST_NAME_COLUMN, _("iCalendar format (.ics)"), -1);
-	gtk_list_store_set (store, &iter, DEST_HANDLER, do_save_calendar_ical, -1);
-	gtk_combo_box_set_active_iter (combo, &iter);
-
-	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, DEST_NAME_COLUMN, _("Comma separated value format (.csv)"), -1);
-	gtk_list_store_set (store, &iter, DEST_HANDLER, do_save_calendar_csv, -1);
-
-	/* Advanced CSV options */
-	gtk_entry_set_text (GTK_ENTRY(delimiter_entry), ", ");
-	gtk_entry_set_text (GTK_ENTRY(quote_entry), "\"");
-	gtk_entry_set_text (GTK_ENTRY(newline_entry), "\\n");
-
-	gtk_table_set_row_spacings (GTK_TABLE (table), 5);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 5);
-	label = gtk_label_new (_("Value delimiter:"));
-	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.0);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 
-			  (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); 
-	gtk_table_attach (GTK_TABLE (table), delimiter_entry, 1, 2, 0, 1,
-			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0);
-	label = gtk_label_new (_("Record delimiter:"));
-	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.0);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, 
-			  (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); 
-	gtk_table_attach (GTK_TABLE (table), newline_entry, 1, 2, 1, 2,
-			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0);
-	label = gtk_label_new (_("Encapsulate values with:"));
-	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.0);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, 
-			  (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); 
-	gtk_table_attach (GTK_TABLE (table), quote_entry, 1, 2, 2, 3,
-			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0);
-	gtk_box_pack_start (GTK_BOX (extra_widget), GTK_WIDGET (csv_options), TRUE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (vbox), header_check, TRUE, TRUE, 0);
-	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
-	gtk_container_add (GTK_CONTAINER (csv_options), vbox);
-	gtk_widget_show_all (vbox);
+	while (format_handlers) {
+		FormatHandler *handler = format_handlers->data;
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, DEST_NAME_COLUMN, 
+			handler->combo_label, -1);
+		gtk_list_store_set (store, &iter, DEST_HANDLER, handler, -1);
+
+		if (handler->options_widget) {
+			gtk_box_pack_start (GTK_BOX (extra_widget), 
+				GTK_WIDGET (handler->options_widget), TRUE, TRUE, 0);
+			gtk_widget_hide (handler->options_widget);
+		}
+
+		if (handler->isdefault) {
+			gtk_combo_box_set_active_iter (combo, &iter);
+			if (handler->options_widget)
+				gtk_widget_show (handler->options_widget);
+		}
+
+		format_handlers = g_list_next (format_handlers);
+	}
 
-	g_signal_connect (G_OBJECT(combo), "changed", G_CALLBACK (on_type_combobox_changed), csv_options);
+
+	g_signal_connect (G_OBJECT(combo), "changed", 
+		G_CALLBACK (on_type_combobox_changed), extra_widget);
 
 #ifdef USE_GTKFILECHOOSER
+
 	dialog = gtk_file_chooser_dialog_new (_("Select destination file"),
 					      NULL,
 					      GTK_FILE_CHOOSER_ACTION_SAVE,
 					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 					      GTK_STOCK_SAVE_AS, GTK_RESPONSE_OK,
 		 			      NULL);
+
 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 	gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), extra_widget);
 #else
-	/* I haven't tested this yet */
 	dialog = gtk_file_selection_new (_("Select destination file"));
 	gtk_box_pack_start (GTK_BOX (GTK_FILE_SELECTION (dialog)->main_vbox), extra_widget);
 #endif
-	gtk_widget_show_all (extra_widget);
+	gtk_widget_show (GTK_WIDGET(combo));
+	gtk_widget_show (extra_widget);
 
-	/* The default is ical, so hide the new options for now */
-	gtk_widget_hide (csv_options);
 
-	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) {
-		gtk_widget_destroy (dialog);
-		return;
-	}
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+		char *tmp = NULL;
 
-	gtk_combo_box_get_active_iter (combo, &iter);
-	gtk_tree_model_get (model, &iter, DEST_HANDLER, &handler, -1);
+		gtk_combo_box_get_active_iter (combo, &iter);
+		gtk_tree_model_get (model, &iter, 
+			DEST_HANDLER, &handler, -1);
 
 #ifdef USE_GTKFILECHOOSER
-       dest_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+	       dest_uri = gtk_file_chooser_get_uri 
+			(GTK_FILE_CHOOSER (dialog));
 #else
-       dest_uri = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (dialog)));
+	       dest_uri = g_strdup (gtk_file_selection_get_filename 
+			(GTK_FILE_SELECTION (dialog)));
 #endif
 
-	if (handler == do_save_calendar_csv) {
-		config = g_new (CsvConfig, 1);
+		tmp = strstr (dest_uri, handler->filename_ext);
 
-		config->delimiter = userstring_to_systemstring (gtk_entry_get_text (GTK_ENTRY(delimiter_entry)));
-		config->newline = userstring_to_systemstring (gtk_entry_get_text (GTK_ENTRY(newline_entry)));
-		config->quote = userstring_to_systemstring (gtk_entry_get_text (GTK_ENTRY(quote_entry)));
-		config->header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (header_check));
+		if (tmp && *(tmp + strlen (handler->filename_ext)) == '\0') {
 
-		data = config;
-	}
+			proceed = TRUE;
+
+		} else {
 
-	gtk_widget_destroy (dialog);
+			GtkWidget *warning = 
+				gtk_message_dialog_new (NULL,
+				GTK_DIALOG_DESTROY_WITH_PARENT,
+				GTK_MESSAGE_QUESTION,
+				GTK_BUTTONS_YES_NO,
+				_("The suggested filename extension of this filetype (%s)"
+				  " is unused in the chosen filename. Do you want to "
+				  "continue?"), handler->filename_ext);
+
+			if (gtk_dialog_run (GTK_DIALOG (warning)) == GTK_RESPONSE_YES)
+				proceed = TRUE;
+
+			gtk_widget_destroy (warning);
 
-	handler (ep, target, type, dest_uri, data);
+		}
 
-	if (handler == do_save_calendar_csv) {
-		g_free (config->delimiter);
-		g_free (config->quote);
-		g_free (config->newline);
-		g_free (config);
-	}
+		if (proceed)
+		{
+			handler->save (handler, ep, target, type, dest_uri);
+			/* Free the handlers */
+			g_list_foreach (format_handlers, format_handlers_foreach_free, NULL);
+			g_list_free (format_handlers);
+
+			/* Now we can destroy it */
+			gtk_widget_destroy (dialog);	
+			g_free (dest_uri);
+		}
 
-	g_free (dest_uri);
+	} else {
+		/* Free the handlers */
+		g_list_foreach (format_handlers, format_handlers_foreach_free, NULL);
+		g_list_free (format_handlers);
+
+		/* Now we can destroy it */
+		gtk_widget_destroy (dialog);	
+		g_free (dest_uri);
+	}
 }
 
 void
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 *  Authors: Philip Van Hoof <pvanhoof gnome org>
 *
 *  Copyright 2004 Novell, Inc. (www.novell.com)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of version 2 of the GNU General Public
 *  License as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <glib/gi18n.h>
#ifdef USE_GTKFILECHOOSER
#  include <gtk/gtkfilechooser.h>
#  include <gtk/gtkfilechooserdialog.h>
#else
#  include <gtk/gtkfilesel.h>
#endif
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkstock.h>
#include <gtk/gtk.h>
#include <libedataserver/e-source.h>
#include <libedataserverui/e-source-selector.h>
#include <libecal/e-cal.h>
#include <calendar/gui/e-cal-popup.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlIO.h>
#include <libxml/xpath.h>
#include <string.h>

#include "format-handler.h"

static void 
add_string_to_rdf (xmlNodePtr node, const gchar *tag, const char *value);

/* Use { */

/* #include <calendar/gui/calendar-config-keys.h> */
/* #include <calendar/gui/calendar-config.h> */

/* } or { */
#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"

GConfClient *config = NULL;

static gchar *
calendar_config_get_timezone (void)
{
	gchar *retval = NULL;

	if (!config)
		config = gconf_client_get_default ();

	retval = gconf_client_get_string (config, CALENDAR_CONFIG_TIMEZONE, NULL);

	if (!retval) 
		retval = g_strdup ("UTC");

	return retval;
}
/* } */

enum { /* XML helper enum */
	ECALCOMPONENTTEXT,
	ECALCOMPONENTATTENDEE,
	CONSTCHAR
};

static void
display_error_message (GtkWidget *parent, GError *error)
{
	GtkWidget *dialog;

	dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
					 error->message);
	gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);
}

/* Some helpers for the xml stuff */
static void 
add_list_to_rdf (xmlNodePtr node, const gchar *tag, GSList *list_in, gint type)
{
	if (list_in) {
		GSList *list = list_in;

		while (list) {
			const char *str = NULL;

			switch (type) {
			case ECALCOMPONENTATTENDEE:
				str = ((ECalComponentAttendee*)list->data)->value;
				break;
			case ECALCOMPONENTTEXT:
				str = ((ECalComponentText*)list->data)->value;
				break;
			case CONSTCHAR:
			default:
				str = list->data;
				break;
			}

			add_string_to_rdf (node, tag, str);

			list = g_slist_next (list);
		}
	}
}

static void 
add_nummeric_to_rdf (xmlNodePtr node, const gchar *tag, gint *nummeric)
{
	if (nummeric) {
		gchar *value = g_strdup_printf ("%d", *nummeric);
		xmlNodePtr cur_node = xmlNewChild (node, NULL, tag, value);
		xmlSetProp (cur_node, "rdf:datatype", "http://www.w3.org/2001/XMLSchema#integer";);
		g_free (value);
	}
}

static void
add_time_to_rdf (xmlNodePtr node, const gchar *tag, icaltimetype *time)
{
	if (time) {
		xmlNodePtr cur_node = NULL;
		gchar *tmp = NULL;
		gchar *str = g_strdup_printf ("%s%d-%s%d-%s%dT%s%d:%s%d:%s%d", 
					(time->year < 10)?"0":"", time->year, 
					(time->month < 10)?"0":"", time->month, 
					(time->day < 10)?"0":"", time->day, 
					(time->hour < 10)?"0":"", time->hour, 
					(time->minute < 10)?"0":"", time->minute, 
					(time->second < 10)?"0":"", time->second);
		cur_node = xmlNewChild (node, NULL, tag, str);

		/* Not sure about this property */
		tmp = g_strdup_printf ("http://www.w3.org/2002/12/cal/tzd/%s#tz";, calendar_config_get_timezone ());
		xmlSetProp (cur_node, "rdf:datatype", tmp);
		g_free (tmp);

		g_free (str);
	}
}


static void 
add_string_to_rdf (xmlNodePtr node, const gchar *tag, const char *value)
{
	if (value) {
		xmlNodePtr cur_node = NULL;
		cur_node = xmlNewChild (node, NULL, tag, value);
		xmlSetProp (cur_node, "rdf:datatype", "http://www.w3.org/2001/XMLSchema#string";);
	}
}




static void
do_save_calendar_rdf (FormatHandler *handler, EPlugin *ep, ECalPopupTargetSource *target, ECalSourceType type, char *dest_uri)
{

	/* 
	 * According to some documentation about CSV, newlines 'are' allowed 
	 * in CSV-files. But you 'do' have to put the value between quotes.
	 * The helper 'string_needsquotes' will check for that 
	 *
	 * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
	 * http://www.creativyst.com/cgi-bin/Prod/15/eg/csv2xml.pl
	 */

	ESource *primary_source;
	ECal *source_client;
	GError *error = NULL;
	GList *objects=NULL;
	GnomeVFSResult result;
	GnomeVFSHandle *handle;
	GnomeVFSURI *uri;
	gchar *temp = NULL;

	if (!dest_uri)
		return;

	primary_source = e_source_selector_peek_primary_selection (target->selector);

	/* open source client */
	source_client = e_cal_new (primary_source, type);
	if (!e_cal_open (source_client, TRUE, &error)) {
		display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (target->selector)), error);
		g_object_unref (source_client);
		g_error_free (error);
		return;
	}

	uri = gnome_vfs_uri_new (dest_uri);
	result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
	if (result != GNOME_VFS_OK) {
		gnome_vfs_create (&handle, dest_uri, GNOME_VFS_OPEN_WRITE, TRUE, GNOME_VFS_PERM_USER_ALL);
		result = gnome_vfs_open_uri (&handle, uri, GNOME_VFS_OPEN_WRITE);
	}

	if (result == GNOME_VFS_OK && e_cal_get_object_list_as_comp (source_client, "#t", &objects, NULL)) {
		xmlBufferPtr buffer=xmlBufferCreate();
		xmlDocPtr doc = xmlNewDoc((xmlChar *) "1.0");
		xmlNodePtr fnode = doc->children;

		doc->children = xmlNewDocNode (doc, NULL, "rdf:RDF", NULL);
		xmlSetProp (doc->children, "xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#";);
		xmlSetProp (doc->children, "xmlns", "http://www.w3.org/2002/12/cal/ical#";);

		fnode = xmlNewChild (doc->children, NULL, "Vcalendar", NULL);

		/* Should Evolution publicise these? */
		xmlSetProp (fnode, "xmlns:x-wr", "http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#";);
		xmlSetProp (fnode, "xmlns:x-lic", "http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#";);

		/* Not sure if it's correct like this */
		xmlNewChild (fnode, NULL, "prodid", "-//" PACKAGE_STRING "//iCal 1.0//EN");

		/* Assuming GREGORIAN is the only supported calendar scale */
		xmlNewChild (fnode, NULL, "calscale", "GREGORIAN");

		temp = calendar_config_get_timezone ();
		xmlNewChild (fnode, NULL, "x-wr:timezone", temp);
		g_free (temp);

		xmlNewChild (fnode, NULL, "method", "PUBLISH");

		xmlNewChild (fnode, NULL, "x-wr:relcalid", e_source_peek_uid (primary_source));

		xmlNewChild (fnode, NULL, "x-wr:calname", e_source_peek_name (primary_source));

		/* Version of this RDF-format */
		xmlNewChild (fnode, NULL, "version", "2.0");

		while (objects != NULL) {
			ECalComponent *comp = objects->data;
			const char *temp_constchar;
			gchar *tmp_str = NULL;
			GSList *temp_list;
			ECalComponentDateTime temp_dt;
			struct icaltimetype *temp_time;
			int *temp_int;
			ECalComponentText temp_comptext; 
			xmlNodePtr c_node = xmlNewChild (fnode, NULL, "component", NULL);
			xmlNodePtr node = xmlNewChild (c_node, NULL, "Vevent", NULL);

			/* Getting the stuff */
			e_cal_component_get_uid (comp, &temp_constchar);
			tmp_str = g_strdup_printf ("#%s", temp_constchar);
			xmlSetProp (node, "about", tmp_str);
			g_free (tmp_str);
			add_string_to_rdf (node, "uid",temp_constchar);

			e_cal_component_get_summary (comp, &temp_comptext);
			add_string_to_rdf (node, "summary",&temp_comptext?temp_comptext.value:NULL);

			e_cal_component_get_description_list (comp, &temp_list);
			add_list_to_rdf (node, "description", temp_list, ECALCOMPONENTTEXT);
			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_categories_list (comp, &temp_list);
			add_list_to_rdf (node, "categories", temp_list, CONSTCHAR);
			if (temp_list)
				e_cal_component_free_categories_list (temp_list);

			e_cal_component_get_comment_list (comp, &temp_list);
			add_list_to_rdf (node, "comment", temp_list, ECALCOMPONENTTEXT);

			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_completed (comp, &temp_time);
			add_time_to_rdf (node, "completed", temp_time);
			if (temp_time)
				e_cal_component_free_icaltimetype (temp_time);

			e_cal_component_get_created (comp, &temp_time);
			add_time_to_rdf (node, "created", temp_time);
			if (temp_time)
				e_cal_component_free_icaltimetype (temp_time);

			e_cal_component_get_contact_list (comp, &temp_list);
			add_list_to_rdf (node, "contact", temp_list, ECALCOMPONENTTEXT);
			if (temp_list)
				e_cal_component_free_text_list (temp_list);

			e_cal_component_get_dtstart (comp, &temp_dt);
			 add_time_to_rdf (node, "dtstart", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_dtend (comp, &temp_dt);
			add_time_to_rdf (node, "dtend", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_due (comp, &temp_dt);
			add_time_to_rdf (node, "due", temp_dt.value ? temp_dt.value : NULL);
			if (temp_dt.value)
				e_cal_component_free_datetime (&temp_dt);

			e_cal_component_get_percent (comp, &temp_int);
			add_nummeric_to_rdf (node, "percentComplete", temp_int);

			e_cal_component_get_priority (comp, &temp_int);
			add_nummeric_to_rdf (node, "priority", temp_int);

			e_cal_component_get_url (comp, &temp_constchar);
			add_string_to_rdf (node, "URL", temp_constchar);

			if (e_cal_component_has_attendees (comp)) {
				e_cal_component_get_attendee_list (comp, &temp_list);
				add_list_to_rdf (node, "attendee", temp_list, ECALCOMPONENTATTENDEE);
				if (temp_list)
					e_cal_component_free_attendee_list (temp_list);
			}

			e_cal_component_get_location (comp, &temp_constchar);
			add_string_to_rdf (node, "location", temp_constchar);

			e_cal_component_get_last_modified (comp, &temp_time);
			add_time_to_rdf (node, "lastModified",temp_time);


			/* Important note!
			 * The documentation is not requiring this!
			 *
			 * if (temp_time) e_cal_component_free_icaltimetype (temp_time);
			 *
			 * Please uncomment and fix documentation if untrue
			 * http://www.gnome.org/projects/evolution/developer-doc/libecal/ECalComponent.html
			 *	#e-cal-component-get-last-modified
			 */

			objects = g_list_next (objects);
		}

		/* I used a buffer rather than xmlDocDump: I want gnome-vfs support */
		xmlNodeDump (buffer, doc, doc->children, 2, 1);

		gnome_vfs_write (handle, xmlBufferContent (buffer), xmlBufferLength (buffer), NULL);

		xmlBufferFree (buffer);
		xmlFreeDoc (doc);
		gnome_vfs_close (handle);
	}

	g_object_unref (source_client);

	return;
}

FormatHandler *rdf_format_handler_new (void)
{
	FormatHandler *handler = g_new (FormatHandler, 1);

	handler->isdefault = FALSE;
	handler->combo_label = _("RDF format (.rdf)");
	handler->filename_ext = ".rdf";
	handler->options_widget = NULL;
	handler->save = do_save_calendar_rdf;

	return handler;
}

Attachment: rdfformat-01.tar
Description: Unix tar archive



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]