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