]> Pileus Git - ~andy/gtk/blob - gtk/gtkappchooserdialog.c
Merge branch 'open-with-dialog'
[~andy/gtk] / gtk / gtkappchooserdialog.c
1 /*
2  * gtkappchooserdialog.c: an app-chooser dialog
3  *
4  * Copyright (C) 2004 Novell, Inc.
5  * Copyright (C) 2007, 2010 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Dave Camp <dave@novell.com>
23  *          Alexander Larsson <alexl@redhat.com>
24  *          Cosimo Cecchi <ccecchi@redhat.com>
25  */
26
27 #include "config.h"
28
29 #include "gtkappchooserdialog.h"
30
31 #include "gtkintl.h"
32 #include "gtkappchooser.h"
33 #include "gtkappchooseronline.h"
34 #include "gtkappchooserprivate.h"
35 #include "gtkappchooserprivate.h"
36
37 #include "gtkmessagedialog.h"
38 #include "gtklabel.h"
39 #include "gtkbbox.h"
40 #include "gtkbutton.h"
41 #include "gtkmenuitem.h"
42 #include "gtkstock.h"
43
44 #include <string.h>
45 #include <glib/gi18n-lib.h>
46 #include <gio/gio.h>
47
48 #define sure_string(s) ((const char *) ((s) != NULL ? (s) : ""))
49
50 struct _GtkAppChooserDialogPrivate {
51   char *content_type;
52   GFile *gfile;
53
54   GtkWidget *label;
55   GtkWidget *button;
56   GtkWidget *online_button;
57
58   GtkWidget *open_label;
59
60   GtkWidget *app_chooser_widget;
61   GtkWidget *show_more_button;
62
63   GtkAppChooserOnline *online;
64
65   gboolean show_more_clicked;
66 };
67
68 enum {
69   PROP_GFILE = 1,
70   PROP_CONTENT_TYPE,
71   N_PROPERTIES
72 };
73
74 static void gtk_app_chooser_dialog_iface_init (GtkAppChooserIface *iface);
75 G_DEFINE_TYPE_WITH_CODE (GtkAppChooserDialog, gtk_app_chooser_dialog, GTK_TYPE_DIALOG,
76                          G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER,
77                                                 gtk_app_chooser_dialog_iface_init));
78
79 static void
80 show_error_dialog (const gchar *primary,
81                    const gchar *secondary,
82                    GtkWindow   *parent)
83 {
84   GtkWidget *message_dialog;
85
86   message_dialog = gtk_message_dialog_new (parent, 0,
87                                            GTK_MESSAGE_ERROR,
88                                            GTK_BUTTONS_OK,
89                                            NULL);
90   g_object_set (message_dialog,
91                 "text", primary,
92                 "secondary-text", secondary,
93                 NULL);
94   gtk_dialog_set_default_response (GTK_DIALOG (message_dialog), GTK_RESPONSE_OK);
95
96   gtk_widget_show (message_dialog);
97
98   g_signal_connect (message_dialog, "response",
99                     G_CALLBACK (gtk_widget_destroy), NULL);
100 }
101
102 static void
103 search_for_mimetype_ready_cb (GObject      *source,
104                               GAsyncResult *res,
105                               gpointer      user_data)
106 {
107   GtkAppChooserOnline *online = GTK_APP_CHOOSER_ONLINE (source);
108   GtkAppChooserDialog *self = user_data;
109   GError *error = NULL;
110
111   gtk_app_chooser_online_search_for_mimetype_finish (online, res, &error);
112
113   if (error != NULL)
114     {
115       show_error_dialog (_("Failed to look for applications online"),
116                          error->message, GTK_WINDOW (self));
117       g_error_free (error);
118     }
119   else
120     {
121       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self->priv->app_chooser_widget));
122     }
123 }
124
125 static void
126 online_button_clicked_cb (GtkButton *b,
127                           gpointer   user_data)
128 {
129   GtkAppChooserDialog *self = user_data;
130
131   gtk_app_chooser_online_search_for_mimetype_async (self->priv->online,
132                                                     self->priv->content_type,
133                                                     GTK_WINDOW (self),
134                                                     search_for_mimetype_ready_cb,
135                                                     self);
136 }
137
138 static void
139 app_chooser_online_get_default_ready_cb (GObject *source,
140                                          GAsyncResult *res,
141                                          gpointer user_data)
142 {
143   GtkAppChooserDialog *self = user_data;
144
145   self->priv->online = gtk_app_chooser_online_get_default_finish (source, res);
146
147   if (self->priv->online != NULL)
148     {
149       GtkWidget *action_area;
150
151       action_area = gtk_dialog_get_action_area (GTK_DIALOG (self));
152       self->priv->online_button = gtk_button_new_with_label (_("Find applications online"));
153       gtk_box_pack_start (GTK_BOX (action_area), self->priv->online_button,
154                           FALSE, FALSE, 0);
155       gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area), self->priv->online_button,
156                                           TRUE);
157       g_signal_connect (self->priv->online_button, "clicked",
158                         G_CALLBACK (online_button_clicked_cb), self);
159
160       gtk_widget_show (self->priv->online_button);
161     }
162 }
163
164 static void
165 ensure_online_button (GtkAppChooserDialog *self)
166 {
167   gtk_app_chooser_online_get_default_async (app_chooser_online_get_default_ready_cb, self);
168 }
169
170 /* An application is valid if:
171  *
172  * 1) The file exists
173  * 2) The user has permissions to run the file
174  */
175 static gboolean
176 check_application (GtkAppChooserDialog  *self,
177                    GAppInfo            **app_out)
178 {
179   const char *command;
180   char *path = NULL;
181   char **argv = NULL;
182   int argc;
183   GError *error = NULL;
184   gint retval = TRUE;
185   GAppInfo *info;
186
187   command = NULL;
188
189   info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (self->priv->app_chooser_widget));
190   command = g_app_info_get_executable (info);
191
192   g_shell_parse_argv (command, &argc, &argv, &error);
193
194   if (error)
195     {
196       show_error_dialog (_("Could not run application"),
197                          error->message,
198                          GTK_WINDOW (self));
199       g_error_free (error);
200       retval = FALSE;
201       goto cleanup;
202     }
203
204   path = g_find_program_in_path (argv[0]);
205   if (!path)
206     {
207       char *error_message;
208
209       error_message = g_strdup_printf (_("Could not find '%s'"),
210                                        argv[0]);
211
212       show_error_dialog (_("Could not find application"),
213                          error_message,
214                          GTK_WINDOW (self));
215       g_free (error_message);
216       retval = FALSE;
217       goto cleanup;
218     }
219
220   *app_out = info;
221
222  cleanup:
223   g_strfreev (argv);
224   g_free (path);
225
226   return retval;
227 }
228
229 static void
230 add_or_find_application (GtkAppChooserDialog *self)
231 {
232   GAppInfo *app;
233
234   app = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (self));
235
236   /* we don't care about reporting errors here */
237   g_app_info_add_supports_type (app,
238                                 self->priv->content_type,
239                                 NULL);
240
241   g_object_unref (app);
242 }
243
244 static void
245 gtk_app_chooser_dialog_response (GtkDialog *dialog,
246                                  gint       response_id,
247                                  gpointer   user_data)
248 {
249   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (dialog);
250
251   switch (response_id)
252     {
253     case GTK_RESPONSE_OK:
254       add_or_find_application (self);
255       break;
256     default :
257       break;
258     }
259 }
260
261 static void
262 widget_application_selected_cb (GtkAppChooserWidget *widget,
263                                 GAppInfo            *app_info,
264                                 gpointer             user_data)
265 {
266   GtkAppChooserDialog *self = user_data;
267
268   gtk_widget_set_sensitive (self->priv->button, TRUE);
269 }
270
271 static void
272 widget_application_activated_cb (GtkAppChooserWidget *widget,
273                                  GAppInfo            *app_info,
274                                  gpointer             user_data)
275 {
276   GtkAppChooserDialog *self = user_data;
277
278   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
279 }
280
281 static char *
282 get_extension (const char *basename)
283 {
284   char *p;
285
286   p = strrchr (basename, '.');
287
288   if (p && *(p + 1) != '\0')
289     return g_strdup (p + 1);
290
291   return NULL;
292 }
293
294 static void
295 set_dialog_properties (GtkAppChooserDialog *self)
296 {
297   gchar *label;
298   gchar *name;
299   gchar *extension;
300   gchar *description;
301   gchar *default_text;
302   gchar *string;
303   PangoFontDescription *font_desc;
304
305   name = NULL;
306   extension = NULL;
307   label = NULL;
308   description = NULL;
309
310   if (self->priv->gfile != NULL)
311     {
312       name = g_file_get_basename (self->priv->gfile);
313       extension = get_extension (name);
314     }
315
316   description = g_content_type_get_description (self->priv->content_type);
317   gtk_window_set_title (GTK_WINDOW (self), "");
318
319   if (name != NULL)
320     {
321       /* Translators: %s is a filename */
322       label = g_strdup_printf (_("Select an application to open \"%s\""), name);
323       string = g_strdup_printf (_("No applications available to open \"%s\""),
324                                 name);
325     }
326   else
327     {
328       /* Translators: %s is a file type description */
329       label = g_strdup_printf (_("Select an application for \"%s\" files"),
330                                g_content_type_is_unknown (self->priv->content_type) ?
331                                self->priv->content_type : description);
332       string = g_strdup_printf (_("No applications available to open \"%s\" files"),
333                                g_content_type_is_unknown (self->priv->content_type) ?
334                                self->priv->content_type : description);
335     }
336
337   font_desc = pango_font_description_new ();
338   pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD);
339   gtk_widget_modify_font (self->priv->label, font_desc);
340   pango_font_description_free (font_desc);
341
342   gtk_label_set_markup (GTK_LABEL (self->priv->label), label);
343
344   default_text = g_strdup_printf ("<big><b>%s</b></big>\n%s",
345                                   string,
346                                   _("Click \"Show other applications\", for more options, or "
347                                     "\"Find applications online\" to install a new application"));
348
349   gtk_app_chooser_widget_set_default_text (GTK_APP_CHOOSER_WIDGET (self->priv->app_chooser_widget),
350                                            default_text);
351
352   g_free (label);
353   g_free (name);
354   g_free (extension);
355   g_free (description);
356   g_free (string);
357   g_free (default_text);
358 }
359
360 static void
361 show_more_button_clicked_cb (GtkButton *button,
362                              gpointer   user_data)
363 {
364   GtkAppChooserDialog *self = user_data;
365
366   g_object_set (self->priv->app_chooser_widget,
367                 "show-recommended", TRUE,
368                 "show-fallback", TRUE,
369                 "show-other", TRUE,
370                 NULL);
371
372   gtk_widget_hide (self->priv->show_more_button);
373   self->priv->show_more_clicked = TRUE;
374 }
375
376 static void
377 widget_notify_for_button_cb (GObject    *source,
378                              GParamSpec *pspec,
379                              gpointer    user_data)
380 {
381   GtkAppChooserDialog *self = user_data;
382   GtkAppChooserWidget *widget = GTK_APP_CHOOSER_WIDGET (source);
383   gboolean should_hide;
384
385   should_hide = gtk_app_chooser_widget_get_show_all (widget) ||
386     self->priv->show_more_clicked;
387
388   if (should_hide)
389     gtk_widget_hide (self->priv->show_more_button);
390 }
391
392 static void
393 forget_menu_item_activate_cb (GtkMenuItem *item,
394                               gpointer     user_data)
395 {
396   GtkAppChooserDialog *self = user_data;
397   GAppInfo *info;
398
399   info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (self));
400
401   if (info != NULL)
402     {
403       g_app_info_remove_supports_type (info, self->priv->content_type, NULL);
404
405       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
406
407       g_object_unref (info);
408     }
409 }
410
411 static GtkWidget *
412 build_forget_menu_item (GtkAppChooserDialog *self)
413 {
414   GtkWidget *retval;
415
416   retval = gtk_menu_item_new_with_label (_("Forget association"));
417   gtk_widget_show (retval);
418
419   g_signal_connect (retval, "activate",
420                     G_CALLBACK (forget_menu_item_activate_cb), self);
421
422   return retval;
423 }
424
425 static void
426 widget_populate_popup_cb (GtkAppChooserWidget *widget,
427                           GtkMenu             *menu,
428                           GAppInfo            *info,
429                           gpointer             user_data)
430 {
431   GtkAppChooserDialog *self = user_data;
432   GtkWidget *menu_item;
433
434   if (g_app_info_can_remove_supports_type (info))
435     {
436       menu_item = build_forget_menu_item (self);
437       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
438     }
439 }
440
441 static void
442 build_dialog_ui (GtkAppChooserDialog *self)
443 {
444   GtkWidget *vbox;
445   GtkWidget *vbox2;
446   GtkWidget *label;
447   GtkWidget *button, *w;
448
449   gtk_container_set_border_width (GTK_CONTAINER (self), 5);
450
451   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
452   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
453   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), vbox, TRUE, TRUE, 0);
454   gtk_widget_show (vbox);
455
456   vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
457   gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0);
458   gtk_widget_show (vbox2);
459
460   self->priv->label = gtk_label_new ("");
461   gtk_misc_set_alignment (GTK_MISC (self->priv->label), 0, 0.5);
462   gtk_label_set_line_wrap (GTK_LABEL (self->priv->label), TRUE);
463   gtk_box_pack_start (GTK_BOX (vbox2), self->priv->label,
464                       FALSE, FALSE, 0);
465   gtk_widget_show (self->priv->label);
466
467   self->priv->app_chooser_widget =
468     gtk_app_chooser_widget_new (self->priv->content_type);
469   gtk_box_pack_start (GTK_BOX (vbox2), self->priv->app_chooser_widget, TRUE, TRUE, 0);
470   gtk_widget_show (self->priv->app_chooser_widget);
471
472   g_signal_connect (self->priv->app_chooser_widget, "application-selected",
473                     G_CALLBACK (widget_application_selected_cb), self);
474   g_signal_connect (self->priv->app_chooser_widget, "application-activated",
475                     G_CALLBACK (widget_application_activated_cb), self);
476   g_signal_connect (self->priv->app_chooser_widget, "notify::show-all",
477                     G_CALLBACK (widget_notify_for_button_cb), self);
478   g_signal_connect (self->priv->app_chooser_widget, "populate-popup",
479                     G_CALLBACK (widget_populate_popup_cb), self);
480
481   button = gtk_button_new_with_label (_("Show other applications"));
482   self->priv->show_more_button = button;
483   w = gtk_image_new_from_stock (GTK_STOCK_ADD,
484                                 GTK_ICON_SIZE_BUTTON);
485   gtk_button_set_image (GTK_BUTTON (button), w);
486   gtk_box_pack_start (GTK_BOX (self->priv->app_chooser_widget), button, FALSE, FALSE, 6);
487   gtk_widget_show_all (button);
488
489   g_signal_connect (button, "clicked",
490                     G_CALLBACK (show_more_button_clicked_cb), self);
491
492   gtk_dialog_add_button (GTK_DIALOG (self),
493                          GTK_STOCK_CANCEL,
494                          GTK_RESPONSE_CANCEL);
495
496   /* Create a custom stock icon */
497   self->priv->button = gtk_button_new ();
498
499   label = gtk_label_new_with_mnemonic (_("_Open"));
500   gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (self->priv->button));
501   gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
502   gtk_widget_show (label);
503   self->priv->open_label = label;
504
505   gtk_container_add (GTK_CONTAINER (self->priv->button),
506                      self->priv->open_label);
507
508   gtk_widget_show (self->priv->button);
509   gtk_widget_set_can_default (self->priv->button, TRUE);
510
511   gtk_dialog_add_action_widget (GTK_DIALOG (self),
512                                 self->priv->button, GTK_RESPONSE_OK);
513
514   gtk_dialog_set_default_response (GTK_DIALOG (self),
515                                    GTK_RESPONSE_OK);
516 }
517
518 static void
519 set_gfile_and_content_type (GtkAppChooserDialog *self,
520                             GFile *file)
521 {
522   GFileInfo *info;
523
524   if (file == NULL)
525     return;
526
527   self->priv->gfile = g_object_ref (file);
528
529   info = g_file_query_info (self->priv->gfile,
530                             G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
531                             0, NULL, NULL);
532   self->priv->content_type = g_strdup (g_file_info_get_content_type (info));
533
534   g_object_unref (info);
535 }
536
537 static GAppInfo *
538 gtk_app_chooser_dialog_get_app_info (GtkAppChooser *object)
539 {
540   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
541   GAppInfo *app = NULL;
542
543   if (!check_application (self, &app))
544     return NULL;
545
546   return app;
547 }
548
549 static void
550 gtk_app_chooser_dialog_refresh (GtkAppChooser *object)
551 {
552   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
553
554   gtk_app_chooser_refresh (GTK_APP_CHOOSER (self->priv->app_chooser_widget));
555 }
556
557 static void
558 gtk_app_chooser_dialog_constructed (GObject *object)
559 {
560   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
561
562   g_assert (self->priv->content_type != NULL ||
563             self->priv->gfile != NULL);
564
565   if (G_OBJECT_CLASS (gtk_app_chooser_dialog_parent_class)->constructed != NULL)
566     G_OBJECT_CLASS (gtk_app_chooser_dialog_parent_class)->constructed (object);
567
568   build_dialog_ui (self);
569   set_dialog_properties (self);
570   ensure_online_button (self);
571 }
572
573 static void
574 gtk_app_chooser_dialog_dispose (GObject *object)
575 {
576   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
577   
578   g_clear_object (&self->priv->gfile);
579   g_clear_object (&self->priv->online);
580
581   G_OBJECT_CLASS (gtk_app_chooser_dialog_parent_class)->dispose (object);
582 }
583
584 static void
585 gtk_app_chooser_dialog_finalize (GObject *object)
586 {
587   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
588
589   g_free (self->priv->content_type);
590
591   G_OBJECT_CLASS (gtk_app_chooser_dialog_parent_class)->finalize (object);
592 }
593
594 static void
595 gtk_app_chooser_dialog_set_property (GObject      *object,
596                                      guint         property_id,
597                                      const GValue *value,
598                                      GParamSpec   *pspec)
599 {
600   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
601
602   switch (property_id)
603     {
604     case PROP_GFILE:
605       set_gfile_and_content_type (self, g_value_get_object (value));
606       break;
607     case PROP_CONTENT_TYPE:
608       /* don't try to override a value previously set with the GFile */
609       if (self->priv->content_type == NULL)
610         self->priv->content_type = g_value_dup_string (value);
611       break;
612     default:
613       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
614       break;
615     }
616 }
617
618 static void
619 gtk_app_chooser_dialog_get_property (GObject    *object,
620                                      guint       property_id,
621                                      GValue     *value,
622                                      GParamSpec *pspec)
623 {
624   GtkAppChooserDialog *self = GTK_APP_CHOOSER_DIALOG (object);
625
626   switch (property_id)
627     {
628     case PROP_GFILE:
629       if (self->priv->gfile != NULL)
630         g_value_set_object (value, self->priv->gfile);
631       break;
632     case PROP_CONTENT_TYPE:
633       g_value_set_string (value, self->priv->content_type);
634       break;
635     default:
636       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
637       break;
638     }
639 }
640
641 static void
642 gtk_app_chooser_dialog_iface_init (GtkAppChooserIface *iface)
643 {
644   iface->get_app_info = gtk_app_chooser_dialog_get_app_info;
645   iface->refresh = gtk_app_chooser_dialog_refresh;
646 }
647
648 static void
649 gtk_app_chooser_dialog_class_init (GtkAppChooserDialogClass *klass)
650 {
651   GObjectClass *gobject_class;
652   GParamSpec *pspec;
653
654   gobject_class = G_OBJECT_CLASS (klass);
655   gobject_class->dispose = gtk_app_chooser_dialog_dispose;
656   gobject_class->finalize = gtk_app_chooser_dialog_finalize;
657   gobject_class->set_property = gtk_app_chooser_dialog_set_property;
658   gobject_class->get_property = gtk_app_chooser_dialog_get_property;
659   gobject_class->constructed = gtk_app_chooser_dialog_constructed;
660
661   g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type");
662
663   /**
664    * GtkAppChooserDialog:gfile:
665    *
666    * The GFile used by the #GtkAppChooserDialog.
667    * The dialog's #GtkAppChooserWidget content type will be guessed from the
668    * file, if present.
669    */
670   pspec = g_param_spec_object ("gfile",
671                                P_("GFile"),
672                                P_("The GFile used by the app chooser dialog"),
673                                G_TYPE_FILE,
674                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
675                                G_PARAM_STATIC_STRINGS);
676   g_object_class_install_property (gobject_class, PROP_GFILE, pspec);
677
678   g_type_class_add_private (klass, sizeof (GtkAppChooserDialogPrivate));
679 }
680
681 static void
682 gtk_app_chooser_dialog_init (GtkAppChooserDialog *self)
683 {
684   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_APP_CHOOSER_DIALOG,
685                                             GtkAppChooserDialogPrivate);
686
687   /* we can't override the class signal handler here, as it's a RUN_LAST;
688    * we want our signal handler instead to be executed before any user code.
689    */
690   g_signal_connect (self, "response",
691                     G_CALLBACK (gtk_app_chooser_dialog_response), NULL);
692 }
693
694 static void
695 set_parent_and_flags (GtkWidget      *dialog,
696                       GtkWindow      *parent,
697                       GtkDialogFlags  flags)
698 {
699   if (parent != NULL)
700     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
701
702   if (flags & GTK_DIALOG_MODAL)
703     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
704
705   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
706     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
707 }
708
709 /**
710  * gtk_app_chooser_dialog_new:
711  * @parent: (allow-none): a #GtkWindow, or %NULL
712  * @flags: flags for this dialog
713  * @file: a #GFile
714  *
715  * Creates a new #GtkAppChooserDialog for the provided #GFile,
716  * to allow the user to select an application for it.
717  *
718  * Returns: a newly created #GtkAppChooserDialog
719  *
720  * Since: 3.0
721  **/
722 GtkWidget *
723 gtk_app_chooser_dialog_new (GtkWindow      *parent,
724                             GtkDialogFlags  flags,
725                             GFile          *file)
726 {
727   GtkWidget *retval;
728
729   g_return_val_if_fail (G_IS_FILE (file), NULL);
730
731   retval = g_object_new (GTK_TYPE_APP_CHOOSER_DIALOG,
732                          "gfile", file,
733                          NULL);
734
735   set_parent_and_flags (retval, parent, flags);
736
737   return retval;
738 }
739
740 /**
741  * gtk_app_chooser_dialog_new_for_content_type:
742  * @parent: (allow-none): a #GtkWindow, or %NULL
743  * @flags: flags for this dialog
744  * @content_type: a content type string
745  *
746  * Creates a new #GtkAppChooserDialog for the provided content type,
747  * to allow the user to select an application for it.
748  *
749  * Returns: a newly created #GtkAppChooserDialog
750  *
751  * Since: 3.0
752  **/
753 GtkWidget *
754 gtk_app_chooser_dialog_new_for_content_type (GtkWindow      *parent,
755                                              GtkDialogFlags  flags,
756                                              const gchar    *content_type)
757 {
758   GtkWidget *retval;
759
760   g_return_val_if_fail (content_type != NULL, NULL);
761
762   retval = g_object_new (GTK_TYPE_APP_CHOOSER_DIALOG,
763                          "content-type", content_type,
764                          NULL);
765
766   set_parent_and_flags (retval, parent, flags);
767
768   return retval;
769 }
770
771 /**
772  * gtk_app_chooser_dialog_get_widget:
773  * @self: a #GtkAppChooserDialog
774  *
775  * Returns the #GtkAppChooserWidget of this dialog.
776  *
777  * Returns: (transfer none): the #GtkAppChooserWidget of @self
778  *
779  * Since: 3.0
780  */
781 GtkWidget *
782 gtk_app_chooser_dialog_get_widget (GtkAppChooserDialog *self)
783 {
784   g_return_val_if_fail (GTK_IS_APP_CHOOSER_DIALOG (self), NULL);
785
786   return self->priv->app_chooser_widget;
787 }