2 * gtkappchooserwidget.c: an app-chooser widget
4 * Copyright (C) 2004 Novell, Inc.
5 * Copyright (C) 2007, 2010 Red Hat, Inc.
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.
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.
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB. If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Authors: Dave Camp <dave@novell.com>
23 * Alexander Larsson <alexl@redhat.com>
24 * Cosimo Cecchi <ccecchi@redhat.com>
29 #include "gtkappchooserwidget.h"
32 #include "gtkmarshalers.h"
33 #include "gtkappchooserwidget.h"
34 #include "gtkappchooserprivate.h"
35 #include "gtkliststore.h"
36 #include "gtkcellrenderertext.h"
37 #include "gtkcellrendererpixbuf.h"
38 #include "gtktreeview.h"
39 #include "gtktreeselection.h"
40 #include "gtktreemodelsort.h"
41 #include "gtkorientable.h"
42 #include "gtkscrolledwindow.h"
45 #include <glib/gi18n-lib.h>
49 * SECTION:gtkappchooserwidget
50 * @Title: GtkAppChooserWidget
51 * @Short_description: Application chooser widget that can be embedded in other widgets
53 * #GtkAppChooserWidget is a widget for selecting applications.
54 * It is the main building block for #GtkAppChooserDialog. Most
55 * applications only need to use the latter; but you can use
56 * this widget as part of a larger widget if you have special needs.
58 * #GtkAppChooserWidget offers detailed control over what applications
59 * are shown, using the
60 * #GtkAppChooserWidget:show-default,
61 * #GtkAppChooserWidget:show-recommended,
62 * #GtkAppChooserWidget:show-fallback,
63 * #GtkAppChooserWidget:show-other and
64 * #GtkAppChooserWidget:show-all
65 * properties. See the #GtkAppChooser documentation for more information
66 * about these groups of applications.
68 * To keep track of the selected application, use the
69 * #GtkAppChooserWidget::application-selected and #GtkAppChooserWidget::application-activated signals.
72 struct _GtkAppChooserWidgetPrivate {
73 GAppInfo *selected_app_info;
78 guint show_default : 1;
79 guint show_recommended : 1;
80 guint show_fallback : 1;
84 GtkWidget *program_list;
85 GtkListStore *program_list_store;
87 GtkCellRenderer *padding_renderer;
106 PROP_CONTENT_TYPE = 1,
109 PROP_SHOW_RECOMMENDED,
118 SIGNAL_APPLICATION_SELECTED,
119 SIGNAL_APPLICATION_ACTIVATED,
120 SIGNAL_POPULATE_POPUP,
124 static guint signals[N_SIGNALS] = { 0, };
126 static void gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface);
128 G_DEFINE_TYPE_WITH_CODE (GtkAppChooserWidget, gtk_app_chooser_widget, GTK_TYPE_BOX,
129 G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER,
130 gtk_app_chooser_widget_iface_init));
133 refresh_and_emit_app_selected (GtkAppChooserWidget *self,
134 GtkTreeSelection *selection)
138 GAppInfo *info = NULL;
139 gboolean should_emit = FALSE;
141 if (gtk_tree_selection_get_selected (selection, &model, &iter))
142 gtk_tree_model_get (model, &iter, COLUMN_APP_INFO, &info, -1);
147 if (self->priv->selected_app_info)
149 if (!g_app_info_equal (self->priv->selected_app_info, info))
152 g_object_unref (self->priv->selected_app_info);
154 self->priv->selected_app_info = info;
160 self->priv->selected_app_info = info;
164 g_signal_emit (self, signals[SIGNAL_APPLICATION_SELECTED], 0,
165 self->priv->selected_app_info);
169 get_app_info_for_event (GtkAppChooserWidget *self,
170 GdkEventButton *event)
172 GtkTreePath *path = NULL;
176 gboolean recommended;
178 if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (self->priv->program_list),
184 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
186 if (!gtk_tree_model_get_iter (model, &iter, path))
188 gtk_tree_path_free (path);
192 /* we only allow interaction with recommended applications */
193 gtk_tree_model_get (model, &iter,
194 COLUMN_APP_INFO, &info,
195 COLUMN_RECOMMENDED, &recommended,
199 g_clear_object (&info);
205 widget_button_press_event_cb (GtkWidget *widget,
206 GdkEventButton *event,
209 GtkAppChooserWidget *self = user_data;
211 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
218 info = get_app_info_for_event (self, event);
223 menu = gtk_menu_new ();
225 g_signal_emit (self, signals[SIGNAL_POPULATE_POPUP], 0,
228 g_object_unref (info);
230 /* see if clients added menu items to this container */
231 children = gtk_container_get_children (GTK_CONTAINER (menu));
232 n_children = g_list_length (children);
236 /* actually popup the menu */
237 gtk_menu_attach_to_widget (GTK_MENU (menu), self->priv->program_list, NULL);
238 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
239 event->button, event->time);
242 g_list_free (children);
249 path_is_heading (GtkTreeView *view,
256 model = gtk_tree_view_get_model (view);
257 gtk_tree_model_get_iter (model, &iter, path);
258 gtk_tree_model_get (model, &iter,
259 COLUMN_HEADING, &res,
266 program_list_selection_activated (GtkTreeView *view,
268 GtkTreeViewColumn *column,
271 GtkAppChooserWidget *self = user_data;
272 GtkTreeSelection *selection;
274 if (path_is_heading (view, path))
277 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
279 refresh_and_emit_app_selected (self, selection);
281 g_signal_emit (self, signals[SIGNAL_APPLICATION_ACTIVATED], 0,
282 self->priv->selected_app_info);
286 gtk_app_chooser_search_equal_func (GtkTreeModel *model,
292 gchar *normalized_key;
293 gchar *name, *normalized_name;
294 gchar *path, *normalized_path;
295 gchar *basename, *normalized_basename;
300 normalized_key = g_utf8_casefold (key, -1);
301 g_assert (normalized_key != NULL);
305 gtk_tree_model_get (model, iter,
312 normalized_name = g_utf8_casefold (name, -1);
313 g_assert (normalized_name != NULL);
315 if (strncmp (normalized_name, normalized_key, strlen (normalized_key)) == 0)
318 g_free (normalized_name);
321 if (ret && path != NULL)
323 normalized_path = g_utf8_casefold (path, -1);
324 g_assert (normalized_path != NULL);
326 basename = g_path_get_basename (path);
327 g_assert (basename != NULL);
329 normalized_basename = g_utf8_casefold (basename, -1);
330 g_assert (normalized_basename != NULL);
332 if (strncmp (normalized_path, normalized_key, strlen (normalized_key)) == 0 ||
333 strncmp (normalized_basename, normalized_key, strlen (normalized_key)) == 0)
337 g_free (normalized_basename);
338 g_free (normalized_path);
343 g_free (normalized_key);
354 gtk_app_chooser_sort_func (GtkTreeModel *model,
359 gboolean a_recommended, b_recommended;
360 gboolean a_fallback, b_fallback;
361 gboolean a_heading, b_heading;
362 gboolean a_default, b_default;
363 gchar *a_name, *b_name, *a_casefold, *b_casefold;
367 * - <0 if a should show before b
368 * - =0 if a is the same as b
369 * - >0 if a should show after b
372 gtk_tree_model_get (model, a,
373 COLUMN_NAME, &a_name,
374 COLUMN_RECOMMENDED, &a_recommended,
375 COLUMN_FALLBACK, &a_fallback,
376 COLUMN_HEADING, &a_heading,
377 COLUMN_DEFAULT, &a_default,
380 gtk_tree_model_get (model, b,
381 COLUMN_NAME, &b_name,
382 COLUMN_RECOMMENDED, &b_recommended,
383 COLUMN_FALLBACK, &b_fallback,
384 COLUMN_HEADING, &b_heading,
385 COLUMN_DEFAULT, &b_default,
388 /* the default one always wins */
389 if (a_default && !b_default)
395 if (b_default && !a_default)
401 /* the recommended one always wins */
402 if (a_recommended && !b_recommended)
408 if (b_recommended && !a_recommended)
414 /* the recommended one always wins */
415 if (a_fallback && !b_fallback)
421 if (b_fallback && !a_fallback)
427 /* they're both recommended/falback or not, so if one is a heading, wins */
440 /* don't order by name recommended applications, but use GLib's ordering */
443 a_casefold = a_name != NULL ?
444 g_utf8_casefold (a_name, -1) : NULL;
445 b_casefold = b_name != NULL ?
446 g_utf8_casefold (b_name, -1) : NULL;
448 retval = g_strcmp0 (a_casefold, b_casefold);
462 padding_cell_renderer_func (GtkTreeViewColumn *column,
463 GtkCellRenderer *cell,
470 gtk_tree_model_get (model, iter,
471 COLUMN_HEADING, &heading,
488 gtk_app_chooser_selection_func (GtkTreeSelection *selection,
491 gboolean path_currently_selected,
497 gtk_tree_model_get_iter (model, &iter, path);
498 gtk_tree_model_get (model, &iter,
499 COLUMN_HEADING, &heading,
506 compare_apps_func (gconstpointer a,
509 return !g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b));
513 gtk_app_chooser_widget_add_section (GtkAppChooserWidget *self,
514 const gchar *heading_title,
515 gboolean show_headings,
516 gboolean recommended,
521 gboolean heading_added, unref_icon;
524 gchar *app_string, *bold_string;
530 heading_added = FALSE;
531 bold_string = g_strdup_printf ("<b>%s</b>", heading_title);
533 for (l = applications; l != NULL; l = l->next)
537 if (!g_app_info_supports_uris (app) &&
538 !g_app_info_supports_files (app))
541 if (g_list_find_custom (exclude_apps, app,
542 (GCompareFunc) compare_apps_func))
545 if (!heading_added && show_headings)
547 gtk_list_store_append (self->priv->program_list_store, &iter);
548 gtk_list_store_set (self->priv->program_list_store, &iter,
549 COLUMN_HEADING_TEXT, bold_string,
550 COLUMN_HEADING, TRUE,
551 COLUMN_RECOMMENDED, recommended,
552 COLUMN_FALLBACK, fallback,
555 heading_added = TRUE;
558 app_string = g_markup_printf_escaped ("%s",
559 g_app_info_get_name (app) != NULL ?
560 g_app_info_get_name (app) : "");
562 icon = g_app_info_get_icon (app);
566 icon = g_themed_icon_new ("application-x-executable");
570 gtk_list_store_append (self->priv->program_list_store, &iter);
571 gtk_list_store_set (self->priv->program_list_store, &iter,
572 COLUMN_APP_INFO, app,
574 COLUMN_NAME, g_app_info_get_name (app),
575 COLUMN_DESC, app_string,
576 COLUMN_EXEC, g_app_info_get_executable (app),
577 COLUMN_HEADING, FALSE,
578 COLUMN_RECOMMENDED, recommended,
579 COLUMN_FALLBACK, fallback,
586 g_object_unref (icon);
589 g_free (bold_string);
596 gtk_app_chooser_add_default (GtkAppChooserWidget *self,
605 string = g_strdup_printf ("<b>%s</b>", _("Default Application"));
607 gtk_list_store_append (self->priv->program_list_store, &iter);
608 gtk_list_store_set (self->priv->program_list_store, &iter,
609 COLUMN_HEADING_TEXT, string,
610 COLUMN_HEADING, TRUE,
611 COLUMN_DEFAULT, TRUE,
616 string = g_markup_printf_escaped ("%s",
617 g_app_info_get_name (app) != NULL ?
618 g_app_info_get_name (app) : "");
620 icon = g_app_info_get_icon (app);
623 icon = g_themed_icon_new ("application-x-executable");
627 gtk_list_store_append (self->priv->program_list_store, &iter);
628 gtk_list_store_set (self->priv->program_list_store, &iter,
629 COLUMN_APP_INFO, app,
631 COLUMN_NAME, g_app_info_get_name (app),
633 COLUMN_EXEC, g_app_info_get_executable (app),
634 COLUMN_HEADING, FALSE,
635 COLUMN_DEFAULT, TRUE,
641 g_object_unref (icon);
645 add_no_applications_label (GtkAppChooserWidget *self)
647 gchar *text = NULL, *desc = NULL;
651 if (self->priv->default_text == NULL)
653 if (self->priv->content_type)
654 desc = g_content_type_get_description (self->priv->content_type);
656 string = text = g_strdup_printf (_("No applications available to open \"%s\""),
662 string = self->priv->default_text;
665 gtk_list_store_append (self->priv->program_list_store, &iter);
666 gtk_list_store_set (self->priv->program_list_store, &iter,
667 COLUMN_HEADING_TEXT, string,
668 COLUMN_HEADING, TRUE,
675 gtk_app_chooser_widget_select_first (GtkAppChooserWidget *self)
678 GAppInfo *info = NULL;
681 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list));
682 gtk_tree_model_get_iter_first (model, &iter);
686 gtk_tree_model_get (model, &iter,
687 COLUMN_APP_INFO, &info,
693 if (!gtk_tree_model_iter_next (model, &iter))
699 GtkTreeSelection *selection;
701 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
702 gtk_tree_selection_select_iter (selection, &iter);
704 g_object_unref (info);
709 gtk_app_chooser_widget_real_add_items (GtkAppChooserWidget *self)
711 GList *all_applications = NULL;
712 GList *recommended_apps = NULL;
713 GList *fallback_apps = NULL;
714 GList *exclude_apps = NULL;
715 GAppInfo *default_app = NULL;
716 gboolean show_headings;
719 show_headings = TRUE;
722 if (self->priv->show_all)
723 show_headings = FALSE;
725 if (self->priv->show_default && self->priv->content_type)
727 default_app = g_app_info_get_default_for_type (self->priv->content_type, FALSE);
729 if (default_app != NULL)
731 gtk_app_chooser_add_default (self, default_app);
733 exclude_apps = g_list_prepend (exclude_apps, default_app);
738 if ((self->priv->content_type && self->priv->show_recommended) || self->priv->show_all)
740 if (self->priv->content_type)
741 recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type);
743 apps_added |= gtk_app_chooser_widget_add_section (self, _("Recommended Applications"),
745 !self->priv->show_all, /* mark as recommended */
746 FALSE, /* mark as fallback */
747 recommended_apps, exclude_apps);
749 exclude_apps = g_list_concat (exclude_apps,
750 g_list_copy (recommended_apps));
753 if ((self->priv->content_type && self->priv->show_fallback) || self->priv->show_all)
755 if (self->priv->content_type)
756 fallback_apps = g_app_info_get_fallback_for_type (self->priv->content_type);
758 apps_added |= gtk_app_chooser_widget_add_section (self, _("Related Applications"),
760 FALSE, /* mark as recommended */
761 !self->priv->show_all, /* mark as fallback */
762 fallback_apps, exclude_apps);
763 exclude_apps = g_list_concat (exclude_apps,
764 g_list_copy (fallback_apps));
768 if (self->priv->show_other || self->priv->show_all)
770 all_applications = g_app_info_get_all ();
772 apps_added |= gtk_app_chooser_widget_add_section (self, _("Other Applications"),
776 all_applications, exclude_apps);
780 add_no_applications_label (self);
782 gtk_app_chooser_widget_select_first (self);
784 if (default_app != NULL)
785 g_object_unref (default_app);
787 if (all_applications != NULL)
788 g_list_free_full (all_applications, g_object_unref);
790 if (recommended_apps != NULL)
791 g_list_free_full (recommended_apps, g_object_unref);
793 if (fallback_apps != NULL)
794 g_list_free_full (fallback_apps, g_object_unref);
796 if (exclude_apps != NULL)
797 g_list_free (exclude_apps);
801 gtk_app_chooser_widget_add_items (GtkAppChooserWidget *self)
803 GtkCellRenderer *renderer;
804 GtkTreeViewColumn *column;
807 /* create list store */
808 self->priv->program_list_store = gtk_list_store_new (NUM_COLUMNS,
819 sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->priv->program_list_store));
821 gtk_tree_view_set_model (GTK_TREE_VIEW (self->priv->program_list),
822 GTK_TREE_MODEL (sort));
823 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort),
826 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort),
828 gtk_app_chooser_sort_func,
830 gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->priv->program_list),
832 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->priv->program_list),
833 gtk_app_chooser_search_equal_func,
836 column = gtk_tree_view_column_new ();
838 /* initial padding */
839 renderer = gtk_cell_renderer_text_new ();
840 gtk_tree_view_column_pack_start (column, renderer, FALSE);
841 g_object_set (renderer,
842 "xpad", self->priv->show_all ? 0 : 6,
844 self->priv->padding_renderer = renderer;
846 /* heading text renderer */
847 renderer = gtk_cell_renderer_text_new ();
848 gtk_tree_view_column_pack_start (column, renderer, FALSE);
849 gtk_tree_view_column_set_attributes (column, renderer,
850 "markup", COLUMN_HEADING_TEXT,
851 "visible", COLUMN_HEADING,
853 g_object_set (renderer,
857 "wrap-mode", PANGO_WRAP_WORD,
860 /* padding renderer for non-heading cells */
861 renderer = gtk_cell_renderer_text_new ();
862 gtk_tree_view_column_pack_start (column, renderer, FALSE);
863 gtk_tree_view_column_set_cell_data_func (column, renderer,
864 padding_cell_renderer_func,
867 /* app icon renderer */
868 renderer = gtk_cell_renderer_pixbuf_new ();
869 gtk_tree_view_column_pack_start (column, renderer, FALSE);
870 gtk_tree_view_column_set_attributes (column, renderer,
871 "gicon", COLUMN_GICON,
873 g_object_set (renderer,
874 "stock-size", GTK_ICON_SIZE_MENU,
877 /* app name renderer */
878 renderer = gtk_cell_renderer_text_new ();
879 gtk_tree_view_column_pack_start (column, renderer, TRUE);
880 gtk_tree_view_column_set_attributes (column, renderer,
881 "markup", COLUMN_DESC,
883 g_object_set (renderer,
884 "ellipsize", PANGO_ELLIPSIZE_END,
885 "ellipsize-set", TRUE,
888 gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
889 gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->program_list), column);
891 /* populate the widget */
892 gtk_app_chooser_widget_real_add_items (self);
896 gtk_app_chooser_widget_set_property (GObject *object,
901 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
905 case PROP_CONTENT_TYPE:
906 self->priv->content_type = g_value_dup_string (value);
908 case PROP_SHOW_DEFAULT:
909 gtk_app_chooser_widget_set_show_default (self, g_value_get_boolean (value));
911 case PROP_SHOW_RECOMMENDED:
912 gtk_app_chooser_widget_set_show_recommended (self, g_value_get_boolean (value));
914 case PROP_SHOW_FALLBACK:
915 gtk_app_chooser_widget_set_show_fallback (self, g_value_get_boolean (value));
917 case PROP_SHOW_OTHER:
918 gtk_app_chooser_widget_set_show_other (self, g_value_get_boolean (value));
921 gtk_app_chooser_widget_set_show_all (self, g_value_get_boolean (value));
923 case PROP_DEFAULT_TEXT:
924 gtk_app_chooser_widget_set_default_text (self, g_value_get_string (value));
927 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
933 gtk_app_chooser_widget_get_property (GObject *object,
938 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
942 case PROP_CONTENT_TYPE:
943 g_value_set_string (value, self->priv->content_type);
945 case PROP_SHOW_DEFAULT:
946 g_value_set_boolean (value, self->priv->show_default);
948 case PROP_SHOW_RECOMMENDED:
949 g_value_set_boolean (value, self->priv->show_recommended);
951 case PROP_SHOW_FALLBACK:
952 g_value_set_boolean (value, self->priv->show_fallback);
954 case PROP_SHOW_OTHER:
955 g_value_set_boolean (value, self->priv->show_other);
958 g_value_set_boolean (value, self->priv->show_all);
960 case PROP_DEFAULT_TEXT:
961 g_value_set_string (value, self->priv->default_text);
964 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
970 gtk_app_chooser_widget_constructed (GObject *object)
972 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
974 if (G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed != NULL)
975 G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed (object);
977 gtk_app_chooser_widget_add_items (self);
981 gtk_app_chooser_widget_finalize (GObject *object)
983 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
985 g_free (self->priv->content_type);
986 g_free (self->priv->default_text);
988 G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->finalize (object);
992 gtk_app_chooser_widget_dispose (GObject *object)
994 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
996 if (self->priv->selected_app_info != NULL)
998 g_object_unref (self->priv->selected_app_info);
999 self->priv->selected_app_info = NULL;
1002 G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->dispose (object);
1006 gtk_app_chooser_widget_class_init (GtkAppChooserWidgetClass *klass)
1008 GObjectClass *gobject_class;
1011 gobject_class = G_OBJECT_CLASS (klass);
1012 gobject_class->dispose = gtk_app_chooser_widget_dispose;
1013 gobject_class->finalize = gtk_app_chooser_widget_finalize;
1014 gobject_class->set_property = gtk_app_chooser_widget_set_property;
1015 gobject_class->get_property = gtk_app_chooser_widget_get_property;
1016 gobject_class->constructed = gtk_app_chooser_widget_constructed;
1018 g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type");
1021 * GtkAppChooserWidget:show-default:
1023 * The ::show-default property determines whether the app chooser
1024 * should show the default handler for the content type in a
1025 * separate section. If %FALSE, the default handler is listed
1026 * among the recommended applications.
1028 pspec = g_param_spec_boolean ("show-default",
1029 P_("Show default app"),
1030 P_("Whether the widget should show the default application"),
1032 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1033 g_object_class_install_property (gobject_class, PROP_SHOW_DEFAULT, pspec);
1036 * GtkAppChooserWidget:show-recommended:
1038 * The #GtkAppChooserWidget:show-recommended property determines
1039 * whether the app chooser should show a section for recommended
1040 * applications. If %FALSE, the recommended applications are listed
1041 * among the other applications.
1043 pspec = g_param_spec_boolean ("show-recommended",
1044 P_("Show recommended apps"),
1045 P_("Whether the widget should show recommended applications"),
1047 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1048 g_object_class_install_property (gobject_class, PROP_SHOW_RECOMMENDED, pspec);
1051 * GtkAppChooserWidget:show-fallback:
1053 * The #GtkAppChooserWidget:show-fallback property determines whether
1054 * the app chooser should show a section for fallback applications.
1055 * If %FALSE, the fallback applications are listed among the other
1058 pspec = g_param_spec_boolean ("show-fallback",
1059 P_("Show fallback apps"),
1060 P_("Whether the widget should show fallback applications"),
1062 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1063 g_object_class_install_property (gobject_class, PROP_SHOW_FALLBACK, pspec);
1066 * GtkAppChooserWidget:show-other:
1068 * The #GtkAppChooserWidget:show-other property determines whether
1069 * the app chooser should show a section for other applications.
1071 pspec = g_param_spec_boolean ("show-other",
1072 P_("Show other apps"),
1073 P_("Whether the widget should show other applications"),
1075 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1076 g_object_class_install_property (gobject_class, PROP_SHOW_OTHER, pspec);
1079 * GtkAppChooserWidget:show-all:
1081 * If the #GtkAppChooserWidget:show-all property is %TRUE, the app
1082 * chooser presents all applications in a single list, without
1083 * subsections for default, recommended or related applications.
1085 pspec = g_param_spec_boolean ("show-all",
1086 P_("Show all apps"),
1087 P_("Whether the widget should show all applications"),
1089 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1090 g_object_class_install_property (gobject_class, PROP_SHOW_ALL, pspec);
1093 * GtkAppChooserWidget:default-text:
1095 * The #GtkAppChooserWidget:default-text property determines the text
1096 * that appears in the widget when there are no applications for the
1097 * given content type.
1098 * See also gtk_app_chooser_widget_set_default_text().
1100 pspec = g_param_spec_string ("default-text",
1101 P_("Widget's default text"),
1102 P_("The default text appearing when there are no applications"),
1104 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1105 g_object_class_install_property (gobject_class, PROP_DEFAULT_TEXT, pspec);
1108 * GtkAppChooserWidget::application-selected:
1109 * @self: the object which received the signal
1110 * @application: the selected #GAppInfo
1112 * Emitted when an application item is selected from the widget's list.
1114 signals[SIGNAL_APPLICATION_SELECTED] =
1115 g_signal_new ("application-selected",
1116 GTK_TYPE_APP_CHOOSER_WIDGET,
1118 G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_selected),
1120 _gtk_marshal_VOID__OBJECT,
1122 1, G_TYPE_APP_INFO);
1125 * GtkAppChooserWidget::application-activated:
1126 * @self: the object which received the signal
1127 * @application: the activated #GAppInfo
1129 * Emitted when an application item is activated from the widget's list.
1131 * This usually happens when the user double clicks an item, or an item
1132 * is selected and the user presses one of the keys Space, Shift+Space,
1135 signals[SIGNAL_APPLICATION_ACTIVATED] =
1136 g_signal_new ("application-activated",
1137 GTK_TYPE_APP_CHOOSER_WIDGET,
1139 G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_activated),
1141 _gtk_marshal_VOID__OBJECT,
1143 1, G_TYPE_APP_INFO);
1146 * GtkAppChooserWidget::populate-popup:
1147 * @self: the object which received the signal
1148 * @menu: the #GtkMenu to populate
1149 * @application: the current #GAppInfo
1151 * Emitted when a context menu is about to popup over an application item.
1152 * Clients can insert menu items into the provided #GtkMenu object in the
1153 * callback of this signal; the context menu will be shown over the item
1154 * if at least one item has been added to the menu.
1156 signals[SIGNAL_POPULATE_POPUP] =
1157 g_signal_new ("populate-popup",
1158 GTK_TYPE_APP_CHOOSER_WIDGET,
1160 G_STRUCT_OFFSET (GtkAppChooserWidgetClass, populate_popup),
1162 _gtk_marshal_VOID__OBJECT_OBJECT,
1164 2, GTK_TYPE_MENU, G_TYPE_APP_INFO);
1166 g_type_class_add_private (klass, sizeof (GtkAppChooserWidgetPrivate));
1170 gtk_app_chooser_widget_init (GtkAppChooserWidget *self)
1172 GtkWidget *scrolled_window;
1173 GtkTreeSelection *selection;
1175 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_APP_CHOOSER_WIDGET,
1176 GtkAppChooserWidgetPrivate);
1177 gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
1179 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1180 gtk_widget_set_size_request (scrolled_window, 400, 300);
1181 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
1183 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1185 GTK_POLICY_AUTOMATIC);
1186 gtk_widget_show (scrolled_window);
1188 self->priv->program_list = gtk_tree_view_new ();
1189 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->priv->program_list),
1191 gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->program_list);
1192 gtk_box_pack_start (GTK_BOX (self), scrolled_window, TRUE, TRUE, 0);
1193 gtk_widget_show (self->priv->program_list);
1195 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list));
1196 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1197 gtk_tree_selection_set_select_function (selection, gtk_app_chooser_selection_func,
1199 g_signal_connect_swapped (selection, "changed",
1200 G_CALLBACK (refresh_and_emit_app_selected),
1202 g_signal_connect (self->priv->program_list, "row-activated",
1203 G_CALLBACK (program_list_selection_activated),
1205 g_signal_connect (self->priv->program_list, "button-press-event",
1206 G_CALLBACK (widget_button_press_event_cb),
1211 gtk_app_chooser_widget_get_app_info (GtkAppChooser *object)
1213 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1215 if (self->priv->selected_app_info == NULL)
1218 return g_object_ref (self->priv->selected_app_info);
1222 gtk_app_chooser_widget_refresh (GtkAppChooser *object)
1224 GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object);
1226 if (self->priv->program_list_store != NULL)
1228 gtk_list_store_clear (self->priv->program_list_store);
1230 /* don't add additional xpad if we don't have headings */
1231 g_object_set (self->priv->padding_renderer,
1232 "visible", !self->priv->show_all,
1235 gtk_app_chooser_widget_real_add_items (self);
1240 gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface)
1242 iface->get_app_info = gtk_app_chooser_widget_get_app_info;
1243 iface->refresh = gtk_app_chooser_widget_refresh;
1247 * gtk_app_chooser_widget_new:
1248 * @content_type: the content type to show applications for
1250 * Creates a new #GtkAppChooserWidget for applications
1251 * that can handle content of the given type.
1253 * Returns: a newly created #GtkAppChooserWidget
1258 gtk_app_chooser_widget_new (const gchar *content_type)
1260 return g_object_new (GTK_TYPE_APP_CHOOSER_WIDGET,
1261 "content-type", content_type,
1266 * gtk_app_chooser_widget_set_show_default:
1267 * @self: a #GtkAppChooserWidget
1268 * @setting: the new value for #GtkAppChooserWidget:show-default
1270 * Sets whether the app chooser should show the default handler
1271 * for the content type in a separate section.
1276 gtk_app_chooser_widget_set_show_default (GtkAppChooserWidget *self,
1279 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1281 if (self->priv->show_default != setting)
1283 self->priv->show_default = setting;
1285 g_object_notify (G_OBJECT (self), "show-default");
1287 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1292 * gtk_app_chooser_widget_get_show_default:
1293 * @self: a #GtkAppChooserWidget
1295 * Returns the current value of the #GtkAppChooserWidget:show-default
1298 * Returns: the value of #GtkAppChooserWidget:show-default
1303 gtk_app_chooser_widget_get_show_default (GtkAppChooserWidget *self)
1305 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1307 return self->priv->show_default;
1311 * gtk_app_chooser_widget_set_show_recommended:
1312 * @self: a #GtkAppChooserWidget
1313 * @setting: the new value for #GtkAppChooserWidget:show-recommended
1315 * Sets whether the app chooser should show recommended applications
1316 * for the content type in a separate section.
1321 gtk_app_chooser_widget_set_show_recommended (GtkAppChooserWidget *self,
1324 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1326 if (self->priv->show_recommended != setting)
1328 self->priv->show_recommended = setting;
1330 g_object_notify (G_OBJECT (self), "show-recommended");
1332 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1337 * gtk_app_chooser_widget_get_show_recommended:
1338 * @self: a #GtkAppChooserWidget
1340 * Returns the current value of the #GtkAppChooserWidget:show-recommended
1343 * Returns: the value of #GtkAppChooserWidget:show-recommended
1348 gtk_app_chooser_widget_get_show_recommended (GtkAppChooserWidget *self)
1350 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1352 return self->priv->show_recommended;
1356 * gtk_app_chooser_widget_set_show_fallback:
1357 * @self: a #GtkAppChooserWidget
1358 * @setting: the new value for #GtkAppChooserWidget:show-fallback
1360 * Sets whether the app chooser should show related applications
1361 * for the content type in a separate section.
1366 gtk_app_chooser_widget_set_show_fallback (GtkAppChooserWidget *self,
1369 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1371 if (self->priv->show_fallback != setting)
1373 self->priv->show_fallback = setting;
1375 g_object_notify (G_OBJECT (self), "show-fallback");
1377 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1382 * gtk_app_chooser_widget_get_show_fallback:
1383 * @self: a #GtkAppChooserWidget
1385 * Returns the current value of the #GtkAppChooserWidget:show-fallback
1388 * Returns: the value of #GtkAppChooserWidget:show-fallback
1393 gtk_app_chooser_widget_get_show_fallback (GtkAppChooserWidget *self)
1395 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1397 return self->priv->show_fallback;
1401 * gtk_app_chooser_widget_set_show_other:
1402 * @self: a #GtkAppChooserWidget
1403 * @setting: the new value for #GtkAppChooserWidget:show-other
1405 * Sets whether the app chooser should show applications
1406 * which are unrelated to the content type.
1411 gtk_app_chooser_widget_set_show_other (GtkAppChooserWidget *self,
1414 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1416 if (self->priv->show_other != setting)
1418 self->priv->show_other = setting;
1420 g_object_notify (G_OBJECT (self), "show-other");
1422 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1427 * gtk_app_chooser_widget_get_show_other:
1428 * @self: a #GtkAppChooserWidget
1430 * Returns the current value of the #GtkAppChooserWidget:show-other
1433 * Returns: the value of #GtkAppChooserWidget:show-other
1438 gtk_app_chooser_widget_get_show_other (GtkAppChooserWidget *self)
1440 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1442 return self->priv->show_other;
1446 * gtk_app_chooser_widget_set_show_all:
1447 * @self: a #GtkAppChooserWidget
1448 * @setting: the new value for #GtkAppChooserWidget:show-all
1450 * Sets whether the app chooser should show all applications
1456 gtk_app_chooser_widget_set_show_all (GtkAppChooserWidget *self,
1459 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1461 if (self->priv->show_all != setting)
1463 self->priv->show_all = setting;
1465 g_object_notify (G_OBJECT (self), "show-all");
1467 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1472 * gtk_app_chooser_widget_get_show_all:
1473 * @self: a #GtkAppChooserWidget
1475 * Returns the current value of the #GtkAppChooserWidget:show-all
1478 * Returns: the value of #GtkAppChooserWidget:show-all
1483 gtk_app_chooser_widget_get_show_all (GtkAppChooserWidget *self)
1485 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE);
1487 return self->priv->show_all;
1491 * gtk_app_chooser_widget_set_default_text:
1492 * @self: a #GtkAppChooserWidget
1493 * @text: the new value for #GtkAppChooserWidget:default-text
1495 * Sets the text that is shown if there are not applications
1496 * that can handle the content type.
1499 gtk_app_chooser_widget_set_default_text (GtkAppChooserWidget *self,
1502 g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self));
1504 if (g_strcmp0 (text, self->priv->default_text) != 0)
1506 g_free (self->priv->default_text);
1507 self->priv->default_text = g_strdup (text);
1509 g_object_notify (G_OBJECT (self), "default-text");
1511 gtk_app_chooser_refresh (GTK_APP_CHOOSER (self));
1516 * gtk_app_chooser_widget_get_default_text:
1517 * @self: a #GtkAppChooserWidget
1519 * Returns the text that is shown if there are not applications
1520 * that can handle the content type.
1522 * Returns: the value of #GtkAppChooserWidget:default-text
1527 gtk_app_chooser_widget_get_default_text (GtkAppChooserWidget *self)
1529 g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), NULL);
1531 return self->priv->default_text;