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