/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-source-option-menu.c * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "e-source-combo-box.h" #include "e-cell-renderer-color.h" #define E_SOURCE_COMBO_BOX_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE_COMBO_BOX, ESourceComboBoxPrivate)) struct _ESourceComboBoxPrivate { ESourceList *source_list; GHashTable *uid_index; gulong handler_id; }; enum { PROP_0, PROP_SOURCE_LIST }; enum { COLUMN_COLOR, /* GDK_TYPE_COLOR */ COLUMN_NAME, /* G_TYPE_STRING */ COLUMN_SENSITIVE, /* G_TYPE_BOOLEAN */ COLUMN_SOURCE, /* G_TYPE_OBJECT */ COLUMN_VISIBLE, /* G_TYPE_BOOLEAN */ NUM_COLUMNS }; static gpointer parent_class = NULL; /** * compare_source_names * Compares sources by name. **/ static gint compare_source_names (gconstpointer a, gconstpointer b) { g_return_val_if_fail (E_IS_SOURCE (a), -1); g_return_val_if_fail (E_IS_SOURCE (b), 1); return g_utf8_collate (e_source_peek_name (E_SOURCE (a)), e_source_peek_name (E_SOURCE (b))); } /** * get_sorted_sources * Creates copy of GSList of sources (do not increase reference count for data members), * and sorts this list alphabetically by source names. * * @param sources List of sources. * @return New GSList of sorted sources, should be freed by g_slist_free, * but do not unref data members. **/ static GSList * get_sorted_sources (GSList *sources) { GSList *res = NULL, *p; if (!sources) return NULL; for (p = sources; p != NULL; p = p->next) res = g_slist_prepend (res, p->data); res = g_slist_sort (res, compare_source_names); return res; } static void source_list_changed_cb (ESourceList *source_list, ESourceComboBox *source_combo_box) { ESourceComboBoxPrivate *priv; GtkComboBox *combo_box; GtkTreeModel *model; GtkListStore *store; GtkTreeIter iter; GtkTreePath *path; GSList *groups; GSList *sources, *s; const gchar *name; const gchar *uid; gchar *indented_name; gboolean visible = FALSE; gboolean iter_valid; priv = source_combo_box->priv; combo_box = GTK_COMBO_BOX (source_combo_box); model = gtk_combo_box_get_model (combo_box); store = GTK_LIST_STORE (model); gtk_tree_model_get_iter_first (model, &iter); for (groups = e_source_list_peek_groups (source_list); groups != NULL; groups = groups->next) { /* Only show source groups that have sources. */ if (e_source_group_peek_sources (groups->data) == NULL) continue; name = e_source_group_peek_name (groups->data); if (!gtk_list_store_iter_is_valid (store, &iter)) gtk_list_store_append (store, &iter); gtk_list_store_set ( store, &iter, COLUMN_COLOR, NULL, COLUMN_NAME, name, COLUMN_SENSITIVE, FALSE, COLUMN_SOURCE, groups->data, -1); gtk_tree_model_iter_next (model, &iter); sources = get_sorted_sources (e_source_group_peek_sources (groups->data)); for (s = sources; s != NULL; s = s->next) { const gchar *color_spec; GdkColor color; name = e_source_peek_name (s->data); indented_name = g_strconcat (" ", name, NULL); color_spec = e_source_peek_color_spec (s->data); if (color_spec != NULL) { gdk_color_parse (color_spec, &color); visible = TRUE; } if (!gtk_list_store_iter_is_valid (store, &iter)) gtk_list_store_append (store, &iter); gtk_list_store_set ( store, &iter, COLUMN_COLOR, color_spec ? &color : NULL, COLUMN_NAME, indented_name, COLUMN_SENSITIVE, TRUE, COLUMN_SOURCE, s->data, -1); uid = e_source_peek_uid (s->data); path = gtk_tree_model_get_path (model, &iter); g_hash_table_replace ( priv->uid_index, g_strdup (uid), gtk_tree_row_reference_new (model, path)); gtk_tree_path_free (path); gtk_tree_model_iter_next (model, &iter); g_free (indented_name); } g_slist_free (sources); } /* Remove any extra references that might be leftover from the original model */ while (gtk_list_store_iter_is_valid (store, &iter)) gtk_list_store_remove (store, &iter); /* Set the visible column based on whether we've seen a color. */ iter_valid = gtk_tree_model_get_iter_first (model, &iter); while (iter_valid) { gtk_list_store_set ( store, &iter, COLUMN_VISIBLE, visible, -1); iter_valid = gtk_tree_model_iter_next (model, &iter); } } static GObject * e_source_combo_box_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GtkCellRenderer *renderer; GtkListStore *store; GObject *object; /* Chain up to parent's "constructor" method. */ object = G_OBJECT_CLASS (parent_class)->constructor ( type, n_construct_properties, construct_properties); store = gtk_list_store_new ( NUM_COLUMNS, GDK_TYPE_COLOR, /* COLUMN_COLOR */ G_TYPE_STRING, /* COLUMN_NAME */ G_TYPE_BOOLEAN, /* COLUMN_SENSITIVE */ G_TYPE_OBJECT, /* COLUMN_SOURCE */ G_TYPE_BOOLEAN); /* COLUMN_VISIBLE */ gtk_combo_box_set_model ( GTK_COMBO_BOX (object), GTK_TREE_MODEL (store)); renderer = e_cell_renderer_color_new (); gtk_cell_layout_pack_start ( GTK_CELL_LAYOUT (object), renderer, FALSE); gtk_cell_layout_set_attributes ( GTK_CELL_LAYOUT (object), renderer, "color", COLUMN_COLOR, "sensitive", COLUMN_SENSITIVE, "visible", COLUMN_VISIBLE, NULL); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start ( GTK_CELL_LAYOUT (object), renderer, TRUE); gtk_cell_layout_set_attributes ( GTK_CELL_LAYOUT (object), renderer, "text", COLUMN_NAME, "sensitive", COLUMN_SENSITIVE, NULL); return object; } static void e_source_combo_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ESourceComboBoxPrivate *priv; priv = E_SOURCE_COMBO_BOX_GET_PRIVATE (object); switch (property_id) { case PROP_SOURCE_LIST: if (priv->source_list != NULL) { g_signal_handler_disconnect ( priv->source_list, priv->handler_id); g_object_unref (priv->source_list); } priv->source_list = g_value_dup_object (value); /* Reset the tree store. */ source_list_changed_cb ( priv->source_list, E_SOURCE_COMBO_BOX (object)); /* Watch for source list changes. */ priv->handler_id = g_signal_connect_object ( priv->source_list, "changed", G_CALLBACK (source_list_changed_cb), object, 0); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void e_source_combo_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ESourceComboBoxPrivate *priv; priv = E_SOURCE_COMBO_BOX_GET_PRIVATE (object); switch (property_id) { case PROP_SOURCE_LIST: g_value_set_object (value, priv->source_list); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void e_source_combo_box_dispose (GObject *object) { ESourceComboBoxPrivate *priv; priv = E_SOURCE_COMBO_BOX_GET_PRIVATE (object); if (priv->source_list != NULL) { g_object_unref (priv->source_list); priv->source_list = NULL; } g_hash_table_remove_all (priv->uid_index); /* Chain up to parent's "dispose" method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } static void e_source_combo_box_finalize (GObject *object) { ESourceComboBoxPrivate *priv; priv = E_SOURCE_COMBO_BOX_GET_PRIVATE (object); g_hash_table_destroy (priv->uid_index); /* Chain up to parent's "finalize" method. */ G_OBJECT_CLASS (parent_class)->finalize (object); } static void e_source_combo_box_class_init (ESourceComboBoxClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (ESourceComboBox)); object_class->constructor = e_source_combo_box_constructor; object_class->set_property = e_source_combo_box_set_property; object_class->get_property = e_source_combo_box_get_property; object_class->dispose = e_source_combo_box_dispose; object_class->finalize = e_source_combo_box_finalize; g_object_class_install_property ( object_class, PROP_SOURCE_LIST, g_param_spec_object ( "source-list", "source-list", "List of sources to choose from", E_TYPE_SOURCE_LIST, G_PARAM_READWRITE)); } static void e_source_combo_box_init (ESourceComboBox *source_combo_box) { source_combo_box->priv = E_SOURCE_COMBO_BOX_GET_PRIVATE (source_combo_box); source_combo_box->priv->uid_index = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) gtk_tree_row_reference_free); } GType e_source_combo_box_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GTypeInfo type_info = { sizeof (ESourceComboBoxClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) e_source_combo_box_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (ESourceComboBox), 0, /* n_preallocs */ (GInstanceInitFunc) e_source_combo_box_init, NULL /* value_table */ }; type = g_type_register_static ( GTK_TYPE_COMBO_BOX, "ESourceComboBox", &type_info, 0); } return type; } /** * e_source_combo_box_new: * @source_list: an #ESourceList * * Creates a new #ESourceComboBox widget that lets the user pick an #ESource * from the provided #ESourceList. * * Returns: a new #ESourceComboBox **/ GtkWidget * e_source_combo_box_new (ESourceList *source_list) { g_return_val_if_fail (E_IS_SOURCE_LIST (source_list), NULL); return g_object_new ( E_TYPE_SOURCE_COMBO_BOX, "source-list", source_list, NULL); } /** * e_source_combo_box_get_source_list: * @source_combo_box: an #ESourceComboBox * * Returns the #ESourceList which is acting as a data source for * @source_combo_box. * * Returns: an #ESourceList **/ ESourceList * e_source_combo_box_get_source_list (ESourceComboBox *source_combo_box) { ESourceList *source_list; g_return_val_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box), NULL); g_object_get (source_combo_box, "source-list", &source_list, NULL); return source_list; } /** * e_source_combo_box_set_source_list: * @source_combo_box: an #ESourceComboBox * @source_list: an #ESourceList * * Sets the source list used by @source_combo_box to be @source_list. This * causes the contents of @source_combo_box to be regenerated. **/ void e_source_combo_box_set_source_list (ESourceComboBox *source_combo_box, ESourceList *source_list) { g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box)); g_return_if_fail (E_IS_SOURCE_LIST (source_list)); g_object_set (source_combo_box, "source-list", source_list, NULL); } /** * e_source_combo_box_get_active: * @source_combo_box: an #ESourceComboBox * * Returns the #ESource corresponding to the currently active item, or %NULL * if there is no active item. * * Returns: an #ESource or %NULL **/ ESource * e_source_combo_box_get_active (ESourceComboBox *source_combo_box) { GtkComboBox *combo_box; GtkTreeIter iter; ESource *source; g_return_val_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box), NULL); combo_box = GTK_COMBO_BOX (source_combo_box); if (!gtk_combo_box_get_active_iter (combo_box, &iter)) return NULL; gtk_tree_model_get ( gtk_combo_box_get_model (combo_box), &iter, COLUMN_SOURCE, &source, -1); return source; } /** * e_source_combo_box_set_active: * @source_combo_box: an #ESourceComboBox * @source: an #ESource * * Sets the active item to the one corresponding to @source. **/ void e_source_combo_box_set_active (ESourceComboBox *source_combo_box, ESource *source) { g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box)); g_return_if_fail (E_IS_SOURCE (source)); e_source_combo_box_set_active_uid ( source_combo_box, e_source_peek_uid (source)); } /** * e_source_combo_box_get_active_uid: * @source_combo_box: an #ESourceComboBox * * Returns the unique ID of the #ESource corresponding to the currently * active item, or %NULL if there is no active item. * * Returns: a unique ID string or %NULL **/ const gchar * e_source_combo_box_get_active_uid (ESourceComboBox *source_combo_box) { ESource *source; g_return_val_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box), NULL); source = e_source_combo_box_get_active (source_combo_box); if (source == NULL) return NULL; return e_source_peek_uid (source); } /** * e_source_combo_box_set_active_uid: * @source_combo_box: an #ESourceComboBox * @uid: a unique ID of an #ESource * * Sets the active item to the one corresponding to @uid. **/ void e_source_combo_box_set_active_uid (ESourceComboBox *source_combo_box, const gchar *uid) { ESourceComboBoxPrivate *priv; GtkTreeRowReference *reference; GtkComboBox *combo_box; GtkTreeIter iter; gboolean iter_was_set; g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box)); g_return_if_fail (uid != NULL); priv = source_combo_box->priv; combo_box = GTK_COMBO_BOX (source_combo_box); reference = g_hash_table_lookup (priv->uid_index, uid); g_return_if_fail (gtk_tree_row_reference_valid (reference)); iter_was_set = gtk_tree_model_get_iter ( gtk_combo_box_get_model (combo_box), &iter, gtk_tree_row_reference_get_path (reference)); g_return_if_fail (iter_was_set); gtk_combo_box_set_active_iter (combo_box, &iter); }