/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2008-2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: David Zeuthen */ #include "config.h" #include #define GOA_API_IS_SUBJECT_TO_CHANGE #define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE #include #include "cc-online-accounts-model.h" struct _GoaPanelAccountsModel { GtkListStore parent_instance; GoaClient *client; }; typedef struct { GtkListStoreClass parent_class; } GoaPanelAccountsModelClass; enum { PROP_0, PROP_CLIENT, }; static void init_model (GoaPanelAccountsModel *model); static gboolean find_iter_for_object (GoaPanelAccountsModel *model, GoaObject *object, GtkTreeIter *out_iter); static void on_account_added (GoaClient *client, GoaObject *object, gpointer user_data); static void on_account_removed (GoaClient *client, GoaObject *object, gpointer user_data); static void on_account_changed (GoaClient *client, GoaObject *object, gpointer user_data); G_DEFINE_TYPE (GoaPanelAccountsModel, goa_panel_accounts_model, GTK_TYPE_LIST_STORE); static void goa_panel_accounts_model_finalize (GObject *object) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (object); g_signal_handlers_disconnect_by_func (model->client, G_CALLBACK (on_account_added), model); g_signal_handlers_disconnect_by_func (model->client, G_CALLBACK (on_account_removed), model); g_signal_handlers_disconnect_by_func (model->client, G_CALLBACK (on_account_changed), model); g_object_unref (model->client); G_OBJECT_CLASS (goa_panel_accounts_model_parent_class)->finalize (object); } static void goa_panel_accounts_model_init (GoaPanelAccountsModel *model) { } static void goa_panel_accounts_model_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (object); switch (prop_id) { case PROP_CLIENT: g_value_set_object (value, goa_panel_accounts_model_get_client (model)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void goa_panel_accounts_model_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (object); switch (prop_id) { case PROP_CLIENT: model->client = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /* ---------------------------------------------------------------------------------------------------- */ static void goa_panel_accounts_model_constructed (GObject *object) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (object); GType types[GOA_PANEL_ACCOUNTS_MODEL_N_COLUMNS]; G_STATIC_ASSERT (5 == GOA_PANEL_ACCOUNTS_MODEL_N_COLUMNS); types[0] = G_TYPE_STRING; types[1] = GOA_TYPE_OBJECT; types[2] = G_TYPE_BOOLEAN; types[3] = G_TYPE_STRING; types[4] = G_TYPE_ICON; gtk_list_store_set_column_types (GTK_LIST_STORE (model), GOA_PANEL_ACCOUNTS_MODEL_N_COLUMNS, types); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), GOA_PANEL_ACCOUNTS_MODEL_COLUMN_SORT_KEY, GTK_SORT_ASCENDING); g_signal_connect (model->client, "account-added", G_CALLBACK (on_account_added), model); g_signal_connect (model->client, "account-removed", G_CALLBACK (on_account_removed), model); g_signal_connect (model->client, "account-changed", G_CALLBACK (on_account_changed), model); init_model (model); if (G_OBJECT_CLASS (goa_panel_accounts_model_parent_class)->constructed != NULL) G_OBJECT_CLASS (goa_panel_accounts_model_parent_class)->constructed (object); } static void goa_panel_accounts_model_class_init (GoaPanelAccountsModelClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = goa_panel_accounts_model_finalize; gobject_class->constructed = goa_panel_accounts_model_constructed; gobject_class->get_property = goa_panel_accounts_model_get_property; gobject_class->set_property = goa_panel_accounts_model_set_property; /** * GoaPanelAccountsModel:client: * * The #GoaClient used by the #GoaPanelAccountsModel instance. */ g_object_class_install_property (gobject_class, PROP_CLIENT, g_param_spec_object ("client", "Client", "The client used by the tree model", GOA_TYPE_CLIENT, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } /** * goa_panel_accounts_model_new: * @client: A #GoaClient. * * Creates a new #GoaPanelAccountsModel for viewing the accounts known * by @client. * * Returns: A #GoaPanelAccountsModel. Free with g_object_unref(). */ GoaPanelAccountsModel * goa_panel_accounts_model_new (GoaClient *client) { return GOA_PANEL_ACCOUNTS_MODEL (g_object_new (GOA_TYPE_PANEL_ACCOUNTS_MODEL, "client", client, NULL)); } /** * goa_panel_accounts_model_get_client: * @model: A #GoaPanelAccountsModel. * * Gets the #GoaClient used by @model. * * Returns: (transfer none): A #GoaClient. Do not free, the object * belongs to @model. */ GoaClient * goa_panel_accounts_model_get_client (GoaPanelAccountsModel *model) { g_return_val_if_fail (GOA_IS_PANEL_ACCOUNTS_MODEL (model), NULL); return model->client; } /** * goa_panel_accounts_model_get_iter_for_object: * @model: A #GoaPanelAccountsModel. * @object: A #GoaObject. * @iter: (out): Return location for #GtkTreeIter. * * Finds @model's row for @object. * * Returns: %TRUE if @iter was set, %FALSE if @object wasn't found. */ gboolean goa_panel_accounts_model_get_iter_for_object (GoaPanelAccountsModel *model, GoaObject *object, GtkTreeIter *iter) { g_return_val_if_fail (GOA_IS_PANEL_ACCOUNTS_MODEL (model), FALSE); g_return_val_if_fail (GOA_IS_OBJECT (object), FALSE); g_return_val_if_fail (iter != NULL, FALSE); return find_iter_for_object (model, object, iter); } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GoaObject *object; GtkTreeIter iter; gboolean found; } FindIterData; static gboolean find_iter_for_object_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { FindIterData *data = user_data; GoaObject *iter_object; iter_object = NULL; gtk_tree_model_get (model, iter, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_OBJECT, &iter_object, -1); if (iter_object == NULL) goto out; if (iter_object == data->object) { data->iter = *iter; data->found = TRUE; goto out; } out: if (iter_object != NULL) g_object_unref (iter_object); return data->found; } static gboolean find_iter_for_object (GoaPanelAccountsModel *model, GoaObject *object, GtkTreeIter *out_iter) { FindIterData data; memset (&data, 0, sizeof (data)); data.object = object; data.found = FALSE; gtk_tree_model_foreach (GTK_TREE_MODEL (model), find_iter_for_object_cb, &data); if (data.found) { if (out_iter != NULL) *out_iter = data.iter; } return data.found; } /* ---------------------------------------------------------------------------------------------------- */ static void set_values (GoaPanelAccountsModel *model, GoaObject *object, GtkTreeIter *iter) { GoaAccount *account; GIcon *icon; gchar *markup; GError *error; account = goa_object_peek_account (object); error = NULL; icon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error); if (icon == NULL) { g_warning ("Error creating GIcon for account: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); g_error_free (error); } markup = g_strdup_printf ("%s\n%s", goa_account_get_provider_name (account), goa_account_get_presentation_identity (account)); gtk_list_store_set (GTK_LIST_STORE (model), iter, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_SORT_KEY, goa_account_get_id (account), GOA_PANEL_ACCOUNTS_MODEL_COLUMN_OBJECT, object, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ATTENTION_NEEDED, goa_account_get_attention_needed (account), GOA_PANEL_ACCOUNTS_MODEL_COLUMN_MARKUP, markup, GOA_PANEL_ACCOUNTS_MODEL_COLUMN_ICON, icon, -1); g_free (markup); g_clear_object (&icon); } static void add_account (GoaPanelAccountsModel *model, GoaObject *object) { GtkTreeIter iter; gtk_list_store_insert (GTK_LIST_STORE (model), &iter, G_MAXINT); /* position */ set_values (model, object, &iter); } static void remove_account (GoaPanelAccountsModel *model, GoaObject *object) { GtkTreeIter iter; if (!find_iter_for_object (model, object, &iter)) { g_warning ("Error removing object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); } else { gtk_list_store_remove (GTK_LIST_STORE (model), &iter); } } static void update_account (GoaPanelAccountsModel *model, GoaObject *object) { GtkTreeIter iter; if (!find_iter_for_object (model, object, &iter)) { g_warning ("Error updating object %s - not in tree", g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); } else { set_values (model, object, &iter); } } static void init_model (GoaPanelAccountsModel *model) { GList *accounts; GList *l; accounts = goa_client_get_accounts (model->client); for (l = accounts; l != NULL; l = l->next) { GoaObject *object = GOA_OBJECT (l->data); add_account (model, object); } g_list_foreach (accounts, (GFunc) g_object_unref, NULL); g_list_free (accounts); } static void on_account_added (GoaClient *client, GoaObject *object, gpointer user_data) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (user_data); add_account (model, object); } static void on_account_removed (GoaClient *client, GoaObject *object, gpointer user_data) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (user_data); remove_account (model, object); } static void on_account_changed (GoaClient *client, GoaObject *object, gpointer user_data) { GoaPanelAccountsModel *model = GOA_PANEL_ACCOUNTS_MODEL (user_data); update_account (model, object); }