#include <errno.h>
#include <stdbool.h>
-#if HAVE_RSVG
-#include "librsvg/rsvg.h"
-#endif
#include "libpspp/assertion.h"
#include "libpspp/string-map.h"
#include "output/cairo-fsm.h"
#include "output/cairo-pager.h"
#include "output/driver-provider.h"
#include "output/driver.h"
-#include "output/chart-item.h"
-#include "output/group-item.h"
-#include "output/message-item.h"
#include "output/output-item.h"
-#include "output/output-item-provider.h"
-#include "output/table-item.h"
-#include "output/text-item.h"
+#include "output/pivot-table.h"
#include "gl/c-xvasprintf.h"
#include "gl/minmax.h"
{
struct output_item *item;
GtkWidget *drawing_area;
+ int width, height;
+ int nesting_depth;
};
struct psppire_output_view
glong y;
GtkTreeView *overview;
- GtkTreePath *cur_group;
GtkWidget *toplevel;
enum
{
- COL_NAME, /* Table name. */
+ COL_LABEL, /* Output item label. */
COL_ADDR, /* Pointer to the table */
- COL_Y, /* Y position of top of name. */
+ COL_Y, /* Y position of top of object. */
N_COLS
};
PangoFontDescription *pf;
gtk_style_context_get (context, state, "font", &pf, NULL);
- PangoFontDescription *ff = pango_font_description_from_string ("Monospace");
- pango_font_description_set_size (ff, pango_font_description_get_size (pf));
struct xr_fsm_style *style = xmalloc (sizeof *style);
*style = (struct xr_fsm_style) {
.ref_cnt = 1,
.size = { [TABLE_HORZ] = xr_width, [TABLE_VERT] = INT_MAX },
.min_break = { [TABLE_HORZ] = xr_width / 2, [TABLE_VERT] = 0 },
- .fonts = {
- [XR_FONT_PROPORTIONAL] = pf,
- [XR_FONT_FIXED] = ff,
- },
+ .font = pf,
.use_system_colors = true,
- .transparent = true,
+ .object_spacing = XR_POINT * 12,
.font_resolution = 96.0,
};
if (view->y > 0)
view->y += view->object_spacing;
- if (is_group_open_item (item->item))
+ if (item->item->type == OUTPUT_ITEM_GROUP)
continue;
- r = xr_fsm_create (item->item, view->style, cr);
+ r = xr_fsm_create_for_scrolling (item->item, view->style, cr);
if (r == NULL)
{
g_warn_if_reached ();
gtk_layout_move (view->output, item->drawing_area, xpos, view->y);
}
+ if (item->item->type == OUTPUT_ITEM_TABLE)
+ gtk_widget_set_tooltip_text (item->drawing_area,
+ item->item->table->notes);
+
{
gint minw;
gint minh;
cairo_region_destroy (region);
}
-
-void
-psppire_output_view_put (struct psppire_output_view *view,
- const struct output_item *item)
+static bool
+init_output_view_item (struct output_view_item *view_item,
+ struct psppire_output_view *view,
+ const struct output_item *item,
+ int nesting_depth)
{
- struct output_view_item *view_item;
- GtkWidget *drawing_area;
- struct string name;
- int tw, th;
-
- if (is_group_close_item (item))
- {
- if (view->cur_group)
- {
- if (!gtk_tree_path_up (view->cur_group))
- {
- gtk_tree_path_free (view->cur_group);
- view->cur_group = NULL;
- }
- }
- return;
- }
- else if (is_text_item (item))
- {
- const struct text_item *text_item = to_text_item (item);
- const char *text = text_item_get_text (text_item);
- if (text[0] == '\0')
- return;
- }
-
- if (view->n_items >= view->allocated_items)
- view->items = x2nrealloc (view->items, &view->allocated_items,
- sizeof *view->items);
- view_item = &view->items[view->n_items++];
- view_item->item = output_item_ref (item);
- view_item->drawing_area = NULL;
+ *view_item = (struct output_view_item) {
+ .item = output_item_ref (item),
+ .nesting_depth = nesting_depth
+ };
GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (view->output));
- if (is_group_open_item (item))
- tw = th = 0;
- else if (win)
+ if (win && item->type != OUTPUT_ITEM_GROUP)
{
- view_item->drawing_area = drawing_area = gtk_drawing_area_new ();
-
if (!view->style)
view->style = get_xr_fsm_style (view);
if (view->y > 0)
view->y += view->object_spacing;
- struct xr_fsm *r = xr_fsm_create (item, view->style, cr);
+ struct xr_fsm *r = xr_fsm_create_for_scrolling (item, view->style, cr);
if (r == NULL)
{
gdk_window_end_draw_frame (win, ctx);
cairo_region_destroy (region);
- return;
+
+ output_item_unref (view_item->item);
+ return false;
}
- xr_fsm_measure (r, cr, &tw, &th);
- create_drawing_area (view, drawing_area, r, tw, th, item);
+ xr_fsm_measure (r, cr, &view_item->width, &view_item->height);
+ view_item->drawing_area = gtk_drawing_area_new ();
+ create_drawing_area (view, view_item->drawing_area, r, view_item->width,
+ view_item->height, item);
gdk_window_end_draw_frame (win, ctx);
cairo_region_destroy (region);
}
- else
- tw = th = 0;
+ return true;
+}
+
+static void
+psppire_output_view_put__ (struct psppire_output_view *view,
+ const struct output_item *item,
+ GtkTreePath *parent_path)
+{
+ if (item->type == OUTPUT_ITEM_TEXT)
+ {
+ char *text = text_item_get_plain_text (item);
+ bool text_is_empty = text[0] == '\0';
+ free (text);
+ if (text_is_empty)
+ return;
+ }
+
+ if (view->n_items >= view->allocated_items)
+ view->items = x2nrealloc (view->items, &view->allocated_items,
+ sizeof *view->items);
+ struct output_view_item *view_item = &view->items[view->n_items];
+ if (!init_output_view_item (view_item, view, item,
+ gtk_tree_path_get_depth (parent_path)))
+ return;
+ view->n_items++;
+
+ GtkTreePath *path = NULL;
if (view->overview)
{
GtkTreeStore *store = GTK_TREE_STORE (
gtk_tree_view_get_model (view->overview));
- ds_init_empty (&name);
-
/* Create a new node in the tree and puts a reference to it in 'iter'. */
GtkTreeIter iter;
GtkTreeIter parent;
- if (view->cur_group
- && gtk_tree_path_get_depth (view->cur_group) > 0
+ if (parent_path
+ && gtk_tree_path_get_depth (parent_path) > 0
&& gtk_tree_model_get_iter (GTK_TREE_MODEL (store),
- &parent, view->cur_group))
+ &parent, parent_path))
gtk_tree_store_append (store, &iter, &parent);
else
gtk_tree_store_append (store, &iter, NULL);
- if (is_group_open_item (item))
- {
- gtk_tree_path_free (view->cur_group);
- view->cur_group = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
- &iter);
- }
-
- ds_clear (&name);
- if (is_text_item (item))
- {
- const struct text_item *text_item = to_text_item (item);
- ds_put_cstr (&name, text_item_type_to_string (
- text_item_get_type (text_item)));
- }
- else if (is_message_item (item))
- {
- const struct message_item *msg_item = to_message_item (item);
- const struct msg *msg = message_item_get_msg (msg_item);
- ds_put_format (&name, "%s: %s", _("Message"),
- msg_severity_to_string (msg->severity));
- }
- else if (is_table_item (item))
- {
- const struct table_item_text *title
- = table_item_get_title (to_table_item (item));
- if (title != NULL)
- ds_put_format (&name, "Table: %s", title->content);
- else
- ds_put_cstr (&name, "Table");
- }
- else if (is_chart_item (item))
- {
- const char *s = chart_item_get_title (to_chart_item (item));
- if (s != NULL)
- ds_put_format (&name, "Chart: %s", s);
- else
- ds_put_cstr (&name, "Chart");
- }
- else if (is_group_open_item (item))
- ds_put_cstr (&name, to_group_open_item (item)->command_name);
gtk_tree_store_set (store, &iter,
- COL_NAME, ds_cstr (&name),
+ COL_LABEL, output_item_get_label (item),
COL_ADDR, item,
- COL_Y, (view->y),
+ COL_Y, view->y,
-1);
- ds_destroy (&name);
- GtkTreePath *path = gtk_tree_model_get_path (
+ /* Get the path of the new row. */
+ path = gtk_tree_model_get_path (
GTK_TREE_MODEL (store), &iter);
gtk_tree_view_expand_row (view->overview, path, TRUE);
- gtk_tree_path_free (path);
}
- if (view->max_width < tw)
- view->max_width = tw;
- view->y += th;
+ if (view->max_width < view_item->width)
+ view->max_width = view_item->width;
+ view->y += view_item->height;
gtk_layout_set_size (view->output, view->max_width, view->y);
+
+ if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ psppire_output_view_put__ (view, item->group.children[i], path);
+
+ gtk_tree_path_free (path);
+}
+
+void
+psppire_output_view_put (struct psppire_output_view *view,
+ const struct output_item *item)
+{
+ psppire_output_view_put__ (view, item, NULL);
}
static void
SELECT_FMT_ODT
};
-/* Returns a pixbuf from a svg file */
-/* You must unref the pixbuf after usage */
-static GdkPixbuf *
-derive_pixbuf_from_svg (const char *filename)
+static void
+clear_rectangle (cairo_surface_t *surface,
+ double x0, double y0, double x1, double y1)
{
- GError *err = NULL;
- GdkPixbuf *pixbuf = NULL;
-#if HAVE_RSVG
- RsvgHandle *handle = rsvg_handle_new_from_file (filename, &err);
- if (err == NULL)
- {
- rsvg_handle_set_dpi (handle, 300.0);
- pixbuf = rsvg_handle_get_pixbuf (handle);
- g_object_unref (handle);
- }
-#else
- pixbuf = gdk_pixbuf_new_from_file (filename, &err);
-#endif
- if (err != NULL)
- {
- msg (ME, _("Could not open file %s during copy operation: %s"),
- filename, err->message);
- g_error_free (err);
- return NULL;
- }
- return pixbuf;
+ cairo_t *cr = cairo_create (surface);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill (cr);
+ cairo_destroy (cr);
}
static void
case SELECT_FMT_TEXT:
string_map_insert (&options, "format", "txt");
+ string_map_insert (&options, "width", "1000");
break;
case SELECT_FMT_HTML:
gdk_window_end_draw_frame (win, ctx);
cairo_region_destroy (region);
- cairo_surface_t *surface = cairo_svg_surface_create (filename, w, h);
- if (surface)
+ cairo_surface_t *surface
+ = (info == SELECT_FMT_SVG
+ ? cairo_svg_surface_create (filename, w, h)
+ : cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h));
+ clear_rectangle (surface, 0, 0, w, h);
+ cairo_t *cr2 = cairo_create (surface);
+ xr_fsm_draw_all (fsm, cr2);
+ cairo_destroy (cr2);
+ if (info == SELECT_FMT_IMG)
{
- cairo_t *cr = cairo_create (surface);
- xr_fsm_draw_all (fsm, cr);
- cairo_destroy (cr);
- cairo_surface_destroy (surface);
+ GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface (surface,
+ 0, 0, w, h);
+ if (pixbuf)
+ {
+ gtk_selection_data_set_pixbuf (selection_data, pixbuf);
+ g_object_unref (pixbuf);
+ }
}
- else
- g_error ("Could not create cairo svg surface with file %s", filename);
+ cairo_surface_destroy (surface);
}
else
{
driver = NULL;
}
- if (info == SELECT_FMT_IMG)
- {
- GdkPixbuf *pixbuf = derive_pixbuf_from_svg (filename);
- if (pixbuf)
- {
- gtk_selection_data_set_pixbuf (selection_data, pixbuf);
- g_object_unref (pixbuf);
- }
- }
- else if (g_file_get_contents (filename, &text, &length, NULL))
- gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
- 8,
- (const guchar *) text, length);
+ if (info != SELECT_FMT_IMG
+ && g_file_get_contents (filename, &text, &length, NULL))
+ gtk_selection_data_set (selection_data,
+ gtk_selection_data_get_target (selection_data),
+ 8, (const guchar *) text, length);
finish:
{
GtkTargetList *tl = gtk_target_list_new (targets, G_N_ELEMENTS (targets));
g_return_val_if_fail (tl, NULL);
- if (is_table_item (item) ||
- is_chart_item (item))
+ if (item->type == OUTPUT_ITEM_TABLE || item->type == OUTPUT_ITEM_CHART)
gtk_target_list_add_image_targets (tl, SELECT_FMT_IMG, TRUE);
return tl;
}
psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
{
struct psppire_output_view *view;
- GtkTreeViewColumn *column;
- GtkCellRenderer *renderer;
-
- GtkTreeModel *model;
view = xmalloc (sizeof *view);
*view = (struct psppire_output_view) {
{
g_signal_connect (overview, "realize", G_CALLBACK (on_realize), view);
- model = GTK_TREE_MODEL (gtk_tree_store_new (
+ GtkTreeModel *model = GTK_TREE_MODEL (gtk_tree_store_new (
N_COLS,
- G_TYPE_STRING, /* COL_NAME */
+ G_TYPE_STRING, /* COL_LABEL */
G_TYPE_POINTER, /* COL_ADDR */
G_TYPE_LONG)); /* COL_Y */
gtk_tree_view_set_model (overview, model);
g_object_unref (model);
- column = gtk_tree_view_column_new ();
+ GtkTreeViewColumn *column = gtk_tree_view_column_new ();
gtk_tree_view_append_column (GTK_TREE_VIEW (overview), column);
- renderer = gtk_cell_renderer_text_new ();
+ GtkCellRenderer * renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer, TRUE);
- gtk_tree_view_column_add_attribute (column, renderer, "text", COL_NAME);
+ gtk_tree_view_column_add_attribute (column, renderer, "text", COL_LABEL);
g_signal_connect (GTK_TREE_VIEW (overview),
"row-activated", G_CALLBACK (on_row_activate), view);
if (view->print_settings != NULL)
g_object_unref (view->print_settings);
- if (view->cur_group)
- gtk_tree_path_free (view->cur_group);
-
free (view);
}
-void
-psppire_output_view_clear (struct psppire_output_view *view)
-{
- size_t i;
-
- view->max_width = 0;
- view->y = 0;
-
- for (i = 0; i < view->n_items; i++)
- {
- gtk_container_remove (GTK_CONTAINER (view->output),
- view->items[i].drawing_area);
- output_item_unref (view->items[i].item);
- }
- free (view->items);
- view->items = NULL;
- view->n_items = view->allocated_items = 0;
-}
-
/* Export. */
void
size_t i;
for (i = 0; i < view->n_items; i++)
- driver->class->submit (driver, view->items[i].item);
+ if (view->items[i].nesting_depth == 0)
+ driver->class->submit (driver, view->items[i].item);
output_driver_destroy (driver);
}
}
for (int a = 0; a < TABLE_N_AXES; a++)
size[a] = paper[a] - margins[a][0] - margins[a][1];
- PangoFontDescription *proportional_font
- = pango_font_description_from_string ("Sans Serif 10");
- PangoFontDescription *fixed_font
- = pango_font_description_from_string ("Monospace 10");
-
view->page_style = xmalloc (sizeof *view->page_style);
*view->page_style = (struct xr_page_style) {
.ref_cnt = 1,
[H] = { margins[H][0], margins[H][1] },
[V] = { margins[V][0], margins[V][1] },
},
- .bg = { .alpha = 0 },
.initial_page_number = 1,
- .object_spacing = 12 * XR_POINT,
};
view->fsm_style = xmalloc (sizeof *view->fsm_style);
.size = { [H] = size[H], [V] = size[V] },
.min_break = { [H] = size[H] / 2, [V] = size[V] / 2 },
- .fonts = {
- [XR_FONT_PROPORTIONAL] = proportional_font,
- [XR_FONT_FIXED] = fixed_font,
- },
+ .font = pango_font_description_from_string ("Sans Serif 10"),
.fg = CELL_COLOR_BLACK,
.use_system_colors = false,
- .transparent = false,
+ .object_spacing = 12 * XR_POINT,
.font_resolution = 72.0
};
complete. Don't let that screw up printing. */
return TRUE;
}
- else if (view->print_item < view->n_items)
+
+ while (view->print_item < view->n_items)
{
- xr_pager_add_item (view->pager, view->items[view->print_item++].item);
- while (xr_pager_needs_new_page (view->pager))
- {
- xr_pager_add_page (view->pager,
- get_cairo_context_from_print_context (context));
- view->print_n_pages ++;
- }
- return FALSE;
+ const struct output_view_item *item = &view->items[view->print_item++];
+ if (item->nesting_depth == 0)
+ {
+ xr_pager_add_item (view->pager, item->item);
+ while (xr_pager_needs_new_page (view->pager))
+ {
+ xr_pager_add_page (view->pager,
+ get_cairo_context_from_print_context (context));
+ view->print_n_pages ++;
+ }
+ return FALSE;
+ }
}
- else
- {
- gtk_print_operation_set_n_pages (operation, MAX (1, view->print_n_pages));
- /* Re-create the driver to do the real printing. */
- xr_pager_destroy (view->pager);
- view->pager = xr_pager_create (view->page_style, view->fsm_style);
- view->print_item = 0;
- view->paginated = TRUE;
+ gtk_print_operation_set_n_pages (operation, MAX (1, view->print_n_pages));
- return TRUE;
- }
+ /* Re-create the driver to do the real printing. */
+ xr_pager_destroy (view->pager);
+ view->pager = xr_pager_create (view->page_style, view->fsm_style);
+ view->print_item = 0;
+ view->paginated = TRUE;
+
+ return TRUE;
}
static void
get_cairo_context_from_print_context (context));
while (!xr_pager_needs_new_page (view->pager)
&& view->print_item < view->n_items)
- xr_pager_add_item (view->pager, view->items [view->print_item++].item);
+ {
+ const struct output_view_item *item = &view->items [view->print_item++];
+ if (item->nesting_depth == 0)
+ xr_pager_add_item (view->pager, item->item);
+ }
}
g_object_unref (print);
}
-\f
-struct psppire_output_view_driver
- {
- struct output_driver driver;
- struct psppire_output_view *view;
- };
-
-static struct psppire_output_view_driver *
-psppire_output_view_driver_cast (struct output_driver *driver)
-{
- return UP_CAST (driver, struct psppire_output_view_driver, driver);
-}
-
-static void
-psppire_output_view_submit (struct output_driver *this,
- const struct output_item *item)
-{
- struct psppire_output_view_driver *povd = psppire_output_view_driver_cast (this);
-
- if (is_table_item (item))
- psppire_output_view_put (povd->view, item);
-}
-
-static struct output_driver_class psppire_output_view_driver_class =
- {
- "PSPPIRE Output View", /* name */
- NULL, /* destroy */
- psppire_output_view_submit, /* submit */
- NULL, /* flush */
- };
-
-void
-psppire_output_view_register_driver (struct psppire_output_view *view)
-{
- struct psppire_output_view_driver *povd;
- struct output_driver *d;
-
- povd = xzalloc (sizeof *povd);
- povd->view = view;
- d = &povd->driver;
- output_driver_init (d, &psppire_output_view_driver_class, "PSPPIRE Output View",
- SETTINGS_DEVICE_UNFILTERED);
- output_driver_register (d);
-}