1 /* GTK - The GIMP Toolkit
2  * gtkprintoperation-portal.c: Print Operation Details for sandboxed apps
3  * Copyright (C) 2016, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <string.h>
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 
27 #include <cairo-pdf.h>
28 #include <cairo-ps.h>
29 
30 #include <gio/gunixfdlist.h>
31 
32 #include "gtkprintoperation-private.h"
33 #include "gtkprintoperation-portal.h"
34 #include "gtkprintsettings.h"
35 #include "gtkpagesetup.h"
36 #include "gtkprintbackend.h"
37 #include "gtkshow.h"
38 #include "gtkintl.h"
39 #include "gtkwindowprivate.h"
40 #include "gtkprivate.h"
41 
42 
43 typedef struct {
44   GtkPrintOperation *op;
45   GDBusProxy *proxy;
46   guint response_signal_id;
47   gboolean do_print;
48   GtkPrintOperationResult result;
49   GtkPrintOperationPrintFunc print_cb;
50   GtkWindow *parent;
51   GMainLoop *loop;
52   guint32 token;
53   GDestroyNotify destroy;
54   GVariant *settings;
55   GVariant *setup;
56   GVariant *options;
57   char *prepare_print_handle;
58 } PortalData;
59 
60 static void
portal_data_free(gpointer data)61 portal_data_free (gpointer data)
62 {
63   PortalData *portal = data;
64 
65   g_object_unref (portal->op);
66   g_object_unref (portal->proxy);
67   if (portal->loop)
68     g_main_loop_unref (portal->loop);
69   if (portal->settings)
70     g_variant_unref (portal->settings);
71   if (portal->setup)
72     g_variant_unref (portal->setup);
73   if (portal->options)
74     g_variant_unref (portal->options);
75   g_free (portal->prepare_print_handle);
76   g_free (portal);
77 }
78 
79 typedef struct {
80   GDBusProxy *proxy;
81   GtkPrintJob *job;
82   guint32 token;
83   cairo_surface_t *surface;
84   GMainLoop *loop;
85   gboolean file_written;
86 } GtkPrintOperationPortal;
87 
88 static void
op_portal_free(GtkPrintOperationPortal * op_portal)89 op_portal_free (GtkPrintOperationPortal *op_portal)
90 {
91   g_clear_object (&op_portal->proxy);
92   g_clear_object (&op_portal->job);
93   if (op_portal->loop)
94     g_main_loop_unref (op_portal->loop);
95   g_free (op_portal);
96 }
97 
98 static void
portal_start_page(GtkPrintOperation * op,GtkPrintContext * print_context,GtkPageSetup * page_setup)99 portal_start_page (GtkPrintOperation *op,
100                    GtkPrintContext   *print_context,
101                    GtkPageSetup      *page_setup)
102 {
103   GtkPrintOperationPortal *op_portal = op->priv->platform_data;
104   GtkPaperSize *paper_size;
105   cairo_surface_type_t type;
106   gdouble w, h;
107 
108   paper_size = gtk_page_setup_get_paper_size (page_setup);
109 
110   w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
111   h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
112 
113   type = cairo_surface_get_type (op_portal->surface);
114 
115   if ((op->priv->manual_number_up < 2) ||
116       (op->priv->page_position % op->priv->manual_number_up == 0))
117     {
118       if (type == CAIRO_SURFACE_TYPE_PS)
119         {
120           cairo_ps_surface_set_size (op_portal->surface, w, h);
121           cairo_ps_surface_dsc_begin_page_setup (op_portal->surface);
122           switch (gtk_page_setup_get_orientation (page_setup))
123             {
124               case GTK_PAGE_ORIENTATION_PORTRAIT:
125               case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
126                 cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Portrait");
127                 break;
128 
129               case GTK_PAGE_ORIENTATION_LANDSCAPE:
130               case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
131                 cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Landscape");
132                 break;
133             }
134          }
135       else if (type == CAIRO_SURFACE_TYPE_PDF)
136         {
137           if (!op->priv->manual_orientation)
138             {
139               w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
140               h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
141             }
142           cairo_pdf_surface_set_size (op_portal->surface, w, h);
143         }
144     }
145 }
146 
147 static void
portal_end_page(GtkPrintOperation * op,GtkPrintContext * print_context)148 portal_end_page (GtkPrintOperation *op,
149                  GtkPrintContext   *print_context)
150 {
151   cairo_t *cr;
152 
153   cr = gtk_print_context_get_cairo_context (print_context);
154 
155   if ((op->priv->manual_number_up < 2) ||
156       ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) ||
157       (op->priv->page_position == op->priv->nr_of_pages_to_print - 1))
158     cairo_show_page (cr);
159 }
160 
161 static void
print_file_done(GObject * source,GAsyncResult * result,gpointer data)162 print_file_done (GObject *source,
163                  GAsyncResult *result,
164                  gpointer data)
165 {
166   GtkPrintOperation *op = data;
167   GtkPrintOperationPortal *op_portal = op->priv->platform_data;
168   GError *error = NULL;
169   GVariant *ret;
170 
171   ret = g_dbus_proxy_call_finish (op_portal->proxy,
172                                   result,
173                                   &error);
174   if (ret == NULL)
175     {
176       if (op->priv->error == NULL)
177         op->priv->error = g_error_copy (error);
178       g_warning ("Print file failed: %s", error->message);
179       g_error_free (error);
180     }
181   else
182     g_variant_unref (ret);
183 
184   if (op_portal->loop)
185     g_main_loop_quit (op_portal->loop);
186 
187   g_object_unref (op);
188 }
189 
190 static void
portal_job_complete(GtkPrintJob * job,gpointer data,const GError * error)191 portal_job_complete (GtkPrintJob  *job,
192                      gpointer      data,
193                      const GError *error)
194 {
195   GtkPrintOperation *op = data;
196   GtkPrintOperationPortal *op_portal = op->priv->platform_data;
197   GtkPrintSettings *settings;
198   const char *uri;
199   char *filename;
200   int fd, idx;
201   GVariantBuilder opt_builder;
202   GUnixFDList *fd_list;
203 
204   if (error != NULL && op->priv->error == NULL)
205     {
206       g_warning ("Print job failed: %s", error->message);
207       op->priv->error = g_error_copy (error);
208       return;
209     }
210 
211   op_portal->file_written = TRUE;
212 
213   settings = gtk_print_job_get_settings (job);
214   uri = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI);
215   filename = g_filename_from_uri (uri, NULL, NULL);
216 
217   fd = open (filename, O_RDONLY|O_CLOEXEC);
218   fd_list = g_unix_fd_list_new ();
219   idx = g_unix_fd_list_append (fd_list, fd, NULL);
220   close (fd);
221 
222   g_free (filename);
223 
224   g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
225   g_variant_builder_add (&opt_builder, "{sv}",  "token", g_variant_new_uint32 (op_portal->token));
226 
227   g_dbus_proxy_call_with_unix_fd_list (op_portal->proxy,
228                                        "Print",
229                                        g_variant_new ("(ssh@a{sv})",
230                                                       "", /* window */
231                                                       _("Print"), /* title */
232                                                       idx,
233                                                       g_variant_builder_end (&opt_builder)),
234                                        G_DBUS_CALL_FLAGS_NONE,
235                                        -1,
236                                        fd_list,
237                                        NULL,
238                                        print_file_done,
239                                        op);
240   g_object_unref (fd_list);
241 }
242 
243 static void
portal_end_run(GtkPrintOperation * op,gboolean wait,gboolean cancelled)244 portal_end_run (GtkPrintOperation *op,
245                 gboolean           wait,
246                 gboolean           cancelled)
247 {
248   GtkPrintOperationPortal *op_portal = op->priv->platform_data;
249 
250   cairo_surface_finish (op_portal->surface);
251 
252   if (cancelled)
253     return;
254 
255   if (wait)
256     op_portal->loop = g_main_loop_new (NULL, FALSE);
257 
258   /* TODO: Check for error */
259   if (op_portal->job != NULL)
260     {
261       g_object_ref (op);
262       gtk_print_job_send (op_portal->job, portal_job_complete, op, NULL);
263     }
264 
265   if (wait)
266     {
267       g_object_ref (op);
268       if (!op_portal->file_written)
269         {
270           gdk_threads_leave ();
271           g_main_loop_run (op_portal->loop);
272           gdk_threads_enter ();
273         }
274       g_object_unref (op);
275     }
276 }
277 
278 static void
finish_print(PortalData * portal,GtkPrinter * printer,GtkPageSetup * page_setup,GtkPrintSettings * settings)279 finish_print (PortalData        *portal,
280               GtkPrinter        *printer,
281               GtkPageSetup      *page_setup,
282               GtkPrintSettings  *settings)
283 {
284   GtkPrintOperation *op = portal->op;
285   GtkPrintOperationPrivate *priv = op->priv;
286   GtkPrintJob *job;
287   GtkPrintOperationPortal *op_portal;
288   cairo_t *cr;
289 
290   if (portal->do_print)
291     {
292       gtk_print_operation_set_print_settings (op, settings);
293       priv->print_context = _gtk_print_context_new (op);
294 
295       _gtk_print_context_set_hard_margins (priv->print_context, 0, 0, 0, 0);
296 
297       gtk_print_operation_set_default_page_setup (op, page_setup);
298       _gtk_print_context_set_page_setup (priv->print_context, page_setup);
299 
300       op_portal = g_new0 (GtkPrintOperationPortal, 1);
301       priv->platform_data = op_portal;
302       priv->free_platform_data = (GDestroyNotify) op_portal_free;
303 
304       priv->start_page = portal_start_page;
305       priv->end_page = portal_end_page;
306       priv->end_run = portal_end_run;
307 
308       job = gtk_print_job_new (priv->job_name, printer, settings, page_setup);
309       op_portal->job = job;
310 
311       op_portal->proxy = g_object_ref (portal->proxy);
312       op_portal->token = portal->token;
313 
314       op_portal->surface = gtk_print_job_get_surface (job, &priv->error);
315       if (op_portal->surface == NULL)
316         {
317           portal->result = GTK_PRINT_OPERATION_RESULT_ERROR;
318           portal->do_print = FALSE;
319           goto out;
320         }
321 
322       cr = cairo_create (op_portal->surface);
323       gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
324       cairo_destroy (cr);
325 
326       priv->print_pages = gtk_print_job_get_pages (job);
327       priv->page_ranges = gtk_print_job_get_page_ranges (job, &priv->num_page_ranges);
328       priv->manual_num_copies = gtk_print_job_get_num_copies (job);
329       priv->manual_collation = gtk_print_job_get_collate (job);
330       priv->manual_reverse = gtk_print_job_get_reverse (job);
331       priv->manual_page_set = gtk_print_job_get_page_set (job);
332       priv->manual_scale = gtk_print_job_get_scale (job);
333       priv->manual_orientation = gtk_print_job_get_rotate (job);
334       priv->manual_number_up = gtk_print_job_get_n_up (job);
335       priv->manual_number_up_layout = gtk_print_job_get_n_up_layout (job);
336     }
337 
338 out:
339   if (portal->print_cb)
340     portal->print_cb (op, portal->parent, portal->do_print, portal->result);
341 
342   if (portal->destroy)
343     portal->destroy (portal);
344 }
345 
346 static GtkPrinter *
find_file_printer(void)347 find_file_printer (void)
348 {
349   GList *backends, *l, *printers;
350   GtkPrinter *printer;
351 
352   printer = NULL;
353 
354   backends = gtk_print_backend_load_modules ();
355   for (l = backends; l; l = l->next)
356     {
357       GtkPrintBackend *backend = l->data;
358       if (strcmp (G_OBJECT_TYPE_NAME (backend), "GtkPrintBackendFile") == 0)
359         {
360           printers = gtk_print_backend_get_printer_list (backend);
361           printer = printers->data;
362           g_list_free (printers);
363           break;
364         }
365     }
366   g_list_free (backends);
367 
368   return printer;
369 }
370 
371 static void
prepare_print_response(GDBusConnection * connection,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)372 prepare_print_response (GDBusConnection *connection,
373                         const char      *sender_name,
374                         const char      *object_path,
375                         const char      *interface_name,
376                         const char      *signal_name,
377                         GVariant        *parameters,
378                         gpointer         data)
379 {
380   PortalData *portal = data;
381   guint32 response;
382   GVariant *options;
383 
384   if (portal->response_signal_id != 0)
385     {
386       g_dbus_connection_signal_unsubscribe (connection,
387                                             portal->response_signal_id);
388       portal->response_signal_id = 0;
389     }
390 
391   g_variant_get (parameters, "(u@a{sv})", &response, &options);
392 
393   portal->do_print = (response == 0);
394 
395   if (portal->do_print)
396     {
397       GVariant *v;
398       GtkPrintSettings *settings;
399       GtkPageSetup *page_setup;
400       GtkPrinter *printer;
401       char *filename;
402       char *uri;
403       int fd;
404 
405       portal->result = GTK_PRINT_OPERATION_RESULT_APPLY;
406 
407       v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
408       settings = gtk_print_settings_new_from_gvariant (v);
409       g_variant_unref (v);
410 
411       v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT);
412       page_setup = gtk_page_setup_new_from_gvariant (v);
413       g_variant_unref (v);
414 
415       g_variant_lookup (options, "token", "u", &portal->token);
416 
417       printer = find_file_printer ();
418 
419       fd = g_file_open_tmp ("gtkprintXXXXXX", &filename, NULL);
420       uri = g_filename_to_uri (filename, NULL, NULL);
421       gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
422       g_free (uri);
423       close (fd);
424 
425       finish_print (portal, printer, page_setup, settings);
426       g_free (filename);
427     }
428   else
429     {
430       portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
431 
432       if (portal->print_cb)
433 	  portal->print_cb (portal->op, portal->parent, portal->do_print, portal->result);
434 
435       if (portal->destroy)
436 	  portal->destroy (portal);
437     }
438   if (portal->loop)
439     g_main_loop_quit (portal->loop);
440 }
441 
442 static void
prepare_print_called(GObject * source,GAsyncResult * result,gpointer data)443 prepare_print_called (GObject      *source,
444                       GAsyncResult *result,
445                       gpointer      data)
446 {
447   PortalData *portal = data;
448   GError *error = NULL;
449   const char *handle = NULL;
450   GVariant *ret;
451 
452   ret = g_dbus_proxy_call_finish (portal->proxy, result, &error);
453   if (ret == NULL)
454     {
455       if (portal->op->priv->error == NULL)
456         portal->op->priv->error = g_error_copy (error);
457       g_error_free (error);
458       if (portal->loop)
459         g_main_loop_quit (portal->loop);
460       return;
461     }
462   else
463     g_variant_get (ret, "(&o)", &handle);
464 
465   if (strcmp (portal->prepare_print_handle, handle) != 0)
466     {
467       g_free (portal->prepare_print_handle);
468       portal->prepare_print_handle = g_strdup (handle);
469       g_dbus_connection_signal_unsubscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
470                                             portal->response_signal_id);
471       portal->response_signal_id =
472         g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
473                                             "org.freedesktop.portal.Desktop",
474                                             "org.freedesktop.portal.Request",
475                                             "Response",
476                                             handle,
477                                             NULL,
478                                             G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
479                                             prepare_print_response,
480                                             portal, NULL);
481      }
482 
483   g_variant_unref (ret);
484 }
485 
486 PortalData *
create_portal_data(GtkPrintOperation * op,GtkWindow * parent,GtkPrintOperationPrintFunc print_cb)487 create_portal_data (GtkPrintOperation          *op,
488                     GtkWindow                  *parent,
489                     GtkPrintOperationPrintFunc  print_cb)
490 {
491   GDBusProxy *proxy;
492   PortalData *portal;
493   guint signal_id;
494   GError *error = NULL;
495 
496   signal_id = g_signal_lookup ("create-custom-widget", GTK_TYPE_PRINT_OPERATION);
497   if (g_signal_has_handler_pending (op, signal_id, 0, TRUE))
498     g_warning ("GtkPrintOperation::create-custom-widget not supported with portal");
499 
500   proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
501                                          G_DBUS_PROXY_FLAGS_NONE,
502                                          NULL,
503                                          "org.freedesktop.portal.Desktop",
504                                          "/org/freedesktop/portal/desktop",
505                                          "org.freedesktop.portal.Print",
506                                          NULL,
507                                          &error);
508 
509   if (proxy == NULL)
510     {
511       if (op->priv->error == NULL)
512         op->priv->error = g_error_copy (error);
513       g_error_free (error);
514       return NULL;
515     }
516 
517   portal = g_new0 (PortalData, 1);
518   portal->proxy = proxy;
519   portal->op = g_object_ref (op);
520   portal->parent = parent;
521   portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
522   portal->print_cb = print_cb;
523 
524   if (print_cb) /* async case */
525     {
526       portal->loop = NULL;
527       portal->destroy = portal_data_free;
528     }
529   else
530     {
531       portal->loop = g_main_loop_new (NULL, FALSE);
532       portal->destroy = NULL;
533     }
534 
535   return portal;
536 }
537 
538 static void
window_handle_exported(GtkWindow * window,const char * handle_str,gpointer user_data)539 window_handle_exported (GtkWindow  *window,
540                         const char *handle_str,
541                         gpointer    user_data)
542 {
543   PortalData *portal = user_data;
544 
545   g_dbus_proxy_call (portal->proxy,
546                      "PreparePrint",
547                      g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
548                                     handle_str,
549                                     _("Print"), /* title */
550                                     portal->settings,
551                                     portal->setup,
552                                     portal->options),
553                      G_DBUS_CALL_FLAGS_NONE,
554                      -1,
555                      NULL,
556                      prepare_print_called,
557                      portal);
558 }
559 
560 static void
call_prepare_print(GtkPrintOperation * op,PortalData * portal)561 call_prepare_print (GtkPrintOperation *op,
562                     PortalData        *portal)
563 {
564   GtkPrintOperationPrivate *priv = op->priv;
565   GVariantBuilder opt_builder;
566   char *token;
567 
568   portal->prepare_print_handle =
569       gtk_get_portal_request_path (g_dbus_proxy_get_connection (portal->proxy), &token);
570 
571   portal->response_signal_id =
572     g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
573                                         "org.freedesktop.portal.Desktop",
574                                         "org.freedesktop.portal.Request",
575                                         "Response",
576                                         portal->prepare_print_handle,
577                                         NULL,
578                                         G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
579                                         prepare_print_response,
580                                         portal, NULL);
581 
582   g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
583   g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (token));
584   g_free (token);
585   portal->options = g_variant_builder_end (&opt_builder);
586 
587   if (priv->print_settings)
588     portal->settings = gtk_print_settings_to_gvariant (priv->print_settings);
589   else
590     {
591       GVariantBuilder builder;
592       g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
593       portal->settings = g_variant_builder_end (&builder);
594     }
595 
596   if (priv->default_page_setup)
597     portal->setup = gtk_page_setup_to_gvariant (priv->default_page_setup);
598   else
599     {
600       GtkPageSetup *page_setup = gtk_page_setup_new ();
601       portal->setup = gtk_page_setup_to_gvariant (page_setup);
602       g_object_unref (page_setup);
603     }
604 
605   g_variant_ref_sink (portal->options);
606   g_variant_ref_sink (portal->settings);
607   g_variant_ref_sink (portal->setup);
608 
609   if (portal->parent != NULL &&
610       gtk_widget_is_visible (GTK_WIDGET (portal->parent)) &&
611       gtk_window_export_handle (portal->parent, window_handle_exported, portal))
612     return;
613 
614   g_dbus_proxy_call (portal->proxy,
615                      "PreparePrint",
616                      g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
617                                     "",
618                                     _("Print"), /* title */
619                                     portal->settings,
620                                     portal->setup,
621                                     portal->options),
622                      G_DBUS_CALL_FLAGS_NONE,
623                      -1,
624                      NULL,
625                      prepare_print_called,
626                      portal);
627 }
628 
629 GtkPrintOperationResult
gtk_print_operation_portal_run_dialog(GtkPrintOperation * op,gboolean show_dialog,GtkWindow * parent,gboolean * do_print)630 gtk_print_operation_portal_run_dialog (GtkPrintOperation *op,
631                                        gboolean           show_dialog,
632                                        GtkWindow         *parent,
633                                        gboolean          *do_print)
634 {
635   PortalData *portal;
636   GtkPrintOperationResult result;
637 
638   portal = create_portal_data (op, parent, NULL);
639   if (portal == NULL)
640     return GTK_PRINT_OPERATION_RESULT_ERROR;
641 
642   call_prepare_print (op, portal);
643 
644   gdk_threads_leave ();
645   g_main_loop_run (portal->loop);
646   gdk_threads_enter ();
647 
648   *do_print = portal->do_print;
649   result = portal->result;
650 
651   portal_data_free (portal);
652 
653   return result;
654 }
655 
656 void
gtk_print_operation_portal_run_dialog_async(GtkPrintOperation * op,gboolean show_dialog,GtkWindow * parent,GtkPrintOperationPrintFunc print_cb)657 gtk_print_operation_portal_run_dialog_async (GtkPrintOperation          *op,
658                                              gboolean                    show_dialog,
659                                              GtkWindow                  *parent,
660                                              GtkPrintOperationPrintFunc  print_cb)
661 {
662   PortalData *portal;
663 
664   portal = create_portal_data (op, parent, print_cb);
665   if (portal == NULL)
666     return;
667 
668   call_prepare_print (op, portal);
669 }
670 
671 void
gtk_print_operation_portal_launch_preview(GtkPrintOperation * op,cairo_surface_t * surface,GtkWindow * parent,const char * filename)672 gtk_print_operation_portal_launch_preview (GtkPrintOperation *op,
673                                            cairo_surface_t   *surface,
674                                            GtkWindow         *parent,
675                                            const char        *filename)
676 {
677   char *uri;
678 
679   uri = g_filename_to_uri (filename, NULL, NULL);
680   gtk_show_uri_on_window (parent, uri, GDK_CURRENT_TIME, NULL);
681   g_free (uri);
682 }
683