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