psppire-output-view: Avoid doubling output when exporting or printing.
[pspp] / src / ui / gui / psppire-output-view.c
index d756af4f7abc6544a55b4937f9167a31e89f3c7b..5cc20b2609582387f8d77f03095126f00c7ec478 100644 (file)
@@ -43,6 +43,8 @@ struct output_view_item
   {
     struct output_item *item;
     GtkWidget *drawing_area;
+    int width, height;
+    int nesting_depth;
   };
 
 struct psppire_output_view
@@ -56,7 +58,6 @@ struct psppire_output_view
     glong y;
 
     GtkTreeView *overview;
-    GtkTreePath *cur_group;
 
     GtkWidget *toplevel;
 
@@ -387,39 +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 (item->type == OUTPUT_ITEM_GROUP)
-    return;
-
-  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);
-  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 (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);
 
@@ -435,17 +417,46 @@ psppire_output_view_put (struct psppire_output_view *view,
        {
          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 (
@@ -454,32 +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);
 
-      /* XXX group? */
       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
@@ -780,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) {
@@ -810,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 */
@@ -818,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);
 
@@ -853,31 +872,9 @@ 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;
-
-  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
@@ -892,7 +889,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)
+          driver->class->submit (driver, view->items[i].item);
       output_driver_destroy (driver);
     }
 }
@@ -968,29 +966,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
@@ -1025,7 +1026,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);
+    }
 }