+static struct psppire_output_driver *
+psppire_output_cast (struct output_driver *driver)
+{
+ assert (driver->class == &psppire_output_class);
+ return UP_CAST (driver, struct psppire_output_driver, driver);
+}
+
+static gboolean
+expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (widget->window);
+ xr_rendering_draw (r, cr, event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static void
+psppire_output_submit (struct output_driver *this,
+ const struct output_item *item)
+{
+ struct psppire_output_driver *pod = psppire_output_cast (this);
+ PsppireOutputWindow *viewer;
+ GtkWidget *drawing_area;
+ struct xr_rendering *r;
+ struct string title;
+ GtkTreeStore *store;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ cairo_t *cr;
+ int tw, th;
+
+ if (pod->viewer == NULL)
+ {
+ pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+ gtk_widget_show_all (GTK_WIDGET (pod->viewer));
+ pod->viewer->driver = pod;
+ }
+ viewer = pod->viewer;
+
+ if (viewer->n_items >= viewer->allocated_items)
+ viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
+ sizeof *viewer->items);
+ viewer->items[viewer->n_items++] = output_item_ref (item);
+
+ 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)
+ {
+ viewer->in_command = false;
+ return;
+ }
+ else if (text[0] == '\0')
+ return;
+ }
+
+ cr = gdk_cairo_create (GTK_WIDGET (pod->viewer)->window);
+ if (pod->xr == NULL)
+ {
+ const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
+ struct string_map options = STRING_MAP_INITIALIZER (options);
+ PangoFontDescription *font_desc;
+ char *font_name;
+
+ /* Use GTK+ default font as proportional font. */
+ font_name = pango_font_description_to_string (style->font_desc);
+ string_map_insert (&options, "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 (&options, "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 (&options, "paper-size", "300x200000mm");
+ string_map_insert (&options, "left-margin", "0");
+ string_map_insert (&options, "right-margin", "0");
+ string_map_insert (&options, "top-margin", "0");
+ string_map_insert (&options, "bottom-margin", "0");
+
+ pod->xr = xr_driver_create (cr, &options);
+
+ string_map_destroy (&options);
+ }
+
+ r = xr_rendering_create (pod->xr, item, cr);
+ if (r == NULL)
+ goto done;
+
+ xr_rendering_measure (r, &tw, &th);
+
+ drawing_area = gtk_drawing_area_new ();
+ gtk_widget_modify_bg (
+ GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
+ >k_widget_get_style (drawing_area)->base[GTK_STATE_NORMAL]);
+ g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
+ gtk_widget_set_size_request (drawing_area, tw, th);
+ gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
+ gtk_widget_show (drawing_area);
+ g_signal_connect (G_OBJECT (drawing_area), "expose_event",
+ G_CALLBACK (expose_event_callback), NULL);
+
+ if (!is_text_item (item)
+ || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
+ || !viewer->in_command)
+ {
+ store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
+
+ ds_init_empty (&title);
+ if (is_text_item (item)
+ && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
+ {
+ gtk_tree_store_append (store, &iter, NULL);
+ viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
+ viewer->in_command = true;
+ }
+ else
+ {
+ GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
+ gtk_tree_store_append (store, &iter, p);
+ }
+
+ ds_clear (&title);
+ if (is_text_item (item))
+ ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
+ else if (is_table_item (item))
+ {
+ const char *caption = table_item_get_caption (to_table_item (item));
+ if (caption != NULL)
+ ds_put_format (&title, "Table: %s", caption);
+ else
+ ds_put_cstr (&title, "Table");
+ }
+ else if (is_chart_item (item))
+ {
+ const char *s = chart_item_get_title (to_chart_item (item));
+ if (s != NULL)
+ ds_put_format (&title, "Chart: %s", s);
+ else
+ ds_put_cstr (&title, "Chart");
+ }
+ gtk_tree_store_set (store, &iter,
+ COL_TITLE, ds_cstr (&title),
+ COL_ADDR, item,
+ COL_Y, viewer->y,
+ -1);
+ ds_destroy (&title);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+ gtk_tree_view_expand_row (viewer->overview, path, TRUE);
+ gtk_tree_path_free (path);
+ }