summaryrefslogtreecommitdiff
path: root/libedataserverui
diff options
context:
space:
mode:
authorHans Petter Jansson <hpj@novell.com>2005-01-22 09:59:42 +0000
committerHans Petter <hansp@src.gnome.org>2005-01-22 09:59:42 +0000
commit2af6e3d199aced9099102f50cebd8dcb38bbdbd4 (patch)
tree8fbbe483680b360add3117fcb6866507b3d98d21 /libedataserverui
parentd7f969661b5e19a47ffb3da03bee59fa580ba3bd (diff)
downloadevolution-data-server-2af6e3d199aced9099102f50cebd8dcb38bbdbd4.tar.gz
Add newly implemented ETreeModelGenerator to build.
2005-01-22 Hans Petter Jansson <hpj@novell.com> * Makefile.am (libedataserverui_1_2_la_SOURCES) (libedataserveruiinclude_HEADERS): Add newly implemented ETreeModelGenerator to build. * e-tree-model-generator.[ch]: Implement ETreeModelGenerator, a model wrapper similar to GtkTreeModelFilter, but with the capability to map each child row to an arbitrary number of rows, including zero, and pass a permutation identifier to the user's modify callback. * e-name-selector-dialog.c (contact_column_formatter): Implement a data formatter for the contact list. This makes it show both names and e-mail addresses. (e_name_selector_dialog_init): Set up data formatter. (sort_iter_to_contact_store_iter): Replace GtkTreeModelFilter with ETreeModelGenerator, and return the email permutation along with the contact iter. (add_destination): Take the email permutation as an argument, and set it in the contact instead of defaulting to the first one. (contact_activated): Pass on email_n. (transfer_button_clicked): Ditto. (setup_name_selector_model): Use ETreeModelGenerator. (deep_free_list): Implement. * e-name-selector-entry.c (completion_match_selected): Add the conversion for the e-mail generator, and set the selected e-mail permutation in the destination. (deep_free_list): Implement. (contact_layout_formatter): Implement a data formatter for the completion list, so we can show both names and e-mail addresses. (generate_contact_rows): Implement an e-mail permutation generator. (setup_contact_store): Set up the e-mail generator. (e_name_selector_entry_init): Init the generator pointer. Set up the data formatter. * e-name-selector-model.c (e_name_selector_model_init): Replace the GtkTreeModelFilter with an ETreeModelGenerator. (deep_free_list): Implement. (generate_contact_rows): Replace filter_show_not_in_destinations () with this function, which both filters and generates per-email permutations. (override_email_address): Override the contents of the E_CONTACT_EMAIL_1 string field with the relevant address. (e_name_selector_model_peek_contact_filter): Now returns a generator.
Diffstat (limited to 'libedataserverui')
-rw-r--r--libedataserverui/ChangeLog13
-rw-r--r--libedataserverui/Makefile.am6
-rw-r--r--libedataserverui/e-name-selector-dialog.c91
-rw-r--r--libedataserverui/e-name-selector-entry.c108
-rw-r--r--libedataserverui/e-name-selector-entry.h18
-rw-r--r--libedataserverui/e-name-selector-model.c74
-rw-r--r--libedataserverui/e-name-selector-model.h38
-rw-r--r--libedataserverui/e-tree-model-generator.c1250
-rw-r--r--libedataserverui/e-tree-model-generator.h92
9 files changed, 1615 insertions, 75 deletions
diff --git a/libedataserverui/ChangeLog b/libedataserverui/ChangeLog
index 2bf36559b..de34c9614 100644
--- a/libedataserverui/ChangeLog
+++ b/libedataserverui/ChangeLog
@@ -1,3 +1,16 @@
+2005-01-22 Hans Petter Jansson <hpj@novell.com>
+
+ * Makefile.am (libedataserverui_1_2_la_SOURCES)
+ (libedataserveruiinclude_HEADERS): Add newly implemented
+ ETreeModelGenerator to build.
+
+ * e-tree-model-generator.[ch]: Implement ETreeModelGenerator, a model
+ wrapper similar to GtkTreeModelFilter, but with the capability to map
+ each child row to an arbitrary number of rows, including zero, and pass
+ a permutation identifier to the user's modify callback.
+
+ * e-name-selector-dialog.c (contact_column_formatter):
+
2005-01-19 Hans Petter Jansson <hpj@novell.com>
* e-name-selector-entry.c (sanitize_string): Implement.
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
index aa6bb691e..73bf1a421 100644
--- a/libedataserverui/Makefile.am
+++ b/libedataserverui/Makefile.am
@@ -28,7 +28,8 @@ libedataserverui_1_2_la_SOURCES = \
e-name-selector-model.c \
e-source-selector.c \
e-source-selector-dialog.c \
- e-source-option-menu.c
+ e-source-option-menu.c \
+ e-tree-model-generator.c
libedataserverui_1_2_la_LIBADD = \
$(top_builddir)/addressbook/libebook/libebook-1.2.la \
@@ -49,7 +50,8 @@ libedataserveruiinclude_HEADERS = \
e-name-selector-model.h \
e-source-selector.h \
e-source-selector-dialog.h \
- e-source-option-menu.h
+ e-source-option-menu.h \
+ e-tree-model-generator.h
# Test programs
test_source_selector_SOURCES = test-source-selector.c
diff --git a/libedataserverui/e-name-selector-dialog.c b/libedataserverui/e-name-selector-dialog.c
index a6782c93e..d4b4b67f5 100644
--- a/libedataserverui/e-name-selector-dialog.c
+++ b/libedataserverui/e-name-selector-dialog.c
@@ -54,6 +54,9 @@ static void contact_activated (ENameSelectorDialog *name_selector_di
static void destination_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path,
GtkTreeViewColumn *column, GtkTreeView *tree_view);
static void remove_books (ENameSelectorDialog *name_selector_dialog);
+static void contact_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *model, GtkTreeIter *iter,
+ ENameSelectorDialog *name_selector_dialog);
/* ------------------ *
* Class/object setup *
@@ -130,7 +133,9 @@ e_name_selector_dialog_init (ENameSelectorDialog *name_selector_dialog)
column = gtk_tree_view_column_new ();
cell_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
- gtk_tree_view_column_add_attribute (column, cell_renderer, "text", E_CONTACT_FILE_AS);
+ gtk_tree_view_column_set_cell_data_func (column, cell_renderer,
+ (GtkTreeCellDataFunc) contact_column_formatter,
+ name_selector_dialog, NULL);
gtk_tree_view_append_column (name_selector_dialog->contact_view, column);
g_signal_connect_swapped (name_selector_dialog->contact_view, "row-activated",
G_CALLBACK (contact_activated), name_selector_dialog);
@@ -254,16 +259,21 @@ escape_sexp_string (const gchar *string)
}
static void
-sort_iter_to_contact_store_iter (ENameSelectorDialog *name_selector_dialog, GtkTreeIter *iter)
+sort_iter_to_contact_store_iter (ENameSelectorDialog *name_selector_dialog, GtkTreeIter *iter,
+ gint *email_n)
{
- GtkTreeModelFilter *contact_filter;
- GtkTreeIter child_iter;
+ ETreeModelGenerator *contact_filter;
+ GtkTreeIter child_iter;
+ gint email_n_local;
contact_filter = e_name_selector_model_peek_contact_filter (name_selector_dialog->name_selector_model);
gtk_tree_model_sort_convert_iter_to_child_iter (name_selector_dialog->contact_sort,
&child_iter, iter);
- gtk_tree_model_filter_convert_iter_to_child_iter (contact_filter, iter, &child_iter);
+ e_tree_model_generator_convert_iter_to_child_iter (contact_filter, iter, &email_n_local, &child_iter);
+
+ if (email_n)
+ *email_n = email_n_local;
}
static ESource *
@@ -287,7 +297,7 @@ find_first_source (ESourceList *source_list)
}
static void
-add_destination (EDestinationStore *destination_store, EContact *contact)
+add_destination (EDestinationStore *destination_store, EContact *contact, gint email_n)
{
EDestination *destination;
@@ -295,7 +305,7 @@ add_destination (EDestinationStore *destination_store, EContact *contact)
* source automatically) */
destination = e_destination_new ();
- e_destination_set_contact (destination, contact, 0);
+ e_destination_set_contact (destination, contact, email_n);
e_destination_store_append_destination (destination_store, destination);
g_object_unref (destination);
}
@@ -567,6 +577,7 @@ contact_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path)
EContact *contact;
GtkTreeIter iter;
Section *section;
+ gint email_n;
/* When a contact is activated, we transfer it to the first destination on our list */
@@ -583,7 +594,7 @@ contact_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path)
&iter, path))
g_assert_not_reached ();
- sort_iter_to_contact_store_iter (name_selector_dialog, &iter);
+ sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
contact = e_contact_store_get_contact (contact_store, &iter);
if (!contact) {
@@ -598,7 +609,7 @@ contact_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path)
return;
}
- add_destination (destination_store, contact);
+ add_destination (destination_store, contact, email_n);
}
static void
@@ -646,6 +657,7 @@ transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *t
gint section_index;
Section *section;
EDestination *destination;
+ gint email_n;
/* Get the contact to be transferred */
@@ -657,7 +669,7 @@ transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *t
return;
}
- sort_iter_to_contact_store_iter (name_selector_dialog, &iter);
+ sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
contact = e_contact_store_get_contact (contact_store, &iter);
if (!contact) {
@@ -680,7 +692,7 @@ transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *t
return;
}
- add_destination (destination_store, contact);
+ add_destination (destination_store, contact, email_n);
}
/* --------------------- *
@@ -690,13 +702,13 @@ transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *t
static void
setup_name_selector_model (ENameSelectorDialog *name_selector_dialog)
{
- EContactStore *contact_store;
- GtkTreeModelFilter *contact_filter;
- GtkTreeSelection *contact_selection;
- EBookQuery *book_query;
- GList *new_sections;
- GList *l;
- gint i;
+ EContactStore *contact_store;
+ ETreeModelGenerator *contact_filter;
+ GtkTreeSelection *contact_selection;
+ EBookQuery *book_query;
+ GList *new_sections;
+ GList *l;
+ gint i;
/* Rid UI of previous destination sections */
@@ -755,6 +767,49 @@ setup_name_selector_model (ENameSelectorDialog *name_selector_dialog)
contact_selection_changed (name_selector_dialog);
}
+static void
+deep_free_list (GList *list)
+{
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l))
+ g_free (l->data);
+
+ g_list_free (list);
+}
+
+static void
+contact_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model,
+ GtkTreeIter *iter, ENameSelectorDialog *name_selector_dialog)
+{
+ EContactStore *contact_store;
+ EContact *contact;
+ GtkTreeIter contact_store_iter;
+ GList *email_list;
+ gchar *string;
+ gchar *file_as_str;
+ gchar *email_str;
+ gint email_n;
+
+ contact_store_iter = *iter;
+ sort_iter_to_contact_store_iter (name_selector_dialog, &contact_store_iter, &email_n);
+
+ contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
+ contact = e_contact_store_get_contact (contact_store, &contact_store_iter);
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ email_str = g_list_nth_data (email_list, email_n);
+ file_as_str = e_contact_get (contact, E_CONTACT_FILE_AS);
+
+ string = g_strdup_printf ("%s <%s>", file_as_str ? file_as_str : "",
+ email_str ? email_str : "");
+
+ g_free (file_as_str);
+ deep_free_list (email_list);
+
+ g_object_set (cell, "text", string, NULL);
+ g_free (string);
+}
+
/* ----------------------- *
* ENameSelectorDialog API *
* ----------------------- */
diff --git a/libedataserverui/e-name-selector-entry.c b/libedataserverui/e-name-selector-entry.c
index b9761c6e4..6e90c5925 100644
--- a/libedataserverui/e-name-selector-entry.c
+++ b/libedataserverui/e-name-selector-entry.c
@@ -1165,30 +1165,23 @@ completion_match_selected (ENameSelectorEntry *name_selector_entry, GtkTreeModel
gint cursor_pos;
const gchar *text;
gchar *raw_address;
+ GtkTreeIter generator_iter;
GtkTreeIter contact_iter;
+ gint email_n;
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
- &contact_iter, iter);
+ &generator_iter, iter);
+ e_tree_model_generator_convert_iter_to_child_iter (name_selector_entry->email_generator,
+ &contact_iter, &email_n,
+ &generator_iter);
contact = e_contact_store_get_contact (name_selector_entry->contact_store, &contact_iter);
-
- /* Find the matching field, in case the user entered a specific e-mail address.
- * The default is to use the first e-mail. */
-
cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
- text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
- raw_address = get_address_at_position (text, cursor_pos);
-
- if (raw_address)
- contact_match_cue (contact, raw_address, &matched_field, NULL);
-
- if (matched_field < E_CONTACT_FIRST_EMAIL_ID || matched_field > E_CONTACT_LAST_EMAIL_ID)
- matched_field = E_CONTACT_EMAIL_1;
/* Set the contact in the model's destination */
destination = find_destination_at_position (name_selector_entry, cursor_pos);
- e_destination_set_contact (destination, contact, matched_field - E_CONTACT_FIRST_EMAIL_ID);
+ e_destination_set_contact (destination, contact, email_n);
sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
/* Place cursor at end of address */
@@ -1217,12 +1210,89 @@ entry_activate (ENameSelectorEntry *name_selector_entry)
}
static void
+deep_free_list (GList *list)
+{
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l))
+ g_free (l->data);
+
+ g_list_free (list);
+}
+
+static void
+contact_layout_formatter (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *model,
+ GtkTreeIter *iter, ENameSelectorEntry *name_selector_entry)
+{
+ EContact *contact;
+ GtkTreeIter generator_iter;
+ GtkTreeIter contact_store_iter;
+ GList *email_list;
+ gchar *string;
+ gchar *file_as_str;
+ gchar *email_str;
+ gint email_n;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &generator_iter, iter);
+ e_tree_model_generator_convert_iter_to_child_iter (name_selector_entry->email_generator,
+ &contact_store_iter, &email_n,
+ &generator_iter);
+
+ contact = e_contact_store_get_contact (name_selector_entry->contact_store, &contact_store_iter);
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ email_str = g_list_nth_data (email_list, email_n);
+ file_as_str = e_contact_get (contact, E_CONTACT_FILE_AS);
+
+ string = g_strdup_printf ("%s <%s>", file_as_str ? file_as_str : "",
+ email_str ? email_str : "");
+
+ g_free (file_as_str);
+ deep_free_list (email_list);
+
+ g_object_set (cell, "text", string, NULL);
+ g_free (string);
+}
+
+static gint
+generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
+ ENameSelectorEntry *name_selector_entry)
+{
+ EContact *contact;
+ const gchar *contact_uid;
+ GList *email_list;
+ gint n_rows;
+
+ contact = e_contact_store_get_contact (contact_store, iter);
+ g_assert (contact != NULL);
+
+ contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
+ if (!contact_uid)
+ return 0; /* Can happen with broken databases */
+
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ n_rows = g_list_length (email_list);
+ deep_free_list (email_list);
+
+ return n_rows;
+}
+
+static void
setup_contact_store (ENameSelectorEntry *name_selector_entry)
{
+ if (name_selector_entry->email_generator)
+ g_object_unref (name_selector_entry->email_generator);
+ name_selector_entry->email_generator =
+ e_tree_model_generator_new (GTK_TREE_MODEL (name_selector_entry->contact_store));
+
+ e_tree_model_generator_set_generate_func (name_selector_entry->email_generator,
+ (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
+ name_selector_entry, NULL);
+
/* Assign the store to the entry completion */
gtk_entry_completion_set_model (name_selector_entry->entry_completion,
- GTK_TREE_MODEL (name_selector_entry->contact_store));
+ GTK_TREE_MODEL (name_selector_entry->email_generator));
}
static void
@@ -1475,6 +1545,8 @@ e_name_selector_entry_init (ENameSelectorEntry *name_selector_entry)
/* Completion */
+ name_selector_entry->email_generator = NULL;
+
name_selector_entry->entry_completion = gtk_entry_completion_new ();
gtk_entry_completion_set_match_func (name_selector_entry->entry_completion,
(GtkEntryCompletionMatchFunc) completion_match_cb, NULL, NULL);
@@ -1488,8 +1560,10 @@ e_name_selector_entry_init (ENameSelectorEntry *name_selector_entry)
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (name_selector_entry->entry_completion),
renderer, TRUE);
- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (name_selector_entry->entry_completion),
- renderer, "text", E_CONTACT_FILE_AS);
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (name_selector_entry->entry_completion),
+ GTK_CELL_RENDERER (renderer),
+ (GtkCellLayoutDataFunc) contact_layout_formatter,
+ name_selector_entry, NULL);
/* Completion store */
diff --git a/libedataserverui/e-name-selector-entry.h b/libedataserverui/e-name-selector-entry.h
index 42994f4ca..b28699ae3 100644
--- a/libedataserverui/e-name-selector-entry.h
+++ b/libedataserverui/e-name-selector-entry.h
@@ -28,6 +28,7 @@
#include <libebook/e-contact.h>
#include <libedataserverui/e-contact-store.h>
#include <libedataserverui/e-destination-store.h>
+#include <libedataserverui/e-tree-model-generator.h>
G_BEGIN_DECLS
@@ -46,18 +47,19 @@ struct ENameSelectorEntryClass {
};
struct ENameSelectorEntry {
- GtkEntry parent;
+ GtkEntry parent;
/* Private */
- PangoAttrList *attr_list;
- ESourceList *source_list;
- EContactStore *contact_store;
- EDestinationStore *destination_store;
- GtkEntryCompletion *entry_completion;
+ PangoAttrList *attr_list;
+ ESourceList *source_list;
+ EContactStore *contact_store;
+ ETreeModelGenerator *email_generator;
+ EDestinationStore *destination_store;
+ GtkEntryCompletion *entry_completion;
- guint type_ahead_complete_cb_id;
- guint update_completions_cb_id;
+ guint type_ahead_complete_cb_id;
+ guint update_completions_cb_id;
};
GType e_name_selector_entry_get_type (void);
diff --git a/libedataserverui/e-name-selector-model.c b/libedataserverui/e-name-selector-model.c
index 5516bb3d7..51437c6a9 100644
--- a/libedataserverui/e-name-selector-model.c
+++ b/libedataserverui/e-name-selector-model.c
@@ -38,8 +38,11 @@ typedef struct {
}
Section;
-static gboolean filter_show_not_in_destinations (EContactStore *contact_store, GtkTreeIter *iter,
- ENameSelectorModel *name_selector_model);
+static gint generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
+ ENameSelectorModel *name_selector_model);
+static void override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
+ gint permutation_n, gint column, GValue *value,
+ ENameSelectorModel *name_selector_model);
/* ------------------ *
* Class/object setup *
@@ -62,11 +65,16 @@ e_name_selector_model_init (ENameSelectorModel *name_selector_model)
{
name_selector_model->sections = g_array_new (FALSE, FALSE, sizeof (Section));
name_selector_model->contact_store = e_contact_store_new ();
- name_selector_model->contact_filter = GTK_TREE_MODEL_FILTER (
- gtk_tree_model_filter_new (GTK_TREE_MODEL (name_selector_model->contact_store), NULL));
- gtk_tree_model_filter_set_visible_func (name_selector_model->contact_filter,
- (GtkTreeModelFilterVisibleFunc) filter_show_not_in_destinations,
+
+ name_selector_model->contact_filter =
+ e_tree_model_generator_new (GTK_TREE_MODEL (name_selector_model->contact_store));
+ e_tree_model_generator_set_generate_func (name_selector_model->contact_filter,
+ (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
+ name_selector_model, NULL);
+ e_tree_model_generator_set_modify_func (name_selector_model->contact_filter,
+ (ETreeModelGeneratorModifyFunc) override_email_address,
name_selector_model, NULL);
+
g_object_unref (name_selector_model->contact_store);
name_selector_model->destination_uid_hash = NULL;
@@ -121,13 +129,25 @@ e_name_selector_model_new (void)
* GtkTreeModelFilter filtering *
* ---------------------------- */
-static gboolean
-filter_show_not_in_destinations (EContactStore *contact_store, GtkTreeIter *iter,
- ENameSelectorModel *name_selector_model)
+static void
+deep_free_list (GList *list)
+{
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l))
+ g_free (l->data);
+
+ g_list_free (list);
+}
+
+static gint
+generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
+ ENameSelectorModel *name_selector_model)
{
EContact *contact;
const gchar *contact_uid;
gboolean result = TRUE;
+ gint n_rows;
gint i;
contact = e_contact_store_get_contact (contact_store, iter);
@@ -159,7 +179,39 @@ filter_show_not_in_destinations (EContactStore *contact_store, GtkTreeIter *iter
g_list_free (destinations);
}
- return result;
+ if (result) {
+ EContact *contact;
+ GList *email_list;
+
+ contact = e_contact_store_get_contact (contact_store, iter);
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ n_rows = g_list_length (email_list);
+ deep_free_list (email_list);
+ } else {
+ n_rows = 0;
+ }
+
+ return n_rows;
+}
+
+static void
+override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
+ gint permutation_n, gint column, GValue *value,
+ ENameSelectorModel *name_selector_model)
+{
+ if (column == E_CONTACT_EMAIL_1) {
+ EContact *contact;
+ GList *email_list;
+ gchar *email;
+
+ contact = e_contact_store_get_contact (contact_store, iter);
+ email_list = e_contact_get (contact, E_CONTACT_EMAIL);
+ email = g_strdup (g_list_nth_data (email_list, permutation_n));
+ g_value_set_string (value, email);
+ deep_free_list (email_list);
+ } else {
+ gtk_tree_model_get_value (GTK_TREE_MODEL (contact_store), iter, column, value);
+ }
}
/* --------------- *
@@ -286,7 +338,7 @@ e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_mode
return name_selector_model->contact_store;
}
-GtkTreeModelFilter *
+ETreeModelGenerator *
e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model)
{
g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
diff --git a/libedataserverui/e-name-selector-model.h b/libedataserverui/e-name-selector-model.h
index 60dc7d05d..94ad73b5a 100644
--- a/libedataserverui/e-name-selector-model.h
+++ b/libedataserverui/e-name-selector-model.h
@@ -24,7 +24,7 @@
#define E_NAME_SELECTOR_MODEL_H
#include <glib.h>
-#include <gtk/gtktreemodelfilter.h>
+#include <libedataserverui/e-tree-model-generator.h>
#include <libedataserverui/e-contact-store.h>
#include <libedataserverui/e-destination-store.h>
@@ -40,15 +40,15 @@ typedef struct ENameSelectorModel ENameSelectorModel;
typedef struct ENameSelectorModelClass ENameSelectorModelClass;
struct ENameSelectorModel {
- GObject parent;
+ GObject parent;
/* Private */
- GArray *sections;
- EContactStore *contact_store;
- GtkTreeModelFilter *contact_filter;
+ GArray *sections;
+ EContactStore *contact_store;
+ ETreeModelGenerator *contact_filter;
- GHashTable *destination_uid_hash;
+ GHashTable *destination_uid_hash;
};
struct ENameSelectorModelClass {
@@ -59,24 +59,24 @@ struct ENameSelectorModelClass {
void (* section_removed) (gchar *name);
};
-GType e_name_selector_model_get_type (void);
-ENameSelectorModel *e_name_selector_model_new (void);
+GType e_name_selector_model_get_type (void);
+ENameSelectorModel *e_name_selector_model_new (void);
-EContactStore *e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_model);
-GtkTreeModelFilter *e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model);
+EContactStore *e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_model);
+ETreeModelGenerator *e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model);
/* Deep copy of section names; free strings and list when you're done */
-GList *e_name_selector_model_list_sections (ENameSelectorModel *name_selector_model);
+GList *e_name_selector_model_list_sections (ENameSelectorModel *name_selector_model);
/* pretty_name will be newly allocated, but destination_store must be reffed if you keep it */
-gboolean e_name_selector_model_peek_section (ENameSelectorModel *name_selector_model,
- const gchar *name, gchar **pretty_name,
- EDestinationStore **destination_store);
-void e_name_selector_model_add_section (ENameSelectorModel *name_selector_model,
- const gchar *name, const gchar *pretty_name,
- EDestinationStore *destination_store);
-void e_name_selector_model_remove_section (ENameSelectorModel *name_selector_model,
- const gchar *name);
+gboolean e_name_selector_model_peek_section (ENameSelectorModel *name_selector_model,
+ const gchar *name, gchar **pretty_name,
+ EDestinationStore **destination_store);
+void e_name_selector_model_add_section (ENameSelectorModel *name_selector_model,
+ const gchar *name, const gchar *pretty_name,
+ EDestinationStore *destination_store);
+void e_name_selector_model_remove_section (ENameSelectorModel *name_selector_model,
+ const gchar *name);
G_END_DECLS
diff --git a/libedataserverui/e-tree-model-generator.c b/libedataserverui/e-tree-model-generator.c
new file mode 100644
index 000000000..239628a44
--- /dev/null
+++ b/libedataserverui/e-tree-model-generator.c
@@ -0,0 +1,1250 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* e-tree-model-generator.c - Model wrapper that permutes underlying rows.
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU 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 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.
+ *
+ * Authors: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtksignal.h>
+#include <libgnome/gnome-i18n.h>
+#include "e-tree-model-generator.h"
+
+#define ETMG_DEBUG(x)
+
+#define ITER_IS_VALID(tree_model_generator, iter) ((iter)->stamp == (tree_model_generator)->stamp)
+#define ITER_GET(iter, group, index) \
+G_STMT_START { \
+ *(group) = (iter)->user_data; \
+ *(index) = GPOINTER_TO_INT ((iter)->user_data2); \
+} G_STMT_END
+
+#define ITER_SET(tree_model_generator, iter, group, index) \
+G_STMT_START { \
+ (iter)->stamp = (tree_model_generator)->stamp; \
+ (iter)->user_data = group; \
+ (iter)->user_data2 = GINT_TO_POINTER (index); \
+} G_STMT_END
+
+static void e_tree_model_generator_init (ETreeModelGenerator *tree_model_generator);
+static void e_tree_model_generator_class_init (ETreeModelGeneratorClass *class);
+static void e_tree_model_generator_tree_model_init (GtkTreeModelIface *iface);
+static void e_tree_model_generator_finalize (GObject *object);
+static guint e_tree_model_generator_get_flags (GtkTreeModel *tree_model);
+static gint e_tree_model_generator_get_n_columns (GtkTreeModel *tree_model);
+static GType e_tree_model_generator_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean e_tree_model_generator_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *e_tree_model_generator_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void e_tree_model_generator_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean e_tree_model_generator_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean e_tree_model_generator_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean e_tree_model_generator_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint e_tree_model_generator_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean e_tree_model_generator_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean e_tree_model_generator_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+static GArray *build_node_map (ETreeModelGenerator *tree_model_generator, GtkTreeIter *parent_iter,
+ GArray *parent_group, gint parent_index);
+static void release_node_map (GArray *group);
+
+static void child_row_changed (ETreeModelGenerator *tree_model_generator, GtkTreePath *path, GtkTreeIter *iter);
+static void child_row_inserted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path, GtkTreeIter *iter);
+static void child_row_deleted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path);
+
+typedef struct {
+ GArray *parent_group;
+ gint parent_index;
+
+ gint n_generated;
+ GArray *child_nodes;
+}
+Node;
+
+enum {
+ PROP_0,
+ PROP_CHILD_MODEL
+};
+
+/* ------------------ *
+ * Class/object setup *
+ * ------------------ */
+
+static GObjectClass *parent_class = NULL;
+
+static void
+e_tree_model_generator_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ g_value_set_object (value, tree_model_generator->child_model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_tree_model_generator_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ tree_model_generator->child_model = g_value_get_object (value);
+ g_object_ref (tree_model_generator->child_model);
+
+ if (tree_model_generator->root_nodes)
+ release_node_map (tree_model_generator->root_nodes);
+ tree_model_generator->root_nodes =
+ build_node_map (tree_model_generator, NULL, NULL, -1);
+
+ g_signal_connect_swapped (tree_model_generator->child_model, "row-changed",
+ G_CALLBACK (child_row_changed), tree_model_generator);
+ g_signal_connect_swapped (tree_model_generator->child_model, "row-deleted",
+ G_CALLBACK (child_row_deleted), tree_model_generator);
+ g_signal_connect_swapped (tree_model_generator->child_model, "row-inserted",
+ G_CALLBACK (child_row_inserted), tree_model_generator);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+GType
+e_tree_model_generator_get_type (void)
+{
+ static GType tree_model_generator_type = 0;
+
+ if (!tree_model_generator_type) {
+ static const GTypeInfo tree_model_generator_info =
+ {
+ sizeof (ETreeModelGeneratorClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) e_tree_model_generator_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (ETreeModelGenerator),
+ 0,
+ (GInstanceInitFunc) e_tree_model_generator_init,
+ };
+
+ static const GInterfaceInfo tree_model_info =
+ {
+ (GInterfaceInitFunc) e_tree_model_generator_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ tree_model_generator_type = g_type_register_static (G_TYPE_OBJECT, "ETreeModelGenerator",
+ &tree_model_generator_info, 0);
+ g_type_add_interface_static (tree_model_generator_type,
+ GTK_TYPE_TREE_MODEL,
+ &tree_model_info);
+ }
+
+ return tree_model_generator_type;
+}
+
+static void
+e_tree_model_generator_class_init (ETreeModelGeneratorClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ object_class = (GObjectClass *) class;
+
+ object_class->get_property = e_tree_model_generator_get_property;
+ object_class->set_property = e_tree_model_generator_set_property;
+ object_class->finalize = e_tree_model_generator_finalize;
+
+ g_object_class_install_property (object_class, PROP_CHILD_MODEL,
+ g_param_spec_object ("child-model",
+ "Child Model",
+ "The child model to extend",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_tree_model_generator_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = e_tree_model_generator_get_flags;
+ iface->get_n_columns = e_tree_model_generator_get_n_columns;
+ iface->get_column_type = e_tree_model_generator_get_column_type;
+ iface->get_iter = e_tree_model_generator_get_iter;
+ iface->get_path = e_tree_model_generator_get_path;
+ iface->get_value = e_tree_model_generator_get_value;
+ iface->iter_next = e_tree_model_generator_iter_next;
+ iface->iter_children = e_tree_model_generator_iter_children;
+ iface->iter_has_child = e_tree_model_generator_iter_has_child;
+ iface->iter_n_children = e_tree_model_generator_iter_n_children;
+ iface->iter_nth_child = e_tree_model_generator_iter_nth_child;
+ iface->iter_parent = e_tree_model_generator_iter_parent;
+}
+
+static void
+e_tree_model_generator_init (ETreeModelGenerator *tree_model_generator)
+{
+ tree_model_generator->stamp = g_random_int ();
+ tree_model_generator->root_nodes = g_array_new (FALSE, FALSE, sizeof (Node));
+}
+
+static void
+e_tree_model_generator_finalize (GObject *object)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (object);
+
+ if (tree_model_generator->child_model) {
+ g_signal_handlers_disconnect_matched (tree_model_generator->child_model,
+ G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+ tree_model_generator);
+ g_object_unref (tree_model_generator->child_model);
+ }
+
+ if (tree_model_generator->root_nodes)
+ release_node_map (tree_model_generator->root_nodes);
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+/* ------------------ *
+ * Row update helpers *
+ * ------------------ */
+
+static void
+row_deleted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ g_assert (path);
+
+ ETMG_DEBUG (g_print ("row_deleted emitting\n"));
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (tree_model_generator), path);
+}
+
+static void
+row_inserted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ GtkTreeIter iter;
+
+ g_assert (path);
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_generator), &iter, path)) {
+ ETMG_DEBUG (g_print ("row_inserted emitting\n"));
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_model_generator), path, &iter);
+ } else {
+ ETMG_DEBUG (g_print ("row_inserted could not get iter!\n"));
+ }
+}
+
+static void
+row_changed (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ GtkTreeIter iter;
+
+ g_assert (path);
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_generator), &iter, path)) {
+ ETMG_DEBUG (g_print ("row_changed emitting\n"));
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_model_generator), path, &iter);
+ } else {
+ ETMG_DEBUG (g_print ("row_changed could not get iter!\n"));
+ }
+}
+
+/* -------------------- *
+ * Node map translation *
+ * -------------------- */
+
+static gint
+generated_offset_to_child_offset (GArray *group, gint offset, gint *internal_offset)
+{
+ gboolean success = FALSE;
+ gint accum_offset = 0;
+ gint i;
+
+ for (i = 0; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+
+ accum_offset += node->n_generated;
+ if (accum_offset > offset) {
+ accum_offset -= node->n_generated;
+ success = TRUE;
+ break;
+ }
+ }
+
+ if (!success)
+ return -1;
+
+ if (internal_offset)
+ *internal_offset = offset - accum_offset;
+
+ return i;
+}
+
+static gint
+child_offset_to_generated_offset (GArray *group, gint offset)
+{
+ gint accum_offset = 0;
+ gint i;
+
+ for (i = 0; i < group->len && i < offset; i++) {
+ Node *node = &g_array_index (group, Node, i);
+
+ accum_offset += node->n_generated;
+ }
+
+ return accum_offset;
+}
+
+static gint
+count_generated_nodes (GArray *group)
+{
+ gint accum_offset = 0;
+ gint i;
+
+ for (i = 0; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+
+ accum_offset += node->n_generated;
+ }
+
+ return accum_offset;
+}
+
+/* ------------------- *
+ * Node map management *
+ * ------------------- */
+
+static void
+release_node_map (GArray *group)
+{
+ gint i;
+
+ for (i = 0; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+
+ if (node->child_nodes)
+ release_node_map (node->child_nodes);
+ }
+
+ g_array_free (group, TRUE);
+}
+
+static gint
+append_node (GArray *group)
+{
+ g_array_set_size (group, group->len + 1);
+ return group->len - 1;
+}
+
+static GArray *
+build_node_map (ETreeModelGenerator *tree_model_generator, GtkTreeIter *parent_iter,
+ GArray *parent_group, gint parent_index)
+{
+ GArray *group;
+ GtkTreeIter iter;
+ gboolean result;
+
+ if (parent_iter)
+ result = gtk_tree_model_iter_children (tree_model_generator->child_model, &iter, parent_iter);
+ else
+ result = gtk_tree_model_get_iter_first (tree_model_generator->child_model, &iter);
+
+ if (!result)
+ return NULL;
+
+ group = g_array_new (FALSE, FALSE, sizeof (Node));
+
+ do {
+ Node *node;
+ gint i;
+
+ i = append_node (group);
+ node = &g_array_index (group, Node, i);
+
+ node->parent_group = parent_group;
+ node->parent_index = parent_index;
+
+ if (tree_model_generator->generate_func)
+ node->n_generated =
+ tree_model_generator->generate_func (tree_model_generator->child_model,
+ &iter, tree_model_generator->generate_func_data);
+ else
+ node->n_generated = 1;
+
+ node->child_nodes = build_node_map (tree_model_generator, &iter, group, i);
+ } while (gtk_tree_model_iter_next (tree_model_generator->child_model, &iter));
+
+ return group;
+}
+
+static gint
+get_first_visible_index_from (GArray *group, gint index)
+{
+ gint i;
+
+ for (i = index; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+
+ if (node->n_generated)
+ break;
+ }
+
+ if (i >= group->len)
+ i = -1;
+
+ return i;
+}
+
+static Node *
+get_node_by_child_path (ETreeModelGenerator *tree_model_generator, GtkTreePath *path, GArray **node_group)
+{
+ Node *node = NULL;
+ GArray *group;
+ gint depth;
+
+ group = tree_model_generator->root_nodes;
+
+ for (depth = 0; depth < gtk_tree_path_get_depth (path); depth++) {
+ gint index;
+ gint generated_index;
+
+ if (!group) {
+ g_warning ("ETreeModelGenerator got unknown child element!");
+ break;
+ }
+
+ index = gtk_tree_path_get_indices (path) [depth];
+ node = &g_array_index (group, Node, index);
+
+ if (depth + 1 < gtk_tree_path_get_depth (path))
+ group = node->child_nodes;
+ }
+
+ if (!node)
+ group = NULL;
+
+ if (node_group)
+ *node_group = group;
+
+ return node;
+}
+
+static Node *
+create_node_at_child_path (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ GtkTreePath *parent_path;
+ gint parent_index;
+ GArray *parent_group;
+ GArray *group;
+ gint index;
+ Node *node;
+
+ parent_path = gtk_tree_path_copy (path);
+ gtk_tree_path_up (parent_path);
+ node = get_node_by_child_path (tree_model_generator, parent_path, &parent_group);
+
+ if (node) {
+ if (!node->child_nodes)
+ node->child_nodes = g_array_new (FALSE, FALSE, sizeof (Node));
+
+ group = node->child_nodes;
+ parent_index = gtk_tree_path_get_indices (parent_path) [gtk_tree_path_get_depth (parent_path) - 1];
+ } else {
+ if (!tree_model_generator->root_nodes)
+ tree_model_generator->root_nodes = g_array_new (FALSE, FALSE, sizeof (Node));
+
+ group = tree_model_generator->root_nodes;
+ parent_index = -1;
+ }
+
+ gtk_tree_path_free (parent_path);
+
+ index = gtk_tree_path_get_indices (path) [gtk_tree_path_get_depth (path) - 1];
+ ETMG_DEBUG (g_print ("Inserting index %d into group of length %d\n", index, group->len));
+ index = MIN (index, group->len);
+
+ append_node (group);
+
+ if (group->len - 1 - index > 0) {
+ gint i;
+
+ memmove ((Node *) group->data + index + 1,
+ (Node *) group->data + index,
+ (group->len - 1 - index) * sizeof (Node));
+
+ /* Update parent pointers */
+ for (i = index + 1; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+ GArray *child_group;
+ gint j;
+
+ child_group = node->child_nodes;
+ if (!child_group)
+ continue;
+
+ for (j = 0; j < child_group->len; j++) {
+ Node *child_node = &g_array_index (child_group, Node, j);
+ child_node->parent_index = i;
+ }
+ }
+ }
+
+ node = &g_array_index (group, Node, index);
+ node->parent_group = parent_group;
+ node->parent_index = parent_index;
+ node->n_generated = 0;
+ node->child_nodes = NULL;
+
+ ETMG_DEBUG (g_print ("Created node at offset %d, parent_group = %p, parent_index = %d\n",
+ index, node->parent_group, node->parent_index));
+
+ return node;
+}
+
+ETMG_DEBUG (
+
+static void
+dump_group (GArray *group)
+{
+ gint i;
+
+ g_print ("\nGroup %p:\n", group);
+
+ for (i = 0; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+ g_print (" %04d: pgroup=%p, pindex=%d, n_generated=%d, child_nodes=%p\n",
+ i, node->parent_group, node->parent_index, node->n_generated, node->child_nodes);
+ }
+}
+
+)
+
+static void
+delete_node_at_child_path (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ GtkTreePath *parent_path;
+ gint parent_index;
+ GArray *parent_group;
+ GArray *group;
+ gint index;
+ Node *node;
+ gint i;
+
+ parent_path = gtk_tree_path_copy (path);
+ gtk_tree_path_up (parent_path);
+ node = get_node_by_child_path (tree_model_generator, parent_path, &parent_group);
+
+ if (node) {
+ group = node->child_nodes;
+ parent_index = gtk_tree_path_get_indices (parent_path) [gtk_tree_path_get_depth (parent_path) - 1];
+ } else {
+ group = tree_model_generator->root_nodes;
+ parent_index = -1;
+ }
+
+ gtk_tree_path_free (parent_path);
+
+ if (!group)
+ return;
+
+ index = gtk_tree_path_get_indices (path) [gtk_tree_path_get_depth (path) - 1];
+ if (index >= group->len)
+ return;
+
+ node = &g_array_index (group, Node, index);
+ if (node->child_nodes)
+ release_node_map (node->child_nodes);
+ g_array_remove_index (group, index);
+
+ /* Update parent pointers */
+ for (i = index; i < group->len; i++) {
+ Node *node = &g_array_index (group, Node, i);
+ GArray *child_group;
+ gint j;
+
+ child_group = node->child_nodes;
+ if (!child_group)
+ continue;
+
+ for (j = 0; j < child_group->len; j++) {
+ Node *child_node = &g_array_index (child_group, Node, j);
+ child_node->parent_index = i;
+ }
+ }
+}
+
+static void
+child_row_changed (ETreeModelGenerator *tree_model_generator, GtkTreePath *path, GtkTreeIter *iter)
+{
+ GtkTreePath *generated_path;
+ Node *node;
+ gint n_generated;
+ gint i;
+
+ if (tree_model_generator->generate_func)
+ n_generated =
+ tree_model_generator->generate_func (tree_model_generator->child_model,
+ iter, tree_model_generator->generate_func_data);
+ else
+ n_generated = 1;
+
+ node = get_node_by_child_path (tree_model_generator, path, NULL);
+ if (!node)
+ return;
+
+ generated_path = e_tree_model_generator_convert_child_path_to_path (tree_model_generator, path);
+
+ /* FIXME: Converting the path to an iter every time is inefficient */
+
+ for (i = 0; i < n_generated && i < node->n_generated; i++) {
+ row_changed (tree_model_generator, generated_path);
+ gtk_tree_path_next (generated_path);
+ }
+
+ for ( ; i < node->n_generated; ) {
+ node->n_generated--;
+ row_deleted (tree_model_generator, generated_path);
+ }
+
+ for ( ; i < n_generated; i++) {
+ node->n_generated++;
+ row_inserted (tree_model_generator, generated_path);
+ gtk_tree_path_next (generated_path);
+ }
+
+ gtk_tree_path_free (generated_path);
+}
+
+static void
+child_row_inserted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path, GtkTreeIter *iter)
+{
+ GtkTreePath *generated_path;
+ Node *node;
+ gint n_generated;
+ gint i;
+
+ if (tree_model_generator->generate_func)
+ n_generated =
+ tree_model_generator->generate_func (tree_model_generator->child_model,
+ iter, tree_model_generator->generate_func_data);
+ else
+ n_generated = 1;
+
+ node = create_node_at_child_path (tree_model_generator, path);
+ if (!node)
+ return;
+
+ generated_path = e_tree_model_generator_convert_child_path_to_path (tree_model_generator, path);
+
+ /* FIXME: Converting the path to an iter every time is inefficient */
+
+ for (node->n_generated = 0; node->n_generated < n_generated; ) {
+ node->n_generated++;
+ row_inserted (tree_model_generator, generated_path);
+ gtk_tree_path_next (generated_path);
+ }
+
+ gtk_tree_path_free (generated_path);
+}
+
+static void
+child_row_deleted (ETreeModelGenerator *tree_model_generator, GtkTreePath *path)
+{
+ GtkTreePath *generated_path;
+ Node *node;
+
+ node = get_node_by_child_path (tree_model_generator, path, NULL);
+ if (!node)
+ return;
+
+ generated_path = e_tree_model_generator_convert_child_path_to_path (tree_model_generator, path);
+
+ /* FIXME: Converting the path to an iter every time is inefficient */
+
+ for ( ; node->n_generated; ) {
+ node->n_generated--;
+ row_deleted (tree_model_generator, generated_path);
+ }
+
+ delete_node_at_child_path (tree_model_generator, path);
+ gtk_tree_path_free (generated_path);
+}
+
+/* ----------------------- *
+ * ETreeModelGenerator API *
+ * ----------------------- */
+
+ETreeModelGenerator *
+e_tree_model_generator_new (GtkTreeModel *child_model)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
+
+ return E_TREE_MODEL_GENERATOR (g_object_new (E_TYPE_TREE_MODEL_GENERATOR,
+ "child-model", child_model, NULL));
+}
+
+GtkTreeModel *
+e_tree_model_generator_get_model (ETreeModelGenerator *tree_model_generator)
+{
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator), NULL);
+
+ return tree_model_generator->child_model;
+}
+
+void
+e_tree_model_generator_set_generate_func (ETreeModelGenerator *tree_model_generator,
+ ETreeModelGeneratorGenerateFunc func,
+ gpointer data, GtkDestroyNotify destroy)
+{
+ g_return_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator));
+
+ tree_model_generator->generate_func = func;
+ tree_model_generator->generate_func_data = data;
+}
+
+void
+e_tree_model_generator_set_modify_func (ETreeModelGenerator *tree_model_generator,
+ ETreeModelGeneratorModifyFunc func,
+ gpointer data, GtkDestroyNotify destroy)
+{
+ g_return_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator));
+
+ tree_model_generator->modify_func = func;
+ tree_model_generator->modify_func_data = data;
+}
+
+GtkTreePath *
+e_tree_model_generator_convert_child_path_to_path (ETreeModelGenerator *tree_model_generator,
+ GtkTreePath *child_path)
+{
+ GtkTreePath *path;
+ GArray *group;
+ gint depth;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator), NULL);
+ g_return_val_if_fail (child_path != NULL, NULL);
+
+ path = gtk_tree_path_new ();
+
+ group = tree_model_generator->root_nodes;
+
+ for (depth = 0; depth < gtk_tree_path_get_depth (child_path); depth++) {
+ Node *node;
+ gint index;
+ gint generated_index;
+
+ if (!group) {
+ g_warning ("ETreeModelGenerator was asked for path to unknown child element!");
+ break;
+ }
+
+ index = gtk_tree_path_get_indices (child_path) [depth];
+ generated_index = child_offset_to_generated_offset (group, index);
+ node = &g_array_index (group, Node, index);
+ group = node->child_nodes;
+
+ gtk_tree_path_append_index (path, generated_index);
+ }
+
+ return path;
+}
+
+void
+e_tree_model_generator_convert_child_iter_to_iter (ETreeModelGenerator *tree_model_generator,
+ GtkTreeIter *generator_iter,
+ GtkTreeIter *child_iter)
+{
+ GtkTreePath *path;
+ GArray *group;
+ gint depth;
+ gint index = 0;
+
+ g_return_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator));
+
+ path = gtk_tree_model_get_path (tree_model_generator->child_model, child_iter);
+ if (!path)
+ return;
+
+ group = tree_model_generator->root_nodes;
+
+ for (depth = 0; depth < gtk_tree_path_get_depth (path); depth++) {
+ Node *node;
+
+ index = gtk_tree_path_get_indices (path) [depth];
+ node = &g_array_index (group, Node, index);
+
+ if (depth + 1 < gtk_tree_path_get_depth (path))
+ group = node->child_nodes;
+
+ if (!group) {
+ g_warning ("ETreeModelGenerator was asked for iter to unknown child element!");
+ break;
+ }
+ }
+
+ index = child_offset_to_generated_offset (group, index);
+ ITER_SET (tree_model_generator, generator_iter, group, index);
+ gtk_tree_path_free (path);
+}
+
+GtkTreePath *
+e_tree_model_generator_convert_path_to_child_path (ETreeModelGenerator *tree_model_generator,
+ GtkTreePath *generator_path)
+{
+ GtkTreePath *path;
+ GArray *group;
+ gint depth;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator), NULL);
+ g_return_val_if_fail (generator_path != NULL, NULL);
+
+ path = gtk_tree_path_new ();
+
+ group = tree_model_generator->root_nodes;
+
+ for (depth = 0; depth < gtk_tree_path_get_depth (generator_path); depth++) {
+ Node *node;
+ gint index;
+ gint child_index;
+
+ if (!group) {
+ g_warning ("ETreeModelGenerator was asked for path to unknown child element!");
+ break;
+ }
+
+ index = gtk_tree_path_get_indices (generator_path) [depth];
+ child_index = generated_offset_to_child_offset (group, index, NULL);
+ node = &g_array_index (group, Node, child_index);
+ group = node->child_nodes;
+
+ gtk_tree_path_append_index (path, child_index);
+ }
+
+ return path;
+}
+
+void
+e_tree_model_generator_convert_iter_to_child_iter (ETreeModelGenerator *tree_model_generator,
+ GtkTreeIter *child_iter,
+ gint *permutation_n,
+ GtkTreeIter *generator_iter)
+{
+ GtkTreePath *path;
+ GArray *group;
+ gint generator_index;
+ gint index;
+ gint internal_offset;
+
+ g_return_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model_generator));
+ g_return_if_fail (ITER_IS_VALID (tree_model_generator, generator_iter));
+
+ path = gtk_tree_path_new ();
+ ITER_GET (generator_iter, &group, &index);
+
+ generator_index = index;
+ index = generated_offset_to_child_offset (group, index, &internal_offset);
+ gtk_tree_path_prepend_index (path, index);
+
+ ETMG_DEBUG (g_print ("iter_to_child_iter: %d -> %d (%d)\n", generator_index, index, internal_offset));
+
+ while (group) {
+ Node *node = &g_array_index (group, Node, index);
+ gint generated_index;
+
+ group = node->parent_group;
+ index = node->parent_index;
+
+ if (group)
+ gtk_tree_path_prepend_index (path, index);
+ }
+
+ if (child_iter)
+ gtk_tree_model_get_iter (tree_model_generator->child_model, child_iter, path);
+ if (permutation_n)
+ *permutation_n = internal_offset;
+
+ gtk_tree_path_free (path);
+}
+
+/* ---------------- *
+ * GtkTreeModel API *
+ * ---------------- */
+
+static guint
+e_tree_model_generator_get_flags (GtkTreeModel *tree_model)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), 0);
+
+ return gtk_tree_model_get_flags (tree_model_generator->child_model);
+}
+
+static gint
+e_tree_model_generator_get_n_columns (GtkTreeModel *tree_model)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), 0);
+
+ return gtk_tree_model_get_n_columns (tree_model_generator->child_model);
+}
+
+static GType
+e_tree_model_generator_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), G_TYPE_INVALID);
+
+ return gtk_tree_model_get_column_type (tree_model_generator->child_model, index);
+}
+
+static gboolean
+e_tree_model_generator_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ GArray *group;
+ gint depth;
+ gint index = 0;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+ g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+ group = tree_model_generator->root_nodes;
+ if (!group)
+ return FALSE;
+
+ for (depth = 0; depth < gtk_tree_path_get_depth (path); depth++) {
+ Node *node;
+ gint child_index;
+
+ index = gtk_tree_path_get_indices (path) [depth];
+ child_index = generated_offset_to_child_offset (group, index, NULL);
+ if (child_index < 0)
+ return FALSE;
+
+ node = &g_array_index (group, Node, child_index);
+
+ if (depth + 1 < gtk_tree_path_get_depth (path)) {
+ group = node->child_nodes;
+ if (!group)
+ return FALSE;
+ }
+ }
+
+ ITER_SET (tree_model_generator, iter, group, index);
+ return TRUE;
+}
+
+static GtkTreePath *
+e_tree_model_generator_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ GtkTreePath *path;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), NULL);
+ g_return_val_if_fail (ITER_IS_VALID (tree_model_generator, iter), NULL);
+
+ ITER_GET (iter, &group, &index);
+ path = gtk_tree_path_new ();
+
+ /* FIXME: Converting a path to an iter is a destructive operation, because
+ * we don't store a node for each generated entry... Doesn't matter for
+ * lists, not sure about trees. */
+
+ gtk_tree_path_prepend_index (path, index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+
+ while (group) {
+ Node *node = &g_array_index (group, Node, index);
+ gint generated_index;
+
+ group = node->parent_group;
+ index = node->parent_index;
+ generated_index = child_offset_to_generated_offset (group, index);
+
+ if (group)
+ gtk_tree_path_prepend_index (path, generated_index);
+ }
+
+ return path;
+}
+
+static gboolean
+e_tree_model_generator_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+ gint child_index;
+ gint internal_offset;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+ g_return_val_if_fail (ITER_IS_VALID (tree_model_generator, iter), FALSE);
+
+ ITER_GET (iter, &group, &index);
+ child_index = generated_offset_to_child_offset (group, index, &internal_offset);
+ node = &g_array_index (group, Node, child_index);
+
+ if (internal_offset + 1 < node->n_generated ||
+ get_first_visible_index_from (group, child_index + 1) >= 0) {
+ ITER_SET (tree_model_generator, iter, group, index + 1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+e_tree_model_generator_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+
+ if (!parent) {
+ if (!tree_model_generator->root_nodes ||
+ !count_generated_nodes (tree_model_generator->root_nodes))
+ return FALSE;
+
+ ITER_SET (tree_model_generator, iter, tree_model_generator->root_nodes, 0);
+ return TRUE;
+ }
+
+ ITER_GET (parent, &group, &index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+ if (index < 0)
+ return FALSE;
+
+ node = &g_array_index (group, Node, index);
+
+ if (!node->child_nodes)
+ return FALSE;
+
+ if (!count_generated_nodes (node->child_nodes))
+ return FALSE;
+
+ ITER_SET (tree_model_generator, iter, node->child_nodes, 0);
+ return TRUE;
+}
+
+static gboolean
+e_tree_model_generator_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+
+ if (iter == NULL) {
+ if (!tree_model_generator->root_nodes ||
+ !count_generated_nodes (tree_model_generator->root_nodes))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ ITER_GET (iter, &group, &index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+ if (!index < 0)
+ return FALSE;
+
+ node = &g_array_index (group, Node, index);
+
+ if (!node->child_nodes)
+ return FALSE;
+
+ if (!count_generated_nodes (node->child_nodes))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gint
+e_tree_model_generator_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+
+ if (iter == NULL)
+ return tree_model_generator->root_nodes ?
+ count_generated_nodes (tree_model_generator->root_nodes) : 0;
+
+ ITER_GET (iter, &group, &index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+ if (index < 0)
+ return 0;
+
+ node = &g_array_index (group, Node, index);
+
+ if (!node->child_nodes)
+ return 0;
+
+ return count_generated_nodes (node->child_nodes);
+}
+
+static gboolean
+e_tree_model_generator_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+
+ if (!parent) {
+ if (!tree_model_generator->root_nodes)
+ return FALSE;
+
+ if (n >= count_generated_nodes (tree_model_generator->root_nodes))
+ return FALSE;
+
+ ITER_SET (tree_model_generator, iter, tree_model_generator->root_nodes, n);
+ return TRUE;
+ }
+
+ ITER_GET (parent, &group, &index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+ if (index < 0)
+ return FALSE;
+
+ node = &g_array_index (group, Node, index);
+
+ if (!node->child_nodes)
+ return FALSE;
+
+ if (n >= count_generated_nodes (node->child_nodes))
+ return FALSE;
+
+ ITER_SET (tree_model_generator, iter, node->child_nodes, n);
+ return TRUE;
+}
+
+static gboolean
+e_tree_model_generator_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ Node *node;
+ GArray *group;
+ gint index;
+
+ g_return_val_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model), FALSE);
+ g_return_val_if_fail (ITER_IS_VALID (tree_model_generator, iter), FALSE);
+
+ ITER_GET (child, &group, &index);
+ index = generated_offset_to_child_offset (group, index, NULL);
+ if (index < 0)
+ return FALSE;
+
+ node = &g_array_index (group, Node, index);
+
+ group = node->parent_group;
+ if (!group)
+ return FALSE;
+
+ ITER_SET (tree_model_generator, iter, group, node->parent_index);
+ return TRUE;
+}
+
+static void
+e_tree_model_generator_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ ETreeModelGenerator *tree_model_generator = E_TREE_MODEL_GENERATOR (tree_model);
+ GtkTreeIter child_iter;
+ gint permutation_n;
+
+ g_return_if_fail (E_IS_TREE_MODEL_GENERATOR (tree_model));
+ g_return_if_fail (ITER_IS_VALID (tree_model_generator, iter));
+
+ e_tree_model_generator_convert_iter_to_child_iter (tree_model_generator, &child_iter,
+ &permutation_n, iter);
+
+ if (tree_model_generator->modify_func) {
+ tree_model_generator->modify_func (tree_model_generator->child_model,
+ &child_iter, permutation_n,
+ column, value,
+ tree_model_generator->modify_func_data);
+ return;
+ }
+
+ gtk_tree_model_get_value (tree_model_generator->child_model, &child_iter, column, value);
+}
diff --git a/libedataserverui/e-tree-model-generator.h b/libedataserverui/e-tree-model-generator.h
new file mode 100644
index 000000000..09a818f4b
--- /dev/null
+++ b/libedataserverui/e-tree-model-generator.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* e-tree-model-generator.h - Model wrapper that permutes underlying rows.
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU 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 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.
+ *
+ * Authors: Hans Petter Jansson <hpj@novell.com>
+ */
+
+#ifndef E_TREE_MODEL_GENERATOR_H
+#define E_TREE_MODEL_GENERATOR_H
+
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_TREE_MODEL_GENERATOR (e_tree_model_generator_get_type ())
+#define E_TREE_MODEL_GENERATOR(obj) (GTK_CHECK_CAST ((obj), E_TYPE_TREE_MODEL_GENERATOR, ETreeModelGenerator))
+#define E_TREE_MODEL_GENERATOR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_TREE_MODEL_GENERATOR, ETreeModelGeneratorClass))
+#define E_IS_TREE_MODEL_GENERATOR(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_TREE_MODEL_GENERATOR))
+#define E_IS_TREE_MODEL_GENERATOR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_TREE_MODEL_GENERATOR))
+#define E_TREE_MODEL_GENERATOR_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), E_TYPE_TREE_MODEL_GENERATOR, ETreeModelGeneratorClass))
+
+typedef gint (*ETreeModelGeneratorGenerateFunc) (GtkTreeModel *model, GtkTreeIter *child_iter,
+ gpointer data);
+typedef void (*ETreeModelGeneratorModifyFunc) (GtkTreeModel *model, GtkTreeIter *child_iter,
+ gint permutation_n, gint column, GValue *value,
+ gpointer data);
+
+typedef struct ETreeModelGenerator ETreeModelGenerator;
+typedef struct ETreeModelGeneratorClass ETreeModelGeneratorClass;
+
+struct ETreeModelGeneratorClass {
+ GObjectClass parent_class;
+};
+
+struct ETreeModelGenerator {
+ GObject parent;
+
+ /* Private */
+
+ gint stamp;
+ GtkTreeModel *child_model;
+ GArray *root_nodes;
+
+ ETreeModelGeneratorGenerateFunc generate_func;
+ gpointer generate_func_data;
+
+ ETreeModelGeneratorModifyFunc modify_func;
+ gpointer modify_func_data;
+};
+
+GtkType e_tree_model_generator_get_type (void);
+
+ETreeModelGenerator *e_tree_model_generator_new (GtkTreeModel *child_model);
+GtkTreeModel *e_tree_model_generator_get_model (ETreeModelGenerator *tree_model_generator);
+
+void e_tree_model_generator_set_generate_func (ETreeModelGenerator *tree_model_generator,
+ ETreeModelGeneratorGenerateFunc func,
+ gpointer data, GtkDestroyNotify destroy);
+void e_tree_model_generator_set_modify_func (ETreeModelGenerator *tree_model_generator,
+ ETreeModelGeneratorModifyFunc func,
+ gpointer data, GtkDestroyNotify destroy);
+
+GtkTreePath *e_tree_model_generator_convert_child_path_to_path (ETreeModelGenerator *tree_model_generator,
+ GtkTreePath *child_path);
+void e_tree_model_generator_convert_child_iter_to_iter (ETreeModelGenerator *tree_model_generator,
+ GtkTreeIter *generator_iter,
+ GtkTreeIter *child_iter);
+GtkTreePath *e_tree_model_generator_convert_path_to_child_path (ETreeModelGenerator *tree_model_generator,
+ GtkTreePath *generator_path);
+void e_tree_model_generator_convert_iter_to_child_iter (ETreeModelGenerator *tree_model_generator,
+ GtkTreeIter *child_iter,
+ gint *permutation_n,
+ GtkTreeIter *generator_iter);
+
+G_END_DECLS
+
+#endif /* E_TREE_MODEL_GENERATOR_H */