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, .show = true
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),
133 case OUTPUT_ITEM_CHART:
134 new->chart = chart_ref (old->chart);
137 case OUTPUT_ITEM_GROUP_OPEN:
140 case OUTPUT_ITEM_GROUP_CLOSE:
143 case OUTPUT_ITEM_IMAGE:
144 new->image = cairo_surface_reference (old->image);
147 case OUTPUT_ITEM_MESSAGE:
148 new->message = msg_dup (old->message);
151 case OUTPUT_ITEM_PAGE_BREAK:
154 case OUTPUT_ITEM_PAGE_SETUP:
155 new->page_setup = page_setup_clone (old->page_setup);
158 case OUTPUT_ITEM_TABLE:
159 new->table = pivot_table_ref (old->table);
162 case OUTPUT_ITEM_TEXT:
163 new->text.subtype = old->text.subtype;
164 new->text.content = pivot_value_clone (old->text.content);
171 output_item_submit (struct output_item *item)
173 output_submit (item);
176 /* Returns the label for ITEM, which the caller must not modify or free. */
178 output_item_get_label (const struct output_item *item)
185 case OUTPUT_ITEM_CHART:
186 return item->chart->title ? item->chart->title : _("Chart");
188 case OUTPUT_ITEM_GROUP_OPEN:
189 return item->command_name ? item->command_name : _("Group");
191 case OUTPUT_ITEM_GROUP_CLOSE:
192 /* Not marked for translation: user should never see it. */
193 return "Group Close";
195 case OUTPUT_ITEM_IMAGE:
198 case OUTPUT_ITEM_MESSAGE:
199 return (item->message->severity == MSG_S_ERROR ? _("Error")
200 : item->message->severity == MSG_S_WARNING ? _("Warning")
203 case OUTPUT_ITEM_PAGE_BREAK:
204 return _("Page Break");
206 case OUTPUT_ITEM_PAGE_SETUP:
207 /* Not marked for translation: user should never see it. */
210 case OUTPUT_ITEM_TABLE:
211 if (!item->cached_label)
213 if (!item->table->title)
216 struct output_item *item_rw = CONST_CAST (struct output_item *, item);
217 item_rw->cached_label = pivot_value_to_string (item->table->title,
220 return item->cached_label;
222 case OUTPUT_ITEM_TEXT:
223 return text_item_subtype_to_string (item->text.subtype);
229 /* Sets the label for ITEM to LABEL. The caller retains ownership of LABEL.
230 If LABEL is nonnull, it overrides any previously set label and the default
231 label. If LABEL is null, ITEM will now use its default label.
233 ITEM must not be shared. */
235 output_item_set_label (struct output_item *item, const char *label)
237 output_item_set_label_nocopy (item, xstrdup_if_nonnull (label));
240 /* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
241 If LABEL is nonnull, it overrides any previously set label and the default
242 label. If LABEL is null, ITEM will now use its default label.
244 ITEM must not be shared. */
246 output_item_set_label_nocopy (struct output_item *item, char *label)
248 assert (!output_item_is_shared (item));
254 chart_item_create (struct chart *chart)
256 struct output_item *item = xmalloc (sizeof *item);
257 *item = (struct output_item) {
258 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_CHART),
265 group_open_item_create (const char *command_name, const char *label)
267 return group_open_item_create_nocopy (
268 xstrdup_if_nonnull (command_name),
269 xstrdup_if_nonnull (label));
273 group_open_item_create_nocopy (char *command_name, char *label)
275 struct output_item *item = xmalloc (sizeof *item);
276 *item = (struct output_item) {
277 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_OPEN),
279 .command_name = command_name,
285 group_close_item_create (void)
287 struct output_item *item = xmalloc (sizeof *item);
288 *item = (struct output_item) {
289 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_CLOSE),
294 /* Creates and returns a new output item containing IMAGE. Takes ownership of
297 image_item_create (cairo_surface_t *image)
299 struct output_item *item = xmalloc (sizeof *item);
300 *item = (struct output_item) {
301 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_IMAGE),
308 message_item_create (const struct msg *msg)
310 struct output_item *item = xmalloc (sizeof *item);
311 *item = (struct output_item) {
312 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_MESSAGE),
313 .message = msg_dup (msg),
319 message_item_get_msg (const struct output_item *item)
321 assert (item->type == OUTPUT_ITEM_MESSAGE);
322 return item->message;
326 message_item_to_text_item (struct output_item *message_item)
328 assert (message_item->type == OUTPUT_ITEM_MESSAGE);
329 struct output_item *text_item = text_item_create_nocopy (
331 msg_to_string (message_item->message),
332 xstrdup (output_item_get_label (message_item)));
333 output_item_unref (message_item);
338 page_break_item_create (void)
340 struct output_item *item = xmalloc (sizeof *item);
341 *item = (struct output_item) {
342 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_BREAK),
348 page_setup_item_create (const struct page_setup *ps)
350 struct output_item *item = xmalloc (sizeof *item);
351 *item = (struct output_item) {
352 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_PAGE_SETUP),
353 .page_setup = page_setup_clone (ps),
358 /* Returns a new output_item for rendering TABLE. Takes ownership of
361 table_item_create (struct pivot_table *table)
363 pivot_table_assign_label_depth (table);
365 struct output_item *item = xmalloc (sizeof *item);
366 *item = (struct output_item) {
367 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TABLE),
368 .command_name = xstrdup_if_nonnull (table->command_c),
374 /* Creates and returns a new text item containing TEXT and the specified
375 SUBTYPE and LABEL. The new text item takes ownership of TEXT and LABEL. If
376 LABEL is NULL, uses the default label for SUBTYPE. */
378 text_item_create_nocopy (enum text_item_subtype subtype,
379 char *text, char *label)
381 return text_item_create_value (subtype,
382 pivot_value_new_user_text_nocopy (text),
386 /* Creates and returns a new text item containing a copy of TEXT and the
387 specified SUBTYPE and LABEL. The caller retains ownership of TEXT and
388 LABEL. If LABEL is null, uses a default label for SUBTYPE. */
390 text_item_create (enum text_item_subtype subtype, const char *text,
393 return text_item_create_nocopy (subtype, xstrdup (text),
394 xstrdup_if_nonnull (label));
397 /* Creates and returns a new text item containing VALUE, SUBTYPE, and LABEL.
398 Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label
401 text_item_create_value (enum text_item_subtype subtype,
402 struct pivot_value *value, char *label)
404 if (subtype == TEXT_ITEM_SYNTAX || subtype == TEXT_ITEM_LOG)
406 if (!value->font_style)
408 value->font_style = xmalloc (sizeof *value->font_style);
409 *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER;
412 free (value->font_style->typeface);
413 value->font_style->typeface = xstrdup ("Monospaced");
416 struct output_item *item = xzalloc (sizeof *item);
417 *item = (struct output_item) {
418 OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_TEXT),
419 .command_name = xstrdup_if_nonnull (output_get_command_name ()),
421 .text = { .subtype = subtype, .content = value },
426 /* Returns ITEM's subtype. */
427 enum text_item_subtype
428 text_item_get_subtype (const struct output_item *item)
430 assert (item->type == OUTPUT_ITEM_TEXT);
431 return item->text.subtype;
434 /* Returns ITEM's text, which the caller must eventually free. */
436 text_item_get_plain_text (const struct output_item *item)
438 assert (item->type == OUTPUT_ITEM_TEXT);
439 return pivot_value_to_string_defaults (item->text.content);
443 nullable_font_style_equal (const struct font_style *a,
444 const struct font_style *b)
446 return a && b ? font_style_equal (a, b) : !a && !b;
449 /* Attempts to append the text in SRC to DST. If successful, returns true,
452 Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
455 DST must not be shared. */
457 text_item_append (struct output_item *dst, const struct output_item *src)
459 assert (dst->type == OUTPUT_ITEM_TEXT);
460 assert (src->type == OUTPUT_ITEM_TEXT);
461 assert (!output_item_is_shared (dst));
463 enum text_item_subtype ds = dst->text.subtype;
464 enum text_item_subtype ss = src->text.subtype;
466 struct pivot_value *dc = dst->text.content;
467 const struct pivot_value *sc = src->text.content;
470 || (ds != TEXT_ITEM_SYNTAX && ds != TEXT_ITEM_LOG)
471 || strcmp (output_item_get_label (dst), output_item_get_label (src))
472 || !nullable_font_style_equal (dc->font_style, sc->font_style)
473 || (dc->font_style && dc->font_style->markup)
474 || sc->type != PIVOT_VALUE_TEXT
475 || dc->type != PIVOT_VALUE_TEXT)
479 /* Calculate new text. */
480 char *new_text = xasprintf ("%s\n%s", dc->text.local, sc->text.local);
482 /* Free the old text. */
483 free (dc->text.local);
484 if (dc->text.c != dc->text.local)
486 if (dc->text.id != dc->text.local && dc->text.id != dc->text.c)
489 /* Put in new text. */
490 dc->text.local = new_text;
491 dc->text.c = new_text;
492 dc->text.id = new_text;
498 static const struct pivot_table_look *
499 text_item_table_look (void)
501 static struct pivot_table_look *look;
504 look = pivot_table_look_new_builtin_default ();
506 for (int a = 0; a < PIVOT_N_AREAS; a++)
507 memset (look->areas[a].cell_style.margin, 0,
508 sizeof look->areas[a].cell_style.margin);
509 for (int b = 0; b < PIVOT_N_BORDERS; b++)
510 look->borders[b].stroke = TABLE_STROKE_NONE;
516 text_item_to_table_item (struct output_item *text_item)
518 assert (text_item->type == OUTPUT_ITEM_TEXT);
520 /* Create a new table whose contents come from TEXT_ITEM. */
521 struct pivot_table *table = pivot_table_create__ (NULL, "Text");
522 pivot_table_set_look (table, text_item_table_look ());
524 struct pivot_dimension *d = pivot_dimension_create (
525 table, PIVOT_AXIS_ROW, N_("Text"));
526 d->hide_all_labels = true;
527 pivot_category_create_leaf (d->root, pivot_value_new_text ("null"));
529 pivot_table_put1 (table, 0, pivot_value_clone (text_item->text.content));
531 /* Free TEXT_ITEM. */
532 output_item_unref (text_item);
534 /* Return a new output item. */
535 return table_item_create (table);
539 text_item_subtype_to_string (enum text_item_subtype subtype)
543 case TEXT_ITEM_PAGE_TITLE:
544 return _("Page Title");
546 case TEXT_ITEM_TITLE:
549 case TEXT_ITEM_SYNTAX: