[evolution] Bug #571881 - Make messages inherit labels from collapsed subtree



commit f41979c499aa770e81441c66eb316abbcd8c4f7a
Author: Matt McCutchen <matt mattmccutchen net>
Date:   Wed Sep 9 11:20:25 2009 +0200

    Bug #571881 - Make messages inherit labels from collapsed subtree

 mail/message-list.c |  347 ++++++++++++++++++++++-----------------------------
 1 files changed, 149 insertions(+), 198 deletions(-)
---
diff --git a/mail/message-list.c b/mail/message-list.c
index 714768e..2439071 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -1136,57 +1136,57 @@ ml_tree_icon_at (ETreeModel *etm, ETreePath path, gpointer model_data)
 	return NULL;
 }
 
-/* return true if there are any unread messages in the subtree */
-static gint
-subtree_unread(MessageList *ml, ETreePath node)
+static void
+for_node_and_subtree_if_collapsed (MessageList *ml, ETreePath node,
+		ETreePathFunc func, gpointer data)
 {
-	CamelMessageInfo *info;
+	ETreeModel *etm = ml->model;
 	ETreePath child;
 
-	while (node) {
-		info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
-		g_return_val_if_fail (info != NULL, FALSE);
+	func (etm, node, data);
 
-		if (!(camel_message_info_flags(info) & CAMEL_MESSAGE_SEEN))
-			return TRUE;
-
-		if ((child = e_tree_model_node_get_first_child (E_TREE_MODEL (ml->model), node)))
-			if (subtree_unread(ml, child))
-				return TRUE;
-		node = e_tree_model_node_get_next (ml->model, node);
-	}
-	return FALSE;
+	child = e_tree_model_node_get_first_child (etm, node);
+	if (child && !e_tree_node_is_expanded (ml->tree, node))
+		e_tree_model_node_traverse (etm, node, func, data);
 }
 
-static time_t
-subtree_latest(MessageList *ml, ETreePath node, gint sent)
+static gboolean
+unread_foreach (ETreeModel *etm, ETreePath node, gpointer data)
 {
+	gboolean *saw_unread = data;
 	CamelMessageInfo *info;
-	time_t latest = 0, date;
-	ETreePath *child;
 
-	while (node) {
-		info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
-		g_return_val_if_fail (info != NULL, 0);
+	info = e_tree_memory_node_get_data ((ETreeMemory *)etm, node);
+	g_return_val_if_fail (info != NULL, FALSE);
 
-		if (sent)
-			date = camel_message_info_date_sent(info);
-		else
-			date = camel_message_info_date_received(info);
+	if (!(camel_message_info_flags (info) & CAMEL_MESSAGE_SEEN))
+		*saw_unread = TRUE;
 
-		if (latest == 0 || date > latest)
-			latest = date;
+	return FALSE;
+}
 
-		if ((child = e_tree_model_node_get_first_child (ml->model, node))) {
-			date = subtree_latest(ml, child, sent);
-			if (latest == 0 || (date != 0 && date > latest))
-				latest = date;
-		}
+struct LatestData {
+	gboolean sent;
+	time_t latest;
+};
 
-		node = e_tree_model_node_get_next (ml->model, node);
-	}
+static gboolean
+latest_foreach (ETreeModel *etm, ETreePath node, gpointer data)
+{
+	struct LatestData *ld = data;
+	CamelMessageInfo *info;
+	time_t date;
+
+	info = e_tree_memory_node_get_data ((ETreeMemory *)etm, node);
+	g_return_val_if_fail (info != NULL, FALSE);
 
-	return latest;
+	date = ld->sent ? camel_message_info_date_sent (info)
+			: camel_message_info_date_received (info);
+
+	if (ld->latest == 0 || date > ld->latest)
+		ld->latest = date;
+
+	return FALSE;
 }
 
 static gchar *
@@ -1232,122 +1232,65 @@ sanitize_recipients (const gchar *string)
 	return g_string_free (recipients, FALSE);
 }
 
-static gint
-get_all_labels (MessageList *message_list,
-                CamelMessageInfo *msg_info,
-                gchar **label_str,
-                gboolean get_tags)
-{
-	EShell *shell;
-	EShellBackend *shell_backend;
-	EShellSettings *shell_settings;
+struct LabelsData {
 	EMailLabelListStore *store;
-	GtkTreeIter iter;
-	GString *str;
-	const gchar *property_name;
-	const gchar *old_label;
-	gchar *new_label;
-	gint count = 0;
-	const CamelFlag *flag;
-
-	shell_backend = message_list_get_shell_backend (message_list);
-	shell = e_shell_backend_get_shell (shell_backend);
-	shell_settings = e_shell_get_shell_settings (shell);
-
-	property_name = "mail-label-list-store";
-	store = e_shell_settings_get_object (shell_settings, property_name);
-
-	str = g_string_new ("");
-
-	for (flag = camel_message_info_user_flags (msg_info); flag; flag = flag->next) {
-		gchar *item;
-
-		if (!e_mail_label_list_store_lookup (store, flag->name, &iter))
-			continue;
-
-		if (get_tags)
-			item = e_mail_label_list_store_get_tag (store, &iter);
-		else
-			item = e_mail_label_list_store_get_name (store, &iter);
-
-		if (str->len)
-			g_string_append (str, ", ");
+	GHashTable *labels_tag2iter;
+};
 
-		g_string_append (str, item);
-		count++;
+static void
+add_label_if_known (struct LabelsData *ld, const gchar *tag)
+{
+	GtkTreeIter label_defn;
 
-		g_free (item);
+	if (e_mail_label_list_store_lookup (ld->store, tag, &label_defn)) {
+		g_hash_table_insert (ld->labels_tag2iter,
+			/* Should be the same as the "tag" arg */
+			e_mail_label_list_store_get_tag (ld->store, &label_defn),
+			gtk_tree_iter_copy (&label_defn));
 	}
+}
 
-	old_label = camel_message_info_user_tag (msg_info, "label");
-	if (old_label == NULL)
-		goto exit;
-
-	/* Convert old-style labels ("<name>") to "$Label<name>". */
-	new_label = g_alloca (strlen (old_label) + 10);
-	g_stpcpy (g_stpcpy (new_label, "$Label"), old_label);
-
-	if (e_mail_label_list_store_lookup (store, new_label, &iter)) {
-		gchar *name = NULL;
+static gboolean
+add_all_labels_foreach (ETreeModel *etm, ETreePath node, gpointer data)
+{
+	struct LabelsData *ld = data;
+	CamelMessageInfo *msg_info;
+	const gchar *old_label;
+	gchar *new_label;
+	const CamelFlag *flag;
 
-		if (str->len)
-			g_string_append (str, ", ");
+	msg_info = e_tree_memory_node_get_data ((ETreeMemory *)etm, node);
+	g_return_val_if_fail (msg_info != NULL, FALSE);
 
-		if (!get_tags)
-			name = e_mail_label_list_store_get_name (store, &iter);
+	for (flag = camel_message_info_user_flags (msg_info); flag; flag = flag->next)
+		add_label_if_known (ld, flag->name);
 
-		g_string_append (str, (get_tags || !name) ? old_label : name);
-		count++;
+	old_label = camel_message_info_user_tag (msg_info, "label");
+	if (old_label != NULL) {
+		/* Convert old-style labels ("<name>") to "$Label<name>". */
+		new_label = g_alloca (strlen (old_label) + 10);
+		g_stpcpy (g_stpcpy (new_label, "$Label"), old_label);
 
-		g_free (name);
+		add_label_if_known (ld, new_label);
 	}
 
-exit:
-	*label_str = g_string_free (str, FALSE);
-
-	g_object_unref (store);
-
-	return count;
+	return FALSE;
 }
 
-static const gchar *
-get_label_color (MessageList *message_list,
-                 const gchar *tag)
+static EMailLabelListStore *
+ml_get_label_list_store (MessageList *message_list)
 {
-	EShell *shell;
 	EShellBackend *shell_backend;
+	EShell *shell;
 	EShellSettings *shell_settings;
 	EMailLabelListStore *store;
-	GtkTreeIter iter;
-	GdkColor color;
-	const gchar *property_name;
-	const gchar *interned = NULL;
-	gchar *color_spec;
-
-	/* FIXME get_all_labels() should return an array of tree iterators,
-	 *       not strings.  Now we just have to lookup the tag again. */
 
 	shell_backend = message_list_get_shell_backend (message_list);
 	shell = e_shell_backend_get_shell (shell_backend);
 	shell_settings = e_shell_get_shell_settings (shell);
+	store = e_shell_settings_get_object (shell_settings, "mail-label-list-store");
 
-	property_name = "mail-label-list-store";
-	store = e_shell_settings_get_object (shell_settings, property_name);
-
-	if (!e_mail_label_list_store_lookup (store, tag, &iter))
-		goto exit;
-
-	e_mail_label_list_store_get_color (store, &iter, &color);
-
-	/* XXX Hack to avoid returning an allocated string. */
-	color_spec = gdk_color_to_string (&color);
-	interned = g_intern_string (color_spec);
-	g_free (color_spec);
-
-exit:
-	g_object_unref (store);
-
-	return interned;
+	return store;
 }
 
 static const gchar *
@@ -1495,23 +1438,22 @@ ml_tree_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data
 	case COL_SUBJECT_NORM:
 		return (gpointer) get_normalised_string (message_list, msg_info, col);
 	case COL_SENT: {
-		ETreePath child;
+		struct LatestData ld;
+		ld.sent = TRUE;
+		ld.latest = 0;
 
-		child = e_tree_model_node_get_first_child(etm, path);
-		if (child && !e_tree_node_is_expanded(message_list->tree, path)) {
-			return GINT_TO_POINTER (subtree_latest (message_list, child, 1));
-		}
+		for_node_and_subtree_if_collapsed (message_list, path, latest_foreach, &ld);
 
-		return GINT_TO_POINTER (camel_message_info_date_sent(msg_info));
+		return GINT_TO_POINTER (ld.latest);
 	}
 	case COL_RECEIVED: {
-		ETreePath child;
+		struct LatestData ld;
+		ld.sent = FALSE;
+		ld.latest = 0;
 
-		child = e_tree_model_node_get_first_child(etm, path);
-		if (child && !e_tree_node_is_expanded(message_list->tree, path)) {
-			return GINT_TO_POINTER (subtree_latest (message_list, child, 0));
-		}
-		return GINT_TO_POINTER (camel_message_info_date_received(msg_info));
+		for_node_and_subtree_if_collapsed (message_list, path, latest_foreach, &ld);
+
+		return GINT_TO_POINTER (ld.latest);
 	}
 	case COL_TO:
 		str = camel_message_info_to (msg_info);
@@ -1523,21 +1465,14 @@ ml_tree_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data
 	case COL_DELETED:
 		return GINT_TO_POINTER ((camel_message_info_flags(msg_info) & CAMEL_MESSAGE_DELETED) != 0);
 	case COL_UNREAD: {
-		ETreePath child;
-		flags = camel_message_info_flags(msg_info);
+		gboolean saw_unread = FALSE;
 
-		child = e_tree_model_node_get_first_child(etm, path);
-		if (child && !e_tree_node_is_expanded(message_list->tree, path)
-		    && (flags & CAMEL_MESSAGE_SEEN)) {
-			return GINT_TO_POINTER (subtree_unread (message_list, child));
-		}
+		for_node_and_subtree_if_collapsed (message_list, path, unread_foreach, &saw_unread);
 
-		return GINT_TO_POINTER (!(flags & CAMEL_MESSAGE_SEEN));
+		return GINT_TO_POINTER (saw_unread);
 	}
 	case COL_COLOUR: {
 		const gchar *colour, *due_by, *completed, *followup;
-		gchar *labels_string = NULL;
-		gint n;
 
 		/* Priority: colour tag; label tag; important flag; due-by tag */
 
@@ -1551,8 +1486,29 @@ ml_tree_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data
 		completed = camel_message_info_user_tag(msg_info, "completed-on");
 		followup = camel_message_info_user_tag(msg_info, "follow-up");
 		if (colour == NULL) {
-			if ((n = get_all_labels (message_list, msg_info,  &labels_string, TRUE)) == 1) {
-				colour = get_label_color (message_list, labels_string);
+			/* Get all applicable labels. */
+			struct LabelsData ld;
+
+			ld.store = ml_get_label_list_store (message_list);
+			ld.labels_tag2iter = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gtk_tree_iter_free);
+			for_node_and_subtree_if_collapsed (message_list, path, add_all_labels_foreach, &ld);
+
+			if (g_hash_table_size (ld.labels_tag2iter) == 1) {
+				GHashTableIter iter;
+				GtkTreeIter *label_defn;
+				GdkColor colour_val;
+				gchar *colour_alloced;
+
+				/* Extract the single label from the hashtable. */
+				g_hash_table_iter_init (&iter, ld.labels_tag2iter);
+				g_hash_table_iter_next (&iter, NULL, (gpointer *) &label_defn);
+
+				e_mail_label_list_store_get_color (ld.store, label_defn, &colour_val);
+
+				/* XXX Hack to avoid returning an allocated string. */
+				colour_alloced = gdk_color_to_string (&colour_val);
+				colour = g_intern_string (colour_alloced);
+				g_free (colour_alloced);
 			} else if (camel_message_info_flags(msg_info) & CAMEL_MESSAGE_FLAGGED) {
 				/* FIXME: extract from the important.xpm somehow. */
 				colour = "#A7453E";
@@ -1562,9 +1518,10 @@ ml_tree_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data
 				if ((followup && *followup) || now >= camel_header_decode_date (due_by, NULL))
 					colour = "#A7453E";
 			}
-		}
 
-		g_free (labels_string);
+			g_hash_table_destroy (ld.labels_tag2iter);
+			g_object_unref (ld.store);
+		}
 
 		return (gpointer) colour;
 	}
@@ -1626,21 +1583,37 @@ ml_tree_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data
 			return (gpointer)("");
 	}
 	case COL_LABELS:{
-		gchar *str = NULL;
-		GString *cleansed_str;
+		struct LabelsData ld;
+		GString *result = g_string_new ("");
 
-		cleansed_str = g_string_new ("");
+		ld.store = ml_get_label_list_store (message_list);
+		ld.labels_tag2iter = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gtk_tree_iter_free);
+		for_node_and_subtree_if_collapsed (message_list, path, add_all_labels_foreach, &ld);
 
-		if (get_all_labels (message_list, msg_info, &str, FALSE)) {
-			gint i;
-			for (i = 0; str[i] != '\0'; ++i) {
-				if (str[i] != '_') {
-					g_string_append_c (cleansed_str, str[i]);
-				}
+		if (g_hash_table_size (ld.labels_tag2iter) > 0) {
+			GHashTableIter iter;
+			GtkTreeIter *label_defn;
+
+			g_hash_table_iter_init (&iter, ld.labels_tag2iter);
+			while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &label_defn)) {
+				gchar *label_name, *label_name_clean;
+				
+				if (result->len > 0)
+					g_string_append (result, ", ");
+
+				label_name = e_mail_label_list_store_get_name (ld.store, label_defn);
+				label_name_clean = e_str_without_underscores (label_name);
+
+				g_string_append (result, label_name_clean);
+
+				g_free(label_name_clean);
+				g_free(label_name);
 			}
-			return (gpointer) (g_string_free (cleansed_str, FALSE));
-		} else
-			return (gpointer) ("");
+		}
+
+		g_hash_table_destroy (ld.labels_tag2iter);
+		g_object_unref (ld.store);
+		return (gpointer) g_string_free (result, FALSE);
 	}
 	default:
 		g_warning ("This shouldn't be reached\n");
@@ -1652,7 +1625,7 @@ static gpointer
 ml_tree_sort_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model_data)
 {
 	MessageList *message_list = model_data;
-	CamelMessageInfo *msg_info;
+	struct LatestData ld;
 
 	if (!(col == COL_SENT || col == COL_RECEIVED))
 		return ml_tree_value_at (etm, path, col, model_data);
@@ -1660,36 +1633,14 @@ ml_tree_sort_value_at (ETreeModel *etm, ETreePath path, gint col, gpointer model
 	if (e_tree_model_node_is_root (etm, path))
 		return NULL;
 
-	/* retrieve the message information array */
-	msg_info = e_tree_memory_node_get_data (E_TREE_MEMORY(etm), path);
-	g_return_val_if_fail (msg_info != NULL, NULL);
-
-	if (col == COL_SENT) {
-		if (message_list->priv->thread_latest) {
-			ETreePath child;
-
-			child = e_tree_model_node_get_first_child (etm, path);
-			if (child) {
-				return GINT_TO_POINTER (subtree_latest (message_list, child, 1));
-			}
-		}
-
-		return GINT_TO_POINTER (camel_message_info_date_sent(msg_info));
-	} else if (col == COL_RECEIVED) {
-		if (message_list->priv->thread_latest) {
-			ETreePath child;
-
-			child = e_tree_model_node_get_first_child (etm, path);
-			if (child) {
-				return GINT_TO_POINTER (subtree_latest (message_list, child, 0));
-			}
-		}
-
-		return GINT_TO_POINTER (camel_message_info_date_received(msg_info));
-	}
+	ld.sent = (col == COL_SENT);
+	ld.latest = 0;
 
-	return ml_tree_value_at (etm, path, col, model_data);
+	latest_foreach (etm, path, &ld);
+	if (message_list->priv->thread_latest)
+		e_tree_model_node_traverse (etm, path, latest_foreach, &ld);
 
+	return GINT_TO_POINTER (ld.latest);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]