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