/*
* OpenMoko Contacts
*
* Copyright (C) 2007 OpenMoko Inc.
*
* hito 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 3 of the License, or
* (at your option) any later version.
*
* hito 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, see .
*/
#include
#include "contacts-attributes-editor.h"
#include "koto-cell-renderer-pixbuf.h"
#define DEFAULT_NEW_ATTRIBUTE_TYPE "Work"
G_DEFINE_TYPE (ContactsAttributesEditor, contacts_attributes_editor, GTK_TYPE_VBOX);
enum
{
PROP_0,
PROP_ATTR_LIST,
PROP_FILTER
};
enum {
TREE_DEL_COLUMN = 0,
TREE_TYPE_COLUMN,
TREE_ICON_COLUMN,
TREE_VALUE_COLUMN
};
typedef struct _ContactsAttributesEditorPriv ContactsAttributesEditorPriv;
struct _ContactsAttributesEditorPriv
{
GtkWidget *frame;
GtkWidget *button;
GtkWidget *treeview;
GtkTreeModel *filter;
GtkTreeModel *store;
gchar *stock_id;
gchar *filter_string;
GtkCellRenderer *icon_renderer;
};
#define GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CONTACTS_TYPE_ATTRIBUTES_EDITOR, ContactsAttributesEditorPriv))
static gboolean
filter_visible_func (GtkTreeModel *model, GtkTreeIter *iter, ContactsAttributesEditorPriv *priv)
{
gchar *value;
gboolean result;
gtk_tree_model_get (model, iter, ATTR_NAME_COLUMN, &value, -1);
result = (priv->filter_string && value && g_str_equal (value, priv->filter_string));
g_free (value);
return result;
}
static void
update_visible_treeviews (ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
GtkTreeIter foo;
/* this hides the treeview completely when there are no rows to display
* (actually hides the parent since the parent draws the frame)
*/
if (!gtk_tree_model_get_iter_first (priv->filter, &foo))
gtk_widget_hide (priv->frame);
else
gtk_widget_show (priv->frame);
}
static void
value_renderer_edited_cb (GtkCellRenderer *renderer, gchar *path, gchar *text, ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
GtkTreeIter filter_iter, model_iter;
gtk_tree_model_get_iter_from_string (priv->filter, &filter_iter, path);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter), &model_iter, &filter_iter);
if (g_str_equal (text, ""))
{
contacts_attribute_store_remove (CONTACTS_ATTRIBUTE_STORE (priv->store), &model_iter);
}
else
{
gtk_list_store_set (GTK_LIST_STORE (priv->store), &model_iter, ATTR_VALUE_COLUMN, text, -1);
}
}
static void
type_renderer_edited_cb (GtkCellRenderer *renderer, gchar *path, gchar *text, ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
GtkTreeIter filter_iter, model_iter;
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (priv->filter), &filter_iter, path);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter),
&model_iter, &filter_iter);
gtk_list_store_set (GTK_LIST_STORE (priv->store), &model_iter, ATTR_TYPE_COLUMN, text, -1);
}
static void
delete_renderer_activated_cb (KotoCellRendererPixbuf *cell, const char *path, ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
GtkTreeIter iter, child_iter;
/* remove attribute row from model */
gtk_tree_model_get_iter_from_string (priv->filter, &iter, path);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter),
&child_iter, &iter);
contacts_attribute_store_remove (CONTACTS_ATTRIBUTE_STORE (priv->store), &child_iter);
}
static void
add_new_attribute_cb (GtkWidget *widget, ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
EVCardAttribute *attr;
GtkTreeIter child_iter, filter_iter;
GtkTreeViewColumn *col;
GtkTreePath *filter_path;
attr = e_vcard_attribute_new (NULL, priv->filter_string);
gtk_list_store_insert_with_values (GTK_LIST_STORE (priv->store), &child_iter, 0,
ATTR_POINTER_COLUMN, attr,
ATTR_NAME_COLUMN, priv->filter_string,
ATTR_TYPE_COLUMN, DEFAULT_NEW_ATTRIBUTE_TYPE , -1);
gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter),
&filter_iter, &child_iter);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->treeview), TREE_VALUE_COLUMN);
filter_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->filter), &filter_iter);
gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->treeview), filter_path, col, TRUE);
gtk_widget_activate (GTK_WIDGET (priv->treeview));
gtk_tree_path_free (filter_path);
}
static void
contacts_attributes_editor_init (ContactsAttributesEditor *self)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (self);
GtkVBox *vb = GTK_VBOX (self);
GtkCellRenderer *renderer;
GtkTreeViewColumn *treeview_column;
priv->frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (vb), priv->frame, FALSE, FALSE, 0);
priv->treeview = gtk_tree_view_new ();
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->treeview), FALSE);
gtk_container_add (GTK_CONTAINER (priv->frame), priv->treeview);
/* delete option column */
renderer = koto_cell_renderer_pixbuf_new ();
g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_DELETE, NULL);
g_signal_connect (G_OBJECT (renderer), "activated", G_CALLBACK (delete_renderer_activated_cb), self);
treeview_column = gtk_tree_view_column_new_with_attributes ("", renderer, NULL);
g_object_set (G_OBJECT (treeview_column), "visible", FALSE, NULL);
gtk_tree_view_insert_column (GTK_TREE_VIEW (priv->treeview), treeview_column, TREE_DEL_COLUMN);
/* type option column */
GtkListStore *liststore;
/* FIXME: this should be translatable somehow */
liststore = gtk_list_store_new (1, G_TYPE_STRING);
gtk_list_store_insert_with_values (liststore, NULL, 0, 0, "Work", -1);
gtk_list_store_insert_with_values (liststore, NULL, 0, 0, "Home", -1);
gtk_list_store_insert_with_values (liststore, NULL, 0, 0, "Other", -1);
gtk_list_store_insert_with_values (liststore, NULL, 0, 0, "Pref", -1);
renderer = gtk_cell_renderer_combo_new();
g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (type_renderer_edited_cb), self);
g_object_set (G_OBJECT (renderer), "model", liststore, "text-column", 0, "has-entry", FALSE, NULL);
treeview_column = gtk_tree_view_column_new_with_attributes ("", renderer, "text", ATTR_TYPE_COLUMN, NULL);
gtk_tree_view_insert_column (GTK_TREE_VIEW (priv->treeview), treeview_column, TREE_TYPE_COLUMN);
/* icon column */
renderer = gtk_cell_renderer_pixbuf_new();
g_object_set (G_OBJECT (renderer), "stock-id", priv->stock_id, NULL);
treeview_column = gtk_tree_view_column_new ();
gtk_tree_view_column_pack_start (treeview_column, renderer, FALSE);
gtk_tree_view_insert_column (GTK_TREE_VIEW (priv->treeview), treeview_column, TREE_ICON_COLUMN);
priv->icon_renderer = renderer;
/* value column */
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (value_renderer_edited_cb), self);
gtk_tree_view_insert_column (GTK_TREE_VIEW (priv->treeview),
gtk_tree_view_column_new_with_attributes ("Value", renderer, "text", ATTR_VALUE_COLUMN, NULL), TREE_VALUE_COLUMN);
/* add attribute button */
priv->button = gtk_button_new_with_label ("Add Phone Number");
gtk_widget_set_name (priv->button, "contacts-add-detail-button");
g_signal_connect (G_OBJECT (priv->button), "clicked", G_CALLBACK (add_new_attribute_cb), self);
g_object_set (G_OBJECT (priv->button), "no-show-all", TRUE, NULL);
gtk_box_pack_start (GTK_BOX (vb), priv->button, FALSE, FALSE, 0);
}
static void
contacts_attributes_editor_finalize (GObject *object)
{
G_OBJECT_CLASS (contacts_attributes_editor_parent_class)->finalize (object);
}
static void
contacts_attributes_editor_get_property (GObject *object, guint property_id,
GValue *value, GParamSpec *pspec)
{
ContactsAttributesEditorPriv *priv = GET_PRIVATE (object);
switch (property_id)
{
case PROP_ATTR_LIST:
g_value_set_object (value, priv->store);
break;
case PROP_FILTER:
g_value_set_object (value, priv->filter_string);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
contacts_attributes_editor_set_property (GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec)
{
switch (property_id)
{
case PROP_ATTR_LIST:
contacts_attributes_editor_set_attribute_store (CONTACTS_ATTRIBUTES_EDITOR (object),
g_value_get_object (value));
break;
case PROP_FILTER:
contacts_attributes_editor_set_type (CONTACTS_ATTRIBUTES_EDITOR (object),
g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
contacts_attributes_editor_class_init (ContactsAttributesEditorClass *klass)
{
GObjectClass* object_class = G_OBJECT_CLASS (klass);
/* GtkVBoxClass* parent_class = GTK_VBOX_CLASS (klass); */
GParamSpec *ps;
object_class->finalize = contacts_attributes_editor_finalize;
object_class->get_property = contacts_attributes_editor_get_property;
object_class->set_property = contacts_attributes_editor_set_property;
ps = g_param_spec_object ("attribute-store",
"attribute-store", NULL,
CONTACTS_TYPE_ATTRIBUTE_STORE,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK);
g_object_class_install_property (object_class, PROP_ATTR_LIST, ps);
ps = g_param_spec_string ("type", "type", NULL, "",
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK);
g_object_class_install_property (object_class, PROP_FILTER, ps);
g_type_class_add_private (klass, sizeof (ContactsAttributesEditorPriv));
}
GtkWidget *
contacts_attributes_editor_new (ContactsAttributeStore *store, const gchar *type)
{
g_return_val_if_fail (CONTACTS_IS_ATTRIBUTE_STORE (store), NULL);
return g_object_new (CONTACTS_TYPE_ATTRIBUTES_EDITOR,
"attribute-store", store,
"type", type,
NULL);
}
void
contacts_attributes_editor_set_attribute_store (ContactsAttributesEditor *editor,
ContactsAttributeStore *store)
{
ContactsAttributesEditorPriv *priv;
g_return_if_fail (CONTACTS_IS_ATTRIBUTES_EDITOR (editor));
g_return_if_fail (CONTACTS_IS_ATTRIBUTE_STORE (store));
priv = GET_PRIVATE (editor);
priv->store = GTK_TREE_MODEL (store);
priv->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview), priv->store);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter),
(GtkTreeModelFilterVisibleFunc) filter_visible_func, priv, NULL);
g_signal_connect_swapped (priv->filter, "row-inserted", G_CALLBACK (update_visible_treeviews), editor);
g_signal_connect_swapped (priv->filter, "row-deleted", G_CALLBACK (update_visible_treeviews), editor);
update_visible_treeviews (editor);
}
void
contacts_attributes_editor_set_type (ContactsAttributesEditor *editor, const gchar *type)
{
ContactsAttributesEditorPriv *priv;
g_return_if_fail (CONTACTS_IS_ATTRIBUTES_EDITOR (editor));
priv = GET_PRIVATE (editor);
if (priv->filter_string)
g_free (priv->filter_string);
priv->filter_string = g_strdup (type);
if (g_str_equal (type, EVC_TEL))
{
g_object_set (priv->icon_renderer, "stock-id", MOKO_STOCK_CONTACT_PHONE, NULL);
gtk_button_set_label (GTK_BUTTON (priv->button), "Add Phone Number");
}
else if (g_str_equal (type, EVC_EMAIL))
{
g_object_set (priv->icon_renderer, "stock-id", MOKO_STOCK_CONTACT_EMAIL, NULL);
gtk_button_set_label (GTK_BUTTON (priv->button), "Add E-Mail Address");
}
else
{
g_object_set (priv->icon_renderer, "stock-id", "", NULL);
gtk_button_set_label (GTK_BUTTON (priv->button), "Add Attribute");
}
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview), priv->filter);
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
}
void
contacts_attributes_editor_set_editable (ContactsAttributesEditor *editor, gboolean editable)
{
ContactsAttributesEditorPriv *priv;
GtkTreeViewColumn *col;
GList *list = NULL;
GtkTreeSelection *sel;
g_return_if_fail (CONTACTS_IS_ATTRIBUTES_EDITOR (editor));
priv = GET_PRIVATE (editor);
g_return_if_fail (GTK_IS_TREE_VIEW (priv->treeview));
g_object_set (G_OBJECT (priv->treeview), "can-focus", editable, NULL);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->treeview), TREE_DEL_COLUMN);
g_object_set (G_OBJECT (col), "visible", editable, NULL);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->treeview), TREE_TYPE_COLUMN);
list = gtk_tree_view_column_get_cell_renderers (col);
g_object_set (G_OBJECT (list->data), "editable", editable, NULL);
g_list_free (list);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->treeview), TREE_VALUE_COLUMN);
list = gtk_tree_view_column_get_cell_renderers (col);
g_object_set (G_OBJECT (list->data), "editable", editable, NULL);
g_list_free (list);
g_object_set (G_OBJECT (priv->button), "visible", editable, NULL);
update_visible_treeviews (editor);
/* ensure selection is possible in edit mode - cell editing is not possible
* without it
*/
if (editable)
{
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
}
else
{
/* disable selection when not in editing mode */
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
gtk_tree_selection_set_mode (sel, GTK_SELECTION_NONE);
}
}