output: Support decimal and mixed alignment,
[pspp] / src / output / table-casereader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2013, 2014 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "output/table-provider.h"
20
21 #include "data/casereader.h"
22 #include "data/data-out.h"
23 #include "data/format.h"
24 #include "libpspp/i18n.h"
25
26 #include "gl/xalloc.h"
27
28 struct table_casereader
29   {
30     struct table table;
31     struct casereader *reader;
32     char *heading;
33     struct fmt_spec format;
34   };
35
36 static const struct table_class table_casereader_class;
37
38 static struct table_casereader *
39 table_casereader_cast (const struct table *table)
40 {
41   assert (table->klass == &table_casereader_class);
42   return UP_CAST (table, struct table_casereader, table);
43 }
44
45 /* Returns a new table that has one column and the same number of rows as
46    READER.  Each row in the table is derived from column COLUMN in the same row
47    of READER by formatting with data_out() using the specified FORMAT (which
48    must be a valid format for the column's width).
49
50    If HEADING is nonnull, adds an additional row above the first row of data
51    that contains HEADING, and sets that row as a header row.
52
53    The returned table has no rules, except that if HEADING is nonnull, a single
54    line (TAL_1) separates HEADING from the first row if data. */
55 struct table *
56 table_from_casereader (const struct casereader *reader, size_t column,
57                        const char *heading, const struct fmt_spec *format)
58 {
59   struct table_casereader *tc;
60   struct table *t;
61
62   assert (fmt_check_width_compat (format,
63                                   caseproto_get_width (
64                                     casereader_get_proto (reader), column)));
65
66   tc = xmalloc (sizeof *tc);
67   t = &tc->table;
68   table_init (t, &table_casereader_class);
69   table_set_nc (t, 1);
70   table_set_nr (t, casereader_count_cases (reader));
71   tc->reader = casereader_project_1 (casereader_clone (reader), column);
72   tc->heading = NULL;
73   tc->format = *format;
74
75   if (heading != NULL)
76     {
77       tc->heading = xstrdup (heading);
78       table_set_nr (t, table_nr (t) + 1);
79       table_set_ht (t, 1);
80     }
81
82   return t;
83 }
84
85 static void
86 table_casereader_destroy (struct table *t)
87 {
88   struct table_casereader *tc = table_casereader_cast (t);
89   casereader_destroy (tc->reader);
90   free (tc->heading);
91   free (t);
92 }
93
94 static void
95 free_string (void *s_)
96 {
97   char *s = s_;
98   free (s);
99 }
100
101 static void
102 table_casereader_get_cell (const struct table *t, int x, int y,
103                            struct table_cell *cell)
104 {
105   struct table_casereader *tc = table_casereader_cast (t);
106   struct ccase *c;
107   char *s;
108
109   static const struct area_style style = {
110     AREA_STYLE_INITIALIZER__,
111     .cell_style.halign = TABLE_HALIGN_RIGHT
112   };
113
114   cell->d[TABLE_HORZ][0] = x;
115   cell->d[TABLE_HORZ][1] = x + 1;
116   cell->d[TABLE_VERT][0] = y;
117   cell->d[TABLE_VERT][1] = y + 1;
118   cell->style = &style;
119   cell->options = 0;
120   cell->n_footnotes = 0;
121   if (tc->heading != NULL)
122     {
123       if (y == 0)
124         {
125           s = xstrdup (tc->heading);
126           cell->text = s;
127           cell->destructor = free_string;
128           cell->destructor_aux = s;
129           return;
130         }
131       y--;
132     }
133
134   c = casereader_peek (tc->reader, y);
135   if (c == NULL)
136     s = xstrdup ("I/O Error");
137   else
138     {
139       s = data_out (case_data_idx (c, 0), UTF8, &tc->format);
140       case_unref (c);
141     }
142   cell->text = s;
143   cell->destructor = free_string;
144   cell->destructor_aux = s;
145 }
146
147 static int
148 table_casereader_get_rule (const struct table *t, enum table_axis axis,
149                            int x UNUSED, int y,
150                            struct cell_color *color UNUSED)
151 {
152   struct table_casereader *tc = table_casereader_cast (t);
153   if (axis == TABLE_VERT)
154     return tc->heading != NULL && y == 1 ? TAL_1 : TAL_0;
155   else
156     return TAL_0;
157 }
158
159 static const struct table_class table_casereader_class =
160   {
161     table_casereader_destroy,
162     table_casereader_get_cell,
163     table_casereader_get_rule,
164     NULL,                       /* paste */
165     NULL,                       /* select (XXX) */
166   };