diff options
author | Cosimo Cecchi <cosimoc@gnome.org> | 2010-11-23 17:23:37 +0100 |
---|---|---|
committer | Cosimo Cecchi <cosimoc@gnome.org> | 2010-11-23 17:50:15 +0100 |
commit | 05bb715c51d8963e47a8b34f94b14f0748f6b8d8 (patch) | |
tree | 51c7877d323de62ff889973b082a3c016f72695d /gtk/gtkappchooserwidget.c | |
parent | 6f0a60589125842294579536e62eb099903a0632 (diff) | |
download | gtk+-05bb715c51d8963e47a8b34f94b14f0748f6b8d8.tar.gz |
app-chooser: rename GtkOpenWith to GtkAppChooser
Diffstat (limited to 'gtk/gtkappchooserwidget.c')
-rw-r--r-- | gtk/gtkappchooserwidget.c | 1198 |
1 files changed, 1198 insertions, 0 deletions
diff --git a/gtk/gtkappchooserwidget.c b/gtk/gtkappchooserwidget.c new file mode 100644 index 0000000000..5bac17b989 --- /dev/null +++ b/gtk/gtkappchooserwidget.c @@ -0,0 +1,1198 @@ +/* + * gtkappchooserwidget.c: an app-chooser widget + * + * Copyright (C) 2004 Novell, Inc. + * Copyright (C) 2007, 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Dave Camp <dave@novell.com> + * Alexander Larsson <alexl@redhat.com> + * Cosimo Cecchi <ccecchi@redhat.com> + */ + +#include <config.h> + +#include "gtkappchooserwidget.h" + +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtkappchooser.h" +#include "gtkappchooserprivate.h" + +#include <string.h> +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> +#include <gio/gio.h> + +struct _GtkAppChooserWidgetPrivate { + GAppInfo *selected_app_info; + + gchar *content_type; + gchar *default_text; + gboolean show_default; + gboolean show_recommended; + gboolean show_fallback; + gboolean show_other; + gboolean show_all; + + GtkWidget *program_list; + GtkListStore *program_list_store; + + GtkCellRenderer *padding_renderer; +}; + +enum { + COLUMN_APP_INFO, + COLUMN_GICON, + COLUMN_NAME, + COLUMN_DESC, + COLUMN_EXEC, + COLUMN_DEFAULT, + COLUMN_HEADING, + COLUMN_HEADING_TEXT, + COLUMN_RECOMMENDED, + COLUMN_FALLBACK, + NUM_COLUMNS +}; + + +enum { + PROP_CONTENT_TYPE = 1, + PROP_GFILE, + PROP_SHOW_DEFAULT, + PROP_SHOW_RECOMMENDED, + PROP_SHOW_FALLBACK, + PROP_SHOW_OTHER, + PROP_SHOW_ALL, + PROP_DEFAULT_TEXT, + N_PROPERTIES +}; + +enum { + SIGNAL_APPLICATION_SELECTED, + SIGNAL_APPLICATION_ACTIVATED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +static void gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkAppChooserWidget, gtk_app_chooser_widget, GTK_TYPE_BOX, + G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER, + gtk_app_chooser_widget_iface_init)); + +static void +refresh_and_emit_app_selected (GtkAppChooserWidget *self, + GtkTreeSelection *selection) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GAppInfo *info = NULL; + gboolean should_emit = FALSE; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + COLUMN_APP_INFO, &info, + -1); + } + + if (info == NULL) + return; + + if (self->priv->selected_app_info != NULL) + { + if (!g_app_info_equal (self->priv->selected_app_info, info)) + { + should_emit = TRUE; + g_object_unref (self->priv->selected_app_info); + + self->priv->selected_app_info = info; + } + } + else + { + should_emit = TRUE; + self->priv->selected_app_info = info; + } + + if (should_emit) + g_signal_emit (self, signals[SIGNAL_APPLICATION_SELECTED], 0, + self->priv->selected_app_info); +} + +static gboolean +path_is_heading (GtkTreeView *view, + GtkTreePath *path) +{ + GtkTreeIter iter; + GtkTreeModel *model; + gboolean res; + + model = gtk_tree_view_get_model (view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COLUMN_HEADING, &res, + -1); + + return res; +} + +static void +program_list_selection_activated (GtkTreeView *view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkAppChooserWidget *self = user_data; + GtkTreeSelection *selection; + + if (path_is_heading (view, path)) + return; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list)); + + refresh_and_emit_app_selected (self, selection); + + g_signal_emit (self, signals[SIGNAL_APPLICATION_ACTIVATED], 0, + self->priv->selected_app_info); +} + +static gboolean +gtk_app_chooser_search_equal_func (GtkTreeModel *model, + int column, + const char *key, + GtkTreeIter *iter, + gpointer user_data) +{ + char *normalized_key; + char *name, *normalized_name; + char *path, *normalized_path; + char *basename, *normalized_basename; + gboolean ret; + + if (key != NULL) + { + normalized_key = g_utf8_casefold (key, -1); + g_assert (normalized_key != NULL); + + ret = TRUE; + + gtk_tree_model_get (model, iter, + COLUMN_NAME, &name, + COLUMN_EXEC, &path, + -1); + + if (name != NULL) + { + normalized_name = g_utf8_casefold (name, -1); + g_assert (normalized_name != NULL); + + if (strncmp (normalized_name, normalized_key, strlen (normalized_key)) == 0) { + ret = FALSE; + } + + g_free (normalized_name); + } + + if (ret && path != NULL) + { + normalized_path = g_utf8_casefold (path, -1); + g_assert (normalized_path != NULL); + + basename = g_path_get_basename (path); + g_assert (basename != NULL); + + normalized_basename = g_utf8_casefold (basename, -1); + g_assert (normalized_basename != NULL); + + if (strncmp (normalized_path, normalized_key, strlen (normalized_key)) == 0 || + strncmp (normalized_basename, normalized_key, strlen (normalized_key)) == 0) { + ret = FALSE; + } + + g_free (basename); + g_free (normalized_basename); + g_free (normalized_path); + } + + g_free (name); + g_free (path); + g_free (normalized_key); + + return ret; + } + else + { + return TRUE; + } +} + +static gint +gtk_app_chooser_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gboolean a_recommended, b_recommended; + gboolean a_fallback, b_fallback; + gboolean a_heading, b_heading; + gboolean a_default, b_default; + gchar *a_name, *b_name, *a_casefold, *b_casefold; + gint retval = 0; + + /* this returns: + * - <0 if a should show before b + * - =0 if a is the same as b + * - >0 if a should show after b + */ + + gtk_tree_model_get (model, a, + COLUMN_NAME, &a_name, + COLUMN_RECOMMENDED, &a_recommended, + COLUMN_FALLBACK, &a_fallback, + COLUMN_HEADING, &a_heading, + COLUMN_DEFAULT, &a_default, + -1); + + gtk_tree_model_get (model, b, + COLUMN_NAME, &b_name, + COLUMN_RECOMMENDED, &b_recommended, + COLUMN_FALLBACK, &b_fallback, + COLUMN_HEADING, &b_heading, + COLUMN_DEFAULT, &b_default, + -1); + + /* the default one always wins */ + if (a_default && !b_default) + { + retval = -1; + goto out; + } + + if (b_default && !a_default) + { + retval = 1; + goto out; + } + + /* the recommended one always wins */ + if (a_recommended && !b_recommended) + { + retval = -1; + goto out; + } + + if (b_recommended && !a_recommended) + { + retval = 1; + goto out; + } + + /* the recommended one always wins */ + if (a_fallback && !b_fallback) + { + retval = -1; + goto out; + } + + if (b_fallback && !a_fallback) + { + retval = 1; + goto out; + } + + /* they're both recommended/falback or not, so if one is a heading, wins */ + if (a_heading) + { + return -1; + goto out; + } + + if (b_heading) + { + return 1; + goto out; + } + + /* don't order by name recommended applications, but use GLib's ordering */ + if (!a_recommended) + { + a_casefold = a_name != NULL ? + g_utf8_casefold (a_name, -1) : NULL; + b_casefold = b_name != NULL ? + g_utf8_casefold (b_name, -1) : NULL; + + retval = g_strcmp0 (a_casefold, b_casefold); + + g_free (a_casefold); + g_free (b_casefold); + } + + out: + g_free (a_name); + g_free (b_name); + + return retval; +} + +static void +padding_cell_renderer_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gboolean heading; + + gtk_tree_model_get (model, iter, + COLUMN_HEADING, &heading, + -1); + if (heading) + g_object_set (cell, + "visible", FALSE, + "xpad", 0, + "ypad", 0, + NULL); + else + g_object_set (cell, + "visible", TRUE, + "xpad", 3, + "ypad", 3, + NULL); +} + +static gboolean +gtk_app_chooser_selection_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + gpointer user_data) +{ + GtkTreeIter iter; + gboolean heading; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COLUMN_HEADING, &heading, + -1); + + return !heading; +} + +static gint +compare_apps_func (gconstpointer a, + gconstpointer b) +{ + return !g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b)); +} + +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) +{ + gboolean heading_added, unref_icon; + GtkTreeIter iter; + GAppInfo *app; + gchar *app_string, *bold_string; + GIcon *icon; + GList *l; + gboolean retval; + + retval = FALSE; + heading_added = FALSE; + bold_string = g_strdup_printf ("<b>%s</b>", heading_title); + + for (l = applications; l != NULL; l = l->next) + { + app = l->data; + + if (exclude_apps != NULL && + g_list_find_custom (exclude_apps, app, + (GCompareFunc) compare_apps_func)) + continue; + + if (!heading_added && show_headings) + { + gtk_list_store_append (self->priv->program_list_store, &iter); + gtk_list_store_set (self->priv->program_list_store, &iter, + COLUMN_HEADING_TEXT, bold_string, + COLUMN_HEADING, TRUE, + COLUMN_RECOMMENDED, recommended, + COLUMN_FALLBACK, fallback, + -1); + + heading_added = TRUE; + } + + app_string = g_strdup_printf ("<b>%s</b>\n%s", + g_app_info_get_display_name (app) != NULL ? + g_app_info_get_display_name (app) : "", + g_app_info_get_description (app) != NULL ? + g_app_info_get_description (app) : ""); + + icon = g_app_info_get_icon (app); + if (icon == NULL) + { + icon = g_themed_icon_new ("application-x-executable"); + unref_icon = TRUE; + } + + gtk_list_store_append (self->priv->program_list_store, &iter); + gtk_list_store_set (self->priv->program_list_store, &iter, + COLUMN_APP_INFO, app, + COLUMN_GICON, icon, + COLUMN_NAME, g_app_info_get_display_name (app), + COLUMN_DESC, app_string, + COLUMN_EXEC, g_app_info_get_executable (app), + COLUMN_HEADING, FALSE, + COLUMN_RECOMMENDED, recommended, + COLUMN_FALLBACK, fallback, + -1); + + retval = TRUE; + + g_free (app_string); + if (unref_icon) + g_object_unref (icon); + + unref_icon = FALSE; + } + + g_free (bold_string); + + return retval; +} + + +static void +gtk_app_chooser_add_default (GtkAppChooserWidget *self, + GAppInfo *app) +{ + GtkTreeIter iter; + GIcon *icon; + gchar *string; + gboolean unref_icon; + + unref_icon = FALSE; + string = g_strdup_printf ("<b>%s</b>", _("Default Application")); + + gtk_list_store_append (self->priv->program_list_store, &iter); + gtk_list_store_set (self->priv->program_list_store, &iter, + COLUMN_HEADING_TEXT, string, + COLUMN_HEADING, TRUE, + COLUMN_DEFAULT, TRUE, + -1); + + g_free (string); + + string = g_strdup_printf ("<b>%s</b>\n%s", + g_app_info_get_display_name (app) != NULL ? + g_app_info_get_display_name (app) : "", + g_app_info_get_description (app) != NULL ? + g_app_info_get_description (app) : ""); + + icon = g_app_info_get_icon (app); + if (icon == NULL) + { + icon = g_themed_icon_new ("application-x-executable"); + unref_icon = TRUE; + } + + gtk_list_store_append (self->priv->program_list_store, &iter); + gtk_list_store_set (self->priv->program_list_store, &iter, + COLUMN_APP_INFO, app, + COLUMN_GICON, icon, + COLUMN_NAME, g_app_info_get_display_name (app), + COLUMN_DESC, string, + COLUMN_EXEC, g_app_info_get_executable (app), + COLUMN_HEADING, FALSE, + COLUMN_DEFAULT, TRUE, + -1); + + g_free (string); + + if (unref_icon) + g_object_unref (icon); +} + +static void +add_no_applications_label (GtkAppChooserWidget *self) +{ + gchar *text = NULL, *desc; + const gchar *string; + GtkTreeIter iter; + + if (self->priv->default_text == NULL) + { + desc = g_content_type_get_description (self->priv->content_type); + string = text = g_strdup_printf (_("No applications available to open \"%s\""), + desc); + g_free (desc); + } + else + { + string = self->priv->default_text; + } + + gtk_list_store_append (self->priv->program_list_store, &iter); + gtk_list_store_set (self->priv->program_list_store, &iter, + COLUMN_HEADING_TEXT, string, + COLUMN_HEADING, TRUE, + -1); + + g_free (text); +} + +static void +gtk_app_chooser_widget_select_first (GtkAppChooserWidget *self) +{ + GtkTreeIter iter; + GAppInfo *info = NULL; + GtkTreeModel *model; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list)); + gtk_tree_model_get_iter_first (model, &iter); + + while (info == NULL) + { + gtk_tree_model_get (model, &iter, + COLUMN_APP_INFO, &info, + -1); + + if (info != NULL) + break; + + if (!gtk_tree_model_iter_next (model, &iter)) + break; + } + + if (info != NULL) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list)); + gtk_tree_selection_select_iter (selection, &iter); + + g_object_unref (info); + } +} + +static void +gtk_app_chooser_widget_real_add_items (GtkAppChooserWidget *self) +{ + GList *all_applications = NULL, *recommended_apps = NULL, *fallback_apps = NULL; + GList *exclude_apps = NULL; + GAppInfo *default_app = NULL; + gboolean show_headings; + gboolean apps_added; + + show_headings = TRUE; + apps_added = FALSE; + + if (self->priv->show_all) + show_headings = FALSE; + + if (self->priv->show_default) + { + default_app = g_app_info_get_default_for_type (self->priv->content_type, FALSE); + + if (default_app != NULL) + { + gtk_app_chooser_add_default (self, default_app); + apps_added = TRUE; + exclude_apps = g_list_prepend (exclude_apps, default_app); + } + } + + if (self->priv->show_recommended || self->priv->show_all) + { + recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type); + + apps_added |= gtk_app_chooser_widget_add_section (self, _("Recommended Applications"), + show_headings, + !self->priv->show_all, /* mark as recommended */ + FALSE, /* mark as fallback */ + recommended_apps, exclude_apps); + + exclude_apps = g_list_concat (exclude_apps, + g_list_copy (recommended_apps)); + } + + if (self->priv->show_fallback || self->priv->show_all) + { + fallback_apps = g_app_info_get_fallback_for_type (self->priv->content_type); + + apps_added |= gtk_app_chooser_widget_add_section (self, _("Related Applications"), + show_headings, + FALSE, /* mark as recommended */ + !self->priv->show_all, /* mark as fallback */ + fallback_apps, exclude_apps); + exclude_apps = g_list_concat (exclude_apps, + g_list_copy (fallback_apps)); + } + + if (self->priv->show_other || self->priv->show_all) + { + all_applications = g_app_info_get_all (); + + apps_added |= gtk_app_chooser_widget_add_section (self, _("Other Applications"), + show_headings, + FALSE, + FALSE, + all_applications, exclude_apps); + } + + if (!apps_added) + add_no_applications_label (self); + + gtk_app_chooser_widget_select_first (self); + + if (default_app != NULL) + g_object_unref (default_app); + + if (all_applications != NULL) + g_list_free_full (all_applications, g_object_unref); + + if (recommended_apps != NULL) + g_list_free_full (recommended_apps, g_object_unref); + + if (fallback_apps != NULL) + g_list_free_full (fallback_apps, g_object_unref); + + if (exclude_apps != NULL) + g_list_free (exclude_apps); +} + +static void +gtk_app_chooser_widget_add_items (GtkAppChooserWidget *self) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeModel *sort; + + /* create list store */ + self->priv->program_list_store = gtk_list_store_new (NUM_COLUMNS, + G_TYPE_APP_INFO, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->priv->program_list_store)); + + gtk_tree_view_set_model (GTK_TREE_VIEW (self->priv->program_list), + GTK_TREE_MODEL (sort)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), + COLUMN_NAME, + GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort), + COLUMN_NAME, + gtk_app_chooser_sort_func, + self, NULL); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->priv->program_list), + COLUMN_NAME); + gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->priv->program_list), + gtk_app_chooser_search_equal_func, + NULL, NULL); + + column = gtk_tree_view_column_new (); + + /* initial padding */ + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + g_object_set (renderer, + "xpad", self->priv->show_all ? 0 : 6, + NULL); + self->priv->padding_renderer = renderer; + + /* heading text renderer */ + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "markup", COLUMN_HEADING_TEXT, + "visible", COLUMN_HEADING, + NULL); + g_object_set (renderer, + "ypad", 6, + "xpad", 0, + "wrap-width", 350, + "wrap-mode", PANGO_WRAP_WORD, + NULL); + + /* padding renderer for non-heading cells */ + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_cell_data_func (column, renderer, + padding_cell_renderer_func, + NULL, NULL); + + /* app icon renderer */ + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "gicon", COLUMN_GICON, + NULL); + g_object_set (renderer, + "stock-size", GTK_ICON_SIZE_DIALOG, + NULL); + + /* app name renderer */ + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "markup", COLUMN_DESC, + NULL); + g_object_set (renderer, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->program_list), column); + + /* populate the widget */ + gtk_app_chooser_widget_real_add_items (self); +} + +static void +gtk_app_chooser_widget_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + switch (property_id) + { + case PROP_CONTENT_TYPE: + self->priv->content_type = g_value_dup_string (value); + break; + case PROP_SHOW_DEFAULT: + gtk_app_chooser_widget_set_show_default (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_RECOMMENDED: + gtk_app_chooser_widget_set_show_recommended (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_FALLBACK: + gtk_app_chooser_widget_set_show_fallback (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_OTHER: + gtk_app_chooser_widget_set_show_other (self, g_value_get_boolean (value)); + break; + case PROP_SHOW_ALL: + gtk_app_chooser_widget_set_show_all (self, g_value_get_boolean (value)); + break; + case PROP_DEFAULT_TEXT: + gtk_app_chooser_widget_set_default_text (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gtk_app_chooser_widget_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + switch (property_id) + { + case PROP_CONTENT_TYPE: + g_value_set_string (value, self->priv->content_type); + break; + case PROP_SHOW_DEFAULT: + g_value_set_boolean (value, self->priv->show_default); + break; + case PROP_SHOW_RECOMMENDED: + g_value_set_boolean (value, self->priv->show_recommended); + break; + case PROP_SHOW_FALLBACK: + g_value_set_boolean (value, self->priv->show_fallback); + break; + case PROP_SHOW_OTHER: + g_value_set_boolean (value, self->priv->show_other); + break; + case PROP_SHOW_ALL: + g_value_set_boolean (value, self->priv->show_all); + break; + case PROP_DEFAULT_TEXT: + g_value_set_string (value, self->priv->default_text); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gtk_app_chooser_widget_constructed (GObject *object) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + g_assert (self->priv->content_type != NULL); + + if (G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed != NULL) + G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed (object); + + gtk_app_chooser_widget_add_items (self); +} + +static void +gtk_app_chooser_widget_finalize (GObject *object) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + g_free (self->priv->content_type); + g_free (self->priv->default_text); + + G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->finalize (object); +} + +static void +gtk_app_chooser_widget_dispose (GObject *object) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + if (self->priv->selected_app_info != NULL) + { + g_object_unref (self->priv->selected_app_info); + self->priv->selected_app_info = NULL; + } + + G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->dispose (object); +} + +static void +gtk_app_chooser_widget_class_init (GtkAppChooserWidgetClass *klass) +{ + GObjectClass *gobject_class; + GParamSpec *pspec; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->dispose = gtk_app_chooser_widget_dispose; + gobject_class->finalize = gtk_app_chooser_widget_finalize; + gobject_class->set_property = gtk_app_chooser_widget_set_property; + gobject_class->get_property = gtk_app_chooser_widget_get_property; + gobject_class->constructed = gtk_app_chooser_widget_constructed; + + g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type"); + + pspec = g_param_spec_boolean ("show-default", + P_("Show default app"), + P_("Whether the widget should show the default application"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SHOW_DEFAULT, pspec); + + pspec = g_param_spec_boolean ("show-recommended", + P_("Show recommended apps"), + P_("Whether the widget should show recommended applications"), + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SHOW_RECOMMENDED, pspec); + + pspec = g_param_spec_boolean ("show-fallback", + P_("Show fallback apps"), + P_("Whether the widget should show fallback applications"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SHOW_FALLBACK, pspec); + + pspec = g_param_spec_boolean ("show-other", + P_("Show other apps"), + P_("Whether the widget should show other applications"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SHOW_OTHER, pspec); + + pspec = g_param_spec_boolean ("show-all", + P_("Show all apps"), + P_("Whether the widget should show all applications"), + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_SHOW_ALL, pspec); + + pspec = g_param_spec_string ("default-text", + P_("Widget's default text"), + P_("The default text appearing when there are no applications"), + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_DEFAULT_TEXT, pspec); + + signals[SIGNAL_APPLICATION_SELECTED] = + g_signal_new ("application-selected", + GTK_TYPE_APP_CHOOSER_WIDGET, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_selected), + NULL, NULL, + _gtk_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, G_TYPE_APP_INFO); + + signals[SIGNAL_APPLICATION_ACTIVATED] = + g_signal_new ("application-activated", + GTK_TYPE_APP_CHOOSER_WIDGET, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_activated), + NULL, NULL, + _gtk_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, G_TYPE_APP_INFO); + + g_type_class_add_private (klass, sizeof (GtkAppChooserWidgetPrivate)); +} + +static void +gtk_app_chooser_widget_init (GtkAppChooserWidget *self) +{ + GtkWidget *scrolled_window; + GtkTreeSelection *selection; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_APP_CHOOSER_WIDGET, + GtkAppChooserWidgetPrivate); + gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL); + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (scrolled_window, 400, 300); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_widget_show (scrolled_window); + + self->priv->program_list = gtk_tree_view_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->priv->program_list), + FALSE); + gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->program_list); + gtk_box_pack_start (GTK_BOX (self), scrolled_window, TRUE, TRUE, 0); + gtk_widget_show (self->priv->program_list); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_selection_set_select_function (selection, gtk_app_chooser_selection_func, + self, NULL); + g_signal_connect_swapped (selection, "changed", + G_CALLBACK (refresh_and_emit_app_selected), + self); + g_signal_connect (self->priv->program_list, "row-activated", + G_CALLBACK (program_list_selection_activated), + self); +} + +static GAppInfo * +gtk_app_chooser_widget_get_app_info (GtkAppChooser *object) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + if (self->priv->selected_app_info == NULL) + return NULL; + + return g_object_ref (self->priv->selected_app_info); +} + +static void +gtk_app_chooser_widget_refresh (GtkAppChooser *object) +{ + GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); + + if (self->priv->program_list_store != NULL) + { + gtk_list_store_clear (self->priv->program_list_store); + + /* don't add additional xpad if we don't have headings */ + g_object_set (self->priv->padding_renderer, + "visible", !self->priv->show_all, + NULL); + + gtk_app_chooser_widget_real_add_items (self); + } +} + +static void +gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface) +{ + iface->get_app_info = gtk_app_chooser_widget_get_app_info; + iface->refresh = gtk_app_chooser_widget_refresh; +} + +GtkWidget * +gtk_app_chooser_widget_new (const gchar *content_type) +{ + return g_object_new (GTK_TYPE_APP_CHOOSER_WIDGET, + "content-type", content_type, + NULL); +} + +void +gtk_app_chooser_widget_set_show_default (GtkAppChooserWidget *self, + gboolean setting) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (self->priv->show_default != setting) + { + self->priv->show_default = setting; + + g_object_notify (G_OBJECT (self), "show-default"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +gboolean +gtk_app_chooser_widget_get_show_default (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); + + return self->priv->show_default; +} + +void +gtk_app_chooser_widget_set_show_recommended (GtkAppChooserWidget *self, + gboolean setting) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (self->priv->show_recommended != setting) + { + self->priv->show_recommended = setting; + + g_object_notify (G_OBJECT (self), "show-recommended"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +gboolean +gtk_app_chooser_widget_get_show_recommended (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); + + return self->priv->show_recommended; +} + +void +gtk_app_chooser_widget_set_show_fallback (GtkAppChooserWidget *self, + gboolean setting) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (self->priv->show_fallback != setting) + { + self->priv->show_fallback = setting; + + g_object_notify (G_OBJECT (self), "show-fallback"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +gboolean +gtk_app_chooser_widget_get_show_fallback (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); + + return self->priv->show_fallback; +} + +void +gtk_app_chooser_widget_set_show_other (GtkAppChooserWidget *self, + gboolean setting) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (self->priv->show_other != setting) + { + self->priv->show_other = setting; + + g_object_notify (G_OBJECT (self), "show-other"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +gboolean gtk_app_chooser_widget_get_show_other (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); + + return self->priv->show_other; +} + +void +gtk_app_chooser_widget_set_show_all (GtkAppChooserWidget *self, + gboolean setting) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (self->priv->show_all != setting) + { + self->priv->show_all = setting; + + g_object_notify (G_OBJECT (self), "show-all"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +gboolean +gtk_app_chooser_widget_get_show_all (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); + + return self->priv->show_all; +} + +void +gtk_app_chooser_widget_set_default_text (GtkAppChooserWidget *self, + const gchar *text) +{ + g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); + + if (g_strcmp0 (text, self->priv->default_text) != 0) + { + g_free (self->priv->default_text); + self->priv->default_text = g_strdup (text); + + g_object_notify (G_OBJECT (self), "default-text"); + + gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); + } +} + +const gchar * +gtk_app_chooser_widget_get_default_text (GtkAppChooserWidget *self) +{ + g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), NULL); + + return self->priv->default_text; +} |