diff options
author | Ettore Perazzoli <ettore@src.gnome.org> | 2003-10-22 02:28:34 +0800 |
---|---|---|
committer | Ettore Perazzoli <ettore@src.gnome.org> | 2003-10-22 02:28:34 +0800 |
commit | 0fb08f3ff81575a4749d851404233f34252dd2f2 (patch) | |
tree | 7e03befedc3a76fd104921dbbc616810d87333be /widgets/misc/e-source-selector.c | |
parent | 0e19f2c16de592607a341eb9974d31e4e47e02b5 (diff) | |
download | gsoc2013-evolution-0fb08f3ff81575a4749d851404233f34252dd2f2.tar.gz gsoc2013-evolution-0fb08f3ff81575a4749d851404233f34252dd2f2.tar.zst gsoc2013-evolution-0fb08f3ff81575a4749d851404233f34252dd2f2.zip |
Merge new-ui-branch to the trunk.
svn path=/trunk/; revision=22964
Diffstat (limited to 'widgets/misc/e-source-selector.c')
-rw-r--r-- | widgets/misc/e-source-selector.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/widgets/misc/e-source-selector.c b/widgets/misc/e-source-selector.c new file mode 100644 index 0000000000..5bc12bcd39 --- /dev/null +++ b/widgets/misc/e-source-selector.c @@ -0,0 +1,682 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-source-selector.c + * + * Copyright (C) 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 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. + * + * Author: Ettore Perazzoli <ettore@ximian.com> + */ + +#include <config.h> + +#include "e-source-selector.h" + +#include "e-util-marshal.h" + +#include <gal/util/e-util.h> + +#include <gtk/gtktreeselection.h> +#include <gtk/gtktreestore.h> +#include <gtk/gtkcellrenderertoggle.h> +#include <gtk/gtkcellrenderertext.h> + + +#define PARENT_TYPE gtk_tree_view_get_type () +static GtkTreeViewClass *parent_class = NULL; + + +struct _ESourceSelectorPrivate { + ESourceList *list; + + GtkTreeStore *tree_store; + + GHashTable *selected_sources; + + int rebuild_model_idle_id; + + gboolean checkboxes_shown; +}; + + +enum { + SELECTION_CHANGED, + PRIMARY_SELECTION_CHANGED, + NUM_SIGNALS +}; +static unsigned int signals[NUM_SIGNALS] = { 0 }; + + +/* Selection management. */ + +static GHashTable * +create_selected_sources_hash (void) +{ + return g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) g_object_unref, NULL); +} + +static gboolean +source_is_selected (ESourceSelector *selector, + ESource *source) +{ + if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL) + return FALSE; + else + return TRUE; +} + +static void +select_source (ESourceSelector *selector, + ESource *source) +{ + if (g_hash_table_lookup (selector->priv->selected_sources, source) != NULL) + return; + + g_hash_table_insert (selector->priv->selected_sources, source, source); + g_object_ref (source); +} + +static void +unselect_source (ESourceSelector *selector, + ESource *source) +{ + if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL) + return; + + /* (This will unref the source.) */ + g_hash_table_remove (selector->priv->selected_sources, source); +} + + +/* Setting up the model. */ + +static void +rebuild_model (ESourceSelector *selector) +{ + GtkTreeStore *tree_store; + GtkTreeIter iter; + GSList *groups, *p; + GHashTable *new_selected_sources; + + new_selected_sources = create_selected_sources_hash (); + + tree_store = selector->priv->tree_store; + gtk_tree_store_clear (GTK_TREE_STORE (tree_store)); + + groups = e_source_list_peek_groups (selector->priv->list); + for (p = groups; p != NULL; p = p->next) { + ESourceGroup *group = E_SOURCE_GROUP (p->data); + GSList *sources, *q; + + gtk_tree_store_append (GTK_TREE_STORE (tree_store), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (tree_store), &iter, 0, group, -1); + + sources = e_source_group_peek_sources (group); + for (q = sources; q != NULL; q = q->next) { + ESource *source = E_SOURCE (q->data); + GtkTreeIter child_iter; + + gtk_tree_store_append (GTK_TREE_STORE (tree_store), &child_iter, &iter); + gtk_tree_store_set (GTK_TREE_STORE (tree_store), &child_iter, 0, source, -1); + + if (g_hash_table_lookup (selector->priv->selected_sources, source) != NULL) { + g_hash_table_insert (new_selected_sources, source, source); + g_object_ref (source); + } + } + } + + /* Since new_selected_sources is a subset of selected_sources, we can tell whether the overall + selection has changed just by comparing the sizes of the hash tables. */ + if (g_hash_table_size (selector->priv->selected_sources) != g_hash_table_size (new_selected_sources)) + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); + + g_hash_table_destroy (selector->priv->selected_sources); + selector->priv->selected_sources = new_selected_sources; +} + +static int +on_idle_rebuild_model_callback (ESourceSelector *selector) +{ + rebuild_model (selector); + selector->priv->rebuild_model_idle_id = 0; + + return FALSE; +} + +static void +list_changed_callback (ESourceList *list, + ESourceSelector *selector) +{ + ESourceSelectorPrivate *priv = selector->priv; + + if (priv->rebuild_model_idle_id == 0) + priv->rebuild_model_idle_id = g_idle_add ((GSourceFunc) on_idle_rebuild_model_callback, + selector); +} + +static void +setup_model (ESourceSelector *selector) +{ + rebuild_model (selector); + + g_signal_connect_object (selector->priv->list, "changed", G_CALLBACK (list_changed_callback), G_OBJECT (selector), 0); +} + + +/* Data functions for rendering the model. */ + +static void +toggle_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + ESourceSelector *selector) +{ + void *data; + + gtk_tree_model_get (model, iter, 0, &data, -1); + + if (E_IS_SOURCE_GROUP (data)) { + g_object_set (renderer, "visible", FALSE, NULL); + } else { + g_assert (E_IS_SOURCE (data)); + + g_object_set (renderer, "visible", selector->priv->checkboxes_shown, NULL); + if (source_is_selected (selector, E_SOURCE (data))) + g_object_set (renderer, "active", TRUE, NULL); + else + g_object_set (renderer, "active", FALSE, NULL); + } +} + +static void +text_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + ESourceSelector *selector) +{ + void *data; + + gtk_tree_model_get (model, iter, 0, &data, -1); + + if (E_IS_SOURCE_GROUP (data)) { + g_object_set (renderer, + "text", e_source_group_peek_name (E_SOURCE_GROUP (data)), + "weight", PANGO_WEIGHT_BOLD, + "foreground_set", FALSE, + NULL); + } else { + ESource *source; + guint32 color; + gboolean has_color; + + g_assert (E_IS_SOURCE (data)); + source = E_SOURCE (data); + + g_object_set (renderer, + "text", e_source_peek_name (source), + "weight", PANGO_WEIGHT_NORMAL, + NULL); + + has_color = e_source_get_color (source, &color); + if (!has_color) { + g_object_set (renderer, + "foreground_set", FALSE, + NULL); + } else { + char *color_string = g_strdup_printf ("#%06x", color); + g_object_set (renderer, + "foreground_set", TRUE, + "foreground", color_string, + NULL); + g_free (color_string); + } + } +} + +/* Custom selection function to make groups non selectable. */ +static gboolean +selection_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + ESourceSelector *selector) +{ + GtkTreeIter iter; + void *data; + + if (! gtk_tree_model_get_iter (model, &iter, path)) + return FALSE; + + gtk_tree_model_get (model, &iter, 0, &data, -1); + if (E_IS_SOURCE_GROUP (data)) + return FALSE; + else + return TRUE; +} + + +/* Callbacks. */ + +static void +cell_toggled_callback (GtkCellRendererToggle *renderer, + const char *path_string, + ESourceSelector *selector) +{ + GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store); + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + GtkTreeIter iter; + ESource *source; + void *data; + + if (! gtk_tree_model_get_iter (model, &iter, path)) { + gtk_tree_path_free (path); + return; + } + + gtk_tree_model_get (model, &iter, 0, &data, -1); + if (! E_IS_SOURCE (data)) { + gtk_tree_path_free (path); + return; + } + + source = E_SOURCE (data); + if (source_is_selected (selector, source)) + unselect_source (selector, source); + else + select_source (selector, source); + + gtk_tree_model_row_changed (model, path, &iter); + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); + + gtk_tree_path_free (path); +} + +static void +selection_changed_callback (GtkTreeSelection *selection, + ESourceSelector *selector) +{ + g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); +} + + +/* GObject methods. */ + +static void +impl_dispose (GObject *object) +{ + ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv; + + if (priv->selected_sources != NULL) { + g_hash_table_destroy (priv->selected_sources); + priv->selected_sources = NULL; + } + + if (priv->rebuild_model_idle_id != 0) { + g_source_remove (priv->rebuild_model_idle_id); + priv->rebuild_model_idle_id = 0; + } + + if (priv->list != NULL) { + g_object_unref (priv->list); + priv->list = NULL; + } + + if (priv->tree_store != NULL) { + g_object_unref (priv->tree_store); + priv->tree_store = NULL; + } + + (* G_OBJECT_CLASS (parent_class)->dispose) (object); +} + +static void +impl_finalize (GObject *object) +{ + ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv; + + g_free (priv); + + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + + +/* Initialization. */ + +static void +class_init (ESourceSelectorClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = impl_dispose; + object_class->finalize = impl_finalize; + + parent_class = g_type_class_peek_parent (class); + + signals[SELECTION_CHANGED] = + g_signal_new ("selection_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed), + NULL, NULL, + e_util_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[PRIMARY_SELECTION_CHANGED] = + g_signal_new ("primary_selection_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed), + NULL, NULL, + e_util_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +init (ESourceSelector *selector) +{ + ESourceSelectorPrivate *priv; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeSelection *selection; + + priv = g_new0 (ESourceSelectorPrivate, 1); + selector->priv = priv; + + priv->checkboxes_shown = TRUE; + + priv->selected_sources = create_selected_sources_hash (); + + priv->tree_store = gtk_tree_store_new (1, G_TYPE_POINTER); + gtk_tree_view_set_model (GTK_TREE_VIEW (selector), GTK_TREE_MODEL (priv->tree_store)); + + column = gtk_tree_view_column_new (); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + cell_renderer = gtk_cell_renderer_toggle_new (); + gtk_tree_view_column_pack_start (column, cell_renderer, FALSE); + gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) toggle_cell_data_func, selector, NULL); + g_signal_connect (cell_renderer, "toggled", G_CALLBACK (cell_toggled_callback), selector); + + cell_renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, cell_renderer, TRUE); + gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) text_cell_data_func, selector, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + gtk_tree_selection_set_select_function (selection, (GtkTreeSelectionFunc) selection_func, selector, NULL); + g_signal_connect_object (selection, "changed", G_CALLBACK (selection_changed_callback), G_OBJECT (selector), 0); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (selector), FALSE); +} + + +/* Public API. */ + +/** + * e_source_selector_new: + * @list: A source list. + * + * Create a new view for @list. The view will update automatically when @list + * changes. + * + * Return value: The newly created widget. + **/ +GtkWidget * +e_source_selector_new (ESourceList *list) +{ + ESourceSelector *selector; + + g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL); + + selector = g_object_new (e_source_selector_get_type (), NULL); + + selector->priv->list = list; + g_object_ref (list); + + setup_model (selector); + + return GTK_WIDGET (selector); +} + + +/** + * e_source_selector_get_selection: + * @selector: + * + * Get the list of selected sources, i.e. those that were enabled through the + * corresponding checkboxes in the tree. + * + * Return value: A list of the ESources currently selected. The sources will + * be in the same order as they appear on the screen, and the list should be + * freed using e_source_selector_free_selection(). + **/ +GSList * +e_source_selector_get_selection (ESourceSelector *selector) +{ + GSList *selection_list; + GSList *groups; + GSList *p; + + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); + + selection_list = NULL; + + groups = e_source_list_peek_groups (selector->priv->list); + for (p = groups; p != NULL; p = p->next) { + ESourceGroup *group = E_SOURCE_GROUP (p->data); + GSList *sources; + GSList *q; + + sources = e_source_group_peek_sources (group); + for (q = sources; q != NULL; q = q->next) { + ESource *source = E_SOURCE (q->data); + + if (source_is_selected (selector, source)) { + selection_list = g_slist_prepend (selection_list, source); + g_object_ref (source); + } + } + } + + return g_slist_reverse (selection_list); +} + +/** + * e_source_list_free_selection: + * @list: A selection list returned by e_source_selector_get_selection(). + * + * Free the selection list. + **/ +void +e_source_selector_free_selection (GSList *list) +{ + g_slist_foreach (list, (GFunc) g_object_unref, NULL); + g_slist_free (list); +} + + +/** + * e_source_selector_show_selection: + * @selector: An ESourceSelector widget + * + * Specify whether the checkboxes in the ESourceSelector should be shown or + * not. + **/ +void +e_source_selector_show_selection (ESourceSelector *selector, + gboolean show) +{ + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); + + show = !! show; + if (show == selector->priv->checkboxes_shown) + return; + + selector->priv->checkboxes_shown = show; + + gtk_tree_model_foreach (GTK_TREE_MODEL (selector->priv->tree_store), + (GtkTreeModelForeachFunc) gtk_tree_model_row_changed, + NULL); +} + +/** + * e_source_selector_selection_shown: + * @selector: + * + * Check whether the checkboxes in the ESourceSelector are being shown or not. + * + * Return value: %TRUE if the checkboxes are shown, %FALSE otherwise. + **/ +gboolean +e_source_selector_selection_shown (ESourceSelector *selector) +{ + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); + + return selector->priv->checkboxes_shown; +} + +/** + * e_source_selector_select_source: + * @selector: An ESourceSelector widget + * @source: An ESource. + * + * Select @source in @selector. + **/ +void +e_source_selector_select_source (ESourceSelector *selector, + ESource *source) +{ + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); + g_return_if_fail (E_IS_SOURCE (source)); + + if (source_is_selected (selector, source)) + return; + + select_source (selector, source); + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); +} + +/** + * e_source_selector_unselect_source: + * @selector: An ESourceSelector widget + * @source: An ESource. + * + * Unselect @source in @selector. + **/ +void +e_source_selector_unselect_source (ESourceSelector *selector, + ESource *source) +{ + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); + g_return_if_fail (E_IS_SOURCE (source)); + + if (! source_is_selected (selector, source)) + return; + + unselect_source (selector, source); + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); +} + +/** + * e_source_selector_source_is_selected: + * @selector: An ESourceSelector widget + * @source: An ESource. + * + * Check whether @source is selected in @selector. + * + * Return value: %TRUE if @source is currently selected, %FALSE otherwise. + **/ +gboolean +e_source_selector_source_is_selected (ESourceSelector *selector, + ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + return source_is_selected (selector, source); +} + +/** + * e_source_selector_peek_primary_selection: + * @selector: An ESourceSelector widget + * + * Get the primary selected source. The primary selection is the one that is + * highlighted through the normal GtkTreeView selection mechanism (as opposed + * to the "normal" selection, which is the set of source whose checkboxes are + * checked). + * + * Return value: The selected source. + **/ +ESource * +e_source_selector_peek_primary_selection (ESourceSelector *selector) +{ + GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store); + GtkTreeIter iter; + void *data; + + if (! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &iter)) + return NULL; + + gtk_tree_model_get (model, &iter, 0, &data, -1); + if (! E_IS_SOURCE (data)) + return NULL; + + return E_SOURCE (data); +} + +/** + * e_source_selector_set_primary_selection: + * @selector: An ESourceSelector widget + * @source: Source to select + * + * Set the primary selected source. + **/ +void +e_source_selector_set_primary_selection (ESourceSelector *selector, + ESource *source) +{ + GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + GtkTreeIter children_iter; + + if (gtk_tree_model_iter_children (model, &children_iter, &iter)) { + do { + void *data; + + gtk_tree_model_get (model, &iter, 0, &data, -1); + g_assert (E_IS_SOURCE (data)); + + if (E_SOURCE (data) == source) { + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selection)); + gtk_tree_selection_select_iter (selection, &children_iter); + return; + } + } while (gtk_tree_model_iter_next (model, &iter)); + } + } while (gtk_tree_model_iter_next (model, &iter)); + } + + g_warning (G_GNUC_FUNCTION ": Cannot find source %p (%s) in selector %p", source, e_source_peek_name (source), selector); +} + + +E_MAKE_TYPE (e_source_selector, "ESourceSelector", ESourceSelector, class_init, init, PARENT_TYPE) |