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