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_);
48 assert (item->ref_cnt > 0);
53 /* Decreases ITEM's reference count, indicating that it has one fewer owner.
54 If ITEM no longer has any owners, it is freed. */
56 output_item_unref (struct output_item *item)
60 assert (item->ref_cnt > 0);
61 if (--item->ref_cnt == 0)
65 case OUTPUT_ITEM_CHART:
66 chart_unref (item->chart);
69 case OUTPUT_ITEM_GROUP_OPEN:
72 case OUTPUT_ITEM_GROUP_CLOSE:
75 case OUTPUT_ITEM_IMAGE:
76 cairo_surface_destroy (item->image);
79 case OUTPUT_ITEM_MESSAGE:
80 msg_destroy (item->message);
83 case OUTPUT_ITEM_PAGE_BREAK:
86 case OUTPUT_ITEM_PAGE_SETUP:
87 page_setup_destroy (item->page_setup);
90 case OUTPUT_ITEM_TABLE:
91 pivot_table_unref (item->table);
94 case OUTPUT_ITEM_TEXT:
95 pivot_value_destroy (item->text.content);
100 free (item->command_name);
101 free (item->cached_label);
107 /* Returns true if ITEM has more than one owner. An output item that is shared
108 among multiple owners must not be modified. */
110 output_item_is_shared (const struct output_item *item)
112 return item->ref_cnt > 1;
116 output_item_unshare (struct output_item *old)
118 assert (old->ref_cnt > 0);
119 if (!output_item_is_shared (old))
121 output_item_unref (old);
123 struct output_item *new = xmalloc (sizeof *new);
124 *new = (struct output_item) {
126 .label = xstrdup_if_nonnull (old->label),
127 .command_name = xstrdup_if_nonnull (old->command_name),
132 case OUTPUT_ITEM_CHART:
133 new->chart = chart_ref (old->chart);
136 case OUTPUT_ITEM_GROUP_OPEN:
139 case OUTPUT_ITEM_GROUP_CLOSE:
142 case OUTPUT_ITEM_IMAGE:
143 new->image = cairo_surface_reference (old->image);
146 case OUTPUT_ITEM_MESSAGE:
147 new->message = msg_dup (old->message);
150 case OUTPUT_ITEM_PAGE_BREAK:
153 case OUTPUT_ITEM_PAGE_SETUP:
154 new->page_setup = page_setup_clone (old->page_setup);
157 case OUTPUT_ITEM_TABLE:
158 new->table = pivot_table_ref (old->table);
161 case OUTPUT_ITEM_TEXT:
162 new->text.subtype = old->text.subtype;
163 new->text.content = pivot_value_clone (old->text.content);
170 output_item_submit (struct output_item *item)
172 output_submit (item);
175 /* Returns the label for ITEM, which the caller must not modify or free. */
177 output_item_get_label (const struct output_item *item)
184 case OUTPUT_ITEM_CHART:
185 return item->chart->title ? item->chart->title : _("Chart");
187 case OUTPUT_ITEM_GROUP_OPEN:
188 return item->command_name ? item->command_name : _("Group");
190 case OUTPUT_ITEM_GROUP_CLOSE:
191 /* Not marked for translation: user should never see it. */
192 return "Group Close";
194 case OUTPUT_ITEM_IMAGE:
197 case OUTPUT_ITEM_MESSAGE:
198 return (item->message->severity == MSG_S_ERROR ? _("Error")
199 : item->message->severity == MSG_S_WARNING ? _("Warning")
202 case OUTPUT_ITEM_PAGE_BREAK:
203 return _("Page Break");
205 case OUTPUT_ITEM_PAGE_SETUP:
206 /* Not marked for translation: user should never see it. */
209 case OUTPUT_ITEM_TABLE:
210 if (!item->cached_label)
212 if (!item->table->title)
215 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
216 item_rw->cached_label = pivot_value_to_string (item->table->title,
219 return item->cached_label;
221 case OUTPUT_ITEM_TEXT:
222 return text_item_subtype_to_string (item->text.subtype);
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.
232 ITEM must not be shared. */
234 output_item_set_label (struct output_item *item, const char *label)
236 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
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.
243 ITEM must not be shared. */
245 output_item_set_label_nocopy (struct output_item *item, char *label)
247 assert (!output_item_is_shared (item));
253 chart_item_create (struct chart *chart)
255 struct output_item *item = xmalloc (sizeof *item);
256 *item = (struct output_item) {
257 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
264 group_open_item_create (const char *command_name, const char *label)
266 return group_open_item_create_nocopy (
267 xstrdup_if_nonnull (command_name),
268 xstrdup_if_nonnull (label));
272 group_open_item_create_nocopy (char *command_name, char *label)
274 struct output_item *item = xmalloc (sizeof *item);
275 *item = (struct output_item) {
276 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_OPEN),
278 .command_name = command_name,
284 group_close_item_create (void)
286 struct output_item *item = xmalloc (sizeof *item);
287 *item = (struct output_item) {
288 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_CLOSE),
293 /* Creates and returns a new output item containing IMAGE. Takes ownership of
296 image_item_create (cairo_surface_t *image)
298 struct output_item *item = xmalloc (sizeof *item);
299 *item = (struct output_item) {
300 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
307 message_item_create (const struct msg *msg)
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),
318 message_item_get_msg (const struct output_item *item)
320 assert (item->type == OUTPUT_ITEM_MESSAGE);
321 return item->message;
325 message_item_to_text_item (struct output_item *message_item)
327 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
328 struct output_item *text_item = text_item_create_nocopy (
330 msg_to_string (message_item->message),
331 xstrdup (output_item_get_label (message_item)));
332 output_item_unref (message_item);
337 page_break_item_create (void)
339 struct output_item *item = xmalloc (sizeof *item);
340 *item = (struct output_item) {
341 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
347 page_setup_item_create (const struct page_setup *ps)
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),
357 /* Returns a new output_item for rendering TABLE. Takes ownership of
360 table_item_create (struct pivot_table *table)
362 pivot_table_assign_label_depth (table);
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),
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. */
377 text_item_create_nocopy (enum text_item_subtype subtype,
378 char *text, char *label)
380 return text_item_create_value (subtype,
381 pivot_value_new_user_text_nocopy (text),
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. */
389 text_item_create (enum text_item_subtype subtype, const char *text,
392 return text_item_create_nocopy (subtype, xstrdup (text),
393 xstrdup_if_nonnull (label));
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
400 text_item_create_value (enum text_item_subtype subtype,
401 struct pivot_value *value, char *label)
403 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
405 if (!value->font_style)
407 value->font_style = xmalloc (sizeof *value->font_style);
408 *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
411 free (value->font_style->typeface);
412 value->font_style->typeface = xstrdup ("Monospaced");
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 ()),
420 .text = { .subtype = subtype, .content = value },
425 /* Returns ITEM's subtype. */
426 enum text_item_subtype
427 text_item_get_subtype (const struct output_item *item)
429 assert (item->type == OUTPUT_ITEM_TEXT);
430 return item->text.subtype;
433 /* Returns ITEM's text, which the caller must eventually free. */
435 text_item_get_plain_text (const struct output_item *item)
437 assert (item->type == OUTPUT_ITEM_TEXT);
438 return pivot_value_to_string_defaults (item->text.content);
442 nullable_font_style_equal (const struct font_style *a,
443 const struct font_style *b)
445 return a && b ? font_style_equal (a, b) : !a && !b;
448 /* Attempts to append the text in SRC to DST. If successful, returns true,
451 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
454 DST must not be shared. */
456 text_item_append (struct output_item *dst, const struct output_item *src)
458 assert (dst->type == OUTPUT_ITEM_TEXT);
459 assert (src->type == OUTPUT_ITEM_TEXT);
460 assert (!output_item_is_shared (dst));
462 enum text_item_subtype ds = dst->text.subtype;
463 enum text_item_subtype ss = src->text.subtype;
465 struct pivot_value *dc = dst->text.content;
466 const struct pivot_value *sc = src->text.content;
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)
478 /* Calculate new text. */
479 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
481 /* Free the old text. */
482 free (dc->text.local);
483 if (dc->text.c != dc->text.local)
485 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
488 /* Put in new text. */
489 dc->text.local = new_text;
490 dc->text.c = new_text;
491 dc->text.id = new_text;
497 static const struct pivot_table_look *
498 text_item_table_look (void)
500 static struct pivot_table_look *look;
503 look = pivot_table_look_new_builtin_default ();
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;
515 text_item_to_table_item (struct output_item *text_item)
517 assert (text_item->type == OUTPUT_ITEM_TEXT);
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 ());
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"));
528 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
530 /* Free TEXT_ITEM. */
531 output_item_unref (text_item);
533 /* Return a new output item. */
534 return table_item_create (table);
538 text_item_subtype_to_string (enum text_item_subtype subtype)
542 case TEXT_ITEM_PAGE_TITLE:
543 return _("Page Title");
545 case TEXT_ITEM_TITLE:
548 case TEXT_ITEM_SYNTAX: