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