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