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