output-item: Make label a part of every output_item.
[pspp] / src / ui / gui / psppire-output-view.c
index 626d52e07e710c525a31c21e3eb0a959ec970582..cc00a05bdaa327f8188f43bdeb826bc311daa00c 100644 (file)
 #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"
@@ -76,7 +73,10 @@ struct psppire_output_view
 
     /* 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;
@@ -84,9 +84,9 @@ struct psppire_output_view
 
 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
   };
 
@@ -164,7 +164,6 @@ get_xr_fsm_style (struct psppire_output_view *view)
       [XR_FONT_FIXED] = ff,
     },
     .use_system_colors = true,
-    .transparent = true,
     .font_resolution = 96.0,
   };
 
@@ -362,6 +361,12 @@ rerender (struct psppire_output_view *view)
           gtk_layout_move (view->output, item->drawing_area, xpos, view->y);
         }
 
+      if (is_table_item (item->item))
+        {
+          const struct table_item *ti = to_table_item (item->item);
+          gtk_widget_set_tooltip_text (item->drawing_area, ti->notes);
+        }
+
       {
        gint minw;
        gint minh;
@@ -400,7 +405,6 @@ psppire_output_view_put (struct psppire_output_view *view,
 {
   struct output_view_item *view_item;
   GtkWidget *drawing_area;
-  struct string name;
   int tw, th;
 
   if (is_group_close_item (item))
@@ -468,8 +472,6 @@ psppire_output_view_put (struct psppire_output_view *view,
       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;
@@ -488,45 +490,11 @@ psppire_output_view_put (struct psppire_output_view *view,
                                                      &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);
@@ -600,32 +568,16 @@ enum {
   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
@@ -665,6 +617,7 @@ clipboard_get_cb (GtkClipboard     *clipboard,
 
     case SELECT_FMT_TEXT:
       string_map_insert (&options, "format", "txt");
+      string_map_insert (&options, "width", "1000");
       break;
 
     case SELECT_FMT_HTML:
@@ -705,16 +658,25 @@ clipboard_get_cb (GtkClipboard     *clipboard,
       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
     {
@@ -733,19 +695,11 @@ clipboard_get_cb (GtkClipboard     *clipboard,
       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:
 
@@ -860,24 +814,12 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
   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);
 
@@ -898,7 +840,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
 
       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);
@@ -908,7 +850,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
       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);
@@ -989,55 +931,66 @@ static cairo_t *
 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];
+
+  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,
+
+    .margins = {
+      [H] = { margins[H][0], margins[H][1] },
+      [V] = { margins[V][0], margins[V][1] },
+    },
+    .initial_page_number = 1,
+    .object_spacing = 12 * XR_POINT,
+  };
+
+  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 },
+    .fonts = {
+      [XR_FONT_PROPORTIONAL] = proportional_font,
+      [XR_FONT_FIXED] = fixed_font,
+    },
+    .fg = CELL_COLOR_BLACK,
+    .use_system_colors = false,
+    .font_resolution = 72.0
+  };
+
+  view->pager = xr_pager_create (view->page_style, view->fsm_style);
 }
 
 static gboolean
@@ -1053,22 +1006,22 @@ paginate (GtkPrintOperation *operation,
     }
   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;
 
@@ -1084,7 +1037,7 @@ begin_print (GtkPrintOperation *operation,
   create_xr_print_driver (context, view);
 
   view->print_item = 0;
-  view->print_n_pages = 1;
+  view->print_n_pages = 0;
   view->paginated = FALSE;
 }
 
@@ -1093,7 +1046,8 @@ end_print (GtkPrintOperation *operation,
           GtkPrintContext   *context,
           struct psppire_output_view *view)
 {
-  xr_driver_destroy (view->print_xrd);
+  xr_pager_destroy (view->pager);
+  view->pager = NULL;
 }
 
 
@@ -1103,10 +1057,11 @@ draw_page (GtkPrintOperation *operation,
           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);
 }
 
 
@@ -1121,6 +1076,9 @@ psppire_output_view_print (struct psppire_output_view *view,
   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);