From cf25ec8fa76721957d4bee91615096aede936c5d Mon Sep 17 00:00:00 2001 From: Cosimo Cecchi Date: Wed, 17 Nov 2010 19:28:48 +0100 Subject: [PATCH] open-with: first attempt to split the dialog into a widget + interface Like GtkFileChooser does; GtkOpenWith is a generic interface, which is now implemented by both GtkOpenWithDialog and GtkOpenWithWidget (and in the future also by GtkOpenWithComboBox). --- gtk/Makefile.am | 5 + gtk/gtk.h | 2 + gtk/gtkenums.h | 7 + gtk/gtkopenwith.c | 68 ++ gtk/gtkopenwith.h | 53 ++ gtk/gtkopenwithdialog.c | 1280 ++++++-------------------------------- gtk/gtkopenwithdialog.h | 21 +- gtk/gtkopenwithprivate.h | 46 ++ gtk/gtkopenwithwidget.c | 793 +++++++++++++++++++++++ gtk/gtkopenwithwidget.h | 82 +++ tests/testopenwith.c | 34 +- 11 files changed, 1251 insertions(+), 1140 deletions(-) create mode 100644 gtk/gtkopenwith.c create mode 100644 gtk/gtkopenwith.h create mode 100644 gtk/gtkopenwithprivate.h create mode 100644 gtk/gtkopenwithwidget.c create mode 100644 gtk/gtkopenwithwidget.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index cf26def992..2cf51999b1 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -246,7 +246,9 @@ gtk_public_h_sources = \ gtkmountoperation.h \ gtknotebook.h \ gtkoffscreenwindow.h \ + gtkopenwith.h \ gtkopenwithdialog.h \ + gtkopenwithwidget.h \ gtkorientable.h \ gtkpagesetup.h \ gtkpaned.h \ @@ -374,6 +376,7 @@ gtk_private_h_sources = \ gtkmenuprivate.h \ gtkmnemonichash.h \ gtkmountoperationprivate.h \ + gtkopenwithprivate.h \ gtkpango.h \ gtkpathbar.h \ gtkplugprivate.h \ @@ -517,6 +520,8 @@ gtk_base_c_sources = \ gtkmountoperation.c \ gtknotebook.c \ gtkoffscreenwindow.c \ + gtkopenwith.c \ + gtkopenwithwidget.c \ gtkopenwithdialog.c \ gtkorientable.c \ gtkpagesetup.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 5cfcba23d9..fa823ae335 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -129,7 +129,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 9fd47d7052..3523699f8f 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -565,6 +565,13 @@ typedef enum } GtkScrollablePolicy; +typedef enum +{ + GTK_OPEN_WITH_WIDGET_SHOW_MODE_RECOMMENDED, + GTK_OPEN_WITH_WIDGET_SHOW_MODE_ALL, + GTK_OPEN_WITH_WIDGET_SHOW_MODE_HEADINGS +} GtkOpenWithWidgetShowMode; + G_END_DECLS diff --git a/gtk/gtkopenwith.c b/gtk/gtkopenwith.c new file mode 100644 index 0000000000..e5123b0726 --- /dev/null +++ b/gtk/gtkopenwith.c @@ -0,0 +1,68 @@ +/* + * gtkopenwith.c: open-with interface + * + * Copyright (C) 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: Cosimo Cecchi + */ + +#include + +#include "gtkopenwith.h" + +#include "gtkintl.h" +#include "gtkopenwithprivate.h" +#include "gtkwidget.h" + +#include + +G_DEFINE_INTERFACE (GtkOpenWith, gtk_open_with, GTK_TYPE_WIDGET); + +static void +gtk_open_with_default_init (GtkOpenWithIface *iface) +{ + GParamSpec *pspec; + + pspec = g_param_spec_string ("content-type", + P_("Content type"), + P_("The content type used by the open with object"), + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + g_object_interface_install_property (iface, pspec); +} + +gchar * +gtk_open_with_get_content_type (GtkOpenWith *self) +{ + gchar *retval = NULL; + + g_return_val_if_fail (GTK_IS_OPEN_WITH (self), NULL); + + g_object_get (self, + "content-type", &retval, + NULL); + + return retval; +} + +GAppInfo * +gtk_open_with_get_app_info (GtkOpenWith *self) +{ + return GTK_OPEN_WITH_GET_IFACE (self)->get_app_info (self); +} diff --git a/gtk/gtkopenwith.h b/gtk/gtkopenwith.h new file mode 100644 index 0000000000..09bca2f400 --- /dev/null +++ b/gtk/gtkopenwith.h @@ -0,0 +1,53 @@ +/* + * gtkopenwith.h: open-with interface + * + * Copyright (C) 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: Cosimo Cecchi + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __GTK_OPEN_WITH_H__ +#define __GTK_OPEN_WITH_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_OPEN_WITH\ + (gtk_open_with_get_type ()) +#define GTK_OPEN_WITH(obj)\ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_OPEN_WITH, GtkOpenWith)) +#define GTK_IS_OPEN_WITH(obj)\ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_OPEN_WITH)) + +typedef struct _GtkOpenWith GtkOpenWith; + +GType gtk_open_with_get_type () G_GNUC_CONST; + +GAppInfo * gtk_open_with_get_app_info (GtkOpenWith *self); +gchar * gtk_open_with_get_content_type (GtkOpenWith *self); + +G_END_DECLS + +#endif /* __GTK_OPEN_WITH_H__ */ + diff --git a/gtk/gtkopenwithdialog.c b/gtk/gtkopenwithdialog.c index 48b48bb3ff..41dada803d 100644 --- a/gtk/gtkopenwithdialog.c +++ b/gtk/gtkopenwithdialog.c @@ -29,6 +29,8 @@ #include "gtkopenwithdialog.h" #include "gtkintl.h" +#include "gtkopenwith.h" +#include "gtkopenwithprivate.h" #include #include @@ -38,55 +40,34 @@ #define sure_string(s) ((const char *) ((s) != NULL ? (s) : "")) struct _GtkOpenWithDialogPrivate { - GAppInfo *selected_app_info; - char *content_type; GFile *gfile; GtkOpenWithDialogMode mode; - GtkOpenWithDialogShowMode show_mode; - gboolean show_set_as_default_button; + + gboolean use_custom; GtkWidget *label; - GtkWidget *entry; GtkWidget *button; - GtkWidget *checkbox; GtkWidget *desc_label; GtkWidget *open_label; - GtkWidget *program_list; - GtkListStore *program_list_store; - gint add_items_idle_id; - - GtkCellRenderer *padding_renderer; -}; - -enum { - COLUMN_APP_INFO, - COLUMN_GICON, - COLUMN_NAME, - COLUMN_COMMENT, - COLUMN_EXEC, - COLUMN_HEADING, - COLUMN_HEADING_TEXT, - COLUMN_RECOMMENDED, - NUM_COLUMNS + GtkWidget *open_with_widget; }; enum { PROP_GFILE = 1, PROP_CONTENT_TYPE, PROP_MODE, - PROP_SHOW_MODE, - PROP_SHOW_SET_AS_DEFAULT, N_PROPERTIES }; #define RESPONSE_REMOVE 1 -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -G_DEFINE_TYPE (GtkOpenWithDialog, gtk_open_with_dialog, GTK_TYPE_DIALOG); +static void gtk_open_with_dialog_iface_init (GtkOpenWithIface *iface); +G_DEFINE_TYPE_WITH_CODE (GtkOpenWithDialog, gtk_open_with_dialog, GTK_TYPE_DIALOG, + G_IMPLEMENT_INTERFACE (GTK_TYPE_OPEN_WITH, + gtk_open_with_dialog_iface_init)); static void show_error_dialog (const gchar *primary, @@ -117,23 +98,24 @@ show_error_dialog (const gchar *primary, * 2) The user has permissions to run the file */ static gboolean -check_application (GtkOpenWithDialog *self) +check_application (GtkOpenWithDialog *self, + GAppInfo **app_out) { - char *command; + const char *command; char *path = NULL; char **argv = NULL; int argc; GError *error = NULL; gint retval = TRUE; + GAppInfo *info; command = NULL; - if (self->priv->selected_app_info != NULL) - command = g_strdup (g_app_info_get_executable (self->priv->selected_app_info)); - if (command == NULL) - command = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->priv->entry))); - + info = gtk_open_with_get_app_info (GTK_OPEN_WITH (self->priv->open_with_widget)); + command = g_app_info_get_executable (info); + g_shell_parse_argv (command, &argc, &argv, &error); + if (error) { show_error_dialog (_("Could not run application"), @@ -160,128 +142,45 @@ check_application (GtkOpenWithDialog *self) goto cleanup; } + *app_out = info; + cleanup: g_strfreev (argv); g_free (path); - g_free (command); return retval; } -/* Only called for non-desktop files */ -static char * -get_app_name (const char *commandline, - GError **error) -{ - char *basename; - char *unquoted; - char **argv; - int argc; - - if (!g_shell_parse_argv (commandline, - &argc, &argv, error)) - return NULL; - - unquoted = g_shell_unquote (argv[0], NULL); - if (unquoted) - basename = g_path_get_basename (unquoted); - else - basename = g_strdup (argv[0]); - - g_free (unquoted); - g_strfreev (argv); - - return basename; -} - -/* This will check if the application the user wanted exists will return that - * application. If it doesn't exist, it will create one and return that. - * It also sets the app info as the default for this type. - */ -static GAppInfo * +static void add_or_find_application (GtkOpenWithDialog *self) { GAppInfo *app; - char *app_name; - const char *commandline; - GError *error; - gboolean success, should_set_default; - char *message; GList *applications; - error = NULL; - app = NULL; - if (self->priv->selected_app_info) - { - app = g_object_ref (self->priv->selected_app_info); - } - else - { - commandline = gtk_entry_get_text (GTK_ENTRY (self->priv->entry)); - app_name = get_app_name (commandline, &error); - if (app_name != NULL) - { - app = g_app_info_create_from_commandline (commandline, - app_name, - G_APP_INFO_CREATE_NONE, - &error); - g_free (app_name); - } - } - + app = gtk_open_with_get_app_info (GTK_OPEN_WITH (self)); + if (app == NULL) { - message = g_strdup_printf (_("Could not add application to the application database: %s"), - error->message); + /* TODO: better error? */ show_error_dialog (_("Could not add application"), - message, + NULL, GTK_WINDOW (self)); - g_free (message); - g_error_free (error); - return NULL; + return; } - should_set_default = - (self->priv->mode == GTK_OPEN_WITH_DIALOG_MODE_SELECT_DEFAULT) || - (self->priv->mode == GTK_OPEN_WITH_DIALOG_MODE_SELECT_ONE && - gtk_widget_get_visible (self->priv->checkbox) && - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->checkbox))); - success = TRUE; - - if (should_set_default) + applications = g_app_info_get_all_for_type (self->priv->content_type); + if (self->priv->content_type != NULL && applications != NULL) { - success = g_app_info_set_as_default_for_type (app, - self->priv->content_type, - &error); - } - else - { - applications = g_app_info_get_all_for_type (self->priv->content_type); - if (self->priv->content_type && applications != NULL) - { - /* we don't care about reporting errors here */ - g_app_info_add_supports_type (app, - self->priv->content_type, - NULL); - } - - if (applications != NULL) { - g_list_free_full (applications, g_object_unref); - } + /* we don't care about reporting errors here */ + g_app_info_add_supports_type (app, + self->priv->content_type, + NULL); } - if (!success && should_set_default) - { - message = g_strdup_printf (_("Could not set application as the default: %s"), - error->message); - show_error_dialog (_("Could not set as default application"), - message, - GTK_WINDOW (self)); - g_free (message); - g_error_free (error); - } + if (applications != NULL) + g_list_free_full (applications, g_object_unref); - return app; + g_object_unref (app); } static void @@ -289,59 +188,27 @@ gtk_open_with_dialog_response (GtkDialog *dialog, gint response_id, gpointer user_data) { - GAppInfo *application; GtkOpenWithDialog *self = GTK_OPEN_WITH_DIALOG (dialog); switch (response_id) { case GTK_RESPONSE_OK: - if (check_application (self)) - { - application = add_or_find_application (self); - - if (self->priv->selected_app_info != NULL) - g_object_unref (self->priv->selected_app_info); - - self->priv->selected_app_info = g_object_ref (application); - } - + add_or_find_application (self); break; case RESPONSE_REMOVE: - if (self->priv->selected_app_info != NULL) - { - if (g_app_info_delete (self->priv->selected_app_info)) - { - GtkTreeModel *model; - GtkTreeIter iter; - GAppInfo *info, *selected; + { + GAppInfo *info; - selected = self->priv->selected_app_info; - self->priv->selected_app_info = NULL; + info = gtk_open_with_get_app_info (GTK_OPEN_WITH (self->priv->open_with_widget)); - model = GTK_TREE_MODEL (self->priv->program_list_store); - if (gtk_tree_model_get_iter_first (model, &iter)) - { - do - { - gtk_tree_model_get (model, &iter, - COLUMN_APP_INFO, &info, - -1); - if (info != NULL && g_app_info_equal (selected, info)) - { - gtk_list_store_remove (self->priv->program_list_store, &iter); - g_object_unref (info); - break; - } - - if (info != NULL) - g_object_unref (info); - } - while (gtk_tree_model_iter_next (model, &iter)); - } - - g_object_unref (selected); - } - } + if (info != NULL) + { + g_app_info_delete (info); + g_object_unref (info); + } + + _gtk_open_with_widget_refilter (GTK_OPEN_WITH_WIDGET (self->priv->open_with_widget)); + } /* don't forward this signal to other clients in this case */ g_signal_stop_emission_by_name (self, "response"); @@ -353,560 +220,34 @@ gtk_open_with_dialog_response (GtkDialog *dialog, } static void -chooser_response_cb (GtkFileChooser *chooser, - int response, - gpointer user_data) -{ - GtkOpenWithDialog *self = user_data; - - if (response == GTK_RESPONSE_OK) - { - char *filename; - - filename = gtk_file_chooser_get_filename (chooser); - - if (filename) - { - char *quoted_text; - - quoted_text = g_shell_quote (filename); - - gtk_entry_set_text (GTK_ENTRY (self->priv->entry), - quoted_text); - gtk_editable_set_position (GTK_EDITABLE (self->priv->entry), -1); - g_free (quoted_text); - g_free (filename); - } - } - - gtk_widget_destroy (GTK_WIDGET (chooser)); -} - -static void -browse_clicked_cb (GtkWidget *button, - gpointer user_data) -{ - GtkOpenWithDialog *self = user_data; - GtkWidget *chooser; - - chooser = gtk_file_chooser_dialog_new (_("Select an Application"), - GTK_WINDOW (self), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, - GTK_RESPONSE_OK, - NULL); - gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE); - g_signal_connect (chooser, "response", - G_CALLBACK (chooser_response_cb), self); - gtk_dialog_set_default_response (GTK_DIALOG (chooser), - GTK_RESPONSE_OK); - gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE); - gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), - FALSE); - gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), - "/usr/bin"); - - gtk_widget_show (chooser); -} - -static void -entry_changed_cb (GtkWidget *entry, - gpointer user_data) -{ - GtkOpenWithDialog *self = user_data; - - /* We are writing in the entry, so we are not using a known appinfo anymore */ - if (self->priv->selected_app_info != NULL) - { - g_object_unref (self->priv->selected_app_info); - self->priv->selected_app_info = NULL; - } - - if (gtk_entry_get_text (GTK_ENTRY (self->priv->entry))[0] == '\000') - gtk_widget_set_sensitive (self->priv->button, FALSE); - else - gtk_widget_set_sensitive (self->priv->button, TRUE); -} - -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; - 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, - -1); - - gtk_tree_model_get (model, b, - COLUMN_NAME, &b_name, - COLUMN_RECOMMENDED, &b_recommended, - -1); - - /* the recommended one always wins */ - if (a_recommended && !b_recommended) - { - retval = -1; - goto out; - } - - if (b_recommended && !a_recommended) - { - retval = 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 -heading_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); - - g_object_set (cell, - "visible", heading, - NULL); -} - -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_dialog_real_add_items (GtkOpenWithDialog *self) -{ - GList *all_applications = NULL, *content_type_apps = NULL, *l; - gboolean heading_added; - gboolean show_recommended, show_headings, show_all; - - if (self->priv->show_mode == GTK_OPEN_WITH_DIALOG_SHOW_MODE_RECOMMENDED) - { - show_all = FALSE; - show_headings = FALSE; - show_recommended = TRUE; - } - else if (self->priv->show_mode == GTK_OPEN_WITH_DIALOG_SHOW_MODE_ALL) - { - show_all = TRUE; - show_headings = FALSE; - show_recommended = FALSE; - } - else - { - show_all = TRUE; - show_headings = TRUE; - show_recommended = TRUE; - } - - if (show_recommended) - content_type_apps = g_app_info_get_all_for_type (self->priv->content_type); - - if (show_all) - all_applications = g_app_info_get_all (); - - heading_added = FALSE; - - for (l = content_type_apps; l != NULL; l = l->next) - { - GAppInfo *app = l->data; - GtkTreeIter iter; - - if (!g_app_info_supports_uris (app) && - !g_app_info_supports_files (app)) - 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, _("Recommended Applications"), - COLUMN_HEADING, TRUE, - COLUMN_RECOMMENDED, TRUE, - -1); - - heading_added = 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, g_app_info_get_icon (app), - COLUMN_NAME, g_app_info_get_display_name (app), - COLUMN_COMMENT, g_app_info_get_description (app), - COLUMN_EXEC, g_app_info_get_executable, - COLUMN_HEADING, FALSE, - COLUMN_RECOMMENDED, TRUE, - -1); - } - - heading_added = FALSE; - - for (l = all_applications; l != NULL && show_all; l = l->next) - { - GAppInfo *app = l->data; - GtkTreeIter iter; - - if (!g_app_info_supports_uris (app) && - !g_app_info_supports_files (app)) - continue; - - if (content_type_apps != NULL && - g_list_find_custom (content_type_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, _("Other Applications"), - COLUMN_HEADING, TRUE, - COLUMN_RECOMMENDED, FALSE, - -1); - - heading_added = 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, g_app_info_get_icon (app), - COLUMN_NAME, g_app_info_get_display_name (app), - COLUMN_COMMENT, g_app_info_get_description (app), - COLUMN_EXEC, g_app_info_get_executable, - COLUMN_HEADING, FALSE, - COLUMN_RECOMMENDED, FALSE, - -1); - } - - 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 gboolean -gtk_open_with_dialog_add_items_idle (gpointer user_data) -{ - GtkOpenWithDialog *self = user_data; - 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); - sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->priv->program_list_store)); - - /* populate the dialog */ - gtk_open_with_dialog_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_DIALOG_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, - "text", COLUMN_HEADING_TEXT, - NULL); - g_object_set (renderer, - "weight", PANGO_WEIGHT_BOLD, - "weight-set", TRUE, - "ypad", 6, - "xpad", 0, - NULL); - gtk_tree_view_column_set_cell_data_func (column, renderer, - heading_cell_renderer_func, - NULL, 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); - - /* 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, - "text", COLUMN_NAME, - 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); - - self->priv->add_items_idle_id = 0; - - return FALSE; -} - -static void -program_list_selection_changed (GtkTreeSelection *selection, +widget_application_selected_cb (GtkOpenWithWidget *widget, + GAppInfo *app_info, gpointer user_data) { GtkOpenWithDialog *self = user_data; - GtkTreeModel *model; - GtkTreeIter iter; - GAppInfo *info; - - if (!gtk_tree_selection_get_selected (selection, &model, &iter)) - { - gtk_widget_set_sensitive (self->priv->button, FALSE); - gtk_dialog_set_response_sensitive (GTK_DIALOG (self), - RESPONSE_REMOVE, - FALSE); - return; - } - info = NULL; - gtk_tree_model_get (model, &iter, - COLUMN_APP_INFO, &info, - -1); - - if (info == NULL) - return; - - gtk_entry_set_text (GTK_ENTRY (self->priv->entry), - sure_string (g_app_info_get_executable (info))); gtk_label_set_text (GTK_LABEL (self->priv->desc_label), - sure_string (g_app_info_get_description (info))); + sure_string (g_app_info_get_description (app_info))); gtk_widget_set_sensitive (self->priv->button, TRUE); gtk_dialog_set_response_sensitive (GTK_DIALOG (self), RESPONSE_REMOVE, - g_app_info_can_delete (info)); + g_app_info_can_delete (app_info)); - if (self->priv->selected_app_info) - g_object_unref (self->priv->selected_app_info); - - self->priv->selected_app_info = info; + self->priv->use_custom = FALSE; } static void -program_list_selection_activated (GtkTreeView *view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer user_data) +widget_application_activated_cb (GtkOpenWithWidget *widget, + GAppInfo *app_info, + gpointer user_data) { GtkOpenWithDialog *self = user_data; - GtkTreeSelection *selection; - /* update the entry with the info from the selection */ - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list)); - program_list_selection_changed (selection, self); + g_print ("app activated\n"); gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK); } -static void -expander_toggled (GtkWidget *expander, - gpointer user_data) -{ - GtkOpenWithDialog *self = user_data; - - if (gtk_expander_get_expanded (GTK_EXPANDER (expander)) == TRUE) - { - gtk_widget_grab_focus (self->priv->entry); - gtk_window_resize (GTK_WINDOW (self), 400, 1); - } - else - { - GtkTreeSelection *selection; - - gtk_widget_grab_focus (self->priv->program_list); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->program_list)); - program_list_selection_changed (selection, self); - } -} - static char * get_extension (const char *basename) { @@ -923,8 +264,7 @@ get_extension (const char *basename) static void set_dialog_properties (GtkOpenWithDialog *self) { - char *label, *emname, *name, *extension, *description, *checkbox_text; - GFileInfo *info; + char *label, *emname, *name, *extension, *description; name = NULL; extension = NULL; @@ -937,12 +277,6 @@ set_dialog_properties (GtkOpenWithDialog *self) name = g_file_get_basename (self->priv->gfile); emname = g_strdup_printf ("%s", name); extension = get_extension (name); - info = g_file_query_info (self->priv->gfile, - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, - 0, NULL, NULL); - self->priv->content_type = g_strdup (g_file_info_get_content_type (info)); - - g_object_unref (info); } description = g_content_type_get_description (self->priv->content_type); @@ -958,15 +292,6 @@ set_dialog_properties (GtkOpenWithDialog *self) { /* Translators: %s is a filename */ label = g_strdup_printf (_("Open %s with:"), emname); - - if (g_content_type_is_unknown (self->priv->content_type)) - /* Translators: the %s is the extension of the file */ - checkbox_text = g_strdup_printf (_("_Remember this application for %s documents"), - extension); - else - /* Translators: %s is a file type description */ - checkbox_text = g_strdup_printf (_("_Remember this application for \"%s\" files"), - description); } else { @@ -976,18 +301,7 @@ set_dialog_properties (GtkOpenWithDialog *self) label = g_strdup_printf (_("Select an application for \"%s\" files:"), g_content_type_is_unknown (self->priv->content_type) ? self->priv->content_type : description); - - /* Translators: %s is a file type description */ - checkbox_text = g_strdup_printf (_("_Remember this application for \"%s\" files"), - g_content_type_is_unknown (self->priv->content_type) ? - self->priv->content_type : description); } - - gtk_button_set_label (GTK_BUTTON (self->priv->checkbox), checkbox_text); - g_free (checkbox_text); - - if (self->priv->show_set_as_default_button) - gtk_widget_show (self->priv->checkbox); } else { @@ -1013,7 +327,6 @@ set_dialog_properties (GtkOpenWithDialog *self) label = g_strdup_printf (_("Open all \"%s\" files with:"), description); } - gtk_widget_hide (self->priv->checkbox); gtk_label_set_text_with_mnemonic (GTK_LABEL (self->priv->open_label), _("_Select")); gtk_window_set_title (GTK_WINDOW (self), _("Select default application")); @@ -1028,6 +341,111 @@ set_dialog_properties (GtkOpenWithDialog *self) g_free (emname); } +static void +build_dialog_ui (GtkOpenWithDialog *self) +{ + GtkWidget *vbox; + GtkWidget *vbox2; + GtkWidget *label; + + gtk_container_set_border_width (GTK_CONTAINER (self), 5); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + + vbox2 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); + + self->priv->label = gtk_label_new (""); + gtk_widget_set_halign (self->priv->label, GTK_ALIGN_START); + gtk_label_set_line_wrap (GTK_LABEL (self->priv->label), TRUE); + gtk_box_pack_start (GTK_BOX (vbox2), self->priv->label, + FALSE, FALSE, 0); + gtk_widget_show (self->priv->label); + + self->priv->open_with_widget = + gtk_open_with_widget_new (self->priv->content_type); + g_signal_connect (self->priv->open_with_widget, "application-selected", + G_CALLBACK (widget_application_selected_cb), self); + g_signal_connect (self->priv->open_with_widget, "application-activated", + G_CALLBACK (widget_application_activated_cb), self); + gtk_box_pack_start (GTK_BOX (vbox2), self->priv->open_with_widget, TRUE, TRUE, 0); + gtk_widget_show (self->priv->open_with_widget); + + self->priv->desc_label = gtk_label_new (_("Select an application to view its description.")); + gtk_widget_set_halign (self->priv->desc_label, GTK_ALIGN_START); + gtk_label_set_justify (GTK_LABEL (self->priv->desc_label), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap (GTK_LABEL (self->priv->desc_label), TRUE); + gtk_label_set_single_line_mode (GTK_LABEL (self->priv->desc_label), FALSE); + gtk_box_pack_start (GTK_BOX (vbox2), self->priv->desc_label, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), vbox, TRUE, TRUE, 0); + gtk_widget_show_all (vbox); + + gtk_dialog_add_button (GTK_DIALOG (self), + GTK_STOCK_REMOVE, + RESPONSE_REMOVE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (self), + RESPONSE_REMOVE, + FALSE); + + gtk_dialog_add_button (GTK_DIALOG (self), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + /* Create a custom stock icon */ + self->priv->button = gtk_button_new (); + + label = gtk_label_new_with_mnemonic (_("_Open")); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (self->priv->button)); + gtk_widget_set_halign (label, GTK_ALIGN_CENTER); + gtk_widget_show (label); + self->priv->open_label = label; + + gtk_container_add (GTK_CONTAINER (self->priv->button), + self->priv->open_label); + + gtk_widget_show (self->priv->button); + gtk_widget_set_can_default (self->priv->button, TRUE); + + gtk_dialog_add_action_widget (GTK_DIALOG (self), + self->priv->button, GTK_RESPONSE_OK); + + gtk_dialog_set_default_response (GTK_DIALOG (self), + GTK_RESPONSE_OK); +} + +static void +set_gfile_and_content_type (GtkOpenWithDialog *self, + GFile *file) +{ + GFileInfo *info; + + if (file == NULL) + return; + + self->priv->gfile = g_object_ref (file); + + info = g_file_query_info (self->priv->gfile, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + self->priv->content_type = g_strdup (g_file_info_get_content_type (info)); + + g_object_unref (info); +} + +static GAppInfo * +gtk_open_with_dialog_get_app_info (GtkOpenWith *object) +{ + GtkOpenWithDialog *self = GTK_OPEN_WITH_DIALOG (object); + GAppInfo *app = NULL; + + if (!check_application (self, &app)) + return NULL; + + return app; +} + static void gtk_open_with_dialog_constructed (GObject *object) { @@ -1039,6 +457,7 @@ gtk_open_with_dialog_constructed (GObject *object) if (G_OBJECT_CLASS (gtk_open_with_dialog_parent_class)->constructed != NULL) G_OBJECT_CLASS (gtk_open_with_dialog_parent_class)->constructed (object); + build_dialog_ui (self); set_dialog_properties (self); } @@ -1047,12 +466,6 @@ gtk_open_with_dialog_finalize (GObject *object) { GtkOpenWithDialog *self = GTK_OPEN_WITH_DIALOG (object); - if (self->priv->add_items_idle_id) - g_source_remove (self->priv->add_items_idle_id); - - if (self->priv->selected_app_info) - g_object_unref (self->priv->selected_app_info); - if (self->priv->gfile) g_object_unref (self->priv->gfile); @@ -1072,22 +485,16 @@ gtk_open_with_dialog_set_property (GObject *object, switch (property_id) { case PROP_GFILE: - self->priv->gfile = g_value_dup_object (value); + set_gfile_and_content_type (self, g_value_get_object (value)); break; case PROP_CONTENT_TYPE: - self->priv->content_type = g_value_dup_string (value); + /* don't try to override a value previously set with the GFile */ + if (self->priv->content_type == NULL) + self->priv->content_type = g_value_dup_string (value); break; case PROP_MODE: self->priv->mode = g_value_get_enum (value); break; - case PROP_SHOW_MODE: - gtk_open_with_dialog_set_show_mode (self, - g_value_get_enum (value)); - break; - case PROP_SHOW_SET_AS_DEFAULT: - gtk_open_with_dialog_set_show_set_as_default_button (self, - g_value_get_boolean (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -1114,22 +521,23 @@ gtk_open_with_dialog_get_property (GObject *object, case PROP_MODE: g_value_set_enum (value, self->priv->mode); break; - case PROP_SHOW_MODE: - g_value_set_enum (value, self->priv->show_mode); - break; - case PROP_SHOW_SET_AS_DEFAULT: - g_value_set_boolean (value, self->priv->show_set_as_default_button); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } +static void +gtk_open_with_dialog_iface_init (GtkOpenWithIface *iface) +{ + iface->get_app_info = gtk_open_with_dialog_get_app_info; +} + static void gtk_open_with_dialog_class_init (GtkOpenWithDialogClass *klass) { GObjectClass *gobject_class; + GParamSpec *pspec; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = gtk_open_with_dialog_finalize; @@ -1137,38 +545,14 @@ gtk_open_with_dialog_class_init (GtkOpenWithDialogClass *klass) gobject_class->get_property = gtk_open_with_dialog_get_property; gobject_class->constructed = gtk_open_with_dialog_constructed; - /** - * GtkOpenWithDialog:gfile: - * - * The #GFile for this dialog, or %NULL in case the dialog has - * been constructed for a content type. - **/ - properties[PROP_GFILE] = - g_param_spec_object ("gfile", - P_("A GFile object"), - P_("The GFile for this dialog"), - G_TYPE_FILE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - /** - * GtkOpenWithDialog:content-type: - * - * The content type string for this dialog, or %NULL in case - * the dialog has been created for a #GFile. - **/ - properties[PROP_CONTENT_TYPE] = - g_param_spec_string ("content-type", - P_("A content type string"), - P_("The content type for this dialog"), - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + g_object_class_override_property (gobject_class, PROP_CONTENT_TYPE, "content-type"); + /** * GtkOpenWithDialog:mode: * * The #GtkOpenWithDialogMode for this dialog. **/ - properties[PROP_MODE] = + pspec = g_param_spec_enum ("mode", P_("The dialog mode"), P_("The operation mode for this dialog"), @@ -1176,35 +560,15 @@ gtk_open_with_dialog_class_init (GtkOpenWithDialogClass *klass) GTK_OPEN_WITH_DIALOG_MODE_SELECT_ONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - /** - * GtkOpenWithDialog::show-mode: - * - * The #GtkOpenWithDialogShowMode for this dialog. - **/ - properties[PROP_SHOW_MODE] = - g_param_spec_enum ("show-mode", - P_("The dialog show mode"), - P_("The show mode for this dialog"), - GTK_TYPE_OPEN_WITH_DIALOG_SHOW_MODE, - GTK_OPEN_WITH_DIALOG_SHOW_MODE_HEADINGS, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - /** - * GtkOpenWithDialog:show-set-as-default: - * - * Whether the dialog in #GTK_OPEN_WITH_DIALOG_MODE_SELECT_ONE mode should show - * a button to remember the choice for the future. - **/ - properties[PROP_SHOW_SET_AS_DEFAULT] = - g_param_spec_boolean ("show-set-as-default", - P_("Whether to show the set as default button"), - P_("Whether the dialog should show a button to set the application as default"), - TRUE, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_MODE, pspec); - g_object_class_install_properties (gobject_class, N_PROPERTIES, - properties); + pspec = g_param_spec_object ("gfile", + P_("GFile"), + P_("The GFile used by the open with dialog"), + G_TYPE_FILE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (gobject_class, PROP_GFILE, pspec); g_type_class_add_private (klass, sizeof (GtkOpenWithDialogPrivate)); } @@ -1212,135 +576,9 @@ gtk_open_with_dialog_class_init (GtkOpenWithDialogClass *klass) static void gtk_open_with_dialog_init (GtkOpenWithDialog *self) { - GtkWidget *hbox; - GtkWidget *vbox; - GtkWidget *vbox2; - GtkWidget *label; - GtkWidget *button; - GtkWidget *scrolled_window; - GtkWidget *expander; - GtkTreeSelection *selection; - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_OPEN_WITH_DIALOG, GtkOpenWithDialogPrivate); - gtk_container_set_border_width (GTK_CONTAINER (self), 5); - - vbox = gtk_vbox_new (FALSE, 12); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); - - vbox2 = gtk_vbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); - - self->priv->label = gtk_label_new (""); - gtk_widget_set_halign (self->priv->label, GTK_ALIGN_START); - gtk_label_set_line_wrap (GTK_LABEL (self->priv->label), TRUE); - gtk_box_pack_start (GTK_BOX (vbox2), self->priv->label, - FALSE, FALSE, 0); - gtk_widget_show (self->priv->label); - - 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); - 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 (vbox2), scrolled_window, TRUE, TRUE, 0); - - self->priv->desc_label = gtk_label_new (_("Select an application to view its description.")); - gtk_widget_set_halign (self->priv->desc_label, GTK_ALIGN_START); - gtk_label_set_justify (GTK_LABEL (self->priv->desc_label), GTK_JUSTIFY_LEFT); - gtk_label_set_line_wrap (GTK_LABEL (self->priv->desc_label), TRUE); - gtk_label_set_single_line_mode (GTK_LABEL (self->priv->desc_label), FALSE); - gtk_box_pack_start (GTK_BOX (vbox2), self->priv->desc_label, FALSE, FALSE, 0); - - 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 (selection, "changed", - G_CALLBACK (program_list_selection_changed), - self); - g_signal_connect (self->priv->program_list, "row-activated", - G_CALLBACK (program_list_selection_activated), - self); - - self->priv->add_items_idle_id = - g_idle_add (gtk_open_with_dialog_add_items_idle, self); - - gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), vbox, TRUE, TRUE, 0); - gtk_widget_show_all (vbox); - - expander = gtk_expander_new_with_mnemonic (_("_Use a custom command")); - gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0); - g_signal_connect_after (expander, "activate", G_CALLBACK (expander_toggled), self); - gtk_widget_show (expander); - - hbox = gtk_hbox_new (FALSE, 6); - gtk_container_add (GTK_CONTAINER (expander), hbox); - gtk_widget_show (hbox); - - self->priv->entry = gtk_entry_new (); - gtk_entry_set_activates_default (GTK_ENTRY (self->priv->entry), TRUE); - - gtk_box_pack_start (GTK_BOX (hbox), self->priv->entry, - TRUE, TRUE, 0); - gtk_widget_show (self->priv->entry); - - button = gtk_button_new_with_mnemonic (_("_Browse...")); - g_signal_connect (button, "clicked", - G_CALLBACK (browse_clicked_cb), self); - gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); - gtk_widget_show (button); - - /* Add remember this application checkbox - only visible in open mode */ - self->priv->checkbox = gtk_check_button_new (); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->priv->checkbox), TRUE); - gtk_button_set_use_underline (GTK_BUTTON (self->priv->checkbox), TRUE); - gtk_box_pack_start (GTK_BOX (vbox), self->priv->checkbox, FALSE, FALSE, 0); - - gtk_dialog_add_button (GTK_DIALOG (self), - GTK_STOCK_REMOVE, - RESPONSE_REMOVE); - gtk_dialog_set_response_sensitive (GTK_DIALOG (self), - RESPONSE_REMOVE, - FALSE); - - gtk_dialog_add_button (GTK_DIALOG (self), - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL); - - /* Create a custom stock icon */ - self->priv->button = gtk_button_new (); - - /* Hook up the entry to the button */ - gtk_widget_set_sensitive (self->priv->button, FALSE); - g_signal_connect (self->priv->entry, "changed", - G_CALLBACK (entry_changed_cb), self); - - label = gtk_label_new_with_mnemonic (_("_Open")); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (self->priv->button)); - gtk_widget_set_halign (label, GTK_ALIGN_CENTER); - gtk_widget_show (label); - self->priv->open_label = label; - - gtk_container_add (GTK_CONTAINER (self->priv->button), - self->priv->open_label); - - gtk_widget_show (self->priv->button); - gtk_widget_set_can_default (self->priv->button, TRUE); - - gtk_dialog_add_action_widget (GTK_DIALOG (self), - self->priv->button, GTK_RESPONSE_OK); - - gtk_dialog_set_default_response (GTK_DIALOG (self), - GTK_RESPONSE_OK); - /* we can't override the class signal handler here, as it's a RUN_LAST; * we want our signal handler instead to be executed before any user code. */ @@ -1364,25 +602,6 @@ set_parent_and_flags (GtkWidget *dialog, gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); } -static void -gtk_open_with_dialog_refilter (GtkOpenWithDialog *self) -{ - /* if the store is NULL it means the idle handler hasn't run - * yet, and it will take care of using the right setting itself. - */ - 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_DIALOG_SHOW_MODE_HEADINGS, - NULL); - - gtk_open_with_dialog_real_add_items (self); - } -} - /** * gtk_open_with_dialog_new: * @parent: (allow-none): a #GtkWindow, or %NULL @@ -1460,135 +679,10 @@ gtk_open_with_dialog_new_for_content_type (GtkWindow *parent, return retval; } -/** - * gtk_open_with_dialog_set_show_mode: - * @self: a #GtkOpenWithDialog - * @show_mode: the new show mode for this dialog - * - * Sets the mode for the dialog to show the list of applications. - * See #GtkOpenWithDialogShowMode for more details. - * - * Since: 3.0 - **/ -void -gtk_open_with_dialog_set_show_mode (GtkOpenWithDialog *self, - GtkOpenWithDialogShowMode show_mode) -{ - g_return_if_fail (GTK_IS_OPEN_WITH_DIALOG (self)); - - if (self->priv->show_mode != show_mode) - { - self->priv->show_mode = show_mode; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_MODE]); - - gtk_open_with_dialog_refilter (self); - } -} - -/** - * gtk_open_with_dialog_get_show_mode: - * @self: a #GtkOpenWithDialog - * - * Returns the current mode for the dialog to show the list of applications. - * See #GtkOpenWithDialogShowMode for mode details. - * - * Returns: a #GtkOpenWithDialogShowMode - * - * Since: 3.0 - **/ -GtkOpenWithDialogShowMode -gtk_open_with_dialog_get_show_mode (GtkOpenWithDialog *self) -{ - g_return_val_if_fail (GTK_IS_OPEN_WITH_DIALOG (self), FALSE); - - return self->priv->show_mode; -} - -/** - * gtk_open_with_dialog_get_selected_application: - * @self: a #GtkOpenWithDialog - * - * Returns a #GAppInfo for the currently selected application in the dialog, - * or %NULL if there are none. - * - * Returns: (transfer full): a #GAppInfo - * - * Since: 3.0 - **/ -GAppInfo * -gtk_open_with_dialog_get_selected_application (GtkOpenWithDialog *self) +GtkWidget * +gtk_open_with_dialog_get_widget (GtkOpenWithDialog *self) { g_return_val_if_fail (GTK_IS_OPEN_WITH_DIALOG (self), NULL); - if (self->priv->selected_app_info != NULL) - return g_object_ref (self->priv->selected_app_info); - - return NULL; -} - -/** - * gtk_open_with_dialog_set_show_set_as_default_button: - * @self: a #GtkOpenWithDialog - * @show_button: whether the button should be visible or not - * - * If the dialog is in #GTK_OPEN_WITH_DIALOG_MODE_SELECT_ONE mode, - * shows a button to set the selected application as default. - * - * Since: 3.0 - **/ -void -gtk_open_with_dialog_set_show_set_as_default_button (GtkOpenWithDialog *self, - gboolean show_button) -{ - g_return_if_fail (GTK_IS_OPEN_WITH_DIALOG (self)); - - if (self->priv->mode == GTK_OPEN_WITH_DIALOG_MODE_SELECT_DEFAULT) - return; - - if (self->priv->show_set_as_default_button != show_button) - { - self->priv->show_set_as_default_button = show_button; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SET_AS_DEFAULT]); - - gtk_widget_set_visible (self->priv->checkbox, show_button); - } -} - -/** - * gtk_open_with_dialog_get_show_set_as_default_button: - * @self: a #GtkOpenWithDialog - * - * Returns whether the dialog is showing a button to set the selected - * application as the default for the provided content type. Note that - * this always returns %FALSE if the dialog is in #GTK_OPEN_WITH_DIALOG_MODE_SELECT_DEFAULT - * mode. - * - * Returns: %TRUE if the button is visible. - * - * Since: 3.0 - **/ -gboolean -gtk_open_with_dialog_get_show_set_as_default_button (GtkOpenWithDialog *self) -{ - g_return_val_if_fail (GTK_IS_OPEN_WITH_DIALOG (self), FALSE); - - return self->priv->show_set_as_default_button; -} - -/** - * gtk_open_with_dialog_get_mode: - * @self: a #GtkOpenWithDialog - * - * Returns the current mode of the dialog - * - * Returns: the current mode of the dialog - * - * Since: 3.0 - */ -GtkOpenWithDialogMode -gtk_open_with_dialog_get_mode (GtkOpenWithDialog *self) -{ - g_return_val_if_fail (GTK_IS_OPEN_WITH_DIALOG (self), -1); - - return self->priv->mode; + return self->priv->open_with_widget; } diff --git a/gtk/gtkopenwithdialog.h b/gtk/gtkopenwithdialog.h index 826e16b019..93acb22a40 100644 --- a/gtk/gtkopenwithdialog.h +++ b/gtk/gtkopenwithdialog.h @@ -47,7 +47,6 @@ #define GTK_OPEN_WITH_DIALOG_GET_CLASS(obj)\ (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_OPEN_WITH_DIALOG, GtkOpenWithDialogClass)) - typedef struct _GtkOpenWithDialog GtkOpenWithDialog; typedef struct _GtkOpenWithDialogClass GtkOpenWithDialogClass; typedef struct _GtkOpenWithDialogPrivate GtkOpenWithDialogPrivate; @@ -62,9 +61,6 @@ struct _GtkOpenWithDialog { struct _GtkOpenWithDialogClass { GtkDialogClass parent_class; - void (*application_selected) (GtkOpenWithDialog *dialog, - GAppInfo *application); - /* padding for future class expansion */ gpointer padding[16]; }; @@ -78,16 +74,9 @@ struct _GtkOpenWithDialogClass { */ typedef enum { GTK_OPEN_WITH_DIALOG_MODE_SELECT_ONE, - GTK_OPEN_WITH_DIALOG_MODE_SELECT_DEFAULT, - GTK_OPEN_WITH_DIALOG_MODE_RADIO, + GTK_OPEN_WITH_DIALOG_MODE_SELECT_DEFAULT } GtkOpenWithDialogMode; -typedef enum { - GTK_OPEN_WITH_DIALOG_SHOW_MODE_RECOMMENDED, - GTK_OPEN_WITH_DIALOG_SHOW_MODE_ALL, - GTK_OPEN_WITH_DIALOG_SHOW_MODE_HEADINGS -} GtkOpenWithDialogShowMode; - GType gtk_open_with_dialog_get_type (void) G_GNUC_CONST; GtkWidget * gtk_open_with_dialog_new (GtkWindow *parent, @@ -99,16 +88,10 @@ GtkWidget * gtk_open_with_dialog_new_for_content_type (GtkWindow *parent, GtkOpenWithDialogMode mode, const gchar *content_type); -void gtk_open_with_dialog_set_show_mode (GtkOpenWithDialog *self, - GtkOpenWithDialogShowMode show_mode); -GtkOpenWithDialogShowMode gtk_open_with_dialog_get_show_mode (GtkOpenWithDialog *self); - void gtk_open_with_dialog_set_show_set_as_default_button (GtkOpenWithDialog *self, gboolean show_button); gboolean gtk_open_with_get_show_set_as_default_button (GtkOpenWithDialog *self); -GAppInfo * gtk_open_with_dialog_get_selected_application (GtkOpenWithDialog *self); - -GtkOpenWithDialogMode gtk_open_with_dialog_get_mode (GtkOpenWithDialog *self); +GtkWidget * gtk_open_with_dialog_get_widget (GtkOpenWithDialog *self); #endif /* __GTK_OPEN_WITH_DIALOG_H__ */ diff --git a/gtk/gtkopenwithprivate.h b/gtk/gtkopenwithprivate.h new file mode 100644 index 0000000000..c25b487b6e --- /dev/null +++ b/gtk/gtkopenwithprivate.h @@ -0,0 +1,46 @@ +/* + * gtkopenwith.c: open-with interface + * + * Copyright (C) 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: Cosimo Cecchi + */ + +#ifndef __GTK_OPEN_WITH_PRIVATE_H__ +#define __GTK_OPEN_WITH_PRIVATE_H__ + +#include +#include + +#include "gtkopenwithwidget.h" + +typedef struct _GtkOpenWithIface GtkOpenWithIface; +typedef GtkOpenWithIface GtkOpenWithInterface; + +#define GTK_OPEN_WITH_GET_IFACE(inst)\ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_OPEN_WITH, GtkOpenWithIface)) + +struct _GtkOpenWithIface { + GTypeInterface base_iface; + + GAppInfo * (* get_app_info) (GtkOpenWith *object); +}; + +void _gtk_open_with_widget_refilter (GtkOpenWithWidget *self); + +#endif /* __GTK_OPEN_WITH_PRIVATE_H__ */ diff --git a/gtk/gtkopenwithwidget.c b/gtk/gtkopenwithwidget.c new file mode 100644 index 0000000000..fbe10e7cc7 --- /dev/null +++ b/gtk/gtkopenwithwidget.c @@ -0,0 +1,793 @@ +/* + * 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; + GtkListStore *program_list_store; + + GtkCellRenderer *padding_renderer; +}; + +enum { + COLUMN_APP_INFO, + COLUMN_GICON, + COLUMN_NAME, + COLUMN_COMMENT, + COLUMN_EXEC, + COLUMN_HEADING, + COLUMN_HEADING_TEXT, + COLUMN_RECOMMENDED, + 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 void +program_list_selection_activated (GtkTreeView *view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkOpenWithWidget *self = user_data; + GtkTreeSelection *selection; + + 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_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; + 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, + -1); + + gtk_tree_model_get (model, b, + COLUMN_NAME, &b_name, + COLUMN_RECOMMENDED, &b_recommended, + -1); + + /* the recommended one always wins */ + if (a_recommended && !b_recommended) + { + retval = -1; + goto out; + } + + if (b_recommended && !a_recommended) + { + retval = 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 +heading_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); + + g_object_set (cell, + "visible", heading, + NULL); +} + +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_real_add_items (GtkOpenWithWidget *self) +{ + GList *all_applications = NULL, *content_type_apps = NULL, *l; + gboolean heading_added; + 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 = TRUE; + show_headings = TRUE; + show_recommended = TRUE; + } + + if (show_recommended) + content_type_apps = g_app_info_get_all_for_type (self->priv->content_type); + + if (show_all) + all_applications = g_app_info_get_all (); + + heading_added = FALSE; + + for (l = content_type_apps; l != NULL; l = l->next) + { + GAppInfo *app = l->data; + GtkTreeIter iter; + + if (!g_app_info_supports_uris (app) && + !g_app_info_supports_files (app)) + 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, _("Recommended Applications"), + COLUMN_HEADING, TRUE, + COLUMN_RECOMMENDED, TRUE, + -1); + + heading_added = 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, g_app_info_get_icon (app), + COLUMN_NAME, g_app_info_get_display_name (app), + COLUMN_COMMENT, g_app_info_get_description (app), + COLUMN_EXEC, g_app_info_get_executable, + COLUMN_HEADING, FALSE, + COLUMN_RECOMMENDED, TRUE, + -1); + } + + heading_added = FALSE; + + for (l = all_applications; l != NULL && show_all; l = l->next) + { + GAppInfo *app = l->data; + GtkTreeIter iter; + + if (!g_app_info_supports_uris (app) && + !g_app_info_supports_files (app)) + continue; + + if (content_type_apps != NULL && + g_list_find_custom (content_type_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, _("Other Applications"), + COLUMN_HEADING, TRUE, + COLUMN_RECOMMENDED, FALSE, + -1); + + heading_added = 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, g_app_info_get_icon (app), + COLUMN_NAME, g_app_info_get_display_name (app), + COLUMN_COMMENT, g_app_info_get_description (app), + COLUMN_EXEC, g_app_info_get_executable, + COLUMN_HEADING, FALSE, + COLUMN_RECOMMENDED, FALSE, + -1); + } + + 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); + 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, + "text", COLUMN_HEADING_TEXT, + NULL); + g_object_set (renderer, + "weight", PANGO_WEIGHT_BOLD, + "weight-set", TRUE, + "ypad", 6, + "xpad", 0, + NULL); + gtk_tree_view_column_set_cell_data_func (column, renderer, + heading_cell_renderer_func, + NULL, 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); + + /* 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, + "text", COLUMN_NAME, + 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 +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_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; + GtkTreeSelection *selection; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_OPEN_WITH_WIDGET, + GtkOpenWithWidgetPrivate); + + gtk_container_set_border_width (GTK_CONTAINER (self), 5); + + 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); + + 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); + + 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); +} + +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) +{ + 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"); + + _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; +} diff --git a/gtk/gtkopenwithwidget.h b/gtk/gtkopenwithwidget.h new file mode 100644 index 0000000000..4ac11934f0 --- /dev/null +++ b/gtk/gtkopenwithwidget.h @@ -0,0 +1,82 @@ +/* + * gtkopenwithwidget.h: 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 + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __GTK_OPEN_WITH_WIDGET_H__ +#define __GTK_OPEN_WITH_WIDGET_H__ + +#include +#include + +#define GTK_TYPE_OPEN_WITH_WIDGET\ + (gtk_open_with_widget_get_type ()) +#define GTK_OPEN_WITH_WIDGET(obj)\ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_OPEN_WITH_WIDGET, GtkOpenWithWidget)) +#define GTK_OPEN_WITH_WIDGET_CLASS(klass)\ + (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_OPEN_WITH_WIDGET, GtkOpenWithWidgetClass)) +#define GTK_IS_OPEN_WITH_WIDGET(obj)\ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_OPEN_WITH_WIDGET)) +#define GTK_IS_OPEN_WITH_WIDGET_CLASS(klass)\ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_OPEN_WITH_WIDGET)) +#define GTK_OPEN_WITH_WIDGET_GET_CLASS(obj)\ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_OPEN_WITH_WIDGET, GtkOpenWithWidgetClass)) + +typedef struct _GtkOpenWithWidget GtkOpenWithWidget; +typedef struct _GtkOpenWithWidgetClass GtkOpenWithWidgetClass; +typedef struct _GtkOpenWithWidgetPrivate GtkOpenWithWidgetPrivate; + +struct _GtkOpenWithWidget { + GtkBox parent; + + /*< private >*/ + GtkOpenWithWidgetPrivate *priv; +}; + +struct _GtkOpenWithWidgetClass { + GtkBoxClass parent_class; + + void (* application_selected) (GtkOpenWithWidget *self, + GAppInfo *app_info); + + void (* application_activated) (GtkOpenWithWidget *self, + GAppInfo *app_info); + + /* padding for future class expansion */ + gpointer padding[16]; +}; + +GType gtk_open_with_widget_get_type (void) G_GNUC_CONST; + +GtkWidget * gtk_open_with_widget_new (const gchar *content_type); + +void gtk_open_with_widget_set_show_mode (GtkOpenWithWidget *self, + GtkOpenWithWidgetShowMode show_mode); +GtkOpenWithWidgetShowMode gtk_open_with_widget_get_show_mode (GtkOpenWithWidget *self); + +#endif /* __GTK_OPEN_WITH_WIDGET_H__ */ diff --git a/tests/testopenwith.c b/tests/testopenwith.c index 7de589a388..1686cde3c8 100644 --- a/tests/testopenwith.c +++ b/tests/testopenwith.c @@ -25,7 +25,7 @@ static GtkWidget *toplevel; static GFile *file; -static GtkWidget *grid, *file_l, *open, *show_mode, *show_set_as_default; +static GtkWidget *grid, *file_l, *open, *show_mode; static GtkWidget *radio_file, *radio_file_default, *radio_content, *radio_content_default, *dialog; static void @@ -40,7 +40,7 @@ dialog_response (GtkDialog *d, if (response_id == GTK_RESPONSE_OK) { - app_info = gtk_open_with_dialog_get_selected_application (GTK_OPEN_WITH_DIALOG (d)); + app_info = gtk_open_with_get_app_info (GTK_OPEN_WITH (d)); name = g_app_info_get_name (app_info); g_print ("Application selected: %s\n", name); @@ -57,6 +57,7 @@ display_dialog (GtkButton *b, gboolean use_file = FALSE; gboolean default_mode = FALSE; gchar *content_type = NULL; + GtkWidget *open_with_widget; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_file))) { @@ -105,11 +106,10 @@ display_dialog (GtkButton *b, content_type); } - gtk_open_with_dialog_set_show_mode (GTK_OPEN_WITH_DIALOG (dialog), + open_with_widget = gtk_open_with_dialog_get_widget (GTK_OPEN_WITH_DIALOG (dialog)); + gtk_open_with_widget_set_show_mode (GTK_OPEN_WITH_WIDGET (open_with_widget), gtk_combo_box_get_active (GTK_COMBO_BOX (show_mode))); - gtk_open_with_dialog_set_show_set_as_default_button (GTK_OPEN_WITH_DIALOG (dialog), - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (show_set_as_default))); gtk_widget_show (dialog); g_signal_connect (dialog, "response", @@ -127,26 +127,11 @@ show_mode_changed (GtkComboBox *b, gint active; active = gtk_combo_box_get_active (b); - gtk_open_with_dialog_set_show_mode (GTK_OPEN_WITH_DIALOG (dialog), + gtk_open_with_widget_set_show_mode (GTK_OPEN_WITH_WIDGET (gtk_open_with_dialog_get_widget (GTK_OPEN_WITH_DIALOG (dialog))), active); } } -static void -show_set_as_default_toggled (GtkToggleButton *b, - gpointer user_data) -{ - if (dialog != NULL) - { - gboolean toggled; - - toggled = gtk_toggle_button_get_active (b); - - gtk_open_with_dialog_set_show_set_as_default_button (GTK_OPEN_WITH_DIALOG (dialog), - toggled); - } -} - static void button_clicked (GtkButton *b, gpointer user_data) @@ -231,13 +216,6 @@ main (int argc, g_signal_connect (show_mode, "changed", G_CALLBACK (show_mode_changed), NULL); - show_set_as_default = gtk_check_button_new_with_label ("Show set as default"); - gtk_grid_attach_next_to (GTK_GRID (grid), show_set_as_default, - show_mode, GTK_POS_BOTTOM, 1, 1); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_set_as_default), TRUE); - g_signal_connect (show_set_as_default, "toggled", - G_CALLBACK (show_set_as_default_toggled), NULL); - gtk_container_add (GTK_CONTAINER (toplevel), grid); gtk_widget_show_all (toplevel);