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