From: Friedrich Beckmann Date: Fri, 11 Sep 2020 16:33:54 +0000 (+0200) Subject: psppire-output-view: modified the way how copy and paste works X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=092c2acd489e88dd99d1f27560d16fc1ba89c88c;p=pspp psppire-output-view: modified the way how copy and paste works Copy (no Paste in output window) works now the usual gui way. You can select an item in the output window like a table or a chart and it will be highlighted. When you now do the copy operation via menu or key, then the item is copied to the clipboard. With the change the clipboard now provides the additional formats svg and various image formats. You can now paste to inkscape, libreoffice suite and gimp. --- diff --git a/INSTALL b/INSTALL index d565ed4c02..c131292cb3 100644 --- a/INSTALL +++ b/INSTALL @@ -127,6 +127,10 @@ Other optional packages: PSPP to test the Perl module more thoroughly. It is not needed to build or use the Perl module. + * librsvg enables 300 dpi copy and paste operation. Without librsvg + the copy action will only provide images with default resolution + which is often 96dpi. This only affects bitmap image formats. + Basic Installation ================== diff --git a/configure.ac b/configure.ac index 5df5bc7686..27a4c87af3 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ $as_echo 'Hallo' >> test.metainfo.xml.in $as_echo '' >> test.metainfo.xml.in xgettext -o mifc.po test.metainfo.xml.in if ! test -f ./mifc.po ; then - AC_MSG_WARN([org.fsf.metainfo.xml will not be translated. Install gettext >=0.20 or appstream.]) + PSPP_OPTIONAL_PREREQ([org.fsf.metainfo.xml will not be translated. Install gettext >=0.20 or appstream.]) fi rm -f mifc.po test.metainfo.xml.in @@ -137,6 +137,10 @@ if test "$with_cairo" != no && test "$with_gui" != "no"; then PKG_CHECK_MODULES([SPREAD_SHEET_WIDGET], [spread-sheet-widget >= 0.6], [], [PSPP_REQUIRED_PREREQ([spread-sheet-widget 0.6 (or use --without-gui)])]) + PKG_CHECK_MODULES([LIBRSVG], [librsvg-2.0 >= 2.44], + [AC_DEFINE([HAVE_RSVG], 1, [Define to 1 if librsvg is available])], + [PSPP_OPTIONAL_PREREQ([librsvg >= 2.44 required for high dpi Copy and Paste])]) + AC_ARG_VAR([GLIB_GENMARSHAL]) AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal]) if test "x$GLIB_GENMARSHAL" = x; then diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 54e8da0c61..d3036d887d 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -90,7 +90,10 @@ src_ui_gui_psppire_CPPFLAGS= bin_PROGRAMS += src/ui/gui/psppire noinst_PROGRAMS += src/ui/gui/spreadsheet-test -src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) $(SPREAD_SHEET_WIDGET_CFLAGS) $(AM_CFLAGS) -DGDK_MULTIHEAD_SAFE=1 +src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GTKSOURCEVIEW_CFLAGS) \ + $(SPREAD_SHEET_WIDGET_CFLAGS) \ + $(LIBRSVG_CFLAGS) \ + $(AM_CFLAGS) -DGDK_MULTIHEAD_SAFE=1 src_ui_gui_spreadsheet_test_CFLAGS = $(GTK_CFLAGS) $(AM_CFLAGS) -DGDK_MULTIHEAD_SAFE=1 if cc_is_gcc @@ -120,6 +123,7 @@ src_ui_gui_psppire_LDADD = \ $(GTHREAD_LIBS) \ $(GTKSOURCEVIEW_LIBS) \ $(SPREAD_SHEET_WIDGET_LIBS) \ + $(LIBRSVG_LIBS) \ $(CAIRO_LIBS) \ $(LIBINTL) \ $(GSL_LIBS) diff --git a/src/ui/gui/psppire-output-view.c b/src/ui/gui/psppire-output-view.c index 490f3de70c..8c14a93125 100644 --- a/src/ui/gui/psppire-output-view.c +++ b/src/ui/gui/psppire-output-view.c @@ -21,6 +21,9 @@ #include #include +#if HAVE_RSVG +#include "librsvg/rsvg.h" +#endif #include "libpspp/assertion.h" #include "libpspp/string-map.h" #include "output/cairo.h" @@ -66,6 +69,7 @@ struct psppire_output_view struct output_view_item *items; size_t n_items, allocated_items; + struct output_view_item *selected_item; /* Variables pertaining to printing */ GtkPrintSettings *print_settings; @@ -109,7 +113,6 @@ draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data) GtkStyleContext *context = gtk_widget_get_style_context (widget); gtk_render_background (context, cr, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height); - /* Select the default foreground color based on current style and state of the widget */ GtkStateFlags state = gtk_widget_get_state_flags (widget); @@ -204,29 +207,74 @@ get_xpos (const struct psppire_output_view *view, gint child_width) return (gtk_widget_get_direction (GTK_WIDGET (view->output)) == GTK_TEXT_DIR_RTL) ? w - child_width - gutter: gutter; } -static void -clear_selection (struct psppire_output_view *view) +static struct output_view_item * +find_selected_item (struct psppire_output_view *view) { struct output_view_item *item = NULL; if (view == NULL) - return; + return NULL; if (view->items == NULL) - return; + return NULL; for (item = view->items; item < &view->items[view->n_items]; item++) { GtkWidget *widget = GTK_WIDGET (item->drawing_area); if GTK_IS_WIDGET (widget) - gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_SELECTED); + { + GtkStateFlags state = gtk_widget_get_state_flags (widget); + if (state & GTK_STATE_FLAG_SELECTED) + return item; + } + } + return NULL; +} + + +static void +set_copy_action (struct psppire_output_view *view, + gboolean state) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view->output)); + GAction *copy_action = g_action_map_lookup_action (G_ACTION_MAP (toplevel), + "copy"); + g_object_set (copy_action, + "enabled", state, + NULL); +} + +static void +clear_selection (struct psppire_output_view *view) +{ + if (view == NULL) + return; + struct output_view_item *item = find_selected_item (view); + if (item == NULL) + return; + set_copy_action (view, FALSE); + GtkWidget *widget = GTK_WIDGET (item->drawing_area); + if (GTK_IS_WIDGET (widget)) + { + gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_SELECTED); + gtk_widget_queue_draw (widget); } } +static gboolean +off_item_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + struct psppire_output_view *view) +{ + clear_selection (view); + return FALSE; /* Forward the event */ +} + static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *event, struct psppire_output_view *view) { clear_selection (view); + set_copy_action (view, TRUE); gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE); gtk_widget_queue_draw (widget); return TRUE; /* We have handled the event */ @@ -237,6 +285,11 @@ create_drawing_area (struct psppire_output_view *view, GtkWidget *drawing_area, struct xr_rendering *r, int tw, int th) { + struct string_map options = STRING_MAP_INITIALIZER (options); + string_map_insert (&options, "transparent", "true"); + string_map_insert (&options, "systemcolors", "true"); + xr_rendering_apply_options (r, &options); + g_object_set_data_full (G_OBJECT (drawing_area), "rendering", r, free_rendering); @@ -406,11 +459,6 @@ psppire_output_view_put (struct psppire_output_view *view, } xr_rendering_measure (r, &tw, &th); - - struct string_map options = STRING_MAP_INITIALIZER (options); - string_map_insert (&options, "transparent", "true"); - string_map_insert (&options, "systemcolors", "true"); - xr_rendering_apply_options (r, &options); create_drawing_area (view, drawing_area, r, tw, th); gdk_window_end_draw_frame (win, ctx); cairo_region_destroy (region); @@ -552,6 +600,8 @@ enum { SELECT_FMT_TEXT, SELECT_FMT_UTF8, SELECT_FMT_HTML, + SELECT_FMT_SVG, + SELECT_FMT_IMG, SELECT_FMT_ODT }; @@ -561,6 +611,34 @@ enum { # define PATH_MAX 1024 #endif +/* Returns a pixbuf from a svg file */ +/* You must unref the pixbuf after usage */ +static GdkPixbuf * +derive_pixbuf_from_svg (const char *filename) +{ + GError *err = NULL; + GdkPixbuf *pixbuf = NULL; +#if HAVE_RSVG + RsvgHandle *handle = rsvg_handle_new_from_file (filename, &err); + if (err == NULL) + { + rsvg_handle_set_dpi (handle, 300.0); + pixbuf = rsvg_handle_get_pixbuf (handle); + g_object_unref (handle); + } +#else + pixbuf = gdk_pixbuf_new_from_file (filename, &err); +#endif + if (err != NULL) + { + msg (ME, _("Could not open file %s during copy operation: %s"), + filename, err->message); + g_error_free (err); + return NULL; + } + return pixbuf; +} + static void clipboard_get_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, @@ -575,13 +653,7 @@ clipboard_get_cb (GtkClipboard *clipboard, 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) + if (view->selected_item == NULL) return; if (path_search (dirname, sizeof dirname, NULL, NULL, true) @@ -611,6 +683,11 @@ clipboard_get_cb (GtkClipboard *clipboard, string_map_insert (&options, "css", "false"); break; + case SELECT_FMT_SVG: + case SELECT_FMT_IMG: + /* see below */ + break; + case SELECT_FMT_ODT: string_map_insert (&options, "format", "odt"); break; @@ -621,39 +698,43 @@ clipboard_get_cb (GtkClipboard *clipboard, break; } - driver = output_driver_create (&options); - if (driver == NULL) - goto finish; - - while (n) + if ((info == SELECT_FMT_IMG) || + (info == SELECT_FMT_SVG) ) { - 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; + GtkWidget *widget = view->selected_item->drawing_area; + struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering"); + xr_draw_svg_file (r, filename); } + else + { + driver = output_driver_create (&options); + if (driver == NULL) + goto finish; - if (driver->class->flush) - driver->class->flush (driver); + driver->class->submit (driver, view->selected_item->item); + 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; + /* 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)) + if (info == SELECT_FMT_IMG) { - gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), - 8, - (const guchar *) text, length); + GdkPixbuf *pixbuf = derive_pixbuf_from_svg (filename); + if (pixbuf) + { + gtk_selection_data_set_pixbuf (selection_data, pixbuf); + g_object_unref (pixbuf); + } } + else 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: @@ -665,8 +746,6 @@ clipboard_get_cb (GtkClipboard *clipboard, unlink (filename); free (filename); rmdir (dirname); - - g_list_free (rows); } static void @@ -682,7 +761,8 @@ CT ( ctn3, "COMPOUND_TEXT", 0, SELECT_FMT_TEXT ) \ CT ( ctn4, "text/plain", 0, SELECT_FMT_TEXT ) \ CT ( ctn5, "UTF8_STRING", 0, SELECT_FMT_UTF8 ) \ CT ( ctn6, "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 ) \ -CT ( ctn7, "text/html", 0, SELECT_FMT_HTML ) +CT ( ctn7, "text/html", 0, SELECT_FMT_HTML ) \ +CT ( ctn8, "image/svg+xml", 0, SELECT_FMT_SVG ) #define CT(ID, TARGET, FLAGS, INFO) static gchar ID[] = TARGET; CBTARGETS @@ -702,10 +782,25 @@ 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), + struct output_view_item *ov_item = find_selected_item (view); + if (ov_item == NULL) + return; + view->selected_item = ov_item; + GtkTargetList *tl = gtk_target_list_new (targets, G_N_ELEMENTS (targets)); + g_return_if_fail (tl); + if (is_table_item (ov_item->item) || + is_chart_item (ov_item->item)) + gtk_target_list_add_image_targets (tl, SELECT_FMT_IMG, TRUE); + gint no_of_targets = 0; + GtkTargetEntry *ta = gtk_target_table_new_from_list (tl, &no_of_targets); + g_return_if_fail (ta); + if (!gtk_clipboard_set_with_data (cb, ta, no_of_targets, clipboard_get_cb, clipboard_clear_cb, view)) clipboard_clear_cb (cb, view); + + gtk_target_list_unref (tl); + gtk_target_table_free (ta,no_of_targets); } static void @@ -782,6 +877,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview) view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output)); view->items = NULL; view->n_items = view->allocated_items = 0; + view->selected_item = NULL; view->print_settings = NULL; view->print_xrd = NULL; view->print_item = 0; @@ -794,6 +890,10 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview) g_signal_connect (output, "size-allocate", G_CALLBACK (on_size_allocate), view); + gtk_widget_add_events (GTK_WIDGET (output), GDK_BUTTON_PRESS_MASK); + g_signal_connect (output, "button-press-event", + G_CALLBACK (off_item_button_press_event_cb), view); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (output)), GTK_STYLE_CLASS_VIEW);