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