/* * gtkopenwithwidget.c: an open-with 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 * Alexander Larsson * Cosimo Cecchi */ #include #include "gtkopenwithwidget.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkopenwith.h" #include "gtkopenwithprivate.h" #include #include #include #include struct _GtkOpenWithWidgetPrivate { GAppInfo *selected_app_info; char *content_type; GtkOpenWithWidgetShowMode show_mode; GtkWidget *program_list; GtkWidget *show_more; GtkListStore *program_list_store; GtkCellRenderer *padding_renderer; gboolean show_more_clicked; }; enum { COLUMN_APP_INFO, COLUMN_GICON, COLUMN_NAME, COLUMN_DESC, COLUMN_EXEC, COLUMN_HEADING, COLUMN_HEADING_TEXT, COLUMN_RECOMMENDED, COLUMN_FALLBACK, NUM_COLUMNS }; enum { PROP_CONTENT_TYPE = 1, PROP_GFILE, PROP_SHOW_MODE, N_PROPERTIES }; enum { SIGNAL_APPLICATION_SELECTED, SIGNAL_APPLICATION_ACTIVATED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; static void gtk_open_with_widget_iface_init (GtkOpenWithIface *iface); G_DEFINE_TYPE_WITH_CODE (GtkOpenWithWidget, gtk_open_with_widget, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE (GTK_TYPE_OPEN_WITH, gtk_open_with_widget_iface_init)); static void refresh_and_emit_app_selected (GtkOpenWithWidget *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) { 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) { GtkOpenWithWidget *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 void item_forget_association_cb (GtkMenuItem *item, gpointer user_data) { GtkOpenWithWidget *self = user_data; GtkTreePath *path = NULL; GtkTreeIter iter; GtkTreeModel *model; GAppInfo *info; gtk_tree_view_get_cursor (GTK_TREE_VIEW (self->priv->program_list), &path, NULL); model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list)); if (path != NULL) { gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, COLUMN_APP_INFO, &info, -1); if (info != NULL && g_app_info_can_remove_supports_type (info)) g_app_info_remove_supports_type (info, self->priv->content_type, NULL); } _gtk_open_with_widget_refilter (self); } static GtkWidget * gtk_open_with_widget_build_popup_menu (GtkOpenWithWidget *self) { GtkWidget *menu; GtkWidget *item; menu = gtk_menu_new (); item = gtk_menu_item_new_with_label (_("Forget association")); g_signal_connect (item, "activate", G_CALLBACK (item_forget_association_cb), self); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); return menu; } static gboolean should_show_menu (GtkOpenWithWidget *self, GdkEventButton *event) { GtkTreeIter iter; GtkTreePath *path; GtkTreeModel *model; gboolean recommended, retval; GAppInfo *info; gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (self->priv->program_list), event->x, event->y, &path, NULL, NULL, NULL); model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->priv->program_list)); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, COLUMN_RECOMMENDED, &recommended, COLUMN_APP_INFO, &info, -1); retval = recommended && (info != NULL); gtk_tree_path_free (path); if (info != NULL) g_object_unref (info); return retval; } static void do_popup_menu (GtkOpenWithWidget *self, GdkEventButton *event) { GtkWidget *menu; if (!should_show_menu (self, event)) return; menu = gtk_open_with_widget_build_popup_menu (self); gtk_menu_attach_to_widget (GTK_MENU (menu), self->priv->program_list, NULL); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); } static gboolean program_list_button_press_event_cb (GtkWidget *treeview, GdkEventButton *event, gpointer user_data) { GtkOpenWithWidget *self = user_data; if (event->button == 3 && event->type == GDK_BUTTON_PRESS) do_popup_menu (self, event); return FALSE; } static gboolean gtk_open_with_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_open_with_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; gchar *a_name, *b_name, *a_casefold, *b_casefold; gint retval; /* 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, -1); gtk_tree_model_get (model, b, COLUMN_NAME, &b_name, COLUMN_RECOMMENDED, &b_recommended, COLUMN_FALLBACK, &b_fallback, COLUMN_HEADING, &b_heading, -1); /* 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; } 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_open_with_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 void gtk_open_with_widget_add_section (GtkOpenWithWidget *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; heading_added = FALSE; bold_string = g_strdup_printf ("%s", heading_title); for (l = applications; l != NULL; l = l->next) { app = l->data; if (!g_app_info_supports_uris (app) && !g_app_info_supports_files (app)) continue; 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 ("%s\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); g_free (app_string); if (unref_icon) g_object_unref (icon); unref_icon = FALSE; } g_free (bold_string); } static void add_no_applications_label (GtkOpenWithWidget *self) { gchar *string, *string2, *desc; GtkTreeIter iter; desc = g_content_type_get_description (self->priv->content_type); string2 = g_strdup_printf (_("No applications available to open \"%s\""), desc); string = g_strdup_printf ("%s\n%s", string2, _("Click \"Show other applications\" for more options")); 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_RECOMMENDED, TRUE, -1); g_free (string); g_free (string2); g_free (desc); } static void gtk_open_with_widget_real_add_items (GtkOpenWithWidget *self) { GList *all_applications = NULL, *content_type_apps = NULL, *recommended_apps = NULL, *fallback_apps = NULL; gboolean show_recommended, show_headings, show_all; if (self->priv->show_mode == GTK_OPEN_WITH_WIDGET_SHOW_MODE_RECOMMENDED) { show_all = FALSE; show_headings = FALSE; show_recommended = TRUE; } else if (self->priv->show_mode == GTK_OPEN_WITH_WIDGET_SHOW_MODE_ALL) { show_all = TRUE; show_headings = FALSE; show_recommended = FALSE; } else { show_all = self->priv->show_more_clicked; show_headings = TRUE; show_recommended = TRUE; } if (show_recommended) { recommended_apps = g_app_info_get_recommended_for_type (self->priv->content_type); fallback_apps = g_app_info_get_fallback_for_type (self->priv->content_type); content_type_apps = g_list_concat (g_list_copy (recommended_apps), g_list_copy (fallback_apps)); } if (show_all) all_applications = g_app_info_get_all (); if (show_recommended) { if (recommended_apps != NULL) gtk_open_with_widget_add_section (self, _("Recommended Applications"), show_headings, TRUE, FALSE, recommended_apps, NULL); else if (!self->priv->show_more_clicked) add_no_applications_label (self); } if (show_all) gtk_open_with_widget_add_section (self, _("Fallback Applications"), show_headings, FALSE, TRUE, fallback_apps, recommended_apps); if (show_all) gtk_open_with_widget_add_section (self, _("Other Applications"), show_headings, FALSE, FALSE, all_applications, content_type_apps); if (content_type_apps != NULL) g_list_free_full (content_type_apps, g_object_unref); if (all_applications != NULL) g_list_free_full (all_applications, g_object_unref); } static void gtk_open_with_widget_add_items (GtkOpenWithWidget *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_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->priv->program_list_store)); /* populate the widget */ gtk_open_with_widget_real_add_items (self); 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_open_with_sort_func, self, NULL); gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->priv->program_list), gtk_open_with_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_mode == GTK_OPEN_WITH_WIDGET_SHOW_MODE_HEADINGS) ? 6 : 0, 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); } static void gtk_open_with_widget_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GtkOpenWithWidget *self = GTK_OPEN_WITH_WIDGET (object); switch (property_id) { case PROP_CONTENT_TYPE: self->priv->content_type = g_value_dup_string (value); break; case PROP_SHOW_MODE: gtk_open_with_widget_set_show_mode (self, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gtk_open_with_widget_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GtkOpenWithWidget *self = GTK_OPEN_WITH_WIDGET (object); switch (property_id) { case PROP_CONTENT_TYPE: g_value_set_string (value, self->priv->content_type); break; case PROP_SHOW_MODE: g_value_set_enum (value, self->priv->show_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void show_more_button_clicked_cb (GtkButton *button, gpointer user_data) { GtkOpenWithWidget *self = user_data; self->priv->show_more_clicked = TRUE; gtk_widget_hide (GTK_WIDGET (button)); _gtk_open_with_widget_refilter (self); } static void gtk_open_with_widget_ensure_show_more_button (GtkOpenWithWidget *self) { if (self->priv->show_mode == GTK_OPEN_WITH_WIDGET_SHOW_MODE_HEADINGS) { if (!self->priv->show_more_clicked) gtk_widget_show (self->priv->show_more); } else { gtk_widget_hide (self->priv->show_more); } } static void gtk_open_with_widget_constructed (GObject *object) { GtkOpenWithWidget *self = GTK_OPEN_WITH_WIDGET (object); g_assert (self->priv->content_type != NULL); if (G_OBJECT_CLASS (gtk_open_with_widget_parent_class)->constructed != NULL) G_OBJECT_CLASS (gtk_open_with_widget_parent_class)->constructed (object); gtk_open_with_widget_ensure_show_more_button (self); gtk_open_with_widget_add_items (self); } static void gtk_open_with_widget_finalize (GObject *object) { GtkOpenWithWidget *self = GTK_OPEN_WITH_WIDGET (object); g_free (self->priv->content_type); G_OBJECT_CLASS (gtk_open_with_widget_parent_class)->finalize (object); } static void gtk_open_with_widget_dispose (GObject *object) { GtkOpenWithWidget *self = GTK_OPEN_WITH_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_open_with_widget_parent_class)->dispose (object); } static void gtk_open_with_widget_class_init (GtkOpenWithWidgetClass *klass) { GObjectClass *gobject_class; GParamSpec *pspec; gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = gtk_open_with_widget_dispose; gobject_class->finalize = gtk_open_with_widget_finalize; gobject_class->set_property = gtk_open_with_widget_set_property; gobject_class->get_property = gtk_open_with_widget_get_property; gobject_class->constructed = gtk_open_with_widget_constructed; g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type"); /** * GtkOpenWithWidget::show-mode: * * The #GtkOpenWithWidgetShowMode for this widget. **/ pspec = g_param_spec_enum ("show-mode", P_("The widget show mode"), P_("The show mode for this widget"), GTK_TYPE_OPEN_WITH_WIDGET_SHOW_MODE, GTK_OPEN_WITH_WIDGET_SHOW_MODE_HEADINGS, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (gobject_class, PROP_SHOW_MODE, pspec); signals[SIGNAL_APPLICATION_SELECTED] = g_signal_new ("application-selected", GTK_TYPE_OPEN_WITH_WIDGET, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkOpenWithWidgetClass, 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_OPEN_WITH_WIDGET, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkOpenWithWidgetClass, application_activated), NULL, NULL, _gtk_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_APP_INFO); g_type_class_add_private (klass, sizeof (GtkOpenWithWidgetPrivate)); } static void gtk_open_with_widget_init (GtkOpenWithWidget *self) { GtkWidget *scrolled_window, *button, *w; GtkTreeSelection *selection; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_OPEN_WITH_WIDGET, GtkOpenWithWidgetPrivate); 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_open_with_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); g_signal_connect (self->priv->program_list, "button-press-event", G_CALLBACK (program_list_button_press_event_cb), self); button = gtk_button_new_with_label (_("Show other applications")); w = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button), w); gtk_box_pack_start (GTK_BOX (self), button, FALSE, FALSE, 6); g_signal_connect (button, "clicked", G_CALLBACK (show_more_button_clicked_cb), self); self->priv->show_more = button; } static GAppInfo * gtk_open_with_widget_get_app_info (GtkOpenWith *object) { GtkOpenWithWidget *self = GTK_OPEN_WITH_WIDGET (object); if (self->priv->selected_app_info == NULL) return NULL; return g_object_ref (self->priv->selected_app_info); } static void gtk_open_with_widget_iface_init (GtkOpenWithIface *iface) { iface->get_app_info = gtk_open_with_widget_get_app_info; } void _gtk_open_with_widget_refilter (GtkOpenWithWidget *self) { gtk_open_with_widget_ensure_show_more_button (self); 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_mode == GTK_OPEN_WITH_WIDGET_SHOW_MODE_HEADINGS, NULL); gtk_open_with_widget_real_add_items (self); } } GtkWidget * gtk_open_with_widget_new (const gchar *content_type) { return g_object_new (GTK_TYPE_OPEN_WITH_WIDGET, "content-type", content_type, NULL); } /** * gtk_open_with_widget_set_show_mode: * @self: a #GtkOpenWithWidget * @show_mode: the new show mode for this widget * * Sets the mode for the widget to show the list of applications. * See #GtkOpenWithWidgetShowMode for more details. * * Since: 3.0 **/ void gtk_open_with_widget_set_show_mode (GtkOpenWithWidget *self, GtkOpenWithWidgetShowMode show_mode) { g_return_if_fail (GTK_IS_OPEN_WITH_WIDGET (self)); if (self->priv->show_mode != show_mode) { self->priv->show_mode = show_mode; g_object_notify (G_OBJECT (self), "show-mode"); self->priv->show_more_clicked = FALSE; _gtk_open_with_widget_refilter (self); } } /** * gtk_open_with_widget_get_show_mode: * @self: a #GtkOpenWithWidget * * Returns the current mode for the widget to show the list of applications. * See #GtkOpenWithWidgetShowMode for mode details. * * Returns: a #GtkOpenWithWidgetShowMode * * Since: 3.0 **/ GtkOpenWithWidgetShowMode gtk_open_with_widget_get_show_mode (GtkOpenWithWidget *self) { g_return_val_if_fail (GTK_IS_OPEN_WITH_WIDGET (self), FALSE); return self->priv->show_mode; }