X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fwidgets%2Fmodest-folder-view.c;h=21aa2314fab8d21956d250d54360c3d3aff4df95;hp=e6c419376194903be6b3483b17263dc7cd358966;hb=HEAD;hpb=a93ef61b78853a4964114c055ab05bb9a0df8412 diff --git a/src/widgets/modest-folder-view.c b/src/widgets/modest-folder-view.c index e6c4193..21aa231 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 @@ -64,6 +62,10 @@ #include "modest-dnd.h" #include "modest-ui-constants.h" #include "widgets/modest-window.h" +#include +#ifdef MODEST_TOOLKIT_HILDON2 +#include +#endif /* Folder view drag types */ const GtkTargetEntry folder_view_drag_types[] = @@ -73,31 +75,20 @@ const GtkTargetEntry folder_view_drag_types[] = }; /* Default icon sizes for Fremantle style are different */ -#ifdef MODEST_TOOLKIT_HILDON2 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG -#else -#define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL -#endif /* Column names depending on we use list store or tree store */ -#ifdef MODEST_TOOLKIT_HILDON2 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN -#else -#define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN -#define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN -#define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN -#define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN -#define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN -#endif /* 'private'/'protected' functions */ static void modest_folder_view_class_init (ModestFolderViewClass *klass); static void modest_folder_view_init (ModestFolderView *obj); static void modest_folder_view_finalize (GObject *obj); +static void modest_folder_view_dispose (GObject *obj); static void tny_account_store_view_init (gpointer g, gpointer iface_data); @@ -134,69 +125,41 @@ static gboolean filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data); -static gboolean on_key_pressed (GtkWidget *self, - GdkEventKey *event, - gpointer user_data); - static void on_configuration_key_changed (ModestConf* conf, const gchar *key, ModestConfEvent event, ModestConfNotificationId notification_id, ModestFolderView *self); -/* DnD functions */ -static void on_drag_data_get (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time, - gpointer data); - -static void on_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time, - gpointer data); - -static gboolean on_drag_motion (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time, - gpointer user_data); - static void expand_root_items (ModestFolderView *self); -static gint expand_row_timeout (gpointer data); - -static void setup_drag_and_drop (GtkTreeView *self); - static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view, gboolean delete); static void _clear_hidding_filter (ModestFolderView *folder_view); -#ifndef MODEST_TOOLKIT_HILDON2 -static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - ModestFolderView *self); -#endif - static void on_display_name_changed (ModestAccountMgr *self, const gchar *account, gpointer user_data); 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); +static void on_activity_changed (TnyGtkFolderListStore *store, + gboolean activity, + ModestFolderView *folder_view); enum { FOLDER_SELECTION_CHANGED_SIGNAL, FOLDER_DISPLAY_NAME_CHANGED_SIGNAL, FOLDER_ACTIVATED_SIGNAL, + VISIBLE_ACCOUNT_CHANGED_SIGNAL, + ACTIVITY_CHANGED_SIGNAL, LAST_SIGNAL }; @@ -221,20 +184,32 @@ struct _ModestFolderViewPrivate { gchar **hidding_ids; guint n_selected; ModestFolderViewFilter filter; +#ifdef MODEST_TOOLKIT_HILDON2 + GtkWidget *live_search; +#endif TnyFolderStoreQuery *query; + gboolean do_refresh; guint timer_expander; gchar *local_account_name; gchar *visible_account_id; + gchar *mailbox; ModestFolderViewStyle style; ModestFolderViewCellStyle cell_style; + gboolean show_message_count; 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; + + GSList *signal_handlers; + GdkColor active_color; }; #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), \ @@ -291,6 +266,7 @@ modest_folder_view_class_init (ModestFolderViewClass *klass) parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = modest_folder_view_finalize; + gobject_class->dispose = modest_folder_view_dispose; g_type_class_add_private (gobject_class, sizeof(ModestFolderViewPrivate)); @@ -332,6 +308,32 @@ 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); + + /* + * Emitted when the underlying GtkListStore is updating data + */ + signals[ACTIVITY_CHANGED_SIGNAL] = + g_signal_new ("activity-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ModestFolderViewClass, + activity_changed), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + treeview_class->select_cursor_parent = NULL; #ifdef MODEST_TOOLKIT_HILDON2 @@ -341,6 +343,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) @@ -427,15 +464,28 @@ static void convert_parent_folders_to_dots (gchar **item_name) { gint n_parents = 0; + gint n_inbox_parents = 0; gchar *c; + gchar *path_start; gchar *last_separator; if (item_name == NULL) return; + path_start = *item_name; for (c = *item_name; *c != '\0'; c++) { if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) { + gchar *compare; + if (c != path_start) { + compare = g_strndup (path_start, c - path_start); + compare = g_strstrip (compare); + if (g_ascii_strcasecmp (compare, "inbox") == 0) { + n_inbox_parents++; + } + g_free (compare); + } n_parents++; + path_start = c + 1; } } @@ -449,7 +499,7 @@ convert_parent_folders_to_dots (gchar **item_name) gint i; buffer = g_string_new (""); - for (i = 0; i < n_parents; i++) { + for (i = 0; i < n_parents - n_inbox_parents; i++) { buffer = g_string_append (buffer, MODEST_FOLDER_DOT); } buffer = g_string_append (buffer, last_separator); @@ -460,8 +510,9 @@ convert_parent_folders_to_dots (gchar **item_name) } static void -format_compact_style (gchar **item_name, +format_compact_style (gchar **item_name, GObject *instance, + const gchar *mailbox, gboolean bold, gboolean multiaccount, gboolean *use_markup) @@ -469,6 +520,7 @@ format_compact_style (gchar **item_name, TnyFolder *folder; gboolean is_special; TnyFolderType folder_type; + gboolean l_use_markup; if (!TNY_IS_FOLDER (instance)) return; @@ -478,6 +530,16 @@ 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 = g_strdup (*item_name + strlen (prefix)); + g_free (*item_name); + *item_name = new_item_name; + } + } + if (!is_special || multiaccount) { TnyAccount *account = tny_folder_get_account (folder); const gchar *folder_name; @@ -502,18 +564,71 @@ format_compact_style (gchar **item_name, buffer = g_string_append (buffer, *item_name); if (concat_folder_name) { - if (bold) buffer = g_string_append (buffer, ""); - buffer = g_string_append (buffer, folder_name); - if (bold) buffer = g_string_append (buffer, ""); + if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) { + buffer = g_string_append (buffer, folder_name); + /* TODO: append a sensitive string to the remote drafts to + * be able to know it's the remote one */ +/* buffer = g_string_append (buffer, " (TODO:remote)"); */ + } else { + buffer = g_string_append (buffer, folder_name); + } } g_free (*item_name); g_object_unref (account); *item_name = g_string_free (buffer, FALSE); - *use_markup = bold; + l_use_markup = FALSE; } else { - *use_markup = FALSE; + l_use_markup = FALSE; + } + if (use_markup) + *use_markup = l_use_markup; +} + +static void +replace_special_folder_prefix (gchar **item_name) +{ + const gchar *separator; + gchar *prefix; + + if (item_name == NULL || *item_name == NULL || **item_name == '\0') + return; + separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR); + if (separator == NULL) + return; + + prefix = g_strndup (*item_name, separator - *item_name); + g_strstrip (prefix); + + if (prefix && *prefix != '\0') { + TnyFolderType folder_type; + gchar *new_name; + gchar *downcase; + + downcase = g_utf8_strdown (prefix, -1); + g_free (prefix); + prefix = downcase; + + if (strcmp (downcase, "inbox") == 0) { + folder_type = TNY_FOLDER_TYPE_INBOX; + new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL); + g_free (*item_name); + *item_name = new_name; + } else { + folder_type = modest_local_folder_info_get_type (prefix); + switch (folder_type) { + case TNY_FOLDER_TYPE_INBOX: + case TNY_FOLDER_TYPE_ARCHIVE: + new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL); + g_free (*item_name); + *item_name = new_name; + break; + default: + break; + } + } } + g_free (prefix); } static void @@ -547,9 +662,12 @@ text_cell_data (GtkTreeViewColumn *column, if (type != TNY_FOLDER_TYPE_ROOT) { gint number = 0; gboolean drafts; + gboolean is_local; + + is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) || + modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)); - if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) || - modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) { + if (is_local) { type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance)); if (type != TNY_FOLDER_TYPE_UNKNOWN) { g_free (fname); @@ -564,18 +682,14 @@ 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 */ - if ((type == TNY_FOLDER_TYPE_DRAFTS) || - (type == TNY_FOLDER_TYPE_OUTBOX) || - (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */ + /* 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 (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) || + (type == TNY_FOLDER_TYPE_OUTBOX) || + (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */ number = tny_folder_get_all_count (TNY_FOLDER(instance)); drafts = TRUE; } else { @@ -593,7 +707,11 @@ text_cell_data (GtkTreeViewColumn *column, } else { /* Use bold font style if there are unread or unset messages */ if (number > 0) { - item_name = g_strdup_printf ("%s (%d)", fname, number); + if (priv->show_message_count) { + item_name = g_strdup_printf ("%s (%d)", fname, number); + } else { + item_name = g_strdup (fname); + } item_weight = 800; } else { item_name = g_strdup (fname); @@ -619,6 +737,13 @@ text_cell_data (GtkTreeViewColumn *column, } } + /* Convert INBOX */ + if (type == TNY_FOLDER_TYPE_INBOX && + g_str_has_suffix (fname, "Inbox")) { + g_free (item_name); + item_name = g_strdup (_("mcen_me_folder_inbox")); + } + if (!item_name) item_name = g_strdup ("unknown"); @@ -627,17 +752,31 @@ 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); + } else { + replace_special_folder_prefix (&item_name); } if (item_name && item_weight) { /* Set the name in the treeview cell: */ - if (use_markup) - g_object_set (rendobj, "markup", item_name, NULL); - else - g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL); + if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && + (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) { + g_object_set (rendobj, + "text", item_name, + "weight-set", FALSE, + "foreground-set", TRUE, + "foreground-gdk", &(priv->active_color), + NULL); + } else { + g_object_set (rendobj, + "text", item_name, + "foreground-set", FALSE, + "weight-set", TRUE, + "weight", item_weight, + NULL); + } /* Notify display name observers */ /* TODO: What listens for this signal, and how can it use only the new name? */ @@ -702,9 +841,12 @@ messages_cell_data (GtkTreeViewColumn *column, if (type != TNY_FOLDER_TYPE_ROOT) { gint number = 0; gboolean drafts; + gboolean is_local; + + is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) || + modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)); - if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) || - modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) { + if (is_local) { type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance)); } else { /* Sometimes an special folder is reported by the server as @@ -719,9 +861,9 @@ messages_cell_data (GtkTreeViewColumn *column, * 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... */ + if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) || + (type == TNY_FOLDER_TYPE_OUTBOX) || + (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */ number = tny_folder_get_all_count (TNY_FOLDER(instance)); drafts = TRUE; } else { @@ -729,14 +871,13 @@ messages_cell_data (GtkTreeViewColumn *column, drafts = FALSE; } - if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) { - if (number > 0) { - item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"), - number); - } - } - - } + if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) { + item_name = + g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message", + (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages", + number), number); + } + } if (!item_name) item_name = g_strdup (""); @@ -791,33 +932,81 @@ get_composite_icons (const gchar *icon_code, { ThreePixbufs *retval; - if (!*pixbuf) - *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE)); + if (pixbuf && !*pixbuf) { + GdkPixbuf *icon; + icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE); + if (icon) { + *pixbuf = gdk_pixbuf_copy (icon); + } else { + *pixbuf = NULL; + } + } - if (!*pixbuf_open) + if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf) *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp", FOLDER_ICON_SIZE, *pixbuf); - if (!*pixbuf_close) + if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf) *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp", FOLDER_ICON_SIZE, *pixbuf); retval = g_slice_new0 (ThreePixbufs); - if (*pixbuf) + if (pixbuf && *pixbuf) retval->pixbuf = g_object_ref (*pixbuf); - if (*pixbuf_open) + else + retval->pixbuf = NULL; + if (pixbuf_open && *pixbuf_open) retval->pixbuf_open = g_object_ref (*pixbuf_open); - if (*pixbuf_close) + else + retval->pixbuf_open = NULL; + if (pixbuf_close && *pixbuf_close) retval->pixbuf_close = g_object_ref (*pixbuf_close); + else + retval->pixbuf_close = NULL; return retval; } +static inline ThreePixbufs * +get_account_protocol_pixbufs (ModestFolderView *folder_view, + ModestProtocolType protocol_type, + GObject *object) +{ + ModestProtocol *protocol; + const GdkPixbuf *pixbuf = NULL; + ModestFolderViewPrivate *priv; + + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view); + + protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (), + protocol_type); + + if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) { + pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol), + priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES? + MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX: + MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER, + object, FOLDER_ICON_SIZE); + } + + if (pixbuf) { + ThreePixbufs *retval; + retval = g_slice_new0 (ThreePixbufs); + retval->pixbuf = g_object_ref ((GObject *) pixbuf); + retval->pixbuf_open = g_object_ref ((GObject *) pixbuf); + retval->pixbuf_close = g_object_ref ((GObject *) pixbuf); + return retval; + } else { + return NULL; + } +} + static inline ThreePixbufs* -get_folder_icons (TnyFolderType type, GObject *instance) +get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance) { + TnyAccount *account = NULL; static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL, *junk_pixbuf = NULL, *sent_pixbuf = NULL, *trash_pixbuf = NULL, *draft_pixbuf = NULL, @@ -838,6 +1027,23 @@ get_folder_icons (TnyFolderType type, GObject *instance) ThreePixbufs *retval = NULL; + if (TNY_IS_ACCOUNT (instance)) { + account = g_object_ref (instance); + } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) { + account = tny_folder_get_account (TNY_FOLDER (instance)); + } + + if (account) { + ModestProtocolType account_store_protocol; + + account_store_protocol = modest_tny_account_get_protocol_type (account); + retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance); + g_object_unref (account); + } + + if (retval) + return retval; + /* Sometimes an special folder is reported by the server as NORMAL, like some versions of Dovecot */ if (type == TNY_FOLDER_TYPE_NORMAL || @@ -845,22 +1051,22 @@ 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, + return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER, &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) { @@ -935,7 +1141,7 @@ get_folder_icons (TnyFolderType type, GObject *instance) &mmc_pixbuf, &mmc_pixbuf_open, &mmc_pixbuf_close); - + break; case TNY_FOLDER_TYPE_NORMAL: default: /* Memory card folders could have an special icon */ @@ -979,6 +1185,7 @@ icon_cell_data (GtkTreeViewColumn *column, TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN; gboolean has_children; ThreePixbufs *pixbufs; + ModestFolderView *folder_view = (ModestFolderView *) data; rendobj = (GObject *) renderer; @@ -991,7 +1198,7 @@ icon_cell_data (GtkTreeViewColumn *column, return; has_children = gtk_tree_model_iter_has_child (tree_model, iter); - pixbufs = get_folder_icons (type, instance); + pixbufs = get_folder_icons (folder_view, type, instance); g_object_unref (instance); /* Set pixbuf */ @@ -1020,17 +1227,19 @@ add_columns (GtkWidget *treeview) /* Set icon and text render function */ renderer = gtk_cell_renderer_pixbuf_new(); + g_object_set (renderer, + "xpad", MODEST_MARGIN_DEFAULT, + "ypad", MODEST_MARGIN_DEFAULT, + NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_cell_data_func(column, renderer, icon_cell_data, treeview, NULL); renderer = gtk_cell_renderer_text_new(); g_object_set (renderer, -#ifdef MODEST_TOOLKIT_HILDON2 "ellipsize", PANGO_ELLIPSIZE_MIDDLE, -#else - "ellipsize", PANGO_ELLIPSIZE_END, -#endif + "ypad", MODEST_MARGIN_DEFAULT, + "xpad", MODEST_MARGIN_DEFAULT, "ellipsize-set", TRUE, NULL); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_cell_data_func(column, renderer, @@ -1038,8 +1247,9 @@ add_columns (GtkWidget *treeview) priv->messages_renderer = gtk_cell_renderer_text_new (); g_object_set (priv->messages_renderer, - "scale", PANGO_SCALE_X_SMALL, - "scale-set", TRUE, + "yalign", 0.5, + "ypad", MODEST_MARGIN_DEFAULT, + "xpad", MODEST_MARGIN_DOUBLE, "alignment", PANGO_ALIGN_RIGHT, "align-set", TRUE, "xalign", 1.0, @@ -1058,6 +1268,7 @@ add_columns (GtkWidget *treeview) gtk_tree_view_column_set_fixed_width (column, TRUE); gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview, TRUE); /* Add column */ gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column); @@ -1074,12 +1285,18 @@ modest_folder_view_init (ModestFolderView *obj) priv->timer_expander = 0; priv->account_store = NULL; priv->query = NULL; + priv->do_refresh = TRUE; 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; + priv->signal_handlers = 0; +#ifdef MODEST_TOOLKIT_HILDON2 + priv->live_search = NULL; +#endif /* Initialize the local account name */ conf = modest_runtime_get_conf(); @@ -1092,37 +1309,32 @@ 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; + priv->show_message_count = TRUE; /* Build treeview */ add_columns (GTK_WIDGET (obj)); - /* Setup drag and drop */ - setup_drag_and_drop (GTK_TREE_VIEW(obj)); - - /* Connect signals */ - g_signal_connect (G_OBJECT (obj), - "key-press-event", - G_CALLBACK (on_key_pressed), NULL); - - priv->display_name_changed_signal = - g_signal_connect (modest_runtime_get_account_mgr (), - "display_name_changed", - G_CALLBACK (on_display_name_changed), - obj); + priv->display_name_changed_signal = + g_signal_connect (modest_runtime_get_account_mgr (), + "display_name_changed", + G_CALLBACK (on_display_name_changed), + obj); /* * Track changes in the local account name (in the device it * will be the device name) */ - priv->conf_key_signal = g_signal_connect (G_OBJECT(conf), - "key_changed", - G_CALLBACK(on_configuration_key_changed), - obj); - - update_style (obj); - g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj); + priv->conf_key_signal = g_signal_connect (G_OBJECT(conf), + "key_changed", + G_CALLBACK(on_configuration_key_changed), + obj); + gdk_color_parse ("000", &priv->active_color); + update_style (obj); + g_signal_connect (G_OBJECT (obj), "notify::style", + G_CALLBACK (on_notify_style), (gpointer) obj); } static void @@ -1134,20 +1346,18 @@ tny_account_store_view_init (gpointer g, gpointer iface_data) } static void -modest_folder_view_finalize (GObject *obj) +modest_folder_view_dispose (GObject *obj) { ModestFolderViewPrivate *priv; - GtkTreeSelection *sel; - - g_return_if_fail (obj); - priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj); - if (priv->timer_expander != 0) { - g_source_remove (priv->timer_expander); - priv->timer_expander = 0; + if (priv->signal_handlers) { + modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers); + priv->signal_handlers = NULL; } + /* Free external references */ if (priv->account_store) { g_signal_handler_disconnect (G_OBJECT(priv->account_store), priv->account_inserted_signal); @@ -1159,13 +1369,6 @@ modest_folder_view_finalize (GObject *obj) priv->account_store = NULL; } - if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), - priv->display_name_changed_signal)) { - g_signal_handler_disconnect (modest_runtime_get_account_mgr (), - priv->display_name_changed_signal); - priv->display_name_changed_signal = 0; - } - if (priv->query) { g_object_unref (G_OBJECT (priv->query)); priv->query = NULL; @@ -1176,12 +1379,59 @@ modest_folder_view_finalize (GObject *obj) priv->folder_to_select = NULL; } + if (priv->cur_folder_store) { + g_object_unref (priv->cur_folder_store); + priv->cur_folder_store = NULL; + } + + if (priv->list_to_move) { + g_object_unref (priv->list_to_move); + priv->list_to_move = NULL; + } + + G_OBJECT_CLASS(parent_class)->dispose (obj); +} + +static void +modest_folder_view_finalize (GObject *obj) +{ + ModestFolderViewPrivate *priv; + GtkTreeSelection *sel; + TnyAccount *local_account; + + g_return_if_fail (obj); + + priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj); + + if (priv->timer_expander != 0) { + g_source_remove (priv->timer_expander); + 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 (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), + priv->display_name_changed_signal)) { + g_signal_handler_disconnect (modest_runtime_get_account_mgr (), + priv->display_name_changed_signal); + priv->display_name_changed_signal = 0; + } + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj)); if (sel) g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal); 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 (), @@ -1189,14 +1439,11 @@ modest_folder_view_finalize (GObject *obj) priv->conf_key_signal = 0; } - if (priv->cur_folder_store) { - g_object_unref (priv->cur_folder_store); - priv->cur_folder_store = NULL; - } - /* Clear hidding array created by cut operation */ _clear_hidding_filter (MODEST_FOLDER_VIEW (obj)); + gdk_color_parse ("000", &priv->active_color); + G_OBJECT_CLASS(parent_class)->finalize (obj); } @@ -1246,18 +1493,39 @@ modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store); priv->reselect = FALSE; - modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self)); g_object_unref (G_OBJECT (device)); } 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 */ @@ -1275,31 +1543,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)); @@ -1334,22 +1600,6 @@ same_account_selected (ModestFolderView *self, return same_account; } -/** - * - * Selects the first inbox or the local account in an idle - */ -static gboolean -on_idle_select_first_inbox_or_local (gpointer user_data) -{ - ModestFolderView *self = MODEST_FOLDER_VIEW (user_data); - - gdk_threads_enter (); - modest_folder_view_select_first_inbox_or_local (self); - gdk_threads_leave (); - - return FALSE; -} - static void on_account_changed (TnyAccountStore *account_store, TnyAccount *tny_account, @@ -1357,7 +1607,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; @@ -1366,26 +1616,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 */ @@ -1396,20 +1635,14 @@ 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)); - /* Select the first INBOX if the currently selected folder - belongs to the account that is being deleted */ - if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account)) - g_idle_add (on_idle_select_first_inbox_or_local, self); } static void @@ -1419,7 +1652,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; @@ -1458,21 +1691,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 */ @@ -1481,6 +1714,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), @@ -1490,10 +1724,6 @@ on_account_removed (TnyAccountStore *account_store, /* Refilter the model */ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); - /* Select the first INBOX if the currently selected folder - belongs to the account that is being deleted */ - if (same_account) - g_idle_add (on_idle_select_first_inbox_or_local, self); } void @@ -1534,7 +1764,6 @@ modest_folder_view_on_map (ModestFolderView *self, deathlock situation */ /* TODO: check if this is still the case */ priv->reselect = FALSE; - modest_folder_view_select_first_inbox_or_local (self); /* Notify the display name observers */ g_signal_emit (G_OBJECT(self), signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0, @@ -1552,6 +1781,12 @@ modest_folder_view_on_map (ModestFolderView *self, GtkWidget* modest_folder_view_new (TnyFolderStoreQuery *query) { + return modest_folder_view_new_full (query, TRUE); +} + +GtkWidget* +modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh) +{ GObject *self; ModestFolderViewPrivate *priv; GtkTreeSelection *sel; @@ -1566,6 +1801,8 @@ modest_folder_view_new (TnyFolderStoreQuery *query) if (query) priv->query = g_object_ref (query); + priv->do_refresh = do_refresh; + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self)); priv->changed_signal = g_signal_connect (sel, "changed", G_CALLBACK (on_selection_changed), self); @@ -1574,6 +1811,9 @@ modest_folder_view_new (TnyFolderStoreQuery *query) g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL); + /* Hide headers by default */ + gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE); + return GTK_WIDGET(self); } @@ -1597,6 +1837,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 @@ -1614,11 +1986,13 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) 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); @@ -1627,14 +2001,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); @@ -1657,7 +2029,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 */ @@ -1673,8 +2044,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); @@ -1698,8 +2078,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: @@ -1710,7 +2097,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) @@ -1718,31 +2105,85 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) break; default: break; + } + } + if (retval && TNY_IS_ACCOUNT (instance) && + modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) { + ModestProtocolType protocol_type; + + protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance)); + retval = !modest_protocol_registry_protocol_type_has_tag + (modest_runtime_get_protocol_registry (), + protocol_type, + MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS); } } /* apply special filters */ + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) { + if (TNY_IS_ACCOUNT (instance)) + retval = FALSE; + } + + if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) { + if (TNY_IS_FOLDER (instance)) + retval = 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))) + retval = FALSE; + } else if (TNY_IS_FOLDER (instance)) { + if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) + retval = 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))) + retval = FALSE; + } else if (TNY_IS_FOLDER (instance)) { + if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) + retval = 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)) { + retval = FALSE; + } else { + const gchar *folder_name; + folder_name = tny_folder_get_name (TNY_FOLDER (instance)); + if (!folder_name || strchr (folder_name, '@') == NULL) + retval = 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 { @@ -1763,7 +2204,7 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) default: break; } - + } else if (TNY_IS_ACCOUNT (instance)) { retval = FALSE; } @@ -1797,8 +2238,53 @@ filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) } } +#ifdef MODEST_TOOLKIT_HILDON2 + if (retval && (priv->live_search)) { + const gchar *needle; + needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search)); + if (needle && needle[0] != '\0') { + gchar *haystack; + gboolean is_local; + + haystack = g_strdup (fname); + if (type != TNY_FOLDER_TYPE_ROOT) { + is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) || + modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)); + if (is_local) { + TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN; + type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance)); + if (type != TNY_FOLDER_TYPE_UNKNOWN) { + g_free (haystack); + haystack = g_strdup (modest_local_folder_info_get_type_display_name (type)); + } + } else { + } + } else { + if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) { + g_free (haystack); + haystack = g_strdup (priv->local_account_name); + } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) { + g_free (haystack); + haystack = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance))); + } + } + + if (type == TNY_FOLDER_TYPE_INBOX && + g_str_has_suffix (haystack, "Inbox")) { + g_free (haystack); + haystack = g_strdup (_("mcen_me_folder_inbox")); + } + format_compact_style (&haystack, instance, priv->mailbox, FALSE, + priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL, NULL); + retval = modest_text_utils_live_search_find (haystack, needle); + g_free (haystack); + } + } +#endif + /* Free */ g_object_unref (instance); + g_free (fname); return retval; } @@ -1809,7 +2295,7 @@ modest_folder_view_update_model (ModestFolderView *self, TnyAccountStore *account_store) { ModestFolderViewPrivate *priv; - GtkTreeModel *model /* , *old_model */; + GtkTreeModel *model; GtkTreeModel *filter_model = NULL, *sortable = NULL; g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE); @@ -1829,19 +2315,84 @@ modest_folder_view_update_model (ModestFolderView *self, /* FIXME: the local accounts are not shown when the query selects only the subscribed folders */ -#ifdef MODEST_TOOLKIT_HILDON2 + TnyGtkFolderListStoreFlags flags; + flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH; + if (priv->do_refresh) + flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH; + else + flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH; model = tny_gtk_folder_list_store_new_with_flags (NULL, - TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH); + flags); tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model), MODEST_FOLDER_PATH_SEPARATOR); -#else - model = tny_gtk_folder_store_tree_model_new (NULL); -#endif - /* Get the accounts: */ - tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store), - TNY_LIST (model), - TNY_ACCOUNT_STORE_STORE_ACCOUNTS); + /* 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); + } + + if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) { + /* Get the accounts */ + tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store), + TNY_LIST (model), + TNY_ACCOUNT_STORE_STORE_ACCOUNTS); + } else { + if (priv->visible_account_id) { + TnyAccount *account; + + /* Add local folders account */ + account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store); + + if (account) { + tny_list_append (TNY_LIST (model), (GObject *) account); + g_object_unref (account); + } + + account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store); + + if (account) { + tny_list_append (TNY_LIST (model), (GObject *) account); + g_object_unref (account); + } + + /* Add visible account */ + account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store, + MODEST_TNY_ACCOUNT_STORE_QUERY_ID, + priv->visible_account_id); + if (account) { + tny_list_append (TNY_LIST (model), (GObject *) account); + g_object_unref (account); + } else { + g_warning ("You need to set an account first"); + g_object_unref (model); + return FALSE; + } + } else { + g_warning ("You need to set an account first"); + g_object_unref (model); + return FALSE; + } + } sortable = gtk_tree_model_sort_new_with_model (model); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable), @@ -1858,12 +2409,23 @@ modest_folder_view_update_model (ModestFolderView *self, self, NULL); + GtkTreeModel *old_tny_model = NULL; + if (get_inner_models (self, NULL, NULL, &old_tny_model)) { + if (priv->signal_handlers > 0) { + priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers, + G_OBJECT (old_tny_model), + "activity-changed"); + } + } + /* Set new model */ gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model); -#ifndef MODEST_TOOLKIT_HILDON2 - g_signal_connect (G_OBJECT(filter_model), "row-inserted", - (GCallback) on_row_inserted_maybe_select_folder, self); -#endif + + priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers, + G_OBJECT (model), + "activity-changed", + G_CALLBACK (on_activity_changed), + self); g_object_unref (model); g_object_unref (filter_model); @@ -1915,9 +2477,6 @@ on_selection_changed (GtkTreeSelection *sel, gpointer user_data) cause (and it actually does it) a free of the summary of the folder (because the main window will clear the headers view */ - if (TNY_IS_FOLDER(priv->cur_folder_store)) - tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store), - FALSE, NULL, NULL, NULL); g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, priv->cur_folder_store, FALSE); @@ -2020,13 +2579,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; @@ -2035,12 +2620,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: { @@ -2077,21 +2668,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; @@ -2104,8 +2705,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; } @@ -2121,6 +2724,51 @@ compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2) return is_account2 - is_account1; } +static gint +compare_folders (const gchar *name1, const gchar *name2) +{ + const gchar *separator1, *separator2; + const gchar *next1, *next2; + gchar *top1, *top2; + gint cmp; + + if (name1 == NULL || name1[0] == '\0') + return -1; + if (name2 == NULL || name2[0] == '\0') + return 1; + + separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR); + if (separator1) { + top1 = g_strndup (name1, separator1 - name1); + } else { + top1 = g_strdup (name1); + } + + separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR); + if (separator2) { + top2 = g_strndup (name2, separator2 - name2); + } else { + top2 = g_strdup (name2); + } + + + cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE); + g_free (top1); + g_free (top2); + + if (cmp != 0) + return cmp; + + if (separator1 == NULL && separator2 == NULL) + return 0; + + next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL; + next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL; + + return compare_folders (next1, next2); +} + + /* * This function orders the mail accounts according to these rules: * 1st - remote accounts @@ -2163,17 +2811,20 @@ 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); + cmp = compare_folders (name1, name2); finish: if (folder1) g_object_unref(G_OBJECT(folder1)); @@ -2186,771 +2837,65 @@ cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2, return cmp; } -/*****************************************************************************/ -/* DRAG and DROP stuff */ -/*****************************************************************************/ + /* - * This function fills the #GtkSelectionData with the row and the - * model that has been dragged. It's called when this widget is a - * source for dnd after the event drop happened + * We listen to the changes in the local folder account name key, + * because we want to show the right name in the view. The local + * folder account name corresponds to the device name in the Maemo + * version. We do this because we do not want to query gconf on each + * tree view refresh. It's better to cache it and change whenever + * necessary. */ static void -on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, - guint info, guint time, gpointer data) +on_configuration_key_changed (ModestConf* conf, + const gchar *key, + ModestConfEvent event, + ModestConfNotificationId id, + ModestFolderView *self) { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *source_row; + ModestFolderViewPrivate *priv; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); - if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - source_row = gtk_tree_model_get_path (model, &iter); - gtk_tree_set_row_drag_data (selection_data, - model, - source_row); + g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self); - gtk_tree_path_free (source_row); - } -} + if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) { + g_free (priv->local_account_name); -typedef struct _DndHelper { - ModestFolderView *folder_view; - gboolean delete_source; - GtkTreePath *source_row; -} DndHelper; + if (event == MODEST_CONF_EVENT_KEY_UNSET) + priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME); + else + priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(), + MODEST_CONF_DEVICE_NAME, NULL); -static void -dnd_helper_destroyer (DndHelper *helper) -{ - /* Free the helper */ - gtk_tree_path_free (helper->source_row); - g_slice_free (DndHelper, helper); -} + /* Force a redraw */ +#if GTK_CHECK_VERSION(2, 8, 0) + GtkTreeViewColumn * tree_column; -static void -xfer_folder_cb (ModestMailOperation *mail_op, - TnyFolder *new_folder, - gpointer user_data) -{ - if (new_folder) { - /* Select the folder */ - modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data), - new_folder, FALSE); + tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), + NAME_COLUMN); + gtk_tree_view_column_queue_resize (tree_column); +#else + gtk_widget_queue_draw (GTK_WIDGET (self)); +#endif } } - -/* get the folder for the row the treepath refers to. */ -/* folder must be unref'd */ -static TnyFolderStore * -tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path) -{ - GtkTreeIter iter; - TnyFolderStore *folder = NULL; - - if (gtk_tree_model_get_iter (model,&iter, path)) - gtk_tree_model_get (model, &iter, - INSTANCE_COLUMN, &folder, - -1); - return folder; -} - - -/* - * This function is used by drag_data_received_cb to manage drag and - * drop of a header, i.e, and drag from the header view to the folder - * view. - */ -static void -drag_and_drop_from_header_view (GtkTreeModel *source_model, - GtkTreeModel *dest_model, - GtkTreePath *dest_row, - GtkSelectionData *selection_data) +void +modest_folder_view_set_style (ModestFolderView *self, + ModestFolderViewStyle style) { - TnyList *headers = NULL; - TnyFolder *folder = NULL, *src_folder = NULL; - TnyFolderType folder_type; - GtkTreeIter source_iter, dest_iter; - ModestWindowMgr *mgr = NULL; - ModestWindow *main_win = NULL; - gchar **uris, **tmp; - - /* Build the list of headers */ - mgr = modest_runtime_get_window_mgr (); - headers = tny_simple_list_new (); - uris = modest_dnd_selection_data_get_paths (selection_data); - tmp = uris; - - while (*tmp != NULL) { - TnyHeader *header; - GtkTreePath *path; - gboolean first = TRUE; - - /* Get header */ - path = gtk_tree_path_new_from_string (*tmp); - gtk_tree_model_get_iter (source_model, &source_iter, path); - gtk_tree_model_get (source_model, &source_iter, - TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, - &header, -1); - - /* Do not enable d&d of headers already opened */ - if (!modest_window_mgr_find_registered_header(mgr, header, NULL)) - tny_list_append (headers, G_OBJECT (header)); - - if (G_UNLIKELY (first)) { - src_folder = tny_header_get_folder (header); - first = FALSE; - } + ModestFolderViewPrivate *priv; - /* Free and go on */ - gtk_tree_path_free (path); - g_object_unref (header); - tmp++; - } - g_strfreev (uris); + g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self)); + g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL || + style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE); - /* This could happen ig we perform a d&d very quickly over the - same row that row could dissapear because message is - transferred */ - if (!TNY_IS_FOLDER (src_folder)) - goto cleanup; + priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self); - /* Get the target folder */ - gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row); - gtk_tree_model_get (dest_model, &dest_iter, - INSTANCE_COLUMN, - &folder, -1); - if (!folder || !TNY_IS_FOLDER(folder)) { -/* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */ - goto cleanup; - } - - folder_type = modest_tny_folder_guess_folder_type (folder); - if (folder_type == TNY_FOLDER_TYPE_INVALID) { -/* g_warning ("%s: invalid target folder", __FUNCTION__); */ - goto cleanup; /* cannot move messages there */ - } - - if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) { -/* g_warning ("folder not writable"); */ - goto cleanup; /* verboten! */ - } - - /* Ask for confirmation to move */ - main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */ - if (!main_win) { - g_warning ("%s: BUG: no main window found", __FUNCTION__); - goto cleanup; - } - - /* Transfer messages */ - modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder, - headers, folder); - - /* Frees */ -cleanup: - if (G_IS_OBJECT (src_folder)) - g_object_unref (src_folder); - if (G_IS_OBJECT(folder)) - g_object_unref (G_OBJECT (folder)); - if (G_IS_OBJECT(headers)) - g_object_unref (headers); -} - -typedef struct { - TnyFolderStore *src_folder; - TnyFolderStore *dst_folder; - ModestFolderView *folder_view; - DndHelper *helper; -} DndFolderInfo; - -static void -dnd_folder_info_destroyer (DndFolderInfo *info) -{ - if (info->src_folder) - g_object_unref (info->src_folder); - if (info->dst_folder) - g_object_unref (info->dst_folder); - g_slice_free (DndFolderInfo, info); -} - -static void -dnd_on_connection_failed_destroyer (DndFolderInfo *info, - GtkWindow *parent_window, - TnyAccount *account) -{ - /* Show error */ - modest_ui_actions_on_account_connection_error (parent_window, account); - - /* Free the helper & info */ - dnd_helper_destroyer (info->helper); - dnd_folder_info_destroyer (info); -} - -static void -drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled, - GError *err, - GtkWindow *parent_window, - TnyAccount *account, - gpointer user_data) -{ - DndFolderInfo *info = NULL; - ModestMailOperation *mail_op; - - info = (DndFolderInfo *) user_data; - - if (err || canceled) { - dnd_on_connection_failed_destroyer (info, parent_window, account); - return; - } - - /* Do the mail operation */ - mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window, - modest_ui_actions_move_folder_error_handler, - info->src_folder, NULL); - - modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), - mail_op); - - /* Transfer the folder */ - modest_mail_operation_xfer_folder (mail_op, - TNY_FOLDER (info->src_folder), - info->dst_folder, - info->helper->delete_source, - xfer_folder_cb, - info->helper->folder_view); - - /* Frees */ - g_object_unref (G_OBJECT (mail_op)); - dnd_helper_destroyer (info->helper); - dnd_folder_info_destroyer (info); -} - - -static void -drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled, - GError *err, - GtkWindow *parent_window, - TnyAccount *account, - gpointer user_data) -{ - DndFolderInfo *info = NULL; - - info = (DndFolderInfo *) user_data; - - if (err || canceled) { - dnd_on_connection_failed_destroyer (info, parent_window, account); - return; - } - - /* Connect to source folder and perform the copy/move */ - modest_platform_connect_if_remote_and_perform (NULL, TRUE, - info->src_folder, - drag_and_drop_from_folder_view_src_folder_performer, - info); -} - -/* - * This function is used by drag_data_received_cb to manage drag and - * drop of a folder, i.e, and drag from the folder view to the same - * folder view. - */ -static void -drag_and_drop_from_folder_view (GtkTreeModel *source_model, - GtkTreeModel *dest_model, - GtkTreePath *dest_row, - GtkSelectionData *selection_data, - DndHelper *helper) -{ - GtkTreeIter dest_iter, iter; - TnyFolderStore *dest_folder = NULL; - TnyFolderStore *folder = NULL; - gboolean forbidden = FALSE; - ModestWindow *win; - DndFolderInfo *info = NULL; - - win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */ - if (!win) { - g_warning ("%s: BUG: no main window", __FUNCTION__); - dnd_helper_destroyer (helper); - return; - } - - if (!forbidden) { - /* check the folder rules for the destination */ - folder = tree_path_to_folder (dest_model, dest_row); - if (TNY_IS_FOLDER(folder)) { - ModestTnyFolderRules rules = - modest_tny_folder_get_rules (TNY_FOLDER (folder)); - forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE; - } else if (TNY_IS_FOLDER_STORE(folder)) { - /* enable local root as destination for folders */ - if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) && - !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder))) - forbidden = TRUE; - } - g_object_unref (folder); - } - if (!forbidden) { - /* check the folder rules for the source */ - folder = tree_path_to_folder (source_model, helper->source_row); - if (TNY_IS_FOLDER(folder)) { - ModestTnyFolderRules rules = - modest_tny_folder_get_rules (TNY_FOLDER (folder)); - forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE; - } else - forbidden = TRUE; - g_object_unref (folder); - } - - - /* Check if the drag is possible */ - if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) { - /* Show error */ - modest_platform_run_information_dialog ((GtkWindow *) win, - _("mail_in_ui_folder_move_target_error"), - FALSE); - /* Restore the previous selection */ - folder = tree_path_to_folder (source_model, helper->source_row); - if (folder) { - if (TNY_IS_FOLDER (folder)) - modest_folder_view_select_folder (helper->folder_view, - TNY_FOLDER (folder), FALSE); - g_object_unref (folder); - } - dnd_helper_destroyer (helper); - return; - } - - /* Get data */ - gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row); - gtk_tree_model_get (dest_model, &dest_iter, - INSTANCE_COLUMN, - &dest_folder, -1); - gtk_tree_model_get_iter (source_model, &iter, helper->source_row); - gtk_tree_model_get (source_model, &iter, - INSTANCE_COLUMN, - &folder, -1); - - /* Create the info for the performer */ - info = g_slice_new0 (DndFolderInfo); - info->src_folder = g_object_ref (folder); - info->dst_folder = g_object_ref (dest_folder); - info->helper = helper; - - /* Connect to the destination folder and perform the copy/move */ - modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE, - dest_folder, - drag_and_drop_from_folder_view_dst_folder_performer, - info); - - /* Frees */ - g_object_unref (dest_folder); - g_object_unref (folder); -} - -/* - * This function receives the data set by the "drag-data-get" signal - * handler. This information comes within the #GtkSelectionData. This - * function will manage both the drags of folders of the treeview and - * drags of headers of the header view widget. - */ -static void -on_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint target_type, - guint time, - gpointer data) -{ - GtkWidget *source_widget; - GtkTreeModel *dest_model, *source_model; - GtkTreePath *source_row, *dest_row; - GtkTreeViewDropPosition pos; - gboolean delete_source = FALSE; - gboolean success = FALSE; - - /* Do not allow further process */ - g_signal_stop_emission_by_name (widget, "drag-data-received"); - source_widget = gtk_drag_get_source_widget (context); - - /* Get the action */ - if (context->action == GDK_ACTION_MOVE) { - delete_source = TRUE; - - /* Notify that there is no folder selected. We need to - do this in order to update the headers view (and - its monitors, because when moving, the old folder - won't longer exist. We can not wait for the end of - the operation, because the operation won't start if - the folder is in use */ - if (source_widget == widget) { - GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); - gtk_tree_selection_unselect_all (sel); - } - } - - /* Check if the get_data failed */ - if (selection_data == NULL || selection_data->length < 0) - goto end; - - /* Select the destination model */ - dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); - - /* Get the path to the destination row. Can not call - gtk_tree_view_get_drag_dest_row() because the source row - is not selected anymore */ - gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y, - &dest_row, &pos); - - /* Only allow drops IN other rows */ - if (!dest_row || - pos == GTK_TREE_VIEW_DROP_BEFORE || - pos == GTK_TREE_VIEW_DROP_AFTER) - goto end; - - success = TRUE; - /* Drags from the header view */ - if (source_widget != widget) { - source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget)); - - drag_and_drop_from_header_view (source_model, - dest_model, - dest_row, - selection_data); - } else { - DndHelper *helper = NULL; - - /* Get the source model and row */ - gtk_tree_get_row_drag_data (selection_data, - &source_model, - &source_row); - - /* Create the helper */ - helper = g_slice_new0 (DndHelper); - helper->delete_source = delete_source; - helper->source_row = gtk_tree_path_copy (source_row); - helper->folder_view = MODEST_FOLDER_VIEW (widget); - - drag_and_drop_from_folder_view (source_model, - dest_model, - dest_row, - selection_data, - helper); - - gtk_tree_path_free (source_row); - } - - /* Frees */ - gtk_tree_path_free (dest_row); - - end: - /* Finish the drag and drop */ - gtk_drag_finish (context, success, FALSE, time); -} - -/* - * We define a "drag-drop" signal handler because we do not want to - * use the default one, because the default one always calls - * gtk_drag_finish and we prefer to do it in the "drag-data-received" - * signal handler, because there we have all the information available - * to know if the dnd was a success or not. - */ -static gboolean -drag_drop_cb (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time, - gpointer user_data) -{ - gpointer target; - - if (!context->targets) - return FALSE; - - /* Check if we're dragging a folder row */ - target = gtk_drag_dest_find_target (widget, context, NULL); - - /* Request the data from the source. */ - gtk_drag_get_data(widget, context, target, time); - - return TRUE; -} - -/* - * This function expands a node of a tree view if it's not expanded - * yet. Not sure why it needs the threads stuff, but gtk+`example code - * does that, so that's why they're here. - */ -static gint -expand_row_timeout (gpointer data) -{ - GtkTreeView *tree_view = data; - GtkTreePath *dest_path = NULL; - GtkTreeViewDropPosition pos; - gboolean result = FALSE; - - gdk_threads_enter (); - - gtk_tree_view_get_drag_dest_row (tree_view, - &dest_path, - &pos); - - if (dest_path && - (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) { - gtk_tree_view_expand_row (tree_view, dest_path, FALSE); - gtk_tree_path_free (dest_path); - } - else { - if (dest_path) - gtk_tree_path_free (dest_path); - - result = TRUE; - } - - gdk_threads_leave (); - - return result; -} - -/* - * This function is called whenever the pointer is moved over a widget - * while dragging some data. It installs a timeout that will expand a - * node of the treeview if not expanded yet. This function also calls - * gdk_drag_status in order to set the suggested action that will be - * used by the "drag-data-received" signal handler to know if we - * should do a move or just a copy of the data. - */ -static gboolean -on_drag_motion (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time, - gpointer user_data) -{ - GtkTreeViewDropPosition pos; - GtkTreePath *dest_row; - GtkTreeModel *dest_model; - ModestFolderViewPrivate *priv; - GdkDragAction suggested_action; - gboolean valid_location = FALSE; - TnyFolderStore *folder = NULL; - - priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget); - - if (priv->timer_expander != 0) { - g_source_remove (priv->timer_expander); - priv->timer_expander = 0; - } - - gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), - x, y, - &dest_row, - &pos); - - /* Do not allow drops between folders */ - if (!dest_row || - pos == GTK_TREE_VIEW_DROP_BEFORE || - pos == GTK_TREE_VIEW_DROP_AFTER) { - gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0); - gdk_drag_status(context, 0, time); - valid_location = FALSE; - goto out; - } else { - valid_location = TRUE; - } - - /* Check that the destination folder is writable */ - dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); - folder = tree_path_to_folder (dest_model, dest_row); - if (folder && TNY_IS_FOLDER (folder)) { - ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder)); - - if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) { - valid_location = FALSE; - goto out; - } - } - - /* Expand the selected row after 1/2 second */ - if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) { - priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget); - } - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos); - - /* Select the desired action. By default we pick MOVE */ - suggested_action = GDK_ACTION_MOVE; - - if (context->actions == GDK_ACTION_COPY) - gdk_drag_status(context, GDK_ACTION_COPY, time); - else if (context->actions == GDK_ACTION_MOVE) - gdk_drag_status(context, GDK_ACTION_MOVE, time); - else if (context->actions & suggested_action) - gdk_drag_status(context, suggested_action, time); - else - gdk_drag_status(context, GDK_ACTION_DEFAULT, time); - - out: - if (folder) - g_object_unref (folder); - if (dest_row) { - gtk_tree_path_free (dest_row); - } - g_signal_stop_emission_by_name (widget, "drag-motion"); - - return valid_location; -} - -/* - * This function sets the treeview as a source and a target for dnd - * events. It also connects all the requirede signals. - */ -static void -setup_drag_and_drop (GtkTreeView *self) -{ - /* Set up the folder view as a dnd destination. Set only the - highlight flag, otherwise gtk will have a different - behaviour */ -#ifdef MODEST_TOOLKIT_HILDON2 - return; -#endif - gtk_drag_dest_set (GTK_WIDGET (self), - GTK_DEST_DEFAULT_HIGHLIGHT, - folder_view_drag_types, - G_N_ELEMENTS (folder_view_drag_types), - GDK_ACTION_MOVE | GDK_ACTION_COPY); - - g_signal_connect (G_OBJECT (self), - "drag_data_received", - G_CALLBACK (on_drag_data_received), - NULL); - - - /* Set up the treeview as a dnd source */ - gtk_drag_source_set (GTK_WIDGET (self), - GDK_BUTTON1_MASK, - folder_view_drag_types, - G_N_ELEMENTS (folder_view_drag_types), - GDK_ACTION_MOVE | GDK_ACTION_COPY); - - g_signal_connect (G_OBJECT (self), - "drag_motion", - G_CALLBACK (on_drag_motion), - NULL); - - g_signal_connect (G_OBJECT (self), - "drag_data_get", - G_CALLBACK (on_drag_data_get), - NULL); - - g_signal_connect (G_OBJECT (self), - "drag_drop", - G_CALLBACK (drag_drop_cb), - NULL); -} - -/* - * This function manages the navigation through the folders using the - * keyboard or the hardware keys in the device - */ -static gboolean -on_key_pressed (GtkWidget *self, - GdkEventKey *event, - gpointer user_data) -{ - GtkTreeSelection *selection; - GtkTreeIter iter; - GtkTreeModel *model; - gboolean retval = FALSE; - - /* Up and Down are automatically managed by the treeview */ - if (event->keyval == GDK_Return) { - /* Expand/Collapse the selected row */ - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self)); - if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - GtkTreePath *path; - - path = gtk_tree_model_get_path (model, &iter); - - if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path)) - gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path); - else - gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE); - gtk_tree_path_free (path); - } - /* No further processing */ - retval = TRUE; - } - - return retval; -} - -/* - * We listen to the changes in the local folder account name key, - * because we want to show the right name in the view. The local - * folder account name corresponds to the device name in the Maemo - * version. We do this because we do not want to query gconf on each - * tree view refresh. It's better to cache it and change whenever - * necessary. - */ -static void -on_configuration_key_changed (ModestConf* conf, - const gchar *key, - ModestConfEvent event, - ModestConfNotificationId id, - ModestFolderView *self) -{ - ModestFolderViewPrivate *priv; - - - g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); - priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self); - - if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) { - g_free (priv->local_account_name); - - if (event == MODEST_CONF_EVENT_KEY_UNSET) - priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME); - else - priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(), - MODEST_CONF_DEVICE_NAME, NULL); - - /* Force a redraw */ -#if GTK_CHECK_VERSION(2, 8, 0) - GtkTreeViewColumn * tree_column; - - tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), - NAME_COLUMN); - gtk_tree_view_column_queue_resize (tree_column); -#else - gtk_widget_queue_draw (GTK_WIDGET (self)); -#endif - } -} - -void -modest_folder_view_set_style (ModestFolderView *self, - ModestFolderViewStyle style) -{ - ModestFolderViewPrivate *priv; - - g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self)); - g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL || - style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE); - - priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self); - - - priv->style = style; -} + priv->style = style; +} void modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self, @@ -2976,10 +2921,18 @@ modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *s model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); if (GTK_IS_TREE_MODEL_FILTER (model)) gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model)); + else + modest_folder_view_update_model(self, + (TnyAccountStore *) modest_runtime_get_account_store()); /* 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 * @@ -2994,76 +2947,6 @@ modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *s return (const gchar *) priv->visible_account_id; } -static gboolean -find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter) -{ - do { - GtkTreeIter child; - TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN; - - gtk_tree_model_get (model, iter, - TYPE_COLUMN, - &type, -1); - - gboolean result = FALSE; - if (type == TNY_FOLDER_TYPE_INBOX) { - result = TRUE; - } - if (result) { - *inbox_iter = *iter; - return TRUE; - } - - if (gtk_tree_model_iter_children (model, &child, iter)) { - if (find_inbox_iter (model, &child, inbox_iter)) - return TRUE; - } - - } while (gtk_tree_model_iter_next (model, iter)); - - return FALSE; -} - - - - -void -modest_folder_view_select_first_inbox_or_local (ModestFolderView *self) -{ - GtkTreeModel *model; - GtkTreeIter iter, inbox_iter; - GtkTreeSelection *sel; - GtkTreePath *path = NULL; - - g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self)); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); - if (!model) - return; - - expand_root_items (self); - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self)); - - if (!gtk_tree_model_get_iter_first (model, &iter)) { - g_warning ("%s: model is empty", __FUNCTION__); - return; - } - - if (find_inbox_iter (model, &iter, &inbox_iter)) - path = gtk_tree_model_get_path (model, &inbox_iter); - else - path = gtk_tree_path_new_first (); - - /* Select the row and free */ - gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE); - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0); - gtk_tree_path_free (path); - - /* set focus */ - gtk_widget_grab_focus (GTK_WIDGET(self)); -} - - /* recursive */ static gboolean find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, @@ -3099,62 +2982,6 @@ find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_it return FALSE; } -#ifndef MODEST_TOOLKIT_HILDON2 -static void -on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - ModestFolderView *self) -{ - ModestFolderViewPrivate *priv = NULL; - GtkTreeSelection *sel; - TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN; - GObject *instance = NULL; - - if (!MODEST_IS_FOLDER_VIEW(self)) - return; - - priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); - - priv->reexpand = TRUE; - - gtk_tree_model_get (tree_model, iter, - TYPE_COLUMN, &type, - INSTANCE_COLUMN, &instance, - -1); - - if (!instance) - return; - - if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) { - priv->folder_to_select = g_object_ref (instance); - } - g_object_unref (instance); - - if (priv->folder_to_select) { - - if (!modest_folder_view_select_folder (self, priv->folder_to_select, - FALSE)) { - GtkTreePath *path; - path = gtk_tree_model_get_path (tree_model, iter); - gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path); - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self)); - - gtk_tree_selection_select_iter (sel, iter); - gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE); - - gtk_tree_path_free (path); - } - - /* Disable next */ - modest_folder_view_disable_next_folder_selection (self); - - /* Refilter the model */ - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model)); - } -} -#endif void modest_folder_view_disable_next_folder_selection (ModestFolderView *self) @@ -3273,11 +3100,23 @@ modest_folder_view_copy_model (ModestFolderView *folder_view_src, GtkTreeModel *filter_model = NULL; GtkTreeModel *model = NULL; GtkTreeModel *new_filter_model = NULL; + GtkTreeModel *old_tny_model = NULL; + GtkTreeModel *new_tny_model = NULL; + ModestFolderViewPrivate *dst_priv; g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src)); g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst)); + dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst); + if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model)) + new_tny_model = NULL; + /* Get src model*/ + if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) { + modest_signal_mgr_disconnect (dst_priv->signal_handlers, + G_OBJECT (old_tny_model), + "activity-changed"); + } filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src)); model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model)); @@ -3287,12 +3126,18 @@ modest_folder_view_copy_model (ModestFolderView *folder_view_src, filter_row, folder_view_dst, NULL); + + + /* Set copied model */ gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model); -#ifndef MODEST_TOOLKIT_HILDON2 - g_signal_connect (G_OBJECT(new_filter_model), "row-inserted", - (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst); -#endif + if (new_tny_model) { + dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers, + G_OBJECT (new_tny_model), + "activity-changed", + G_CALLBACK (on_activity_changed), + folder_view_dst); + } /* Free */ g_object_unref (new_filter_model); @@ -3319,6 +3164,22 @@ modest_folder_view_show_non_move_folders (ModestFolderView *folder_view, } } +void +modest_folder_view_show_message_count (ModestFolderView *folder_view, + gboolean show) +{ + ModestFolderViewPrivate* priv; + + g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view)); + + priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view); + priv->show_message_count = show; + + g_object_set (G_OBJECT (priv->messages_renderer), + "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count), + NULL); +} + /* Returns FALSE if it did not selected anything */ static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view, @@ -3397,7 +3258,7 @@ modest_folder_view_set_cell_style (ModestFolderView *self, priv->cell_style = cell_style; g_object_set (G_OBJECT (priv->messages_renderer), - "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT), + "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count), NULL); gtk_widget_queue_draw (GTK_WIDGET (self)); @@ -3407,19 +3268,47 @@ static void update_style (ModestFolderView *self) { ModestFolderViewPrivate *priv; - GdkColor style_color; + GdkColor style_color, style_active_color; + PangoAttrList *attr_list; + GtkStyle *style; + PangoAttribute *attr; g_return_if_fail (MODEST_IS_FOLDER_VIEW (self)); priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); - if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) { - gdk_color_parse ("grey", &style_color); - } + /* Set color */ - g_object_set (G_OBJECT (priv->messages_renderer), - "foreground-gdk", &style_color, - "foreground-set", TRUE, - NULL); + attr_list = pango_attr_list_new (); + + if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) { + gdk_color_parse (MODEST_SECONDARY_COLOR, &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, + "attributes", attr_list, + NULL); + pango_attr_list_unref (attr_list); + } + + if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) { + priv->active_color = style_active_color; + } else { + gdk_color_parse ("000", &(priv->active_color)); + } } static void @@ -3441,10 +3330,189 @@ 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; +} + +gboolean +modest_folder_view_get_activity (ModestFolderView *self) +{ + ModestFolderViewPrivate *priv; + GtkTreeModel *inner_model; + + g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + + if (!get_inner_models (self, NULL, NULL, &inner_model)) + return FALSE; + + if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) { + return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model)); + } else { + return FALSE; + } +} + +static void +on_activity_changed (TnyGtkFolderListStore *store, + gboolean activity, + ModestFolderView *folder_view) +{ + ModestFolderViewPrivate *priv; + + g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); + g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store)); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view); + + g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0, + activity); +} + +TnyList * +modest_folder_view_get_model_tny_list (ModestFolderView *self) +{ + GtkTreeModel *model; + TnyList *ret_value; + + ret_value = NULL; + model = NULL; + + if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) { + ret_value = TNY_LIST (model); + g_object_ref (ret_value); + } + + return ret_value; + +} + +#ifdef MODEST_TOOLKIT_HILDON2 +static gboolean +on_live_search_refilter (HildonLiveSearch *livesearch, + ModestFolderView *self) +{ + GtkTreeModel *filter_model; + + if (get_inner_models (self, &filter_model, NULL, NULL)) + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model)); + + return TRUE; +} + +GtkWidget * +modest_folder_view_setup_live_search (ModestFolderView *self) +{ + ModestFolderViewPrivate *priv; + + g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL); + priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self); + priv->live_search = hildon_live_search_new (); + + g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self); + + return priv->live_search; +} +#endif