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