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