/* modest-tny-header-tree-view.c
- *
- * This file is part of modest.
- *
- * Copyright (C) 2006 Nokia Corporation
- *
- * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <glib/gi18n.h>
#include "modest-tny-header-tree-view.h"
+#include <tny-list-iface.h>
+#include <string.h>
/* 'private'/'protected' functions */
static void modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass);
static void selection_changed (GtkTreeSelection *sel, gpointer user_data);
static void column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data);
-
-/* list my signals */
+
+#define PIXMAP_PREFIX PREFIX "/share/modest/pixmaps/"
+
enum {
- /* MY_SIGNAL_1, */
MESSAGE_SELECTED_SIGNAL,
LAST_SIGNAL
};
+
+enum {
+ HEADER_ICON_READ = 1,
+ HEADER_ICON_UNREAD,
+ HEADER_ICON_ATTACH,
+ HEADER_ICON_NUM
+};
+
+
+enum {
+ SORT_COLUMN_FROM = 1,
+ SORT_COLUMN_TO,
+ SORT_COLUMN_SUBJECT,
+ SORT_COLUMN_ATTACH,
+ SORT_COLUMN_RECEIVED,
+ SORT_COLUMN_SENT,
+ SORT_COLUMN_MSGTYPE,
+ SORT_COLUMN_NUM
+};
+
+
typedef struct _ModestTnyHeaderTreeViewPrivate ModestTnyHeaderTreeViewPrivate;
struct _ModestTnyHeaderTreeViewPrivate {
TnyMsgFolderIface *tny_msg_folder;
- GtkTreeModel *header_tree_model;
+ TnyListIface *headers;
+
+ GdkPixbuf *icons[HEADER_ICON_NUM];
+ guint sort_columns[SORT_COLUMN_NUM];
};
#define MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
MODEST_TYPE_TNY_HEADER_TREE_VIEW, \
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
+
+
+
+
+static void
+msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter,
+ GdkPixbuf *icons[HEADER_ICON_NUM])
+{
+ TnyMsgHeaderFlags flags;
+ GdkPixbuf *pixbuf;
+
+ gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &flags, -1);
+
+ if (flags & TNY_MSG_HEADER_FLAG_SEEN)
+ pixbuf = icons[HEADER_ICON_READ];
+ else
+ pixbuf = icons[HEADER_ICON_UNREAD];
+
+ g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf,
+ NULL);
+}
+
+static void
+attach_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, GdkPixbuf *icons[HEADER_ICON_NUM])
+{
+ TnyMsgHeaderFlags flags;
+ GdkPixbuf *pixbuf;
+
+ gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &flags, -1);
+ if (flags & TNY_MSG_HEADER_FLAG_ATTACHMENTS)
+ pixbuf = icons[HEADER_ICON_ATTACH];
+ else
+ pixbuf = NULL;
+
+ g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
+}
+
+
+
+static void
+header_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
+{
+ GObject *rendobj;
+ TnyMsgHeaderFlags flags;
+
+ gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &flags, -1);
+ rendobj = G_OBJECT(renderer);
+
+ if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
+ g_object_set (rendobj, "weight", 800, NULL);
+ else
+ g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
+}
+
+
+
+static void
+sender_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
+{
+ GObject *rendobj;
+ TnyMsgHeaderFlags flags;
+ gchar *from;
+ gchar *address;
+
+ gtk_tree_model_get (tree_model, iter,
+ TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN, &from,
+ TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
+ -1);
+ rendobj = G_OBJECT(renderer);
+
+ /* simplistic --> remove <email@address> from display */
+ address = g_strstr_len (from, strlen(from), "<");
+ if (address) {
+ address[0]='\0';
+ g_object_set (rendobj, "text", from, NULL);
+ g_free (from);
+ }
+
+ if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
+ g_object_set (rendobj, "weight", 800, NULL);
+ else
+ g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
+}
+
+
+static void
+init_icons (GdkPixbuf *icons[HEADER_ICON_NUM])
+{
+ icons[HEADER_ICON_READ] =
+ gdk_pixbuf_new_from_file (PIXMAP_PREFIX "read.xpm",NULL);
+ icons[HEADER_ICON_UNREAD] =
+ gdk_pixbuf_new_from_file (PIXMAP_PREFIX "unread.xpm",NULL);
+ icons[HEADER_ICON_ATTACH] =
+ gdk_pixbuf_new_from_file (PIXMAP_PREFIX "clip.xpm",NULL);
+}
+
+
+
+static GtkTreeViewColumn*
+get_new_column (const gchar *name, GtkCellRenderer *renderer,
+ gboolean resizable, gint sort_col_id, gboolean show_as_text,
+ GtkTreeCellDataFunc cell_data_func, gpointer user_data)
+{
+ GtkTreeViewColumn *column;
+
+ column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
+ gtk_tree_view_column_set_resizable (column, resizable);
+
+ if (show_as_text)
+ gtk_tree_view_column_add_attribute (column, renderer, "text",
+ sort_col_id);
+ if (sort_col_id >= 0)
+ gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
+
+ gtk_tree_view_column_set_sort_indicator (column, FALSE);
+ gtk_tree_view_column_set_reorderable (column, TRUE);
+
+ if (cell_data_func)
+ gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
+ user_data, NULL);
+
+/* g_signal_connect (G_OBJECT (column), "clicked", */
+/* G_CALLBACK (column_clicked), obj); */
+
+ return column;
+}
+
+
+
+
+
static void
modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
{
GtkTreeViewColumn *column;
- GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
- ModestTnyHeaderTreeViewPrivate *priv;
+ GtkCellRenderer *renderer_msgtype,
+ *renderer_header,
+ *renderer_attach;
+ int i;
+ ModestTnyHeaderTreeViewPrivate *priv;
priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj);
- priv->tny_msg_folder = NULL;
- priv->header_tree_model = NULL;
+ init_icons (priv->icons);
- column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer,
- "text",
- TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
- NULL);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN);
- gtk_tree_view_column_set_sort_indicator (column, TRUE);
- gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
- g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
+ renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
+ renderer_attach = gtk_cell_renderer_pixbuf_new ();
+ renderer_header = gtk_cell_renderer_text_new ();
+
+ priv->tny_msg_folder = NULL;
+ priv->headers = NULL;
+
+ for (i = 0; i != SORT_COLUMN_NUM; ++i)
+ priv->sort_columns[i] = -1;
+ /* msgtype */
+ column = get_new_column (_("M"), renderer_msgtype, FALSE, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ FALSE, (GtkTreeCellDataFunc)msgtype_cell_data, priv->icons);
+ gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
+ priv->sort_columns[SORT_COLUMN_MSGTYPE] =
+ gtk_tree_view_column_get_sort_column_id (column);
+ g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
+
+ /* attachment */
+ column = get_new_column (_("A"), renderer_attach, FALSE, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ FALSE, (GtkTreeCellDataFunc)attach_cell_data, priv->icons);
+ gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
+ priv->sort_columns[SORT_COLUMN_ATTACH] =
+ gtk_tree_view_column_get_sort_column_id (column);
+ g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
+
+ /* received */
+ column = get_new_column (_("Received"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
+ TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
+ priv->sort_columns[SORT_COLUMN_RECEIVED] =
+ gtk_tree_view_column_get_sort_column_id (column);
+ g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
- column = gtk_tree_view_column_new_with_attributes(_("From"), renderer,
- "text",
- TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
- NULL);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN);
- gtk_tree_view_column_set_sort_indicator (column, TRUE);
+
+ /* from */
+ column = get_new_column (_("From"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
+ TRUE, (GtkTreeCellDataFunc)sender_cell_data, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
- g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
+ priv->sort_columns[SORT_COLUMN_FROM] =
+ gtk_tree_view_column_get_sort_column_id (column);
+ g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
- column = gtk_tree_view_column_new_with_attributes(_("Subject"), renderer,
- "text",
- TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
- NULL);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN);
- gtk_tree_view_column_set_sort_indicator (column, TRUE);
+ /* subject */
+ column = get_new_column (_("Subject"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
+ TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
- g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
+ priv->sort_columns[SORT_COLUMN_SUBJECT] =
+ gtk_tree_view_column_get_sort_column_id (column);
+ g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
+
+ /* all cols */
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(obj), TRUE);
gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
-
+
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj), TRUE); /* alternating row colors */
}
{
ModestTnyHeaderTreeView *self;
ModestTnyHeaderTreeViewPrivate *priv;
-
+ int i;
+
self = MODEST_TNY_HEADER_TREE_VIEW(obj);
priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
- if (priv->header_tree_model)
- g_object_unref (G_OBJECT(priv->header_tree_model));
-
- priv->header_tree_model = NULL;
+ if (priv->headers)
+ g_object_unref (G_OBJECT(priv->headers));
+
+ priv->headers = NULL;
priv->tny_msg_folder = NULL;
+
+ /* cleanup our icons */
+ for (i = 0; i != HEADER_ICON_NUM; ++i) {
+ if (priv->icons[i]) {
+ g_object_unref (G_OBJECT(priv->icons[i]));
+ priv->icons[i] = NULL;
+ }
+ }
}
GtkWidget*
g_object_unref (obj);
return NULL;
}
-
+
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
g_signal_connect (sel, "changed",
G_CALLBACK(selection_changed), self);
}
+static gint
+cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
+ gpointer user_data)
+{
+ gint col_id = GPOINTER_TO_INT (user_data);
+ gint val1, val2;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL(tree_model), -1);
+
+ switch (col_id) {
+
+ case SORT_COLUMN_RECEIVED:
+ gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
+ &val1,-1);
+ gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
+ &val2,-1);
+
+ g_message ("%d %d %d %d", col_id, val1, val2, val1 - val2);
+
+ return val1 - val2;
+
+ case SORT_COLUMN_SENT:
+ gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
+ &val1,-1);
+ gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
+ &val2,-1);
+ return val1 - val2;
+
+ case SORT_COLUMN_ATTACH:
+ gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &val1,-1);
+ gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &val2,-1);
+
+ return (val1 & TNY_MSG_HEADER_FLAG_ATTACHMENTS) - (val2 & TNY_MSG_HEADER_FLAG_ATTACHMENTS);
+
+
+ case SORT_COLUMN_MSGTYPE:
+ gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &val1,-1);
+ gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
+ &val2,-1);
+
+ return (val1 & TNY_MSG_HEADER_FLAG_SEEN) - (val2 & TNY_MSG_HEADER_FLAG_SEEN);
+
+ default:
+ g_message ("%p %p", iter1, iter2);
+ return &iter1 - &iter2;
+ }
+}
+
+
+
gboolean
modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
TnyMsgFolderIface *folder)
{
- GtkTreeModel *sortable;
+ int i;
+ GtkTreeModel *oldsortable, *sortable, *oldmodel;
ModestTnyHeaderTreeViewPrivate *priv;
-
+
g_return_val_if_fail (self, FALSE);
priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
-
- /* clean up old stuff */
- if (priv->header_tree_model)
- g_object_unref (G_OBJECT(priv->header_tree_model));
- priv->header_tree_model = NULL;
-
- priv->header_tree_model = GTK_TREE_MODEL (tny_msg_header_list_model_new());
+
if (folder) {
- tny_msg_header_list_model_set_folder (
- TNY_MSG_HEADER_LIST_MODEL(priv->header_tree_model),
- folder, TRUE); /* FIXME: refresh?*/
+ priv->headers = TNY_LIST_IFACE(tny_msg_header_list_model_new ());
+ tny_msg_folder_iface_get_headers (folder, priv->headers,
+ FALSE);
+ tny_msg_header_list_model_set_folder (TNY_MSG_HEADER_LIST_MODEL(priv->headers),
+ folder);
- sortable = gtk_tree_model_sort_new_with_model (priv->header_tree_model);
+ oldsortable = gtk_tree_view_get_model(GTK_TREE_VIEW (self));
+ if (oldsortable && GTK_IS_TREE_MODEL_SORT(oldsortable)) {
+ GtkTreeModel *oldmodel = gtk_tree_model_sort_get_model
+ (GTK_TREE_MODEL_SORT(oldsortable));
+ if (oldmodel)
+ g_object_unref (G_OBJECT(oldmodel));
+ g_object_unref (oldsortable);
+ }
+
+ sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
- } else {
- static GtkTreeModel *empty_model = NULL;
- if (!empty_model)
- empty_model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING));
-
- sortable = empty_model;
- }
-
- gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
+ /* set special sorting functions */
+#if 0 /* FIXME */
+ gtk_tree_model_sort_reset_default_sort_func (sortable);
+
+ for (i = 0; i != SORT_COLUMN_NUM; ++i) {
+ int col_id = priv->sort_columns[i];
+ if (col_id >= 0) {
+ g_message ("%d: %p: %p: %d", i, GTK_TREE_SORTABLE(sortable), sortable, col_id);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable), col_id,
+ (GtkTreeIterCompareFunc)cmp_rows,
+ GINT_TO_POINTER(col_id), NULL);
+ }
+ }
+#endif
+ gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
+ gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), TRUE);
- if (sortable)
- g_object_unref (G_OBJECT(sortable));
-
+ /* no need to unref sortable */
+
+ } else /* when there is no folder */
+ gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), FALSE);
+
return TRUE;
}
&header, -1);
if (header) {
+ TnyMsgHeaderFlags flags;
const TnyMsgIface *msg;
const TnyMsgFolderIface *folder;
else {
msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
header);
- if (!msg)
- g_message ("cannot find msg");
+ if (!msg) {
+ g_message ("cannot find msg");
+ /* FIXME: update display */
+ }
}
+
g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
- msg);
+ msg);
+
+ /* mark message as seen; _set_flags crashes, bug in tinymail? */
+ flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
+ //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
}
}
static void
-column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data)
+column_clicked (GtkTreeViewColumn *col, gpointer user_data)
{
- GtkTreeView *treeview = GTK_TREE_VIEW (user_data);
+ GtkTreeView *treeview;
gint id;
-
- id = gtk_tree_view_column_get_sort_column_id (treeviewcolumn);
+
+ treeview = GTK_TREE_VIEW (user_data);
+ id = gtk_tree_view_column_get_sort_column_id (col);
gtk_tree_view_set_search_column (treeview, id);
}