psppire-output-view: Export correctly to drivers that don't handle groups.
[pspp] / src / ui / gui / psppire-output-view.c
index 76e0e1c1267493533174a79f19ae7c3952ccfdba..07dbd646161dbf5dc56ee1bdd0aee541bc880040 100644 (file)
 #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"
@@ -48,6 +43,8 @@ struct output_view_item
   {
     struct output_item *item;
     GtkWidget *drawing_area;
+    int width, height;
+    int nesting_depth;
   };
 
 struct psppire_output_view
@@ -61,7 +58,6 @@ struct psppire_output_view
     glong y;
 
     GtkTreeView *overview;
-    GtkTreePath *cur_group;
 
     GtkWidget *toplevel;
 
@@ -159,6 +155,7 @@ get_xr_fsm_style (struct psppire_output_view *view)
     .min_break = { [TABLE_HORZ] = xr_width / 2, [TABLE_VERT] = 0 },
     .font = pf,
     .use_system_colors = true,
+    .object_spacing = XR_POINT * 12,
     .font_resolution = 96.0,
   };
 
@@ -329,10 +326,10 @@ rerender (struct psppire_output_view *view)
       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 ();
@@ -356,11 +353,9 @@ 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);
-        }
+      if (item->item->type == OUTPUT_ITEM_TABLE)
+        gtk_widget_set_tooltip_text (item->drawing_area,
+                                     item->item->table->notes);
 
       {
        gint minw;
@@ -393,49 +388,20 @@ rerender (struct psppire_output_view *view)
   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;
-  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);
 
@@ -446,22 +412,51 @@ psppire_output_view_put (struct psppire_output_view *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,
+                              parent_path ? gtk_tree_path_get_depth (parent_path) : 0))
+    return;
+  view->n_items++;
+
+  GtkTreePath *path = NULL;
   if (view->overview)
     {
       GtkTreeStore *store = GTK_TREE_STORE (
@@ -470,38 +465,44 @@ psppire_output_view_put (struct psppire_output_view *view,
       /* 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);
-        }
-
       gtk_tree_store_set (store, &iter,
                           COL_LABEL, output_item_get_label (item),
                          COL_ADDR, item,
                           COL_Y, view->y,
                           -1);
 
-      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
@@ -741,8 +742,7 @@ build_target_list (const struct output_item *item)
 {
   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;
 }
@@ -803,10 +803,6 @@ struct psppire_output_view *
 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) {
@@ -833,7 +829,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
     {
       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_LABEL */
                                 G_TYPE_POINTER, /* COL_ADDR */
@@ -841,9 +837,9 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
       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_LABEL);
 
@@ -876,33 +872,24 @@ psppire_output_view_destroy (struct psppire_output_view *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;
+/* Export. */
 
-  for (i = 0; i < view->n_items; i++)
+static void
+psppire_output_view_export__ (struct output_driver *driver,
+                              const struct output_item *item)
+{
+  if (item->type == OUTPUT_ITEM_GROUP && !driver->class->handles_groups)
     {
-      gtk_container_remove (GTK_CONTAINER (view->output),
-                            view->items[i].drawing_area);
-      output_item_unref (view->items[i].item);
+      for (size_t i = 0; i < item->group.n_children; i++)
+        psppire_output_view_export__ (driver, item->group.children[i]);
     }
-  free (view->items);
-  view->items = NULL;
-  view->n_items = view->allocated_items = 0;
+  else
+    driver->class->submit (driver, item);
 }
 
-/* Export. */
-
 void
 psppire_output_view_export (struct psppire_output_view *view,
                             struct string_map *options)
@@ -915,7 +902,8 @@ psppire_output_view_export (struct psppire_output_view *view,
       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)
+          psppire_output_view_export__ (driver, view->items[i].item);
       output_driver_destroy (driver);
     }
 }
@@ -962,7 +950,6 @@ create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *vi
       [V] = { margins[V][0], margins[V][1] },
     },
     .initial_page_number = 1,
-    .object_spacing = 12 * XR_POINT,
   };
 
   view->fsm_style = xmalloc (sizeof *view->fsm_style);
@@ -974,6 +961,7 @@ create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *vi
     .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
   };
 
@@ -991,29 +979,32 @@ paginate (GtkPrintOperation *operation,
          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
@@ -1048,7 +1039,11 @@ draw_page (GtkPrintOperation *operation,
                      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);
+    }
 }
 
 
@@ -1083,47 +1078,3 @@ psppire_output_view_print (struct psppire_output_view *view,
 
   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);
-}