2002-12-11 Mike Kestner <mkestner@ximian.com> * e-storage-set-store.[ch] : GtkTreeStore wrapper. not built yet svn path=/trunk/; revision=19101
1362 lines
36 KiB
C
1362 lines
36 KiB
C
/* e-storage-set-store.c
|
|
* Copyright (C) 2002 Ximian, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <gtk/gtktreemodel.h>
|
|
#include <gtk/gtktreednd.h>
|
|
|
|
#include "e-icon-factory.h"
|
|
#include "e-storage-set-store.h"
|
|
#include "e-shell-constants.h"
|
|
|
|
struct _EStorageSetStorePrivate {
|
|
EStorageSet *storage_set;
|
|
GHashTable *checkboxes;
|
|
GHashTable *path_to_node;
|
|
GHashTable *type_name_to_pixbuf;
|
|
GNode *root;
|
|
gint stamp;
|
|
EStorageSetStoreHasCheckBoxFunc has_checkbox_func;
|
|
gpointer has_checkbox_func_data;
|
|
};
|
|
|
|
#define G_NODE(node) ((GNode *)(node))
|
|
#define VALID_ITER(iter, store) ((iter) != NULL && (iter)->user_data != NULL && (store)->priv->stamp == (iter)->stamp)
|
|
#define VALID_COL(col) ((col) >= 0 && (col) < E_STORAGE_SET_STORE_COLUMN_COUNT)
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
static gboolean
|
|
has_checkbox (EStorageSetStore *store, const gchar *folder_path)
|
|
{
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
|
|
g_return_val_if_fail (folder_path != NULL, FALSE);
|
|
|
|
if (strchr (folder_path + 1, '/') == NULL) {
|
|
/* If it's a toplevel, never allow checking it. */
|
|
return FALSE;
|
|
}
|
|
|
|
#if 0
|
|
if (priv->has_checkbox_func)
|
|
return (* priv->has_checkbox_func) (priv->storage_set,
|
|
folder_path,
|
|
priv->has_checkbox_func_data);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* GtkTreeModel interface implementation */
|
|
|
|
static guint
|
|
esss_get_flags(GtkTreeModel *tree_model)
|
|
{
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), 0);
|
|
|
|
return GTK_TREE_MODEL_ITERS_PERSIST;
|
|
}
|
|
|
|
static gint
|
|
esss_get_n_columns(GtkTreeModel *tree_model)
|
|
{
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), 0);
|
|
|
|
return E_STORAGE_SET_STORE_COLUMN_COUNT;
|
|
}
|
|
|
|
static GType
|
|
esss_get_column_type(GtkTreeModel *tree_model, gint index)
|
|
{
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
GType retval;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), G_TYPE_INVALID);
|
|
g_return_val_if_fail(VALID_COL(index), G_TYPE_INVALID);
|
|
|
|
switch (index) {
|
|
case E_STORAGE_SET_STORE_COLUMN_NAME:
|
|
retval = G_TYPE_STRING;
|
|
break;
|
|
case E_STORAGE_SET_STORE_COLUMN_HIGHLIGHT:
|
|
retval = G_TYPE_INT;
|
|
break;
|
|
case E_STORAGE_SET_STORE_COLUMN_CHECKED:
|
|
case E_STORAGE_SET_STORE_COLUMN_CHECKABLE:
|
|
retval = G_TYPE_BOOLEAN;
|
|
break;
|
|
case E_STORAGE_SET_STORE_COLUMN_ICON:
|
|
retval = GDK_TYPE_PIXBUF;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
esss_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
|
|
{
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
GtkTreeIter parent;
|
|
gint *indices;
|
|
gint depth, i;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
|
|
indices = gtk_tree_path_get_indices(path);
|
|
depth = gtk_tree_path_get_depth(path);
|
|
|
|
g_return_val_if_fail(depth > 0, FALSE);
|
|
|
|
parent.stamp = store->priv->stamp;
|
|
parent.user_data = store->priv->root;
|
|
|
|
for (i = 0; i < depth; i++) {
|
|
if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
|
|
return FALSE;
|
|
parent = *iter;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GtkTreePath *
|
|
tree_path_from_node (EStorageSetStore *store, GNode *node)
|
|
{
|
|
GtkTreePath *retval;
|
|
GNode *tmp_node, *curr_node;
|
|
gint i = 0;
|
|
|
|
if (node == store->priv->root)
|
|
return NULL;
|
|
|
|
retval = gtk_tree_path_new();
|
|
|
|
for (curr_node = node; curr_node != store->priv->root; curr_node = curr_node->parent) {
|
|
|
|
if (curr_node->parent == NULL) {
|
|
gtk_tree_path_free(retval);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0, tmp_node = curr_node->parent->children;
|
|
tmp_node && tmp_node != curr_node;
|
|
i++, tmp_node = tmp_node->next);
|
|
|
|
if (tmp_node == NULL) {
|
|
gtk_tree_path_free(retval);
|
|
return NULL;
|
|
}
|
|
|
|
gtk_tree_path_prepend_index(retval, i);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static GtkTreePath *
|
|
esss_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), NULL);
|
|
g_return_val_if_fail(VALID_ITER(iter, store), NULL);
|
|
|
|
if (iter->user_data == store->priv->root)
|
|
return NULL;
|
|
|
|
return tree_path_from_node (store, G_NODE (iter->user_data));
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
get_pixbuf_for_folder (EStorageSetStore *store, EFolder *folder)
|
|
{
|
|
const char *type_name;
|
|
EStorageSetStorePrivate *priv;
|
|
EFolderTypeRegistry *folder_type_registry;
|
|
EStorageSet *storage_set;
|
|
GdkPixbuf *icon_pixbuf;
|
|
GdkPixbuf *scaled_pixbuf;
|
|
const char *custom_icon_name;
|
|
int icon_pixbuf_width, icon_pixbuf_height;
|
|
|
|
priv = store->priv;
|
|
|
|
custom_icon_name = e_folder_get_custom_icon_name (folder);
|
|
if (custom_icon_name != NULL)
|
|
return e_icon_factory_get_icon (custom_icon_name, TRUE);
|
|
|
|
type_name = e_folder_get_type_string (folder);
|
|
|
|
scaled_pixbuf = g_hash_table_lookup (priv->type_name_to_pixbuf, type_name);
|
|
if (scaled_pixbuf != NULL)
|
|
return scaled_pixbuf;
|
|
|
|
storage_set = priv->storage_set;
|
|
folder_type_registry = e_storage_set_get_folder_type_registry (storage_set);
|
|
|
|
icon_pixbuf = e_folder_type_registry_get_icon_for_type (folder_type_registry,
|
|
type_name, TRUE);
|
|
|
|
if (icon_pixbuf == NULL)
|
|
return NULL;
|
|
|
|
icon_pixbuf_width = gdk_pixbuf_get_width (icon_pixbuf);
|
|
icon_pixbuf_height = gdk_pixbuf_get_height (icon_pixbuf);
|
|
|
|
if (icon_pixbuf_width == E_SHELL_MINI_ICON_SIZE && icon_pixbuf_height == E_SHELL_MINI_ICON_SIZE) {
|
|
scaled_pixbuf = g_object_ref (icon_pixbuf);
|
|
} else {
|
|
scaled_pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (icon_pixbuf),
|
|
gdk_pixbuf_get_has_alpha (icon_pixbuf),
|
|
gdk_pixbuf_get_bits_per_sample (icon_pixbuf),
|
|
E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE);
|
|
|
|
gdk_pixbuf_scale (icon_pixbuf, scaled_pixbuf,
|
|
0, 0, E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE,
|
|
0.0, 0.0,
|
|
(double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_width (icon_pixbuf),
|
|
(double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_height (icon_pixbuf),
|
|
GDK_INTERP_HYPER);
|
|
}
|
|
|
|
g_hash_table_insert (priv->type_name_to_pixbuf, g_strdup (type_name), scaled_pixbuf);
|
|
|
|
return scaled_pixbuf;
|
|
}
|
|
|
|
static void
|
|
esss_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value)
|
|
{
|
|
gchar *folder_path;
|
|
const gchar *folder_name;
|
|
int unread_count;
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
EFolder *folder;
|
|
GNode *node;
|
|
gboolean is_storage;
|
|
|
|
g_return_if_fail(E_IS_STORAGE_SET_STORE(tree_model));
|
|
g_return_if_fail(VALID_ITER(iter, store));
|
|
g_return_if_fail(VALID_COL(column));
|
|
|
|
node = G_NODE (iter->user_data);
|
|
g_value_init(value, esss_get_column_type(tree_model, column));
|
|
|
|
is_storage = (node->parent == store->priv->root);
|
|
|
|
if (is_storage)
|
|
folder_path = g_strconcat ("/", node->data, NULL);
|
|
else
|
|
folder_path = g_strdup (node->data);
|
|
|
|
folder = e_storage_set_get_folder(store->priv->storage_set, folder_path);
|
|
g_free (folder_path);
|
|
|
|
switch (column) {
|
|
case E_STORAGE_SET_STORE_COLUMN_NAME:
|
|
if (!folder) {
|
|
g_value_set_string(value, "?");
|
|
return;
|
|
}
|
|
folder_name = e_folder_get_name(folder);
|
|
unread_count = e_folder_get_unread_count(folder);
|
|
if (unread_count > 0) {
|
|
gchar *with_unread = g_strdup_printf("%s (%d)", folder_name, unread_count);
|
|
g_object_set_data_full(G_OBJECT(folder), "name_with_unread",
|
|
with_unread, g_free);
|
|
g_value_set_string(value, with_unread);
|
|
} else
|
|
g_value_set_string(value, folder_name);
|
|
break;
|
|
|
|
case E_STORAGE_SET_STORE_COLUMN_HIGHLIGHT:
|
|
if (!folder) {
|
|
g_value_set_boolean(value, FALSE);
|
|
return;
|
|
}
|
|
g_value_set_int(value, is_storage || e_folder_get_highlighted(folder) ? PANGO_WEIGHT_BOLD : 0);
|
|
break;
|
|
|
|
case E_STORAGE_SET_STORE_COLUMN_CHECKED:
|
|
if (is_storage || !store->priv->checkboxes) {
|
|
g_value_set_boolean(value, FALSE);
|
|
return;
|
|
}
|
|
g_value_set_boolean(value,
|
|
g_hash_table_lookup(store->priv->checkboxes, folder_path) ? TRUE : FALSE);
|
|
break;
|
|
|
|
case E_STORAGE_SET_STORE_COLUMN_CHECKABLE:
|
|
g_value_set_boolean(value, !is_storage && has_checkbox(store, (gchar *)node->data));
|
|
break;
|
|
|
|
case E_STORAGE_SET_STORE_COLUMN_ICON:
|
|
if (!is_storage)
|
|
g_value_set_object (value, get_pixbuf_for_folder (store, folder));
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
esss_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(VALID_ITER(iter, E_STORAGE_SET_STORE(tree_model)), FALSE);
|
|
|
|
if (G_NODE(iter->user_data)->next) {
|
|
iter->user_data = G_NODE(iter->user_data)->next;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
esss_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
|
|
{
|
|
GNode *children;
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
|
|
g_return_val_if_fail(parent == NULL || parent->stamp == store->priv->stamp, FALSE);
|
|
|
|
if (parent)
|
|
children = G_NODE(parent->user_data)->children;
|
|
else
|
|
children =
|
|
G_NODE(store->priv->root)->children;
|
|
|
|
if (children) {
|
|
iter->stamp = store->priv->stamp;
|
|
iter->user_data = children;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
esss_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
|
|
|
|
return G_NODE(iter->user_data)->children != NULL;
|
|
}
|
|
|
|
static gint
|
|
esss_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|
{
|
|
GNode *node;
|
|
gint i = 0;
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), 0);
|
|
g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
|
|
|
|
if (iter == NULL)
|
|
node = G_NODE(store->priv->root)->children;
|
|
else
|
|
node = G_NODE(iter->user_data)->children;
|
|
|
|
while (node) {
|
|
i++;
|
|
node = node->next;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static gboolean
|
|
esss_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
|
|
{
|
|
GNode *parent_node;
|
|
GNode *child;
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
|
|
|
|
if (parent == NULL)
|
|
parent_node = store->priv->root;
|
|
else
|
|
parent_node = parent->user_data;
|
|
|
|
child = g_node_nth_child(parent_node, n);
|
|
|
|
if (child) {
|
|
iter->user_data = child;
|
|
iter->stamp = store->priv->stamp;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
esss_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
|
|
{
|
|
GNode *parent;
|
|
EStorageSetStore *store = (EStorageSetStore *) tree_model;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(tree_model), FALSE);
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
g_return_val_if_fail(VALID_ITER(child, store), FALSE);
|
|
|
|
parent = G_NODE(child->user_data)->parent;
|
|
|
|
g_assert(parent != NULL);
|
|
|
|
if (parent != store->priv->root) {
|
|
iter->user_data = parent;
|
|
iter->stamp = store->priv->stamp;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
esss_tree_model_init(GtkTreeModelIface *iface)
|
|
{
|
|
iface->get_flags = esss_get_flags;
|
|
iface->get_n_columns = esss_get_n_columns;
|
|
iface->get_column_type = esss_get_column_type;
|
|
iface->get_iter = esss_get_iter;
|
|
iface->get_path = esss_get_path;
|
|
iface->get_value = esss_get_value;
|
|
iface->iter_next = esss_iter_next;
|
|
iface->iter_children = esss_iter_children;
|
|
iface->iter_has_child = esss_iter_has_child;
|
|
iface->iter_n_children = esss_iter_n_children;
|
|
iface->iter_nth_child = esss_iter_nth_child;
|
|
iface->iter_parent = esss_iter_parent;
|
|
}
|
|
|
|
/* GtkTreeDragSource interface implementation */
|
|
|
|
static gboolean
|
|
esss_drag_data_delete(GtkTreeDragSource *source, GtkTreePath *path)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(source), FALSE);
|
|
|
|
if (gtk_tree_model_get_iter(GTK_TREE_MODEL(source), &iter, path)) {
|
|
#if 0
|
|
e_storage_set_store_remove(E_STORAGE_SET_STORE(source), &iter);
|
|
#endif
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
esss_drag_data_get(GtkTreeDragSource *source, GtkTreePath *path, GtkSelectionData *selection_data)
|
|
{
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(source), FALSE);
|
|
|
|
/* Note that we don't need to handle the GTK_TREE_MODEL_ROW
|
|
* target, because the default handler does it for us, but
|
|
* we do anyway for the convenience of someone maybe overriding the
|
|
* default handler.
|
|
*/
|
|
|
|
if (gtk_tree_set_row_drag_data(selection_data, GTK_TREE_MODEL(source), path)) {
|
|
return TRUE;
|
|
} else {
|
|
/* FIXME handle text targets at least. */
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
esss_drag_source_init(GtkTreeDragSourceIface * iface)
|
|
{
|
|
iface->drag_data_delete = esss_drag_data_delete;
|
|
iface->drag_data_get = esss_drag_data_get;
|
|
}
|
|
|
|
/* GtkTreeDragDest interface implementation */
|
|
|
|
static void
|
|
copy_node_data(EStorageSetStore *store, GtkTreeIter *src_iter, GtkTreeIter *dest_iter)
|
|
{
|
|
}
|
|
|
|
static void
|
|
recursive_node_copy(EStorageSetStore * store,
|
|
GtkTreeIter * src_iter, GtkTreeIter * dest_iter)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
esss_drag_data_received(GtkTreeDragDest * drag_dest,
|
|
GtkTreePath * dest,
|
|
GtkSelectionData * selection_data)
|
|
{
|
|
#if 0
|
|
GtkTreeModel *tree_model;
|
|
EStorageSetStore *store;
|
|
GtkTreeModel *src_model = NULL;
|
|
GtkTreePath *src_path = NULL;
|
|
gboolean retval = FALSE;
|
|
|
|
g_return_val_if_fail(E_IS_STORAGE_SET_STORE(drag_dest), FALSE);
|
|
|
|
tree_model = GTK_TREE_MODEL(drag_dest);
|
|
store = E_STORAGE_SET_STORE(drag_dest);
|
|
|
|
validate_tree(store);
|
|
|
|
if (gtk_tree_get_row_drag_data(selection_data,
|
|
&src_model,
|
|
&src_path) &&
|
|
src_model == tree_model) {
|
|
/* Copy the given row to a new position */
|
|
GtkTreeIter src_iter;
|
|
GtkTreeIter dest_iter;
|
|
GtkTreePath *prev;
|
|
|
|
if (!gtk_tree_model_get_iter(src_model,
|
|
&src_iter, src_path)) {
|
|
goto out;
|
|
}
|
|
|
|
/* Get the path to insert _after_ (dest is the path to insert _before_) */
|
|
prev = gtk_tree_path_copy(dest);
|
|
|
|
if (!gtk_tree_path_prev(prev)) {
|
|
GtkTreeIter dest_parent;
|
|
GtkTreePath *parent;
|
|
GtkTreeIter *dest_parent_p;
|
|
|
|
/* dest was the first spot at the current depth; which means
|
|
* we are supposed to prepend.
|
|
*/
|
|
|
|
/* Get the parent, NULL if parent is the root */
|
|
dest_parent_p = NULL;
|
|
parent = gtk_tree_path_copy(dest);
|
|
if (gtk_tree_path_up(parent) &&
|
|
gtk_tree_path_get_depth(parent) > 0) {
|
|
gtk_tree_model_get_iter(tree_model,
|
|
&dest_parent,
|
|
parent);
|
|
dest_parent_p = &dest_parent;
|
|
}
|
|
gtk_tree_path_free(parent);
|
|
parent = NULL;
|
|
|
|
e_storage_set_store_prepend(E_STORAGE_SET_STORE(tree_model),
|
|
&dest_iter, dest_parent_p);
|
|
|
|
retval = TRUE;
|
|
} else {
|
|
if (gtk_tree_model_get_iter
|
|
(GTK_TREE_MODEL(tree_model), &dest_iter,
|
|
prev)) {
|
|
GtkTreeIter tmp_iter = dest_iter;
|
|
|
|
if (GPOINTER_TO_INT
|
|
(g_object_get_data
|
|
(G_OBJECT(tree_model),
|
|
"gtk-tree-model-drop-append"))) {
|
|
GtkTreeIter parent;
|
|
|
|
if (gtk_tree_model_iter_parent
|
|
(GTK_TREE_MODEL(tree_model),
|
|
&parent, &tmp_iter))
|
|
e_storage_set_store_append
|
|
(E_STORAGE_SET_STORE
|
|
(tree_model),
|
|
&dest_iter, &parent);
|
|
else
|
|
e_storage_set_store_append
|
|
(E_STORAGE_SET_STORE
|
|
(tree_model),
|
|
&dest_iter, NULL);
|
|
} else
|
|
e_storage_set_store_insert_after
|
|
(E_STORAGE_SET_STORE(tree_model),
|
|
&dest_iter, NULL, &tmp_iter);
|
|
retval = TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
g_object_set_data(G_OBJECT(tree_model),
|
|
"gtk-tree-model-drop-append", NULL);
|
|
|
|
gtk_tree_path_free(prev);
|
|
|
|
/* If we succeeded in creating dest_iter, walk src_iter tree branch,
|
|
* duplicating it below dest_iter.
|
|
*/
|
|
|
|
if (retval) {
|
|
recursive_node_copy(store,
|
|
&src_iter, &dest_iter);
|
|
}
|
|
} else {
|
|
/* FIXME maybe add some data targets eventually, or handle text
|
|
* targets in the simple case.
|
|
*/
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (src_path)
|
|
gtk_tree_path_free(src_path);
|
|
|
|
return retval;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
esss_row_drop_possible(GtkTreeDragDest * drag_dest,
|
|
GtkTreePath * dest_path,
|
|
GtkSelectionData * selection_data)
|
|
{
|
|
#if 0
|
|
GtkTreeModel *src_model = NULL;
|
|
GtkTreePath *src_path = NULL;
|
|
GtkTreePath *tmp = NULL;
|
|
gboolean retval = FALSE;
|
|
|
|
if (!gtk_tree_get_row_drag_data(selection_data,
|
|
&src_model, &src_path))
|
|
goto out;
|
|
|
|
/* can only drag to ourselves */
|
|
if (src_model != GTK_TREE_MODEL(drag_dest))
|
|
goto out;
|
|
|
|
/* Can't drop into ourself. */
|
|
if (gtk_tree_path_is_ancestor(src_path, dest_path))
|
|
goto out;
|
|
|
|
/* Can't drop if dest_path's parent doesn't exist */
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
if (gtk_tree_path_get_depth(dest_path) > 1) {
|
|
tmp = gtk_tree_path_copy(dest_path);
|
|
gtk_tree_path_up(tmp);
|
|
|
|
if (!gtk_tree_model_get_iter
|
|
(GTK_TREE_MODEL(drag_dest), &iter, tmp))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Can otherwise drop anywhere. */
|
|
retval = TRUE;
|
|
|
|
out:
|
|
|
|
if (src_path)
|
|
gtk_tree_path_free(src_path);
|
|
if (tmp)
|
|
gtk_tree_path_free(tmp);
|
|
|
|
return retval;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
esss_drag_dest_init(GtkTreeDragDestIface * iface)
|
|
{
|
|
iface->drag_data_received = esss_drag_data_received;
|
|
iface->row_drop_possible = esss_row_drop_possible;
|
|
}
|
|
|
|
typedef struct {
|
|
gint offset;
|
|
GNode *node;
|
|
} SortTuple;
|
|
|
|
static gint
|
|
folder_sort_callback (gconstpointer a, gconstpointer b, gpointer user_data)
|
|
{
|
|
EStorageSetStore *store = (EStorageSetStore *) user_data;
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
EFolder *folder_1, *folder_2;
|
|
char *folder_path_1, *folder_path_2;
|
|
int priority_1, priority_2;
|
|
|
|
|
|
folder_path_1 = (gchar *)((SortTuple *)a)->node->data;
|
|
folder_path_2 = (gchar *)((SortTuple *)b)->node->data;
|
|
|
|
folder_1 = e_storage_set_get_folder (priv->storage_set, folder_path_1);
|
|
folder_2 = e_storage_set_get_folder (priv->storage_set, folder_path_2);
|
|
|
|
priority_1 = e_folder_get_sorting_priority (folder_1);
|
|
priority_2 = e_folder_get_sorting_priority (folder_2);
|
|
|
|
if (priority_1 == priority_2)
|
|
return g_utf8_collate (e_folder_get_name (folder_1), e_folder_get_name (folder_2));
|
|
else if (priority_1 < priority_2)
|
|
return -1;
|
|
else /* priority_1 > priority_2 */
|
|
return +1;
|
|
}
|
|
|
|
static gint
|
|
storage_sort_callback (gconstpointer a, gconstpointer b, gpointer user_data)
|
|
{
|
|
char *folder_path_1;
|
|
char *folder_path_2;
|
|
gboolean path_1_local;
|
|
gboolean path_2_local;
|
|
|
|
folder_path_1 = (gchar *)((SortTuple *)a)->node->data;
|
|
folder_path_2 = (gchar *)((SortTuple *)b)->node->data;
|
|
|
|
/* FIXME bad hack to put the "my evolution" and "local" storages on
|
|
* top. */
|
|
|
|
if (strcmp (folder_path_1, E_SUMMARY_STORAGE_NAME) == 0)
|
|
return -1;
|
|
if (strcmp (folder_path_2, E_SUMMARY_STORAGE_NAME) == 0)
|
|
return +1;
|
|
|
|
path_1_local = ! strcmp (folder_path_1, E_LOCAL_STORAGE_NAME);
|
|
path_2_local = ! strcmp (folder_path_2, E_LOCAL_STORAGE_NAME);
|
|
|
|
if (path_1_local && path_2_local)
|
|
return 0;
|
|
if (path_1_local)
|
|
return -1;
|
|
if (path_2_local)
|
|
return 1;
|
|
|
|
return g_utf8_collate (folder_path_1, folder_path_2);
|
|
}
|
|
|
|
static void
|
|
esss_sort (EStorageSetStore *store, GNode *parent, GCompareDataFunc callback)
|
|
{
|
|
GtkTreeIter iter;
|
|
GArray *sort_array;
|
|
GNode *node;
|
|
GNode *tmp_node;
|
|
gint list_length;
|
|
gint i;
|
|
gint *new_order;
|
|
GtkTreePath *path;
|
|
|
|
node = parent->children;
|
|
if (node == NULL || node->next == NULL)
|
|
return;
|
|
|
|
list_length = 0;
|
|
for (tmp_node = node; tmp_node; tmp_node = tmp_node->next)
|
|
list_length++;
|
|
|
|
sort_array = g_array_sized_new(FALSE, FALSE, sizeof(SortTuple), list_length);
|
|
|
|
i = 0;
|
|
for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) {
|
|
SortTuple tuple;
|
|
|
|
tuple.offset = i;
|
|
tuple.node = tmp_node;
|
|
g_array_append_val(sort_array, tuple);
|
|
i++;
|
|
}
|
|
|
|
/* Sort the array */
|
|
g_array_sort_with_data(sort_array, callback, store);
|
|
|
|
for (i = 0; i < list_length - 1; i++) {
|
|
g_array_index(sort_array, SortTuple, i).node->next =
|
|
g_array_index(sort_array, SortTuple, i + 1).node;
|
|
g_array_index(sort_array, SortTuple, i + 1).node->prev =
|
|
g_array_index(sort_array, SortTuple, i).node;
|
|
}
|
|
g_array_index(sort_array, SortTuple, list_length - 1).node->next = NULL;
|
|
g_array_index(sort_array, SortTuple, 0).node->prev = NULL;
|
|
parent->children = g_array_index(sort_array, SortTuple, 0).node;
|
|
|
|
/* Let the world know about our new order */
|
|
new_order = g_new(gint, list_length);
|
|
for (i = 0; i < list_length; i++)
|
|
new_order[i] = g_array_index(sort_array, SortTuple, i).offset;
|
|
|
|
iter.stamp = store->priv->stamp;
|
|
iter.user_data = parent;
|
|
path = esss_get_path(GTK_TREE_MODEL(store), &iter);
|
|
gtk_tree_model_rows_reordered(GTK_TREE_MODEL(store), path, &iter, new_order);
|
|
if (path)
|
|
gtk_tree_path_free(path);
|
|
g_free(new_order);
|
|
g_array_free(sort_array, TRUE);
|
|
}
|
|
|
|
static void
|
|
esss_init(EStorageSetStore *store)
|
|
{
|
|
store->priv = g_new0(EStorageSetStorePrivate, 1);
|
|
|
|
store->priv->storage_set = NULL;
|
|
store->priv->checkboxes = NULL;
|
|
store->priv->root = g_node_new(NULL);
|
|
do {
|
|
store->priv->stamp = g_random_int();
|
|
} while (store->priv->stamp == 0);
|
|
|
|
store->priv->path_to_node = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
store->priv->type_name_to_pixbuf = g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
static void
|
|
esss_dispose(GObject *object)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE(object);
|
|
|
|
if (store->priv->storage_set)
|
|
g_object_unref(store->priv->storage_set);
|
|
store->priv->storage_set = NULL;
|
|
|
|
(*parent_class->dispose) (object);
|
|
}
|
|
|
|
static void
|
|
node_free (GNode *node, gpointer data)
|
|
{
|
|
g_free (node->data);
|
|
}
|
|
|
|
static void
|
|
pixbuf_free_func (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
g_free (key);
|
|
g_object_unref (value);
|
|
}
|
|
|
|
static void
|
|
esss_finalize(GObject *object)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE(object);
|
|
|
|
g_node_children_foreach(store->priv->root, G_TRAVERSE_ALL, node_free, NULL);
|
|
|
|
g_hash_table_foreach (store->priv->type_name_to_pixbuf, pixbuf_free_func, NULL);
|
|
g_hash_table_destroy (store->priv->type_name_to_pixbuf);
|
|
g_hash_table_destroy (store->priv->path_to_node);
|
|
if (store->priv->checkboxes)
|
|
g_hash_table_destroy (store->priv->checkboxes);
|
|
|
|
g_free (store->priv);
|
|
|
|
(*parent_class->finalize) (object);
|
|
}
|
|
|
|
static void
|
|
esss_class_init(EStorageSetStoreClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
parent_class = g_type_class_peek_parent(class);
|
|
object_class = (GObjectClass *) class;
|
|
|
|
object_class->dispose = esss_dispose;
|
|
object_class->finalize = esss_finalize;
|
|
}
|
|
|
|
GType
|
|
e_storage_set_store_get_type(void)
|
|
{
|
|
static GType store_type = 0;
|
|
|
|
if (!store_type) {
|
|
static const GTypeInfo store_info = {
|
|
sizeof(EStorageSetStoreClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) esss_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof(EStorageSetStore),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) esss_init
|
|
};
|
|
|
|
static const GInterfaceInfo tree_model_info = {
|
|
(GInterfaceInitFunc)
|
|
esss_tree_model_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static const GInterfaceInfo drag_source_info = {
|
|
(GInterfaceInitFunc)
|
|
esss_drag_source_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static const GInterfaceInfo drag_dest_info = {
|
|
(GInterfaceInitFunc) esss_drag_dest_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
store_type = g_type_register_static(G_TYPE_OBJECT, "EStorageSetStore", &store_info, 0);
|
|
|
|
g_type_add_interface_static(store_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
|
|
g_type_add_interface_static(store_type, GTK_TYPE_TREE_DRAG_SOURCE, &drag_source_info);
|
|
g_type_add_interface_static(store_type, GTK_TYPE_TREE_DRAG_DEST, &drag_dest_info);
|
|
}
|
|
|
|
return store_type;
|
|
}
|
|
|
|
/* Handling of the "changed" signal in EFolders displayed in the EStorageSetStore. */
|
|
|
|
typedef struct {
|
|
EStorageSetStore *store;
|
|
GNode * node;
|
|
} FolderChangedCallbackData;
|
|
|
|
static void
|
|
folder_changed_cb (EFolder *folder, void *data)
|
|
{
|
|
FolderChangedCallbackData *callback_data;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
callback_data = (FolderChangedCallbackData *) data;
|
|
iter.user_data = callback_data->node;
|
|
iter.stamp = callback_data->store->priv->stamp;
|
|
path = esss_get_path (GTK_TREE_MODEL (callback_data->store), &iter);
|
|
|
|
gtk_tree_model_row_changed (GTK_TREE_MODEL (callback_data->store), path, &iter);
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
folder_name_changed_cb (EFolder *folder, void *data)
|
|
{
|
|
FolderChangedCallbackData *callback_data;
|
|
|
|
callback_data = (FolderChangedCallbackData *) data;
|
|
|
|
esss_sort (callback_data->store, callback_data->node->parent, folder_sort_callback);
|
|
}
|
|
|
|
static void
|
|
setup_folder_changed_callbacks (EStorageSetStore *store, EFolder *folder, GNode *node)
|
|
{
|
|
FolderChangedCallbackData *callback_data = g_new0 (FolderChangedCallbackData, 1);
|
|
callback_data->store = store;
|
|
callback_data->node = node;
|
|
|
|
g_signal_connect (G_OBJECT (folder), "changed",
|
|
G_CALLBACK (folder_changed_cb), callback_data);
|
|
|
|
g_signal_connect_data (G_OBJECT (folder), "name_changed",
|
|
G_CALLBACK (folder_name_changed_cb),
|
|
callback_data, (GClosureNotify)g_free, 0);
|
|
}
|
|
|
|
static void
|
|
insert_folders (EStorageSetStore *store, GNode *parent, EStorage *storage, const gchar *path)
|
|
{
|
|
EStorageSetStorePrivate *priv;
|
|
GList *folder_path_list, *p;
|
|
const gchar *storage_name = e_storage_get_name (storage);
|
|
|
|
priv = store->priv;
|
|
|
|
folder_path_list = e_storage_get_subfolder_paths (storage, path);
|
|
if (folder_path_list == NULL)
|
|
return;
|
|
|
|
for (p = folder_path_list; p != NULL; p = p->next) {
|
|
EFolder *folder;
|
|
const char *subpath = (const char *) p->data;
|
|
char *folder_path = g_strconcat ("/", storage_name, subpath, NULL);
|
|
gchar *key = g_strdup (folder_path+1);
|
|
GNode *node = g_node_new (folder_path);
|
|
|
|
g_node_append (parent, node);
|
|
g_hash_table_replace (priv->path_to_node, key, node);
|
|
|
|
folder = e_storage_get_folder (storage, subpath);
|
|
setup_folder_changed_callbacks (store, folder, node);
|
|
|
|
insert_folders (store, node, storage, subpath);
|
|
}
|
|
|
|
esss_sort (store, parent, folder_sort_callback);
|
|
|
|
e_free_string_list (folder_path_list);
|
|
}
|
|
|
|
/* StorageSet signal handling. */
|
|
|
|
static void
|
|
new_storage_cb (EStorageSet *storage_set, EStorage *storage, void *data)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE (data);
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
gchar *storage_name = g_strdup (e_storage_get_name (storage));
|
|
GNode *node = g_node_new (g_strdup (e_storage_get_name (storage)));
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
g_hash_table_replace (priv->path_to_node, storage_name, node);
|
|
g_node_append (priv->root, node);
|
|
esss_sort (store, priv->root, storage_sort_callback);
|
|
|
|
iter.user_data = node;
|
|
iter.stamp = store->priv->stamp;
|
|
path = esss_get_path (GTK_TREE_MODEL (store), &iter);
|
|
gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter);
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
removed_storage_cb (EStorageSet *storage_set, EStorage *storage, void *data)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE (data);
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
const gchar *name = e_storage_get_name (storage);
|
|
GNode *node = g_hash_table_lookup (priv->path_to_node, name);
|
|
GtkTreePath *path;
|
|
|
|
if (node == NULL) {
|
|
g_warning ("EStorageSetStore: unknown storage removed -- %s", name);
|
|
return;
|
|
}
|
|
|
|
g_hash_table_remove (priv->path_to_node, name);
|
|
/* FIXME: subfolder hashtable entries might be leaked */
|
|
|
|
path = tree_path_from_node (store, node);
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path);
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
g_node_destroy (node);
|
|
}
|
|
|
|
static void
|
|
new_folder_cb (EStorageSet *storage_set, const char *path, void *data)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE (data);
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
GNode *parent_node, *new_node;
|
|
const char *last_separator;
|
|
char *parent_path;
|
|
char *copy_of_path;
|
|
GtkTreeIter iter;
|
|
GtkTreePath *treepath;
|
|
|
|
last_separator = strrchr (path, E_PATH_SEPARATOR);
|
|
|
|
parent_path = g_strndup (path + 1, last_separator - path - 1);
|
|
parent_node = g_hash_table_lookup (priv->path_to_node, parent_path);
|
|
g_free (parent_path);
|
|
if (parent_node == NULL) {
|
|
g_warning ("EStorageSetStore: EStorageSet reported new subfolder for non-existing folder -- %s", parent_path);
|
|
return;
|
|
}
|
|
|
|
copy_of_path = g_strdup (path);
|
|
new_node = g_node_new (copy_of_path);
|
|
g_node_append (parent_node, new_node);
|
|
iter.user_data = new_node;
|
|
iter.stamp = priv->stamp;
|
|
treepath = esss_get_path (GTK_TREE_MODEL (store), &iter);
|
|
gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), treepath, &iter);
|
|
if (treepath)
|
|
gtk_tree_path_free (treepath);
|
|
|
|
g_hash_table_replace (priv->path_to_node, g_strdup (path + 1), new_node);
|
|
|
|
setup_folder_changed_callbacks (store, e_storage_set_get_folder (storage_set, path), new_node);
|
|
esss_sort (store, parent_node, folder_sort_callback);
|
|
}
|
|
|
|
static void
|
|
updated_folder_cb (EStorageSet *storage_set, const char *path, void *data)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE (data);
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
GNode *node;
|
|
GtkTreeIter iter;
|
|
GtkTreePath *treepath;
|
|
|
|
node = g_hash_table_lookup (priv->path_to_node, path+1);
|
|
if (node == NULL) {
|
|
g_warning ("EStorageSetStore: unknown folder updated -- %s", path);
|
|
return;
|
|
}
|
|
|
|
iter.user_data = node;
|
|
iter.stamp = priv->stamp;
|
|
treepath = esss_get_path (GTK_TREE_MODEL (store), &iter);
|
|
gtk_tree_model_row_changed (GTK_TREE_MODEL (store), treepath, &iter);
|
|
if (treepath)
|
|
gtk_tree_path_free (treepath);
|
|
}
|
|
|
|
static void
|
|
removed_folder_cb (EStorageSet *storage_set, const char *path, void *data)
|
|
{
|
|
EStorageSetStore *store = E_STORAGE_SET_STORE (data);
|
|
EStorageSetStorePrivate *priv = store->priv;
|
|
GNode *node;
|
|
GtkTreePath *treepath;
|
|
|
|
node = g_hash_table_lookup (priv->path_to_node, path+1);
|
|
if (node == NULL) {
|
|
g_warning ("EStorageSetStore: unknown folder removed -- %s", path);
|
|
return;
|
|
}
|
|
|
|
g_hash_table_remove (priv->path_to_node, path+1);
|
|
/* FIXME: subfolder hashtable entries might be leaked */
|
|
|
|
treepath = tree_path_from_node (store, node);
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), treepath);
|
|
if (treepath)
|
|
gtk_tree_path_free (treepath);
|
|
g_node_destroy (node);
|
|
}
|
|
|
|
static void
|
|
close_folder_cb (EStorageSet *storage_set,
|
|
const char *path,
|
|
void *data)
|
|
{
|
|
g_warning ("FIXME: EStorageSetStore: needs to handle close_folder properly");
|
|
#if 0
|
|
EStorageSetStore *store;
|
|
EStorageSetStorePrivate *priv;
|
|
ETreeModel *etree;
|
|
ETreePath node;
|
|
|
|
store = E_STORAGE_SET_STORE (data);
|
|
priv = store->priv;
|
|
etree = priv->etree_model;
|
|
|
|
node = lookup_node_in_hash (store, path);
|
|
e_tree_model_node_request_collapse (priv->etree_model, node);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
connect_storage_set (EStorageSetStore *store, EStorageSet *storage_set, gboolean show_folders)
|
|
{
|
|
EStorageSetStorePrivate *priv;
|
|
GList *storage_list;
|
|
GList *p;
|
|
|
|
priv = store->priv;
|
|
priv->storage_set = storage_set;
|
|
g_object_ref (storage_set);
|
|
|
|
storage_list = e_storage_set_get_storage_list (storage_set);
|
|
|
|
for (p = storage_list; p != NULL; p = p->next) {
|
|
EStorage *storage = E_STORAGE (p->data);
|
|
const char *name = e_storage_get_name (storage);
|
|
GNode *node = g_node_new (g_strdup (name));
|
|
g_node_append (priv->root, node);
|
|
g_hash_table_replace (priv->path_to_node, g_strdup (name), node);
|
|
|
|
if (show_folders)
|
|
insert_folders (store, node, storage, "/");
|
|
}
|
|
|
|
esss_sort (store, priv->root, storage_sort_callback);
|
|
|
|
e_free_object_list (storage_list);
|
|
|
|
g_signal_connect_object (storage_set, "new_storage", G_CALLBACK (new_storage_cb), store, 0);
|
|
g_signal_connect_object (storage_set, "removed_storage", G_CALLBACK (removed_storage_cb), store, 0);
|
|
if (!show_folders)
|
|
return;
|
|
|
|
g_signal_connect_object (storage_set, "new_folder", G_CALLBACK (new_folder_cb), store, 0);
|
|
g_signal_connect_object (storage_set, "updated_folder", G_CALLBACK (updated_folder_cb), store, 0);
|
|
g_signal_connect_object (storage_set, "removed_folder", G_CALLBACK (removed_folder_cb), store, 0);
|
|
g_signal_connect_object (storage_set, "close_folder", G_CALLBACK (close_folder_cb), store, 0);
|
|
}
|
|
|
|
/**
|
|
* e_storage_set_store_new:
|
|
* @storage_set: the #EStorageSet that the store exposes
|
|
* @show_folders: flag indicating if subfolders should be shown
|
|
*
|
|
* Creates a new tree store from the provided #EStorageSet.
|
|
*
|
|
* Return value: a new #EStorageSetStore
|
|
**/
|
|
EStorageSetStore *
|
|
e_storage_set_store_new(EStorageSet *storage_set, gboolean show_folders)
|
|
{
|
|
EStorageSetStore *store;
|
|
g_return_val_if_fail (E_IS_STORAGE_SET(storage_set), NULL);
|
|
|
|
store = E_STORAGE_SET_STORE (g_object_new (E_STORAGE_SET_STORE_TYPE, NULL));
|
|
connect_storage_set (store, storage_set, show_folders);
|
|
|
|
return store;
|
|
}
|
|
|
|
static gboolean
|
|
esss_real_set_value(EStorageSetStore *store, GtkTreeIter *iter, gint column, GValue *value)
|
|
{
|
|
gchar *path;
|
|
|
|
if (column != E_STORAGE_SET_STORE_COLUMN_CHECKED)
|
|
return FALSE;
|
|
|
|
path = G_NODE (iter->user_data)->data;
|
|
|
|
if (g_value_get_boolean (value)) {
|
|
g_hash_table_insert (store->priv->checkboxes, path, path);
|
|
} else {
|
|
g_hash_table_remove (store->priv->checkboxes, path);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* e_storage_set_store_set_value:
|
|
* @store: a #EStorageSetStore
|
|
* @iter: A valid #GtkTreeIter for the row being modified
|
|
* @column: column number to modify
|
|
* @value: new value for the cell
|
|
*
|
|
* Sets the data in the cell specified by @iter and @column.
|
|
* The type of @value must be convertible to the type of the
|
|
* column.
|
|
*
|
|
**/
|
|
void
|
|
e_storage_set_store_set_value(EStorageSetStore *store, GtkTreeIter *iter,
|
|
gint column, GValue *value)
|
|
{
|
|
g_return_if_fail(E_IS_STORAGE_SET_STORE(store));
|
|
g_return_if_fail(VALID_ITER(iter, store));
|
|
g_return_if_fail(VALID_COL(column));
|
|
g_return_if_fail(G_IS_VALUE(value));
|
|
|
|
if (esss_real_set_value (store, iter, column, value)) {
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), iter);
|
|
gtk_tree_model_row_changed(GTK_TREE_MODEL(store), path, iter);
|
|
if (path)
|
|
gtk_tree_path_free(path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* e_storage_set_store_get_tree_path:
|
|
* @store: a #EStorageSetStore
|
|
* @folder_path: a string representing the #EStorageSet folder path
|
|
*
|
|
* Gets a #GtkTreePath corresponding to the folder path specified.
|
|
*
|
|
* Return value: the tree path of the folder
|
|
**/
|
|
GtkTreePath *
|
|
e_storage_set_store_get_tree_path (EStorageSetStore *store, const gchar *folder_path)
|
|
{
|
|
GNode *node;
|
|
|
|
g_return_if_fail(E_IS_STORAGE_SET_STORE(store));
|
|
|
|
node = g_hash_table_lookup (store->priv->path_to_node, folder_path+1);
|
|
|
|
return tree_path_from_node (store, node);
|
|
}
|
|
|
|
/**
|
|
* e_storage_set_store_get_folder_path:
|
|
* @store: a #EStorageSetStore
|
|
* @folder_path: a string representing the #EStorageSet folder path
|
|
*
|
|
* Gets a #GtkTreePath corresponding to the folder path specified.
|
|
*
|
|
* Return value: the tree path of the folder
|
|
**/
|
|
const gchar *
|
|
e_storage_set_store_get_folder_path (EStorageSetStore *store, GtkTreePath *tree_path)
|
|
{
|
|
GtkTreeIter iter;
|
|
GNode *node;
|
|
|
|
g_return_if_fail(E_IS_STORAGE_SET_STORE(store));
|
|
|
|
if (!esss_get_iter (GTK_TREE_MODEL (store), &iter, tree_path))
|
|
return NULL;
|
|
|
|
node = G_NODE (iter.user_data);
|
|
|
|
return (const gchar *)node->data;
|
|
}
|
|
|
|
/**
|
|
* e_storage_set_store_set_has_checkbox_func:
|
|
* @store: a #EStorageSetStore
|
|
* @func: a callback to determine if a row is checked
|
|
* @data: callback data
|
|
*
|
|
* Sets a callback function for checkbox visibility determination
|
|
**/
|
|
void
|
|
e_storage_set_store_set_has_checkbox_func (EStorageSetStore *store, EStorageSetStoreHasCheckBoxFunc func, gpointer data)
|
|
{
|
|
g_return_if_fail(E_IS_STORAGE_SET_STORE(store));
|
|
|
|
store->priv->has_checkbox_func = func;
|
|
store->priv->has_checkbox_func_data = data;
|
|
}
|
|
|