1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/output-item.h"
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"
33 #include "gl/xalloc.h"
36 #define _(msgid) gettext (msgid)
37 #define N_(msgid) msgid
39 #define OUTPUT_ITEM_INITIALIZER(TYPE) .type = TYPE, .ref_cnt = 1
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
45 output_item_ref (const struct output_item *item_)
47 struct output_item *item = CONST_CAST (struct output_item *, item_);
52 /* Decreases ITEM's reference count, indicating that it has one fewer owner.
53 If ITEM no longer has any owners, it is freed. */
55 output_item_unref (struct output_item *item)
59 assert (item->ref_cnt > 0);
60 if (--item->ref_cnt == 0)
64 case OUTPUT_ITEM_CHART:
65 chart_unref (item->chart);
68 case OUTPUT_ITEM_GROUP_OPEN:
71 case OUTPUT_ITEM_GROUP_CLOSE:
74 case OUTPUT_ITEM_IMAGE:
75 cairo_surface_destroy (item->image);
78 case OUTPUT_ITEM_MESSAGE:
79 msg_destroy (item->message);
82 case OUTPUT_ITEM_PAGE_BREAK:
85 case OUTPUT_ITEM_PAGE_SETUP:
86 page_setup_destroy (item->page_setup);
89 case OUTPUT_ITEM_TABLE:
90 pivot_table_unref (item->table);
93 case OUTPUT_ITEM_TEXT:
94 pivot_value_destroy (item->text.content);
99 free (item->command_name);
100 free (item->cached_label);
106 /* Returns true if ITEM has more than one owner. An output item that is shared
107 among multiple owners must not be modified. */
109 output_item_is_shared (const struct output_item *item)
111 return item->ref_cnt > 1;
115 output_item_unshare (struct output_item *old)
117 assert (old->ref_cnt > 0);
118 if (!output_item_is_shared (old))
120 output_item_unref (old);
122 struct output_item *new = xmalloc (sizeof *new);
123 *new = (struct output_item) {
125 .label = xstrdup_if_nonnull (old->label),
126 .command_name = xstrdup_if_nonnull (old->command_name),
131 case OUTPUT_ITEM_CHART:
132 new->chart = chart_ref (old->chart);
135 case OUTPUT_ITEM_GROUP_OPEN:
138 case OUTPUT_ITEM_GROUP_CLOSE:
141 case OUTPUT_ITEM_IMAGE:
142 new->image = cairo_surface_reference (old->image);
145 case OUTPUT_ITEM_MESSAGE:
146 new->message = msg_dup (old->message);
149 case OUTPUT_ITEM_PAGE_BREAK:
152 case OUTPUT_ITEM_PAGE_SETUP:
153 new->page_setup = page_setup_clone (old->page_setup);
156 case OUTPUT_ITEM_TABLE:
157 new->table = pivot_table_ref (old->table);
160 case OUTPUT_ITEM_TEXT:
161 new->text.subtype = old->text.subtype;
162 new->text.content = pivot_value_clone (old->text.content);
169 output_item_submit (struct output_item *item)
171 output_submit (item);
174 /* Returns the label for ITEM, which the caller must not modify or free. */
176 output_item_get_label (const struct output_item *item)
183 case OUTPUT_ITEM_CHART:
184 return item->chart->title ? item->chart->title : _("Chart");
186 case OUTPUT_ITEM_GROUP_OPEN:
187 return item->command_name ? item->command_name : _("Group");
189 case OUTPUT_ITEM_GROUP_CLOSE:
190 /* Not marked for translation: user should never see it. */
191 return "Group Close";
193 case OUTPUT_ITEM_IMAGE:
196 case OUTPUT_ITEM_MESSAGE:
197 return (item->message->severity == MSG_S_ERROR ? _("Error")
198 : item->message->severity == MSG_S_WARNING ? _("Warning")
201 case OUTPUT_ITEM_PAGE_BREAK:
202 return _("Page Break");
204 case OUTPUT_ITEM_PAGE_SETUP:
205 /* Not marked for translation: user should never see it. */
208 case OUTPUT_ITEM_TABLE:
209 if (!item->cached_label)
211 if (!item->table->title)
214 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
215 item_rw->cached_label = pivot_value_to_string (item->table->title,
218 return item->cached_label;
220 case OUTPUT_ITEM_TEXT:
221 return text_item_subtype_to_string (item->text.subtype);
227 /* Sets the label for ITEM to LABEL. The caller retains ownership of LABEL.
228 If LABEL is nonnull, it overrides any previously set label and the default
229 label. If LABEL is null, ITEM will now use its default label.
231 ITEM must not be shared. */
233 output_item_set_label (struct output_item *item, const char *label)
235 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
238 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
239 If LABEL is nonnull, it overrides any previously set label and the default
240 label. If LABEL is null, ITEM will now use its default label.
242 ITEM must not be shared. */
244 output_item_set_label_nocopy (struct output_item *item, char *label)
246 assert (!output_item_is_shared (item));
252 chart_item_create (struct chart *chart)
254 struct output_item *item = xmalloc (sizeof *item);
255 *item = (struct output_item) {
256 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
263 group_open_item_create (const char *command_name, const char *label)
265 return group_open_item_create_nocopy (
266 xstrdup_if_nonnull (command_name),
267 xstrdup_if_nonnull (label));
271 group_open_item_create_nocopy (char *command_name, char *label)
273 struct output_item *item = xmalloc (sizeof *item);
274 *item = (struct output_item) {
275 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_OPEN),
277 .command_name = command_name,
283 group_close_item_create (void)
285 struct output_item *item = xmalloc (sizeof *item);
286 *item = (struct output_item) {
287 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_CLOSE),
292 /* Creates and returns a new output item containing IMAGE. Takes ownership of
295 image_item_create (cairo_surface_t *image)
297 struct output_item *item = xmalloc (sizeof *item);
298 *item = (struct output_item) {
299 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
306 message_item_create (const struct msg *msg)
308 struct output_item *item = xmalloc (sizeof *msg);
309 *item = (struct output_item) {
310 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
311 .message = msg_dup (msg),
317 message_item_get_msg (const struct output_item *item)
319 assert (item->type == OUTPUT_ITEM_MESSAGE);
320 return item->message;
324 message_item_to_text_item (struct output_item *message_item)
326 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
327 struct output_item *text_item = text_item_create_nocopy (
329 msg_to_string (message_item->message),
330 xstrdup (output_item_get_label (message_item)));
331 output_item_unref (message_item);
336 page_break_item_create (void)
338 struct output_item *item = xmalloc (sizeof *item);
339 *item = (struct output_item) {
340 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
346 page_setup_item_create (const struct page_setup *ps)
348 struct output_item *item = xmalloc (sizeof *item);
349 *item = (struct output_item) {
350 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_SETUP),
351 .page_setup = page_setup_clone (ps),
356 /* Returns a new output_item for rendering TABLE. Takes ownership of
359 table_item_create (struct pivot_table *table)
361 pivot_table_assign_label_depth (table);
363 struct output_item *item = xmalloc (sizeof *item);
364 *item = (struct output_item) {
365 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
366 .command_name = xstrdup_if_nonnull (table->command_c),
372 /* Creates and returns a new text item containing TEXT and the specified
373 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
374 LABEL is NULL, uses the default label for SUBTYPE. */
376 text_item_create_nocopy (enum text_item_subtype subtype,
377 char *text, char *label)
379 return text_item_create_value (subtype,
380 pivot_value_new_user_text_nocopy (text),
384 /* Creates and returns a new text item containing a copy of TEXT and the
385 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
386 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
388 text_item_create (enum text_item_subtype subtype, const char *text,
391 return text_item_create_nocopy (subtype, xstrdup (text),
392 xstrdup_if_nonnull (label));
395 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
396 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
399 text_item_create_value (enum text_item_subtype subtype,
400 struct pivot_value *value, char *label)
402 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
404 if (!value->font_style)
406 value->font_style = xmalloc (sizeof *value->font_style);
407 *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
410 free (value->font_style->typeface);
411 value->font_style->typeface = xstrdup ("Monospaced");
414 struct output_item *item = xzalloc (sizeof *item);
415 *item = (struct output_item) {
416 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
417 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
419 .text = { .subtype = subtype, .content = value },
424 /* Returns ITEM's subtype. */
425 enum text_item_subtype
426 text_item_get_subtype (const struct output_item *item)
428 assert (item->type == OUTPUT_ITEM_TEXT);
429 return item->text.subtype;
432 /* Returns ITEM's text, which the caller must eventually free. */
434 text_item_get_plain_text (const struct output_item *item)
436 assert (item->type == OUTPUT_ITEM_TEXT);
437 return pivot_value_to_string_defaults (item->text.content);
441 nullable_font_style_equal (const struct font_style *a,
442 const struct font_style *b)
444 return a && b ? font_style_equal (a, b) : !a && !b;
447 /* Attempts to append the text in SRC to DST. If successful, returns true,
450 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
453 DST must not be shared. */
455 text_item_append (struct output_item *dst, const struct output_item *src)
457 assert (dst->type == OUTPUT_ITEM_TEXT);
458 assert (src->type == OUTPUT_ITEM_TEXT);
459 assert (!output_item_is_shared (dst));
461 enum text_item_subtype ds = dst->text.subtype;
462 enum text_item_subtype ss = src->text.subtype;
464 struct pivot_value *dc = dst->text.content;
465 const struct pivot_value *sc = src->text.content;
468 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
469 || strcmp (output_item_get_label (dst), output_item_get_label (src))
470 || !nullable_font_style_equal (dc->font_style, sc->font_style)
471 || (dc->font_style && dc->font_style->markup)
472 || sc->type != PIVOT_VALUE_TEXT
473 || dc->type != PIVOT_VALUE_TEXT)
477 /* Calculate new text. */
478 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
480 /* Free the old text. */
481 free (dc->text.local);
482 if (dc->text.c != dc->text.local)
484 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
487 /* Put in new text. */
488 dc->text.local = new_text;
489 dc->text.c = new_text;
490 dc->text.id = new_text;
496 static const struct pivot_table_look *
497 text_item_table_look (void)
499 static struct pivot_table_look *look;
502 look = pivot_table_look_new_builtin_default ();
504 for (int a = 0; a < PIVOT_N_AREAS; a++)
505 memset (look->areas[a].cell_style.margin, 0,
506 sizeof look->areas[a].cell_style.margin);
507 for (int b = 0; b < PIVOT_N_BORDERS; b++)
508 look->borders[b].stroke = TABLE_STROKE_NONE;
514 text_item_to_table_item (struct output_item *text_item)
516 assert (text_item->type == OUTPUT_ITEM_TEXT);
518 /* Create a new table whose contents come from TEXT_ITEM. */
519 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
520 pivot_table_set_look (table, text_item_table_look ());
522 struct pivot_dimension *d = pivot_dimension_create (
523 table, PIVOT_AXIS_ROW, N_("Text"));
524 d->hide_all_labels = true;
525 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
527 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
529 /* Free TEXT_ITEM. */
530 output_item_unref (text_item);
532 /* Return a new output item. */
533 return table_item_create (table);
537 text_item_subtype_to_string (enum text_item_subtype subtype)
541 case TEXT_ITEM_PAGE_TITLE:
542 return _("Page Title");
544 case TEXT_ITEM_TITLE:
547 case TEXT_ITEM_SYNTAX: