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