1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008-2015, 2016, 2023 Free Software Foundation.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/psppire-output-view.h"
21 #include <cairo/cairo-svg.h>
25 #include "libpspp/assertion.h"
26 #include "libpspp/string-map.h"
27 #include "output/cairo-fsm.h"
28 #include "output/cairo-pager.h"
29 #include "output/driver-provider.h"
30 #include "output/driver.h"
31 #include "output/output-item.h"
32 #include "output/pivot-table.h"
34 #include "gl/c-xvasprintf.h"
35 #include "gl/minmax.h"
36 #include "gl/clean-temp.h"
37 #include "gl/xalloc.h"
40 #define _(msgid) gettext (msgid)
42 struct output_view_item
44 struct output_item
*item
;
45 GtkWidget
*drawing_area
;
50 struct psppire_output_view
52 struct xr_fsm_style
*style
;
60 GtkTreeView
*overview
;
62 guint buttontime
; /* Time of the button event */
64 struct output_view_item
*items
;
65 size_t n_items
, allocated_items
;
66 struct output_view_item
*selected_item
;
68 /* Variables pertaining to printing */
69 GtkPrintSettings
*print_settings
;
71 struct xr_fsm_style
*fsm_style
;
72 struct xr_page_style
*page_style
;
73 struct xr_pager
*pager
;
81 COL_LABEL
, /* Output item label. */
82 COL_ADDR
, /* Pointer to the table */
83 COL_Y
, /* Y position of top of object. */
87 static GtkTargetList
*build_target_list (const struct output_item
*item
);
88 static void clipboard_get_cb (GtkClipboard
*clipboard
,
89 GtkSelectionData
*selection_data
,
93 /* Draws a white background on the GtkLayout to match the white background of
94 each of the output items. */
96 layout_draw_callback (GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
98 int width
= gtk_widget_get_allocated_width (widget
);
99 int height
= gtk_widget_get_allocated_height (widget
);
100 GtkStyleContext
*context
= gtk_widget_get_style_context (widget
);
101 gtk_render_background (context
, cr
, 0, 0, width
, height
);
102 return FALSE
; /* Continue drawing the GtkDrawingAreas. */
106 draw_callback (GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
109 if (!gdk_cairo_get_clip_rectangle (cr
, &clip
))
112 struct xr_fsm
*fsm
= g_object_get_data (G_OBJECT (widget
), "fsm");
114 /* Draw the background based on the state of the widget
115 which can be selected or not selected */
116 GtkStyleContext
*context
= gtk_widget_get_style_context (widget
);
117 gtk_render_background (context
, cr
, clip
.x
, clip
.y
,
118 clip
.x
+ clip
.width
, clip
.y
+ clip
.height
);
119 /* Select the default foreground color based on current style
120 and state of the widget */
121 GtkStateFlags state
= gtk_widget_get_state_flags (widget
);
123 gtk_style_context_get_color (context
, state
, &color
);
124 cairo_set_source_rgba (cr
, color
.red
, color
.green
, color
.blue
, color
.alpha
);
125 xr_fsm_draw_region (fsm
, cr
, clip
.x
, clip
.y
, clip
.width
, clip
.height
);
131 free_fsm (gpointer fsm_
)
133 struct xr_fsm
*fsm
= fsm_
;
134 xr_fsm_destroy (fsm
);
137 static struct xr_fsm_style
*
138 get_xr_fsm_style (struct psppire_output_view
*view
)
140 GtkStyleContext
*context
141 = gtk_widget_get_style_context (GTK_WIDGET (view
->output
));
142 GtkStateFlags state
= gtk_widget_get_state_flags (GTK_WIDGET (view
->output
));
144 int xr_width
= view
->render_width
* 1000;
146 PangoFontDescription
*pf
;
147 gtk_style_context_get (context
, state
, "font", &pf
, NULL
);
149 struct xr_fsm_style
*style
= xmalloc (sizeof *style
);
150 *style
= (struct xr_fsm_style
) {
152 .size
= { [TABLE_HORZ
] = xr_width
, [TABLE_VERT
] = INT_MAX
},
153 .min_break
= { [TABLE_HORZ
] = xr_width
/ 2, [TABLE_VERT
] = 0 },
155 .use_system_colors
= true,
156 .object_spacing
= XR_POINT
* 12,
157 .font_resolution
= 96.0,
163 /* Return the horizontal position to place a widget whose
164 width is CHILD_WIDTH */
166 get_xpos (const struct psppire_output_view
*view
, gint child_width
)
168 GdkWindow
*gdkw
= gtk_widget_get_window (GTK_WIDGET (view
->output
));
169 guint w
= gdk_window_get_width (gdkw
);
171 g_object_get (view
->output
, "border-width", &gutter
, NULL
);
172 return (gtk_widget_get_direction (GTK_WIDGET (view
->output
)) == GTK_TEXT_DIR_RTL
) ? w
- child_width
- gutter
: gutter
;
175 static struct output_view_item
*
176 find_selected_item (struct psppire_output_view
*view
)
178 struct output_view_item
*item
= NULL
;
181 if (view
->items
== NULL
)
184 for (item
= view
->items
; item
< &view
->items
[view
->n_items
]; item
++)
186 GtkWidget
*widget
= GTK_WIDGET (item
->drawing_area
);
187 if (GTK_IS_WIDGET (widget
))
189 GtkStateFlags state
= gtk_widget_get_state_flags (widget
);
190 if (state
& GTK_STATE_FLAG_SELECTED
)
199 set_copy_action (struct psppire_output_view
*view
,
202 GtkWidget
*toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (view
->output
));
203 GAction
*copy_action
= g_action_map_lookup_action (G_ACTION_MAP (toplevel
),
205 g_object_set (copy_action
,
211 clear_selection (struct psppire_output_view
*view
)
215 struct output_view_item
*item
= find_selected_item (view
);
218 set_copy_action (view
, FALSE
);
219 GtkWidget
*widget
= GTK_WIDGET (item
->drawing_area
);
220 if (GTK_IS_WIDGET (widget
))
222 gtk_widget_unset_state_flags (widget
, GTK_STATE_FLAG_SELECTED
);
223 gtk_widget_queue_draw (widget
);
228 off_item_button_press_event_cb (GtkWidget
*widget
,
229 GdkEventButton
*event
,
230 struct psppire_output_view
*view
)
232 /* buttontime is set by button_press_event_cb
233 If our event->time is equal to the time from the
234 button_press_event_cb, then we handle the same event.
235 In that case we must not clear the selection because
236 it was just set by button_press_event_cb from the item */
237 if (event
->time
!= view
->buttontime
)
238 clear_selection (view
);
239 return FALSE
; /* Forward the event -> DragNDrop */
243 button_press_event_cb (GtkWidget
*widget
,
244 GdkEventButton
*event
,
245 struct psppire_output_view
*view
)
247 view
->buttontime
= event
->time
;
248 clear_selection (view
);
249 set_copy_action (view
, TRUE
);
250 gtk_widget_set_state_flags (widget
, GTK_STATE_FLAG_SELECTED
, FALSE
);
251 gtk_widget_queue_draw (widget
);
252 return FALSE
; /* Forward Event -> off_item will trigger */
256 drag_data_get_cb (GtkWidget
*widget
, GdkDragContext
*context
,
257 GtkSelectionData
*selection_data
,
258 guint target_type
, guint time
,
259 struct psppire_output_view
*view
)
261 view
->selected_item
= find_selected_item (view
);
262 clipboard_get_cb (NULL
, selection_data
, target_type
, view
);
266 create_drawing_area (struct psppire_output_view
*view
,
267 GtkWidget
*drawing_area
, struct xr_fsm
*r
,
268 int tw
, int th
, const struct output_item
*item
)
270 g_object_set_data_full (G_OBJECT (drawing_area
),
272 g_signal_connect (drawing_area
, "button-press-event",
273 G_CALLBACK (button_press_event_cb
), view
);
274 gtk_widget_add_events (drawing_area
, GDK_BUTTON_PRESS_MASK
);
276 { /* Drag and Drop */
277 GtkTargetList
*tl
= build_target_list (item
);
279 gtk_drag_source_set (drawing_area
, GDK_BUTTON1_MASK
, NULL
, 0, GDK_ACTION_COPY
);
280 gtk_drag_source_set_target_list (drawing_area
, tl
);
281 gtk_target_list_unref (tl
);
282 g_signal_connect (drawing_area
, "drag-data-get",
283 G_CALLBACK (drag_data_get_cb
), view
);
285 GtkStyleContext
*context
= gtk_widget_get_style_context (drawing_area
);
286 gtk_style_context_add_class (context
,
287 GTK_STYLE_CLASS_VIEW
);
288 g_signal_connect (drawing_area
, "draw",
289 G_CALLBACK (draw_callback
), view
);
291 gtk_widget_set_size_request (drawing_area
, tw
, th
);
292 gint xpos
= get_xpos (view
, tw
);
294 gtk_layout_put (view
->output
, drawing_area
, xpos
, view
->y
);
296 gtk_widget_show (drawing_area
);
300 rerender (struct psppire_output_view
*view
)
302 struct output_view_item
*item
;
303 GdkWindow
*gdkw
= gtk_widget_get_window (GTK_WIDGET (view
->output
));
305 if (!view
->n_items
|| ! gdkw
)
309 view
->style
= get_xr_fsm_style (view
);
311 GdkWindow
*win
= gtk_layout_get_bin_window (view
->output
);
312 cairo_region_t
*region
= gdk_window_get_visible_region (win
);
313 GdkDrawingContext
*ctx
= gdk_window_begin_draw_frame (win
, region
);
314 cairo_t
*cr
= gdk_drawing_context_get_cairo_context (ctx
);
318 for (item
= view
->items
; item
< &view
->items
[view
->n_items
]; item
++)
325 view
->y
+= view
->object_spacing
;
327 if (item
->item
->type
== OUTPUT_ITEM_GROUP
)
330 r
= xr_fsm_create_for_scrolling (item
->item
, view
->style
, cr
);
333 g_warn_if_reached ();
337 xr_fsm_measure (r
, cr
, &tw
, &th
);
339 gint xpos
= get_xpos (view
, tw
);
341 if (!item
->drawing_area
)
343 item
->drawing_area
= gtk_drawing_area_new ();
344 create_drawing_area (view
, item
->drawing_area
, r
, tw
, th
, item
->item
);
348 g_object_set_data_full (G_OBJECT (item
->drawing_area
),
350 gtk_widget_set_size_request (item
->drawing_area
, tw
, th
);
351 gtk_layout_move (view
->output
, item
->drawing_area
, xpos
, view
->y
);
354 if (item
->item
->type
== OUTPUT_ITEM_TABLE
)
355 gtk_widget_set_tooltip_text (item
->drawing_area
,
356 item
->item
->table
->notes
);
361 /* This code probably doesn't bring us anthing, but Gtk
362 shows warnings if get_preferred_width/height is not
363 called before the size_allocate below is called. */
364 gtk_widget_get_preferred_width (item
->drawing_area
, &minw
, NULL
);
365 gtk_widget_get_preferred_height (item
->drawing_area
, &minh
, NULL
);
366 if (th
> minh
) th
= minh
;
367 if (tw
> minw
) tw
= minw
;
374 gtk_widget_size_allocate (item
->drawing_area
, &alloc
);
376 if (view
->max_width
< tw
)
377 view
->max_width
= tw
;
381 gtk_layout_set_size (view
->output
,
382 view
->max_width
+ view
->object_spacing
,
383 view
->y
+ view
->object_spacing
);
385 gdk_window_end_draw_frame (win
, ctx
);
386 cairo_region_destroy (region
);
390 init_output_view_item (struct output_view_item
*view_item
,
391 struct psppire_output_view
*view
,
392 const struct output_item
*item
,
395 *view_item
= (struct output_view_item
) {
396 .item
= output_item_ref (item
),
397 .nesting_depth
= nesting_depth
400 GdkWindow
*win
= gtk_widget_get_window (GTK_WIDGET (view
->output
));
401 if (win
&& item
->type
!= OUTPUT_ITEM_GROUP
)
404 view
->style
= get_xr_fsm_style (view
);
406 cairo_region_t
*region
= gdk_window_get_visible_region (win
);
407 GdkDrawingContext
*ctx
= gdk_window_begin_draw_frame (win
, region
);
408 cairo_t
*cr
= gdk_drawing_context_get_cairo_context (ctx
);
411 view
->y
+= view
->object_spacing
;
413 struct xr_fsm
*r
= xr_fsm_create_for_scrolling (item
, view
->style
, cr
);
416 gdk_window_end_draw_frame (win
, ctx
);
417 cairo_region_destroy (region
);
419 output_item_unref (view_item
->item
);
423 xr_fsm_measure (r
, cr
, &view_item
->width
, &view_item
->height
);
424 view_item
->drawing_area
= gtk_drawing_area_new ();
425 create_drawing_area (view
, view_item
->drawing_area
, r
, view_item
->width
,
426 view_item
->height
, item
);
427 gdk_window_end_draw_frame (win
, ctx
);
428 cairo_region_destroy (region
);
435 psppire_output_view_put__ (struct psppire_output_view
*view
,
436 const struct output_item
*item
,
437 GtkTreePath
*parent_path
)
439 if (item
->type
== OUTPUT_ITEM_TEXT
)
441 char *text
= text_item_get_plain_text (item
);
442 bool text_is_empty
= text
[0] == '\0';
448 if (view
->n_items
>= view
->allocated_items
)
449 view
->items
= x2nrealloc (view
->items
, &view
->allocated_items
,
450 sizeof *view
->items
);
451 struct output_view_item
*view_item
= &view
->items
[view
->n_items
];
452 if (!init_output_view_item (view_item
, view
, item
,
453 parent_path
? gtk_tree_path_get_depth (parent_path
) : 0))
457 GtkTreePath
*path
= NULL
;
460 GtkTreeStore
*store
= GTK_TREE_STORE (
461 gtk_tree_view_get_model (view
->overview
));
463 /* Create a new node in the tree and puts a reference to it in 'iter'. */
467 && gtk_tree_path_get_depth (parent_path
) > 0
468 && gtk_tree_model_get_iter (GTK_TREE_MODEL (store
),
469 &parent
, parent_path
))
470 gtk_tree_store_append (store
, &iter
, &parent
);
472 gtk_tree_store_append (store
, &iter
, NULL
);
474 gtk_tree_store_set (store
, &iter
,
475 COL_LABEL
, output_item_get_label (item
),
480 /* Get the path of the new row. */
481 path
= gtk_tree_model_get_path (
482 GTK_TREE_MODEL (store
), &iter
);
483 gtk_tree_view_expand_row (view
->overview
, path
, TRUE
);
486 if (view
->max_width
< view_item
->width
)
487 view
->max_width
= view_item
->width
;
488 view
->y
+= view_item
->height
;
490 gtk_layout_set_size (view
->output
, view
->max_width
, view
->y
);
492 if (item
->type
== OUTPUT_ITEM_GROUP
)
493 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
494 psppire_output_view_put__ (view
, item
->group
.children
[i
], path
);
496 gtk_tree_path_free (path
);
500 psppire_output_view_put (struct psppire_output_view
*view
,
501 const struct output_item
*item
)
503 psppire_output_view_put__ (view
, item
, NULL
);
507 on_row_activate (GtkTreeView
*overview
,
509 GtkTreeViewColumn
*column
,
510 struct psppire_output_view
*view
)
518 model
= gtk_tree_view_get_model (overview
);
519 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
522 gtk_tree_model_get_value (model
, &iter
, COL_Y
, &value
);
523 y
= g_value_get_long (&value
);
524 g_value_unset (&value
);
526 vadj
= gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view
->output
));
527 min
= gtk_adjustment_get_lower (vadj
);
528 max
= gtk_adjustment_get_upper (vadj
) - gtk_adjustment_get_page_size (vadj
);
533 gtk_adjustment_set_value (vadj
, y
);
537 on_style_updated (GtkWidget
*toplevel
, struct psppire_output_view
*view
)
539 if (!view
->n_items
|| !gtk_widget_get_window (GTK_WIDGET (view
->output
)))
542 /* GTK+ fires this signal for trivial changes like the mouse moving in or out
543 of the window. Check whether the actual fsm options changed and
544 re-render only if they did. */
545 struct xr_fsm_style
*style
= get_xr_fsm_style (view
);
546 if (!view
->style
|| !xr_fsm_style_equals (style
, view
->style
))
548 xr_fsm_style_unref (view
->style
);
549 view
->style
= xr_fsm_style_ref (style
);
552 xr_fsm_style_unref (style
);
566 clear_rectangle (cairo_surface_t
*surface
,
567 double x0
, double y0
, double x1
, double y1
)
569 cairo_t
*cr
= cairo_create (surface
);
570 cairo_set_source_rgb (cr
, 1, 1, 1);
572 cairo_rectangle (cr
, x0
, y0
, x1
- x0
, y1
- y0
);
578 clipboard_get_cb (GtkClipboard
*clipboard
,
579 GtkSelectionData
*selection_data
,
583 struct psppire_output_view
*view
= data
;
587 struct output_driver
*driver
= NULL
;
589 struct string_map options
;
590 struct temp_dir
*td
= NULL
;
592 if (view
->selected_item
== NULL
)
595 g_message ("clipboard paste of item type %d", info
);
596 td
= create_temp_dir ("pspp", NULL
, false);
599 msg_error (errno
, _("failed to create temporary directory during clipboard operation"));
602 filename
= xasprintf ("%s/clip.tmp", td
->dir_name
);
604 string_map_init (&options
);
605 string_map_insert (&options
, "output-file", filename
);
609 case SELECT_FMT_UTF8
:
610 string_map_insert (&options
, "box", "unicode");
613 case SELECT_FMT_TEXT
:
614 string_map_insert (&options
, "format", "txt");
615 string_map_insert (&options
, "width", "1000");
618 case SELECT_FMT_HTML
:
619 string_map_insert (&options
, "format", "html");
620 string_map_insert (&options
, "borders", "false");
621 string_map_insert (&options
, "css", "false");
630 string_map_insert (&options
, "format", "odt");
634 g_warning ("unsupported clip target\n");
639 if ((info
== SELECT_FMT_IMG
) ||
640 (info
== SELECT_FMT_SVG
) )
642 GtkWidget
*widget
= view
->selected_item
->drawing_area
;
643 struct xr_fsm
*fsm
= g_object_get_data (G_OBJECT (widget
), "fsm");
645 GdkWindow
*win
= gtk_layout_get_bin_window (view
->output
);
646 cairo_region_t
*region
= gdk_window_get_visible_region (win
);
647 GdkDrawingContext
*ctx
= gdk_window_begin_draw_frame (win
, region
);
648 cairo_t
*cr
= gdk_drawing_context_get_cairo_context (ctx
);
651 xr_fsm_measure (fsm
, cr
, &w
, &h
);
653 gdk_window_end_draw_frame (win
, ctx
);
654 cairo_region_destroy (region
);
656 cairo_surface_t
*surface
657 = (info
== SELECT_FMT_SVG
658 ? cairo_svg_surface_create (filename
, w
, h
)
659 : cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, w
, h
));
660 clear_rectangle (surface
, 0, 0, w
, h
);
661 cairo_t
*cr2
= cairo_create (surface
);
662 xr_fsm_draw_all (fsm
, cr2
);
664 if (info
== SELECT_FMT_IMG
)
666 GdkPixbuf
*pixbuf
= gdk_pixbuf_get_from_surface (surface
,
670 gtk_selection_data_set_pixbuf (selection_data
, pixbuf
);
671 g_object_unref (pixbuf
);
674 cairo_surface_destroy (surface
);
678 driver
= output_driver_create (&options
);
682 driver
->class->submit (driver
, view
->selected_item
->item
);
684 if (driver
->class->flush
)
685 driver
->class->flush (driver
);
687 /* Some drivers (eg: the odt one) don't write anything until they
689 output_driver_destroy (driver
);
693 if (info
!= SELECT_FMT_IMG
694 && g_file_get_contents (filename
, &text
, &length
, NULL
))
695 gtk_selection_data_set (selection_data
,
696 gtk_selection_data_get_target (selection_data
),
697 8, (const guchar
*) text
, length
);
702 output_driver_destroy (driver
);
708 cleanup_temp_dir (td
);
712 clipboard_clear_cb (GtkClipboard
*clipboard
,
717 static gchar cbstringtargetname
[] = "STRING";
718 static gchar cbtexttargetname
[] = "TEXT";
719 static gchar cbctexttargetname
[] = "COMPOUND_TEXT";
720 static gchar cbtextmimetargetname
[] = "text/plain";
721 static gchar cbutf8targetname
[] = "UTF8_STRING";
722 static gchar cbutf8mimetargetname
[] = "text/plain;charset=utf-8";
723 static const GtkTargetEntry cbtexttargets
[] = {
724 {cbstringtargetname
, 0, SELECT_FMT_TEXT
},
725 {cbtexttargetname
, 0, SELECT_FMT_TEXT
},
726 {cbctexttargetname
, 0, SELECT_FMT_TEXT
},
727 {cbtextmimetargetname
, 0, SELECT_FMT_TEXT
},
728 {cbutf8targetname
, 0, SELECT_FMT_UTF8
},
729 {cbutf8mimetargetname
, 0, SELECT_FMT_UTF8
}
732 static gchar cbhtmltargetname
[] = "text/html";
733 static const GtkTargetEntry cbhtmltargets
[] = {
734 {cbhtmltargetname
, 0, SELECT_FMT_HTML
}
737 static gchar cbsvgmimetargetname
[] = "image/svg+xml";
738 static const GtkTargetEntry cbsvgtargets
[] = {
739 {cbsvgmimetargetname
, 0, SELECT_FMT_SVG
}
742 static gchar cbodttargetname
[] = "application/vnd.oasis.opendocument.text";
743 static const GtkTargetEntry cbodttargets
[] = {
744 {cbodttargetname
, 0, SELECT_FMT_ODT
}
747 static GtkTargetList
*
748 build_target_list (const struct output_item
*item
)
750 GtkTargetList
*tl
= gtk_target_list_new (NULL
, 0);
751 g_return_val_if_fail (tl
, NULL
);
754 case OUTPUT_ITEM_CHART
:
755 case OUTPUT_ITEM_IMAGE
:
756 gtk_target_list_add_table(tl
, cbsvgtargets
, G_N_ELEMENTS(cbsvgtargets
));
757 gtk_target_list_add_image_targets (tl
, SELECT_FMT_IMG
, TRUE
);
759 case OUTPUT_ITEM_TABLE
:
760 /* Some applications at least on MacOS paste differently based on the
761 the position of the targets in the target list.
762 gtk_target_list_add_table prepends to the list. At least on MacOS
763 this results in html having higher priority over text when
764 html is added later ending up before text in the list. */
765 gtk_target_list_add_table(tl
, cbodttargets
, G_N_ELEMENTS(cbodttargets
));
766 gtk_target_list_add_table(tl
, cbtexttargets
, G_N_ELEMENTS(cbtexttargets
));
767 gtk_target_list_add_table(tl
, cbhtmltargets
, G_N_ELEMENTS(cbhtmltargets
));
769 case OUTPUT_ITEM_MESSAGE
:
770 case OUTPUT_ITEM_TEXT
:
771 gtk_target_list_add_table(tl
, cbhtmltargets
, G_N_ELEMENTS(cbhtmltargets
));
772 gtk_target_list_add_table(tl
, cbtexttargets
, G_N_ELEMENTS(cbtexttargets
));
773 gtk_target_list_add_table(tl
, cbodttargets
, G_N_ELEMENTS(cbodttargets
));
775 case OUTPUT_ITEM_GROUP
:
776 case OUTPUT_ITEM_PAGE_BREAK
:
779 g_critical ("Unknown output item type: %d", item
->type
);
786 on_copy (struct psppire_output_view
*view
)
788 GtkWidget
*widget
= GTK_WIDGET (view
->overview
);
789 GtkClipboard
*cb
= gtk_widget_get_clipboard (widget
, GDK_SELECTION_CLIPBOARD
);
791 struct output_view_item
*ov_item
= find_selected_item (view
);
794 view
->selected_item
= ov_item
;
795 GtkTargetList
*tl
= build_target_list (ov_item
->item
);
796 g_return_if_fail (tl
);
797 gint no_of_targets
= 0;
798 GtkTargetEntry
*ta
= gtk_target_table_new_from_list (tl
, &no_of_targets
);
799 g_return_if_fail (ta
);
800 if (!gtk_clipboard_set_with_data (cb
, ta
, no_of_targets
,
801 clipboard_get_cb
, clipboard_clear_cb
,
803 clipboard_clear_cb (cb
, view
);
805 gtk_target_list_unref (tl
);
806 gtk_target_table_free (ta
,no_of_targets
);
810 on_size_allocate (GtkWidget
*widget
,
811 GdkRectangle
*allocation
,
812 struct psppire_output_view
*view
)
814 view
->render_width
= MAX (300, allocation
->width
);
819 on_realize (GtkWidget
*overview
, GObject
*view
)
821 GtkWidget
*toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (overview
));
823 GAction
*copy_action
= g_action_map_lookup_action (G_ACTION_MAP (toplevel
),
826 GAction
*select_all_action
= g_action_map_lookup_action (G_ACTION_MAP (toplevel
),
829 g_object_set (copy_action
, "enabled", FALSE
, NULL
);
830 g_object_set (select_all_action
, "enabled", FALSE
, NULL
);
832 g_signal_connect_swapped (copy_action
, "activate",
833 G_CALLBACK (on_copy
), view
);
837 struct psppire_output_view
*
838 psppire_output_view_new (GtkLayout
*output
, GtkTreeView
*overview
)
840 struct psppire_output_view
*view
;
842 view
= xmalloc (sizeof *view
);
843 *view
= (struct psppire_output_view
) {
844 .object_spacing
= 10,
846 .overview
= overview
,
849 g_signal_connect (output
, "draw", G_CALLBACK (layout_draw_callback
), NULL
);
851 g_signal_connect (output
, "style-updated", G_CALLBACK (on_style_updated
), view
);
853 g_signal_connect (output
, "size-allocate", G_CALLBACK (on_size_allocate
), view
);
855 gtk_widget_add_events (GTK_WIDGET (output
), GDK_BUTTON_PRESS_MASK
);
856 g_signal_connect (output
, "button-press-event",
857 G_CALLBACK (off_item_button_press_event_cb
), view
);
859 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (output
)),
860 GTK_STYLE_CLASS_VIEW
);
864 g_signal_connect (overview
, "realize", G_CALLBACK (on_realize
), view
);
866 GtkTreeModel
*model
= GTK_TREE_MODEL (gtk_tree_store_new (
868 G_TYPE_STRING
, /* COL_LABEL */
869 G_TYPE_POINTER
, /* COL_ADDR */
870 G_TYPE_LONG
)); /* COL_Y */
871 gtk_tree_view_set_model (overview
, model
);
872 g_object_unref (model
);
874 GtkTreeViewColumn
*column
= gtk_tree_view_column_new ();
875 gtk_tree_view_append_column (GTK_TREE_VIEW (overview
), column
);
876 GtkCellRenderer
* renderer
= gtk_cell_renderer_text_new ();
877 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
878 gtk_tree_view_column_add_attribute (column
, renderer
, "text", COL_LABEL
);
880 g_signal_connect (GTK_TREE_VIEW (overview
),
881 "row-activated", G_CALLBACK (on_row_activate
), view
);
888 psppire_output_view_destroy (struct psppire_output_view
*view
)
895 g_signal_handlers_disconnect_by_func (view
->output
,
896 G_CALLBACK (on_style_updated
), view
);
898 xr_fsm_style_unref (view
->style
);
900 for (i
= 0; i
< view
->n_items
; i
++)
901 output_item_unref (view
->items
[i
].item
);
904 view
->n_items
= view
->allocated_items
= 0;
906 if (view
->print_settings
!= NULL
)
907 g_object_unref (view
->print_settings
);
915 psppire_output_view_export__ (struct output_driver
*driver
,
916 const struct output_item
*item
)
918 if (item
->type
== OUTPUT_ITEM_GROUP
&& !driver
->class->handles_groups
)
920 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
921 psppire_output_view_export__ (driver
, item
->group
.children
[i
]);
924 driver
->class->submit (driver
, item
);
928 psppire_output_view_export (struct psppire_output_view
*view
,
929 struct string_map
*options
)
931 struct output_driver
*driver
;
933 driver
= output_driver_create (options
);
938 for (i
= 0; i
< view
->n_items
; i
++)
939 if (view
->items
[i
].nesting_depth
== 0)
940 psppire_output_view_export__ (driver
, view
->items
[i
].item
);
941 output_driver_destroy (driver
);
948 get_cairo_context_from_print_context (GtkPrintContext
*context
)
950 cairo_t
*cr
= gtk_print_context_get_cairo_context (context
);
951 return cairo_reference (cr
);
955 create_xr_print_driver (GtkPrintContext
*context
, struct psppire_output_view
*view
)
957 GtkPageSetup
*ps
= gtk_print_context_get_page_setup (context
);
959 enum { H
= TABLE_HORZ
, V
= TABLE_VERT
};
960 int paper
[TABLE_N_AXES
] = {
961 [H
] = gtk_page_setup_get_paper_width (ps
, GTK_UNIT_POINTS
) * XR_POINT
,
962 [V
] = gtk_page_setup_get_paper_height (ps
, GTK_UNIT_POINTS
) * XR_POINT
,
965 /* These are all 1/2 inch. The "margins" that GTK+ gives us are useless:
966 they are the printer's imagable area. */
967 int margins
[TABLE_N_AXES
][2] = {
968 [H
][0] = XR_POINT
* 36,
969 [H
][1] = XR_POINT
* 36,
970 [V
][0] = XR_POINT
* 36,
971 [V
][1] = XR_POINT
* 36,
974 double size
[TABLE_N_AXES
];
975 for (int a
= 0; a
< TABLE_N_AXES
; a
++)
976 size
[a
] = paper
[a
] - margins
[a
][0] - margins
[a
][1];
978 view
->page_style
= xmalloc (sizeof *view
->page_style
);
979 *view
->page_style
= (struct xr_page_style
) {
983 [H
] = { margins
[H
][0], margins
[H
][1] },
984 [V
] = { margins
[V
][0], margins
[V
][1] },
986 .initial_page_number
= 1,
989 view
->fsm_style
= xmalloc (sizeof *view
->fsm_style
);
990 *view
->fsm_style
= (struct xr_fsm_style
) {
993 .size
= { [H
] = size
[H
], [V
] = size
[V
] },
994 .min_break
= { [H
] = size
[H
] / 2, [V
] = size
[V
] / 2 },
995 .font
= pango_font_description_from_string ("Sans Serif 10"),
996 .fg
= CELL_COLOR_BLACK
,
997 .use_system_colors
= false,
998 .object_spacing
= 12 * XR_POINT
,
999 .font_resolution
= 72.0
1002 view
->pager
= xr_pager_create (view
->page_style
, view
->fsm_style
);
1006 paginate (GtkPrintOperation
*operation
,
1007 GtkPrintContext
*context
,
1008 struct psppire_output_view
*view
)
1010 if (view
->paginated
)
1012 /* Sometimes GTK+ emits this signal again even after pagination is
1013 complete. Don't let that screw up printing. */
1017 while (view
->print_item
< view
->n_items
)
1019 const struct output_view_item
*item
= &view
->items
[view
->print_item
++];
1020 if (item
->nesting_depth
== 0)
1022 xr_pager_add_item (view
->pager
, item
->item
);
1023 while (xr_pager_needs_new_page (view
->pager
))
1025 xr_pager_add_page (view
->pager
,
1026 get_cairo_context_from_print_context (context
));
1027 view
->print_n_pages
++;
1033 gtk_print_operation_set_n_pages (operation
, MAX (1, view
->print_n_pages
));
1035 /* Re-create the driver to do the real printing. */
1036 xr_pager_destroy (view
->pager
);
1037 view
->pager
= xr_pager_create (view
->page_style
, view
->fsm_style
);
1038 view
->print_item
= 0;
1039 view
->paginated
= TRUE
;
1045 begin_print (GtkPrintOperation
*operation
,
1046 GtkPrintContext
*context
,
1047 struct psppire_output_view
*view
)
1049 create_xr_print_driver (context
, view
);
1051 view
->print_item
= 0;
1052 view
->print_n_pages
= 0;
1053 view
->paginated
= FALSE
;
1057 end_print (GtkPrintOperation
*operation
,
1058 GtkPrintContext
*context
,
1059 struct psppire_output_view
*view
)
1061 xr_pager_destroy (view
->pager
);
1067 draw_page (GtkPrintOperation
*operation
,
1068 GtkPrintContext
*context
,
1070 struct psppire_output_view
*view
)
1072 xr_pager_add_page (view
->pager
,
1073 get_cairo_context_from_print_context (context
));
1074 while (!xr_pager_needs_new_page (view
->pager
)
1075 && view
->print_item
< view
->n_items
)
1077 const struct output_view_item
*item
= &view
->items
[view
->print_item
++];
1078 if (item
->nesting_depth
== 0)
1079 xr_pager_add_item (view
->pager
, item
->item
);
1085 psppire_output_view_print (struct psppire_output_view
*view
,
1086 GtkWindow
*parent_window
)
1088 GtkPrintOperationResult res
;
1090 GtkPrintOperation
*print
= gtk_print_operation_new ();
1092 if (view
->print_settings
!= NULL
)
1093 gtk_print_operation_set_print_settings (print
, view
->print_settings
);
1095 gtk_print_operation_set_use_full_page (print
, TRUE
);
1096 gtk_print_operation_set_unit (print
, GTK_UNIT_POINTS
);
1098 g_signal_connect (print
, "begin_print", G_CALLBACK (begin_print
), view
);
1099 g_signal_connect (print
, "end_print", G_CALLBACK (end_print
), view
);
1100 g_signal_connect (print
, "paginate", G_CALLBACK (paginate
), view
);
1101 g_signal_connect (print
, "draw_page", G_CALLBACK (draw_page
), view
);
1103 res
= gtk_print_operation_run (print
, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
,
1104 parent_window
, NULL
);
1106 if (res
== GTK_PRINT_OPERATION_RESULT_APPLY
)
1108 if (view
->print_settings
!= NULL
)
1109 g_object_unref (view
->print_settings
);
1110 view
->print_settings
= g_object_ref (gtk_print_operation_get_print_settings (print
));
1113 g_object_unref (print
);