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