diff --git a/divelist.c b/divelist.c
index c25ee6271..198e2d643 100644
--- a/divelist.c
+++ b/divelist.c
@@ -2102,6 +2102,92 @@ static void export_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path)
}
#endif
+#if defined(XSLT)
+static void export_dives_uddf(const gboolean selected)
+{
+ FILE *f;
+ char *filename = NULL;
+ size_t streamsize;
+ char *membuf;
+ xmlDoc *doc;
+ xsltStylesheetPtr xslt = NULL;
+ xmlDoc *transformed;
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new(_("Export As UDDF File"),
+ GTK_WINDOW(main_window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+ gtk_widget_destroy(dialog);
+
+ if (!filename)
+ return;
+
+ /* Save XML to file and convert it into a memory buffer */
+ save_dives_logic(filename, selected);
+ f = fopen(filename, "r");
+ fseek(f, 0, SEEK_END);
+ streamsize = ftell(f);
+ rewind(f);
+
+ membuf = malloc(streamsize + 1);
+ if (!membuf || !fread(membuf, streamsize, 1, f)) {
+ fprintf(stderr, "Failed to read memory buffer\n");
+ return;
+ }
+ membuf[streamsize] = 0;
+ fclose(f);
+ g_unlink(filename);
+
+ /*
+ * Parse the memory buffer into XML document and
+ * transform it to UDDF format, finally dumping
+ * the XML into a character buffer.
+ */
+ doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0);
+ if (!doc) {
+ fprintf(stderr, "Failed to read XML memory\n");
+ return;
+ }
+ free((void *)membuf);
+
+ /* Convert to UDDF format */
+ xslt = get_stylesheet("uddf-export.xslt");
+ if (!xslt) {
+ fprintf(stderr, "Failed to open UDDF conversion stylesheet\n");
+ return;
+ }
+ transformed = xsltApplyStylesheet(xslt, doc, NULL);
+ xsltFreeStylesheet(xslt);
+ xmlFreeDoc(doc);
+
+ /* Write the transformed XML to file */
+ f = g_fopen(filename, "w");
+ xmlDocFormatDump(f, transformed, 1);
+ xmlFreeDoc(transformed);
+
+ fclose(f);
+ g_free(filename);
+}
+
+static void export_selected_dives_uddf_cb(GtkWidget *menuitem, GtkTreePath *path)
+{
+ export_dives_uddf(TRUE);
+}
+
+void export_all_dives_uddf_cb()
+{
+ export_dives_uddf(FALSE);
+}
+#endif
+
static void merge_dive_index(int i, struct dive *a)
{
struct dive *b = get_dive(i+1);
@@ -2171,6 +2257,9 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int
char deleteplurallabel[] = N_("Delete dives");
char deletesinglelabel[] = N_("Delete dive");
char *deletelabel;
+#if defined(XSLT)
+ char exportuddflabel[] = N_("Export dive(s) to UDDF");
+#endif
#if defined(LIBZIP) && defined(XSLT)
char exportlabel[] = N_("Export dive(s)");
#endif
@@ -2247,6 +2336,12 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
#endif
+#if defined(XSLT)
+ menuitem = gtk_menu_item_new_with_label(exportuddflabel);
+ g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_uddf_cb), path);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+#endif
+
menuitem = gtk_menu_item_new_with_label(editlabel);
g_signal_connect(menuitem, "activate", G_CALLBACK(edit_selected_dives_cb), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
diff --git a/divelist.h b/divelist.h
index 47f79e278..856318e2d 100644
--- a/divelist.h
+++ b/divelist.h
@@ -15,4 +15,5 @@ extern void select_next_dive(void);
extern void select_prev_dive(void);
extern void show_and_select_dive(struct dive *dive);
extern double init_decompression(struct dive * dive);
+extern void export_all_dives_uddf_cb();
#endif
diff --git a/gtk-gui.c b/gtk-gui.c
index c5b9a8071..82e9a3141 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -1509,6 +1509,7 @@ static GtkActionEntry menu_items[] = {
{ "CloseFile", GTK_STOCK_CLOSE, N_("Close"), NULL, NULL, G_CALLBACK(file_close) },
{ "Print", GTK_STOCK_PRINT, N_("Print..."), CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
{ "ImportFile", NULL, N_("Import File(s)..."), CTRLCHAR "I", NULL, G_CALLBACK(import_files) },
+ { "ExportUDDF", NULL, N_("Export UDDF..."), NULL, NULL, G_CALLBACK(export_all_dives_uddf_cb) },
{ "DownloadLog", NULL, N_("Download From Dive Computer..."), CTRLCHAR "D", NULL, G_CALLBACK(download_dialog) },
{ "DownloadWeb", GTK_STOCK_CONNECT, N_("Download From Web Service..."), NULL, NULL, G_CALLBACK(webservice_download_dialog) },
{ "AddDive", GTK_STOCK_ADD, N_("Add Dive..."), NULL, NULL, G_CALLBACK(add_dive_cb) },
@@ -1550,6 +1551,7 @@ static const gchar* ui_string = " \
\
\
\
+ \
\
\
\
diff --git a/xslt/uddf-export.xslt b/xslt/uddf-export.xslt
new file mode 100644
index 000000000..107234ebf
--- /dev/null
+++ b/xslt/uddf-export.xslt
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+ Subsurface Team
+ http://subsurface.hohndel.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+