str: Add function xstrdup_if_nonnull() and introduce many users.
[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.label = label,
104     .type = type,
105     .text = value,
106   };
107   return item;
108 }
109
110 /* Returns ITEM's type. */
111 enum text_item_type
112 text_item_get_type (const struct text_item *item)
113 {
114   return item->type;
115 }
116
117 /* Returns ITEM's text, which the caller must eventually free. */
118 char *
119 text_item_get_plain_text (const struct text_item *item)
120 {
121   return pivot_value_to_string_defaults (item->text);
122 }
123
124 /* Submits ITEM to the configured output drivers, and transfers ownership to
125    the output subsystem. */
126 void
127 text_item_submit (struct text_item *item)
128 {
129   output_submit (&item->output_item);
130 }
131
132 struct text_item *
133 text_item_unshare (struct text_item *old)
134 {
135   assert (old->output_item.ref_cnt > 0);
136   if (!text_item_is_shared (old))
137     return old;
138   text_item_unref (old);
139
140   struct text_item *new = xmalloc (sizeof *new);
141   *new = (struct text_item) {
142     .output_item = OUTPUT_ITEM_CLONE_INITIALIZER (&old->output_item),
143     .text = pivot_value_clone (old->text),
144     .type = old->type,
145   };
146   return new;
147 }
148
149 static bool
150 nullable_font_style_equal (const struct font_style *a,
151                            const struct font_style *b)
152 {
153   return a && b ? font_style_equal (a, b) : !a && !b;
154 }
155
156 /* Attempts to append the text in SRC to DST.  If successful, returns true,
157    otherwise false.
158
159    Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
160    each other.
161
162    DST must not be shared. */
163 bool
164 text_item_append (struct text_item *dst, const struct text_item *src)
165 {
166   assert (!text_item_is_shared (dst));
167   if (dst->type != src->type
168       || (dst->type != TEXT_ITEM_SYNTAX && dst->type != TEXT_ITEM_LOG)
169       || strcmp (output_item_get_label (&dst->output_item),
170                  output_item_get_label (&src->output_item))
171       || !nullable_font_style_equal (dst->text->font_style,
172                                      src->text->font_style)
173       || (dst->text->font_style && dst->text->font_style->markup)
174       || src->text->type != PIVOT_VALUE_TEXT
175       || dst->text->type != PIVOT_VALUE_TEXT)
176     return false;
177   else
178     {
179       char *new_text = xasprintf ("%s\n%s", dst->text->text.local,
180                                   src->text->text.local);
181
182       free (dst->text->text.local);
183       if (dst->text->text.c != dst->text->text.local)
184         free (dst->text->text.c);
185       if (dst->text->text.id != dst->text->text.local
186           && dst->text->text.id != dst->text->text.c)
187         free (dst->text->text.id);
188
189       dst->text->text.local = new_text;
190       dst->text->text.c = new_text;
191       dst->text->text.id = new_text;
192
193       return true;
194     }
195 }
196
197 static const struct pivot_table_look *
198 text_item_table_look (void)
199 {
200   static struct pivot_table_look *look;
201   if (!look)
202     {
203       look = pivot_table_look_new_builtin_default ();
204
205       for (int a = 0; a < PIVOT_N_AREAS; a++)
206         memset (look->areas[a].cell_style.margin, 0,
207                 sizeof look->areas[a].cell_style.margin);
208       for (int b = 0; b < PIVOT_N_BORDERS; b++)
209         look->borders[b].stroke = TABLE_STROKE_NONE;
210     }
211   return look;
212 }
213
214 struct table_item *
215 text_item_to_table_item (struct text_item *text_item)
216 {
217   struct pivot_table *table = pivot_table_create__ (NULL, "Text");
218   pivot_table_set_look (table, text_item_table_look ());
219
220   struct pivot_dimension *d = pivot_dimension_create (
221     table, PIVOT_AXIS_ROW, N_("Text"));
222   d->hide_all_labels = true;
223   pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
224
225   pivot_table_put1 (table, 0, pivot_value_clone (text_item->text));
226
227   text_item_unref (text_item);
228
229   return table_item_create (table);
230 }
231 \f
232 static const char *
233 text_item_get_label (const struct output_item *output_item)
234 {
235   const struct text_item *item = to_text_item (output_item);
236   return text_item_type_to_string (item->type);
237 }
238
239 static void
240 text_item_destroy (struct output_item *output_item)
241 {
242   struct text_item *item = to_text_item (output_item);
243   pivot_value_destroy (item->text);
244   free (item);
245 }
246
247 const struct output_item_class text_item_class =
248   {
249     text_item_get_label,
250     text_item_destroy,
251   };