zip-reader: Make the zip_reader reference counted.
[pspp] / src / output / output-item.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 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/output-item.h"
20
21 #include <assert.h>
22 #include <stdlib.h>
23
24 #include "libpspp/assertion.h"
25 #include "libpspp/cast.h"
26 #include "libpspp/message.h"
27 #include "libpspp/str.h"
28 #include "output/chart.h"
29 #include "output/driver.h"
30 #include "output/page-setup.h"
31 #include "output/pivot-table.h"
32
33 #include "gl/xalloc.h"
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37 #define N_(msgid) msgid
38 \f
39 #define OUTPUT_ITEM_INITIALIZER(TYPE) .type = TYPE, .ref_cnt = 1
40
41 /* Increases ITEM's reference count, indicating that it has an additional
42    owner.  An output item that is shared among multiple owners must not be
43    modified. */
44 struct output_item *
45 output_item_ref (const struct output_item *item_)
46 {
47   struct output_item *item = CONST_CAST (struct output_item *, item_);
48   assert (item->ref_cnt > 0);
49   item->ref_cnt++;
50   return item;
51 }
52
53 /* Decreases ITEM's reference count, indicating that it has one fewer owner.
54    If ITEM no longer has any owners, it is freed. */
55 void
56 output_item_unref (struct output_item *item)
57 {
58   if (item != NULL)
59     {
60       assert (item->ref_cnt > 0);
61       if (--item->ref_cnt == 0)
62         {
63           switch (item->type)
64             {
65             case OUTPUT_ITEM_CHART:
66               chart_unref (item->chart);
67               break;
68
69             case OUTPUT_ITEM_GROUP_OPEN:
70               break;
71
72             case OUTPUT_ITEM_GROUP_CLOSE:
73               break;
74
75             case OUTPUT_ITEM_IMAGE:
76               cairo_surface_destroy (item->image);
77               break;
78
79             case OUTPUT_ITEM_MESSAGE:
80               msg_destroy (item->message);
81               break;
82
83             case OUTPUT_ITEM_PAGE_BREAK:
84               break;
85
86             case OUTPUT_ITEM_PAGE_SETUP:
87               page_setup_destroy (item->page_setup);
88               break;
89
90             case OUTPUT_ITEM_TABLE:
91               pivot_table_unref (item->table);
92               break;
93
94             case OUTPUT_ITEM_TEXT:
95               pivot_value_destroy (item->text.content);
96               break;
97             }
98
99           free (item->label);
100           free (item->command_name);
101           free (item->cached_label);
102           free (item);
103         }
104     }
105 }
106
107 /* Returns true if ITEM has more than one owner.  An output item that is shared
108    among multiple owners must not be modified. */
109 bool
110 output_item_is_shared (const struct output_item *item)
111 {
112   return item->ref_cnt > 1;
113 }
114
115 struct output_item *
116 output_item_unshare (struct output_item *old)
117 {
118   assert (old->ref_cnt > 0);
119   if (!output_item_is_shared (old))
120     return old;
121   output_item_unref (old);
122
123   struct output_item *new = xmalloc (sizeof *new);
124   *new = (struct output_item) {
125     .ref_cnt = 1,
126     .label = xstrdup_if_nonnull (old->label),
127     .command_name = xstrdup_if_nonnull (old->command_name),
128     .type = old->type,
129   };
130   switch (old->type)
131     {
132     case OUTPUT_ITEM_CHART:
133       new->chart = chart_ref (old->chart);
134       break;
135
136     case OUTPUT_ITEM_GROUP_OPEN:
137       break;
138
139     case OUTPUT_ITEM_GROUP_CLOSE:
140       break;
141
142     case OUTPUT_ITEM_IMAGE:
143       new->image = cairo_surface_reference (old->image);
144       break;
145
146     case OUTPUT_ITEM_MESSAGE:
147       new->message = msg_dup (old->message);
148       break;
149
150     case OUTPUT_ITEM_PAGE_BREAK:
151       break;
152
153     case OUTPUT_ITEM_PAGE_SETUP:
154       new->page_setup = page_setup_clone (old->page_setup);
155       break;
156
157     case OUTPUT_ITEM_TABLE:
158       new->table = pivot_table_ref (old->table);
159       break;
160
161     case OUTPUT_ITEM_TEXT:
162       new->text.subtype = old->text.subtype;
163       new->text.content = pivot_value_clone (old->text.content);
164       break;
165     }
166   return new;
167 }
168
169 void
170 output_item_submit (struct output_item *item)
171 {
172   output_submit (item);
173 }
174
175 /* Returns the label for ITEM, which the caller must not modify or free. */
176 const char *
177 output_item_get_label (const struct output_item *item)
178 {
179   if (item->label)
180     return item->label;
181
182   switch (item->type)
183     {
184     case OUTPUT_ITEM_CHART:
185       return item->chart->title ? item->chart->title : _("Chart");
186
187     case OUTPUT_ITEM_GROUP_OPEN:
188       return item->command_name ? item->command_name : _("Group");
189
190     case OUTPUT_ITEM_GROUP_CLOSE:
191       /* Not marked for translation: user should never see it. */
192       return "Group Close";
193
194     case OUTPUT_ITEM_IMAGE:
195       return "Image";
196
197     case OUTPUT_ITEM_MESSAGE:
198       return (item->message->severity == MSG_S_ERROR ? _("Error")
199               : item->message->severity == MSG_S_WARNING ? _("Warning")
200               : _("Note"));
201
202     case OUTPUT_ITEM_PAGE_BREAK:
203       return _("Page Break");
204
205     case OUTPUT_ITEM_PAGE_SETUP:
206       /* Not marked for translation: user should never see it. */
207       return "Page Setup";
208
209     case OUTPUT_ITEM_TABLE:
210       if (!item->cached_label)
211         {
212           if (!item->table->title)
213             return _("Table");
214
215           struct output_item *item_rw = CONST_CAST (struct output_item *, item);
216           item_rw->cached_label = pivot_value_to_string (item->table->title,
217                                                          item->table);
218         }
219       return item->cached_label;
220
221     case OUTPUT_ITEM_TEXT:
222       return text_item_subtype_to_string (item->text.subtype);
223     }
224
225   NOT_REACHED ();
226 }
227
228 /* Sets the label for ITEM to LABEL.  The caller retains ownership of LABEL.
229    If LABEL is nonnull, it overrides any previously set label and the default
230    label.  If LABEL is null, ITEM will now use its default label.
231
232    ITEM must not be shared. */
233 void
234 output_item_set_label (struct output_item *item, const char *label)
235 {
236   output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
237 }
238
239 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
240    If LABEL is nonnull, it overrides any previously set label and the default
241    label.  If LABEL is null, ITEM will now use its default label.
242
243    ITEM must not be shared. */
244 void
245 output_item_set_label_nocopy (struct output_item *item, char *label)
246 {
247   assert (!output_item_is_shared (item));
248   free (item->label);
249   item->label = label;
250 }
251 \f
252 struct output_item *
253 chart_item_create (struct chart *chart)
254 {
255   struct output_item *item = xmalloc (sizeof *item);
256   *item = (struct output_item) {
257     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
258     .chart = chart,
259   };
260   return item;
261 }
262 \f
263 struct output_item *
264 group_open_item_create (const char *command_name, const char *label)
265 {
266   return group_open_item_create_nocopy (
267     xstrdup_if_nonnull (command_name),
268     xstrdup_if_nonnull (label));
269 }
270
271 struct output_item *
272 group_open_item_create_nocopy (char *command_name, char *label)
273 {
274   struct output_item *item = xmalloc (sizeof *item);
275   *item = (struct output_item) {
276     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_OPEN),
277     .label = label,
278     .command_name = command_name,
279   };
280   return item;
281 }
282
283 struct output_item *
284 group_close_item_create (void)
285 {
286   struct output_item *item = xmalloc (sizeof *item);
287   *item = (struct output_item) {
288     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_CLOSE),
289   };
290   return item;
291 }
292 \f
293 /* Creates and returns a new output item containing IMAGE.  Takes ownership of
294    IMAGE. */
295 struct output_item *
296 image_item_create (cairo_surface_t *image)
297 {
298   struct output_item *item = xmalloc (sizeof *item);
299   *item = (struct output_item) {
300     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
301     .image = image,
302   };
303   return item;
304 }
305 \f
306 struct output_item *
307 message_item_create (const struct msg *msg)
308 {
309   struct output_item *item = xmalloc (sizeof *item);
310   *item = (struct output_item) {
311     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
312     .message = msg_dup (msg),
313   };
314   return item;
315 }
316
317 const struct msg *
318 message_item_get_msg (const struct output_item *item)
319 {
320   assert (item->type == OUTPUT_ITEM_MESSAGE);
321   return item->message;
322 }
323
324 struct output_item *
325 message_item_to_text_item (struct output_item *message_item)
326 {
327   assert (message_item->type == OUTPUT_ITEM_MESSAGE);
328   struct output_item *text_item = text_item_create_nocopy (
329     TEXT_ITEM_LOG,
330     msg_to_string (message_item->message),
331     xstrdup (output_item_get_label (message_item)));
332   output_item_unref (message_item);
333   return text_item;
334 }
335 \f
336 struct output_item *
337 page_break_item_create (void)
338 {
339   struct output_item *item = xmalloc (sizeof *item);
340   *item = (struct output_item) {
341     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
342   };
343   return item;
344 }
345 \f
346 struct output_item *
347 page_setup_item_create (const struct page_setup *ps)
348 {
349   struct output_item *item = xmalloc (sizeof *item);
350   *item = (struct output_item) {
351     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_SETUP),
352     .page_setup = page_setup_clone (ps),
353   };
354   return item;
355 }
356 \f
357 /* Returns a new output_item for rendering TABLE.  Takes ownership of
358    TABLE. */
359 struct output_item *
360 table_item_create (struct pivot_table *table)
361 {
362   pivot_table_assign_label_depth (table);
363
364   struct output_item *item = xmalloc (sizeof *item);
365   *item = (struct output_item) {
366     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
367     .command_name = xstrdup_if_nonnull (table->command_c),
368     .table = table,
369   };
370   return item;
371 }
372 \f
373 /* Creates and returns a new text item containing TEXT and the specified
374    SUBTYPE and LABEL.  The new text item takes ownership of TEXT and LABEL.  If
375    LABEL is NULL, uses the default label for SUBTYPE. */
376 struct output_item *
377 text_item_create_nocopy (enum text_item_subtype subtype,
378                          char *text, char *label)
379 {
380   return text_item_create_value (subtype,
381                                  pivot_value_new_user_text_nocopy (text),
382                                  label);
383 }
384
385 /* Creates and returns a new text item containing a copy of TEXT and the
386    specified SUBTYPE and LABEL.  The caller retains ownership of TEXT and
387    LABEL.  If LABEL is null, uses a default label for SUBTYPE. */
388 struct output_item *
389 text_item_create (enum text_item_subtype subtype, const char *text,
390                   const char *label)
391 {
392   return text_item_create_nocopy (subtype, xstrdup (text),
393                                   xstrdup_if_nonnull (label));
394 }
395
396 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
397    Takes ownership of VALUE and LABEL.  If LABEL is null, uses a default label
398    for SUBTYPE. */
399 struct output_item *
400 text_item_create_value (enum text_item_subtype subtype,
401                         struct pivot_value *value, char *label)
402 {
403   if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
404     {
405       if (!value->font_style)
406         {
407           value->font_style = xmalloc (sizeof *value->font_style);
408           *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
409         }
410
411       free (value->font_style->typeface);
412       value->font_style->typeface = xstrdup ("Monospaced");
413     }
414
415   struct output_item *item = xzalloc (sizeof *item);
416   *item = (struct output_item) {
417     OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
418     .command_name = xstrdup_if_nonnull (output_get_command_name ()),
419     .label = label,
420     .text = { .subtype = subtype, .content = value },
421   };
422   return item;
423 }
424
425 /* Returns ITEM's subtype. */
426 enum text_item_subtype
427 text_item_get_subtype (const struct output_item *item)
428 {
429   assert (item->type == OUTPUT_ITEM_TEXT);
430   return item->text.subtype;
431 }
432
433 /* Returns ITEM's text, which the caller must eventually free. */
434 char *
435 text_item_get_plain_text (const struct output_item *item)
436 {
437   assert (item->type == OUTPUT_ITEM_TEXT);
438   return pivot_value_to_string_defaults (item->text.content);
439 }
440
441 static bool
442 nullable_font_style_equal (const struct font_style *a,
443                            const struct font_style *b)
444 {
445   return a && b ? font_style_equal (a, b) : !a && !b;
446 }
447
448 /* Attempts to append the text in SRC to DST.  If successful, returns true,
449    otherwise false.
450
451    Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
452    each other.
453
454    DST must not be shared. */
455 bool
456 text_item_append (struct output_item *dst, const struct output_item *src)
457 {
458   assert (dst->type == OUTPUT_ITEM_TEXT);
459   assert (src->type == OUTPUT_ITEM_TEXT);
460   assert (!output_item_is_shared (dst));
461
462   enum text_item_subtype ds = dst->text.subtype;
463   enum text_item_subtype ss = src->text.subtype;
464
465   struct pivot_value *dc = dst->text.content;
466   const struct pivot_value *sc = src->text.content;
467
468   if (ds != ss
469       || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
470       || strcmp (output_item_get_label (dst), output_item_get_label (src))
471       || !nullable_font_style_equal (dc->font_style, sc->font_style)
472       || (dc->font_style && dc->font_style->markup)
473       || sc->type != PIVOT_VALUE_TEXT
474       || dc->type != PIVOT_VALUE_TEXT)
475     return false;
476   else
477     {
478       /* Calculate new text. */
479       char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
480
481       /* Free the old text. */
482       free (dc->text.local);
483       if (dc->text.c != dc->text.local)
484         free (dc->text.c);
485       if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
486         free (dc->text.id);
487
488       /* Put in new text. */
489       dc->text.local = new_text;
490       dc->text.c = new_text;
491       dc->text.id = new_text;
492
493       return true;
494     }
495 }
496
497 static const struct pivot_table_look *
498 text_item_table_look (void)
499 {
500   static struct pivot_table_look *look;
501   if (!look)
502     {
503       look = pivot_table_look_new_builtin_default ();
504
505       for (int a = 0; a < PIVOT_N_AREAS; a++)
506         memset (look->areas[a].cell_style.margin, 0,
507                 sizeof look->areas[a].cell_style.margin);
508       for (int b = 0; b < PIVOT_N_BORDERS; b++)
509         look->borders[b].stroke = TABLE_STROKE_NONE;
510     }
511   return look;
512 }
513
514 struct output_item *
515 text_item_to_table_item (struct output_item *text_item)
516 {
517   assert (text_item->type == OUTPUT_ITEM_TEXT);
518
519   /* Create a new table whose contents come from TEXT_ITEM. */
520   struct pivot_table *table = pivot_table_create__ (NULL, "Text");
521   pivot_table_set_look (table, text_item_table_look ());
522
523   struct pivot_dimension *d = pivot_dimension_create (
524     table, PIVOT_AXIS_ROW, N_("Text"));
525   d->hide_all_labels = true;
526   pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
527
528   pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
529
530   /* Free TEXT_ITEM. */
531   output_item_unref (text_item);
532
533   /* Return a new output item. */
534   return table_item_create (table);
535 }
536
537 const char *
538 text_item_subtype_to_string (enum text_item_subtype subtype)
539 {
540   switch (subtype)
541     {
542     case TEXT_ITEM_PAGE_TITLE:
543       return _("Page Title");
544
545     case TEXT_ITEM_TITLE:
546       return _("Title");
547
548     case TEXT_ITEM_SYNTAX:
549     case TEXT_ITEM_LOG:
550       return _("Log");
551
552     default:
553       return _("Text");
554     }
555 }
556