--- Makefile.am	2005-12-21 09:30:50.000000000 -0300
+++ Makefile.am	2005-12-21 16:47:12.863777304 -0300
@@ -7,7 +7,7 @@ gnumeric_plugin_openofficedir = $(gnumer
 xmldir = $(gnumeric_plugin_openofficedir)
 gnumeric_plugin_openoffice_LTLIBRARIES = openoffice.la
 openoffice_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
-openoffice_la_SOURCES = openoffice-read.c openoffice-write.c
+openoffice_la_SOURCES = openoffice-read.c openoffice-write.c opendocument-write.c
 
 xml_in_files = plugin.xml.in
 xml_DATA = $(xml_in_files:.xml.in=.xml)
--- opendocument-write.c	1969-12-31 21:00:00.000000000 -0300
+++ opendocument-write.c	2005-12-22 15:16:50.291337128 -0300
@@ -0,0 +1,444 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * opendocument-write.c : export OpenDocument v1.0 .ods files
+ *
+ * Author:  Luciano Wolf (luciano.wolf@indt.org.br)
+ *
+ * Copyright (C) 2005 INdT - Instituto Nokia de Tecnologia
+ * http://www.indt.org.br
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ */
+
+/*****************************************************************************/
+
+#include <gnumeric-config.h>
+#include <gnumeric.h>
+#include <workbook-view.h>
+#include <goffice/app/file.h>
+#include <goffice/app/io-context.h>
+#include <gnm-format.h>
+#include <workbook.h>
+#include <workbook-priv.h> /* Workbook::names */
+#include <cell.h>
+#include <sheet.h>
+#include <sheet-view.h>
+#include <sheet-style.h>
+#include <summary.h>
+#include <style-color.h>
+#include <expr.h>
+#include <expr-impl.h>
+#include <expr-name.h>
+#include <value.h>
+#include <str.h>
+#include <ranges.h>
+#include <mstyle.h>
+#include <style-border.h>
+#include <validation.h>
+#include <hlink.h>
+#include <solver.h>
+#include <sheet-filter.h>
+#include <print-info.h>
+#include <print-info.h>
+#include <tools/scenarios.h>
+
+#include <gsf/gsf-libxml.h>
+#include <gsf/gsf-output.h>
+#include <gsf/gsf-outfile.h>
+#include <gsf/gsf-outfile-zip.h>
+#include <gsf/gsf-utils.h>
+#include <gsf/gsf-opendoc-utils.h>
+#include <gsf/gsf-doc-meta-data.h>
+#include <glib/gi18n.h>
+#include <locale.h>
+
+#define MANIFEST "manifest:"
+#define OFFICE	 "office:"
+#define STYLE	 "style:"
+#define TABLE	 "table:"
+#define TEXT	 "text:"
+
+typedef struct {
+	GsfXMLOut *xml;
+	IOContext *ioc;
+	WorkbookView const *wbv;
+	Workbook const	   *wb;
+} GnmOOExport;
+
+void	opendocument_file_save (GOFileSaver const *fs, IOContext *ioc,
+			      WorkbookView const *wbv, GsfOutput *output);
+
+static void
+od_write_mimetype (GnmOOExport *state, GsfOutput *child)
+{
+	gsf_output_puts (child, "application/vnd.oasis.opendocument.spreadsheet");
+}
+
+/*****************************************************************************/
+
+static unsigned od_max_cols (void) { return 256; }
+static unsigned od_max_rows (void) { return 32000; }
+
+static void
+od_start_style (GsfXMLOut *xml, char const *name, char const *family)
+{
+	gsf_xml_out_start_element (xml, STYLE "style");
+	gsf_xml_out_add_cstr_unchecked (xml, STYLE "name", name);
+	gsf_xml_out_add_cstr_unchecked (xml, STYLE "family", family);
+}
+
+static void
+od_write_table_styles (GnmOOExport *state)
+{
+	od_start_style (state->xml, "ta1", "table");
+	gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "master-page-name", "Default");
+
+	gsf_xml_out_start_element (state->xml, STYLE "properties");
+	gsf_xml_out_add_bool (state->xml, TABLE "display", TRUE);
+	gsf_xml_out_end_element (state->xml); /* </style:properties> */
+
+	gsf_xml_out_end_element (state->xml); /* </style:style> */
+}
+
+static char *
+od_parse_expr (GnmExpr const *expr)
+{
+	//TODO
+}
+
+static void
+od_write_sheet (GnmOOExport *state, Sheet const *sheet)
+{
+	GnmStyle *col_styles [SHEET_MAX_COLS];
+	GnmRange  extent;
+	int max_cols = od_max_cols ();
+	int max_rows = od_max_rows ();
+	int i, col, row, null_cols;
+	GnmCell *current_cell = NULL;
+
+	extent = sheet_get_extent (sheet, FALSE); 
+	if (extent.end.row >= max_rows) {
+		gnm_io_warning (state->ioc,
+			_("Some content will be lost when saving as OpenDocument .ods. "
+			  "It only supports %d rows, and sheet '%s' has %d"),
+			max_rows, sheet->name_unquoted, extent.end.row);
+		extent.end.row = max_rows;
+	}
+	if (extent.end.col >= max_cols) {
+		gnm_io_warning (state->ioc,
+			_("Some content will be lost when saving as OpenDocument .ods. "
+			  "It only supports %d columns, and sheet '%s' has %d"),
+			max_cols, sheet->name_unquoted, extent.end.col);
+		extent.end.col = max_cols;
+	}
+
+	sheet_style_get_extent (sheet, &extent, col_styles);
+
+	/* include collapsed or hidden cols and rows */
+	for (i = max_rows ; i-- > extent.end.row ; )
+		if (!colrow_is_empty (sheet_row_get (sheet, i))) {
+			extent.end.row = i;
+			break;
+		}
+	for (i = max_cols ; i-- > extent.end.col ; )
+		if (!colrow_is_empty (sheet_col_get (sheet, i))) {
+			extent.end.col = i;
+			break;
+		}
+	
+	for (row = 0; row <= extent.end.row; row++) {
+		null_cols = 0;
+		gsf_xml_out_start_element (state->xml, TABLE "table-row");
+
+		for (col = 0; col <= extent.end.col; col++) {
+			current_cell = sheet_cell_get (sheet, col, row);
+			if (!cell_is_empty (current_cell)) {
+				gsf_xml_out_start_element (state->xml, TABLE "table-cell");
+				if (null_cols > 1) {
+					gsf_xml_out_add_cstr_unchecked (state->xml, "table:number-columns-repeated", g_strdup_printf ("%d", null_cols));
+					gsf_xml_out_end_element (state->xml);
+					null_cols = 0;
+					gsf_xml_out_start_element (state->xml, TABLE "table-cell");
+				} else if (null_cols == 1) {
+					gsf_xml_out_end_element (state->xml);
+					null_cols = 0;
+					gsf_xml_out_start_element (state->xml, TABLE "table-cell");
+				}
+//TODO				if (cell_has_expr (current_cell))
+//					gsf_xml_out_add_cstr_unchecked (state->xml, "table:formula", od_parse_expr (current_cell->base.expression));
+				if (cell_is_number (current_cell)) {
+					gsf_xml_out_add_cstr_unchecked (state->xml, "office:value-type", "float");
+					gsf_xml_out_add_cstr_unchecked (state->xml, "office:value", g_strdup_printf ("%f", value_get_as_float (current_cell->value)));
+					gsf_xml_out_start_element (state->xml, TEXT "p");
+					gsf_xml_out_add_float (state->xml, NULL, value_get_as_float (current_cell->value), -1);
+					gsf_xml_out_end_element (state->xml);
+				} else {
+					gsf_xml_out_add_cstr_unchecked (state->xml, "office:value-type", "string");
+					gsf_xml_out_start_element (state->xml, TEXT "p");
+					gsf_xml_out_add_cstr_unchecked (state->xml, NULL, value_get_as_string (current_cell->value));
+					gsf_xml_out_end_element (state->xml);
+				}
+				gsf_xml_out_end_element (state->xml); /*<table:table-cell/>*/
+			} else
+				null_cols ++;
+		}
+		if (null_cols > 1) {
+			gsf_xml_out_start_element (state->xml, TABLE "table-cell");
+			gsf_xml_out_add_cstr_unchecked (state->xml, "table:number-columns-repeated", g_strdup_printf ("%d", null_cols));
+			gsf_xml_out_end_element (state->xml);
+		} else if (null_cols == 1) {
+			gsf_xml_out_start_element (state->xml, TABLE "table-cell");
+			gsf_xml_out_end_element (state->xml);
+		}		
+		gsf_xml_out_end_element (state->xml); /*<table:table-row/>*/
+	}
+}
+
+static void
+od_write_content (GnmOOExport *state, GsfOutput *child)
+{
+	static struct {
+		char const *key;
+		char const *url;
+	} const ns[] = {
+		
+		{ "xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0" },
+		{ "xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"},
+		{ "xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0" },
+		{ "xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0" },
+		{ "xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" },
+		{ "xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" },
+		{ "xmlns:xlink", "http://www.w3.org/1999/xlink" },
+		{ "xmlns:dc", "http://purl.org/dc/elements/1.1/" },
+		{ "xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0" },
+		{ "xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" },
+		{ "xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" },
+		{ "xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0" },
+		{ "xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" },
+		{ "xmlns:math", "http://www.w3.org/1998/Math/MathML" },
+		{ "xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0" },
+		{ "xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0" },
+		{ "xmlns:ooo", "http://openoffice.org/2004/office" },
+		{ "xmlns:ooow", "http://openoffice.org/2004/writer" },
+		{ "xmlns:oooc", "http://openoffice.org/2004/calc" },
+		{ "xmlns:dom", "http://www.w3.org/2001/xml-events" },
+		{ "xmlns:xforms", "http://www.w3.org/2002/xforms" },
+		{ "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" },
+		{ "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" },
+	};
+	int i;
+
+	state->xml = gsf_xml_out_new (child);
+	gsf_xml_out_set_doc_type (state->xml, "\n");
+	gsf_xml_out_start_element (state->xml, OFFICE "document-content");
+
+	for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
+		gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
+	gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version", "1.0");
+
+	gsf_xml_out_simple_element (state->xml, OFFICE "scripts", NULL);
+
+	gsf_xml_out_start_element (state->xml, OFFICE "font-face-decls");
+	gsf_xml_out_end_element (state->xml); /* </office:font-face-decls> */
+
+	gsf_xml_out_start_element (state->xml, OFFICE "automatic-styles");
+	gsf_xml_out_end_element (state->xml); /* </office:automatic-styles> */
+
+	gsf_xml_out_start_element (state->xml, OFFICE "body");
+	gsf_xml_out_start_element (state->xml, OFFICE "spreadsheet");
+	for (i = 0; i < workbook_sheet_count (state->wb); i++) {
+		Sheet *sheet = workbook_sheet_by_index (state->wb, i);
+#warning validate sheet name against OOo conventions
+		gsf_xml_out_start_element (state->xml, TABLE "table");
+		gsf_xml_out_add_cstr (state->xml, TABLE "name", sheet->name_unquoted);
+		od_write_sheet (state, sheet);
+		gsf_xml_out_end_element (state->xml); /* </table:table> */
+	}
+	gsf_xml_out_end_element (state->xml); /* </office:spreadsheet> */
+	gsf_xml_out_end_element (state->xml); /* </office:body> */
+
+	gsf_xml_out_end_element (state->xml); /* </office:document-content> */
+}
+
+/*****************************************************************************/
+
+static void
+od_write_styles (GnmOOExport *state, GsfOutput *child)
+{
+	GsfXMLOut *xml = gsf_xml_out_new (child);
+	
+	gsf_xml_out_set_doc_type (xml, "\n");
+	gsf_xml_out_start_element (xml, OFFICE "document-styles");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:office",
+		"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:style",
+		"urn:oasis:names:tc:opendocument:xmlns:style:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:text",
+		"urn:oasis:names:tc:opendocument:xmlns:text:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:table",
+		"urn:oasis:names:tc:opendocument:xmlns:table:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:draw",
+		"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:fo",
+		"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:xlink",
+		"http://www.w3.org/1999/xlink");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:dc",
+		"http://purl.org/dc/elements/1.1/");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:meta",
+		"urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:number",
+		"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:svg",
+		"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:chart",
+		"urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:dr3d",
+		"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:math",
+		"http://www.w3.org/1998/Math/MathML");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:form",
+		"urn:oasis:names:tc:opendocument:xmlns:form:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:script",
+		"urn:oasis:names:tc:opendocument:xmlns:script:1.0");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:ooo",
+		"http://openoffice.org/2004/office");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:ooow",
+		"http://openoffice.org/2004/writer");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:oooc",
+		"http://openoffice.org/2004/calc");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:dom",
+		"http://www.w3.org/2001/xml-events");
+	gsf_xml_out_add_cstr_unchecked (xml, "office:version", "1.0");
+
+	gsf_xml_out_end_element (xml); /* </office:document-styles> */
+}
+
+/*****************************************************************************/
+
+static void
+od_write_meta (GnmOOExport *state, GsfOutput *child)
+{
+	GsfXMLOut *xml = gsf_xml_out_new (child);
+	GsfDocMetaData	*meta_data;
+	GValue *res = NULL;
+	gboolean err;
+
+	meta_data = g_object_get_data (G_OBJECT(state->wb), "GsfDocMetaData");
+	if (meta_data == NULL) {
+		meta_data = gsf_doc_meta_data_new ();
+		res = g_new0 (GValue, 1);
+		res = g_value_init (res, G_TYPE_STRING);
+		g_value_set_string (res, g_strconcat ("Gnumeric v", GNUMERIC_VERSION, NULL));
+		gsf_doc_meta_data_insert (meta_data, g_strdup ("meta:generator"), res);
+	}
+	err = gsf_opendoc_metadata_write (xml, meta_data);
+}
+
+/*****************************************************************************/
+
+static void
+od_write_settings (GnmOOExport *state, GsfOutput *child)
+{
+}
+
+/**********************************************************************************/
+
+static void
+od_file_entry (GsfXMLOut *out, char const *type, char const *name)
+{
+	gsf_xml_out_start_element (out, MANIFEST "file-entry");
+	gsf_xml_out_add_cstr (out, MANIFEST "media-type", type);
+	gsf_xml_out_add_cstr (out, MANIFEST "full-path", name);
+	gsf_xml_out_end_element (out); /* </manifest:file-entry> */
+}
+
+static void
+od_write_manifest (GnmOOExport *state, GsfOutput *child)
+{
+	GsfXMLOut *xml = gsf_xml_out_new (child);
+	gsf_xml_out_set_doc_type (xml, 
+		"<!DOCTYPE "
+		   "manifest:manifest "
+		   "PUBLIC "
+		   "\"-//OpenOffice.org//DTD "
+		   "Manifest "
+		   "1.0//EN\" \"Manifest.dtd\">\n");
+	gsf_xml_out_start_element (xml, MANIFEST "manifest");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns:manifest",
+		"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
+	od_file_entry (xml, "application/vnd.oasis.opendocument.spreadsheet" ,"/");
+	od_file_entry (xml, "application/vnd.sun.xml.ui.configuration" ,"Configurations2/");
+	od_file_entry (xml, "", "Pictures/");
+	od_file_entry (xml, "text/xml", "content.xml");
+	od_file_entry (xml, "text/xml", "styles.xml");
+	od_file_entry (xml, "text/xml", "meta.xml");
+	od_file_entry (xml, "text/xml", "settings.xml");
+	gsf_xml_out_end_element (xml); /* </manifest:manifest> */
+}
+
+/**********************************************************************************/
+
+void
+opendocument_file_save (GOFileSaver const *fs, IOContext *ioc,
+		      WorkbookView const *wbv, GsfOutput *output)
+{
+	static struct {
+		void (*func) (GnmOOExport *state, GsfOutput *child);
+		char const *name;
+	} const streams[] = {
+		{ od_write_mimetype,	"mimetype" },
+		{ od_write_content,	"content.xml" },
+		{ od_write_styles,	"styles.xml" },
+		{ od_write_meta,	"meta.xml" },
+		{ od_write_settings,	"settings.xml" },
+		{ od_write_manifest,	"META-INF/manifest.xml" }
+	};
+
+	GnmOOExport state;
+	char *old_num_locale, *old_monetary_locale;
+	GsfOutfile *outfile = NULL;
+	GsfOutput  *child;
+	GError *err;
+	unsigned i;
+
+	old_num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
+	go_setlocale (LC_NUMERIC, "C");
+	old_monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
+	go_setlocale (LC_MONETARY, "C");
+	go_set_untranslated_bools ();
+
+	outfile = gsf_outfile_zip_new (output, &err);
+
+	state.ioc = ioc;
+	state.wbv = wbv;
+	state.wb  = wb_view_workbook (wbv);
+	for (i = 0 ; i < G_N_ELEMENTS (streams); i++) {
+		child = gsf_outfile_new_child  (outfile, streams[i].name, FALSE);
+		streams[i].func (&state, child);
+		gsf_output_close (child);
+		g_object_unref (G_OBJECT (child));
+	}
+
+	gsf_output_close (GSF_OUTPUT (outfile));
+	g_object_unref (G_OBJECT (outfile));
+
+	/* go_setlocale restores bools to locale translation */
+	go_setlocale (LC_MONETARY, old_monetary_locale);
+	g_free (old_monetary_locale);
+	go_setlocale (LC_NUMERIC, old_num_locale);
+	g_free (old_num_locale);
+}
--- plugin.xml.in	2005-12-21 09:30:09.000000000 -0300
+++ plugin.xml.in	2005-12-21 16:47:39.721694280 -0300
@@ -17,11 +17,17 @@
 				<suffix>ods</suffix>
 			</suffixes>
 		</service>
-		<service type="file_saver" id="openoffice" file_extension="swc"
+		<service type="file_saver" id="openoffice" file_extension="sxc"
 		         format_level="auto" overwrite_files="TRUE">
 			<information>
 				<_description>OpenOffice OASIS _UNFINISHED_</_description>
 			</information>
 		</service>
+		<service type="file_saver" id="opendocument" file_extension="ods"
+		         format_level="auto" overwrite_files="TRUE">
+			<information>
+				<_description>OpenDocument v1.0</_description>
+			</information>
+		</service>
 	</services>
 </plugin>
