dnl Initialize.
AC_PREREQ(2.60)
-AC_INIT([pspp],[0.7.2],[bug-gnu-pspp@gnu.org])
+AC_INIT([pspp],[0.7.3],[bug-gnu-pspp@gnu.org])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE
dnl Checks for libraries.
AC_SYS_LARGEFILE
AC_SEARCH_LIBS([sin], [m])
-PSPP_LIBPLOT
PSPP_LC_PAPER
-AM_CONDITIONAL(WITHCHARTS, test x"$with_libplot" != x"no")
AC_ARG_VAR([PSPP_LDFLAGS], [linker flags to be used for linking the pspp binary only])
AC_ARG_VAR([PSPPIRE_LDFLAGS], [linker flags to be used for linking the psppire binary only])
-
-AC_ARG_WITH(
- gui,
- [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])])
-
-required_gtk_version=2.12
-
-if test x"$with_gui" != x"no" ; then
- PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $required_gtk_version,,
- [PSPP_REQUIRED_PREREQ([gtk+ 2.0 v$required_gtk_version or later (or use --without-gui)])])
+# Support for Cairo and Pango.
+AC_ARG_WITH([cairo],
+ [AS_HELP_STRING(
+ [--without-cairo],
+ [Don't build support for charts (using Cairo and Pango);
+ implies --without-gui])],
+ [], [with_cairo=yes])
+AM_CONDITIONAL([HAVE_CAIRO], [test "$with_cairo" != no])
+if test "$with_cairo" != no; then
+ PKG_CHECK_MODULES([CAIRO], [cairo >= 1.5 pango >= 1.22 pangocairo],
+ [CPPFLAGS="$CPPFLAGS $CAIRO_CFLAGS"
+ AC_DEFINE([HAVE_CAIRO], 1,
+ [Define to 1 if Cairo and Pango are available.])],
+ [PSPP_REQUIRED_PREREQ([cairo 1.5 or later and pango 1.22 or later (or use --without-cairo)])])
fi
-AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no")
+# Support for GUI.
+AC_ARG_WITH([gui],
+ [AS_HELP_STRING([--without-gui],
+ [Don't build the PSPPIRE GUI (using GTK+)])],
+ [], [with_gui=yes])
+AM_CONDITIONAL([HAVE_GUI],
+ [test "$with_cairo" != no && test "$with_gui" != "no"])
+if test "$with_cairo" != no && test "$with_gui" != "no"; then
+ PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.12], [],
+ [PSPP_REQUIRED_PREREQ([gtk+ 2.0 version 2.12 or later (or use --without-gui)])])
+fi
dnl Checks needed for psql reader
fi
AM_CONDITIONAL(PSQL_SUPPORT, test -n "$PG_CONFIG")
- dnl Checks needed for gnumeric reader
+ dnl Checks needed for Gnumeric reader
gnm_support=yes;
PKG_CHECK_MODULES(LIBXML2, libxml-2.0,,
[PSPP_OPTIONAL_PREREQ([libxml2]); gnm_support=no;]);
subcase_add_var (sc, var, direction);
}
+
+ void
+ subcase_init (struct subcase *sc, int index, int width,
+ enum subcase_direction direction)
+ {
+ subcase_init_empty (sc);
+ subcase_add (sc, index, width, direction);
+ }
+
+
/* Removes all the fields from SC. */
void
subcase_clear (struct subcase *sc)
caseproto_unref (sc->proto);
}
+
/* Add a field for VAR to SC, with DIRECTION as the sort order.
Returns true if successful, false if VAR already has a field
in SC. */
subcase_add_var (struct subcase *sc, const struct variable *var,
enum subcase_direction direction)
{
- size_t case_index = var_get_case_index (var);
+ return subcase_add (sc, var_get_case_index (var),
+ var_get_width (var), direction);
+ }
+
+ /* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the sort order.
+ Returns true if successful, false if CASE_INDEX already has a field
+ in SC. */
+ bool
+ subcase_add (struct subcase *sc, int case_index, int width,
+ enum subcase_direction direction)
+ {
struct subcase_field *field;
size_t i;
sc->fields = xnrealloc (sc->fields, sc->n_fields + 1, sizeof *sc->fields);
field = &sc->fields[sc->n_fields++];
field->case_index = case_index;
- field->width = var_get_width (var);
+ field->width = width;
field->direction = direction;
invalidate_proto (sc);
return true;
const struct caseproto *
subcase_get_proto (const struct subcase *sc_)
{
- struct subcase *sc = (struct subcase *) sc_;
+ struct subcase *sc = CONST_CAST (struct subcase *, sc_);
if (sc->proto == NULL)
{
return new_var;
}
- /* Create a variable to be used for internal calculations only.
- The variable is assigned a unique dictionary index and a case
- index of CASE_IDX. */
+ /* Create a variable of the specified WIDTH to be used for
+ internal calculations only. The variable is assigned a unique
+ dictionary index and a case index of CASE_IDX. */
struct variable *
- var_create_internal (int case_idx)
+ var_create_internal (int case_idx, int width)
{
- struct variable *v = var_create ("$internal", 0);
+ struct variable *v = var_create ("$internal", width);
struct vardict_info vdi;
static int counter = INT_MAX / 2;
S[] must contain exactly as many characters as V's width.
V must be a string variable. */
bool
- var_is_str_missing (const struct variable *v, const char s[],
+ var_is_str_missing (const struct variable *v, const uint8_t s[],
enum mv_class class)
{
return mv_is_str_missing (&v->miss, s, class);
struct string *str)
{
const char *name = var_lookup_value_label (v, value);
+ const struct dictionary *dict = var_get_vardict (v)->dict;
if (name == NULL)
{
- char *s = ds_put_uninit (str, v->print.w);
- data_out (value, &v->print, s);
+ char *s = data_out (value, dict_get_encoding (dict), &v->print);
+ ds_put_cstr (str, s);
+ free (s);
}
else
ds_put_cstr (str, name);
var_attach_aux (const struct variable *v_,
void *aux, void (*aux_dtor) (struct variable *))
{
- struct variable *v = (struct variable *) v_ ; /* cast away const */
+ struct variable *v = CONST_CAST (struct variable *, v_);
assert (v->aux == NULL);
assert (aux != NULL);
v->aux = aux;
void
var_set_obs_vals (const struct variable *v_, struct cat_vals *cat_vals)
{
- struct variable *v = (struct variable *) v_ ; /* cast away const */
+ struct variable *v = CONST_CAST (struct variable *, v_ );
cat_stored_values_destroy (v->obs_vals);
v->obs_vals = cat_vals;
}
struct attrset *
var_get_attributes (const struct variable *v)
{
- return (struct attrset *) &v->attributes;
+ return CONST_CAST (struct attrset *, &v->attributes);
}
/* Replaces variable V's attributes set by a copy of ATTRS. */
#include <libpspp/message.h>
#include <libpspp/str.h>
#include <output/manager.h>
-#include <output/table.h>
#include <libpspp/getl.h>
#if HAVE_SYS_WAIT_H
}
else if (command->function == NULL)
{
- msg (SE, _("%s is unimplemented."), command->name);
+ msg (SE, _("%s is not yet implemented."), command->name);
result = CMD_NOT_IMPLEMENTED;
goto finish;
}
/* Execute command. */
msg_set_command_name (command->name);
- tab_set_command_name (command->name);
+ som_set_command_name (command->name);
result = command->function (lexer, ds);
- tab_set_command_name (NULL);
+ som_set_command_name (NULL);
msg_set_command_name (NULL);
assert (cmd_result_is_valid (result));
/* Data parser for textual data like that read by DATA LIST. */
struct data_parser
{
+ const struct dictionary *dict; /*Dictionary of destination */
enum data_parser_type type; /* Type of data to parse. */
int skip_records; /* Records to skip before first real data. */
casenumber max_cases; /* Max number of cases to read. */
/* Creates and returns a new data parser. */
struct data_parser *
- data_parser_create (void)
+ data_parser_create (const struct dictionary *dict)
{
struct data_parser *parser = xmalloc (sizeof *parser);
parser->fields = NULL;
parser->field_cnt = 0;
parser->field_allocated = 0;
+ parser->dict = dict;
parser->span = true;
parser->empty_line_has_field = false;
/* Regular field. */
ss_get_chars (&p, ss_cspan (p, ds_ss (&parser->any_sep)), field);
*last_column = dfm_column_start (reader);
- if (!ss_ltrim (&p, parser->soft_seps) || ss_is_empty (p))
+ if (!ss_ltrim (&p, parser->soft_seps) || ss_is_empty (p)
+ || ss_find_char (parser->hard_seps, p.string[0]) != SIZE_MAX)
{
/* Advance past a trailing hard separator,
regardless of whether one actually existed. If
parse_fixed (const struct data_parser *parser, struct dfm_reader *reader,
struct ccase *c)
{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+ const char *encoding = dfm_reader_get_legacy_encoding (reader);
struct field *f;
int row;
f->format.w),
encoding, f->format.type, f->format.d,
f->first_column, f->first_column + f->format.w,
+ parser->dict,
case_data_rw_idx (c, f->case_idx),
fmt_var_width (&f->format));
parse_delimited_span (const struct data_parser *parser,
struct dfm_reader *reader, struct ccase *c)
{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+ const char *encoding = dfm_reader_get_legacy_encoding (reader);
struct string tmp = DS_EMPTY_INITIALIZER;
struct field *f;
data_in (s, encoding, f->format.type, 0,
first_column, last_column,
+ parser->dict,
case_data_rw_idx (c, f->case_idx),
fmt_var_width (&f->format));
}
parse_delimited_no_span (const struct data_parser *parser,
struct dfm_reader *reader, struct ccase *c)
{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+ const char *encoding = dfm_reader_get_legacy_encoding (reader);
struct string tmp = DS_EMPTY_INITIALIZER;
struct substring s;
struct field *f;
data_in (s, encoding, f->format.type, 0,
first_column, last_column,
+ parser->dict,
case_data_rw_idx (c, f->case_idx),
fmt_var_width (&f->format));
}
struct tab_table *t;
size_t i;
- t = tab_create (4, parser->field_cnt + 1, 0);
- tab_columns (t, TAB_COL_DOWN, 1);
+ t = tab_create (4, parser->field_cnt + 1);
+ tab_columns (t, TAB_COL_DOWN);
tab_headers (t, 0, 0, 1, 0);
tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, parser->field_cnt);
tab_hline (t, TAL_2, 0, 3, 1);
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
for (i = 0; i < parser->field_cnt; i++)
{
struct tab_table *t;
size_t i;
- t = tab_create (2, parser->field_cnt + 1, 0);
- tab_columns (t, TAB_COL_DOWN, 1);
+ t = tab_create (2, parser->field_cnt + 1);
+ tab_columns (t, TAB_COL_DOWN);
tab_headers (t, 0, 0, 1, 0);
tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Format"));
tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, parser->field_cnt);
tab_hline (t, TAL_2, 0, 1, 1);
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
for (i = 0; i < parser->field_cnt; i++)
{
#include <language/dictionary/split-file.h>
#include <language/lexer/lexer.h>
#include <libpspp/compiler.h>
-#include <libpspp/message.h>
+#include <libpspp/ll.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <output/htmlP.h>
/* (functions) */
/* Layout for one output driver. */
-struct list_ext
+struct list_target
{
+ struct ll ll;
+ struct outp_driver *driver;
int type; /* 0=Values and labels fit across the page. */
size_t n_vertical; /* Number of labels to list vertically. */
size_t header_rows; /* Number of header rows. */
/* Other functions. */
static void list_case (const struct ccase *, casenumber case_idx,
- const struct dataset *);
-static void determine_layout (void);
-static void clean_up (void);
-static void write_header (struct outp_driver *);
-static void write_all_headers (struct casereader *, const struct dataset*);
+ const struct dataset *, struct ll_list *targets);
+static void determine_layout (struct ll_list *targets);
+static void clean_up (struct ll_list *targets);
+static void write_header (struct list_target *);
+static void write_all_headers (struct casereader *, const struct dataset *,
+ struct ll_list *targets);
/* Returns the number of text lines that can fit on the remainder of
the page. */
struct variable *casenum_var = NULL;
struct casegrouper *grouper;
struct casereader *group;
+ struct ll_list targets;
casenumber case_idx;
bool ok;
cmd.v_variables[0] = casenum_var;
}
- determine_layout ();
+ determine_layout (&targets);
case_idx = 0;
for (grouper = casegrouper_create_splits (proc_open (ds), dict);
{
struct ccase *c;
- write_all_headers (group, ds);
+ write_all_headers (group, ds, &targets);
for (; (c = casereader_read (group)) != NULL; case_unref (c))
{
case_idx++;
if (case_idx >= cmd.first && case_idx <= cmd.last
&& (case_idx - cmd.first) % cmd.step == 0)
- list_case (c, case_idx, ds);
+ list_case (c, case_idx, ds, &targets);
}
}
ok = casegrouper_destroy (grouper);
ds_destroy(&line_buffer);
- clean_up ();
+ clean_up (&targets);
var_destroy (casenum_var);
/* Writes headers to all devices. This is done at the beginning of
each SPLIT FILE group. */
static void
-write_all_headers (struct casereader *input, const struct dataset *ds)
+write_all_headers (struct casereader *input, const struct dataset *ds,
+ struct ll_list *targets)
{
- struct outp_driver *d;
+ struct list_target *target;
struct ccase *c;
c = casereader_peek (input, 0);
output_split_file_values (ds, c);
case_unref (c);
- for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+ ll_for_each (target, struct list_target, ll, targets)
{
+ struct outp_driver *d = target->driver;
if (!d->class->special)
{
d->cp_y += d->font_height; /* Blank line. */
- write_header (d);
+ write_header (target);
}
else if (d->class == &html_class)
{
/* Writes the headers. Some of them might be vertical; most are
probably horizontal. */
static void
-write_header (struct outp_driver *d)
+write_header (struct list_target *target)
{
- struct list_ext *prc = d->prc;
+ struct outp_driver *d = target->driver;
- if (!prc->header_rows)
+ if (d->class->special || !target->header_rows)
return;
- if (n_lines_remaining (d) < prc->header_rows + 1)
+ if (n_lines_remaining (d) < target->header_rows + 1)
{
outp_eject_page (d);
- assert (n_lines_remaining (d) >= prc->header_rows + 1);
+ assert (n_lines_remaining (d) >= target->header_rows + 1);
}
/* Design the header. */
- if (!prc->header)
+ if (!target->header)
{
size_t i;
size_t x;
/* Allocate, initialize header. */
- prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
+ target->header = xnmalloc (target->header_rows, sizeof *target->header);
{
int w = n_chars_width (d);
- for (i = 0; i < prc->header_rows; i++)
+ for (i = 0; i < target->header_rows; i++)
{
- prc->header[i] = xmalloc (w + 1);
- memset (prc->header[i], ' ', w);
+ target->header[i] = xmalloc (w + 1);
+ memset (target->header[i], ' ', w);
}
}
/* Put in vertical names. */
- for (i = x = 0; i < prc->n_vertical; i++)
+ for (i = x = 0; i < target->n_vertical; i++)
{
const struct variable *v = cmd.v_variables[i];
const char *name = var_get_name (v);
const struct fmt_spec *print = var_get_print_format (v);
size_t j;
- memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
+ memset (&target->header[target->header_rows - 1][x], '-', print->w);
x += print->w - 1;
for (j = 0; j < name_len; j++)
- prc->header[name_len - j - 1][x] = name[j];
+ target->header[name_len - j - 1][x] = name[j];
x += 2;
}
size_t name_len = strlen (name);
const struct fmt_spec *print = var_get_print_format (v);
- memset (&prc->header[prc->header_rows - 1][x], '-',
+ memset (&target->header[target->header_rows - 1][x], '-',
MAX (print->w, (int) name_len));
if ((int) name_len < print->w)
x += print->w - name_len;
- memcpy (&prc->header[0][x], name, name_len);
+ memcpy (&target->header[0][x], name, name_len);
x += name_len + 1;
}
/* Add null bytes. */
- for (i = 0; i < prc->header_rows; i++)
+ for (i = 0; i < target->header_rows; i++)
{
for (x = n_chars_width (d); x >= 1; x--)
- if (prc->header[i][x - 1] != ' ')
+ if (target->header[i][x - 1] != ' ')
{
- prc->header[i][x] = 0;
+ target->header[i][x] = 0;
break;
}
assert (x);
}
/* Write out the header, in back-to-front order except for the last line. */
- if (prc->header_rows >= 2)
+ if (target->header_rows >= 2)
{
size_t i;
- for (i = prc->header_rows - 1; i-- != 0; )
- write_line (d, prc->header[i]);
+ for (i = target->header_rows - 1; i-- != 0; )
+ write_line (d, target->header[i]);
}
- write_line (d, prc->header[prc->header_rows - 1]);
+ write_line (d, target->header[target->header_rows - 1]);
}
/* Frees up all the memory we've allocated. */
static void
-clean_up (void)
+clean_up (struct ll_list *targets)
{
- struct outp_driver *d;
+ struct list_target *target, *next;
- for (d = outp_drivers (NULL); d; d = outp_drivers (d))
- if (d->class->special == 0)
- {
- struct list_ext *prc = d->prc;
- size_t i;
-
- if (prc->header)
- {
- for (i = 0; i < prc->header_rows; i++)
- free (prc->header[i]);
- free (prc->header);
- }
- free (prc);
- }
- else if (d->class == &html_class)
- {
- if (d->page_open)
- {
- struct html_driver_ext *x = d->ext;
+ ll_for_each_safe (target, next, struct list_target, ll, targets)
+ {
+ struct outp_driver *d = target->driver;
+ if (d->class->special == 0)
+ {
+ if (target->header)
+ {
+ size_t i;
+ for (i = 0; i < target->header_rows; i++)
+ free (target->header[i]);
+ free (target->header);
+ }
+ }
+ else if (d->class == &html_class)
+ {
+ if (d->page_open)
+ {
+ struct html_driver_ext *x = d->ext;
- fputs ("</TABLE>\n", x->file);
- }
- }
- else
- NOT_REACHED ();
+ fputs ("</TABLE>\n", x->file);
+ }
+ }
+ else
+ NOT_REACHED ();
+ ll_remove (&target->ll);
+ free (target);
+ }
+
free (cmd.v_variables);
}
This is complicated by the fact that we have to do all this for
every output driver, not just once. */
static void
-determine_layout (void)
+determine_layout (struct ll_list *targets)
{
struct outp_driver *d;
size buffer to allocate. */
int largest_page_width = 0;
+ ll_init (targets);
for (d = outp_drivers (NULL); d; d = outp_drivers (d))
{
size_t column; /* Current column. */
int height; /* Height of vertical names. */
int max_width; /* Page width. */
- struct list_ext *prc;
+ struct list_target *target;
+
+ target = xmalloc (sizeof *target);
+ ll_push_tail (targets, &target->ll);
+ target->driver = d;
+ target->type = 0;
+ target->n_vertical = 0;
+ target->header = NULL;
if (d->class == &html_class)
continue;
-
assert (d->class->special == 0);
outp_open_page (d);
max_width = n_chars_width (d);
largest_page_width = MAX (largest_page_width, max_width);
- prc = d->prc = xmalloc (sizeof *prc);
- prc->type = 0;
- prc->n_vertical = 0;
- prc->header = NULL;
-
/* Try layout #1. */
for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
{
}
if (width <= max_width)
{
- prc->header_rows = 2;
+ target->header_rows = 2;
continue;
}
if (width <= max_width && height <= SHORT_NAME_LEN)
{
#ifndef NDEBUG
- prc->n_vertical = SIZE_MAX;
+ target->n_vertical = SIZE_MAX;
#endif
for (column = cmd.n_variables; column-- != 0; )
{
int trial_width = width - fmt_width + MAX (fmt_width, name_len);
if (trial_width > max_width)
{
- prc->n_vertical = column + 1;
+ target->n_vertical = column + 1;
break;
}
width = trial_width;
}
- assert (prc->n_vertical != SIZE_MAX);
+ assert (target->n_vertical != SIZE_MAX);
- prc->n_vertical = cmd.n_variables;
+ target->n_vertical = cmd.n_variables;
/* Finally determine the length of the headers. */
- for (prc->header_rows = 0, column = 0;
- column < prc->n_vertical;
+ for (target->header_rows = 0, column = 0;
+ column < target->n_vertical;
column++)
{
const struct variable *var = cmd.v_variables[column];
size_t name_len = strlen (var_get_name (var));
- prc->header_rows = MAX (prc->header_rows, name_len);
+ target->header_rows = MAX (target->header_rows, name_len);
}
- prc->header_rows++;
+ target->header_rows++;
continue;
}
/* Otherwise use the ugly fallback listing format. */
- prc->type = 1;
- prc->header_rows = 0;
+ target->type = 1;
+ target->header_rows = 0;
d->cp_y += d->font_height;
write_fallback_headers (d);
/* Writes case C to output. */
static void
list_case (const struct ccase *c, casenumber case_idx,
- const struct dataset *ds)
+ const struct dataset *ds, struct ll_list *targets)
{
struct dictionary *dict = dataset_dict (ds);
- struct outp_driver *d;
++ const char *encoding = dict_get_encoding (dict);
+ struct list_target *target;
- for (d = outp_drivers (NULL); d; d = outp_drivers (d))
- if (d->class->special == 0)
- {
- const struct list_ext *prc = d->prc;
- const int max_width = n_chars_width (d);
- int column;
-
- if (!prc->header_rows)
- {
- ds_put_format(&line_buffer, "%8s: ",
- var_get_name (cmd.v_variables[0]));
- }
-
-
- for (column = 0; column < cmd.n_variables; column++)
- {
- const struct variable *v = cmd.v_variables[column];
- const struct fmt_spec *print = var_get_print_format (v);
- int width;
-
- if (prc->type == 0 && column >= prc->n_vertical)
- {
- int name_len = strlen (var_get_name (v));
- width = MAX (name_len, print->w);
- }
- else
- width = print->w;
-
- if (width + ds_length(&line_buffer) > max_width &&
- ds_length(&line_buffer) != 0)
- {
- if (!n_lines_remaining (d))
- {
- outp_eject_page (d);
- write_header (d);
- }
+ ll_for_each (target, struct list_target, ll, targets)
+ {
+ struct outp_driver *d = target->driver;
- write_line (d, ds_cstr (&line_buffer));
- ds_clear(&line_buffer);
+ if (d->class->special == 0)
+ {
+ const int max_width = n_chars_width (d);
+ int column;
- if (!prc->header_rows)
- ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
- }
+ if (!target->header_rows)
+ {
+ ds_put_format(&line_buffer, "%8s: ",
+ var_get_name (cmd.v_variables[0]));
+ }
- if (width > print->w)
- ds_put_char_multiple(&line_buffer, ' ', width - print->w);
- if (fmt_is_string (print->type)
- || dict_contains_var (dict, v))
- {
- char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
- ds_put_cstr (&line_buffer, s);
- free (s);
- }
- else
- {
- char *s;
- union value case_idx_value;
- case_idx_value.f = case_idx;
- s = data_out (&case_idx_value, dict_get_encoding (dict), print);
- ds_put_cstr (&line_buffer, s);
- free (s);
- }
-
- ds_put_char (&line_buffer, ' ');
- }
+ for (column = 0; column < cmd.n_variables; column++)
+ {
+ const struct variable *v = cmd.v_variables[column];
+ const struct fmt_spec *print = var_get_print_format (v);
+ int width;
++ char *s;
+
+ if (target->type == 0 && column >= target->n_vertical)
+ {
+ int name_len = strlen (var_get_name (v));
+ width = MAX (name_len, print->w);
+ }
+ else
+ width = print->w;
+
+ if (width + ds_length(&line_buffer) > max_width &&
+ ds_length(&line_buffer) != 0)
+ {
+ if (!n_lines_remaining (d))
+ {
+ outp_eject_page (d);
+ write_header (target);
+ }
+
+ write_line (d, ds_cstr (&line_buffer));
+ ds_clear(&line_buffer);
+
+ if (!target->header_rows)
+ ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
+ }
+
+ if (width > print->w)
+ ds_put_char_multiple(&line_buffer, ' ', width - print->w);
+
- if (fmt_is_string (print->type)
- || dict_contains_var (dict, v))
- {
- data_out (case_data (c, v), print,
- ds_put_uninit (&line_buffer, print->w));
- }
++ if (fmt_is_string (print->type) || dict_contains_var (dict, v))
++ s = data_out (case_data (c, v), encoding, print);
+ else
+ {
+ union value case_idx_value;
+ case_idx_value.f = case_idx;
- data_out (&case_idx_value, print,
- ds_put_uninit (&line_buffer,print->w));
++ s = data_out (&case_idx_value, encoding, print);
+ }
+
++ ds_put_cstr (&line_buffer, s);
++ free (s);
+ ds_put_char(&line_buffer, ' ');
+ }
- if (!n_lines_remaining (d))
- {
- outp_eject_page (d);
- write_header (d);
- }
+ if (!n_lines_remaining (d))
+ {
+ outp_eject_page (d);
+ write_header (target);
+ }
- write_line (d, ds_cstr (&line_buffer));
- ds_clear(&line_buffer);
- }
- else if (d->class == &html_class)
- {
- struct html_driver_ext *x = d->ext;
- int column;
+ write_line (d, ds_cstr (&line_buffer));
+ ds_clear(&line_buffer);
+ }
+ else if (d->class == &html_class)
+ {
+ struct html_driver_ext *x = d->ext;
+ int column;
- fputs (" <TR>\n", x->file);
+ fputs (" <TR>\n", x->file);
- for (column = 0; column < cmd.n_variables; column++)
- {
- const struct variable *v = cmd.v_variables[column];
- const struct fmt_spec *print = var_get_print_format (v);
- char *s = NULL;
-
- if (fmt_is_string (print->type)
- || dict_contains_var (dict, v))
- s = data_out (case_data (c, v), dict_get_encoding (dict), print);
- else
- {
- union value case_idx_value;
- case_idx_value.f = case_idx;
- s = data_out (&case_idx_value, dict_get_encoding (dict), print);
- }
-
- fputs (" <TD>", x->file);
- html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
- free (s);
- fputs ("</TD>\n", x->file);
- }
+ for (column = 0; column < cmd.n_variables; column++)
+ {
+ const struct variable *v = cmd.v_variables[column];
+ const struct fmt_spec *print = var_get_print_format (v);
- char buf[256];
++ char *s;
+
+ if (fmt_is_string (print->type)
+ || dict_contains_var (dict, v))
- data_out (case_data (c, v), print, buf);
++ s = data_out (case_data (c, v), encoding, print);
+ else
+ {
+ union value case_idx_value;
+ case_idx_value.f = case_idx;
- data_out (&case_idx_value, print, buf);
++ s = data_out (&case_idx_value, encoding, print);
+ }
+
+ fputs (" <TD>", x->file);
- html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
++ html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
+ fputs ("</TD>\n", x->file);
++
++ free (s);
+ }
- fputs (" </TR>\n", x->file);
- }
- else
- NOT_REACHED ();
+ fputs (" </TR>\n", x->file);
+ }
+ else
+ NOT_REACHED ();
+ }
}
+
/*
Local Variables:
mode: c
#include <language/lexer/lexer.h>
#include <language/lexer/variable-parser.h>
#include <libpspp/assertion.h>
+ #include <libpspp/i18n.h>
#include <libpspp/compiler.h>
#include <libpspp/ll.h>
#include <libpspp/message.h>
struct pool *pool; /* Stores related data. */
bool eject; /* Eject page before printing? */
bool include_prefix; /* Prefix lines with space? */
- enum legacy_encoding encoding; /* Encoding to use for output. */
+ const char *encoding; /* Encoding to use for output. */
struct dfm_writer *writer; /* Output file, NULL=listing file. */
struct ll_list specs; /* List of struct prt_out_specs. */
size_t record_cnt; /* Number of records to write. */
int row;
spec_cnt = ll_count (&trns->specs);
- t = tab_create (4, spec_cnt + 1, 0);
- tab_columns (t, TAB_COL_DOWN, 1);
+ t = tab_create (4, spec_cnt + 1);
+ tab_columns (t, TAB_COL_DOWN);
tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
tab_hline (t, TAL_2, 0, 3, 1);
tab_headers (t, 0, 0, 1, 0);
tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
row = 1;
ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
{
else
{
ds_put_substring (&trns->line, ds_ss (&spec->string));
- if (trns->encoding != LEGACY_NATIVE)
+ if (0 != strcmp (trns->encoding, LEGACY_NATIVE))
{
size_t length = ds_length (&spec->string);
char *data = ss_data (ds_tail (&trns->line, length));
- legacy_recode (LEGACY_NATIVE, data,
- trns->encoding, data, length);
+ char *s = recode_string (trns->encoding, LEGACY_NATIVE, data, length);
+ memcpy (data, s, length);
+ free (s);
}
}
}
if (split_cnt == 0)
return;
- t = tab_create (3, split_cnt + 1, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ t = tab_create (3, split_cnt + 1);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_vline (t, TAL_GAP, 1, 0, split_cnt);
tab_vline (t, TAL_GAP, 2, 0, split_cnt);
tab_text (t, 0, 0, TAB_NONE, _("Variable"));
for (i = 0; i < split_cnt; i++)
{
const struct variable *v = split[i];
- char temp_buf[80];
+ char *s;
const char *val_lab;
const struct fmt_spec *print = var_get_print_format (v);
tab_text_format (t, 0, i + 1, TAB_LEFT, "%s", var_get_name (v));
- data_out (case_data (c, v), print, temp_buf);
- temp_buf[print->w] = 0;
+ s = data_out (case_data (c, v), dict_get_encoding (dict), print);
+
- tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, temp_buf);
+ tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, s);
+ free (s);
+
val_lab = var_lookup_value_label (v, case_data (c, v));
if (val_lab)
tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
case MEDIAN:
{
struct casereader *sorted_reader;
- struct order_stats *median = percentile_create (0.5, i->cc);
+ struct percentile *median = percentile_create (0.5, i->cc);
+ struct order_stats *os = &median->parent;
sorted_reader = casewriter_make_reader (i->writer);
- order_stats_accumulate (&median, 1,
+ order_stats_accumulate (&os, 1,
sorted_reader,
i->weight,
i->subject,
i->exclude);
- v->f = percentile_calculate ((struct percentile *) median,
- PC_HAVERAGE);
+ v->f = percentile_calculate (median, PC_HAVERAGE);
- statistic_destroy ((struct statistic *) median);
+ statistic_destroy (&median->parent.parent);
}
break;
case SD:
proto = caseproto_add_width (proto, 0);
if ( ! iter->subject)
- iter->subject = var_create_internal (0);
+ iter->subject = var_create_internal (0, 0);
if ( ! iter->weight)
- iter->weight = var_create_internal (1);
+ iter->weight = var_create_internal (1, 0);
subcase_init_var (&ordering, iter->subject, SC_ASCEND);
iter->writer = sort_create_writer (&ordering, proto);
struct crosstabs_proc
{
+ const struct dictionary *dict;
enum { INTEGER, GENERAL } mode;
enum mv_class exclude;
bool pivot;
unsigned int statistics; /* Bit k is 1 if statistic k is requested. */
};
+/* Auxiliary data structure for tab_dim. */
+struct crosstabs_dim_aux
+ {
+ enum mv_class exclude;
+ };
+
static void
init_proc (struct crosstabs_proc *proc, struct dataset *ds)
{
const struct variable *wv = dict_get_weight (dataset_dict (ds));
+ proc->dict = dataset_dict (ds);
proc->bad_warn = true;
proc->variables = NULL;
proc->n_variables = 0;
The rest of the data was allocated and destroyed at a
lower level already. */
- free (pt);
}
+ free (proc->pivots);
}
static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds,
struct string name;
int i;
- summary = tab_create (7, 3 + proc->n_pivots, 1);
+ summary = tab_create (7, 3 + proc->n_pivots);
tab_title (summary, _("Summary."));
tab_headers (summary, 1, 0, 3, 0);
tab_joint_text (summary, 1, 0, 6, 0, TAB_CENTER, _("Cases"));
static void display_risk (struct pivot_table *, struct tab_table *);
static void display_directional (struct crosstabs_proc *, struct pivot_table *,
struct tab_table *);
-static void crosstabs_dim (struct tab_table *, struct outp_driver *,
- void *proc);
+static void crosstabs_dim (struct tab_rendering *, void *aux);
+static void crosstabs_dim_free (void *aux);
static void table_value_missing (struct crosstabs_proc *proc,
struct tab_table *table, int c, int r,
unsigned char opt, const union value *v,
int i;
table = tab_create (pt->n_consts + 1 + pt->n_cols + 1,
- (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10,
- true);
+ (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10);
tab_headers (table, pt->n_consts + 1, 0, 2, 0);
/* First header line. */
{
const struct variable *var = pt->const_vars[i];
size_t ofs;
+ char *s = NULL;
ds_put_format (&title, ", %s=", var_get_name (var));
/* Insert the formatted value of the variable, then trim
leading spaces in what was just inserted. */
ofs = ds_length (&title);
- data_out (&pt->const_values[i], var_get_print_format (var),
- ds_put_uninit (&title, var_get_width (var)));
+ s = data_out (&pt->const_values[i], dict_get_encoding (proc->dict), var_get_print_format (var));
+ ds_put_cstr (&title, s);
+ free (s);
ds_remove (&title, ofs, ss_cspan (ds_substr (&title, ofs, SIZE_MAX),
ss_cstr (" ")));
}
struct tab_table *chisq;
chisq = tab_create (6 + (pt->n_vars - 2),
- pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10,
- 1);
+ pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10);
tab_headers (chisq, 1 + (pt->n_vars - 2), 0, 1, 0);
tab_title (chisq, _("Chi-square tests."));
tab_text (chisq, 3, 0, TAB_RIGHT | TAT_TITLE,
_("Asymp. Sig. (2-sided)"));
tab_text (chisq, 4, 0, TAB_RIGHT | TAT_TITLE,
- _("Exact. Sig. (2-sided)"));
+ _("Exact Sig. (2-sided)"));
tab_text (chisq, 5, 0, TAB_RIGHT | TAT_TITLE,
- _("Exact. Sig. (1-sided)"));
+ _("Exact Sig. (1-sided)"));
tab_offset (chisq, 0, 1);
return chisq;
struct tab_table *sym;
sym = tab_create (6 + (pt->n_vars - 2),
- pt->n_entries / pt->n_cols * 7 + 10, 1);
+ pt->n_entries / pt->n_cols * 7 + 10);
tab_headers (sym, 2 + (pt->n_vars - 2), 0, 1, 0);
tab_title (sym, _("Symmetric measures."));
{
struct tab_table *risk;
- risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10,
- 1);
+ risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10);
tab_headers (risk, 1 + pt->n_vars - 2, 0, 2, 0);
tab_title (risk, _("Risk estimate."));
struct tab_table *direct;
direct = tab_create (7 + (pt->n_vars - 2),
- pt->n_entries / pt->n_cols * 7 + 10, 1);
+ pt->n_entries / pt->n_cols * 7 + 10);
tab_headers (direct, 3 + (pt->n_vars - 2), 0, 1, 0);
tab_title (direct, _("Directional measures."));
submit (struct crosstabs_proc *proc, struct pivot_table *pt,
struct tab_table *t)
{
+ struct crosstabs_dim_aux *aux;
int i;
if (t == NULL)
tab_box (t, -1, -1, -1, TAL_GAP, 0, tab_t (t), tab_l (t) - 1,
tab_nr (t) - 1);
tab_vline (t, TAL_2, tab_l (t), 0, tab_nr (t) - 1);
- tab_dim (t, crosstabs_dim, proc);
+
+ aux = xmalloc (sizeof *aux);
+ aux->exclude = proc->exclude;
+ tab_dim (t, crosstabs_dim, crosstabs_dim_free, aux);
+
tab_submit (t);
}
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
+crosstabs_dim (struct tab_rendering *r, void *aux_)
{
- struct crosstabs_proc *proc = proc_;
+ const struct tab_table *t = r->table;
+ struct outp_driver *d = r->driver;
+ struct crosstabs_dim_aux *aux = aux_;
int i;
/* Width of a numerical column. */
int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
- if (proc->exclude == MV_NEVER)
+ if (aux->exclude == MV_NEVER)
c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
/* Set width for header columns. */
- if (t->l != 0)
+ if (tab_l (t) != 0)
{
size_t i;
int w;
- w = d->width - c * (t->nc - t->l);
- for (i = 0; i <= t->nc; i++)
- w -= t->wrv[i];
- w /= t->l;
+ w = d->width - c * (tab_nc (t) - tab_l (t));
+ for (i = 0; i <= tab_nc (t); i++)
+ w -= r->wrv[i];
+ w /= tab_l (t);
if (w < d->prop_em_width * 8)
w = d->prop_em_width * 8;
if (w > d->prop_em_width * 15)
w = d->prop_em_width * 15;
- for (i = 0; i < t->l; i++)
- t->w[i] = w;
+ for (i = 0; i < tab_l (t); i++)
+ r->w[i] = w;
}
- for (i = t->l; i < t->nc; i++)
- t->w[i] = c;
+ for (i = tab_l (t); i < tab_nc (t); i++)
+ r->w[i] = c;
+
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = tab_natural_height (r, i);
+}
- for (i = 0; i < t->nr; i++)
- t->h[i] = tab_natural_height (t, d, i);
+static void
+crosstabs_dim_free (void *aux_)
+{
+ struct crosstabs_dim_aux *aux = aux_;
+ free (aux);
}
static bool
struct tab_table *table, int c, int r, unsigned char opt,
const union value *v, const struct variable *var)
{
- struct substring s;
- const struct fmt_spec *print = var_get_print_format (var);
-
const char *label = var_lookup_value_label (var, v);
- if (label)
- {
- tab_text (table, c, r, TAB_LEFT, label);
- return;
- }
-
- s = ss_cstr (data_out_pool (v, dict_get_encoding (proc->dict), print,
- table->container));
- if (proc->exclude == MV_NEVER && var_is_num_missing (var, v->f, MV_USER))
- s.string[s.length++] = 'M';
- while (s.length && *s.string == ' ')
+ if (label != NULL)
+ tab_text (table, c, r, TAB_LEFT, label);
+ else
{
- s.length--;
- s.string++;
+ const struct fmt_spec *print = var_get_print_format (var);
+ if (proc->exclude == MV_NEVER && var_is_value_missing (var, v, MV_USER))
+ {
- char *s = xmalloc (print->w + 2);
- strcpy (&s[print->w], "M");
- tab_text (table, c, r, opt, s + strspn (s, " "));
++ char *s = data_out (v, dict_get_encoding (proc->dict), print);
++ tab_text_format (table, c, r, opt, "%sM", s + strspn (s, " "));
+ free (s);
+ }
+ else
- tab_value (table, c, r, opt, v, print);
++ tab_value (table, c, r, opt, v, proc->dict, print);
}
- tab_raw (table, c, r, opt, &s);
}
/* Draws a line across TABLE at the current row to indicate the most
additionally suffixed with a letter `M'. */
static void
format_cell_entry (struct tab_table *table, int c, int r, double value,
- char suffix, bool mark_missing)
+ char suffix, bool mark_missing, const struct dictionary *dict)
{
const struct fmt_spec f = {FMT_F, 10, 1};
union value v;
- int len = 10;
- char s[16];
- struct substring s;
++ char suffixes[3];
++ int suffix_len;
++ char *s;
v.f = value;
- data_out (&v, &f, s);
- s = ss_cstr (data_out_pool (&v, dict_get_encoding (dict), &f, table->container));
++ s = data_out (&v, dict_get_encoding (dict), &f);
+
- while (*s.string == ' ')
- {
- s.length--;
- s.string++;
- }
++ suffix_len = 0;
if (suffix != 0)
- s[len++] = suffix;
- s.string[s.length++] = suffix;
++ suffixes[suffix_len++] = suffix;
if (mark_missing)
- s[len++] = 'M';
- s[len] = '\0';
- s.string[s.length++] = 'M';
++ suffixes[suffix_len++] = 'M';
++ suffixes[suffix_len] = '\0';
- tab_text (table, c, r, TAB_RIGHT, s + strspn (s, " "));
- tab_raw (table, c, r, TAB_RIGHT, &s);
++ tab_text_format (table, c, r, TAB_RIGHT, "%s%s",
++ s + strspn (s, " "), suffixes);
}
/* Displays the crosstabulation table. */
default:
NOT_REACHED ();
}
- format_cell_entry (table, c, i, v, suffix, mark_missing);
+ format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
}
mp++;
NOT_REACHED ();
}
- format_cell_entry (table, pt->n_cols, 0, v, suffix, mark_missing);
+ format_cell_entry (table, pt->n_cols, 0, v, suffix, mark_missing, proc->dict);
tab_next_row (table);
}
}
NOT_REACHED ();
}
- format_cell_entry (table, c, i, v, suffix, mark_missing);
+ format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
}
last_row = i;
}
struct hsh_table *data; /* Undifferentiated data. */
struct freq_mutable *valid; /* Valid freqs. */
int n_valid; /* Number of total freqs. */
+ const struct dictionary *dict; /* The dict from whence entries in the table
+ come */
struct freq_mutable *missing; /* Missing freqs. */
int n_missing; /* Number of missing freqs. */
hist = freq_tab_to_hist (ft,v);
- histogram_plot_n (hist, var_to_string(v),
+ chart_submit (histogram_chart_create (
+ hist, var_to_string(v),
vf->tab.valid_cases,
d[frq_mean],
d[frq_stddev],
- normal);
+ normal));
- statistic_destroy ((struct statistic *)hist);
+ statistic_destroy (&hist->parent);
}
if ( chart == GFT_PIE)
}
vf = var_attach_aux (v, xmalloc (sizeof *vf), var_dtor_free);
vf->tab.valid = vf->tab.missing = NULL;
+ vf->tab.dict = dataset_dict (ds);
vf->n_groups = 0;
vf->groups = NULL;
vf->width = var_get_width (v);
\f
/* Frequency table display. */
+struct full_dim_aux
+ {
+ bool show_labels;
+ };
+
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-full_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+full_dim (struct tab_rendering *r, void *aux_)
{
- int i = 0;
- int columns = 5;
+ const struct outp_driver *d = r->driver;
+ const struct tab_table *t = r->table;
+ const struct full_dim_aux *aux = aux_;
+ int i;
- if (cmd.labels == FRQ_LABELS)
+ for (i = 0; i < tab_nc (t); i++)
{
- t->w[0] = MIN (tab_natural_width (t, d, 0), d->prop_em_width * 15);
- i = 1;
- columns ++;
+ r->w[i] = tab_natural_width (r, i);
+ if (aux->show_labels && i == 0)
+ r->w[i] = MIN (r->w[i], d->prop_em_width * 15);
+ else
+ r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
}
- for (;i < columns; i++)
- t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = d->font_height;
+}
- for (i = 0; i < t->nr; i++)
- t->h[i] = d->font_height;
+static void
+full_dim_free (void *aux_)
+{
+ struct full_dim_aux *aux = aux_;
+ free (aux);
}
/* Displays a full frequency table for variable V. */
const bool lab = (cmd.labels == FRQ_LABELS);
+ struct full_dim_aux *aux;
+
vf = get_var_freqs (v);
ft = &vf->tab;
n_categories = ft->n_valid + ft->n_missing;
- t = tab_create (5 + lab, n_categories + 3, 0);
+ t = tab_create (5 + lab, n_categories + 3);
tab_headers (t, 0, 0, 2, 0);
- tab_dim (t, full_dim, NULL);
+
+ aux = xmalloc (sizeof *aux);
+ aux->show_labels = lab;
+ tab_dim (t, full_dim, full_dim_free, aux);
if (lab)
tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value Label"));
tab_text (t, 0, r, TAB_LEFT, label);
}
- tab_value (t, 0 + lab, r, TAB_NONE, &f->value, &vf->print);
+ tab_value (t, 0 + lab, r, TAB_NONE, &f->value, ft->dict, &vf->print);
tab_double (t, 1 + lab, r, TAB_NONE, f->count, wfmt);
tab_double (t, 2 + lab, r, TAB_NONE, percent, NULL);
tab_double (t, 3 + lab, r, TAB_NONE, valid_percent, NULL);
tab_text (t, 0, r, TAB_LEFT, label);
}
- tab_value (t, 0 + lab, r, TAB_NONE, &f->value, &vf->print);
+ tab_value (t, 0 + lab, r, TAB_NONE, &f->value, ft->dict, &vf->print);
tab_double (t, 1 + lab, r, TAB_NONE, f->count, wfmt);
tab_double (t, 2 + lab, r, TAB_NONE,
f->count / ft->total_cases * 100.0, NULL);
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-condensed_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+condensed_dim (struct tab_rendering *r, void *aux UNUSED)
{
- int cum_w = MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
- MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
- outp_string_width (d, "000", OUTP_PROPORTIONAL)));
+ struct outp_driver *d = r->driver;
+ const struct tab_table *t = r->table;
+
+ int cum_width = outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL);
+ int zeros_width = outp_string_width (d, "000", OUTP_PROPORTIONAL);
+ int max_width = MAX (cum_width, zeros_width);
int i;
for (i = 0; i < 2; i++)
- t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+ {
+ r->w[i] = tab_natural_width (r, i);
+ r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
+ }
for (i = 2; i < 4; i++)
- t->w[i] = cum_w;
- for (i = 0; i < t->nr; i++)
- t->h[i] = d->font_height;
+ r->w[i] = max_width;
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = d->font_height;
}
/* Display condensed frequency table for variable V. */
vf = get_var_freqs (v);
ft = &vf->tab;
n_categories = ft->n_valid + ft->n_missing;
- t = tab_create (4, n_categories + 2, 0);
+ t = tab_create (4, n_categories + 2);
tab_headers (t, 0, 0, 2, 0);
tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value"));
tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Cum"));
tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
- tab_dim (t, condensed_dim, NULL);
+ tab_dim (t, condensed_dim, NULL, NULL);
r = 2;
for (f = ft->valid; f < ft->missing; f++)
percent = f->count / ft->total_cases * 100.0;
cum_total += f->count / ft->valid_cases * 100.0;
- tab_value (t, 0, r, TAB_NONE, &f->value, &vf->print);
+ tab_value (t, 0, r, TAB_NONE, &f->value, ft->dict, &vf->print);
tab_double (t, 1, r, TAB_NONE, f->count, wfmt);
tab_double (t, 2, r, TAB_NONE, percent, NULL);
tab_double (t, 3, r, TAB_NONE, cum_total, NULL);
}
for (; f < &ft->valid[n_categories]; f++)
{
- tab_value (t, 0, r, TAB_NONE, &f->value, &vf->print);
+ tab_value (t, 0, r, TAB_NONE, &f->value, ft->dict, &vf->print);
tab_double (t, 1, r, TAB_NONE, f->count, wfmt);
tab_double (t, 2, r, TAB_NONE,
f->count / ft->total_cases * 100.0, NULL);
0, 0, 3, r - 1);
tab_hline (t, TAL_2, 0, 3, 2);
tab_title (t, "%s", var_to_string (v));
- tab_columns (t, SOM_COL_DOWN, 1);
+ tab_columns (t, SOM_COL_DOWN);
tab_submit (t);
}
\f
}
calc_stats (v, stat_value);
- t = tab_create (3, n_stats + n_percentiles + 2, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ t = tab_create (3, n_stats + n_percentiles + 2);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_box (t, TAL_1, TAL_1, -1, -1 , 0 , 0 , 2, tab_nr(t) - 1) ;
var_get_print_format (v));
}
- tab_columns (t, SOM_COL_DOWN, 1);
+ tab_columns (t, SOM_COL_DOWN);
if (show_varname)
tab_title (t, "%s", var_to_string (v));
else
double x_min = DBL_MAX;
double x_max = -DBL_MAX;
- struct statistic *hist;
+ struct histogram *hist;
const double bins = 11;
struct hsh_iterator hi;
for( i = 0 ; i < ft->n_valid ; ++i )
{
frq = &ft->valid[i];
- histogram_add ((struct histogram *)hist, frq->value.f, frq->count);
+ histogram_add (hist, frq->value.f, frq->count);
}
- return (struct histogram *)hist;
+ return hist;
}
ds_init_empty (&slices[i].label);
var_append_value_name (var, &frq->value, &slices[i].label);
- slices[i].magnetude = frq->count;
+ slices[i].magnitude = frq->count;
}
return slices;
slices = freq_tab_to_slice_array(frq_tab, var, &n_slices);
- piechart_plot(var_to_string(var), slices, n_slices);
+ chart_submit (piechart_create (var_to_string(var), slices, n_slices));
for (i = 0 ; i < n_slices ; ++i )
- {
- ds_destroy (&slices[i].label);
- }
+ ds_destroy (&slices[i].label);
- free(slices);
+ free (slices);
}
rsq = c->ssm / c->sst;
adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
std_error = sqrt (pspp_linreg_mse (c));
- t = tab_create (n_cols, n_rows, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ t = tab_create (n_cols, n_rows);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
tab_hline (t, TAL_2, 0, n_cols - 1, 1);
tab_vline (t, TAL_2, 2, 0, n_rows - 1);
assert (c != NULL);
n_rows = c->n_coeffs + 3;
- t = tab_create (n_cols, n_rows, 0);
+ t = tab_create (n_cols, n_rows);
tab_headers (t, 2, 0, 1, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
tab_hline (t, TAL_2, 0, n_cols - 1, 1);
tab_vline (t, TAL_2, 2, 0, n_rows - 1);
struct tab_table *t;
assert (c != NULL);
- t = tab_create (n_cols, n_rows, 0);
+ t = tab_create (n_cols, n_rows);
tab_headers (t, 2, 0, 1, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
assert (c != NULL);
n_cols = c->n_indeps + 1 + 2;
n_rows = 2 * (c->n_indeps + 1);
- t = tab_create (n_cols, n_rows, 0);
+ t = tab_create (n_cols, n_rows);
tab_headers (t, 2, 0, 1, 0);
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
tab_hline (t, TAL_2, 0, n_cols - 1, 1);
tab_vline (t, TAL_2, 2, 0, n_rows - 1);
lopts.get_indep_mean_std[i] = 1;
}
models[k] = pspp_linreg_cache_alloc (dep_var, (const struct variable **) indep_vars,
- X->m->size1, X->m->size2);
+ X->m->size1, n_indep);
models[k]->depvar = dep_var;
/*
For large data sets, use QR decomposition.
--- /dev/null
-#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
+ /* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+ #include <config.h>
+
++#include <language/stats/roc.h>
++
+ #include <data/procedure.h>
+ #include <language/lexer/variable-parser.h>
+ #include <language/lexer/value-parser.h>
+ #include <language/command.h>
+ #include <language/lexer/lexer.h>
+
+ #include <data/casegrouper.h>
+ #include <data/casereader.h>
+ #include <data/casewriter.h>
+ #include <data/dictionary.h>
+ #include <data/format.h>
+ #include <math/sort.h>
+ #include <data/subcase.h>
+
+
+ #include <libpspp/misc.h>
+
+ #include <gsl/gsl_cdf.h>
+ #include <output/table.h>
+
-#define CUTPOINT 0
-#define TP 1
-#define FN 2
-#define TN 3
-#define FP 4
-
-
++#include <output/chart.h>
++#include <output/charts/roc-chart.h>
+
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+
+ struct cmd_roc
+ {
+ size_t n_vars;
+ const struct variable **vars;
+ const struct dictionary *dict;
+
+ const struct variable *state_var ;
+ union value state_value;
+
+ /* Plot the roc curve */
+ bool curve;
+ /* Plot the reference line */
+ bool reference;
+
+ double ci;
+
+ bool print_coords;
+ bool print_se;
+ bool bi_neg_exp; /* True iff the bi-negative exponential critieria
+ should be used */
+ enum mv_class exclude;
+
+ bool invert ; /* True iff a smaller test result variable indicates
+ a positive result */
+
+ double pos;
+ double neg;
+ double pos_weighted;
+ double neg_weighted;
+ };
+
+ static int run_roc (struct dataset *ds, struct cmd_roc *roc);
+
+ int
+ cmd_roc (struct lexer *lexer, struct dataset *ds)
+ {
+ struct cmd_roc roc ;
+ const struct dictionary *dict = dataset_dict (ds);
+
+ roc.vars = NULL;
+ roc.n_vars = 0;
+ roc.print_se = false;
+ roc.print_coords = false;
+ roc.exclude = MV_ANY;
+ roc.curve = true;
+ roc.reference = false;
+ roc.ci = 95;
+ roc.bi_neg_exp = false;
+ roc.invert = false;
+ roc.pos = roc.pos_weighted = 0;
+ roc.neg = roc.neg_weighted = 0;
+ roc.dict = dataset_dict (ds);
+
+ if (!parse_variables_const (lexer, dict, &roc.vars, &roc.n_vars,
+ PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
+ goto error;
+
+ if ( ! lex_force_match (lexer, T_BY))
+ {
+ goto error;
+ }
+
+ roc.state_var = parse_variable (lexer, dict);
+
+ if ( !lex_force_match (lexer, '('))
+ {
+ goto error;
+ }
+
+ parse_value (lexer, &roc.state_value, var_get_width (roc.state_var));
+
+
+ if ( !lex_force_match (lexer, ')'))
+ {
+ goto error;
+ }
+
+
+ while (lex_token (lexer) != '.')
+ {
+ lex_match (lexer, '/');
+ if (lex_match_id (lexer, "MISSING"))
+ {
+ lex_match (lexer, '=');
+ while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+ {
+ if (lex_match_id (lexer, "INCLUDE"))
+ {
+ roc.exclude = MV_SYSTEM;
+ }
+ else if (lex_match_id (lexer, "EXCLUDE"))
+ {
+ roc.exclude = MV_ANY;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ }
+ }
+ else if (lex_match_id (lexer, "PLOT"))
+ {
+ lex_match (lexer, '=');
+ if (lex_match_id (lexer, "CURVE"))
+ {
+ roc.curve = true;
+ if (lex_match (lexer, '('))
+ {
+ roc.reference = true;
+ lex_force_match_id (lexer, "REFERENCE");
+ lex_force_match (lexer, ')');
+ }
+ }
+ else if (lex_match_id (lexer, "NONE"))
+ {
+ roc.curve = false;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ }
+ else if (lex_match_id (lexer, "PRINT"))
+ {
+ lex_match (lexer, '=');
+ while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+ {
+ if (lex_match_id (lexer, "SE"))
+ {
+ roc.print_se = true;
+ }
+ else if (lex_match_id (lexer, "COORDINATES"))
+ {
+ roc.print_coords = true;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ }
+ }
+ else if (lex_match_id (lexer, "CRITERIA"))
+ {
+ lex_match (lexer, '=');
+ while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+ {
+ if (lex_match_id (lexer, "CUTOFF"))
+ {
+ lex_force_match (lexer, '(');
+ if (lex_match_id (lexer, "INCLUDE"))
+ {
+ roc.exclude = MV_SYSTEM;
+ }
+ else if (lex_match_id (lexer, "EXCLUDE"))
+ {
+ roc.exclude = MV_USER | MV_SYSTEM;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ lex_force_match (lexer, ')');
+ }
+ else if (lex_match_id (lexer, "TESTPOS"))
+ {
+ lex_force_match (lexer, '(');
+ if (lex_match_id (lexer, "LARGE"))
+ {
+ roc.invert = false;
+ }
+ else if (lex_match_id (lexer, "SMALL"))
+ {
+ roc.invert = true;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ lex_force_match (lexer, ')');
+ }
+ else if (lex_match_id (lexer, "CI"))
+ {
+ lex_force_match (lexer, '(');
+ lex_force_num (lexer);
+ roc.ci = lex_number (lexer);
+ lex_get (lexer);
+ lex_force_match (lexer, ')');
+ }
+ else if (lex_match_id (lexer, "DISTRIBUTION"))
+ {
+ lex_force_match (lexer, '(');
+ if (lex_match_id (lexer, "FREE"))
+ {
+ roc.bi_neg_exp = false;
+ }
+ else if (lex_match_id (lexer, "NEGEXPO"))
+ {
+ roc.bi_neg_exp = true;
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ lex_force_match (lexer, ')');
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ lex_error (lexer, NULL);
+ break;
+ }
+ }
+
+ if ( ! run_roc (ds, &roc))
+ goto error;
+
+ free (roc.vars);
+ return CMD_SUCCESS;
+
+ error:
+ free (roc.vars);
+ return CMD_FAILURE;
+ }
+
+
+
+
+ static void
+ do_roc (struct cmd_roc *roc, struct casereader *group, struct dictionary *dict);
+
+
+ static int
+ run_roc (struct dataset *ds, struct cmd_roc *roc)
+ {
+ struct dictionary *dict = dataset_dict (ds);
+ bool ok;
+ struct casereader *group;
+
+ struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), dict);
+ while (casegrouper_get_next_group (grouper, &group))
+ {
+ do_roc (roc, group, dataset_dict (ds));
+ }
+ ok = casegrouper_destroy (grouper);
+ ok = proc_commit (ds) && ok;
+
+ return ok;
+ }
+
+ #if 0
+ static void
+ dump_casereader (struct casereader *reader)
+ {
+ struct ccase *c;
+ struct casereader *r = casereader_clone (reader);
+
+ for ( ; (c = casereader_read (r) ); case_unref (c))
+ {
+ int i;
+ for (i = 0 ; i < case_get_value_cnt (c); ++i)
+ {
+ printf ("%g ", case_data_idx (c, i)->f);
+ }
+ printf ("\n");
+ }
+
+ casereader_destroy (r);
+ }
+ #endif
+
+
+ /*
+ Return true iff the state variable indicates that C has positive actual state.
+
+ As a side effect, this function also accumulates the roc->{pos,neg} and
+ roc->{pos,neg}_weighted counts.
+ */
+ static bool
+ match_positives (const struct ccase *c, void *aux)
+ {
+ struct cmd_roc *roc = aux;
+ const struct variable *wv = dict_get_weight (roc->dict);
+ const double weight = wv ? case_data (c, wv)->f : 1.0;
+
+ const bool positive =
+ ( 0 == value_compare_3way (case_data (c, roc->state_var), &roc->state_value,
+ var_get_width (roc->state_var)));
+
+ if ( positive )
+ {
+ roc->pos++;
+ roc->pos_weighted += weight;
+ }
+ else
+ {
+ roc->neg++;
+ roc->neg_weighted += weight;
+ }
+
+ return positive;
+ }
+
+
+ #define VALUE 0
+ #define N_EQ 1
+ #define N_PRED 2
+
+ /* Some intermediate state for calculating the cutpoints and the
+ standard error values */
+ struct roc_state
+ {
+ double auc; /* Area under the curve */
+
+ double n1; /* total weight of positives */
+ double n2; /* total weight of negatives */
+
+ /* intermediates for standard error */
+ double q1hat;
+ double q2hat;
+
+ /* intermediates for cutpoints */
+ struct casewriter *cutpoint_wtr;
+ struct casereader *cutpoint_rdr;
+ double prev_result;
+ double min;
+ double max;
+ };
+
- const double cp = case_data_idx (cpc, CUTPOINT)->f;
+ /*
+ Return a new casereader based upon CUTPOINT_RDR.
+ The number of "positive" cases are placed into
+ the position TRUE_INDEX, and the number of "negative" cases
+ into FALSE_INDEX.
+ POS_COND and RESULT determine the semantics of what is
+ "positive".
+ WEIGHT is the value of a single count.
+ */
+ static struct casereader *
+ accumulate_counts (struct casereader *cutpoint_rdr,
+ double result, double weight,
+ bool (*pos_cond) (double, double),
+ int true_index, int false_index)
+ {
+ const struct caseproto *proto = casereader_get_proto (cutpoint_rdr);
+ struct casewriter *w =
+ autopaging_writer_create (proto);
+ struct casereader *r = casereader_clone (cutpoint_rdr);
+ struct ccase *cpc;
+ double prev_cp = SYSMIS;
+
+ for ( ; (cpc = casereader_read (r) ); case_unref (cpc))
+ {
+ struct ccase *new_case;
- TP, FN);
++ const double cp = case_data_idx (cpc, ROC_CUTPOINT)->f;
+
+ assert (cp != SYSMIS);
+
+ /* We don't want duplicates here */
+ if ( cp == prev_cp )
+ continue;
+
+ new_case = case_clone (cpc);
+
+ if ( pos_cond (result, cp))
+ case_data_rw_idx (new_case, true_index)->f += weight;
+ else
+ case_data_rw_idx (new_case, false_index)->f += weight;
+
+ prev_cp = cp;
+
+ casewriter_write (w, new_case);
+ }
+ casereader_destroy (r);
+
+ return casewriter_make_reader (w);
+ }
+
+
+
+ static void output_roc (struct roc_state *rs, const struct cmd_roc *roc);
+
+ /*
+ This function does 3 things:
+
+ 1. Counts the number of cases which are equal to every other case in READER,
+ and those cases for which the relationship between it and every other case
+ satifies PRED (normally either > or <). VAR is variable defining a case's value
+ for this purpose.
+
+ 2. Counts the number of true and false cases in reader, and populates
+ CUTPOINT_RDR accordingly. TRUE_INDEX and FALSE_INDEX are the indices
+ which receive these values. POS_COND is the condition defining true
+ and false.
+
+ 3. CC is filled with the cumulative weight of all cases of READER.
+ */
+ static struct casereader *
+ process_group (const struct variable *var, struct casereader *reader,
+ bool (*pred) (double, double),
+ const struct dictionary *dict,
+ double *cc,
+ struct casereader **cutpoint_rdr,
+ bool (*pos_cond) (double, double),
+ int true_index,
+ int false_index)
+ {
+ const struct variable *w = dict_get_weight (dict);
+
+ struct casereader *r1 =
+ casereader_create_distinct (sort_execute_1var (reader, var), var, w);
+
+ const int weight_idx = w ? var_get_case_index (w) :
+ caseproto_get_n_widths (casereader_get_proto (r1)) - 1;
+
+ struct ccase *c1;
+
+ struct casereader *rclone = casereader_clone (r1);
+ struct casewriter *wtr;
+ struct caseproto *proto = caseproto_create ();
+
+ proto = caseproto_add_width (proto, 0);
+ proto = caseproto_add_width (proto, 0);
+ proto = caseproto_add_width (proto, 0);
+
+ wtr = autopaging_writer_create (proto);
+
+ *cc = 0;
+
+ for ( ; (c1 = casereader_read (r1) ); case_unref (c1))
+ {
+ struct ccase *new_case = case_create (proto);
+ struct ccase *c2;
+ struct casereader *r2 = casereader_clone (rclone);
+
+ const double weight1 = case_data_idx (c1, weight_idx)->f;
+ const double d1 = case_data (c1, var)->f;
+ double n_eq = 0.0;
+ double n_pred = 0.0;
+
+ *cutpoint_rdr = accumulate_counts (*cutpoint_rdr, d1, weight1,
+ pos_cond,
+ true_index, false_index);
+
+ *cc += weight1;
+
+ for ( ; (c2 = casereader_read (r2) ); case_unref (c2))
+ {
+ const double d2 = case_data (c2, var)->f;
+ const double weight2 = case_data_idx (c2, weight_idx)->f;
+
+ if ( d1 == d2 )
+ {
+ n_eq += weight2;
+ continue;
+ }
+ else if ( pred (d2, d1))
+ {
+ n_pred += weight2;
+ }
+ }
+
+ case_data_rw_idx (new_case, VALUE)->f = d1;
+ case_data_rw_idx (new_case, N_EQ)->f = n_eq;
+ case_data_rw_idx (new_case, N_PRED)->f = n_pred;
+
+ casewriter_write (wtr, new_case);
+
+ casereader_destroy (r2);
+ }
+
+ casereader_destroy (r1);
+ casereader_destroy (rclone);
+
+ return casewriter_make_reader (wtr);
+ }
+
+ /* Some more indeces into case data */
+ #define N_POS_EQ 1 /* number of positive cases with values equal to n */
+ #define N_POS_GT 2 /* number of postive cases with values greater than n */
+ #define N_NEG_EQ 3 /* number of negative cases with values equal to n */
+ #define N_NEG_LT 4 /* number of negative cases with values less than n */
+
+ static bool
+ gt (double d1, double d2)
+ {
+ return d1 > d2;
+ }
+
+
+ static bool
+ ge (double d1, double d2)
+ {
+ return d1 > d2;
+ }
+
+ static bool
+ lt (double d1, double d2)
+ {
+ return d1 < d2;
+ }
+
+
+ /*
+ Return a casereader with width 3,
+ populated with cases based upon READER.
+ The cases will have the values:
+ (N, number of cases equal to N, number of cases greater than N)
+ As a side effect, update RS->n1 with the number of positive cases.
+ */
+ static struct casereader *
+ process_positive_group (const struct variable *var, struct casereader *reader,
+ const struct dictionary *dict,
+ struct roc_state *rs)
+ {
+ return process_group (var, reader, gt, dict, &rs->n1,
+ &rs->cutpoint_rdr,
+ ge,
- TN, FP);
++ ROC_TP, ROC_FN);
+ }
+
+ /*
+ Return a casereader with width 3,
+ populated with cases based upon READER.
+ The cases will have the values:
+ (N, number of cases equal to N, number of cases less than N)
+ As a side effect, update RS->n2 with the number of negative cases.
+ */
+ static struct casereader *
+ process_negative_group (const struct variable *var, struct casereader *reader,
+ const struct dictionary *dict,
+ struct roc_state *rs)
+ {
+ return process_group (var, reader, lt, dict, &rs->n2,
+ &rs->cutpoint_rdr,
+ lt,
- case_data_rw_idx (cc, CUTPOINT)->f = cutpoint;
- case_data_rw_idx (cc, TP)->f = 0;
- case_data_rw_idx (cc, FN)->f = 0;
- case_data_rw_idx (cc, TN)->f = 0;
- case_data_rw_idx (cc, FP)->f = 0;
++ ROC_TN, ROC_FP);
+ }
+
+
+
+
+ static void
+ append_cutpoint (struct casewriter *writer, double cutpoint)
+ {
+ struct ccase *cc = case_create (casewriter_get_proto (writer));
+
- be created with width 5, ready to take the values (cutpoint, TP, FN, TN, FP), and the
++ case_data_rw_idx (cc, ROC_CUTPOINT)->f = cutpoint;
++ case_data_rw_idx (cc, ROC_TP)->f = 0;
++ case_data_rw_idx (cc, ROC_FN)->f = 0;
++ case_data_rw_idx (cc, ROC_TN)->f = 0;
++ case_data_rw_idx (cc, ROC_FP)->f = 0;
+
+ casewriter_write (writer, cc);
+ }
+
+
+ /*
+ Create and initialise the rs[x].cutpoint_rdr casereaders. That is, the readers will
- However on exit from this function, only CUTPOINT entries will be set to their final
++ be created with width 5, ready to take the values (cutpoint, ROC_TP, ROC_FN, ROC_TN, ROC_FP), and the
+ reader will be populated with its final number of cases.
- subcase_init (&ordering, CUTPOINT, 0, SC_ASCEND);
++ However on exit from this function, only ROC_CUTPOINT entries will be set to their final
+ value. The other entries will be initialised to zero.
+ */
+ static void
+ prepare_cutpoints (struct cmd_roc *roc, struct roc_state *rs, struct casereader *input)
+ {
+ int i;
+ struct casereader *r = casereader_clone (input);
+ struct ccase *c;
+ struct caseproto *proto = caseproto_create ();
+
+ struct subcase ordering;
- proto = caseproto_add_width (proto, 0); /* TP */
- proto = caseproto_add_width (proto, 0); /* FN */
- proto = caseproto_add_width (proto, 0); /* TN */
- proto = caseproto_add_width (proto, 0); /* FP */
++ subcase_init (&ordering, ROC_CUTPOINT, 0, SC_ASCEND);
+
+ proto = caseproto_add_width (proto, 0); /* cutpoint */
- struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
++ proto = caseproto_add_width (proto, 0); /* ROC_TP */
++ proto = caseproto_add_width (proto, 0); /* ROC_FN */
++ proto = caseproto_add_width (proto, 0); /* ROC_TN */
++ proto = caseproto_add_width (proto, 0); /* ROC_FP */
+
+ for (i = 0 ; i < roc->n_vars; ++i)
+ {
+ rs[i].cutpoint_wtr = sort_create_writer (&ordering, proto);
+ rs[i].prev_result = SYSMIS;
+ rs[i].max = -DBL_MAX;
+ rs[i].min = DBL_MAX;
+ }
+
+ for (; (c = casereader_read (r)) != NULL; case_unref (c))
+ {
+ for (i = 0 ; i < roc->n_vars; ++i)
+ {
+ const union value *v = case_data (c, roc->vars[i]);
+ const double result = v->f;
+
+ if ( mv_is_value_missing (var_get_missing_values (roc->vars[i]), v, roc->exclude))
+ continue;
+
+ minimize (&rs[i].min, result);
+ maximize (&rs[i].max, result);
+
+ if ( rs[i].prev_result != SYSMIS && rs[i].prev_result != result )
+ {
+ const double mean = (result + rs[i].prev_result ) / 2.0;
+ append_cutpoint (rs[i].cutpoint_wtr, mean);
+ }
+
+ rs[i].prev_result = result;
+ }
+ }
+ casereader_destroy (r);
+
+
+ /* Append the min and max cutpoints */
+ for (i = 0 ; i < roc->n_vars; ++i)
+ {
+ append_cutpoint (rs[i].cutpoint_wtr, rs[i].min - 1);
+ append_cutpoint (rs[i].cutpoint_wtr, rs[i].max + 1);
+
+ rs[i].cutpoint_rdr = casewriter_make_reader (rs[i].cutpoint_wtr);
+ }
+ }
+
+ static void
+ do_roc (struct cmd_roc *roc, struct casereader *reader, struct dictionary *dict)
+ {
+ int i;
+
+ struct roc_state *rs = xcalloc (roc->n_vars, sizeof *rs);
+
+ struct casereader *negatives = NULL;
+ struct casereader *positives = NULL;
+
+ struct caseproto *n_proto = caseproto_create ();
+
+ struct subcase up_ordering;
+ struct subcase down_ordering;
+
+ struct casewriter *neg_wtr = NULL;
+
+ struct casereader *input = casereader_create_filter_missing (reader,
+ roc->vars, roc->n_vars,
+ roc->exclude,
+ NULL,
+ NULL);
+
+ input = casereader_create_filter_missing (input,
+ &roc->state_var, 1,
+ roc->exclude,
+ NULL,
+ NULL);
+
+ neg_wtr = autopaging_writer_create (casereader_get_proto (input));
+
+ prepare_cutpoints (roc, rs, input);
+
+
+ /* Separate the positive actual state cases from the negative ones */
+ positives =
+ casereader_create_filter_func (input,
+ match_positives,
+ NULL,
+ roc,
+ neg_wtr);
+
+ n_proto = caseproto_create ();
+
+ n_proto = caseproto_add_width (n_proto, 0);
+ n_proto = caseproto_add_width (n_proto, 0);
+ n_proto = caseproto_add_width (n_proto, 0);
+ n_proto = caseproto_add_width (n_proto, 0);
+ n_proto = caseproto_add_width (n_proto, 0);
+
+ subcase_init (&up_ordering, VALUE, 0, SC_ASCEND);
+ subcase_init (&down_ordering, VALUE, 0, SC_DESCEND);
+
+ for (i = 0 ; i < roc->n_vars; ++i)
+ {
+ struct casewriter *w = NULL;
+ struct casereader *r = NULL;
+
+ struct ccase *c;
+
+ struct ccase *cpos;
+ struct casereader *n_neg ;
+ const struct variable *var = roc->vars[i];
+
+ struct casereader *neg ;
+ struct casereader *pos = casereader_clone (positives);
+
+
+ struct casereader *n_pos =
+ process_positive_group (var, pos, dict, &rs[i]);
+
+ if ( negatives == NULL)
+ {
+ negatives = casewriter_make_reader (neg_wtr);
+ }
+
+ neg = casereader_clone (negatives);
+
+ n_neg = process_negative_group (var, neg, dict, &rs[i]);
+
+
+ /* Merge the n_pos and n_neg casereaders */
+ w = sort_create_writer (&up_ordering, n_proto);
+ for ( ; (cpos = casereader_read (n_pos) ); case_unref (cpos))
+ {
+ struct ccase *pos_case = case_create (n_proto);
+ struct ccase *cneg;
+ const double jpos = case_data_idx (cpos, VALUE)->f;
+
+ while ((cneg = casereader_read (n_neg)))
+ {
+ struct ccase *nc = case_create (n_proto);
+
+ const double jneg = case_data_idx (cneg, VALUE)->f;
+
+ case_data_rw_idx (nc, VALUE)->f = jneg;
+ case_data_rw_idx (nc, N_POS_EQ)->f = 0;
+
+ case_data_rw_idx (nc, N_POS_GT)->f = SYSMIS;
+
+ *case_data_rw_idx (nc, N_NEG_EQ) = *case_data_idx (cneg, N_EQ);
+ *case_data_rw_idx (nc, N_NEG_LT) = *case_data_idx (cneg, N_PRED);
+
+ casewriter_write (w, nc);
+
+ case_unref (cneg);
+ if ( jneg > jpos)
+ break;
+ }
+
+ case_data_rw_idx (pos_case, VALUE)->f = jpos;
+ *case_data_rw_idx (pos_case, N_POS_EQ) = *case_data_idx (cpos, N_EQ);
+ *case_data_rw_idx (pos_case, N_POS_GT) = *case_data_idx (cpos, N_PRED);
+ case_data_rw_idx (pos_case, N_NEG_EQ)->f = 0;
+ case_data_rw_idx (pos_case, N_NEG_LT)->f = SYSMIS;
+
+ casewriter_write (w, pos_case);
+ }
+
+ /* These aren't used anymore */
+ #undef N_EQ
+ #undef N_PRED
+
+ r = casewriter_make_reader (w);
+
+ /* Propagate the N_POS_GT values from the positive cases
+ to the negative ones */
+ {
+ double prev_pos_gt = rs[i].n1;
+ w = sort_create_writer (&down_ordering, n_proto);
+
+ for ( ; (c = casereader_read (r) ); case_unref (c))
+ {
+ double n_pos_gt = case_data_idx (c, N_POS_GT)->f;
+ struct ccase *nc = case_clone (c);
+
+ if ( n_pos_gt == SYSMIS)
+ {
+ n_pos_gt = prev_pos_gt;
+ case_data_rw_idx (nc, N_POS_GT)->f = n_pos_gt;
+ }
+
+ casewriter_write (w, nc);
+ prev_pos_gt = n_pos_gt;
+ }
+
+ r = casewriter_make_reader (w);
+ }
+
+ /* Propagate the N_NEG_LT values from the negative cases
+ to the positive ones */
+ {
+ double prev_neg_lt = rs[i].n2;
+ w = sort_create_writer (&up_ordering, n_proto);
+
+ for ( ; (c = casereader_read (r) ); case_unref (c))
+ {
+ double n_neg_lt = case_data_idx (c, N_NEG_LT)->f;
+ struct ccase *nc = case_clone (c);
+
+ if ( n_neg_lt == SYSMIS)
+ {
+ n_neg_lt = prev_neg_lt;
+ case_data_rw_idx (nc, N_NEG_LT)->f = n_neg_lt;
+ }
+
+ casewriter_write (w, nc);
+ prev_neg_lt = n_neg_lt;
+ }
+
+ r = casewriter_make_reader (w);
+ }
+
+ {
+ struct ccase *prev_case = NULL;
+ for ( ; (c = casereader_read (r) ); case_unref (c))
+ {
+ const struct ccase *next_case = casereader_peek (r, 0);
+
+ const double j = case_data_idx (c, VALUE)->f;
+ double n_pos_eq = case_data_idx (c, N_POS_EQ)->f;
+ double n_pos_gt = case_data_idx (c, N_POS_GT)->f;
+ double n_neg_eq = case_data_idx (c, N_NEG_EQ)->f;
+ double n_neg_lt = case_data_idx (c, N_NEG_LT)->f;
+
+ if ( prev_case && j == case_data_idx (prev_case, VALUE)->f)
+ {
+ if ( 0 == case_data_idx (c, N_POS_EQ)->f)
+ {
+ n_pos_eq = case_data_idx (prev_case, N_POS_EQ)->f;
+ n_pos_gt = case_data_idx (prev_case, N_POS_GT)->f;
+ }
+
+ if ( 0 == case_data_idx (c, N_NEG_EQ)->f)
+ {
+ n_neg_eq = case_data_idx (prev_case, N_NEG_EQ)->f;
+ n_neg_lt = case_data_idx (prev_case, N_NEG_LT)->f;
+ }
+ }
+
+ if ( NULL == next_case || j != case_data_idx (next_case, VALUE)->f)
+ {
+ rs[i].auc += n_pos_gt * n_neg_eq + (n_pos_eq * n_neg_eq) / 2.0;
+
+ rs[i].q1hat +=
+ n_neg_eq * ( pow2 (n_pos_gt) + n_pos_gt * n_pos_eq + pow2 (n_pos_eq) / 3.0);
+ rs[i].q2hat +=
+ n_pos_eq * ( pow2 (n_neg_lt) + n_neg_lt * n_neg_eq + pow2 (n_neg_eq) / 3.0);
+
+ }
+
+ case_unref (prev_case);
+ prev_case = case_clone (c);
+ }
+
+ rs[i].auc /= rs[i].n1 * rs[i].n2;
+ if ( roc->invert )
+ rs[i].auc = 1 - rs[i].auc;
+
+ if ( roc->bi_neg_exp )
+ {
+ rs[i].q1hat = rs[i].auc / ( 2 - rs[i].auc);
+ rs[i].q2hat = 2 * pow2 (rs[i].auc) / ( 1 + rs[i].auc);
+ }
+ else
+ {
+ rs[i].q1hat /= rs[i].n2 * pow2 (rs[i].n1);
+ rs[i].q2hat /= rs[i].n1 * pow2 (rs[i].n2);
+ }
+ }
+ }
+
+ casereader_destroy (positives);
+ casereader_destroy (negatives);
+
+ output_roc (rs, roc);
+
+ free (rs);
+ }
+
+ static void
+ show_auc (struct roc_state *rs, const struct cmd_roc *roc)
+ {
+ int i;
+ const int n_fields = roc->print_se ? 5 : 1;
+ const int n_cols = roc->n_vars > 1 ? n_fields + 1: n_fields;
+ const int n_rows = 2 + roc->n_vars;
- tab_dim (tbl, tab_natural_dimensions, NULL);
++ struct tab_table *tbl = tab_create (n_cols, n_rows);
+
+ if ( roc->n_vars > 1)
+ tab_title (tbl, _("Area Under the Curve"));
+ else
+ tab_title (tbl, _("Area Under the Curve (%s)"), var_to_string (roc->vars[0]));
+
+ tab_headers (tbl, n_cols - n_fields, 0, 1, 0);
+
- struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
++ tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
+
+ tab_text (tbl, n_cols - n_fields, 1, TAT_TITLE, _("Area"));
+
+ tab_hline (tbl, TAL_2, 0, n_cols - 1, 2);
+
+ tab_box (tbl,
+ TAL_2, TAL_2,
+ -1, TAL_1,
+ 0, 0,
+ n_cols - 1,
+ n_rows - 1);
+
+ if ( roc->print_se )
+ {
+ tab_text (tbl, n_cols - 4, 1, TAT_TITLE, _("Std. Error"));
+ tab_text (tbl, n_cols - 3, 1, TAT_TITLE, _("Asymptotic Sig."));
+
+ tab_text (tbl, n_cols - 2, 1, TAT_TITLE, _("Lower Bound"));
+ tab_text (tbl, n_cols - 1, 1, TAT_TITLE, _("Upper Bound"));
+
+ tab_joint_text_format (tbl, n_cols - 2, 0, 4, 0,
+ TAT_TITLE | TAB_CENTER,
+ _("Asymp. %g%% Confidence Interval"), roc->ci);
+ tab_vline (tbl, 0, n_cols - 1, 0, 0);
+ tab_hline (tbl, TAL_1, n_cols - 2, n_cols - 1, 1);
+ }
+
+ if ( roc->n_vars > 1)
+ tab_text (tbl, 0, 1, TAT_TITLE, _("Variable under test"));
+
+ if ( roc->n_vars > 1)
+ tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+
+ for ( i = 0 ; i < roc->n_vars ; ++i )
+ {
+ tab_text (tbl, 0, 2 + i, TAT_TITLE, var_to_string (roc->vars[i]));
+
+ tab_double (tbl, n_cols - n_fields, 2 + i, 0, rs[i].auc, NULL);
+
+ if ( roc->print_se )
+ {
+ double se ;
+ const double sd_0_5 = sqrt ((rs[i].n1 + rs[i].n2 + 1) /
+ (12 * rs[i].n1 * rs[i].n2));
+ double ci ;
+ double yy ;
+
+ se = rs[i].auc * (1 - rs[i].auc) + (rs[i].n1 - 1) * (rs[i].q1hat - pow2 (rs[i].auc)) +
+ (rs[i].n2 - 1) * (rs[i].q2hat - pow2 (rs[i].auc));
+
+ se /= rs[i].n1 * rs[i].n2;
+
+ se = sqrt (se);
+
+ tab_double (tbl, n_cols - 4, 2 + i, 0,
+ se,
+ NULL);
+
+ ci = 1 - roc->ci / 100.0;
+ yy = gsl_cdf_gaussian_Qinv (ci, se) ;
+
+ tab_double (tbl, n_cols - 2, 2 + i, 0,
+ rs[i].auc - yy,
+ NULL);
+
+ tab_double (tbl, n_cols - 1, 2 + i, 0,
+ rs[i].auc + yy,
+ NULL);
+
+ tab_double (tbl, n_cols - 3, 2 + i, 0,
+ 2.0 * gsl_cdf_ugaussian_Q (fabs ((rs[i].auc - 0.5 ) / sd_0_5)),
+ NULL);
+ }
+ }
+
+ tab_submit (tbl);
+ }
+
+
+ static void
+ show_summary (const struct cmd_roc *roc)
+ {
+ const int n_cols = 3;
+ const int n_rows = 4;
- tab_dim (tbl, tab_natural_dimensions, NULL);
++ struct tab_table *tbl = tab_create (n_cols, n_rows);
+
+ tab_title (tbl, _("Case Summary"));
+
+ tab_headers (tbl, 1, 0, 2, 0);
+
- tbl = tab_create (n_cols, n_rows, 0);
++ tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
+
+ tab_box (tbl,
+ TAL_2, TAL_2,
+ -1, -1,
+ 0, 0,
+ n_cols - 1,
+ n_rows - 1);
+
+ tab_hline (tbl, TAL_2, 0, n_cols - 1, 2);
+ tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+
+ tab_hline (tbl, TAL_2, 1, n_cols - 1, 1);
+ tab_vline (tbl, TAL_1, 2, 1, n_rows - 1);
+
+
+ tab_text (tbl, 0, 1, TAT_TITLE | TAB_LEFT, var_to_string (roc->state_var));
+ tab_text (tbl, 1, 1, TAT_TITLE, _("Unweighted"));
+ tab_text (tbl, 2, 1, TAT_TITLE, _("Weighted"));
+
+ tab_joint_text (tbl, 1, 0, 2, 0,
+ TAT_TITLE | TAB_CENTER,
+ _("Valid N (listwise)"));
+
+
+ tab_text (tbl, 0, 2, TAB_LEFT, _("Positive"));
+ tab_text (tbl, 0, 3, TAB_LEFT, _("Negative"));
+
+
+ tab_double (tbl, 1, 2, 0, roc->pos, &F_8_0);
+ tab_double (tbl, 1, 3, 0, roc->neg, &F_8_0);
+
+ tab_double (tbl, 2, 2, 0, roc->pos_weighted, 0);
+ tab_double (tbl, 2, 3, 0, roc->neg_weighted, 0);
+
+ tab_submit (tbl);
+ }
+
+
+ static void
+ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
+ {
+ int x = 1;
+ int i;
+ const int n_cols = roc->n_vars > 1 ? 4 : 3;
+ int n_rows = 1;
+ struct tab_table *tbl ;
+
+ for (i = 0; i < roc->n_vars; ++i)
+ n_rows += casereader_count_cases (rs[i].cutpoint_rdr);
+
- tab_dim (tbl, tab_natural_dimensions, NULL);
++ tbl = tab_create (n_cols, n_rows);
+
+ if ( roc->n_vars > 1)
+ tab_title (tbl, _("Coordinates of the Curve"));
+ else
+ tab_title (tbl, _("Coordinates of the Curve (%s)"), var_to_string (roc->vars[0]));
+
+
+ tab_headers (tbl, 1, 0, 1, 0);
+
- const double se = case_data_idx (cc, TP)->f /
++ tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
+
+ tab_hline (tbl, TAL_2, 0, n_cols - 1, 1);
+
+ if ( roc->n_vars > 1)
+ tab_text (tbl, 0, 0, TAT_TITLE, _("Test variable"));
+
+ tab_text (tbl, n_cols - 3, 0, TAT_TITLE, _("Positive if greater than or equal to"));
+ tab_text (tbl, n_cols - 2, 0, TAT_TITLE, _("Sensitivity"));
+ tab_text (tbl, n_cols - 1, 0, TAT_TITLE, _("1 - Specificity"));
+
+ tab_box (tbl,
+ TAL_2, TAL_2,
+ -1, TAL_1,
+ 0, 0,
+ n_cols - 1,
+ n_rows - 1);
+
+ if ( roc->n_vars > 1)
+ tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+ for (i = 0; i < roc->n_vars; ++i)
+ {
+ struct ccase *cc;
+ struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
+
+ if ( roc->n_vars > 1)
+ tab_text (tbl, 0, x, TAT_TITLE, var_to_string (roc->vars[i]));
+
+ if ( i > 0)
+ tab_hline (tbl, TAL_1, 0, n_cols - 1, x);
+
+
+ for (; (cc = casereader_read (r)) != NULL;
+ case_unref (cc), x++)
+ {
- case_data_idx (cc, TP)->f
++ const double se = case_data_idx (cc, ROC_TP)->f /
+ (
- case_data_idx (cc, FN)->f
++ case_data_idx (cc, ROC_TP)->f
+ +
- const double sp = case_data_idx (cc, TN)->f /
++ case_data_idx (cc, ROC_FN)->f
+ );
+
- case_data_idx (cc, TN)->f
++ const double sp = case_data_idx (cc, ROC_TN)->f /
+ (
- case_data_idx (cc, FP)->f
++ case_data_idx (cc, ROC_TN)->f
+ +
- tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, CUTPOINT)->f,
++ case_data_idx (cc, ROC_FP)->f
+ );
+
-static void
-draw_roc (struct roc_state *rs, const struct cmd_roc *roc)
-{
- int i;
-
- struct chart *roc_chart = chart_create ();
-
- chart_write_title (roc_chart, _("ROC Curve"));
- chart_write_xlabel (roc_chart, _("1 - Specificity"));
- chart_write_ylabel (roc_chart, _("Sensitivity"));
-
- chart_write_xscale (roc_chart, 0, 1, 5);
- chart_write_yscale (roc_chart, 0, 1, 5);
-
- if ( roc->reference )
- {
- chart_line (roc_chart, 1.0, 0,
- 0.0, 1.0,
- CHART_DIM_X);
- }
-
- for (i = 0; i < roc->n_vars; ++i)
- {
- struct ccase *cc;
- struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
-
- chart_vector_start (roc_chart, var_get_name (roc->vars[i]));
- for (; (cc = casereader_read (r)) != NULL;
- case_unref (cc))
- {
- double se = case_data_idx (cc, TP)->f;
- double sp = case_data_idx (cc, TN)->f;
-
- se /= case_data_idx (cc, FN)->f +
- case_data_idx (cc, TP)->f ;
-
- sp /= case_data_idx (cc, TN)->f +
- case_data_idx (cc, FP)->f ;
-
- chart_vector (roc_chart, 1 - sp, se);
- }
- chart_vector_end (roc_chart);
- casereader_destroy (r);
- }
-
- chart_write_legend (roc_chart);
-
- chart_submit (roc_chart);
-}
-
-
++ tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, ROC_CUTPOINT)->f,
+ var_get_print_format (roc->vars[i]));
+
+ tab_double (tbl, n_cols - 2, x, 0, se, NULL);
+ tab_double (tbl, n_cols - 1, x, 0, 1 - sp, NULL);
+ }
+
+ casereader_destroy (r);
+ }
+
+ tab_submit (tbl);
+ }
+
+
- draw_roc (rs, roc);
+ static void
+ output_roc (struct roc_state *rs, const struct cmd_roc *roc)
+ {
+ show_summary (roc);
+
+ if ( roc->curve )
-
++ {
++ struct roc_chart *rc;
++ size_t i;
++
++ rc = roc_chart_create (roc->reference);
++ for (i = 0; i < roc->n_vars; i++)
++ roc_chart_add_var (rc, var_get_name (roc->vars[i]),
++ rs[i].cutpoint_rdr);
++ chart_submit (roc_chart_get_chart (rc));
++ }
+
+ show_auc (rs, roc);
+
+ if ( roc->print_coords )
+ show_coords (rs, roc);
+ }
+
--- /dev/null
--- /dev/null
++/* PSPP - a program for statistical analysis.
++ Copyright (C) 2009 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ 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, see <http://www.gnu.org/licenses/>. */
++
++#ifndef LANGUAGE_STATS_ROC_H
++#define LANGUAGE_STATS_ROC_H 1
++
++/* These are case indexes into the cutpoint case readers for ROC
++ output, used by roc.c and roc-chart.c. */
++#define ROC_CUTPOINT 0
++#define ROC_TP 1
++#define ROC_FN 2
++#define ROC_TN 3
++#define ROC_FP 4
++
++#endif /* language/stats/roc.h */
ssbox_base_init (struct ssbox *this, int cols, int rows)
{
this->finalize = ssbox_base_finalize;
- this->t = tab_create (cols, rows, 0);
+ this->t = tab_create (cols, rows);
- tab_columns (this->t, SOM_COL_DOWN, 1);
+ tab_columns (this->t, SOM_COL_DOWN);
tab_headers (this->t, 0, 0, 1, 0);
tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
tab_hline (this->t, TAL_2, 0, cols- 1, 1);
- tab_dim (this->t, tab_natural_dimensions, NULL);
+ tab_dim (this->t, tab_natural_dimensions, NULL, NULL);
}
\f
/* ssbox implementations. */
/* Degrees of freedom */
tab_double (trb->t, 8, i + 3, TAB_RIGHT, df, &proc->weight_format);
- p = gsl_cdf_tdist_P (t, df);
- q = gsl_cdf_tdist_P (t, df);
+ p = gsl_cdf_tdist_P (t,df);
+ q = gsl_cdf_tdist_Q (t,df);
tab_double (trb->t, 9, i + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p), NULL);
}
const size_t rows = 3 + data_rows;
self->finalize = trbox_base_finalize;
- self->t = tab_create (cols, rows, 0);
+ self->t = tab_create (cols, rows);
tab_headers (self->t, 0, 0, 3, 0);
tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
tab_hline (self->t, TAL_2, 0, cols- 1, 3);
- tab_dim (self->t, tab_natural_dimensions, NULL);
+ tab_dim (self->t, tab_natural_dimensions, NULL, NULL);
}
/* Base finalizer for the trbox */
struct tab_table *table;
- table = tab_create (cols, rows, 0);
+ table = tab_create (cols, rows);
- tab_columns (table, SOM_COL_DOWN, 1);
+ tab_columns (table, SOM_COL_DOWN);
tab_headers (table, 0, 0, 1, 0);
tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
tab_hline (table, TAL_2, 0, cols - 1, 1);
tab_vline (table, TAL_2, 2, 0, rows - 1);
- tab_dim (table, tab_natural_dimensions, NULL);
+ tab_dim (table, tab_natural_dimensions, NULL, NULL);
tab_title (table, _("Paired Samples Correlations"));
/* column headings */
struct wilcoxon_state *ws = xcalloc (sizeof (*ws), t2s->n_pairs);
const struct variable *weight = dict_get_weight (dict);
- struct variable *weightx = var_create_internal (WEIGHT_IDX);
+ struct variable *weightx = var_create_internal (WEIGHT_IDX, 0);
struct caseproto *proto;
input =
struct subcase ordering;
variable_pair *vp = &t2s->pairs[i];
- ws[i].sign = var_create_internal (0);
- ws[i].absdiff = var_create_internal (1);
+ ws[i].sign = var_create_internal (0, 0);
+ ws[i].absdiff = var_create_internal (1, 0);
r = casereader_create_filter_missing (r, *vp, 2,
exclude,
const struct variable *wv = dict_get_weight (dict);
const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
- struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs, 0);
+ struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs);
- tab_dim (table, tab_natural_dimensions, NULL);
+ tab_dim (table, tab_natural_dimensions, NULL, NULL);
tab_title (table, _("Ranks"));
/* Vertical lines inside the box */
tab_box (table, 0, 0, -1, TAL_1,
- 1, 0, table->nc - 1, tab_nr (table) - 1 );
+ 1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
/* Box around entire table */
tab_box (table, TAL_2, TAL_2, -1, -1,
- 0, 0, table->nc - 1, tab_nr (table) - 1 );
+ 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
tab_text (table, 2, 0, TAB_CENTER, _("N"));
tab_text (table, 1, 3 + i * 4, TAB_LEFT, _("Ties"));
tab_text (table, 1, 4 + i * 4, TAB_LEFT, _("Total"));
- tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+ tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
tab_text (table, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
}
- tab_hline (table, TAL_2, 0, table->nc - 1, 1);
- tab_vline (table, TAL_2, 2, 0, table->nr - 1);
+ tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+ tab_vline (table, TAL_2, 2, 0, tab_nr (table) - 1);
tab_submit (table);
)
{
size_t i;
- struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3, 0);
+ struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3);
- tab_dim (table, tab_natural_dimensions, NULL);
+ tab_dim (table, tab_natural_dimensions, NULL, NULL);
tab_title (table, _("Test Statistics"));
/* Vertical lines inside the box */
tab_box (table, 0, 0, -1, TAL_1,
- 0, 0, table->nc - 1, tab_nr (table) - 1 );
+ 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
/* Box around entire table */
tab_box (table, TAL_2, TAL_2, -1, -1,
- 0, 0, table->nc - 1, tab_nr (table) - 1 );
+ 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
tab_text (table, 0, 1, TAB_LEFT, _("Z"));
- tab_text (table, 0, 2, TAB_LEFT, _("Asymp. Sig (2-tailed)"));
+ tab_text (table, 0, 2, TAB_LEFT, _("Asymp. Sig. (2-tailed)"));
if ( exact )
{
- tab_text (table, 0, 3, TAB_LEFT, _("Exact Sig (2-tailed)"));
- tab_text (table, 0, 4, TAB_LEFT, _("Exact Sig (1-tailed)"));
+ tab_text (table, 0, 3, TAB_LEFT, _("Exact Sig. (2-tailed)"));
+ tab_text (table, 0, 4, TAB_LEFT, _("Exact Sig. (1-tailed)"));
#if 0
tab_text (table, 0, 5, TAB_LEFT, _("Point Probability"));
}
}
- tab_hline (table, TAL_2, 0, table->nc - 1, 1);
- tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+ tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+ tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
tab_submit (table);
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <libpspp/message.h>
#include <libpspp/str.h>
static const char *
name (const struct getl_interface *s)
{
- const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+ const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+ parent);
return sfs->fn;
}
static int
line_number (const struct getl_interface *s)
{
- const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+ const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+ parent);
return sfs->ln;
}
read_syntax_file (struct getl_interface *s,
struct string *line)
{
- struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+ struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+ parent);
/* Open file, if not yet opened. */
if (sfs->syntax_file == NULL)
do
{
sfs->ln++;
+ ds_clear (line);
if (!ds_read_line (line, sfs->syntax_file, SIZE_MAX))
{
if (ferror (sfs->syntax_file))
static void
syntax_close (struct getl_interface *s)
{
- struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+ struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+ parent);
if (sfs->syntax_file && EOF == fn_close (sfs->fn, sfs->syntax_file))
msg (MW, _("Closing `%s': %s."), sfs->fn, strerror (errno));
ss->parent.name = name ;
ss->parent.location = line_number;
- return (struct getl_interface *) ss;
+ return &ss->parent;
}
#include <stdint.h>
#include <stdlib.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <libpspp/pool.h>
char *
ds_cstr (const struct string *st_)
{
- struct string *st = (struct string *) st_;
+ struct string *st = CONST_CAST (struct string *, st_);
if (st->ss.string == NULL)
ds_extend (st, 1);
st->ss.string[st->ss.length] = '\0';
free ((char *) rel);
}
}
+
+
+ \f
+
+ /* Operations on uint8_t "strings" */
+
+ /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
+ DST is truncated to DST_SIZE bytes or padded on the right with
+ copies of PAD as needed. */
+ void
+ u8_buf_copy_rpad (uint8_t *dst, size_t dst_size,
+ const uint8_t *src, size_t src_size,
+ char pad)
+ {
+ if (src_size >= dst_size)
+ memmove (dst, src, dst_size);
+ else
+ {
+ memmove (dst, src, src_size);
+ memset (&dst[src_size], pad, dst_size - src_size);
+ }
+ }
## Process this file with automake to produce Makefile.in -*- makefile -*-
-
-include $(top_srcdir)/src/output/charts/automake.mk
-
noinst_LTLIBRARIES += src/output/liboutput.la
-output_sources = \
+src_output_liboutput_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(AM_CPPFLAGS)
+
+src_output_liboutput_la_SOURCES = \
src/output/afm.c \
src/output/afm.h \
src/output/ascii.c \
+ src/output/chart.c \
+ src/output/chart.h \
+ src/output/charts/box-whisker.c \
+ src/output/charts/box-whisker.h \
+ src/output/charts/cartesian.c \
+ src/output/charts/cartesian.h \
+ src/output/charts/np-plot.c \
+ src/output/charts/np-plot.h \
+ src/output/charts/piechart.c \
+ src/output/charts/piechart.h \
+ src/output/charts/plot-chart.c \
+ src/output/charts/plot-chart.h \
+ src/output/charts/plot-hist.c \
+ src/output/charts/plot-hist.h \
++ src/output/charts/roc-chart.c \
++ src/output/charts/roc-chart.h \
src/output/html.c \
src/output/htmlP.h \
src/output/journal.c \
src/output/journal.h \
+ src/output/manager.c \
+ src/output/manager.h \
+ src/output/odt.c \
src/output/output.c \
src/output/output.h \
src/output/postscript.c \
- src/output/manager.c \
- src/output/manager.h \
- src/output/chart.h \
- src/output/table.c src/output/table.h
-
-
-if WITHCHARTS
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/chart.c
-
-EXTRA_DIST += src/output/dummy-chart.c
-else
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/dummy-chart.c
-
-EXTRA_DIST += src/output/chart.c
+ src/output/table.c \
+ src/output/table.h
+if HAVE_CAIRO
+src_output_liboutput_la_SOURCES += src/output/cairo.c
endif
EXTRA_DIST += src/output/OChangeLog
--- /dev/null
- chart_geometry_free (x->cairo);
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/cairo.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/start-date.h>
+#include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/manager.h>
+#include <output/output.h>
+
+#include <cairo/cairo-pdf.h>
+#include <cairo/cairo-ps.h>
+#include <cairo/cairo-svg.h>
+#include <cairo/cairo.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdlib.h>
+
+#include "error.h"
+#include "intprops.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Cairo driver options: (defaults listed first)
+
+ output-file="pspp.pdf"
+ output-type=pdf|ps|png|svg
+ paper-size=letter (see "papersize" file)
+ orientation=portrait|landscape
+ headers=on|off
+
+ left-margin=0.5in
+ right-margin=0.5in
+ top-margin=0.5in
+ bottom-margin=0.5in
+
+ prop-font=serif
+ emph-font=serif italic
+ fixed-font=monospace
+ font-size=10000
+
+ line-gutter=1pt
+ line-spacing=1pt
+ line-width=0.5pt
+ */
+
+/* Measurements as we present to the rest of PSPP. */
+#define XR_POINT PANGO_SCALE
+#define XR_INCH (XR_POINT * 72)
+
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+ return x / (double) XR_POINT;
+}
+
+static int
+pt_to_xr (double x)
+{
+ return x * XR_POINT + 0.5;
+}
+
+/* Output types. */
+enum xr_output_type
+ {
+ XR_PDF,
+ XR_PS,
+ XR_SVG
+ };
+
+/* A font for use with Cairo. */
+struct xr_font
+ {
+ char *string;
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ PangoFontMetrics *metrics;
+ };
+
+/* Cairo output driver extension record. */
+struct xr_driver_ext
+ {
+ cairo_t *cairo;
+ struct xr_font fonts[OUTP_FONT_CNT];
+
+ bool draw_headers; /* Draw headers at top of page? */
+ int page_number; /* Current page number. */
+
+ int line_gutter; /* Space around lines. */
+ int line_space; /* Space between lines. */
+ int line_width; /* Width of lines. */
+ };
+
+struct xr_driver_options
+ {
+ struct outp_driver *driver;
+
+ char *file_name; /* Output file name. */
+ enum xr_output_type file_type; /* Type of output file. */
+
+
+ bool portrait; /* Portrait mode? */
+
+ int paper_width; /* Width of paper before dropping margins. */
+ int paper_length; /* Length of paper before dropping margins. */
+ int left_margin; /* Left margin in XR units. */
+ int right_margin; /* Right margin in XR units. */
+ int top_margin; /* Top margin in XR units. */
+ int bottom_margin; /* Bottom margin in XR units. */
+ };
+
+static bool handle_option (void *options, const char *key,
+ const struct string *val);
+static void draw_headers (struct outp_driver *this);
+
+static bool load_font (struct outp_driver *this, struct xr_font *);
+static void free_font (struct xr_font *);
+static int text_width (struct outp_driver *, const char *, enum outp_font);
+\f
+/* Driver initialization. */
+
+static struct outp_driver *
+xr_allocate (const char *name, int types)
+{
+ struct outp_driver *this;
+ struct xr_driver_ext *x;
+ size_t i;
+
+ this = outp_allocate_driver (&cairo_class, name, types);
+ this->width = this->length = 0;
+ this->font_height = XR_POINT * 10;
+ this->ext = x = xzalloc (sizeof *x);
+ x->cairo = NULL;
+ x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
+ x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
+ x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ {
+ struct xr_font *font = &x->fonts[i];
+ font->desc = NULL;
+ font->metrics = NULL;
+ font->layout = NULL;
+ }
+ x->draw_headers = true;
+ x->page_number = 0;
+ x->line_gutter = XR_POINT;
+ x->line_space = XR_POINT;
+ x->line_width = XR_POINT / 2;
+
+ return this;
+}
+
+static bool
+xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+{
+ struct xr_driver_ext *x = this->ext;
+ int i;
+
+ x->cairo = cairo;
+
+ cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ if (!load_font (this, &x->fonts[i]))
+ return false;
+
+ this->fixed_width = text_width (this, "0", OUTP_FIXED);
+ this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
+
+ this->horiz_line_width[OUTP_L_NONE] = 0;
+ this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
+ this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
+ + 2 * x->line_width);
+ memcpy (this->vert_line_width, this->horiz_line_width,
+ sizeof this->vert_line_width);
+
+ return true;
+}
+
+struct outp_driver *
+xr_create_driver (cairo_t *cairo)
+{
+ struct outp_driver *this;
+
+ this = xr_allocate ("cairo", 0);
+ this->width = INT_MAX / 8;
+ this->length = INT_MAX / 8;
+ if (!xr_set_cairo (this, cairo))
+ {
+ this->class->close_driver (this);
+ outp_free_driver (this);
+ return NULL;
+ }
+ return this;
+}
+
+static bool
+xr_open_driver (const char *name, int types, struct substring option_string)
+{
+ struct outp_driver *this;
+ struct xr_driver_ext *x;
+ struct xr_driver_options options;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ double width_pt, length_pt;
+
+ this = xr_allocate (name, types);
+ x = this->ext;
+
+ options.driver = this;
+ options.file_name = xstrdup ("pspp.pdf");
+ options.file_type = XR_PDF;
+ options.portrait = true;
+ outp_get_paper_size ("", &options.paper_width, &options.paper_length);
+ options.left_margin = XR_INCH / 2;
+ options.right_margin = XR_INCH / 2;
+ options.top_margin = XR_INCH / 2;
+ options.bottom_margin = XR_INCH / 2;
+
+ outp_parse_options (this->name, option_string, handle_option, &options);
+
+ width_pt = options.paper_width / 1000.0;
+ length_pt = options.paper_length / 1000.0;
+ if (options.portrait)
+ {
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
+ }
+ else
+ {
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
+ }
+ if (x->draw_headers)
+ options.top_margin += 3 * this->font_height;
+ this->width -= options.left_margin + options.right_margin;
+ this->length -= options.top_margin + options.bottom_margin;
+
+ if (options.file_type == XR_PDF)
+ surface = cairo_pdf_surface_create (options.file_name,
+ width_pt, length_pt);
+ else if (options.file_type == XR_PS)
+ surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
+ else if (options.file_type == XR_SVG)
+ surface = cairo_svg_surface_create (options.file_name,
+ width_pt, length_pt);
+ else
+ NOT_REACHED ();
+
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ error (0, 0, _("opening output file \"%s\": %s"),
+ options.file_name, cairo_status_to_string (status));
+ cairo_surface_destroy (surface);
+ goto error;
+ }
+
+ x->cairo = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ cairo_translate (x->cairo,
+ xr_to_pt (options.left_margin),
+ xr_to_pt (options.top_margin));
+
+ if (this->length / this->font_height < 15)
+ {
+ error (0, 0, _("The defined page is not long "
+ "enough to hold margins and headers, plus least 15 "
+ "lines of the default fonts. In fact, there's only "
+ "room for %d lines."),
+ this->length / this->font_height);
+ goto error;
+ }
+
+ if (!xr_set_cairo (this, x->cairo))
+ goto error;
+
+ outp_register_driver (this);
+ free (options.file_name);
+ return true;
+
+ error:
+ this->class->close_driver (this);
+ outp_free_driver (this);
+ free (options.file_name);
+ return false;
+}
+
+static bool
+xr_close_driver (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+ bool ok = true;
+ size_t i;
+
+ if (x->cairo != NULL)
+ {
+ cairo_status_t status;
+
+ cairo_surface_finish (cairo_get_target (x->cairo));
+ status = cairo_status (x->cairo);
+ if (status != CAIRO_STATUS_SUCCESS)
+ error (0, 0, _("error writing output file for %s driver: %s"),
+ this->name, cairo_status_to_string (status));
+ cairo_destroy (x->cairo);
+ }
+
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ free_font (&x->fonts[i]);
+ free (x);
+
+ return ok;
+}
+
+/* Generic option types. */
+enum
+{
+ output_file_arg,
+ output_type_arg,
+ paper_size_arg,
+ orientation_arg,
+ line_style_arg,
+ boolean_arg,
+ dimension_arg,
+ string_arg,
+ nonneg_int_arg
+};
+
+/* All the options that the Cairo driver supports. */
+static const struct outp_option option_tab[] =
+{
+ {"output-file", output_file_arg,0},
+ {"output-type", output_type_arg,0},
+ {"paper-size", paper_size_arg, 0},
+ {"orientation", orientation_arg,0},
+
+ {"headers", boolean_arg, 1},
+
+ {"prop-font", string_arg, OUTP_PROPORTIONAL},
+ {"emph-font", string_arg, OUTP_EMPHASIS},
+ {"fixed-font", string_arg, OUTP_FIXED},
+
+ {"left-margin", dimension_arg, 0},
+ {"right-margin", dimension_arg, 1},
+ {"top-margin", dimension_arg, 2},
+ {"bottom-margin", dimension_arg, 3},
+ {"font-size", dimension_arg, 4},
+ {"line-width", dimension_arg, 5},
+ {"line-gutter", dimension_arg, 6},
+ {"line-width", dimension_arg, 7},
+ {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+ struct xr_driver_options *options = options_;
+ struct outp_driver *this = options->driver;
+ struct xr_driver_ext *x = this->ext;
+ int subcat;
+ char *value = ds_cstr (val);
+
+ switch (outp_match_keyword (key, option_tab, &subcat))
+ {
+ case -1:
+ error (0, 0,
+ _("unknown configuration parameter `%s' for %s device "
+ "driver"), key, this->class->name);
+ break;
+ case output_file_arg:
+ free (options->file_name);
+ options->file_name = xstrdup (value);
+ break;
+ case output_type_arg:
+ if (!strcmp (value, "pdf"))
+ options->file_type = XR_PDF;
+ else if (!strcmp (value, "ps"))
+ options->file_type = XR_PS;
+ else if (!strcmp (value, "svg"))
+ options->file_type = XR_SVG;
+ else
+ {
+ error (0, 0, _("unknown Cairo output type \"%s\""), value);
+ return false;
+ }
+ break;
+ case paper_size_arg:
+ outp_get_paper_size (value,
+ &options->paper_width, &options->paper_length);
+ break;
+ case orientation_arg:
+ if (!strcmp (value, "portrait"))
+ options->portrait = true;
+ else if (!strcmp (value, "landscape"))
+ options->portrait = false;
+ else
+ error (0, 0, _("unknown orientation `%s' (valid orientations are "
+ "`portrait' and `landscape')"), value);
+ break;
+ case boolean_arg:
+ if (!strcmp (value, "on") || !strcmp (value, "true")
+ || !strcmp (value, "yes") || atoi (value))
+ x->draw_headers = true;
+ else if (!strcmp (value, "off") || !strcmp (value, "false")
+ || !strcmp (value, "no") || !strcmp (value, "0"))
+ x->draw_headers = false;
+ else
+ {
+ error (0, 0, _("boolean value expected for %s"), key);
+ return false;
+ }
+ break;
+ case dimension_arg:
+ {
+ int dimension = outp_evaluate_dimension (value);
+
+ if (dimension <= 0)
+ break;
+ switch (subcat)
+ {
+ case 0:
+ options->left_margin = dimension;
+ break;
+ case 1:
+ options->right_margin = dimension;
+ break;
+ case 2:
+ options->top_margin = dimension;
+ break;
+ case 3:
+ options->bottom_margin = dimension;
+ break;
+ case 4:
+ this->font_height = dimension;
+ break;
+ case 5:
+ x->line_width = dimension;
+ break;
+ case 6:
+ x->line_gutter = dimension;
+ break;
+ case 7:
+ x->line_width = dimension;
+ break;
+ default:
+ NOT_REACHED ();
+ }
+ }
+ break;
+ case string_arg:
+ free (x->fonts[subcat].string);
+ x->fonts[subcat].string = ds_xstrdup (val);
+ break;
+ default:
+ NOT_REACHED ();
+ }
+
+ return true;
+}
+\f
+/* Basic file operations. */
+
+static void
+xr_open_page (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+
+ x->page_number++;
+
+ if (x->draw_headers)
+ draw_headers (this);
+}
+
+static void
+xr_close_page (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+ cairo_show_page (x->cairo);
+}
+
+static void
+xr_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+ struct xr_driver_ext *x = this->ext;
+ struct chart_geometry geom;
+
+ outp_eject_page (this);
+ outp_open_page (this);
+
+ cairo_save (x->cairo);
+ cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
+ cairo_scale (x->cairo, 1.0, -1.0);
+ chart_geometry_init (x->cairo, &geom,
+ xr_to_pt (this->width), xr_to_pt (this->length));
+ chart_draw (chart, x->cairo, &geom);
++ chart_geometry_free (x->cairo, &geom);
+ cairo_restore (x->cairo);
+
+ outp_close_page (this);
+}
+\f
+/* Draws a line from (x0,y0) to (x1,y1). */
+static void
+dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
+{
+ struct xr_driver_ext *x = this->ext;
+ cairo_new_path (x->cairo);
+ cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
+ cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
+ cairo_stroke (x->cairo);
+}
+
+/* Draws a horizontal line X0...X2 at Y if LEFT says so,
+ shortening it to X0...X1 if SHORTEN is true.
+ Draws a horizontal line X1...X3 at Y if RIGHT says so,
+ shortening it to X2...X3 if SHORTEN is true. */
+static void
+horz_line (struct outp_driver *this,
+ int x0, int x1, int x2, int x3, int y,
+ enum outp_line_style left, enum outp_line_style right,
+ bool shorten)
+{
+ if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
+ dump_line (this, x0, y, x3, y);
+ else
+ {
+ if (left != OUTP_L_NONE)
+ dump_line (this, x0, y, shorten ? x1 : x2, y);
+ if (right != OUTP_L_NONE)
+ dump_line (this, shorten ? x2 : x1, y, x3, y);
+ }
+}
+
+/* Draws a vertical line Y0...Y2 at X if TOP says so,
+ shortening it to Y0...Y1 if SHORTEN is true.
+ Draws a vertical line Y1...Y3 at X if BOTTOM says so,
+ shortening it to Y2...Y3 if SHORTEN is true. */
+static void
+vert_line (struct outp_driver *this,
+ int y0, int y1, int y2, int y3, int x,
+ enum outp_line_style top, enum outp_line_style bottom,
+ bool shorten)
+{
+ if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
+ dump_line (this, x, y0, x, y3);
+ else
+ {
+ if (top != OUTP_L_NONE)
+ dump_line (this, x, y0, x, shorten ? y1 : y2);
+ if (bottom != OUTP_L_NONE)
+ dump_line (this, x, shorten ? y2 : y1, x, y3);
+ }
+}
+
+/* Draws a generalized intersection of lines in the rectangle
+ (X0,Y0)-(X3,Y3). The line coming from the top to the center
+ is of style TOP, from left to center of style LEFT, from
+ bottom to center of style BOTTOM, and from right to center of
+ style RIGHT. */
+static void
+xr_line (struct outp_driver *this,
+ int x0, int y0, int x3, int y3,
+ enum outp_line_style top, enum outp_line_style left,
+ enum outp_line_style bottom, enum outp_line_style right)
+{
+ /* The algorithm here is somewhat subtle, to allow it to handle
+ all the kinds of intersections that we need.
+
+ Three additional ordinates are assigned along the x axis. The
+ first is xc, midway between x0 and x3. The others are x1 and
+ x2; for a single vertical line these are equal to xc, and for
+ a double vertical line they are the ordinates of the left and
+ right half of the double line.
+
+ yc, y1, and y2 are assigned similarly along the y axis.
+
+ The following diagram shows the coordinate system and output
+ for double top and bottom lines, single left line, and no
+ right line:
+
+ x0 x1 xc x2 x3
+ y0 ________________________
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y1 = y2 = yc |######### # |
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y3 |________#_____#_______|
+ */
+ struct xr_driver_ext *ext = this->ext;
+
+ /* Offset from center of each line in a pair of double lines. */
+ int double_line_ofs = (ext->line_space + ext->line_width) / 2;
+
+ /* Are the lines along each axis single or double?
+ (It doesn't make sense to have different kinds of line on the
+ same axis, so we don't try to gracefully handle that case.) */
+ bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
+ bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
+
+ /* When horizontal lines are doubled,
+ the left-side line along y1 normally runs from x0 to x2,
+ and the right-side line along y1 from x3 to x1.
+ If the top-side line is also doubled, we shorten the y1 lines,
+ so that the left-side line runs only to x1,
+ and the right-side line only to x2.
+ Otherwise, the horizontal line at y = y1 below would cut off
+ the intersection, which looks ugly:
+ x0 x1 x2 x3
+ y0 ________________________
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y1 |######### ########|
+ | |
+ | |
+ y2 |######################|
+ | |
+ | |
+ y3 |______________________|
+ It is more of a judgment call when the horizontal line is
+ single. We actually choose to cut off the line anyhow, as
+ shown in the first diagram above.
+ */
+ bool shorten_y1_lines = top == OUTP_L_DOUBLE;
+ bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
+ bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
+ int horz_line_ofs = double_vert ? double_line_ofs : 0;
+ int xc = (x0 + x3) / 2;
+ int x1 = xc - horz_line_ofs;
+ int x2 = xc + horz_line_ofs;
+
+ bool shorten_x1_lines = left == OUTP_L_DOUBLE;
+ bool shorten_x2_lines = right == OUTP_L_DOUBLE;
+ bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
+ int vert_line_ofs = double_horz ? double_line_ofs : 0;
+ int yc = (y0 + y3) / 2;
+ int y1 = yc - vert_line_ofs;
+ int y2 = yc + vert_line_ofs;
+
+ if (!double_horz)
+ horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+ else
+ {
+ horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
+ horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+ }
+
+ if (!double_vert)
+ vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+ else
+ {
+ vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
+ vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+ }
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+ and with the given JUSTIFICATION for THIS driver. */
+static int
+draw_text (struct outp_driver *this,
+ const char *string, int x, int y, int max_width,
+ enum outp_justification justification)
+{
+ struct outp_text text;
+ int width;
+
+ text.font = OUTP_PROPORTIONAL;
+ text.justification = justification;
+ text.string = ss_cstr (string);
+ text.h = max_width;
+ text.v = this->font_height;
+ text.x = x;
+ text.y = y;
+ this->class->text_metrics (this, &text, &width, NULL);
+ this->class->text_draw (this, &text);
+ return width;
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+ and with the given JUSTIFICATION for THIS driver. */
+static int
+text_width (struct outp_driver *this, const char *string, enum outp_font font)
+{
+ struct outp_text text;
+ int width;
+
+ text.font = font;
+ text.justification = OUTP_LEFT;
+ text.string = ss_cstr (string);
+ text.h = INT_MAX;
+ text.v = this->font_height;
+ text.x = 0;
+ text.y = 0;
+ this->class->text_metrics (this, &text, &width, NULL);
+ return width;
+}
+
+/* Writes LEFT left-justified and RIGHT right-justified within
+ (X0...X1) at Y. LEFT or RIGHT or both may be null. */
+static void
+draw_header_line (struct outp_driver *this,
+ const char *left, const char *right,
+ int x0, int x1, int y)
+{
+ int right_width = 0;
+ if (right != NULL)
+ right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
+ + this->prop_em_width);
+ if (left != NULL)
+ draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
+}
+
+/* Draw top of page headers for THIS driver. */
+static void
+draw_headers (struct outp_driver *this)
+{
+ struct xr_driver_ext *ext = this->ext;
+ char *r1, *r2;
+ int x0, x1;
+ int y;
+
+ y = -3 * this->font_height;
+ x0 = this->prop_em_width;
+ x1 = this->width - this->prop_em_width;
+
+ /* Draw box. */
+ cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
+ xr_to_pt (2 * (this->font_height
+ + ext->line_width + ext->line_gutter)));
+ cairo_save (ext->cairo);
+ cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
+ cairo_fill_preserve (ext->cairo);
+ cairo_restore (ext->cairo);
+ cairo_stroke (ext->cairo);
+
+ y += ext->line_width + ext->line_gutter;
+
+ r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
+ r2 = xasprintf ("%s - %s", version, host_system);
+
+ draw_header_line (this, outp_title, r1, x0, x1, y);
+ y += this->font_height;
+
+ draw_header_line (this, outp_subtitle, r2, x0, x1, y);
+
+ free (r1);
+ free (r2);
+}
+\f
+/* Format TEXT on THIS driver.
+ If DRAW is nonzero, draw the text.
+ The width of the widest line is stored into *WIDTH, if WIDTH
+ is nonnull.
+ The total height of the text written is stored into *HEIGHT,
+ if HEIGHT is nonnull. */
+static void
+text (struct outp_driver *this, const struct outp_text *text, bool draw,
+ int *width, int *height)
+{
+ struct xr_driver_ext *ext = this->ext;
+ struct xr_font *font = &ext->fonts[text->font];
+
+ pango_layout_set_text (font->layout,
+ text->string.string, text->string.length);
+ pango_layout_set_alignment (
+ font->layout,
+ (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
+ : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
+ : PANGO_ALIGN_CENTER));
+ pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
+ pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
+ /* XXX need to limit number of lines to those that fit in text->v. */
+
+ if (draw)
+ {
+ int x = text->x;
+ if (text->justification != OUTP_LEFT && text->h != INT_MAX)
+ {
+ int w, h, excess;
+ pango_layout_get_size (font->layout, &w, &h);
+ excess = text->h - w;
+ if (excess > 0)
+ {
+ if (text->justification == OUTP_CENTER)
+ x += excess / 2;
+ else
+ x += excess;
+ }
+ }
+ cairo_save (ext->cairo);
+ cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
+ pango_cairo_show_layout (ext->cairo, font->layout);
+ cairo_restore (ext->cairo);
+ }
+
+ if (width != NULL || height != NULL)
+ {
+ int w, h;
+ pango_layout_get_size (font->layout, &w, &h);
+ if (width != NULL)
+ *width = w;
+ if (height != NULL)
+ *height = h;
+ }
+}
+
+static void
+xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
+ int *width, int *height)
+{
+ text (this, t, false, width, height);
+}
+
+static void
+xr_text_draw (struct outp_driver *this, const struct outp_text *t)
+{
+ text (this, t, true, NULL, NULL);
+}
+\f
+/* Attempts to load FONT, initializing its other members based on
+ its 'string' member and the information in THIS. Returns true
+ if successful, otherwise false. */
+static bool
+load_font (struct outp_driver *this, struct xr_font *font)
+{
+ struct xr_driver_ext *x = this->ext;
+ PangoContext *context;
+ PangoLanguage *language;
+
+ font->desc = pango_font_description_from_string (font->string);
+ if (font->desc == NULL)
+ {
+ error (0, 0, _("\"%s\": bad font specification"), font->string);
+ return false;
+ }
+ pango_font_description_set_absolute_size (font->desc, this->font_height);
+
+ font->layout = pango_cairo_create_layout (x->cairo);
+ pango_layout_set_font_description (font->layout, font->desc);
+
+ language = pango_language_get_default ();
+ context = pango_layout_get_context (font->layout);
+ font->metrics = pango_context_get_metrics (context, font->desc, language);
+
+ return true;
+}
+
+/* Frees FONT. */
+static void
+free_font (struct xr_font *font)
+{
+ free (font->string);
+ if (font->desc != NULL)
+ pango_font_description_free (font->desc);
+ pango_font_metrics_unref (font->metrics);
+ g_object_unref (font->layout);
+}
+
+/* Cairo driver class. */
+const struct outp_class cairo_class =
+{
+ "cairo",
+ 0,
+
+ xr_open_driver,
+ xr_close_driver,
+
+ xr_open_page,
+ xr_close_page,
+ NULL,
+
+ xr_output_chart,
+
+ NULL,
+
+ xr_line,
+ xr_text_metrics,
+ xr_text_draw,
+};
--- /dev/null
- void chart_geometry_free (cairo_t *);
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_PROVIDER_H
+#define OUTPUT_CHART_PROVIDER_H 1
+
+#include <cairo/cairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <output/chart.h>
+
+struct chart_colour
+ {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ };
+
+/* The geometry of a chart. */
+struct chart_geometry
+ {
+ int data_top ;
+ int data_right ;
+ int data_bottom;
+ int data_left ;
+
+ int abscissa_top;
+
+ int ordinate_right ;
+
+ int title_bottom ;
+
++ /* Legend. */
+ int legend_left ;
+ int legend_right ;
++ const char **dataset;
++ int n_datasets;
+
+ /* Default font size for the plot. */
+ double font_size;
+
+ struct chart_colour fill_colour;
+
+ /* Stuff Particular to Cartesians (and Boxplots ) */
+ double ordinate_scale;
+ double abscissa_scale;
+ double x_min;
+ double x_max;
+ double y_min;
+ double y_max;
++ bool in_path;
+ };
+
+struct chart_class
+ {
+ void (*draw) (const struct chart *, cairo_t *, struct chart_geometry *);
+ void (*destroy) (struct chart *);
+ };
+
+struct chart
+ {
+ const struct chart_class *class;
+ int ref_cnt;
+ };
+
+void chart_init (struct chart *, const struct chart_class *);
+
+void chart_geometry_init (cairo_t *, struct chart_geometry *,
+ double width, double length);
++void chart_geometry_free (cairo_t *, struct chart_geometry *);
+
+void chart_draw (const struct chart *, cairo_t *, struct chart_geometry *);
+char *chart_draw_png (const struct chart *, const char *file_name_template,
+ int number);
+
+#endif /* output/chart-provider.h */
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
#include <output/chart.h>
+#include <output/chart-provider.h>
#include <assert.h>
+#include <cairo/cairo.h>
#include <errno.h>
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
-#include <plot.h>
-
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/output.h>
extern struct som_table_class tab_table_class;
-struct chart *
-chart_create(void)
+void
+chart_init (struct chart *chart, const struct chart_class *class)
{
- struct chart *chart;
- struct outp_driver *d;
-
- d = outp_drivers (NULL);
- if (d == NULL)
- return NULL;
-
- chart = xmalloc (sizeof *chart);
- chart->lp = NULL;
- d->class->initialise_chart(d, chart);
- if (!chart->lp)
- {
- free (chart);
- return NULL;
- }
-
- if (pl_openpl_r (chart->lp) < 0) /* open Plotter */
- return NULL;
-
- pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
- pl_flinewidth_r (chart->lp, 0.25); /* set line thickness */
- pl_pencolorname_r (chart->lp, "black");
-
- pl_erase_r (chart->lp); /* erase graphics display */
- pl_filltype_r(chart->lp,0);
-
- pl_savestate_r(chart->lp);
-
- /* Set default chartetry */
- chart->data_top = 900;
- chart->data_right = 800;
- chart->data_bottom = 120;
- chart->data_left = 150;
- chart->abscissa_top = 70;
- chart->ordinate_right = 120;
- chart->title_bottom = 920;
- chart->legend_left = 810;
- chart->legend_right = 1000;
- chart->font_size = 0;
- chart->in_path = false;
- chart->dataset = NULL;
- chart->n_datasets = 0;
- strcpy(chart->fill_colour,"red");
-
- /* Get default font size */
- if ( !chart->font_size)
- chart->font_size = pl_fontsize_r(chart->lp, -1);
-
- /* Draw the data area */
- pl_box_r(chart->lp,
- chart->data_left, chart->data_bottom,
- chart->data_right, chart->data_top);
+ chart->class = class;
+ chart->ref_cnt = 1;
+}
- return chart;
+void
+chart_geometry_init (cairo_t *cr, struct chart_geometry *geom,
+ double width, double length)
+{
+ /* Set default chartetry. */
+ geom->data_top = 0.900 * length;
+ geom->data_right = 0.800 * width;
+ geom->data_bottom = 0.120 * length;
+ geom->data_left = 0.150 * width;
+ geom->abscissa_top = 0.070 * length;
+ geom->ordinate_right = 0.120 * width;
+ geom->title_bottom = 0.920 * length;
+ geom->legend_left = 0.810 * width;
+ geom->legend_right = width;
+ geom->font_size = 15.0;
++ geom->in_path = false;
++ geom->dataset = NULL;
++ geom->n_datasets = 0;
+
+ geom->fill_colour.red = 255;
+ geom->fill_colour.green = 0;
+ geom->fill_colour.blue = 0;
+
+ cairo_set_line_width (cr, 1.0);
+
+ cairo_rectangle (cr, geom->data_left, geom->data_bottom,
+ geom->data_right - geom->data_left,
+ geom->data_top - geom->data_bottom);
+ cairo_stroke (cr);
}
void
- chart_geometry_free (cairo_t *cr UNUSED)
-chart_submit(struct chart *chart)
++chart_geometry_free (cairo_t *cr UNUSED, struct chart_geometry *geom)
{
- struct som_entity s;
- struct outp_driver *d;
-
- if ( ! chart )
- return ;
-
- pl_restorestate_r(chart->lp);
-
- s.class = &tab_table_class;
- s.ext = chart;
- s.type = SOM_CHART;
- som_submit (&s);
+ int i;
- if (pl_closepl_r (chart->lp) < 0) /* close Plotter */
- {
- fprintf (stderr, "Couldn't close Plotter\n");
- }
-
- pl_deletepl_r(chart->lp);
+
++ for (i = 0 ; i < geom->n_datasets; ++i)
++ free (geom->dataset[i]);
++ free (geom->dataset);
+}
- pl_deleteplparams(chart->pl_params);
+void
+chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ chart->class->draw (chart, cr, geom);
+}
- d = outp_drivers (NULL);
- d->class->finalise_chart(d, chart);
+char *
+chart_draw_png (const struct chart *chart, const char *file_name_template,
+ int number)
+{
+ const int width = 640;
+ const int length = 480;
+
+ struct chart_geometry geom;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ const char *number_pos;
+ char *file_name;
+ cairo_t *cr;
+
+ number_pos = strchr (file_name_template, '#');
+ if (number_pos != NULL)
+ file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
+ file_name_template, number, number_pos + 1);
+ else
+ file_name = xstrdup (file_name_template);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
+ cr = cairo_create (surface);
+
+ cairo_translate (cr, 0.0, length);
+ cairo_scale (cr, 1.0, -1.0);
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr, 0, 0, width, length);
+ cairo_fill (cr);
+ cairo_restore (cr);
+
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+ chart_geometry_init (cr, &geom, width, length);
+ chart_draw (chart, cr, &geom);
- chart_geometry_free (cr);
++ chart_geometry_free (cr, &geom);
+
+ status = cairo_surface_write_to_png (surface, file_name);
+ if (status != CAIRO_STATUS_SUCCESS)
+ error (0, 0, _("writing output file \"%s\": %s"),
+ file_name, cairo_status_to_string (status));
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ return file_name;
+}
- for (i = 0 ; i < chart->n_datasets; ++i)
- free (chart->dataset[i]);
- free (chart->dataset);
- free(chart);
+struct chart *
+chart_ref (const struct chart *chart_)
+{
+ struct chart *chart = CONST_CAST (struct chart *, chart_);
+ chart->ref_cnt++;
+ return chart;
}
void
-chart_init_separate (struct chart *ch, const char *type,
- const char *file_name_tmpl, int number)
+chart_unref (struct chart *chart)
{
- FILE *fp;
- int number_pos;
-
- number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl;
- ch->file_name = xasprintf ("%.*s%d%s",
- number_pos, file_name_tmpl,
- number,
- file_name_tmpl + number_pos + 1);
- fp = fopen (ch->file_name, "wb");
- if (fp == NULL)
+ if (chart != NULL)
{
- error (0, errno, _("creating \"%s\""), ch->file_name);
- free (ch->file_name);
- ch->file_name = NULL;
- return;
+ assert (chart->ref_cnt > 0);
+ if (--chart->ref_cnt == 0)
+ chart->class->destroy (chart);
}
-
- ch->pl_params = pl_newplparams ();
- ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params);
}
void
-chart_finalise_separate (struct chart *ch)
+chart_submit (struct chart *chart)
{
- free (ch->file_name);
+#ifdef HAVE_CAIRO
+ struct outp_driver *d;
+
+ for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+ if (d->class->output_chart != NULL)
+ d->class->output_chart (d, chart);
+#endif
+
+ chart_unref (chart);
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
+#include <output/charts/cartesian.h>
+
+#include <cairo/cairo.h>
#include <math.h>
#include <assert.h>
#include <output/chart.h>
-
+#include <output/chart-provider.h>
#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
#include <libpspp/compiler.h>
- struct dataset
++#include "xalloc.h"
+
+ /* Start a new vector called NAME */
+ void
-chart_vector_start (struct chart *ch, const char *name)
++chart_vector_start (cairo_t *cr, struct chart_geometry *geom, const char *name)
{
- int n_data;
- const char *label;
- };
- if ( ! ch )
- return ;
++ const struct chart_colour *colour;
- pl_savestate_r (ch->lp);
++ cairo_save (cr);
- #define DATASETS 2
- pl_colorname_r (ch->lp, data_colour [ch->n_datasets % N_CHART_COLOURS]);
++ colour = &data_colour[geom->n_datasets % N_CHART_COLOURS];
++ cairo_set_source_rgb (cr,
++ colour->red / 255.0,
++ colour->green / 255.0,
++ colour->blue / 255.0);
- static const struct dataset dataset[DATASETS] =
- {
- { 13, "male"},
- { 11, "female"},
- };
- ch->n_datasets++;
- ch->dataset = xrealloc (ch->dataset, ch->n_datasets * sizeof (*ch->dataset));
++ geom->n_datasets++;
++ geom->dataset = xrealloc (geom->dataset,
++ geom->n_datasets * sizeof (*geom->dataset));
- ch->dataset[ch->n_datasets - 1] = strdup (name);
++ geom->dataset[geom->n_datasets - 1] = strdup (name);
+ }
/* Plot a data point */
void
-chart_datum (struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (cairo_t *cr, const struct chart_geometry *geom,
+ int dataset UNUSED, double x, double y)
{
- if ( ! ch )
- return ;
-
- {
- const double x_pos =
- (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+ double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+ double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
- const double y_pos =
- (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
- pl_savestate_r(ch->lp);
-
- pl_fmarker_r(ch->lp, x_pos, y_pos, 6, 15);
-
- pl_restorestate_r(ch->lp);
- }
+ chart_draw_marker (cr, x_pos, y_pos, MARKER_SQUARE, 15);
}
-chart_vector_end (struct chart *ch)
+ void
- pl_endpath_r (ch->lp);
- pl_colorname_r (ch->lp, "black");
- ch->in_path = false;
- pl_restorestate_r (ch->lp);
++chart_vector_end (cairo_t *cr, struct chart_geometry *geom)
+ {
-chart_vector (struct chart *ch, double x, double y)
++ cairo_stroke (cr);
++ cairo_restore (cr);
++ geom->in_path = false;
+ }
+
+ /* Plot a data point */
+ void
- if ( ! ch )
- return ;
-
- {
- const double x_pos =
- (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
- const double y_pos =
- (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
- if ( ch->in_path)
- pl_fcont_r (ch->lp, x_pos, y_pos);
- else
- {
- pl_fmove_r (ch->lp, x_pos, y_pos);
- ch->in_path = true;
- }
- }
++chart_vector (cairo_t *cr, struct chart_geometry *geom, double x, double y)
+ {
++ const double x_pos =
++ (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
++
++ const double y_pos =
++ (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
++
++ if (geom->in_path)
++ cairo_line_to (cr, x_pos, y_pos);
++ else
++ {
++ cairo_move_to (cr, x_pos, y_pos);
++ geom->in_path = true;
++ }
+ }
+
+
+
/* Draw a line with slope SLOPE and intercept INTERCEPT.
between the points limit1 and limit2.
If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
y axis otherwise the x axis
*/
void
-chart_line (struct chart *ch, double slope, double intercept,
+chart_line(cairo_t *cr, const struct chart_geometry *geom,
+ double slope, double intercept,
double limit1, double limit2, enum CHART_DIM lim_dim)
{
double x1, y1;
- double x2, y2 ;
-
- if ( ! ch )
- return ;
-
+ double x2, y2;
if ( lim_dim == CHART_DIM_Y )
{
- x1 = ( limit1 - intercept ) / slope ;
- x2 = ( limit2 - intercept ) / slope ;
+ x1 = ( limit1 - intercept ) / slope;
+ x2 = ( limit2 - intercept ) / slope;
y1 = limit1;
y2 = limit2;
}
y2 = slope * x2 + intercept;
}
- y1 = (y1 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
- y2 = (y2 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
- x1 = (x1 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
- x2 = (x2 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
- pl_savestate_r(ch->lp);
-
- pl_fline_r(ch->lp, x1, y1, x2, y2);
+ y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+ y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+ x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+ x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
- pl_restorestate_r(ch->lp);
+ cairo_move_to (cr, x1, y1);
+ cairo_line_to (cr, x2, y2);
+ cairo_stroke (cr);
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#ifndef CARTESIAN_H
#define CARTESIAN_H
+#include <cairo/cairo.h>
+#include <libpspp/compiler.h>
+#include <output/chart.h>
enum CHART_DIM
{
CHART_DIM_Y
};
+struct chart_geometry;
-void chart_vector_start (struct chart *ch, const char *name);
-void chart_vector (struct chart *ch, double x, double y);
-void chart_vector_end (struct chart *ch);
++void chart_vector_start (cairo_t *, struct chart_geometry *,
++ const char *name);
++void chart_vector_end (cairo_t *, struct chart_geometry *);
++void chart_vector (cairo_t *, struct chart_geometry *, double x, double y);
+
/* Plot a data point */
-void chart_datum (struct chart *ch, int dataset UNUSED, double x, double y);
+void chart_datum(cairo_t *, const struct chart_geometry *,
+ int dataset UNUSED, double x, double y);
/* Draw a line with slope SLOPE and intercept INTERCEPT.
between the points limit1 and limit2.
If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
y axis otherwise the x axis
*/
-void chart_line (struct chart *ch, double slope, double intercept,
+void chart_line(cairo_t *, const struct chart_geometry *,
+ double slope, double intercept,
double limit1, double limit2, enum CHART_DIM lim_dim);
#include <config.h>
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
#include <output/charts/plot-chart.h>
-#include <math/chart-geometry.h>
-
-
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
-#include <libpspp/str.h>
#include <libpspp/assertion.h>
+#include <libpspp/str.h>
+#include <math/chart-geometry.h>
+#include <output/chart-provider.h>
#include <output/manager.h>
#include <output/output.h>
#include "xalloc.h"
-const char *const data_colour[N_CHART_COLOURS] =
+const struct chart_colour data_colour[N_CHART_COLOURS] =
{
- "brown",
- "red",
- "orange",
- "yellow",
- "green",
- "blue",
- "violet",
- "grey",
- "pink"
+ { 165, 42, 42 }, /* brown */
+ { 255, 0, 0 }, /* red */
+ { 255, 165, 0 }, /* orange */
+ { 255, 255, 0 }, /* yellow */
+ { 0, 255, 0 }, /* green */
+ { 0, 0, 255 }, /* blue */
+ { 238, 130, 238 }, /* violet */
+ { 190, 190, 190 }, /* grey */
+ { 255, 192, 203 }, /* pink */
};
+void
+chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker,
+ double size)
+{
+ cairo_save (cr);
+ cairo_translate (cr, x, y);
+ cairo_scale (cr, size / 2.0, size / 2.0);
+ cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
+ switch (marker)
+ {
+ case MARKER_CIRCLE:
+ cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
+ cairo_stroke (cr);
+ break;
+
+ case MARKER_ASTERISK:
+ cairo_move_to (cr, 0, -1.0); /* | */
+ cairo_line_to (cr, 0, 1.0);
+ cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
+ cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
+ cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
+ cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
+ cairo_stroke (cr);
+ break;
+
+ case MARKER_SQUARE:
+ cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
+ cairo_stroke (cr);
+ break;
+ }
+ cairo_restore (cr);
+}
+
+void
+chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size,
+ const char *string)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ double x, y;
+
+ desc = pango_font_description_from_string ("sans serif");
+ if (desc == NULL)
+ {
+ cairo_new_path (cr);
+ return;
+ }
+ pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
+
+ cairo_save (cr);
+ cairo_get_current_point (cr, &x, &y);
+ cairo_translate (cr, x, y);
+ cairo_move_to (cr, 0, 0);
+ cairo_scale (cr, 1.0, -1.0);
+
+ layout = pango_cairo_create_layout (cr);
+ pango_layout_set_font_description (layout, desc);
+ pango_layout_set_text (layout, string, -1);
+ if (horz_justify != 'l')
+ {
+ int width_pango;
+ double width;
+
+ pango_layout_get_size (layout, &width_pango, NULL);
+ width = (double) width_pango / PANGO_SCALE;
+ if (horz_justify == 'r')
+ cairo_rel_move_to (cr, -width, 0);
+ else
+ cairo_rel_move_to (cr, -width / 2.0, 0);
+ }
+ if (vert_justify == 'x')
+ {
+ int baseline_pango = pango_layout_get_baseline (layout);
+ double baseline = (double) baseline_pango / PANGO_SCALE;
+ cairo_rel_move_to (cr, 0, -baseline);
+ }
+ else if (vert_justify != 't')
+ {
+ int height_pango;
+ double height;
+
+ pango_layout_get_size (layout, NULL, &height_pango);
+ height = (double) height_pango / PANGO_SCALE;
+ if (vert_justify == 'b')
+ cairo_rel_move_to (cr, 0, -height);
+ else if (vert_justify == 'c')
+ cairo_rel_move_to (cr, 0, -height / 2.0);
+ }
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref (layout);
+ cairo_restore (cr);
+
+ cairo_new_path (cr);
+
+ pango_font_description_free (desc);
+}
/* Draw a tick mark at position
If label is non zero, then print it at the tick mark
*/
void
-draw_tick(struct chart *chart,
- enum tick_orientation orientation,
- double position,
- const char *label, ...)
+draw_tick (cairo_t *cr, const struct chart_geometry *geom,
+ enum tick_orientation orientation,
+ double position,
+ const char *label, ...)
{
const int tickSize = 10;
+ double x, y;
- assert(chart);
-
- pl_savestate_r(chart->lp);
-
- pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
+ cairo_move_to (cr, geom->data_left, geom->data_bottom);
- if ( orientation == TICK_ABSCISSA )
- pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
- else if (orientation == TICK_ORDINATE )
- pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+ if (orientation == TICK_ABSCISSA)
+ {
+ cairo_rel_move_to (cr, position, 0);
+ cairo_rel_line_to (cr, 0, -tickSize);
+ }
+ else if (orientation == TICK_ORDINATE)
+ {
+ cairo_rel_move_to (cr, 0, position);
+ cairo_rel_line_to (cr, -tickSize, 0);
+ }
else
NOT_REACHED ();
+ cairo_get_current_point (cr, &x, &y);
- if ( label ) {
- char buf[10];
- va_list ap;
- va_start(ap,label);
- vsnprintf(buf,10,label,ap);
-
- if ( orientation == TICK_ABSCISSA )
- pl_alabel_r(chart->lp, 'c','t', buf);
- else if (orientation == TICK_ORDINATE )
- {
- if ( fabs(position) < DBL_EPSILON )
- pl_moverel_r(chart->lp, 0, 10);
-
- pl_alabel_r(chart->lp, 'r','c', buf);
- }
-
- va_end(ap);
- }
+ cairo_stroke (cr);
- pl_restorestate_r(chart->lp);
+ if (label != NULL)
+ {
+ va_list ap;
+ char *s;
+
+ cairo_move_to (cr, x, y);
+
+ va_start (ap, label);
+ s = xvasprintf (label, ap);
+ if (orientation == TICK_ABSCISSA)
+ chart_label (cr, 'c', 't', geom->font_size, s);
+ else if (orientation == TICK_ORDINATE)
+ {
+ if (fabs (position) < DBL_EPSILON)
+ cairo_rel_move_to (cr, 0, 10);
+ chart_label (cr, 'r', 'c', geom->font_size, s);
+ }
+ free (s);
+ va_end (ap);
+ }
}
/* Write the title on a chart*/
void
-chart_write_title(struct chart *chart, const char *title, ...)
+chart_write_title (cairo_t *cr, const struct chart_geometry *geom,
+ const char *title, ...)
{
va_list ap;
- char buf[100];
-
- if ( ! chart )
- return ;
+ char *s;
- pl_savestate_r(chart->lp);
- pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
- pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+ cairo_save (cr);
+ cairo_move_to (cr, geom->data_left, geom->title_bottom);
- va_start(ap,title);
- vsnprintf(buf,100,title,ap);
- pl_alabel_r(chart->lp,0,0,buf);
- va_end(ap);
+ va_start(ap, title);
+ s = xvasprintf (title, ap);
+ chart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
+ free (s);
+ va_end (ap);
- pl_restorestate_r(chart->lp);
+ cairo_restore (cr);
}
/* Set the scale for the abscissa */
void
-chart_write_xscale(struct chart *ch, double min, double max, int ticks)
+chart_write_xscale (cairo_t *cr, struct chart_geometry *geom,
+ double min, double max, int ticks)
{
double x;
const double tick_interval =
- chart_rounded_tick( (max - min) / (double) ticks);
-
- assert ( ch );
-
+ chart_rounded_tick ((max - min) / (double) ticks);
- ch->x_max = ceil( max / tick_interval ) * tick_interval ;
- ch->x_min = floor ( min / tick_interval ) * tick_interval ;
-
-
- ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
- fabs(ch->x_max - ch->x_min);
-
- for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
- {
- draw_tick (ch, TICK_ABSCISSA,
- (x - ch->x_min) * ch->abscissa_scale, "%g", x);
- }
+ geom->x_max = ceil (max / tick_interval) * tick_interval;
+ geom->x_min = floor (min / tick_interval) * tick_interval;
+ geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+ fabs(geom->x_max - geom->x_min);
+ for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+ draw_tick (cr, geom, TICK_ABSCISSA,
+ (x - geom->x_min) * geom->abscissa_scale, "%g", x);
}
/* Set the scale for the ordinate */
void
-chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
+chart_write_yscale (cairo_t *cr, struct chart_geometry *geom,
+ double smin, double smax, int ticks)
{
double y;
const double tick_interval =
- chart_rounded_tick( (smax - smin) / (double) ticks);
+ chart_rounded_tick ((smax - smin) / (double) ticks);
- if ( !ch )
- return;
+ geom->y_max = ceil (smax / tick_interval) * tick_interval;
+ geom->y_min = floor (smin / tick_interval) * tick_interval;
- ch->y_max = ceil ( smax / tick_interval ) * tick_interval ;
- ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
+ geom->ordinate_scale =
+ (fabs (geom->data_top - geom->data_bottom)
+ / fabs (geom->y_max - geom->y_min));
- ch->ordinate_scale =
- fabs(ch->data_top - ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
-
- for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
- {
- draw_tick (ch, TICK_ORDINATE,
- (y - ch->y_min) * ch->ordinate_scale, "%g", y);
- }
+ for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+ draw_tick (cr, geom, TICK_ORDINATE,
+ (y - geom->y_min) * geom->ordinate_scale, "%g", y);
}
-
/* Write the abscissa label */
void
-chart_write_xlabel(struct chart *ch, const char *label)
+chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom,
+ const char *label)
{
- if ( ! ch )
- return ;
-
- pl_savestate_r(ch->lp);
-
- pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
- pl_alabel_r(ch->lp,0,'t',label);
-
- pl_restorestate_r(ch->lp);
-
+ cairo_move_to (cr, geom->data_left, geom->abscissa_top);
+ chart_label (cr, 'l', 't', geom->font_size, label);
}
-
-
/* Write the ordinate label */
void
-chart_write_ylabel(struct chart *ch, const char *label)
+chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom,
+ const char *label)
{
- if ( ! ch )
- return ;
-
- pl_savestate_r(ch->lp);
-
- pl_move_r(ch->lp, ch->data_bottom, ch->ordinate_right);
- pl_textangle_r(ch->lp, 90);
- pl_alabel_r(ch->lp, 0, 0, label);
-
- pl_restorestate_r(ch->lp);
+ cairo_save (cr);
+ cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
+ cairo_move_to (cr, 0, 0);
+ cairo_rotate (cr, M_PI / 2.0);
+ chart_label (cr, 'l', 'x', geom->font_size, label);
+ cairo_restore (cr);
}
-chart_write_legend (struct chart *ch)
+
+
+ void
- const int vstep = ch->font_size * 2;
++chart_write_legend (cairo_t *cr, const struct chart_geometry *geom)
+ {
+ int i;
- const int legend_top = ch->data_top;
++ const int vstep = geom->font_size * 2;
+ const int xpad = 10;
+ const int ypad = 10;
+ const int swatch = 20;
- (vstep * ch->n_datasets + 2 * ypad );
++ const int legend_top = geom->data_top;
+ const int legend_bottom = legend_top -
- if ( ! ch )
- return ;
++ (vstep * geom->n_datasets + 2 * ypad );
+
- pl_savestate_r (ch->lp);
++ cairo_save (cr);
+
- pl_box_r (ch->lp, ch->legend_left, legend_top,
- ch->legend_right - xpad, legend_bottom);
-
- for (i = 0 ; i < ch->n_datasets ; ++i )
++ cairo_rectangle (cr, geom->legend_left, legend_top,
++ geom->legend_right - xpad - geom->legend_left,
++ legend_bottom - legend_top);
++ cairo_stroke (cr);
+
- const int ypos = vstep * (i + 1);
- const int xpos = ch->legend_left + xpad;
- pl_move_r (ch->lp, xpos, legend_top - ypos);
-
- pl_savestate_r(ch->lp);
- pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
-
- pl_pentype_r (ch->lp, 1);
- pl_filltype_r (ch->lp, 1);
- pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
-
-
- pl_moverel_r (ch->lp, swatch, 0);
- pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
-
- pl_restorestate_r (ch->lp);
++ for (i = 0 ; i < geom->n_datasets ; ++i )
+ {
- pl_restorestate_r (ch->lp);
++ const int ypos = legend_top - vstep * (i + 1);
++ const int xpos = geom->legend_left + xpad;
++ const struct chart_colour *colour;
++
++ cairo_move_to (cr, xpos, ypos);
++
++ cairo_save (cr);
++ colour = &data_colour [ i % N_CHART_COLOURS];
++ cairo_set_source_rgb (cr,
++ colour->red / 255.0,
++ colour->green / 255.0,
++ colour->blue / 255.0);
++ cairo_rectangle (cr, xpos, ypos, swatch, swatch);
++ cairo_fill_preserve (cr);
++ cairo_stroke (cr);
++ cairo_restore (cr);
++
++ cairo_move_to (cr, xpos + swatch * 1.5, ypos);
++ chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
+ }
+
++ cairo_restore (cr);
+ }
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#ifndef PLOT_CHART_H
+#define PLOT_CHART_H
+
+#include <cairo/cairo.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math/chart-geometry.h>
#include <output/chart.h>
+#include <output/chart-provider.h>
+#include <libpspp/compiler.h>
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/output.h>
-#include "xalloc.h"
-
-#ifndef PLOT_CHART_H
-#define PLOT_CHART_H
-
#define N_CHART_COLOURS 9
-extern const char *const data_colour[];
+extern const struct chart_colour data_colour[];
enum tick_orientation
{
TICK_ORDINATE
};
+struct chart_geometry;
+
+
+enum marker_type
+ {
+ MARKER_CIRCLE, /* Hollow circle. */
+ MARKER_ASTERISK, /* Asterisk (*). */
+ MARKER_SQUARE /* Hollow square. */
+ };
+
+void chart_draw_marker (cairo_t *, double x, double y, enum marker_type,
+ double size);
+
+void chart_label (cairo_t *, int horz_justify, int vert_justify,
+ double font_size, const char *);
/* Draw a tick mark at position
If label is non zero, then print it at the tick mark
*/
-void draw_tick(struct chart *chart,
+void draw_tick(cairo_t *, const struct chart_geometry *,
enum tick_orientation orientation,
double position,
- const char *label, ...);
+ const char *label, ...)
+ PRINTF_FORMAT (5, 6);
/* Write the title on a chart*/
-void chart_write_title(struct chart *chart, const char *title, ...);
+void chart_write_title(cairo_t *, const struct chart_geometry *,
+ const char *title, ...)
+ PRINTF_FORMAT (3, 4);
/* Set the scale for the abscissa */
-void chart_write_xscale(struct chart *ch, double min, double max, int ticks);
+void chart_write_xscale(cairo_t *, struct chart_geometry *,
+ double min, double max, int ticks);
/* Set the scale for the ordinate */
-void chart_write_yscale(struct chart *ch, double smin, double smax, int ticks);
+void chart_write_yscale(cairo_t *, struct chart_geometry *,
+ double smin, double smax, int ticks);
-void chart_write_xlabel(struct chart *ch, const char *label) ;
+void chart_write_xlabel(cairo_t *, const struct chart_geometry *,
+ const char *label) ;
/* Write the ordinate label */
-void chart_write_ylabel(struct chart *ch, const char *label);
+void chart_write_ylabel(cairo_t *, const struct chart_geometry *,
+ const char *label);
-void chart_write_legend (struct chart *ch);
++void chart_write_legend (cairo_t *, const struct chart_geometry *);
+
#endif
--- /dev/null
--- /dev/null
++/* PSPP - a program for statistical analysis.
++ Copyright (C) 2009 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ 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, see <http://www.gnu.org/licenses/>. */
++
++#include <config.h>
++
++#include <output/charts/roc-chart.h>
++
++#include <output/chart-provider.h>
++#include <output/charts/cartesian.h>
++#include <output/charts/plot-chart.h>
++#include <data/casereader.h>
++#include <language/stats/roc.h>
++
++#include "xalloc.h"
++
++#include "gettext.h"
++#define _(msgid) gettext (msgid)
++
++struct roc_var
++ {
++ char *name;
++ struct casereader *cutpoint_reader;
++ };
++
++struct roc_chart
++ {
++ struct chart chart;
++ bool reference;
++ struct roc_var *vars;
++ size_t n_vars;
++ size_t allocated_vars;
++ };
++
++static const struct chart_class roc_chart_class;
++
++struct roc_chart *
++roc_chart_create (bool reference)
++{
++ struct roc_chart *rc = xmalloc (sizeof *rc);
++ chart_init (&rc->chart, &roc_chart_class);
++ rc->reference = reference;
++ rc->vars = NULL;
++ rc->n_vars = 0;
++ rc->allocated_vars = 0;
++ return rc;
++}
++
++void
++roc_chart_add_var (struct roc_chart *rc, const char *var_name,
++ const struct casereader *cutpoint_reader)
++{
++ struct roc_var *rv;
++
++ if (rc->n_vars >= rc->allocated_vars)
++ rc->vars = x2nrealloc (rc->vars, &rc->allocated_vars, sizeof *rc->vars);
++
++ rv = &rc->vars[rc->n_vars++];
++ rv->name = xstrdup (var_name);
++ rv->cutpoint_reader = casereader_clone (cutpoint_reader);
++}
++
++struct chart *
++roc_chart_get_chart (struct roc_chart *rc)
++{
++ return &rc->chart;
++}
++
++static void
++roc_chart_draw (const struct chart *chart, cairo_t *cr,
++ struct chart_geometry *geom)
++{
++ const struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
++ size_t i;
++
++ chart_write_title (cr, geom, _("ROC Curve"));
++ chart_write_xlabel (cr, geom, _("1 - Specificity"));
++ chart_write_ylabel (cr, geom, _("Sensitivity"));
++
++ chart_write_xscale (cr, geom, 0, 1, 5);
++ chart_write_yscale (cr, geom, 0, 1, 5);
++
++ if ( rc->reference )
++ {
++ chart_line (cr, geom, 1.0, 0,
++ 0.0, 1.0,
++ CHART_DIM_X);
++ }
++
++ for (i = 0; i < rc->n_vars; ++i)
++ {
++ const struct roc_var *rv = &rc->vars[i];
++ struct casereader *r = casereader_clone (rv->cutpoint_reader);
++ struct ccase *cc;
++
++ chart_vector_start (cr, geom, rv->name);
++ for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
++ {
++ double se = case_data_idx (cc, ROC_TP)->f;
++ double sp = case_data_idx (cc, ROC_TN)->f;
++
++ se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
++ sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
++
++ chart_vector (cr, geom, 1 - sp, se);
++ }
++ chart_vector_end (cr, geom);
++ casereader_destroy (r);
++ }
++
++ chart_write_legend (cr, geom);
++}
++
++static void
++roc_chart_destroy (struct chart *chart)
++{
++ struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
++ size_t i;
++
++ for (i = 0; i < rc->n_vars; i++)
++ {
++ struct roc_var *rv = &rc->vars[i];
++ free (rv->name);
++ casereader_destroy (rv->cutpoint_reader);
++ }
++ free (rc->vars);
++ free (rc);
++}
++
++static const struct chart_class roc_chart_class =
++ {
++ roc_chart_draw,
++ roc_chart_destroy
++ };
++
++
--- /dev/null
--- /dev/null
++/* PSPP - a program for statistical analysis.
++ Copyright (C) 2009 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 3 of the License, or
++ (at your option) any later version.
++
++ 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, see <http://www.gnu.org/licenses/>. */
++
++#ifndef OUTPUT_CHARTS_ROC_CHART_H
++#define OUTPUT_CHARTS_ROC_CHART_H 1
++
++#include <stdbool.h>
++
++struct casereader;
++
++struct roc_chart *roc_chart_create (bool reference);
++void roc_chart_add_var (struct roc_chart *, const char *var_name,
++ const struct casereader *cutpoint_reader);
++struct chart *roc_chart_get_chart (struct roc_chart *);
++
++#endif /* output/charts/roc-chart.h */
#include <data/data-out.h>
#include <data/format.h>
#include <data/value.h>
+ #include <data/dictionary.h>
#include <libpspp/assertion.h>
#include <libpspp/compiler.h>
#include <libpspp/misc.h>
#include <data/settings.h>
+#include "error.h"
#include "minmax.h"
#include "xalloc.h"
#define _(msgid) gettext (msgid)
\f
const struct som_table_class tab_table_class;
-static char *command_name;
/* Returns the font to use for a cell with the given OPTIONS. */
static enum outp_font
/* Creates a table with NC columns and NR rows. */
struct tab_table *
-tab_create (int nc, int nr, int reallocable UNUSED)
+tab_create (int nc, int nr)
{
struct tab_table *t;
t = pool_create_container (struct tab_table, container);
+ t->ref_cnt = 1;
t->col_style = TAB_COL_NONE;
- t->col_group = 0;
t->title = NULL;
t->flags = SOMF_NONE;
t->nr = nr;
memset (t->rv, UCHAR_MAX, nr * (nc + 1));
t->dim = NULL;
- t->w = t->h = NULL;
t->col_ofs = t->row_ofs = 0;
return t;
}
-/* Destroys table T. */
+/* Increases T's reference count and, if this causes T's
+ reference count to reach 0, destroys T. */
void
tab_destroy (struct tab_table *t)
{
- assert (t != NULL);
+ assert (t->ref_cnt > 0);
+ if (--t->ref_cnt > 0)
+ return;
+ if (t->dim_free != NULL)
+ t->dim_free (t->dim_aux);
free (t->title);
pool_destroy (t->container);
}
+/* Increases T's reference count. */
+void
+tab_ref (struct tab_table *t)
+{
+ assert (t->ref_cnt > 0);
+ t->ref_cnt++;
+}
+
/* Sets the width and height of a table, in columns and rows,
respectively. Use only to reduce the size of a table, since it
does not change the amount of allocated memory. */
}
if (nr != -1)
{
- assert (nr + t->row_ofs <= t->nr);
+ assert (nr + t->row_ofs <= tab_nr (t));
t->nr = nr + t->row_ofs;
}
}
tab_offset (t, 0, 0);
if (nc == -1)
- nc = t->nc;
+ nc = tab_nc (t);
if (nr == -1)
- nr = t->nr;
+ nr = tab_nr (t);
- assert (nc == t->nc);
+ assert (nc == tab_nc (t));
if (nc > t->cf)
{
- int mr1 = MIN (nr, t->nr);
- int mc1 = MIN (nc, t->nc);
+ int mr1 = MIN (nr, tab_nr (t));
+ int mc1 = MIN (nc, tab_nc (t));
struct substring *new_cc;
unsigned char *new_ct;
new_ct = pool_malloc (t->container, nr * nc);
for (r = 0; r < mr1; r++)
{
- memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
- memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
- memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
+ memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
+ memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
+ memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
}
pool_free (t->container, t->cc);
pool_free (t->container, t->ct);
t->ct = new_ct;
t->cf = nc;
}
- else if (nr != t->nr)
+ else if (nr != tab_nr (t))
{
t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
t->ct = pool_realloc (t->container, t->ct, nr * nc);
t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
- if (nr > t->nr)
+ if (nr > tab_nr (t))
{
- memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
- memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
- (nr - t->nr) * (nc + 1));
+ memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
+ memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
+ (nr - tab_nr (t)) * (nc + 1));
}
}
- memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
+ memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
t->nr = nr;
t->nc = nc;
/* Set up table T so that, when it is an appropriate size, it will be
displayed across the page in columns.
- STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
- as a unit. */
+ STYLE is a TAB_COL_* constant. */
void
-tab_columns (struct tab_table *t, int style, int group)
+tab_columns (struct tab_table *t, int style)
{
assert (t != NULL);
t->col_style = style;
- t->col_group = group;
}
\f
/* Rules. */
assert (t != NULL);
#if DEBUGGING
- if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
- || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
- || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+ if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
+ || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+ || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
{
printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
"table size (%d,%d)\n"),
x, t->col_ofs, x + t->col_ofs,
y1, t->row_ofs, y1 + t->row_ofs,
y2, t->row_ofs, y2 + t->row_ofs,
- t->nc, t->nr);
+ tab_nc (t), tab_nr (t));
return;
}
#endif
y2 += t->row_ofs;
assert (x > 0);
- assert (x < t->nc);
+ assert (x < tab_nc (t));
assert (y1 >= 0);
assert (y2 >= y1);
- assert (y2 <= t->nr);
+ assert (y2 <= tab_nr (t));
if (style != -1)
{
y += t->row_ofs;
assert (y >= 0);
- assert (y <= t->nr);
+ assert (y <= tab_nr (t));
assert (x2 >= x1 );
assert (x1 >= 0 );
- assert (x2 < t->nc);
+ assert (x2 < tab_nc (t));
if (style != -1)
{
assert (t != NULL);
#if DEBUGGING
- if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
- || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
- || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
- || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+ if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
+ || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
+ || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+ || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
{
printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
"in table size (%d,%d)\n"),
y1, t->row_ofs, y1 + t->row_ofs,
x2, t->col_ofs, x2 + t->col_ofs,
y2, t->row_ofs, y2 + t->row_ofs,
- t->nc, t->nr);
+ tab_nc (t), tab_nr (t));
NOT_REACHED ();
}
#endif
assert (y2 >= y1);
assert (x1 >= 0);
assert (y1 >= 0);
- assert (x2 < t->nc);
- assert (y2 < t->nr);
+ assert (x2 < tab_nc (t));
+ assert (y2 < tab_nr (t));
if (f_h != -1)
{
va_end (args);
}
-/* Set DIM_FUNC as the dimension function for table T. */
+/* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
+ dimension function for table T.
+
+ DIM_FUNC must not assume that it is called from the same
+ context as tab_dim; for example, table T might be kept in
+ memory and, thus, DIM_FUNC might be called after the currently
+ running command completes. If it is non-null, FREE_FUNC is
+ called when the table is destroyed, to allow any data
+ allocated for use by DIM_FUNC to be freed. */
void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
+tab_dim (struct tab_table *t,
+ tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
{
- assert (t != NULL && t->dim == NULL);
+ assert (t->dim == NULL);
t->dim = dim_func;
+ t->dim_free = free_func;
t->dim_aux = aux;
}
wrapping. The width will be no larger than the page width minus
left and right rule widths. */
int
-tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
+tab_natural_width (const struct tab_rendering *r, int col)
{
- int width;
+ const struct tab_table *t = r->table;
+ int width, row, max_width;
- assert (t != NULL && c >= 0 && c < t->nc);
- {
- int r;
+ assert (col >= 0 && col < tab_nc (t));
- for (width = r = 0; r < t->nr; r++)
- {
- struct outp_text text;
- unsigned char opt = t->ct[c + r * t->cf];
- int w;
+ width = 0;
+ for (row = 0; row < tab_nr (t); row++)
+ {
+ struct outp_text text;
+ unsigned char opt = t->ct[col + row * t->cf];
+ int w;
- if (opt & (TAB_JOIN | TAB_EMPTY))
- continue;
+ if (opt & (TAB_JOIN | TAB_EMPTY))
+ continue;
- text.string = t->cc[c + r * t->cf];
- text.justification = OUTP_LEFT;
- text.font = options_to_font (opt);
- text.h = text.v = INT_MAX;
+ text.string = t->cc[col + row * t->cf];
+ text.justification = OUTP_LEFT;
+ text.font = options_to_font (opt);
+ text.h = text.v = INT_MAX;
- d->class->text_metrics (d, &text, &w, NULL);
- if (w > width)
- width = w;
- }
- }
+ r->driver->class->text_metrics (r->driver, &text, &w, NULL);
+ if (w > width)
+ width = w;
+ }
if (width == 0)
{
/* FIXME: This is an ugly kluge to compensate for the fact
that we don't let joined cells contribute to column
widths. */
- width = d->prop_em_width * 8;
+ width = r->driver->prop_em_width * 8;
}
- {
- const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
-
- if (width > clamp)
- width = clamp;
- }
-
- return width;
+ max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
+ return MIN (width, max_width);
}
/* Returns the natural height of row R in table T for driver D, that
is, the minimum height necessary to display the information in the
cell at the widths set for each column. */
int
-tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
+tab_natural_height (const struct tab_rendering *r, int row)
{
- int height;
+ const struct tab_table *t = r->table;
+ int height, col;
- assert (t != NULL && r >= 0 && r < t->nr);
+ assert (row >= 0 && row < tab_nr (t));
- {
- int c;
-
- for (height = d->font_height, c = 0; c < t->nc; c++)
- {
- struct outp_text text;
- unsigned char opt = t->ct[c + r * t->cf];
- int h;
-
- if (opt & (TAB_JOIN | TAB_EMPTY))
- continue;
-
- text.string = t->cc[c + r * t->cf];
- text.justification = OUTP_LEFT;
- text.font = options_to_font (opt);
- text.h = t->w[c];
- text.v = INT_MAX;
- d->class->text_metrics (d, &text, NULL, &h);
-
- if (h > height)
- height = h;
- }
- }
+ height = r->driver->font_height;
+ for (col = 0; col < tab_nc (t); col++)
+ {
+ struct outp_text text;
+ unsigned char opt = t->ct[col + row * t->cf];
+ int h;
+
+ if (opt & (TAB_JOIN | TAB_EMPTY))
+ continue;
+
+ text.string = t->cc[col + row * t->cf];
+ text.justification = OUTP_LEFT;
+ text.font = options_to_font (opt);
+ text.h = r->w[col];
+ text.v = INT_MAX;
+ r->driver->class->text_metrics (r->driver, &text, NULL, &h);
+
+ if (h > height)
+ height = h;
+ }
return height;
}
/* Callback function to set all columns and rows to their natural
dimensions. Not really meant to be called directly. */
void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
- void *aux UNUSED)
+tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
{
+ const struct tab_table *t = r->table;
int i;
- assert (t != NULL);
-
- for (i = 0; i < t->nc; i++)
- t->w[i] = tab_natural_width (t, d, i);
+ for (i = 0; i < tab_nc (t); i++)
+ r->w[i] = tab_natural_width (r, i);
- for (i = 0; i < t->nr; i++)
- t->h[i] = tab_natural_height (t, d, i);
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = tab_natural_height (r, i);
}
\f
from V, displayed with format spec F. */
void
tab_value (struct tab_table *table, int c, int r, unsigned char opt,
- const union value *v, const struct fmt_spec *f)
+ const union value *v, const struct dictionary *dict,
+ const struct fmt_spec *f)
{
char *contents;
assert (table != NULL && v != NULL && f != NULL);
#if DEBUGGING
if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
+ || c + table->col_ofs >= tab_nc (table)
+ || r + table->row_ofs >= tab_nr (table))
{
printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
"(%d,%d)\n",
c, table->col_ofs, c + table->col_ofs,
r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
+ tab_nc (table), tab_nr (table));
return;
}
#endif
- contents = pool_alloc (table->container, f->w);
- table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
- table->ct[c + r * table->cf] = opt;
+ contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
- data_out (v, f, contents);
+ table->cc[c + r * table->cf] = ss_cstr (contents);
+ table->ct[c + r * table->cf] = opt;
}
/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
double val, int w, int d)
{
- char *contents;
- char buf[40], *cp;
+ char *s, *cp;
struct fmt_spec f;
union value double_value;
assert (table != NULL && w <= 40);
assert (c >= 0);
- assert (c < table->nc);
+ assert (c < tab_nc (table));
assert (r >= 0);
- assert (r < table->nr);
+ assert (r < tab_nr (table));
f = fmt_for_output (FMT_F, w, d);
#if DEBUGGING
if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
+ || c + table->col_ofs >= tab_nc (table)
+ || r + table->row_ofs >= tab_nr (table))
{
printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
"(%d,%d)\n",
c, table->col_ofs, c + table->col_ofs,
r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
+ tab_nc (table), tab_nr (table));
return;
}
#endif
double_value.f = val;
- data_out (&double_value, &f, buf);
+ s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
- cp = buf;
- while (isspace ((unsigned char) *cp) && cp < &buf[w])
+ cp = s;
+ while (isspace ((unsigned char) *cp) && cp < &s[w])
cp++;
- f.w = w - (cp - buf);
+ f.w = w - (cp - s);
- contents = pool_alloc (table->container, f.w);
- table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
+ table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
table->ct[c + r * table->cf] = opt;
- memcpy (contents, cp, f.w);
}
/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
tab_double (struct tab_table *table, int c, int r, unsigned char opt,
double val, const struct fmt_spec *fmt)
{
- int w;
- char *contents;
- char buf[40], *cp;
-
- union value double_value;
+ struct substring ss;
+ union value double_value ;
assert (table != NULL);
assert (c >= 0);
- assert (c < table->nc);
+ assert (c < tab_nc (table));
assert (r >= 0);
- assert (r < table->nr);
+ assert (r < tab_nr (table));
if ( fmt == NULL)
fmt = settings_get_format ();
#if DEBUGGING
if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
+ || c + table->col_ofs >= tab_nc (table)
+ || r + table->row_ofs >= tab_nr (table))
{
printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
"(%d,%d)\n",
c, table->col_ofs, c + table->col_ofs,
r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
+ tab_nc (table), tab_nr (table));
return;
}
#endif
double_value.f = val;
- data_out (&double_value, fmt, buf);
+ ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
- cp = buf;
- while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
- cp++;
- w = fmt->w - (cp - buf);
+ ss_ltrim (&ss, ss_cstr (" "));
- contents = pool_alloc (table->container, w);
- table->cc[c + r * table->cf] = ss_buffer (contents, w);
+ table->cc[c + r * table->cf] = ss;
table->ct[c + r * table->cf] = opt;
- memcpy (contents, cp, w);
}
static void
do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
{
- assert (c >= 0);
- assert (r >= 0);
- assert (c < table->nc);
- assert (r < table->nr);
+ assert (c >= 0 );
+ assert (r >= 0 );
+ assert (c < tab_nc (table));
+ assert (r < tab_nr (table));
#if DEBUGGING
if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
+ || c + table->col_ofs >= tab_nc (table)
+ || r + table->row_ofs >= tab_nr (table))
{
printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
"(%d,%d)\n",
c, table->col_ofs, c + table->col_ofs,
r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
+ tab_nc (table), tab_nr (table));
return;
}
#endif
assert (y1 + table->row_ofs >= 0);
assert (y2 >= y1);
assert (x2 >= x1);
- assert (y2 + table->row_ofs < table->nr);
- assert (x2 + table->col_ofs < table->nc);
+ assert (y2 + table->row_ofs < tab_nr (table));
+ assert (x2 + table->col_ofs < tab_nc (table));
#if DEBUGGING
- if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
- || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
- || x2 < x1 || x2 + table->col_ofs >= table->nc
- || y2 < y2 || y2 + table->row_ofs >= table->nr)
+ if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
+ || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
+ || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
+ || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
{
printf ("tab_joint_text(): bad cell "
"(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
y1, table->row_ofs, y1 + table->row_ofs,
x2, table->col_ofs, x2 + table->col_ofs,
y2, table->row_ofs, y2 + table->row_ofs,
- table->nc, table->nr);
+ tab_nc (table), tab_nr (table));
return;
}
#endif
tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
j = pool_alloc (table->container, sizeof *j);
- j->hit = 0;
j->x1 = x1 + table->col_ofs;
j->y1 = y1 + table->row_ofs;
j->x2 = ++x2 + table->col_ofs;
pool_vasprintf (table->container, format, args));
va_end (args);
}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
-void
-tab_raw (struct tab_table *table, int c, int r, unsigned opt,
- struct substring *string)
-{
- assert (table != NULL && string != NULL);
-
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
- {
- printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- table->cc[c + r * table->cf] = *string;
- table->ct[c + r * table->cf] = opt;
-}
\f
/* Miscellaneous. */
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = d->font_height;
+ r->w[0] = tab_natural_width (r, 0);
+ r->h[0] = r->driver->font_height;
}
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+wrap_dim (struct tab_rendering *r, void *aux UNUSED)
{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = tab_natural_height (t, d, 0);
+ r->w[0] = tab_natural_width (r, 0);
+ r->h[0] = tab_natural_height (r, 0);
}
static void
{
do_tab_text (t, 0, 0, options, text);
tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
- tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
+ tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
tab_submit (t);
}
void
tab_output_text (int options, const char *text)
{
- struct tab_table *table = tab_create (1, 1, 0);
+ struct tab_table *table = tab_create (1, 1);
do_tab_output_text (table, options, pool_strdup (table->container, text));
}
struct tab_table *table;
va_list args;
- table = tab_create (1, 1, 0);
+ table = tab_create (1, 1);
va_start (args, format);
do_tab_output_text (table, options,
assert (t != NULL);
#if DEBUGGING
- if (row < -1 || row > t->nr)
+ if (row < -1 || row > tab_nr (t))
{
- printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
+ printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
NOT_REACHED ();
}
- if (col < -1 || col > t->nc)
+ if (col < -1 || col > tab_nc (t))
{
- printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
+ printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
NOT_REACHED ();
}
#endif
assert (t != NULL);
t->cc += t->cf;
t->ct += t->cf;
- if (++t->row_ofs >= t->nr)
- tab_realloc (t, -1, t->nr * 4 / 3);
+ if (++t->row_ofs >= tab_nr (t))
+ tab_realloc (t, -1, tab_nr (t) * 4 / 3);
}
\f
-static struct tab_table *t;
-static struct outp_driver *d;
-static int tab_hit;
+/* Return the number of columns and rows in the table into N_COLUMNS
+ and N_ROWS, respectively. */
+static void
+tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
+{
+ struct tab_table *t = t_->ext;
+ *n_columns = t->nc;
+ *n_rows = t->nr;
+}
-/* Set the current table to TABLE. */
+/* Return the column style for this table into STYLE. */
static void
-tabi_table (struct som_entity *table)
+tabi_columns (struct som_entity *t_, int *style)
{
- assert (table != NULL);
- assert (table->type == SOM_TABLE);
+ struct tab_table *t = t_->ext;
+ *style = t->col_style;
+}
- t = table->ext;
- tab_offset (t, 0, 0);
+/* Return the number of header rows/columns on the left, right, top,
+ and bottom sides into HL, HR, HT, and HB, respectively. */
+static void
+tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
+{
+ struct tab_table *t = t_->ext;
+ *hl = t->l;
+ *hr = t->r;
+ *ht = t->t;
+ *hb = t->b;
+}
- assert (t->w == NULL && t->h == NULL);
- t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
- t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
- t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
- t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
+/* Return flags set for the current table into FLAGS. */
+static void
+tabi_flags (struct som_entity *t_, unsigned *flags)
+{
+ struct tab_table *t = t_->ext;
+ *flags = t->flags;
}
/* Returns the line style to use for spacing purposes for a rule
}
}
-/* Set the current output device to DRIVER. */
-static void
-tabi_driver (struct outp_driver *driver)
+static void *
+tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
+ int hl, int hr, int ht, int hb)
{
- int c, r;
+ const struct tab_table *t = t_->ext;
+ struct tab_rendering *r;
+ int col, row;
int i;
- assert (driver != NULL);
- d = driver;
+ tab_offset (t_->ext, 0, 0);
+
+ r = xmalloc (sizeof *r);
+ r->table = t;
+ r->driver = driver;
+ r->w = xnmalloc (tab_nc (t), sizeof *r->w);
+ r->h = xnmalloc (tab_nr (t), sizeof *r->h);
+ r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
+ r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
+ r->l = hl;
+ r->r = hr;
+ r->t = ht;
+ r->b = hb;
/* Figure out sizes of rules. */
- for (r = 0; r <= t->nr; r++)
+ for (row = 0; row <= tab_nr (t); row++)
{
int width = 0;
- for (c = 0; c < t->nc; c++)
+ for (col = 0; col < tab_nc (t); col++)
{
- unsigned char rh = t->rh[c + r * t->cf];
+ unsigned char rh = t->rh[col + row * t->cf];
int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
if (w > width)
width = w;
}
- t->hrh[r] = width;
+ r->hrh[row] = width;
}
- for (c = 0; c <= t->nc; c++)
+ for (col = 0; col <= tab_nc (t); col++)
{
int width = 0;
- for (r = 0; r < t->nr; r++)
+ for (row = 0; row < tab_nr (t); row++)
{
- unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
+ unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
int w;
if (*rv == UCHAR_MAX)
- *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
+ *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
w = driver->vert_line_width[rule_to_spacing_type (*rv)];
if (w > width)
width = w;
}
- t->wrv[c] = width;
+ r->wrv[col] = width;
}
-#if DEBUGGING
- for (i = 0; i < t->nr; i++)
- t->h[i] = -1;
- for (i = 0; i < t->nc; i++)
- t->w[i] = -1;
-#endif
-
- assert (t->dim != NULL);
- t->dim (t, d, t->dim_aux);
+ /* Determine row heights and columns widths. */
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = -1;
+ for (i = 0; i < tab_nc (t); i++)
+ r->w[i] = -1;
-#if DEBUGGING
- {
- int error = 0;
-
- for (i = 0; i < t->nr; i++)
- {
- if (t->h[i] == -1)
- {
- printf ("Table row %d height not initialized.\n", i);
- error = 1;
- }
- assert (t->h[i] > 0);
- }
+ t->dim (r, t->dim_aux);
- for (i = 0; i < t->nc; i++)
- {
- if (t->w[i] == -1)
- {
- printf ("Table column %d width not initialized.\n", i);
- error = 1;
- }
- assert (t->w[i] > 0);
- }
- }
-#endif
+ for (i = 0; i < tab_nr (t); i++)
+ if (r->h[i] < 0)
+ error (0, 0, "height of table row %d is %d (not initialized?)",
+ i, r->h[i]);
+ for (i = 0; i < tab_nc (t); i++)
+ if (r->w[i] < 0)
+ error (0, 0, "width of table column %d is %d (not initialized?)",
+ i, r->w[i]);
/* Add up header sizes. */
- for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
- t->wl += t->w[i] + t->wrv[i + 1];
- for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
- t->ht += t->h[i] + t->hrh[i + 1];
- for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
- t->wr += t->w[i] + t->wrv[i + 1];
- for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
- t->hb += t->h[i] + t->hrh[i + 1];
+ for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
+ r->wl += r->w[i] + r->wrv[i + 1];
+ for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
+ r->ht += r->h[i] + r->hrh[i + 1];
+ for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
+ r->wr += r->w[i] + r->wrv[i + 1];
+ for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
+ r->hb += r->h[i] + r->hrh[i + 1];
/* Title. */
if (!(t->flags & SOMF_NO_TITLE))
- t->ht += d->font_height;
+ r->ht += driver->font_height;
+
+ return r;
}
-/* Return the number of columns and rows in the table into N_COLUMNS
- and N_ROWS, respectively. */
static void
-tabi_count (int *n_columns, int *n_rows)
+tabi_render_free (void *r_)
{
- assert (n_columns != NULL && n_rows != NULL);
- *n_columns = t->nc;
- *n_rows = t->nr;
-}
+ struct tab_rendering *r = r_;
-static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
+ free (r->w);
+ free (r->h);
+ free (r->hrh);
+ free (r->wrv);
+ free (r);
+}
/* Return the horizontal and vertical size of the entire table,
including headers, for the current output device, into HORIZ and
VERT. */
static void
-tabi_area (int *horiz, int *vert)
+tabi_area (void *r_, int *horiz, int *vert)
{
- assert (horiz != NULL && vert != NULL);
-
- {
- int w, c;
-
- for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
- c < t->nc - t->r; c++)
- w += t->w[c] + t->wrv[c];
- *horiz = w;
- }
-
- {
- int h, r;
- for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
- r < t->nr - t->b; r++)
- h += t->h[r] + t->hrh[r];
- *vert = h;
- }
-}
-
-/* Return the column style for this table into STYLE. */
-static void
-tabi_columns (int *style)
-{
- assert (style != NULL);
- *style = t->col_style;
-}
-
-/* Return the number of header rows/columns on the left, right, top,
- and bottom sides into HL, HR, HT, and HB, respectively. */
-static void
-tabi_headers (int *hl, int *hr, int *ht, int *hb)
-{
- assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
- *hl = t->l;
- *hr = t->r;
- *ht = t->t;
- *hb = t->b;
+ struct tab_rendering *r = r_;
+ const struct tab_table *t = r->table;
+ int width, col;
+ int height, row;
+
+ width = 0;
+ for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
+ col < tab_nc (t) - r->r; col++)
+ width += r->w[col] + r->wrv[col];
+ *horiz = width;
+
+ height = 0;
+ for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
+ row < tab_nr (t) - tab_b (t); row++)
+ height += r->h[row] + r->hrh[row];
+ *vert = height;
}
/* Determines the number of rows or columns (including appropriate
space the selected rows/columns (including appropriate headers)
filled. */
static void
-tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
+tabi_cumulate (void *r_, int cumtype, int start, int *end,
+ int max, int *actual)
{
- int n;
- int *d;
- int *r;
+ const struct tab_rendering *r = r_;
+ const struct tab_table *t = r->table;
+ int limit;
+ int *cells, *rules;
int total;
+ int idx;
assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
if (cumtype == SOM_ROWS)
{
- assert (start >= 0 && start < t->nr);
- n = t->nr - t->b;
- d = &t->h[start];
- r = &t->hrh[start + 1];
- total = t->ht + t->hb;
+ assert (start >= 0 && start < tab_nr (t));
+ limit = tab_nr (t) - r->b;
+ cells = &r->h[start];
+ rules = &r->hrh[start + 1];
+ total = r->ht + r->hb;
}
else
{
- assert (start >= 0 && start < t->nc);
- n = t->nc - t->r;
- d = &t->w[start];
- r = &t->wrv[start + 1];
- total = t->wl + t->wr;
+ assert (start >= 0 && start < tab_nc (t));
+ limit = tab_nc (t) - tab_r (t);
+ cells = &r->w[start];
+ rules = &r->wrv[start + 1];
+ total = r->wl + r->wr;
}
- total += *d++;
+ total += *cells++;
if (total > max)
{
if (end)
return;
}
- {
- int x;
-
- for (x = start + 1; x < n; x++)
- {
- int amt = *d++ + *r++;
-
- total += amt;
- if (total > max)
- {
- total -= amt;
- break;
- }
- }
-
- if (end)
- *end = x;
-
- if (actual)
- *actual = total;
- }
-}
-
-/* Return flags set for the current table into FLAGS. */
-static void
-tabi_flags (unsigned *flags)
-{
- assert (flags != NULL);
- *flags = t->flags;
-}
-
-/* Returns true if the table will fit in the given page WIDTH,
- false otherwise. */
-static bool
-tabi_fits_width (int width)
-{
- int i;
-
- for (i = t->l; i < t->nc - t->r; i++)
- if (t->wl + t->wr + t->w[i] > width)
- return false;
-
- return true;
-}
+ for (idx = start + 1; idx < limit; idx++)
+ {
+ int amt = *cells++ + *rules++;
-/* Returns true if the table will fit in the given page LENGTH,
- false otherwise. */
-static bool
-tabi_fits_length (int length)
-{
- int i;
+ total += amt;
+ if (total > max)
+ {
+ total -= amt;
+ break;
+ }
+ }
- for (i = t->t; i < t->nr - t->b; i++)
- if (t->ht + t->hb + t->h[i] > length)
- return false;
+ if (end)
+ *end = idx;
- return true;
-}
-
-/* Sets the number of header rows/columns on the left, right, top,
- and bottom sides to HL, HR, HT, and HB, respectively. */
-static void
-tabi_set_headers (int hl, int hr, int ht, int hb)
-{
- t->l = hl;
- t->r = hr;
- t->t = ht;
- t->b = hb;
+ if (actual)
+ *actual = total;
}
/* Render title for current table, with major index X and minor index
Y. Y may be zero, or X and Y may be zero, but X should be nonzero
if Y is nonzero. */
static void
-tabi_title (int x, int y)
+tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
+ const char *command_name)
{
- char buf[1024];
- char *cp;
+ const struct tab_rendering *r = r_;
+ const struct tab_table *t = r->table;
+ struct outp_text text;
+ struct string title;
if (t->flags & SOMF_NO_TITLE)
return;
- cp = spprintf (buf, "%d.%d", table_num, subtable_num);
+ ds_init_empty (&title);
+ ds_put_format (&title,"%d.%d", table_num, subtable_num);
if (x && y)
- cp = spprintf (cp, "(%d:%d)", x, y);
+ ds_put_format (&title, "(%d:%d)", x, y);
else if (x)
- cp = spprintf (cp, "(%d)", x);
+ ds_put_format (&title, "(%d)", x);
if (command_name != NULL)
- cp = spprintf (cp, " %s", command_name);
- cp = stpcpy (cp, ". ");
+ ds_put_format (&title, " %s", command_name);
+ ds_put_cstr (&title, ". ");
if (t->title != NULL)
- {
- size_t length = strlen (t->title);
- memcpy (cp, t->title, length);
- cp += length;
- }
- *cp = 0;
-
- {
- struct outp_text text;
-
- text.font = OUTP_PROPORTIONAL;
- text.justification = OUTP_LEFT;
- text.string = ss_buffer (buf, cp - buf);
- text.h = d->width;
- text.v = d->font_height;
- text.x = 0;
- text.y = d->cp_y;
- d->class->text_draw (d, &text);
- }
+ ds_put_cstr (&title, t->title);
+
+ text.font = OUTP_PROPORTIONAL;
+ text.justification = OUTP_LEFT;
+ text.string = ds_ss (&title);
+ text.h = r->driver->width;
+ text.v = r->driver->font_height;
+ text.x = 0;
+ text.y = r->driver->cp_y;
+ r->driver->class->text_draw (r->driver, &text);
+
+ ds_destroy (&title);
}
-static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
+static int render_strip (const struct tab_rendering *,
+ int x, int y, int r, int c1, int c2, int r1, int r2);
-/* Renders columns C0...C1, plus headers, of rows R0...R1,
- at the given vertical position Y.
- C0 and C1 count vertical rules as columns,
- but R0 and R1 do not count horizontal rules as rows.
- Returns the vertical position after rendering. */
-static int
-render_rows (int y, int c0, int c1, int r0, int r1)
+static void
+add_range (int ranges[][2], int *np, int start, int end)
{
- int r;
- for (r = r0; r < r1; r++)
+ int n = *np;
+ if (n == 0 || start > ranges[n - 1][1])
{
- int x = d->cp_x;
- x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
- x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
- x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
- y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
+ ranges[n][0] = start;
+ ranges[n][1] = end;
+ ++*np;
}
- return y;
+ else
+ ranges[n - 1][1] = end;
}
/* Draws table region (C0,R0)-(C1,R1), plus headers, at the
current position on the current output device. */
static void
-tabi_render (int c0, int r0, int c1, int r1)
+tabi_render (void *r_, int c0, int r0, int c1, int r1)
{
- int y;
+ const struct tab_rendering *r = r_;
+ const struct tab_table *t = r->table;
+ int rows[3][2], cols[3][2];
+ int n_row_ranges, n_col_ranges;
+ int y, i;
+
+ /* Rows to render, counting horizontal rules as rows. */
+ n_row_ranges = 0;
+ add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
+ add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
+ add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
+ tab_nr (t) * 2 + 1);
+
+ /* Columns to render, counting vertical rules as columns. */
+ n_col_ranges = 0;
+ add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
+ add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
+ add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
+
+ y = r->driver->cp_y;
+ if (!(t->flags & SOMF_NO_TITLE))
+ y += r->driver->font_height;
+ for (i = 0; i < n_row_ranges; i++)
+ {
+ int row;
- tab_hit++;
+ for (row = rows[i][0]; row < rows[i][1]; row++)
+ {
+ int x, j;
- y = d->cp_y;
- if (!(t->flags & SOMF_NO_TITLE))
- y += d->font_height;
+ x = r->driver->cp_x;
+ for (j = 0; j < n_col_ranges; j++)
+ x = render_strip (r, x, y, row,
+ cols[j][0], cols[j][1],
+ rows[i][0], rows[i][1]);
- y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
- y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
- y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
+ y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
+ }
+ }
}
const struct som_table_class tab_table_class =
{
- tabi_table,
- tabi_driver,
-
tabi_count,
- tabi_area,
- NULL,
- NULL,
tabi_columns,
- NULL,
tabi_headers,
- NULL,
- tabi_cumulate,
tabi_flags,
- tabi_fits_width,
- tabi_fits_length,
- NULL,
- NULL,
- tabi_set_headers,
+ tabi_render_init,
+ tabi_render_free,
+ tabi_area,
+ tabi_cumulate,
tabi_title,
tabi_render,
};
/* Returns the horizontal rule at the given column and row. */
static int
-get_hrule (int c, int r)
+get_hrule (const struct tab_table *t, int col, int row)
{
- return t->rh[c + r * t->cf];
+ return t->rh[col + row * t->cf];
}
/* Returns the vertical rule at the given column and row. */
static int
-get_vrule (int c, int r)
+get_vrule (const struct tab_table *t, int col, int row)
{
- return t->rv[c + r * (t->cf + 1)];
+ return t->rv[col + row * (t->cf + 1)];
}
/* Renders the horizontal rule at the given column and row
at (X,Y) on the page. */
static void
-render_horz_rule (int x, int y, int c, int r)
+render_horz_rule (const struct tab_rendering *r,
+ int x, int y, int col, int row)
{
- enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
+ enum outp_line_style style;
+ style = rule_to_draw_type (get_hrule (r->table, col, row));
if (style != OUTP_L_NONE)
- d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
- OUTP_L_NONE, style, OUTP_L_NONE, style);
+ r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
+ OUTP_L_NONE, style, OUTP_L_NONE, style);
}
/* Renders the vertical rule at the given column and row
at (X,Y) on the page. */
static void
-render_vert_rule (int x, int y, int c, int r)
+render_vert_rule (const struct tab_rendering *r,
+ int x, int y, int col, int row)
{
- enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
+ enum outp_line_style style;
+ style = rule_to_draw_type (get_vrule (r->table, col, row));
if (style != OUTP_L_NONE)
- d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
- style, OUTP_L_NONE, style, OUTP_L_NONE);
+ r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
+ style, OUTP_L_NONE, style, OUTP_L_NONE);
}
/* Renders the rule intersection at the given column and row
at (X,Y) on the page. */
static void
-render_rule_intersection (int x, int y, int c, int r)
+render_rule_intersection (const struct tab_rendering *r,
+ int x, int y, int col, int row)
{
+ const struct tab_table *t = r->table;
+
/* Bounds of intersection. */
int x0 = x;
int y0 = y;
- int x1 = x + t->wrv[c];
- int y1 = y + t->hrh[r];
+ int x1 = x + r->wrv[col];
+ int y1 = y + r->hrh[row];
/* Lines on each side of intersection. */
- int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
- int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
- int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
- int right = c < t->nc ? get_hrule (c, r) : TAL_0;
+ int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
+ int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
+ int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
+ int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
/* Output style for each line. */
enum outp_line_style o_top = rule_to_draw_type (top);
if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
|| o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
- d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
+ r->driver->class->line (r->driver, x0, y0, x1, y1,
+ o_top, o_left, o_bottom, o_right);
}
/* Returns the width of columns C1...C2 exclusive,
including interior but not exterior rules. */
static int
-strip_width (int c1, int c2)
+strip_width (const struct tab_rendering *r, int c1, int c2)
{
int width = 0;
int c;
for (c = c1; c < c2; c++)
- width += t->w[c] + t->wrv[c + 1];
+ width += r->w[c] + r->wrv[c + 1];
if (c1 < c2)
- width -= t->wrv[c2];
+ width -= r->wrv[c2];
return width;
}
/* Returns the height of rows R1...R2 exclusive,
including interior but not exterior rules. */
static int
-strip_height (int r1, int r2)
+strip_height (const struct tab_rendering *r, int r1, int r2)
{
int height = 0;
- int r;
+ int row;
- for (r = r1; r < r2; r++)
- height += t->h[r] + t->hrh[r + 1];
+ for (row = r1; row < r2; row++)
+ height += r->h[row] + r->hrh[row + 1];
if (r1 < r2)
- height -= t->hrh[r2];
+ height -= r->hrh[r2];
return height;
}
page. Also renders joined cells that extend as far to the
right as C1 and as far down as R1. */
static void
-render_cell (int x, int y, int c, int r, int c1, int r1)
+render_cell (const struct tab_rendering *r,
+ int x, int y, int col, int row, int c1, int r1)
{
- const int index = c + (r * t->cf);
+ const struct tab_table *t = r->table;
+ const int index = col + (row * t->cf);
unsigned char type = t->ct[index];
struct substring *content = &t->cc[index];
text.font = options_to_font (type);
text.justification = translate_justification (type);
text.string = *content;
- text.h = t->w[c];
- text.v = t->h[r];
+ text.h = r->w[col];
+ text.v = r->h[row];
text.x = x;
text.y = y;
- d->class->text_draw (d, &text);
+ r->driver->class->text_draw (r->driver, &text);
}
}
else
struct tab_joined_cell *j
= (struct tab_joined_cell *) ss_data (*content);
- if (j->hit != tab_hit)
+ if (j->x1 == col && j->y1 == row)
{
- j->hit = tab_hit;
-
- if (j->x1 == c && j->y1 == r)
- {
- struct outp_text text;
- text.font = options_to_font (type);
- text.justification = translate_justification (type);
- text.string = j->contents;
- text.x = x;
- text.y = y;
- text.h = strip_width (j->x1, MIN (j->x2, c1));
- text.v = strip_height (j->y1, MIN (j->y2, r1));
- d->class->text_draw (d, &text);
- }
+ struct outp_text text;
+ text.font = options_to_font (type);
+ text.justification = translate_justification (type);
+ text.string = j->contents;
+ text.x = x;
+ text.y = y;
+ text.h = strip_width (r, j->x1, MIN (j->x2, c1));
+ text.v = strip_height (r, j->y1, MIN (j->y2, r1));
+ r->driver->class->text_draw (r->driver, &text);
}
}
}
/* Render contiguous strip consisting of columns C0...C1, exclusive,
- on row R, at (X,Y). Returns X position after rendering.
+ on row ROW, at (X,Y). Returns X position after rendering.
Also renders joined cells that extend beyond that strip,
cropping them to lie within rendering region (C0,R0)-(C1,R1).
C0 and C1 count vertical rules as columns.
- R counts horizontal rules as rows, but R0 and R1 do not. */
+ ROW counts horizontal rules as rows, but R0 and R1 do not. */
static int
-render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
+render_strip (const struct tab_rendering *r,
+ int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
{
- int c;
+ int col;
- for (c = c0; c < c1; c++)
- if (c & 1)
+ for (col = c0; col < c1; col++)
+ if (col & 1)
{
- if (r & 1)
- render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
+ if (row & 1)
+ render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
else
- render_horz_rule (x, y, c / 2, r / 2);
- x += t->w[c / 2];
+ render_horz_rule (r, x, y, col / 2, row / 2);
+ x += r->w[col / 2];
}
else
{
- if (r & 1)
- render_vert_rule (x, y, c / 2, r / 2);
+ if (row & 1)
+ render_vert_rule (r, x, y, col / 2, row / 2);
else
- render_rule_intersection (x, y, c / 2, r / 2);
- x += t->wrv[c / 2];
+ render_rule_intersection (r, x, y, col / 2, row / 2);
+ x += r->wrv[col / 2];
}
return x;
}
-
-/* Sets COMMAND_NAME as the name of the current command,
- for embedding in output. */
-void
-tab_set_command_name (const char *command_name_)
-{
- free (command_name);
- command_name = command_name_ ? xstrdup (command_name_) : NULL;
-}
{
int x1, y1;
int x2, y2;
- int hit;
struct substring contents;
};
struct outp_driver;
struct tab_table;
-typedef void tab_dim_func (struct tab_table *, struct outp_driver *,
- void *aux);
+struct tab_rendering;
+
+typedef void tab_dim_func (struct tab_rendering *, void *aux);
+typedef void tab_dim_free_func (void *aux);
/* A table. */
struct tab_table
{
struct pool *container;
+ int ref_cnt; /* Reference count. */
/* Contents. */
int col_style; /* Columns: One of TAB_COL_*. */
- int col_group; /* Number of rows per column group. */
char *title; /* Table title. */
unsigned flags; /* SOMF_*. */
int nc, nr; /* Number of columns, rows. */
unsigned char *ct; /* Cell types; unsigned char[nr][nc]. */
unsigned char *rh; /* Horiz rules; unsigned char[nr+1][nc]. */
unsigned char *rv; /* Vert rules; unsigned char[nr][nc+1]. */
+
+ /* Calculating row and column dimensions. */
tab_dim_func *dim; /* Calculates cell widths and heights. */
+ tab_dim_free_func *dim_free; /* Frees space allocated for dim function. */
void *dim_aux; /* Auxiliary data for dim function. */
- /* Calculated during output. */
- int *w; /* Column widths; [nc]. */
- int *h; /* Row heights; [nr]. */
- int *hrh; /* Heights of horizontal rules; [nr+1]. */
- int *wrv; /* Widths of vertical rules; [nc+1]. */
- int wl, wr, ht, hb; /* Width/height of header rows/columns. */
-
/* Editing info. */
int col_ofs, row_ofs; /* X and Y offsets. */
};
-/* Number of rows in TABLE. */
-#define tab_nr(TABLE) ((TABLE)->nr)
-
-/* Number of columns in TABLE. */
-#define tab_nc(TABLE) ((TABLE)->nc)
+/* Number of rows or columns in TABLE. */
+static inline int tab_nr (const struct tab_table *table) { return table->nr; }
+static inline int tab_nc (const struct tab_table *table) { return table->nc; }
-/* Number of left header columns in TABLE. */
-#define tab_l(TABLE) ((TABLE)->l)
+/* Number of left/right/top/bottom header columns/rows in TABLE. */
+static inline int tab_l (const struct tab_table *table) { return table->l; }
+static inline int tab_r (const struct tab_table *table) { return table->r; }
+static inline int tab_t (const struct tab_table *table) { return table->t; }
+static inline int tab_b (const struct tab_table *table) { return table->b; }
-/* Number of right header columns in TABLE. */
-#define tab_r(TABLE) ((TABLE)->r)
+struct tab_rendering
+ {
+ const struct tab_table *table;
+ struct outp_driver *driver;
-/* Number of top header rows in TABLE. */
-#define tab_t(TABLE) ((TABLE)->t)
+ int *w; /* Column widths; [nc]. */
+ int *h; /* Row heights; [nr]. */
+ int *hrh; /* Heights of horizontal rules; [nr+1]. */
+ int *wrv; /* Widths of vertical rules; [nc+1]. */
-/* Number of bottom header rows in TABLE. */
-#define tab_b(TABLE) ((TABLE)->b)
+ /* These fields would be redundant with those in struct tab_table, except
+ that a table will be rendered with fewer header rows or columns than
+ requested when we are pressed for space. */
+ int l, r, t, b; /* Number of header rows/columns. */
+ int wl, wr, ht, hb; /* Width/height of header rows/columns. */
+ };
/* Tables. */
-struct tab_table *tab_create (int nc, int nr, int reallocable);
+struct tab_table *tab_create (int nc, int nr);
void tab_destroy (struct tab_table *);
+void tab_ref (struct tab_table *);
void tab_resize (struct tab_table *, int nc, int nr);
void tab_realloc (struct tab_table *, int nc, int nr);
void tab_headers (struct tab_table *, int l, int r, int t, int b);
-void tab_columns (struct tab_table *, int style, int group);
+void tab_columns (struct tab_table *, int style);
void tab_title (struct tab_table *, const char *, ...)
PRINTF_FORMAT (2, 3);
void tab_flags (struct tab_table *, unsigned);
/* Dimensioning. */
tab_dim_func tab_natural_dimensions;
-int tab_natural_width (struct tab_table *t, struct outp_driver *d, int c);
-int tab_natural_height (struct tab_table *t, struct outp_driver *d, int r);
-void tab_dim (struct tab_table *, tab_dim_func *, void *aux);
+int tab_natural_width (const struct tab_rendering *, int c);
+int tab_natural_height (const struct tab_rendering *, int r);
+void tab_dim (struct tab_table *,
+ tab_dim_func *, tab_dim_free_func *, void *aux);
/* Rules. */
void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
/* Cells. */
struct fmt_spec;
+ struct dictionary;
union value;
void tab_value (struct tab_table *, int c, int r, unsigned char opt,
- const union value *, const struct fmt_spec *);
+ const union value *, const struct dictionary *dict,
+ const struct fmt_spec *);
void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
double v, int w, int d);
unsigned opt, const char *, ...)
PRINTF_FORMAT (7, 8);
-/* Cell low-level access. */
-#define tab_alloc(TABLE, AMT) pool_alloc ((TABLE)->container, (AMT))
-void tab_raw (struct tab_table *, int c, int r, unsigned opt,
- struct substring *);
-
/* Editing. */
void tab_offset (struct tab_table *, int col, int row);
void tab_next_row (struct tab_table *);
void tab_output_text_format (int options, const char *, ...)
PRINTF_FORMAT (2, 3);
-/* Embedding the command name in the output. */
-void tab_set_command_name (const char *);
-
#endif /* tab_h */
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <gtk/gtk.h>
"data-store", &ds,
NULL);
- fd.dict = vs->dict;
+ g_object_get (vs, "dictionary", &fd.dict, NULL);
+
fd.data = ds->datasheet;
fd.variable_entry = get_widget_assert (fd.xml, "find-variable-entry");
{
const struct variable *var;
enum string_cmp_flags flags;
+ const PsppireDict *dict;
bool (*compare) (const struct comparator *,
const union value *);
string_value_compare (const struct comparator *cmptr,
const union value *val)
{
+ bool found;
+ char *text;
const struct string_comparator *ssc =
(const struct string_comparator *) cmptr;
int width = var_get_width (cmptr->var);
- const char *text = value_str (val, width);
-
+ g_return_val_if_fail (width > 0, false);
assert ( ! (cmptr->flags & STR_CMP_LABELS));
- g_return_val_if_fail (width > 0, false);
+ text = value_to_text (*val, cmptr->dict, *var_get_write_format (cmptr->var));
if ( cmptr->flags & STR_CMP_SUBSTR)
- return (NULL != g_strstr_len (text, width, ssc->pattern));
+ found = (NULL != g_strstr_len (text, width, ssc->pattern));
else
- return (0 == strncmp (text, ssc->pattern, width));
+ found = (0 == strncmp (text, ssc->pattern, width));
+
+ free (text);
+ return found;
}
g_return_val_if_fail (width > 0, false);
+ text = value_to_text (*val, cmptr->dict, *var_get_write_format (cmptr->var));
/* We must remove trailing whitespace, otherwise $ will not match where
one would expect */
- text = g_strndup (value_str (val, width), width);
g_strchomp (text);
retval = (0 == regexec (&rec->re, text, 0, 0, 0));
static void
regexp_destroy (struct comparator *cmptr)
{
- struct regexp_comparator *rec = (struct regexp_comparator *) cmptr;
+ struct regexp_comparator *rec
+ = UP_CAST (cmptr, struct regexp_comparator, parent);
regfree (&rec->re);
}
static void
cmptr_value_destroy (struct comparator *cmptr)
{
- struct value_comparator *vc = (struct value_comparator *) cmptr;
+ struct value_comparator *vc
+ = UP_CAST (cmptr, struct value_comparator, parent);
value_destroy (&vc->pattern, var_get_width (cmptr->var));
}
static struct comparator *
- value_comparator_create (const struct variable *var, const char *target)
+ value_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target)
{
- const struct fmt_spec *fmt;
- int width ;
struct value_comparator *vc = xzalloc (sizeof (*vc));
- struct comparator *cmptr = (struct comparator *) vc;
+ struct comparator *cmptr = &vc->parent;
cmptr->flags = 0;
cmptr->var = var;
cmptr->compare = value_compare ;
cmptr->destroy = cmptr_value_destroy;
+ cmptr->dict = dict;
- width = var_get_width (var);
- fmt = var_get_write_format (var);
-
- value_init (&vc->pattern, width);
-
- if ( ! data_in (ss_cstr (target),
- LEGACY_NATIVE,
- fmt->type,
- 0, 0, 0,
- &vc->pattern, width) )
- {
- value_destroy (&vc->pattern, width);
- free (vc);
- return NULL;
- }
+ text_to_value (target, dict, var, &vc->pattern);
return cmptr;
}
static struct comparator *
- string_comparator_create (const struct variable *var, const char *target,
+ string_comparator_create (const struct variable *var, const PsppireDict *dict,
+ const char *target,
enum string_cmp_flags flags)
{
struct string_comparator *ssc = xzalloc (sizeof (*ssc));
- struct comparator *cmptr = (struct comparator *) ssc;
+ struct comparator *cmptr = &ssc->parent;
cmptr->flags = flags;
cmptr->var = var;
+ cmptr->dict = dict;
if ( flags & STR_CMP_LABELS)
cmptr->compare = string_label_compare;
static struct comparator *
- regexp_comparator_create (const struct variable *var, const char *target,
+ regexp_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target,
enum string_cmp_flags flags)
{
int code;
struct regexp_comparator *rec = xzalloc (sizeof (*rec));
- struct comparator *cmptr = (struct comparator *) rec;
+ struct comparator *cmptr = &rec->parent;
cmptr->flags = flags;
cmptr->var = var;
+ cmptr->dict = dict;
cmptr->compare = (flags & STR_CMP_LABELS)
? regexp_label_compare : regexp_value_compare ;
static struct comparator *
- comparator_factory (const struct variable *var, const char *str,
+ comparator_factory (const struct variable *var, const PsppireDict *dict, const char *str,
enum string_cmp_flags flags)
{
if ( flags & STR_CMP_REGEXP )
- return regexp_comparator_create (var, str, flags);
+ return regexp_comparator_create (var, dict, str, flags);
if ( flags & (STR_CMP_SUBSTR | STR_CMP_LABELS) )
- return string_comparator_create (var, str, flags);
+ return string_comparator_create (var, dict, str, flags);
- return value_comparator_create (var, str);
+ return value_comparator_create (var, dict, str);
}
casenumber i;
const struct casenum_iterator *ip = get_iteration_params (fd);
struct comparator *cmptr =
- comparator_factory (var, target_string, flags);
+ comparator_factory (var, fd->dict, target_string, flags);
value_init (&val, width);
if ( ! cmptr)
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2008 Free Software Foundation
+ Copyright (C) 2008, 2009 Free Software Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <gtk/gtkaction.h>
#include <gtk/gtktextbuffer.h>
#include "psppire-window.h"
+ #include "psppire.h"
#include <gtk/gtk.h>
extern int viewer_length;
extern int viewer_width ;
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-
-
G_BEGIN_DECLS
#define PSPPIRE_OUTPUT_WINDOW_TYPE (psppire_output_window_get_type ())
PsppireWindow parent;
/* <private> */
- GtkTextBuffer *buffer; /* The buffer which contains the text */
- GtkWidget *textview ;
- FILE *fp; /* The file it's viewing */
+ GtkLayout *output;
+ int max_width;
+ int y;
+
+ GtkTreeView *overview;
+ int last_table_num;
+ GtkTreeIter last_top_level;
};
struct _PsppireOutputWindowClass
GType psppire_output_window_get_type (void);
GtkWidget* psppire_output_window_new (void);
-
-void psppire_output_window_reload (void);
-
+void psppire_output_window_setup (void);
G_END_DECLS