* coverity fixes: remove unused params, dead code
[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"), "<CTRL>F", 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         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
918
919         modest_msg_view_window_update_priority (window);
920
921         /* Check toolbar dimming rules */
922         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
923
924         return MODEST_WINDOW(window);
925 }
926
927 ModestWindow *
928 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
929                                               const gchar *modest_account_name,
930                                               const gchar *msg_uid)
931 {
932         ModestMsgViewWindow *window = NULL;
933         ModestMsgViewWindowPrivate *priv = NULL;
934
935         window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
936         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
937         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
938
939         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
940
941         /* Remember that this is a search result, 
942          * so we can disable some UI appropriately: */
943         priv->is_search_result = TRUE;
944
945         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
946
947         return MODEST_WINDOW(window);
948 }
949
950 ModestWindow *
951 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
952                             const gchar *modest_account_name,
953                             const gchar *msg_uid)
954 {
955         GObject *obj = NULL;
956         ModestMsgViewWindowPrivate *priv;
957         g_return_val_if_fail (msg, NULL);
958         
959         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
960         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
961         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
962                 modest_account_name, msg_uid);
963
964         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
965
966         return MODEST_WINDOW(obj);
967 }
968
969 void modest_msg_view_window_on_row_changed(
970                 GtkTreeModel *header_model,
971                 GtkTreePath *arg1,
972                 GtkTreeIter *arg2,
973                 ModestMsgViewWindow *window){
974         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
975 }
976
977 void modest_msg_view_window_on_row_deleted(
978                 GtkTreeModel *header_model,
979                 GtkTreePath *arg1,
980                 ModestMsgViewWindow *window){
981         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
982 }
983
984 /* On insertions we check if the folder still has the message we are
985  * showing or do not. If do not, we do nothing. Which means we are still
986  * not attached to any header folder and thus next/prev buttons are
987  * still dimmed. Once the message that is shown by msg-view is found, the
988  * new model of header-view will be attached and the references will be set.
989  * On each further insertions dimming rules will be checked. However
990  * this requires extra CPU time at least works.
991  * (An message might be deleted from TnyFolder and thus will not be
992  * inserted into the model again for example if it is removed by the
993  * imap server and the header view is refreshed.)
994  */
995 void modest_msg_view_window_on_row_inserted(
996                 GtkTreeModel *new_model,
997                 GtkTreePath *tree_path,
998                 GtkTreeIter *tree_iter,
999                 ModestMsgViewWindow *window){
1000         ModestMsgViewWindowPrivate *priv = NULL; 
1001         TnyHeader *header = NULL;
1002         gchar *uid = NULL;
1003
1004         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1005
1006         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1007         
1008         /* If we already has a model attached then the message shown by
1009          * msg-view is in it, and thus we do not need any actions but
1010          * to check the dimming rules.*/
1011         if(priv->header_model != NULL){
1012                 gtk_tree_row_reference_free(priv->next_row_reference);
1013                 priv->next_row_reference = gtk_tree_row_reference_copy(
1014                                 priv->row_reference);
1015                 select_next_valid_row (priv->header_model,
1016                                 &(priv->next_row_reference), FALSE);
1017                 modest_ui_actions_check_toolbar_dimming_rules (
1018                                 MODEST_WINDOW (window));
1019                 return;
1020         }
1021
1022         /* Check if the newly inserted message is the same we are actually
1023          * showing. IF not, we should remain detached from the header model
1024          * and thus prev and next toolbarbuttons should remain dimmed. */
1025         gtk_tree_model_get (new_model, tree_iter, 
1026                         TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1027         uid = modest_tny_folder_get_header_unique_id(header);
1028         if(!g_str_equal(priv->msg_uid, uid)){
1029                 g_free(uid);
1030                 return;
1031         }
1032         g_free(uid);
1033
1034         /* Setup row_reference for the actual msg. */
1035         priv->row_reference = gtk_tree_row_reference_new(
1036                         new_model, tree_path);
1037         if(priv->row_reference == NULL){
1038                 g_warning("No reference for msg header item.");
1039                 return;
1040         }
1041
1042         /* Attach new_model and connect some callback to it to become able
1043          * to detect changes in header-view. */
1044         priv->header_model = g_object_ref(new_model);
1045         g_signal_connect (new_model, "row-changed",
1046                         G_CALLBACK (modest_msg_view_window_on_row_changed),
1047                         window);
1048         g_signal_connect (new_model, "row-deleted",
1049                         G_CALLBACK (modest_msg_view_window_on_row_deleted),
1050                         window);
1051         g_signal_connect (new_model, "rows-reordered",
1052                         G_CALLBACK (modest_msg_view_window_on_row_reordered),
1053                         window);
1054
1055         /* Now set up next_row_reference. */
1056         priv->next_row_reference = gtk_tree_row_reference_copy(
1057                         priv->row_reference);
1058         select_next_valid_row (priv->header_model,
1059                         &(priv->next_row_reference), FALSE);
1060
1061         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1062 }
1063
1064 void modest_msg_view_window_on_row_reordered(
1065                 GtkTreeModel *header_model,
1066                 GtkTreePath *arg1,
1067                 GtkTreeIter *arg2,
1068                 gpointer arg3,
1069                 ModestMsgViewWindow *window){
1070         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1071 }
1072
1073 /* The modest_msg_view_window_update_model_replaced implements update
1074  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1075  * actually belongs to the header-view is the same as the TnyFolder of
1076  * the message of msg-view or not. If they are different, there is
1077  * nothing to do. If they are the same, then the model has replaced and
1078  * the reference in msg-view shall be replaced from the old model to
1079  * the new model. In this case the view will be detached from it's
1080  * header folder. From this point the next/prev buttons are dimmed.
1081  */
1082 void modest_msg_view_window_update_model_replaced(
1083                 ModestHeaderViewObserver *observer,
1084                 GtkTreeModel *model,
1085                 const gchar *tny_folder_id){
1086         ModestMsgViewWindowPrivate *priv = NULL; 
1087         ModestMsgViewWindow *window = NULL;
1088
1089         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1090         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1091
1092         window = MODEST_MSG_VIEW_WINDOW(observer);
1093         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1094
1095         /* If there is an other folder in the header-view then we do
1096          * not care about it's model (msg list). Else if the
1097          * header-view shows the folder the msg shown by us is in, we
1098          * shall replace our model reference and make some check. */
1099         if(tny_folder_id == NULL || !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         g_assert(model != NULL);
1136
1137         /* Also we must connect to the new model for row insertions.
1138          * Only for insertions now. We will need other ones only after
1139          * the msg is show by msg-view is added to the new model. */
1140         priv->row_inserted_handler = g_signal_connect (
1141                         model, "row-inserted",
1142                         G_CALLBACK(modest_msg_view_window_on_row_inserted),
1143                         window);
1144 }
1145
1146 gboolean 
1147 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1148 {
1149         ModestMsgViewWindowPrivate *priv= NULL; 
1150
1151         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1152         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1153
1154         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1155 }
1156
1157 TnyHeader*
1158 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1159 {
1160         ModestMsgViewWindowPrivate *priv= NULL; 
1161         TnyMsg *msg = NULL;
1162         TnyHeader *header = NULL;
1163         GtkTreePath *path = NULL;
1164         GtkTreeIter iter;
1165
1166         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1167         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1168
1169         /* If the message was not obtained from a treemodel,
1170          * for instance if it was opened directly by the search UI:
1171          */
1172         if (priv->header_model == NULL) {
1173                 msg = modest_msg_view_window_get_message (self);
1174                 if (msg) {
1175                         header = tny_msg_get_header (msg);
1176                         g_object_unref (msg);
1177                 }
1178                 return header;
1179         }
1180
1181         /* Get iter of the currently selected message in the header view: */
1182         /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1183          * instead of sometimes retrieving it from the header view?
1184          * Then we wouldn't be dependent on the message actually still being selected 
1185          * in the header view. murrayc. */
1186         if (!gtk_tree_row_reference_valid (priv->row_reference))
1187                 return NULL;
1188         path = gtk_tree_row_reference_get_path (priv->row_reference);
1189         g_return_val_if_fail (path != NULL, NULL);
1190         gtk_tree_model_get_iter (priv->header_model, 
1191                                  &iter, 
1192                                  path);
1193
1194         /* Get current message header */
1195         gtk_tree_model_get (priv->header_model, &iter, 
1196                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1197                             &header, -1);
1198
1199         gtk_tree_path_free (path);
1200         return header;
1201 }
1202
1203 TnyMsg*
1204 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1205 {
1206         ModestMsgViewWindowPrivate *priv;
1207         
1208         g_return_val_if_fail (self, NULL);
1209         
1210         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1211         
1212         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1213 }
1214
1215 const gchar*
1216 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1217 {
1218         ModestMsgViewWindowPrivate *priv;
1219
1220         g_return_val_if_fail (self, NULL);
1221         
1222         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1223
1224         return (const gchar*) priv->msg_uid;
1225 }
1226
1227 static void 
1228 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1229                                             gpointer data)
1230 {
1231         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1232         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1233         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1234         gboolean is_active;
1235         GtkAction *action;
1236
1237         is_active = gtk_toggle_action_get_active (toggle);
1238
1239         if (is_active) {
1240                 gtk_widget_show (priv->find_toolbar);
1241                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1242         } else {
1243                 gtk_widget_hide (priv->find_toolbar);
1244         }
1245
1246         /* update the toggle buttons status */
1247         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1248         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1249         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1250         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1251         
1252 }
1253
1254 static void
1255 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1256                                            ModestMsgViewWindow *obj)
1257 {
1258         GtkToggleAction *toggle;
1259         ModestWindowPrivate *parent_priv;
1260         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1261         
1262         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1263         gtk_toggle_action_set_active (toggle, FALSE);
1264 }
1265
1266 static void
1267 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1268                                            ModestMsgViewWindow *obj)
1269 {
1270         gchar *current_search;
1271         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1272
1273         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1274                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1275                 return;
1276         }
1277
1278         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1279
1280         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1281                 g_free (current_search);
1282                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1283                 return;
1284         }
1285
1286         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1287                 gboolean result;
1288                 g_free (priv->last_search);
1289                 priv->last_search = g_strdup (current_search);
1290                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1291                                                      priv->last_search);
1292                 if (!result) {
1293                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1294                         g_free (priv->last_search);
1295                         priv->last_search = NULL;
1296                 } else {
1297                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1298                 }
1299         } else {
1300                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1301                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1302                         g_free (priv->last_search);
1303                         priv->last_search = NULL;
1304                 } else {
1305                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1306                 }
1307         }
1308         
1309         g_free (current_search);
1310                 
1311 }
1312
1313 static void
1314 modest_msg_view_window_set_zoom (ModestWindow *window,
1315                                  gdouble zoom)
1316 {
1317         ModestMsgViewWindowPrivate *priv;
1318         ModestWindowPrivate *parent_priv;
1319         GtkAction *action = NULL;
1320         gint int_zoom = (gint) rint (zoom*100.0+0.1);
1321      
1322         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1323
1324         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1325         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1326         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1327
1328         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1329                                             "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1330
1331         gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1332 }
1333
1334 static gdouble
1335 modest_msg_view_window_get_zoom (ModestWindow *window)
1336 {
1337         ModestMsgViewWindowPrivate *priv;
1338      
1339         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1340
1341         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1342         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1343 }
1344
1345 static gboolean
1346 modest_msg_view_window_zoom_plus (ModestWindow *window)
1347 {
1348         ModestWindowPrivate *parent_priv;
1349         GtkRadioAction *zoom_radio_action;
1350         GSList *group, *node;
1351
1352         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1353         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1354                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1355
1356         group = gtk_radio_action_get_group (zoom_radio_action);
1357
1358         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1359                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1360                 return FALSE;
1361         }
1362
1363         for (node = group; node != NULL; node = g_slist_next (node)) {
1364                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1365                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1366                         return TRUE;
1367                 }
1368         }
1369         return FALSE;
1370 }
1371
1372 static gboolean
1373 modest_msg_view_window_zoom_minus (ModestWindow *window)
1374 {
1375         ModestWindowPrivate *parent_priv;
1376         GtkRadioAction *zoom_radio_action;
1377         GSList *group, *node;
1378
1379         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1380         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1381                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1382
1383         group = gtk_radio_action_get_group (zoom_radio_action);
1384
1385         for (node = group; node != NULL; node = g_slist_next (node)) {
1386                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1387                         if (node->next != NULL) {
1388                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1389                                 return TRUE;
1390                         } else {
1391                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1392                                 return FALSE;
1393                         }
1394                         break;
1395                 }
1396         }
1397         return FALSE;
1398 }
1399
1400 static gboolean
1401 modest_msg_view_window_key_release_event (GtkWidget *window,
1402                                           GdkEventKey *event,
1403                                           gpointer userdata)
1404 {
1405         if (event->type == GDK_KEY_RELEASE) {
1406                 switch (event->keyval) {
1407                 case GDK_Up:
1408                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1409                         return TRUE;
1410                         break;
1411                 case GDK_Down:
1412                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1413                         return TRUE;
1414                         break;
1415                 default:
1416                         return FALSE;
1417                         break;
1418                 };
1419         } else {
1420                 return FALSE;
1421         }
1422 }
1423
1424 static void
1425 modest_msg_view_window_scroll_up (ModestWindow *window)
1426 {
1427         ModestMsgViewWindowPrivate *priv;
1428         gboolean return_value;
1429
1430         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1431         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1432 }
1433
1434 static void
1435 modest_msg_view_window_scroll_down (ModestWindow *window)
1436 {
1437         ModestMsgViewWindowPrivate *priv;
1438         gboolean return_value;
1439
1440         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1441         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1442 }
1443
1444 gboolean
1445 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1446 {
1447         GtkTreePath *path;
1448         ModestMsgViewWindowPrivate *priv;
1449         GtkTreeIter tmp_iter;
1450         gboolean is_last_selected;
1451
1452         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1453         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1454
1455         /*if no model (so no rows at all), then virtually we are the last*/
1456         if (!priv->header_model)
1457                 return TRUE;
1458
1459         path = gtk_tree_row_reference_get_path (priv->row_reference);
1460         if (path == NULL)
1461                 return TRUE;
1462
1463         is_last_selected = TRUE;
1464         while (is_last_selected) {
1465                 TnyHeader *header;
1466                 gtk_tree_path_next (path);
1467                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1468                         break;
1469                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1470                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1471                                 &header, -1);
1472                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1473                         is_last_selected = FALSE;
1474         }
1475         gtk_tree_path_free (path);
1476         return is_last_selected;
1477 }
1478
1479 gboolean
1480 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1481 {
1482         ModestMsgViewWindowPrivate *priv;
1483
1484         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1485         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1486
1487         return priv->header_model != NULL;
1488 }
1489
1490 gboolean
1491 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1492 {
1493         ModestMsgViewWindowPrivate *priv;
1494
1495         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1496         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1497
1498         return priv->is_search_result;
1499 }
1500
1501 gboolean
1502 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1503 {
1504         GtkTreePath *path;
1505         ModestMsgViewWindowPrivate *priv;
1506         gboolean is_first_selected;
1507         GtkTreeIter tmp_iter;
1508 /*      gchar * path_string;*/
1509
1510         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1511         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1512
1513         /*if no model (so no rows at all), then virtually we are the first*/
1514         if (!priv->header_model)
1515                 return TRUE;
1516
1517         path = gtk_tree_row_reference_get_path (priv->row_reference);
1518         if (!path)
1519                 return TRUE;
1520
1521 /*      path_string = gtk_tree_path_to_string (path);
1522         is_first_selected = strcmp (path_string, "0");
1523
1524         g_free (path_string);
1525         gtk_tree_path_free (path);
1526
1527         return is_first_selected;*/
1528
1529         is_first_selected = TRUE;
1530         while (is_first_selected) {
1531                 TnyHeader *header;
1532                 if(!gtk_tree_path_prev (path))
1533                         break;
1534                 /* Here the 'if' is needless for logic, but let make sure
1535                  * iter is valid for gtk_tree_model_get. */
1536                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1537                         break;
1538                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1539                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1540                                 &header, -1);
1541                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1542                         is_first_selected = FALSE;
1543         }
1544         gtk_tree_path_free (path);
1545         return is_first_selected;
1546 }
1547
1548 /**
1549  * Reads the message whose summary item is @header. It takes care of
1550  * several things, among others:
1551  *
1552  * If the message was not previously downloaded then ask the user
1553  * before downloading. If there is no connection launch the connection
1554  * dialog. Update toolbar dimming rules.
1555  *
1556  * Returns: TRUE if the mail operation was started, otherwise if the
1557  * user do not want to download the message, or if the user do not
1558  * want to connect, then the operation is not issued
1559  **/
1560 static gboolean
1561 message_reader (ModestMsgViewWindow *window,
1562                 ModestMsgViewWindowPrivate *priv,
1563                 TnyHeader *header,
1564                 GtkTreeRowReference *row_reference)
1565 {
1566         ModestMailOperation *mail_op = NULL;
1567         ModestMailOperationTypeOperation op_type;
1568         gboolean already_showing = FALSE;
1569         ModestWindow *msg_window = NULL;
1570         ModestWindowMgr *mgr;
1571
1572         g_return_val_if_fail (row_reference != NULL, FALSE);
1573
1574         mgr = modest_runtime_get_window_mgr ();
1575         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1576         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1577                 gboolean retval;
1578                 if (msg_window)
1579                         gtk_window_present (GTK_WINDOW (msg_window));
1580                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1581                 return TRUE;
1582         }
1583
1584         /* Msg download completed */
1585         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1586                 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1587         } else {
1588                 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1589
1590                 /* Ask the user if he wants to download the message if
1591                    we're not online */
1592                 if (!tny_device_is_online (modest_runtime_get_device())) {
1593                         TnyFolder *folder = NULL;
1594                         GtkResponseType response;
1595
1596                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1597                                                                             _("mcen_nc_get_msg"));
1598                         if (response == GTK_RESPONSE_CANCEL)
1599                                 return FALSE;
1600                 
1601                         /* Offer the connection dialog if necessary */
1602                         folder = tny_header_get_folder (header);
1603                         if (folder) {
1604                                 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, 
1605                                                                                               TNY_FOLDER_STORE (folder))) {
1606                                         g_object_unref (folder);
1607                                         return FALSE;
1608                                 }
1609                                 g_object_unref (folder);
1610                         }
1611                 }
1612         }
1613
1614         /* New mail operation */
1615         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1616                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1617                                                                  NULL, NULL);
1618                                 
1619         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1620         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1621         g_object_unref (mail_op);
1622
1623         /* Update toolbar dimming rules */
1624         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1625
1626         return TRUE;
1627 }
1628
1629 gboolean        
1630 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1631 {
1632         ModestMsgViewWindowPrivate *priv;
1633         GtkTreePath *path= NULL;
1634         GtkTreeIter tmp_iter;
1635         TnyHeader *header;
1636         gboolean retval = TRUE;
1637         GtkTreeRowReference *row_reference = NULL;
1638
1639         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1640         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1641
1642         /* Update the next row reference if it's not valid. This could
1643            happen if for example the header which it was pointing to,
1644            was deleted. The best place to do it is in the row-deleted
1645            handler but the tinymail model do not work like the glib
1646            tree models and reports the deletion when the row is still
1647            there */
1648         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1649                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1650                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1651                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1652                 }
1653         }
1654         if (priv->next_row_reference)
1655                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1656         if (path == NULL)
1657                 return FALSE;
1658
1659         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1660
1661         gtk_tree_model_get_iter (priv->header_model,
1662                                  &tmp_iter,
1663                                  path);
1664         gtk_tree_path_free (path);
1665
1666         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1667                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1668                             &header, -1);
1669         
1670         /* Read the message & show it */
1671         if (!message_reader (window, priv, header, row_reference)) {
1672                 retval = FALSE;
1673                 gtk_tree_row_reference_free (row_reference);
1674         }
1675
1676         /* Free */
1677         g_object_unref (header);
1678
1679         return retval;          
1680 }
1681
1682 gboolean 
1683 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1684 {
1685         ModestMsgViewWindowPrivate *priv = NULL;
1686         TnyHeader *header = NULL;
1687         GtkTreeIter iter;
1688         GtkTreePath *path = NULL;
1689         GtkTreeRowReference *row_reference = NULL;
1690
1691         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1692         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1693
1694         /* Check that the model is not empty */
1695         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1696                 return FALSE;
1697
1698         /* Get the header */
1699         gtk_tree_model_get (priv->header_model, 
1700                             &iter, 
1701                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1702                             &header, -1);
1703         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1704         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1705                 g_object_unref (header);
1706                 return modest_msg_view_window_select_next_message (self);
1707         }
1708         
1709         path = gtk_tree_model_get_path (priv->header_model, &iter);
1710         row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1711         gtk_tree_path_free (path);
1712
1713         /* Read the message & show it */
1714         message_reader (self, priv, header, row_reference);
1715         
1716         /* Free */
1717         g_object_unref (header);
1718
1719         return TRUE;
1720 }
1721  
1722 gboolean        
1723 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1724 {
1725         ModestMsgViewWindowPrivate *priv = NULL;
1726         GtkTreePath *path;
1727         GtkTreeRowReference *row_reference = NULL;
1728
1729         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1730         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1731
1732         /* Return inmediatly if there is no header model */
1733         if (!priv->header_model)
1734                 return FALSE;
1735
1736         path = gtk_tree_row_reference_get_path (priv->row_reference);
1737         while (gtk_tree_path_prev (path)) {
1738                 TnyHeader *header;
1739                 GtkTreeIter iter;
1740
1741                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1742                 gtk_tree_model_get (priv->header_model, &iter, 
1743                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1744                                     &header, -1);
1745                 if (!header)
1746                         break;
1747                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1748                         g_object_unref (header);
1749                         continue;
1750                 }
1751
1752                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1753                 /* Read the message & show it */
1754                 if (!message_reader (window, priv, header, row_reference)) {
1755                         gtk_tree_row_reference_free (row_reference);
1756                         g_object_unref (header);
1757                         break;
1758                 }
1759
1760                 gtk_tree_path_free (path);
1761                 g_object_unref (header);
1762
1763                 return TRUE;
1764         }
1765
1766         gtk_tree_path_free (path);
1767         return FALSE;
1768 }
1769
1770 static void
1771 view_msg_cb (ModestMailOperation *mail_op, 
1772              TnyHeader *header, 
1773              TnyMsg *msg, 
1774              gpointer user_data)
1775 {
1776         ModestMsgViewWindow *self = NULL;
1777         ModestMsgViewWindowPrivate *priv = NULL;
1778         GtkTreeRowReference *row_reference = NULL;
1779
1780         /* If there was any error */
1781         row_reference = (GtkTreeRowReference *) user_data;
1782         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1783                 gtk_tree_row_reference_free (row_reference);                    
1784                 return;
1785         }
1786
1787         /* Get the window */ 
1788         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1789         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1790         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1791
1792         /* Update the row reference */
1793         gtk_tree_row_reference_free (priv->row_reference);
1794         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1795         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1796         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1797         gtk_tree_row_reference_free (row_reference);
1798
1799         /* Mark header as read */
1800         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1801                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1802
1803         /* Set new message */
1804         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1805         modest_msg_view_window_update_priority (self);
1806         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1807         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1808
1809         /* Set the new message uid of the window  */
1810         if (priv->msg_uid) {
1811                 g_free (priv->msg_uid);
1812                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1813         }
1814
1815         /* Notify the observers */
1816         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1817                        0, priv->header_model, priv->row_reference);
1818
1819         /* Free new references */
1820         g_object_unref (self);
1821 }
1822
1823 TnyFolderType
1824 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1825 {
1826         ModestMsgViewWindowPrivate *priv;
1827         TnyMsg *msg;
1828         TnyFolderType folder_type;
1829
1830         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1831
1832         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1833
1834         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1835         if (msg) {
1836                 TnyFolder *folder;
1837
1838                 folder = tny_msg_get_folder (msg);
1839                 if (folder) {
1840                         folder_type = tny_folder_get_folder_type (folder);
1841                         g_object_unref (folder);
1842                 }
1843                 g_object_unref (msg);
1844         }
1845
1846         return folder_type;
1847 }
1848
1849
1850 static void
1851 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1852 {
1853         ModestMsgViewWindowPrivate *priv;
1854         TnyHeaderFlags flags = 0;
1855
1856         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1857
1858         if (priv->header_model) {
1859                 TnyHeader *header;
1860                 GtkTreeIter iter;
1861                 GtkTreePath *path = NULL;
1862
1863                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1864                 g_return_if_fail (path != NULL);
1865                 gtk_tree_model_get_iter (priv->header_model, 
1866                                          &iter, 
1867                                          gtk_tree_row_reference_get_path (priv->row_reference));
1868
1869                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1870                                     &header, -1);
1871                 flags = tny_header_get_flags (header);
1872                 gtk_tree_path_free (path);
1873         }
1874
1875         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1876
1877 }
1878
1879 static gboolean
1880 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1881 {
1882         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1883                 ModestWindowPrivate *parent_priv;
1884                 ModestWindowMgr *mgr;
1885                 gboolean is_fullscreen;
1886                 GtkAction *fs_toggle_action;
1887                 gboolean active;
1888
1889                 mgr = modest_runtime_get_window_mgr ();
1890                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1891
1892                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1893                 
1894                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1895                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1896                 if (is_fullscreen != active) {
1897                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1898                 }
1899         }
1900
1901         return FALSE;
1902
1903 }
1904
1905 static void
1906 set_homogeneous (GtkWidget *widget,
1907                  gpointer data)
1908 {
1909         if (GTK_IS_TOOL_ITEM (widget)) {
1910                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1911                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1912         }
1913 }
1914
1915 static void
1916 modest_msg_view_window_show_toolbar (ModestWindow *self,
1917                                      gboolean show_toolbar)
1918 {
1919         ModestMsgViewWindowPrivate *priv = NULL;
1920         ModestWindowPrivate *parent_priv;
1921         GtkWidget *reply_button = NULL, *menu = NULL;
1922         GtkWidget *placeholder = NULL;
1923         gint insert_index;
1924         const gchar *action_name;
1925         GtkAction *action;
1926         
1927         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1928         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1929
1930         /* Set optimized view status */
1931         priv->optimized_view = !show_toolbar;
1932
1933         if (!parent_priv->toolbar) {
1934                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1935                                                                   "/ToolBar");
1936                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1937
1938                 /* Set homogeneous toolbar */
1939                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1940                                        set_homogeneous, NULL);
1941
1942                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1943                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1944                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1945                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1946                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1947                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1948                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1949                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1950
1951                 /* Add ProgressBar (Transfer toolbar) */ 
1952                 priv->progress_bar = modest_progress_bar_widget_new ();
1953                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1954                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1955                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1956                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1957                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1958                 
1959                 /* Connect cancel 'clicked' signal to abort progress mode */
1960                 g_signal_connect(priv->cancel_toolitem, "clicked",
1961                                  G_CALLBACK(cancel_progressbar),
1962                                  self);
1963                 
1964                 /* Add it to the observers list */
1965                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1966
1967                 /* Add to window */
1968                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1969                                            GTK_TOOLBAR (parent_priv->toolbar));
1970
1971
1972                 /* Set reply button tap and hold menu */        
1973                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1974                                                           "/ToolBar/ToolbarMessageReply");
1975                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1976                                                   "/ToolbarReplyCSM");
1977                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1978         }
1979
1980         if (show_toolbar) {
1981                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1982                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1983                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1984                 
1985                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1986                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1987                 
1988         } else {
1989                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1990                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1991         }
1992
1993         /* Update also the actions (to update the toggles in the
1994            menus), we have to do it manually because some other window
1995            of the same time could have changed it (remember that the
1996            toolbar fullscreen mode is shared by all the windows of the
1997            same type */
1998         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1999                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2000         else
2001                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2002
2003         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2004         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2005                                                             show_toolbar);
2006 }
2007
2008 static void 
2009 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2010                                                GdkEvent *event,
2011                                                ModestMsgViewWindow *window)
2012 {
2013         if (!GTK_WIDGET_VISIBLE (window))
2014                 return;
2015
2016         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2017 }
2018
2019 gboolean 
2020 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2021 {
2022         ModestMsgViewWindowPrivate *priv;
2023         
2024         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2025         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2026
2027         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2028 }
2029
2030 static void
2031 cancel_progressbar (GtkToolButton *toolbutton,
2032                     ModestMsgViewWindow *self)
2033 {
2034         GSList *tmp;
2035         ModestMsgViewWindowPrivate *priv;
2036         
2037         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2038
2039         /* Get operation observers and cancel its current operation */
2040         tmp = priv->progress_widgets;
2041         while (tmp) {
2042                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2043                 tmp=g_slist_next(tmp);
2044         }
2045 }
2046 static gboolean
2047 observers_empty (ModestMsgViewWindow *self)
2048 {
2049         GSList *tmp = NULL;
2050         ModestMsgViewWindowPrivate *priv;
2051         gboolean is_empty = TRUE;
2052         guint pending_ops = 0;
2053  
2054         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2055         tmp = priv->progress_widgets;
2056
2057         /* Check all observers */
2058         while (tmp && is_empty)  {
2059                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2060                 is_empty = pending_ops == 0;
2061                 
2062                 tmp = g_slist_next(tmp);
2063         }
2064         
2065         return is_empty;
2066 }
2067
2068 static void
2069 on_account_removed (TnyAccountStore *account_store, 
2070                     TnyAccount *account,
2071                     gpointer user_data)
2072 {
2073         /* Do nothing if it's a transport account, because we only
2074            show the messages of a store account */
2075         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2076                 const gchar *parent_acc = NULL;
2077                 const gchar *our_acc = NULL;
2078
2079                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2080                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2081
2082                 /* Close this window if I'm showing a message of the removed account */
2083                 if (strcmp (parent_acc, our_acc) == 0)
2084                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2085         }
2086 }
2087
2088 static void
2089 on_queue_changed (ModestMailOperationQueue *queue,
2090                   ModestMailOperation *mail_op,
2091                   ModestMailOperationQueueNotification type,
2092                   ModestMsgViewWindow *self)
2093 {
2094         GSList *tmp;
2095         ModestMsgViewWindowPrivate *priv;
2096         ModestMailOperationTypeOperation op_type;
2097         ModestToolBarModes mode;
2098         
2099         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2100         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2101
2102         /* If this operations was created by another window, do nothing */
2103         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2104             return;
2105
2106         /* Get toolbar mode from operation id*/
2107         op_type = modest_mail_operation_get_type_operation (mail_op);
2108         switch (op_type) {
2109 /*      case MODEST_MAIL_OPERATION_TYPE_SEND: */
2110         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2111         case MODEST_MAIL_OPERATION_TYPE_OPEN:
2112                 mode = TOOLBAR_MODE_TRANSFER;
2113                 break;
2114         default:
2115                 mode = TOOLBAR_MODE_NORMAL;
2116                 
2117         }
2118                 
2119         /* Add operation observers and change toolbar if neccessary*/
2120         tmp = priv->progress_widgets;
2121         switch (type) {
2122         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2123                 if (mode == TOOLBAR_MODE_TRANSFER) {
2124                         /* Enable transfer toolbar mode */
2125                         set_toolbar_transfer_mode(self);
2126                         while (tmp) {
2127                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2128                                                                       mail_op);
2129                                 tmp = g_slist_next (tmp);
2130                         }
2131                         
2132                 }
2133                 break;
2134         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2135                 if (mode == TOOLBAR_MODE_TRANSFER) {
2136                         while (tmp) {
2137                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2138                                                                  mail_op);
2139                                 tmp = g_slist_next (tmp);
2140                                 
2141                         }
2142
2143                         /* If no more operations are being observed, NORMAL mode is enabled again */
2144                         if (observers_empty (self)) {
2145                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2146                         }
2147                 }
2148                 break;
2149         }
2150 }
2151
2152 GList *
2153 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2154 {
2155         ModestMsgViewWindowPrivate *priv;
2156         GList *selected_attachments = NULL;
2157         
2158         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2159         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2160
2161         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2162         
2163         return selected_attachments;
2164 }
2165
2166 void
2167 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2168 {
2169         ModestMsgViewWindowPrivate *priv;
2170         const gchar *msg_uid;
2171         gchar *attachment_uid = NULL;
2172         gint attachment_index = 0;
2173         GList *attachments;
2174
2175         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2176         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2177         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2178
2179         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2180         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2181         attachment_index = g_list_index (attachments, mime_part);
2182         g_list_free (attachments);
2183         
2184         if (msg_uid && attachment_index >= 0) {
2185                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2186         }
2187
2188         if (mime_part == NULL) {
2189                 gboolean error = FALSE;
2190                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2191                 if (selected_attachments == NULL) {
2192                         error = TRUE;
2193                 } else if (g_list_length (selected_attachments) > 1) {
2194                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2195                         error = TRUE;
2196                 } else {
2197                         mime_part = (TnyMimePart *) selected_attachments->data;
2198                         g_object_ref (mime_part);
2199                 }
2200                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2201                 g_list_free (selected_attachments);
2202
2203                 if (error)
2204                         return;
2205         } else {
2206                 g_object_ref (mime_part);
2207         }
2208
2209         if (tny_mime_part_is_purged (mime_part)) {
2210                 g_object_unref (mime_part);
2211                 return;
2212         }
2213
2214         if (!TNY_IS_MSG (mime_part)) {
2215                 gchar *filepath = NULL;
2216                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2217                 TnyFsStream *temp_stream = NULL;
2218                 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, attachment_uid, &filepath);
2219                 
2220                 if (temp_stream) {
2221                         const gchar *content_type;
2222                         content_type = tny_mime_part_get_content_type (mime_part);
2223                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2224                         
2225                         modest_platform_activate_file (filepath, content_type);
2226                         g_object_unref (temp_stream);
2227                         g_free (filepath);
2228                         /* NOTE: files in the temporary area will be automatically
2229                          * cleaned after some time if they are no longer in use */
2230                 }
2231         } else {
2232                 /* message attachment */
2233                 TnyHeader *header = NULL;
2234                 ModestWindowMgr *mgr;
2235                 ModestWindow *msg_win = NULL;
2236                 gboolean found;
2237                 
2238                 header = tny_msg_get_header (TNY_MSG (mime_part));
2239                 mgr = modest_runtime_get_window_mgr ();         
2240                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2241
2242                 if (found) {
2243                         if (msg_win)                            /* there is already a window for this uid; top it */
2244                                 gtk_window_present (GTK_WINDOW(msg_win));
2245                         else 
2246                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2247                                  * thus, we don't do anything */
2248                                 g_warning ("window for is already being created");
2249                 } else { 
2250                         /* it's not found, so create a new window for it */
2251                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2252                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2253                         if (!account)
2254                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2255                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2256                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
2257                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2258                         modest_window_mgr_register_window (mgr, msg_win);
2259                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2260                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2261                 }
2262         }
2263         g_object_unref (mime_part);
2264 }
2265
2266 typedef struct
2267 {
2268         gchar *filename;
2269         TnyMimePart *part;
2270 } SaveMimePartPair;
2271
2272 typedef struct
2273 {
2274         GList *pairs;
2275         GtkWidget *banner;
2276         gboolean result;
2277 } SaveMimePartInfo;
2278
2279 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2280 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2281 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2282 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2283
2284 static void 
2285 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2286 {
2287         
2288         GList *node;
2289         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2290                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2291                 g_free (pair->filename);
2292                 g_object_unref (pair->part);
2293                 g_slice_free (SaveMimePartPair, pair);
2294         }
2295         g_list_free (info->pairs);
2296         info->pairs = NULL;
2297         if (with_struct) {
2298                 gtk_widget_destroy (info->banner);
2299                 g_object_unref (info->banner);
2300                 g_slice_free (SaveMimePartInfo, info);
2301         }
2302 }
2303
2304 static gboolean
2305 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2306 {
2307         if (info->pairs != NULL) {
2308                 save_mime_part_to_file (info);
2309         } else {
2310                 gboolean result;
2311                 result = info->result;
2312
2313                 /* This is a GDK lock because we are an idle callback and
2314                  * hildon_banner_show_information is or does Gtk+ code */
2315
2316                 gdk_threads_enter (); /* CHECKED */
2317                 save_mime_part_info_free (info, TRUE);
2318                 if (result) {
2319                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2320                 } else {
2321                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2322                 }
2323                 gdk_threads_leave (); /* CHECKED */
2324         }
2325
2326         return FALSE;
2327 }
2328
2329 static gpointer
2330 save_mime_part_to_file (SaveMimePartInfo *info)
2331 {
2332         GnomeVFSResult result;
2333         GnomeVFSHandle *handle;
2334         TnyStream *stream;
2335         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2336
2337         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0444);
2338         if (result == GNOME_VFS_OK) {
2339                 stream = tny_vfs_stream_new (handle);
2340                 tny_mime_part_decode_to_stream (pair->part, stream);
2341                 g_object_unref (G_OBJECT (stream));
2342                 g_object_unref (pair->part);
2343                 g_slice_free (SaveMimePartPair, pair);
2344                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2345                 info->result = TRUE;
2346         } else {
2347                 save_mime_part_info_free (info, FALSE);
2348                 info->result = FALSE;
2349         }
2350
2351         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2352         return NULL;
2353 }
2354
2355 static void
2356 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2357 {
2358         gboolean is_ok = TRUE;
2359         gint replaced_files = 0;
2360         const GList *files = info->pairs;
2361         const GList *iter;
2362
2363         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2364                 SaveMimePartPair *pair = iter->data;
2365                 if (modest_maemo_utils_file_exists (pair->filename)) {
2366                         replaced_files++;
2367                 }
2368         }
2369         if (replaced_files) {
2370                 GtkWidget *confirm_overwrite_dialog;
2371                 const gchar *message = (replaced_files == 1) ?
2372                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2373                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2374                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2375                         is_ok = FALSE;
2376                 }
2377                 gtk_widget_destroy (confirm_overwrite_dialog);
2378         }
2379
2380         if (!is_ok) {
2381                 save_mime_part_info_free (info, TRUE);
2382         } else {
2383                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2384                                                                   _CS("sfil_ib_saving"));
2385                 info->banner = g_object_ref (banner);
2386                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2387                 g_object_unref (banner);
2388         }
2389
2390 }
2391
2392
2393 void
2394 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2395 {
2396         gboolean clean_list = FALSE;
2397         ModestMsgViewWindowPrivate *priv;
2398         GList *files_to_save = NULL;
2399         GtkWidget *save_dialog = NULL;
2400         gchar *folder = NULL;
2401         gboolean canceled = FALSE;
2402         const gchar *filename = NULL;
2403         gchar *save_multiple_str = NULL;
2404
2405         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2406         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2407
2408         if (mime_parts == NULL) {
2409                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2410                 if (mime_parts == NULL)
2411                         return;
2412                 clean_list = TRUE;
2413         }
2414
2415         /* prepare dialog */
2416         if (mime_parts->next == NULL) {
2417                 /* only one attachment selected */
2418                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2419                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2420                         filename = tny_mime_part_get_filename (mime_part);
2421                 } else {
2422                         g_warning ("Tried to save a non-file attachment");
2423                         canceled = TRUE;
2424                 }
2425         } else {
2426                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2427                                                      g_list_length (mime_parts));
2428         }
2429         
2430         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2431                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2432
2433         /* set folder */
2434         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2435         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2436         g_free (folder);
2437
2438         /* set filename */
2439         if (filename != NULL)
2440                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2441                                                    filename);
2442
2443         /* if multiple, set multiple string */
2444         if (save_multiple_str) {
2445                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2446         }
2447                 
2448         /* show dialog */
2449         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2450                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2451
2452                 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2453                         hildon_banner_show_information 
2454                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2455                 } else {
2456                         GList *node = NULL;
2457
2458                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2459                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2460                                 
2461                                 if (tny_mime_part_is_attachment (mime_part)) {
2462                                         SaveMimePartPair *pair;
2463
2464                                         if ((mime_parts->next != NULL) &&
2465                                             (tny_mime_part_get_filename (mime_part) == NULL))
2466                                                 continue;
2467                                         
2468                                         pair = g_slice_new0 (SaveMimePartPair);
2469                                         if (mime_parts->next == NULL) {
2470                                                 pair->filename = g_strdup (chooser_uri);
2471                                         } else {
2472                                                 pair->filename = 
2473                                                         g_build_filename (chooser_uri,
2474                                                                           tny_mime_part_get_filename (mime_part), NULL);
2475                                         }
2476                                         pair->part = g_object_ref (mime_part);
2477                                         files_to_save = g_list_prepend (files_to_save, pair);
2478                                 }
2479                         }
2480                 }
2481                 g_free (chooser_uri);
2482         }
2483
2484         gtk_widget_destroy (save_dialog);
2485
2486         if (clean_list) {
2487                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2488                 g_list_free (mime_parts);
2489         }
2490
2491         if (files_to_save != NULL) {
2492                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2493                 info->pairs = files_to_save;
2494                 info->result = TRUE;
2495                 save_mime_parts_to_file_with_checks (info);
2496         }
2497 }
2498
2499 static gboolean
2500 show_remove_attachment_information (gpointer userdata)
2501 {
2502         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2503         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2504
2505         /* We're outside the main lock */
2506         gdk_threads_enter ();
2507
2508         if (priv->remove_attachment_banner != NULL) {
2509                 gtk_widget_destroy (priv->remove_attachment_banner);
2510                 g_object_unref (priv->remove_attachment_banner);
2511         }
2512
2513         priv->remove_attachment_banner = g_object_ref (
2514                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2515
2516         gdk_threads_leave ();
2517
2518         return FALSE;
2519 }
2520
2521 void
2522 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2523 {
2524         ModestMsgViewWindowPrivate *priv;
2525         GList *mime_parts = NULL, *node;
2526         gchar *confirmation_message;
2527         gint response;
2528         gint n_attachments;
2529         TnyMsg *msg;
2530 /*      TnyFolder *folder; */
2531
2532         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2533         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2534
2535         if (get_all)
2536                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2537         else
2538                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2539                 
2540         /* Remove already purged messages from mime parts list */
2541         node = mime_parts;
2542         while (node != NULL) {
2543                 TnyMimePart *part = TNY_MIME_PART (node->data);
2544                 if (tny_mime_part_is_purged (part)) {
2545                         GList *deleted_node = node;
2546                         node = g_list_next (node);
2547                         g_object_unref (part);
2548                         mime_parts = g_list_delete_link (mime_parts, deleted_node);
2549                 } else {
2550                         node = g_list_next (node);
2551                 }
2552         }
2553
2554         if (mime_parts == NULL)
2555                 return;
2556
2557         n_attachments = g_list_length (mime_parts);
2558         if (n_attachments == 1) {
2559                 const gchar *filename;
2560
2561                 if (TNY_IS_MSG (mime_parts->data)) {
2562                         TnyHeader *header;
2563                         header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2564                         filename = tny_header_get_subject (header);
2565                         g_object_unref (header);
2566                         if (filename == NULL)
2567                                 filename = _("mail_va_no_subject");
2568                 } else {
2569                         filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2570                 }
2571                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2572         } else {
2573                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2574                                                                  "mcen_nc_purge_files_text", 
2575                                                                  n_attachments), n_attachments);
2576         }
2577         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2578                                                             confirmation_message);
2579         g_free (confirmation_message);
2580
2581         if (response != GTK_RESPONSE_OK)
2582                 return;
2583
2584         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2585 /*      folder = tny_msg_get_folder (msg); */
2586 /*      tny_msg_uncache_attachments (msg); */
2587 /*      tny_folder_refresh (folder, NULL); */
2588 /*      g_object_unref (folder); */
2589         
2590         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2591                 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2592 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2593         }
2594
2595         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2596         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2597         tny_msg_rewrite_cache (msg);
2598         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2599
2600         g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2601         g_list_free (mime_parts);
2602
2603         if (priv->purge_timeout > 0) {
2604                 g_source_remove (priv->purge_timeout);
2605                 priv->purge_timeout = 0;
2606         }
2607
2608         if (priv->remove_attachment_banner) {
2609                 gtk_widget_destroy (priv->remove_attachment_banner);
2610                 g_object_unref (priv->remove_attachment_banner);
2611                 priv->remove_attachment_banner = NULL;
2612         }
2613
2614
2615 }
2616
2617
2618 static void
2619 update_window_title (ModestMsgViewWindow *window)
2620 {
2621         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2622         TnyMsg *msg = NULL;
2623         TnyHeader *header = NULL;
2624         const gchar *subject = NULL;
2625         
2626         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2627
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
2641 static void on_move_focus (ModestMsgViewWindow *window,
2642                            GtkDirectionType direction,
2643                            gpointer userdata)
2644 {
2645         GtkWidget *current_focus = NULL;
2646
2647         current_focus = gtk_window_get_focus (GTK_WINDOW (window));
2648         if ((current_focus != NULL) &&
2649             MODEST_IS_ATTACHMENTS_VIEW (current_focus)) {
2650                 g_signal_stop_emission_by_name (G_OBJECT (window), "move-focus");
2651         }
2652 }
2653