]> Pileus Git - ~andy/gtk/blob - gtk/gtkappchooserwidget.c
GtkAppChooserButton: Add a way to include the default app
[~andy/gtk] / gtk / gtkappchooserwidget.c
1 /*
2  * gtkappchooserwidget.c: an app-chooser widget
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 "gtkappchooserwidget.h"
30
31 #include "gtkintl.h"
32 #include "gtkmarshalers.h"
33 #include "gtkappchooserwidget.h"
34 #include "gtkappchooserprivate.h"
35 #include "gtkliststore.h"
36 #include "gtkcellrenderertext.h"
37 #include "gtkcellrendererpixbuf.h"
38 #include "gtktreeview.h"
39 #include "gtktreeselection.h"
40 #include "gtktreemodelsort.h"
41 #include "gtkorientable.h"
42 #include "gtkscrolledwindow.h"
43
44 #include <string.h>
45 #include <glib/gi18n-lib.h>
46 #include <gio/gio.h>
47
48 /**
49  * SECTION:gtkappchooserwidget
50  * @Title: GtkAppChooserWidget
51  * @Short_description: Application chooser widget that can be embedded in other widgets
52  *
53  * #GtkAppChooserWidget is a widget for selecting applications.
54  * It is the main building block for #GtkAppChooserDialog. Most
55  * applications only need to use the latter; but you can use
56  * this widget as part of a larger widget if you have special needs.
57  *
58  * #GtkAppChooserWidget offers detailed control over what applications
59  * are shown, using the
60  * #GtkAppChooserWidget:show-default,
61  * #GtkAppChooserWidget:show-recommended,
62  * #GtkAppChooserWidget:show-fallback,
63  * #GtkAppChooserWidget:show-other and
64  * #GtkAppChooserWidget:show-all
65  * properties. See the #GtkAppChooser documentation for more information
66  * about these groups of applications.
67  *
68  * To keep track of the selected application, use the
69  * #GtkAppChooserWidget::application-selected and #GtkAppChooserWidget::application-activated signals.
70  */
71
72 struct _GtkAppChooserWidgetPrivate {
73   GAppInfo *selected_app_info;
74
75   gchar *content_type;
76   gchar *default_text;
77
78   guint show_default     : 1;
79   guint show_recommended : 1;
80   guint show_fallback    : 1;
81   guint show_other       : 1;
82   guint show_all         : 1;
83
84   GtkWidget *program_list;
85   GtkListStore *program_list_store;
86
87   GtkCellRenderer *padding_renderer;
88 };
89
90 enum {
91   COLUMN_APP_INFO,
92   COLUMN_GICON,
93   COLUMN_NAME,
94   COLUMN_DESC,
95   COLUMN_EXEC,
96   COLUMN_DEFAULT,
97   COLUMN_HEADING,
98   COLUMN_HEADING_TEXT,
99   COLUMN_RECOMMENDED,
100   COLUMN_FALLBACK,
101   NUM_COLUMNS
102 };
103
104
105 enum {
106   PROP_CONTENT_TYPE = 1,
107   PROP_GFILE,
108   PROP_SHOW_DEFAULT,
109   PROP_SHOW_RECOMMENDED,
110   PROP_SHOW_FALLBACK,
111   PROP_SHOW_OTHER,
112   PROP_SHOW_ALL,
113   PROP_DEFAULT_TEXT,
114   N_PROPERTIES
115 };
116
117 enum {
118   SIGNAL_APPLICATION_SELECTED,
119   SIGNAL_APPLICATION_ACTIVATED,
120   SIGNAL_POPULATE_POPUP,
121   N_SIGNALS
122 };
123
124 static guint signals[N_SIGNALS] = { 0, };
125
126 static void gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface);
127
128 G_DEFINE_TYPE_WITH_CODE (GtkAppChooserWidget, gtk_app_chooser_widget, GTK_TYPE_BOX,
129                          G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER,
130                                                 gtk_app_chooser_widget_iface_init));
131
132 static void
133 refresh_and_emit_app_selected (GtkAppChooserWidget *self,
134                                GtkTreeSelection    *selection)
135 {
136   GtkTreeModel *model;
137   GtkTreeIter iter;
138   GAppInfo *info = NULL;
139   gboolean should_emit = FALSE;
140
141   if (gtk_tree_selection_get_selected (selection, &model, &iter))
142     gtk_tree_model_get (model, &iter, COLUMN_APP_INFO, &info, -1);
143
144   if (info == NULL)
145     return;
146
147   if (self->priv->selected_app_info)
148     {
149       if (!g_app_info_equal (self->priv->selected_app_info, info))
150         {
151           should_emit = TRUE;
152           g_object_unref (self->priv->selected_app_info);
153
154           self->priv->selected_app_info = info;
155         }
156     }
157   else
158     {
159       should_emit = TRUE;
160       self->priv->selected_app_info = info;
161     }
162
163   if (should_emit)
164     g_signal_emit (self, signals[SIGNAL_APPLICATION_SELECTED], 0,
165                    self->priv->selected_app_info);
166 }
167
168 static GAppInfo *
169 get_app_info_for_event (GtkAppChooserWidget *self,
170                         GdkEventButton      *event)
171 {
172   GtkTreePath *path = NULL;
173   GtkTreeIter iter;
174   GtkTreeModel *model;
175   GAppInfo *info;
176   gboolean recommended;
177
178   if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (self->priv->program_list),
179                                       event->x, event->y,
180                                       &path,
181                                       NULL, NULL, NULL))
182     return NULL;
183
184   model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
185
186   if (!gtk_tree_model_get_iter (model, &iter, path))
187     {
188       gtk_tree_path_free (path);
189       return NULL;
190     }
191
192   /* we only allow interaction with recommended applications */
193   gtk_tree_model_get (model, &iter,
194                       COLUMN_APP_INFO, &info,
195                       COLUMN_RECOMMENDED, &recommended,
196                       -1);
197
198   if (!recommended)
199     g_clear_object (&info);
200
201   return info;
202 }
203
204 static gboolean
205 widget_button_press_event_cb (GtkWidget      *widget,
206                               GdkEventButton *event,
207                               gpointer        user_data)
208 {
209   GtkAppChooserWidget *self = user_data;
210
211   if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
212     {
213       GAppInfo *info;
214       GtkWidget *menu;
215       GList *children;
216       gint n_children;
217
218       info = get_app_info_for_event (self, event);
219
220       if (info == NULL)
221         return FALSE;
222
223       menu = gtk_menu_new ();
224
225       g_signal_emit (self, signals[SIGNAL_POPULATE_POPUP], 0,
226                      menu, info);
227
228       g_object_unref (info);
229
230       /* see if clients added menu items to this container */
231       children = gtk_container_get_children (GTK_CONTAINER (menu));
232       n_children = g_list_length (children);
233
234       if (n_children > 0)
235         {
236           /* actually popup the menu */
237           gtk_menu_attach_to_widget (GTK_MENU (menu), self->priv->program_list, NULL);
238           gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
239                           event->button, event->time);
240         }
241
242       g_list_free (children);
243     }
244
245   return FALSE;
246 }
247
248 static gboolean
249 path_is_heading (GtkTreeView *view,
250                  GtkTreePath *path)
251 {
252   GtkTreeIter iter;
253   GtkTreeModel *model;
254   gboolean res;
255
256   model = gtk_tree_view_get_model (view);
257   gtk_tree_model_get_iter (model, &iter, path);
258   gtk_tree_model_get (model, &iter,
259                       COLUMN_HEADING, &res,
260                       -1);
261
262   return res;
263 }
264
265 static void
266 program_list_selection_activated (GtkTreeView       *view,
267                                   GtkTreePath       *path,
268                                   GtkTreeViewColumn *column,
269                                   gpointer           user_data)
270 {
271   GtkAppChooserWidget *self = user_data;
272   GtkTreeSelection *selection;
273
274   if (path_is_heading (view, path))
275     return;
276
277   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
278
279   refresh_and_emit_app_selected (self, selection);
280
281   g_signal_emit (self, signals[SIGNAL_APPLICATION_ACTIVATED], 0,
282                  self->priv->selected_app_info);
283 }
284
285 static gboolean
286 gtk_app_chooser_search_equal_func (GtkTreeModel *model,
287                                    gint          column,
288                                    const gchar  *key,
289                                    GtkTreeIter  *iter,
290                                    gpointer      user_data)
291 {
292   gchar *normalized_key;
293   gchar *name, *normalized_name;
294   gchar *path, *normalized_path;
295   gchar *basename, *normalized_basename;
296   gboolean ret;
297
298   if (key != NULL)
299     {
300       normalized_key = g_utf8_casefold (key, -1);
301       g_assert (normalized_key != NULL);
302
303       ret = TRUE;
304
305       gtk_tree_model_get (model, iter,
306                           COLUMN_NAME, &name,
307                           COLUMN_EXEC, &path,
308                           -1);
309
310       if (name != NULL)
311         {
312           normalized_name = g_utf8_casefold (name, -1);
313           g_assert (normalized_name != NULL);
314
315           if (strncmp (normalized_name, normalized_key, strlen (normalized_key)) == 0)
316             ret = FALSE;
317
318           g_free (normalized_name);
319         }
320
321       if (ret && path != NULL)
322         {
323           normalized_path = g_utf8_casefold (path, -1);
324           g_assert (normalized_path != NULL);
325
326           basename = g_path_get_basename (path);
327           g_assert (basename != NULL);
328
329           normalized_basename = g_utf8_casefold (basename, -1);
330           g_assert (normalized_basename != NULL);
331
332           if (strncmp (normalized_path, normalized_key, strlen (normalized_key)) == 0 ||
333               strncmp (normalized_basename, normalized_key, strlen (normalized_key)) == 0)
334             ret = FALSE;
335
336           g_free (basename);
337           g_free (normalized_basename);
338           g_free (normalized_path);
339         }
340
341       g_free (name);
342       g_free (path);
343       g_free (normalized_key);
344
345       return ret;
346     }
347   else
348     {
349       return TRUE;
350     }
351 }
352
353 static gint
354 gtk_app_chooser_sort_func (GtkTreeModel *model,
355                            GtkTreeIter  *a,
356                            GtkTreeIter  *b,
357                            gpointer      user_data)
358 {
359   gboolean a_recommended, b_recommended;
360   gboolean a_fallback, b_fallback;
361   gboolean a_heading, b_heading;
362   gboolean a_default, b_default;
363   gchar *a_name, *b_name, *a_casefold, *b_casefold;
364   gint retval = 0;
365
366   /* this returns:
367    * - <0 if a should show before b
368    * - =0 if a is the same as b
369    * - >0 if a should show after b
370    */
371
372   gtk_tree_model_get (model, a,
373                       COLUMN_NAME, &a_name,
374                       COLUMN_RECOMMENDED, &a_recommended,
375                       COLUMN_FALLBACK, &a_fallback,
376                       COLUMN_HEADING, &a_heading,
377                       COLUMN_DEFAULT, &a_default,
378                       -1);
379
380   gtk_tree_model_get (model, b,
381                       COLUMN_NAME, &b_name,
382                       COLUMN_RECOMMENDED, &b_recommended,
383                       COLUMN_FALLBACK, &b_fallback,
384                       COLUMN_HEADING, &b_heading,
385                       COLUMN_DEFAULT, &b_default,
386                       -1);
387
388   /* the default one always wins */
389   if (a_default && !b_default)
390     {
391       retval = -1;
392       goto out;
393     }
394
395   if (b_default && !a_default)
396     {
397       retval = 1;
398       goto out;
399     }
400   
401   /* the recommended one always wins */
402   if (a_recommended && !b_recommended)
403     {
404       retval = -1;
405       goto out;
406     }
407
408   if (b_recommended && !a_recommended)
409     {
410       retval = 1;
411       goto out;
412     }
413
414   /* the recommended one always wins */
415   if (a_fallback && !b_fallback)
416     {
417       retval = -1;
418       goto out;
419     }
420
421   if (b_fallback && !a_fallback)
422     {
423       retval = 1;
424       goto out;
425     }
426
427   /* they're both recommended/falback or not, so if one is a heading, wins */
428   if (a_heading)
429     {
430       retval = -1;
431       goto out;
432     }
433
434   if (b_heading)
435     {
436       retval = 1;
437       goto out;
438     }
439
440   /* don't order by name recommended applications, but use GLib's ordering */
441   if (!a_recommended)
442     {
443       a_casefold = a_name != NULL ?
444         g_utf8_casefold (a_name, -1) : NULL;
445       b_casefold = b_name != NULL ?
446         g_utf8_casefold (b_name, -1) : NULL;
447
448       retval = g_strcmp0 (a_casefold, b_casefold);
449
450       g_free (a_casefold);
451       g_free (b_casefold);
452     }
453
454  out:
455   g_free (a_name);
456   g_free (b_name);
457
458   return retval;
459 }
460
461 static void
462 padding_cell_renderer_func (GtkTreeViewColumn *column,
463                             GtkCellRenderer   *cell,
464                             GtkTreeModel      *model,
465                             GtkTreeIter       *iter,
466                             gpointer           user_data)
467 {
468   gboolean heading;
469
470   gtk_tree_model_get (model, iter,
471                       COLUMN_HEADING, &heading,
472                       -1);
473   if (heading)
474     g_object_set (cell,
475                   "visible", FALSE,
476                   "xpad", 0,
477                   "ypad", 0,
478                   NULL);
479   else
480     g_object_set (cell,
481                   "visible", TRUE,
482                   "xpad", 3,
483                   "ypad", 3,
484                   NULL);
485 }
486
487 static gboolean
488 gtk_app_chooser_selection_func (GtkTreeSelection *selection,
489                                 GtkTreeModel     *model,
490                                 GtkTreePath      *path,
491                                 gboolean          path_currently_selected,
492                                 gpointer          user_data)
493 {
494   GtkTreeIter iter;
495   gboolean heading;
496
497   gtk_tree_model_get_iter (model, &iter, path);
498   gtk_tree_model_get (model, &iter,
499                       COLUMN_HEADING, &heading,
500                       -1);
501
502   return !heading;
503 }
504
505 static gint
506 compare_apps_func (gconstpointer a,
507                    gconstpointer b)
508 {
509   return !g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b));
510 }
511
512 static gboolean
513 gtk_app_chooser_widget_add_section (GtkAppChooserWidget *self,
514                                     const gchar         *heading_title,
515                                     gboolean             show_headings,
516                                     gboolean             recommended,
517                                     gboolean             fallback,
518                                     GList               *applications,
519                                     GList               *exclude_apps)
520 {
521   gboolean heading_added, unref_icon;
522   GtkTreeIter iter;
523   GAppInfo *app;
524   gchar *app_string, *bold_string;
525   GIcon *icon;
526   GList *l;
527   gboolean retval;
528
529   retval = FALSE;
530   heading_added = FALSE;
531   bold_string = g_strdup_printf ("<b>%s</b>", heading_title);
532   
533   for (l = applications; l != NULL; l = l->next)
534     {
535       app = l->data;
536
537       if (!g_app_info_supports_uris (app) &&
538           !g_app_info_supports_files (app))
539         continue;
540
541       if (g_list_find_custom (exclude_apps, app,
542                               (GCompareFunc) compare_apps_func))
543         continue;
544
545       if (!heading_added && show_headings)
546         {
547           gtk_list_store_append (self->priv->program_list_store, &iter);
548           gtk_list_store_set (self->priv->program_list_store, &iter,
549                               COLUMN_HEADING_TEXT, bold_string,
550                               COLUMN_HEADING, TRUE,
551                               COLUMN_RECOMMENDED, recommended,
552                               COLUMN_FALLBACK, fallback,
553                               -1);
554
555           heading_added = TRUE;
556         }
557
558       app_string = g_markup_printf_escaped ("%s",
559                                             g_app_info_get_name (app) != NULL ?
560                                             g_app_info_get_name (app) : "");
561
562       icon = g_app_info_get_icon (app);
563       unref_icon = FALSE;
564       if (icon == NULL)
565         {
566           icon = g_themed_icon_new ("application-x-executable");
567           unref_icon = TRUE;
568         }
569
570       gtk_list_store_append (self->priv->program_list_store, &iter);
571       gtk_list_store_set (self->priv->program_list_store, &iter,
572                           COLUMN_APP_INFO, app,
573                           COLUMN_GICON, icon,
574                           COLUMN_NAME, g_app_info_get_name (app),
575                           COLUMN_DESC, app_string,
576                           COLUMN_EXEC, g_app_info_get_executable (app),
577                           COLUMN_HEADING, FALSE,
578                           COLUMN_RECOMMENDED, recommended,
579                           COLUMN_FALLBACK, fallback,
580                           -1);
581
582       retval = TRUE;
583
584       g_free (app_string);
585       if (unref_icon)
586         g_object_unref (icon);
587     }
588
589   g_free (bold_string);
590
591   return retval;
592 }
593
594
595 static void
596 gtk_app_chooser_add_default (GtkAppChooserWidget *self,
597                              GAppInfo            *app)
598 {
599   GtkTreeIter iter;
600   GIcon *icon;
601   gchar *string;
602   gboolean unref_icon;
603
604   unref_icon = FALSE;
605   string = g_strdup_printf ("<b>%s</b>", _("Default Application"));
606
607   gtk_list_store_append (self->priv->program_list_store, &iter);
608   gtk_list_store_set (self->priv->program_list_store, &iter,
609                       COLUMN_HEADING_TEXT, string,
610                       COLUMN_HEADING, TRUE,
611                       COLUMN_DEFAULT, TRUE,
612                       -1);
613
614   g_free (string);
615
616   string = g_markup_printf_escaped ("%s",
617                                     g_app_info_get_name (app) != NULL ?
618                                     g_app_info_get_name (app) : "");
619
620   icon = g_app_info_get_icon (app);
621   if (icon == NULL)
622     {
623       icon = g_themed_icon_new ("application-x-executable");
624       unref_icon = TRUE;
625     }
626
627   gtk_list_store_append (self->priv->program_list_store, &iter);
628   gtk_list_store_set (self->priv->program_list_store, &iter,
629                       COLUMN_APP_INFO, app,
630                       COLUMN_GICON, icon,
631                       COLUMN_NAME, g_app_info_get_name (app),
632                       COLUMN_DESC, string,
633                       COLUMN_EXEC, g_app_info_get_executable (app),
634                       COLUMN_HEADING, FALSE,
635                       COLUMN_DEFAULT, TRUE,
636                       -1);
637
638   g_free (string);
639
640   if (unref_icon)
641     g_object_unref (icon);
642 }
643
644 static void
645 add_no_applications_label (GtkAppChooserWidget *self)
646 {
647   gchar *text = NULL, *desc = NULL;
648   const gchar *string;
649   GtkTreeIter iter;
650
651   if (self->priv->default_text == NULL)
652     {
653       if (self->priv->content_type)
654         desc = g_content_type_get_description (self->priv->content_type);
655
656       string = text = g_strdup_printf (_("No applications available to open \"%s\""),
657                                        desc);
658       g_free (desc);
659     }
660   else
661     {
662       string = self->priv->default_text;
663     }
664
665   gtk_list_store_append (self->priv->program_list_store, &iter);
666   gtk_list_store_set (self->priv->program_list_store, &iter,
667                       COLUMN_HEADING_TEXT, string,
668                       COLUMN_HEADING, TRUE,
669                       -1);
670
671   g_free (text);
672 }
673
674 static void
675 gtk_app_chooser_widget_select_first (GtkAppChooserWidget *self)
676 {
677   GtkTreeIter iter;
678   GAppInfo *info = NULL;
679   GtkTreeModel *model;
680
681   model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
682   gtk_tree_model_get_iter_first (model, &iter);
683
684   while (info == NULL)
685     {
686       gtk_tree_model_get (model, &iter,
687                           COLUMN_APP_INFO, &info,
688                           -1);
689
690       if (info != NULL)
691         break;
692
693       if (!gtk_tree_model_iter_next (model, &iter))
694         break;
695     }
696
697   if (info != NULL)
698     {
699       GtkTreeSelection *selection;
700
701       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
702       gtk_tree_selection_select_iter (selection, &iter);
703
704       g_object_unref (info);
705     }
706 }
707
708 static void
709 gtk_app_chooser_widget_real_add_items (GtkAppChooserWidget *self)
710 {
711   GList *all_applications = NULL;
712   GList *recommended_apps = NULL;
713   GList *fallback_apps = NULL;
714   GList *exclude_apps = NULL;
715   GAppInfo *default_app = NULL;
716   gboolean show_headings;
717   gboolean apps_added;
718
719   show_headings = TRUE;
720   apps_added = FALSE;
721
722   if (self->priv->show_all)
723     show_headings = FALSE;
724
725   if (self->priv->show_default && self->priv->content_type)
726     {
727       default_app = g_app_info_get_default_for_type (self->priv->content_type, FALSE);
728
729       if (default_app != NULL)
730         {
731           gtk_app_chooser_add_default (self, default_app);
732           apps_added = TRUE;
733           exclude_apps = g_list_prepend (exclude_apps, default_app);
734         }
735     }
736
737 #ifndef G_OS_WIN32
738   if ((self->priv->content_type && self->priv->show_recommended) || self->priv->show_all)
739     {
740       if (self->priv->content_type)
741         recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type);
742
743       apps_added |= gtk_app_chooser_widget_add_section (self, _("Recommended Applications"),
744                                                         show_headings,
745                                                         !self->priv->show_all, /* mark as recommended */
746                                                         FALSE, /* mark as fallback */
747                                                         recommended_apps, exclude_apps);
748
749       exclude_apps = g_list_concat (exclude_apps,
750                                     g_list_copy (recommended_apps));
751     }
752
753   if ((self->priv->content_type && self->priv->show_fallback) || self->priv->show_all)
754     {
755       if (self->priv->content_type)
756         fallback_apps = g_app_info_get_fallback_for_type (self->priv->content_type);
757
758       apps_added |= gtk_app_chooser_widget_add_section (self, _("Related Applications"),
759                                                         show_headings,
760                                                         FALSE, /* mark as recommended */
761                                                         !self->priv->show_all, /* mark as fallback */
762                                                         fallback_apps, exclude_apps);
763       exclude_apps = g_list_concat (exclude_apps,
764                                     g_list_copy (fallback_apps));
765     }
766 #endif
767
768   if (self->priv->show_other || self->priv->show_all)
769     {
770       all_applications = g_app_info_get_all ();
771
772       apps_added |= gtk_app_chooser_widget_add_section (self, _("Other Applications"),
773                                                         show_headings,
774                                                         FALSE,
775                                                         FALSE,
776                                                         all_applications, exclude_apps);
777     }
778
779   if (!apps_added)
780     add_no_applications_label (self);
781
782   gtk_app_chooser_widget_select_first (self);
783
784   if (default_app != NULL)
785     g_object_unref (default_app);
786
787   if (all_applications != NULL)
788     g_list_free_full (all_applications, g_object_unref);
789
790   if (recommended_apps != NULL)
791     g_list_free_full (recommended_apps, g_object_unref);
792
793   if (fallback_apps != NULL)
794     g_list_free_full (fallback_apps, g_object_unref);
795
796   if (exclude_apps != NULL)
797     g_list_free (exclude_apps);
798 }
799
800 static void
801 gtk_app_chooser_widget_add_items (GtkAppChooserWidget *self)
802 {
803   GtkCellRenderer *renderer;
804   GtkTreeViewColumn *column;
805   GtkTreeModel *sort;
806
807   /* create list store */
808   self->priv->program_list_store = gtk_list_store_new (NUM_COLUMNS,
809                                                        G_TYPE_APP_INFO,
810                                                        G_TYPE_ICON,
811                                                        G_TYPE_STRING,
812                                                        G_TYPE_STRING,
813                                                        G_TYPE_STRING,
814                                                        G_TYPE_BOOLEAN,
815                                                        G_TYPE_BOOLEAN,
816                                                        G_TYPE_STRING,
817                                                        G_TYPE_BOOLEAN,
818                                                        G_TYPE_BOOLEAN);
819   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->priv->program_list_store));
820
821   gtk_tree_view_set_model (GTK_TREE_VIEW (self->priv->program_list),
822                            GTK_TREE_MODEL (sort));
823   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort),
824                                         COLUMN_NAME,
825                                         GTK_SORT_ASCENDING);
826   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort),
827                                    COLUMN_NAME,
828                                    gtk_app_chooser_sort_func,
829                                    self, NULL);
830   gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->priv->program_list),
831                                    COLUMN_NAME);
832   gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->priv->program_list),
833                                        gtk_app_chooser_search_equal_func,
834                                        NULL, NULL);
835
836   column = gtk_tree_view_column_new ();
837
838   /* initial padding */
839   renderer = gtk_cell_renderer_text_new ();
840   gtk_tree_view_column_pack_start (column, renderer, FALSE);
841   g_object_set (renderer,
842                 "xpad", self->priv->show_all ? 0 : 6,
843                 NULL);
844   self->priv->padding_renderer = renderer;
845
846   /* heading text renderer */
847   renderer = gtk_cell_renderer_text_new ();
848   gtk_tree_view_column_pack_start (column, renderer, FALSE);
849   gtk_tree_view_column_set_attributes (column, renderer,
850                                        "markup", COLUMN_HEADING_TEXT,
851                                        "visible", COLUMN_HEADING,
852                                        NULL);
853   g_object_set (renderer,
854                 "ypad", 6,
855                 "xpad", 0,
856                 "wrap-width", 350,
857                 "wrap-mode", PANGO_WRAP_WORD,
858                 NULL);
859
860   /* padding renderer for non-heading cells */
861   renderer = gtk_cell_renderer_text_new ();
862   gtk_tree_view_column_pack_start (column, renderer, FALSE);
863   gtk_tree_view_column_set_cell_data_func (column, renderer,
864                                            padding_cell_renderer_func,
865                                            NULL, NULL);
866
867   /* app icon renderer */
868   renderer = gtk_cell_renderer_pixbuf_new ();
869   gtk_tree_view_column_pack_start (column, renderer, FALSE);
870   gtk_tree_view_column_set_attributes (column, renderer,
871                                        "gicon", COLUMN_GICON,
872                                        NULL);
873   g_object_set (renderer,
874                 "stock-size", GTK_ICON_SIZE_MENU,
875                 NULL);
876
877   /* app name renderer */
878   renderer = gtk_cell_renderer_text_new ();
879   gtk_tree_view_column_pack_start (column, renderer, TRUE);
880   gtk_tree_view_column_set_attributes (column, renderer,
881                                        "markup", COLUMN_DESC,
882                                        NULL);
883   g_object_set (renderer,
884                 "ellipsize", PANGO_ELLIPSIZE_END,
885                 "ellipsize-set", TRUE,
886                 NULL);
887   
888   gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
889   gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->program_list), column);
890
891   /* populate the widget */
892   gtk_app_chooser_widget_real_add_items (self);
893 }
894
895 static void
896 gtk_app_chooser_widget_set_property (GObject      *object,
897                                      guint         property_id,
898                                      const GValue *value,
899                                      GParamSpec   *pspec)
900 {
901   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
902
903   switch (property_id)
904     {
905     case PROP_CONTENT_TYPE:
906       self->priv->content_type = g_value_dup_string (value);
907       break;
908     case PROP_SHOW_DEFAULT:
909       gtk_app_chooser_widget_set_show_default (self, g_value_get_boolean (value));
910       break;
911     case PROP_SHOW_RECOMMENDED:
912       gtk_app_chooser_widget_set_show_recommended (self, g_value_get_boolean (value));
913       break;
914     case PROP_SHOW_FALLBACK:
915       gtk_app_chooser_widget_set_show_fallback (self, g_value_get_boolean (value));
916       break;
917     case PROP_SHOW_OTHER:
918       gtk_app_chooser_widget_set_show_other (self, g_value_get_boolean (value));
919       break;
920     case PROP_SHOW_ALL:
921       gtk_app_chooser_widget_set_show_all (self, g_value_get_boolean (value));
922       break;
923     case PROP_DEFAULT_TEXT:
924       gtk_app_chooser_widget_set_default_text (self, g_value_get_string (value));
925       break;
926     default:
927       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
928       break;
929     }
930 }
931
932 static void
933 gtk_app_chooser_widget_get_property (GObject    *object,
934                                      guint       property_id,
935                                      GValue     *value,
936                                      GParamSpec *pspec)
937 {
938   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
939
940   switch (property_id)
941     {
942     case PROP_CONTENT_TYPE:
943       g_value_set_string (value, self->priv->content_type);
944       break;
945     case PROP_SHOW_DEFAULT:
946       g_value_set_boolean (value, self->priv->show_default);
947       break;
948     case PROP_SHOW_RECOMMENDED:
949       g_value_set_boolean (value, self->priv->show_recommended);
950       break;
951     case PROP_SHOW_FALLBACK:
952       g_value_set_boolean (value, self->priv->show_fallback);
953       break;
954     case PROP_SHOW_OTHER:
955       g_value_set_boolean (value, self->priv->show_other);
956       break;
957     case PROP_SHOW_ALL:
958       g_value_set_boolean (value, self->priv->show_all);
959       break;
960     case PROP_DEFAULT_TEXT:
961       g_value_set_string (value, self->priv->default_text);
962       break;
963     default:
964       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
965       break;
966     }
967 }
968
969 static void
970 gtk_app_chooser_widget_constructed (GObject *object)
971 {
972   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
973
974   if (G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed != NULL)
975     G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed (object);
976
977   gtk_app_chooser_widget_add_items (self);
978 }
979
980 static void
981 gtk_app_chooser_widget_finalize (GObject *object)
982 {
983   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
984
985   g_free (self->priv->content_type);
986   g_free (self->priv->default_text);
987
988   G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->finalize (object);
989 }
990
991 static void
992 gtk_app_chooser_widget_dispose (GObject *object)
993 {
994   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
995
996   if (self->priv->selected_app_info != NULL)
997     {
998       g_object_unref (self->priv->selected_app_info);
999       self->priv->selected_app_info = NULL;
1000     }
1001
1002   G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->dispose (object);
1003 }
1004
1005 static void
1006 gtk_app_chooser_widget_class_init (GtkAppChooserWidgetClass *klass)
1007 {
1008   GObjectClass *gobject_class;
1009   GParamSpec *pspec;
1010
1011   gobject_class = G_OBJECT_CLASS (klass);
1012   gobject_class->dispose = gtk_app_chooser_widget_dispose;
1013   gobject_class->finalize = gtk_app_chooser_widget_finalize;
1014   gobject_class->set_property = gtk_app_chooser_widget_set_property;
1015   gobject_class->get_property = gtk_app_chooser_widget_get_property;
1016   gobject_class->constructed = gtk_app_chooser_widget_constructed;
1017
1018   g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type");
1019
1020   /**
1021    * GtkAppChooserWidget:show-default:
1022    *
1023    * The ::show-default property determines whether the app chooser
1024    * should show the default handler for the content type in a
1025    * separate section. If %FALSE, the default handler is listed
1026    * among the recommended applications.
1027    */
1028   pspec = g_param_spec_boolean ("show-default",
1029                                 P_("Show default app"),
1030                                 P_("Whether the widget should show the default application"),
1031                                 FALSE,
1032                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1033   g_object_class_install_property (gobject_class, PROP_SHOW_DEFAULT, pspec);
1034
1035   /**
1036    * GtkAppChooserWidget:show-recommended:
1037    *
1038    * The #GtkAppChooserWidget:show-recommended property determines
1039    * whether the app chooser should show a section for recommended
1040    * applications. If %FALSE, the recommended applications are listed
1041    * among the other applications.
1042    */
1043   pspec = g_param_spec_boolean ("show-recommended",
1044                                 P_("Show recommended apps"),
1045                                 P_("Whether the widget should show recommended applications"),
1046                                 TRUE,
1047                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1048   g_object_class_install_property (gobject_class, PROP_SHOW_RECOMMENDED, pspec);
1049
1050   /**
1051    * GtkAppChooserWidget:show-fallback:
1052    *
1053    * The #GtkAppChooserWidget:show-fallback property determines whether
1054    * the app chooser should show a section for fallback applications.
1055    * If %FALSE, the fallback applications are listed among the other
1056    * applications.
1057    */
1058   pspec = g_param_spec_boolean ("show-fallback",
1059                                 P_("Show fallback apps"),
1060                                 P_("Whether the widget should show fallback applications"),
1061                                 FALSE,
1062                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1063   g_object_class_install_property (gobject_class, PROP_SHOW_FALLBACK, pspec);
1064
1065   /**
1066    * GtkAppChooserWidget:show-other:
1067    *
1068    * The #GtkAppChooserWidget:show-other property determines whether
1069    * the app chooser should show a section for other applications.
1070    */
1071   pspec = g_param_spec_boolean ("show-other",
1072                                 P_("Show other apps"),
1073                                 P_("Whether the widget should show other applications"),
1074                                 FALSE,
1075                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1076   g_object_class_install_property (gobject_class, PROP_SHOW_OTHER, pspec);
1077
1078   /**
1079    * GtkAppChooserWidget:show-all:
1080    *
1081    * If the #GtkAppChooserWidget:show-all property is %TRUE, the app
1082    * chooser presents all applications in a single list, without
1083    * subsections for default, recommended or related applications.
1084    */
1085   pspec = g_param_spec_boolean ("show-all",
1086                                 P_("Show all apps"),
1087                                 P_("Whether the widget should show all applications"),
1088                                 FALSE,
1089                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1090   g_object_class_install_property (gobject_class, PROP_SHOW_ALL, pspec);
1091
1092   /**
1093    * GtkAppChooserWidget:default-text:
1094    *
1095    * The #GtkAppChooserWidget:default-text property determines the text
1096    * that appears in the widget when there are no applications for the
1097    * given content type.
1098    * See also gtk_app_chooser_widget_set_default_text().
1099    */
1100   pspec = g_param_spec_string ("default-text",
1101                                P_("Widget's default text"),
1102                                P_("The default text appearing when there are no applications"),
1103                                NULL,
1104                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1105   g_object_class_install_property (gobject_class, PROP_DEFAULT_TEXT, pspec);
1106
1107   /**
1108    * GtkAppChooserWidget::application-selected:
1109    * @self: the object which received the signal
1110    * @application: the selected #GAppInfo
1111    *
1112    * Emitted when an application item is selected from the widget's list.
1113    */
1114   signals[SIGNAL_APPLICATION_SELECTED] =
1115     g_signal_new ("application-selected",
1116                   GTK_TYPE_APP_CHOOSER_WIDGET,
1117                   G_SIGNAL_RUN_FIRST,
1118                   G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_selected),
1119                   NULL, NULL,
1120                   _gtk_marshal_VOID__OBJECT,
1121                   G_TYPE_NONE,
1122                   1, G_TYPE_APP_INFO);
1123
1124   /**
1125    * GtkAppChooserWidget::application-activated:
1126    * @self: the object which received the signal
1127    * @application: the activated #GAppInfo
1128    *
1129    * Emitted when an application item is activated from the widget's list.
1130    *
1131    * This usually happens when the user double clicks an item, or an item
1132    * is selected and the user presses one of the keys Space, Shift+Space,
1133    * Return or Enter.
1134    */
1135   signals[SIGNAL_APPLICATION_ACTIVATED] =
1136     g_signal_new ("application-activated",
1137                   GTK_TYPE_APP_CHOOSER_WIDGET,
1138                   G_SIGNAL_RUN_FIRST,
1139                   G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_activated),
1140                   NULL, NULL,
1141                   _gtk_marshal_VOID__OBJECT,
1142                   G_TYPE_NONE,
1143                   1, G_TYPE_APP_INFO);
1144
1145   /**
1146    * GtkAppChooserWidget::populate-popup:
1147    * @self: the object which received the signal
1148    * @menu: the #GtkMenu to populate
1149    * @application: the current #GAppInfo
1150    *
1151    * Emitted when a context menu is about to popup over an application item.
1152    * Clients can insert menu items into the provided #GtkMenu object in the
1153    * callback of this signal; the context menu will be shown over the item
1154    * if at least one item has been added to the menu.
1155    */
1156   signals[SIGNAL_POPULATE_POPUP] =
1157     g_signal_new ("populate-popup",
1158                   GTK_TYPE_APP_CHOOSER_WIDGET,
1159                   G_SIGNAL_RUN_FIRST,
1160                   G_STRUCT_OFFSET (GtkAppChooserWidgetClass, populate_popup),
1161                   NULL, NULL,
1162                   _gtk_marshal_VOID__OBJECT_OBJECT,
1163                   G_TYPE_NONE,
1164                   2, GTK_TYPE_MENU, G_TYPE_APP_INFO);
1165
1166   g_type_class_add_private (klass, sizeof (GtkAppChooserWidgetPrivate));
1167 }
1168
1169 static void
1170 gtk_app_chooser_widget_init (GtkAppChooserWidget *self)
1171 {
1172   GtkWidget *scrolled_window;
1173   GtkTreeSelection *selection;
1174
1175   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_APP_CHOOSER_WIDGET,
1176                                             GtkAppChooserWidgetPrivate);
1177   gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
1178
1179   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1180   gtk_widget_set_size_request (scrolled_window, 400, 300);
1181   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
1182                                        GTK_SHADOW_IN);
1183   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1184                                   GTK_POLICY_NEVER,
1185                                   GTK_POLICY_AUTOMATIC);
1186   gtk_widget_show (scrolled_window);
1187
1188   self->priv->program_list = gtk_tree_view_new ();
1189   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->priv->program_list),
1190                                      FALSE);
1191   gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->program_list);
1192   gtk_box_pack_start (GTK_BOX (self), scrolled_window, TRUE, TRUE, 0);
1193   gtk_widget_show (self->priv->program_list);
1194
1195   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
1196   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1197   gtk_tree_selection_set_select_function (selection, gtk_app_chooser_selection_func,
1198                                           self, NULL);
1199   g_signal_connect_swapped (selection, "changed",
1200                             G_CALLBACK (refresh_and_emit_app_selected),
1201                             self);
1202   g_signal_connect (self->priv->program_list, "row-activated",
1203                     G_CALLBACK (program_list_selection_activated),
1204                     self);
1205   g_signal_connect (self->priv->program_list, "button-press-event",
1206                     G_CALLBACK (widget_button_press_event_cb),
1207                     self);
1208 }
1209
1210 static GAppInfo *
1211 gtk_app_chooser_widget_get_app_info (GtkAppChooser *object)
1212 {
1213   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1214
1215   if (self->priv->selected_app_info == NULL)
1216     return NULL;
1217
1218   return g_object_ref (self->priv->selected_app_info);
1219 }
1220
1221 static void
1222 gtk_app_chooser_widget_refresh (GtkAppChooser *object)
1223 {
1224   GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1225
1226   if (self->priv->program_list_store != NULL)
1227     {
1228       gtk_list_store_clear (self->priv->program_list_store);
1229
1230       /* don't add additional xpad if we don't have headings */
1231       g_object_set (self->priv->padding_renderer,
1232                     "visible", !self->priv->show_all,
1233                     NULL);
1234
1235       gtk_app_chooser_widget_real_add_items (self);
1236     }
1237 }
1238
1239 static void
1240 gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface)
1241 {
1242   iface->get_app_info = gtk_app_chooser_widget_get_app_info;
1243   iface->refresh = gtk_app_chooser_widget_refresh;
1244 }
1245
1246 /**
1247  * gtk_app_chooser_widget_new:
1248  * @content_type: the content type to show applications for
1249  *
1250  * Creates a new #GtkAppChooserWidget for applications
1251  * that can handle content of the given type.
1252  *
1253  * Returns: a newly created #GtkAppChooserWidget
1254  *
1255  * Since: 3.0
1256  */
1257 GtkWidget *
1258 gtk_app_chooser_widget_new (const gchar *content_type)
1259 {
1260   return g_object_new (GTK_TYPE_APP_CHOOSER_WIDGET,
1261                        "content-type", content_type,
1262                        NULL);
1263 }
1264
1265 /**
1266  * gtk_app_chooser_widget_set_show_default:
1267  * @self: a #GtkAppChooserWidget
1268  * @setting: the new value for #GtkAppChooserWidget:show-default
1269  *
1270  * Sets whether the app chooser should show the default handler
1271  * for the content type in a separate section.
1272  *
1273  * Since: 3.0
1274  */
1275 void
1276 gtk_app_chooser_widget_set_show_default (GtkAppChooserWidget *self,
1277                                          gboolean             setting)
1278 {
1279   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1280
1281   if (self->priv->show_default != setting)
1282     {
1283       self->priv->show_default = setting;
1284
1285       g_object_notify (G_OBJECT (self), "show-default");
1286
1287       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1288     }
1289 }
1290
1291 /**
1292  * gtk_app_chooser_widget_get_show_default:
1293  * @self: a #GtkAppChooserWidget
1294  *
1295  * Returns the current value of the #GtkAppChooserWidget:show-default
1296  * property.
1297  *
1298  * Returns: the value of #GtkAppChooserWidget:show-default
1299  *
1300  * Since: 3.0
1301  */
1302 gboolean
1303 gtk_app_chooser_widget_get_show_default (GtkAppChooserWidget *self)
1304 {
1305   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1306
1307   return self->priv->show_default;
1308 }
1309
1310 /**
1311  * gtk_app_chooser_widget_set_show_recommended:
1312  * @self: a #GtkAppChooserWidget
1313  * @setting: the new value for #GtkAppChooserWidget:show-recommended
1314  *
1315  * Sets whether the app chooser should show recommended applications
1316  * for the content type in a separate section.
1317  *
1318  * Since: 3.0
1319  */
1320 void
1321 gtk_app_chooser_widget_set_show_recommended (GtkAppChooserWidget *self,
1322                                              gboolean             setting)
1323 {
1324   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1325
1326   if (self->priv->show_recommended != setting)
1327     {
1328       self->priv->show_recommended = setting;
1329
1330       g_object_notify (G_OBJECT (self), "show-recommended");
1331
1332       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1333     }
1334 }
1335
1336 /**
1337  * gtk_app_chooser_widget_get_show_recommended:
1338  * @self: a #GtkAppChooserWidget
1339  *
1340  * Returns the current value of the #GtkAppChooserWidget:show-recommended
1341  * property.
1342  *
1343  * Returns: the value of #GtkAppChooserWidget:show-recommended
1344  *
1345  * Since: 3.0
1346  */
1347 gboolean
1348 gtk_app_chooser_widget_get_show_recommended (GtkAppChooserWidget *self)
1349 {
1350   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1351
1352   return self->priv->show_recommended;
1353 }
1354
1355 /**
1356  * gtk_app_chooser_widget_set_show_fallback:
1357  * @self: a #GtkAppChooserWidget
1358  * @setting: the new value for #GtkAppChooserWidget:show-fallback
1359  *
1360  * Sets whether the app chooser should show related applications
1361  * for the content type in a separate section.
1362  *
1363  * Since: 3.0
1364  */
1365 void
1366 gtk_app_chooser_widget_set_show_fallback (GtkAppChooserWidget *self,
1367                                           gboolean             setting)
1368 {
1369   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1370
1371   if (self->priv->show_fallback != setting)
1372     {
1373       self->priv->show_fallback = setting;
1374
1375       g_object_notify (G_OBJECT (self), "show-fallback");
1376
1377       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1378     }
1379 }
1380
1381 /**
1382  * gtk_app_chooser_widget_get_show_fallback:
1383  * @self: a #GtkAppChooserWidget
1384  *
1385  * Returns the current value of the #GtkAppChooserWidget:show-fallback
1386  * property.
1387  *
1388  * Returns: the value of #GtkAppChooserWidget:show-fallback
1389  *
1390  * Since: 3.0
1391  */
1392 gboolean
1393 gtk_app_chooser_widget_get_show_fallback (GtkAppChooserWidget *self)
1394 {
1395   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1396
1397   return self->priv->show_fallback;
1398 }
1399
1400 /**
1401  * gtk_app_chooser_widget_set_show_other:
1402  * @self: a #GtkAppChooserWidget
1403  * @setting: the new value for #GtkAppChooserWidget:show-other
1404  *
1405  * Sets whether the app chooser should show applications
1406  * which are unrelated to the content type.
1407  *
1408  * Since: 3.0
1409  */
1410 void
1411 gtk_app_chooser_widget_set_show_other (GtkAppChooserWidget *self,
1412                                        gboolean             setting)
1413 {
1414   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1415
1416   if (self->priv->show_other != setting)
1417     {
1418       self->priv->show_other = setting;
1419
1420       g_object_notify (G_OBJECT (self), "show-other");
1421
1422       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1423     }
1424 }
1425
1426 /**
1427  * gtk_app_chooser_widget_get_show_other:
1428  * @self: a #GtkAppChooserWidget
1429  *
1430  * Returns the current value of the #GtkAppChooserWidget:show-other
1431  * property.
1432  *
1433  * Returns: the value of #GtkAppChooserWidget:show-other
1434  *
1435  * Since: 3.0
1436  */
1437 gboolean
1438 gtk_app_chooser_widget_get_show_other (GtkAppChooserWidget *self)
1439 {
1440   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1441
1442   return self->priv->show_other;
1443 }
1444
1445 /**
1446  * gtk_app_chooser_widget_set_show_all:
1447  * @self: a #GtkAppChooserWidget
1448  * @setting: the new value for #GtkAppChooserWidget:show-all
1449  *
1450  * Sets whether the app chooser should show all applications
1451  * in a flat list.
1452  *
1453  * Since: 3.0
1454  */
1455 void
1456 gtk_app_chooser_widget_set_show_all (GtkAppChooserWidget *self,
1457                                      gboolean             setting)
1458 {
1459   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1460
1461   if (self->priv->show_all != setting)
1462     {
1463       self->priv->show_all = setting;
1464
1465       g_object_notify (G_OBJECT (self), "show-all");
1466
1467       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1468     }
1469 }
1470
1471 /**
1472  * gtk_app_chooser_widget_get_show_all:
1473  * @self: a #GtkAppChooserWidget
1474  *
1475  * Returns the current value of the #GtkAppChooserWidget:show-all
1476  * property.
1477  *
1478  * Returns: the value of #GtkAppChooserWidget:show-all
1479  *
1480  * Since: 3.0
1481  */
1482 gboolean
1483 gtk_app_chooser_widget_get_show_all (GtkAppChooserWidget *self)
1484 {
1485   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1486
1487   return self->priv->show_all;
1488 }
1489
1490 /**
1491  * gtk_app_chooser_widget_set_default_text:
1492  * @self: a #GtkAppChooserWidget
1493  * @text: the new value for #GtkAppChooserWidget:default-text
1494  *
1495  * Sets the text that is shown if there are not applications
1496  * that can handle the content type.
1497  */
1498 void
1499 gtk_app_chooser_widget_set_default_text (GtkAppChooserWidget *self,
1500                                          const gchar         *text)
1501 {
1502   g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1503
1504   if (g_strcmp0 (text, self->priv->default_text) != 0)
1505     {
1506       g_free (self->priv->default_text);
1507       self->priv->default_text = g_strdup (text);
1508
1509       g_object_notify (G_OBJECT (self), "default-text");
1510
1511       gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1512     }
1513 }
1514
1515 /**
1516  * gtk_app_chooser_widget_get_default_text:
1517  * @self: a #GtkAppChooserWidget
1518  *
1519  * Returns the text that is shown if there are not applications
1520  * that can handle the content type.
1521  *
1522  * Returns: the value of #GtkAppChooserWidget:default-text
1523  *
1524  * Since: 3.0
1525  */
1526 const gchar *
1527 gtk_app_chooser_widget_get_default_text (GtkAppChooserWidget *self)
1528 {
1529   g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), NULL);
1530
1531   return self->priv->default_text;
1532 }