f1cfae48c778f117446ca688d4a1ef099723ef39
[modest] / src / maemo / modest-msg-view-window.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <glib/gi18n.h>
30 #include <string.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
33 #include <tny-msg.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-maemo-utils.h>
39 #include <modest-tny-msg.h>
40 #include <modest-msg-view-window.h>
41 #include <modest-attachments-view.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar-widget.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <math.h>
59
60 #define DEFAULT_FOLDER "MyDocs/.documents"
61
62 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
63 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
64 static void  modest_header_view_observer_init(
65                 ModestHeaderViewObserverIface *iface_class);
66 static void  modest_msg_view_window_finalize     (GObject *obj);
67 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
68                                                          gpointer data);
69 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
70                                                         ModestMsgViewWindow *obj);
71 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
72                                                         ModestMsgViewWindow *obj);
73
74 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
75 static void modest_msg_view_window_set_zoom (ModestWindow *window,
76                                              gdouble zoom);
77 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
78 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
79 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
80 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
81                                                           GdkEventKey *event,
82                                                           gpointer userdata);
83 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
84                                                            GdkEventWindowState *event, 
85                                                            gpointer userdata);
86 static void modest_msg_view_window_scroll_up (ModestWindow *window);
87 static void modest_msg_view_window_scroll_down (ModestWindow *window);
88 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
89
90 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
91                                                    gboolean show_toolbar);
92
93 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
94                                                            GdkEvent *event,
95                                                            ModestMsgViewWindow *window);
96 void modest_msg_view_window_on_row_changed(
97                 GtkTreeModel *header_model,
98                 GtkTreePath *arg1,
99                 GtkTreeIter *arg2,
100                 ModestMsgViewWindow *window);
101
102 void modest_msg_view_window_on_row_deleted(
103                 GtkTreeModel *header_model,
104                 GtkTreePath *arg1,
105                 ModestMsgViewWindow *window);
106
107 void modest_msg_view_window_on_row_inserted(
108                 GtkTreeModel *header_model,
109                 GtkTreePath *tree_path,
110                 GtkTreeIter *tree_iter,
111                 ModestMsgViewWindow *window);
112
113 void modest_msg_view_window_on_row_reordered(
114                 GtkTreeModel *header_model,
115                 GtkTreePath *arg1,
116                 GtkTreeIter *arg2,
117                 gpointer arg3,
118                 ModestMsgViewWindow *window);
119
120 void modest_msg_view_window_update_model_replaced(
121                 ModestHeaderViewObserver *window,
122                 GtkTreeModel *model,
123                 const gchar *tny_folder_id);
124
125 static void cancel_progressbar  (GtkToolButton *toolbutton,
126                                  ModestMsgViewWindow *self);
127
128 static void on_queue_changed    (ModestMailOperationQueue *queue,
129                                  ModestMailOperation *mail_op,
130                                  ModestMailOperationQueueNotification type,
131                                  ModestMsgViewWindow *self);
132
133 static void on_account_removed  (TnyAccountStore *account_store, 
134                                  TnyAccount *account,
135                                  gpointer user_data);
136
137 static void on_move_focus (ModestMsgViewWindow *window,
138                            GtkDirectionType direction,
139                            gpointer userdata);
140
141 static void view_msg_cb         (ModestMailOperation *mail_op, 
142                                  TnyHeader *header, 
143                                  TnyMsg *msg, 
144                                  gpointer user_data);
145
146 static void set_toolbar_mode    (ModestMsgViewWindow *self, 
147                                  ModestToolBarModes mode);
148
149 static void update_window_title (ModestMsgViewWindow *window);
150
151 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
152
153
154 /* list my signals */
155 enum {
156         MSG_CHANGED_SIGNAL,
157         LAST_SIGNAL
158 };
159
160 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
161         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
162         { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
163 };
164
165 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
166         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
167         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
168         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
169         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
170         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
171         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
172 };
173
174 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
175 struct _ModestMsgViewWindowPrivate {
176
177         GtkWidget   *msg_view;
178         GtkWidget   *main_scroll;
179         GtkWidget   *find_toolbar;
180         gchar       *last_search;
181
182         /* Progress observers */
183         GtkWidget        *progress_bar;
184         GSList           *progress_widgets;
185
186         /* Tollbar items */
187         GtkWidget   *progress_toolitem;
188         GtkWidget   *cancel_toolitem;
189         GtkWidget   *prev_toolitem;
190         GtkWidget   *next_toolitem;
191         ModestToolBarModes current_toolbar_mode;
192
193         /* Optimized view enabled */
194         gboolean optimized_view;
195
196         /* Whether this was created via the *_new_for_search_result() function. */
197         gboolean is_search_result;
198         
199         /* A reference to the @model of the header view 
200          * to allow selecting previous/next messages,
201          * if the message is currently selected in the header view.
202          */
203         const gchar *header_folder_id;
204         GtkTreeModel *header_model;
205         GtkTreeRowReference *row_reference;
206         GtkTreeRowReference *next_row_reference;
207
208         gulong clipboard_change_handler;
209         gulong queue_change_handler;
210         gulong account_removed_handler;
211         gulong row_changed_handler;
212         gulong row_deleted_handler;
213         gulong row_inserted_handler;
214         gulong rows_reordered_handler;
215
216         guint purge_timeout;
217         GtkWidget *remove_attachment_banner;
218
219         guint progress_bar_timeout;
220
221         gchar *msg_uid;
222 };
223
224 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
225                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
226                                                     ModestMsgViewWindowPrivate))
227 /* globals */
228 static GtkWindowClass *parent_class = NULL;
229
230 /* uncomment the following if you have defined any signals */
231 static guint signals[LAST_SIGNAL] = {0};
232
233 GType
234 modest_msg_view_window_get_type (void)
235 {
236         static GType my_type = 0;
237         if (!my_type) {
238                 static const GTypeInfo my_info = {
239                         sizeof(ModestMsgViewWindowClass),
240                         NULL,           /* base init */
241                         NULL,           /* base finalize */
242                         (GClassInitFunc) modest_msg_view_window_class_init,
243                         NULL,           /* class finalize */
244                         NULL,           /* class data */
245                         sizeof(ModestMsgViewWindow),
246                         1,              /* n_preallocs */
247                         (GInstanceInitFunc) modest_msg_view_window_init,
248                         NULL
249                 };
250                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
251                                                   "ModestMsgViewWindow",
252                                                   &my_info, 0);
253
254                 static const GInterfaceInfo modest_header_view_observer_info = 
255                 {
256                         (GInterfaceInitFunc) modest_header_view_observer_init,
257                         NULL,         /* interface_finalize */
258                         NULL          /* interface_data */
259                 };
260
261                 g_type_add_interface_static (my_type,
262                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
263                                 &modest_header_view_observer_info);
264         }
265         return my_type;
266 }
267
268 static void
269 save_state (ModestWindow *self)
270 {
271         modest_widget_memory_save (modest_runtime_get_conf (),
272                                    G_OBJECT(self), 
273                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
274 }
275
276
277 static void
278 restore_settings (ModestMsgViewWindow *self)
279 {
280         modest_widget_memory_restore (modest_runtime_get_conf (),
281                                       G_OBJECT(self), 
282                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
283 }
284
285 static void
286 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
287 {
288         GObjectClass *gobject_class;
289         ModestWindowClass *modest_window_class;
290         gobject_class = (GObjectClass*) klass;
291         modest_window_class = (ModestWindowClass *) klass;
292
293         parent_class            = g_type_class_peek_parent (klass);
294         gobject_class->finalize = modest_msg_view_window_finalize;
295
296         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
297         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
298         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
299         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
300         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
301         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
302
303         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
304
305         modest_window_class->save_state_func = save_state;
306
307         signals[MSG_CHANGED_SIGNAL] =
308                 g_signal_new ("msg-changed",
309                               G_TYPE_FROM_CLASS (gobject_class),
310                               G_SIGNAL_RUN_FIRST,
311                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
312                               NULL, NULL,
313                               modest_marshal_VOID__POINTER_POINTER,
314                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
315 }
316
317 static void modest_header_view_observer_init(
318                 ModestHeaderViewObserverIface *iface_class)
319 {
320         iface_class->update_func = modest_msg_view_window_update_model_replaced;
321 }
322
323 static void
324 modest_msg_view_window_init (ModestMsgViewWindow *obj)
325 {
326         ModestMsgViewWindowPrivate *priv;
327         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
328
329         priv->is_search_result = FALSE;
330
331         priv->msg_view      = NULL;
332         priv->header_model  = NULL;
333         priv->header_folder_id  = NULL;
334         priv->clipboard_change_handler = 0;
335         priv->queue_change_handler = 0;
336         priv->account_removed_handler = 0;
337         priv->row_changed_handler = 0;
338         priv->row_deleted_handler = 0;
339         priv->row_inserted_handler = 0;
340         priv->rows_reordered_handler = 0;
341         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
342
343         priv->optimized_view  = FALSE;
344         priv->progress_bar_timeout = 0;
345         priv->purge_timeout = 0;
346         priv->remove_attachment_banner = NULL;
347         priv->msg_uid = NULL;
348         
349         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
350                                             GTK_WINDOW(obj),"applications_email_viewer");
351 }
352
353
354 static gboolean
355 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
356 {
357         ModestMsgViewWindowPrivate *priv = NULL;
358         
359         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
360
361         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
362
363         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
364         
365         if (priv->progress_bar_timeout > 0) {
366                 g_source_remove (priv->progress_bar_timeout);
367                 priv->progress_bar_timeout = 0;
368         }
369         
370         return FALSE;
371 }
372
373 static void 
374 set_toolbar_mode (ModestMsgViewWindow *self, 
375                   ModestToolBarModes mode)
376 {
377         ModestWindowPrivate *parent_priv;
378         ModestMsgViewWindowPrivate *priv;
379 /*      GtkWidget *widget = NULL; */
380
381         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
382
383         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
384         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
385                         
386         /* Sets current toolbar mode */
387         priv->current_toolbar_mode = mode;
388
389         /* Update toolbar dimming state */
390         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
391
392         switch (mode) {
393         case TOOLBAR_MODE_NORMAL:               
394 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
395 /*              gtk_action_set_sensitive (widget, TRUE); */
396 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
397 /*              gtk_action_set_sensitive (widget, TRUE); */
398 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
399 /*              gtk_action_set_sensitive (widget, TRUE); */
400 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
401 /*              gtk_action_set_sensitive (widget, TRUE); */
402
403                 if (priv->prev_toolitem)
404                         gtk_widget_show (priv->prev_toolitem);
405                 
406                 if (priv->next_toolitem)
407                         gtk_widget_show (priv->next_toolitem);
408                         
409                 if (priv->progress_toolitem)
410                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
411                 if (priv->progress_bar)
412                         gtk_widget_hide (priv->progress_bar);
413                         
414                 if (priv->cancel_toolitem)
415                         gtk_widget_hide (priv->cancel_toolitem);
416
417                 /* Hide toolbar if optimized view is enabled */
418                 if (priv->optimized_view) {
419                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
420                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
421                 }
422
423                 break;
424         case TOOLBAR_MODE_TRANSFER:
425 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
426 /*              gtk_action_set_sensitive (widget, FALSE); */
427 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
428 /*              gtk_action_set_sensitive (widget, FALSE); */
429 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
430 /*              gtk_action_set_sensitive (widget, FALSE); */
431 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
432 /*              gtk_action_set_sensitive (widget, FALSE); */
433
434                 if (priv->prev_toolitem)
435                         gtk_widget_hide (priv->prev_toolitem);
436                 
437                 if (priv->next_toolitem)
438                         gtk_widget_hide (priv->next_toolitem);
439                 
440                 if (priv->progress_toolitem)
441                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
442                 if (priv->progress_bar)
443                         gtk_widget_show (priv->progress_bar);
444                         
445                 if (priv->cancel_toolitem)
446                         gtk_widget_show (priv->cancel_toolitem);
447
448                 /* Show toolbar if it's hiden (optimized view ) */
449                 if (priv->optimized_view) {
450                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
451                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
452                 }
453
454                 break;
455         default:
456                 g_return_if_reached ();
457         }
458
459 }
460
461
462 static GtkWidget *
463 menubar_to_menu (GtkUIManager *ui_manager)
464 {
465         GtkWidget *main_menu;
466         GtkWidget *menubar;
467         GList *iter, *children;
468
469         /* Create new main menu */
470         main_menu = gtk_menu_new();
471
472         /* Get the menubar from the UI manager */
473         menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
474
475         iter = children = gtk_container_get_children (GTK_CONTAINER (menubar));
476         while (iter) {
477                 GtkWidget *menu;
478
479                 menu = GTK_WIDGET (iter->data);
480                 gtk_widget_reparent(menu, main_menu);
481                 
482                 iter = g_list_next (iter);
483         }
484         g_list_free (children);
485                      
486         return main_menu;
487 }
488
489 static void
490 init_window (ModestMsgViewWindow *obj)
491 {
492         GtkWidget *main_vbox;
493         ModestMsgViewWindowPrivate *priv;
494         ModestWindowPrivate *parent_priv;
495         ModestConf *conf;
496         GtkAction *action = NULL;
497
498         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
499         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
500
501         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
502         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
503         main_vbox = gtk_vbox_new  (FALSE, 6);
504
505         /* Menubar */
506         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
507         conf = modest_runtime_get_conf ();
508         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
509                                             "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
510         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
511                                       modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
512         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
513                                             "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
514         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
515                                       modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
516         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
517         gtk_widget_show (GTK_WIDGET(parent_priv->menubar));
518
519 #ifdef MODEST_USE_MOZEMBED
520         priv->main_scroll = priv->msg_view;
521         gtk_widget_set_size_request (priv->msg_view, -1, 1600);
522 #else
523         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
524         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
525 #endif
526         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
527         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
528         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
529
530         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
531         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
532
533         priv->find_toolbar = hildon_find_toolbar_new (NULL);
534         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
535         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
536         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
537         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
538         
539         priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
540         gtk_widget_show_all (GTK_WIDGET(main_vbox));
541 }
542
543 static void
544 modest_msg_view_window_disconnect_signals (ModestWindow *self)
545 {
546         ModestMsgViewWindowPrivate *priv;
547         ModestHeaderView *header_view = NULL;
548         ModestWindow *main_window = NULL;
549         
550         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
551
552         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
553             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
554                                            priv->clipboard_change_handler)) 
555                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
556                                              priv->clipboard_change_handler);
557
558         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
559                                            priv->queue_change_handler))
560                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
561                                              priv->queue_change_handler);
562
563         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
564                                            priv->account_removed_handler))
565                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
566                                              priv->account_removed_handler);
567
568         if (priv->header_model) {
569                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
570                                                   priv->row_changed_handler))
571                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
572                                                     priv->row_changed_handler);
573                 
574                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
575                                                   priv->row_deleted_handler))
576                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
577                                              priv->row_deleted_handler);
578                 
579                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
580                                                   priv->row_inserted_handler))
581                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
582                                                     priv->row_inserted_handler);
583                 
584                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
585                                                   priv->rows_reordered_handler))
586                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
587                                                     priv->rows_reordered_handler);
588         }
589
590         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
591                                                          FALSE); /* don't create */
592         if (!main_window)
593                 return;
594         
595         header_view = MODEST_HEADER_VIEW(
596                         modest_main_window_get_child_widget(
597                                 MODEST_MAIN_WINDOW(main_window),
598                                 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
599         if (header_view == NULL)
600                 return;
601         
602         modest_header_view_remove_observer(header_view,
603                         MODEST_HEADER_VIEW_OBSERVER(self));
604 }       
605
606 static void
607 modest_msg_view_window_finalize (GObject *obj)
608 {
609         ModestMsgViewWindowPrivate *priv;
610
611         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
612
613         /* Sanity check: shouldn't be needed, the window mgr should
614            call this function before */
615         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
616
617         if (priv->header_model != NULL) {
618                 g_object_unref (priv->header_model);
619                 priv->header_model = NULL;
620         }
621
622         if (priv->progress_bar_timeout > 0) {
623                 g_source_remove (priv->progress_bar_timeout);
624                 priv->progress_bar_timeout = 0;
625         }
626
627         if (priv->remove_attachment_banner) {
628                 gtk_widget_destroy (priv->remove_attachment_banner);
629                 g_object_unref (priv->remove_attachment_banner);
630                 priv->remove_attachment_banner = NULL;
631         }
632
633         if (priv->purge_timeout > 0) {
634                 g_source_remove (priv->purge_timeout);
635                 priv->purge_timeout = 0;
636         }
637
638         if (priv->row_reference) {
639                 gtk_tree_row_reference_free (priv->row_reference);
640                 priv->row_reference = NULL;
641         }
642
643         if (priv->next_row_reference) {
644                 gtk_tree_row_reference_free (priv->next_row_reference);
645                 priv->next_row_reference = NULL;
646         }
647
648         if (priv->msg_uid) {
649                 g_free (priv->msg_uid);
650                 priv->msg_uid = NULL;
651         }
652
653         G_OBJECT_CLASS(parent_class)->finalize (obj);
654 }
655
656 static gboolean
657 select_next_valid_row (GtkTreeModel *model,
658                        GtkTreeRowReference **row_reference,
659                        gboolean cycle)
660 {
661         GtkTreeIter tmp_iter;
662         GtkTreePath *path, *next;
663         gboolean retval = FALSE;
664
665         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
666
667         path = gtk_tree_row_reference_get_path (*row_reference);
668         gtk_tree_model_get_iter (model, &tmp_iter, path);
669         gtk_tree_row_reference_free (*row_reference);
670         *row_reference = NULL;
671
672         if (gtk_tree_model_iter_next (model, &tmp_iter)) {
673                 next = gtk_tree_model_get_path (model, &tmp_iter);
674                 *row_reference = gtk_tree_row_reference_new (model, next);
675                 retval = TRUE;
676         } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
677                 next = gtk_tree_model_get_path (model, &tmp_iter);
678
679                 /* Ensure that we are not selecting the same */
680                 if (gtk_tree_path_compare (path, next) != 0) {
681                         *row_reference = gtk_tree_row_reference_new (model, next);
682                         retval = TRUE;
683                 }
684         }
685
686         /* Free */
687         gtk_tree_path_free (path);
688
689         return retval;
690 }
691
692 /* TODO: This should be in _init(), with the parameters as properties. */
693 static void
694 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
695                             const gchar *modest_account_name,
696                             const gchar *msg_uid)
697 {
698         GObject *obj = NULL;
699         ModestMsgViewWindowPrivate *priv = NULL;
700         ModestWindowPrivate *parent_priv = NULL;
701         ModestDimmingRulesGroup *menu_rules_group = NULL;
702         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
703         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
704         GtkActionGroup *action_group = NULL;
705         GError *error = NULL;
706         GdkPixbuf *window_icon;
707
708         obj = G_OBJECT (self);
709         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
710         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
711
712         priv->msg_uid = g_strdup (msg_uid);
713
714         parent_priv->ui_manager = gtk_ui_manager_new();
715         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
716
717         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
718         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
719
720         menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
721         toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
722         clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
723
724         /* Add common actions */
725         gtk_action_group_add_actions (action_group,
726                                       modest_action_entries,
727                                       G_N_ELEMENTS (modest_action_entries),
728                                       obj);
729         gtk_action_group_add_toggle_actions (action_group,
730                                              modest_toggle_action_entries,
731                                              G_N_ELEMENTS (modest_toggle_action_entries),
732                                              obj);
733         gtk_action_group_add_toggle_actions (action_group,
734                                              msg_view_toggle_action_entries,
735                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
736                                              obj);
737         gtk_action_group_add_radio_actions (action_group,
738                                             msg_view_zoom_action_entries,
739                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
740                                             100,
741                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
742                                             obj);
743
744         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
745         g_object_unref (action_group);
746
747         /* Load the UI definition */
748         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
749                                          &error);
750         if (error) {
751                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
752                 g_error_free (error);
753                 error = NULL;
754         }
755         /* ****** */
756
757         /* Add common dimming rules */
758         modest_dimming_rules_group_add_rules (menu_rules_group, 
759                                               modest_msg_view_menu_dimming_entries,
760                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
761                                               MODEST_WINDOW (self));
762         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
763                                               modest_msg_view_toolbar_dimming_entries,
764                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
765                                               MODEST_WINDOW (self));
766         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
767                                               modest_msg_view_clipboard_dimming_entries,
768                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
769                                               MODEST_WINDOW (self));
770
771         /* Insert dimming rules group for this window */
772         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
773         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
774         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
775         g_object_unref (menu_rules_group);
776         g_object_unref (toolbar_rules_group);
777         g_object_unref (clipboard_rules_group);
778
779         /* Add accelerators */
780         gtk_window_add_accel_group (GTK_WINDOW (obj), 
781                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
782         
783         /* Init window */
784         init_window (MODEST_MSG_VIEW_WINDOW(obj));
785         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
786         
787         /* Set window icon */
788         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON); 
789         if (window_icon) {
790                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
791                 g_object_unref (window_icon);
792         }
793
794         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
795
796         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
797                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
798         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
799                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
800         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
801                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
802         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
803                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
804         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
805                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
806
807         g_signal_connect (G_OBJECT (obj), "key-release-event",
808                           G_CALLBACK (modest_msg_view_window_key_release_event),
809                           NULL);
810
811         g_signal_connect (G_OBJECT (obj), "window-state-event",
812                           G_CALLBACK (modest_msg_view_window_window_state_event),
813                           NULL);
814
815         g_signal_connect (G_OBJECT (obj), "move-focus",
816                           G_CALLBACK (on_move_focus), obj);
817
818         /* Mail Operation Queue */
819         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
820                                                        "queue-changed",
821                                                        G_CALLBACK (on_queue_changed),
822                                                        obj);
823
824         /* Account manager */
825         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
826                                                           "account_removed",
827                                                           G_CALLBACK(on_account_removed),
828                                                           obj);
829
830         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
831
832         priv->last_search = NULL;
833
834         /* Init the clipboard actions dim status */
835         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
836
837         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
838
839         /* Check toolbar dimming rules */
840         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
841         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
842
843 }
844
845 /* FIXME: parameter checks */
846 ModestWindow *
847 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
848                                               const gchar *modest_account_name,
849                                               const gchar *msg_uid,
850                                               GtkTreeModel *model, 
851                                               GtkTreeRowReference *row_reference)
852 {
853         ModestMsgViewWindow *window = NULL;
854         ModestMsgViewWindowPrivate *priv = NULL;
855         TnyFolder *header_folder = NULL;
856         ModestHeaderView *header_view = NULL;
857         ModestWindow *main_window = NULL;
858         
859         window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
860         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
861
862         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
863
864         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
865
866         /* Remember the message list's TreeModel so we can detect changes 
867          * and change the list selection when necessary: */
868
869         main_window = modest_window_mgr_get_main_window(
870                 modest_runtime_get_window_mgr(), FALSE); /* don't create */
871         if (!main_window) {
872                 g_warning ("%s: BUG: no main window", __FUNCTION__);
873                 return NULL;
874         }
875         
876         header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
877                                                  MODEST_MAIN_WINDOW(main_window),
878                                                  MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
879         if (header_view != NULL){
880                 header_folder = modest_header_view_get_folder(header_view);
881                 g_assert(header_folder != NULL);
882                 priv->header_folder_id = tny_folder_get_id(header_folder);
883                 g_assert(priv->header_folder_id != NULL);
884                 g_object_unref(header_folder);
885         }
886
887         priv->header_model = g_object_ref(model);
888         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
889         priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
890         select_next_valid_row (model, &(priv->next_row_reference), TRUE);
891
892         priv->row_changed_handler = g_signal_connect(
893                         GTK_TREE_MODEL(model), "row-changed",
894                         G_CALLBACK(modest_msg_view_window_on_row_changed),
895                         window);
896         priv->row_deleted_handler = g_signal_connect(
897                         GTK_TREE_MODEL(model), "row-deleted",
898                         G_CALLBACK(modest_msg_view_window_on_row_deleted),
899                         window);
900         priv->row_inserted_handler = g_signal_connect (
901                         GTK_TREE_MODEL(model), "row-inserted",
902                         G_CALLBACK(modest_msg_view_window_on_row_inserted),
903                         window);
904         priv->rows_reordered_handler = g_signal_connect(
905                         GTK_TREE_MODEL(model), "rows-reordered",
906                         G_CALLBACK(modest_msg_view_window_on_row_reordered),
907                         window);
908
909         if (header_view != NULL){
910                 modest_header_view_add_observer(header_view,
911                                 MODEST_HEADER_VIEW_OBSERVER(window));
912         }
913
914         gtk_widget_show_all (GTK_WIDGET (window));
915
916         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
917
918         modest_msg_view_window_update_priority (window);
919
920         /* Check toolbar dimming rules */
921         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
922
923         return MODEST_WINDOW(window);
924 }
925
926 ModestWindow *
927 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
928                                               const gchar *modest_account_name,
929                                               const gchar *msg_uid)
930 {
931         ModestMsgViewWindow *window = NULL;
932         ModestMsgViewWindowPrivate *priv = NULL;
933
934         window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
935         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
936         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
937
938         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
939
940         /* Remember that this is a search result, 
941          * so we can disable some UI appropriately: */
942         priv->is_search_result = TRUE;
943
944         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
945
946         return MODEST_WINDOW(window);
947 }
948
949 ModestWindow *
950 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
951                             const gchar *modest_account_name,
952                             const gchar *msg_uid)
953 {
954         GObject *obj = NULL;
955         ModestMsgViewWindowPrivate *priv;
956         g_return_val_if_fail (msg, NULL);
957         
958         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
959         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
960         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
961                 modest_account_name, msg_uid);
962
963         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
964
965         return MODEST_WINDOW(obj);
966 }
967
968 void modest_msg_view_window_on_row_changed(
969                 GtkTreeModel *header_model,
970                 GtkTreePath *arg1,
971                 GtkTreeIter *arg2,
972                 ModestMsgViewWindow *window){
973         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
974 }
975
976 void modest_msg_view_window_on_row_deleted(
977                 GtkTreeModel *header_model,
978                 GtkTreePath *arg1,
979                 ModestMsgViewWindow *window){
980         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
981 }
982
983 /* On insertions we check if the folder still has the message we are
984  * showing or do not. If do not, we do nothing. Which means we are still
985  * not attached to any header folder and thus next/prev buttons are
986  * still dimmed. Once the message that is shown by msg-view is found, the
987  * new model of header-view will be attached and the references will be set.
988  * On each further insertions dimming rules will be checked. However
989  * this requires extra CPU time at least works.
990  * (An message might be deleted from TnyFolder and thus will not be
991  * inserted into the model again for example if it is removed by the
992  * imap server and the header view is refreshed.)
993  */
994 void modest_msg_view_window_on_row_inserted(
995                 GtkTreeModel *new_model,
996                 GtkTreePath *tree_path,
997                 GtkTreeIter *tree_iter,
998                 ModestMsgViewWindow *window){
999         ModestMsgViewWindowPrivate *priv = NULL; 
1000         TnyHeader *header = NULL;
1001         gchar *uid = NULL;
1002
1003         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1004
1005         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1006         
1007         /* If we already has a model attached then the message shown by
1008          * msg-view is in it, and thus we do not need any actions but
1009          * to check the dimming rules.*/
1010         if(priv->header_model != NULL){
1011                 gtk_tree_row_reference_free(priv->next_row_reference);
1012                 priv->next_row_reference = gtk_tree_row_reference_copy(
1013                                 priv->row_reference);
1014                 select_next_valid_row (priv->header_model,
1015                                 &(priv->next_row_reference), FALSE);
1016                 modest_ui_actions_check_toolbar_dimming_rules (
1017                                 MODEST_WINDOW (window));
1018                 return;
1019         }
1020
1021         /* Check if the newly inserted message is the same we are actually
1022          * showing. IF not, we should remain detached from the header model
1023          * and thus prev and next toolbarbuttons should remain dimmed. */
1024         gtk_tree_model_get (new_model, tree_iter, 
1025                         TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1026         uid = modest_tny_folder_get_header_unique_id(header);
1027         if(!g_str_equal(priv->msg_uid, uid)){
1028                 g_free(uid);
1029                 return;
1030         }
1031         g_free(uid);
1032
1033         /* Setup row_reference for the actual msg. */
1034         priv->row_reference = gtk_tree_row_reference_new(
1035                         new_model, tree_path);
1036         if(priv->row_reference == NULL){
1037                 g_warning("No reference for msg header item.");
1038                 return;
1039         }
1040
1041         /* Attach new_model and connect some callback to it to become able
1042          * to detect changes in header-view. */
1043         priv->header_model = g_object_ref(new_model);
1044         g_signal_connect (new_model, "row-changed",
1045                         G_CALLBACK (modest_msg_view_window_on_row_changed),
1046                         window);
1047         g_signal_connect (new_model, "row-deleted",
1048                         G_CALLBACK (modest_msg_view_window_on_row_deleted),
1049                         window);
1050         g_signal_connect (new_model, "rows-reordered",
1051                         G_CALLBACK (modest_msg_view_window_on_row_reordered),
1052                         window);
1053
1054         /* Now set up next_row_reference. */
1055         priv->next_row_reference = gtk_tree_row_reference_copy(
1056                         priv->row_reference);
1057         select_next_valid_row (priv->header_model,
1058                         &(priv->next_row_reference), FALSE);
1059
1060         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1061 }
1062
1063 void modest_msg_view_window_on_row_reordered(
1064                 GtkTreeModel *header_model,
1065                 GtkTreePath *arg1,
1066                 GtkTreeIter *arg2,
1067                 gpointer arg3,
1068                 ModestMsgViewWindow *window){
1069         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1070 }
1071
1072 /* The modest_msg_view_window_update_model_replaced implements update
1073  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1074  * actually belongs to the header-view is the same as the TnyFolder of
1075  * the message of msg-view or not. If they are different, there is
1076  * nothing to do. If they are the same, then the model has replaced and
1077  * the reference in msg-view shall be replaced from the old model to
1078  * the new model. In this case the view will be detached from it's
1079  * header folder. From this point the next/prev buttons are dimmed.
1080  */
1081 void modest_msg_view_window_update_model_replaced(
1082                 ModestHeaderViewObserver *observer,
1083                 GtkTreeModel *model,
1084                 const gchar *tny_folder_id){
1085         ModestMsgViewWindowPrivate *priv = NULL; 
1086         ModestMsgViewWindow *window = NULL;
1087
1088         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1089         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1090
1091         window = MODEST_MSG_VIEW_WINDOW(observer);
1092         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1093
1094         /* If there is an other folder in the header-view then we do
1095          * not care about it's model (msg list). Else if the
1096          * header-view shows the folder the msg shown by us is in, we
1097          * shall replace our model reference and make some check. */
1098         if(tny_folder_id == NULL ||
1099                         !g_str_equal(tny_folder_id, priv->header_folder_id))
1100                 return;
1101
1102         /* Model is changed(replaced), so we should forget the old
1103          * one. Because there might be other references and there
1104          * might be some change on the model even if we unreferenced
1105          * it, we need to disconnect our signals here. */
1106         if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1107                                            priv->row_changed_handler))
1108                 g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1109                                              priv->row_changed_handler);
1110         priv->row_changed_handler = 0;
1111         if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1112                                            priv->row_deleted_handler))
1113                 g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1114                                              priv->row_deleted_handler);
1115         priv->row_deleted_handler = 0;
1116         if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1117                                            priv->row_inserted_handler))
1118                 g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1119                                              priv->row_inserted_handler);
1120         priv->row_inserted_handler = 0;
1121         if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1122                                            priv->rows_reordered_handler))
1123                 g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1124                                              priv->rows_reordered_handler);
1125         priv->rows_reordered_handler = 0;
1126         g_object_unref(priv->header_model);
1127         priv->header_model = NULL;
1128         g_object_unref(priv->row_reference);
1129         priv->row_reference = NULL;
1130         g_object_unref(priv->next_row_reference);
1131         priv->next_row_reference = NULL;
1132
1133         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1134
1135         if(tny_folder_id == NULL)
1136                 return;
1137
1138         g_assert(model != NULL);
1139
1140         /* Also we must connect to the new model for row insertions.
1141          * Only for insertions now. We will need other ones only after
1142          * the msg is show by msg-view is added to the new model. */
1143         priv->row_inserted_handler = g_signal_connect (
1144                         model, "row-inserted",
1145                         G_CALLBACK(modest_msg_view_window_on_row_inserted),
1146                         window);
1147 }
1148
1149 gboolean 
1150 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1151 {
1152         ModestMsgViewWindowPrivate *priv= NULL; 
1153
1154         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1155         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1156
1157         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1158 }
1159
1160 TnyHeader*
1161 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1162 {
1163         ModestMsgViewWindowPrivate *priv= NULL; 
1164         TnyMsg *msg = NULL;
1165         TnyHeader *header = NULL;
1166         GtkTreePath *path = NULL;
1167         GtkTreeIter iter;
1168
1169         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1170         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1171
1172         /* If the message was not obtained from a treemodel,
1173          * for instance if it was opened directly by the search UI:
1174          */
1175         if (priv->header_model == NULL) {
1176                 msg = modest_msg_view_window_get_message (self);
1177                 if (msg) {
1178                         header = tny_msg_get_header (msg);
1179                         g_object_unref (msg);
1180                 }
1181                 return header;
1182         }
1183
1184         /* Get iter of the currently selected message in the header view: */
1185         /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1186          * instead of sometimes retrieving it from the header view?
1187          * Then we wouldn't be dependent on the message actually still being selected 
1188          * in the header view. murrayc. */
1189         if (!gtk_tree_row_reference_valid (priv->row_reference))
1190                 return NULL;
1191         path = gtk_tree_row_reference_get_path (priv->row_reference);
1192         g_return_val_if_fail (path != NULL, NULL);
1193         gtk_tree_model_get_iter (priv->header_model, 
1194                                  &iter, 
1195                                  path);
1196
1197         /* Get current message header */
1198         gtk_tree_model_get (priv->header_model, &iter, 
1199                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1200                             &header, -1);
1201
1202         gtk_tree_path_free (path);
1203         return header;
1204 }
1205
1206 TnyMsg*
1207 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1208 {
1209         ModestMsgView *msg_view;
1210         ModestMsgViewWindowPrivate *priv;
1211
1212         g_return_val_if_fail (self, NULL);
1213
1214         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1215
1216         msg_view = MODEST_MSG_VIEW (priv->msg_view);
1217
1218         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1219 }
1220
1221 const gchar*
1222 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1223 {
1224         ModestMsgViewWindowPrivate *priv;
1225
1226         g_return_val_if_fail (self, NULL);
1227         
1228         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1229
1230         return (const gchar*) priv->msg_uid;
1231 }
1232
1233 static void 
1234 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1235                                             gpointer data)
1236 {
1237         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1238         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1239         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1240         gboolean is_active;
1241         GtkAction *action;
1242
1243         is_active = gtk_toggle_action_get_active (toggle);
1244
1245         if (is_active) {
1246                 gtk_widget_show (priv->find_toolbar);
1247                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1248         } else {
1249                 gtk_widget_hide (priv->find_toolbar);
1250         }
1251
1252         /* update the toggle buttons status */
1253         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1254         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1255         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1256         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1257         
1258 }
1259
1260 static void
1261 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1262                                            ModestMsgViewWindow *obj)
1263 {
1264         GtkToggleAction *toggle;
1265         ModestWindowPrivate *parent_priv;
1266         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1267         
1268         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1269         gtk_toggle_action_set_active (toggle, FALSE);
1270 }
1271
1272 static void
1273 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1274                                            ModestMsgViewWindow *obj)
1275 {
1276         gchar *current_search;
1277         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1278
1279         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1280                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1281                 return;
1282         }
1283
1284         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1285
1286         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1287                 g_free (current_search);
1288                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1289                 return;
1290         }
1291
1292         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1293                 gboolean result;
1294                 g_free (priv->last_search);
1295                 priv->last_search = g_strdup (current_search);
1296                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1297                                                      priv->last_search);
1298                 if (!result) {
1299                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1300                         g_free (priv->last_search);
1301                         priv->last_search = NULL;
1302                 } else {
1303                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1304                 }
1305         } else {
1306                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1307                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1308                         g_free (priv->last_search);
1309                         priv->last_search = NULL;
1310                 } else {
1311                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1312                 }
1313         }
1314         
1315         g_free (current_search);
1316                 
1317 }
1318
1319 static void
1320 modest_msg_view_window_set_zoom (ModestWindow *window,
1321                                  gdouble zoom)
1322 {
1323         ModestMsgViewWindowPrivate *priv;
1324         ModestWindowPrivate *parent_priv;
1325         GtkAction *action = NULL;
1326         gint int_zoom = (gint) rint (zoom*100.0+0.1);
1327      
1328         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1329
1330         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1331         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1332         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1333
1334         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1335                                             "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1336
1337         gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1338 }
1339
1340 static gdouble
1341 modest_msg_view_window_get_zoom (ModestWindow *window)
1342 {
1343         ModestMsgViewWindowPrivate *priv;
1344      
1345         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1346
1347         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1348         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1349 }
1350
1351 static gboolean
1352 modest_msg_view_window_zoom_plus (ModestWindow *window)
1353 {
1354         ModestWindowPrivate *parent_priv;
1355         GtkRadioAction *zoom_radio_action;
1356         GSList *group, *node;
1357
1358         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1359         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1360                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1361
1362         group = gtk_radio_action_get_group (zoom_radio_action);
1363
1364         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1365                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1366                 return FALSE;
1367         }
1368
1369         for (node = group; node != NULL; node = g_slist_next (node)) {
1370                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1371                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1372                         return TRUE;
1373                 }
1374         }
1375         return FALSE;
1376 }
1377
1378 static gboolean
1379 modest_msg_view_window_zoom_minus (ModestWindow *window)
1380 {
1381         ModestWindowPrivate *parent_priv;
1382         GtkRadioAction *zoom_radio_action;
1383         GSList *group, *node;
1384
1385         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1386         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1387                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1388
1389         group = gtk_radio_action_get_group (zoom_radio_action);
1390
1391         for (node = group; node != NULL; node = g_slist_next (node)) {
1392                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1393                         if (node->next != NULL) {
1394                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1395                                 return TRUE;
1396                         } else {
1397                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1398                                 return FALSE;
1399                         }
1400                         break;
1401                 }
1402         }
1403         return FALSE;
1404 }
1405
1406 static gboolean
1407 modest_msg_view_window_key_release_event (GtkWidget *window,
1408                                           GdkEventKey *event,
1409                                           gpointer userdata)
1410 {
1411         if (event->type == GDK_KEY_RELEASE) {
1412                 switch (event->keyval) {
1413                 case GDK_Up:
1414                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1415                         return TRUE;
1416                         break;
1417                 case GDK_Down:
1418                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1419                         return TRUE;
1420                         break;
1421                 default:
1422                         return FALSE;
1423                         break;
1424                 };
1425         } else {
1426                 return FALSE;
1427         }
1428 }
1429
1430 static void
1431 modest_msg_view_window_scroll_up (ModestWindow *window)
1432 {
1433         ModestMsgViewWindowPrivate *priv;
1434         gboolean return_value;
1435
1436         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1437         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1438 }
1439
1440 static void
1441 modest_msg_view_window_scroll_down (ModestWindow *window)
1442 {
1443         ModestMsgViewWindowPrivate *priv;
1444         gboolean return_value;
1445
1446         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1447         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1448 }
1449
1450 gboolean
1451 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1452 {
1453         GtkTreePath *path;
1454         ModestMsgViewWindowPrivate *priv;
1455         GtkTreeIter tmp_iter;
1456         gboolean is_last_selected;
1457
1458         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1459         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1460
1461         /*if no model (so no rows at all), then virtually we are the last*/
1462         if (!priv->header_model)
1463                 return TRUE;
1464
1465         path = gtk_tree_row_reference_get_path (priv->row_reference);
1466         if (path == NULL)
1467                 return TRUE;
1468
1469         is_last_selected = TRUE;
1470         while (is_last_selected) {
1471                 TnyHeader *header;
1472                 gtk_tree_path_next (path);
1473                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1474                         break;
1475                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1476                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1477                                 &header, -1);
1478                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1479                         is_last_selected = FALSE;
1480         }
1481         gtk_tree_path_free (path);
1482         return is_last_selected;
1483 }
1484
1485 gboolean
1486 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1487 {
1488         ModestMsgViewWindowPrivate *priv;
1489
1490         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1491         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492
1493         return priv->header_model != NULL;
1494 }
1495
1496 gboolean
1497 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1498 {
1499         ModestMsgViewWindowPrivate *priv;
1500
1501         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1502         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1503
1504         return priv->is_search_result;
1505 }
1506
1507 gboolean
1508 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1509 {
1510         GtkTreePath *path;
1511         ModestMsgViewWindowPrivate *priv;
1512         gboolean is_first_selected;
1513         GtkTreeIter tmp_iter;
1514 /*      gchar * path_string;*/
1515
1516         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1517         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518
1519         /*if no model (so no rows at all), then virtually we are the first*/
1520         if (!priv->header_model)
1521                 return TRUE;
1522
1523         path = gtk_tree_row_reference_get_path (priv->row_reference);
1524         if (!path)
1525                 return TRUE;
1526
1527 /*      path_string = gtk_tree_path_to_string (path);
1528         is_first_selected = strcmp (path_string, "0");
1529
1530         g_free (path_string);
1531         gtk_tree_path_free (path);
1532
1533         return is_first_selected;*/
1534
1535         is_first_selected = TRUE;
1536         while (is_first_selected) {
1537                 TnyHeader *header;
1538                 if(!gtk_tree_path_prev (path))
1539                         break;
1540                 /* Here the 'if' is needless for logic, but let make sure
1541                  * iter is valid for gtk_tree_model_get. */
1542                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1543                         break;
1544                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1545                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1546                                 &header, -1);
1547                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1548                         is_first_selected = FALSE;
1549         }
1550         gtk_tree_path_free (path);
1551         return is_first_selected;
1552 }
1553
1554 /**
1555  * Reads the message whose summary item is @header. It takes care of
1556  * several things, among others:
1557  *
1558  * If the message was not previously downloaded then ask the user
1559  * before downloading. If there is no connection launch the connection
1560  * dialog. Update toolbar dimming rules.
1561  *
1562  * Returns: TRUE if the mail operation was started, otherwise if the
1563  * user do not want to download the message, or if the user do not
1564  * want to connect, then the operation is not issued
1565  **/
1566 static gboolean
1567 message_reader (ModestMsgViewWindow *window,
1568                 ModestMsgViewWindowPrivate *priv,
1569                 TnyHeader *header,
1570                 GtkTreeRowReference *row_reference)
1571 {
1572         ModestMailOperation *mail_op = NULL;
1573         ModestMailOperationTypeOperation op_type;
1574         gboolean already_showing = FALSE;
1575         ModestWindow *msg_window = NULL;
1576         ModestWindowMgr *mgr;
1577
1578         g_return_val_if_fail (row_reference != NULL, FALSE);
1579
1580         mgr = modest_runtime_get_window_mgr ();
1581         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1582         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1583                 gboolean retval;
1584                 if (msg_window)
1585                         gtk_window_present (GTK_WINDOW (msg_window));
1586                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1587                 return TRUE;
1588         }
1589
1590         /* Msg download completed */
1591         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1592                 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1593         } else {
1594                 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1595
1596                 /* Ask the user if he wants to download the message if
1597                    we're not online */
1598                 if (!tny_device_is_online (modest_runtime_get_device())) {
1599                         TnyFolder *folder = NULL;
1600                         GtkResponseType response;
1601
1602                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1603                                                                             _("mcen_nc_get_msg"));
1604                         if (response == GTK_RESPONSE_CANCEL)
1605                                 return FALSE;
1606                 
1607                         /* Offer the connection dialog if necessary */
1608                         folder = tny_header_get_folder (header);
1609                         if (folder) {
1610                                 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, 
1611                                                                                               TNY_FOLDER_STORE (folder))) {
1612                                         g_object_unref (folder);
1613                                         return FALSE;
1614                                 }
1615                                 g_object_unref (folder);
1616                         }
1617                 }
1618         }
1619
1620         /* New mail operation */
1621         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1622                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1623                                                                  NULL, NULL);
1624                                 
1625         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1626         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1627         g_object_unref (mail_op);
1628
1629         /* Update toolbar dimming rules */
1630         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1631
1632         return TRUE;
1633 }
1634
1635 gboolean        
1636 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1637 {
1638         ModestMsgViewWindowPrivate *priv;
1639         GtkTreePath *path= NULL;
1640         GtkTreeIter tmp_iter;
1641         TnyHeader *header;
1642         gboolean retval = TRUE;
1643         GtkTreeRowReference *row_reference = NULL;
1644
1645         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1646         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1647
1648         /* Update the next row reference if it's not valid. This could
1649            happen if for example the header which it was pointing to,
1650            was deleted. The best place to do it is in the row-deleted
1651            handler but the tinymail model do not work like the glib
1652            tree models and reports the deletion when the row is still
1653            there */
1654         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1655                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1656                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1657                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1658                 }
1659         }
1660         if (priv->next_row_reference)
1661                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1662         if (path == NULL)
1663                 return FALSE;
1664
1665         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1666
1667         gtk_tree_model_get_iter (priv->header_model,
1668                                  &tmp_iter,
1669                                  path);
1670         gtk_tree_path_free (path);
1671
1672         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1673                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1674                             &header, -1);
1675         
1676         /* Read the message & show it */
1677         if (!message_reader (window, priv, header, row_reference)) {
1678                 retval = FALSE;
1679                 gtk_tree_row_reference_free (row_reference);
1680         }
1681
1682         /* Free */
1683         g_object_unref (header);
1684
1685         return retval;          
1686 }
1687
1688 gboolean 
1689 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1690 {
1691         ModestMsgViewWindowPrivate *priv = NULL;
1692         TnyHeader *header = NULL;
1693         GtkTreeIter iter;
1694         GtkTreePath *path = NULL;
1695         GtkTreeRowReference *row_reference = NULL;
1696
1697         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1698         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1699
1700         /* Check that the model is not empty */
1701         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1702                 return FALSE;
1703
1704         /* Get the header */
1705         gtk_tree_model_get (priv->header_model, 
1706                             &iter, 
1707                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1708                             &header, -1);
1709         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1710         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1711                 g_object_unref (header);
1712                 return modest_msg_view_window_select_next_message (self);
1713         }
1714         
1715         path = gtk_tree_model_get_path (priv->header_model, &iter);
1716         row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1717         gtk_tree_path_free (path);
1718
1719         /* Read the message & show it */
1720         message_reader (self, priv, header, row_reference);
1721         
1722         /* Free */
1723         g_object_unref (header);
1724
1725         return TRUE;
1726 }
1727  
1728 gboolean        
1729 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1730 {
1731         ModestMsgViewWindowPrivate *priv = NULL;
1732         GtkTreePath *path;
1733         GtkTreeRowReference *row_reference = NULL;
1734
1735         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1736         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1737
1738         /* Return inmediatly if there is no header model */
1739         if (!priv->header_model)
1740                 return FALSE;
1741
1742         path = gtk_tree_row_reference_get_path (priv->row_reference);
1743         while (gtk_tree_path_prev (path)) {
1744                 TnyHeader *header;
1745                 GtkTreeIter iter;
1746
1747                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1748                 gtk_tree_model_get (priv->header_model, &iter, 
1749                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1750                                     &header, -1);
1751                 if (!header)
1752                         break;
1753                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1754                         g_object_unref (header);
1755                         continue;
1756                 }
1757
1758                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1759                 /* Read the message & show it */
1760                 if (!message_reader (window, priv, header, row_reference)) {
1761                         gtk_tree_row_reference_free (row_reference);
1762                         g_object_unref (header);
1763                         break;
1764                 }
1765
1766                 gtk_tree_path_free (path);
1767                 g_object_unref (header);
1768
1769                 return TRUE;
1770         }
1771
1772         gtk_tree_path_free (path);
1773         return FALSE;
1774 }
1775
1776 static void
1777 view_msg_cb (ModestMailOperation *mail_op, 
1778              TnyHeader *header, 
1779              TnyMsg *msg, 
1780              gpointer user_data)
1781 {
1782         ModestMsgViewWindow *self = NULL;
1783         ModestMsgViewWindowPrivate *priv = NULL;
1784         GtkTreeRowReference *row_reference = NULL;
1785
1786         /* If there was any error */
1787         row_reference = (GtkTreeRowReference *) user_data;
1788         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1789                 gtk_tree_row_reference_free (row_reference);                    
1790                 return;
1791         }
1792
1793         /* Get the window */ 
1794         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1795         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1796         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1797
1798         /* Update the row reference */
1799         gtk_tree_row_reference_free (priv->row_reference);
1800         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1801         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1802         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1803         gtk_tree_row_reference_free (row_reference);
1804
1805         /* Mark header as read */
1806         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1807                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1808
1809         /* Set new message */
1810         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1811         modest_msg_view_window_update_priority (self);
1812         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1813         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1814
1815         /* Set the new message uid of the window  */
1816         if (priv->msg_uid) {
1817                 g_free (priv->msg_uid);
1818                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1819         }
1820
1821         /* Notify the observers */
1822         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1823                        0, priv->header_model, priv->row_reference);
1824
1825         /* Free new references */
1826         g_object_unref (self);
1827 }
1828
1829 TnyFolderType
1830 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1831 {
1832         ModestMsgViewWindowPrivate *priv;
1833         TnyMsg *msg;
1834         TnyFolderType folder_type;
1835
1836         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1837
1838         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1839
1840         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1841         if (msg) {
1842                 TnyFolder *folder;
1843
1844                 folder = tny_msg_get_folder (msg);
1845                 if (folder) {
1846                         folder_type = tny_folder_get_folder_type (folder);
1847                         g_object_unref (folder);
1848                 }
1849                 g_object_unref (msg);
1850         }
1851
1852         return folder_type;
1853 }
1854
1855
1856 static void
1857 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1858 {
1859         ModestMsgViewWindowPrivate *priv;
1860         TnyHeaderFlags flags = 0;
1861
1862         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1863
1864         if (priv->header_model) {
1865                 TnyHeader *header;
1866                 GtkTreeIter iter;
1867                 GtkTreePath *path = NULL;
1868
1869                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1870                 g_return_if_fail (path != NULL);
1871                 gtk_tree_model_get_iter (priv->header_model, 
1872                                          &iter, 
1873                                          gtk_tree_row_reference_get_path (priv->row_reference));
1874
1875                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1876                                     &header, -1);
1877                 flags = tny_header_get_flags (header);
1878                 gtk_tree_path_free (path);
1879         }
1880
1881         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1882
1883 }
1884
1885 static gboolean
1886 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1887 {
1888         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1889                 ModestWindowPrivate *parent_priv;
1890                 ModestWindowMgr *mgr;
1891                 gboolean is_fullscreen;
1892                 GtkAction *fs_toggle_action;
1893                 gboolean active;
1894
1895                 mgr = modest_runtime_get_window_mgr ();
1896                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1897
1898                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1899                 
1900                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1901                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1902                 if (is_fullscreen != active) {
1903                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1904                 }
1905         }
1906
1907         return FALSE;
1908
1909 }
1910
1911 static void
1912 set_homogeneous (GtkWidget *widget,
1913                  gpointer data)
1914 {
1915         if (GTK_IS_TOOL_ITEM (widget)) {
1916                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1917                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1918         }
1919 }
1920
1921 static void
1922 modest_msg_view_window_show_toolbar (ModestWindow *self,
1923                                      gboolean show_toolbar)
1924 {
1925         ModestMsgViewWindowPrivate *priv = NULL;
1926         ModestWindowPrivate *parent_priv;
1927         GtkWidget *reply_button = NULL, *menu = NULL;
1928         GtkWidget *placeholder = NULL;
1929         gint insert_index;
1930         const gchar *action_name;
1931         GtkAction *action;
1932         
1933         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1934         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1935
1936         /* Set optimized view status */
1937         priv->optimized_view = !show_toolbar;
1938
1939         if (!parent_priv->toolbar) {
1940                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1941                                                                   "/ToolBar");
1942                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1943
1944                 /* Set homogeneous toolbar */
1945                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1946                                        set_homogeneous, NULL);
1947
1948                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1949                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1950                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1951                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1952                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1953                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1954                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1955                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1956
1957                 /* Add ProgressBar (Transfer toolbar) */ 
1958                 priv->progress_bar = modest_progress_bar_widget_new ();
1959                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1960                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1961                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1962                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1963                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1964                 
1965                 /* Connect cancel 'clicked' signal to abort progress mode */
1966                 g_signal_connect(priv->cancel_toolitem, "clicked",
1967                                  G_CALLBACK(cancel_progressbar),
1968                                  self);
1969                 
1970                 /* Add it to the observers list */
1971                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1972
1973                 /* Add to window */
1974                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1975                                            GTK_TOOLBAR (parent_priv->toolbar));
1976
1977
1978                 /* Set reply button tap and hold menu */        
1979                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1980                                                           "/ToolBar/ToolbarMessageReply");
1981                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1982                                                   "/ToolbarReplyCSM");
1983                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1984         }
1985
1986         if (show_toolbar) {
1987                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1988                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1989                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1990                 
1991                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1992                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1993                 
1994         } else {
1995                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1996                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1997         }
1998
1999         /* Update also the actions (to update the toggles in the
2000            menus), we have to do it manually because some other window
2001            of the same time could have changed it (remember that the
2002            toolbar fullscreen mode is shared by all the windows of the
2003            same type */
2004         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2005                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2006         else
2007                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2008
2009         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2010         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2011                                                             show_toolbar);
2012 }
2013
2014 static void 
2015 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2016                                                GdkEvent *event,
2017                                                ModestMsgViewWindow *window)
2018 {
2019         if (!GTK_WIDGET_VISIBLE (window))
2020                 return;
2021
2022         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2023 }
2024
2025 gboolean 
2026 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2027 {
2028         ModestMsgViewWindowPrivate *priv;
2029         
2030         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2031         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2032
2033         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2034 }
2035
2036 static void
2037 cancel_progressbar (GtkToolButton *toolbutton,
2038                     ModestMsgViewWindow *self)
2039 {
2040         GSList *tmp;
2041         ModestMsgViewWindowPrivate *priv;
2042         
2043         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2044
2045         /* Get operation observers and cancel its current operation */
2046         tmp = priv->progress_widgets;
2047         while (tmp) {
2048                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2049                 tmp=g_slist_next(tmp);
2050         }
2051 }
2052 static gboolean
2053 observers_empty (ModestMsgViewWindow *self)
2054 {
2055         GSList *tmp = NULL;
2056         ModestMsgViewWindowPrivate *priv;
2057         gboolean is_empty = TRUE;
2058         guint pending_ops = 0;
2059  
2060         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2061         tmp = priv->progress_widgets;
2062
2063         /* Check all observers */
2064         while (tmp && is_empty)  {
2065                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2066                 is_empty = pending_ops == 0;
2067                 
2068                 tmp = g_slist_next(tmp);
2069         }
2070         
2071         return is_empty;
2072 }
2073
2074 static void
2075 on_account_removed (TnyAccountStore *account_store, 
2076                     TnyAccount *account,
2077                     gpointer user_data)
2078 {
2079         /* Do nothing if it's a transport account, because we only
2080            show the messages of a store account */
2081         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2082                 const gchar *parent_acc = NULL;
2083                 const gchar *our_acc = NULL;
2084
2085                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2086                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2087
2088                 /* Close this window if I'm showing a message of the removed account */
2089                 if (strcmp (parent_acc, our_acc) == 0)
2090                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2091         }
2092 }
2093
2094 static void
2095 on_queue_changed (ModestMailOperationQueue *queue,
2096                   ModestMailOperation *mail_op,
2097                   ModestMailOperationQueueNotification type,
2098                   ModestMsgViewWindow *self)
2099 {
2100         GSList *tmp;
2101         ModestMsgViewWindowPrivate *priv;
2102         ModestMailOperationTypeOperation op_type;
2103         ModestToolBarModes mode;
2104         
2105         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2106         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2107
2108         /* If this operations was created by another window, do nothing */
2109         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2110             return;
2111
2112         /* Get toolbar mode from operation id*/
2113         op_type = modest_mail_operation_get_type_operation (mail_op);
2114         switch (op_type) {
2115 /*      case MODEST_MAIL_OPERATION_TYPE_SEND: */
2116         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2117         case MODEST_MAIL_OPERATION_TYPE_OPEN:
2118                 mode = TOOLBAR_MODE_TRANSFER;
2119                 break;
2120         default:
2121                 mode = TOOLBAR_MODE_NORMAL;
2122                 
2123         }
2124                 
2125         /* Add operation observers and change toolbar if neccessary*/
2126         tmp = priv->progress_widgets;
2127         switch (type) {
2128         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2129                 if (mode == TOOLBAR_MODE_TRANSFER) {
2130                         /* Enable transfer toolbar mode */
2131                         set_toolbar_transfer_mode(self);
2132                         while (tmp) {
2133                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2134                                                                       mail_op);
2135                                 tmp = g_slist_next (tmp);
2136                         }
2137                         
2138                 }
2139                 break;
2140         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2141                 if (mode == TOOLBAR_MODE_TRANSFER) {
2142                         while (tmp) {
2143                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2144                                                                  mail_op);
2145                                 tmp = g_slist_next (tmp);
2146                                 
2147                         }
2148
2149                         /* If no more operations are being observed, NORMAL mode is enabled again */
2150                         if (observers_empty (self)) {
2151                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2152                         }
2153                 }
2154                 break;
2155         }
2156 }
2157
2158 GList *
2159 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2160 {
2161         ModestMsgViewWindowPrivate *priv;
2162         GList *selected_attachments = NULL;
2163         
2164         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2165         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2166
2167         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2168         
2169         return selected_attachments;
2170 }
2171
2172 void
2173 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2174 {
2175         ModestMsgViewWindowPrivate *priv;
2176         const gchar *msg_uid;
2177         gchar *attachment_uid = NULL;
2178         gint attachment_index = 0;
2179         GList *attachments;
2180
2181         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2182         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2183         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2184
2185         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2186         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2187         attachment_index = g_list_index (attachments, mime_part);
2188         g_list_free (attachments);
2189         
2190         if (msg_uid && attachment_index >= 0) {
2191                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2192         }
2193
2194         if (mime_part == NULL) {
2195                 gboolean error = FALSE;
2196                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2197                 if (selected_attachments == NULL) {
2198                         error = TRUE;
2199                 } else if (g_list_length (selected_attachments) > 1) {
2200                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2201                         error = TRUE;
2202                 } else {
2203                         mime_part = (TnyMimePart *) selected_attachments->data;
2204                         g_object_ref (mime_part);
2205                 }
2206                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2207                 g_list_free (selected_attachments);
2208
2209                 if (error)
2210                         return;
2211         } else {
2212                 g_object_ref (mime_part);
2213         }
2214
2215         if (tny_mime_part_is_purged (mime_part)) {
2216                 g_object_unref (mime_part);
2217                 return;
2218         }
2219
2220         if (!TNY_IS_MSG (mime_part)) {
2221                 gchar *filepath = NULL;
2222                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2223                 TnyFsStream *temp_stream = NULL;
2224                 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, attachment_uid, &filepath);
2225                 
2226                 if (temp_stream) {
2227                         const gchar *content_type;
2228                         content_type = tny_mime_part_get_content_type (mime_part);
2229                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2230                         
2231                         modest_platform_activate_file (filepath, content_type);
2232                         g_object_unref (temp_stream);
2233                         g_free (filepath);
2234                         /* NOTE: files in the temporary area will be automatically
2235                          * cleaned after some time if they are no longer in use */
2236                 }
2237         } else {
2238                 /* message attachment */
2239                 TnyHeader *header = NULL;
2240                 ModestWindowMgr *mgr;
2241                 ModestWindow *msg_win = NULL;
2242                 gboolean found;
2243                 
2244                 header = tny_msg_get_header (TNY_MSG (mime_part));
2245                 mgr = modest_runtime_get_window_mgr ();         
2246                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2247
2248                 if (found) {
2249                         if (msg_win)                            /* there is already a window for this uid; top it */
2250                                 gtk_window_present (GTK_WINDOW(msg_win));
2251                         else 
2252                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2253                                  * thus, we don't do anything */
2254                                 g_warning ("window for is already being created");
2255                 } else { 
2256                         /* it's not found, so create a new window for it */
2257                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2258                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2259                         if (!account)
2260                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2261                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2262                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
2263                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2264                         modest_window_mgr_register_window (mgr, msg_win);
2265                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2266                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2267                 }
2268         }
2269         g_object_unref (mime_part);
2270 }
2271
2272 typedef struct
2273 {
2274         gchar *filename;
2275         TnyMimePart *part;
2276 } SaveMimePartPair;
2277
2278 typedef struct
2279 {
2280         GList *pairs;
2281         GtkWidget *banner;
2282         gboolean result;
2283 } SaveMimePartInfo;
2284
2285 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2286 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2287 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2288 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2289
2290 static void 
2291 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2292 {
2293         
2294         GList *node;
2295         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2296                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2297                 g_free (pair->filename);
2298                 g_object_unref (pair->part);
2299                 g_slice_free (SaveMimePartPair, pair);
2300         }
2301         g_list_free (info->pairs);
2302         info->pairs = NULL;
2303         if (with_struct) {
2304                 gtk_widget_destroy (info->banner);
2305                 g_object_unref (info->banner);
2306                 g_slice_free (SaveMimePartInfo, info);
2307         }
2308 }
2309
2310 static gboolean
2311 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2312 {
2313         if (info->pairs != NULL) {
2314                 save_mime_part_to_file (info);
2315         } else {
2316                 gboolean result;
2317                 result = info->result;
2318
2319                 /* This is a GDK lock because we are an idle callback and
2320                  * hildon_banner_show_information is or does Gtk+ code */
2321
2322                 gdk_threads_enter (); /* CHECKED */
2323                 save_mime_part_info_free (info, TRUE);
2324                 if (result) {
2325                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2326                 } else {
2327                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2328                 }
2329                 gdk_threads_leave (); /* CHECKED */
2330         }
2331
2332         return FALSE;
2333 }
2334
2335 static gpointer
2336 save_mime_part_to_file (SaveMimePartInfo *info)
2337 {
2338         GnomeVFSResult result;
2339         GnomeVFSHandle *handle;
2340         TnyStream *stream;
2341         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2342
2343         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2344         if (result == GNOME_VFS_OK) {
2345                 stream = tny_vfs_stream_new (handle);
2346                 tny_mime_part_decode_to_stream (pair->part, stream);
2347                 g_object_unref (G_OBJECT (stream));
2348                 g_object_unref (pair->part);
2349                 g_slice_free (SaveMimePartPair, pair);
2350                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2351                 info->result = TRUE;
2352         } else {
2353                 save_mime_part_info_free (info, FALSE);
2354                 info->result = FALSE;
2355         }
2356
2357         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2358         return NULL;
2359 }
2360
2361 static void
2362 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2363 {
2364         gboolean is_ok = TRUE;
2365         gint replaced_files = 0;
2366         const GList *files = info->pairs;
2367         const GList *iter;
2368
2369         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2370                 SaveMimePartPair *pair = iter->data;
2371                 if (modest_maemo_utils_file_exists (pair->filename)) {
2372                         replaced_files++;
2373                 }
2374         }
2375         if (replaced_files) {
2376                 GtkWidget *confirm_overwrite_dialog;
2377                 const gchar *message = (replaced_files == 1) ?
2378                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2379                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2380                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2381                         is_ok = FALSE;
2382                 }
2383                 gtk_widget_destroy (confirm_overwrite_dialog);
2384         }
2385
2386         if (!is_ok) {
2387                 save_mime_part_info_free (info, TRUE);
2388         } else {
2389                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2390                                                                   _CS("sfil_ib_saving"));
2391                 info->banner = g_object_ref (banner);
2392                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2393                 g_object_unref (banner);
2394         }
2395
2396 }
2397
2398
2399 void
2400 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2401 {
2402         gboolean clean_list = FALSE;
2403         ModestMsgViewWindowPrivate *priv;
2404         GList *files_to_save = NULL;
2405         GtkWidget *save_dialog = NULL;
2406         gchar *folder = NULL;
2407         gboolean canceled = FALSE;
2408         const gchar *filename = NULL;
2409         gchar *save_multiple_str = NULL;
2410
2411         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2412         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2413
2414         if (mime_parts == NULL) {
2415                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2416                 if (mime_parts == NULL)
2417                         return;
2418                 clean_list = TRUE;
2419         }
2420
2421         /* prepare dialog */
2422         if (mime_parts->next == NULL) {
2423                 /* only one attachment selected */
2424                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2425                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2426                         filename = tny_mime_part_get_filename (mime_part);
2427                 } else {
2428                         g_warning ("Tried to save a non-file attachment");
2429                         canceled = TRUE;
2430                 }
2431         } else {
2432                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2433                                                      g_list_length (mime_parts));
2434         }
2435         
2436         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2437                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2438
2439         /* set folder */
2440         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2441         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2442         g_free (folder);
2443
2444         /* set filename */
2445         if (filename != NULL)
2446                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2447                                                    filename);
2448
2449         /* if multiple, set multiple string */
2450         if (save_multiple_str) {
2451                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2452         }
2453                 
2454         /* show dialog */
2455         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2456                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2457
2458                 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2459                         hildon_banner_show_information 
2460                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2461                 } else {
2462                         GList *node = NULL;
2463
2464                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2465                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2466                                 
2467                                 if (tny_mime_part_is_attachment (mime_part)) {
2468                                         SaveMimePartPair *pair;
2469
2470                                         if ((mime_parts->next != NULL) &&
2471                                             (tny_mime_part_get_filename (mime_part) == NULL))
2472                                                 continue;
2473                                         
2474                                         pair = g_slice_new0 (SaveMimePartPair);
2475                                         if (mime_parts->next == NULL) {
2476                                                 pair->filename = g_strdup (chooser_uri);
2477                                         } else {
2478                                                 pair->filename = 
2479                                                         g_build_filename (chooser_uri,
2480                                                                           tny_mime_part_get_filename (mime_part), NULL);
2481                                         }
2482                                         pair->part = g_object_ref (mime_part);
2483                                         files_to_save = g_list_prepend (files_to_save, pair);
2484                                 }
2485                         }
2486                 }
2487                 g_free (chooser_uri);
2488         }
2489
2490         gtk_widget_destroy (save_dialog);
2491
2492         if (clean_list) {
2493                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2494                 g_list_free (mime_parts);
2495         }
2496
2497         if (files_to_save != NULL) {
2498                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2499                 info->pairs = files_to_save;
2500                 info->result = TRUE;
2501                 save_mime_parts_to_file_with_checks (info);
2502         }
2503 }
2504
2505 static gboolean
2506 show_remove_attachment_information (gpointer userdata)
2507 {
2508         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2509         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2510
2511         if (priv->remove_attachment_banner != NULL) {
2512                 gtk_widget_destroy (priv->remove_attachment_banner);
2513                 g_object_unref (priv->remove_attachment_banner);
2514         }
2515
2516         priv->remove_attachment_banner = g_object_ref (
2517                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2518
2519         return FALSE;
2520 }
2521
2522 void
2523 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2524 {
2525         ModestMsgViewWindowPrivate *priv;
2526         GList *mime_parts = NULL, *node;
2527         gchar *confirmation_message;
2528         gint response;
2529         gint n_attachments;
2530         TnyMsg *msg;
2531 /*      TnyFolder *folder; */
2532
2533         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2534         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2535
2536         if (get_all)
2537                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2538         else
2539                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2540                 
2541         /* Remove already purged messages from mime parts list */
2542         node = mime_parts;
2543         while (node != NULL) {
2544                 TnyMimePart *part = TNY_MIME_PART (node->data);
2545                 if (tny_mime_part_is_purged (part)) {
2546                         GList *deleted_node = node;
2547                         node = g_list_next (node);
2548                         g_object_unref (part);
2549                         mime_parts = g_list_delete_link (mime_parts, deleted_node);
2550                 } else {
2551                         node = g_list_next (node);
2552                 }
2553         }
2554
2555         if (mime_parts == NULL)
2556                 return;
2557
2558         n_attachments = g_list_length (mime_parts);
2559         if (n_attachments == 1) {
2560                 const gchar *filename;
2561
2562                 if (TNY_IS_MSG (mime_parts->data)) {
2563                         TnyHeader *header;
2564                         header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2565                         filename = tny_header_get_subject (header);
2566                         g_object_unref (header);
2567                         if (filename == NULL)
2568                                 filename = _("mail_va_no_subject");
2569                 } else {
2570                         filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2571                 }
2572                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2573         } else {
2574                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2575                                                                  "mcen_nc_purge_files_text", 
2576                                                                  n_attachments), n_attachments);
2577         }
2578         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2579                                                             confirmation_message);
2580         g_free (confirmation_message);
2581
2582         if (response != GTK_RESPONSE_OK)
2583                 return;
2584
2585         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2586 /*      folder = tny_msg_get_folder (msg); */
2587 /*      tny_msg_uncache_attachments (msg); */
2588 /*      tny_folder_refresh (folder, NULL); */
2589 /*      g_object_unref (folder); */
2590         
2591         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2592                 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2593 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2594         }
2595
2596         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2597         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2598         tny_msg_rewrite_cache (msg);
2599         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2600
2601         g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2602         g_list_free (mime_parts);
2603
2604         if (priv->purge_timeout > 0) {
2605                 g_source_remove (priv->purge_timeout);
2606                 priv->purge_timeout = 0;
2607         }
2608
2609         if (priv->remove_attachment_banner) {
2610                 gtk_widget_destroy (priv->remove_attachment_banner);
2611                 g_object_unref (priv->remove_attachment_banner);
2612                 priv->remove_attachment_banner = NULL;
2613         }
2614
2615
2616 }
2617
2618
2619 static void
2620 update_window_title (ModestMsgViewWindow *window)
2621 {
2622         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2623         TnyMsg *msg = NULL;
2624         TnyHeader *header = NULL;
2625         const gchar *subject = NULL;
2626
2627         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2628         if (msg != NULL) {
2629                 header = tny_msg_get_header (msg);
2630                 subject = tny_header_get_subject (header);
2631                 g_object_unref (msg);
2632         }
2633
2634         if ((subject == NULL)||(subject[0] == '\0'))
2635                 subject = _("mail_va_no_subject");
2636
2637         gtk_window_set_title (GTK_WINDOW (window), subject);
2638 }
2639
2640 static void on_move_focus (ModestMsgViewWindow *window,
2641                            GtkDirectionType direction,
2642                            gpointer userdata)
2643 {
2644         GtkWidget *current_focus = NULL;
2645
2646         current_focus = gtk_window_get_focus (GTK_WINDOW (window));
2647         if ((current_focus != NULL) &&
2648             MODEST_IS_ATTACHMENTS_VIEW (current_focus)) {
2649                 g_signal_stop_emission_by_name (G_OBJECT (window), "move-focus");
2650         }
2651 }
2652