output-item: Make command name part of every output_item.
[pspp] / src / output / text-item.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011 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/text-item.h"
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23
24 #include "libpspp/cast.h"
25 #include "libpspp/pool.h"
26 #include "output/driver.h"
27 #include "output/output-item-provider.h"
28 #include "output/pivot-table.h"
29 #include "output/table.h"
30 #include "output/table-item.h"
31 #include "output/table-provider.h"
32
33 #include "gl/xalloc.h"
34 #include "gl/xvasprintf.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
39
40 const char *
41 text_item_type_to_string (enum text_item_type type)
42 {
43   switch (type)
44     {
45     case TEXT_ITEM_PAGE_TITLE:
46       return _("Page Title");
47
48     case TEXT_ITEM_TITLE:
49       return _("Title");
50
51     case TEXT_ITEM_SYNTAX:
52     case TEXT_ITEM_LOG:
53       return _("Log");
54
55     default:
56       return _("Text");
57     }
58 }
59
60 /* Creates and returns a new text item containing TEXT and the specified TYPE
61    and LABEL.  The new text item takes ownership of TEXT and LABEL.  If LABEL
62    is NULL, uses the default label for TYPE. */
63 struct text_item *
64 text_item_create_nocopy (enum text_item_type type, char *text, char *label)
65 {
66   return text_item_create_value (type, pivot_value_new_user_text_nocopy (text),
67                                  label);
68 }
69
70 /* Creates and returns a new text item containing a copy of TEXT and the
71    specified TYPE and LABEL.  The caller retains ownership of TEXT and LABEL.
72    If LABEL is null, uses a default label for TYPE. */
73 struct text_item *
74 text_item_create (enum text_item_type type, const char *text,
75                   const char *label)
76 {
77   return text_item_create_nocopy (type, xstrdup (text),
78                                   xstrdup_if_nonnull (label));
79 }
80
81 /* Creates and returns a new text item containing VALUE, TYPE, and LABEL.
82    Takes ownership of VALUE and LABEL.  If LABEL is null, uses a default label
83    for TYPE. */
84 struct text_item *
85 text_item_create_value (enum text_item_type type, struct pivot_value *value,
86                         char *label)
87 {
88   if (type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_LOG)
89     {
90       if (!value->font_style)
91         {
92           value->font_style = xmalloc (sizeof *value->font_style);
93           *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
94         }
95
96       free (value->font_style->typeface);
97       value->font_style->typeface = xstrdup ("Monospaced");
98     }
99
100   struct text_item *item = xzalloc (sizeof *item);
101   *item = (struct text_item) {
102     .output_item = OUTPUT_ITEM_INITIALIZER (&text_item_class),
103     .output_item.command_name = xstrdup_if_nonnull (output_get_command_name ()),
104     .output_item.label = label,
105     .type = type,
106     .text = value,
107   };
108   return item;
109 }
110
111 /* Returns ITEM's type. */
112 enum text_item_type
113 text_item_get_type (const struct text_item *item)
114 {
115   return item->type;
116 }
117
118 /* Returns ITEM's text, which the caller must eventually free. */
119 char *
120 text_item_get_plain_text (const struct text_item *item)
121 {
122   return pivot_value_to_string_defaults (item->text);
123 }
124
125 /* Submits ITEM to the configured output drivers, and transfers ownership to
126    the output subsystem. */
127 void
128 text_item_submit (struct text_item *item)
129 {
130   output_submit (&item->output_item);
131 }
132
133 struct text_item *
134 text_item_unshare (struct text_item *old)
135 {
136   assert (old->output_item.ref_cnt > 0);
137   if (!text_item_is_shared (old))
138     return old;
139   text_item_unref (old);
140
141   struct text_item *new = xmalloc (sizeof *new);
142   *new = (struct text_item) {
143     .output_item = OUTPUT_ITEM_CLONE_INITIALIZER (&old->output_item),
144     .text = pivot_value_clone (old->text),
145     .type = old->type,
146   };
147   return new;
148 }
149
150 static bool
151 nullable_font_style_equal (const struct font_style *a,
152                            const struct font_style *b)
153 {
154   return a && b ? font_style_equal (a, b) : !a && !b;
155 }
156
157 /* Attempts to append the text in SRC to DST.  If successful, returns true,
158    otherwise false.
159
160    Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
161    each other.
162
163    DST must not be shared. */
164 bool
165 text_item_append (struct text_item *dst, const struct text_item *src)
166 {
167   assert (!text_item_is_shared (dst));
168   if (dst->type != src->type
169       || (dst->type != TEXT_ITEM_SYNTAX && dst->type != TEXT_ITEM_LOG)
170       || strcmp (output_item_get_label (&dst->output_item),
171                  output_item_get_label (&src->output_item))
172       || !nullable_font_style_equal (dst->text->font_style,
173                                      src->text->font_style)
174       || (dst->text->font_style && dst->text->font_style->markup)
175       || src->text->type != PIVOT_VALUE_TEXT
176       || dst->text->type != PIVOT_VALUE_TEXT)
177     return false;
178   else
179     {
180       char *new_text = xasprintf ("%s\n%s", dst->text->text.local,
181                                   src->text->text.local);
182
183       free (dst->text->text.local);
184       if (dst->text->text.c != dst->text->text.local)
185         free (dst->text->text.c);
186       if (dst->text->text.id != dst->text->text.local
187           && dst->text->text.id != dst->text->text.c)
188         free (dst->text->text.id);
189
190       dst->text->text.local = new_text;
191       dst->text->text.c = new_text;
192       dst->text->text.id = new_text;
193
194       return true;
195     }
196 }
197
198 static const struct pivot_table_look *
199 text_item_table_look (void)
200 {
201   static struct pivot_table_look *look;
202   if (!look)
203     {
204       look = pivot_table_look_new_builtin_default ();
205
206       for (int a = 0; a < PIVOT_N_AREAS; a++)
207         memset (look->areas[a].cell_style.margin, 0,
208                 sizeof look->areas[a].cell_style.margin);
209       for (int b = 0; b < PIVOT_N_BORDERS; b++)
210         look->borders[b].stroke = TABLE_STROKE_NONE;
211     }
212   return look;
213 }
214
215 struct table_item *
216 text_item_to_table_item (struct text_item *text_item)
217 {
218   struct pivot_table *table = pivot_table_create__ (NULL, "Text");
219   pivot_table_set_look (table, text_item_table_look ());
220
221   struct pivot_dimension *d = pivot_dimension_create (
222     table, PIVOT_AXIS_ROW, N_("Text"));
223   d->hide_all_labels = true;
224   pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
225
226   pivot_table_put1 (table, 0, pivot_value_clone (text_item->text));
227
228   text_item_unref (text_item);
229
230   return table_item_create (table);
231 }
232 \f
233 static const char *
234 text_item_get_label (const struct output_item *output_item)
235 {
236   const struct text_item *item = to_text_item (output_item);
237   return text_item_type_to_string (item->type);
238 }
239
240 static void
241 text_item_destroy (struct output_item *output_item)
242 {
243   struct text_item *item = to_text_item (output_item);
244   pivot_value_destroy (item->text);
245   free (item);
246 }
247
248 const struct output_item_class text_item_class =
249   {
250     text_item_get_label,
251     text_item_destroy,
252   };