diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2012-12-10 21:09:59 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2012-12-13 03:33:43 +0800 |
commit | d09d8de870b6697c8a8b262e7e077b871a69b315 (patch) | |
tree | 3b718882e7a0bb0a996daf2967a033d91714c9b5 /e-util/e-categories-selector.c | |
parent | b61331ed03ac1c7a9b8614e25510040b9c60ae02 (diff) | |
download | gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.gz gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.zst gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.zip |
Consolidate base utility libraries into libeutil.
Evolution consists of entirely too many small utility libraries, which
increases linking and loading time, places a burden on higher layers of
the application (e.g. modules) which has to remember to link to all the
small in-tree utility libraries, and makes it difficult to generate API
documentation for these utility libraries in one Gtk-Doc module.
Merge the following utility libraries under the umbrella of libeutil,
and enforce a single-include policy on libeutil so we can reorganize
the files as desired without disrupting its pseudo-public API.
libemail-utils/libemail-utils.la
libevolution-utils/libevolution-utils.la
filter/libfilter.la
widgets/e-timezone-dialog/libetimezonedialog.la
widgets/menus/libmenus.la
widgets/misc/libemiscwidgets.la
widgets/table/libetable.la
widgets/text/libetext.la
This also merges libedataserverui from the Evolution-Data-Server module,
since Evolution is its only consumer nowadays, and I'd like to make some
improvements to those APIs without concern for backward-compatibility.
And finally, start a Gtk-Doc module for libeutil. It's going to be a
project just getting all the symbols _listed_ much less _documented_.
But the skeletal structure is in place and I'm off to a good start.
Diffstat (limited to 'e-util/e-categories-selector.c')
-rw-r--r-- | e-util/e-categories-selector.c | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/e-util/e-categories-selector.c b/e-util/e-categories-selector.c new file mode 100644 index 0000000000..5a05238626 --- /dev/null +++ b/e-util/e-categories-selector.c @@ -0,0 +1,587 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include <libedataserver/libedataserver.h> + +#include "e-categories-selector.h" + +#define E_CATEGORIES_SELECTOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CATEGORIES_SELECTOR, ECategoriesSelectorPrivate)) + +struct _ECategoriesSelectorPrivate { + gboolean checkable; + GHashTable *selected_categories; + + gboolean ignore_category_changes; +}; + +enum { + PROP_0, + PROP_ITEMS_CHECKABLE +}; + +enum { + CATEGORY_CHECKED, + SELECTION_CHANGED, + LAST_SIGNAL +}; + +enum { + COLUMN_ACTIVE, + COLUMN_ICON, + COLUMN_CATEGORY, + N_COLUMNS +}; + +static gint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE ( + ECategoriesSelector, + e_categories_selector, + GTK_TYPE_TREE_VIEW) + +static void +categories_selector_build_model (ECategoriesSelector *selector) +{ + GtkListStore *store; + GList *list, *iter; + + store = gtk_list_store_new ( + N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING); + + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (store), + COLUMN_CATEGORY, GTK_SORT_ASCENDING); + + list = e_categories_get_list (); + for (iter = list; iter != NULL; iter = iter->next) { + const gchar *category_name = iter->data; + const gchar *filename; + GdkPixbuf *pixbuf = NULL; + GtkTreeIter iter; + gboolean active; + + /* Only add user-visible categories. */ + if (!e_categories_is_searchable (category_name)) + continue; + + active = (g_hash_table_lookup ( + selector->priv->selected_categories, + category_name) != NULL); + + filename = e_categories_get_icon_file_for (category_name); + if (filename != NULL) + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + + gtk_list_store_append (store, &iter); + + gtk_list_store_set ( + store, &iter, + COLUMN_ACTIVE, active, + COLUMN_ICON, pixbuf, + COLUMN_CATEGORY, category_name, + -1); + + if (pixbuf != NULL) + g_object_unref (pixbuf); + } + + gtk_tree_view_set_model ( + GTK_TREE_VIEW (selector), GTK_TREE_MODEL (store)); + + /* This has to be reset everytime we install a new model */ + gtk_tree_view_set_search_column ( + GTK_TREE_VIEW (selector), COLUMN_CATEGORY); + + g_list_free (list); + g_object_unref (store); +} + +static void +category_toggled_cb (GtkCellRenderer *renderer, + const gchar *path, + ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreePath *tree_path; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_if_fail (model); + + tree_path = gtk_tree_path_new_from_string (path); + g_return_if_fail (tree_path); + + if (gtk_tree_model_get_iter (model, &iter, tree_path)) { + gchar *category; + gboolean active; + + gtk_tree_model_get ( + model, &iter, + COLUMN_ACTIVE, &active, + COLUMN_CATEGORY, &category, -1); + + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_ACTIVE, !active, -1); + + if (active) + g_hash_table_remove ( + selector->priv->selected_categories, category); + else + g_hash_table_insert ( + selector->priv->selected_categories, + g_strdup (category), g_strdup (category)); + + g_signal_emit ( + selector, signals[CATEGORY_CHECKED], 0, + category, !active); + + g_free (category); + } + + gtk_tree_path_free (tree_path); +} + +static void +categories_selector_listener_cb (gpointer useless_pointer, + ECategoriesSelector *selector) +{ + if (!selector->priv->ignore_category_changes) + categories_selector_build_model (selector); +} + +static gboolean +categories_selector_key_press_event (ECategoriesSelector *selector, + GdkEventKey *event) +{ + if (event->keyval == GDK_KEY_Delete) { + e_categories_selector_delete_selection (selector); + return TRUE; + } + + return FALSE; +} + +static void +categories_selector_selection_changed (GtkTreeSelection *selection, + ECategoriesSelector *selector) +{ + g_signal_emit (selector, signals[SELECTION_CHANGED], 0, selection); +} + +static void +categories_selector_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ITEMS_CHECKABLE: + g_value_set_boolean ( + value, + e_categories_selector_get_items_checkable ( + E_CATEGORIES_SELECTOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +categories_selector_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ITEMS_CHECKABLE: + e_categories_selector_set_items_checkable ( + E_CATEGORIES_SELECTOR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +categories_selector_dispose (GObject *object) +{ + ECategoriesSelectorPrivate *priv; + + priv = E_CATEGORIES_SELECTOR_GET_PRIVATE (object); + + if (priv->selected_categories != NULL) { + g_hash_table_destroy (priv->selected_categories); + priv->selected_categories = NULL; + } + + /* Chain up to parent's dispose() method.*/ + G_OBJECT_CLASS (e_categories_selector_parent_class)->dispose (object); +} + +static void +categories_selector_finalize (GObject *object) +{ + e_categories_unregister_change_listener ( + G_CALLBACK (categories_selector_listener_cb), object); +} + +static void +e_categories_selector_class_init (ECategoriesSelectorClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ECategoriesSelectorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = categories_selector_set_property; + object_class->get_property = categories_selector_get_property; + object_class->dispose = categories_selector_dispose; + object_class->finalize = categories_selector_finalize; + + g_object_class_install_property ( + object_class, + PROP_ITEMS_CHECKABLE, + g_param_spec_boolean ( + "items-checkable", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + signals[CATEGORY_CHECKED] = g_signal_new ( + "category-checked", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ECategoriesSelectorClass, category_checked), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + signals[SELECTION_CHANGED] = g_signal_new ( + "selection-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ECategoriesSelectorClass, selection_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_SELECTION); +} + +static void +e_categories_selector_init (ECategoriesSelector *selector) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + selector->priv = E_CATEGORIES_SELECTOR_GET_PRIVATE (selector); + + selector->priv->checkable = TRUE; + selector->priv->selected_categories = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + selector->priv->ignore_category_changes = FALSE; + + renderer = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes ( + "?", renderer, "active", COLUMN_ACTIVE, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + g_signal_connect ( + renderer, "toggled", + G_CALLBACK (category_toggled_cb), selector); + + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes ( + _("Icon"), renderer, "pixbuf", COLUMN_ICON, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ( + _("Category"), renderer, "text", COLUMN_CATEGORY, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + g_signal_connect ( + selection, "changed", + G_CALLBACK (categories_selector_selection_changed), selector); + + g_signal_connect ( + selector, "key-press-event", + G_CALLBACK (categories_selector_key_press_event), NULL); + + e_categories_register_change_listener ( + G_CALLBACK (categories_selector_listener_cb), selector); + + categories_selector_build_model (selector); +} + +/** + * e_categories_selector_new: + * + * Since: 3.2 + **/ +GtkWidget * +e_categories_selector_new (void) +{ + return g_object_new ( + E_TYPE_CATEGORIES_SELECTOR, + "items-checkable", TRUE, NULL); +} + +/** + * e_categories_selector_get_items_checkable: + * + * Since: 3.2 + **/ +gboolean +e_categories_selector_get_items_checkable (ECategoriesSelector *selector) +{ + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), TRUE); + + return selector->priv->checkable; +} + +/** + * e_categories_selector_set_items_checkable: + * + * Since: 3.2 + **/ +void +e_categories_selector_set_items_checkable (ECategoriesSelector *selector, + gboolean checkable) +{ + GtkTreeViewColumn *column; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + if ((selector->priv->checkable ? 1 : 0) == (checkable ? 1 : 0)) + return; + + selector->priv->checkable = checkable; + + column = gtk_tree_view_get_column ( + GTK_TREE_VIEW (selector), COLUMN_ACTIVE); + gtk_tree_view_column_set_visible (column, checkable); + + g_object_notify (G_OBJECT (selector), "items-checkable"); +} + +/** + * e_categories_selector_get_checked: + * + * Free returned pointer with g_free(). + * + * Since: 3.2 + **/ +gchar * +e_categories_selector_get_checked (ECategoriesSelector *selector) +{ + GString *str; + GList *list, *category; + + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), NULL); + + str = g_string_new (""); + list = g_hash_table_get_values (selector->priv->selected_categories); + + /* to get them always in the same order */ + list = g_list_sort (list, (GCompareFunc) g_utf8_collate); + + for (category = list; category != NULL; category = category->next) { + if (str->len > 0) + g_string_append_printf ( + str, ",%s", (gchar *) category->data); + else + g_string_append (str, (gchar *) category->data); + } + + g_list_free (list); + + return g_string_free (str, FALSE); +} + +/** + * e_categories_selector_set_checked: + * + * Since: 3.2 + **/ +void +e_categories_selector_set_checked (ECategoriesSelector *selector, + const gchar *categories) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar **arr; + gint i; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + /* Clean up table of selected categories. */ + g_hash_table_remove_all (selector->priv->selected_categories); + + arr = g_strsplit (categories, ",", 0); + if (arr) { + for (i = 0; arr[i] != NULL; i++) { + g_strstrip (arr[i]); + g_hash_table_insert ( + selector->priv->selected_categories, + g_strdup (arr[i]), g_strdup (arr[i])); + } + g_strfreev (arr); + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + gchar *category_name; + gboolean found; + + gtk_tree_model_get ( + model, &iter, + COLUMN_CATEGORY, &category_name, + -1); + found = (g_hash_table_lookup ( + selector->priv->selected_categories, + category_name) != NULL); + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_ACTIVE, found, -1); + + g_free (category_name); + } while (gtk_tree_model_iter_next (model, &iter)); + } +} + +/** + * e_categories_selector_delete_selection: + * + * Since: 3.2 + **/ +void +e_categories_selector_delete_selection (ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GList *selected, *item; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_if_fail (model != NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + selected = gtk_tree_selection_get_selected_rows (selection, &model); + + /* Remove categories in reverse order to avoid invalidating + * tree paths as we iterate over the list. Note, the list is + * probably already sorted but we sort again just to be safe. */ + selected = g_list_reverse (g_list_sort ( + selected, (GCompareFunc) gtk_tree_path_compare)); + + /* Prevent the model from being rebuilt every time we + * remove a category, since we're already modifying it. */ + selector->priv->ignore_category_changes = TRUE; + + for (item = selected; item != NULL; item = item->next) { + GtkTreePath *path = item->data; + GtkTreeIter iter; + gchar *category; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get ( + model, &iter, + COLUMN_CATEGORY, &category, -1); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + e_categories_remove (category); + g_free (category); + } + + selector->priv->ignore_category_changes = FALSE; + + /* If we only remove one category, try to select another */ + if (g_list_length (selected) == 1) { + GtkTreePath *path = selected->data; + + gtk_tree_selection_select_path (selection, path); + if (!gtk_tree_selection_path_is_selected (selection, path)) + if (gtk_tree_path_prev (path)) + gtk_tree_selection_select_path (selection, path); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); +} + +/** + * e_categories_selector_get_selected: + * + * Free returned pointer with g_free(). + * + * Since: 3.2 + **/ +gchar * +e_categories_selector_get_selected (ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GList *selected, *item; + GString *str = g_string_new (""); + + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), NULL); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_val_if_fail (model != NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + selected = gtk_tree_selection_get_selected_rows (selection, &model); + + for (item = selected; item != NULL; item = item->next) { + GtkTreePath *path = item->data; + GtkTreeIter iter; + gchar *category; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get ( + model, &iter, + COLUMN_CATEGORY, &category, -1); + if (str->len == 0) + g_string_assign (str, category); + else + g_string_append_printf (str, ",%s", category); + + g_free (category); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + + return g_string_free (str, FALSE); +} |