* src/widgets/modest-msg-edit-window.h:
[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
2109                         if (gnome_vfs_get_file_info (uri, &info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2110                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2111                             == GNOME_VFS_OK)
2112                                 mime_type = gnome_vfs_file_info_get_mime_type (&info);
2113
2114                         mime_part = tny_platform_factory_new_mime_part
2115                                 (modest_runtime_get_platform_factory ());
2116                                 
2117                         TnyStream *stream = create_stream_for_uri (uri);
2118                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2119                         
2120                         content_id = g_strdup_printf ("%d", priv->last_cid);
2121                         tny_mime_part_set_content_id (mime_part, content_id);
2122                         g_free (content_id);
2123                         priv->last_cid++;
2124                         
2125                         basename = g_path_get_basename (filename);
2126                         tny_mime_part_set_filename (mime_part, basename);
2127                         g_free (basename);
2128
2129                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
2130                         
2131                         if (pixbuf != NULL) {
2132                                 priv->images_size += stream_size;
2133                                 priv->images_count ++;
2134                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2135                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2136                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
2137                         } 
2138
2139                         tny_list_prepend (priv->images, (GObject *) mime_part);
2140                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2141                         g_free (filename);
2142                         g_object_unref (mime_part);
2143
2144                 }
2145         }
2146
2147
2148 }
2149
2150 void
2151 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2152 {       
2153         GtkWidget *dialog = NULL;
2154         gint response = 0;
2155         GSList *uris = NULL;
2156         GSList *uri_node;
2157         
2158         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2159         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2160         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2161
2162         response = gtk_dialog_run (GTK_DIALOG (dialog));
2163         switch (response) {
2164         case GTK_RESPONSE_OK:
2165                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2166                 break;
2167         default:
2168                 break;
2169         }
2170         gtk_widget_destroy (dialog);
2171
2172         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2173                 const gchar *uri = (const gchar *) uri_node->data;
2174                 modest_msg_edit_window_attach_file_one (window, uri);
2175         }
2176         g_slist_foreach (uris, (GFunc) g_free, NULL);
2177         g_slist_free (uris);
2178 }
2179
2180 void
2181 modest_msg_edit_window_attach_file_one (
2182                 ModestMsgEditWindow *window,
2183                 const gchar *uri)
2184 {
2185         g_return_if_fail (window);
2186         g_return_if_fail (uri);
2187                 
2188         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2189         
2190         
2191         GnomeVFSHandle *handle = NULL;
2192         GnomeVFSResult result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2193         if (result == GNOME_VFS_OK) {
2194                 TnyMimePart *mime_part;
2195                 TnyStream *stream;
2196                 const gchar *mime_type = NULL;
2197                 gchar *basename;
2198                 gchar *escaped_filename;
2199                 gchar *filename;
2200                 gchar *content_id;
2201                 GnomeVFSFileInfo info;
2202                 GnomeVFSURI *vfs_uri;
2203
2204                 vfs_uri = gnome_vfs_uri_new (uri);
2205                 
2206
2207                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2208                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2209                 g_free (escaped_filename);
2210                 gnome_vfs_uri_unref (vfs_uri);
2211                 
2212                 if (gnome_vfs_get_file_info (uri, 
2213                                              &info, 
2214                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2215                     == GNOME_VFS_OK)
2216                         mime_type = gnome_vfs_file_info_get_mime_type (&info);
2217                 mime_part = tny_platform_factory_new_mime_part
2218                         (modest_runtime_get_platform_factory ());
2219                 stream = TNY_STREAM (tny_vfs_stream_new (handle));
2220                 
2221                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2222
2223                 g_object_unref (stream);
2224                 
2225                 content_id = g_strdup_printf ("%d", priv->last_cid);
2226                 tny_mime_part_set_content_id (mime_part, content_id);
2227                 g_free (content_id);
2228                 priv->last_cid++;
2229                 
2230                 basename = g_path_get_basename (filename);
2231                 tny_mime_part_set_filename (mime_part, basename);
2232                 g_free (basename);
2233                 
2234                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2235                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2236                                                         mime_part,
2237                                                         info.size == 0, info.size);
2238                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2239                 gtk_widget_show_all (priv->attachments_caption);
2240                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2241                 g_free (filename);
2242                 g_object_unref (mime_part);
2243         }
2244 }
2245
2246 void
2247 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2248                                            TnyList *att_list)
2249 {
2250         ModestMsgEditWindowPrivate *priv;
2251         TnyIterator *iter;
2252
2253         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2254         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2255
2256         if (att_list == NULL) {
2257                 att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2258         } else {
2259                 g_object_ref (att_list);
2260         }
2261
2262         if (tny_list_get_length (att_list) == 0) {
2263                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2264         } else {
2265                 GtkWidget *confirmation_dialog = NULL;
2266                 gboolean dialog_response;
2267                 gchar *message = NULL;
2268                 gchar *filename = NULL;
2269
2270                 if (tny_list_get_length (att_list) == 1) {
2271                         TnyMimePart *part;
2272                         iter = tny_list_create_iterator (att_list);
2273                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2274                         g_object_unref (iter);
2275                         if (TNY_IS_MSG (part)) {
2276                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2277                                 if (header) {
2278                                         filename = g_strdup (tny_header_get_subject (header));
2279                                         g_object_unref (header);
2280                                 }
2281                                 if (filename == NULL) {
2282                                         filename = g_strdup (_("mail_va_no_subject"));
2283                                 }
2284                         } else {
2285                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2286                         }
2287                         g_object_unref (part);
2288                 } else {
2289                         filename = g_strdup ("");
2290                 }
2291                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments",
2292                                                     (tny_list_get_length (att_list) == 1)), filename);
2293                 g_free (filename);
2294                 confirmation_dialog = hildon_note_new_confirmation (GTK_WINDOW (window), message);
2295                 g_free (message);
2296                 dialog_response = (gtk_dialog_run (GTK_DIALOG (confirmation_dialog))==GTK_RESPONSE_OK);
2297                 gtk_widget_destroy (confirmation_dialog);
2298                 if (!dialog_response) {
2299                         g_object_unref (att_list);
2300                         return;
2301                 }
2302                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_removing_attachment"));
2303                 
2304                 for (iter = tny_list_create_iterator (att_list);
2305                      !tny_iterator_is_done (iter);
2306                      tny_iterator_next (iter)) {
2307                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2308                         const gchar *att_id;
2309                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2310
2311                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2312                                                                    mime_part);
2313                         if (tny_list_get_length (priv->attachments) == 0)
2314                                 gtk_widget_hide (priv->attachments_caption);
2315                         att_id = tny_mime_part_get_content_id (mime_part);
2316                         if (att_id != NULL)
2317                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2318                                                                  att_id);
2319                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2320                         g_object_unref (mime_part);
2321                 }
2322         }
2323
2324         g_object_unref (att_list);
2325
2326         /* if the last attachment has been removed, focus the Subject: field */
2327         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) 
2328                 gtk_widget_grab_focus (priv->subject_field);
2329 }
2330
2331 static void
2332 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2333                                             gpointer userdata)
2334 {
2335         ModestMsgEditWindowPrivate *priv;
2336         GdkColor *new_color;
2337         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2338         
2339 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2340         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2341 #else 
2342         GdkColor col;
2343         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2344         new_color = &col;
2345 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2346
2347         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2348         
2349         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2350
2351 }
2352
2353 static void
2354 modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
2355                                     gpointer userdata)
2356 {
2357         ModestMsgEditWindowPrivate *priv;
2358         gint new_size_index;
2359         ModestMsgEditWindow *window;
2360         GtkWidget *label;
2361         
2362         window = MODEST_MSG_EDIT_WINDOW (userdata);
2363         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2364         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2365
2366         if (gtk_check_menu_item_get_active (menu_item)) {
2367                 gchar *markup;
2368                 WPTextBufferFormat format;
2369
2370                 memset (&format, 0, sizeof (format));
2371                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2372
2373                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2374                 
2375                 new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label)));
2376                 format.cs.font_size = TRUE;
2377                 format.cs.text_position = TRUE;
2378                 format.cs.font = TRUE;
2379                 format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE);
2380 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2381
2382                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2383                                                    GINT_TO_POINTER (wp_get_font_size_index (new_size_index, 12))))
2384                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2385                 
2386                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2387                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
2388                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2389                 g_free (markup);
2390         }
2391 }
2392
2393 static void
2394 modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
2395                                     gpointer userdata)
2396 {
2397         ModestMsgEditWindowPrivate *priv;
2398         gint new_font_index;
2399         ModestMsgEditWindow *window;
2400         GtkWidget *label;
2401         
2402         window = MODEST_MSG_EDIT_WINDOW (userdata);
2403         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2404         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2405
2406         if (gtk_check_menu_item_get_active (menu_item)) {
2407                 gchar *markup;
2408
2409                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2410                 
2411                 new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT);
2412
2413                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2414                                                    GINT_TO_POINTER(new_font_index)))
2415                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2416                 
2417                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2418                     markup = g_strconcat ("<span font_family='",gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
2419                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2420                 g_free (markup);
2421         }
2422 }
2423
2424 static gboolean
2425 modest_msg_edit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2426 {
2427         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2428                 ModestWindowPrivate *parent_priv;
2429                 ModestWindowMgr *mgr;
2430                 gboolean is_fullscreen;
2431                 GtkAction *fs_toggle_action;
2432                 gboolean active;
2433
2434                 mgr = modest_runtime_get_window_mgr ();
2435                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2436
2437                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2438                 
2439                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2440                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2441                 if (is_fullscreen != active)
2442                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2443         }
2444
2445         return FALSE;
2446
2447 }
2448
2449 void
2450 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2451                                 gboolean show)
2452 {
2453         ModestMsgEditWindowPrivate *priv = NULL;
2454         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2455
2456         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2457         if (!priv->update_caption_visibility)
2458                 return;
2459
2460         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2461         if (show)
2462                 gtk_widget_show (priv->cc_caption);
2463         else
2464                 gtk_widget_hide (priv->cc_caption);
2465
2466         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2467 }
2468
2469 void
2470 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2471                                  gboolean show)
2472 {
2473         ModestMsgEditWindowPrivate *priv = NULL;
2474         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2475
2476         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2477         if (!priv->update_caption_visibility)
2478                 return;
2479
2480         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2481         if (show)
2482                 gtk_widget_show (priv->bcc_caption);
2483         else
2484                 gtk_widget_hide (priv->bcc_caption);
2485
2486         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2487 }
2488
2489 static void
2490 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2491                                          ModestRecptEditor *editor)
2492 {
2493         ModestMsgEditWindowPrivate *priv;
2494
2495         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2496         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2497         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2498
2499         if (editor == NULL) {
2500                 GtkWidget *view_focus;
2501                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2502
2503                 /* This code should be kept in sync with ModestRecptEditor. The
2504                    textview inside the recpt editor is the one that really gets the
2505                    focus. As it's inside a scrolled window, and this one inside the
2506                    hbox recpt editor inherits from, we'll need to go up in the 
2507                    hierarchy to know if the text view is part of the recpt editor
2508                    or if it's a different text entry */
2509
2510                 if (gtk_widget_get_parent (view_focus)) {
2511                         GtkWidget *first_parent;
2512
2513                         first_parent = gtk_widget_get_parent (view_focus);
2514                         if (gtk_widget_get_parent (first_parent) && 
2515                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2516                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2517                         }
2518                 }
2519
2520                 if (editor == NULL)
2521                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2522
2523         }
2524
2525         modest_address_book_select_addresses (editor);
2526
2527 }
2528
2529 void
2530 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2531 {
2532         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2533
2534         modest_msg_edit_window_open_addressbook (window, NULL);
2535 }
2536
2537 static void
2538 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2539                                      gboolean show_toolbar)
2540 {
2541         ModestWindowPrivate *parent_priv;
2542         const gchar *action_name;
2543         GtkAction *action;
2544         
2545         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2546         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2547
2548         /* We can not just use the code of
2549            modest_msg_edit_window_setup_toolbar because it has a
2550            mixture of both initialization and creation code. */
2551         if (show_toolbar)
2552                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2553         else
2554                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2555
2556         /* Update also the actions (to update the toggles in the
2557            menus), we have to do it manually because some other window
2558            of the same time could have changed it (remember that the
2559            toolbar fullscreen mode is shared by all the windows of the
2560            same type */
2561         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2562                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2563         else
2564                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2565         
2566         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2567         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2568                                                             show_toolbar);
2569
2570 }
2571
2572 void
2573 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2574                                            TnyHeaderFlags priority_flags)
2575 {
2576         ModestMsgEditWindowPrivate *priv;
2577         ModestWindowPrivate *parent_priv;
2578
2579         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2580
2581         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2582         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2583
2584         if (priv->priority_flags != priority_flags) {
2585                 GtkAction *priority_action = NULL;
2586
2587                 priv->priority_flags = priority_flags;
2588
2589                 switch (priority_flags) {
2590                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2591                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_high", GTK_ICON_SIZE_MENU);
2592                         gtk_widget_show (priv->priority_icon);
2593                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2594                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2595                         break;
2596                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2597                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_low", GTK_ICON_SIZE_MENU);
2598                         gtk_widget_show (priv->priority_icon);
2599                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2600                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2601                         break;
2602                 default:
2603                         gtk_widget_hide (priv->priority_icon);
2604                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2605                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2606                         break;
2607                 }
2608                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2609                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2610         }
2611 }
2612
2613 void
2614 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2615                                         gint file_format)
2616 {
2617         ModestMsgEditWindowPrivate *priv;
2618         ModestWindowPrivate *parent_priv;
2619         gint current_format;
2620
2621         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2622
2623         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2624         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2625
2626         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2627                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2628
2629         if (current_format != file_format) {
2630                 switch (file_format) {
2631                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2632                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2633                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2634                         break;
2635                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2636                 {
2637                         GtkWidget *dialog;
2638                         gint response;
2639                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2640                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2641                         gtk_widget_destroy (dialog);
2642                         if (response == GTK_RESPONSE_OK) {
2643                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2644                         } else {
2645                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu"));
2646                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
2647                         }
2648                 }
2649                         break;
2650                 }
2651                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2652         }
2653 }
2654
2655 void
2656 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2657 {
2658         GtkWidget *dialog;
2659         ModestMsgEditWindowPrivate *priv;
2660         WPTextBufferFormat oldfmt, fmt;
2661         gint old_position = 0;
2662         gint response = 0;
2663         gint position = 0;
2664         gint font_size;
2665         GdkColor *color = NULL;
2666         gboolean bold, bold_set, italic, italic_set;
2667         gboolean underline, underline_set;
2668         gboolean strikethrough, strikethrough_set;
2669         gboolean position_set;
2670         gboolean font_size_set, font_set, color_set;
2671         gchar *font_name;
2672
2673         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2674         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2675         
2676         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2677
2678         /* First we get the currently selected font information */
2679         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2680
2681         switch (oldfmt.text_position) {
2682         case TEXT_POSITION_NORMAL:
2683                 old_position = 0;
2684                 break;
2685         case TEXT_POSITION_SUPERSCRIPT:
2686                 old_position = 1;
2687                 break;
2688         default:
2689                 old_position = -1;
2690                 break;
2691         }
2692
2693         g_object_set (G_OBJECT (dialog),
2694                       "bold", oldfmt.bold != FALSE,
2695                       "bold-set", !oldfmt.cs.bold,
2696                       "underline", oldfmt.underline != FALSE,
2697                       "underline-set", !oldfmt.cs.underline,
2698                       "italic", oldfmt.italic != FALSE,
2699                       "italic-set", !oldfmt.cs.italic,
2700                       "strikethrough", oldfmt.strikethrough != FALSE,
2701                       "strikethrough-set", !oldfmt.cs.strikethrough,
2702                       "color", &oldfmt.color,
2703                       "color-set", !oldfmt.cs.color,
2704                       "size", wp_font_size[oldfmt.font_size],
2705                       "size-set", !oldfmt.cs.font_size,
2706                       "position", old_position,
2707                       "position-set", !oldfmt.cs.text_position,
2708                       "family", wp_get_font_name (oldfmt.font),
2709                       "family-set", !oldfmt.cs.font,
2710                       NULL);
2711
2712         gtk_widget_show_all (dialog);
2713         response = gtk_dialog_run (GTK_DIALOG (dialog));
2714         if (response == GTK_RESPONSE_OK) {
2715
2716                 g_object_get( dialog,
2717                               "bold", &bold,
2718                               "bold-set", &bold_set,
2719                               "underline", &underline,
2720                               "underline-set", &underline_set,
2721                               "italic", &italic,
2722                               "italic-set", &italic_set,
2723                               "strikethrough", &strikethrough,
2724                               "strikethrough-set", &strikethrough_set,
2725                               "color", &color,
2726                               "color-set", &color_set,
2727                               "size", &font_size,
2728                               "size-set", &font_size_set,
2729                               "family", &font_name,
2730                               "family-set", &font_set,
2731                               "position", &position,
2732                               "position-set", &position_set,
2733                               NULL );
2734                 
2735         }       
2736
2737         if (response == GTK_RESPONSE_OK) {
2738                 memset(&fmt, 0, sizeof(fmt));
2739                 if (bold_set) {
2740                         fmt.bold = bold;
2741                         fmt.cs.bold = TRUE;
2742                 }
2743                 if (italic_set) {
2744                         fmt.italic = italic;
2745                         fmt.cs.italic = TRUE;
2746                 }
2747                 if (underline_set) {
2748                         fmt.underline = underline;
2749                         fmt.cs.underline = TRUE;
2750                 }
2751                 if (strikethrough_set) {
2752                         fmt.strikethrough = strikethrough;
2753                         fmt.cs.strikethrough = TRUE;
2754                 }
2755                 if (position_set) {
2756                         fmt.text_position =
2757                                 ( position == 0 )
2758                                 ? TEXT_POSITION_NORMAL
2759                                 : ( ( position == 1 )
2760                                     ? TEXT_POSITION_SUPERSCRIPT
2761                                     : TEXT_POSITION_SUBSCRIPT );
2762                         fmt.cs.text_position = TRUE;
2763                         fmt.font_size = oldfmt.font_size;
2764                 }
2765                 if (color_set) {
2766                         fmt.color = *color;
2767                         fmt.cs.color = TRUE;
2768                 }
2769                 if (font_set) {
2770                         fmt.font = wp_get_font_index(font_name,
2771                                                      DEFAULT_FONT);
2772                         fmt.cs.font = TRUE;
2773                 }
2774                 g_free(font_name);
2775                 if (font_size_set) {
2776                         fmt.cs.font_size = TRUE;
2777                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
2778                 }
2779                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2780                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2781         }
2782         gtk_widget_destroy (dialog);
2783         
2784         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2785 }
2786
2787 void
2788 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2789 {
2790         ModestMsgEditWindowPrivate *priv;
2791
2792         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2793         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2794         
2795         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
2796
2797         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2798         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2799
2800 }
2801
2802 void
2803 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
2804 {
2805         ModestMsgEditWindowPrivate *priv;
2806
2807         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2808         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2809         
2810         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
2811
2812         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2813         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2814
2815 }
2816
2817 static void  
2818 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
2819 {
2820         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2821
2822         priv->can_undo = can_undo;
2823 }
2824
2825 static void  
2826 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
2827 {
2828         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2829
2830         priv->can_redo = can_redo;
2831 }
2832
2833 gboolean            
2834 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
2835 {
2836         ModestMsgEditWindowPrivate *priv;
2837         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2838         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2839
2840         return priv->can_undo;
2841 }
2842
2843 gboolean            
2844 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
2845 {
2846         ModestMsgEditWindowPrivate *priv;
2847         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2848         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2849
2850         return priv->can_redo;
2851 }
2852
2853
2854 static void
2855 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
2856 {
2857         GtkTextIter iter;
2858         GtkTextIter match_start, match_end;
2859
2860         if (image_id == NULL)
2861                 return;
2862
2863         gtk_text_buffer_get_start_iter (buffer, &iter);
2864
2865         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
2866                 GSList *tags = gtk_text_iter_get_tags (&match_start);
2867                 GSList *node;
2868                 for (node = tags; node != NULL; node = g_slist_next (node)) {
2869                         GtkTextTag *tag = (GtkTextTag *) node->data;
2870                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
2871                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
2872                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
2873                                         gint offset;
2874                                         offset = gtk_text_iter_get_offset (&match_start);
2875                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
2876                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
2877                                 }
2878                         }
2879                 }
2880                 gtk_text_iter_forward_char (&iter);
2881         }
2882 }
2883
2884 gboolean
2885 message_is_empty (ModestMsgEditWindow *window)
2886 {
2887         ModestMsgEditWindowPrivate *priv = NULL;
2888
2889         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2890         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2891
2892         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
2893          * so we can ignore markup.
2894          */
2895         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
2896         gint count = 0;
2897         if (buf)
2898                 count = gtk_text_buffer_get_char_count (buf);
2899
2900         return count == 0;
2901 }
2902
2903 static gboolean
2904 msg_body_focus (GtkWidget *focus,
2905                 GdkEventFocus *event,
2906                 gpointer userdata)
2907 {
2908         
2909         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
2910         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
2911         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
2912         return FALSE;
2913 }
2914
2915 static void
2916 recpt_field_changed (GtkTextBuffer *buffer,
2917                   ModestMsgEditWindow *editor)
2918 {
2919         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2920         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
2921 }
2922
2923 static void
2924 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
2925 {
2926         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2927         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
2928 }
2929
2930 void
2931 modest_msg_edit_window_reset_modified (ModestMsgEditWindow *editor)
2932 {
2933         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2934         GtkTextBuffer *buffer;
2935
2936         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2937         gtk_text_buffer_set_modified (buffer, FALSE);
2938         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2939         gtk_text_buffer_set_modified (buffer, FALSE);
2940         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2941         gtk_text_buffer_set_modified (buffer, FALSE);
2942         gtk_text_buffer_set_modified (priv->text_buffer, FALSE);
2943 }
2944
2945 gboolean
2946 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
2947 {
2948         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2949         GtkTextBuffer *buffer;
2950
2951         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2952         if (gtk_text_buffer_get_modified (buffer))
2953                 return TRUE;
2954         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2955         if (gtk_text_buffer_get_modified (buffer))
2956                 return TRUE;
2957         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2958         if (gtk_text_buffer_get_modified (buffer))
2959                 return TRUE;
2960         if (gtk_text_buffer_get_modified (priv->text_buffer))
2961                 return TRUE;
2962
2963         return FALSE;
2964 }
2965
2966
2967
2968
2969 gboolean
2970 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
2971 {
2972         ModestMsgEditWindowPrivate *priv = NULL;
2973         
2974         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2975         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2976
2977         /* check if there's no recipient added */
2978         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
2979             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
2980             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
2981                 /* no recipient contents, then select contacts */
2982                 modest_msg_edit_window_open_addressbook (window, NULL);
2983                 return FALSE;
2984         }
2985
2986         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
2987                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
2988                 return FALSE;
2989         }
2990         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
2991                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
2992                 return FALSE;
2993         }
2994         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
2995                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
2996                 return FALSE;
2997         }
2998
2999         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3000             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3001                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3002
3003         return TRUE;
3004
3005 }
3006
3007 static void
3008 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3009                                                ModestMsgEditWindow *window)
3010 {
3011         modest_msg_edit_window_offer_attach_file (window);
3012 }
3013
3014 const gchar *
3015 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3016 {
3017         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3018
3019         return priv->clipboard_text;
3020 }
3021
3022 static void
3023 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3024                                                GdkEvent *event,
3025                                                ModestMsgEditWindow *window)
3026 {
3027         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3028         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3029         gchar *text = NULL;
3030         if (!GTK_WIDGET_VISIBLE (window))
3031                 return;
3032
3033         text = gtk_clipboard_wait_for_text (selection_clipboard);
3034
3035         if (priv->clipboard_text != NULL) {
3036                 g_free (priv->clipboard_text);
3037         }
3038         priv->clipboard_text = text;
3039
3040         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3041 }
3042 static void 
3043 subject_field_move_cursor (GtkEntry *entry,
3044                            GtkMovementStep step,
3045                            gint a1,
3046                            gboolean a2,
3047                            gpointer window)
3048 {
3049         if (!GTK_WIDGET_VISIBLE (window))
3050                 return;
3051
3052         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3053 }
3054
3055 static void 
3056 update_window_title (ModestMsgEditWindow *window)
3057 {
3058         ModestMsgEditWindowPrivate *priv = NULL;
3059         const gchar *subject;
3060
3061         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3062         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3063         if (subject == NULL || subject[0] == '\0')
3064                 subject = _("mail_va_new_email");
3065
3066         gtk_window_set_title (GTK_WINDOW (window), subject);
3067
3068 }
3069
3070 static void  
3071 subject_field_changed (GtkEditable *editable, 
3072                        ModestMsgEditWindow *window)
3073 {
3074         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3075         update_window_title (window);
3076         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3077         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3078         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3079 }
3080
3081 static void  
3082 subject_field_insert_text (GtkEditable *editable, 
3083                            gchar *new_text,
3084                            gint new_text_length,
3085                            gint *position,
3086                            ModestMsgEditWindow *window)
3087 {
3088         GString *result = g_string_new ("");
3089         gchar *current;
3090         gint result_len = 0;
3091         const gchar *entry_text = NULL;
3092         gint old_length;
3093
3094         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3095         old_length = g_utf8_strlen (entry_text, -1);
3096
3097         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3098                 gunichar c = g_utf8_get_char_validated (current, 8);
3099                 /* Invalid unichar, stop */
3100                 if (c == -1)
3101                         break;
3102                 /* a bullet */
3103                 if (c == 0x2022)
3104                         continue;
3105                 result = g_string_append_unichar (result, c);
3106                 result_len++;
3107         }
3108
3109         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3110                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3111                 if (result_len > 0)
3112                 {
3113                         /* Prevent endless recursion */
3114                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3115                         g_signal_emit_by_name (editable, "insert-text", 
3116                                                (gpointer) result->str, (gpointer) result->len,
3117                                                (gpointer) position, (gpointer) window);
3118                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3119                 }
3120         }
3121
3122         if (result_len + old_length > 1000) {
3123                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3124                                                 dgettext("hildon-common-strings",
3125                                                          "ckdg_ib_maximum_characters_reached"));
3126         }
3127         
3128         g_string_free (result, TRUE);
3129 }
3130
3131 static void  
3132 text_buffer_insert_text (GtkTextBuffer *buffer, 
3133                          GtkTextIter *iter,
3134                          gchar *new_text,
3135                          gint new_text_length,
3136                          ModestMsgEditWindow *window)
3137 {
3138         GString *result = g_string_new ("");
3139         gchar *current;
3140         gint result_len = 0;
3141         gboolean changed = FALSE;
3142
3143         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3144                 gunichar c = g_utf8_get_char_validated (current, 8);
3145                 /* Invalid unichar, stop */
3146                 if (c == -1)
3147                         break;
3148                 /* a bullet */
3149                 switch (c) {
3150                 case 0x2022:
3151                         result = g_string_append_c (result, ' ');
3152                         changed = TRUE;
3153                         break;
3154                 default:
3155                         result = g_string_append_unichar (result, c);
3156                 }
3157                 result_len++;
3158         }
3159
3160         if (changed) {
3161                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
3162                 g_signal_handlers_block_by_func(G_OBJECT(buffer), G_CALLBACK(text_buffer_insert_text), window);
3163                 g_signal_emit_by_name (buffer, "insert-text", 
3164                                        (gpointer) iter,
3165                                        (gpointer) result->str, (gpointer) result->len,
3166                                        (gpointer) window);
3167                 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), G_CALLBACK(text_buffer_insert_text), window);
3168         }
3169
3170         g_string_free (result, TRUE);
3171 }
3172
3173 void
3174 modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window,
3175                                             gboolean show)
3176 {
3177         ModestMsgEditWindowPrivate *priv = NULL;
3178
3179         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3180         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3181
3182         gtk_widget_set_no_show_all (priv->find_toolbar, FALSE);
3183
3184         if (show) {
3185                 gtk_widget_show_all (priv->find_toolbar);
3186                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
3187         } else {
3188                 gtk_widget_hide_all (priv->find_toolbar);
3189                 gtk_widget_grab_focus (priv->msg_body);
3190         }
3191     
3192 }
3193
3194 static gboolean 
3195 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3196                                           const gchar *str,
3197                                           GtkTextIter *match_start,
3198                                           GtkTextIter *match_end)
3199 {
3200         GtkTextIter end_iter;
3201         gchar *str_casefold;
3202         gint str_chars_n;
3203         gchar *range_text;
3204         gchar *range_casefold;
3205         gint offset;
3206         gint range_chars_n;
3207         gboolean result = FALSE;
3208
3209         if (str == NULL)
3210                 return TRUE;
3211         
3212         /* get end iter */
3213         end_iter = *iter;
3214         gtk_text_iter_forward_to_end (&end_iter);
3215
3216         str_casefold = g_utf8_casefold (str, -1);
3217         str_chars_n = strlen (str);
3218
3219         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3220         range_casefold = g_utf8_casefold (range_text, -1);
3221         range_chars_n = strlen (range_casefold);
3222
3223         if (range_chars_n < str_chars_n) {
3224                 g_free (str_casefold);
3225                 g_free (range_text);
3226                 g_free (range_casefold);
3227                 return FALSE;
3228         }
3229
3230         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3231                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3232                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3233                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3234                         result = TRUE;
3235                         gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3236                                                       match_start, match_end, NULL);
3237                         g_free (found_text);
3238                 }
3239                 g_free (range_subtext);
3240                 if (result)
3241                         break;
3242         }
3243         g_free (str_casefold);
3244         g_free (range_text);
3245         g_free (range_casefold);
3246
3247         return result;
3248 }
3249
3250
3251 static void 
3252 modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
3253                                             ModestMsgEditWindow *window)
3254 {
3255         gchar *current_search = NULL;
3256         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3257         gboolean result;
3258         GtkTextIter selection_start, selection_end;
3259         GtkTextIter match_start, match_end;
3260         gboolean continue_search = FALSE;
3261
3262         if (message_is_empty (window)) {
3263                 g_free (priv->last_search);
3264                 priv->last_search = NULL;
3265                 hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3266                 return;
3267         }
3268
3269         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
3270         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3271                 g_free (current_search);
3272                 g_free (priv->last_search);
3273                 priv->last_search = NULL;
3274                 /* Information banner about empty search */
3275                 hildon_banner_show_information (NULL, NULL, dgettext ("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
3276                 return;
3277         }
3278
3279         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3280                 continue_search = TRUE;
3281         } else {
3282                 g_free (priv->last_search);
3283                 priv->last_search = g_strdup (current_search);
3284         }
3285
3286         if (continue_search) {
3287                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3288                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3289                                                                    &match_start, &match_end);
3290                 if (!result)
3291                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_search_complete"));
3292         } else {
3293                 GtkTextIter buffer_start;
3294                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3295                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3296                                                                    &match_start, &match_end);
3297                 if (!result)
3298                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_no_matches"));
3299         }
3300
3301         /* Mark as selected the string found in search */
3302         if (result) {
3303                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3304                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3305         } else {
3306                 g_free (priv->last_search);
3307                 priv->last_search = NULL;
3308         }
3309         g_free (current_search);
3310 }
3311
3312 static void
3313 modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
3314                                            ModestMsgEditWindow *window)
3315 {
3316         GtkToggleAction *toggle;
3317         ModestWindowPrivate *parent_priv;
3318         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3319
3320         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/FindInMessageMenu"));
3321         gtk_toggle_action_set_active (toggle, FALSE);
3322 }
3323
3324 gboolean 
3325 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3326 {
3327         ModestMsgEditWindowPrivate *priv;
3328
3329         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3330         return priv->sent;
3331 }
3332
3333 void 
3334 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3335                                  gboolean sent)
3336 {
3337         ModestMsgEditWindowPrivate *priv;
3338
3339         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3340         priv->sent = sent;
3341 }
3342
3343
3344 void            
3345 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3346                                   TnyMsg *draft)
3347 {
3348         ModestMsgEditWindowPrivate *priv;
3349         TnyHeader *header = NULL;
3350
3351         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3352         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3353
3354         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3355         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
3356
3357         if (priv->draft_msg != NULL) {
3358                 g_object_unref (priv->draft_msg);
3359         }
3360
3361         if (draft != NULL) {
3362                 g_object_ref (draft);
3363                 header = tny_msg_get_header (draft);
3364                 if (priv->msg_uid) {
3365                         g_free (priv->msg_uid);
3366                         priv->msg_uid = NULL;
3367                 }
3368                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3369                 if (GTK_WIDGET_REALIZED (window))
3370                         modest_window_mgr_register_window (mgr, MODEST_WINDOW (window));
3371         }
3372
3373         priv->draft_msg = draft;
3374 }
3375
3376 static void  
3377 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3378                        GtkTextIter *start, GtkTextIter *end,
3379                        gpointer userdata)
3380 {
3381         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3382         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3383         gchar *tag_name;
3384
3385         if (tag == NULL+13) return;
3386         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3387         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3388                 replace_with_images (window, priv->images);
3389         }
3390 }
3391
3392 void                    
3393 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3394                                  TnyMimePart *part)
3395 {
3396         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3397
3398         g_return_if_fail (TNY_IS_MIME_PART (part));
3399         tny_list_prepend (priv->attachments, (GObject *) part);
3400         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3401         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3402         gtk_widget_show_all (priv->attachments_caption);
3403         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3404 }
3405
3406 const gchar*    
3407 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3408 {
3409         ModestMsgEditWindowPrivate *priv;
3410
3411         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3412         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3413
3414         return priv->msg_uid;
3415 }
3416
3417 GtkWidget *
3418 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3419                                          ModestMsgEditWindowWidgetType widget_type)
3420 {
3421         ModestMsgEditWindowPrivate *priv;
3422
3423         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3424         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3425
3426         switch (widget_type) {
3427         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3428                 return priv->msg_body;
3429                 break;
3430         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3431                 return priv->to_field;
3432                 break;
3433         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3434                 return priv->cc_field;
3435                 break;
3436         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3437                 return priv->bcc_field;
3438                 break;
3439         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3440                 return priv->subject_field;
3441                 break;
3442         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3443                 return priv->attachments_view;
3444                 break;
3445         default:
3446                 return NULL;
3447         }
3448 }
3449
3450 static void 
3451 remove_tags (WPTextBuffer *buffer)
3452 {
3453         GtkTextIter start, end;
3454
3455         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3456         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3457
3458         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3459 }
3460
3461 static void
3462 on_account_removed (TnyAccountStore *account_store, 
3463                     TnyAccount *account,
3464                     gpointer user_data)
3465 {
3466         /* Do nothing if it's a store account, because we use the
3467            transport to send the messages */
3468         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
3469                 const gchar *parent_acc = NULL;
3470                 const gchar *our_acc = NULL;
3471
3472                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
3473                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3474                 /* Close this window if I'm showing a message of the removed account */
3475                 if (strcmp (parent_acc, our_acc) == 0)
3476                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
3477         }
3478 }
3479
3480 static gboolean
3481 on_zoom_minus_plus_not_implemented (ModestWindow *window)
3482 {
3483         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3484
3485         hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
3486         return FALSE;
3487
3488 }
3489
3490 static void
3491 set_zoom_do_nothing (ModestWindow *window,
3492                                  gdouble zoom)
3493 {
3494 }
3495
3496 static gdouble
3497 get_zoom_do_nothing (ModestWindow *window)
3498 {
3499         return 1.0;
3500 }
3501