#include "output/pivot-table.h"
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <math.h>
#include <stdlib.h>
#include "data/data-out.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid
-static const struct fmt_spec *pivot_table_get_format (
- const struct pivot_table *, const char *s);
+static void pivot_table_use_rc (const struct pivot_table *, const char *s,
+ struct fmt_spec *, bool *honor_small);
\f
/* Pivot table display styling. */
static void
pivot_category_set_rc (struct pivot_category *category, const char *s)
{
- const struct fmt_spec *format = pivot_table_get_format (
- category->dimension->table, s);
- if (format)
- category->format = *format;
+ if (!s)
+ return;
+
+ pivot_table_use_rc (category->dimension->table, s,
+ &category->format, &category->honor_small);
/* Ensure that the category itself, in addition to the cells within it, takes
the format. (It's kind of rare for a category to have a numeric format
though.) */
struct pivot_value *name = category->name;
if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
- name->numeric.format = format ? *format : *settings_get_format ();
+ pivot_table_use_rc (category->dimension->table, s,
+ &name->numeric.format, &name->numeric.honor_small);
}
static void
return NULL;
}
-static const struct fmt_spec *
-pivot_table_get_format (const struct pivot_table *table, const char *s)
+static void
+pivot_table_use_rc (const struct pivot_table *table, const char *s,
+ struct fmt_spec *format, bool *honor_small)
{
- if (!s)
- return NULL;
- else if (!strcmp (s, PIVOT_RC_OTHER))
- return settings_get_format ();
- else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
- return &table->weight_format;
- else
+ if (s)
{
- const struct result_class *rc = pivot_result_class_find (s);
- return rc ? &rc->format : NULL;
+ if (!strcmp (s, PIVOT_RC_OTHER))
+ {
+ *format = *settings_get_format ();
+ *honor_small = true;
+ }
+ else if (!strcmp (s, PIVOT_RC_COUNT) && !overridden_count_format)
+ {
+ *format = table->weight_format;
+ *honor_small = false;
+ }
+ else
+ {
+ const struct result_class *rc = pivot_result_class_find (s);
+ if (rc)
+ {
+ *format = rc->format;
+ *honor_small = false;
+ }
+ else
+ {
+ printf ("unknown class %s\n", s);
+ }
+ }
}
}
table->command_c = output_get_command_name ();
table->look = pivot_table_look_ref (pivot_table_look_get_default ());
table->settings = fmt_settings_copy (settings_get_fmt_settings ());
+ table->small = settings_get_small ();
hmap_init (&table->cells);
return table;
}
-static char *
-xstrdup_if_nonnull (const char *s)
-{
- return s ? xstrdup (s) : NULL;
-}
-
static struct pivot_table_sizing
clone_sizing (const struct pivot_table_sizing *s)
{
if (pivot_category_is_leaf (old))
{
+ assert (new->data_index < new_dimension->n_leaves);
new->dimension->data_leaves[new->data_index] = new;
+
+ assert (new->presentation_index < new_dimension->n_leaves);
new->dimension->presentation_leaves[new->presentation_index] = new;
}
return;
}
-
/* Update the current layer, if necessary. If we're moving within the layer
axis, preserve the current layer. */
if (dim->axis_type == PIVOT_AXIS_LAYER)
if (c->format.w)
{
value->numeric.format = c->format;
+ value->numeric.honor_small = c->honor_small;
goto done;
}
}
}
value->numeric.format = *settings_get_format ();
+ value->numeric.honor_small = true;
done:;
}
NULL, content);
}
-static struct pivot_value *
-pivot_make_default_footnote_marker (int idx, bool show_numeric_markers)
-{
- char text[INT_BUFSIZE_BOUND (size_t)];
- if (show_numeric_markers)
- snprintf (text, sizeof text, "%d", idx + 1);
+void
+pivot_footnote_format_marker (const struct pivot_footnote *f,
+ const struct pivot_table *pt,
+ struct string *s)
+{
+ if (f->marker)
+ pivot_value_format_body (f->marker, pt, s);
+ else if (pt->look->show_numeric_markers)
+ ds_put_format (s, "%zu", f->idx + 1);
else
- str_format_26adic (idx + 1, false, text, sizeof text);
- return pivot_value_new_user_text (text, -1);
+ {
+ char text[INT_BUFSIZE_BOUND (size_t)];
+ str_format_26adic (f->idx + 1, false, text, sizeof text);
+ ds_put_cstr (s, text);
+ }
+}
+
+char *
+pivot_footnote_marker_string (const struct pivot_footnote *f,
+ const struct pivot_table *pt)
+{
+ struct string s = DS_EMPTY_INITIALIZER;
+ pivot_footnote_format_marker (f, pt, &s);
+ return ds_steal_cstr (&s);
}
/* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates
while (idx >= table->n_footnotes)
{
struct pivot_footnote *f = xmalloc (sizeof *f);
- f->idx = table->n_footnotes;
- f->marker = pivot_make_default_footnote_marker (
- f->idx, table->look->show_numeric_markers);
- f->content = NULL;
- f->show = true;
-
+ *f = (struct pivot_footnote) {
+ .idx = table->n_footnotes,
+ .show = true,
+ };
table->footnotes[table->n_footnotes++] = f;
}
}
for (size_t j = 0; j < axis->n_dimensions; j++)
{
const struct pivot_dimension *d = axis->dimensions[j];
- dindexes[d->top_index]
- = d->presentation_leaves[pindexes[i][j]]->data_index;
+ size_t pindex = pindexes[i][j];
+ dindexes[d->top_index] = d->presentation_leaves[pindex]->data_index;
}
}
}
: global_show);
}
+/* Appends to OUT the actual text content from the given Pango MARKUP. */
+static void
+get_text_from_markup (const char *markup, struct string *out)
+{
+ xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
+ if (!parser)
+ {
+ ds_put_cstr (out, markup);
+ return;
+ }
+
+ xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
+ xmlParseChunk (parser, markup, strlen (markup), false);
+ xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
+
+ if (parser->wellFormed)
+ {
+ xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc));
+ ds_put_cstr (out, CHAR_CAST (char *, s));
+ xmlFree (s);
+ }
+ else
+ ds_put_cstr (out, markup);
+ xmlFreeDoc (parser->myDoc);
+ xmlFreeParserCtxt (parser);
+}
+
/* Appends a text representation of the body of VALUE to OUT. Settings on
PT control whether variable and value labels are included.
value->numeric.value_label != NULL);
if (show & SETTINGS_VALUE_SHOW_VALUE)
{
+ const struct fmt_spec *f = &value->numeric.format;
+ const struct fmt_spec *format
+ = (f->type == FMT_F
+ && value->numeric.honor_small
+ && value->numeric.x != 0
+ && fabs (value->numeric.x) < pt->small
+ ? &(struct fmt_spec) { .type = FMT_E, .w = 40, .d = f->d }
+ : f);
+
char *s = data_out (&(union value) { .f = value->numeric.x },
- "UTF-8", &value->numeric.format, &pt->settings);
+ "UTF-8", format, &pt->settings);
ds_put_cstr (out, s + strspn (s, " "));
free (s);
}
break;
case PIVOT_VALUE_TEXT:
- ds_put_cstr (out, value->text.local);
+ if (value->font_style && value->font_style->markup)
+ get_text_from_markup (value->text.local, out);
+ else
+ ds_put_cstr (out, value->text.local);
break;
case PIVOT_VALUE_TEMPLATE:
/* Appends a text representation of VALUE to OUT. Settings on
PT control whether variable and value labels are included.
- Subscripts and footnotes are included. */
-void
+ Subscripts and footnotes are included.
+
+ Returns true if OUT is a number (or a number plus a value label), false
+ otherwise. */
+bool
pivot_value_format (const struct pivot_value *value,
const struct pivot_table *pt,
struct string *out)
{
- pivot_value_format_body (value, pt, out);
+ bool numeric = pivot_value_format_body (value, pt, out);
if (value->n_subscripts)
{
size_t idx = value->footnote_indexes[i];
const struct pivot_footnote *f = pt->footnotes[idx];
- pivot_value_format (f->marker, pt, out);
+ pivot_footnote_format_marker (f, pt, out);
ds_put_byte (out, ']');
}
+
+ return numeric;
}
/* Returns a text representation of VALUE. The caller must free the string,
static const struct pivot_table pt = {
.show_values = SETTINGS_VALUE_SHOW_DEFAULT,
.show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
+ .settings = FMT_SETTINGS_INIT,
};
return pivot_value_to_string (value, &pt);
}
font_style_copy (NULL, new->font_style, old->font_style);
}
if (old->cell_style)
- new->font_style = xmemdup (old->font_style, sizeof *new->font_style);
+ new->cell_style = xmemdup (old->cell_style, sizeof *new->cell_style);
if (old->n_subscripts)
{
new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
case PIVOT_VALUE_TEXT:
new->text.local = xstrdup (old->text.local);
new->text.c = (old->text.c == old->text.local ? new->text.local
- : xstrdup (old->text.c));
+ : xstrdup_if_nonnull (old->text.c));
new->text.id = (old->text.id == old->text.local ? new->text.local
: old->text.id == old->text.c ? new->text.c
- : xstrdup (old->text.id));
+ : xstrdup_if_nonnull (old->text.id));
break;
case PIVOT_VALUE_TEMPLATE:
v->footnote_indexes = xrealloc (
v->footnote_indexes, (v->n_footnotes + 1) * sizeof *v->footnote_indexes);
v->footnote_indexes[v->n_footnotes++] = footnote->idx;
+ pivot_value_sort_footnotes (v);
+}
+
+static int
+compare_footnote_indexes (const void *a_, const void *b_)
+{
+ const size_t *ap = a_;
+ const size_t *bp = b_;
+ size_t a = *ap;
+ size_t b = *bp;
+ return a < b ? -1 : a > b;
+}
+
+/* Sorts the footnote references in V in the standard ascending order.
+
+ This is only necessary if code adds (plural) footnotes to a pivot_value by
+ itself, because pivot_value_add_footnote() does it automatically. */
+void
+pivot_value_sort_footnotes (struct pivot_value *v)
+{
+ if (v->n_footnotes > 1)
+ qsort (v->footnote_indexes, v->n_footnotes, sizeof *v->footnote_indexes,
+ compare_footnote_indexes);
}
/* If VALUE is a numeric value, and RC is a result class such as
const char *rc)
{
if (value->type == PIVOT_VALUE_NUMERIC)
- {
- const struct fmt_spec *f = pivot_table_get_format (table, rc);
- if (f)
- value->numeric.format = *f;
- }
+ pivot_table_use_rc (table, rc,
+ &value->numeric.format, &value->numeric.honor_small);
}