aboutsummaryrefslogtreecommitdiffstats
path: root/mail/message-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/message-list.c')
-rw-r--r--mail/message-list.c2917
1 files changed, 0 insertions, 2917 deletions
diff --git a/mail/message-list.c b/mail/message-list.c
deleted file mode 100644
index bb2d3e1464..0000000000
--- a/mail/message-list.c
+++ /dev/null
@@ -1,2917 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Miguel de Icaza (miguel@ximian.com)
- * Bertrand Guiheneuf (bg@aful.org)
- * And just about everyone else in evolution ...
- *
- * Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <ctype.h>
-
-#include <glib/gunicode.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <gal/util/e-util.h>
-#include <gal/widgets/e-gui-utils.h>
-#include <gal/e-table/e-cell-text.h>
-#include <gal/e-table/e-cell-toggle.h>
-#include <gal/e-table/e-cell-checkbox.h>
-#include <gal/e-table/e-cell-tree.h>
-#include <gal/e-table/e-cell-date.h>
-#include <gal/e-table/e-cell-size.h>
-#include <gal/e-table/e-tree-memory.h>
-#include <gal/e-table/e-tree-memory-callbacks.h>
-
-#include <camel/camel-exception.h>
-#include <camel/camel-file-utils.h>
-#include <camel/camel-folder.h>
-#include <camel/camel-folder-thread.h>
-#include <e-util/ename/e-name-western.h>
-#include <e-util/e-memory.h>
-
-#include "filter/filter-label.h"
-
-#include "mail-config.h"
-#include "message-list.h"
-#include "mail-mt.h"
-#include "mail-tools.h"
-#include "mail-ops.h"
-
-#include "art/mail-new.xpm"
-#include "art/mail-read.xpm"
-#include "art/mail-replied.xpm"
-#include "art/attachment.xpm"
-#include "art/priority-high.xpm"
-#include "art/empty.xpm"
-#include "art/score-lowest.xpm"
-#include "art/score-lower.xpm"
-#include "art/score-low.xpm"
-#include "art/score-normal.xpm"
-#include "art/score-high.xpm"
-#include "art/score-higher.xpm"
-#include "art/score-highest.xpm"
-#include "art/flag-for-followup.xpm"
-
-/*#define TIMEIT */
-
-#ifdef TIMEIT
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#define d(x)
-#define t(x)
-
-/*
- * Default sizes for the ETable display
- *
- */
-#define N_CHARS(x) (CHAR_WIDTH * (x))
-
-#define COL_ICON_WIDTH (16)
-#define COL_ATTACH_WIDTH (16)
-#define COL_CHECK_BOX_WIDTH (16)
-#define COL_FROM_EXPANSION (24.0)
-#define COL_FROM_WIDTH_MIN (32)
-#define COL_SUBJECT_EXPANSION (30.0)
-#define COL_SUBJECT_WIDTH_MIN (32)
-#define COL_SENT_EXPANSION (24.0)
-#define COL_SENT_WIDTH_MIN (32)
-#define COL_RECEIVED_EXPANSION (20.0)
-#define COL_RECEIVED_WIDTH_MIN (32)
-#define COL_TO_EXPANSION (24.0)
-#define COL_TO_WIDTH_MIN (32)
-#define COL_SIZE_EXPANSION (6.0)
-#define COL_SIZE_WIDTH_MIN (32)
-
-enum {
- NORMALISED_SUBJECT,
- NORMALISED_FROM,
- NORMALISED_TO,
- NORMALISED_LAST,
-};
-
-#define PARENT_TYPE (e_tree_scrolled_get_type ())
-
-/* #define SMART_ADDRESS_COMPARE */
-
-#ifdef SMART_ADDRESS_COMPARE
-struct _EMailAddress {
- ENameWestern *wname;
- char *address;
-};
-
-typedef struct _EMailAddress EMailAddress;
-#endif /* SMART_ADDRESS_COMPARE */
-
-static ETreeScrolledClass *message_list_parent_class;
-
-static void on_cursor_activated_cmd (ETree *tree, int row, ETreePath path, gpointer user_data);
-static void on_selection_changed_cmd(ETree *tree, MessageList *ml);
-static gint on_click (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, MessageList *list);
-static char *filter_date (time_t date);
-static char *filter_size (int size);
-
-static void folder_changed (CamelObject *o, gpointer event_data, gpointer user_data);
-static void message_changed (CamelObject *o, gpointer event_data, gpointer user_data);
-
-static void hide_save_state(MessageList *ml);
-static void hide_load_state(MessageList *ml);
-
-/* note: @changes is owned/freed by the caller */
-/*static void mail_do_regenerate_messagelist (MessageList *list, const char *search, const char *hideexpr, CamelFolderChangeInfo *changes);*/
-static void mail_regen_list(MessageList *ml, const char *search, const char *hideexpr, CamelFolderChangeInfo *changes);
-
-static void clear_info(char *key, ETreePath *node, MessageList *ml);
-
-enum {
- MESSAGE_SELECTED,
- MESSAGE_LIST_BUILT,
- LAST_SIGNAL
-};
-
-static guint message_list_signals [LAST_SIGNAL] = {0, };
-
-static struct {
- char **image_base;
- GdkPixbuf *pixbuf;
-} states_pixmaps [] = {
- { mail_new_xpm, NULL },
- { mail_read_xpm, NULL },
- { mail_replied_xpm, NULL },
-/* FIXME: Replace these with pixmaps for multiple_read and multiple_unread */
- { mail_new_xpm, NULL },
- { mail_read_xpm, NULL },
- { empty_xpm, NULL },
- { attachment_xpm, NULL },
- { priority_high_xpm, NULL },
- { score_lowest_xpm, NULL },
- { score_lower_xpm, NULL },
- { score_low_xpm, NULL },
- { score_normal_xpm, NULL },
- { score_high_xpm, NULL },
- { score_higher_xpm, NULL },
- { score_highest_xpm, NULL },
- { flag_for_followup_xpm,NULL },
- { NULL, NULL }
-};
-
-#ifdef SMART_ADDRESS_COMPARE
-static EMailAddress *
-e_mail_address_new (const char *address)
-{
- CamelInternetAddress *cia;
- EMailAddress *new;
- const char *name = NULL, *addr = NULL;
-
- cia = camel_internet_address_new ();
- if (camel_address_unformat (CAMEL_ADDRESS (cia), address) == -1) {
- camel_object_unref (cia);
- return NULL;
- }
- camel_internet_address_get (cia, 0, &name, &addr);
-
- new = g_new (EMailAddress, 1);
- new->address = g_strdup (addr);
- if (name && *name) {
- new->wname = e_name_western_parse (name);
- } else {
- new->wname = NULL;
- }
-
- camel_object_unref (cia);
-
- return new;
-}
-
-static void
-e_mail_address_free (EMailAddress *addr)
-{
- g_return_if_fail (addr != NULL);
-
- g_free (addr->address);
- if (addr->wname)
- e_name_western_free (addr->wname);
- g_free (addr);
-}
-
-/* Note: by the time this function is called, the strings have already
- been normalised which means we can assume all lowercase chars and
- we can just use strcmp for the final comparison. */
-static int
-e_mail_address_compare (gconstpointer address1, gconstpointer address2)
-{
- const EMailAddress *addr1 = address1;
- const EMailAddress *addr2 = address2;
- int retval;
-
- g_return_val_if_fail (addr1 != NULL, 1);
- g_return_val_if_fail (addr2 != NULL, -1);
-
- if (!addr1->wname && !addr2->wname) {
- /* have to compare addresses, one or both don't have names */
- g_return_val_if_fail (addr1->address != NULL, 1);
- g_return_val_if_fail (addr2->address != NULL, -1);
-
- return strcmp (addr1->address, addr2->address);
- }
-
- if (!addr1->wname)
- return -1;
- if (!addr2->wname)
- return 1;
-
- if (!addr1->wname->last && !addr2->wname->last) {
- /* neither has a last name - default to address? */
- /* FIXME: what do we compare next? */
- g_return_val_if_fail (addr1->address != NULL, 1);
- g_return_val_if_fail (addr2->address != NULL, -1);
-
- return strcmp (addr1->address, addr2->address);
- }
-
- if (!addr1->wname->last)
- return -1;
- if (!addr2->wname->last)
- return 1;
-
- retval = strcmp (addr1->wname->last, addr2->wname->last);
- if (retval)
- return retval;
-
- /* last names are identical - compare first names */
-
- if (!addr1->wname->first && !addr2->wname->first)
- return strcmp (addr1->address, addr2->address);
-
- if (!addr1->wname->first)
- return -1;
- if (!addr2->wname->first)
- return 1;
-
- retval = strcmp (addr1->wname->first, addr2->wname->first);
- if (retval)
- return retval;
-
- return strcmp (addr1->address, addr2->address);
-}
-#endif /* SMART_ADDRESS_COMPARE */
-
-/* Note: by the time this function is called, the strings have already
- been normalised which means we can assume all lowercase chars and
- we can just use strcmp for the final comparison. */
-static int
-address_compare (gconstpointer address1, gconstpointer address2)
-{
-#ifdef SMART_ADDRESS_COMPARE
- EMailAddress *addr1, *addr2;
-#endif /* SMART_ADDRESS_COMPARE */
- int retval;
-
- g_return_val_if_fail (address1 != NULL, 1);
- g_return_val_if_fail (address2 != NULL, -1);
-
-#ifdef SMART_ADDRESS_COMPARE
- addr1 = e_mail_address_new (address1);
- addr2 = e_mail_address_new (address2);
- retval = e_mail_address_compare (addr1, addr2);
- e_mail_address_free (addr1);
- e_mail_address_free (addr2);
-#else
- retval = strcmp ((const char *) address1, (const char *) address2);
-#endif /* SMART_ADDRESS_COMPARE */
-
- return retval;
-}
-
-static char *
-filter_size (gint size)
-{
- gfloat fsize;
-
- if (size < 1024) {
- return g_strdup_printf ("%d", size);
- } else {
- fsize = ((gfloat) size) / 1024.0;
- if (fsize < 1024.0) {
- return g_strdup_printf ("%.2f K", fsize);
- } else {
- fsize /= 1024.0;
- return g_strdup_printf ("%.2f M", fsize);
- }
- }
-}
-
-/* Gets the uid of the message displayed at a given view row */
-static const char *
-get_message_uid (MessageList *message_list, ETreePath node)
-{
- CamelMessageInfo *info;
-
- g_assert (node != NULL);
- info = e_tree_memory_node_get_data (E_TREE_MEMORY (message_list->model), node);
- /* correct me if I'm wrong, but this should never be NULL, should it? */
- g_assert (info != NULL);
-
- return camel_message_info_uid (info);
-}
-
-/* Gets the CamelMessageInfo for the message displayed at the given
- * view row.
- */
-static CamelMessageInfo *
-get_message_info (MessageList *message_list, ETreePath node)
-{
- CamelMessageInfo *info;
-
- g_assert (node != NULL);
- info = e_tree_memory_node_get_data (E_TREE_MEMORY (message_list->model), node);
- g_assert (info != NULL);
-
- return info;
-}
-
-static const char *
-get_normalised_string (MessageList *message_list, CamelMessageInfo *info, int col)
-{
- const char *string, *str;
- char *normalised;
- EPoolv *poolv;
- int index;
-
- switch (col) {
- case COL_SUBJECT_NORM:
- string = camel_message_info_subject (info);
- index = NORMALISED_SUBJECT;
- break;
- case COL_FROM_NORM:
- string = camel_message_info_from (info);
- index = NORMALISED_FROM;
- break;
- case COL_TO_NORM:
- string = camel_message_info_to (info);
- index = NORMALISED_TO;
- break;
- default:
- string = NULL;
- index = NORMALISED_LAST;
- g_assert_not_reached ();
- }
-
- /* slight optimisation */
- if (string == NULL || string[0] == '\0')
- return "";
-
- poolv = g_hash_table_lookup (message_list->normalised_hash, camel_message_info_uid (info));
- if (poolv == NULL) {
- poolv = e_poolv_new (NORMALISED_LAST);
- g_hash_table_insert (message_list->normalised_hash, (char *) camel_message_info_uid (info), poolv);
- } else {
- str = e_poolv_get (poolv, index);
- if (*str)
- return str;
- }
-
- if (col == COL_SUBJECT_NORM) {
- const unsigned char *subject;
-
- subject = (const unsigned char *) string;
- while (!g_ascii_strncasecmp (subject, "Re:", 3)) {
- subject += 3;
-
- /* jump over any spaces */
- while (*subject && isspace ((int) *subject))
- subject++;
- }
-
- /* jump over any spaces */
- while (*subject && isspace ((int) *subject))
- subject++;
-
- string = (const char *) subject;
- }
-
- normalised = g_utf8_collate_key (string, -1);
- e_poolv_set (poolv, index, normalised, TRUE);
-
- return e_poolv_get (poolv, index);
-}
-
-struct search_func_data {
- MessageList *message_list;
- guint32 flags;
- guint32 mask;
-};
-
-static gboolean
-search_func (ETreeModel *model, ETreePath path, struct search_func_data *data)
-{
- CamelMessageInfo *info;
-
- if (e_tree_model_node_is_root (data->message_list->model, path))
- return FALSE;
-
- info = get_message_info (data->message_list, path);
-
- if (info && (info->flags & data->mask) == data->flags) {
- if (data->message_list->cursor_uid) {
- g_free (data->message_list->cursor_uid);
- data->message_list->cursor_uid = g_strdup (camel_message_info_uid (info));
- }
- g_signal_emit (GTK_OBJECT (data->message_list), message_list_signals[MESSAGE_SELECTED], 0,
- camel_message_info_uid (info));
- return TRUE;
- }
- return FALSE;
-}
-
-
-/**
- * message_list_select:
- * @message_list: a MessageList
- * @base_row: the (model) row to start from
- * @direction: the direction to search in
- * @flags: a set of flag values
- * @mask: a mask for comparing against @flags
- * @wraparound: if %TRUE, go back to the beginning for
- * the next match if necessary.
- *
- * This moves the message list selection to a suitable row. @base_row
- * lists the first (model) row to try, but as a special case, model
- * row -1 is mapped to the last row. @flags and @mask combine to specify
- * what constitutes a suitable row. @direction is
- * %MESSAGE_LIST_SELECT_NEXT if it should find the next matching
- * message, or %MESSAGE_LIST_SELECT_PREVIOUS if it should find the
- * previous. If no suitable row is found, the selection will be
- * unchanged.
- *
- * Returns %TRUE if a new message has been selected or %FALSE otherwise.
- **/
-gboolean
-message_list_select (MessageList *message_list,
- MessageListSelectDirection direction,
- guint32 flags,
- guint32 mask,
- gboolean wraparound)
-{
- struct search_func_data data;
- ETreeFindNextParams params = 0;
-
- data.message_list = message_list;
- data.flags = flags;
- data.mask = mask;
-
- if (direction == MESSAGE_LIST_SELECT_NEXT)
- params |= E_TREE_FIND_NEXT_FORWARD;
- else
- params |= E_TREE_FIND_NEXT_BACKWARD;
-
- if (wraparound)
- params |= E_TREE_FIND_NEXT_WRAP;
-
- return e_tree_find_next (message_list->tree, params, (ETreePathFunc) search_func, &data);
-}
-
-
-/**
- * message_list_select_uid:
- * @message_list:
- * @uid:
- *
- * Selects the message with the given UID.
- **/
-void
-message_list_select_uid (MessageList *message_list, const char *uid)
-{
- ETreePath node;
-
- node = g_hash_table_lookup (message_list->uid_nodemap, uid);
- if (node) {
- CamelMessageInfo *info;
-
- info = get_message_info (message_list, node);
- e_tree_set_cursor (message_list->tree, node);
-
- g_free (message_list->cursor_uid);
- message_list->cursor_uid = g_strdup (camel_message_info_uid (info));
-
- g_signal_emit (GTK_OBJECT (message_list), message_list_signals[MESSAGE_SELECTED], 0,
- camel_message_info_uid (info));
- } else {
- g_free (message_list->cursor_uid);
- message_list->cursor_uid = NULL;
- g_signal_emit (GTK_OBJECT (message_list), message_list_signals[MESSAGE_SELECTED], 0, NULL);
- }
-}
-
-
-void
-message_list_select_next_thread (MessageList *message_list)
-{
- ETreePath node, last;
-
- if (!message_list->cursor_uid)
- return;
-
- /* get the thread parent node */
- last = node = g_hash_table_lookup (message_list->uid_nodemap, message_list->cursor_uid);
- while (!e_tree_model_node_is_root (message_list->model, node)) {
- last = node;
- node = e_tree_model_node_get_parent (message_list->model, node);
- }
-
- /* get the next toplevel node */
- node = e_tree_model_node_get_next (message_list->model, last);
-
- if (node) {
- CamelMessageInfo *info;
-
- info = get_message_info (message_list, node);
- e_tree_set_cursor (message_list->tree, node);
-
- g_free (message_list->cursor_uid);
- message_list->cursor_uid = g_strdup (camel_message_info_uid (info));
-
- g_signal_emit (GTK_OBJECT (message_list), message_list_signals[MESSAGE_SELECTED], 0,
- camel_message_info_uid (info));
- }
-}
-
-
-/*
- * SimpleTableModel::col_count
- */
-static int
-ml_column_count (ETreeModel *etm, void *data)
-{
- return COL_LAST;
-}
-
-/*
- * SimpleTableModel::has_save_id
- */
-static gboolean
-ml_has_save_id (ETreeModel *etm, void *data)
-{
- return TRUE;
-}
-
-/*
- * SimpleTableModel::get_save_id
- */
-static char *
-ml_get_save_id (ETreeModel *etm, ETreePath path, void *data)
-{
- CamelMessageInfo *info;
-
- info = e_tree_memory_node_get_data (E_TREE_MEMORY(etm), path);
- if (info == NULL)
- return g_strdup("root");
- return g_strdup (camel_message_info_uid(info));
-}
-
-/*
- * SimpleTableModel::has_save_id
- */
-static gboolean
-ml_has_get_node_by_id (ETreeModel *etm, void *data)
-{
- return TRUE;
-}
-
-/*
- * SimpleTableModel::get_save_id
- */
-static ETreePath
-ml_get_node_by_id (ETreeModel *etm, const char *save_id, void *data)
-{
- MessageList *ml;
-
- ml = data;
-
- if (!strcmp (save_id, "root"))
- return e_tree_model_get_root (etm);
-
- return g_hash_table_lookup(ml->uid_nodemap, save_id);
-}
-
-static void *
-ml_duplicate_value (ETreeModel *etm, int col, const void *value, void *data)
-{
- switch (col){
- case COL_MESSAGE_STATUS:
- case COL_FLAGGED:
- case COL_SCORE:
- case COL_ATTACHMENT:
- case COL_DELETED:
- case COL_UNREAD:
- case COL_SENT:
- case COL_RECEIVED:
- case COL_SIZE:
- case COL_FOLLOWUP_FLAG_STATUS:
- case COL_FOLLOWUP_DUE_BY:
- return (void *) value;
-
- case COL_FROM:
- case COL_SUBJECT:
- case COL_TO:
- case COL_FOLLOWUP_FLAG:
- case COL_LOCATION:
- return g_strdup (value);
-
- default:
- g_assert_not_reached ();
- }
- return NULL;
-}
-
-static void
-ml_free_value (ETreeModel *etm, int col, void *value, void *data)
-{
- switch (col){
- case COL_MESSAGE_STATUS:
- case COL_FLAGGED:
- case COL_SCORE:
- case COL_ATTACHMENT:
- case COL_DELETED:
- case COL_UNREAD:
- case COL_SENT:
- case COL_RECEIVED:
- case COL_SIZE:
- case COL_FOLLOWUP_FLAG_STATUS:
- case COL_FOLLOWUP_DUE_BY:
- break;
-
- case COL_FROM:
- case COL_SUBJECT:
- case COL_TO:
- case COL_FOLLOWUP_FLAG:
- case COL_LOCATION:
- g_free (value);
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-static void *
-ml_initialize_value (ETreeModel *etm, int col, void *data)
-{
- switch (col){
- case COL_MESSAGE_STATUS:
- case COL_FLAGGED:
- case COL_SCORE:
- case COL_ATTACHMENT:
- case COL_DELETED:
- case COL_UNREAD:
- case COL_SENT:
- case COL_RECEIVED:
- case COL_SIZE:
- case COL_FOLLOWUP_FLAG_STATUS:
- case COL_FOLLOWUP_DUE_BY:
- return NULL;
-
- case COL_FROM:
- case COL_SUBJECT:
- case COL_TO:
- case COL_FOLLOWUP_FLAG:
- case COL_LOCATION:
- return g_strdup ("");
- default:
- g_assert_not_reached ();
- }
-
- return NULL;
-}
-
-static gboolean
-ml_value_is_empty (ETreeModel *etm, int col, const void *value, void *data)
-{
- switch (col){
- case COL_MESSAGE_STATUS:
- case COL_FLAGGED:
- case COL_SCORE:
- case COL_ATTACHMENT:
- case COL_DELETED:
- case COL_UNREAD:
- case COL_SENT:
- case COL_RECEIVED:
- case COL_SIZE:
- case COL_FOLLOWUP_FLAG_STATUS:
- case COL_FOLLOWUP_DUE_BY:
- return value == NULL;
-
- case COL_FROM:
- case COL_SUBJECT:
- case COL_TO:
- case COL_FOLLOWUP_FLAG:
- case COL_LOCATION:
- return !(value && *(char *)value);
- default:
- g_assert_not_reached ();
- return FALSE;
- }
-}
-
-static const char *status_map[] = {
- N_("Unseen"),
- N_("Seen"),
- N_("Answered"),
- N_("Multiple Unseen Messages"),
- N_("Multiple Messages"),
-};
-
-static const char *score_map[] = {
- N_("Lowest"),
- N_("Lower"),
- N_("Low"),
- N_("Normal"),
- N_("High"),
- N_("Higher"),
- N_("Highest"),
-};
-
-
-static char *
-ml_value_to_string (ETreeModel *etm, int col, const void *value, void *data)
-{
- unsigned int i;
-
- switch (col){
- case COL_MESSAGE_STATUS:
- i = GPOINTER_TO_UINT(value);
- if (i > 4)
- return g_strdup ("");
- return g_strdup (_(status_map[i]));
-
- case COL_SCORE:
- i = GPOINTER_TO_UINT(value) + 3;
- if (i > 6)
- i = 3;
- return g_strdup (_(score_map[i]));
-
- case COL_ATTACHMENT:
- case COL_FLAGGED:
- case COL_DELETED:
- case COL_UNREAD:
- case COL_FOLLOWUP_FLAG_STATUS:
- return g_strdup_printf ("%d", GPOINTER_TO_UINT(value));
-
- case COL_SENT:
- case COL_RECEIVED:
- case COL_FOLLOWUP_DUE_BY:
- return filter_date (GPOINTER_TO_INT (value));
-
- case COL_SIZE:
- return filter_size (GPOINTER_TO_INT (value));
-
- case COL_FROM:
- case COL_SUBJECT:
- case COL_TO:
- case COL_FOLLOWUP_FLAG:
- case COL_LOCATION:
- return g_strdup (value);
- default:
- g_assert_not_reached ();
- return NULL;
- }
-}
-
-static GdkPixbuf *
-ml_tree_icon_at (ETreeModel *etm, ETreePath path, void *model_data)
-{
- /* we dont really need an icon ... */
- return NULL;
-}
-
-/* return true if there are any unread messages in the subtree */
-static int
-subtree_unread(MessageList *ml, ETreePath node)
-{
- CamelMessageInfo *info;
- ETreePath child;
-
- while (node) {
- info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
- g_assert(info);
-
- if (!(info->flags & 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;
-}
-
-static int
-subtree_size(MessageList *ml, ETreePath node)
-{
- CamelMessageInfo *info;
- int size = 0;
- ETreePath child;
-
- while (node) {
- info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
- g_assert(info);
-
- size += info->size;
- if ((child = e_tree_model_node_get_first_child (E_TREE_MODEL (ml->model), node)))
- size += subtree_size(ml, child);
-
- node = e_tree_model_node_get_next (ml->model, node);
- }
- return size;
-}
-
-static time_t
-subtree_earliest(MessageList *ml, ETreePath node, int sent)
-{
- CamelMessageInfo *info;
- time_t earliest = 0, date;
- ETreePath *child;
-
- while (node) {
- info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
- g_assert(info);
-
- if (sent)
- date = info->date_sent;
- else
- date = info->date_received;
-
- if (earliest == 0 || date < earliest)
- earliest = date;
-
- if ((child = e_tree_model_node_get_first_child (ml->model, node))) {
- date = subtree_earliest(ml, child, sent);
- if (earliest == 0 || (date != 0 && date < earliest))
- earliest = date;
- }
-
- node = e_tree_model_node_get_next (ml->model, node);
- }
-
- return earliest;
-}
-
-static void *
-ml_tree_value_at (ETreeModel *etm, ETreePath path, int col, void *model_data)
-{
- MessageList *message_list = model_data;
- CamelMessageInfo *msg_info;
- const char *str;
-
- 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_assert (msg_info != NULL);
-
- switch (col){
- case COL_MESSAGE_STATUS:
- if (msg_info->flags & CAMEL_MESSAGE_ANSWERED)
- return GINT_TO_POINTER (2);
- else if (msg_info->flags & CAMEL_MESSAGE_SEEN)
- return GINT_TO_POINTER (1);
- else
- return GINT_TO_POINTER (0);
- break;
- case COL_FLAGGED:
- return GINT_TO_POINTER ((msg_info->flags & CAMEL_MESSAGE_FLAGGED) != 0);
- case COL_SCORE: {
- const char *tag;
- int score = 0;
-
- tag = camel_tag_get ((CamelTag **) &msg_info->user_tags, "score");
- if (tag)
- score = atoi (tag);
-
- return GINT_TO_POINTER (score);
- }
- case COL_FOLLOWUP_FLAG_STATUS: {
- const char *tag;
-
- tag = camel_tag_get ((CamelTag **) &msg_info->user_tags, "follow-up");
- if (tag && *tag)
- return GINT_TO_POINTER (TRUE);
- else
- return GINT_TO_POINTER (FALSE);
- }
- case COL_FOLLOWUP_DUE_BY: {
- const char *tag;
- time_t due_by;
-
- tag = camel_tag_get ((CamelTag **) &msg_info->user_tags, "due-by");
- if (tag && *tag) {
- due_by = header_decode_date (tag, NULL);
- return GINT_TO_POINTER (due_by);
- } else {
- return GINT_TO_POINTER (0);
- }
- }
- case COL_FOLLOWUP_FLAG:
- str = camel_tag_get ((CamelTag **) &msg_info->user_tags, "follow-up");
- return (void *)(str ? str : "");
- case COL_ATTACHMENT:
- return GINT_TO_POINTER ((msg_info->flags & CAMEL_MESSAGE_ATTACHMENTS) != 0);
- case COL_FROM:
- str = camel_message_info_from (msg_info);
- return (void *)(str ? str : "");
- case COL_FROM_NORM:
- return (void *) get_normalised_string (message_list, msg_info, col);
- case COL_SUBJECT:
- str = camel_message_info_subject (msg_info);
- return (void *)(str ? str : "");
- case COL_SUBJECT_NORM:
- return (void *) get_normalised_string (message_list, msg_info, col);
- case COL_SENT:
- return GINT_TO_POINTER (msg_info->date_sent);
- case COL_RECEIVED:
- return GINT_TO_POINTER (msg_info->date_received);
- case COL_TO:
- str = camel_message_info_to (msg_info);
- return (void *)(str ? str : "");
- case COL_TO_NORM:
- return (void *) get_normalised_string (message_list, msg_info, col);
- case COL_SIZE:
- return GINT_TO_POINTER (msg_info->size);
- case COL_DELETED:
- return GINT_TO_POINTER ((msg_info->flags & CAMEL_MESSAGE_DELETED) != 0);
- case COL_UNREAD: {
- ETreePath child;
-
- child = e_tree_model_node_get_first_child(etm, path);
- if (child && !e_tree_node_is_expanded(message_list->tree, path)
- && (msg_info->flags & CAMEL_MESSAGE_SEEN)) {
- return GINT_TO_POINTER (subtree_unread (message_list, child));
- }
-
- return GINT_TO_POINTER (!(msg_info->flags & CAMEL_MESSAGE_SEEN));
- }
- case COL_COLOUR: {
- const char *colour, *due_by, *completed, *label;
-
- /* Priority: colour tag; label tag; important flag; due-by tag */
-
- colour = camel_tag_get ((CamelTag **) &msg_info->user_tags, "colour");
- due_by = camel_tag_get ((CamelTag **) &msg_info->user_tags, "due-by");
- completed = camel_tag_get ((CamelTag **) &msg_info->user_tags, "completed-on");
- label = camel_tag_get ((CamelTag **) &msg_info->user_tags, "label");
- if (colour == NULL) {
- find_colour:
- if (label != NULL) {
- colour = mail_config_get_label_color_by_name (label);
- if (colour == NULL) {
- /* dead label? */
- label = NULL;
- goto find_colour;
- }
- } else if (msg_info->flags & CAMEL_MESSAGE_FLAGGED) {
- /* FIXME: extract from the xpm somehow. */
- colour = "#A7453E";
- } else if ((due_by && *due_by) && !(completed && *completed)) {
- time_t now = time (NULL);
- time_t target_date;
-
- target_date = header_decode_date (due_by, NULL);
- if (now >= target_date) {
- /* FIXME: extract from the xpm somehow. */
- colour = "#A7453E";
- }
- }
- }
- return (void *) colour;
- }
- case COL_LOCATION: {
- CamelFolder *folder;
- char *name;
-
- if (CAMEL_IS_VEE_FOLDER(message_list->folder)) {
- folder = camel_vee_folder_get_location((CamelVeeFolder *)message_list->folder, (CamelVeeMessageInfo *)msg_info, NULL);
- } else {
- folder = message_list->folder;
- }
-
- camel_object_get(folder, NULL, CAMEL_OBJECT_DESCRIPTION, &name, 0);
- return name;
- }
- default:
- g_assert_not_reached ();
- return NULL;
- }
-}
-
-static void
-ml_tree_set_value_at (ETreeModel *etm, ETreePath path, int col,
- const void *val, void *model_data)
-{
- g_assert_not_reached ();
-}
-
-static gboolean
-ml_tree_is_cell_editable (ETreeModel *etm, ETreePath path, int col, void *model_data)
-{
- return FALSE;
-}
-
-static void
-message_list_init_images (void)
-{
- int i;
-
- /*
- * Only load once, and share
- */
- if (states_pixmaps [0].pixbuf)
- return;
-
- for (i = 0; states_pixmaps [i].image_base; i++){
- states_pixmaps [i].pixbuf = gdk_pixbuf_new_from_xpm_data (
- (const char **) states_pixmaps [i].image_base);
- }
-}
-
-static char *
-filter_date (time_t date)
-{
- time_t nowdate = time(NULL);
- time_t yesdate;
- struct tm then, now, yesterday;
- char buf[26];
- gboolean done = FALSE;
-
- if (date == 0)
- return g_strdup (_("?"));
-
- localtime_r (&date, &then);
- localtime_r (&nowdate, &now);
- if (then.tm_mday == now.tm_mday &&
- then.tm_mon == now.tm_mon &&
- then.tm_year == now.tm_year) {
- e_strftime_fix_am_pm (buf, 26, _("Today %l:%M %p"), &then);
- done = TRUE;
- }
- if (!done) {
- yesdate = nowdate - 60 * 60 * 24;
- localtime_r (&yesdate, &yesterday);
- if (then.tm_mday == yesterday.tm_mday &&
- then.tm_mon == yesterday.tm_mon &&
- then.tm_year == yesterday.tm_year) {
- e_strftime_fix_am_pm (buf, 26, _("Yesterday %l:%M %p"), &then);
- done = TRUE;
- }
- }
- if (!done) {
- int i;
- for (i = 2; i < 7; i++) {
- yesdate = nowdate - 60 * 60 * 24 * i;
- localtime_r (&yesdate, &yesterday);
- if (then.tm_mday == yesterday.tm_mday &&
- then.tm_mon == yesterday.tm_mon &&
- then.tm_year == yesterday.tm_year) {
- e_strftime_fix_am_pm (buf, 26, _("%a %l:%M %p"), &then);
- done = TRUE;
- break;
- }
- }
- }
- if (!done) {
- if (then.tm_year == now.tm_year) {
- e_strftime_fix_am_pm (buf, 26, _("%b %d %l:%M %p"), &then);
- } else {
- e_strftime_fix_am_pm (buf, 26, _("%b %d %Y"), &then);
- }
- }
-#if 0
-#ifdef CTIME_R_THREE_ARGS
- ctime_r (&date, buf, 26);
-#else
- ctime_r (&date, buf);
-#endif
-#endif
-
- return g_strdup (buf);
-}
-
-static ETableExtras *
-message_list_create_extras (void)
-{
- int i;
- GdkPixbuf *images [7];
- ETableExtras *extras;
- ECell *cell;
-
- extras = e_table_extras_new ();
- e_table_extras_add_pixbuf (extras, "status", states_pixmaps [0].pixbuf);
- e_table_extras_add_pixbuf (extras, "score", states_pixmaps [13].pixbuf);
- e_table_extras_add_pixbuf (extras, "attachment", states_pixmaps [6].pixbuf);
- e_table_extras_add_pixbuf (extras, "flagged", states_pixmaps [7].pixbuf);
- e_table_extras_add_pixbuf (extras, "followup", states_pixmaps [15].pixbuf);
-
- e_table_extras_add_compare (extras, "address_compare", address_compare);
-
- for (i = 0; i < 5; i++)
- images [i] = states_pixmaps [i].pixbuf;
-
- e_table_extras_add_cell (extras, "render_message_status", e_cell_toggle_new (0, 5, images));
-
- for (i = 0; i < 2; i++)
- images [i] = states_pixmaps [i + 5].pixbuf;
-
- e_table_extras_add_cell (extras, "render_attachment", e_cell_toggle_new (0, 2, images));
-
- images [1] = states_pixmaps [7].pixbuf;
- e_table_extras_add_cell (extras, "render_flagged", e_cell_toggle_new (0, 2, images));
-
- images[1] = states_pixmaps [15].pixbuf;
- e_table_extras_add_cell (extras, "render_flag_status", e_cell_toggle_new (0, 2, images));
-
- for (i = 0; i < 7; i++)
- images[i] = states_pixmaps [i + 7].pixbuf;
-
- e_table_extras_add_cell (extras, "render_score", e_cell_toggle_new (0, 7, images));
-
- /* date cell */
- cell = e_cell_date_new (NULL, GTK_JUSTIFY_LEFT);
- g_object_set (G_OBJECT (cell),
- "bold_column", COL_UNREAD,
- "color_column", COL_COLOUR,
- NULL);
- e_table_extras_add_cell (extras, "render_date", cell);
-
- /* text cell */
- cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
- g_object_set (G_OBJECT (cell),
- "bold_column", COL_UNREAD,
- "color_column", COL_COLOUR,
- NULL);
- e_table_extras_add_cell (extras, "render_text", cell);
-
- e_table_extras_add_cell (extras, "render_tree",
- e_cell_tree_new (NULL, NULL, /* let the tree renderer default the pixmaps */
- TRUE, cell));
-
- /* size cell */
- cell = e_cell_size_new (NULL, GTK_JUSTIFY_RIGHT);
- g_object_set (G_OBJECT (cell),
- "bold_column", COL_UNREAD,
- "color_column", COL_COLOUR,
- NULL);
- e_table_extras_add_cell (extras, "render_size", cell);
-
- return extras;
-}
-
-static void
-save_tree_state(MessageList *ml)
-{
- char *filename;
-
- if (ml->folder == NULL || ml->tree == NULL)
- return;
-
- filename = mail_config_folder_to_cachename(ml->folder, "et-expanded-");
- e_tree_save_expanded_state(ml->tree, filename);
- g_free(filename);
-}
-
-static void
-load_tree_state (MessageList *ml)
-{
- char *filename;
-
- if (ml->folder == NULL || ml->tree == NULL)
- return;
-
- filename = mail_config_folder_to_cachename (ml->folder, "et-expanded-");
- e_tree_load_expanded_state (ml->tree, filename);
- g_free (filename);
-}
-
-
-void
-message_list_save_state (MessageList *ml)
-{
- save_tree_state (ml);
- hide_save_state (ml);
-}
-
-static void
-message_list_setup_etree (MessageList *message_list, gboolean outgoing)
-{
- /* build the spec based on the folder, and possibly from a saved file */
- /* otherwise, leave default */
- if (message_list->folder) {
- char *path;
- char *name;
- struct stat st;
-
- g_object_set (message_list->tree,
- "uniform_row_height", TRUE,
- NULL);
-
- name = camel_service_get_name (CAMEL_SERVICE (message_list->folder->parent_store), TRUE);
- d(printf ("folder name is '%s'\n", name));
-
- path = mail_config_folder_to_cachename (message_list->folder, "et-expanded-");
- if (path && stat (path, &st) == 0 && st.st_size > 0 && S_ISREG (st.st_mode)) {
- /* build based on saved file */
- e_tree_load_expanded_state (message_list->tree, path);
- }
- g_free (path);
-
- g_free (name);
- }
-}
-
-/*
- * GtkObject::init
- */
-static void
-message_list_init (GtkObject *object)
-{
- MessageList *message_list = MESSAGE_LIST (object);
-
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (message_list), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-
- message_list->normalised_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
- message_list->hidden = NULL;
- message_list->hidden_pool = NULL;
- message_list->hide_before = ML_HIDE_NONE_START;
- message_list->hide_after = ML_HIDE_NONE_END;
-
- message_list->search = NULL;
-
- message_list->hide_lock = g_mutex_new();
-
- message_list->uid_nodemap = g_hash_table_new (g_str_hash, g_str_equal);
- message_list->async_event = mail_async_event_new();
-}
-
-static void
-normalised_free (gpointer key, gpointer value, gpointer user_data)
-{
- e_poolv_destroy (value);
-}
-
-static void
-message_list_destroy(GtkObject *object)
-{
- MessageList *message_list = MESSAGE_LIST (object);
-
- if (message_list->async_event) {
- mail_async_event_destroy(message_list->async_event);
- message_list->async_event = NULL;
- }
-
- if (message_list->folder) {
- /* need to do this before removing folder, folderinfo's might not exist after */
- if (message_list->uid_nodemap) {
- g_hash_table_foreach(message_list->uid_nodemap, (GHFunc)clear_info, message_list);
- g_hash_table_destroy (message_list->uid_nodemap);
- }
-
- save_tree_state(message_list);
- hide_save_state(message_list);
- camel_object_unhook_event(message_list->folder, "folder_changed", folder_changed, message_list);
- camel_object_unhook_event(message_list->folder, "message_changed", message_changed, message_list);
- camel_object_unref (message_list->folder);
- message_list->folder = NULL;
- }
-
- if (message_list->extras) {
- g_object_unref (message_list->extras);
- message_list->extras = NULL;
- }
-
- if (message_list->model) {
- g_object_unref (message_list->model);
- message_list->model = NULL;
- }
-
- if (message_list->idle_id != 0) {
- g_source_remove (message_list->idle_id);
- message_list->idle_id = 0;
- }
-
- if (message_list->seen_id) {
- g_source_remove (message_list->seen_id);
- message_list->seen_id = 0;
- }
-
- message_list->destroyed = TRUE;
-
- GTK_OBJECT_CLASS (message_list_parent_class)->destroy(object);
-}
-
-static void
-message_list_finalise (GObject *object)
-{
- MessageList *message_list = MESSAGE_LIST (object);
-
- g_hash_table_foreach (message_list->normalised_hash, normalised_free, NULL);
- g_hash_table_destroy (message_list->normalised_hash);
-
- if (message_list->thread_tree)
- camel_folder_thread_messages_unref(message_list->thread_tree);
-
- if (message_list->hidden) {
- g_hash_table_destroy(message_list->hidden);
- e_mempool_destroy(message_list->hidden_pool);
- message_list->hidden = NULL;
- message_list->hidden_pool = NULL;
- }
-
- g_free(message_list->cursor_uid);
-
- g_mutex_free(message_list->hide_lock);
-
- G_OBJECT_CLASS (message_list_parent_class)->finalize (object);
-}
-
-/*
- * GtkObjectClass::init
- */
-static void
-message_list_class_init (GObjectClass *object_class)
-{
- message_list_parent_class = g_type_class_ref(PARENT_TYPE);
-
- object_class->finalize = message_list_finalise;
- ((GtkObjectClass *)object_class)->destroy = message_list_destroy;
-
- message_list_signals[MESSAGE_SELECTED] =
- g_signal_new ("message_selected",
- MESSAGE_LIST_TYPE,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MessageListClass, message_selected),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- message_list_signals[MESSAGE_LIST_BUILT] =
- g_signal_new ("message_list_built",
- MESSAGE_LIST_TYPE,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MessageListClass, message_list_built),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- message_list_init_images ();
-}
-
-static void
-message_list_construct (MessageList *message_list)
-{
- gboolean construct_failed;
- message_list->model =
- e_tree_memory_callbacks_new (ml_tree_icon_at,
-
- ml_column_count,
-
- ml_has_save_id,
- ml_get_save_id,
-
- ml_has_get_node_by_id,
- ml_get_node_by_id,
-
- ml_tree_value_at,
- ml_tree_set_value_at,
- ml_tree_is_cell_editable,
-
- ml_duplicate_value,
- ml_free_value,
- ml_initialize_value,
- ml_value_is_empty,
- ml_value_to_string,
-
- message_list);
-
- e_tree_memory_set_expanded_default(E_TREE_MEMORY(message_list->model), TRUE);
-
- /*
- * The etree
- */
- message_list->extras = message_list_create_extras ();
- construct_failed = (e_tree_scrolled_construct_from_spec_file (E_TREE_SCROLLED (message_list),
- message_list->model,
- message_list->extras,
- EVOLUTION_ETSPECDIR "/message-list.etspec",
- NULL)
- == NULL);
-
- message_list->tree = e_tree_scrolled_get_tree(E_TREE_SCROLLED (message_list));
- if (!construct_failed)
- e_tree_root_node_set_visible (message_list->tree, FALSE);
-
- g_signal_connect((message_list->tree), "cursor_activated",
- G_CALLBACK (on_cursor_activated_cmd),
- message_list);
-
- g_signal_connect((message_list->tree), "click",
- G_CALLBACK (on_click), message_list);
-
- g_signal_connect((message_list->tree), "selection_change",
- G_CALLBACK (on_selection_changed_cmd), message_list);
-
-}
-
-GtkWidget *
-message_list_new (void)
-{
- MessageList *message_list;
-
- message_list = MESSAGE_LIST (g_object_new(message_list_get_type (),
- "hadjustment", NULL,
- "vadjustment", NULL,
- NULL));
- message_list_construct (message_list);
-
- return GTK_WIDGET (message_list);
-}
-
-static void
-clear_info(char *key, ETreePath *node, MessageList *ml)
-{
- CamelMessageInfo *info;
-
- info = e_tree_memory_node_get_data((ETreeMemory *)ml->model, node);
- camel_folder_free_message_info(ml->folder, info);
-}
-
-static void
-clear_tree (MessageList *ml)
-{
- ETreeModel *etm = ml->model;
-
-#ifdef TIMEIT
- struct timeval start, end;
- unsigned long diff;
-
- printf("Clearing tree\n");
- gettimeofday(&start, NULL);
-#endif
-
- /* we also reset the uid_rowmap since it is no longer useful/valid anyway */
- if (ml->folder)
- g_hash_table_foreach (ml->uid_nodemap, (GHFunc)clear_info, ml);
- g_hash_table_destroy (ml->uid_nodemap);
- ml->uid_nodemap = g_hash_table_new (g_str_hash, g_str_equal);
-
- if (ml->tree_root) {
- /* we should be frozen already */
- e_tree_memory_node_remove (E_TREE_MEMORY(etm), ml->tree_root);
- }
-
- ml->tree_root = e_tree_memory_node_insert (E_TREE_MEMORY(etm), NULL, 0, NULL);
-
-#ifdef TIMEIT
- gettimeofday(&end, NULL);
- diff = end.tv_sec * 1000 + end.tv_usec/1000;
- diff -= start.tv_sec * 1000 + start.tv_usec/1000;
- printf("Clearing tree took %ld.%03ld seconds\n", diff / 1000, diff % 1000);
-#endif
-
-}
-
-/* we try and find something that isn't deleted in our tree
- there is actually no assurance that we'll find somethign that will
- still be there next time, but its probably going to work most of the time */
-static char *
-find_next_undeleted (MessageList *ml)
-{
- ETreePath node;
- int last;
- int vrow;
- ETree *et = ml->tree;
- CamelMessageInfo *info;
-
- node = g_hash_table_lookup (ml->uid_nodemap, ml->cursor_uid);
- if (node == NULL)
- return NULL;
-
- info = get_message_info (ml, node);
- if (info && (info->flags & CAMEL_MESSAGE_DELETED) == 0) {
- return NULL;
- }
-
- last = e_tree_row_count (ml->tree);
-
- /* model_to_view_row etc simply dont work for sorted views. Sigh. */
- vrow = e_tree_row_of_node (et, node);
-
- /* We already checked this node. */
- vrow ++;
-
- while (vrow < last) {
- CamelMessageInfo *info;
-
- node = e_tree_node_at_row (et, vrow);
- info = get_message_info (ml, node);
- if (info && (info->flags & CAMEL_MESSAGE_DELETED) == 0) {
- return g_strdup (camel_message_info_uid (info));
- }
- vrow ++;
- }
-
- return NULL;
-}
-
-/* only call if we have a tree model */
-/* builds the tree structure */
-static void build_subtree (MessageList *ml, ETreePath parent, CamelFolderThreadNode *c, int *row);
-
-static void build_subtree_diff (MessageList *ml, ETreePath parent, ETreePath path, CamelFolderThreadNode *c, int *row);
-
-static void
-build_tree (MessageList *ml, CamelFolderThread *thread, CamelFolderChangeInfo *changes)
-{
- int row = 0;
- ETreeModel *etm = ml->model;
- ETreePath *top;
- char *saveuid = NULL;
-
-#ifdef TIMEIT
- struct timeval start, end;
- unsigned long diff;
-
- printf("Building tree\n");
- gettimeofday(&start, NULL);
-#endif
-
-#ifdef TIMEIT
- gettimeofday(&end, NULL);
- diff = end.tv_sec * 1000 + end.tv_usec/1000;
- diff -= start.tv_sec * 1000 + start.tv_usec/1000;
- printf("Loading tree state took %ld.%03ld seconds\n", diff / 1000, diff % 1000);
-#endif
-
- if (ml->tree_root == NULL) {
- ml->tree_root = e_tree_memory_node_insert(E_TREE_MEMORY(etm), NULL, 0, NULL);
- }
-
- if (ml->cursor_uid) {
- if (ml->hidedeleted) {
- saveuid = find_next_undeleted(ml);
- }
- }
-
-#define BROKEN_ETREE /* avoid some broken code in etree(?) by not using the incremental update */
-
- top = e_tree_model_node_get_first_child(etm, ml->tree_root);
-#ifndef BROKEN_ETREE
- if (top == NULL || changes == NULL) {
-#endif
- e_tree_memory_freeze(E_TREE_MEMORY(etm));
- clear_tree (ml);
-
- build_subtree(ml, ml->tree_root, thread->tree, &row);
-
- e_tree_memory_thaw(E_TREE_MEMORY(etm));
-#ifndef BROKEN_ETREE
- } else {
- static int tree_equal(ETreeModel *etm, ETreePath ap, CamelFolderThreadNode *bp);
-
- build_subtree_diff(ml, ml->tree_root, top, thread->tree, &row);
- top = e_tree_model_node_get_first_child(etm, ml->tree_root);
- tree_equal(ml->model, top, thread->tree);
- }
-#endif
-
- if (saveuid) {
- ETreePath *node = g_hash_table_lookup (ml->uid_nodemap, saveuid);
- if (node == NULL) {
- g_free (ml->cursor_uid);
- ml->cursor_uid = NULL;
- g_signal_emit (ml, message_list_signals[MESSAGE_SELECTED], 0, NULL);
- } else {
- e_tree_set_cursor (ml->tree, node);
- }
- g_free (saveuid);
- } else if (ml->cursor_uid && !g_hash_table_lookup (ml->uid_nodemap, ml->cursor_uid)) {
- g_free (ml->cursor_uid);
- ml->cursor_uid = NULL;
- g_signal_emit (ml, message_list_signals[MESSAGE_SELECTED], 0, NULL);
- }
-
-#ifdef TIMEIT
- gettimeofday(&end, NULL);
- diff = end.tv_sec * 1000 + end.tv_usec/1000;
- diff -= start.tv_sec * 1000 + start.tv_usec/1000;
- printf("Building tree took %ld.%03ld seconds\n", diff / 1000, diff % 1000);
-#endif
-}
-
-/* this is about 20% faster than build_subtree_diff,
- entirely because e_tree_model_node_insert(xx, -1 xx)
- is faster than inserting to the right row :( */
-/* Otherwise, this code would probably go as it does the same thing essentially */
-static void
-build_subtree (MessageList *ml, ETreePath parent, CamelFolderThreadNode *c, int *row)
-{
- ETreeModel *tree = ml->model;
- ETreePath node;
-
- while (c) {
- /* phantom nodes no longer allowed */
- g_assert(c->message);
-
- node = e_tree_memory_node_insert(E_TREE_MEMORY(tree), parent, -1, (void *)c->message);
- g_hash_table_insert(ml->uid_nodemap, (void *)camel_message_info_uid(c->message), node);
- camel_folder_ref_message_info(ml->folder, (CamelMessageInfo *)c->message);
-
- if (c->child) {
- build_subtree(ml, node, c->child, row);
- }
- c = c->next;
- }
-}
-
-/* compares a thread tree node with the etable tree node to see if they point to
- the same object */
-static int
-node_equal(ETreeModel *etm, ETreePath ap, CamelFolderThreadNode *bp)
-{
- CamelMessageInfo *info;
-
- info = e_tree_memory_node_get_data(E_TREE_MEMORY(etm), ap);
-
- if (bp->message && strcmp(camel_message_info_uid(info), camel_message_info_uid(bp->message))==0)
- return 1;
-
- return 0;
-}
-
-#ifndef BROKEN_ETREE
-/* debug function - compare the two trees to see if they are the same */
-static int
-tree_equal(ETreeModel *etm, ETreePath ap, CamelFolderThreadNode *bp)
-{
- CamelMessageInfo *info;
-
- while (ap && bp) {
- if (!node_equal(etm, ap, bp)) {
- g_warning("Nodes in tree differ");
- info = e_tree_memory_node_get_data(E_TREE_MEMORY(etm), ap);
- printf("table uid = %s\n", camel_message_info_uid(info));
- printf("camel uid = %s\n", camel_message_info_uid(bp->message));
- return FALSE;
- } else {
- if (!tree_equal(etm, e_tree_model_node_get_first_child(etm, ap), bp->child))
- return FALSE;
- }
- bp = bp->next;
- ap = e_tree_model_node_get_next(etm, ap);
- }
-
- if (ap || bp) {
- g_warning("Tree differs, out of nodes in one branch");
- if (ap) {
- info = e_tree_memory_node_get_data(E_TREE_MEMORY(etm), ap);
- if (info)
- printf("table uid = %s\n", camel_message_info_uid(info));
- else
- printf("info is empty?\n");
- }
- if (bp) {
- printf("camel uid = %s\n", camel_message_info_uid(bp->message));
- return FALSE;
- }
- return FALSE;
- }
- return TRUE;
-}
-#endif
-
-/* adds a single node, retains save state, and handles adding children if required */
-static void
-add_node_diff(MessageList *ml, ETreePath parent, ETreePath path, CamelFolderThreadNode *c, int *row, int myrow)
-{
- ETreeModel *etm = ml->model;
- ETreePath node;
-
- g_assert(c->message);
-
- /* we just update the hashtable key, umm, does this leak the info on the message node? */
- g_hash_table_remove(ml->uid_nodemap, camel_message_info_uid(c->message));
- node = e_tree_memory_node_insert(E_TREE_MEMORY(etm), parent, myrow, (void *)c->message);
- g_hash_table_insert(ml->uid_nodemap, (void *)camel_message_info_uid(c->message), node);
- camel_folder_ref_message_info(ml->folder, (CamelMessageInfo *)c->message);
- (*row)++;
-
- if (c->child) {
- build_subtree_diff(ml, node, NULL, c->child, row);
- }
-}
-
-/* removes node, children recursively and all associated data */
-static void
-remove_node_diff(MessageList *ml, ETreePath node, int depth)
-{
- ETreeModel *etm = ml->model;
- ETreePath cp, cn;
- CamelMessageInfo *info;
-
- t(printf("Removing node: %s\n", (char *)e_tree_memory_node_get_data(etm, node)));
-
- /* we depth-first remove all node data's ... */
- cp = e_tree_model_node_get_first_child(etm, node);
- while (cp) {
- cn = e_tree_model_node_get_next(etm, cp);
- remove_node_diff(ml, cp, depth+1);
- cp = cn;
- }
-
- /* and the rowid entry - if and only if it is referencing this node */
- info = e_tree_memory_node_get_data(E_TREE_MEMORY (etm), node);
-
- /* and only at the toplevel, remove the node (etree should optimise this remove somewhat) */
- if (depth == 0)
- e_tree_memory_node_remove(E_TREE_MEMORY(etm), node);
-
- g_assert(info);
- g_hash_table_remove(ml->uid_nodemap, camel_message_info_uid(info));
- camel_folder_free_message_info(ml->folder, info);
-}
-
-/* applies a new tree structure to an existing tree, but only by changing things
- that have changed */
-static void
-build_subtree_diff(MessageList *ml, ETreePath parent, ETreePath path, CamelFolderThreadNode *c, int *row)
-{
- ETreeModel *etm = ml->model;
- ETreePath ap, *ai, *at, *tmp;
- CamelFolderThreadNode *bp, *bi, *bt;
- int i, j, myrow = 0;
-
- ap = path;
- bp = c;
-
- while (ap || bp) {
- t(printf("Processing row: %d (subtree row %d)\n", *row, myrow));
- if (ap == NULL) {
- t(printf("out of old nodes\n"));
- /* ran out of old nodes - remaining nodes are added */
- add_node_diff(ml, parent, ap, bp, row, myrow);
- myrow++;
- bp = bp->next;
- } else if (bp == NULL) {
- t(printf("out of new nodes\n"));
- /* ran out of new nodes - remaining nodes are removed */
- tmp = e_tree_model_node_get_next(etm, ap);
- remove_node_diff(ml, ap, 0);
- ap = tmp;
- } else if (node_equal(etm, ap, bp)) {
- /*t(printf("nodes match, verify\n"));*/
- /* matching nodes, verify details/children */
-#if 0
- if (bp->message) {
- char *olduid;
- int oldrow;
- /* if this is a message row, check/update the row id map */
- if (g_hash_table_lookup_extended(ml->uid_rowmap, camel_message_info_uid(bp->message), (void *)&olduid, (void *)&oldrow)) {
- if (oldrow != (*row)) {
- g_hash_table_insert(ml->uid_rowmap, olduid, (void *)(*row));
- }
- } else {
- g_warning("Cannot find uid %s in table?", camel_message_info_uid(bp->message));
- /*g_assert_not_reached();*/
- }
- }
-#endif
- *row = (*row)+1;
- myrow++;
- tmp = e_tree_model_node_get_first_child(etm, ap);
- /* make child lists match (if either has one) */
- if (bp->child || tmp) {
- build_subtree_diff(ml, ap, tmp, bp->child, row);
- }
- ap = e_tree_model_node_get_next(etm, ap);
- bp = bp->next;
- } else {
- t(printf("searching for matches\n"));
- /* we have to scan each side for a match */
- bi = bp->next;
- ai = e_tree_model_node_get_next(etm, ap);
- for (i=1;bi!=NULL;i++,bi=bi->next) {
- if (node_equal(etm, ap, bi))
- break;
- }
- for (j=1;ai!=NULL;j++,ai=e_tree_model_node_get_next(etm, ai)) {
- if (node_equal(etm, ai, bp))
- break;
- }
- if (i<j) {
- /* smaller run of new nodes - must be nodes to add */
- if (bi) {
- bt = bp;
- while (bt != bi) {
- t(printf("adding new node 0\n"));
- add_node_diff(ml, parent, NULL, bt, row, myrow);
- myrow++;
- bt = bt->next;
- }
- bp = bi;
- } else {
- t(printf("adding new node 1\n"));
- /* no match in new nodes, add one, try next */
- add_node_diff(ml, parent, NULL, bp, row, myrow);
- myrow++;
- bp = bp->next;
- }
- } else {
- /* bigger run of old nodes - must be nodes to remove */
- if (ai) {
- at = ap;
- while (at != ai) {
- t(printf("removing old node 0\n"));
- tmp = e_tree_model_node_get_next(etm, at);
- remove_node_diff(ml, at, 0);
- at = tmp;
- }
- ap = ai;
- } else {
- t(printf("adding new node 2\n"));
- /* didn't find match in old nodes, must be new node? */
- add_node_diff(ml, parent, NULL, bp, row, myrow);
- myrow++;
- bp = bp->next;
-#if 0
- tmp = e_tree_model_node_get_next(etm, ap);
- remove_node_diff(etm, ap, 0);
- ap = tmp;
-#endif
- }
- }
- }
- }
-}
-
-#ifndef BROKEN_ETREE
-static void build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes);
-#endif
-
-static void
-build_flat (MessageList *ml, GPtrArray *summary, CamelFolderChangeInfo *changes)
-{
- ETreeModel *etm = ml->model;
- ETreePath node;
- char *saveuid = NULL;
- int i;
-
-#ifdef TIMEIT
- struct timeval start, end;
- unsigned long diff;
-
- printf("Building flat\n");
- gettimeofday(&start, NULL);
-#endif
-
- if (ml->cursor_uid) {
- if (ml->hidedeleted) {
- saveuid = find_next_undeleted(ml);
- }
- }
-
-#ifndef BROKEN_ETREE
- if (changes) {
- build_flat_diff(ml, changes);
- } else {
-#endif
- e_tree_memory_freeze(E_TREE_MEMORY(etm));
- clear_tree (ml);
- for (i = 0; i < summary->len; i++) {
- CamelMessageInfo *info = summary->pdata[i];
-
- node = e_tree_memory_node_insert(E_TREE_MEMORY(etm), ml->tree_root, -1, info);
- g_hash_table_insert(ml->uid_nodemap, (void *)camel_message_info_uid(info), node);
- camel_folder_ref_message_info(ml->folder, info);
- }
- e_tree_memory_thaw(E_TREE_MEMORY(etm));
-
-#ifndef BROKEN_ETREE
- }
-#endif
-
- if (saveuid) {
- ETreePath *node = g_hash_table_lookup(ml->uid_nodemap, saveuid);
- if (node == NULL) {
- g_free (ml->cursor_uid);
- ml->cursor_uid = NULL;
- g_signal_emit (ml, message_list_signals[MESSAGE_SELECTED], 0, NULL);
- } else {
- e_tree_set_cursor (ml->tree, node);
- }
- g_free (saveuid);
- }
-
-#ifdef TIMEIT
- gettimeofday(&end, NULL);
- diff = end.tv_sec * 1000 + end.tv_usec/1000;
- diff -= start.tv_sec * 1000 + start.tv_usec/1000;
- printf("Building flat took %ld.%03ld seconds\n", diff / 1000, diff % 1000);
-#endif
-
-}
-
-static void
-message_list_change_first_visible_parent (MessageList *ml, ETreePath node)
-{
- ETreePath first_visible = NULL;
-
- while (node && (node = e_tree_model_node_get_parent (ml->model, node))) {
- if (!e_tree_node_is_expanded (ml->tree, node))
- first_visible = node;
- }
-
- if (first_visible != NULL) {
- e_tree_model_pre_change (ml->model);
- e_tree_model_node_data_changed (ml->model, first_visible);
- }
-}
-
-#ifndef BROKEN_ETREE
-
-static void
-build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes)
-{
- int i;
- ETreePath node;
- CamelMessageInfo *info;
-
-#ifdef TIMEIT
- struct timeval start, end;
- unsigned long diff;
-
- gettimeofday(&start, NULL);
-#endif
-
- d(printf("updating changes to display\n"));
-
- /* remove individual nodes? */
- d(printf("Removing messages from view:\n"));
- for (i=0;i<changes->uid_removed->len;i++) {
- node = g_hash_table_lookup(ml->uid_nodemap, changes->uid_removed->pdata[i]);
- if (node) {
- info = e_tree_memory_node_get_data(E_TREE_MEMORY(ml->model), node);
- e_tree_memory_node_remove(E_TREE_MEMORY(ml->model), node);
- camel_folder_free_message_info(ml->folder, info);
- g_hash_table_remove(ml->uid_nodemap, changes->uid_removed->pdata[i]);
- }
- }
-
- /* add new nodes? - just append to the end */
- d(printf("Adding messages to view:\n"));
- for (i=0;i<changes->uid_added->len;i++) {
- info = camel_folder_get_message_info (ml->folder, changes->uid_added->pdata[i]);
- if (info) {
- d(printf(" %s\n", (char *)changes->uid_added->pdata[i]));
- node = e_tree_memory_node_insert (E_TREE_MEMORY (ml->model), ml->tree_root, -1, info);
- g_hash_table_insert (ml->uid_nodemap, (void *)camel_message_info_uid (info), node);
- }
- }
-
- /* and update changes too */
- d(printf("Changing messages to view:\n"));
- for (i = 0; i < changes->uid_changed->len; i++) {
- ETreePath *node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]);
- if (node) {
- e_tree_model_pre_change (ml->model);
- e_tree_model_node_data_changed (ml->model, node);
-
- message_list_change_first_visible_parent (ml, node)
- }
- }
-
-#ifdef TIMEIT
- gettimeofday(&end, NULL);
- diff = end.tv_sec * 1000 + end.tv_usec/1000;
- diff -= start.tv_sec * 1000 + start.tv_usec/1000;
- printf("Inserting changes took %ld.%03ld seconds\n", diff / 1000, diff % 1000);
-#endif
-
-}
-#endif /* ! BROKEN_ETREE */
-
-
-static void
-main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data)
-{
- MessageList *ml = MESSAGE_LIST (user_data);
- CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data, *newchanges;
- CamelMessageInfo *info;
- CamelFolder *folder = (CamelFolder *)o;
- int i;
-
- d(printf("folder changed event, changes = %p\n", changes));
- if (changes) {
- d(printf("changed = %d added = %d removed = %d\n",
- changes->uid_changed->len, changes->uid_added->len, changes->uid_removed->len));
-
- for (i = 0; i < changes->uid_removed->len; i++) {
- /* uncache the normalised strings for these uids */
- EPoolv *poolv;
-
- poolv = g_hash_table_lookup (ml->normalised_hash, changes->uid_removed->pdata[i]);
- if (poolv != NULL) {
- g_hash_table_remove (ml->normalised_hash, changes->uid_removed->pdata[i]);
- e_poolv_destroy (poolv);
- }
- }
-
- /* check if the hidden state has changed, if so modify accordingly, then regenerate */
- if (ml->hidedeleted) {
- newchanges = camel_folder_change_info_new ();
-
- for (i = 0; i < changes->uid_changed->len; i++) {
- ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]);
-
- info = camel_folder_get_message_info (folder, changes->uid_changed->pdata[i]);
- if (node != NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) != 0) {
- camel_folder_change_info_remove_uid (newchanges, changes->uid_changed->pdata[i]);
- } else if (node == NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) == 0) {
- camel_folder_change_info_add_uid (newchanges, changes->uid_changed->pdata[i]);
- } else {
- camel_folder_change_info_change_uid (newchanges, changes->uid_changed->pdata[i]);
- }
- camel_folder_free_message_info (folder, info);
- }
-
- if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) {
- for (i = 0; i < changes->uid_added->len; i++)
- camel_folder_change_info_add_uid (newchanges, changes->uid_added->pdata[i]);
- for (i = 0; i < changes->uid_removed->len; i++)
- camel_folder_change_info_remove_uid (newchanges, changes->uid_removed->pdata[i]);
- camel_folder_change_info_free (changes);
- changes = newchanges;
- } else {
- camel_folder_change_info_free (newchanges);
- }
- }
-
- if (changes->uid_added->len == 0 && changes->uid_removed->len == 0 && changes->uid_changed->len < 100) {
- for (i = 0; i < changes->uid_changed->len; i++) {
- ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]);
- if (node) {
- e_tree_model_pre_change (ml->model);
- e_tree_model_node_data_changed (ml->model, node);
-
- message_list_change_first_visible_parent (ml, node);
- }
- }
-
- camel_folder_change_info_free (changes);
- return;
- }
- }
-
- mail_regen_list (ml, ml->search, NULL, changes);
-}
-
-static void
-folder_changed (CamelObject *o, gpointer event_data, gpointer user_data)
-{
- CamelFolderChangeInfo *changes;
- MessageList *ml = MESSAGE_LIST (user_data);
-
- if (event_data) {
- changes = camel_folder_change_info_new();
- camel_folder_change_info_cat(changes, (CamelFolderChangeInfo *)event_data);
- } else {
- changes = NULL;
- }
-
- mail_async_event_emit(ml->async_event, MAIL_ASYNC_GUI, (MailAsyncFunc)main_folder_changed, o, changes, user_data);
-}
-
-static void
-message_changed (CamelObject *o, gpointer event_data, gpointer user_data)
-{
- CamelFolderChangeInfo *changes;
- MessageList *ml = MESSAGE_LIST (user_data);
-
- changes = camel_folder_change_info_new();
- camel_folder_change_info_change_uid(changes, (char *)event_data);
-
- mail_async_event_emit(ml->async_event, MAIL_ASYNC_GUI, (MailAsyncFunc)main_folder_changed, o, changes, user_data);
-}
-
-void
-message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, gboolean outgoing)
-{
- gboolean hide_deleted;
- GConfClient *gconf;
- CamelException ex;
-
- g_return_if_fail (message_list != NULL);
- g_return_if_fail (IS_MESSAGE_LIST (message_list));
-
- if (message_list->folder == camel_folder)
- return;
-
- camel_exception_init (&ex);
-
- /* cancel any outstanding regeneration requests */
- if (message_list->regen) {
- GList *l = message_list->regen;
-
- while (l) {
- struct _mail_msg *mm = l->data;
-
- if (mm->cancel)
- camel_operation_cancel(mm->cancel);
- l = l->next;
- }
- }
-
- clear_tree (message_list);
-
- if (message_list->folder) {
- hide_save_state(message_list);
- camel_object_unhook_event((CamelObject *)message_list->folder, "folder_changed",
- folder_changed, message_list);
- camel_object_unhook_event((CamelObject *)message_list->folder, "message_changed",
- message_changed, message_list);
- camel_object_unref (message_list->folder);
- }
-
- if (message_list->thread_tree) {
- camel_folder_thread_messages_unref(message_list->thread_tree);
- message_list->thread_tree = NULL;
- }
-
- message_list->folder = camel_folder;
-
- if (message_list->cursor_uid) {
- g_free(message_list->cursor_uid);
- message_list->cursor_uid = NULL;
- g_signal_emit(message_list, message_list_signals[MESSAGE_SELECTED], 0, NULL);
- }
-
- if (camel_folder) {
- /* Setup the strikeout effect for non-trash folders */
- if (!(camel_folder->folder_flags & CAMEL_FOLDER_IS_TRASH)) {
- ECell *cell;
-
- cell = e_table_extras_get_cell (message_list->extras, "render_date");
- g_object_set (cell,
- "strikeout_column", COL_DELETED,
- NULL);
-
- cell = e_table_extras_get_cell (message_list->extras, "render_text");
- g_object_set (cell,
- "strikeout_column", COL_DELETED,
- NULL);
-
- cell = e_table_extras_get_cell (message_list->extras, "render_size");
- g_object_set (cell,
- "strikeout_column", COL_DELETED,
- NULL);
- }
-
- /* Build the etree suitable for this folder */
- message_list_setup_etree (message_list, outgoing);
-
- camel_object_hook_event (camel_folder, "folder_changed",
- folder_changed, message_list);
- camel_object_hook_event (camel_folder, "message_changed",
- message_changed, message_list);
-
- camel_object_ref (camel_folder);
-
- gconf = mail_config_get_gconf_client ();
- hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL);
- message_list->hidedeleted = hide_deleted && !(camel_folder->folder_flags & CAMEL_FOLDER_IS_TRASH);
-
- hide_load_state (message_list);
- mail_regen_list (message_list, message_list->search, NULL, NULL);
- }
-}
-
-E_MAKE_TYPE (message_list, "MessageList", MessageList, message_list_class_init, message_list_init, PARENT_TYPE);
-
-static gboolean
-on_cursor_activated_idle (gpointer data)
-{
- MessageList *message_list = data;
- ESelectionModel *esm = e_tree_get_selection_model (message_list->tree);
- int selected = e_selection_model_selected_count (esm);
-
- if (selected == 1 && message_list->cursor_uid) {
- d(printf ("emitting cursor changed signal, for uid %s\n", message_list->cursor_uid));
- g_signal_emit (message_list, message_list_signals[MESSAGE_SELECTED], 0, message_list->cursor_uid);
- } else {
- g_signal_emit (message_list, message_list_signals[MESSAGE_SELECTED], 0, NULL);
- }
-
- message_list->idle_id = 0;
- return FALSE;
-}
-
-static void
-on_cursor_activated_cmd (ETree *tree, int row, ETreePath path, gpointer user_data)
-{
- MessageList *message_list = MESSAGE_LIST (user_data);
- const char *new_uid;
-
- if (path == NULL)
- new_uid = NULL;
- else
- new_uid = get_message_uid (message_list, path);
-
- if (message_list->cursor_uid != NULL && new_uid != NULL && !strcmp (message_list->cursor_uid, new_uid))
- return;
-
- message_list->cursor_row = row;
- g_free (message_list->cursor_uid);
- message_list->cursor_uid = g_strdup (new_uid);
-
- if (!message_list->idle_id) {
- message_list->idle_id =
- g_idle_add_full (G_PRIORITY_LOW, on_cursor_activated_idle,
- message_list, NULL);
- }
-}
-
-static void
-get_selected_cb(ETreePath path, MessageList *ml)
-{
- g_free(ml->cursor_uid);
- ml->cursor_uid = g_strdup(get_message_uid(ml, path));
-}
-
-static void
-on_selection_changed_cmd(ETree *tree, MessageList *ml)
-{
- ESelectionModel *esm = e_tree_get_selection_model (ml->tree);
- int selected = e_selection_model_selected_count (esm);
-
- g_free(ml->cursor_uid);
- ml->cursor_uid = NULL;
-
- if (selected == 1)
- e_tree_selected_path_foreach(ml->tree, (ETreeForeachFunc)get_selected_cb, ml);
-
- if ((selected == 1 || selected == 0) && !ml->idle_id)
- ml->idle_id = g_idle_add_full (G_PRIORITY_LOW, on_cursor_activated_idle, ml, NULL);
-}
-
-static gint
-on_click (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, MessageList *list)
-{
- CamelMessageInfo *info;
- int flag;
-
- if (col == COL_MESSAGE_STATUS)
- flag = CAMEL_MESSAGE_SEEN;
- else if (col == COL_FLAGGED)
- flag = CAMEL_MESSAGE_FLAGGED;
- else
- return FALSE;
-
- info = get_message_info (list, path);
- if (info == NULL) {
- return FALSE;
- }
-
- /* If a message was marked as deleted and the user flags it as
- important, marks it as needing a reply, marks it as unread,
- then undelete the message. */
- if (info->flags & CAMEL_MESSAGE_DELETED) {
- if (col == COL_FLAGGED && !(info->flags & CAMEL_MESSAGE_FLAGGED))
- flag |= CAMEL_MESSAGE_DELETED;
-
- if (col == COL_MESSAGE_STATUS && (info->flags & CAMEL_MESSAGE_SEEN))
- flag |= CAMEL_MESSAGE_DELETED;
- }
-
- camel_folder_set_message_flags (list->folder, camel_message_info_uid (info), flag, ~info->flags);
-
- if (flag == CAMEL_MESSAGE_SEEN && list->seen_id) {
- g_source_remove (list->seen_id);
- list->seen_id = 0;
- }
-
- return TRUE;
-}
-
-struct message_list_foreach_data {
- MessageList *message_list;
- MessageListForeachFunc callback;
- gpointer user_data;
-};
-
-static void
-mlfe_callback (ETreePath path, gpointer user_data)
-{
- struct message_list_foreach_data *mlfe_data = user_data;
- const char *uid;
-
- if (e_tree_model_node_is_root (mlfe_data->message_list->model, path))
- return;
-
- uid = get_message_uid (mlfe_data->message_list,
- path);
- if (uid) {
- mlfe_data->callback (mlfe_data->message_list, uid,
- mlfe_data->user_data);
- } else {
- /* FIXME: could this the cause of bug #6637 and friends? */
- g_warning ("I wonder if this could be the cause of bug #6637 and friends?");
- g_assert_not_reached ();
- }
-}
-
-void
-message_list_foreach (MessageList *message_list,
- MessageListForeachFunc callback,
- gpointer user_data)
-{
- struct message_list_foreach_data mlfe_data;
-
- mlfe_data.message_list = message_list;
- mlfe_data.callback = callback;
- mlfe_data.user_data = user_data;
- e_tree_selected_path_foreach (message_list->tree,
- mlfe_callback, &mlfe_data);
-}
-
-/* set whether we are in threaded view or flat view */
-void
-message_list_set_threaded (MessageList *ml, gboolean threaded)
-{
- if (ml->threaded != threaded) {
- ml->threaded = threaded;
-
- mail_regen_list (ml, ml->search, NULL, NULL);
- }
-}
-
-void
-message_list_set_hidedeleted (MessageList *ml, gboolean hidedeleted)
-{
- if (ml->hidedeleted != hidedeleted) {
- ml->hidedeleted = hidedeleted;
-
- mail_regen_list (ml, ml->search, NULL, NULL);
- }
-}
-
-void
-message_list_set_search (MessageList *ml, const char *search)
-{
- if (search == NULL || search[0] == '\0')
- if (ml->search == NULL || ml->search[0] == '\0')
- return;
-
- if (search != NULL && ml->search != NULL && strcmp (search, ml->search) == 0)
- return;
-
- if (ml->thread_tree) {
- camel_folder_thread_messages_unref(ml->thread_tree);
- ml->thread_tree = NULL;
- }
-
- mail_regen_list (ml, search, NULL, NULL);
-}
-
-/* returns the number of messages displayable *after* expression hiding has taken place */
-unsigned int
-message_list_length (MessageList *ml)
-{
- return ml->hide_unhidden;
-}
-
-struct _glibsuxcrap {
- unsigned int count;
- CamelFolder *folder;
-};
-
-static void
-glib_crapback(void *key, void *data, void *x)
-{
- struct _glibsuxcrap *y = x;
- CamelMessageInfo *mi;
-
- mi = camel_folder_get_message_info(y->folder, key);
- if (mi) {
- y->count++;
- camel_folder_free_message_info(y->folder, mi);
- }
-}
-
-/* returns number of hidden messages */
-unsigned int
-message_list_hidden(MessageList *ml)
-{
- unsigned int hidden = 0;
-
- MESSAGE_LIST_LOCK (ml, hide_lock);
- if (ml->hidden && ml->folder) {
- /* this is a hack, should probably just maintain the hidden table better */
- struct _glibsuxcrap x = { 0, ml->folder };
- g_hash_table_foreach(ml->hidden, glib_crapback, &x);
- hidden = x.count;
- }
- MESSAGE_LIST_UNLOCK (ml, hide_lock);
-
- return hidden;
-}
-
-
-/* add a new expression to hide, or set the range.
- @expr: A new search expression - all matching messages will be hidden. May be %NULL.
- @lower: Use ML_HIDE_NONE_START to specify no messages hidden from the start of the list.
- @upper: Use ML_HIDE_NONE_END to specify no message hidden from the end of the list.
-
- For either @upper or @lower, use ML_HIDE_SAME, to keep the previously set hide range.
- If either range is negative, then the range is taken from the end of the available list
- of messages, once other hiding has been performed. Use message_list_length() to find out
- how many messages are available for hiding.
-
- Example: hide_add(ml, NULL, -100, ML_HIDE_NONE_END) -> hide all but the last (most recent)
- 100 messages.
-*/
-void
-message_list_hide_add (MessageList *ml, const char *expr, unsigned int lower, unsigned int upper)
-{
- MESSAGE_LIST_LOCK (ml, hide_lock);
-
- if (lower != ML_HIDE_SAME)
- ml->hide_before = lower;
- if (upper != ML_HIDE_SAME)
- ml->hide_after = upper;
-
- MESSAGE_LIST_UNLOCK (ml, hide_lock);
-
- mail_regen_list (ml, ml->search, expr, NULL);
-}
-
-/* hide specific uid's */
-void
-message_list_hide_uids (MessageList *ml, GPtrArray *uids)
-{
- int i;
- char *uid;
-
- /* first see if we need to do any work, if so, then do it all at once */
- for (i = 0; i < uids->len; i++) {
- if (g_hash_table_lookup (ml->uid_nodemap, uids->pdata[i])) {
- MESSAGE_LIST_LOCK (ml, hide_lock);
- if (ml->hidden == NULL) {
- ml->hidden = g_hash_table_new (g_str_hash, g_str_equal);
- ml->hidden_pool = e_mempool_new (512, 256, E_MEMPOOL_ALIGN_BYTE);
- }
-
- uid = e_mempool_strdup (ml->hidden_pool, uids->pdata[i]);
- g_hash_table_insert (ml->hidden, uid, uid);
- for ( ; i < uids->len; i++) {
- if (g_hash_table_lookup (ml->uid_nodemap, uids->pdata[i])) {
- uid = e_mempool_strdup (ml->hidden_pool, uids->pdata[i]);
- g_hash_table_insert (ml->hidden, uid, uid);
- }
- }
- MESSAGE_LIST_UNLOCK (ml, hide_lock);
- mail_regen_list (ml, ml->search, NULL, NULL);
- break;
- }
- }
-}
-
-/* no longer hide any messages */
-void
-message_list_hide_clear (MessageList *ml)
-{
- MESSAGE_LIST_LOCK (ml, hide_lock);
- if (ml->hidden) {
- g_hash_table_destroy (ml->hidden);
- e_mempool_destroy (ml->hidden_pool);
- ml->hidden = NULL;
- ml->hidden_pool = NULL;
- }
- ml->hide_before = ML_HIDE_NONE_START;
- ml->hide_after = ML_HIDE_NONE_END;
- MESSAGE_LIST_UNLOCK (ml, hide_lock);
-
- if (ml->thread_tree) {
- camel_folder_thread_messages_unref(ml->thread_tree);
- ml->thread_tree = NULL;
- }
-
- mail_regen_list (ml, ml->search, NULL, NULL);
-}
-
-#define HIDE_STATE_VERSION (1)
-
-/* version 1 file is:
- uintf 1
- uintf hide_before
- uintf hide_after
- string* uids
-*/
-
-static void
-hide_load_state (MessageList *ml)
-{
- char *filename;
- FILE *in;
- guint32 version, lower, upper;
-
- filename = mail_config_folder_to_cachename(ml->folder, "hidestate-");
- in = fopen(filename, "r");
- if (in) {
- camel_file_util_decode_fixed_int32 (in, &version);
- if (version == HIDE_STATE_VERSION) {
- MESSAGE_LIST_LOCK(ml, hide_lock);
- if (ml->hidden == NULL) {
- ml->hidden = g_hash_table_new(g_str_hash, g_str_equal);
- ml->hidden_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
- }
- camel_file_util_decode_fixed_int32 (in, &lower);
- ml->hide_before = lower;
- camel_file_util_decode_fixed_int32 (in, &upper);
- ml->hide_after = upper;
- while (!feof(in)) {
- char *olduid, *uid;
-
- if (camel_file_util_decode_string (in, &olduid) != -1) {
- uid = e_mempool_strdup(ml->hidden_pool, olduid);
- g_free (olduid);
- g_hash_table_insert(ml->hidden, uid, uid);
- }
- }
- MESSAGE_LIST_UNLOCK(ml, hide_lock);
- }
- fclose(in);
- }
- g_free(filename);
-}
-
-static void
-hide_save_1 (char *uid, char *keydata, FILE *out)
-{
- camel_file_util_encode_string (out, uid);
-}
-
-/* save the hide state. Note that messages are hidden by uid, if the uid's change, then
- this will become invalid, but is easy to reset in the ui */
-static void
-hide_save_state (MessageList *ml)
-{
- char *filename;
- FILE *out;
-
- if (ml->folder == NULL)
- return;
-
- MESSAGE_LIST_LOCK(ml, hide_lock);
-
- filename = mail_config_folder_to_cachename(ml->folder, "hidestate-");
- if (ml->hidden == NULL && ml->hide_before == ML_HIDE_NONE_START && ml->hide_after == ML_HIDE_NONE_END) {
- unlink(filename);
- } else if ((out = fopen (filename, "w"))) {
- camel_file_util_encode_fixed_int32 (out, HIDE_STATE_VERSION);
- camel_file_util_encode_fixed_int32 (out, ml->hide_before);
- camel_file_util_encode_fixed_int32 (out, ml->hide_after);
- if (ml->hidden)
- g_hash_table_foreach(ml->hidden, (GHFunc)hide_save_1, out);
- fclose(out);
- }
- g_free (filename);
-
- MESSAGE_LIST_UNLOCK(ml, hide_lock);
-}
-
-/* ** REGENERATE MESSAGELIST ********************************************** */
-struct _regen_list_msg {
- struct _mail_msg msg;
-
- int complete;
-
- MessageList *ml;
- char *search;
- char *hideexpr;
- CamelFolderChangeInfo *changes;
- gboolean dotree; /* we are building a tree */
- gboolean hidedel; /* we want to/dont want to show deleted messages */
- gboolean thread_subject;
- CamelFolderThread *tree;
-
- CamelFolder *folder;
- GPtrArray *summary;
-};
-
-/*
- maintain copy of summary
-
- any new messages added
- any removed removed, etc.
-
- use vfolder to implement searches ???
-
- */
-
-static char *
-regen_list_describe (struct _mail_msg *mm, gint complete)
-{
- return g_strdup (_("Generating message list"));
-}
-
-static void
-regen_list_regen (struct _mail_msg *mm)
-{
- struct _regen_list_msg *m = (struct _regen_list_msg *)mm;
- GPtrArray *uids, *uidnew, *showuids, *searchuids = NULL;
- CamelMessageInfo *info;
- int i;
-
- /* if we have hidedeleted on, use a search to find it out, merge with existing search if set */
- if (!camel_folder_has_search_capability(m->folder)) {
- /* if we have no search capability, dont let search or hide deleted work */
- uids = camel_folder_get_uids(m->folder);
- } else if (m->hidedel) {
- char *expr;
-
- if (m->search) {
- expr = alloca(strlen(m->search) + 64);
- sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search);
- } else
- expr = "(match-all (not (system-flag \"deleted\")))";
- searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex);
- } else {
- if (m->search)
- searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex);
- else
- uids = camel_folder_get_uids (m->folder);
- }
-
- if (camel_exception_is_set (&mm->ex))
- return;
-
- /* perform hiding */
- if (m->hideexpr && camel_folder_has_search_capability(m->folder)) {
- uidnew = camel_folder_search_by_expression (m->ml->folder, m->hideexpr, &mm->ex);
- /* well, lets not abort just because this faileld ... */
- camel_exception_clear (&mm->ex);
-
- if (uidnew) {
- MESSAGE_LIST_LOCK(m->ml, hide_lock);
-
- if (m->ml->hidden == NULL) {
- m->ml->hidden = g_hash_table_new (g_str_hash, g_str_equal);
- m->ml->hidden_pool = e_mempool_new (512, 256, E_MEMPOOL_ALIGN_BYTE);
- }
-
- for (i = 0; i < uidnew->len; i++) {
- if (g_hash_table_lookup (m->ml->hidden, uidnew->pdata[i]) == 0) {
- char *uid = e_mempool_strdup (m->ml->hidden_pool, uidnew->pdata[i]);
- g_hash_table_insert (m->ml->hidden, uid, uid);
- }
- }
-
- MESSAGE_LIST_UNLOCK(m->ml, hide_lock);
-
- camel_folder_search_free (m->ml->folder, uidnew);
- }
- }
-
- MESSAGE_LIST_LOCK(m->ml, hide_lock);
-
- m->ml->hide_unhidden = uids->len;
-
- /* what semantics do we want from hide_before, hide_after?
- probably <0 means measure from the end of the list */
-
- /* perform uid hiding */
- if (m->ml->hidden || m->ml->hide_before != ML_HIDE_NONE_START || m->ml->hide_after != ML_HIDE_NONE_END) {
- int start, end;
- uidnew = g_ptr_array_new ();
-
- /* first, hide matches */
- if (m->ml->hidden) {
- for (i = 0; i < uids->len; i++) {
- if (g_hash_table_lookup (m->ml->hidden, uids->pdata[i]) == 0)
- g_ptr_array_add (uidnew, uids->pdata[i]);
- }
- }
-
- /* then calculate the subrange visible and chop it out */
- m->ml->hide_unhidden = uidnew->len;
-
- if (m->ml->hide_before != ML_HIDE_NONE_START || m->ml->hide_after != ML_HIDE_NONE_END) {
- GPtrArray *uid2 = g_ptr_array_new ();
-
- start = m->ml->hide_before;
- if (start < 0)
- start += m->ml->hide_unhidden;
- end = m->ml->hide_after;
- if (end < 0)
- end += m->ml->hide_unhidden;
-
- start = MAX(start, 0);
- end = MIN(end, uidnew->len);
- for (i = start; i < end; i++) {
- g_ptr_array_add (uid2, uidnew->pdata[i]);
- }
-
- g_ptr_array_free (uidnew, TRUE);
- uidnew = uid2;
- }
- showuids = uidnew;
- } else {
- uidnew = NULL;
- showuids = uids;
- }
-
- MESSAGE_LIST_UNLOCK(m->ml, hide_lock);
-
- if (!camel_operation_cancel_check(mm->cancel)) {
- /* update/build a new tree */
- if (m->dotree) {
- if (m->tree)
- camel_folder_thread_messages_apply (m->tree, showuids);
- else
- m->tree = camel_folder_thread_messages_new (m->folder, showuids, m->thread_subject);
- } else {
- m->summary = g_ptr_array_new ();
- for (i = 0; i < showuids->len; i++) {
- info = camel_folder_get_message_info (m->folder, showuids->pdata[i]);
- if (info)
- g_ptr_array_add(m->summary, info);
- }
- }
-
- m->complete = TRUE;
- }
-
- if (uidnew)
- g_ptr_array_free (uidnew, TRUE);
-
- if (searchuids)
- camel_folder_search_free (m->folder, searchuids);
- else
- camel_folder_free_uids (m->folder, uids);
-}
-
-static void
-regen_list_regened (struct _mail_msg *mm)
-{
- struct _regen_list_msg *m = (struct _regen_list_msg *)mm;
-
- if (m->ml->destroyed)
- return;
-
- if (!m->complete)
- return;
-
- if (camel_operation_cancel_check(mm->cancel))
- return;
-
- if (m->dotree) {
- save_tree_state (m->ml);
-
- build_tree (m->ml, m->tree, m->changes);
- if (m->ml->thread_tree)
- camel_folder_thread_messages_unref(m->ml->thread_tree);
- m->ml->thread_tree = m->tree;
- m->tree = NULL;
-
- load_tree_state (m->ml);
- } else
- build_flat (m->ml, m->summary, m->changes);
-
- g_signal_emit (m->ml, message_list_signals[MESSAGE_LIST_BUILT], 0);
-}
-
-static void
-regen_list_free (struct _mail_msg *mm)
-{
- struct _regen_list_msg *m = (struct _regen_list_msg *)mm;
- int i;
-
- if (m->summary) {
- for (i = 0; i < m->summary->len; i++)
- camel_folder_free_message_info (m->folder, m->summary->pdata[i]);
- g_ptr_array_free (m->summary, TRUE);
- }
-
- if (m->tree)
- camel_folder_thread_messages_unref (m->tree);
-
- if (m->ml->search && m->ml->search != m->search)
- g_free (m->ml->search);
- m->ml->search = m->search;
-
- g_free (m->hideexpr);
-
- camel_object_unref (m->folder);
-
- if (m->changes)
- camel_folder_change_info_free (m->changes);
-
- /* This should probably lock the list.
- However, since we have a received function, this will always be called in gui thread */
- m->ml->regen = g_list_remove(m->ml->regen, m);
-
- g_object_unref(m->ml);
-}
-
-static struct _mail_msg_op regen_list_op = {
- regen_list_describe,
- regen_list_regen,
- regen_list_regened,
- regen_list_free,
-};
-
-static void
-mail_regen_list (MessageList *ml, const char *search, const char *hideexpr, CamelFolderChangeInfo *changes)
-{
- struct _regen_list_msg *m;
- GConfClient *gconf;
-
- if (ml->folder == NULL)
- return;
-
- /* cancel any outstanding regeneration requests, we rebuild from scratch anyway */
- if (ml->regen) {
- GList *l = ml->regen;
-
- while (l) {
- struct _mail_msg *mm = l->data;
-
- if (mm->cancel)
- camel_operation_cancel(mm->cancel);
- l = l->next;
- }
- }
-
- gconf = mail_config_get_gconf_client ();
-
-#ifndef BROKEN_ETREE
- /* this can sometimes crash,so ... */
-
- /* see if we need to goto the child thread at all anyway */
- /* currently the only case is the flat view with updates and no search */
- if (hideexpr == NULL && search == NULL && changes != NULL && !ml->threaded) {
- build_flat_diff(ml, changes);
- camel_folder_change_info_free(changes);
- return;
- }
-#endif
-
- m = mail_msg_new (&regen_list_op, NULL, sizeof (*m));
- m->ml = ml;
- m->search = g_strdup (search);
- m->hideexpr = g_strdup (hideexpr);
- m->changes = changes;
- m->dotree = ml->threaded;
- m->hidedel = ml->hidedeleted;
- m->thread_subject = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/thread_subject", NULL);
- g_object_ref(ml);
- m->folder = ml->folder;
- camel_object_ref(m->folder);
-
- if ((!m->hidedel || !m->dotree) && ml->thread_tree) {
- camel_folder_thread_messages_unref(ml->thread_tree);
- ml->thread_tree = NULL;
- } else if (ml->thread_tree) {
- m->tree = ml->thread_tree;
- camel_folder_thread_messages_ref(m->tree);
- }
-
- ml->regen = g_list_prepend(ml->regen, m);
-
- e_thread_put (mail_thread_queued, (EMsg *)m);
-}