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