#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.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"
/* Variables pertaining to printing */
GtkPrintSettings *print_settings;
- struct xr_driver *print_xrd;
+
+ struct xr_fsm_style *fsm_style;
+ struct xr_page_style *page_style;
+ struct xr_pager *pager;
int print_item;
int print_n_pages;
gboolean paginated;
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,
- .font_scale = 1.0,
+ .object_spacing = XR_POINT * 12,
+ .font_resolution = 96.0,
};
return style;
if (view->y > 0)
view->y += view->object_spacing;
- if (is_group_open_item (item->item))
+ if (item->item->type == OUTPUT_ITEM_GROUP_OPEN)
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;
{
struct output_view_item *view_item;
GtkWidget *drawing_area;
- struct string name;
int tw, th;
- if (is_group_close_item (item))
+ if (item->type == OUTPUT_ITEM_GROUP_CLOSE)
{
if (view->cur_group)
{
}
return;
}
- else if (is_text_item (item))
+ else if (item->type == OUTPUT_ITEM_TEXT)
{
- const struct text_item *text_item = to_text_item (item);
- const char *text = text_item_get_text (text_item);
- if (text[0] == '\0')
+ char *text = text_item_get_plain_text (item);
+ bool text_is_empty = text[0] == '\0';
+ free (text);
+ if (text_is_empty)
return;
}
view_item->drawing_area = NULL;
GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (view->output));
- if (is_group_open_item (item))
+ if (item->type == OUTPUT_ITEM_GROUP_OPEN)
tw = th = 0;
else if (win)
{
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);
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;
else
gtk_tree_store_append (store, &iter, NULL);
- if (is_group_open_item (item))
+ if (item->type == OUTPUT_ITEM_GROUP_OPEN)
{
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 (
GTK_TREE_MODEL (store), &iter);
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;
}
GtkTreeModel *model;
view = xmalloc (sizeof *view);
- view->style = NULL;
- view->object_spacing = 10;
- view->output = output;
- view->render_width = 0;
- view->max_width = 0;
- view->y = 0;
- view->overview = overview;
- view->cur_group = NULL;
- view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output));
- view->buttontime = 0;
- view->items = NULL;
- view->n_items = view->allocated_items = 0;
- view->selected_item = NULL;
- view->print_settings = NULL;
- view->print_xrd = NULL;
- view->print_item = 0;
- view->print_n_pages = 0;
- view->paginated = FALSE;
+ *view = (struct psppire_output_view) {
+ .object_spacing = 10,
+ .output = output,
+ .overview = overview,
+ .toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output)),
+ };
g_signal_connect (output, "draw", G_CALLBACK (layout_draw_callback), NULL);
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);
gtk_tree_view_append_column (GTK_TREE_VIEW (overview), column);
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);
get_cairo_context_from_print_context (GtkPrintContext *context)
{
cairo_t *cr = gtk_print_context_get_cairo_context (context);
-
- /*
- For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
- Windows returns 600.
- */
- double xres = gtk_print_context_get_dpi_x (context);
- double yres = gtk_print_context_get_dpi_y (context);
-
- /* This means that the cairo context now has its dimensions in Points */
- cairo_scale (cr, xres / 72.0, yres / 72.0);
-
- return cr;
+ return cairo_reference (cr);
}
-
static void
create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *view)
{
- struct string_map options;
- GtkPageSetup *page_setup;
- double width, height;
- double left_margin;
- double right_margin;
- double top_margin;
- double bottom_margin;
-
- page_setup = gtk_print_context_get_page_setup (context);
- width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
- height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
- left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
- right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
- top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
- bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
+ GtkPageSetup *ps = gtk_print_context_get_page_setup (context);
- string_map_init (&options);
- string_map_insert_nocopy (&options, xstrdup ("paper-size"),
- c_xasprintf("%.2fx%.2fmm", width, height));
- string_map_insert_nocopy (&options, xstrdup ("left-margin"),
- c_xasprintf ("%.2fmm", left_margin));
- string_map_insert_nocopy (&options, xstrdup ("right-margin"),
- c_xasprintf ("%.2fmm", right_margin));
- string_map_insert_nocopy (&options, xstrdup ("top-margin"),
- c_xasprintf ("%.2fmm", top_margin));
- string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
- c_xasprintf ("%.2fmm", bottom_margin));
-
- view->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
-
- string_map_destroy (&options);
+ enum { H = TABLE_HORZ, V = TABLE_VERT };
+ int paper[TABLE_N_AXES] = {
+ [H] = gtk_page_setup_get_paper_width (ps, GTK_UNIT_POINTS) * XR_POINT,
+ [V] = gtk_page_setup_get_paper_height (ps, GTK_UNIT_POINTS) * XR_POINT,
+ };
+
+ /* These are all 1/2 inch. The "margins" that GTK+ gives us are useless:
+ they are the printer's imagable area. */
+ int margins[TABLE_N_AXES][2] = {
+ [H][0] = XR_POINT * 36,
+ [H][1] = XR_POINT * 36,
+ [V][0] = XR_POINT * 36,
+ [V][1] = XR_POINT * 36,
+ };
+
+ double size[TABLE_N_AXES];
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ size[a] = paper[a] - margins[a][0] - margins[a][1];
+
+ view->page_style = xmalloc (sizeof *view->page_style);
+ *view->page_style = (struct xr_page_style) {
+ .ref_cnt = 1,
+
+ .margins = {
+ [H] = { margins[H][0], margins[H][1] },
+ [V] = { margins[V][0], margins[V][1] },
+ },
+ .initial_page_number = 1,
+ };
+
+ view->fsm_style = xmalloc (sizeof *view->fsm_style);
+ *view->fsm_style = (struct xr_fsm_style) {
+ .ref_cnt = 1,
+
+ .size = { [H] = size[H], [V] = size[V] },
+ .min_break = { [H] = size[H] / 2, [V] = size[V] / 2 },
+ .font = pango_font_description_from_string ("Sans Serif 10"),
+ .fg = CELL_COLOR_BLACK,
+ .use_system_colors = false,
+ .object_spacing = 12 * XR_POINT,
+ .font_resolution = 72.0
+ };
+
+ view->pager = xr_pager_create (view->page_style, view->fsm_style);
}
static gboolean
}
else if (view->print_item < view->n_items)
{
- xr_driver_output_item (view->print_xrd,
- view->items[view->print_item++].item);
- while (xr_driver_need_new_page (view->print_xrd))
+ xr_pager_add_item (view->pager, view->items[view->print_item++].item);
+ while (xr_pager_needs_new_page (view->pager))
{
- xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
+ 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, view->print_n_pages);
+ gtk_print_operation_set_n_pages (operation, MAX (1, view->print_n_pages));
/* Re-create the driver to do the real printing. */
- xr_driver_destroy (view->print_xrd);
- create_xr_print_driver (context, view);
+ xr_pager_destroy (view->pager);
+ view->pager = xr_pager_create (view->page_style, view->fsm_style);
view->print_item = 0;
view->paginated = TRUE;
create_xr_print_driver (context, view);
view->print_item = 0;
- view->print_n_pages = 1;
+ view->print_n_pages = 0;
view->paginated = FALSE;
}
GtkPrintContext *context,
struct psppire_output_view *view)
{
- xr_driver_destroy (view->print_xrd);
+ xr_pager_destroy (view->pager);
+ view->pager = NULL;
}
gint page_number,
struct psppire_output_view *view)
{
- xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
- while (!xr_driver_need_new_page (view->print_xrd)
+ xr_pager_add_page (view->pager,
+ get_cairo_context_from_print_context (context));
+ while (!xr_pager_needs_new_page (view->pager)
&& view->print_item < view->n_items)
- xr_driver_output_item (view->print_xrd, view->items [view->print_item++].item);
+ xr_pager_add_item (view->pager, view->items [view->print_item++].item);
}
if (view->print_settings != NULL)
gtk_print_operation_set_print_settings (print, view->print_settings);
+ gtk_print_operation_set_use_full_page (print, TRUE);
+ gtk_print_operation_set_unit (print, GTK_UNIT_POINTS);
+
g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), view);
g_signal_connect (print, "end_print", G_CALLBACK (end_print), view);
g_signal_connect (print, "paginate", G_CALLBACK (paginate), view);
{
struct psppire_output_view_driver *povd = psppire_output_view_driver_cast (this);
- if (is_table_item (item))
+ if (item->type == OUTPUT_ITEM_TABLE)
psppire_output_view_put (povd->view, item);
}