summaryrefslogtreecommitdiff
path: root/gtk/gtkappchooserwidget.c
diff options
context:
space:
mode:
authorCosimo Cecchi <cosimoc@gnome.org>2010-11-23 17:23:37 +0100
committerCosimo Cecchi <cosimoc@gnome.org>2010-11-23 17:50:15 +0100
commit05bb715c51d8963e47a8b34f94b14f0748f6b8d8 (patch)
tree51c7877d323de62ff889973b082a3c016f72695d /gtk/gtkappchooserwidget.c
parent6f0a60589125842294579536e62eb099903a0632 (diff)
downloadgtk+-05bb715c51d8963e47a8b34f94b14f0748f6b8d8.tar.gz
app-chooser: rename GtkOpenWith to GtkAppChooser
Diffstat (limited to 'gtk/gtkappchooserwidget.c')
-rw-r--r--gtk/gtkappchooserwidget.c1198
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;
+}