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