* src/maemo/modest-msg-edit-window.c:
[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         g_object_unref (iter);
1012 }
1013
1014 static void
1015 get_related_images (ModestMsgEditWindow *self, TnyMsg *msg)
1016 {
1017         TnyMimePart *parent = NULL;
1018         const gchar *content_type = NULL;
1019         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1020
1021         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
1022         
1023         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1024                 parent = g_object_ref (msg);
1025         } else if (content_type && !g_strcasecmp (content_type, "multipart/mixed")) {
1026                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1027                 TnyIterator *iter;
1028
1029                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
1030                 iter = tny_list_create_iterator (parts);
1031                 while (!tny_iterator_is_done (iter)) {
1032                         TnyMimePart *part;
1033                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1034                         content_type = tny_mime_part_get_content_type (part);
1035                         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1036                                 parent = part;
1037                                 break;
1038                         } else {
1039                                 g_object_unref (part);
1040                         }
1041                         tny_iterator_next (iter);
1042                 }
1043                 g_object_unref (iter);
1044                 g_object_unref (parts);
1045         }
1046
1047         if (parent != NULL) {
1048                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1049                 TnyIterator *iter;
1050
1051                 tny_mime_part_get_parts (TNY_MIME_PART (parent), parts);
1052                 iter = tny_list_create_iterator (parts);
1053                 while (!tny_iterator_is_done (iter)) {
1054                         TnyMimePart *part;
1055                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1056                         content_type = tny_mime_part_get_content_type (part);
1057                         if (content_type && g_str_has_prefix (content_type, "image/")) {
1058                                 tny_list_prepend (priv->images, (GObject *) part);
1059                         } 
1060                         g_object_unref (part);
1061                         tny_iterator_next (iter);
1062                 }
1063                 g_object_unref (iter);
1064                 g_object_unref (parts);
1065                 g_object_unref (parent);
1066         }
1067 }
1068
1069 static void
1070 update_last_cid (ModestMsgEditWindow *self, TnyList *attachments)
1071 {
1072         TnyIterator *iter;
1073         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1074
1075         for (iter = tny_list_create_iterator (attachments) ; 
1076              !tny_iterator_is_done (iter);
1077              tny_iterator_next (iter)) {
1078                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1079                 const gchar *cid = tny_mime_part_get_content_id (part);
1080                 if (cid != NULL) {
1081                         char *invalid = NULL;
1082                         gint int_cid = strtol (cid, &invalid, 10);
1083                         if ((invalid != NULL) && (*invalid == '\0') && (int_cid > priv->last_cid)) {
1084                                 priv->last_cid = int_cid;
1085                         }
1086                 }
1087                 g_object_unref (part);
1088         }
1089         g_object_unref (iter);
1090 }
1091
1092 static void
1093 set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich)
1094 {
1095         TnyHeader *header;
1096         const gchar *to, *cc, *bcc, *subject;
1097         gchar *body;
1098         ModestMsgEditWindowPrivate *priv;
1099         GtkTextIter iter;
1100         TnyHeaderFlags priority_flags;
1101         TnyFolder *msg_folder;
1102         gboolean is_html = FALSE;
1103         
1104         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1105         g_return_if_fail (TNY_IS_MSG (msg));
1106
1107         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1108
1109         header = tny_msg_get_header (msg);
1110         to      = tny_header_get_to (header);
1111         cc      = tny_header_get_cc (header);
1112         bcc     = tny_header_get_bcc (header);
1113         subject = tny_header_get_subject (header);
1114         priority_flags = tny_header_get_priority (header);
1115
1116         if (to)
1117                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field),  to);
1118         if (cc) {
1119                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field),  cc);
1120                 gtk_widget_set_no_show_all (priv->cc_caption, FALSE);
1121                 gtk_widget_show (priv->cc_caption);
1122         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) {
1123                 gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
1124                 gtk_widget_hide (priv->cc_caption);
1125         }
1126         if (bcc) {
1127                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), bcc);
1128                 gtk_widget_set_no_show_all (priv->bcc_caption, FALSE);
1129                 gtk_widget_show (priv->bcc_caption);
1130         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) {
1131                 gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
1132                 gtk_widget_hide (priv->bcc_caption);
1133         } 
1134         if (subject)
1135                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
1136         modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self),
1137                                                    priority_flags);
1138
1139         update_window_title (self);
1140
1141         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1142         body = modest_tny_msg_get_body (msg, TRUE, &is_html);
1143
1144         if ((body == NULL)||(body[0] == '\0')) {
1145                 g_free (body);
1146                 body = modest_text_utils_convert_to_html ("");
1147                 is_html = FALSE;
1148         }
1149         wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1150         wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer),
1151                                             (gchar *) body,
1152                                             strlen (body));
1153         wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer));
1154         g_free (body);
1155
1156         /* Add attachments to the view */
1157         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg);
1158         priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1159         if (tny_list_get_length (priv->attachments) == 0) {
1160                 gtk_widget_hide (priv->attachments_caption);
1161         } else {
1162                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
1163                 gtk_widget_show_all (priv->attachments_caption);
1164         }
1165         get_related_images (self, msg);
1166         update_last_cid (self, priv->attachments);
1167         update_last_cid (self, priv->images);
1168         replace_with_images (self, priv->images);
1169
1170         if (preserve_is_rich && !is_html) {
1171                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1172         /* Get the default format required from configuration */
1173         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) {
1174                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1175         }
1176
1177         /* Set the default focus depending on having already a To: field or not */
1178         if ((!to)||(*to == '\0')) {
1179                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
1180         } else {
1181                 gtk_widget_grab_focus (priv->msg_body);
1182         }
1183
1184         /* TODO: lower priority, select in the From: combo to the
1185            value that comes from msg <- not sure, should it be
1186            allowed? */
1187         
1188         DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer));
1189
1190         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1191         gtk_text_buffer_place_cursor (priv->text_buffer, &iter);
1192
1193         modest_msg_edit_window_reset_modified (self);
1194
1195         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1196         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1197         text_buffer_can_undo (priv->text_buffer, FALSE, self);
1198         text_buffer_can_redo (priv->text_buffer, FALSE, self);
1199
1200         if (priv->msg_uid) {
1201                 g_free (priv->msg_uid);
1202                 priv->msg_uid = NULL;
1203         }
1204
1205         /* we should set a reference to the incoming message if it is a draft */
1206         msg_folder = tny_msg_get_folder (msg);
1207         if (msg_folder) {               
1208                 if (modest_tny_folder_is_local_folder (msg_folder)) {
1209                         TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder);
1210                         if (type == TNY_FOLDER_TYPE_INVALID)
1211                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1212                         
1213                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
1214                                 priv->draft_msg = g_object_ref(msg);
1215                         if (type == TNY_FOLDER_TYPE_OUTBOX)
1216                                 priv->outbox_msg = g_object_ref(msg);
1217                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1218                 }
1219                 g_object_unref (msg_folder);
1220         }
1221 }
1222
1223 static void
1224 menu_tool_button_clicked_popup (GtkMenuToolButton *item,
1225                                 gpointer data)
1226 {
1227         GList *item_children, *node;
1228         GtkWidget *bin_child;
1229
1230         bin_child = gtk_bin_get_child (GTK_BIN(item));
1231
1232         item_children = gtk_container_get_children (GTK_CONTAINER (bin_child));
1233         
1234         for (node = item_children; node != NULL; node = g_list_next (node)) {
1235                 if (GTK_IS_TOGGLE_BUTTON (node->data)) {
1236                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
1237                 }
1238         }
1239         g_list_free (item_children);
1240 }
1241
1242 static void
1243 menu_tool_button_dont_expand (GtkMenuToolButton *item)
1244 {
1245         GtkWidget *box;
1246         GList *item_children, *node;
1247
1248         box = gtk_bin_get_child (GTK_BIN (item));
1249         gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
1250         item_children = gtk_container_get_children (GTK_CONTAINER (box));
1251         
1252         for (node = item_children; node != NULL; node = g_list_next (node)) {
1253                 gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START);
1254                 if (GTK_IS_TOGGLE_BUTTON (node->data))
1255                         gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5);
1256                 else if (GTK_IS_BUTTON (node->data))
1257                         gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5);
1258         }
1259         g_list_free (item_children);
1260 }
1261
1262
1263 static void
1264 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1265 {
1266         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1267         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1268         GtkWidget *placeholder;
1269         GtkWidget *tool_item;
1270         gint insert_index;
1271         gchar size_text[5];
1272         gint size_index;
1273         gint font_index;
1274         GtkWidget *sizes_menu;
1275         GtkWidget *fonts_menu;
1276         GSList *radio_group = NULL;
1277         GSList *node = NULL;
1278         gchar *markup;
1279
1280         /* Toolbar */
1281         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1282         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1283
1284         /* Font color placeholder */
1285         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1286         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1287
1288         /* font color */
1289         tool_item = GTK_WIDGET (gtk_tool_item_new ());
1290         priv->font_color_button = hildon_color_button_new ();
1291         GTK_WIDGET_UNSET_FLAGS (tool_item, GTK_CAN_FOCUS);
1292         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1293         gtk_container_add (GTK_CONTAINER (tool_item), priv->font_color_button);
1294         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1295         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1296         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1297         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1298                                   "notify::color", 
1299                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1300                                   window);
1301
1302         /* Font size and face placeholder */
1303         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1304         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1305         /* font_size */
1306         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1307         priv->size_tool_button_label = gtk_label_new (NULL);
1308         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1309         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1310                               size_text,"</span>", NULL);
1311         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1312         g_free (markup);
1313         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label);
1314         sizes_menu = gtk_menu_new ();
1315         priv->size_items_group = NULL;
1316         radio_group = NULL;
1317         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1318                 GtkWidget *size_menu_item;
1319
1320                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1321                 size_menu_item = gtk_radio_menu_item_new_with_label (radio_group, size_text);
1322                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (size_menu_item));
1323                 gtk_menu_shell_append (GTK_MENU_SHELL (sizes_menu), size_menu_item);
1324                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (size_menu_item), (wp_font_size[size_index] == 12));
1325                 gtk_widget_show (size_menu_item);
1326
1327                 priv->size_items_group = g_slist_prepend (priv->size_items_group, size_menu_item);
1328                         
1329         }
1330
1331         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1332                 GtkWidget *item = (GtkWidget *) node->data;
1333                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_size_change),
1334                                   window);
1335         }
1336
1337         priv->size_items_group = g_slist_reverse (priv->size_items_group);
1338         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), sizes_menu);
1339         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1340         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1341         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1342         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1343         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1344         priv->font_size_toolitem = tool_item;
1345
1346         /* font face */
1347         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1348         priv->font_tool_button_label = gtk_label_new (NULL);
1349         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1350         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1351         g_free(markup);
1352         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label);
1353         fonts_menu = gtk_menu_new ();
1354         priv->font_items_group = NULL;
1355         radio_group = NULL;
1356         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1357                 GtkWidget *font_menu_item;
1358                 GtkWidget *child_label;
1359
1360                 font_menu_item = gtk_radio_menu_item_new_with_label (radio_group, "");
1361                 child_label = gtk_bin_get_child (GTK_BIN (font_menu_item));
1362                 markup = g_strconcat ("<span font_family='", wp_get_font_name (font_index),"'>", 
1363                                       wp_get_font_name (font_index), "</span>", NULL);
1364                 gtk_label_set_markup (GTK_LABEL (child_label), markup);
1365                 g_free (markup);
1366                 
1367                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (font_menu_item));
1368                 gtk_menu_shell_append (GTK_MENU_SHELL (fonts_menu), font_menu_item);
1369                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (font_menu_item), (font_index == DEFAULT_FONT));
1370                 gtk_widget_show (font_menu_item);
1371
1372                 priv->font_items_group = g_slist_prepend (priv->font_items_group, font_menu_item);
1373                         
1374         }
1375         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1376                 GtkWidget *item = (GtkWidget *) node->data;
1377                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_font_change),
1378                                   window);
1379         }
1380         priv->font_items_group = g_slist_reverse (priv->font_items_group);
1381         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), fonts_menu);
1382         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1383         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1384         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1385         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1386         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1387         priv->font_face_toolitem = tool_item;
1388
1389         /* Set expand and homogeneous for remaining items */
1390         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend");
1391         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1392         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1393         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1394         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1395         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1396         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1397         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1398         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1399
1400         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1401            will not show the tool items added to the placeholders) */
1402         gtk_widget_show_all (parent_priv->toolbar);
1403
1404         /* Set the no show all *after* showing all items. We do not
1405            want the toolbar to be shown with a show all because it
1406            could go agains the gconf setting regarding showing or not
1407            the toolbar of the editor window */
1408         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1409 }
1410
1411
1412
1413 ModestWindow*
1414 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich)
1415 {
1416         GObject *obj;
1417         ModestWindowPrivate *parent_priv;
1418         ModestMsgEditWindowPrivate *priv;
1419         ModestPair *account_pair = NULL;
1420         ModestDimmingRulesGroup *menu_rules_group = NULL;
1421         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1422         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1423         ModestWindowMgr *mgr = NULL;
1424
1425         g_return_val_if_fail (msg, NULL);
1426         g_return_val_if_fail (account_name, NULL);
1427
1428         mgr = modest_runtime_get_window_mgr ();
1429         
1430         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1431
1432         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1433         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1434
1435         /* Menubar. Update the state of some toggles */
1436         parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
1437         hildon_window_set_menu (HILDON_WINDOW (obj), GTK_MENU (parent_priv->menubar));
1438         priv->from_field_protos = get_transports ();
1439         modest_combo_box_set_pair_list (MODEST_COMBO_BOX (priv->from_field), priv->from_field_protos);
1440         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1441         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1442
1443         /* Init window */
1444         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1445
1446         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1447                 
1448         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1449
1450         account_pair = modest_pair_list_find_by_first_as_string (priv->from_field_protos, account_name);
1451         if (account_pair != NULL)
1452                 modest_combo_box_set_active_id (MODEST_COMBO_BOX (priv->from_field), account_pair->first);
1453
1454         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new ();
1455         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
1456         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1457         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1458         /* Add common dimming rules */
1459         modest_dimming_rules_group_add_rules (menu_rules_group, 
1460                                               modest_msg_edit_window_menu_dimming_entries,
1461                                               G_N_ELEMENTS (modest_msg_edit_window_menu_dimming_entries),
1462                                               MODEST_WINDOW (obj));
1463         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1464                                               modest_msg_edit_window_toolbar_dimming_entries,
1465                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1466                                               MODEST_WINDOW (obj));
1467         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_button,
1468                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1469                                                     MODEST_WINDOW (obj));
1470         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1471                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1472                                                     MODEST_WINDOW (obj));
1473         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1474                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1475                                                     MODEST_WINDOW (obj));
1476         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
1477                                               modest_msg_edit_window_clipboard_dimming_entries,
1478                                               G_N_ELEMENTS (modest_msg_edit_window_clipboard_dimming_entries),
1479                                               MODEST_WINDOW (obj));
1480         /* Insert dimming rules group for this window */
1481         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
1482         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1483         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1484         /* Checks the dimming rules */
1485         g_object_unref (menu_rules_group);
1486         g_object_unref (toolbar_rules_group);
1487         g_object_unref (clipboard_rules_group);
1488         gtk_widget_show_all (GTK_WIDGET (obj));
1489         modest_msg_edit_window_clipboard_owner_change (NULL, NULL, MODEST_MSG_EDIT_WINDOW (obj));
1490
1491         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1492
1493         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1494
1495         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1496         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1497         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1498         priv->update_caption_visibility = TRUE;
1499
1500         modest_msg_edit_window_reset_modified (MODEST_MSG_EDIT_WINDOW (obj));
1501
1502         /* Track account-removed signal, this window should be closed
1503            in the case we're creating a mail associated to the account
1504            that is deleted */
1505         priv->account_removed_handler_id = 
1506                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1507                                   "account_removed",
1508                                   G_CALLBACK(on_account_removed),
1509                                   obj);
1510         
1511         return (ModestWindow*) obj;
1512 }
1513
1514 static gint
1515 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1516 {
1517         GString **string_buffer = (GString **) user_data;
1518
1519         *string_buffer = g_string_append (*string_buffer, buffer);
1520    
1521         return 0;
1522 }
1523
1524 /**
1525  * @result: A new string which should be freed with g_free().
1526  */
1527 static gchar *
1528 get_formatted_data (ModestMsgEditWindow *edit_window)
1529 {
1530         ModestMsgEditWindowPrivate *priv;
1531         GString *string_buffer = g_string_new ("");
1532         
1533         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1534
1535         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1536
1537         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1538
1539         return g_string_free (string_buffer, FALSE);
1540                                                                         
1541 }
1542
1543 MsgData * 
1544 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1545 {
1546         MsgData *data;
1547         const gchar *account_name;
1548         ModestMsgEditWindowPrivate *priv;
1549         TnyIterator *att_iter;
1550         
1551         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1552
1553         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1554                                                                         
1555         account_name = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
1556         g_return_val_if_fail (account_name, NULL);
1557         
1558         
1559         /* don't free these (except from) */
1560         data = g_slice_new0 (MsgData);
1561         data->from    =  modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(),
1562                                                              account_name);
1563         data->account_name = g_strdup (account_name);
1564         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1565         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1566         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1567         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1568         if (priv->draft_msg) {
1569                 data->draft_msg = g_object_ref (priv->draft_msg);
1570         } else if (priv->outbox_msg) {
1571                 data->draft_msg = g_object_ref (priv->outbox_msg);
1572         } else {
1573                 data->draft_msg = NULL;
1574         }
1575
1576         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1577         GtkTextIter b, e;
1578         gtk_text_buffer_get_bounds (buf, &b, &e);
1579         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1580
1581         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1582                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1583         else
1584                 data->html_body = NULL;
1585
1586         /* deep-copy the data */
1587         att_iter = tny_list_create_iterator (priv->attachments);
1588         data->attachments = NULL;
1589         while (!tny_iterator_is_done (att_iter)) {
1590                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1591                 if (!(TNY_IS_MIME_PART(part))) {
1592                         g_warning ("strange data in attachment list");
1593                         g_object_unref (part);
1594                         tny_iterator_next (att_iter);
1595                         continue;
1596                 }
1597                 data->attachments = g_list_append (data->attachments,
1598                                                    part);
1599                 tny_iterator_next (att_iter);
1600         }
1601         g_object_unref (att_iter);
1602
1603         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1604         att_iter = tny_list_create_iterator (priv->images);
1605         data->images = NULL;
1606         while (!tny_iterator_is_done (att_iter)) {
1607                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1608                 const gchar *cid;
1609                 if (!(TNY_IS_MIME_PART(part))) {
1610                         g_warning ("strange data in attachment list");
1611                         g_object_unref (part);
1612                         tny_iterator_next (att_iter);
1613                         continue;
1614                 }
1615                 cid = tny_mime_part_get_content_id (part);
1616                 if (cid) {                      
1617                         gchar *image_tag_id;
1618                         GtkTextTag *image_tag;
1619                         GtkTextIter iter;
1620                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1621                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1622                         g_free (image_tag_id);
1623                         
1624                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1625                         if (image_tag && 
1626                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1627                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1628                                 data->images = g_list_append (data->images,
1629                                                               g_object_ref (part));
1630                 }
1631                 g_object_unref (part);
1632                 tny_iterator_next (att_iter);
1633         }
1634         g_object_unref (att_iter);
1635         
1636         data->priority_flags = priv->priority_flags;
1637
1638         return data;
1639 }
1640
1641
1642 static void
1643 unref_gobject (GObject *obj, gpointer data)
1644 {
1645         if (!G_IS_OBJECT(obj))
1646                 return;
1647         g_object_unref (obj);
1648 }
1649
1650 void 
1651 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1652                                                       MsgData *data)
1653 {
1654         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1655
1656         if (!data)
1657                 return;
1658
1659         g_free (data->to);
1660         g_free (data->cc);
1661         g_free (data->bcc);
1662         g_free (data->from);
1663         g_free (data->subject);
1664         g_free (data->plain_body);
1665         g_free (data->html_body);
1666         g_free (data->account_name);
1667         
1668         if (data->draft_msg != NULL) {
1669                 g_object_unref (data->draft_msg);
1670                 data->draft_msg = NULL;
1671         }
1672         
1673         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1674         g_list_free (data->attachments);
1675         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1676         g_list_free (data->images);
1677         
1678         g_slice_free (MsgData, data);
1679 }
1680
1681 void                    
1682 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1683                                        gint *parts_count,
1684                                        guint64 *parts_size)
1685 {
1686         ModestMsgEditWindowPrivate *priv;
1687
1688         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1689
1690         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1691
1692         /* TODO: add images */
1693         *parts_size += priv->images_size;
1694         *parts_count += priv->images_count;
1695
1696 }
1697
1698 ModestMsgEditFormat
1699 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1700 {
1701         gboolean rich_text;
1702         ModestMsgEditWindowPrivate *priv = NULL;
1703         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1704
1705         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1706
1707         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1708         if (rich_text)
1709                 return MODEST_MSG_EDIT_FORMAT_HTML;
1710         else
1711                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1712 }
1713
1714 void
1715 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1716                                    ModestMsgEditFormat format)
1717 {
1718         ModestMsgEditWindowPrivate *priv;
1719
1720         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1721         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1722
1723         switch (format) {
1724         case MODEST_MSG_EDIT_FORMAT_HTML:
1725                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1726                 break;
1727         case MODEST_MSG_EDIT_FORMAT_TEXT:
1728                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1729                 break;
1730         default:
1731                 g_return_if_reached ();
1732         }
1733 }
1734
1735 ModestMsgEditFormatState *
1736 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1737 {
1738         ModestMsgEditFormatState *format_state = NULL;
1739         ModestMsgEditWindowPrivate *priv;
1740         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1741
1742         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
1743         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1744
1745         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
1746
1747         format_state = g_new0 (ModestMsgEditFormatState, 1);
1748         format_state->bold = buffer_format->bold&0x1;
1749         format_state->italics = buffer_format->italic&0x1;
1750         format_state->bullet = buffer_format->bullet&0x1;
1751         format_state->color = buffer_format->color;
1752         format_state->font_size = buffer_format->font_size;
1753         format_state->font_family = wp_get_font_name (buffer_format->font);
1754         format_state->justification = buffer_format->justification;
1755         g_free (buffer_format);
1756
1757         return format_state;
1758  
1759 }
1760
1761 void
1762 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
1763                                          const ModestMsgEditFormatState *format_state)
1764 {
1765         ModestMsgEditWindowPrivate *priv;
1766         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1767         WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1);
1768         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1769         g_return_if_fail (format_state != NULL);
1770
1771         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1772         gtk_widget_grab_focus (priv->msg_body);
1773         buffer_format->bold = (format_state->bold != FALSE);
1774         buffer_format->italic = (format_state->italics != FALSE);
1775         buffer_format->color = format_state->color;
1776         buffer_format->font_size = format_state->font_size;
1777         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
1778         buffer_format->justification = format_state->justification;
1779         buffer_format->bullet = format_state->bullet;
1780
1781         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
1782
1783         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
1784         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
1785         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
1786         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
1787         buffer_format->cs.font = (buffer_format->font != current_format->font);
1788         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
1789         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
1790
1791         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
1792         if (buffer_format->cs.bold) {
1793                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
1794                                               GINT_TO_POINTER (buffer_format->bold&0x1));
1795         }
1796         if (buffer_format->cs.italic) {
1797                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
1798                                               GINT_TO_POINTER (buffer_format->italic&0x1));
1799         }
1800         if (buffer_format->cs.color) {
1801                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1802                                               GINT_TO_POINTER (&(buffer_format->color)));
1803         }
1804         if (buffer_format->cs.font_size) {
1805                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
1806                                               GINT_TO_POINTER (buffer_format->font_size));
1807         }
1808         if (buffer_format->cs.justification) {
1809                 switch (buffer_format->justification) {
1810                 case GTK_JUSTIFY_LEFT:
1811                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
1812                                                       GINT_TO_POINTER(TRUE));
1813                         break;
1814                 case GTK_JUSTIFY_CENTER:
1815                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
1816                                                       GINT_TO_POINTER(TRUE));
1817                         break;
1818                 case GTK_JUSTIFY_RIGHT:
1819                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
1820                                                       GINT_TO_POINTER(TRUE));
1821                         break;
1822                 default:
1823                         break;
1824                 }
1825                         
1826         }
1827         if (buffer_format->cs.font) {
1828                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
1829                                               GINT_TO_POINTER (buffer_format->font));
1830         }
1831         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
1832         if (buffer_format->cs.bullet) {
1833                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
1834                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
1835         }
1836 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
1837         
1838         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
1839         
1840         g_free (current_format);
1841
1842 }
1843
1844 static void
1845 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
1846 {
1847         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1848         GtkAction *action;
1849         ModestWindowPrivate *parent_priv;
1850         ModestMsgEditWindowPrivate *priv;
1851         GtkWidget *new_size_menuitem;
1852         GtkWidget *new_font_menuitem;
1853         
1854         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1855         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1856
1857         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
1858                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu");
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         } else {
1862                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatPlainTextMenu");
1863                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1864                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1865         }
1866
1867         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1868
1869         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1870         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
1871
1872         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1873         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
1874
1875 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
1876 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
1877
1878         action = NULL;
1879         switch (buffer_format->justification)
1880         {
1881         case GTK_JUSTIFY_LEFT:
1882                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentLeftMenu");
1883                 break;
1884         case GTK_JUSTIFY_CENTER:
1885                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentCenterMenu");
1886                 break;
1887         case GTK_JUSTIFY_RIGHT:
1888                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentRightMenu");
1889                 break;
1890         default:
1891                 break;
1892         }
1893         
1894         if (action != NULL)
1895                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1896         
1897         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
1898                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
1899                                          window);
1900         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
1901         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
1902                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
1903                                            window);
1904
1905         new_size_menuitem = GTK_WIDGET ((g_slist_nth (priv->size_items_group, 
1906                                                       buffer_format->font_size))->data);
1907         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_size_menuitem))) {
1908                 GtkWidget *label;
1909                 gchar *markup;
1910
1911                 label = gtk_bin_get_child (GTK_BIN (new_size_menuitem));
1912                 markup = g_strconcat ("<span font_family='Sans'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
1913                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1914                 g_free (markup);
1915                 g_signal_handlers_block_by_func (G_OBJECT (new_size_menuitem),
1916                                                  G_CALLBACK (modest_msg_edit_window_size_change),
1917                                                  window);
1918                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_size_menuitem), TRUE);
1919                 g_signal_handlers_unblock_by_func (G_OBJECT (new_size_menuitem),
1920                                                    G_CALLBACK (modest_msg_edit_window_size_change),
1921                                                    window);
1922         }
1923
1924         new_font_menuitem = GTK_WIDGET ((g_slist_nth (priv->font_items_group, 
1925                                                       buffer_format->font))->data);
1926         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_font_menuitem))) {
1927                 GtkWidget *label;
1928                 gchar *markup;
1929
1930                 label = gtk_bin_get_child (GTK_BIN (new_font_menuitem));
1931                 markup = g_strconcat ("<span font_family='", gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
1932                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1933                 g_free (markup);
1934                 g_signal_handlers_block_by_func (G_OBJECT (new_font_menuitem),
1935                                                  G_CALLBACK (modest_msg_edit_window_font_change),
1936                                                  window);
1937                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_font_menuitem), TRUE);
1938                 g_signal_handlers_unblock_by_func (G_OBJECT (new_font_menuitem),
1939                                                    G_CALLBACK (modest_msg_edit_window_font_change),
1940                                                    window);
1941         }
1942
1943         g_free (buffer_format);
1944
1945 }
1946
1947 #ifdef MODEST_HILDON_VERSION_0
1948 void
1949 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1950 {
1951         
1952         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1953         ModestMsgEditWindowPrivate *priv;
1954         GtkWidget *dialog = NULL;
1955         gint response;
1956         GdkColor *new_color = NULL;
1957
1958         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1959         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1960         
1961         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1962         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color));
1963         g_free (buffer_format);
1964
1965         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
1966                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1967                 if (new_color != NULL) {
1968                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1969                                                       (gpointer) new_color);
1970                 }
1971         }
1972         gtk_widget_destroy (dialog);
1973 }
1974
1975
1976 void
1977 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
1978 {
1979         
1980         ModestMsgEditWindowPrivate *priv;
1981         GtkWidget *dialog = NULL;
1982         gint response;
1983         GdkColor *old_color = NULL;
1984         const GdkColor *new_color = NULL;
1985         
1986         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1987         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
1988         
1989         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1990         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color);
1991
1992         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
1993                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1994                 if (new_color != NULL)
1995                         wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color);
1996         }
1997         gtk_widget_destroy (dialog);
1998
1999 }
2000
2001 #else 
2002 void
2003 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2004 {
2005         
2006         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2007         ModestMsgEditWindowPrivate *priv;
2008         GtkWidget *dialog = NULL;
2009
2010         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2011         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2012                 
2013         dialog = hildon_color_chooser_new ();
2014         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2015         g_free (buffer_format);
2016
2017         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2018                 GdkColor col;
2019                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2020                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2021                                               (gpointer) &col);
2022         }
2023         gtk_widget_destroy (dialog);
2024 }
2025
2026
2027 void
2028 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2029 {
2030         
2031         ModestMsgEditWindowPrivate *priv;
2032         GtkWidget *dialog = NULL;
2033         GdkColor *old_color = NULL;
2034         
2035         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2036         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2037         
2038         dialog = hildon_color_chooser_new ();
2039         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2040
2041         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2042                 GdkColor col;
2043                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2044                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2045         }
2046         gtk_widget_destroy (dialog);
2047 }
2048
2049 #endif /*!MODEST_HILDON_VERSION_0*/
2050
2051
2052
2053 static TnyStream* create_stream_for_uri (const gchar* uri)
2054 {
2055         if (!uri)
2056                 return NULL;
2057                 
2058         TnyStream *result = NULL;
2059
2060         GnomeVFSHandle *handle = NULL;
2061         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2062         if (test == GNOME_VFS_OK) {
2063                 /* Create the tinymail stream: */
2064                 /* Presumably tinymai will call gnome_vfs_close (handle) later. */
2065                 result = TNY_STREAM (tny_vfs_stream_new (handle));
2066         }
2067         
2068         return result;
2069 }
2070
2071 void
2072 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2073 {
2074         
2075         ModestMsgEditWindowPrivate *priv;
2076         GtkWidget *dialog = NULL;
2077         gint response = 0;
2078         GSList *uris = NULL;
2079         GSList *uri_node = NULL;
2080         
2081         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2082         
2083         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2084         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
2085         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2086
2087         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2088
2089         response = gtk_dialog_run (GTK_DIALOG (dialog));
2090         switch (response) {
2091         case GTK_RESPONSE_OK:
2092                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2093                 break;
2094         default:
2095                 break;
2096         }
2097         gtk_widget_destroy (dialog);
2098
2099         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2100                 const gchar *uri;
2101                 GnomeVFSHandle *handle = NULL;
2102                 GnomeVFSResult result;
2103                 GtkTextIter position;
2104                 GtkTextMark *insert_mark;
2105
2106                 uri = (const gchar *) uri_node->data;
2107                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2108                 if (result == GNOME_VFS_OK) {
2109                         GdkPixbuf *pixbuf;
2110                         GnomeVFSFileInfo *info;
2111                         gchar *filename, *basename, *escaped_filename;
2112                         TnyMimePart *mime_part;
2113                         gchar *content_id;
2114                         const gchar *mime_type = NULL;
2115                         GnomeVFSURI *vfs_uri;
2116                         guint64 stream_size;
2117
2118                         vfs_uri = gnome_vfs_uri_new (uri);
2119
2120                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2121                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2122                         g_free (escaped_filename);
2123                         gnome_vfs_uri_unref (vfs_uri);
2124                         info = gnome_vfs_file_info_new ();
2125
2126                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2127                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2128                             == GNOME_VFS_OK)
2129                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2130
2131                         mime_part = tny_platform_factory_new_mime_part
2132                                 (modest_runtime_get_platform_factory ());
2133                                 
2134                         TnyStream *stream = create_stream_for_uri (uri);
2135                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2136                         
2137                         content_id = g_strdup_printf ("%d", priv->last_cid);
2138                         tny_mime_part_set_content_id (mime_part, content_id);
2139                         g_free (content_id);
2140                         priv->last_cid++;
2141                         
2142                         basename = g_path_get_basename (filename);
2143                         tny_mime_part_set_filename (mime_part, basename);
2144                         g_free (basename);
2145
2146                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
2147                         
2148                         if (pixbuf != NULL) {
2149                                 priv->images_size += stream_size;
2150                                 priv->images_count ++;
2151                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2152                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2153                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
2154                         } 
2155
2156                         tny_list_prepend (priv->images, (GObject *) mime_part);
2157                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2158                         g_free (filename);
2159                         g_object_unref (mime_part);
2160                         gnome_vfs_file_info_unref (info);
2161
2162                 }
2163         }
2164
2165
2166 }
2167
2168 void
2169 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2170 {       
2171         GtkWidget *dialog = NULL;
2172         gint response = 0;
2173         GSList *uris = NULL;
2174         GSList *uri_node;
2175         
2176         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2177         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2178         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2179
2180         response = gtk_dialog_run (GTK_DIALOG (dialog));
2181         switch (response) {
2182         case GTK_RESPONSE_OK:
2183                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2184                 break;
2185         default:
2186                 break;
2187         }
2188         gtk_widget_destroy (dialog);
2189
2190         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2191                 const gchar *uri = (const gchar *) uri_node->data;
2192                 modest_msg_edit_window_attach_file_one (window, uri);
2193         }
2194         g_slist_foreach (uris, (GFunc) g_free, NULL);
2195         g_slist_free (uris);
2196 }
2197
2198 void
2199 modest_msg_edit_window_attach_file_one (
2200                 ModestMsgEditWindow *window,
2201                 const gchar *uri)
2202 {
2203         GnomeVFSHandle *handle = NULL;
2204         ModestMsgEditWindowPrivate *priv;
2205         GnomeVFSResult result;
2206
2207         g_return_if_fail (window);
2208         g_return_if_fail (uri);
2209                 
2210         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2211         
2212         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2213         if (result == GNOME_VFS_OK) {
2214                 TnyMimePart *mime_part;
2215                 TnyStream *stream;
2216                 const gchar *mime_type = NULL;
2217                 gchar *basename;
2218                 gchar *escaped_filename;
2219                 gchar *filename;
2220                 gchar *content_id;
2221                 GnomeVFSFileInfo *info;
2222                 GnomeVFSURI *vfs_uri;
2223
2224                 vfs_uri = gnome_vfs_uri_new (uri);
2225                 
2226
2227                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2228                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2229                 g_free (escaped_filename);
2230                 gnome_vfs_uri_unref (vfs_uri);
2231
2232                 info = gnome_vfs_file_info_new ();
2233                 
2234                 if (gnome_vfs_get_file_info (uri, 
2235                                              info, 
2236                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2237                     == GNOME_VFS_OK)
2238                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2239                 mime_part = tny_platform_factory_new_mime_part
2240                         (modest_runtime_get_platform_factory ());
2241                 stream = TNY_STREAM (tny_vfs_stream_new (handle));
2242                 
2243                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2244
2245                 g_object_unref (stream);
2246                 
2247                 content_id = g_strdup_printf ("%d", priv->last_cid);
2248                 tny_mime_part_set_content_id (mime_part, content_id);
2249                 g_free (content_id);
2250                 priv->last_cid++;
2251                 
2252                 basename = g_path_get_basename (filename);
2253                 tny_mime_part_set_filename (mime_part, basename);
2254                 g_free (basename);
2255                 
2256                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2257                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2258                                                         mime_part,
2259                                                         info->size == 0, info->size);
2260                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2261                 gtk_widget_show_all (priv->attachments_caption);
2262                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2263                 g_free (filename);
2264                 g_object_unref (mime_part);
2265                 gnome_vfs_file_info_unref (info);
2266         }
2267 }
2268
2269 void
2270 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2271                                            TnyList *att_list)
2272 {
2273         ModestMsgEditWindowPrivate *priv;
2274         TnyIterator *iter;
2275
2276         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2277         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2278
2279         if (att_list == NULL) {
2280                 att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2281         } else {
2282                 g_object_ref (att_list);
2283         }
2284
2285         if (tny_list_get_length (att_list) == 0) {
2286                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2287         } else {
2288                 GtkWidget *confirmation_dialog = NULL;
2289                 gboolean dialog_response;
2290                 gchar *message = NULL;
2291                 gchar *filename = NULL;
2292
2293                 if (tny_list_get_length (att_list) == 1) {
2294                         TnyMimePart *part;
2295                         iter = tny_list_create_iterator (att_list);
2296                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2297                         g_object_unref (iter);
2298                         if (TNY_IS_MSG (part)) {
2299                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2300                                 if (header) {
2301                                         filename = g_strdup (tny_header_get_subject (header));
2302                                         g_object_unref (header);
2303                                 }
2304                                 if (filename == NULL) {
2305                                         filename = g_strdup (_("mail_va_no_subject"));
2306                                 }
2307                         } else {
2308                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2309                         }
2310                         g_object_unref (part);
2311                 } else {
2312                         filename = g_strdup ("");
2313                 }
2314                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments",
2315                                                     (tny_list_get_length (att_list) == 1)), filename);
2316                 g_free (filename);
2317                 confirmation_dialog = hildon_note_new_confirmation (GTK_WINDOW (window), message);
2318                 g_free (message);
2319                 dialog_response = (gtk_dialog_run (GTK_DIALOG (confirmation_dialog))==GTK_RESPONSE_OK);
2320                 gtk_widget_destroy (confirmation_dialog);
2321                 if (!dialog_response) {
2322                         g_object_unref (att_list);
2323                         return;
2324                 }
2325                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_removing_attachment"));
2326                 
2327                 for (iter = tny_list_create_iterator (att_list);
2328                      !tny_iterator_is_done (iter);
2329                      tny_iterator_next (iter)) {
2330                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2331                         const gchar *att_id;
2332                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2333
2334                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2335                                                                    mime_part);
2336                         if (tny_list_get_length (priv->attachments) == 0)
2337                                 gtk_widget_hide (priv->attachments_caption);
2338                         att_id = tny_mime_part_get_content_id (mime_part);
2339                         if (att_id != NULL)
2340                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2341                                                                  att_id);
2342                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2343                         g_object_unref (mime_part);
2344                 }
2345                 g_object_unref (iter);
2346         }
2347
2348         g_object_unref (att_list);
2349
2350         /* if the last attachment has been removed, focus the Subject: field */
2351         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) 
2352                 gtk_widget_grab_focus (priv->subject_field);
2353 }
2354
2355 static void
2356 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2357                                             gpointer userdata)
2358 {
2359         ModestMsgEditWindowPrivate *priv;
2360         GdkColor *new_color;
2361         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2362         
2363 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2364         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2365 #else 
2366         GdkColor col;
2367         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2368         new_color = &col;
2369 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2370
2371         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2372         
2373         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2374
2375 }
2376
2377 static void
2378 modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
2379                                     gpointer userdata)
2380 {
2381         ModestMsgEditWindowPrivate *priv;
2382         gint new_size_index;
2383         ModestMsgEditWindow *window;
2384         GtkWidget *label;
2385         
2386         window = MODEST_MSG_EDIT_WINDOW (userdata);
2387         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2388         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2389
2390         if (gtk_check_menu_item_get_active (menu_item)) {
2391                 gchar *markup;
2392                 WPTextBufferFormat format;
2393
2394                 memset (&format, 0, sizeof (format));
2395                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2396
2397                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2398                 
2399                 new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label)));
2400                 format.cs.font_size = TRUE;
2401                 format.cs.text_position = TRUE;
2402                 format.cs.font = TRUE;
2403                 format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE);
2404 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2405
2406                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2407                                                    GINT_TO_POINTER (wp_get_font_size_index (new_size_index, 12))))
2408                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2409                 
2410                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2411                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
2412                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2413                 g_free (markup);
2414         }
2415 }
2416
2417 static void
2418 modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
2419                                     gpointer userdata)
2420 {
2421         ModestMsgEditWindowPrivate *priv;
2422         gint new_font_index;
2423         ModestMsgEditWindow *window;
2424         GtkWidget *label;
2425         
2426         window = MODEST_MSG_EDIT_WINDOW (userdata);
2427         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2428         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2429
2430         if (gtk_check_menu_item_get_active (menu_item)) {
2431                 gchar *markup;
2432
2433                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2434                 
2435                 new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT);
2436
2437                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2438                                                    GINT_TO_POINTER(new_font_index)))
2439                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2440                 
2441                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2442                     markup = g_strconcat ("<span font_family='",gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
2443                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2444                 g_free (markup);
2445         }
2446 }
2447
2448 static gboolean
2449 modest_msg_edit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2450 {
2451         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2452                 ModestWindowPrivate *parent_priv;
2453                 ModestWindowMgr *mgr;
2454                 gboolean is_fullscreen;
2455                 GtkAction *fs_toggle_action;
2456                 gboolean active;
2457
2458                 mgr = modest_runtime_get_window_mgr ();
2459                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2460
2461                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2462                 
2463                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2464                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2465                 if (is_fullscreen != active)
2466                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2467         }
2468
2469         return FALSE;
2470
2471 }
2472
2473 void
2474 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2475                                 gboolean show)
2476 {
2477         ModestMsgEditWindowPrivate *priv = NULL;
2478         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2479
2480         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2481         if (!priv->update_caption_visibility)
2482                 return;
2483
2484         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2485         if (show)
2486                 gtk_widget_show (priv->cc_caption);
2487         else
2488                 gtk_widget_hide (priv->cc_caption);
2489
2490         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2491 }
2492
2493 void
2494 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2495                                  gboolean show)
2496 {
2497         ModestMsgEditWindowPrivate *priv = NULL;
2498         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2499
2500         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2501         if (!priv->update_caption_visibility)
2502                 return;
2503
2504         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2505         if (show)
2506                 gtk_widget_show (priv->bcc_caption);
2507         else
2508                 gtk_widget_hide (priv->bcc_caption);
2509
2510         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2511 }
2512
2513 static void
2514 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2515                                          ModestRecptEditor *editor)
2516 {
2517         ModestMsgEditWindowPrivate *priv;
2518
2519         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2520         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2521         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2522
2523         if (editor == NULL) {
2524                 GtkWidget *view_focus;
2525                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2526
2527                 /* This code should be kept in sync with ModestRecptEditor. The
2528                    textview inside the recpt editor is the one that really gets the
2529                    focus. As it's inside a scrolled window, and this one inside the
2530                    hbox recpt editor inherits from, we'll need to go up in the 
2531                    hierarchy to know if the text view is part of the recpt editor
2532                    or if it's a different text entry */
2533
2534                 if (gtk_widget_get_parent (view_focus)) {
2535                         GtkWidget *first_parent;
2536
2537                         first_parent = gtk_widget_get_parent (view_focus);
2538                         if (gtk_widget_get_parent (first_parent) && 
2539                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2540                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2541                         }
2542                 }
2543
2544                 if (editor == NULL)
2545                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2546
2547         }
2548
2549         modest_address_book_select_addresses (editor);
2550
2551 }
2552
2553 void
2554 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2555 {
2556         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2557
2558         modest_msg_edit_window_open_addressbook (window, NULL);
2559 }
2560
2561 static void
2562 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2563                                      gboolean show_toolbar)
2564 {
2565         ModestWindowPrivate *parent_priv;
2566         const gchar *action_name;
2567         GtkAction *action;
2568         
2569         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2570         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2571
2572         /* We can not just use the code of
2573            modest_msg_edit_window_setup_toolbar because it has a
2574            mixture of both initialization and creation code. */
2575         if (show_toolbar)
2576                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2577         else
2578                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2579
2580         /* Update also the actions (to update the toggles in the
2581            menus), we have to do it manually because some other window
2582            of the same time could have changed it (remember that the
2583            toolbar fullscreen mode is shared by all the windows of the
2584            same type */
2585         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2586                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2587         else
2588                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2589         
2590         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2591         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2592                                                             show_toolbar);
2593
2594 }
2595
2596 void
2597 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2598                                            TnyHeaderFlags priority_flags)
2599 {
2600         ModestMsgEditWindowPrivate *priv;
2601         ModestWindowPrivate *parent_priv;
2602
2603         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2604
2605         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2606         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2607
2608         if (priv->priority_flags != priority_flags) {
2609                 GtkAction *priority_action = NULL;
2610
2611                 priv->priority_flags = priority_flags;
2612
2613                 switch (priority_flags) {
2614                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2615                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_high", GTK_ICON_SIZE_MENU);
2616                         gtk_widget_show (priv->priority_icon);
2617                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2618                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2619                         break;
2620                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2621                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_low", GTK_ICON_SIZE_MENU);
2622                         gtk_widget_show (priv->priority_icon);
2623                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2624                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2625                         break;
2626                 default:
2627                         gtk_widget_hide (priv->priority_icon);
2628                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2629                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2630                         break;
2631                 }
2632                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2633                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2634         }
2635 }
2636
2637 void
2638 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2639                                         gint file_format)
2640 {
2641         ModestMsgEditWindowPrivate *priv;
2642         ModestWindowPrivate *parent_priv;
2643         gint current_format;
2644
2645         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2646
2647         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2648         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2649
2650         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2651                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2652
2653         if (current_format != file_format) {
2654                 switch (file_format) {
2655                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2656                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2657                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2658                         break;
2659                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2660                 {
2661                         GtkWidget *dialog;
2662                         gint response;
2663                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2664                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2665                         gtk_widget_destroy (dialog);
2666                         if (response == GTK_RESPONSE_OK) {
2667                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2668                         } else {
2669                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu"));
2670                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
2671                         }
2672                 }
2673                         break;
2674                 }
2675                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2676         }
2677 }
2678
2679 void
2680 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2681 {
2682         GtkWidget *dialog;
2683         ModestMsgEditWindowPrivate *priv;
2684         WPTextBufferFormat oldfmt, fmt;
2685         gint old_position = 0;
2686         gint response = 0;
2687         gint position = 0;
2688         gint font_size;
2689         GdkColor *color = NULL;
2690         gboolean bold, bold_set, italic, italic_set;
2691         gboolean underline, underline_set;
2692         gboolean strikethrough, strikethrough_set;
2693         gboolean position_set;
2694         gboolean font_size_set, font_set, color_set;
2695         gchar *font_name;
2696
2697         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2698         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2699         
2700         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2701
2702         /* First we get the currently selected font information */
2703         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2704
2705         switch (oldfmt.text_position) {
2706         case TEXT_POSITION_NORMAL:
2707                 old_position = 0;
2708                 break;
2709         case TEXT_POSITION_SUPERSCRIPT:
2710                 old_position = 1;
2711                 break;
2712         default:
2713                 old_position = -1;
2714                 break;
2715         }
2716
2717         g_object_set (G_OBJECT (dialog),
2718                       "bold", oldfmt.bold != FALSE,
2719                       "bold-set", !oldfmt.cs.bold,
2720                       "underline", oldfmt.underline != FALSE,
2721                       "underline-set", !oldfmt.cs.underline,
2722                       "italic", oldfmt.italic != FALSE,
2723                       "italic-set", !oldfmt.cs.italic,
2724                       "strikethrough", oldfmt.strikethrough != FALSE,
2725                       "strikethrough-set", !oldfmt.cs.strikethrough,
2726                       "color", &oldfmt.color,
2727                       "color-set", !oldfmt.cs.color,
2728                       "size", wp_font_size[oldfmt.font_size],
2729                       "size-set", !oldfmt.cs.font_size,
2730                       "position", old_position,
2731                       "position-set", !oldfmt.cs.text_position,
2732                       "family", wp_get_font_name (oldfmt.font),
2733                       "family-set", !oldfmt.cs.font,
2734                       NULL);
2735
2736         gtk_widget_show_all (dialog);
2737         response = gtk_dialog_run (GTK_DIALOG (dialog));
2738         if (response == GTK_RESPONSE_OK) {
2739
2740                 g_object_get( dialog,
2741                               "bold", &bold,
2742                               "bold-set", &bold_set,
2743                               "underline", &underline,
2744                               "underline-set", &underline_set,
2745                               "italic", &italic,
2746                               "italic-set", &italic_set,
2747                               "strikethrough", &strikethrough,
2748                               "strikethrough-set", &strikethrough_set,
2749                               "color", &color,
2750                               "color-set", &color_set,
2751                               "size", &font_size,
2752                               "size-set", &font_size_set,
2753                               "family", &font_name,
2754                               "family-set", &font_set,
2755                               "position", &position,
2756                               "position-set", &position_set,
2757                               NULL );
2758                 
2759         }       
2760
2761         if (response == GTK_RESPONSE_OK) {
2762                 memset(&fmt, 0, sizeof(fmt));
2763                 if (bold_set) {
2764                         fmt.bold = bold;
2765                         fmt.cs.bold = TRUE;
2766                 }
2767                 if (italic_set) {
2768                         fmt.italic = italic;
2769                         fmt.cs.italic = TRUE;
2770                 }
2771                 if (underline_set) {
2772                         fmt.underline = underline;
2773                         fmt.cs.underline = TRUE;
2774                 }
2775                 if (strikethrough_set) {
2776                         fmt.strikethrough = strikethrough;
2777                         fmt.cs.strikethrough = TRUE;
2778                 }
2779                 if (position_set) {
2780                         fmt.text_position =
2781                                 ( position == 0 )
2782                                 ? TEXT_POSITION_NORMAL
2783                                 : ( ( position == 1 )
2784                                     ? TEXT_POSITION_SUPERSCRIPT
2785                                     : TEXT_POSITION_SUBSCRIPT );
2786                         fmt.cs.text_position = TRUE;
2787                         fmt.font_size = oldfmt.font_size;
2788                 }
2789                 if (color_set) {
2790                         fmt.color = *color;
2791                         fmt.cs.color = TRUE;
2792                 }
2793                 if (font_set) {
2794                         fmt.font = wp_get_font_index(font_name,
2795                                                      DEFAULT_FONT);
2796                         fmt.cs.font = TRUE;
2797                 }
2798                 g_free(font_name);
2799                 if (font_size_set) {
2800                         fmt.cs.font_size = TRUE;
2801                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
2802                 }
2803                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2804                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2805         }
2806         gtk_widget_destroy (dialog);
2807         
2808         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2809 }
2810
2811 void
2812 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2813 {
2814         ModestMsgEditWindowPrivate *priv;
2815
2816         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2817         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2818         
2819         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
2820
2821         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2822         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2823
2824 }
2825
2826 void
2827 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
2828 {
2829         ModestMsgEditWindowPrivate *priv;
2830
2831         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2832         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2833         
2834         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
2835
2836         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2837         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2838
2839 }
2840
2841 static void  
2842 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
2843 {
2844         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2845
2846         priv->can_undo = can_undo;
2847 }
2848
2849 static void  
2850 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
2851 {
2852         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2853
2854         priv->can_redo = can_redo;
2855 }
2856
2857 gboolean            
2858 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
2859 {
2860         ModestMsgEditWindowPrivate *priv;
2861         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2862         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2863
2864         return priv->can_undo;
2865 }
2866
2867 gboolean            
2868 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
2869 {
2870         ModestMsgEditWindowPrivate *priv;
2871         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2872         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2873
2874         return priv->can_redo;
2875 }
2876
2877
2878 static void
2879 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
2880 {
2881         GtkTextIter iter;
2882         GtkTextIter match_start, match_end;
2883
2884         if (image_id == NULL)
2885                 return;
2886
2887         gtk_text_buffer_get_start_iter (buffer, &iter);
2888
2889         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
2890                 GSList *tags = gtk_text_iter_get_tags (&match_start);
2891                 GSList *node;
2892                 for (node = tags; node != NULL; node = g_slist_next (node)) {
2893                         GtkTextTag *tag = (GtkTextTag *) node->data;
2894                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
2895                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
2896                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
2897                                         gint offset;
2898                                         offset = gtk_text_iter_get_offset (&match_start);
2899                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
2900                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
2901                                 }
2902                         }
2903                 }
2904                 gtk_text_iter_forward_char (&iter);
2905         }
2906 }
2907
2908 gboolean
2909 message_is_empty (ModestMsgEditWindow *window)
2910 {
2911         ModestMsgEditWindowPrivate *priv = NULL;
2912
2913         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2914         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2915
2916         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
2917          * so we can ignore markup.
2918          */
2919         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
2920         gint count = 0;
2921         if (buf)
2922                 count = gtk_text_buffer_get_char_count (buf);
2923
2924         return count == 0;
2925 }
2926
2927 static gboolean
2928 msg_body_focus (GtkWidget *focus,
2929                 GdkEventFocus *event,
2930                 gpointer userdata)
2931 {
2932         
2933         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
2934         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
2935         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
2936         return FALSE;
2937 }
2938
2939 static void
2940 recpt_field_changed (GtkTextBuffer *buffer,
2941                   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 static void
2948 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
2949 {
2950         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2951         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
2952 }
2953
2954 void
2955 modest_msg_edit_window_reset_modified (ModestMsgEditWindow *editor)
2956 {
2957         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2958         GtkTextBuffer *buffer;
2959
2960         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2961         gtk_text_buffer_set_modified (buffer, FALSE);
2962         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2963         gtk_text_buffer_set_modified (buffer, FALSE);
2964         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2965         gtk_text_buffer_set_modified (buffer, FALSE);
2966         gtk_text_buffer_set_modified (priv->text_buffer, FALSE);
2967 }
2968
2969 gboolean
2970 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
2971 {
2972         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2973         GtkTextBuffer *buffer;
2974
2975         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2976         if (gtk_text_buffer_get_modified (buffer))
2977                 return TRUE;
2978         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2979         if (gtk_text_buffer_get_modified (buffer))
2980                 return TRUE;
2981         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2982         if (gtk_text_buffer_get_modified (buffer))
2983                 return TRUE;
2984         if (gtk_text_buffer_get_modified (priv->text_buffer))
2985                 return TRUE;
2986
2987         return FALSE;
2988 }
2989
2990
2991
2992
2993 gboolean
2994 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
2995 {
2996         ModestMsgEditWindowPrivate *priv = NULL;
2997         
2998         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2999         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3000
3001         /* check if there's no recipient added */
3002         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3003             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3004             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3005                 /* no recipient contents, then select contacts */
3006                 modest_msg_edit_window_open_addressbook (window, NULL);
3007                 return FALSE;
3008         }
3009
3010         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
3011                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3012                 return FALSE;
3013         }
3014         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
3015                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3016                 return FALSE;
3017         }
3018         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
3019                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3020                 return FALSE;
3021         }
3022
3023         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3024             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3025                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3026
3027         return TRUE;
3028
3029 }
3030
3031 static void
3032 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3033                                                ModestMsgEditWindow *window)
3034 {
3035         modest_msg_edit_window_offer_attach_file (window);
3036 }
3037
3038 const gchar *
3039 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3040 {
3041         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3042
3043         return priv->clipboard_text;
3044 }
3045
3046 static void
3047 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3048                                                GdkEvent *event,
3049                                                ModestMsgEditWindow *window)
3050 {
3051         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3052         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3053         gchar *text = NULL;
3054         if (!GTK_WIDGET_VISIBLE (window))
3055                 return;
3056
3057         text = gtk_clipboard_wait_for_text (selection_clipboard);
3058
3059         if (priv->clipboard_text != NULL) {
3060                 g_free (priv->clipboard_text);
3061         }
3062         priv->clipboard_text = text;
3063
3064         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3065 }
3066 static void 
3067 subject_field_move_cursor (GtkEntry *entry,
3068                            GtkMovementStep step,
3069                            gint a1,
3070                            gboolean a2,
3071                            gpointer window)
3072 {
3073         if (!GTK_WIDGET_VISIBLE (window))
3074                 return;
3075
3076         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3077 }
3078
3079 static void 
3080 update_window_title (ModestMsgEditWindow *window)
3081 {
3082         ModestMsgEditWindowPrivate *priv = NULL;
3083         const gchar *subject;
3084
3085         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3086         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3087         if (subject == NULL || subject[0] == '\0')
3088                 subject = _("mail_va_new_email");
3089
3090         gtk_window_set_title (GTK_WINDOW (window), subject);
3091
3092 }
3093
3094 static void  
3095 subject_field_changed (GtkEditable *editable, 
3096                        ModestMsgEditWindow *window)
3097 {
3098         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3099         update_window_title (window);
3100         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3101         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3102         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3103 }
3104
3105 static void  
3106 subject_field_insert_text (GtkEditable *editable, 
3107                            gchar *new_text,
3108                            gint new_text_length,
3109                            gint *position,
3110                            ModestMsgEditWindow *window)
3111 {
3112         GString *result = g_string_new ("");
3113         gchar *current;
3114         gint result_len = 0;
3115         const gchar *entry_text = NULL;
3116         gint old_length;
3117
3118         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3119         old_length = g_utf8_strlen (entry_text, -1);
3120
3121         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3122                 gunichar c = g_utf8_get_char_validated (current, 8);
3123                 /* Invalid unichar, stop */
3124                 if (c == -1)
3125                         break;
3126                 /* a bullet */
3127                 if (c == 0x2022)
3128                         continue;
3129                 result = g_string_append_unichar (result, c);
3130                 result_len++;
3131         }
3132
3133         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3134                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3135                 if (result_len > 0)
3136                 {
3137                         /* Prevent endless recursion */
3138                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3139                         g_signal_emit_by_name (editable, "insert-text", 
3140                                                (gpointer) result->str, (gpointer) result->len,
3141                                                (gpointer) position, (gpointer) window);
3142                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3143                 }
3144         }
3145
3146         if (result_len + old_length > 1000) {
3147                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3148                                                 dgettext("hildon-common-strings",
3149                                                          "ckdg_ib_maximum_characters_reached"));
3150         }
3151         
3152         g_string_free (result, TRUE);
3153 }