aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-action-combo-box.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-12-10 21:09:59 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-12-13 03:33:43 +0800
commitd09d8de870b6697c8a8b262e7e077b871a69b315 (patch)
tree3b718882e7a0bb0a996daf2967a033d91714c9b5 /e-util/e-action-combo-box.c
parentb61331ed03ac1c7a9b8614e25510040b9c60ae02 (diff)
downloadgsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.gz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.bz2
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.lz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.xz
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-action-combo-box.c')
-rw-r--r--e-util/e-action-combo-box.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/e-util/e-action-combo-box.c b/e-util/e-action-combo-box.c
new file mode 100644
index 0000000000..0747a6ed27
--- /dev/null
+++ b/e-util/e-action-combo-box.c
@@ -0,0 +1,578 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-action-combo-box.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 <config.h>
+#endif
+
+#include "e-action-combo-box.h"
+
+#include <glib/gi18n.h>
+
+#define E_ACTION_COMBO_BOX_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTION_COMBO_BOX, EActionComboBoxPrivate))
+
+enum {
+ COLUMN_ACTION,
+ COLUMN_SORT
+};
+
+enum {
+ PROP_0,
+ PROP_ACTION
+};
+
+struct _EActionComboBoxPrivate {
+ GtkRadioAction *action;
+ GtkActionGroup *action_group;
+ GHashTable *index;
+ guint changed_handler_id; /* action::changed */
+ guint group_sensitive_handler_id; /* action-group::sensitive */
+ guint group_visible_handler_id; /* action-group::visible */
+ gboolean group_has_icons : 1;
+};
+
+G_DEFINE_TYPE (
+ EActionComboBox,
+ e_action_combo_box,
+ GTK_TYPE_COMBO_BOX)
+
+static void
+action_combo_box_action_changed_cb (GtkRadioAction *action,
+ GtkRadioAction *current,
+ EActionComboBox *combo_box)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ reference = g_hash_table_lookup (
+ combo_box->priv->index, GINT_TO_POINTER (
+ gtk_radio_action_get_current_value (current)));
+ g_return_if_fail (reference != NULL);
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ valid = gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ g_return_if_fail (valid);
+
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+}
+
+static void
+action_combo_box_action_group_notify_cb (GtkActionGroup *action_group,
+ GParamSpec *pspec,
+ EActionComboBox *combo_box)
+{
+ g_object_set (
+ combo_box, "sensitive",
+ gtk_action_group_get_sensitive (action_group), "visible",
+ gtk_action_group_get_visible (action_group), NULL);
+}
+
+static void
+action_combo_box_render_pixbuf (GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ EActionComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ gchar *icon_name;
+ gchar *stock_id;
+ gboolean sensitive;
+ gboolean visible;
+ gint width;
+
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+
+ /* Do any of the actions have an icon? */
+ if (!combo_box->priv->group_has_icons)
+ return;
+
+ /* A NULL action means the row is a separator. */
+ if (action == NULL)
+ return;
+
+ g_object_get (
+ G_OBJECT (action),
+ "icon-name", &icon_name,
+ "sensitive", &sensitive,
+ "stock-id", &stock_id,
+ "visible", &visible,
+ NULL);
+
+ /* Keep the pixbuf renderer a fixed size for proper alignment. */
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+
+ /* We can't set both "icon-name" and "stock-id" because setting
+ * one unsets the other. So pick the one that has a non-NULL
+ * value. If both are non-NULL, "stock-id" wins. */
+
+ if (stock_id != NULL)
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "icon-name", NULL,
+ "stock-id", stock_id,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ "visible", visible,
+ "width", width,
+ NULL);
+ else
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "icon-name", icon_name,
+ "stock-id", NULL,
+ "stock-size", GTK_ICON_SIZE_MENU,
+ "visible", visible,
+ "width", width,
+ NULL);
+
+ g_free (icon_name);
+ g_free (stock_id);
+}
+
+static void
+action_combo_box_render_text (GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ EActionComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ gchar **strv;
+ gchar *label;
+ gboolean sensitive;
+ gboolean visible;
+ gint xpad;
+
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+
+ /* A NULL action means the row is a separator. */
+ if (action == NULL)
+ return;
+
+ g_object_get (
+ G_OBJECT (action),
+ "label", &label,
+ "sensitive", &sensitive,
+ "visible", &visible,
+ NULL);
+
+ /* Strip out underscores. */
+ strv = g_strsplit (label, "_", -1);
+ g_free (label);
+ label = g_strjoinv (NULL, strv);
+ g_strfreev (strv);
+
+ xpad = combo_box->priv->group_has_icons ? 3 : 0;
+
+ g_object_set (
+ G_OBJECT (renderer),
+ "sensitive", sensitive,
+ "text", label,
+ "visible", visible,
+ "xpad", xpad,
+ NULL);
+
+ g_free (label);
+}
+
+static gboolean
+action_combo_box_is_row_separator (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkAction *action;
+ gboolean separator;
+
+ /* NULL actions are rendered as separators. */
+ gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
+ separator = (action == NULL);
+ if (action != NULL)
+ g_object_unref (action);
+
+ return separator;
+}
+
+static void
+action_combo_box_update_model (EActionComboBox *combo_box)
+{
+ GtkListStore *list_store;
+ GSList *list;
+
+ g_hash_table_remove_all (combo_box->priv->index);
+
+ if (combo_box->priv->action == NULL) {
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), NULL);
+ return;
+ }
+
+ /* We store values in the sort column as floats so that we can
+ * insert separators in between consecutive integer values and
+ * still maintain the proper ordering. */
+ list_store = gtk_list_store_new (
+ 2, GTK_TYPE_RADIO_ACTION, G_TYPE_FLOAT);
+
+ list = gtk_radio_action_get_group (combo_box->priv->action);
+ combo_box->priv->group_has_icons = FALSE;
+
+ while (list != NULL) {
+ GtkTreeRowReference *reference;
+ GtkRadioAction *action = list->data;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gchar *icon_name;
+ gchar *stock_id;
+ gint value;
+
+ g_object_get (
+ action, "icon-name", &icon_name,
+ "stock-id", &stock_id, NULL);
+ combo_box->priv->group_has_icons |=
+ (icon_name != NULL || stock_id != NULL);
+ g_free (icon_name);
+ g_free (stock_id);
+
+ gtk_list_store_append (list_store, &iter);
+ g_object_get (action, "value", &value, NULL);
+ gtk_list_store_set (
+ list_store, &iter, COLUMN_ACTION,
+ list->data, COLUMN_SORT, (gfloat) value, -1);
+
+ path = gtk_tree_model_get_path (
+ GTK_TREE_MODEL (list_store), &iter);
+ reference = gtk_tree_row_reference_new (
+ GTK_TREE_MODEL (list_store), path);
+ g_hash_table_insert (
+ combo_box->priv->index,
+ GINT_TO_POINTER (value), reference);
+ gtk_tree_path_free (path);
+
+ list = g_slist_next (list);
+ }
+
+ gtk_tree_sortable_set_sort_column_id (
+ GTK_TREE_SORTABLE (list_store),
+ COLUMN_SORT, GTK_SORT_ASCENDING);
+ gtk_combo_box_set_model (
+ GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store));
+
+ action_combo_box_action_changed_cb (
+ combo_box->priv->action,
+ combo_box->priv->action,
+ combo_box);
+}
+
+static void
+action_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTION:
+ e_action_combo_box_set_action (
+ E_ACTION_COMBO_BOX (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+action_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTION:
+ g_value_set_object (
+ value, e_action_combo_box_get_action (
+ E_ACTION_COMBO_BOX (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+action_combo_box_dispose (GObject *object)
+{
+ EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
+
+ if (priv->action != NULL) {
+ g_object_unref (priv->action);
+ priv->action = NULL;
+ }
+
+ if (priv->action_group != NULL) {
+ g_object_unref (priv->action_group);
+ priv->action_group = NULL;
+ }
+
+ g_hash_table_remove_all (priv->index);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_action_combo_box_parent_class)->dispose (object);
+}
+
+static void
+action_combo_box_finalize (GObject *object)
+{
+ EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->index);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_action_combo_box_parent_class)->finalize (object);
+}
+
+static void
+action_combo_box_constructed (GObject *object)
+{
+ GtkComboBox *combo_box;
+ GtkCellRenderer *renderer;
+
+ combo_box = GTK_COMBO_BOX (object);
+
+ /* This needs to happen after constructor properties are set
+ * so that GtkCellLayout.get_area() returns something valid. */
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (
+ GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
+ gtk_cell_layout_set_cell_data_func (
+ GTK_CELL_LAYOUT (combo_box), renderer,
+ (GtkCellLayoutDataFunc) action_combo_box_render_pixbuf,
+ combo_box, NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (
+ GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
+ gtk_cell_layout_set_cell_data_func (
+ GTK_CELL_LAYOUT (combo_box), renderer,
+ (GtkCellLayoutDataFunc) action_combo_box_render_text,
+ combo_box, NULL);
+
+ gtk_combo_box_set_row_separator_func (
+ combo_box, (GtkTreeViewRowSeparatorFunc)
+ action_combo_box_is_row_separator, NULL, NULL);
+}
+
+static void
+action_combo_box_changed (GtkComboBox *combo_box)
+{
+ GtkRadioAction *action;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint value;
+
+ /* This method is virtual, so no need to chain up. */
+
+ if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+ return;
+
+ model = gtk_combo_box_get_model (combo_box);
+ gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
+ g_object_get (action, "value", &value, NULL);
+ gtk_radio_action_set_current_value (action, value);
+}
+
+static void
+e_action_combo_box_class_init (EActionComboBoxClass *class)
+{
+ GObjectClass *object_class;
+ GtkComboBoxClass *combo_box_class;
+
+ g_type_class_add_private (class, sizeof (EActionComboBoxPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = action_combo_box_set_property;
+ object_class->get_property = action_combo_box_get_property;
+ object_class->dispose = action_combo_box_dispose;
+ object_class->finalize = action_combo_box_finalize;
+ object_class->constructed = action_combo_box_constructed;
+
+ combo_box_class = GTK_COMBO_BOX_CLASS (class);
+ combo_box_class->changed = action_combo_box_changed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTION,
+ g_param_spec_object (
+ "action",
+ "Action",
+ "A GtkRadioAction",
+ GTK_TYPE_RADIO_ACTION,
+ G_PARAM_READWRITE));
+}
+
+static void
+e_action_combo_box_init (EActionComboBox *combo_box)
+{
+ combo_box->priv = E_ACTION_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ combo_box->priv->index = g_hash_table_new_full (
+ g_direct_hash, g_direct_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) gtk_tree_row_reference_free);
+}
+
+GtkWidget *
+e_action_combo_box_new (void)
+{
+ return e_action_combo_box_new_with_action (NULL);
+}
+
+GtkWidget *
+e_action_combo_box_new_with_action (GtkRadioAction *action)
+{
+ return g_object_new (E_TYPE_ACTION_COMBO_BOX, "action", action, NULL);
+}
+
+GtkRadioAction *
+e_action_combo_box_get_action (EActionComboBox *combo_box)
+{
+ g_return_val_if_fail (E_ACTION_IS_COMBO_BOX (combo_box), NULL);
+
+ return combo_box->priv->action;
+}
+
+void
+e_action_combo_box_set_action (EActionComboBox *combo_box,
+ GtkRadioAction *action)
+{
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ if (action != NULL)
+ g_return_if_fail (GTK_IS_RADIO_ACTION (action));
+
+ if (combo_box->priv->action != NULL) {
+ g_signal_handler_disconnect (
+ combo_box->priv->action,
+ combo_box->priv->changed_handler_id);
+ g_object_unref (combo_box->priv->action);
+ }
+
+ if (combo_box->priv->action_group != NULL) {
+ g_signal_handler_disconnect (
+ combo_box->priv->action_group,
+ combo_box->priv->group_sensitive_handler_id);
+ g_signal_handler_disconnect (
+ combo_box->priv->action_group,
+ combo_box->priv->group_visible_handler_id);
+ g_object_unref (combo_box->priv->action_group);
+ combo_box->priv->action_group = NULL;
+ }
+
+ if (action != NULL)
+ g_object_get (
+ g_object_ref (action), "action-group",
+ &combo_box->priv->action_group, NULL);
+
+ combo_box->priv->action = action;
+ action_combo_box_update_model (combo_box);
+
+ if (combo_box->priv->action != NULL)
+ combo_box->priv->changed_handler_id = g_signal_connect (
+ combo_box->priv->action, "changed",
+ G_CALLBACK (action_combo_box_action_changed_cb),
+ combo_box);
+
+ if (combo_box->priv->action_group != NULL) {
+ g_object_ref (combo_box->priv->action_group);
+ combo_box->priv->group_sensitive_handler_id =
+ g_signal_connect (
+ combo_box->priv->action_group,
+ "notify::sensitive", G_CALLBACK (
+ action_combo_box_action_group_notify_cb),
+ combo_box);
+ combo_box->priv->group_visible_handler_id =
+ g_signal_connect (
+ combo_box->priv->action_group,
+ "notify::visible", G_CALLBACK (
+ action_combo_box_action_group_notify_cb),
+ combo_box);
+ }
+
+ g_object_notify (G_OBJECT (combo_box), "action");
+}
+
+gint
+e_action_combo_box_get_current_value (EActionComboBox *combo_box)
+{
+ g_return_val_if_fail (E_ACTION_IS_COMBO_BOX (combo_box), 0);
+ g_return_val_if_fail (combo_box->priv->action != NULL, 0);
+
+ return gtk_radio_action_get_current_value (combo_box->priv->action);
+}
+
+void
+e_action_combo_box_set_current_value (EActionComboBox *combo_box,
+ gint current_value)
+{
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+ g_return_if_fail (combo_box->priv->action != NULL);
+
+ gtk_radio_action_set_current_value (
+ combo_box->priv->action, current_value);
+}
+
+void
+e_action_combo_box_add_separator_before (EActionComboBox *combo_box,
+ gint action_value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ /* NULL actions are rendered as separators. */
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
+ NULL, COLUMN_SORT, (gfloat) action_value - 0.5, -1);
+}
+
+void
+e_action_combo_box_add_separator_after (EActionComboBox *combo_box,
+ gint action_value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_ACTION_IS_COMBO_BOX (combo_box));
+
+ /* NULL actions are rendered as separators. */
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
+ NULL, COLUMN_SORT, (gfloat) action_value + 0.5, -1);
+}