1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
33 #include <tny-account-tree-model.h>
34 #include <tny-account-store-iface.h>
35 #include <tny-account-iface.h>
36 #include <tny-msg-folder-iface.h>
37 #include <tny-summary-window-iface.h>
38 #include <modest-icon-names.h>
39 #include "modest-icon-factory.h"
41 #include "modest-folder-view.h"
44 /* 'private'/'protected' functions */
45 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
46 static void modest_folder_view_init (ModestFolderView *obj);
47 static void modest_folder_view_finalize (GObject *obj);
49 static gboolean update_model (ModestFolderView *self,TnyAccountStoreIface *iface);
50 static gboolean update_model_empty (ModestFolderView *self);
52 static void selection_changed (GtkTreeSelection *sel, gpointer data);
55 FOLDER_SELECTED_SIGNAL,
59 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
60 struct _ModestFolderViewPrivate {
62 TnyAccountStoreIface *tny_account_store;
63 TnyMsgFolderIface *cur_folder;
64 gboolean view_is_empty;
68 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
69 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
70 MODEST_TYPE_FOLDER_VIEW, \
71 ModestFolderViewPrivate))
73 static GObjectClass *parent_class = NULL;
75 static guint signals[LAST_SIGNAL] = {0};
78 modest_folder_view_get_type (void)
80 static GType my_type = 0;
82 static const GTypeInfo my_info = {
83 sizeof(ModestFolderViewClass),
85 NULL, /* base finalize */
86 (GClassInitFunc) modest_folder_view_class_init,
87 NULL, /* class finalize */
88 NULL, /* class data */
89 sizeof(ModestFolderView),
91 (GInstanceInitFunc) modest_folder_view_init,
94 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
102 modest_folder_view_class_init (ModestFolderViewClass *klass)
104 GObjectClass *gobject_class;
105 gobject_class = (GObjectClass*) klass;
107 parent_class = g_type_class_peek_parent (klass);
108 gobject_class->finalize = modest_folder_view_finalize;
110 klass->update_model = modest_folder_view_update_model;
112 g_type_class_add_private (gobject_class,
113 sizeof(ModestFolderViewPrivate));
115 signals[FOLDER_SELECTED_SIGNAL] =
116 g_signal_new ("folder_selected",
117 G_TYPE_FROM_CLASS (gobject_class),
119 G_STRUCT_OFFSET (ModestFolderViewClass,
122 g_cclosure_marshal_VOID__POINTER,
123 G_TYPE_NONE, 1, G_TYPE_POINTER);
129 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
130 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
135 TnyMsgFolderType type;
137 gtk_tree_model_get (tree_model, iter,
138 TNY_ACCOUNT_TREE_MODEL_NAME_COLUMN, &fname,
139 TNY_ACCOUNT_TREE_MODEL_TYPE_COLUMN, &type,
140 TNY_ACCOUNT_TREE_MODEL_UNREAD_COLUMN, &unread, -1);
141 rendobj = G_OBJECT(renderer);
144 gchar *folder_title = g_strdup_printf ("%s (%d)", fname, unread);
145 g_object_set (rendobj,"text", folder_title, "weight", 800, NULL);
146 g_free (folder_title);
148 g_object_set (rendobj,"text", fname, "weight", 400, NULL);
153 /* FIXME: move these to TnyMail */
156 TNY_MSG_FOLDER_TYPE_NOTES = TNY_MSG_FOLDER_TYPE_SENT + 1, /* urgh */
157 TNY_MSG_FOLDER_TYPE_DRAFTS,
158 TNY_MSG_FOLDER_TYPE_CONTACTS,
159 TNY_MSG_FOLDER_TYPE_CALENDAR
162 static TnyMsgFolderType
163 guess_folder_type (const gchar* name)
165 TnyMsgFolderType type;
168 g_return_val_if_fail (name, TNY_MSG_FOLDER_TYPE_NORMAL);
170 type = TNY_MSG_FOLDER_TYPE_NORMAL;
171 folder = g_utf8_strdown (name, strlen(name));
173 if (strcmp (folder, "inbox") == 0 ||
174 strcmp (folder, _("inbox")) == 0)
175 type = TNY_MSG_FOLDER_TYPE_INBOX;
176 else if (strcmp (folder, "outbox") == 0 ||
177 strcmp (folder, _("outbox")) == 0)
178 type = TNY_MSG_FOLDER_TYPE_OUTBOX;
179 else if (g_str_has_prefix(folder, "junk") ||
180 g_str_has_prefix(folder, _("junk")))
181 type = TNY_MSG_FOLDER_TYPE_JUNK;
182 else if (g_str_has_prefix(folder, "trash") ||
183 g_str_has_prefix(folder, _("trash")))
184 type = TNY_MSG_FOLDER_TYPE_JUNK;
185 else if (g_str_has_prefix(folder, "sent") ||
186 g_str_has_prefix(folder, _("sent")))
187 type = TNY_MSG_FOLDER_TYPE_SENT;
189 /* these are not *really* TNY_ types */
190 else if (g_str_has_prefix(folder, "draft") ||
191 g_str_has_prefix(folder, _("draft")))
192 type = TNY_MSG_FOLDER_TYPE_DRAFTS;
193 else if (g_str_has_prefix(folder, "notes") ||
194 g_str_has_prefix(folder, _("notes")))
195 type = TNY_MSG_FOLDER_TYPE_NOTES;
196 else if (g_str_has_prefix(folder, "contacts") ||
197 g_str_has_prefix(folder, _("contacts")))
198 type = TNY_MSG_FOLDER_TYPE_CONTACTS;
199 else if (g_str_has_prefix(folder, "calendar") ||
200 g_str_has_prefix(folder, _("calendar")))
201 type = TNY_MSG_FOLDER_TYPE_CALENDAR;
209 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
210 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
214 TnyMsgFolderType type;
218 rendobj = G_OBJECT(renderer);
219 gtk_tree_model_get (tree_model, iter,
220 TNY_ACCOUNT_TREE_MODEL_TYPE_COLUMN, &type,
221 TNY_ACCOUNT_TREE_MODEL_NAME_COLUMN, &fname,
222 TNY_ACCOUNT_TREE_MODEL_UNREAD_COLUMN, &unread, -1);
223 rendobj = G_OBJECT(renderer);
225 if (type == TNY_MSG_FOLDER_TYPE_NORMAL)
226 type = guess_folder_type (fname);
232 case TNY_MSG_FOLDER_TYPE_INBOX:
233 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_INBOX);
235 case TNY_MSG_FOLDER_TYPE_OUTBOX:
236 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_OUTBOX);
238 case TNY_MSG_FOLDER_TYPE_JUNK:
239 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_JUNK);
241 case TNY_MSG_FOLDER_TYPE_SENT:
242 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_SENT);
244 case TNY_MSG_FOLDER_TYPE_DRAFTS:
245 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_DRAFTS);
247 case TNY_MSG_FOLDER_TYPE_NOTES:
248 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_NOTES);
250 case TNY_MSG_FOLDER_TYPE_CALENDAR:
251 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CALENDAR);
253 case TNY_MSG_FOLDER_TYPE_CONTACTS:
254 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CONTACTS);
256 case TNY_MSG_FOLDER_TYPE_NORMAL:
258 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_NORMAL);
262 g_object_set (rendobj,
263 "pixbuf-expander-open",
264 modest_icon_factory_get_icon (MODEST_FOLDER_ICON_OPEN),
265 "pixbuf-expander-closed",
266 modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CLOSED),
272 modest_folder_view_init (ModestFolderView *obj)
274 ModestFolderViewPrivate *priv;
275 GtkTreeViewColumn *column;
276 GtkCellRenderer *renderer;
277 GtkTreeSelection *sel;
279 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
281 priv->view_is_empty = TRUE;
282 priv->tny_account_store = NULL;
283 priv->cur_folder = NULL;
285 priv->lock = g_mutex_new ();
287 column = gtk_tree_view_column_new ();
288 gtk_tree_view_column_set_title (column,
289 _("All Mail Folders"));
291 gtk_tree_view_append_column (GTK_TREE_VIEW(obj),
294 renderer = gtk_cell_renderer_pixbuf_new();
295 gtk_tree_view_column_pack_start (column, renderer, FALSE);
296 gtk_tree_view_column_set_cell_data_func(column, renderer,
297 icon_cell_data, NULL, NULL);
299 renderer = gtk_cell_renderer_text_new();
300 gtk_tree_view_column_pack_start (column, renderer, FALSE);
301 gtk_tree_view_column_set_cell_data_func(column, renderer,
302 text_cell_data, NULL, NULL);
304 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
305 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
307 gtk_tree_view_column_set_spacing (column, 2);
308 gtk_tree_view_column_set_resizable (column, TRUE);
309 gtk_tree_view_column_set_fixed_width (column, TRUE);
310 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(obj), TRUE);
311 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), FALSE);
317 modest_folder_view_finalize (GObject *obj)
319 ModestFolderViewPrivate *priv;
321 g_return_if_fail (obj);
323 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
324 if (priv->tny_account_store) {
325 g_object_unref (G_OBJECT(priv->tny_account_store));
326 priv->tny_account_store = NULL;
331 g_mutex_free (priv->lock);
335 G_OBJECT_CLASS(parent_class)->finalize (obj);
340 on_accounts_update (TnyAccountStoreIface *account_store, const gchar *account,
343 update_model_empty (MODEST_FOLDER_VIEW(user_data));
345 if (!update_model (MODEST_FOLDER_VIEW(user_data), account_store))
346 g_printerr ("modest: failed to update model for changes in '%s'",
352 modest_folder_view_new (TnyAccountStoreIface *account_store)
355 ModestFolderViewPrivate *priv;
356 GtkTreeSelection *sel;
358 self = G_OBJECT(g_object_new(MODEST_TYPE_FOLDER_VIEW, NULL));
359 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
361 g_return_val_if_fail (account_store, NULL);
363 if (!update_model (MODEST_FOLDER_VIEW(self), account_store))
364 g_printerr ("modest: failed to update model");
366 g_signal_connect (G_OBJECT(account_store), "update_accounts",
367 G_CALLBACK (on_accounts_update), self);
369 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
370 g_signal_connect (sel, "changed",
371 G_CALLBACK(selection_changed), self);
373 return GTK_WIDGET(self);
380 update_model_empty (ModestFolderView *self)
384 ModestFolderViewPrivate *priv;
386 g_return_val_if_fail (self, FALSE);
388 store = gtk_tree_store_new (1, G_TYPE_STRING);
389 gtk_tree_store_append (store, &iter, NULL);
391 gtk_tree_store_set (store, &iter, 0,
394 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
395 GTK_TREE_MODEL(store));
396 g_object_unref (store);
398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
399 priv->view_is_empty = TRUE;
406 update_model (ModestFolderView *self, TnyAccountStoreIface *account_store)
408 ModestFolderViewPrivate *priv;
409 TnyListIface *account_list;
410 GtkTreeModel *model, *sortable;
412 g_return_val_if_fail (account_store, FALSE);
414 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
416 model = GTK_TREE_MODEL(tny_account_tree_model_new ());
417 account_list = TNY_LIST_IFACE(model);
419 update_model_empty (self); /* cleanup */
420 priv->view_is_empty = TRUE;
422 tny_account_store_iface_get_accounts (account_store, account_list,
423 TNY_ACCOUNT_STORE_IFACE_STORE_ACCOUNTS);
424 if (!account_list) /* no store accounts found */
427 sortable = gtk_tree_model_sort_new_with_model (model);
428 gtk_tree_view_set_model (GTK_TREE_VIEW(self), sortable);
430 priv->view_is_empty = FALSE;
431 g_object_unref (model);
438 selection_changed (GtkTreeSelection *sel, gpointer user_data)
441 TnyMsgFolderIface *folder = NULL;
443 ModestFolderView *tree_view;
444 ModestFolderViewPrivate *priv;
446 g_return_if_fail (sel);
447 g_return_if_fail (user_data);
449 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
451 /* is_empty means that there is only the 'empty' item */
452 if (priv->view_is_empty)
455 /* folder was _un_selected if true */
456 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
457 priv->cur_folder = NULL;
461 tree_view = MODEST_FOLDER_VIEW (user_data);
463 gtk_tree_model_get (model, &iter,
464 TNY_ACCOUNT_TREE_MODEL_INSTANCE_COLUMN,
467 if (priv->cur_folder)
468 tny_msg_folder_iface_expunge (priv->cur_folder);
469 priv->cur_folder = folder;
471 /* folder will not be defined if you click eg. on the root node */
473 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTED_SIGNAL], 0,
479 modest_folder_view_update_model(ModestFolderView *self,
480 TnyAccountStoreIface *iface)
482 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
484 return update_model (self, iface);