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