e2f203442e6541fd983a36de345f233f054c4d33
[modest] / src / hildon2 / 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-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.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-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include "hildon/hildon-pannable-area.h"
52 #include <hildon/hildon-app-menu.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <math.h>
62 #include <errno.h>
63 #include <glib/gstdio.h>
64 #include <modest-debug.h>
65 #include <modest-header-window.h>
66
67 #define DEFAULT_FOLDER "MyDocs/.documents"
68
69 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
70 struct _ModestMsgViewWindowPrivate {
71
72         GtkWidget   *msg_view;
73         GtkWidget   *main_scroll;
74         GtkWidget   *find_toolbar;
75         gchar       *last_search;
76
77         /* Progress observers */
78         GSList           *progress_widgets;
79
80         /* Tollbar items */
81         GtkWidget   *prev_toolitem;
82         GtkWidget   *next_toolitem;
83         gboolean    progress_hint;
84
85         /* Optimized view enabled */
86         gboolean optimized_view;
87
88         /* Whether this was created via the *_new_for_search_result() function. */
89         gboolean is_search_result;
90
91         /* Whether the message is in outbox */
92         gboolean is_outbox;
93         
94         /* A reference to the @model of the header view 
95          * to allow selecting previous/next messages,
96          * if the message is currently selected in the header view.
97          */
98         const gchar *header_folder_id;
99         GtkTreeModel *header_model;
100         GtkTreeRowReference *row_reference;
101         GtkTreeRowReference *next_row_reference;
102
103         gulong clipboard_change_handler;
104         gulong queue_change_handler;
105         gulong account_removed_handler;
106         gulong row_changed_handler;
107         gulong row_deleted_handler;
108         gulong row_inserted_handler;
109         gulong rows_reordered_handler;
110
111         guint purge_timeout;
112         GtkWidget *remove_attachment_banner;
113
114         gchar *msg_uid;
115         
116         GSList *sighandlers;
117 };
118
119 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
120 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
121 static void  modest_header_view_observer_init(
122                 ModestHeaderViewObserverIface *iface_class);
123 static void  modest_msg_view_window_finalize     (GObject *obj);
124 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
125                                                          gpointer data);
126 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
127                                                         ModestMsgViewWindow *obj);
128 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
129                                                         ModestMsgViewWindow *obj);
130
131 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
132
133 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
134 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
135                                                   GdkEventKey *event,
136                                                   gpointer userdata);
137 static gboolean modest_msg_view_window_toggle_menu (HildonWindow *window,
138                                                     guint button,
139                                                     guint32 time);
140 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
141
142 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
143                                                    gboolean show_toolbar);
144
145 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
146                                                            GdkEvent *event,
147                                                            ModestMsgViewWindow *window);
148
149 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
150                                                    GtkTreePath *arg1,
151                                                    GtkTreeIter *arg2,
152                                                    ModestMsgViewWindow *window);
153
154 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
155                                                    GtkTreePath *arg1,
156                                                    ModestMsgViewWindow *window);
157
158 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
159                                                     GtkTreePath *tree_path,
160                                                     GtkTreeIter *tree_iter,
161                                                     ModestMsgViewWindow *window);
162
163 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
164                                                      GtkTreePath *arg1,
165                                                      GtkTreeIter *arg2,
166                                                      gpointer arg3,
167                                                      ModestMsgViewWindow *window);
168
169 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
170                                                           GtkTreeModel *model,
171                                                           const gchar *tny_folder_id);
172
173 static void on_queue_changed    (ModestMailOperationQueue *queue,
174                                  ModestMailOperation *mail_op,
175                                  ModestMailOperationQueueNotification type,
176                                  ModestMsgViewWindow *self);
177
178 static void on_account_removed  (TnyAccountStore *account_store, 
179                                  TnyAccount *account,
180                                  gpointer user_data);
181
182 static void on_move_focus (GtkWidget *widget,
183                            GtkDirectionType direction,
184                            gpointer userdata);
185
186 static void view_msg_cb         (ModestMailOperation *mail_op, 
187                                  TnyHeader *header, 
188                                  gboolean canceled,
189                                  TnyMsg *msg, 
190                                  GError *error,
191                                  gpointer user_data);
192
193 static void set_progress_hint    (ModestMsgViewWindow *self, 
194                                   gboolean enabled);
195
196 static void update_window_title (ModestMsgViewWindow *window);
197
198 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
199 static void init_window (ModestMsgViewWindow *obj);
200
201 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
202
203 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
204
205 static gboolean on_fetch_image (ModestMsgView *msgview,
206                                 const gchar *uri,
207                                 TnyStream *stream,
208                                 ModestMsgViewWindow *window);
209
210 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
211                                                      GtkScrollType scroll_type,
212                                                      gboolean horizontal,
213                                                      gpointer userdata);
214 static gboolean message_reader (ModestMsgViewWindow *window,
215                                 ModestMsgViewWindowPrivate *priv,
216                                 TnyHeader *header,
217                                 GtkTreeRowReference *row_reference);
218
219 static void add_to_menu (ModestMsgViewWindow *self,
220                          HildonAppMenu *menu,
221                          gchar *label,
222                          GCallback callback,
223                          ModestDimmingRulesGroup *group,
224                          GCallback dimming_callback);
225 static void setup_menu (ModestMsgViewWindow *self,
226                         ModestDimmingRulesGroup *group);
227
228 /* list my signals */
229 enum {
230         MSG_CHANGED_SIGNAL,
231         SCROLL_CHILD_SIGNAL,
232         LAST_SIGNAL
233 };
234
235 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
236         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
237 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
238         { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
239 =======
240 };
241
242 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
243 struct _ModestMsgViewWindowPrivate {
244
245         GtkWidget   *msg_view;
246         GtkWidget   *main_scroll;
247         GtkWidget   *find_toolbar;
248         gchar       *last_search;
249
250         /* Progress observers */
251         GtkWidget        *progress_bar;
252         GSList           *progress_widgets;
253
254         /* Tollbar items */
255         GtkWidget   *progress_toolitem;
256         GtkWidget   *cancel_toolitem;
257         GtkWidget   *prev_toolitem;
258         GtkWidget   *next_toolitem;
259         ModestToolBarModes current_toolbar_mode;
260
261         /* Optimized view enabled */
262         gboolean optimized_view;
263
264         /* Whether this was created via the *_new_for_search_result() function. */
265         gboolean is_search_result;
266
267         /* Whether the message is in outbox */
268         gboolean is_outbox;
269         
270         /* A reference to the @model of the header view 
271          * to allow selecting previous/next messages,
272          * if the message is currently selected in the header view.
273          */
274         const gchar *header_folder_id;
275         GtkTreeModel *header_model;
276         GtkTreeRowReference *row_reference;
277         GtkTreeRowReference *next_row_reference;
278
279         gulong clipboard_change_handler;
280         gulong queue_change_handler;
281         gulong account_removed_handler;
282         gulong row_changed_handler;
283         gulong row_deleted_handler;
284         gulong row_inserted_handler;
285         gulong rows_reordered_handler;
286
287         guint purge_timeout;
288         GtkWidget *remove_attachment_banner;
289
290         guint progress_bar_timeout;
291
292         gchar *msg_uid;
293         
294         GSList *sighandlers;
295 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
296 };
297
298 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
299                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
300                                                     ModestMsgViewWindowPrivate))
301 /* globals */
302 static GtkWindowClass *parent_class = NULL;
303
304 /* uncomment the following if you have defined any signals */
305 static guint signals[LAST_SIGNAL] = {0};
306
307 GType
308 modest_msg_view_window_get_type (void)
309 {
310         static GType my_type = 0;
311         if (!my_type) {
312                 static const GTypeInfo my_info = {
313                         sizeof(ModestMsgViewWindowClass),
314                         NULL,           /* base init */
315                         NULL,           /* base finalize */
316                         (GClassInitFunc) modest_msg_view_window_class_init,
317                         NULL,           /* class finalize */
318                         NULL,           /* class data */
319                         sizeof(ModestMsgViewWindow),
320                         1,              /* n_preallocs */
321                         (GInstanceInitFunc) modest_msg_view_window_init,
322                         NULL
323                 };
324                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
325                                                   "ModestMsgViewWindow",
326                                                   &my_info, 0);
327
328                 static const GInterfaceInfo modest_header_view_observer_info = 
329                 {
330                         (GInterfaceInitFunc) modest_header_view_observer_init,
331                         NULL,         /* interface_finalize */
332                         NULL          /* interface_data */
333                 };
334
335                 g_type_add_interface_static (my_type,
336                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
337                                 &modest_header_view_observer_info);
338         }
339         return my_type;
340 }
341
342 static void
343 save_state (ModestWindow *self)
344 {
345         modest_widget_memory_save (modest_runtime_get_conf (),
346                                    G_OBJECT(self), 
347                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
348 }
349
350 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
351
352 static void
353 restore_settings (ModestMsgViewWindow *self)
354 {
355         ModestConf *conf;
356
357         conf = modest_runtime_get_conf ();
358         modest_widget_memory_restore (conf,
359                                       G_OBJECT(self), 
360                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
361 }
362
363 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
364                                                      GtkScrollType scroll_type,
365                                                      gboolean horizontal,
366                                                      gpointer userdata)
367 =======
368 static 
369 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
370                                               GtkScrollType scroll_type,
371                                               gboolean horizontal,
372                                               gpointer userdata)
373 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
374 {
375         ModestMsgViewWindowPrivate *priv;
376         gboolean return_value;
377
378         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
379         g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
380         return return_value;
381 }
382
383 static void
384 add_scroll_binding (GtkBindingSet *binding_set,
385                     guint keyval,
386                     GtkScrollType scroll)
387 {
388         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
389         
390         gtk_binding_entry_add_signal (binding_set, keyval, 0,
391                                       "scroll_child", 2,
392                                       GTK_TYPE_SCROLL_TYPE, scroll,
393                                       G_TYPE_BOOLEAN, FALSE);
394         gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
395                                       "scroll_child", 2,
396                                       GTK_TYPE_SCROLL_TYPE, scroll,
397                                       G_TYPE_BOOLEAN, FALSE);
398 }
399
400 static void
401 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
402 {
403         GObjectClass *gobject_class;
404         HildonWindowClass *hildon_window_class;
405         ModestWindowClass *modest_window_class;
406         GtkBindingSet *binding_set;
407
408         gobject_class = (GObjectClass*) klass;
409         hildon_window_class = (HildonWindowClass *) klass;
410         modest_window_class = (ModestWindowClass *) klass;
411
412         parent_class            = g_type_class_peek_parent (klass);
413         gobject_class->finalize = modest_msg_view_window_finalize;
414
415         hildon_window_class->toggle_menu = modest_msg_view_window_toggle_menu;
416
417         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
418         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
419         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
420         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
421
422         modest_window_class->save_state_func = save_state;
423
424         klass->scroll_child = modest_msg_view_window_scroll_child;
425
426         signals[MSG_CHANGED_SIGNAL] =
427                 g_signal_new ("msg-changed",
428                               G_TYPE_FROM_CLASS (gobject_class),
429                               G_SIGNAL_RUN_FIRST,
430                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
431                               NULL, NULL,
432                               modest_marshal_VOID__POINTER_POINTER,
433                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
434
435         signals[SCROLL_CHILD_SIGNAL] =
436                 g_signal_new ("scroll-child",
437                               G_TYPE_FROM_CLASS (gobject_class),
438                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
439                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
440                               NULL, NULL,
441                               modest_marshal_BOOLEAN__ENUM_BOOLEAN,
442                               G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
443
444         binding_set = gtk_binding_set_by_class (klass);
445         add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
446         add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
447         add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
448         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
449         add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
450         add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
451
452         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
453
454 }
455
456 static void modest_header_view_observer_init(
457                 ModestHeaderViewObserverIface *iface_class)
458 {
459         iface_class->update_func = modest_msg_view_window_update_model_replaced;
460 }
461
462 static void
463 modest_msg_view_window_init (ModestMsgViewWindow *obj)
464 {
465         ModestMsgViewWindowPrivate *priv;
466         ModestWindowPrivate *parent_priv = NULL;
467         GtkActionGroup *action_group = NULL;
468         GError *error = NULL;
469         GdkPixbuf *window_icon;
470
471         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
472         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
473         parent_priv->ui_manager = gtk_ui_manager_new();
474
475         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
476         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
477
478         /* Add common actions */
479         gtk_action_group_add_actions (action_group,
480                                       modest_action_entries,
481                                       G_N_ELEMENTS (modest_action_entries),
482                                       obj);
483         gtk_action_group_add_toggle_actions (action_group,
484                                              msg_view_toggle_action_entries,
485                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
486                                              obj);
487
488         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
489         g_object_unref (action_group);
490
491         /* Load the UI definition */
492         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
493                                          &error);
494         if (error) {
495                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
496                 g_error_free (error);
497                 error = NULL;
498         }
499         /* ****** */
500
501         /* Add accelerators */
502         gtk_window_add_accel_group (GTK_WINDOW (obj), 
503                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
504         
505         priv->is_search_result = FALSE;
506         priv->is_outbox = FALSE;
507
508         priv->msg_view      = NULL;
509         priv->header_model  = NULL;
510         priv->header_folder_id  = NULL;
511         priv->clipboard_change_handler = 0;
512         priv->queue_change_handler = 0;
513         priv->account_removed_handler = 0;
514         priv->row_changed_handler = 0;
515         priv->row_deleted_handler = 0;
516         priv->row_inserted_handler = 0;
517         priv->rows_reordered_handler = 0;
518         priv->progress_hint = FALSE;
519
520         priv->optimized_view  = FALSE;
521         priv->purge_timeout = 0;
522         priv->remove_attachment_banner = NULL;
523         priv->msg_uid = NULL;
524         
525         priv->sighandlers = NULL;
526         
527         /* Init window */
528         init_window (MODEST_MSG_VIEW_WINDOW(obj));
529         
530         /* Set window icon */
531         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); 
532         if (window_icon) {
533                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
534                 g_object_unref (window_icon);
535         }       
536         
537         hildon_program_add_window (hildon_program_get_instance(),
538                                    HILDON_WINDOW(obj));
539
540 }
541
542
543 static gboolean
544 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
545 {
546         ModestMsgViewWindowPrivate *priv = NULL;
547         
548         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
549
550         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
551
552         set_progress_hint (self, TRUE);
553         
554         return FALSE;
555 }
556
557 static void 
558 set_progress_hint (ModestMsgViewWindow *self, 
559                    gboolean enabled)
560 {
561         ModestWindowPrivate *parent_priv;
562         ModestMsgViewWindowPrivate *priv;
563
564         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
565
566         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
567         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
568                         
569         /* Sets current progress hint */
570         priv->progress_hint = enabled;
571
572         if (GTK_WIDGET_VISIBLE (self)) {
573                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
574         }
575
576 }
577
578
579 static void
580 init_window (ModestMsgViewWindow *obj)
581 {
582         GtkWidget *main_vbox;
583         ModestMsgViewWindowPrivate *priv;
584
585         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
586
587         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
588         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
589         main_vbox = gtk_vbox_new  (FALSE, 6);
590 #ifdef MODEST_TOOLKIT_HILDON2
591         priv->main_scroll = hildon_pannable_area_new ();
592         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
593 #else
594 #ifdef MODEST_USE_MOZEMBED
595         priv->main_scroll = priv->msg_view;
596         gtk_widget_set_size_request (priv->msg_view, -1, 1600);
597 #else
598         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
599         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
600 #endif
601         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
602         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
603         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
604
605 #endif
606         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
607         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
608
609         priv->find_toolbar = hildon_find_toolbar_new (NULL);
610         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
611         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
612         
613         gtk_widget_show_all (GTK_WIDGET(main_vbox));
614 }
615
616 static void
617 modest_msg_view_window_disconnect_signals (ModestWindow *self)
618 {
619         ModestMsgViewWindowPrivate *priv;
620         GtkWidget *header_view = NULL;
621         GtkWindow *parent_window = NULL;
622         
623         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
624
625         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
626             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
627                                            priv->clipboard_change_handler)) 
628                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
629                                              priv->clipboard_change_handler);
630
631         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
632                                            priv->queue_change_handler))
633                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
634                                              priv->queue_change_handler);
635
636         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
637                                            priv->account_removed_handler))
638                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
639                                              priv->account_removed_handler);
640
641         if (priv->header_model) {
642                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
643                                                   priv->row_changed_handler))
644                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
645                                                     priv->row_changed_handler);
646                 
647                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
648                                                   priv->row_deleted_handler))
649                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
650                                              priv->row_deleted_handler);
651                 
652                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
653                                                   priv->row_inserted_handler))
654                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
655                                                     priv->row_inserted_handler);
656                 
657                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
658                                                   priv->rows_reordered_handler))
659                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
660                                                     priv->rows_reordered_handler);
661         }
662
663         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
664         priv->sighandlers = NULL;
665
666         parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
667         if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
668                 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
669                 if (header_view) {
670                         modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
671                                                            MODEST_HEADER_VIEW_OBSERVER(self));
672                 }
673         }
674 }       
675
676 static void
677 modest_msg_view_window_finalize (GObject *obj)
678 {
679         ModestMsgViewWindowPrivate *priv;
680
681         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
682
683         /* Sanity check: shouldn't be needed, the window mgr should
684            call this function before */
685         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
686
687         if (priv->header_model != NULL) {
688                 g_object_unref (priv->header_model);
689                 priv->header_model = NULL;
690         }
691
692         if (priv->remove_attachment_banner) {
693                 gtk_widget_destroy (priv->remove_attachment_banner);
694                 g_object_unref (priv->remove_attachment_banner);
695                 priv->remove_attachment_banner = NULL;
696         }
697
698         if (priv->purge_timeout > 0) {
699                 g_source_remove (priv->purge_timeout);
700                 priv->purge_timeout = 0;
701         }
702
703         if (priv->row_reference) {
704                 gtk_tree_row_reference_free (priv->row_reference);
705                 priv->row_reference = NULL;
706         }
707
708         if (priv->next_row_reference) {
709                 gtk_tree_row_reference_free (priv->next_row_reference);
710                 priv->next_row_reference = NULL;
711         }
712
713         if (priv->msg_uid) {
714                 g_free (priv->msg_uid);
715                 priv->msg_uid = NULL;
716         }
717
718         G_OBJECT_CLASS(parent_class)->finalize (obj);
719 }
720
721 static gboolean
722 select_next_valid_row (GtkTreeModel *model,
723                        GtkTreeRowReference **row_reference,
724                        gboolean cycle,
725                        gboolean is_outbox)
726 {
727         GtkTreeIter tmp_iter;
728         GtkTreePath *path;
729         GtkTreePath *next = NULL;
730         gboolean retval = FALSE, finished;
731
732         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
733
734         path = gtk_tree_row_reference_get_path (*row_reference);
735         gtk_tree_model_get_iter (model, &tmp_iter, path);
736         gtk_tree_row_reference_free (*row_reference);
737         *row_reference = NULL;
738
739         finished = FALSE;
740         do {
741                 TnyHeader *header = NULL;
742
743                 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
744                         gtk_tree_model_get (model, &tmp_iter, 
745                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
746                                             &header, -1);
747
748                         if (header) {
749                                 if (msg_is_visible (header, is_outbox)) {
750                                         next = gtk_tree_model_get_path (model, &tmp_iter);
751                                         *row_reference = gtk_tree_row_reference_new (model, next);
752                                         gtk_tree_path_free (next);
753                                         retval = TRUE;
754                                         finished = TRUE;
755                                 }
756                                 g_object_unref (header);
757                                 header = NULL;
758                         }
759                 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
760                         next = gtk_tree_model_get_path (model, &tmp_iter);
761                         
762                         /* Ensure that we are not selecting the same */
763                         if (gtk_tree_path_compare (path, next) != 0) {
764                                 gtk_tree_model_get (model, &tmp_iter, 
765                                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
766                                                     &header, -1);                               
767                                 if (header) {
768                                         if (msg_is_visible (header, is_outbox)) {
769                                                 *row_reference = gtk_tree_row_reference_new (model, next);
770                                                 retval = TRUE;
771                                                 finished = TRUE;
772                                         }
773                                         g_object_unref (header);
774                                         header = NULL;
775                                 }
776                         } else {
777                                 /* If we ended up in the same message
778                                    then there is no valid next
779                                    message */
780                                 finished = TRUE;
781                         }
782                         gtk_tree_path_free (next);
783                 } else {
784                         /* If there are no more messages and we don't
785                            want to start again in the first one then
786                            there is no valid next message */
787                         finished = TRUE;
788                 }
789         } while (!finished);
790
791         /* Free */
792         gtk_tree_path_free (path);
793
794         return retval;
795 }
796
797 /* TODO: This should be in _init(), with the parameters as properties. */
798 static void
799 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
800                                   const gchar *modest_account_name,
801                                   const gchar *msg_uid)
802 {
803         GObject *obj = NULL;
804         ModestMsgViewWindowPrivate *priv = NULL;
805         ModestWindowPrivate *parent_priv = NULL;
806         ModestDimmingRulesGroup *menu_rules_group = NULL;
807         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
808         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
809
810         obj = G_OBJECT (self);
811         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
812         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
813
814         priv->msg_uid = g_strdup (msg_uid);
815
816         /* Menubar */
817         parent_priv->menubar = NULL;
818         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
819
820         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
821         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
822         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
823
824         setup_menu (self, menu_rules_group);
825         /* Add common dimming rules */
826         modest_dimming_rules_group_add_rules (menu_rules_group, 
827                                               modest_msg_view_menu_dimming_entries,
828                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
829                                               MODEST_WINDOW (self));
830         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
831                                               modest_msg_view_toolbar_dimming_entries,
832                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
833                                               MODEST_WINDOW (self));
834         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
835                                               modest_msg_view_clipboard_dimming_entries,
836                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
837                                               MODEST_WINDOW (self));
838
839         /* Insert dimming rules group for this window */
840         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
841         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
842         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
843         g_object_unref (menu_rules_group);
844         g_object_unref (toolbar_rules_group);
845         g_object_unref (clipboard_rules_group);
846
847         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
848
849         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);
850         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
851                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
852         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
853                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
854         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
855                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
856         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
857                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
858         g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
859                           G_CALLBACK (modest_ui_actions_on_details), obj);
860         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
861                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
862         g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
863                           G_CALLBACK (on_fetch_image), obj);
864
865         g_signal_connect (G_OBJECT (obj), "key-release-event",
866                           G_CALLBACK (modest_msg_view_window_key_event),
867                           NULL);
868
869         g_signal_connect (G_OBJECT (obj), "key-press-event",
870                           G_CALLBACK (modest_msg_view_window_key_event),
871                           NULL);
872
873         g_signal_connect (G_OBJECT (obj), "move-focus",
874                           G_CALLBACK (on_move_focus), obj);
875
876         /* Mail Operation Queue */
877         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
878                                                        "queue-changed",
879                                                        G_CALLBACK (on_queue_changed),
880                                                        obj);
881
882         /* Account manager */
883         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
884                                                           "account_removed",
885                                                           G_CALLBACK(on_account_removed),
886                                                           obj);
887
888         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
889
890         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
891         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
892         priv->last_search = NULL;
893
894         modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
895
896         /* Init the clipboard actions dim status */
897         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
898
899         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
900
901
902 }
903
904 /* FIXME: parameter checks */
905 ModestWindow *
906 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
907                                               const gchar *modest_account_name,
908                                               const gchar *msg_uid,
909                                               GtkTreeModel *model, 
910                                               GtkTreeRowReference *row_reference)
911 {
912         ModestMsgViewWindow *window = NULL;
913         ModestMsgViewWindowPrivate *priv = NULL;
914         TnyFolder *header_folder = NULL;
915         ModestHeaderView *header_view = NULL;
916         ModestWindow *main_window = NULL;
917         ModestWindowMgr *mgr = NULL;
918
919         MODEST_DEBUG_BLOCK (
920                modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
921         );
922
923         mgr = modest_runtime_get_window_mgr ();
924         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
925         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
926
927         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
928
929         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
930
931         /* Remember the message list's TreeModel so we can detect changes 
932          * and change the list selection when necessary: */
933
934         main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
935         if (main_window) {
936                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
937                                                          MODEST_MAIN_WINDOW(main_window),
938                                                          MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
939         }
940         
941         if (header_view != NULL){
942                 header_folder = modest_header_view_get_folder(header_view);
943                 /* This could happen if the header folder was
944                    unseleted before opening this msg window (for
945                    example if the user selects an account in the
946                    folder view of the main window */
947                 if (header_folder) {
948                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
949                         priv->header_folder_id = tny_folder_get_id(header_folder);
950                         g_assert(priv->header_folder_id != NULL);
951                         g_object_unref(header_folder);
952                 }
953         }
954
955         /* Setup row references and connect signals */
956         priv->header_model = g_object_ref (model);
957
958         if (row_reference) {
959                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
960                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
961                 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
962         } else {
963                 priv->row_reference = NULL;
964                 priv->next_row_reference = NULL;
965         }
966
967         /* Connect signals */
968         priv->row_changed_handler = 
969                 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
970                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
971                                   window);
972         priv->row_deleted_handler = 
973                 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
974                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
975                                   window);
976         priv->row_inserted_handler = 
977                 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
978                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
979                                   window);
980         priv->rows_reordered_handler = 
981                 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
982                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
983                                  window);
984
985         if (header_view != NULL){
986                 modest_header_view_add_observer(header_view,
987                                 MODEST_HEADER_VIEW_OBSERVER(window));
988         }
989
990         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
991         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
992
993         /* gtk_widget_show_all (GTK_WIDGET (window)); */
994         modest_msg_view_window_update_priority (window);
995         /* Check dimming rules */
996         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
997         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
998         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
999
1000         return MODEST_WINDOW(window);
1001 }
1002
1003 ModestWindow *
1004 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view, 
1005                                               const gchar *modest_account_name,
1006                                               const gchar *msg_uid,
1007                                               GtkTreeRowReference *row_reference)
1008 {
1009         ModestMsgViewWindow *window = NULL;
1010         ModestMsgViewWindowPrivate *priv = NULL;
1011         TnyFolder *header_folder = NULL;
1012         ModestWindowMgr *mgr = NULL;
1013         GtkTreePath *path;
1014         GtkTreeIter iter;
1015
1016         mgr = modest_runtime_get_window_mgr ();
1017         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1018         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1019
1020         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1021
1022         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1023
1024         /* Remember the message list's TreeModel so we can detect changes 
1025          * and change the list selection when necessary: */
1026
1027         if (header_view != NULL){
1028                 header_folder = modest_header_view_get_folder(header_view);
1029                 /* This could happen if the header folder was
1030                    unseleted before opening this msg window (for
1031                    example if the user selects an account in the
1032                    folder view of the main window */
1033                 if (header_folder) {
1034                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
1035                         priv->header_folder_id = tny_folder_get_id(header_folder);
1036                         g_assert(priv->header_folder_id != NULL);
1037                         g_object_unref(header_folder);
1038                 }
1039         }
1040
1041         /* Setup row references and connect signals */
1042         priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1043
1044         if (row_reference) {
1045                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1046                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1047                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1048         } else {
1049                 priv->row_reference = NULL;
1050                 priv->next_row_reference = NULL;
1051         }
1052
1053         /* Connect signals */
1054         priv->row_changed_handler = 
1055                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1056                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
1057                                   window);
1058         priv->row_deleted_handler = 
1059                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1060                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
1061                                   window);
1062         priv->row_inserted_handler = 
1063                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1064                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1065                                   window);
1066         priv->rows_reordered_handler = 
1067                 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1068                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
1069                                  window);
1070
1071         if (header_view != NULL){
1072                 modest_header_view_add_observer(header_view,
1073                                 MODEST_HEADER_VIEW_OBSERVER(window));
1074         }
1075
1076         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1077
1078         path = gtk_tree_row_reference_get_path (row_reference);
1079         if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1080                 TnyHeader *header;
1081                 gtk_tree_model_get (priv->header_model, &iter, 
1082                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1083                                     &header, -1);
1084                 message_reader (window, priv, header, row_reference);
1085         }
1086         gtk_tree_path_free (path);
1087
1088         /* Check dimming rules */
1089         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1090         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1091         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1092
1093         return MODEST_WINDOW(window);
1094 }
1095
1096 ModestWindow *
1097 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
1098                                               const gchar *modest_account_name,
1099                                               const gchar *msg_uid)
1100 {
1101         ModestMsgViewWindow *window = NULL;
1102         ModestMsgViewWindowPrivate *priv = NULL;
1103         ModestWindowMgr *mgr = NULL;
1104
1105         mgr = modest_runtime_get_window_mgr ();
1106         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1107         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1108         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1109
1110         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1111
1112         /* Remember that this is a search result, 
1113          * so we can disable some UI appropriately: */
1114         priv->is_search_result = TRUE;
1115
1116         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1117         
1118         update_window_title (window);
1119         /* gtk_widget_show_all (GTK_WIDGET (window));*/
1120         modest_msg_view_window_update_priority (window);
1121
1122         /* Check dimming rules */
1123         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1124         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1125         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1126
1127         return MODEST_WINDOW(window);
1128 }
1129
1130 ModestWindow *
1131 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1132                             const gchar *modest_account_name,
1133                             const gchar *msg_uid)
1134 {
1135         GObject *obj = NULL;
1136         ModestMsgViewWindowPrivate *priv;       
1137         ModestWindowMgr *mgr = NULL;
1138
1139         g_return_val_if_fail (msg, NULL);
1140         mgr = modest_runtime_get_window_mgr ();
1141         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1142         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1143         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1144                 modest_account_name, msg_uid);
1145
1146         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1147         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1148
1149         /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1150
1151         /* Check dimming rules */
1152         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1153         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1154         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1155
1156         return MODEST_WINDOW(obj);
1157 }
1158
1159 static void
1160 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1161                                        GtkTreePath *arg1,
1162                                        GtkTreeIter *arg2,
1163                                        ModestMsgViewWindow *window)
1164 {
1165         check_dimming_rules_after_change (window);
1166 }
1167
1168 static void 
1169 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1170                                       GtkTreePath *arg1,
1171                                       ModestMsgViewWindow *window)
1172 {
1173         check_dimming_rules_after_change (window);
1174 }
1175         /* The window could have dissapeared */
1176
1177 static void
1178 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1179 {
1180         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1181         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1182 }
1183
1184
1185 /* On insertions we check if the folder still has the message we are
1186  * showing or do not. If do not, we do nothing. Which means we are still
1187  * not attached to any header folder and thus next/prev buttons are
1188  * still dimmed. Once the message that is shown by msg-view is found, the
1189  * new model of header-view will be attached and the references will be set.
1190  * On each further insertions dimming rules will be checked. However
1191  * this requires extra CPU time at least works.
1192  * (An message might be deleted from TnyFolder and thus will not be
1193  * inserted into the model again for example if it is removed by the
1194  * imap server and the header view is refreshed.)
1195  */
1196 static void 
1197 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1198                                         GtkTreePath *tree_path,
1199                                         GtkTreeIter *tree_iter,
1200                                         ModestMsgViewWindow *window)
1201 {
1202         ModestMsgViewWindowPrivate *priv = NULL; 
1203         TnyHeader *header = NULL;
1204
1205         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1206         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1207
1208         g_assert (model == priv->header_model);
1209         
1210         /* Check if the newly inserted message is the same we are actually
1211          * showing. IF not, we should remain detached from the header model
1212          * and thus prev and next toolbar buttons should remain dimmed. */
1213         gtk_tree_model_get (model, tree_iter, 
1214                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1215                             &header, -1);
1216
1217         if (TNY_IS_HEADER (header)) {
1218                 gchar *uid = NULL;
1219
1220                 uid = modest_tny_folder_get_header_unique_id (header);
1221                 if (!g_str_equal(priv->msg_uid, uid)) {
1222                         check_dimming_rules_after_change (window);
1223                         g_free(uid);
1224                         g_object_unref (G_OBJECT(header));
1225                         return;
1226                 }
1227                 g_free(uid);
1228                 g_object_unref(G_OBJECT(header));
1229         }
1230
1231         if (priv->row_reference) {
1232                 gtk_tree_row_reference_free (priv->row_reference); 
1233         }
1234
1235         /* Setup row_reference for the actual msg. */
1236         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1237         if (priv->row_reference == NULL) {
1238                 g_warning("No reference for msg header item.");
1239                 return;
1240         }
1241
1242         /* Now set up next_row_reference. */
1243         if (priv->next_row_reference) {
1244                 gtk_tree_row_reference_free (priv->next_row_reference); 
1245         }
1246
1247         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1248         select_next_valid_row (priv->header_model,
1249                                &(priv->next_row_reference), FALSE, priv->is_outbox);
1250
1251         /* Connect the remaining callbacks to become able to detect
1252          * changes in header-view. */
1253         priv->row_changed_handler = 
1254                 g_signal_connect (priv->header_model, "row-changed",
1255                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1256                                   window);
1257         priv->row_deleted_handler = 
1258                 g_signal_connect (priv->header_model, "row-deleted",
1259                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1260                                   window);
1261         priv->rows_reordered_handler = 
1262                 g_signal_connect (priv->header_model, "rows-reordered",
1263                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1264                                   window);
1265
1266         check_dimming_rules_after_change (window);      
1267 }
1268
1269 static void 
1270 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1271                                          GtkTreePath *arg1,
1272                                          GtkTreeIter *arg2,
1273                                          gpointer arg3,
1274                                          ModestMsgViewWindow *window)
1275 {
1276         ModestMsgViewWindowPrivate *priv = NULL; 
1277         gboolean already_changed = FALSE;
1278
1279         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1280
1281         /* If the current row was reordered select the proper next
1282            valid row. The same if the next row reference changes */
1283         if (priv->row_reference && 
1284             gtk_tree_row_reference_valid (priv->row_reference)) {
1285                 GtkTreePath *path;
1286                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1287                 if (gtk_tree_path_compare (path, arg1) == 0) {
1288                         if (priv->next_row_reference) {
1289                                 gtk_tree_row_reference_free (priv->next_row_reference);
1290                         }
1291                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1292                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1293                         already_changed = TRUE;
1294                 }
1295                 gtk_tree_path_free (path);
1296         }
1297         if (!already_changed &&
1298             priv->next_row_reference &&
1299             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1300                 GtkTreePath *path;
1301                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1302                 if (gtk_tree_path_compare (path, arg1) == 0) {
1303                         if (priv->next_row_reference) {
1304                                 gtk_tree_row_reference_free (priv->next_row_reference);
1305                         }
1306                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1307                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1308                 }
1309                 gtk_tree_path_free (path);
1310         }
1311         check_dimming_rules_after_change (window);
1312 }
1313
1314 /* The modest_msg_view_window_update_model_replaced implements update
1315  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1316  * actually belongs to the header-view is the same as the TnyFolder of
1317  * the message of msg-view or not. If they are different, there is
1318  * nothing to do. If they are the same, then the model has replaced and
1319  * the reference in msg-view shall be replaced from the old model to
1320  * the new model. In this case the view will be detached from it's
1321  * header folder. From this point the next/prev buttons are dimmed.
1322  */
1323 static void 
1324 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1325                                               GtkTreeModel *model,
1326                                               const gchar *tny_folder_id)
1327 {
1328         ModestMsgViewWindowPrivate *priv = NULL; 
1329         ModestMsgViewWindow *window = NULL;
1330
1331         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1332         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1333
1334         window = MODEST_MSG_VIEW_WINDOW(observer);
1335         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1336
1337         /* If there is an other folder in the header-view then we do
1338          * not care about it's model (msg list). Else if the
1339          * header-view shows the folder the msg shown by us is in, we
1340          * shall replace our model reference and make some check. */
1341         if(model == NULL || tny_folder_id == NULL || 
1342            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1343                 return;
1344
1345         /* Model is changed(replaced), so we should forget the old
1346          * one. Because there might be other references and there
1347          * might be some change on the model even if we unreferenced
1348          * it, we need to disconnect our signals here. */
1349         if (priv->header_model) {
1350                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1351                                                   priv->row_changed_handler))
1352                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1353                                                     priv->row_changed_handler);
1354                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1355                                                   priv->row_deleted_handler))
1356                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1357                                                     priv->row_deleted_handler);
1358                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1359                                                   priv->row_inserted_handler))
1360                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1361                                                     priv->row_inserted_handler);
1362                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1363                                                   priv->rows_reordered_handler))
1364                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1365                                                     priv->rows_reordered_handler);
1366
1367                 /* Frees */
1368                 if (priv->row_reference)
1369                         gtk_tree_row_reference_free (priv->row_reference);
1370                 if (priv->next_row_reference)
1371                         gtk_tree_row_reference_free (priv->next_row_reference);
1372                 g_object_unref(priv->header_model);
1373
1374                 /* Initialize */
1375                 priv->row_changed_handler = 0;
1376                 priv->row_deleted_handler = 0;
1377                 priv->row_inserted_handler = 0;
1378                 priv->rows_reordered_handler = 0;
1379                 priv->next_row_reference = NULL;
1380                 priv->row_reference = NULL;
1381                 priv->header_model = NULL;
1382         }
1383
1384         priv->header_model = g_object_ref (model);
1385
1386         /* Also we must connect to the new model for row insertions.
1387          * Only for insertions now. We will need other ones only after
1388          * the msg is show by msg-view is added to the new model. */
1389         priv->row_inserted_handler =
1390                 g_signal_connect (priv->header_model, "row-inserted",
1391                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1392                                   window);
1393
1394         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1395         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1396 }
1397
1398 gboolean 
1399 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1400 {
1401         ModestMsgViewWindowPrivate *priv= NULL; 
1402
1403         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1404         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1405
1406         return priv->progress_hint;
1407 }
1408
1409 TnyHeader*
1410 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1411 {
1412         ModestMsgViewWindowPrivate *priv= NULL; 
1413         TnyMsg *msg = NULL;
1414         TnyHeader *header = NULL;
1415         GtkTreePath *path = NULL;
1416         GtkTreeIter iter;
1417
1418         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1419         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1420
1421         /* If the message was not obtained from a treemodel,
1422          * for instance if it was opened directly by the search UI:
1423          */
1424         if (priv->header_model == NULL || 
1425             priv->row_reference == NULL ||
1426             !gtk_tree_row_reference_valid (priv->row_reference)) {
1427                 msg = modest_msg_view_window_get_message (self);
1428                 if (msg) {
1429                         header = tny_msg_get_header (msg);
1430                         g_object_unref (msg);
1431                 }
1432                 return header;
1433         }
1434
1435         /* Get iter of the currently selected message in the header view: */
1436         path = gtk_tree_row_reference_get_path (priv->row_reference);
1437         g_return_val_if_fail (path != NULL, NULL);
1438         gtk_tree_model_get_iter (priv->header_model, 
1439                                  &iter, 
1440                                  path);
1441
1442         /* Get current message header */
1443         gtk_tree_model_get (priv->header_model, &iter, 
1444                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1445                             &header, -1);
1446
1447         gtk_tree_path_free (path);
1448         return header;
1449 }
1450
1451 TnyMsg*
1452 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1453 {
1454         ModestMsgViewWindowPrivate *priv;
1455         
1456         g_return_val_if_fail (self, NULL);
1457         
1458         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1459         
1460         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1461 }
1462
1463 const gchar*
1464 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1465 {
1466         ModestMsgViewWindowPrivate *priv;
1467
1468         g_return_val_if_fail (self, NULL);
1469         
1470         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1471
1472         return (const gchar*) priv->msg_uid;
1473 }
1474
1475 static void 
1476 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1477                                             gpointer data)
1478 {
1479         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1480         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1481         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1482         gboolean is_active;
1483         GtkAction *action;
1484
1485         is_active = gtk_toggle_action_get_active (toggle);
1486
1487         if (is_active) {
1488                 gtk_widget_show (priv->find_toolbar);
1489                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1490         } else {
1491                 gtk_widget_hide (priv->find_toolbar);
1492                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1493         }
1494
1495         /* update the toggle buttons status */
1496         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1497 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1498         if (action)
1499                 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1500
1501 =======
1502         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1503 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1504 }
1505
1506 static void
1507 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1508                                            ModestMsgViewWindow *obj)
1509 {
1510         GtkToggleAction *toggle;
1511         ModestWindowPrivate *parent_priv;
1512         ModestMsgViewWindowPrivate *priv;
1513
1514         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1515         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1516
1517         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1518         gtk_toggle_action_set_active (toggle, FALSE);
1519         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1520 }
1521
1522 static void
1523 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1524                                            ModestMsgViewWindow *obj)
1525 {
1526         gchar *current_search;
1527         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1528
1529         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1530                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1531                 return;
1532         }
1533
1534         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1535
1536         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1537                 g_free (current_search);
1538                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1539                 return;
1540         }
1541
1542         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1543                 gboolean result;
1544                 g_free (priv->last_search);
1545                 priv->last_search = g_strdup (current_search);
1546                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1547                                                      priv->last_search);
1548                 if (!result) {
1549                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1550                         g_free (priv->last_search);
1551                         priv->last_search = NULL;
1552                 } else {
1553                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1554                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1555                 }
1556         } else {
1557                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1558                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1559                         g_free (priv->last_search);
1560                         priv->last_search = NULL;
1561                 } else {
1562                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1563                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1564                 }
1565         }
1566         
1567         g_free (current_search);
1568                 
1569 }
1570
1571 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1572 static void
1573 modest_msg_view_window_set_zoom (ModestWindow *window,
1574                                  gdouble zoom)
1575 {
1576         ModestMsgViewWindowPrivate *priv;
1577         ModestWindowPrivate *parent_priv;
1578      
1579         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1580
1581         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1582         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1583         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1584
1585 }
1586
1587 =======
1588 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1589 static gdouble
1590 modest_msg_view_window_get_zoom (ModestWindow *window)
1591 {
1592         ModestMsgViewWindowPrivate *priv;
1593      
1594         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1595
1596         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1597         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1598 }
1599
1600 static gboolean
1601 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1602 modest_msg_view_window_zoom_plus (ModestWindow *window)
1603 {
1604         gdouble zoom_level;
1605         ModestMsgViewWindowPrivate *priv;
1606      
1607         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1608         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1609   
1610         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1611
1612         if (zoom_level >= 2.0) {
1613                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1614                 return FALSE;
1615         } else if (zoom_level >= 1.5) {
1616                 zoom_level = 2.0;
1617         } else if (zoom_level >= 1.2) {
1618                 zoom_level = 1.5;
1619         } else if (zoom_level >= 1.0) {
1620                 zoom_level = 1.2;
1621         } else if (zoom_level >= 0.8) {
1622                 zoom_level = 1.0;
1623         } else if (zoom_level >= 0.5) {
1624                 zoom_level = 0.8;
1625         } else {
1626                 zoom_level = 0.5;
1627         }
1628
1629         /* set zoom level */
1630         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1631
1632         return TRUE;
1633         
1634 }
1635
1636 static gboolean
1637 modest_msg_view_window_zoom_minus (ModestWindow *window)
1638 {
1639         gdouble zoom_level;
1640         ModestMsgViewWindowPrivate *priv;
1641      
1642         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1643         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1644   
1645         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1646
1647         if (zoom_level <= 0.5) {
1648                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1649                 return FALSE;
1650         } else if (zoom_level <= 0.8) {
1651                 zoom_level = 0.5;
1652         } else if (zoom_level <= 1.0) {
1653                 zoom_level = 0.8;
1654         } else if (zoom_level <= 1.2) {
1655                 zoom_level = 1.0;
1656         } else if (zoom_level <= 1.5) {
1657                 zoom_level = 1.2;
1658         } else if (zoom_level <= 2.0) {
1659                 zoom_level = 1.5;
1660         } else {
1661                 zoom_level = 2.0;
1662         }
1663
1664         /* set zoom level */
1665         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1666
1667         return TRUE;
1668         
1669 }
1670
1671 static gboolean
1672 =======
1673 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1674 modest_msg_view_window_key_event (GtkWidget *window,
1675                                   GdkEventKey *event,
1676                                   gpointer userdata)
1677 {
1678         GtkWidget *focus;
1679
1680         focus = gtk_window_get_focus (GTK_WINDOW (window));
1681
1682         /* for the find toolbar case */
1683         if (focus && GTK_IS_ENTRY (focus)) {
1684                 if (event->keyval == GDK_BackSpace) {
1685                         GdkEvent *copy;
1686                         copy = gdk_event_copy ((GdkEvent *) event);
1687                         gtk_widget_event (focus, copy);
1688                         gdk_event_free (copy);
1689                         return TRUE;
1690                 } else 
1691                         return FALSE;
1692         }
1693         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1694             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1695             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1696             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1697             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1698             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1699                 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1700                 /* gboolean return_value; */
1701
1702                 if (event->type == GDK_KEY_PRESS) {
1703                         GtkScrollType scroll_type;
1704                         
1705                         switch (event->keyval) {
1706                         case GDK_Up: 
1707                         case GDK_KP_Up:
1708                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1709                         case GDK_Down: 
1710                         case GDK_KP_Down:
1711                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1712                         case GDK_Page_Up:
1713                         case GDK_KP_Page_Up:
1714                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1715                         case GDK_Page_Down:
1716                         case GDK_KP_Page_Down:
1717                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1718                         case GDK_Home:
1719                         case GDK_KP_Home:
1720                                 scroll_type = GTK_SCROLL_START; break;
1721                         case GDK_End:
1722                         case GDK_KP_End:
1723                                 scroll_type = GTK_SCROLL_END; break;
1724                         default: scroll_type = GTK_SCROLL_NONE;
1725                         }
1726                         
1727                         /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",  */
1728                         /*                     scroll_type, FALSE, &return_value); */
1729                         return FALSE;
1730                 } else {
1731                         return FALSE;
1732                 }
1733         } else {
1734                 return FALSE;
1735         }
1736 }
1737
1738 gboolean
1739 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1740 {
1741         GtkTreePath *path;
1742         ModestMsgViewWindowPrivate *priv;
1743         GtkTreeIter tmp_iter;
1744         gboolean is_last_selected;
1745
1746         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1748
1749         /*if no model (so no rows at all), then virtually we are the last*/
1750         if (!priv->header_model || !priv->row_reference)
1751                 return TRUE;
1752
1753         if (!gtk_tree_row_reference_valid (priv->row_reference))
1754                 return TRUE;
1755
1756         path = gtk_tree_row_reference_get_path (priv->row_reference);
1757         if (path == NULL)
1758                 return TRUE;
1759
1760         is_last_selected = TRUE;
1761         while (is_last_selected) {
1762                 TnyHeader *header;
1763                 gtk_tree_path_next (path);
1764                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1765                         break;
1766                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1767                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1768                                 &header, -1);
1769                 if (header) {
1770                         if (msg_is_visible (header, priv->is_outbox))
1771                                 is_last_selected = FALSE;
1772                         g_object_unref(G_OBJECT(header));
1773                 }
1774         }
1775         gtk_tree_path_free (path);
1776         return is_last_selected;
1777 }
1778
1779 gboolean
1780 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1781 {
1782         ModestMsgViewWindowPrivate *priv;
1783
1784         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1785         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1786
1787         return priv->header_model != NULL;
1788 }
1789
1790 gboolean
1791 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1792 {
1793         ModestMsgViewWindowPrivate *priv;
1794
1795         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1796         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1797
1798         return priv->is_search_result;
1799 }
1800
1801 static gboolean
1802 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1803 {
1804         if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1805                 return FALSE;
1806         if (!check_outbox) {
1807                 return TRUE;
1808         } else {
1809                 ModestTnySendQueueStatus status;
1810                 status = modest_tny_all_send_queues_get_msg_status (header);
1811                 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1812                         (status != MODEST_TNY_SEND_QUEUE_SENDING));
1813         }
1814 }
1815
1816 gboolean
1817 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1818 {
1819         GtkTreePath *path;
1820         ModestMsgViewWindowPrivate *priv;
1821         gboolean is_first_selected;
1822         GtkTreeIter tmp_iter;
1823
1824         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1825         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1826
1827         /*if no model (so no rows at all), then virtually we are the first*/
1828         if (!priv->header_model || !priv->row_reference)
1829                 return TRUE;
1830
1831         if (!gtk_tree_row_reference_valid (priv->row_reference))
1832                 return TRUE;
1833
1834         path = gtk_tree_row_reference_get_path (priv->row_reference);
1835         if (!path)
1836                 return TRUE;
1837
1838         is_first_selected = TRUE;
1839         while (is_first_selected) {
1840                 TnyHeader *header;
1841                 if(!gtk_tree_path_prev (path))
1842                         break;
1843                 /* Here the 'if' is needless for logic, but let make sure
1844                  * iter is valid for gtk_tree_model_get. */
1845                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1846                         break;
1847                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1848                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1849                                 &header, -1);
1850                 if (header) {
1851                         if (msg_is_visible (header, priv->is_outbox))
1852                                 is_first_selected = FALSE;
1853                         g_object_unref(G_OBJECT(header));
1854                 }
1855         }
1856         gtk_tree_path_free (path);
1857         return is_first_selected;
1858 }
1859
1860 typedef struct {
1861         TnyHeader *header;
1862         GtkTreeRowReference *row_reference;
1863 } MsgReaderInfo;
1864
1865 static void
1866 message_reader_performer (gboolean canceled, 
1867                           GError *err,
1868                           GtkWindow *parent_window, 
1869                           TnyAccount *account, 
1870                           gpointer user_data)
1871 {
1872         ModestMailOperation *mail_op = NULL;
1873         MsgReaderInfo *info;
1874
1875         info = (MsgReaderInfo *) user_data;
1876         if (canceled || err) {
1877                 goto frees;
1878         }
1879
1880         /* Register the header - it'll be unregistered in the callback */
1881         modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1882
1883         /* New mail operation */
1884         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1885                                                                  modest_ui_actions_disk_operations_error_handler, 
1886                                                                  NULL, NULL);
1887                                 
1888         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1889         modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1890         g_object_unref (mail_op);
1891
1892         /* Update dimming rules */
1893         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1894         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1895
1896  frees:
1897         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1898         g_object_unref (info->header);
1899         g_slice_free (MsgReaderInfo, info);
1900 }
1901
1902
1903 /**
1904  * Reads the message whose summary item is @header. It takes care of
1905  * several things, among others:
1906  *
1907  * If the message was not previously downloaded then ask the user
1908  * before downloading. If there is no connection launch the connection
1909  * dialog. Update toolbar dimming rules.
1910  *
1911  * Returns: TRUE if the mail operation was started, otherwise if the
1912  * user do not want to download the message, or if the user do not
1913  * want to connect, then the operation is not issued
1914  **/
1915 static gboolean
1916 message_reader (ModestMsgViewWindow *window,
1917                 ModestMsgViewWindowPrivate *priv,
1918                 TnyHeader *header,
1919                 GtkTreeRowReference *row_reference)
1920 {
1921         ModestWindowMgr *mgr;
1922         TnyAccount *account;
1923         TnyFolder *folder;
1924         MsgReaderInfo *info;
1925
1926         g_return_val_if_fail (row_reference != NULL, FALSE);
1927
1928         mgr = modest_runtime_get_window_mgr ();
1929         /* Msg download completed */
1930         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1931                 /* Ask the user if he wants to download the message if
1932                    we're not online */
1933                 if (!tny_device_is_online (modest_runtime_get_device())) {
1934                         GtkResponseType response;
1935
1936                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1937                                                                             _("mcen_nc_get_msg"));
1938                         if (response == GTK_RESPONSE_CANCEL)
1939                                 return FALSE;
1940                 
1941                         folder = tny_header_get_folder (header);
1942                         info = g_slice_new (MsgReaderInfo);
1943                         info->header = g_object_ref (header);
1944                         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1945
1946                         /* Offer the connection dialog if necessary */
1947                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
1948                                                                        TRUE,
1949                                                                        TNY_FOLDER_STORE (folder),
1950                                                                        message_reader_performer, 
1951                                                                        info);
1952                         g_object_unref (folder);
1953                         return TRUE;
1954                 }
1955         }
1956         
1957         folder = tny_header_get_folder (header);
1958         account = tny_folder_get_account (folder);
1959         info = g_slice_new (MsgReaderInfo);
1960         info->header = g_object_ref (header);
1961         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1962         
1963         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1964         g_object_unref (account);
1965         g_object_unref (folder);
1966
1967         return TRUE;
1968 }
1969
1970 gboolean        
1971 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1972 {
1973         ModestMsgViewWindowPrivate *priv;
1974         GtkTreePath *path= NULL;
1975         GtkTreeIter tmp_iter;
1976         TnyHeader *header;
1977         gboolean retval = TRUE;
1978         GtkTreeRowReference *row_reference = NULL;
1979
1980         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1981         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1982
1983         if (!priv->row_reference)
1984                 return FALSE;
1985
1986         /* Update the next row reference if it's not valid. This could
1987            happen if for example the header which it was pointing to,
1988            was deleted. The best place to do it is in the row-deleted
1989            handler but the tinymail model do not work like the glib
1990            tree models and reports the deletion when the row is still
1991            there */
1992         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1993                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1994                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1995                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1996                 }
1997         }
1998         if (priv->next_row_reference)
1999                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2000         if (path == NULL)
2001                 return FALSE;
2002
2003         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2004
2005         gtk_tree_model_get_iter (priv->header_model,
2006                                  &tmp_iter,
2007                                  path);
2008         gtk_tree_path_free (path);
2009
2010         gtk_tree_model_get (priv->header_model, &tmp_iter, 
2011                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2012                             &header, -1);
2013         
2014         /* Read the message & show it */
2015         if (!message_reader (window, priv, header, row_reference)) {
2016                 retval = FALSE;
2017         }
2018         gtk_tree_row_reference_free (row_reference);
2019
2020         /* Free */
2021         g_object_unref (header);
2022
2023         return retval;
2024 }
2025
2026 gboolean        
2027 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2028 {
2029         ModestMsgViewWindowPrivate *priv = NULL;
2030         GtkTreePath *path;
2031         gboolean finished = FALSE;
2032         gboolean retval = FALSE;
2033
2034         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2035         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2036
2037         /* Return inmediatly if there is no header model */
2038         if (!priv->header_model || !priv->row_reference)
2039                 return FALSE;
2040
2041         path = gtk_tree_row_reference_get_path (priv->row_reference);
2042         while (!finished && gtk_tree_path_prev (path)) {
2043                 TnyHeader *header;
2044                 GtkTreeIter iter;
2045
2046                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2047                 gtk_tree_model_get (priv->header_model, &iter, 
2048                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2049                                     &header, -1);
2050                 finished = TRUE;
2051                 if (header) {
2052                         if (msg_is_visible (header, priv->is_outbox)) {
2053                                 GtkTreeRowReference *row_reference;
2054                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2055                                 /* Read the message & show it */
2056                                 retval = message_reader (window, priv, header, row_reference);
2057                                 gtk_tree_row_reference_free (row_reference);
2058                         } else {
2059                                 finished = FALSE;
2060                         }
2061                         g_object_unref (header);
2062                 }
2063         }
2064
2065         gtk_tree_path_free (path);
2066         return retval;
2067 }
2068
2069 static void
2070 view_msg_cb (ModestMailOperation *mail_op, 
2071              TnyHeader *header, 
2072              gboolean canceled,
2073              TnyMsg *msg, 
2074              GError *error,
2075              gpointer user_data)
2076 {
2077         ModestMsgViewWindow *self = NULL;
2078         ModestMsgViewWindowPrivate *priv = NULL;
2079         GtkTreeRowReference *row_reference = NULL;
2080
2081         /* Unregister the header (it was registered before creating the mail operation) */
2082         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2083
2084         row_reference = (GtkTreeRowReference *) user_data;
2085         if (canceled) {
2086                 gtk_tree_row_reference_free (row_reference);
2087                 return;
2088         }
2089         
2090         /* If there was any error */
2091         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2092                 gtk_tree_row_reference_free (row_reference);                    
2093                 return;
2094         }
2095
2096         /* Get the window */ 
2097         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2098         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2099         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2100
2101         /* Update the row reference */
2102         if (priv->row_reference != NULL) {
2103                 gtk_tree_row_reference_free (priv->row_reference);
2104                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2105                 if (priv->next_row_reference != NULL) {
2106                         gtk_tree_row_reference_free (priv->next_row_reference);
2107                 }
2108                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2109                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2110         }
2111
2112         /* Mark header as read */
2113         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2114                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2115
2116         /* Set new message */
2117         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2118                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2119                 modest_msg_view_window_update_priority (self);
2120                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2121                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2122         }
2123
2124         /* Set the new message uid of the window  */
2125         if (priv->msg_uid) {
2126                 g_free (priv->msg_uid);
2127                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2128         }
2129
2130         /* Notify the observers */
2131         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
2132                        0, priv->header_model, priv->row_reference);
2133
2134         /* Frees */
2135         g_object_unref (self);
2136         gtk_tree_row_reference_free (row_reference);            
2137 }
2138
2139 TnyFolderType
2140 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2141 {
2142         ModestMsgViewWindowPrivate *priv;
2143         TnyMsg *msg;
2144         TnyFolderType folder_type;
2145
2146         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2147
2148         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2149
2150         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2151         if (msg) {
2152                 TnyFolder *folder;
2153
2154                 folder = tny_msg_get_folder (msg);
2155                 if (folder) {
2156                         folder_type = modest_tny_folder_guess_folder_type (folder);
2157                         g_object_unref (folder);
2158                 }
2159                 g_object_unref (msg);
2160         }
2161
2162         return folder_type;
2163 }
2164
2165
2166 static void
2167 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2168 {
2169         ModestMsgViewWindowPrivate *priv;
2170         TnyHeader *header = NULL;
2171         TnyHeaderFlags flags = 0;
2172
2173         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2174
2175         if (priv->header_model && priv->row_reference) {
2176                 GtkTreeIter iter;
2177                 GtkTreePath *path = NULL;
2178
2179                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2180                 g_return_if_fail (path != NULL);
2181                 gtk_tree_model_get_iter (priv->header_model, 
2182                                          &iter, 
2183                                          gtk_tree_row_reference_get_path (priv->row_reference));
2184
2185                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2186                                     &header, -1);
2187                 gtk_tree_path_free (path);
2188         } else {
2189                 TnyMsg *msg;
2190                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2191                 if (msg) {
2192                         header = tny_msg_get_header (msg);
2193                         g_object_unref (msg);
2194                 }
2195         }
2196
2197         if (header) {
2198                 flags = tny_header_get_flags (header);
2199                 g_object_unref(G_OBJECT(header));
2200         }
2201
2202         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2203
2204 }
2205
2206 static void
2207 toolbar_resize (ModestMsgViewWindow *self)
2208 {
2209         ModestMsgViewWindowPrivate *priv = NULL;
2210         ModestWindowPrivate *parent_priv = NULL;
2211         GtkWidget *widget;
2212         gint static_button_size;
2213         ModestWindowMgr *mgr;
2214
2215         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2216         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2217         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2218
2219         mgr = modest_runtime_get_window_mgr ();
2220         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2221
2222         if (parent_priv->toolbar) {
2223                 /* left size buttons */
2224                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2225                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2226                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2227                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2228                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2229                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2230                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2231                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2232                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2233                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2234                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2235                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2236                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2237                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2238                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2239                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2240                 
2241                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2242                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2243                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2244                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2245         }
2246                 
2247 }
2248
2249 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2250
2251 =======
2252 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2253 static void
2254 modest_msg_view_window_show_toolbar (ModestWindow *self,
2255                                      gboolean show_toolbar)
2256 {
2257         ModestMsgViewWindowPrivate *priv = NULL;
2258         ModestWindowPrivate *parent_priv;
2259         GtkWidget *reply_button = NULL, *menu = NULL;
2260 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2261 =======
2262         GtkWidget *placeholder = NULL;
2263         gint insert_index;
2264 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2265
2266         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2267         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2268
2269         /* Set optimized view status */
2270         priv->optimized_view = !show_toolbar;
2271
2272         if (!parent_priv->toolbar) {
2273                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2274                                                                   "/ToolBar");
2275                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2276
2277                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2278                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2279                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2280
2281 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2282 =======
2283                 /* Add ProgressBar (Transfer toolbar) */ 
2284                 priv->progress_bar = modest_progress_bar_new ();
2285                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2286                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2287                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2288                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2289                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2290
2291                 /* Connect cancel 'clicked' signal to abort progress mode */
2292                 g_signal_connect(priv->cancel_toolitem, "clicked",
2293                                  G_CALLBACK(cancel_progressbar),
2294                                  self);
2295
2296                 /* Add it to the observers list */
2297                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2298
2299 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2300                 /* Add to window */
2301                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2302                                            GTK_TOOLBAR (parent_priv->toolbar));
2303
2304                 /* Set reply button tap and hold menu */
2305                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2306                                                           "/ToolBar/ToolbarMessageReply");
2307                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2308                                                   "/ToolbarReplyCSM");
2309                 if (menu && reply_button)
2310                         gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2311         }
2312
2313         if (show_toolbar) {
2314                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2315                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2316                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2317
2318                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2319                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2320                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2321                 else
2322                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2323
2324         } else {
2325                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2326                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2327         }
2328 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2329
2330 =======
2331 >>>>>>> fe21497...  * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2332 }
2333
2334 static void 
2335 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2336                                                GdkEvent *event,
2337                                                ModestMsgViewWindow *window)
2338 {
2339         if (!GTK_WIDGET_VISIBLE (window))
2340                 return;
2341
2342         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2343 }
2344
2345 gboolean 
2346 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2347 {
2348         ModestMsgViewWindowPrivate *priv;
2349         
2350         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2351         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2352
2353         return priv->progress_hint;
2354 }
2355
2356 static gboolean
2357 observers_empty (ModestMsgViewWindow *self)
2358 {
2359         GSList *tmp = NULL;
2360         ModestMsgViewWindowPrivate *priv;
2361         gboolean is_empty = TRUE;
2362         guint pending_ops = 0;
2363  
2364         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2365         tmp = priv->progress_widgets;
2366
2367         /* Check all observers */
2368         while (tmp && is_empty)  {
2369                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2370                 is_empty = pending_ops == 0;
2371                 
2372                 tmp = g_slist_next(tmp);
2373         }
2374         
2375         return is_empty;
2376 }
2377
2378 static void
2379 on_account_removed (TnyAccountStore *account_store, 
2380                     TnyAccount *account,
2381                     gpointer user_data)
2382 {
2383         /* Do nothing if it's a transport account, because we only
2384            show the messages of a store account */
2385         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2386                 const gchar *parent_acc = NULL;
2387                 const gchar *our_acc = NULL;
2388
2389                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2390                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2391
2392                 /* Close this window if I'm showing a message of the removed account */
2393                 if (strcmp (parent_acc, our_acc) == 0)
2394                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2395         }
2396 }
2397
2398 static void 
2399 on_mail_operation_started (ModestMailOperation *mail_op,
2400                            gpointer user_data)
2401 {
2402         ModestMsgViewWindow *self;
2403         ModestMailOperationTypeOperation op_type;
2404         GSList *tmp;
2405         ModestMsgViewWindowPrivate *priv;
2406         GObject *source = NULL;
2407
2408         self = MODEST_MSG_VIEW_WINDOW (user_data);
2409         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2410         op_type = modest_mail_operation_get_type_operation (mail_op);
2411         tmp = priv->progress_widgets;
2412         source = modest_mail_operation_get_source(mail_op);
2413         if (G_OBJECT (self) == source) {
2414                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2415                         set_toolbar_transfer_mode(self);
2416                         while (tmp) {
2417                                 modest_progress_object_add_operation (
2418                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2419                                                 mail_op);
2420                                 tmp = g_slist_next (tmp);
2421                         }
2422                 }
2423         }
2424         g_object_unref (source);
2425 }
2426
2427 static void 
2428 on_mail_operation_finished (ModestMailOperation *mail_op,
2429                             gpointer user_data)
2430 {
2431         ModestMsgViewWindow *self;
2432         ModestMailOperationTypeOperation op_type;
2433         GSList *tmp;
2434         ModestMsgViewWindowPrivate *priv;
2435         
2436         self = MODEST_MSG_VIEW_WINDOW (user_data);
2437         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2438         op_type = modest_mail_operation_get_type_operation (mail_op);
2439         tmp = priv->progress_widgets;
2440         
2441         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2442                 while (tmp) {
2443                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2444                                                                  mail_op);
2445                         tmp = g_slist_next (tmp);
2446                 }
2447
2448                 /* If no more operations are being observed, NORMAL mode is enabled again */
2449                 if (observers_empty (self)) {
2450                         set_progress_hint (self, FALSE);
2451                 }
2452
2453                 /* Update dimming rules. We have to do this right here
2454                    and not in view_msg_cb because at that point the
2455                    transfer mode is still enabled so the dimming rule
2456                    won't let the user delete the message that has been
2457                    readed for example */
2458                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2459                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2460         }
2461 }
2462
2463 static void
2464 on_queue_changed (ModestMailOperationQueue *queue,
2465                   ModestMailOperation *mail_op,
2466                   ModestMailOperationQueueNotification type,
2467                   ModestMsgViewWindow *self)
2468 {       
2469         ModestMsgViewWindowPrivate *priv;
2470
2471         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2472
2473         /* If this operations was created by another window, do nothing */
2474         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2475             return;
2476
2477         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2478                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2479                                                                G_OBJECT (mail_op),
2480                                                                "operation-started",
2481                                                                G_CALLBACK (on_mail_operation_started),
2482                                                                self);
2483                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2484                                                                G_OBJECT (mail_op),
2485                                                                "operation-finished",
2486                                                                G_CALLBACK (on_mail_operation_finished),
2487                                                                self);
2488         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2489                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2490                                                                   G_OBJECT (mail_op),
2491                                                                   "operation-started");
2492                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2493                                                                   G_OBJECT (mail_op),
2494                                                                   "operation-finished");
2495         }
2496 }
2497
2498 TnyList *
2499 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2500 {
2501         ModestMsgViewWindowPrivate *priv;
2502         TnyList *selected_attachments = NULL;
2503         
2504         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2505         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2506
2507         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2508         
2509         return selected_attachments;
2510 }
2511
2512 typedef struct {
2513         gchar *filepath;
2514         GtkWidget *banner;
2515         guint banner_idle_id;
2516 } DecodeAsyncHelper;
2517
2518 static gboolean
2519 decode_async_banner_idle (gpointer user_data)
2520 {
2521         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2522
2523         helper->banner_idle_id = 0;
2524         helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2525         g_object_ref (helper->banner);
2526
2527         return FALSE;
2528 }
2529
2530 static void
2531 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2532                                    gboolean cancelled, 
2533                                    TnyStream *stream, 
2534                                    GError *err, 
2535                                    gpointer user_data)
2536 {
2537         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2538
2539         if (helper->banner_idle_id > 0) {
2540                 g_source_remove (helper->banner_idle_id);
2541                 helper->banner_idle_id = 0;
2542         }
2543         if (helper->banner) {
2544                 gtk_widget_destroy (helper->banner);
2545         }
2546         if (cancelled || err) {
2547                 modest_platform_information_banner (NULL, NULL, 
2548                                                     _("mail_ib_file_operation_failed"));
2549                 goto free;
2550         }
2551
2552         /* make the file read-only */
2553         g_chmod(helper->filepath, 0444);
2554         
2555         /* Activate the file */
2556         modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2557
2558  free:
2559         /* Frees */
2560         g_free (helper->filepath);
2561         g_object_unref (helper->banner);
2562         g_slice_free (DecodeAsyncHelper, helper);
2563 }
2564
2565 void
2566 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2567                                         TnyMimePart *mime_part)
2568 {
2569         ModestMsgViewWindowPrivate *priv;
2570         const gchar *msg_uid;
2571         gchar *attachment_uid = NULL;
2572         gint attachment_index = 0;
2573         TnyList *attachments;
2574
2575         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2576         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2577         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2578
2579         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2580         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2581         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2582         g_object_unref (attachments);
2583         
2584         if (msg_uid && attachment_index >= 0) {
2585                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2586         }
2587
2588         if (mime_part == NULL) {
2589                 gboolean error = FALSE;
2590                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2591                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2592                         error = TRUE;
2593                 } else if (tny_list_get_length (selected_attachments) > 1) {
2594                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2595                         error = TRUE;
2596                 } else {
2597                         TnyIterator *iter;
2598                         iter = tny_list_create_iterator (selected_attachments);
2599                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2600                         g_object_unref (iter);
2601                 }
2602                 g_object_unref (selected_attachments);
2603
2604                 if (error)
2605                         return;
2606         } else {
2607                 g_object_ref (mime_part);
2608         }
2609
2610         if (tny_mime_part_is_purged (mime_part)) {
2611                 g_object_unref (mime_part);
2612                 return;
2613         }
2614
2615         if (!modest_tny_mime_part_is_msg (mime_part)) {
2616                 gchar *filepath = NULL;
2617                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2618                 gboolean show_error_banner = FALSE;
2619                 TnyFsStream *temp_stream = NULL;
2620                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2621                                                                &filepath);
2622                 
2623                 if (temp_stream != NULL) {
2624                         DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2625                         helper->filepath = g_strdup (filepath);
2626                         helper->banner = NULL;
2627                         helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2628                         tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream), 
2629                                                               on_decode_to_stream_async_handler, 
2630                                                               NULL, 
2631                                                               helper);
2632                         g_object_unref (temp_stream);
2633                         /* NOTE: files in the temporary area will be automatically
2634                          * cleaned after some time if they are no longer in use */
2635                 } else {
2636                         if (filepath) {
2637                                 const gchar *content_type;
2638                                 /* the file may already exist but it isn't writable,
2639                                  * let's try to open it anyway */
2640                                 content_type = tny_mime_part_get_content_type (mime_part);
2641                                 modest_platform_activate_file (filepath, content_type);
2642                         } else {
2643                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2644                                 show_error_banner = TRUE;
2645                         }
2646                 }
2647                 if (filepath)
2648                         g_free (filepath);
2649                 if (show_error_banner)
2650                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2651         } else {
2652                 /* message attachment */
2653                 TnyHeader *header = NULL;
2654                 ModestWindowMgr *mgr;
2655                 ModestWindow *msg_win = NULL;
2656                 gboolean found;
2657
2658                 header = tny_msg_get_header (TNY_MSG (mime_part));
2659                 mgr = modest_runtime_get_window_mgr ();
2660                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2661
2662                 if (found) {
2663                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2664                          * thus, we don't do anything */
2665                         g_warning ("window for is already being created");
2666                 } else {
2667                         /* it's not found, so create a new window for it */
2668                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2669                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2670                         if (!account)
2671                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2672                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2673                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2674                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2675                         modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2676                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2677                 }
2678         }
2679         g_object_unref (mime_part);
2680 }
2681
2682 typedef struct
2683 {
2684         gchar *filename;
2685         TnyMimePart *part;
2686 } SaveMimePartPair;
2687
2688 typedef struct
2689 {
2690         GList *pairs;
2691         GtkWidget *banner;
2692         GnomeVFSResult result;
2693 } SaveMimePartInfo;
2694
2695 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2696 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2697 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2698 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2699
2700 static void 
2701 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2702 {
2703         
2704         GList *node;
2705         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2706                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2707                 g_free (pair->filename);
2708                 g_object_unref (pair->part);
2709                 g_slice_free (SaveMimePartPair, pair);
2710         }
2711         g_list_free (info->pairs);
2712         info->pairs = NULL;
2713         if (with_struct) {
2714                 gtk_widget_destroy (info->banner);
2715                 g_slice_free (SaveMimePartInfo, info);
2716         }
2717 }
2718
2719 static gboolean
2720 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2721 {
2722         if (info->pairs != NULL) {
2723                 save_mime_part_to_file (info);
2724         } else {
2725                 /* This is a GDK lock because we are an idle callback and
2726                  * hildon_banner_show_information is or does Gtk+ code */
2727
2728                 gdk_threads_enter (); /* CHECKED */
2729                 save_mime_part_info_free (info, TRUE);
2730                 if (info->result == GNOME_VFS_OK) {
2731                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2732                 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2733                         hildon_banner_show_information (NULL, NULL, dgettext("ke-recv", 
2734                                                                              "cerm_device_memory_full"));
2735                 } else {
2736                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2737                 }
2738                 gdk_threads_leave (); /* CHECKED */
2739         }
2740
2741         return FALSE;
2742 }
2743
2744 static gpointer
2745 save_mime_part_to_file (SaveMimePartInfo *info)
2746 {
2747         GnomeVFSHandle *handle;
2748         TnyStream *stream;
2749         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2750
2751         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2752         if (info->result == GNOME_VFS_OK) {
2753                 GError *error = NULL;
2754                 stream = tny_vfs_stream_new (handle);
2755                 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2756                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2757                         
2758                         info->result = GNOME_VFS_ERROR_IO;
2759                 }
2760                 g_object_unref (G_OBJECT (stream));
2761                 g_object_unref (pair->part);
2762                 g_slice_free (SaveMimePartPair, pair);
2763                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2764         } else {
2765                 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2766                 save_mime_part_info_free (info, FALSE);
2767         }
2768
2769         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2770         return NULL;
2771 }
2772
2773 static void
2774 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2775 {
2776         gboolean is_ok = TRUE;
2777         gint replaced_files = 0;
2778         const GList *files = info->pairs;
2779         const GList *iter;
2780
2781         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2782                 SaveMimePartPair *pair = iter->data;
2783                 if (modest_utils_file_exists (pair->filename)) {
2784                         replaced_files++;
2785                 }
2786         }
2787         if (replaced_files) {
2788                 GtkWidget *confirm_overwrite_dialog;
2789                 const gchar *message = (replaced_files == 1) ?
2790                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2791                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2792                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2793                         is_ok = FALSE;
2794                 }
2795                 gtk_widget_destroy (confirm_overwrite_dialog);
2796         }
2797
2798         if (!is_ok) {
2799                 save_mime_part_info_free (info, TRUE);
2800         } else {
2801                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2802                                                                   _CS("sfil_ib_saving"));
2803                 info->banner = banner;
2804                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2805         }
2806
2807 }
2808
2809 static void
2810 save_attachments_response (GtkDialog *dialog,
2811                            gint       arg1,
2812                            gpointer   user_data)  
2813 {
2814         TnyList *mime_parts;
2815         gchar *chooser_uri;
2816         GList *files_to_save = NULL;
2817
2818         mime_parts = TNY_LIST (user_data);
2819
2820         if (arg1 != GTK_RESPONSE_OK)
2821                 goto end;
2822
2823         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2824
2825         if (!modest_utils_folder_writable (chooser_uri)) {
2826                 hildon_banner_show_information 
2827                         (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2828         } else {
2829                 TnyIterator *iter;
2830
2831                 iter = tny_list_create_iterator (mime_parts);
2832                 while (!tny_iterator_is_done (iter)) {
2833                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2834
2835                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2836                             !tny_mime_part_is_purged (mime_part) &&
2837                             (tny_mime_part_get_filename (mime_part) != NULL)) {
2838                                 SaveMimePartPair *pair;
2839                                         
2840                                 pair = g_slice_new0 (SaveMimePartPair);
2841
2842                                 if (tny_list_get_length (mime_parts) > 1) {
2843                                         gchar *escaped = 
2844                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2845                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2846                                         g_free (escaped);
2847                                 } else {
2848                                         pair->filename = g_strdup (chooser_uri);
2849                                 }
2850                                 pair->part = mime_part;
2851                                 files_to_save = g_list_prepend (files_to_save, pair);
2852                         }
2853                         tny_iterator_next (iter);
2854                 }
2855                 g_object_unref (iter);
2856         }
2857         g_free (chooser_uri);
2858
2859         if (files_to_save != NULL) {
2860                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2861                 info->pairs = files_to_save;
2862                 info->result = TRUE;
2863                 save_mime_parts_to_file_with_checks (info);
2864         }
2865
2866  end:
2867         /* Free and close the dialog */
2868         g_object_unref (mime_parts);
2869         gtk_widget_destroy (GTK_WIDGET (dialog));
2870 }
2871
2872 void
2873 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2874 {
2875         ModestMsgViewWindowPrivate *priv;
2876         GtkWidget *save_dialog = NULL;
2877         gchar *folder = NULL;
2878         gchar *filename = NULL;
2879         gchar *save_multiple_str = NULL;
2880
2881         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2882         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2883
2884         if (mime_parts == NULL) {
2885                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2886                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2887                         return;
2888         } else {
2889                 g_object_ref (mime_parts);
2890         }
2891
2892         /* prepare dialog */
2893         if (tny_list_get_length (mime_parts) == 1) {
2894                 TnyIterator *iter;
2895                 /* only one attachment selected */
2896                 iter = tny_list_create_iterator (mime_parts);
2897                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2898                 g_object_unref (iter);
2899                 if (!modest_tny_mime_part_is_msg (mime_part) && 
2900                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2901                     !tny_mime_part_is_purged (mime_part)) {
2902                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
2903                 } else {
2904                         /* TODO: show any error? */
2905                         g_warning ("Tried to save a non-file attachment");
2906                         g_object_unref (mime_parts);
2907                         return;
2908                 }
2909                 g_object_unref (mime_part);
2910         } else {
2911                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2912                                                      tny_list_get_length (mime_parts));
2913         }
2914         
2915         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2916                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2917
2918         /* set folder */
2919         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2920         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2921         g_free (folder);
2922
2923         /* set filename */
2924         if (filename) {
2925                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2926                                                    filename);
2927                 g_free (filename);
2928         }
2929
2930         /* if multiple, set multiple string */
2931         if (save_multiple_str) {
2932                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2933                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2934         }
2935
2936         /* We must run this asynchronously, because the hildon dialog
2937            performs a gtk_dialog_run by itself which leads to gdk
2938            deadlocks */
2939         g_signal_connect (save_dialog, "response", 
2940                           G_CALLBACK (save_attachments_response), mime_parts);
2941
2942         gtk_widget_show_all (save_dialog);
2943 }
2944
2945 static gboolean
2946 show_remove_attachment_information (gpointer userdata)
2947 {
2948         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2949         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2950
2951         /* We're outside the main lock */
2952         gdk_threads_enter ();
2953
2954         if (priv->remove_attachment_banner != NULL) {
2955                 gtk_widget_destroy (priv->remove_attachment_banner);
2956                 g_object_unref (priv->remove_attachment_banner);
2957         }
2958
2959         priv->remove_attachment_banner = g_object_ref (
2960                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2961
2962         gdk_threads_leave ();
2963
2964         return FALSE;
2965 }
2966
2967 void
2968 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2969 {
2970         ModestMsgViewWindowPrivate *priv;
2971         TnyList *mime_parts = NULL;
2972         gchar *confirmation_message;
2973         gint response;
2974         gint n_attachments;
2975         TnyMsg *msg;
2976         TnyIterator *iter;
2977
2978         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2979         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2980
2981         if (get_all)
2982                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2983         else
2984                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2985                 
2986         /* Remove already purged messages from mime parts list */
2987         iter = tny_list_create_iterator (mime_parts);
2988         while (!tny_iterator_is_done (iter)) {
2989                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2990                 tny_iterator_next (iter);
2991                 if (tny_mime_part_is_purged (part)) {
2992                         tny_list_remove (mime_parts, (GObject *) part);
2993                 }
2994                 g_object_unref (part);
2995         }
2996         g_object_unref (iter);
2997
2998         if (tny_list_get_length (mime_parts) == 0) {
2999                 g_object_unref (mime_parts);
3000                 return;
3001         }
3002
3003         n_attachments = tny_list_get_length (mime_parts);
3004         if (n_attachments == 1) {
3005                 gchar *filename;
3006                 TnyMimePart *part;
3007
3008                 iter = tny_list_create_iterator (mime_parts);
3009                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3010                 g_object_unref (iter);
3011                 if (modest_tny_mime_part_is_msg (part)) {
3012                         TnyHeader *header;
3013                         header = tny_msg_get_header (TNY_MSG (part));
3014                         filename = tny_header_dup_subject (header);
3015                         g_object_unref (header);
3016                         if (filename == NULL)
3017                                 filename = g_strdup (_("mail_va_no_subject"));
3018                 } else {
3019                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3020                 }
3021                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3022                 g_free (filename);
3023                 g_object_unref (part);
3024         } else {
3025                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
3026                                                                  "mcen_nc_purge_files_text", 
3027                                                                  n_attachments), n_attachments);
3028         }
3029         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3030                                                             confirmation_message);
3031         g_free (confirmation_message);
3032
3033         if (response != GTK_RESPONSE_OK) {
3034                 g_object_unref (mime_parts);
3035                 return;
3036         }
3037
3038         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3039         
3040         iter = tny_list_create_iterator (mime_parts);
3041         while (!tny_iterator_is_done (iter)) {
3042                 TnyMimePart *part;
3043
3044                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3045                 tny_mime_part_set_purged (TNY_MIME_PART (part));
3046                 g_object_unref (part);
3047                 tny_iterator_next (iter);
3048         }
3049         g_object_unref (iter);
3050
3051         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3052         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3053         tny_msg_rewrite_cache (msg);
3054         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3055         g_object_unref (msg);
3056
3057         g_object_unref (mime_parts);
3058
3059         if (priv->purge_timeout > 0) {
3060                 g_source_remove (priv->purge_timeout);
3061                 priv->purge_timeout = 0;
3062         }
3063
3064         if (priv->remove_attachment_banner) {
3065                 gtk_widget_destroy (priv->remove_attachment_banner);
3066                 g_object_unref (priv->remove_attachment_banner);
3067                 priv->remove_attachment_banner = NULL;
3068         }
3069
3070
3071 }
3072
3073
3074 static void
3075 update_window_title (ModestMsgViewWindow *window)
3076 {
3077         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3078         TnyMsg *msg = NULL;
3079         TnyHeader *header = NULL;
3080         gchar *subject = NULL;
3081         
3082         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3083
3084         if (msg != NULL) {
3085                 header = tny_msg_get_header (msg);
3086                 subject = tny_header_dup_subject (header);
3087                 g_object_unref (header);
3088                 g_object_unref (msg);
3089         }
3090
3091         if ((subject == NULL)||(subject[0] == '\0')) {
3092                 g_free (subject);
3093                 subject = g_strdup (_("mail_va_no_subject"));
3094         }
3095
3096         gtk_window_set_title (GTK_WINDOW (window), subject);
3097 }
3098
3099
3100 static void on_move_focus (GtkWidget *widget,
3101                            GtkDirectionType direction,
3102                            gpointer userdata)
3103 {
3104         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3105 }
3106
3107 static TnyStream *
3108 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3109 {
3110         GnomeVFSResult result;
3111         GnomeVFSHandle *handle = NULL;
3112         GnomeVFSFileInfo *info = NULL;
3113         TnyStream *stream;
3114
3115         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3116         if (result != GNOME_VFS_OK) {
3117                 *expected_size = 0;
3118                 return NULL;
3119         }
3120         
3121         info = gnome_vfs_file_info_new ();
3122         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3123         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3124                 /* We put a "safe" default size for going to cache */
3125                 *expected_size = (300*1024);
3126         } else {
3127                 *expected_size = info->size;
3128         }
3129         gnome_vfs_file_info_unref (info);
3130
3131         stream = tny_vfs_stream_new (handle);
3132
3133         return stream;
3134
3135 }
3136
3137 typedef struct {
3138         gchar *uri;
3139         gchar *cache_id;
3140         TnyStream *output_stream;
3141         GtkWidget *msg_view;
3142 } FetchImageData;
3143
3144 gboolean
3145 on_fetch_image_idle_refresh_view (gpointer userdata)
3146 {
3147
3148         FetchImageData *fidata = (FetchImageData *) userdata;
3149         g_message ("REFRESH VIEW");
3150         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3151                 g_message ("QUEUING DRAW");
3152                 gtk_widget_queue_draw (fidata->msg_view);
3153         }
3154         g_object_unref (fidata->msg_view);
3155         g_slice_free (FetchImageData, fidata);
3156         return FALSE;
3157 }
3158
3159 static gpointer
3160 on_fetch_image_thread (gpointer userdata)
3161 {
3162         FetchImageData *fidata = (FetchImageData *) userdata;
3163         TnyStreamCache *cache;
3164         TnyStream *cache_stream;
3165
3166         cache = modest_runtime_get_images_cache ();
3167         cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3168         g_free (fidata->cache_id);
3169         g_free (fidata->uri);
3170
3171         if (cache_stream != NULL) {
3172                 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3173                 tny_stream_close (cache_stream);
3174                 g_object_unref (cache_stream);
3175         }
3176
3177         tny_stream_close (fidata->output_stream);
3178         g_object_unref (fidata->output_stream);
3179
3180
3181         gdk_threads_enter ();
3182         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3183         gdk_threads_leave ();
3184
3185         return NULL;
3186 }
3187
3188 static gboolean
3189 on_fetch_image (ModestMsgView *msgview,
3190                 const gchar *uri,
3191                 TnyStream *stream,
3192                 ModestMsgViewWindow *window)
3193 {
3194         const gchar *current_account;
3195         ModestMsgViewWindowPrivate *priv;
3196         FetchImageData *fidata;
3197
3198         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3199
3200         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3201
3202         fidata = g_slice_new0 (FetchImageData);
3203         fidata->msg_view = g_object_ref (msgview);
3204         fidata->uri = g_strdup (uri);
3205         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3206         fidata->output_stream = g_object_ref (stream);
3207
3208         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3209                 g_object_unref (fidata->output_stream);
3210                 g_free (fidata->cache_id);
3211                 g_free (fidata->uri);
3212                 g_object_unref (fidata->msg_view);
3213                 g_slice_free (FetchImageData, fidata);
3214                 tny_stream_close (stream);
3215                 return FALSE;
3216         }
3217
3218         return TRUE;;
3219 }
3220
3221 static void 
3222 add_to_menu (ModestMsgViewWindow *self,
3223              HildonAppMenu *menu,
3224              gchar *label,
3225              GCallback callback,
3226              ModestDimmingRulesGroup *dimming_group,
3227              GCallback dimming_callback)
3228 {
3229         GtkWidget *button;
3230
3231         button = gtk_button_new_with_label (label);
3232         g_signal_connect_after (G_OBJECT (button), "clicked",
3233                                 callback, (gpointer) self);
3234         modest_dimming_rules_group_add_widget_rule (dimming_group,
3235                                                     button,
3236                                                     dimming_callback,
3237                                                     MODEST_WINDOW (self));
3238         hildon_app_menu_append (menu, GTK_BUTTON (button));
3239 }
3240
3241 static void 
3242 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3243 {
3244         ModestMsgViewWindowPrivate *priv = NULL;
3245         GtkWidget *app_menu;
3246
3247         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3248
3249         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3250
3251         app_menu = hildon_app_menu_new ();
3252
3253         /* Settings menu buttons */
3254         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_reply"),
3255                      G_CALLBACK (modest_ui_actions_on_reply),
3256                      group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3257         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3258                      G_CALLBACK (modest_ui_actions_on_reply_all),
3259                      group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3260         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3261                      G_CALLBACK (modest_ui_actions_on_forward),
3262                      group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3263         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3264                      G_CALLBACK (modest_ui_actions_on_new_msg),
3265                      group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3266         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_messagedetails"),
3267                      G_CALLBACK (modest_ui_actions_on_details),
3268                      group, G_CALLBACK (modest_ui_dimming_rules_on_details));
3269         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3270                      G_CALLBACK (modest_ui_actions_save_attachments),
3271                      group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3272         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3273                      G_CALLBACK (modest_ui_actions_remove_attachments),
3274                      group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3275         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3276                      G_CALLBACK (modest_ui_actions_on_add_to_contacts),
3277                      group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3278
3279         hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self), 
3280                                                HILDON_APP_MENU (app_menu));
3281 }
3282
3283 static gboolean 
3284 modest_msg_view_window_toggle_menu (HildonWindow *window,
3285                                     guint button,
3286                                     guint32 time)
3287 {
3288         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3289
3290         return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);  
3291 }