[gnumeric] xlsx: improve radio button export.



commit d6714c551da7141e82f00917d16bce0be94d8417
Author: Morten Welinder <terra gnome org>
Date:   Mon Feb 23 09:29:11 2015 -0500

    xlsx: improve radio button export.

 plugins/excel/xlsx-write-drawing.c |  306 +++++++++++++++++++++++++-----------
 plugins/excel/xlsx-write.c         |   14 ++-
 2 files changed, 222 insertions(+), 98 deletions(-)
---
diff --git a/plugins/excel/xlsx-write-drawing.c b/plugins/excel/xlsx-write-drawing.c
index 4f25501..1ff5f7b 100644
--- a/plugins/excel/xlsx-write-drawing.c
+++ b/plugins/excel/xlsx-write-drawing.c
@@ -1168,7 +1168,8 @@ xlsx_write_object_anchor (GsfXMLOut *xml, GnmCellPos const *pos, char const *ele
 }
 
 static char const *
-xlsx_write_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part, GSList *objects)
+xlsx_write_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part,
+                           GSList *objects, GHashTable *zorder)
 {
        GSList *obj, *rId_ptr, *rIds = NULL;
        char *name;
@@ -1355,18 +1356,208 @@ xlsx_write_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part, GSList
        return rId;
 }
 
+static int
+cb_radio_value_cmp (void const *ptr_a, void const *ptr_b)
+{
+       SheetObject *a = (gpointer)ptr_a;
+       SheetObject *b = (gpointer)ptr_b;
+       GnmValue *va, *vb;
+       g_object_get (a, "value", &va, NULL);
+       g_object_get (b, "value", &vb, NULL);
+       return value_cmp (&va, &vb);
+}
+
+static GHashTable *
+xlsx_preprocess_radio (XLSXWriteState *state, GSList *objects)
+{
+       GHashTable *radio_by_link = g_hash_table_new_full
+               ((GHashFunc)gnm_expr_top_hash, (GEqualFunc)gnm_expr_top_equal,
+                (GDestroyNotify)gnm_expr_top_unref, (GDestroyNotify)g_slist_free);
+       GHashTableIter hiter;
+       gpointer hkey, hval;
+       GSList *obj;
+       GnmParsePos pp0;
+
+       parse_pos_init_sheet (&pp0, state->sheet);
+
+       /* Collect objects on a per-link basis. */
+       for (obj = objects ; obj != NULL ; obj = obj->next) {
+               SheetObject *so = obj->data;
+               GSList *sos;
+               GnmExprTop const *tlink;
+
+               if (!GNM_IS_SOW_RADIO_BUTTON (so))
+                       continue;
+
+               tlink = sheet_widget_radio_button_get_link (so);
+               if (!tlink)
+                       continue;
+
+               sos = g_hash_table_lookup (radio_by_link, tlink);
+               if (sos) {
+                       gnm_expr_top_unref (tlink);
+                       sos->next = g_slist_prepend (sos->next, so);
+               } else {
+                       sos = g_slist_prepend (sos, so);
+                       g_hash_table_insert (radio_by_link, (gpointer)tlink, sos);
+               }
+       }
+       /* Sort by value. */
+       g_hash_table_iter_init (&hiter, radio_by_link);
+       while (g_hash_table_iter_next (&hiter, &hkey, &hval)) {
+               GSList *sos = g_slist_copy (hval);
+               int i;
+
+               sos = g_slist_sort (sos, cb_radio_value_cmp);
+               g_hash_table_iter_replace (&hiter, sos);
+
+               for (i = 1; sos; sos = sos->next, i++) {
+                       SheetObject *so = sos->data;
+                       GnmValue const *v;
+                       g_object_get (so, "value", &v, NULL);
+                       if (!v || !VALUE_IS_FLOAT (v) || value_get_as_float (v) != i) {
+                               char *etxt = gnm_expr_top_as_string (hkey, &pp0, state->sheet->convs);
+                               g_printerr ("One or more radio buttons linked to %s has non-sequential 
values, first one %s\n",
+                                           etxt, value_peek_string (v));
+                               g_free (etxt);
+                               break;
+                       }
+               }
+       }
+
+       return radio_by_link;
+}
+
+
+static void
+xlsx_write_legacy_object (XLSXWriteState *state, GsfXMLOut *xml, SheetObject *so, GHashTable *zorder, 
GHashTable *radio_by_link)
+{
+       const char *otype = NULL;
+       GnmExprTop const *tlink = NULL;
+       double res_pts[4] = {0.,0.,0.,0.};
+       GtkAdjustment *adj = NULL;
+       int horiz = -1;
+       int checked = -1;
+       gboolean has_text_prop =
+               g_object_class_find_property (G_OBJECT_GET_CLASS (so), "text") != NULL;
+       char *text = NULL;
+       GnmParsePos pp0;
+       const char *shapetype = "#_x0000_t201";
+       gboolean firstbutton = FALSE;
+
+       parse_pos_init_sheet (&pp0, state->sheet);
+
+       sheet_object_position_pts_get (so, res_pts);
+
+       gsf_xml_out_start_element (xml, "v:shape");
+       gsf_xml_out_add_cstr (xml, "type", shapetype);
+       {
+               int z = GPOINTER_TO_INT (g_hash_table_lookup (zorder, so));
+               GString *str = g_string_new (NULL);
+               g_string_append (str, "position:absolute;");
+               g_string_append_printf (str, "margin-left:%.2fpt;", res_pts[0]);
+               g_string_append_printf (str, "margin-top:%.2fpt;", res_pts[1]);
+               g_string_append_printf (str, "width:%.2fpt;", res_pts[2] - res_pts[0]);
+               g_string_append_printf (str, "height:%.2fpt;", res_pts[3] - res_pts[1]);
+               g_string_append_printf (str, "z-index:%d;", z);
+               gsf_xml_out_add_cstr (xml, "style", str->str);
+               g_string_free (str, TRUE);
+       }
+
+       if (has_text_prop)
+               g_object_get (so, "text", &text, NULL);
+       if (text) {
+               gsf_xml_out_start_element (xml, "v:textbox");
+               gsf_xml_out_start_element (xml, "div");
+               gsf_xml_out_add_cstr (xml, NULL, text);
+               gsf_xml_out_end_element (xml);  /* </div> */
+               gsf_xml_out_end_element (xml);  /* </v:textbox> */
+               g_free (text);
+       }
+
+       gsf_xml_out_start_element (xml, "x:ClientData");
+       if (GNM_IS_SOW_SCROLLBAR (so) || GNM_IS_SOW_SLIDER (so)) {
+               otype = "Scroll";
+               tlink = sheet_widget_adjustment_get_link (so);
+               adj = sheet_widget_adjustment_get_adjustment (so);
+               horiz = sheet_widget_adjustment_get_horizontal (so);
+       } else if (GNM_IS_SOW_SPINBUTTON (so)) {
+               otype = "Spin";
+               tlink = sheet_widget_adjustment_get_link (so);
+               adj = sheet_widget_adjustment_get_adjustment (so);
+       } else if (GNM_IS_SOW_BUTTON (so)) {
+               otype = "Button";
+               tlink = sheet_widget_button_get_link (so);
+       } else if (GNM_IS_SOW_RADIO_BUTTON (so)) {
+               gboolean c;
+               GSList *sos;
+               otype = "Radio";
+               tlink = sheet_widget_radio_button_get_link (so);
+               sos = tlink ? g_hash_table_lookup (radio_by_link, tlink) : NULL;
+               firstbutton = (!sos || so == sos->data);
+               g_object_get (so, "active", &c, NULL);
+               checked = c;
+       } else if (GNM_IS_SOW_CHECKBOX (so)) {
+               gboolean c;
+               otype = "Checkbox";
+               tlink = sheet_widget_checkbox_get_link (so);
+               g_object_get (so, "active", &c, NULL);
+               checked = c;
+       } else {
+               g_assert_not_reached ();
+       }
+       gsf_xml_out_add_cstr_unchecked (xml, "ObjectType", otype);
+       gsf_xml_out_start_element (xml, "x:Anchor");
+       gsf_xml_out_end_element (xml);  /* </x:Anchor> */
+       if (checked != -1)
+               gsf_xml_out_simple_int_element (xml, "x:Checked", checked);
+       if (tlink) {
+               char *s = gnm_expr_top_as_string (tlink, &pp0, state->convs);
+               gsf_xml_out_start_element (xml, "x:FmlaLink");
+               gsf_xml_out_add_cstr (xml, NULL, s);
+               gsf_xml_out_end_element (xml);  /* </x:FmlaLink> */
+               g_free (s);
+               gnm_expr_top_unref (tlink);
+       }
+       if (firstbutton)
+               gsf_xml_out_simple_element (xml, "x:FirstButton", NULL);
+       if (adj) {
+               gsf_xml_out_simple_float_element (xml, "x:Val",
+                                                 gtk_adjustment_get_value (adj), -1);
+               gsf_xml_out_simple_float_element (xml, "x:Min",
+                                                 gtk_adjustment_get_lower (adj), -1);
+               gsf_xml_out_simple_float_element (xml, "x:Max",
+                                                 gtk_adjustment_get_upper (adj), -1);
+               gsf_xml_out_simple_float_element (xml, "x:Inc",
+                                                 gtk_adjustment_get_step_increment (adj), -1);
+               gsf_xml_out_simple_float_element (xml, "x:Page",
+                                                 gtk_adjustment_get_page_increment (adj), -1);
+       }
+       if (horiz >= 0)
+               gsf_xml_out_simple_element (xml, "x:Horiz", horiz ? "t" : "f");
+
+       gsf_xml_out_end_element (xml);  /* </x:ClientData> */
+
+       gsf_xml_out_end_element (xml);  /* </v:shape> */
+}
+
 static char const *
-xlsx_write_legacy_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part, GSList *objects)
+xlsx_write_legacy_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part,
+                                  GSList *objects, GHashTable *zorder)
 {
        GSList *obj;
        char *name;
        char const *rId;
        GsfOutput *drawing_part;
        GsfXMLOut *xml;
-       GnmParsePos pp0;
        const char *shapetype = "#_x0000_t201";
+       GHashTable *radio_by_link;
 
-       parse_pos_init_sheet (&pp0, state->sheet);
+       /*
+        * Radio buttons need extra work.  Excel doesn't have our concept of
+        * a value field, so the buttons need to be written in value order.
+        */
+       radio_by_link = xlsx_preprocess_radio (state, objects);
 
        /* Note: we use drawing.dir here.  */
        if (NULL == state->drawing.dir) {
@@ -1396,101 +1587,24 @@ xlsx_write_legacy_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part,
 
        for (obj = objects ; obj != NULL ; obj = obj->next) {
                SheetObject *so = obj->data;
-               const char *otype = NULL;
-               GnmExprTop const *tlink = NULL;
-               double res_pts[4] = {0.,0.,0.,0.};
-               GtkAdjustment *adj = NULL;
-               int horiz = -1;
-               int checked = -1;
-               gboolean has_text_prop =
-                       g_object_class_find_property (G_OBJECT_GET_CLASS (so), "text") != NULL;
-               char *text = NULL;
-
-               sheet_object_position_pts_get (so, res_pts);
-
-               gsf_xml_out_start_element (xml, "v:shape");
-               gsf_xml_out_add_cstr (xml, "type", shapetype);
-               {
-                       GString *str = g_string_new (NULL);
-                       g_string_append (str, "position:absolute;");
-                       g_string_append_printf (str, "margin-left:%.2fpt;", res_pts[0]);
-                       g_string_append_printf (str, "margin-top:%.2fpt;", res_pts[1]);
-                       g_string_append_printf (str, "width:%.2fpt;", res_pts[2] - res_pts[0]);
-                       g_string_append_printf (str, "height:%.2fpt;", res_pts[3] - res_pts[1]);
-                       gsf_xml_out_add_cstr (xml, "style", str->str);
-                       g_string_free (str, TRUE);
-               }
 
-               if (has_text_prop)
-                       g_object_get (so, "text", &text, NULL);
-               if (text) {
-                       gsf_xml_out_start_element (xml, "v:textbox");
-                       gsf_xml_out_start_element (xml, "div");
-                       gsf_xml_out_add_cstr (xml, NULL, text);
-                       gsf_xml_out_end_element (xml);  /* </div> */
-                       gsf_xml_out_end_element (xml);  /* </v:textbox> */
-                       g_free (text);
-               }
+               if (GNM_IS_SOW_RADIO_BUTTON (so)) {
+                       GnmExprTop const *tlink = sheet_widget_radio_button_get_link (so);
+                       if (tlink) {
+                               GSList *sos = g_hash_table_lookup (radio_by_link, tlink);
+                               gnm_expr_top_unref (tlink);
+                               if (so == sos->data) {
+                                       for (; sos; sos = sos->next) {
+                                               so = sos->data;
+                                               xlsx_write_legacy_object (state, xml, so, zorder, 
radio_by_link);
+                                       }
 
-               gsf_xml_out_start_element (xml, "x:ClientData");
-               if (GNM_IS_SOW_SCROLLBAR (so) || GNM_IS_SOW_SLIDER (so)) {
-                       otype = "Scroll";
-                       tlink = sheet_widget_adjustment_get_link (so);
-                       adj = sheet_widget_adjustment_get_adjustment (so);
-                       horiz = sheet_widget_adjustment_get_horizontal (so);
-               } else if (GNM_IS_SOW_SPINBUTTON (so)) {
-                       otype = "Spin";
-                       tlink = sheet_widget_adjustment_get_link (so);
-                       adj = sheet_widget_adjustment_get_adjustment (so);
-               } else if (GNM_IS_SOW_BUTTON (so)) {
-                       otype = "Button";
-                       tlink = sheet_widget_button_get_link (so);
-               } else if (GNM_IS_SOW_RADIO_BUTTON (so)) {
-                       gboolean c;
-                       otype = "Radio";
-                       tlink = sheet_widget_radio_button_get_link (so);
-                       g_object_get (so, "active", &c, NULL);
-                       checked = c;
-               } else if (GNM_IS_SOW_CHECKBOX (so)) {
-                       gboolean c;
-                       otype = "Checkbox";
-                       tlink = sheet_widget_checkbox_get_link (so);
-                       g_object_get (so, "active", &c, NULL);
-                       checked = c;
-               } else {
-                       g_assert_not_reached ();
-               }
-               gsf_xml_out_add_cstr_unchecked (xml, "ObjectType", otype);
-               gsf_xml_out_start_element (xml, "x:Anchor");
-               gsf_xml_out_end_element (xml);  /* </x:Anchor> */
-               if (checked != -1)
-                       gsf_xml_out_simple_int_element (xml, "x:Checked", checked);
-               if (tlink) {
-                       char *s = gnm_expr_top_as_string (tlink, &pp0, state->convs);
-                       gsf_xml_out_start_element (xml, "x:FmlaLink");
-                       gsf_xml_out_add_cstr (xml, NULL, s);
-                       gsf_xml_out_end_element (xml);  /* </x:FmlaLink> */
-                       g_free (s);
-                       gnm_expr_top_unref (tlink);
-               }
-               if (adj) {
-                       gsf_xml_out_simple_float_element (xml, "x:Val",
-                                                         gtk_adjustment_get_value (adj), -1);
-                       gsf_xml_out_simple_float_element (xml, "x:Min",
-                                                         gtk_adjustment_get_lower (adj), -1);
-                       gsf_xml_out_simple_float_element (xml, "x:Max",
-                                                         gtk_adjustment_get_upper (adj), -1);
-                       gsf_xml_out_simple_float_element (xml, "x:Inc",
-                                                         gtk_adjustment_get_step_increment (adj), -1);
-                       gsf_xml_out_simple_float_element (xml, "x:Page",
-                                                         gtk_adjustment_get_page_increment (adj), -1);
+                               }
+                               continue;
+                       }
                }
-               if (horiz >= 0)
-                       gsf_xml_out_simple_element (xml, "x:Horiz", horiz ? "t" : "f");
 
-               gsf_xml_out_end_element (xml);  /* </x:ClientData> */
-
-               gsf_xml_out_end_element (xml);  /* </v:shape> */
+               xlsx_write_legacy_object (state, xml, so, zorder, radio_by_link);
        }
 
        gsf_xml_out_end_element (xml);  /* </xml> */
@@ -1498,5 +1612,7 @@ xlsx_write_legacy_drawing_objects (XLSXWriteState *state, GsfOutput *sheet_part,
        gsf_output_close (drawing_part);
        g_object_unref (drawing_part);
 
+       g_hash_table_destroy (radio_by_link);
+
        return rId;
 }
diff --git a/plugins/excel/xlsx-write.c b/plugins/excel/xlsx-write.c
index ac234a6..f5f03f1 100644
--- a/plugins/excel/xlsx-write.c
+++ b/plugins/excel/xlsx-write.c
@@ -2702,6 +2702,8 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
        char const *legacy_drawing_rel_id = NULL;
        GnmStyle **col_styles;
        PrintInformation *pi = NULL;
+       GHashTable *zorder;
+       int z;
 
        state->sheet = workbook_sheet_by_index (state->base.wb, i);
        col_styles = sheet_style_most_common (state->sheet, TRUE);
@@ -2712,8 +2714,12 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
 
        objects = sheet_objects_get (state->sheet, NULL, G_TYPE_NONE);
        drawing_objs = legacy_drawing_objs = comments = others = NULL;
-       for (p = objects; p; p = p->next) {
+       zorder = g_hash_table_new (g_direct_hash, g_direct_equal);
+       for (p = objects, z = 1; p; p = p->next, z++) {
                SheetObject *so = p->data;
+
+               g_hash_table_insert (zorder, so, GINT_TO_POINTER (z));
+
                if (IS_CELL_COMMENT (so))
                        comments = g_slist_prepend (comments, so);
                else if (IS_SHEET_OBJECT_GRAPH (so) ||
@@ -2741,13 +2747,13 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
 
        if (drawing_objs) {
                drawing_objs = g_slist_reverse (drawing_objs);
-               chart_drawing_rel_id = xlsx_write_drawing_objects (state, sheet_part, drawing_objs);
+               chart_drawing_rel_id = xlsx_write_drawing_objects (state, sheet_part, drawing_objs, zorder);
                g_slist_free (drawing_objs);
        }
 
        if (legacy_drawing_objs) {
                legacy_drawing_objs = g_slist_reverse (legacy_drawing_objs);
-               legacy_drawing_rel_id = xlsx_write_legacy_drawing_objects (state, sheet_part, 
legacy_drawing_objs);
+               legacy_drawing_rel_id = xlsx_write_legacy_drawing_objects (state, sheet_part, 
legacy_drawing_objs, zorder);
                g_slist_free (legacy_drawing_objs);
        }
 
@@ -2763,6 +2769,8 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
        }
        g_slist_free (others);
 
+       g_hash_table_destroy (zorder);
+
        xml = gsf_xml_out_new (sheet_part);
 /* CT_Worksheet =                                          */
        gsf_xml_out_start_element (xml, "worksheet");


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