* Added a missing error handling code
[modest] / src / maemo / modest-msg-edit-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
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32 #include <fcntl.h>
33 #include <glib/gstdio.h>
34 #include <string.h>
35 #include <tny-account-store.h>
36 #include <tny-fs-stream.h>
37 #include <tny-vfs-stream.h>
38
39 #include <config.h>
40
41 #include <modest-account-mgr.h>
42 #include <modest-account-mgr-helpers.h>
43
44 #include <widgets/modest-msg-edit-window.h>
45 #include <widgets/modest-combo-box.h>
46 #include <widgets/modest-recpt-editor.h>
47 #include <widgets/modest-attachments-view.h>
48
49 #include <modest-runtime.h>
50
51 #include "modest-platform.h"
52 #include "modest-icon-names.h"
53 #include "modest-widget-memory.h"
54 #include "modest-window-priv.h"
55 #include "modest-mail-operation.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-tny-msg.h"
58 #include "modest-tny-folder.h"
59 #include "modest-tny-account.h"
60 #include "modest-address-book.h"
61 #include "modest-text-utils.h"
62 #include <tny-simple-list.h>
63 #include <wptextview.h>
64 #include <wptextbuffer.h>
65 #include "modest-scroll-area.h"
66 #include "modest-msg-edit-window-ui-dimming.h"
67
68 #include "modest-hildon-includes.h"
69 #ifdef MODEST_HAVE_HILDON0_WIDGETS
70 #include <hildon-widgets/hildon-color-chooser.h>
71 #endif
72 #include "widgets/modest-msg-edit-window-ui.h"
73 #ifdef MODEST_HAVE_HILDON0_WIDGETS
74 #include <libgnomevfs/gnome-vfs-mime-utils.h>
75 #else
76 #include <libgnomevfs/gnome-vfs-mime.h>
77 #endif
78 #include <modest-utils.h>
79 #include "modest-maemo-utils.h"
80
81
82 #define DEFAULT_FONT_SIZE 3
83 #define DEFAULT_FONT 2
84 #define DEFAULT_SIZE_BUTTON_FONT_FAMILY "Sans"
85 #define DEFAULT_SIZE_COMBOBOX_WIDTH 80
86 #define DEFAULT_MAIN_VBOX_SPACING 6
87 #define SUBJECT_MAX_LENGTH 1000
88 #define IMAGE_MAX_WIDTH 560
89 #define DEFAULT_FONT_SCALE 1.5
90
91 static gboolean is_wp_text_buffer_started = FALSE;
92
93 static void  modest_msg_edit_window_class_init   (ModestMsgEditWindowClass *klass);
94 static void  modest_msg_edit_window_init         (ModestMsgEditWindow *obj);
95 static void  modest_msg_edit_window_finalize     (GObject *obj);
96
97 static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata);
98 static void  body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
99 static void  recpt_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
100
101 static void  text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window);
102 static void  text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window);
103 static void  text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window);
104 static void  text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
105                                     GtkTextIter *start, GtkTextIter *end,
106                                     gpointer userdata);
107 static void  text_buffer_insert_text (GtkTextBuffer *buffer,
108                                       GtkTextIter *location,
109                                       gchar *text,
110                                       gint len,
111                                       ModestMsgEditWindow *window);
112 static void  text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id);
113 static void  subject_field_changed (GtkEditable *editable, ModestMsgEditWindow *window);
114 static void  subject_field_insert_text (GtkEditable *editable, 
115                                         gchar *new_text,
116                                         gint new_text_length,
117                                         gint *position,
118                                         ModestMsgEditWindow *window);
119 static void  modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
120                                                          gpointer userdata);
121 static void  modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
122                                                  gpointer userdata);
123 static void  modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
124                                                  gpointer userdata);
125 static void  modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window);
126 static gboolean modest_msg_edit_window_window_state_event (GtkWidget *widget, 
127                                                            GdkEventWindowState *event, 
128                                                            gpointer userdata);
129 static void modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
130                                                      ModestRecptEditor *editor);
131 static void modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
132                                                            ModestMsgEditWindow *window);
133
134 /* ModestWindow methods implementation */
135 static void modest_msg_edit_window_disconnect_signals (ModestWindow *window);
136 static void modest_msg_edit_window_show_toolbar   (ModestWindow *window,
137                                                    gboolean show_toolbar);
138 static void modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
139                                                            GdkEvent *event,
140                                                            ModestMsgEditWindow *window);
141 static void subject_field_move_cursor (GtkEntry *entry,
142                                        GtkMovementStep step,
143                                        gint a1,
144                                        gboolean a2,
145                                        gpointer userdata);
146 static void update_window_title (ModestMsgEditWindow *window);
147
148 /* Find toolbar */
149 static void modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
150                                                         ModestMsgEditWindow *window);
151 static void modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
152                                                        ModestMsgEditWindow *window);
153 static gboolean gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
154                                                           const gchar *str,
155                                                           GtkTextIter *match_start,
156                                                           GtkTextIter *match_end);
157
158 static void remove_tags (WPTextBuffer *buffer);
159
160 static void on_account_removed (TnyAccountStore *account_store, 
161                                 TnyAccount *account,
162                                 gpointer user_data);
163
164 static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window);
165 static void set_zoom_do_nothing (ModestWindow *window, gdouble zoom);
166 static gdouble get_zoom_do_nothing (ModestWindow *window);
167
168 static void init_window (ModestMsgEditWindow *obj);
169
170 gboolean scroll_drag_timeout (gpointer userdata);
171 static void correct_scroll (ModestMsgEditWindow *w);
172 static void correct_scroll_without_drag_check (ModestMsgEditWindow *w);
173 static void text_buffer_end_user_action (GtkTextBuffer *buffer,
174                                          ModestMsgEditWindow *userdata);
175 static void text_buffer_mark_set (GtkTextBuffer *buffer,
176                                   GtkTextIter *iter,
177                                   GtkTextMark *mark,
178                                   ModestMsgEditWindow *userdata);
179 void vadj_changed (GtkAdjustment *adj, 
180                    ModestMsgEditWindow *window);
181
182
183
184
185
186 static void DEBUG_BUFFER (WPTextBuffer *buffer)
187 {
188 #ifdef DEBUG
189         GtkTextIter iter;
190         g_message ("BEGIN BUFFER OF SIZE %d", gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (buffer)));
191
192         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
193         while (!gtk_text_iter_is_end (&iter)) {
194                 GString *output = g_string_new ("");
195                 GSList *toggled_tags;
196                 GSList *node;
197
198                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
199                 g_string_append_printf (output, "%d: CLOSED [ ", gtk_text_iter_get_offset (&iter));
200                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
201                         GtkTextTag *tag = (GtkTextTag *) node->data;
202                         const gchar *name;
203                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
204                         output = g_string_append (output, name);
205                         g_string_append (output, " ");
206                 }
207                 output = g_string_append (output, "] OPENED [ ");
208                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
209                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
210                         GtkTextTag *tag = (GtkTextTag *) node->data;
211                         const gchar *name;
212                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
213                         output = g_string_append (output, name);
214                         g_string_append (output, " ");
215                 }
216                 output = g_string_append (output, "]\n");
217                 g_message ("%s", output->str);
218                 g_string_free (output, TRUE);
219                 gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
220         }
221         g_message ("END BUFFER");
222 #endif
223 }
224
225
226 /* static gboolean */
227 /* on_key_pressed (GtkWidget *self, */
228 /*              GdkEventKey *event, */
229 /*              gpointer user_data); */
230
231 /* list my signals */
232 enum {
233         /* MY_SIGNAL_1, */
234         /* MY_SIGNAL_2, */
235         LAST_SIGNAL
236 };
237
238 typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate;
239 struct _ModestMsgEditWindowPrivate {
240         GtkWidget   *msg_body;
241         GtkWidget   *frame;
242         GtkWidget   *header_box;
243         
244         ModestPairList *from_field_protos;
245         GtkWidget   *from_field;
246         
247         GtkWidget   *to_field;
248         GtkWidget   *cc_field;
249         GtkWidget   *bcc_field;
250         GtkWidget   *subject_field;
251         GtkWidget   *attachments_view;
252         GtkWidget   *priority_icon;
253         GtkWidget   *add_attachment_button;
254
255         GtkWidget   *cc_caption;
256         GtkWidget   *bcc_caption;
257         gboolean     update_caption_visibility;
258         GtkWidget   *attachments_caption;
259
260         GtkTextBuffer *text_buffer;
261
262         GtkWidget   *font_size_toolitem;
263         GtkWidget   *font_face_toolitem;
264         GtkWidget   *font_color_button;
265         GSList      *font_items_group;
266         GtkWidget   *font_tool_button_label;
267         GSList      *size_items_group;
268         GtkWidget   *size_tool_button_label;
269         
270         GtkWidget   *find_toolbar;
271         gchar       *last_search;
272
273         GtkWidget   *scroll;
274         guint        scroll_drag_timeout_id;
275
276         gint last_cid;
277         TnyList *attachments;
278         TnyList *images;
279         guint64 images_size;
280         gint images_count;
281
282         TnyHeaderFlags priority_flags;
283         
284         gboolean    can_undo, can_redo;
285         gulong      clipboard_change_handler_id;
286         gulong      default_clipboard_change_handler_id;
287         gulong      account_removed_handler_id;
288         gchar       *clipboard_text;
289
290         TnyMsg      *draft_msg;
291         TnyMsg      *outbox_msg;
292         gchar       *msg_uid;
293
294         gboolean    sent;
295 };
296
297 #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
298                                                     MODEST_TYPE_MSG_EDIT_WINDOW, \
299                                                     ModestMsgEditWindowPrivate))
300 /* globals */
301 static GtkWindowClass *parent_class = NULL;
302
303 /* uncomment the following if you have defined any signals */
304 /* static guint signals[LAST_SIGNAL] = {0}; */
305
306 GType
307 modest_msg_edit_window_get_type (void)
308 {
309         static GType my_type = 0;
310         if (!my_type) {
311                 static const GTypeInfo my_info = {
312                         sizeof(ModestMsgEditWindowClass),
313                         NULL,           /* base init */
314                         NULL,           /* base finalize */
315                         (GClassInitFunc) modest_msg_edit_window_class_init,
316                         NULL,           /* class finalize */
317                         NULL,           /* class data */
318                         sizeof(ModestMsgEditWindow),
319                         1,              /* n_preallocs */
320                         (GInstanceInitFunc) modest_msg_edit_window_init,
321                         NULL
322                 };
323                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
324                                                   "ModestMsgEditWindow",
325                                                   &my_info, 0);
326
327         }
328         return my_type;
329 }
330
331 static void
332 save_state (ModestWindow *self)
333 {
334         modest_widget_memory_save (modest_runtime_get_conf(),
335                                    G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
336 }
337
338
339 static void
340 restore_settings (ModestMsgEditWindow *self)
341 {
342         ModestConf *conf = NULL;
343         GtkAction *action;
344         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
345
346         conf = modest_runtime_get_conf ();
347         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
348                                             "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
349         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
350                                       modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR, NULL));
351         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
352                                             "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu");
353         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
354                                       modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
355
356         /* set initial state of cc and bcc */
357         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewCcFieldMenu");
358         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
359                                                modest_conf_get_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, NULL));
360         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewBccFieldMenu");
361         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
362                                                modest_conf_get_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, NULL));
363
364         /* Dim at start clipboard actions */
365         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CutMenu");
366         gtk_action_set_sensitive (action, FALSE);
367         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CopyMenu");
368         gtk_action_set_sensitive (action, FALSE);
369         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/RemoveAttachmentsMenu");
370         gtk_action_set_sensitive (action, FALSE);
371
372         modest_widget_memory_restore (conf,
373                                       G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
374 }
375
376
377 static void
378 modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass)
379 {
380         GObjectClass *gobject_class;
381         ModestWindowClass *modest_window_class;
382         gobject_class = (GObjectClass*) klass;
383         modest_window_class = (ModestWindowClass*) klass;
384
385         parent_class            = g_type_class_peek_parent (klass);
386         gobject_class->finalize = modest_msg_edit_window_finalize;
387
388         modest_window_class->set_zoom_func = set_zoom_do_nothing;
389         modest_window_class->get_zoom_func = get_zoom_do_nothing;
390         modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented;
391         modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented;
392         modest_window_class->show_toolbar_func = modest_msg_edit_window_show_toolbar;
393         modest_window_class->save_state_func = save_state;
394         modest_window_class->disconnect_signals_func = modest_msg_edit_window_disconnect_signals;
395
396         g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate));
397 }
398
399 static void
400 modest_msg_edit_window_init (ModestMsgEditWindow *obj)
401 {
402         ModestMsgEditWindowPrivate *priv;
403         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
404
405         priv->msg_body      = NULL;
406         priv->frame         = NULL;
407         priv->from_field    = NULL;
408         priv->to_field      = NULL;
409         priv->cc_field      = NULL;
410         priv->bcc_field     = NULL;
411         priv->subject_field = NULL;
412         priv->attachments   = TNY_LIST (tny_simple_list_new ());
413         priv->images        = TNY_LIST (tny_simple_list_new ());
414         priv->images_size   = 0;
415         priv->images_count  = 0;
416         priv->last_cid      = 0;
417
418         priv->cc_caption    = NULL;
419         priv->bcc_caption    = NULL;
420         priv->update_caption_visibility = FALSE;
421
422         priv->priority_flags = 0;
423
424         priv->find_toolbar = NULL;
425         priv->last_search = NULL;
426
427         priv->draft_msg = NULL;
428         priv->outbox_msg = NULL;
429         priv->msg_uid = NULL;
430
431         priv->can_undo = FALSE;
432         priv->can_redo = FALSE;
433         priv->clipboard_change_handler_id = 0;
434         priv->default_clipboard_change_handler_id = 0;
435         priv->account_removed_handler_id = 0;
436         priv->clipboard_text = NULL;
437         priv->sent = FALSE;
438
439         priv->scroll_drag_timeout_id = 0;
440
441         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
442                                             GTK_WINDOW(obj),"applications_email_editor");
443
444         if (!is_wp_text_buffer_started) {
445                 is_wp_text_buffer_started = TRUE;
446                 wp_text_buffer_library_init ();
447         }
448
449         init_window (obj);
450         
451         hildon_program_add_window (hildon_program_get_instance(),
452                                    HILDON_WINDOW(obj));
453 }
454
455
456 /* FIXME: this is a dup from the one in gtk/ */
457
458 /** 
459  * @result: A ModestPairList, which must be freed with modest_pair_list_free().
460  */
461 static ModestPairList*
462 get_transports (void)
463 {
464         GSList *transports = NULL;
465         
466         ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr();
467         GSList *accounts = modest_account_mgr_account_names (account_mgr, 
468                                                              TRUE /* only enabled accounts. */); 
469                                                 
470         GSList *cursor = accounts;
471         while (cursor) {
472                 gchar *account_name = cursor->data;
473                 gchar *from_string  = NULL;
474                 if (account_name) {
475                         from_string = modest_account_mgr_get_from_string (account_mgr,
476                                                                           account_name);
477                 }
478                 
479                 if (from_string && account_name) {
480                         gchar *name = account_name;
481                         ModestPair *pair = modest_pair_new ((gpointer) name,
482                                                 (gpointer) from_string , TRUE);
483                         transports = g_slist_prepend (transports, pair);
484                 }
485                 
486                 cursor = cursor->next;
487         }
488         g_slist_free (accounts); /* only free the accounts, not the elements,
489                                   * because they are used in the pairlist */
490         return transports;
491 }
492
493 static void window_focus (GtkWindow *window,
494                           GtkWidget *widget,
495                           gpointer userdata)
496 {
497         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
498 }
499
500 gboolean
501 scroll_drag_timeout (gpointer userdata)
502 {
503         ModestMsgEditWindow *win = (ModestMsgEditWindow *) userdata;
504         ModestMsgEditWindowPrivate *priv;
505
506         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(win);
507
508         correct_scroll_without_drag_check (win);
509
510         priv->scroll_drag_timeout_id = 0;
511
512         return FALSE;
513 }
514
515 static void
516 correct_scroll_without_drag_check (ModestMsgEditWindow *w)
517 {
518         ModestMsgEditWindowPrivate *priv;
519         GtkTextMark *insert;
520         GtkTextIter iter;
521         GdkRectangle rectangle;
522         GtkAdjustment *vadj;
523         gdouble new_value;
524         gint offset;
525         GdkWindow *window;
526
527         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
528
529         insert = gtk_text_buffer_get_insert (priv->text_buffer);
530         gtk_text_buffer_get_iter_at_mark (priv->text_buffer, &iter, insert);
531
532         gtk_text_view_get_iter_location (GTK_TEXT_VIEW (priv->msg_body), &iter, &rectangle);
533         vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll));
534         offset = priv->msg_body->allocation.y;
535
536         new_value = vadj->value;
537         
538         if ((offset + rectangle.y + rectangle.height) > 
539             ((gint) (vadj->value +vadj->page_size))) {
540                 new_value = (offset + rectangle.y) + vadj->page_size * 0.75;
541                 if (new_value > vadj->upper - vadj->page_size)
542                         new_value = vadj->upper - vadj->page_size;
543         } else if ((offset + rectangle.y) < ((gint) vadj->value)) {
544                 new_value = (offset + rectangle.y - vadj->page_size * 0.75);
545                 if (((gint) (new_value + vadj->page_size)) < (offset + rectangle.y + rectangle.height))
546                         new_value = offset + rectangle.y + rectangle.height - (gint) vadj->page_size;
547                 if (new_value < 0.0)
548                         new_value = 0.0;
549                 if (new_value > vadj->value)
550                         new_value = vadj->value;
551         }
552
553         if (vadj->value != new_value) {
554                 g_signal_emit_by_name (GTK_TEXT_VIEW(priv->msg_body)->layout,
555                                        "invalidated");
556                 vadj->value = new_value;
557                 gtk_adjustment_value_changed (vadj);
558                 /* invalidate body */
559                 window = gtk_widget_get_parent_window (priv->msg_body);
560                 if (window)
561                         gdk_window_invalidate_rect (window, NULL, TRUE);
562         }
563
564 }
565
566 static void
567 correct_scroll (ModestMsgEditWindow *w)
568 {
569         ModestMsgEditWindowPrivate *priv;
570
571         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
572         if (gtk_grab_get_current () == priv->msg_body) {
573                 if (priv->scroll_drag_timeout_id == 0) {
574                         priv->scroll_drag_timeout_id = g_timeout_add (500, (GSourceFunc) scroll_drag_timeout,
575                                                                       (gpointer) w);
576                 }
577                 return;
578         }
579
580         correct_scroll_without_drag_check (w);
581 }
582
583 static void
584 text_buffer_end_user_action (GtkTextBuffer *buffer,
585                              ModestMsgEditWindow *userdata)
586 {
587
588         correct_scroll (userdata);
589 }
590
591 static void
592 text_buffer_mark_set (GtkTextBuffer *buffer,
593                       GtkTextIter *iter,
594                       GtkTextMark *mark,
595                       ModestMsgEditWindow *userdata)
596 {
597         gtk_text_buffer_begin_user_action (buffer);
598         gtk_text_buffer_end_user_action (buffer);
599 }
600
601 void vadj_changed (GtkAdjustment *adj,
602                    ModestMsgEditWindow *window)
603 {
604         correct_scroll (window);
605 }
606
607 static void
608 connect_signals (ModestMsgEditWindow *obj)
609 {
610         ModestMsgEditWindowPrivate *priv;
611
612         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
613
614         g_signal_connect (G_OBJECT (priv->text_buffer), "refresh_attributes",
615                           G_CALLBACK (text_buffer_refresh_attributes), obj);
616         g_signal_connect (G_OBJECT (priv->text_buffer), "can-undo",
617                           G_CALLBACK (text_buffer_can_undo), obj);
618         g_signal_connect (G_OBJECT (priv->text_buffer), "can-redo",
619                           G_CALLBACK (text_buffer_can_redo), obj);
620         g_signal_connect (G_OBJECT (priv->text_buffer), "changed",
621                           G_CALLBACK (body_changed), obj);
622         g_signal_connect (G_OBJECT (priv->text_buffer), "insert-text", 
623                           G_CALLBACK (text_buffer_insert_text), obj);
624         g_signal_connect (G_OBJECT (obj), "window-state-event",
625                           G_CALLBACK (modest_msg_edit_window_window_state_event),
626                           NULL);
627         g_signal_connect (G_OBJECT (priv->text_buffer), "end-user-action",
628                           G_CALLBACK (text_buffer_end_user_action), obj);
629         g_signal_connect (G_OBJECT (priv->text_buffer), "mark-set",
630                           G_CALLBACK (text_buffer_mark_set), obj);
631         g_signal_connect_after (G_OBJECT (priv->text_buffer), "apply-tag",
632                                 G_CALLBACK (text_buffer_apply_tag), obj);
633         g_signal_connect_swapped (G_OBJECT (priv->to_field), "open-addressbook", 
634                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
635         g_signal_connect_swapped (G_OBJECT (priv->cc_field), "open-addressbook", 
636                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
637         g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", 
638                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
639
640         g_signal_connect (G_OBJECT (priv->add_attachment_button), "clicked",
641                           G_CALLBACK (modest_msg_edit_window_add_attachment_clicked), obj);
642
643         g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event",
644                           G_CALLBACK (msg_body_focus), obj);
645         g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event",
646                           G_CALLBACK (msg_body_focus), obj);
647         g_signal_connect (G_OBJECT (obj), "set-focus", G_CALLBACK (window_focus), obj);
648         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))),
649                           "changed", G_CALLBACK (recpt_field_changed), obj);
650         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))),
651                           "changed", G_CALLBACK (recpt_field_changed), obj);
652         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))),
653                           "changed", G_CALLBACK (recpt_field_changed), obj);
654         g_signal_connect (G_OBJECT (priv->subject_field), "changed", G_CALLBACK (subject_field_changed), obj);
655         g_signal_connect_after (G_OBJECT (priv->subject_field), "move-cursor", G_CALLBACK (subject_field_move_cursor), obj);
656         g_signal_connect (G_OBJECT (priv->subject_field), "insert-text", G_CALLBACK (subject_field_insert_text), obj);
657
658         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_edit_window_find_toolbar_close), obj);
659         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_edit_window_find_toolbar_search), obj);
660
661         g_signal_connect (G_OBJECT (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll))),
662                           "changed",
663                           G_CALLBACK (vadj_changed),
664                           obj);
665
666         priv->clipboard_change_handler_id = 
667                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change",
668                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
669         priv->default_clipboard_change_handler_id = 
670                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)), "owner-change",
671                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
672
673 }
674
675 static void
676 init_window (ModestMsgEditWindow *obj)
677 {
678         GtkWidget *from_caption, *to_caption, *subject_caption;
679         GtkWidget *main_vbox;
680         ModestMsgEditWindowPrivate *priv;
681         GtkActionGroup *action_group;
682         ModestWindowPrivate *parent_priv;
683         GdkPixbuf *window_icon = NULL;
684         GError *error = NULL;
685
686         GtkSizeGroup *size_group;
687         GtkWidget *subject_box;
688         GtkWidget *attachment_icon;
689         GtkWidget *window_box;
690 #if (GTK_MINOR_VERSION >= 10)
691         GdkAtom deserialize_type;
692 #endif
693         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
694         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
695
696         parent_priv->ui_manager = gtk_ui_manager_new();
697         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
698         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
699
700         /* Add common actions */
701         gtk_action_group_add_actions (action_group,
702                                       modest_msg_edit_action_entries,
703                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
704                                       obj);
705         gtk_action_group_add_toggle_actions (action_group,
706                                              modest_msg_edit_toggle_action_entries,
707                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
708                                              obj);
709         gtk_action_group_add_radio_actions (action_group,
710                                             modest_msg_edit_alignment_radio_action_entries,
711                                             G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries),
712                                             GTK_JUSTIFY_LEFT,
713                                             G_CALLBACK (modest_ui_actions_on_change_justify),
714                                             obj);
715         gtk_action_group_add_radio_actions (action_group,
716                                             modest_msg_edit_priority_action_entries,
717                                             G_N_ELEMENTS (modest_msg_edit_priority_action_entries),
718                                             0,
719                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority),
720                                             obj);
721         gtk_action_group_add_radio_actions (action_group,
722                                             modest_msg_edit_file_format_action_entries,
723                                             G_N_ELEMENTS (modest_msg_edit_file_format_action_entries),
724                                             modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL),
725                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format),
726                                             obj);
727         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
728         g_object_unref (action_group);
729
730         /* Load the UI definition */
731         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml",
732                                          &error);
733         if (error != NULL) {
734                 g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message);
735                 g_clear_error (&error);
736         }
737
738         /* Add accelerators */
739         gtk_window_add_accel_group (GTK_WINDOW (obj), 
740                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
741
742         parent_priv->menubar = NULL;
743
744         size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
745
746         /* Note: This ModestPairList* must exist for as long as the combo
747          * that uses it, because the ModestComboBox uses the ID opaquely, 
748          * so it can't know how to manage its memory. */ 
749         priv->from_field    = modest_combo_box_new (NULL, g_str_equal);
750
751         priv->to_field      = modest_recpt_editor_new ();
752         priv->cc_field      = modest_recpt_editor_new ();
753         priv->bcc_field     = modest_recpt_editor_new ();
754         subject_box = gtk_hbox_new (FALSE, 0);
755         priv->priority_icon = gtk_image_new ();
756         gtk_box_pack_start (GTK_BOX (subject_box), priv->priority_icon, FALSE, FALSE, 0);
757         priv->subject_field = gtk_entry_new_with_max_length (SUBJECT_MAX_LENGTH);
758         g_object_set (G_OBJECT (priv->subject_field), "truncate-multiline", TRUE, NULL);
759         hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->subject_field), 
760                                          HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
761         gtk_box_pack_start (GTK_BOX (subject_box), priv->subject_field, TRUE, TRUE, 0);
762         priv->add_attachment_button = gtk_button_new ();
763         GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (priv->add_attachment_button), GTK_CAN_FOCUS);
764         gtk_button_set_relief (GTK_BUTTON (priv->add_attachment_button), GTK_RELIEF_NONE);
765         gtk_button_set_focus_on_click (GTK_BUTTON (priv->add_attachment_button), FALSE);
766         gtk_button_set_alignment (GTK_BUTTON (priv->add_attachment_button), 1.0, 1.0);
767         attachment_icon = gtk_image_new_from_icon_name (MODEST_HEADER_ICON_ATTACH, GTK_ICON_SIZE_BUTTON);
768         gtk_container_add (GTK_CONTAINER (priv->add_attachment_button), attachment_icon);
769         gtk_box_pack_start (GTK_BOX (subject_box), priv->add_attachment_button, FALSE, FALSE, 0);
770         priv->attachments_view = modest_attachments_view_new (NULL);
771         
772         priv->header_box = gtk_vbox_new (FALSE, 0);
773         
774         from_caption = hildon_caption_new (size_group, _("mail_va_from"), priv->from_field, NULL, 0);
775         to_caption = hildon_caption_new (size_group, _("mail_va_to"), priv->to_field, NULL, 0);
776         priv->cc_caption = hildon_caption_new (size_group, _("mail_va_cc"), priv->cc_field, NULL, 0);
777         priv->bcc_caption = hildon_caption_new (size_group, _("mail_va_hotfix1"), priv->bcc_field, NULL, 0);
778         subject_caption = hildon_caption_new (size_group, _("mail_va_subject"), subject_box, NULL, 0);
779         priv->attachments_caption = hildon_caption_new (size_group, _("mail_va_attachment"), priv->attachments_view, NULL, 0);
780         g_object_unref (size_group);
781
782         size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
783         modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->to_field), size_group);
784         modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->cc_field), size_group);
785         modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->bcc_field), size_group);
786         gtk_size_group_add_widget (size_group, priv->subject_field);
787         gtk_size_group_add_widget (size_group, priv->attachments_view);
788         g_object_unref (size_group);
789
790         gtk_box_pack_start (GTK_BOX (priv->header_box), from_caption, FALSE, FALSE, 0);
791         gtk_box_pack_start (GTK_BOX (priv->header_box), to_caption, FALSE, FALSE, 0);
792         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->cc_caption, FALSE, FALSE, 0);
793         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->bcc_caption, FALSE, FALSE, 0);
794         gtk_box_pack_start (GTK_BOX (priv->header_box), subject_caption, FALSE, FALSE, 0);
795         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->attachments_caption, FALSE, FALSE, 0);
796         gtk_widget_set_no_show_all (priv->attachments_caption, TRUE);
797
798
799         priv->msg_body = wp_text_view_new ();
800         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->msg_body), GTK_WRAP_WORD_CHAR);
801         priv->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
802         g_object_set (priv->text_buffer, "font_scale", DEFAULT_FONT_SCALE, NULL);
803         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
804 #if (GTK_MINOR_VERSION >= 10)
805         gtk_text_buffer_register_serialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), "wp-text-buffer");
806         deserialize_type = gtk_text_buffer_register_deserialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), 
807                                                                        "wp-text-buffer");
808         gtk_text_buffer_deserialize_set_can_create_tags (GTK_TEXT_BUFFER (priv->text_buffer), 
809                                                          deserialize_type, TRUE);
810 #endif
811         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
812
813         priv->find_toolbar = hildon_find_toolbar_new (NULL);
814         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
815
816 /*      g_signal_connect (G_OBJECT (obj), "key_pressed", G_CALLBACK (on_key_pressed), NULL) */
817
818         priv->scroll = gtk_scrolled_window_new (NULL, NULL);
819         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
820         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scroll), GTK_SHADOW_NONE);
821         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->scroll), TRUE);
822
823         main_vbox = gtk_vbox_new  (FALSE, DEFAULT_MAIN_VBOX_SPACING);
824
825         gtk_box_pack_start (GTK_BOX(main_vbox), priv->header_box, FALSE, FALSE, 0);
826         priv->frame = gtk_frame_new (NULL);
827         gtk_box_pack_start (GTK_BOX(main_vbox), priv->frame, TRUE, TRUE, 0);
828
829         gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (priv->scroll), main_vbox);
830         gtk_container_set_focus_vadjustment (GTK_CONTAINER (main_vbox), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll)));
831         gtk_widget_show_all (GTK_WIDGET(priv->scroll));
832         
833         window_box = gtk_vbox_new (FALSE, 0);
834         gtk_container_add (GTK_CONTAINER(obj), window_box);
835
836         gtk_box_pack_start (GTK_BOX (window_box), priv->scroll, TRUE, TRUE, 0);
837
838         gtk_container_add (GTK_CONTAINER (priv->frame), priv->msg_body);
839
840         /* Set window icon */
841         window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON, MODEST_ICON_SIZE_BIG); 
842         if (window_icon) {
843                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
844                 g_object_unref (window_icon);
845         }       
846 }
847         
848 static void
849 modest_msg_edit_window_disconnect_signals (ModestWindow *window)
850 {
851         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
852
853         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
854             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
855                                            priv->clipboard_change_handler_id))
856                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
857                                              priv->clipboard_change_handler_id);
858         if (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD) &&
859             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
860                                            priv->default_clipboard_change_handler_id))
861                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
862                                              priv->default_clipboard_change_handler_id);
863
864         if (priv->account_removed_handler_id && 
865             g_signal_handler_is_connected (modest_runtime_get_account_store (), 
866                                            priv->account_removed_handler_id))
867                 g_signal_handler_disconnect(modest_runtime_get_account_store (), 
868                                            priv->account_removed_handler_id);
869 }
870
871 static void
872 modest_msg_edit_window_finalize (GObject *obj)
873 {
874         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
875         
876         /* Sanity check: shouldn't be needed, the window mgr should
877            call this function before */
878         modest_msg_edit_window_disconnect_signals (MODEST_WINDOW (obj));
879
880         if (priv->clipboard_text != NULL) {
881                 g_free (priv->clipboard_text);
882                 priv->clipboard_text = NULL;
883         }
884         
885         if (priv->draft_msg != NULL) {
886                 TnyHeader *header = tny_msg_get_header (priv->draft_msg);
887                 if (TNY_IS_HEADER (header)) {
888                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
889                         modest_window_mgr_unregister_header (mgr, header);
890                 }
891                 g_object_unref (priv->draft_msg);
892                 priv->draft_msg = NULL;
893         }
894         if (priv->outbox_msg != NULL) {
895                 TnyHeader *header = tny_msg_get_header (priv->outbox_msg);
896                 if (TNY_IS_HEADER (header)) {
897                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
898                         modest_window_mgr_unregister_header (mgr, header);
899                 }
900                 g_object_unref (priv->outbox_msg);
901                 priv->outbox_msg = NULL;
902         }
903         if (priv->scroll_drag_timeout_id > 0) {
904                 g_source_remove (priv->scroll_drag_timeout_id);
905                 priv->scroll_drag_timeout_id = 0;
906         }
907         g_free (priv->msg_uid);
908         g_free (priv->last_search);
909         g_slist_free (priv->font_items_group);
910         g_slist_free (priv->size_items_group);
911         g_object_unref (priv->attachments);
912         g_object_unref (priv->images);
913
914         /* This had to stay alive for as long as the combobox that used it: */
915         modest_pair_list_free (priv->from_field_protos);
916         
917         G_OBJECT_CLASS(parent_class)->finalize (obj);
918 }
919
920 static GdkPixbuf *
921 pixbuf_from_stream (TnyStream *stream, const gchar *mime_type, guint64 *stream_size)
922 {
923         GdkPixbufLoader *loader;
924         GdkPixbuf *pixbuf;
925         guint64 size;
926         
927         size = 0;
928
929         loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL);
930
931         if (loader == NULL) {
932                 if (stream_size)
933                         *stream_size = 0;
934                 return NULL;
935         }
936
937         tny_stream_reset (TNY_STREAM (stream));
938         while (!tny_stream_is_eos (TNY_STREAM (stream))) {
939                 unsigned char read_buffer[128];
940                 gint readed;
941                 readed = tny_stream_read (TNY_STREAM (stream), (char *) read_buffer, 128);
942                 size += readed;
943                 if (!gdk_pixbuf_loader_write (loader, read_buffer, readed, NULL))
944                         break;
945         }
946
947         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
948         g_object_ref (pixbuf);
949         gdk_pixbuf_loader_close (loader, NULL);
950         g_object_unref (loader);
951
952         if (gdk_pixbuf_get_width (pixbuf) > IMAGE_MAX_WIDTH) {
953                 GdkPixbuf *new_pixbuf;
954                 gint new_height;
955                 new_height = (gdk_pixbuf_get_height (pixbuf) * IMAGE_MAX_WIDTH) /
956                         gdk_pixbuf_get_width (pixbuf);
957                 new_pixbuf = gdk_pixbuf_scale_simple (pixbuf, IMAGE_MAX_WIDTH, new_height, GDK_INTERP_BILINEAR);
958                 g_object_unref (pixbuf);
959                 pixbuf = new_pixbuf;
960         }
961
962         if (stream_size)
963                 *stream_size = size;
964
965         return pixbuf;
966 }
967
968 static void
969 replace_with_images (ModestMsgEditWindow *self, TnyList *attachments)
970 {
971         ModestMsgEditWindowPrivate *priv;
972         TnyIterator *iter;
973
974         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
975
976         for (iter = tny_list_create_iterator (attachments);
977              !tny_iterator_is_done (iter);
978              tny_iterator_next (iter)) {
979                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
980                 const gchar *cid = tny_mime_part_get_content_id (part);
981                 const gchar *mime_type = tny_mime_part_get_content_type (part);
982                 if ((cid != NULL)&&(mime_type != NULL)) {
983                         guint64 stream_size;
984                         TnyStream *stream = tny_mime_part_get_stream (part);
985                         GdkPixbuf *pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
986
987
988                         g_object_unref (stream);
989
990                         if (pixbuf != NULL) {
991                                 priv->images_count ++;
992                                 priv->images_size += stream_size;
993                                 wp_text_buffer_replace_image (WP_TEXT_BUFFER (priv->text_buffer), cid, pixbuf);
994                                 g_object_unref (pixbuf);
995                         }
996                 }
997                 g_object_unref (part);
998         }
999 }
1000
1001 static void
1002 get_related_images (ModestMsgEditWindow *self, TnyMsg *msg)
1003 {
1004         TnyMimePart *parent = NULL;
1005         const gchar *content_type = NULL;
1006         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1007
1008         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
1009         
1010         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1011                 parent = g_object_ref (msg);
1012         } else if (content_type && !g_strcasecmp (content_type, "multipart/mixed")) {
1013                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1014                 TnyIterator *iter;
1015
1016                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
1017                 iter = tny_list_create_iterator (parts);
1018                 while (!tny_iterator_is_done (iter)) {
1019                         TnyMimePart *part;
1020                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1021                         content_type = tny_mime_part_get_content_type (part);
1022                         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1023                                 parent = part;
1024                                 break;
1025                         } else {
1026                                 g_object_unref (part);
1027                         }
1028                         tny_iterator_next (iter);
1029                 }
1030                 g_object_unref (iter);
1031                 g_object_unref (parts);
1032         }
1033
1034         if (parent != NULL) {
1035                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1036                 TnyIterator *iter;
1037
1038                 tny_mime_part_get_parts (TNY_MIME_PART (parent), parts);
1039                 iter = tny_list_create_iterator (parts);
1040                 while (!tny_iterator_is_done (iter)) {
1041                         TnyMimePart *part;
1042                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1043                         content_type = tny_mime_part_get_content_type (part);
1044                         if (content_type && g_str_has_prefix (content_type, "image/")) {
1045                                 tny_list_prepend (priv->images, (GObject *) part);
1046                         } 
1047                         g_object_unref (part);
1048                         tny_iterator_next (iter);
1049                 }
1050                 g_object_unref (iter);
1051                 g_object_unref (parts);
1052                 g_object_unref (parent);
1053         }
1054 }
1055
1056 static void
1057 update_last_cid (ModestMsgEditWindow *self, TnyList *attachments)
1058 {
1059         TnyIterator *iter;
1060         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1061
1062         for (iter = tny_list_create_iterator (attachments) ; 
1063              !tny_iterator_is_done (iter);
1064              tny_iterator_next (iter)) {
1065                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1066                 const gchar *cid = tny_mime_part_get_content_id (part);
1067                 if (cid != NULL) {
1068                         char *invalid = NULL;
1069                         gint int_cid = strtol (cid, &invalid, 10);
1070                         if ((invalid != NULL) && (*invalid == '\0') && (int_cid > priv->last_cid)) {
1071                                 priv->last_cid = int_cid;
1072                         }
1073                 }
1074                 g_object_unref (part);
1075         }
1076 }
1077
1078 static void
1079 set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich)
1080 {
1081         TnyHeader *header;
1082         const gchar *to, *cc, *bcc, *subject;
1083         gchar *body;
1084         ModestMsgEditWindowPrivate *priv;
1085         GtkTextIter iter;
1086         TnyHeaderFlags priority_flags;
1087         TnyFolder *msg_folder;
1088         gboolean is_html = FALSE;
1089         
1090         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1091         g_return_if_fail (TNY_IS_MSG (msg));
1092
1093         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1094
1095         header = tny_msg_get_header (msg);
1096         to      = tny_header_get_to (header);
1097         cc      = tny_header_get_cc (header);
1098         bcc     = tny_header_get_bcc (header);
1099         subject = tny_header_get_subject (header);
1100         priority_flags = tny_header_get_priority (header);
1101
1102         if (to)
1103                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field),  to);
1104         if (cc) {
1105                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field),  cc);
1106                 gtk_widget_set_no_show_all (priv->cc_caption, FALSE);
1107                 gtk_widget_show (priv->cc_caption);
1108         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) {
1109                 gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
1110                 gtk_widget_hide (priv->cc_caption);
1111         }
1112         if (bcc) {
1113                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), bcc);
1114                 gtk_widget_set_no_show_all (priv->bcc_caption, FALSE);
1115                 gtk_widget_show (priv->bcc_caption);
1116         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) {
1117                 gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
1118                 gtk_widget_hide (priv->bcc_caption);
1119         } 
1120         if (subject)
1121                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
1122         modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self),
1123                                                    priority_flags);
1124
1125         update_window_title (self);
1126
1127         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1128         body = modest_tny_msg_get_body (msg, TRUE, &is_html);
1129
1130         if ((body == NULL)||(body[0] == '\0')) {
1131                 g_free (body);
1132                 body = modest_text_utils_convert_to_html ("");
1133                 is_html = FALSE;
1134         }
1135         wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1136         wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer),
1137                                             (gchar *) body,
1138                                             strlen (body));
1139         wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer));
1140         g_free (body);
1141
1142         /* Add attachments to the view */
1143         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg);
1144         priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1145         if (tny_list_get_length (priv->attachments) == 0) {
1146                 gtk_widget_hide (priv->attachments_caption);
1147         } else {
1148                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
1149                 gtk_widget_show_all (priv->attachments_caption);
1150         }
1151         get_related_images (self, msg);
1152         update_last_cid (self, priv->attachments);
1153         update_last_cid (self, priv->images);
1154         replace_with_images (self, priv->images);
1155
1156         if (preserve_is_rich && !is_html) {
1157                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1158         /* Get the default format required from configuration */
1159         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) {
1160                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1161         }
1162
1163         /* Set the default focus depending on having already a To: field or not */
1164         if ((!to)||(*to == '\0')) {
1165                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
1166         } else {
1167                 gtk_widget_grab_focus (priv->msg_body);
1168         }
1169
1170         /* TODO: lower priority, select in the From: combo to the
1171            value that comes from msg <- not sure, should it be
1172            allowed? */
1173         
1174         DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer));
1175
1176         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1177         gtk_text_buffer_place_cursor (priv->text_buffer, &iter);
1178
1179         modest_msg_edit_window_reset_modified (self);
1180
1181         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1182         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1183         text_buffer_can_undo (priv->text_buffer, FALSE, self);
1184         text_buffer_can_redo (priv->text_buffer, FALSE, self);
1185
1186         if (priv->msg_uid) {
1187                 g_free (priv->msg_uid);
1188                 priv->msg_uid = NULL;
1189         }
1190
1191         /* we should set a reference to the incoming message if it is a draft */
1192         msg_folder = tny_msg_get_folder (msg);
1193         if (msg_folder) {               
1194                 if (modest_tny_folder_is_local_folder (msg_folder)) {
1195                         TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder);
1196                         if (type == TNY_FOLDER_TYPE_INVALID)
1197                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1198                         
1199                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
1200                                 priv->draft_msg = g_object_ref(msg);
1201                         if (type == TNY_FOLDER_TYPE_OUTBOX)
1202                                 priv->outbox_msg = g_object_ref(msg);
1203                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1204                 }
1205                 g_object_unref (msg_folder);
1206         }
1207 }
1208
1209 static void
1210 menu_tool_button_clicked_popup (GtkMenuToolButton *item,
1211                                 gpointer data)
1212 {
1213         GList *item_children, *node;
1214         GtkWidget *bin_child;
1215
1216         bin_child = gtk_bin_get_child (GTK_BIN(item));
1217
1218         item_children = gtk_container_get_children (GTK_CONTAINER (bin_child));
1219         
1220         for (node = item_children; node != NULL; node = g_list_next (node)) {
1221                 if (GTK_IS_TOGGLE_BUTTON (node->data)) {
1222                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
1223                 }
1224         }
1225         g_list_free (item_children);
1226 }
1227
1228 static void
1229 menu_tool_button_dont_expand (GtkMenuToolButton *item)
1230 {
1231         GtkWidget *box;
1232         GList *item_children, *node;
1233
1234         box = gtk_bin_get_child (GTK_BIN (item));
1235         gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
1236         item_children = gtk_container_get_children (GTK_CONTAINER (box));
1237         
1238         for (node = item_children; node != NULL; node = g_list_next (node)) {
1239                 gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START);
1240                 if (GTK_IS_TOGGLE_BUTTON (node->data))
1241                         gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5);
1242                 else if (GTK_IS_BUTTON (node->data))
1243                         gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5);
1244         }
1245         g_list_free (item_children);
1246 }
1247
1248
1249 static void
1250 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1251 {
1252         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1253         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1254         GtkWidget *placeholder;
1255         GtkWidget *tool_item;
1256         gint insert_index;
1257         gchar size_text[5];
1258         gint size_index;
1259         gint font_index;
1260         GtkWidget *sizes_menu;
1261         GtkWidget *fonts_menu;
1262         GSList *radio_group = NULL;
1263         GSList *node = NULL;
1264         gchar *markup;
1265
1266         /* Toolbar */
1267         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1268         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1269
1270         /* Font color placeholder */
1271         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1272         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1273
1274         /* font color */
1275         tool_item = GTK_WIDGET (gtk_tool_item_new ());
1276         priv->font_color_button = hildon_color_button_new ();
1277         GTK_WIDGET_UNSET_FLAGS (tool_item, GTK_CAN_FOCUS);
1278         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1279         gtk_container_add (GTK_CONTAINER (tool_item), priv->font_color_button);
1280         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1281         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1282         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1283         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1284                                   "notify::color", 
1285                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1286                                   window);
1287
1288         /* Font size and face placeholder */
1289         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1290         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1291         /* font_size */
1292         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1293         priv->size_tool_button_label = gtk_label_new (NULL);
1294         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1295         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1296                               size_text,"</span>", NULL);
1297         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1298         g_free (markup);
1299         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label);
1300         sizes_menu = gtk_menu_new ();
1301         priv->size_items_group = NULL;
1302         radio_group = NULL;
1303         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1304                 GtkWidget *size_menu_item;
1305
1306                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1307                 size_menu_item = gtk_radio_menu_item_new_with_label (radio_group, size_text);
1308                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (size_menu_item));
1309                 gtk_menu_shell_append (GTK_MENU_SHELL (sizes_menu), size_menu_item);
1310                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (size_menu_item), (wp_font_size[size_index] == 12));
1311                 gtk_widget_show (size_menu_item);
1312
1313                 priv->size_items_group = g_slist_prepend (priv->size_items_group, size_menu_item);
1314                         
1315         }
1316
1317         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1318                 GtkWidget *item = (GtkWidget *) node->data;
1319                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_size_change),
1320                                   window);
1321         }
1322
1323         priv->size_items_group = g_slist_reverse (priv->size_items_group);
1324         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), sizes_menu);
1325         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1326         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1327         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1328         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1329         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1330         priv->font_size_toolitem = tool_item;
1331
1332         /* font face */
1333         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1334         priv->font_tool_button_label = gtk_label_new (NULL);
1335         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1336         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1337         g_free(markup);
1338         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label);
1339         fonts_menu = gtk_menu_new ();
1340         priv->font_items_group = NULL;
1341         radio_group = NULL;
1342         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1343                 GtkWidget *font_menu_item;
1344                 GtkWidget *child_label;
1345
1346                 font_menu_item = gtk_radio_menu_item_new_with_label (radio_group, "");
1347                 child_label = gtk_bin_get_child (GTK_BIN (font_menu_item));
1348                 markup = g_strconcat ("<span font_family='", wp_get_font_name (font_index),"'>", 
1349                                       wp_get_font_name (font_index), "</span>", NULL);
1350                 gtk_label_set_markup (GTK_LABEL (child_label), markup);
1351                 g_free (markup);
1352                 
1353                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (font_menu_item));
1354                 gtk_menu_shell_append (GTK_MENU_SHELL (fonts_menu), font_menu_item);
1355                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (font_menu_item), (font_index == DEFAULT_FONT));
1356                 gtk_widget_show (font_menu_item);
1357
1358                 priv->font_items_group = g_slist_prepend (priv->font_items_group, font_menu_item);
1359                         
1360         }
1361         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1362                 GtkWidget *item = (GtkWidget *) node->data;
1363                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_font_change),
1364                                   window);
1365         }
1366         priv->font_items_group = g_slist_reverse (priv->font_items_group);
1367         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), fonts_menu);
1368         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1369         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1370         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1371         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1372         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1373         priv->font_face_toolitem = tool_item;
1374
1375         /* Set expand and homogeneous for remaining items */
1376         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend");
1377         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1378         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1379         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1380         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1381         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1382         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1383         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1384         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1385
1386         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1387            will not show the tool items added to the placeholders) */
1388         gtk_widget_show_all (parent_priv->toolbar);
1389
1390         /* Set the no show all *after* showing all items. We do not
1391            want the toolbar to be shown with a show all because it
1392            could go agains the gconf setting regarding showing or not
1393            the toolbar of the editor window */
1394         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1395 }
1396
1397
1398
1399 ModestWindow*
1400 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich)
1401 {
1402         GObject *obj;
1403         ModestWindowPrivate *parent_priv;
1404         ModestMsgEditWindowPrivate *priv;
1405         ModestPair *account_pair = NULL;
1406         ModestDimmingRulesGroup *menu_rules_group = NULL;
1407         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1408         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1409         ModestWindowMgr *mgr = NULL;
1410
1411         g_return_val_if_fail (msg, NULL);
1412         g_return_val_if_fail (account_name, NULL);
1413
1414         mgr = modest_runtime_get_window_mgr ();
1415         
1416         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1417
1418         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1419         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1420
1421         /* Menubar. Update the state of some toggles */
1422         parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
1423         hildon_window_set_menu (HILDON_WINDOW (obj), GTK_MENU (parent_priv->menubar));
1424         priv->from_field_protos = get_transports ();
1425         modest_combo_box_set_pair_list (MODEST_COMBO_BOX (priv->from_field), priv->from_field_protos);
1426         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1427         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1428
1429         /* Init window */
1430         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1431
1432         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1433                 
1434         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1435
1436         account_pair = modest_pair_list_find_by_first_as_string (priv->from_field_protos, account_name);
1437         if (account_pair != NULL)
1438                 modest_combo_box_set_active_id (MODEST_COMBO_BOX (priv->from_field), account_pair->first);
1439
1440         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new ();
1441         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
1442         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1443         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1444         /* Add common dimming rules */
1445         modest_dimming_rules_group_add_rules (menu_rules_group, 
1446                                               modest_msg_edit_window_menu_dimming_entries,
1447                                               G_N_ELEMENTS (modest_msg_edit_window_menu_dimming_entries),
1448                                               MODEST_WINDOW (obj));
1449         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1450                                               modest_msg_edit_window_toolbar_dimming_entries,
1451                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1452                                               MODEST_WINDOW (obj));
1453         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_button,
1454                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1455                                                     MODEST_WINDOW (obj));
1456         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1457                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1458                                                     MODEST_WINDOW (obj));
1459         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1460                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1461                                                     MODEST_WINDOW (obj));
1462         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
1463                                               modest_msg_edit_window_clipboard_dimming_entries,
1464                                               G_N_ELEMENTS (modest_msg_edit_window_clipboard_dimming_entries),
1465                                               MODEST_WINDOW (obj));
1466         /* Insert dimming rules group for this window */
1467         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
1468         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1469         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1470         /* Checks the dimming rules */
1471         g_object_unref (menu_rules_group);
1472         g_object_unref (toolbar_rules_group);
1473         g_object_unref (clipboard_rules_group);
1474         gtk_widget_show_all (GTK_WIDGET (obj));
1475
1476         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1477
1478         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1479
1480         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1481         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1482         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1483         priv->update_caption_visibility = TRUE;
1484
1485         modest_msg_edit_window_reset_modified (MODEST_MSG_EDIT_WINDOW (obj));
1486
1487         /* Track account-removed signal, this window should be closed
1488            in the case we're creating a mail associated to the account
1489            that is deleted */
1490         priv->account_removed_handler_id = 
1491                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1492                                   "account_removed",
1493                                   G_CALLBACK(on_account_removed),
1494                                   obj);
1495         
1496         return (ModestWindow*) obj;
1497 }
1498
1499 static gint
1500 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1501 {
1502         GString **string_buffer = (GString **) user_data;
1503
1504         *string_buffer = g_string_append (*string_buffer, buffer);
1505    
1506         return 0;
1507 }
1508
1509 /**
1510  * @result: A new string which should be freed with g_free().
1511  */
1512 static gchar *
1513 get_formatted_data (ModestMsgEditWindow *edit_window)
1514 {
1515         ModestMsgEditWindowPrivate *priv;
1516         GString *string_buffer = g_string_new ("");
1517         
1518         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1519
1520         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1521
1522         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1523
1524         return g_string_free (string_buffer, FALSE);
1525                                                                         
1526 }
1527
1528 MsgData * 
1529 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1530 {
1531         MsgData *data;
1532         const gchar *account_name;
1533         ModestMsgEditWindowPrivate *priv;
1534         TnyIterator *att_iter;
1535         
1536         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1537
1538         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1539                                                                         
1540         account_name = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
1541         g_return_val_if_fail (account_name, NULL);
1542         
1543         
1544         /* don't free these (except from) */
1545         data = g_slice_new0 (MsgData);
1546         data->from    =  modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(),
1547                                                              account_name);
1548         data->account_name = g_strdup (account_name);
1549         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1550         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1551         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1552         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1553         if (priv->draft_msg) {
1554                 data->draft_msg = g_object_ref (priv->draft_msg);
1555         } else if (priv->outbox_msg) {
1556                 data->draft_msg = g_object_ref (priv->outbox_msg);
1557         } else {
1558                 data->draft_msg = NULL;
1559         }
1560
1561         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1562         GtkTextIter b, e;
1563         gtk_text_buffer_get_bounds (buf, &b, &e);
1564         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1565
1566         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1567                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1568         else
1569                 data->html_body = NULL;
1570
1571         /* deep-copy the data */
1572         att_iter = tny_list_create_iterator (priv->attachments);
1573         data->attachments = NULL;
1574         while (!tny_iterator_is_done (att_iter)) {
1575                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1576                 if (!(TNY_IS_MIME_PART(part))) {
1577                         g_warning ("strange data in attachment list");
1578                         g_object_unref (part);
1579                         tny_iterator_next (att_iter);
1580                         continue;
1581                 }
1582                 data->attachments = g_list_append (data->attachments,
1583                                                    part);
1584                 tny_iterator_next (att_iter);
1585         }
1586         g_object_unref (att_iter);
1587
1588         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1589         att_iter = tny_list_create_iterator (priv->images);
1590         data->images = NULL;
1591         while (!tny_iterator_is_done (att_iter)) {
1592                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1593                 const gchar *cid;
1594                 if (!(TNY_IS_MIME_PART(part))) {
1595                         g_warning ("strange data in attachment list");
1596                         g_object_unref (part);
1597                         tny_iterator_next (att_iter);
1598                         continue;
1599                 }
1600                 cid = tny_mime_part_get_content_id (part);
1601                 if (cid) {                      
1602                         gchar *image_tag_id;
1603                         GtkTextTag *image_tag;
1604                         GtkTextIter iter;
1605                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1606                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1607                         g_free (image_tag_id);
1608                         
1609                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1610                         if (image_tag && 
1611                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1612                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1613                                 data->images = g_list_append (data->images,
1614                                                               g_object_ref (part));
1615                 }
1616                 g_object_unref (part);
1617                 tny_iterator_next (att_iter);
1618         }
1619         
1620         data->priority_flags = priv->priority_flags;
1621
1622         return data;
1623 }
1624
1625
1626 static void
1627 unref_gobject (GObject *obj, gpointer data)
1628 {
1629         if (!G_IS_OBJECT(obj))
1630                 return;
1631         g_object_unref (obj);
1632 }
1633
1634 void 
1635 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1636                                                       MsgData *data)
1637 {
1638         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1639
1640         if (!data)
1641                 return;
1642
1643         g_free (data->to);
1644         g_free (data->cc);
1645         g_free (data->bcc);
1646         g_free (data->from);
1647         g_free (data->subject);
1648         g_free (data->plain_body);
1649         g_free (data->html_body);
1650         g_free (data->account_name);
1651         
1652         if (data->draft_msg != NULL) {
1653                 g_object_unref (data->draft_msg);
1654                 data->draft_msg = NULL;
1655         }
1656         
1657         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1658         g_list_free (data->attachments);
1659         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1660         g_list_free (data->images);
1661         
1662         g_slice_free (MsgData, data);
1663 }
1664
1665 void                    
1666 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1667                                        gint *parts_count,
1668                                        guint64 *parts_size)
1669 {
1670         ModestMsgEditWindowPrivate *priv;
1671
1672         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1673
1674         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1675
1676         /* TODO: add images */
1677         *parts_size += priv->images_size;
1678         *parts_count += priv->images_count;
1679
1680 }
1681
1682 ModestMsgEditFormat
1683 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1684 {
1685         gboolean rich_text;
1686         ModestMsgEditWindowPrivate *priv = NULL;
1687         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1688
1689         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1690
1691         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1692         if (rich_text)
1693                 return MODEST_MSG_EDIT_FORMAT_HTML;
1694         else
1695                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1696 }
1697
1698 void
1699 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1700                                    ModestMsgEditFormat format)
1701 {
1702         ModestMsgEditWindowPrivate *priv;
1703
1704         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1705         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1706
1707         switch (format) {
1708         case MODEST_MSG_EDIT_FORMAT_HTML:
1709                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1710                 break;
1711         case MODEST_MSG_EDIT_FORMAT_TEXT:
1712                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1713                 break;
1714         default:
1715                 g_return_if_reached ();
1716         }
1717 }
1718
1719 ModestMsgEditFormatState *
1720 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1721 {
1722         ModestMsgEditFormatState *format_state = NULL;
1723         ModestMsgEditWindowPrivate *priv;
1724         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1725
1726         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
1727         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1728
1729         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
1730
1731         format_state = g_new0 (ModestMsgEditFormatState, 1);
1732         format_state->bold = buffer_format->bold&0x1;
1733         format_state->italics = buffer_format->italic&0x1;
1734         format_state->bullet = buffer_format->bullet&0x1;
1735         format_state->color = buffer_format->color;
1736         format_state->font_size = buffer_format->font_size;
1737         format_state->font_family = wp_get_font_name (buffer_format->font);
1738         format_state->justification = buffer_format->justification;
1739         g_free (buffer_format);
1740
1741         return format_state;
1742  
1743 }
1744
1745 void
1746 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
1747                                          const ModestMsgEditFormatState *format_state)
1748 {
1749         ModestMsgEditWindowPrivate *priv;
1750         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1751         WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1);
1752         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1753         g_return_if_fail (format_state != NULL);
1754
1755         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1756         gtk_widget_grab_focus (priv->msg_body);
1757         buffer_format->bold = (format_state->bold != FALSE);
1758         buffer_format->italic = (format_state->italics != FALSE);
1759         buffer_format->color = format_state->color;
1760         buffer_format->font_size = format_state->font_size;
1761         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
1762         buffer_format->justification = format_state->justification;
1763         buffer_format->bullet = format_state->bullet;
1764
1765         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
1766
1767         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
1768         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
1769         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
1770         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
1771         buffer_format->cs.font = (buffer_format->font != current_format->font);
1772         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
1773         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
1774
1775         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
1776         if (buffer_format->cs.bold) {
1777                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
1778                                               GINT_TO_POINTER (buffer_format->bold&0x1));
1779         }
1780         if (buffer_format->cs.italic) {
1781                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
1782                                               GINT_TO_POINTER (buffer_format->italic&0x1));
1783         }
1784         if (buffer_format->cs.color) {
1785                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1786                                               GINT_TO_POINTER (&(buffer_format->color)));
1787         }
1788         if (buffer_format->cs.font_size) {
1789                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
1790                                               GINT_TO_POINTER (buffer_format->font_size));
1791         }
1792         if (buffer_format->cs.justification) {
1793                 switch (buffer_format->justification) {
1794                 case GTK_JUSTIFY_LEFT:
1795                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
1796                                                       GINT_TO_POINTER(TRUE));
1797                         break;
1798                 case GTK_JUSTIFY_CENTER:
1799                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
1800                                                       GINT_TO_POINTER(TRUE));
1801                         break;
1802                 case GTK_JUSTIFY_RIGHT:
1803                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
1804                                                       GINT_TO_POINTER(TRUE));
1805                         break;
1806                 default:
1807                         break;
1808                 }
1809                         
1810         }
1811         if (buffer_format->cs.font) {
1812                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
1813                                               GINT_TO_POINTER (buffer_format->font));
1814         }
1815         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
1816         if (buffer_format->cs.bullet) {
1817                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
1818                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
1819         }
1820 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
1821         
1822         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
1823         
1824         g_free (current_format);
1825
1826 }
1827
1828 static void
1829 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
1830 {
1831         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1832         GtkAction *action;
1833         ModestWindowPrivate *parent_priv;
1834         ModestMsgEditWindowPrivate *priv;
1835         GtkWidget *new_size_menuitem;
1836         GtkWidget *new_font_menuitem;
1837         
1838         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1839         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1840
1841         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
1842                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu");
1843                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1844                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1845         } else {
1846                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatPlainTextMenu");
1847                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1848                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1849         }
1850
1851         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1852
1853         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1854         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
1855
1856         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1857         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
1858
1859 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
1860 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
1861
1862         action = NULL;
1863         switch (buffer_format->justification)
1864         {
1865         case GTK_JUSTIFY_LEFT:
1866                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentLeftMenu");
1867                 break;
1868         case GTK_JUSTIFY_CENTER:
1869                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentCenterMenu");
1870                 break;
1871         case GTK_JUSTIFY_RIGHT:
1872                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentRightMenu");
1873                 break;
1874         default:
1875                 break;
1876         }
1877         
1878         if (action != NULL)
1879                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1880         
1881         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
1882                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
1883                                          window);
1884         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
1885         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
1886                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
1887                                            window);
1888
1889         new_size_menuitem = GTK_WIDGET ((g_slist_nth (priv->size_items_group, 
1890                                                       buffer_format->font_size))->data);
1891         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_size_menuitem))) {
1892                 GtkWidget *label;
1893                 gchar *markup;
1894
1895                 label = gtk_bin_get_child (GTK_BIN (new_size_menuitem));
1896                 markup = g_strconcat ("<span font_family='Sans'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
1897                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1898                 g_free (markup);
1899                 g_signal_handlers_block_by_func (G_OBJECT (new_size_menuitem),
1900                                                  G_CALLBACK (modest_msg_edit_window_size_change),
1901                                                  window);
1902                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_size_menuitem), TRUE);
1903                 g_signal_handlers_unblock_by_func (G_OBJECT (new_size_menuitem),
1904                                                    G_CALLBACK (modest_msg_edit_window_size_change),
1905                                                    window);
1906         }
1907
1908         new_font_menuitem = GTK_WIDGET ((g_slist_nth (priv->font_items_group, 
1909                                                       buffer_format->font))->data);
1910         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_font_menuitem))) {
1911                 GtkWidget *label;
1912                 gchar *markup;
1913
1914                 label = gtk_bin_get_child (GTK_BIN (new_font_menuitem));
1915                 markup = g_strconcat ("<span font_family='", gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
1916                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1917                 g_free (markup);
1918                 g_signal_handlers_block_by_func (G_OBJECT (new_font_menuitem),
1919                                                  G_CALLBACK (modest_msg_edit_window_font_change),
1920                                                  window);
1921                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_font_menuitem), TRUE);
1922                 g_signal_handlers_unblock_by_func (G_OBJECT (new_font_menuitem),
1923                                                    G_CALLBACK (modest_msg_edit_window_font_change),
1924                                                    window);
1925         }
1926
1927         g_free (buffer_format);
1928
1929 }
1930
1931 #ifdef MODEST_HILDON_VERSION_0
1932 void
1933 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1934 {
1935         
1936         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1937         ModestMsgEditWindowPrivate *priv;
1938         GtkWidget *dialog = NULL;
1939         gint response;
1940         GdkColor *new_color = NULL;
1941
1942         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1943         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1944         
1945         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1946         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color));
1947         g_free (buffer_format);
1948
1949         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
1950                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1951                 if (new_color != NULL) {
1952                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1953                                                       (gpointer) new_color);
1954                 }
1955         }
1956         gtk_widget_destroy (dialog);
1957 }
1958
1959
1960 void
1961 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
1962 {
1963         
1964         ModestMsgEditWindowPrivate *priv;
1965         GtkWidget *dialog = NULL;
1966         gint response;
1967         GdkColor *old_color = NULL;
1968         const GdkColor *new_color = NULL;
1969         
1970         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1971         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
1972         
1973         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1974         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color);
1975
1976         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
1977                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1978                 if (new_color != NULL)
1979                         wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color);
1980         }
1981         gtk_widget_destroy (dialog);
1982
1983 }
1984
1985 #else 
1986 void
1987 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1988 {
1989         
1990         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1991         ModestMsgEditWindowPrivate *priv;
1992         GtkWidget *dialog = NULL;
1993
1994         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1995         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1996                 
1997         dialog = hildon_color_chooser_new ();
1998         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
1999         g_free (buffer_format);
2000
2001         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2002                 GdkColor col;
2003                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2004                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2005                                               (gpointer) &col);
2006         }
2007         gtk_widget_destroy (dialog);
2008 }
2009
2010
2011 void
2012 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2013 {
2014         
2015         ModestMsgEditWindowPrivate *priv;
2016         GtkWidget *dialog = NULL;
2017         GdkColor *old_color = NULL;
2018         
2019         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2020         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2021         
2022         dialog = hildon_color_chooser_new ();
2023         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2024
2025         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2026                 GdkColor col;
2027                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2028                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2029         }
2030         gtk_widget_destroy (dialog);
2031 }
2032
2033 #endif /*!MODEST_HILDON_VERSION_0*/
2034
2035
2036
2037 static TnyStream* create_stream_for_uri (const gchar* uri)
2038 {
2039         if (!uri)
2040                 return NULL;
2041                 
2042         TnyStream *result = NULL;
2043
2044         GnomeVFSHandle *handle = NULL;
2045         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2046         if (test == GNOME_VFS_OK) {
2047                 /* Create the tinymail stream: */
2048                 /* Presumably tinymai will call gnome_vfs_close (handle) later. */
2049                 result = TNY_STREAM (tny_vfs_stream_new (handle));
2050         }
2051         
2052         return result;
2053 }
2054
2055 void
2056 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2057 {
2058         
2059         ModestMsgEditWindowPrivate *priv;
2060         GtkWidget *dialog = NULL;
2061         gint response = 0;
2062         GSList *uris = NULL;
2063         GSList *uri_node = NULL;
2064         
2065         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2066         
2067         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2068         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
2069         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2070
2071         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2072
2073         response = gtk_dialog_run (GTK_DIALOG (dialog));
2074         switch (response) {
2075         case GTK_RESPONSE_OK:
2076                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2077                 break;
2078         default:
2079                 break;
2080         }
2081         gtk_widget_destroy (dialog);
2082
2083         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2084                 const gchar *uri;
2085                 GnomeVFSHandle *handle = NULL;
2086                 GnomeVFSResult result;
2087                 GtkTextIter position;
2088                 GtkTextMark *insert_mark;
2089
2090                 uri = (const gchar *) uri_node->data;
2091                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2092                 if (result == GNOME_VFS_OK) {
2093                         GdkPixbuf *pixbuf;
2094                         GnomeVFSFileInfo *info;
2095                         gchar *filename, *basename, *escaped_filename;
2096                         TnyMimePart *mime_part;
2097                         gchar *content_id;
2098                         const gchar *mime_type = NULL;
2099                         GnomeVFSURI *vfs_uri;
2100                         guint64 stream_size;
2101
2102                         vfs_uri = gnome_vfs_uri_new (uri);
2103
2104                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2105                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2106                         g_free (escaped_filename);
2107                         gnome_vfs_uri_unref (vfs_uri);
2108                         info = gnome_vfs_file_info_new ();
2109
2110                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2111                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2112                             == GNOME_VFS_OK)
2113                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2114
2115                         mime_part = tny_platform_factory_new_mime_part
2116                                 (modest_runtime_get_platform_factory ());
2117                                 
2118                         TnyStream *stream = create_stream_for_uri (uri);
2119                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2120                         
2121                         content_id = g_strdup_printf ("%d", priv->last_cid);
2122                         tny_mime_part_set_content_id (mime_part, content_id);
2123                         g_free (content_id);
2124                         priv->last_cid++;
2125                         
2126                         basename = g_path_get_basename (filename);
2127                         tny_mime_part_set_filename (mime_part, basename);
2128                         g_free (basename);
2129
2130                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
2131                         
2132                         if (pixbuf != NULL) {
2133                                 priv->images_size += stream_size;
2134                                 priv->images_count ++;
2135                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2136                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2137                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
2138                         } 
2139
2140                         tny_list_prepend (priv->images, (GObject *) mime_part);
2141                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2142                         g_free (filename);
2143                         g_object_unref (mime_part);
2144                         gnome_vfs_file_info_unref (info);
2145
2146                 }
2147         }
2148
2149
2150 }
2151
2152 void
2153 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2154 {       
2155         GtkWidget *dialog = NULL;
2156         gint response = 0;
2157         GSList *uris = NULL;
2158         GSList *uri_node;
2159         
2160         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2161         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2162         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2163
2164         response = gtk_dialog_run (GTK_DIALOG (dialog));
2165         switch (response) {
2166         case GTK_RESPONSE_OK:
2167                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2168                 break;
2169         default:
2170                 break;
2171         }
2172         gtk_widget_destroy (dialog);
2173
2174         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2175                 const gchar *uri = (const gchar *) uri_node->data;
2176                 modest_msg_edit_window_attach_file_one (window, uri);
2177         }
2178         g_slist_foreach (uris, (GFunc) g_free, NULL);
2179         g_slist_free (uris);
2180 }
2181
2182 void
2183 modest_msg_edit_window_attach_file_one (
2184                 ModestMsgEditWindow *window,
2185                 const gchar *uri)
2186 {
2187         g_return_if_fail (window);
2188         g_return_if_fail (uri);
2189                 
2190         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2191         
2192         
2193         GnomeVFSHandle *handle = NULL;
2194         GnomeVFSResult result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2195         if (result == GNOME_VFS_OK) {
2196                 TnyMimePart *mime_part;
2197                 TnyStream *stream;
2198                 const gchar *mime_type = NULL;
2199                 gchar *basename;
2200                 gchar *escaped_filename;
2201                 gchar *filename;
2202                 gchar *content_id;
2203                 GnomeVFSFileInfo *info;
2204                 GnomeVFSURI *vfs_uri;
2205
2206                 vfs_uri = gnome_vfs_uri_new (uri);
2207                 
2208
2209                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2210                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2211                 g_free (escaped_filename);
2212                 gnome_vfs_uri_unref (vfs_uri);
2213
2214                 info = gnome_vfs_file_info_new ();
2215                 
2216                 if (gnome_vfs_get_file_info (uri, 
2217                                              info, 
2218                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2219                     == GNOME_VFS_OK)
2220                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2221                 mime_part = tny_platform_factory_new_mime_part
2222                         (modest_runtime_get_platform_factory ());
2223                 stream = TNY_STREAM (tny_vfs_stream_new (handle));
2224                 
2225                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2226
2227                 g_object_unref (stream);
2228                 
2229                 content_id = g_strdup_printf ("%d", priv->last_cid);
2230                 tny_mime_part_set_content_id (mime_part, content_id);
2231                 g_free (content_id);
2232                 priv->last_cid++;
2233                 
2234                 basename = g_path_get_basename (filename);
2235                 tny_mime_part_set_filename (mime_part, basename);
2236                 g_free (basename);
2237                 
2238                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2239                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2240                                                         mime_part,
2241                                                         info->size == 0, info->size);
2242                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2243                 gtk_widget_show_all (priv->attachments_caption);
2244                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2245                 g_free (filename);
2246                 g_object_unref (mime_part);
2247                 gnome_vfs_file_info_unref (info);
2248         }
2249 }
2250
2251 void
2252 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2253                                            TnyList *att_list)
2254 {
2255         ModestMsgEditWindowPrivate *priv;
2256         TnyIterator *iter;
2257
2258         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2259         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2260
2261         if (att_list == NULL) {
2262                 att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2263         } else {
2264                 g_object_ref (att_list);
2265         }
2266
2267         if (tny_list_get_length (att_list) == 0) {
2268                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2269         } else {
2270                 GtkWidget *confirmation_dialog = NULL;
2271                 gboolean dialog_response;
2272                 gchar *message = NULL;
2273                 gchar *filename = NULL;
2274
2275                 if (tny_list_get_length (att_list) == 1) {
2276                         TnyMimePart *part;
2277                         iter = tny_list_create_iterator (att_list);
2278                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2279                         g_object_unref (iter);
2280                         if (TNY_IS_MSG (part)) {
2281                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2282                                 if (header) {
2283                                         filename = g_strdup (tny_header_get_subject (header));
2284                                         g_object_unref (header);
2285                                 }
2286                                 if (filename == NULL) {
2287                                         filename = g_strdup (_("mail_va_no_subject"));
2288                                 }
2289                         } else {
2290                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2291                         }
2292                         g_object_unref (part);
2293                 } else {
2294                         filename = g_strdup ("");
2295                 }
2296                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments",
2297                                                     (tny_list_get_length (att_list) == 1)), filename);
2298                 g_free (filename);
2299                 confirmation_dialog = hildon_note_new_confirmation (GTK_WINDOW (window), message);
2300                 g_free (message);
2301                 dialog_response = (gtk_dialog_run (GTK_DIALOG (confirmation_dialog))==GTK_RESPONSE_OK);
2302                 gtk_widget_destroy (confirmation_dialog);
2303                 if (!dialog_response) {
2304                         g_object_unref (att_list);
2305                         return;
2306                 }
2307                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_removing_attachment"));
2308                 
2309                 for (iter = tny_list_create_iterator (att_list);
2310                      !tny_iterator_is_done (iter);
2311                      tny_iterator_next (iter)) {
2312                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2313                         const gchar *att_id;
2314                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2315
2316                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2317                                                                    mime_part);
2318                         if (tny_list_get_length (priv->attachments) == 0)
2319                                 gtk_widget_hide (priv->attachments_caption);
2320                         att_id = tny_mime_part_get_content_id (mime_part);
2321                         if (att_id != NULL)
2322                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2323                                                                  att_id);
2324                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2325                         g_object_unref (mime_part);
2326                 }
2327         }
2328
2329         g_object_unref (att_list);
2330
2331         /* if the last attachment has been removed, focus the Subject: field */
2332         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) 
2333                 gtk_widget_grab_focus (priv->subject_field);
2334 }
2335
2336 static void
2337 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2338                                             gpointer userdata)
2339 {
2340         ModestMsgEditWindowPrivate *priv;
2341         GdkColor *new_color;
2342         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2343         
2344 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2345         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2346 #else 
2347         GdkColor col;
2348         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2349         new_color = &col;
2350 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2351
2352         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2353         
2354         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2355
2356 }
2357
2358 static void
2359 modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
2360                                     gpointer userdata)
2361 {
2362         ModestMsgEditWindowPrivate *priv;
2363         gint new_size_index;
2364         ModestMsgEditWindow *window;
2365         GtkWidget *label;
2366         
2367         window = MODEST_MSG_EDIT_WINDOW (userdata);
2368         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2369         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2370
2371         if (gtk_check_menu_item_get_active (menu_item)) {
2372                 gchar *markup;
2373                 WPTextBufferFormat format;
2374
2375                 memset (&format, 0, sizeof (format));
2376                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2377
2378                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2379                 
2380                 new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label)));
2381                 format.cs.font_size = TRUE;
2382                 format.cs.text_position = TRUE;
2383                 format.cs.font = TRUE;
2384                 format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE);
2385 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2386
2387                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2388                                                    GINT_TO_POINTER (wp_get_font_size_index (new_size_index, 12))))
2389                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2390                 
2391                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2392                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
2393                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2394                 g_free (markup);
2395         }
2396 }
2397
2398 static void
2399 modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
2400                                     gpointer userdata)
2401 {
2402         ModestMsgEditWindowPrivate *priv;
2403         gint new_font_index;
2404         ModestMsgEditWindow *window;
2405         GtkWidget *label;
2406         
2407         window = MODEST_MSG_EDIT_WINDOW (userdata);
2408         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2409         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2410
2411         if (gtk_check_menu_item_get_active (menu_item)) {
2412                 gchar *markup;
2413
2414                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2415                 
2416                 new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT);
2417
2418                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2419                                                    GINT_TO_POINTER(new_font_index)))
2420                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2421                 
2422                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2423                     markup = g_strconcat ("<span font_family='",gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
2424                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2425                 g_free (markup);
2426         }
2427 }
2428
2429 static gboolean
2430 modest_msg_edit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2431 {
2432         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2433                 ModestWindowPrivate *parent_priv;
2434                 ModestWindowMgr *mgr;
2435                 gboolean is_fullscreen;
2436                 GtkAction *fs_toggle_action;
2437                 gboolean active;
2438
2439                 mgr = modest_runtime_get_window_mgr ();
2440                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2441
2442                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2443                 
2444                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2445                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2446                 if (is_fullscreen != active)
2447                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2448         }
2449
2450         return FALSE;
2451
2452 }
2453
2454 void
2455 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2456                                 gboolean show)
2457 {
2458         ModestMsgEditWindowPrivate *priv = NULL;
2459         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2460
2461         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2462         if (!priv->update_caption_visibility)
2463                 return;
2464
2465         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2466         if (show)
2467                 gtk_widget_show (priv->cc_caption);
2468         else
2469                 gtk_widget_hide (priv->cc_caption);
2470
2471         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2472 }
2473
2474 void
2475 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2476                                  gboolean show)
2477 {
2478         ModestMsgEditWindowPrivate *priv = NULL;
2479         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2480
2481         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2482         if (!priv->update_caption_visibility)
2483                 return;
2484
2485         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2486         if (show)
2487                 gtk_widget_show (priv->bcc_caption);
2488         else
2489                 gtk_widget_hide (priv->bcc_caption);
2490
2491         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2492 }
2493
2494 static void
2495 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2496                                          ModestRecptEditor *editor)
2497 {
2498         ModestMsgEditWindowPrivate *priv;
2499
2500         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2501         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2502         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2503
2504         if (editor == NULL) {
2505                 GtkWidget *view_focus;
2506                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2507
2508                 /* This code should be kept in sync with ModestRecptEditor. The
2509                    textview inside the recpt editor is the one that really gets the
2510                    focus. As it's inside a scrolled window, and this one inside the
2511                    hbox recpt editor inherits from, we'll need to go up in the 
2512                    hierarchy to know if the text view is part of the recpt editor
2513                    or if it's a different text entry */
2514
2515                 if (gtk_widget_get_parent (view_focus)) {
2516                         GtkWidget *first_parent;
2517
2518                         first_parent = gtk_widget_get_parent (view_focus);
2519                         if (gtk_widget_get_parent (first_parent) && 
2520                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2521                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2522                         }
2523                 }
2524
2525                 if (editor == NULL)
2526                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2527
2528         }
2529
2530         modest_address_book_select_addresses (editor);
2531
2532 }
2533
2534 void
2535 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2536 {
2537         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2538
2539         modest_msg_edit_window_open_addressbook (window, NULL);
2540 }
2541
2542 static void
2543 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2544                                      gboolean show_toolbar)
2545 {
2546         ModestWindowPrivate *parent_priv;
2547         const gchar *action_name;
2548         GtkAction *action;
2549         
2550         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2551         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2552
2553         /* We can not just use the code of
2554            modest_msg_edit_window_setup_toolbar because it has a
2555            mixture of both initialization and creation code. */
2556         if (show_toolbar)
2557                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2558         else
2559                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2560
2561         /* Update also the actions (to update the toggles in the
2562            menus), we have to do it manually because some other window
2563            of the same time could have changed it (remember that the
2564            toolbar fullscreen mode is shared by all the windows of the
2565            same type */
2566         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2567                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2568         else
2569                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2570         
2571         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2572         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2573                                                             show_toolbar);
2574
2575 }
2576
2577 void
2578 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2579                                            TnyHeaderFlags priority_flags)
2580 {
2581         ModestMsgEditWindowPrivate *priv;
2582         ModestWindowPrivate *parent_priv;
2583
2584         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2585
2586         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2587         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2588
2589         if (priv->priority_flags != priority_flags) {
2590                 GtkAction *priority_action = NULL;
2591
2592                 priv->priority_flags = priority_flags;
2593
2594                 switch (priority_flags) {
2595                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2596                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_high", GTK_ICON_SIZE_MENU);
2597                         gtk_widget_show (priv->priority_icon);
2598                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2599                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2600                         break;
2601                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2602                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_low", GTK_ICON_SIZE_MENU);
2603                         gtk_widget_show (priv->priority_icon);
2604                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2605                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2606                         break;
2607                 default:
2608                         gtk_widget_hide (priv->priority_icon);
2609                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2610                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2611                         break;
2612                 }
2613                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2614                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2615         }
2616 }
2617
2618 void
2619 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2620                                         gint file_format)
2621 {
2622         ModestMsgEditWindowPrivate *priv;
2623         ModestWindowPrivate *parent_priv;
2624         gint current_format;
2625
2626         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2627
2628         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2629         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2630
2631         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2632                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2633
2634         if (current_format != file_format) {
2635                 switch (file_format) {
2636                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2637                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2638                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2639                         break;
2640                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2641                 {
2642                         GtkWidget *dialog;
2643                         gint response;
2644                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2645                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2646                         gtk_widget_destroy (dialog);
2647                         if (response == GTK_RESPONSE_OK) {
2648                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2649                         } else {
2650                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu"));
2651                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
2652                         }
2653                 }
2654                         break;
2655                 }
2656                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2657         }
2658 }
2659
2660 void
2661 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2662 {
2663         GtkWidget *dialog;
2664         ModestMsgEditWindowPrivate *priv;
2665         WPTextBufferFormat oldfmt, fmt;
2666         gint old_position = 0;
2667         gint response = 0;
2668         gint position = 0;
2669         gint font_size;
2670         GdkColor *color = NULL;
2671         gboolean bold, bold_set, italic, italic_set;
2672         gboolean underline, underline_set;
2673         gboolean strikethrough, strikethrough_set;
2674         gboolean position_set;
2675         gboolean font_size_set, font_set, color_set;
2676         gchar *font_name;
2677
2678         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2679         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2680         
2681         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2682
2683         /* First we get the currently selected font information */
2684         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2685
2686         switch (oldfmt.text_position) {
2687         case TEXT_POSITION_NORMAL:
2688                 old_position = 0;
2689                 break;
2690         case TEXT_POSITION_SUPERSCRIPT:
2691                 old_position = 1;
2692                 break;
2693         default:
2694                 old_position = -1;
2695                 break;
2696         }
2697
2698         g_object_set (G_OBJECT (dialog),
2699                       "bold", oldfmt.bold != FALSE,
2700                       "bold-set", !oldfmt.cs.bold,
2701                       "underline", oldfmt.underline != FALSE,
2702                       "underline-set", !oldfmt.cs.underline,
2703                       "italic", oldfmt.italic != FALSE,
2704                       "italic-set", !oldfmt.cs.italic,
2705                       "strikethrough", oldfmt.strikethrough != FALSE,
2706                       "strikethrough-set", !oldfmt.cs.strikethrough,
2707                       "color", &oldfmt.color,
2708                       "color-set", !oldfmt.cs.color,
2709                       "size", wp_font_size[oldfmt.font_size],
2710                       "size-set", !oldfmt.cs.font_size,
2711                       "position", old_position,
2712                       "position-set", !oldfmt.cs.text_position,
2713                       "family", wp_get_font_name (oldfmt.font),
2714                       "family-set", !oldfmt.cs.font,
2715                       NULL);
2716
2717         gtk_widget_show_all (dialog);
2718         response = gtk_dialog_run (GTK_DIALOG (dialog));
2719         if (response == GTK_RESPONSE_OK) {
2720
2721                 g_object_get( dialog,
2722                               "bold", &bold,
2723                               "bold-set", &bold_set,
2724                               "underline", &underline,
2725                               "underline-set", &underline_set,
2726                               "italic", &italic,
2727                               "italic-set", &italic_set,
2728                               "strikethrough", &strikethrough,
2729                               "strikethrough-set", &strikethrough_set,
2730                               "color", &color,
2731                               "color-set", &color_set,
2732                               "size", &font_size,
2733                               "size-set", &font_size_set,
2734                               "family", &font_name,
2735                               "family-set", &font_set,
2736                               "position", &position,
2737                               "position-set", &position_set,
2738                               NULL );
2739                 
2740         }       
2741
2742         if (response == GTK_RESPONSE_OK) {
2743                 memset(&fmt, 0, sizeof(fmt));
2744                 if (bold_set) {
2745                         fmt.bold = bold;
2746                         fmt.cs.bold = TRUE;
2747                 }
2748                 if (italic_set) {
2749                         fmt.italic = italic;
2750                         fmt.cs.italic = TRUE;
2751                 }
2752                 if (underline_set) {
2753                         fmt.underline = underline;
2754                         fmt.cs.underline = TRUE;
2755                 }
2756                 if (strikethrough_set) {
2757                         fmt.strikethrough = strikethrough;
2758                         fmt.cs.strikethrough = TRUE;
2759                 }
2760                 if (position_set) {
2761                         fmt.text_position =
2762                                 ( position == 0 )
2763                                 ? TEXT_POSITION_NORMAL
2764                                 : ( ( position == 1 )
2765                                     ? TEXT_POSITION_SUPERSCRIPT
2766                                     : TEXT_POSITION_SUBSCRIPT );
2767                         fmt.cs.text_position = TRUE;
2768                         fmt.font_size = oldfmt.font_size;
2769                 }
2770                 if (color_set) {
2771                         fmt.color = *color;
2772                         fmt.cs.color = TRUE;
2773                 }
2774                 if (font_set) {
2775                         fmt.font = wp_get_font_index(font_name,
2776                                                      DEFAULT_FONT);
2777                         fmt.cs.font = TRUE;
2778                 }
2779                 g_free(font_name);
2780                 if (font_size_set) {
2781                         fmt.cs.font_size = TRUE;
2782                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
2783                 }
2784                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2785                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2786         }
2787         gtk_widget_destroy (dialog);
2788         
2789         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2790 }
2791
2792 void
2793 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2794 {
2795         ModestMsgEditWindowPrivate *priv;
2796
2797         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2798         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2799         
2800         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
2801
2802         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2803         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2804
2805 }
2806
2807 void
2808 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
2809 {
2810         ModestMsgEditWindowPrivate *priv;
2811
2812         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2813         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2814         
2815         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
2816
2817         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2818         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2819
2820 }
2821
2822 static void  
2823 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
2824 {
2825         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2826
2827         priv->can_undo = can_undo;
2828 }
2829
2830 static void  
2831 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
2832 {
2833         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2834
2835         priv->can_redo = can_redo;
2836 }
2837
2838 gboolean            
2839 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
2840 {
2841         ModestMsgEditWindowPrivate *priv;
2842         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2843         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2844
2845         return priv->can_undo;
2846 }
2847
2848 gboolean            
2849 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
2850 {
2851         ModestMsgEditWindowPrivate *priv;
2852         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2853         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2854
2855         return priv->can_redo;
2856 }
2857
2858
2859 static void
2860 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
2861 {
2862         GtkTextIter iter;
2863         GtkTextIter match_start, match_end;
2864
2865         if (image_id == NULL)
2866                 return;
2867
2868         gtk_text_buffer_get_start_iter (buffer, &iter);
2869
2870         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
2871                 GSList *tags = gtk_text_iter_get_tags (&match_start);
2872                 GSList *node;
2873                 for (node = tags; node != NULL; node = g_slist_next (node)) {
2874                         GtkTextTag *tag = (GtkTextTag *) node->data;
2875                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
2876                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
2877                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
2878                                         gint offset;
2879                                         offset = gtk_text_iter_get_offset (&match_start);
2880                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
2881                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
2882                                 }
2883                         }
2884                 }
2885                 gtk_text_iter_forward_char (&iter);
2886         }
2887 }
2888
2889 gboolean
2890 message_is_empty (ModestMsgEditWindow *window)
2891 {
2892         ModestMsgEditWindowPrivate *priv = NULL;
2893
2894         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2895         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2896
2897         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
2898          * so we can ignore markup.
2899          */
2900         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
2901         gint count = 0;
2902         if (buf)
2903                 count = gtk_text_buffer_get_char_count (buf);
2904
2905         return count == 0;
2906 }
2907
2908 static gboolean
2909 msg_body_focus (GtkWidget *focus,
2910                 GdkEventFocus *event,
2911                 gpointer userdata)
2912 {
2913         
2914         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
2915         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
2916         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
2917         return FALSE;
2918 }
2919
2920 static void
2921 recpt_field_changed (GtkTextBuffer *buffer,
2922                   ModestMsgEditWindow *editor)
2923 {
2924         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2925         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
2926 }
2927
2928 static void
2929 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
2930 {
2931         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2932         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
2933 }
2934
2935 void
2936 modest_msg_edit_window_reset_modified (ModestMsgEditWindow *editor)
2937 {
2938         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2939         GtkTextBuffer *buffer;
2940
2941         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2942         gtk_text_buffer_set_modified (buffer, FALSE);
2943         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2944         gtk_text_buffer_set_modified (buffer, FALSE);
2945         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2946         gtk_text_buffer_set_modified (buffer, FALSE);
2947         gtk_text_buffer_set_modified (priv->text_buffer, FALSE);
2948 }
2949
2950 gboolean
2951 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
2952 {
2953         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2954         GtkTextBuffer *buffer;
2955
2956         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2957         if (gtk_text_buffer_get_modified (buffer))
2958                 return TRUE;
2959         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2960         if (gtk_text_buffer_get_modified (buffer))
2961                 return TRUE;
2962         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2963         if (gtk_text_buffer_get_modified (buffer))
2964                 return TRUE;
2965         if (gtk_text_buffer_get_modified (priv->text_buffer))
2966                 return TRUE;
2967
2968         return FALSE;
2969 }
2970
2971
2972
2973
2974 gboolean
2975 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
2976 {
2977         ModestMsgEditWindowPrivate *priv = NULL;
2978         
2979         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2980         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2981
2982         /* check if there's no recipient added */
2983         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
2984             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
2985             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
2986                 /* no recipient contents, then select contacts */
2987                 modest_msg_edit_window_open_addressbook (window, NULL);
2988                 return FALSE;
2989         }
2990
2991         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
2992                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
2993                 return FALSE;
2994         }
2995         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
2996                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
2997                 return FALSE;
2998         }
2999         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
3000                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3001                 return FALSE;
3002         }
3003
3004         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3005             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3006                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3007
3008         return TRUE;
3009
3010 }
3011
3012 static void
3013 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3014                                                ModestMsgEditWindow *window)
3015 {
3016         modest_msg_edit_window_offer_attach_file (window);
3017 }
3018
3019 const gchar *
3020 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3021 {
3022         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3023
3024         return priv->clipboard_text;
3025 }
3026
3027 static void
3028 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3029                                                GdkEvent *event,
3030                                                ModestMsgEditWindow *window)
3031 {
3032         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3033         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3034         gchar *text = NULL;
3035         if (!GTK_WIDGET_VISIBLE (window))
3036                 return;
3037
3038         text = gtk_clipboard_wait_for_text (selection_clipboard);
3039
3040         if (priv->clipboard_text != NULL) {
3041                 g_free (priv->clipboard_text);
3042         }
3043         priv->clipboard_text = text;
3044
3045         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3046 }
3047 static void 
3048 subject_field_move_cursor (GtkEntry *entry,
3049                            GtkMovementStep step,
3050                            gint a1,
3051                            gboolean a2,
3052                            gpointer window)
3053 {
3054         if (!GTK_WIDGET_VISIBLE (window))
3055                 return;
3056
3057         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3058 }
3059
3060 static void 
3061 update_window_title (ModestMsgEditWindow *window)
3062 {
3063         ModestMsgEditWindowPrivate *priv = NULL;
3064         const gchar *subject;
3065
3066         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3067         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3068         if (subject == NULL || subject[0] == '\0')
3069                 subject = _("mail_va_new_email");
3070
3071         gtk_window_set_title (GTK_WINDOW (window), subject);
3072
3073 }
3074
3075 static void  
3076 subject_field_changed (GtkEditable *editable, 
3077                        ModestMsgEditWindow *window)
3078 {
3079         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3080         update_window_title (window);
3081         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3082         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3083         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3084 }
3085
3086 static void  
3087 subject_field_insert_text (GtkEditable *editable, 
3088                            gchar *new_text,
3089                            gint new_text_length,
3090                            gint *position,
3091                            ModestMsgEditWindow *window)
3092 {
3093         GString *result = g_string_new ("");
3094         gchar *current;
3095         gint result_len = 0;
3096         const gchar *entry_text = NULL;
3097         gint old_length;
3098
3099         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3100         old_length = g_utf8_strlen (entry_text, -1);
3101
3102         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3103                 gunichar c = g_utf8_get_char_validated (current, 8);
3104                 /* Invalid unichar, stop */
3105                 if (c == -1)
3106                         break;
3107                 /* a bullet */
3108                 if (c == 0x2022)
3109                         continue;
3110                 result = g_string_append_unichar (result, c);
3111                 result_len++;
3112         }
3113
3114         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3115                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3116                 if (result_len > 0)
3117                 {
3118                         /* Prevent endless recursion */
3119                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3120                         g_signal_emit_by_name (editable, "insert-text", 
3121                                                (gpointer) result->str, (gpointer) result->len,
3122                                                (gpointer) position, (gpointer) window);
3123                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3124                 }
3125         }
3126
3127         if (result_len + old_length > 1000) {
3128                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3129                                                 dgettext("hildon-common-strings",
3130                                                          "ckdg_ib_maximum_characters_reached"));
3131         }
3132         
3133         g_string_free (result, TRUE);
3134 }
3135
3136 static void  
3137 text_buffer_insert_text (GtkTextBuffer *buffer, 
3138                          GtkTextIter *iter,
3139                          gchar *new_text,
3140                          gint new_text_length,
3141                          ModestMsgEditWindow *window)
3142 {
3143         GString *result = g_string_new ("");
3144         gchar *current;
3145         gint result_len = 0;
3146         gboolean changed = FALSE;
3147
3148         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3149                 gunichar c = g_utf8_get_char_validated (current, 8);
3150                 /* Invalid unichar, stop */
3151                 if (c == -1)
3152                         break;
3153                 /* a bullet */
3154                 switch (c) {
3155                 case 0x2022:
3156                         result = g_string_append_c (result, ' ');
3157                         changed = TRUE;
3158                         break;
3159                 default:
3160                         result = g_string_append_unichar (result, c);
3161                 }
3162                 result_len++;
3163         }
3164
3165         if (changed) {
3166                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
3167                 g_signal_handlers_block_by_func(G_OBJECT(buffer), G_CALLBACK(text_buffer_insert_text), window);
3168                 g_signal_emit_by_name (buffer, "insert-text", 
3169                                        (gpointer) iter,
3170                                        (gpointer) result->str, (gpointer) result->len,
3171                                        (gpointer) window);
3172                 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), G_CALLBACK(text_buffer_insert_text), window);
3173         }
3174
3175         g_string_free (result, TRUE);
3176 }
3177
3178 void
3179 modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window,
3180                                             gboolean show)
3181 {
3182         ModestMsgEditWindowPrivate *priv = NULL;
3183
3184         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3185         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3186
3187         gtk_widget_set_no_show_all (priv->find_toolbar, FALSE);
3188
3189         if (show) {
3190                 gtk_widget_show_all (priv->find_toolbar);
3191                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
3192         } else {
3193                 gtk_widget_hide_all (priv->find_toolbar);
3194                 gtk_widget_grab_focus (priv->msg_body);
3195         }
3196     
3197 }
3198
3199 static gboolean 
3200 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3201                                           const gchar *str,
3202                                           GtkTextIter *match_start,
3203                                           GtkTextIter *match_end)
3204 {
3205         GtkTextIter end_iter;
3206         gchar *str_casefold;
3207         gint str_chars_n;
3208         gchar *range_text;
3209         gchar *range_casefold;
3210         gint offset;
3211         gint range_chars_n;
3212         gboolean result = FALSE;
3213
3214         if (str == NULL)
3215                 return TRUE;
3216         
3217         /* get end iter */
3218         end_iter = *iter;
3219         gtk_text_iter_forward_to_end (&end_iter);
3220
3221         str_casefold = g_utf8_casefold (str, -1);
3222         str_chars_n = strlen (str);
3223
3224         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3225         range_casefold = g_utf8_casefold (range_text, -1);
3226         range_chars_n = strlen (range_casefold);
3227
3228         if (range_chars_n < str_chars_n) {
3229                 g_free (str_casefold);
3230                 g_free (range_text);
3231                 g_free (range_casefold);
3232                 return FALSE;
3233         }
3234
3235         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3236                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3237                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3238                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3239                         result = TRUE;
3240                         gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3241                                                       match_start, match_end, NULL);
3242                         g_free (found_text);
3243                 }
3244                 g_free (range_subtext);
3245                 if (result)
3246                         break;
3247         }
3248         g_free (str_casefold);
3249         g_free (range_text);
3250         g_free (range_casefold);
3251
3252         return result;
3253 }
3254
3255
3256 static void 
3257 modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
3258                                             ModestMsgEditWindow *window)
3259 {
3260         gchar *current_search = NULL;
3261         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3262         gboolean result;
3263         GtkTextIter selection_start, selection_end;
3264         GtkTextIter match_start, match_end;
3265         gboolean continue_search = FALSE;
3266
3267         if (message_is_empty (window)) {
3268                 g_free (priv->last_search);
3269                 priv->last_search = NULL;
3270                 hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3271                 return;
3272         }
3273
3274         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
3275         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3276                 g_free (current_search);
3277                 g_free (priv->last_search);
3278                 priv->last_search = NULL;
3279                 /* Information banner about empty search */
3280                 hildon_banner_show_information (NULL, NULL, dgettext ("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
3281                 return;
3282         }
3283
3284         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3285                 continue_search = TRUE;
3286         } else {
3287                 g_free (priv->last_search);
3288                 priv->last_search = g_strdup (current_search);
3289         }
3290
3291         if (continue_search) {
3292                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3293                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3294                                                                    &match_start, &match_end);
3295                 if (!result)
3296                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_search_complete"));
3297         } else {
3298                 GtkTextIter buffer_start;
3299                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3300                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3301                                                                    &match_start, &match_end);
3302                 if (!result)
3303                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_no_matches"));
3304         }
3305
3306         /* Mark as selected the string found in search */
3307         if (result) {
3308                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3309                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3310         } else {
3311                 g_free (priv->last_search);
3312                 priv->last_search = NULL;
3313         }
3314         g_free (current_search);
3315 }
3316
3317 static void
3318 modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
3319                                            ModestMsgEditWindow *window)
3320 {
3321         GtkToggleAction *toggle;
3322         ModestWindowPrivate *parent_priv;
3323         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3324
3325         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/FindInMessageMenu"));
3326         gtk_toggle_action_set_active (toggle, FALSE);
3327 }
3328
3329 gboolean 
3330 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3331 {
3332         ModestMsgEditWindowPrivate *priv;
3333
3334         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3335         return priv->sent;
3336 }
3337
3338 void 
3339 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3340                                  gboolean sent)
3341 {
3342         ModestMsgEditWindowPrivate *priv;
3343
3344         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3345         priv->sent = sent;
3346 }
3347
3348
3349 void            
3350 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3351                                   TnyMsg *draft)
3352 {
3353         ModestMsgEditWindowPrivate *priv;
3354         TnyHeader *header = NULL;
3355
3356         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3357         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3358
3359         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3360         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
3361
3362         if (priv->draft_msg != NULL) {
3363                 g_object_unref (priv->draft_msg);
3364         }
3365
3366         if (draft != NULL) {
3367                 g_object_ref (draft);
3368                 header = tny_msg_get_header (draft);
3369                 if (priv->msg_uid) {
3370                         g_free (priv->msg_uid);
3371                         priv->msg_uid = NULL;
3372                 }
3373                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3374                 if (GTK_WIDGET_REALIZED (window))
3375                         modest_window_mgr_register_window (mgr, MODEST_WINDOW (window));
3376         }
3377
3378         priv->draft_msg = draft;
3379 }
3380
3381 static void  
3382 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3383                        GtkTextIter *start, GtkTextIter *end,
3384                        gpointer userdata)
3385 {
3386         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3387         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3388         gchar *tag_name;
3389
3390         if (tag == NULL+13) return;
3391         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3392         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3393                 replace_with_images (window, priv->images);
3394         }
3395 }
3396
3397 void                    
3398 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3399                                  TnyMimePart *part)
3400 {
3401         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3402
3403         g_return_if_fail (TNY_IS_MIME_PART (part));
3404         tny_list_prepend (priv->attachments, (GObject *) part);
3405         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3406         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3407         gtk_widget_show_all (priv->attachments_caption);
3408         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3409 }
3410
3411 const gchar*    
3412 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3413 {
3414         ModestMsgEditWindowPrivate *priv;
3415
3416         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3417         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3418
3419         return priv->msg_uid;
3420 }
3421
3422 GtkWidget *
3423 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3424                                          ModestMsgEditWindowWidgetType widget_type)
3425 {
3426         ModestMsgEditWindowPrivate *priv;
3427
3428         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3429         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3430
3431         switch (widget_type) {
3432         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3433                 return priv->msg_body;
3434                 break;
3435         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3436                 return priv->to_field;
3437                 break;
3438         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3439                 return priv->cc_field;
3440                 break;
3441         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3442                 return priv->bcc_field;
3443                 break;
3444         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3445                 return priv->subject_field;
3446                 break;
3447         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3448                 return priv->attachments_view;
3449                 break;
3450         default:
3451                 return NULL;
3452         }
3453 }
3454
3455 static void 
3456 remove_tags (WPTextBuffer *buffer)
3457 {
3458         GtkTextIter start, end;
3459
3460         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3461         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3462
3463         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3464 }
3465
3466 static void
3467 on_account_removed (TnyAccountStore *account_store, 
3468                     TnyAccount *account,
3469                     gpointer user_data)
3470 {
3471         /* Do nothing if it's a store account, because we use the
3472            transport to send the messages */
3473         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
3474                 const gchar *parent_acc = NULL;
3475                 const gchar *our_acc = NULL;
3476
3477                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
3478                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3479                 /* Close this window if I'm showing a message of the removed account */
3480                 if (strcmp (parent_acc, our_acc) == 0)
3481                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
3482         }
3483 }
3484
3485 static gboolean
3486 on_zoom_minus_plus_not_implemented (ModestWindow *window)
3487 {
3488         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3489
3490         hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
3491         return FALSE;
3492
3493 }
3494
3495 static void
3496 set_zoom_do_nothing (ModestWindow *window,
3497                                  gdouble zoom)
3498 {
3499 }
3500
3501 static gdouble
3502 get_zoom_do_nothing (ModestWindow *window)
3503 {
3504         return 1.0;
3505 }
3506