X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fwidgets%2Fmodest-folder-view.c;h=9124187f7e5e58106691613ce701df72480da3fb;hp=810976fede08bbb881d1363134cec55af56f73b4;hb=beae686e790fa6f231fbfeee19795330f0443a3a;hpb=0b1d178419df194132bd357bc0ef85ee79e2be5b diff --git a/src/widgets/modest-folder-view.c b/src/widgets/modest-folder-view.c index 810976f..9124187 100644 --- a/src/widgets/modest-folder-view.c +++ b/src/widgets/modest-folder-view.c @@ -32,11 +32,8 @@ #include #include #include -#ifdef MODEST_TOOLKIT_HILDON2 #include -#else #include -#endif #include #include #include @@ -47,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -192,11 +190,18 @@ static void on_display_name_changed (ModestAccountMgr *self, static void update_style (ModestFolderView *self); static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata); static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store); +static gboolean inbox_is_special (TnyFolderStore *folder_store); + +static gboolean get_inner_models (ModestFolderView *self, + GtkTreeModel **filter_model, + GtkTreeModel **sort_model, + GtkTreeModel **tny_model); enum { FOLDER_SELECTION_CHANGED_SIGNAL, FOLDER_DISPLAY_NAME_CHANGED_SIGNAL, FOLDER_ACTIVATED_SIGNAL, + VISIBLE_ACCOUNT_CHANGED_SIGNAL, LAST_SIGNAL }; @@ -227,14 +232,18 @@ struct _ModestFolderViewPrivate { gchar *local_account_name; gchar *visible_account_id; + gchar *mailbox; ModestFolderViewStyle style; ModestFolderViewCellStyle cell_style; gboolean reselect; /* we use this to force a reselection of the INBOX */ gboolean show_non_move; + TnyList *list_to_move; gboolean reexpand; /* next time we expose, we'll expand all root folders */ GtkCellRenderer *messages_renderer; + + gulong outbox_deleted_handler; }; #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), \ @@ -332,6 +341,19 @@ modest_folder_view_class_init (ModestFolderViewClass *klass) g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + /* + * Emitted whenever the visible account changes + */ + signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] = + g_signal_new ("visible-account-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ModestFolderViewClass, + visible_account_changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + treeview_class->select_cursor_parent = NULL; #ifdef MODEST_TOOLKIT_HILDON2 @@ -341,6 +363,41 @@ modest_folder_view_class_init (ModestFolderViewClass *klass) } +/* Retrieves the filter, sort and tny models of the folder view. If + any of these does not exist then it returns FALSE */ +static gboolean +get_inner_models (ModestFolderView *self, + GtkTreeModel **filter_model, + GtkTreeModel **sort_model, + GtkTreeModel **tny_model) +{ + GtkTreeModel *s_model, *f_model, *t_model; + + f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); + if (!GTK_IS_TREE_MODEL_FILTER(f_model)) { + g_debug ("%s: emtpy model or not filter model", __FUNCTION__); + return FALSE; + } + + s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model)); + if (!GTK_IS_TREE_MODEL_SORT(s_model)) { + g_warning ("BUG: %s: not a valid sort model", __FUNCTION__); + return FALSE; + } + + t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model)); + + /* Assign values */ + if (filter_model) + *filter_model = f_model; + if (sort_model) + *sort_model = s_model; + if (tny_model) + *tny_model = t_model; + + return TRUE; +} + /* Simplify checks for NULLs: */ static gboolean strings_are_equal (const gchar *a, const gchar *b) @@ -424,8 +481,45 @@ on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data) } static void -format_compact_style (gchar **item_name, +convert_parent_folders_to_dots (gchar **item_name) +{ + gint n_parents = 0; + gchar *c; + gchar *last_separator; + + if (item_name == NULL) + return; + + for (c = *item_name; *c != '\0'; c++) { + if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) { + n_parents++; + } + } + + last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR); + if (last_separator != NULL) { + last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR); + } + + if (n_parents > 0) { + GString *buffer; + gint i; + + buffer = g_string_new (""); + for (i = 0; i < n_parents; i++) { + buffer = g_string_append (buffer, MODEST_FOLDER_DOT); + } + buffer = g_string_append (buffer, last_separator); + g_free (*item_name); + *item_name = g_string_free (buffer, FALSE); + } + +} + +static void +format_compact_style (gchar **item_name, GObject *instance, + const gchar *mailbox, gboolean bold, gboolean multiaccount, gboolean *use_markup) @@ -442,6 +536,22 @@ format_compact_style (gchar **item_name, folder_type = tny_folder_get_folder_type (folder); is_special = (get_cmp_pos (folder_type, folder)!= 4); + if (mailbox) { + /* Remove mailbox prefix if any */ + gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL); + if (g_str_has_prefix (*item_name, prefix)) { + gchar *new_item_name; + + new_item_name = g_strdup (*item_name + strlen (prefix)); + if (!g_ascii_strcasecmp (new_item_name, "Inbox")) { + g_free (new_item_name); + new_item_name = g_strdup (_("mcen_me_folder_inbox")); + } + g_free (*item_name); + *item_name = new_item_name; + } + } + if (!is_special || multiaccount) { TnyAccount *account = tny_folder_get_account (folder); const gchar *folder_name; @@ -452,6 +562,9 @@ format_compact_style (gchar **item_name, if (account == NULL) return; + /* convert parent folders to dots */ + convert_parent_folders_to_dots (item_name); + folder_name = tny_folder_get_name (folder); if (g_str_has_suffix (*item_name, folder_name)) { gchar *offset = g_strrstr (*item_name, folder_name); @@ -460,8 +573,7 @@ format_compact_style (gchar **item_name, } buffer = g_string_new (""); - buffer = g_string_append (buffer, tny_account_get_name (account)); - buffer = g_string_append (buffer, MODEST_FOLDER_PATH_SEPARATOR); + buffer = g_string_append (buffer, *item_name); if (concat_folder_name) { if (bold) buffer = g_string_append (buffer, ""); @@ -526,15 +638,11 @@ text_cell_data (GtkTreeViewColumn *column, } } - if (type == TNY_FOLDER_TYPE_INBOX) { - g_free (fname); - fname = g_strdup (_("mcen_me_folder_inbox")); - } - - /* note: we cannot reliably get the counts from the tree model, we need - * to use explicit calls on tny_folder for some reason. - */ - /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */ + /* note: we cannot reliably get the counts from the + * tree model, we need to use explicit calls on + * tny_folder for some reason. Select the number to + * show: the unread or unsent messages. in case of + * outbox/drafts, show all */ if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX) || (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */ @@ -589,7 +697,7 @@ text_cell_data (GtkTreeViewColumn *column, multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL); /* Convert item_name to markup */ - format_compact_style (&item_name, instance, + format_compact_style (&item_name, instance, priv->mailbox, item_weight == 800, multiaccount, &use_markup); } @@ -783,19 +891,19 @@ get_folder_icons (TnyFolderType type, GObject *instance) static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL, *junk_pixbuf = NULL, *sent_pixbuf = NULL, *trash_pixbuf = NULL, *draft_pixbuf = NULL, - *normal_pixbuf = NULL, *anorm_pixbuf = NULL, + *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL, *ammc_pixbuf = NULL, *avirt_pixbuf = NULL; static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL, *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL, *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL, - *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, + *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL, *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL; static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL, *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL, *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL, - *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, + *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL, *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL; ThreePixbufs *retval = NULL; @@ -807,19 +915,32 @@ get_folder_icons (TnyFolderType type, GObject *instance) type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance)); } + /* It's not enough with check the folder type. We need to + ensure that we're not giving a special folder icon to a + normal folder with the same name than a special folder */ + if (TNY_IS_FOLDER (instance) && + get_cmp_pos (type, TNY_FOLDER (instance)) == 4) + type = TNY_FOLDER_TYPE_NORMAL; + /* Remote folders should not be treated as special folders */ if (TNY_IS_FOLDER_STORE (instance) && !TNY_IS_ACCOUNT (instance) && type != TNY_FOLDER_TYPE_INBOX && modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) { +#ifdef MODEST_TOOLKIT_HILDON2 + return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT, + &anorm_pixbuf, + &anorm_pixbuf_open, + &anorm_pixbuf_close); +#else return get_composite_icons (MODEST_FOLDER_ICON_NORMAL, &normal_pixbuf, &normal_pixbuf_open, &normal_pixbuf_close); +#endif } switch (type) { - const gchar *icon_code; case TNY_FOLDER_TYPE_INVALID: g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__); @@ -886,18 +1007,26 @@ get_folder_icons (TnyFolderType type, GObject *instance) &draft_pixbuf_open, &draft_pixbuf_close); break; + case TNY_FOLDER_TYPE_ARCHIVE: + retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER, + &mmc_pixbuf, + &mmc_pixbuf_open, + &mmc_pixbuf_close); + break; case TNY_FOLDER_TYPE_NORMAL: default: /* Memory card folders could have an special icon */ - if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) - icon_code = MODEST_FOLDER_ICON_MMC_FOLDER; - else - icon_code = MODEST_FOLDER_ICON_NORMAL; - - retval = get_composite_icons (icon_code, - &normal_pixbuf, - &normal_pixbuf_open, - &normal_pixbuf_close); + if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) { + retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER, + &mmc_pixbuf, + &mmc_pixbuf_open, + &mmc_pixbuf_close); + } else { + retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL, + &normal_pixbuf, + &normal_pixbuf_open, + &normal_pixbuf_close); + } break; } @@ -968,6 +1097,12 @@ add_columns (GtkWidget *treeview) /* Set icon and text render function */ renderer = gtk_cell_renderer_pixbuf_new(); +#ifdef MODEST_TOOLKIT_HILDON2 + g_object_set (renderer, + "xpad", MODEST_MARGIN_DEFAULT, + "ypad", MODEST_MARGIN_DEFAULT, + NULL); +#endif gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_cell_data_func(column, renderer, icon_cell_data, treeview, NULL); @@ -976,6 +1111,8 @@ add_columns (GtkWidget *treeview) g_object_set (renderer, #ifdef MODEST_TOOLKIT_HILDON2 "ellipsize", PANGO_ELLIPSIZE_MIDDLE, + "ypad", MODEST_MARGIN_DEFAULT, + "xpad", MODEST_MARGIN_DEFAULT, #else "ellipsize", PANGO_ELLIPSIZE_END, #endif @@ -986,8 +1123,14 @@ add_columns (GtkWidget *treeview) priv->messages_renderer = gtk_cell_renderer_text_new (); g_object_set (priv->messages_renderer, +#ifdef MODEST_TOOLKIT_HILDON2 + "yalign", 0.0, + "ypad", MODEST_MARGIN_DEFAULT, + "xpad", MODEST_MARGIN_DOUBLE, +#else "scale", PANGO_SCALE_X_SMALL, "scale-set", TRUE, +#endif "alignment", PANGO_ALIGN_RIGHT, "align-set", TRUE, "xalign", 1.0, @@ -1025,8 +1168,9 @@ modest_folder_view_init (ModestFolderView *obj) priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL; priv->cur_folder_store = NULL; priv->visible_account_id = NULL; + priv->mailbox = NULL; priv->folder_to_select = NULL; - + priv->outbox_deleted_handler = 0; priv->reexpand = TRUE; /* Initialize the local account name */ @@ -1040,6 +1184,7 @@ modest_folder_view_init (ModestFolderView *obj) priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE; priv->reselect = FALSE; priv->show_non_move = TRUE; + priv->list_to_move = NULL; /* Build treeview */ add_columns (GTK_WIDGET (obj)); @@ -1086,6 +1231,7 @@ modest_folder_view_finalize (GObject *obj) { ModestFolderViewPrivate *priv; GtkTreeSelection *sel; + TnyAccount *local_account; g_return_if_fail (obj); @@ -1096,6 +1242,16 @@ modest_folder_view_finalize (GObject *obj) priv->timer_expander = 0; } + local_account = (TnyAccount *) + modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ()); + if (local_account) { + if (g_signal_handler_is_connected (local_account, + priv->outbox_deleted_handler)) + g_signal_handler_disconnect (local_account, + priv->outbox_deleted_handler); + g_object_unref (local_account); + } + if (priv->account_store) { g_signal_handler_disconnect (G_OBJECT(priv->account_store), priv->account_inserted_signal); @@ -1130,6 +1286,7 @@ modest_folder_view_finalize (GObject *obj) g_free (priv->local_account_name); g_free (priv->visible_account_id); + g_free (priv->mailbox); if (priv->conf_key_signal) { g_signal_handler_disconnect (modest_runtime_get_conf (), @@ -1142,6 +1299,11 @@ modest_folder_view_finalize (GObject *obj) priv->cur_folder_store = NULL; } + if (priv->list_to_move) { + g_object_unref (priv->list_to_move); + priv->list_to_move = NULL; + } + /* Clear hidding array created by cut operation */ _clear_hidding_filter (MODEST_FOLDER_VIEW (obj)); @@ -1200,12 +1362,34 @@ modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore } static void +on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account, + gpointer user_data) +{ + ModestFolderView *self; + GtkTreeModel *model, *filter_model; + TnyFolder *outbox; + + self = MODEST_FOLDER_VIEW (user_data); + + if (!get_inner_models (self, &filter_model, NULL, &model)) + return; + + /* Remove outbox from model */ + outbox = modest_tny_local_folders_account_get_merged_outbox (local_account); + tny_list_remove (TNY_LIST (model), G_OBJECT (outbox)); + g_object_unref (outbox); + + /* Refilter view */ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); +} + +static void on_account_inserted (TnyAccountStore *account_store, TnyAccount *account, gpointer user_data) { ModestFolderViewPrivate *priv; - GtkTreeModel *sort_model, *filter_model; + GtkTreeModel *model, *filter_model; /* Ignore transport account insertions, we're not showing them in the folder view */ @@ -1223,31 +1407,29 @@ on_account_inserted (TnyAccountStore *account_store, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY); - if (!GTK_IS_TREE_VIEW(user_data)) { - g_warning ("BUG: %s: not a valid tree view", __FUNCTION__); - return; - } - - /* Get the inner model */ - /* check, is some rare cases, we did not get the right thing here, - * NB#84097 */ - filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data)); - if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) { - g_warning ("BUG: %s: not a valid filter model", __FUNCTION__); - return; - } - /* check, is some rare cases, we did not get the right thing here, - * NB#84097 */ - sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model)); - if (!GTK_IS_TREE_MODEL_SORT(sort_model)) { - g_warning ("BUG: %s: not a valid sort model", __FUNCTION__); + /* Get models */ + if (!get_inner_models (MODEST_FOLDER_VIEW (user_data), + &filter_model, NULL, &model)) return; - } /* Insert the account in the model */ - tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))), - G_OBJECT (account)); + tny_list_append (TNY_LIST (model), G_OBJECT (account)); + + /* When the model is a list store (plain representation) the + outbox is not a child of any account so we have to manually + delete it because removing the local folders account won't + delete it (because tny_folder_get_account() is not defined + for a merge folder */ + if (TNY_IS_GTK_FOLDER_LIST_STORE (model) && + MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) { + + priv->outbox_deleted_handler = + g_signal_connect (account, + "outbox-deleted", + G_CALLBACK (on_outbox_deleted_cb), + user_data); + } /* Refilter the model */ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); @@ -1305,7 +1487,7 @@ on_account_changed (TnyAccountStore *account_store, { ModestFolderView *self; ModestFolderViewPrivate *priv; - GtkTreeModel *sort_model, *filter_model; + GtkTreeModel *model, *filter_model; GtkTreeSelection *sel; gboolean same_account; @@ -1314,26 +1496,15 @@ on_account_changed (TnyAccountStore *account_store, if (TNY_IS_TRANSPORT_ACCOUNT (tny_account)) return; - if (!MODEST_IS_FOLDER_VIEW(user_data)) { - g_warning ("BUG: %s: not a valid folder view", __FUNCTION__); - return; - } - self = MODEST_FOLDER_VIEW (user_data); priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data); /* Get the inner model */ - filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data)); - if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) { - g_warning ("BUG: %s: not a valid filter model", __FUNCTION__); + if (!get_inner_models (MODEST_FOLDER_VIEW (user_data), + &filter_model, NULL, &model)) return; - } - sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model)); - if (!GTK_IS_TREE_MODEL_SORT(sort_model)) { - g_warning ("BUG: %s: not a valid sort model", __FUNCTION__); - return; - } + filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data)); /* Invalidate the cur_folder_store only if the selected folder belongs to the account that is being removed */ @@ -1344,12 +1515,10 @@ on_account_changed (TnyAccountStore *account_store, } /* Remove the account from the model */ - tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))), - G_OBJECT (tny_account)); + tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account)); /* Insert the account in the model */ - tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))), - G_OBJECT (tny_account)); + tny_list_append (TNY_LIST (model), G_OBJECT (tny_account)); /* Refilter the model */ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); @@ -1367,7 +1536,7 @@ on_account_removed (TnyAccountStore *account_store, { ModestFolderView *self = NULL; ModestFolderViewPrivate *priv; - GtkTreeModel *sort_model, *filter_model; + GtkTreeModel *model, *filter_model; GtkTreeSelection *sel = NULL; gboolean same_account = FALSE; @@ -1406,21 +1575,21 @@ on_account_removed (TnyAccountStore *account_store, g_object_unref (folder_to_select_account); } - /* Remove the account from the model */ - filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); - if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) { - g_warning ("BUG: %s: not a valid filter model", __FUNCTION__); + if (!get_inner_models (MODEST_FOLDER_VIEW (user_data), + &filter_model, NULL, &model)) return; - } - sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model)); - if (!GTK_IS_TREE_MODEL_SORT(sort_model)) { - g_warning ("BUG: %s: not a valid sort model", __FUNCTION__); - return; + /* Disconnect the signal handler */ + if (TNY_IS_GTK_FOLDER_LIST_STORE (model) && + MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) { + if (g_signal_handler_is_connected (account, + priv->outbox_deleted_handler)) + g_signal_handler_disconnect (account, + priv->outbox_deleted_handler); } - tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))), - G_OBJECT (account)); + /* Remove the account from the model */ + tny_list_remove (TNY_LIST (model), G_OBJECT (account)); /* If the removed account is the currently viewed one then clear the configuration value. The new visible account will be the default account */ @@ -1429,6 +1598,7 @@ on_account_removed (TnyAccountStore *account_store, /* Clear the current visible account_id */ modest_folder_view_set_account_id_of_visible_server_account (self, NULL); + modest_folder_view_set_mailbox (self, NULL); /* Call the restore method, this will set the new visible account */ modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self), @@ -1545,6 +1715,138 @@ expand_root_items (ModestFolderView *self) gtk_tree_path_free (path); } +static gboolean +is_parent_of (TnyFolder *a, TnyFolder *b) +{ + const gchar *a_id; + gboolean retval = FALSE; + + a_id = tny_folder_get_id (a); + if (a_id) { + gchar *string_to_match; + const gchar *b_id; + + string_to_match = g_strconcat (a_id, "/", NULL); + b_id = tny_folder_get_id (b); + retval = g_str_has_prefix (b_id, string_to_match); + g_free (string_to_match); + } + + return retval; +} + +typedef struct _ForeachFolderInfo { + gchar *needle; + gboolean found; +} ForeachFolderInfo; + +static gboolean +foreach_folder_with_id (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + ForeachFolderInfo *info; + GObject *instance; + + info = (ForeachFolderInfo *) data; + gtk_tree_model_get (model, iter, + INSTANCE_COLUMN, &instance, + -1); + + if (TNY_IS_FOLDER (instance)) { + const gchar *id; + gchar *collate; + id = tny_folder_get_id (TNY_FOLDER (instance)); + if (id) { + collate = g_utf8_collate_key (id, -1); + info->found = !strcmp (info->needle, collate); + g_free (collate); + } + } + + if (instance) + g_object_unref (instance); + + return info->found; + +} + + +static gboolean +has_folder_with_id (ModestFolderView *self, const gchar *id) +{ + GtkTreeModel *model; + ForeachFolderInfo info = {NULL, FALSE}; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); + info.needle = g_utf8_collate_key (id, -1); + + gtk_tree_model_foreach (model, foreach_folder_with_id, &info); + g_free (info.needle); + + return info.found; +} + +static gboolean +has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b) +{ + const gchar *a_id; + gboolean retval = FALSE; + + a_id = tny_folder_get_id (a); + if (a_id) { + const gchar *b_id; + b_id = tny_folder_get_id (b); + + if (b_id) { + const gchar *last_bar; + gchar *string_to_match; + last_bar = g_strrstr (b_id, "/"); + if (last_bar) + last_bar++; + else + last_bar = b_id; + string_to_match = g_strconcat (a_id, "/", last_bar, NULL); + retval = has_folder_with_id (self, string_to_match); + g_free (string_to_match); + } + } + + return retval; +} + +static gboolean +check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder) +{ + ModestFolderViewPrivate *priv; + TnyIterator *iterator; + gboolean retval = TRUE; + + g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + for (iterator = tny_list_create_iterator (priv->list_to_move); + retval && !tny_iterator_is_done (iterator); + tny_iterator_next (iterator)) { + GObject *instance; + instance = tny_iterator_get_current (iterator); + if (instance == (GObject *) folder) { + retval = FALSE; + } else if (TNY_IS_FOLDER (instance)) { + retval = !is_parent_of (TNY_FOLDER (instance), folder); + if (retval) { + retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance)); + } + } + g_object_unref (instance); + } + g_object_unref (iterator); + + return retval; +} + + /* * We use this function to implement the * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default @@ -1561,11 +1863,14 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) guint i; gboolean found = FALSE; gboolean cleared = FALSE; + ModestTnyFolderRules rules = 0; + gchar *fname; g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE); priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data); gtk_tree_model_get (model, iter, + NAME_COLUMN, &fname, TYPE_COLUMN, &type, INSTANCE_COLUMN, &instance, -1); @@ -1574,14 +1879,12 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) happen when the model is being modified while it's being drawn. This could occur for example when moving folders using drag&drop */ - if (!instance) + if (!instance) { + g_free (fname); return FALSE; + } if (TNY_IS_ACCOUNT (instance)) { -#ifdef MODEST_TOOLKIT_HILDON2 - /* In hildon 2.2 we don't show the account rows */ - return FALSE; -#else TnyAccount *acc = TNY_ACCOUNT (instance); const gchar *account_id = tny_account_get_id (acc); @@ -1604,7 +1907,6 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) * in the local-folders account instead: */ if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc)) retval = FALSE; -#endif } else { if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) { /* Only show special folders for current account if needed */ @@ -1620,8 +1922,17 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) { /* Show only the visible account id */ if (priv->visible_account_id) { - if (strcmp (account_id, priv->visible_account_id)) - retval = FALSE; + if (strcmp (account_id, priv->visible_account_id)) { + retval = FALSE; + } else if (priv->mailbox) { + /* Filter mailboxes */ + if (!g_str_has_prefix (fname, priv->mailbox)) { + retval = FALSE; + } else if (!strcmp (fname, priv->mailbox)) { + /* Hide mailbox parent */ + retval = FALSE; + } + } } } g_object_unref (account); @@ -1645,8 +1956,15 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) /* If this is a move to dialog, hide Sent, Outbox and Drafts folder as no message can be move there according to UI specs */ - if (!priv->show_non_move) { - switch (type) { + if (retval && !priv->show_non_move) { + if (priv->list_to_move && + tny_list_get_length (priv->list_to_move) > 0 && + TNY_IS_FOLDER (instance)) { + retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance)); + } + if (retval && TNY_IS_FOLDER (instance) && + modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) { + switch (type) { case TNY_FOLDER_TYPE_OUTBOX: case TNY_FOLDER_TYPE_SENT: case TNY_FOLDER_TYPE_DRAFTS: @@ -1657,7 +1975,7 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance)); if (type == TNY_FOLDER_TYPE_INVALID) g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__); - + if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT || type == TNY_FOLDER_TYPE_DRAFTS) @@ -1665,31 +1983,75 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) break; default: break; + } } } /* apply special filters */ + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) { + if (TNY_IS_ACCOUNT (instance)) + return FALSE; + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) { + if (TNY_IS_FOLDER (instance)) + return FALSE; + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) { + if (TNY_IS_ACCOUNT (instance)) { + if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) + return FALSE; + } else if (TNY_IS_FOLDER (instance)) { + if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) + return FALSE; + } + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) { + if (TNY_IS_ACCOUNT (instance)) { + if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) + return FALSE; + } else if (TNY_IS_FOLDER (instance)) { + if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) + return FALSE; + } + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) { + /* A mailbox is a fake folder with an @ in the middle of the name */ + if (!TNY_IS_FOLDER (instance) || + !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) { + return FALSE; + } else { + const gchar *folder_name; + folder_name = tny_folder_get_name (TNY_FOLDER (instance)); + if (!folder_name || strchr (folder_name, '@') == NULL) + return FALSE; + } + + } + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) { if (TNY_IS_FOLDER (instance)) { - TnyFolderCaps capabilities; + /* Check folder rules */ + ModestTnyFolderRules rules; - capabilities = tny_folder_get_caps (TNY_FOLDER (instance)); - retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN); - - if (retval) { - retval = ((type != TNY_FOLDER_TYPE_DRAFTS) && - (type != TNY_FOLDER_TYPE_OUTBOX) && - (type != TNY_FOLDER_TYPE_SENT)); - } + rules = modest_tny_folder_get_rules (TNY_FOLDER (instance)); + retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE); } else if (TNY_IS_ACCOUNT (instance)) { - retval = FALSE; + if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) { + retval = FALSE; + } else { + retval = TRUE; + } } } if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) { if (TNY_IS_FOLDER (instance)) { TnyFolderType guess_type; - + if (TNY_FOLDER_TYPE_NORMAL) { guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance)); } else { @@ -1710,7 +2072,35 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) default: break; } - + + } else if (TNY_IS_ACCOUNT (instance)) { + retval = FALSE; + } + } + + if (retval && TNY_IS_FOLDER (instance)) { + rules = modest_tny_folder_get_rules (TNY_FOLDER (instance)); + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) { + if (TNY_IS_FOLDER (instance)) { + retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE); + } else if (TNY_IS_ACCOUNT (instance)) { + retval = FALSE; + } + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) { + if (TNY_IS_FOLDER (instance)) { + retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE); + } else if (TNY_IS_ACCOUNT (instance)) { + retval = FALSE; + } + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) { + if (TNY_IS_FOLDER (instance)) { + retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE); } else if (TNY_IS_ACCOUNT (instance)) { retval = FALSE; } @@ -1718,6 +2108,7 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) /* Free */ g_object_unref (instance); + g_free (fname); return retval; } @@ -1757,6 +2148,31 @@ modest_folder_view_update_model (ModestFolderView *self, model = tny_gtk_folder_store_tree_model_new (NULL); #endif + /* When the model is a list store (plain representation) the + outbox is not a child of any account so we have to manually + delete it because removing the local folders account won't + delete it (because tny_folder_get_account() is not defined + for a merge folder */ + if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) { + TnyAccount *account; + ModestTnyAccountStore *acc_store; + + acc_store = modest_runtime_get_account_store (); + account = modest_tny_account_store_get_local_folders_account (acc_store); + + if (g_signal_handler_is_connected (account, + priv->outbox_deleted_handler)) + g_signal_handler_disconnect (account, + priv->outbox_deleted_handler); + + priv->outbox_deleted_handler = + g_signal_connect (account, + "outbox-deleted", + G_CALLBACK (on_outbox_deleted_cb), + self); + g_object_unref (account); + } + /* Get the accounts: */ tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store), TNY_LIST (model), @@ -1939,13 +2355,39 @@ get_cmp_rows_type_pos (GObject *folder) } } +static gboolean +inbox_is_special (TnyFolderStore *folder_store) +{ + gboolean is_special = TRUE; + + if (TNY_IS_FOLDER (folder_store)) { + const gchar *id; + gchar *downcase; + gchar *last_bar; + gchar *last_inbox_bar; + + id = tny_folder_get_id (TNY_FOLDER (folder_store)); + downcase = g_utf8_strdown (id, -1); + last_bar = g_strrstr (downcase, "/"); + if (last_bar) { + last_inbox_bar = g_strrstr (downcase, "inbox/"); + if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar)) + is_special = FALSE; + } else { + is_special = FALSE; + } + g_free (downcase); + } + return is_special; +} + static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store) { TnyAccount *account; gboolean is_special; /* Inbox, Outbox, Drafts, Sent, User */ - /* 0, 1, 2, 3, 4 */ + /* 0, 1, 2, 3, 4 */ if (!TNY_IS_FOLDER (folder_store)) return 4; @@ -1954,12 +2396,18 @@ get_cmp_pos (TnyFolderType t, TnyFolder *folder_store) { account = tny_folder_get_account (folder_store); is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0); + + /* In inbox case we need to know if the inbox is really the top + * inbox of the account, or if it's a submailbox inbox. To do + * this we'll apply an heuristic rule: Find last "/" and check + * if it's preceeded by another Inbox */ + is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store)); g_object_unref (account); return is_special?0:4; } break; case TNY_FOLDER_TYPE_OUTBOX: - return 2; + return (TNY_IS_MERGE_FOLDER (folder_store))?2:4; break; case TNY_FOLDER_TYPE_DRAFTS: { @@ -1996,21 +2444,31 @@ compare_account_names (TnyAccount *a1, TnyAccount *a2) static gint compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2) { - TnyAccount *a1, *a2; + TnyAccount *a1 = NULL, *a2 = NULL; gint cmp; if (TNY_IS_ACCOUNT (s1)) { a1 = TNY_ACCOUNT (g_object_ref (s1)); - } else { + } else if (!TNY_IS_MERGE_FOLDER (s1)) { a1 = tny_folder_get_account (TNY_FOLDER (s1)); } if (TNY_IS_ACCOUNT (s2)) { a2 = TNY_ACCOUNT (g_object_ref (s2)); - } else { + } else if (!TNY_IS_MERGE_FOLDER (s2)) { a2 = tny_folder_get_account (TNY_FOLDER (s2)); } + if (!a1 || !a2) { + if (!a1 && !a2) + cmp = 0; + else if (!a1) + cmp = 1; + else + cmp = -1; + goto finish; + } + if (a1 == a2) { cmp = 0; goto finish; @@ -2023,8 +2481,10 @@ compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2) cmp = compare_account_names (a1, a2); finish: - g_object_unref (a1); - g_object_unref (a2); + if (a1) + g_object_unref (a1); + if (a2) + g_object_unref (a2); return cmp; } @@ -2082,14 +2542,17 @@ cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2, goto finish; /* Now we sort using the account of each folder */ - cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2)); - if (cmp != 0) - goto finish; + if (TNY_IS_FOLDER_STORE (folder1) && + TNY_IS_FOLDER_STORE (folder2)) { + cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2)); + if (cmp != 0) + goto finish; - /* Each group is preceeded by its account */ - cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2)); - if (cmp != 0) - goto finish; + /* Each group is preceeded by its account */ + cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2)); + if (cmp != 0) + goto finish; + } /* Pure sort by name */ cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE); @@ -2899,6 +3362,11 @@ modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *s /* Save settings to gconf */ modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self), MODEST_CONF_FOLDER_VIEW_KEY); + + /* Notify observers */ + g_signal_emit (G_OBJECT(self), + signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0, + account_id); } const gchar * @@ -2949,6 +3417,7 @@ find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter void modest_folder_view_select_first_inbox_or_local (ModestFolderView *self) { +#ifndef MODEST_TOOLKIT_HILDON2 GtkTreeModel *model; GtkTreeIter iter, inbox_iter; GtkTreeSelection *sel; @@ -2980,6 +3449,7 @@ modest_folder_view_select_first_inbox_or_local (ModestFolderView *self) /* set focus */ gtk_widget_grab_focus (GTK_WIDGET(self)); +#endif } @@ -3327,18 +3797,39 @@ update_style (ModestFolderView *self) { ModestFolderViewPrivate *priv; GdkColor style_color; + PangoAttrList *attr_list; + GtkStyle *style; + PangoAttribute *attr; g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + /* Set color */ + + attr_list = pango_attr_list_new (); if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) { gdk_color_parse ("grey", &style_color); } + attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue); + pango_attr_list_insert (attr_list, attr); + + /* set font */ + style = gtk_rc_get_style_by_paths (gtk_widget_get_settings + (GTK_WIDGET(self)), + "SmallSystemFont", NULL, + G_TYPE_NONE); + if (style) { + attr = pango_attr_font_desc_new (pango_font_description_copy + (style->font_desc)); + pango_attr_list_insert (attr_list, attr); - g_object_set (G_OBJECT (priv->messages_renderer), - "foreground-gdk", &style_color, - "foreground-set", TRUE, - NULL); + g_object_set (G_OBJECT (priv->messages_renderer), + "foreground-gdk", &style_color, + "foreground-set", TRUE, + "attributes", attr_list, + NULL); + pango_attr_list_unref (attr_list); + } } static void @@ -3360,10 +3851,109 @@ modest_folder_view_set_filter (ModestFolderView *self, g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); - priv->filter = filter; + priv->filter |= filter; filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); if (GTK_IS_TREE_MODEL_FILTER(filter_model)) { gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); } } + +void +modest_folder_view_unset_filter (ModestFolderView *self, + ModestFolderViewFilter filter) +{ + ModestFolderViewPrivate *priv; + GtkTreeModel *filter_model; + + g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + priv->filter &= ~filter; + + filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); + if (GTK_IS_TREE_MODEL_FILTER(filter_model)) { + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); + } +} + +gboolean +modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self, + ModestTnyFolderRules rules) +{ + GtkTreeModel *filter_model; + GtkTreeIter iter; + gboolean fulfil = FALSE; + + if (!get_inner_models (self, &filter_model, NULL, NULL)) + return FALSE; + + if (!gtk_tree_model_get_iter_first (filter_model, &iter)) + return FALSE; + + do { + TnyFolderStore *folder; + + gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1); + if (folder) { + if (TNY_IS_FOLDER (folder)) { + ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder)); + /* Folder rules are negative: non_writable, non_deletable... */ + if (!(folder_rules & rules)) + fulfil = TRUE; + } + g_object_unref (folder); + } + + } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil); + + return fulfil; +} + +void +modest_folder_view_set_list_to_move (ModestFolderView *self, + TnyList *list) +{ + ModestFolderViewPrivate *priv; + + g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + if (priv->list_to_move) + g_object_unref (priv->list_to_move); + + if (list) + g_object_ref (list); + + priv->list_to_move = list; +} + +void +modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox) +{ + ModestFolderViewPrivate *priv; + + g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + if (priv->mailbox) + g_free (priv->mailbox); + + priv->mailbox = g_strdup (mailbox); + + /* Notify observers */ + g_signal_emit (G_OBJECT(self), + signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0, + priv->visible_account_id); +} + +const gchar * +modest_folder_view_get_mailbox (ModestFolderView *self) +{ + ModestFolderViewPrivate *priv; + + g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + return (const gchar *) priv->mailbox; +}