Function to convert a folder name/path to a filename for per-folder data.
2000-09-25 Not Zed <NotZed@HelixCode.com> * message-list.c (folder_to_cachename): Function to convert a folder name/path to a filename for per-folder data. (save_tree_state): (load_tree_state): (free_tree_state): For loading/saving the state of the expansion of nodes in the tree. (message_list_destroy): Save the tree state when done. (save_node_state): Changed logic, we save when the node should be closed on startup. i.e. any new nodes with children automatically default to being open. (subtree_unread): Check for unread messages in a subtree. So false messages (for tree roots) are properly displayed. svn path=/trunk/; revision=5584
This commit is contained in:
@ -1,3 +1,18 @@
|
||||
2000-09-25 Not Zed <NotZed@HelixCode.com>
|
||||
|
||||
* message-list.c (folder_to_cachename): Function to convert a
|
||||
folder name/path to a filename for per-folder data.
|
||||
(save_tree_state):
|
||||
(load_tree_state):
|
||||
(free_tree_state): For loading/saving the state of the expansion
|
||||
of nodes in the tree.
|
||||
(message_list_destroy): Save the tree state when done.
|
||||
(save_node_state): Changed logic, we save when the node should be
|
||||
closed on startup. i.e. any new nodes with children automatically
|
||||
default to being open.
|
||||
(subtree_unread): Check for unread messages in a subtree. So
|
||||
false messages (for tree roots) are properly displayed.
|
||||
|
||||
2000-09-25 Jeffrey Stedfast <fejj@helixcode.com>
|
||||
|
||||
* message-list.c (address_compare): Updated to use Nat's
|
||||
|
||||
@ -88,6 +88,8 @@ static void select_msg (MessageList *message_list, gint row);
|
||||
static char *filter_date (const void *data);
|
||||
static void nuke_uids (GtkObject *o);
|
||||
|
||||
static void save_tree_state(MessageList *ml);
|
||||
|
||||
static struct {
|
||||
char **image_base;
|
||||
GdkPixbuf *pixbuf;
|
||||
@ -632,6 +634,28 @@ ml_tree_icon_at (ETreeModel *etm, ETreePath *path, void *model_data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return true if there are any unread messages in the subtree */
|
||||
static int
|
||||
subtree_unread(MessageList *ml, ETreePath *node)
|
||||
{
|
||||
const CamelMessageInfo *info;
|
||||
char *uid;
|
||||
|
||||
while (node) {
|
||||
uid = e_tree_model_node_get_data((ETreeModel *)ml->table_model, node);
|
||||
if (strncmp (uid, "uid:", 4) == 0) {
|
||||
info = camel_folder_get_message_info(ml->folder, uid+4);
|
||||
if (!(info->flags & CAMEL_MESSAGE_SEEN))
|
||||
return TRUE;
|
||||
}
|
||||
if (node->children)
|
||||
if (subtree_unread(ml, node->children))
|
||||
return TRUE;
|
||||
node = node->next;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void *
|
||||
ml_tree_value_at (ETreeModel *etm, ETreePath *path, int col, void *model_data)
|
||||
{
|
||||
@ -719,12 +743,15 @@ ml_tree_value_at (ETreeModel *etm, ETreePath *path, int col, void *model_data)
|
||||
fake:
|
||||
/* This is a fake tree parent */
|
||||
switch (col){
|
||||
case COL_UNREAD:
|
||||
/* this value should probably be cached, as it could take a bit
|
||||
of processing to evaluate all the time */
|
||||
return (void *)subtree_unread(message_list, path->children);
|
||||
case COL_MESSAGE_STATUS:
|
||||
case COL_SCORE:
|
||||
case COL_ATTACHMENT:
|
||||
case COL_DELETED:
|
||||
case COL_COLOUR:
|
||||
case COL_UNREAD:
|
||||
case COL_SENT:
|
||||
case COL_RECEIVED:
|
||||
return (void *) 0;
|
||||
@ -1076,6 +1103,7 @@ message_list_destroy (GtkObject *object)
|
||||
MessageList *message_list = MESSAGE_LIST (object);
|
||||
int i;
|
||||
|
||||
save_tree_state(message_list);
|
||||
|
||||
gtk_object_unref (GTK_OBJECT (message_list->table_model));
|
||||
gtk_object_unref (GTK_OBJECT (message_list->header_model));
|
||||
@ -1234,41 +1262,156 @@ clear_tree (MessageList *ml)
|
||||
e_tree_model_node_set_expanded (etm, ml->tree_root, TRUE);
|
||||
}
|
||||
|
||||
static char *
|
||||
folder_to_cachename(CamelFolder *folder, const char *prefix)
|
||||
{
|
||||
char *url, *p, *filename;
|
||||
|
||||
url = camel_url_to_string (CAMEL_SERVICE (folder->parent_store)->url, FALSE);
|
||||
for (p = url; *p; p++) {
|
||||
if (!isprint((unsigned char)*p) || strchr(" /'\"`&();|<>${}!", *p))
|
||||
*p = '_';
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/config/%s%s", evolution_dir, prefix, url);
|
||||
g_free(url);
|
||||
return filename;
|
||||
}
|
||||
|
||||
/* we save the node id to the file if the node should be closed when
|
||||
we start up. We only save nodeid's for messages with children */
|
||||
static void
|
||||
save_node_state(MessageList *ml, FILE *out, ETreePath *node)
|
||||
{
|
||||
char *data;
|
||||
const CamelMessageInfo *info;
|
||||
|
||||
while (node) {
|
||||
if (node->children
|
||||
&& !e_tree_model_node_is_expanded((ETreeModel *)ml->table_model, node)) {
|
||||
data = e_tree_model_node_get_data((ETreeModel *)ml->table_model, node);
|
||||
if (data) {
|
||||
if (!strncmp(data, "uid:", 4)) {
|
||||
info = camel_folder_get_message_info(ml->folder, data+4);
|
||||
if (info) {
|
||||
fprintf(out, "%s\n", info->message_id);
|
||||
}
|
||||
} else {
|
||||
fprintf(out, "%s\n", data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node->children) {
|
||||
save_node_state(ml, out, node->children);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
load_tree_state(MessageList *ml)
|
||||
{
|
||||
char *filename, linebuf[10240];
|
||||
GHashTable *result;
|
||||
FILE *in;
|
||||
int len;
|
||||
|
||||
result = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
filename = folder_to_cachename(ml->folder, "treestate-");
|
||||
in = fopen(filename, "r");
|
||||
if (in) {
|
||||
while (fgets(linebuf, sizeof(linebuf), in) != NULL) {
|
||||
len = strlen(linebuf);
|
||||
if (len) {
|
||||
linebuf[len-1] = 0;
|
||||
g_hash_table_insert(result, g_strdup(linebuf), (void *)1);
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
g_free(filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* save tree info */
|
||||
static void
|
||||
save_tree_state(MessageList *ml)
|
||||
{
|
||||
char *filename;
|
||||
ETreePath *node;
|
||||
FILE *out;
|
||||
|
||||
filename = folder_to_cachename(ml->folder, "treestate-");
|
||||
out = fopen(filename, "w");
|
||||
if (out) {
|
||||
node = e_tree_model_get_root((ETreeModel *)ml->table_model);
|
||||
if (node && node->children) {
|
||||
save_node_state(ml, out, node->children);
|
||||
}
|
||||
fclose(out);
|
||||
}
|
||||
g_free(filename);
|
||||
}
|
||||
|
||||
static void
|
||||
free_node_state(void *key, void *value, void *data)
|
||||
{
|
||||
g_free(key);
|
||||
}
|
||||
|
||||
static void
|
||||
free_tree_state(GHashTable *expanded_nodes)
|
||||
{
|
||||
g_hash_table_foreach(expanded_nodes, free_node_state, 0);
|
||||
g_hash_table_destroy(expanded_nodes);
|
||||
}
|
||||
|
||||
/* only call if we have a tree model */
|
||||
/* builds the tree structure */
|
||||
static void build_subtree (MessageList *ml, ETreePath *parent,
|
||||
struct _container *c, int *row);
|
||||
static void build_subtree (MessageList *ml, ETreePath *parent, struct _container *c, int *row, GHashTable *);
|
||||
|
||||
static void
|
||||
build_tree (MessageList *ml, struct _container *c)
|
||||
{
|
||||
int row = 0;
|
||||
GHashTable *expanded_nodes;
|
||||
|
||||
clear_tree (ml);
|
||||
build_subtree (ml, ml->tree_root, c, &row);
|
||||
expanded_nodes = load_tree_state(ml);
|
||||
build_subtree (ml, ml->tree_root, c, &row, expanded_nodes);
|
||||
free_tree_state(expanded_nodes);
|
||||
}
|
||||
|
||||
static void
|
||||
build_subtree (MessageList *ml, ETreePath *parent,
|
||||
struct _container *c, int *row)
|
||||
build_subtree (MessageList *ml, ETreePath *parent, struct _container *c, int *row, GHashTable *expanded_nodes)
|
||||
{
|
||||
ETreeModel *tree = E_TREE_MODEL (ml->table_model);
|
||||
ETreePath *node;
|
||||
char *id;
|
||||
int expanded;
|
||||
|
||||
while (c) {
|
||||
if (c->message) {
|
||||
id = g_strdup_printf ("uid:%s", c->message->uid);
|
||||
g_hash_table_insert (ml->uid_rowmap,
|
||||
g_strdup (c->message->uid),
|
||||
GINT_TO_POINTER ((*row)++));
|
||||
} else
|
||||
id = g_strdup_printf ("subject:%s", c->root_subject);
|
||||
node = e_tree_model_node_insert (tree, parent, 0, id);
|
||||
id = g_strdup_printf("uid:%s", c->message->uid);
|
||||
g_hash_table_insert(ml->uid_rowmap, g_strdup (c->message->uid), GINT_TO_POINTER ((*row)++));
|
||||
if (c->child) {
|
||||
if (c->message && c->message->message_id)
|
||||
expanded = !g_hash_table_lookup(expanded_nodes, c->message->message_id) != 0;
|
||||
else
|
||||
expanded = TRUE;
|
||||
}
|
||||
} else {
|
||||
id = g_strdup_printf("subject:%s", c->root_subject);
|
||||
if (c->child) {
|
||||
expanded = !g_hash_table_lookup(expanded_nodes, id) != 0;
|
||||
}
|
||||
}
|
||||
node = e_tree_model_node_insert(tree, parent, 0, id);
|
||||
if (c->child) {
|
||||
/* by default, open all trees */
|
||||
e_tree_model_node_set_expanded (tree, node, TRUE);
|
||||
build_subtree (ml, node, c->child, row);
|
||||
if (expanded)
|
||||
e_tree_model_node_set_expanded(tree, node, expanded);
|
||||
build_subtree(ml, node, c->child, row, expanded_nodes);
|
||||
}
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ struct _container {
|
||||
const CamelMessageInfo *message;
|
||||
char *root_subject; /* cached root equivalent subject */
|
||||
int re; /* re version of subject? */
|
||||
int order; /* the order of this message in the folder */
|
||||
int order;
|
||||
};
|
||||
|
||||
void mail_do_thread_messages (MessageList *ml, GPtrArray *uids,
|
||||
|
||||
Reference in New Issue
Block a user