merge master->gtk3, fixed psppire-output-view.c refactoring; this compiles and runs...
authorFriedrich Beckmann <friedrich.beckmann@gmx.de>
Fri, 1 May 2015 09:04:20 +0000 (11:04 +0200)
committerFriedrich Beckmann <friedrich.beckmann@gmx.de>
Fri, 1 May 2015 09:04:20 +0000 (11:04 +0200)
17 files changed:
1  2 
INSTALL
configure.ac
src/output/cairo.c
src/output/cairo.h
src/output/render.c
src/output/render.h
src/ui/gui/automake.mk
src/ui/gui/main.c
src/ui/gui/pspp-widget-facade.c
src/ui/gui/psppire-data-window.c
src/ui/gui/psppire-output-view.c
src/ui/gui/psppire-output-window.c
src/ui/gui/psppire-syntax-window.c
src/ui/gui/psppire-window.c
src/ui/gui/roc.ui
src/ui/gui/spreadsheet-test.c
src/ui/gui/text-data-import-dialog.c

diff --cc INSTALL
Simple merge
diff --cc configure.ac
index b0f16685b599c12fb827513abc3cba0d58822433,733923fb01d34c45b33e46743128a51099755372..cc1dbba0635a410c799638687fff6f5f72bee3b3
@@@ -71,12 -70,16 +70,15 @@@ if test "$with_cairo" != no && test "$w
    PKG_CHECK_MODULES([GTHREAD], [gthread-2.0], [],
      [PSPP_REQUIRED_PREREQ([gthread 2.0 (or use --without-gui)])])
  
 -  PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.24], [],
 -    [PSPP_REQUIRED_PREREQ([gtk+ 2.0 version 2.24 or later (or use --without-gui)])])
 +  PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= 3.4.2], [],
 +    [PSPP_REQUIRED_PREREQ([gtk+ 3.0 version 3.4.2 or later (or use --without-gui)])])
 +
 +  PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-3.0 >= 3.4.2], [],
 +    [PSPP_REQUIRED_PREREQ([gtksourceview 3.0 version 3.4.2 or later (or use --without-gui)])])
  
 -
 -  PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-2.0 >= 2.2], [],
 -    [PSPP_REQUIRED_PREREQ([gtksourceview 2.0 version 2.2 or later (or use --without-gui)])])
 -
+   PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.32], [],
+     [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.32 or later (or use --without-gui)])])
    AC_ARG_VAR([GLIB_GENMARSHAL])
    AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal])
    if test "x$GLIB_GENMARSHAL" = x; then
index 2debb27f4725a344e3a9a53a98059cfa95878e64,4af75caae455f3a5f03f1c687bcfba7efc45c324..a3070dddef3e2e69dc65ff58a3c4c8f1b82d7739
@@@ -1037,15 -1383,10 +1381,8 @@@ xr_rendering_draw_all (struct xr_render
  
        xr_set_cairo (xr, cr);
  
-       if (r->title_height > 0)
-         {
-           xr->y = 0;
-           xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
-                          r->title_width, r->title_height);
-         }
 -      xr->y = 0;
 -      render_pager_draw_region (r->p,
 -                                x * XR_POINT, y * XR_POINT,
 -                                w * XR_POINT, h * XR_POINT);
++      render_pager_draw (r->p);
 +
-       xr->y = r->title_height;
-       render_page_draw (r->page);
      }
    else
      xr_draw_chart (to_chart_item (r->item), cr,
Simple merge
index c178839a0bfd1d7f79348f371b4d15cead9b3daf,d282a8fce68797e0430fc7a95cf86a5c1dbe35dd..94d89665b73f09cc2da9f4c367226524f47bb379
@@@ -955,12 -1095,88 +1095,88 @@@ render_page_draw (const struct render_p
    bb[V][0] = 0;
    bb[V][1] = page->n[V] * 2 + 1;
  
-   render_page_draw_cells (page, bb);
+   render_page_draw_cells (page, ofs, bb);
+ }
+ /* Returns the greatest value i, 0 <= i < n, such that cp[i] <= x0. */
+ static int
+ get_clip_min_extent (int x0, const int cp[], int n)
+ {
+   int low, high, best;
+   low = 0;
+   high = n;
+   best = 0;
+   while (low < high)
+     {
+       int middle = low + (high - low) / 2;
+       if (cp[middle] <= x0)
+         {
+           best = middle;
+           low = middle + 1;
+         }
+       else
+         high = middle;
+     }
+   return best;
+ }
+ /* Returns the least value i, 0 <= i < n, such that cp[i] >= x1. */
+ static int
+ get_clip_max_extent (int x1, const int cp[], int n)
+ {
+   int low, high, best;
+   low = 0;
+   high = n;
+   best = n;
+   while (low < high)
+     {
+       int middle = low + (high - low) / 2;
+       if (cp[middle] >= x1)
+         best = high = middle;
+       else
+         low = middle + 1;
+     }
+   while (best > 0 && cp[best - 1] == cp[best])
+     best--;
+   return best;
+ }
+ /* Renders the cells of PAGE that intersect (X,Y)-(X+W,Y+H), by calling the
+    'draw_line' and 'draw_cell' functions from the render_params provided to
+    render_page_create(). */
+ void
+ render_page_draw_region (const struct render_page *page,
+                          int ofs[TABLE_N_AXES], int clip[TABLE_N_AXES][2])
+ {
+   int bb[TABLE_N_AXES][2];
+   bb[H][0] = get_clip_min_extent (clip[H][0], page->cp[H], page->n[H] * 2 + 1);
+   bb[H][1] = get_clip_max_extent (clip[H][1], page->cp[H], page->n[H] * 2 + 1);
+   bb[V][0] = get_clip_min_extent (clip[V][0], page->cp[V], page->n[V] * 2 + 1);
+   bb[V][1] = get_clip_max_extent (clip[V][1], page->cp[V], page->n[V] * 2 + 1);
+   render_page_draw_cells (page, ofs, bb);
  }
 -\f
 +
\f
  /* Breaking up tables to fit on a page. */
  
+ /* An iterator for breaking render_pages into smaller chunks. */
+ struct render_break
+   {
+     struct render_page *page;   /* Page being broken up. */
+     enum table_axis axis;       /* Axis along which 'page' is being broken. */
+     int z;                      /* Next cell along 'axis'. */
+     int pixel;                  /* Pixel offset within cell 'z' (usually 0). */
+     int hw;                     /* Width of headers of 'page' along 'axis'. */
+   };
  static int needed_size (const struct render_break *, int cell);
  static bool cell_is_breakable (const struct render_break *, int cell);
  static struct render_page *render_page_select (const struct render_page *,
index bdcc01264f92022536d1df6a7cf4d5fd66e7dc73,c3f852f80a6b811e64c363c78279bea7516b68c8..3e9d8a83adaf04f8c89e830e0d63d51ca555225b
@@@ -78,39 -123,27 +123,26 @@@ struct render_param
  
      /* Width of different kinds of lines. */
      int line_widths[TABLE_N_AXES][RENDER_N_LINES];
-   };
\f
- /* A "page" of content that is ready to be rendered.
-    A page's size is not limited to the size passed in as part of render_params.
-    Use render_break (see below) to break a too-big render_page into smaller
-    render_pages that will fit in the available space. */
- struct render_page *render_page_create (const struct render_params *,
-                                         const struct table *);
  
- struct render_page *render_page_ref (const struct render_page *);
- void render_page_unref (struct render_page *);
+     /* Minimum cell width or height before allowing the cell to be broken
+        across two pages.  (Joined cells may always be broken at join
+        points.) */
+     int min_break[TABLE_N_AXES];
+   };
 -\f
 -\f
 +
- int render_page_get_size (const struct render_page *, enum table_axis);
- void render_page_draw (const struct render_page *);
\f
  /* An iterator for breaking render_pages into smaller chunks. */
- struct render_break
-   {
-     struct render_page *page;   /* Page being broken up. */
-     enum table_axis axis;       /* Axis along which 'page' is being broken. */
-     int cell;                   /* Next cell. */
-     int pixel;                  /* Pixel offset within 'cell' (usually 0). */
-     int hw;                     /* Width of headers of 'page' along 'axis'. */
-   };
+ struct render_pager *render_pager_create (const struct render_params *,
+                                           const struct table_item *);
+ void render_pager_destroy (struct render_pager *);
+ bool render_pager_has_next (const struct render_pager *);
+ int render_pager_draw_next (struct render_pager *, int space);
  
- void render_break_init (struct render_break *, struct render_page *,
-                         enum table_axis);
- void render_break_init_empty (struct render_break *);
- void render_break_destroy (struct render_break *);
+ void render_pager_draw (const struct render_pager *);
+ void render_pager_draw_region (const struct render_pager *,
+                                int x, int y, int w, int h);
  
- bool render_break_has_next (const struct render_break *);
- int render_break_next_size (const struct render_break *);
- struct render_page *render_break_next (struct render_break *, int size);
+ int render_pager_get_size (const struct render_pager *, enum table_axis);
+ int render_pager_get_best_breakpoint (const struct render_pager *, int height);
  
  #endif /* output/render.h */
index b19ea4b3f99748172a59951fe78084fe1d992a94,1b3acc3c5919c5c0823032a2fe385b94da66ca07..e6da60e164e50e4681941ce41c425c12759eb71c
@@@ -55,10 -65,10 +65,10 @@@ EXTRA_DIST += 
  
  if HAVE_GUI
  bin_PROGRAMS += src/ui/gui/psppire 
- noinst_PROGRAMS += src/ui/gui/spreadsheet-test
+ noinst_PROGRAMS += src/ui/gui/spreadsheet-test 
  
 -src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1
 -src_ui_gui_spreadsheet_test_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1
 +src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 
 +src_ui_gui_spreadsheet_test_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1 
  
  
  src_ui_gui_psppire_LDFLAGS = \
Simple merge
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,d6e75f32f5b63f7a31a808085843304c9ab747e3..9800c2ba2d1b1b0ed373ec1cbedfdf343d959d9d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,966 +1,963 @@@
 -expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008-2015 Free Software Foundation.
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "ui/gui/psppire-output-view.h"
+ #include <errno.h>
+ #include <stdbool.h>
+ #include "libpspp/assertion.h"
+ #include "libpspp/string-map.h"
+ #include "output/cairo.h"
+ #include "output/driver-provider.h"
+ #include "output/driver.h"
+ #include "output/chart-item.h"
+ #include "output/message-item.h"
+ #include "output/output-item.h"
+ #include "output/table-item.h"
+ #include "output/text-item.h"
+ #include "gl/c-xvasprintf.h"
+ #include "gl/minmax.h"
+ #include "gl/tmpdir.h"
+ #include "gl/xalloc.h"
+ #include <gettext.h>
+ #define _(msgid) gettext (msgid)
+ struct output_view_item
+   {
+     struct output_item *item;
+     GtkWidget *drawing_area;
+   };
+ struct psppire_output_view
+   {
+     struct xr_driver *xr;
+     int font_height;
+     GtkLayout *output;
+     int render_width;
+     int max_width;
+     int y;
+     struct string_map render_opts;
+     GtkTreeView *overview;
+     GtkTreeIter cur_command;
+     bool in_command;
+     GtkWidget *toplevel;
+     struct output_view_item *items;
+     size_t n_items, allocated_items;
+     /* Variables pertaining to printing */
+     GtkPrintSettings *print_settings;
+     struct xr_driver *print_xrd;
+     int print_item;
+     int print_n_pages;
+     gboolean paginated;
+   };
+ enum
+   {
+     COL_NAME,                   /* Table name. */
+     COL_ADDR,                   /* Pointer to the table */
+     COL_Y,                      /* Y position of top of name. */
+     N_COLS
+   };
+ static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
+ static gboolean
 -  cairo_t *cr = gdk_cairo_create (widget->window);
++draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
+ {
+   struct psppire_output_view *view = data;
+   struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
 -  xr_rendering_draw (r, cr, event->area.x, event->area.y,
 -                     event->area.width, event->area.height);
 -  cairo_destroy (cr);
+   const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view->output));
+   PangoFontDescription *font_desc;
+   char *font_name;
+   gchar *fgc =
+     gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (view->output))]);
+   string_map_replace (&view->render_opts, "foreground-color", fgc);
+   free (fgc);
+   /* Use GTK+ default font as proportional font. */
+   font_name = pango_font_description_to_string (style->font_desc);
+   string_map_replace (&view->render_opts, "prop-font", font_name);
+   g_free (font_name);
+   /* Derived emphasized font from proportional font. */
+   font_desc = pango_font_description_copy (style->font_desc);
+   pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
+   font_name = pango_font_description_to_string (font_desc);
+   string_map_replace (&view->render_opts, "emph-font", font_name);
+   g_free (font_name);
+   pango_font_description_free (font_desc);
+   xr_rendering_apply_options (r, &view->render_opts);
 -  cr = gdk_cairo_create (GTK_WIDGET (view->output)->window);
++  xr_rendering_draw_all (r, cr);
+   return TRUE;
+ }
+ static void
+ free_rendering (gpointer rendering_)
+ {
+   struct xr_rendering *rendering = rendering_;
+   xr_rendering_destroy (rendering);
+ }
+ static void
+ create_xr (struct psppire_output_view *view)
+ {
+   const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view->output));
+   struct text_item *text_item;
+   PangoFontDescription *font_desc;
+   struct xr_rendering *r;
+   char *font_name;
+   int font_width;
+   cairo_t *cr;
+   gchar *fgc;
 -  g_signal_connect (drawing_area, "expose_event",
 -                    G_CALLBACK (expose_event_callback), view);
++  cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET (view->output)));
+   /* Set the widget's text color as the foreground color for the output driver */
+   fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (view->output))]);
+   string_map_insert (&view->render_opts, "foreground-color", fgc);
+   g_free (fgc);
+   /* Use GTK+ default font as proportional font. */
+   font_name = pango_font_description_to_string (style->font_desc);
+   string_map_insert (&view->render_opts, "prop-font", font_name);
+   g_free (font_name);
+   /* Derived emphasized font from proportional font. */
+   font_desc = pango_font_description_copy (style->font_desc);
+   pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
+   font_name = pango_font_description_to_string (font_desc);
+   string_map_insert (&view->render_opts, "emph-font", font_name);
+   g_free (font_name);
+   pango_font_description_free (font_desc);
+   /* Pretend that the "page" has a reasonable width and a very big length,
+      so that most tables can be conveniently viewed on-screen with vertical
+      scrolling only.  (The length should not be increased very much because
+      it is already close enough to INT_MAX when expressed as thousands of a
+      point.) */
+   string_map_insert_nocopy (&view->render_opts, xstrdup ("paper-size"),
+                             xasprintf ("%dx1000000pt", view->render_width));
+   string_map_insert (&view->render_opts, "left-margin", "0");
+   string_map_insert (&view->render_opts, "right-margin", "0");
+   string_map_insert (&view->render_opts, "top-margin", "0");
+   string_map_insert (&view->render_opts, "bottom-margin", "0");
+   view->xr = xr_driver_create (cr, &view->render_opts);
+   text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
+   r = xr_rendering_create (view->xr, text_item_super (text_item), cr);
+   xr_rendering_measure (r, &font_width, &view->font_height);
+   /* xr_rendering_destroy (r); */
+   text_item_unref (text_item);
+   cairo_destroy (cr);
+ }
+ static void
+ create_drawing_area (struct psppire_output_view *view,
+                      GtkWidget *drawing_area, struct xr_rendering *r,
+                      int tw, int th)
+ {
+   g_object_set_data_full (G_OBJECT (drawing_area),
+                           "rendering", r, free_rendering);
+   g_signal_connect (drawing_area, "realize",
+                     G_CALLBACK (on_dwgarea_realize), view);
 -  if (!view->n_items || !GTK_WIDGET (view->output)->window)
++  g_signal_connect (drawing_area, "draw",
++                    G_CALLBACK (draw_callback), view);
+   gtk_widget_set_size_request (drawing_area, tw, th);
+   gtk_layout_put (view->output, drawing_area, 0, view->y);
+   gtk_widget_show (drawing_area);
+ }
+ static void
+ rerender (struct psppire_output_view *view)
+ {
+   struct output_view_item *item;
+   cairo_t *cr;
 -  cr = gdk_cairo_create (GTK_WIDGET (view->output)->window);
++  if (!view->n_items || !gtk_widget_get_window (GTK_WIDGET (view->output)))
+     return;
+   string_map_clear (&view->render_opts);
+   xr_driver_destroy (view->xr);
+   create_xr (view);
 -  if (GTK_WIDGET (view->output)->window)
++  cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET (view->output)));
+   view->y = 0;
+   view->max_width = 0;
+   for (item = view->items; item < &view->items[view->n_items]; item++)
+     {
+       struct xr_rendering *r;
+       int tw, th;
+       if (view->y > 0)
+         view->y += view->font_height / 2;
+       r = xr_rendering_create (view->xr, item->item, cr);
+       if (r == NULL)
+         {
+           g_warn_if_reached ();
+           continue;
+         }
+       xr_rendering_measure (r, &tw, &th);
+       if (!item->drawing_area)
+         {
+           item->drawing_area = gtk_drawing_area_new ();
+           create_drawing_area (view, item->drawing_area, r, tw, th);
+         }
+       else
+         {
+           g_object_set_data_full (G_OBJECT (item->drawing_area),
+                                   "rendering", r, free_rendering);
+           gtk_widget_set_size_request (item->drawing_area, tw, th);
+           gtk_layout_move (view->output, item->drawing_area, 0, view->y);
+         }
+       if (view->max_width < tw)
+         view->max_width = tw;
+       view->y += th;
+     }
+   gtk_layout_set_size (view->output, view->max_width, view->y);
+   cairo_destroy (cr);
+ }
+ void
+ psppire_output_view_put (struct psppire_output_view *view,
+                          const struct output_item *item)
+ {
+   struct output_view_item *view_item;
+   GtkWidget *drawing_area;
+   struct xr_rendering *r;
+   struct string name;
+   GtkTreeStore *store;
+   cairo_t *cr = NULL;
+   GtkTreePath *path;
+   GtkTreeIter iter;
+   int tw, th;
+   if (is_text_item (item))
+     {
+       const struct text_item *text_item = to_text_item (item);
+       enum text_item_type type = text_item_get_type (text_item);
+       const char *text = text_item_get_text (text_item);
+       if (type == TEXT_ITEM_COMMAND_CLOSE)
+         {
+           view->in_command = false;
+           return;
+         }
+       else 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;
 -      cr = gdk_cairo_create (GTK_WIDGET (view->output)->window);
++  if (gtk_widget_get_window (GTK_WIDGET (view->output)))
+     {
+       view_item->drawing_area = drawing_area = gtk_drawing_area_new ();
 -  min = vadj->lower;
 -  max = vadj->upper - vadj->page_size;
++      cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET (view->output)));
+       if (view->xr == NULL)
+         create_xr (view);
+       if (view->y > 0)
+         view->y += view->font_height / 2;
+       r = xr_rendering_create (view->xr, item, cr);
+       if (r == NULL)
+         goto done;
+       xr_rendering_measure (r, &tw, &th);
+       create_drawing_area (view, drawing_area, r, tw, th);
+     }
+   else
+     tw = th = 0;
+   if (view->overview
+       && (!is_text_item (item)
+           || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
+           || !view->in_command))
+     {
+       store = GTK_TREE_STORE (gtk_tree_view_get_model (view->overview));
+       ds_init_empty (&name);
+       if (is_text_item (item)
+           && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
+         {
+           gtk_tree_store_append (store, &iter, NULL);
+           view->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
+           view->in_command = true;
+         }
+       else
+         {
+           GtkTreeIter *p = view->in_command ? &view->cur_command : NULL;
+           gtk_tree_store_append (store, &iter, p);
+         }
+       ds_clear (&name);
+       if (is_text_item (item))
+         ds_put_cstr (&name, text_item_get_text (to_text_item (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 char *title = table_item_get_title (to_table_item (item));
+           if (title != NULL)
+             ds_put_format (&name, "Table: %s", title);
+           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");
+         }
+       gtk_tree_store_set (store, &iter,
+                           COL_NAME, ds_cstr (&name),
+                         COL_ADDR, item,
+                           COL_Y, view->y,
+                           -1);
+       ds_destroy (&name);
+       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;
+   gtk_layout_set_size (view->output, view->max_width, view->y);
+ done:
+   cairo_destroy (cr);
+ }
+ static void
+ on_row_activate (GtkTreeView *overview,
+                  GtkTreePath *path,
+                  GtkTreeViewColumn *column,
+                  struct psppire_output_view *view)
+ {
+   GtkTreeModel *model;
+   GtkTreeIter iter;
+   GtkAdjustment *vadj;
+   GValue value = {0};
+   double y, min, max;
+   model = gtk_tree_view_get_model (overview);
+   if (!gtk_tree_model_get_iter (model, &iter, path))
+     return;
+   gtk_tree_model_get_value (model, &iter, COL_Y, &value);
+   y = g_value_get_long (&value);
+   g_value_unset (&value);
+   vadj = gtk_layout_get_vadjustment (view->output);
 -      gtk_selection_data_set (selection_data, selection_data->target,
++  min = gtk_adjustment_get_lower (vadj);
++  max = gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj);
+   if (y < min)
+     y = min;
+   else if (y > max)
+     y = max;
+   gtk_adjustment_set_value (vadj, y);
+ }
+ static void
+ copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
+ {
+   int i;
+   for (i = 0; i < 5; ++i)
+     {
+       gtk_widget_modify_bg (dest, i, &gtk_widget_get_style (src)->base[i]);
+       gtk_widget_modify_fg (dest, i, &gtk_widget_get_style (src)->text[i]);
+     }
+ }
+ /* Copy the base style from the parent widget to the container and all its
+    children.  We do this because the container's primary purpose is to display
+    text.  This way psppire appears to follow the chosen gnome theme. */
+ static void
+ on_style_set (GtkWidget *toplevel, GtkStyle *prev,
+               struct psppire_output_view *view)
+ {
+   copy_base_to_bg (GTK_WIDGET (view->output), toplevel);
+   gtk_container_foreach (GTK_CONTAINER (view->output),
+                          (GtkCallback) copy_base_to_bg, view->output);
+ }
+ static void
+ on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
+ {
+   copy_base_to_bg (dwg_area, gtk_widget_get_toplevel (dwg_area));
+ }
+ enum {
+   SELECT_FMT_NULL,
+   SELECT_FMT_TEXT,
+   SELECT_FMT_UTF8,
+   SELECT_FMT_HTML,
+   SELECT_FMT_ODT
+ };
+ /* GNU Hurd doesn't have PATH_MAX.  Use a fallback.
+    Temporary directory names are usually not that long.  */
+ #ifndef PATH_MAX
+ # define PATH_MAX 1024
+ #endif
+ static void
+ clipboard_get_cb (GtkClipboard     *clipboard,
+                 GtkSelectionData *selection_data,
+                 guint             info,
+                 gpointer          data)
+ {
+   struct psppire_output_view *view = data;
+   gsize length;
+   gchar *text = NULL;
+   struct output_driver *driver = NULL;
+   char dirname[PATH_MAX], *filename;
+   struct string_map options;
+   GtkTreeSelection *sel = gtk_tree_view_get_selection (view->overview);
+   GtkTreeModel *model = gtk_tree_view_get_model (view->overview);
+   GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
+   GList *n = rows;
+   if ( n == NULL)
+     return;
+   if (path_search (dirname, sizeof dirname, NULL, NULL, true)
+       || mkdtemp (dirname) == NULL)
+     {
+       msg_error (errno, _("failed to create temporary directory during clipboard operation"));
+       return;
+     }
+   filename = xasprintf ("%s/clip.tmp", dirname);
+   string_map_init (&options);
+   string_map_insert (&options, "output-file", filename);
+   switch (info)
+     {
+     case SELECT_FMT_UTF8:
+       string_map_insert (&options, "box", "unicode");
+       /* fall-through */
+     case SELECT_FMT_TEXT:
+       string_map_insert (&options, "format", "txt");
+       break;
+     case SELECT_FMT_HTML:
+       string_map_insert (&options, "format", "html");
+       string_map_insert (&options, "borders", "false");
+       string_map_insert (&options, "css", "false");
+       break;
+     case SELECT_FMT_ODT:
+       string_map_insert (&options, "format", "odt");
+       break;
+     default:
+       g_warning ("unsupported clip target\n");
+       goto finish;
+       break;
+     }
+   driver = output_driver_create (&options);
+   if (driver == NULL)
+     goto finish;
+   while (n)
+     {
+       GtkTreePath *path = n->data ;
+       GtkTreeIter iter;
+       struct output_item *item ;
+       gtk_tree_model_get_iter (model, &iter, path);
+       gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
+       driver->class->submit (driver, item);
+       n = n->next;
+     }
+   if ( driver->class->flush)
+     driver->class->flush (driver);
+   /* Some drivers (eg: the odt one) don't write anything until they
+      are closed */
+   output_driver_destroy (driver);
+   driver = NULL;
+   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);
+     }
+  finish:
+   if (driver != NULL)
+     output_driver_destroy (driver);
+   g_free (text);
+   unlink (filename);
+   free (filename);
+   rmdir (dirname);
+   g_list_free (rows);
+ }
+ static void
+ clipboard_clear_cb (GtkClipboard *clipboard,
+                   gpointer data)
+ {
+ }
+ static const GtkTargetEntry targets[] = {
+   { "STRING",        0, SELECT_FMT_TEXT },
+   { "TEXT",          0, SELECT_FMT_TEXT },
+   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
+   { "text/plain",    0, SELECT_FMT_TEXT },
+   { "UTF8_STRING",   0, SELECT_FMT_UTF8 },
+   { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
+   { "text/html",     0, SELECT_FMT_HTML },
+   { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
+ };
+ static void
+ on_copy (struct psppire_output_view *view)
+ {
+   GtkWidget *widget = GTK_WIDGET (view->overview);
+   GtkClipboard *cb = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
+   if (!gtk_clipboard_set_with_data (cb, targets, G_N_ELEMENTS (targets),
+                                     clipboard_get_cb, clipboard_clear_cb,
+                                     view))
+     clipboard_clear_cb (cb, view);
+ }
+ static void
+ on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
+ {
+   /* The Copy action is available only if there is something selected */
+   gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
+ }
+ static void
+ on_select_all (struct psppire_output_view *view)
+ {
+   GtkTreeSelection *sel = gtk_tree_view_get_selection (view->overview);
+   gtk_tree_view_expand_all (view->overview);
+   gtk_tree_selection_select_all (sel);
+ }
+ static void
+ on_size_allocate (GtkWidget    *widget,
+                   GdkRectangle *allocation,
+                   struct psppire_output_view *view)
+ {
+   int new_render_width = MAX (300, allocation->width);
+   if (view->render_width != new_render_width)
+     {
+       view->render_width = new_render_width;
+       rerender (view);
+     }
+ }
+ struct psppire_output_view *
+ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview,
+                          GtkAction *copy_action, GtkAction *select_all_action)
+ {
+   struct psppire_output_view *view;
+   GtkTreeViewColumn *column;
+   GtkCellRenderer *renderer;
+   GtkTreeSelection *sel;
+   GtkTreeModel *model;
+   view = xmalloc (sizeof *view);
+   view->xr = NULL;
+   view->font_height = 0;
+   view->output = output;
+   view->render_width = 0;
+   view->max_width = 0;
+   view->y = 0;
+   string_map_init (&view->render_opts);
+   view->overview = overview;
+   memset (&view->cur_command, 0, sizeof view->cur_command);
+   view->in_command = false;
+   view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output));
+   view->items = NULL;
+   view->n_items = view->allocated_items = 0;
+   view->print_settings = NULL;
+   view->print_xrd = NULL;
+   view->print_item = 0;
+   view->print_n_pages = 0;
+   view->paginated = FALSE;
+   g_signal_connect (view->toplevel, "style-set", G_CALLBACK (on_style_set), view);
+   g_signal_connect (output, "size-allocate", G_CALLBACK (on_size_allocate), view);
+   if (overview)
+     {
+       model = GTK_TREE_MODEL (gtk_tree_store_new (
+                                 N_COLS,
+                                 G_TYPE_STRING,  /* COL_NAME */
+                                 G_TYPE_POINTER, /* COL_ADDR */
+                                 G_TYPE_LONG));  /* COL_Y */
+       gtk_tree_view_set_model (overview, model);
+       g_object_unref (model);
+       sel = gtk_tree_view_get_selection (overview);
+       gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
+       g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change),
+                         copy_action);
+       column = gtk_tree_view_column_new ();
+       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);
+       g_signal_connect (GTK_TREE_VIEW (overview),
+                         "row-activated", G_CALLBACK (on_row_activate), view);
+       gtk_action_set_sensitive (copy_action, FALSE);
+       g_signal_connect_swapped (copy_action, "activate",
+                                 G_CALLBACK (on_copy), view);
+       g_signal_connect_swapped (select_all_action, "activate",
+                                 G_CALLBACK (on_select_all), view);
+     }
+   return view;
+ }
+ void
+ psppire_output_view_destroy (struct psppire_output_view *view)
+ {
+   size_t i;
+   if (!view)
+     return;
+   g_signal_handlers_disconnect_by_func (view->toplevel,
+                                         G_CALLBACK (on_style_set), view);
+   string_map_destroy (&view->render_opts);
+   for (i = 0; i < view->n_items; i++)
+     output_item_unref (view->items[i].item);
+   free (view->items);
+   view->items = NULL;
+   view->n_items = view->allocated_items = 0;
+   if (view->print_settings != NULL)
+     g_object_unref (view->print_settings);
+   xr_driver_destroy (view->xr);
+   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;
+ }
\f
+ /* Export. */
+ void
+ psppire_output_view_export (struct psppire_output_view *view,
+                             struct string_map *options)
+ {
+   struct output_driver *driver;
+   driver = output_driver_create (options);
+   if (driver)
+     {
+       size_t i;
+       for (i = 0; i < view->n_items; i++)
+         driver->class->submit (driver, view->items[i].item);
+       output_driver_destroy (driver);
+     }
+ }
\f
+ /* Print. */
+ 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;
+ }
+ 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);
+   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);
+ }
+ static gboolean
+ paginate (GtkPrintOperation *operation,
+         GtkPrintContext   *context,
+         struct psppire_output_view *view)
+ {
+   if (view->paginated)
+     {
+       /* Sometimes GTK+ emits this signal again even after pagination is
+          complete.  Don't let that screw up printing. */
+       return TRUE;
+     }
+   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_driver_next_page (view->print_xrd, 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);
+       /* Re-create the driver to do the real printing. */
+       xr_driver_destroy (view->print_xrd);
+       create_xr_print_driver (context, view);
+       view->print_item = 0;
+       view->paginated = TRUE;
+       return TRUE;
+     }
+ }
+ static void
+ begin_print (GtkPrintOperation *operation,
+            GtkPrintContext   *context,
+            struct psppire_output_view *view)
+ {
+   create_xr_print_driver (context, view);
+   view->print_item = 0;
+   view->print_n_pages = 1;
+   view->paginated = FALSE;
+ }
+ static void
+ end_print (GtkPrintOperation *operation,
+          GtkPrintContext   *context,
+          struct psppire_output_view *view)
+ {
+   xr_driver_destroy (view->print_xrd);
+ }
+ static void
+ draw_page (GtkPrintOperation *operation,
+          GtkPrintContext   *context,
+          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)
+          && view->print_item < view->n_items)
+     xr_driver_output_item (view->print_xrd, view->items [view->print_item++].item);
+ }
+ void
+ psppire_output_view_print (struct psppire_output_view *view,
+                            GtkWindow *parent_window)
+ {
+   GtkPrintOperationResult res;
+   GtkPrintOperation *print = gtk_print_operation_new ();
+   if (view->print_settings != NULL) 
+     gtk_print_operation_set_print_settings (print, view->print_settings);
+   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);
+   g_signal_connect (print, "draw_page",   G_CALLBACK (draw_page),   view);
+   res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+                                  parent_window, NULL);
+   if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+     {
+       if (view->print_settings != NULL)
+         g_object_unref (view->print_settings);
+       view->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
+     }
+   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);
+ }
Simple merge
index 2402b341c117d0c831c47755aef8f97a0c6ebb47,ca047560696b40ad9d5fc77b8086ac5b72c695f2..efe6a4003207a92df4ad925a57047168b21cb22c
@@@ -369,27 -369,7 +369,6 @@@ on_edit_copy (PsppireSyntaxWindow *sw
    set_clip (sw, &begin, &end);
  }
  
- /* A callback for when the clipboard contents have been received */
- static void
- contents_received_callback (GtkClipboard *clipboard,
-                           GtkSelectionData *sd,
-                           gpointer data)
- {
-   PsppireSyntaxWindow *syntax_window = data;
-   if ( gtk_selection_data_get_length (sd) < 0 )
-     return;
-   if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
-     return;
-   gtk_text_buffer_insert_at_cursor (GTK_TEXT_BUFFER (syntax_window->buffer),
-                                   (gchar *) gtk_selection_data_get_data (sd),
-                                   gtk_selection_data_get_length (sd));
- }
--
  static void
  on_edit_paste (PsppireSyntaxWindow *sw)
  {
Simple merge
Simple merge
Simple merge
Simple merge