/* ** Copyright (C) 2008-2010 Dirk-Jan C. Binnema ** ** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include "mug-msg-list-view.h" #include "mu-query.h" #include "mu-str.h" /* include other impl specific header files */ /* 'private'/'protected' functions */ static void mug_msg_list_view_class_init(MugMsgListViewClass * klass); static void mug_msg_list_view_init(MugMsgListView * obj); static void mug_msg_list_view_finalize(GObject * obj); /* list my signals */ enum { MUG_MSG_SELECTED, MUG_ERROR_OCCURED, LAST_SIGNAL }; enum { MUG_COL_DATESTR, MUG_COL_MAILDIR, MUG_COL_FLAGSSTR, MUG_COL_FROM, MUG_COL_TO, MUG_COL_SUBJECT, MUG_COL_PATH, MUG_COL_PRIO, MUG_COL_FLAGS, MUG_COL_TIME, MUG_N_COLS }; typedef struct _MugMsgListViewPrivate MugMsgListViewPrivate; struct _MugMsgListViewPrivate { GtkListStore *_store; char *_xpath; char *_query; }; #define MUG_MSG_LIST_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MUG_TYPE_MSG_LIST_VIEW, \ MugMsgListViewPrivate)) /* globals */ static GtkTreeViewClass *parent_class = NULL; /* uncomment the following if you have defined any signals */ static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE(MugMsgListView, mug_msg_list_view, GTK_TYPE_TREE_VIEW); static void mug_msg_list_view_class_init(MugMsgListViewClass * klass) { GObjectClass *gobject_class; gobject_class = (GObjectClass *) klass; parent_class = g_type_class_peek_parent(klass); gobject_class->finalize = mug_msg_list_view_finalize; g_type_class_add_private(gobject_class, sizeof(MugMsgListViewPrivate)); signals[MUG_MSG_SELECTED] = g_signal_new("msg-selected", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(MugMsgListViewClass, msg_selected), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[MUG_ERROR_OCCURED] = g_signal_new("error-occured", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(MugMsgListViewClass, error_occured), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); } static void on_cursor_changed(GtkTreeView * tview, MugMsgListView * lst) { GtkTreeSelection *sel; GtkTreeIter iter; MugMsgListViewPrivate *priv; priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(tview); sel = gtk_tree_view_get_selection(tview); if (!sel) return; /* hmmm */ if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { char *path; gtk_tree_model_get(GTK_TREE_MODEL(priv->_store), &iter, MUG_COL_PATH, &path, -1); g_signal_emit(G_OBJECT(lst), signals[MUG_MSG_SELECTED], 0, path); g_free(path); } } static void treecell_func(GtkTreeViewColumn * tree_column, GtkCellRenderer * renderer, GtkTreeModel * tree_model, GtkTreeIter * iter, gpointer data) { MuMsgFlags flags; MuMsgPrio prio; gtk_tree_model_get(tree_model, iter, MUG_COL_FLAGS, &flags, MUG_COL_PRIO, &prio, -1); g_object_set(G_OBJECT(renderer), "weight", (flags & MU_MSG_FLAG_NEW) ? 800 : 400, "weight", (flags & MU_MSG_FLAG_SEEN) ? 400 : 800, "foreground", prio == MU_MSG_PRIO_HIGH ? "red" : NULL, NULL); } /* sortcolidx == -1 means 'sortcolidx = colidx' */ static void append_col(GtkTreeView * treeview, const char *label, int colidx, int sortcolidx, gint maxwidth) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; renderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); col = gtk_tree_view_column_new_with_attributes(label, renderer, "text", colidx, NULL); g_object_set(G_OBJECT(col), "resizable", TRUE, NULL); gtk_tree_view_column_set_sort_indicator(col, TRUE); if (sortcolidx == -1) sortcolidx = colidx; gtk_tree_view_column_set_sort_column_id(col, sortcolidx); if (maxwidth) { gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_fixed_width(col, maxwidth); gtk_tree_view_column_set_expand(col, FALSE); } else gtk_tree_view_column_set_expand(col, TRUE); gtk_tree_view_column_set_cell_data_func(col, renderer, (GtkTreeCellDataFunc) treecell_func, NULL, NULL); gtk_tree_view_append_column(treeview, col); } static void mug_msg_list_view_init(MugMsgListView * obj) { MugMsgListViewPrivate *priv; GtkTreeView *tview; priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(obj); priv->_xpath = priv->_query = NULL; priv->_store = gtk_list_store_new(MUG_N_COLS, G_TYPE_STRING, /* date */ G_TYPE_STRING, /* folder */ G_TYPE_STRING, /* flagstr */ G_TYPE_STRING, /* from */ G_TYPE_STRING, /* to */ G_TYPE_STRING, /* subject */ G_TYPE_STRING, /* path */ G_TYPE_UINT, /* prio */ G_TYPE_UINT, /* flags */ G_TYPE_INT); /* timeval */ tview = GTK_TREE_VIEW(obj); gtk_tree_view_set_model(tview, GTK_TREE_MODEL(priv->_store)); gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(obj), TRUE); gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(obj), GTK_TREE_VIEW_GRID_LINES_VERTICAL); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(obj), TRUE); append_col(tview, "Date", MUG_COL_DATESTR, MUG_COL_TIME, 80); append_col(tview, "Folder", MUG_COL_MAILDIR, -1, 60); append_col(tview, "F", MUG_COL_FLAGSSTR, -1, 25); append_col(tview, "From", MUG_COL_FROM, -1, 0); append_col(tview, "To", MUG_COL_TO, -1, 0); append_col(tview, "Subject", MUG_COL_SUBJECT, -1, 0); g_signal_connect(G_OBJECT(obj), "cursor-changed", G_CALLBACK(on_cursor_changed), obj); } static void mug_msg_list_view_finalize(GObject * obj) { MugMsgListViewPrivate *priv; priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(obj); if (priv->_store) g_object_unref(priv->_store); g_free(priv->_xpath); g_free(priv->_query); G_OBJECT_CLASS(parent_class)->finalize(obj); } void mug_msg_list_view_move_first(MugMsgListView * self) { GtkTreePath *path; g_return_if_fail(MUG_IS_MSG_LIST_VIEW(self)); path = gtk_tree_path_new_first(); gtk_tree_view_set_cursor(GTK_TREE_VIEW(self), path, NULL, FALSE); gtk_tree_path_free(path); } static gboolean msg_list_view_move(MugMsgListView * self, gboolean next) { GtkTreePath *path; gtk_tree_view_get_cursor(GTK_TREE_VIEW(self), &path, NULL); if (!path) return FALSE; if (next) gtk_tree_path_next(path); else gtk_tree_path_prev(path); gtk_tree_view_set_cursor(GTK_TREE_VIEW(self), path, NULL, FALSE); gtk_tree_path_free(path); return TRUE; } gboolean mug_msg_list_view_move_next(MugMsgListView * self) { g_return_val_if_fail(MUG_IS_MSG_LIST_VIEW(self), FALSE); return msg_list_view_move(self, TRUE); } gboolean mug_msg_list_view_move_prev(MugMsgListView * self) { g_return_val_if_fail(MUG_IS_MSG_LIST_VIEW(self), FALSE); return msg_list_view_move(self, FALSE); } GtkWidget *mug_msg_list_view_new(const char *xpath) { GtkWidget *w; MugMsgListViewPrivate *priv; g_return_val_if_fail(xpath, NULL); w = GTK_WIDGET(g_object_new(MUG_TYPE_MSG_LIST_VIEW, NULL)); priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(w); priv->_xpath = g_strdup(xpath); return w; } static gchar *empty_or_display_contact(const gchar * str) { if (!str || *str == '\0') return g_strdup("-"); else return mu_str_display_contact(str); } static MugError mu_result_to_mug_error(MuResult r) { switch (r) { case MU_ERROR_XAPIAN_DIR: return MUG_ERROR_XAPIAN_DIR; case MU_ERROR_XAPIAN_NOT_UPTODATE: return MUG_ERROR_XAPIAN_NOT_UPTODATE; case MU_ERROR_QUERY: return MUG_ERROR_QUERY; default: return MUG_ERROR_OTHER; } } static MuMsgIter *run_query(const char *xpath, const char *query, MugMsgListView * self) { GError *err; MuQuery *xapian; MuMsgIter *iter; err = NULL; xapian = mu_query_new(xpath, &err); if (!xapian) { g_warning("Error: %s", err->message); g_signal_emit(G_OBJECT(self), signals[MUG_ERROR_OCCURED], 0, mu_result_to_mug_error(err->code)); g_error_free(err); return NULL; } iter = mu_query_run(xapian, query, MU_MSG_FIELD_ID_DATE, TRUE, 0, &err); mu_query_destroy(xapian); if (!iter) { g_warning("Error: %s", err->message); g_signal_emit(G_OBJECT(self), signals[MUG_ERROR_OCCURED], 0, mu_result_to_mug_error(err->code)); g_error_free(err); return NULL; } return iter; } static void add_row(GtkListStore * store, MuMsgIter * iter) { GtkTreeIter treeiter; const gchar *datestr, *flagstr; gchar *from, *to; time_t timeval; timeval = mu_msg_iter_get_date(iter); datestr = timeval == 0 ? "-" : mu_str_display_date_s(timeval); from = empty_or_display_contact(mu_msg_iter_get_from(iter)); to = empty_or_display_contact(mu_msg_iter_get_to(iter)); flagstr = mu_msg_flags_str_s(mu_msg_iter_get_flags(iter)); gtk_list_store_append(store, &treeiter); gtk_list_store_set(store, &treeiter, MUG_COL_DATESTR, datestr, MUG_COL_MAILDIR, mu_msg_iter_get_maildir(iter), MUG_COL_FLAGSSTR, flagstr, MUG_COL_FROM, from, MUG_COL_TO, to, MUG_COL_SUBJECT, mu_msg_iter_get_subject(iter), MUG_COL_PATH, mu_msg_iter_get_path(iter), MUG_COL_PRIO, mu_msg_iter_get_prio(iter), MUG_COL_FLAGS, mu_msg_iter_get_flags(iter), MUG_COL_TIME, timeval, -1); g_free(from); g_free(to); } static int update_model(GtkListStore * store, const char *xpath, const char *query, MugMsgListView * self) { MuMsgIter *iter; int count; iter = run_query(xpath, query, self); if (!iter) { g_warning("error: running query failed\n"); return -1; } for (count = 0; !mu_msg_iter_is_done(iter); mu_msg_iter_next(iter), ++count) add_row(store, iter); mu_msg_iter_destroy(iter); return count; } int mug_msg_list_view_query(MugMsgListView * self, const char *query) { MugMsgListViewPrivate *priv; gboolean rv; g_return_val_if_fail(MUG_IS_MSG_LIST_VIEW(self), FALSE); priv = MUG_MSG_LIST_VIEW_GET_PRIVATE(self); gtk_list_store_clear(priv->_store); g_free(priv->_query); priv->_query = query ? g_strdup(query) : NULL; if (!query) return TRUE; rv = update_model(priv->_store, priv->_xpath, query, self); gtk_tree_view_columns_autosize(GTK_TREE_VIEW(self)); return rv; } const gchar *mug_msg_list_view_get_query(MugMsgListView * self) { g_return_val_if_fail(MUG_IS_MSG_LIST_VIEW(self), NULL); return MUG_MSG_LIST_VIEW_GET_PRIVATE(self)->_query; }