* make modest_text_utils_get_display_date return a ptr to
[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_priority (header);
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_INVALID)
972                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
973                         
974                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
975                                 priv->draft_msg = g_object_ref(msg);
976                         if (type == TNY_FOLDER_TYPE_OUTBOX)
977                                 priv->outbox_msg = g_object_ref(msg);
978                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
979                 }
980                 g_object_unref (msg_folder);
981         }
982 }
983
984 static void
985 menu_tool_button_clicked_popup (GtkMenuToolButton *item,
986                                 gpointer data)
987 {
988         GList *item_children, *node;
989         GtkWidget *bin_child;
990
991         bin_child = gtk_bin_get_child (GTK_BIN(item));
992
993         item_children = gtk_container_get_children (GTK_CONTAINER (bin_child));
994         
995         for (node = item_children; node != NULL; node = g_list_next (node)) {
996                 if (GTK_IS_TOGGLE_BUTTON (node->data)) {
997                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
998                 }
999         }
1000         g_list_free (item_children);
1001 }
1002
1003 static void
1004 menu_tool_button_dont_expand (GtkMenuToolButton *item)
1005 {
1006         GtkWidget *box;
1007         GList *item_children, *node;
1008
1009         box = gtk_bin_get_child (GTK_BIN (item));
1010         gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
1011         item_children = gtk_container_get_children (GTK_CONTAINER (box));
1012         
1013         for (node = item_children; node != NULL; node = g_list_next (node)) {
1014                 gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START);
1015                 if (GTK_IS_TOGGLE_BUTTON (node->data))
1016                         gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5);
1017                 else if (GTK_IS_BUTTON (node->data))
1018                         gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5);
1019         }
1020         g_list_free (item_children);
1021 }
1022
1023
1024 static void
1025 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1026 {
1027         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1028         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1029         GtkWidget *placeholder;
1030         GtkWidget *tool_item;
1031         gint insert_index;
1032         gchar size_text[5];
1033         gint size_index;
1034         gint font_index;
1035         GtkWidget *sizes_menu;
1036         GtkWidget *fonts_menu;
1037         GSList *radio_group = NULL;
1038         GSList *node = NULL;
1039         gchar *markup;
1040
1041         /* Toolbar */
1042         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1043         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1044
1045         /* Font color placeholder */
1046         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1047         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1048
1049         /* font color */
1050         tool_item = GTK_WIDGET (gtk_tool_item_new ());
1051         priv->font_color_button = hildon_color_button_new ();
1052         GTK_WIDGET_UNSET_FLAGS (tool_item, GTK_CAN_FOCUS);
1053         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1054         gtk_container_add (GTK_CONTAINER (tool_item), priv->font_color_button);
1055         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1056         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1057         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1058         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1059                                   "notify::color", 
1060                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1061                                   window);
1062
1063         /* Font size and face placeholder */
1064         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1065         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1066         /* font_size */
1067         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1068         priv->size_tool_button_label = gtk_label_new (NULL);
1069         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1070         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1071                               size_text,"</span>", NULL);
1072         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1073         g_free (markup);
1074         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label);
1075         sizes_menu = gtk_menu_new ();
1076         priv->size_items_group = NULL;
1077         radio_group = NULL;
1078         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1079                 GtkWidget *size_menu_item;
1080
1081                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1082                 size_menu_item = gtk_radio_menu_item_new_with_label (radio_group, size_text);
1083                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (size_menu_item));
1084                 gtk_menu_shell_append (GTK_MENU_SHELL (sizes_menu), size_menu_item);
1085                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (size_menu_item), (wp_font_size[size_index] == 12));
1086                 gtk_widget_show (size_menu_item);
1087
1088                 priv->size_items_group = g_slist_prepend (priv->size_items_group, size_menu_item);
1089                         
1090         }
1091
1092         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1093                 GtkWidget *item = (GtkWidget *) node->data;
1094                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_size_change),
1095                                   window);
1096         }
1097
1098         priv->size_items_group = g_slist_reverse (priv->size_items_group);
1099         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), sizes_menu);
1100         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1101         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1102         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1103         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1104         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1105         priv->font_size_toolitem = tool_item;
1106
1107         /* font face */
1108         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1109         priv->font_tool_button_label = gtk_label_new (NULL);
1110         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1111         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1112         g_free(markup);
1113         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label);
1114         fonts_menu = gtk_menu_new ();
1115         priv->font_items_group = NULL;
1116         radio_group = NULL;
1117         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1118                 GtkWidget *font_menu_item;
1119                 GtkWidget *child_label;
1120
1121                 font_menu_item = gtk_radio_menu_item_new_with_label (radio_group, "");
1122                 child_label = gtk_bin_get_child (GTK_BIN (font_menu_item));
1123                 markup = g_strconcat ("<span font_family='", wp_get_font_name (font_index),"'>", 
1124                                       wp_get_font_name (font_index), "</span>", NULL);
1125                 gtk_label_set_markup (GTK_LABEL (child_label), markup);
1126                 g_free (markup);
1127                 
1128                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (font_menu_item));
1129                 gtk_menu_shell_append (GTK_MENU_SHELL (fonts_menu), font_menu_item);
1130                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (font_menu_item), (font_index == DEFAULT_FONT));
1131                 gtk_widget_show (font_menu_item);
1132
1133                 priv->font_items_group = g_slist_prepend (priv->font_items_group, font_menu_item);
1134                         
1135         }
1136         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1137                 GtkWidget *item = (GtkWidget *) node->data;
1138                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_font_change),
1139                                   window);
1140         }
1141         priv->font_items_group = g_slist_reverse (priv->font_items_group);
1142         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), fonts_menu);
1143         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1144         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1145         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1146         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1147         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1148         priv->font_face_toolitem = tool_item;
1149
1150         /* Set expand and homogeneous for remaining items */
1151         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend");
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/ActionsBold");
1155         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1156         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1157         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1158         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1159         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1160
1161         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1162            will not show the tool items added to the placeholders) */
1163         gtk_widget_show_all (parent_priv->toolbar);
1164
1165         /* Set the no show all *after* showing all items. We do not
1166            want the toolbar to be shown with a show all because it
1167            could go agains the gconf setting regarding showing or not
1168            the toolbar of the editor window */
1169         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1170 }
1171
1172
1173
1174 ModestWindow*
1175 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich)
1176 {
1177         GObject *obj;
1178         ModestWindowPrivate *parent_priv;
1179         ModestMsgEditWindowPrivate *priv;
1180         GtkActionGroup *action_group;
1181         GError *error = NULL;
1182         GdkPixbuf *window_icon = NULL;
1183         GtkAction *action;
1184         ModestConf *conf;
1185         ModestPair *account_pair = NULL;
1186         ModestDimmingRulesGroup *menu_rules_group = NULL;
1187         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1188         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1189
1190         g_return_val_if_fail (msg, NULL);
1191         g_return_val_if_fail (account_name, NULL);
1192         
1193         obj = g_object_new(MODEST_TYPE_MSG_EDIT_WINDOW, NULL);
1194
1195         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1196         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1197
1198         parent_priv->ui_manager = gtk_ui_manager_new();
1199         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
1200         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
1201
1202         /* Add common actions */
1203         gtk_action_group_add_actions (action_group,
1204                                       modest_msg_edit_action_entries,
1205                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
1206                                       obj);
1207         gtk_action_group_add_toggle_actions (action_group,
1208                                              modest_msg_edit_toggle_action_entries,
1209                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
1210                                              obj);
1211         gtk_action_group_add_radio_actions (action_group,
1212                                             modest_msg_edit_alignment_radio_action_entries,
1213                                             G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries),
1214                                             GTK_JUSTIFY_LEFT,
1215                                             G_CALLBACK (modest_ui_actions_on_change_justify),
1216                                             obj);
1217         gtk_action_group_add_radio_actions (action_group,
1218                                             modest_msg_edit_zoom_action_entries,
1219                                             G_N_ELEMENTS (modest_msg_edit_zoom_action_entries),
1220                                             100,
1221                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
1222                                             obj);
1223         gtk_action_group_add_radio_actions (action_group,
1224                                             modest_msg_edit_priority_action_entries,
1225                                             G_N_ELEMENTS (modest_msg_edit_priority_action_entries),
1226                                             0,
1227                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority),
1228                                             obj);
1229         gtk_action_group_add_radio_actions (action_group,
1230                                             modest_msg_edit_file_format_action_entries,
1231                                             G_N_ELEMENTS (modest_msg_edit_file_format_action_entries),
1232                                             modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL),
1233                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format),
1234                                             obj);
1235         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
1236         g_object_unref (action_group);
1237
1238         /* Load the UI definition */
1239         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml",
1240                                          &error);
1241         if (error != NULL) {
1242                 g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message);
1243                 g_clear_error (&error);
1244         }
1245
1246         /* Add accelerators */
1247         gtk_window_add_accel_group (GTK_WINDOW (obj), 
1248                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
1249
1250         /* Menubar. Update the state of some toggles */
1251         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
1252         conf = modest_runtime_get_conf ();
1253         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1254                                             "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
1255         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1256                                       modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR, NULL));
1257         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1258                                             "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu");
1259         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1260                                       modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
1261
1262         hildon_window_set_menu (HILDON_WINDOW (obj), GTK_MENU (parent_priv->menubar));
1263
1264         /* Init window */
1265         init_window (MODEST_MSG_EDIT_WINDOW(obj));
1266
1267         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1268                 
1269         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1270
1271         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1272         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1273
1274         account_pair = modest_pair_list_find_by_first_as_string (priv->from_field_protos, account_name);
1275         if (account_pair != NULL)
1276                 modest_combo_box_set_active_id (MODEST_COMBO_BOX (priv->from_field), account_pair->first);
1277
1278         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new ();
1279         menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
1280         toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
1281         clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
1282         /* Add common dimming rules */
1283         modest_dimming_rules_group_add_rules (menu_rules_group, 
1284                                               modest_msg_edit_window_menu_dimming_entries,
1285                                               G_N_ELEMENTS (modest_msg_edit_window_menu_dimming_entries),
1286                                               MODEST_WINDOW (obj));
1287         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1288                                               modest_msg_edit_window_toolbar_dimming_entries,
1289                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1290                                               MODEST_WINDOW (obj));
1291         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_button,
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_size_toolitem,
1295                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1296                                                     MODEST_WINDOW (obj));
1297         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1298                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1299                                                     MODEST_WINDOW (obj));
1300         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
1301                                               modest_msg_edit_window_clipboard_dimming_entries,
1302                                               G_N_ELEMENTS (modest_msg_edit_window_clipboard_dimming_entries),
1303                                               MODEST_WINDOW (obj));
1304         /* Insert dimming rules group for this window */
1305         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
1306         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1307         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1308         /* Checks the dimming rules */
1309         g_object_unref (menu_rules_group);
1310         g_object_unref (toolbar_rules_group);
1311         g_object_unref (clipboard_rules_group);
1312
1313         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1314
1315         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1316
1317         /* Set window icon */
1318         window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON);
1319         if (window_icon) {
1320                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
1321                 g_object_unref (window_icon);
1322         }
1323
1324         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1325         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
1326
1327         /* Dim at start clipboard actions */
1328         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CutMenu");
1329         gtk_action_set_sensitive (action, FALSE);
1330         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CopyMenu");
1331         gtk_action_set_sensitive (action, FALSE);
1332         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/RemoveAttachmentsMenu");
1333         gtk_action_set_sensitive (action, FALSE);
1334
1335         /* set initial state of cc and bcc */
1336         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewCcFieldMenu");
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_CC, NULL));
1339         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewBccFieldMenu");
1340         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1341                                                modest_conf_get_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, NULL));
1342
1343         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
1344         priv->update_caption_visibility = TRUE;
1345
1346         modest_msg_edit_window_reset_modified (MODEST_MSG_EDIT_WINDOW (obj));
1347
1348         /* Track account-removed signal, this window should be closed
1349            in the case we're creating a mail associated to the account
1350            that is deleted */
1351         priv->account_removed_handler_id = 
1352                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1353                                   "account_removed",
1354                                   G_CALLBACK(on_account_removed),
1355                                   obj);
1356         
1357         return (ModestWindow*) obj;
1358 }
1359
1360 static gint
1361 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1362 {
1363         GString **string_buffer = (GString **) user_data;
1364
1365         *string_buffer = g_string_append (*string_buffer, buffer);
1366    
1367         return 0;
1368 }
1369
1370 /**
1371  * @result: A new string which should be freed with g_free().
1372  */
1373 static gchar *
1374 get_formatted_data (ModestMsgEditWindow *edit_window)
1375 {
1376         ModestMsgEditWindowPrivate *priv;
1377         GString *string_buffer = g_string_new ("");
1378         
1379         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1380
1381         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1382
1383         return g_string_free (string_buffer, FALSE);
1384                                                                         
1385 }
1386
1387 MsgData * 
1388 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1389 {
1390         MsgData *data;
1391         const gchar *account_name;
1392         ModestMsgEditWindowPrivate *priv;
1393         
1394         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1395
1396         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1397                                                                         
1398         account_name = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
1399         g_return_val_if_fail (account_name, NULL);
1400         
1401         
1402         /* don't free these (except from) */
1403         data = g_slice_new0 (MsgData);
1404         data->from    =  modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(),
1405                                                              account_name);
1406         data->account_name = g_strdup (account_name);
1407         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1408         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1409         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1410         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1411         if (priv->draft_msg) {
1412                 data->draft_msg = g_object_ref (priv->draft_msg);
1413         } else if (priv->outbox_msg) {
1414                 data->draft_msg = g_object_ref (priv->outbox_msg);
1415         } else {
1416                 data->draft_msg = NULL;
1417         }
1418
1419         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1420         GtkTextIter b, e;
1421         gtk_text_buffer_get_bounds (buf, &b, &e);
1422         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1423
1424         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1425                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1426         else
1427                 data->html_body = NULL;
1428
1429         /* deep-copy the data */
1430         GList *cursor = priv->attachments;
1431         data->attachments = NULL;
1432         while (cursor) {
1433                 if (!(TNY_IS_MIME_PART(cursor->data))) {
1434                         g_warning ("strange data in attachment list");
1435                         cursor = g_list_next (cursor);
1436                         continue;
1437                 }
1438                 data->attachments = g_list_append (data->attachments,
1439                                                    g_object_ref (cursor->data));
1440                 cursor = g_list_next (cursor);
1441         }
1442
1443         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1444         cursor = priv->images;
1445         data->images = NULL;
1446         while (cursor) {
1447                 const gchar *cid;
1448                 if (!(TNY_IS_MIME_PART(cursor->data))) {
1449                         g_warning ("strange data in attachment list");
1450                         cursor = g_list_next (cursor);
1451                         continue;
1452                 }
1453                 cid = tny_mime_part_get_content_id (cursor->data);
1454                 if (cid) {                      
1455                         gchar *image_tag_id;
1456                         GtkTextTag *image_tag;
1457                         GtkTextIter iter;
1458                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1459                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1460                         g_free (image_tag_id);
1461                         
1462                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1463                         if (image_tag && 
1464                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1465                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1466                                 data->images = g_list_append (data->images,
1467                                                               g_object_ref (cursor->data));
1468                 }
1469                 cursor = g_list_next (cursor);
1470         }
1471         
1472         data->priority_flags = priv->priority_flags;
1473
1474         return data;
1475 }
1476
1477
1478 static void
1479 unref_gobject (GObject *obj, gpointer data)
1480 {
1481         if (!G_IS_OBJECT(obj))
1482                 return;
1483         g_object_unref (obj);
1484 }
1485
1486 void 
1487 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1488                                                       MsgData *data)
1489 {
1490         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1491
1492         if (!data)
1493                 return;
1494
1495         g_free (data->to);
1496         g_free (data->cc);
1497         g_free (data->bcc);
1498         g_free (data->subject);
1499         g_free (data->plain_body);
1500         g_free (data->html_body);
1501         g_free (data->account_name);
1502         
1503         if (data->draft_msg != NULL) {
1504                 g_object_unref (data->draft_msg);
1505                 data->draft_msg = NULL;
1506         }
1507         
1508         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1509         g_list_free (data->attachments);
1510         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1511         g_list_free (data->images);
1512         
1513         g_slice_free (MsgData, data);
1514 }
1515
1516 ModestMsgEditFormat
1517 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1518 {
1519         gboolean rich_text;
1520         ModestMsgEditWindowPrivate *priv = NULL;
1521         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1522
1523         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1524
1525         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1526         if (rich_text)
1527                 return MODEST_MSG_EDIT_FORMAT_HTML;
1528         else
1529                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1530 }
1531
1532 void
1533 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1534                                    ModestMsgEditFormat format)
1535 {
1536         ModestMsgEditWindowPrivate *priv;
1537
1538         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1539         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1540
1541         switch (format) {
1542         case MODEST_MSG_EDIT_FORMAT_HTML:
1543                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1544                 break;
1545         case MODEST_MSG_EDIT_FORMAT_TEXT:
1546                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1547                 break;
1548         default:
1549                 g_return_if_reached ();
1550         }
1551 }
1552
1553 ModestMsgEditFormatState *
1554 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1555 {
1556         ModestMsgEditFormatState *format_state = NULL;
1557         ModestMsgEditWindowPrivate *priv;
1558         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1559
1560         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
1561         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1562
1563         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
1564
1565         format_state = g_new0 (ModestMsgEditFormatState, 1);
1566         format_state->bold = buffer_format->bold&0x1;
1567         format_state->italics = buffer_format->italic&0x1;
1568         format_state->bullet = buffer_format->bullet&0x1;
1569         format_state->color = buffer_format->color;
1570         format_state->font_size = buffer_format->font_size;
1571         format_state->font_family = wp_get_font_name (buffer_format->font);
1572         format_state->justification = buffer_format->justification;
1573         g_free (buffer_format);
1574
1575         return format_state;
1576  
1577 }
1578
1579 void
1580 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
1581                                          const ModestMsgEditFormatState *format_state)
1582 {
1583         ModestMsgEditWindowPrivate *priv;
1584         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1585         WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1);
1586         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1587         g_return_if_fail (format_state != NULL);
1588
1589         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1590         gtk_widget_grab_focus (priv->msg_body);
1591         buffer_format->bold = (format_state->bold != FALSE);
1592         buffer_format->italic = (format_state->italics != FALSE);
1593         buffer_format->color = format_state->color;
1594         buffer_format->font_size = format_state->font_size;
1595         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
1596         buffer_format->justification = format_state->justification;
1597         buffer_format->bullet = format_state->bullet;
1598
1599         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
1600
1601         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
1602         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
1603         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
1604         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
1605         buffer_format->cs.font = (buffer_format->font != current_format->font);
1606         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
1607         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
1608
1609         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
1610         if (buffer_format->cs.bold) {
1611                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD, (gpointer) (buffer_format->bold&0x1));
1612         }
1613         if (buffer_format->cs.italic) {
1614                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC, (gpointer) (buffer_format->italic&0x1));
1615         }
1616         if (buffer_format->cs.color) {
1617                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) (&(buffer_format->color)));
1618         }
1619         if (buffer_format->cs.font_size) {
1620                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE, (gpointer) (buffer_format->font_size));
1621         }
1622         if (buffer_format->cs.justification) {
1623                 switch (buffer_format->justification) {
1624                 case GTK_JUSTIFY_LEFT:
1625                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT, (gpointer) TRUE);
1626                         break;
1627                 case GTK_JUSTIFY_CENTER:
1628                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER, (gpointer) TRUE);
1629                         break;
1630                 case GTK_JUSTIFY_RIGHT:
1631                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT, (gpointer) TRUE);
1632                         break;
1633                 default:
1634                         break;
1635                 }
1636                         
1637         }
1638         if (buffer_format->cs.font) {
1639                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, (gpointer) (buffer_format->font));
1640         }
1641         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
1642         if (buffer_format->cs.bullet) {
1643           wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET, (gpointer) ((buffer_format->bullet)?1:0));
1644         }
1645 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
1646
1647         g_free (current_format);
1648
1649 }
1650
1651 static void
1652 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
1653 {
1654         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1655         GtkAction *action;
1656         ModestWindowPrivate *parent_priv;
1657         ModestMsgEditWindowPrivate *priv;
1658         GtkWidget *new_size_menuitem;
1659         GtkWidget *new_font_menuitem;
1660         
1661         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1662         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1663
1664         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
1665                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu");
1666                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1667                         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1668         } else {
1669                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatPlainTextMenu");
1670                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1671                         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1672         }
1673
1674         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1675
1676         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1677         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
1678
1679         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1680         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
1681
1682 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
1683 /*      modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
1684
1685         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
1686                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
1687                                          window);
1688         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
1689         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
1690                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
1691                                            window);
1692
1693         new_size_menuitem = GTK_WIDGET ((g_slist_nth (priv->size_items_group, 
1694                                                       buffer_format->font_size))->data);
1695         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_size_menuitem))) {
1696                 GtkWidget *label;
1697                 gchar *markup;
1698
1699                 label = gtk_bin_get_child (GTK_BIN (new_size_menuitem));
1700                 markup = g_strconcat ("<span font_family='Serif'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
1701                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1702                 g_free (markup);
1703                 g_signal_handlers_block_by_func (G_OBJECT (new_size_menuitem),
1704                                                  G_CALLBACK (modest_msg_edit_window_size_change),
1705                                                  window);
1706                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_size_menuitem), TRUE);
1707                 g_signal_handlers_unblock_by_func (G_OBJECT (new_size_menuitem),
1708                                                    G_CALLBACK (modest_msg_edit_window_size_change),
1709                                                    window);
1710         }
1711
1712         new_font_menuitem = GTK_WIDGET ((g_slist_nth (priv->font_items_group, 
1713                                                       buffer_format->font))->data);
1714         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_font_menuitem))) {
1715                 GtkWidget *label;
1716                 gchar *markup;
1717
1718                 label = gtk_bin_get_child (GTK_BIN (new_font_menuitem));
1719                 markup = g_strconcat ("<span font_family='", gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
1720                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1721                 g_free (markup);
1722                 g_signal_handlers_block_by_func (G_OBJECT (new_font_menuitem),
1723                                                  G_CALLBACK (modest_msg_edit_window_font_change),
1724                                                  window);
1725                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_font_menuitem), TRUE);
1726                 g_signal_handlers_unblock_by_func (G_OBJECT (new_font_menuitem),
1727                                                    G_CALLBACK (modest_msg_edit_window_font_change),
1728                                                    window);
1729         }
1730
1731         g_free (buffer_format);
1732
1733 }
1734
1735 #ifdef MODEST_HILDON_VERSION_0
1736 void
1737 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1738 {
1739         
1740         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1741         ModestMsgEditWindowPrivate *priv;
1742         GtkWidget *dialog = NULL;
1743         gint response;
1744         GdkColor *new_color = NULL;
1745
1746         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1747         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1748         
1749         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1750         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color));
1751         g_free (buffer_format);
1752
1753         response = gtk_dialog_run (GTK_DIALOG (dialog));
1754         switch (response) {
1755         case GTK_RESPONSE_OK: {
1756                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1757         }
1758                 break;
1759         default:
1760                 break;
1761         }
1762         gtk_widget_destroy (dialog);
1763         
1764         if (new_color != NULL) {
1765                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
1766                 /* FIXME: free new_color here? */
1767         }
1768 }
1769 #else 
1770 void
1771 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1772 {
1773         
1774         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1775         ModestMsgEditWindowPrivate *priv;
1776         GtkWidget *dialog = NULL;
1777         gint response;
1778         const GdkColor *new_color = NULL;
1779         GdkColor col;
1780
1781         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1782         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1783                 
1784         dialog = hildon_color_chooser_new ();
1785         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
1786         g_free (buffer_format);
1787
1788         response = gtk_dialog_run (GTK_DIALOG (dialog));
1789         switch (response) {
1790         case GTK_RESPONSE_OK: {
1791                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
1792                 new_color = &col;
1793         }
1794                 
1795                 break;
1796         default:
1797                 break;
1798         }
1799         gtk_widget_destroy (dialog);
1800         if (new_color != NULL)
1801                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1802                                               (gpointer) new_color);
1803 }
1804
1805 #endif /*!MODEST_HILDON_VERSION_0*/
1806
1807 void
1808 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
1809 {
1810         
1811         ModestMsgEditWindowPrivate *priv;
1812         GtkWidget *dialog = NULL;
1813         gint response;
1814         GdkColor *old_color = NULL;
1815         const GdkColor *new_color = NULL;
1816         
1817         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1818         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
1819         
1820 #ifdef MODEST_HILDON_VERSION_0  
1821         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1822         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color);
1823 #else
1824         dialog = hildon_color_chooser_new ();
1825         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
1826 #endif /*MODEST_HILDON_VERSION_9*/              
1827
1828         response = gtk_dialog_run (GTK_DIALOG (dialog));
1829         switch (response) {
1830         case GTK_RESPONSE_OK: {
1831 #ifdef MODEST_HILDON_VERSION_0
1832                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1833 #else
1834                 GdkColor col;
1835                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
1836                 new_color = &col;
1837 #endif /*MODEST_HILDON_VERSION_0*/
1838           }
1839                 break;
1840         default:
1841                 break;
1842         }
1843         gtk_widget_destroy (dialog);
1844
1845         if (new_color != NULL)
1846                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color);
1847
1848 }
1849
1850 static TnyStream* create_stream_for_uri (const gchar* uri)
1851 {
1852         if (!uri)
1853                 return NULL;
1854                 
1855         TnyStream *result = NULL;
1856
1857         GnomeVFSHandle *handle = NULL;
1858         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
1859         if (test == GNOME_VFS_OK) {
1860                 /* Create the tinymail stream: */
1861                 /* Presumably tinymai will call gnome_vfs_close (handle) later. */
1862                 result = TNY_STREAM (tny_vfs_stream_new (handle));
1863         }
1864         
1865         return result;
1866 }
1867
1868 void
1869 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
1870 {
1871         
1872         ModestMsgEditWindowPrivate *priv;
1873         GtkWidget *dialog = NULL;
1874         gint response = 0;
1875         GSList *uris = NULL;
1876         GSList *uri_node = NULL;
1877         
1878         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1879         
1880         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
1881         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
1882         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
1883
1884         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
1885
1886         response = gtk_dialog_run (GTK_DIALOG (dialog));
1887         switch (response) {
1888         case GTK_RESPONSE_OK:
1889                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
1890                 break;
1891         default:
1892                 break;
1893         }
1894         gtk_widget_destroy (dialog);
1895
1896         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
1897                 const gchar *uri;
1898                 GnomeVFSHandle *handle = NULL;
1899                 GnomeVFSResult result;
1900                 GtkTextIter position;
1901                 GtkTextMark *insert_mark;
1902
1903                 uri = (const gchar *) uri_node->data;
1904                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
1905                 if (result == GNOME_VFS_OK) {
1906                         GdkPixbuf *pixbuf;
1907                         GnomeVFSFileInfo info;
1908                         gchar *filename, *basename, *escaped_filename;
1909                         TnyMimePart *mime_part;
1910                         gchar *content_id;
1911                         const gchar *mime_type = NULL;
1912                         GnomeVFSURI *vfs_uri;
1913
1914                         vfs_uri = gnome_vfs_uri_new (uri);
1915
1916                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
1917                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
1918                         g_free (escaped_filename);
1919                         gnome_vfs_uri_unref (vfs_uri);
1920
1921                         if (gnome_vfs_get_file_info (uri, &info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
1922                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
1923                             == GNOME_VFS_OK)
1924                                 mime_type = gnome_vfs_file_info_get_mime_type (&info);
1925
1926                         mime_part = tny_platform_factory_new_mime_part
1927                                 (modest_runtime_get_platform_factory ());
1928                                 
1929                         TnyStream *stream = create_stream_for_uri (uri);
1930                         tny_mime_part_construct_from_stream (mime_part, stream, mime_type);
1931                         
1932                         content_id = g_strdup_printf ("%d", priv->last_cid);
1933                         tny_mime_part_set_content_id (mime_part, content_id);
1934                         g_free (content_id);
1935                         priv->last_cid++;
1936                         
1937                         basename = g_path_get_basename (filename);
1938                         tny_mime_part_set_filename (mime_part, basename);
1939                         g_free (basename);
1940
1941                         pixbuf = pixbuf_from_stream (stream, mime_type);
1942                         
1943                         if (pixbuf != NULL) {
1944                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
1945                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
1946                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
1947                         } 
1948
1949                         priv->images = g_list_prepend (priv->images, mime_part);
1950                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1951                         g_free (filename);
1952
1953                 }
1954         }
1955
1956
1957 }
1958
1959 void
1960 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
1961 {
1962         
1963         ModestMsgEditWindowPrivate *priv;
1964         GtkWidget *dialog = NULL;
1965         gint response = 0;
1966         GSList *uris = NULL;
1967         GSList *uri_node;
1968         
1969         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1970         
1971         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
1972         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
1973         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
1974
1975         response = gtk_dialog_run (GTK_DIALOG (dialog));
1976         switch (response) {
1977         case GTK_RESPONSE_OK:
1978                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
1979                 break;
1980         default:
1981                 break;
1982         }
1983         gtk_widget_destroy (dialog);
1984
1985         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
1986                 const gchar *uri = (const gchar *) uri_node->data;
1987                 modest_msg_edit_window_attach_file_one (window, uri);
1988         }
1989         g_slist_foreach (uris, (GFunc) g_free, NULL);
1990         g_slist_free (uris);
1991 }
1992
1993 void
1994 modest_msg_edit_window_attach_file_one (
1995                 ModestMsgEditWindow *window,
1996                 const gchar *uri)
1997 {
1998         g_return_if_fail (window);
1999         g_return_if_fail (uri);
2000                 
2001         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2002         
2003         
2004         GnomeVFSHandle *handle = NULL;
2005         GnomeVFSResult result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2006         if (result == GNOME_VFS_OK) {
2007                 TnyMimePart *mime_part;
2008                 TnyStream *stream;
2009                 const gchar *mime_type = NULL;
2010                 gchar *basename;
2011                 gchar *escaped_filename;
2012                 gchar *filename;
2013                 gchar *content_id;
2014                 GnomeVFSFileInfo info;
2015                 GnomeVFSURI *vfs_uri;
2016
2017                 vfs_uri = gnome_vfs_uri_new (uri);
2018                 
2019
2020                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2021                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2022                 g_free (escaped_filename);
2023                 gnome_vfs_uri_unref (vfs_uri);
2024                 
2025                 if (gnome_vfs_get_file_info (uri, 
2026                                              &info, 
2027                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
2028                                              GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE)
2029                     == GNOME_VFS_OK)
2030                         mime_type = gnome_vfs_file_info_get_mime_type (&info);
2031                 mime_part = tny_platform_factory_new_mime_part
2032                         (modest_runtime_get_platform_factory ());
2033                 stream = TNY_STREAM (tny_vfs_stream_new (handle));
2034                 
2035                 tny_mime_part_construct_from_stream (mime_part, stream, mime_type);
2036                 
2037                 content_id = g_strdup_printf ("%d", priv->last_cid);
2038                 tny_mime_part_set_content_id (mime_part, content_id);
2039                 g_free (content_id);
2040                 priv->last_cid++;
2041                 
2042                 basename = g_path_get_basename (filename);
2043                 tny_mime_part_set_filename (mime_part, basename);
2044                 g_free (basename);
2045                 
2046                 priv->attachments = g_list_prepend (priv->attachments, mime_part);
2047                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2048                                                         mime_part);
2049                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2050                 gtk_widget_show_all (priv->attachments_caption);
2051                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2052                 g_free (filename);
2053         }
2054 }
2055
2056 void
2057 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2058                                           GList *att_list)
2059 {
2060         ModestMsgEditWindowPrivate *priv;
2061         gboolean clean_list = FALSE;
2062
2063         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2064         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2065
2066         if (att_list == NULL) {
2067                 att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2068                 clean_list = TRUE;
2069         }
2070
2071         if (att_list == NULL) {
2072                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2073         } else {
2074                 GtkWidget *confirmation_dialog = NULL;
2075                 gboolean dialog_response;
2076                 GList *node;
2077                 gchar *message = NULL;
2078                 gchar *filename = NULL;
2079
2080                 if (att_list->next == NULL) {
2081                         if (TNY_IS_MSG (att_list->data)) {
2082                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (att_list->data));
2083                                 if (header) {
2084                                         filename = g_strdup (tny_header_get_subject (header));
2085                                         g_object_unref (header);
2086                                 }
2087                                 if (filename == NULL) {
2088                                         filename = g_strdup (_("mail_va_no_subject"));
2089                                 }
2090                         } else {
2091                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (att_list->data)));
2092                         }
2093                 } else {
2094                         filename = g_strdup ("");
2095                 }
2096                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments",
2097                                                     att_list->next == NULL), filename);
2098                 g_free (filename);
2099                 confirmation_dialog = hildon_note_new_confirmation (GTK_WINDOW (window), message);
2100                 g_free (message);
2101                 dialog_response = (gtk_dialog_run (GTK_DIALOG (confirmation_dialog))==GTK_RESPONSE_OK);
2102                 gtk_widget_destroy (confirmation_dialog);
2103                 if (!dialog_response) {
2104                         if (clean_list)
2105                                 g_list_free (att_list);
2106                         return;
2107                 }
2108                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_removing_attachment"));
2109
2110                 for (node = att_list; node != NULL; node = g_list_next (node)) {
2111                         TnyMimePart *mime_part = (TnyMimePart *) node->data;
2112                         const gchar *att_id;
2113                         priv->attachments = g_list_remove (priv->attachments, mime_part);
2114
2115                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2116                                                                    mime_part);
2117                         if (priv->attachments == NULL)
2118                                 gtk_widget_hide (priv->attachments_caption);
2119                         att_id = tny_mime_part_get_content_id (mime_part);
2120                         if (att_id != NULL)
2121                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2122                                                                  att_id);
2123                         g_object_unref (mime_part);
2124                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2125                 }
2126         }
2127
2128         if (clean_list)
2129                 g_list_free (att_list);
2130 }
2131
2132 static void
2133 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2134                                             gpointer userdata)
2135 {
2136         ModestMsgEditWindowPrivate *priv;
2137         GdkColor *new_color;
2138         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2139         
2140 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2141         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2142 #else 
2143         GdkColor col;
2144         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2145         new_color = &col;
2146 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2147
2148         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2149         
2150         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2151
2152 }
2153
2154 static void
2155 modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
2156                                     gpointer userdata)
2157 {
2158         ModestMsgEditWindowPrivate *priv;
2159         gint new_size_index;
2160         ModestMsgEditWindow *window;
2161         GtkWidget *label;
2162         
2163         window = MODEST_MSG_EDIT_WINDOW (userdata);
2164         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2165         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2166
2167         if (gtk_check_menu_item_get_active (menu_item)) {
2168                 gchar *markup;
2169                 WPTextBufferFormat format;
2170
2171                 memset (&format, 0, sizeof (format));
2172                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2173
2174                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2175                 
2176                 new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label)));
2177                 format.cs.font_size = TRUE;
2178                 format.cs.text_position = TRUE;
2179                 format.cs.font = TRUE;
2180                 format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE);
2181 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2182
2183                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2184                                                    (gpointer) wp_get_font_size_index (new_size_index, 12)))
2185                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2186                 
2187                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2188                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
2189                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2190                 g_free (markup);
2191         }
2192 }
2193
2194 static void
2195 modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
2196                                     gpointer userdata)
2197 {
2198         ModestMsgEditWindowPrivate *priv;
2199         gint new_font_index;
2200         ModestMsgEditWindow *window;
2201         GtkWidget *label;
2202         
2203         window = MODEST_MSG_EDIT_WINDOW (userdata);
2204         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2205         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2206
2207         if (gtk_check_menu_item_get_active (menu_item)) {
2208                 gchar *markup;
2209
2210                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2211                 
2212                 new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT);
2213
2214                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2215                                                    (gpointer) new_font_index))
2216                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2217                 
2218                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2219                     markup = g_strconcat ("<span font_family='",gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
2220                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2221                 g_free (markup);
2222         }
2223 }
2224
2225 static void
2226 modest_msg_edit_window_set_zoom (ModestWindow *window,
2227                                  gdouble zoom)
2228 {
2229         ModestMsgEditWindowPrivate *priv;
2230         ModestWindowPrivate *parent_priv;
2231         GtkRadioAction *zoom_radio_action;
2232      
2233         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2234
2235         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2236         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2237         priv->zoom_level = zoom;
2238         wp_text_buffer_set_font_scaling_factor (WP_TEXT_BUFFER (priv->text_buffer), zoom*DEFAULT_FONT_SCALE);
2239
2240         /* Zoom level menu options should be updated with the current zoom level */
2241         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2242         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
2243                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
2244 #ifdef MODEST_HAVE_HILDON0_WIDGETS
2245         /* FIXME: Not availible before Gtk 2.10 */
2246 #else
2247         gtk_radio_action_set_current_value (zoom_radio_action, (gint) (zoom*100.0+0.1));
2248 #endif
2249 }
2250
2251 static gdouble
2252 modest_msg_edit_window_get_zoom (ModestWindow *window)
2253 {
2254         ModestMsgEditWindowPrivate *priv;
2255      
2256         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), 1.0);
2257
2258         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2259         return priv->zoom_level;
2260 }
2261
2262 static gboolean
2263 zoom_allowed (ModestMsgEditWindow *window)
2264 {
2265         GtkWidget *focus;
2266
2267         focus = gtk_window_get_focus (GTK_WINDOW (window));
2268         return (focus != NULL && WP_IS_TEXT_VIEW (focus));
2269 }
2270
2271 static gboolean
2272 modest_msg_edit_window_zoom_plus (ModestWindow *window)
2273 {
2274         ModestWindowPrivate *parent_priv;
2275         GtkRadioAction *zoom_radio_action;
2276         GSList *group, *node;
2277
2278         /* First we check if the text view is focused. If not, zooming is not allowed */
2279         if (!zoom_allowed (MODEST_MSG_EDIT_WINDOW (window))) {
2280                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
2281                 return FALSE;
2282         }
2283
2284         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2285         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
2286                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
2287
2288         group = gtk_radio_action_get_group (zoom_radio_action);
2289
2290         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
2291                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
2292                 return FALSE;
2293         }
2294
2295         for (node = group; node != NULL; node = g_slist_next (node)) {
2296                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
2297                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
2298                         return TRUE;
2299                 }
2300         }
2301         return FALSE;
2302 }
2303
2304 static gboolean
2305 modest_msg_edit_window_zoom_minus (ModestWindow *window)
2306 {
2307         ModestWindowPrivate *parent_priv;
2308         GtkRadioAction *zoom_radio_action;
2309         GSList *group, *node;
2310
2311         /* First we check if the text view is focused. If not, zooming is not allowed */
2312         if (!zoom_allowed (MODEST_MSG_EDIT_WINDOW (window))) {
2313                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
2314                 return FALSE;
2315         }
2316
2317         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2318         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
2319                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
2320
2321         group = gtk_radio_action_get_group (zoom_radio_action);
2322
2323         for (node = group; node != NULL; node = g_slist_next (node)) {
2324                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
2325                         if (node->next != NULL) {
2326                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
2327                                 return TRUE;
2328                         } else
2329                                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
2330                         break;
2331                 }
2332         }
2333         return FALSE;
2334 }
2335
2336 static gboolean
2337 modest_msg_edit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2338 {
2339         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2340                 ModestWindowPrivate *parent_priv;
2341                 ModestWindowMgr *mgr;
2342                 gboolean is_fullscreen;
2343                 GtkAction *fs_toggle_action;
2344                 gboolean active;
2345
2346                 mgr = modest_runtime_get_window_mgr ();
2347                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2348
2349                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2350                 
2351                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2352                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2353                 if (is_fullscreen != active)
2354                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2355         }
2356
2357         return FALSE;
2358
2359 }
2360
2361 void
2362 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2363                                 gboolean show)
2364 {
2365         ModestMsgEditWindowPrivate *priv = NULL;
2366         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2367
2368         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2369         if (!priv->update_caption_visibility)
2370                 return;
2371
2372         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2373         if (show)
2374                 gtk_widget_show (priv->cc_caption);
2375         else
2376                 gtk_widget_hide (priv->cc_caption);
2377
2378         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2379 }
2380
2381 void
2382 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2383                                  gboolean show)
2384 {
2385         ModestMsgEditWindowPrivate *priv = NULL;
2386         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2387
2388         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2389         if (!priv->update_caption_visibility)
2390                 return;
2391
2392         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2393         if (show)
2394                 gtk_widget_show (priv->bcc_caption);
2395         else
2396                 gtk_widget_hide (priv->bcc_caption);
2397
2398         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2399 }
2400
2401 static void
2402 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2403                                          ModestRecptEditor *editor)
2404 {
2405         ModestMsgEditWindowPrivate *priv;
2406
2407         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2408         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2409         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2410
2411         if (editor == NULL) {
2412                 GtkWidget *view_focus;
2413                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2414
2415                 /* This code should be kept in sync with ModestRecptEditor. The
2416                    textview inside the recpt editor is the one that really gets the
2417                    focus. As it's inside a scrolled window, and this one inside the
2418                    hbox recpt editor inherits from, we'll need to go up in the 
2419                    hierarchy to know if the text view is part of the recpt editor
2420                    or if it's a different text entry */
2421
2422                 if (gtk_widget_get_parent (view_focus)) {
2423                         GtkWidget *first_parent;
2424
2425                         first_parent = gtk_widget_get_parent (view_focus);
2426                         if (gtk_widget_get_parent (first_parent) && 
2427                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2428                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2429                         }
2430                 }
2431
2432                 if (editor == NULL)
2433                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2434
2435         }
2436
2437         modest_address_book_select_addresses (editor);
2438
2439 }
2440
2441 void
2442 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2443 {
2444         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2445
2446         modest_msg_edit_window_open_addressbook (window, NULL);
2447 }
2448
2449 static void
2450 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2451                                      gboolean show_toolbar)
2452 {
2453         ModestWindowPrivate *parent_priv;
2454         const gchar *action_name;
2455         GtkAction *action;
2456         
2457         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2458         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2459
2460         /* We can not just use the code of
2461            modest_msg_edit_window_setup_toolbar because it has a
2462            mixture of both initialization and creation code. */
2463         if (show_toolbar)
2464                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2465         else
2466                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2467
2468         /* Update also the actions (to update the toggles in the
2469            menus), we have to do it manually because some other window
2470            of the same time could have changed it (remember that the
2471            toolbar fullscreen mode is shared by all the windows of the
2472            same type */
2473         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2474                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2475         else
2476                 action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2477         
2478         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2479         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2480                                                             show_toolbar);
2481
2482 }
2483
2484 void
2485 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2486                                            TnyHeaderFlags priority_flags)
2487 {
2488         ModestMsgEditWindowPrivate *priv;
2489         ModestWindowPrivate *parent_priv;
2490
2491         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2492
2493         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2494         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2495
2496         if (priv->priority_flags != priority_flags) {
2497                 GtkAction *priority_action = NULL;
2498
2499                 priv->priority_flags = priority_flags;
2500
2501                 switch (priority_flags) {
2502                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2503                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_high", GTK_ICON_SIZE_MENU);
2504                         gtk_widget_show (priv->priority_icon);
2505                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2506                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2507                         break;
2508                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2509                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_low", GTK_ICON_SIZE_MENU);
2510                         gtk_widget_show (priv->priority_icon);
2511                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2512                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2513                         break;
2514                 default:
2515                         gtk_widget_hide (priv->priority_icon);
2516                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
2517                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2518                         break;
2519                 }
2520                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2521                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2522         }
2523 }
2524
2525 void
2526 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2527                                         gint file_format)
2528 {
2529         ModestMsgEditWindowPrivate *priv;
2530         ModestWindowPrivate *parent_priv;
2531         gint current_format;
2532
2533         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2534
2535         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2536         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2537
2538         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2539                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2540
2541         if (current_format != file_format) {
2542                 switch (file_format) {
2543                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2544                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2545                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2546                         break;
2547                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2548                 {
2549                         GtkWidget *dialog;
2550                         gint response;
2551                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2552                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2553                         gtk_widget_destroy (dialog);
2554                         if (response == GTK_RESPONSE_OK) {
2555                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2556                         } else {
2557                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu"));
2558                                 modest_maemo_toggle_action_set_active_block_notify (action, TRUE);
2559                         }
2560                 }
2561                         break;
2562                 }
2563                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2564         }
2565 }
2566
2567 void
2568 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2569 {
2570         GtkWidget *dialog;
2571         ModestMsgEditWindowPrivate *priv;
2572         WPTextBufferFormat oldfmt, fmt;
2573         gint old_position = 0;
2574         gint response = 0;
2575         gint position = 0;
2576         gint font_size;
2577         GdkColor *color = NULL;
2578         gboolean bold, bold_set, italic, italic_set;
2579         gboolean underline, underline_set;
2580         gboolean strikethrough, strikethrough_set;
2581         gboolean position_set;
2582         gboolean font_size_set, font_set, color_set;
2583         gchar *font_name;
2584
2585         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2586         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2587         
2588         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2589
2590         /* First we get the currently selected font information */
2591         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2592         g_object_set (G_OBJECT (dialog), "font-scaling", priv->zoom_level, NULL);
2593
2594         switch (oldfmt.text_position) {
2595         case TEXT_POSITION_NORMAL:
2596                 old_position = 0;
2597                 break;
2598         case TEXT_POSITION_SUPERSCRIPT:
2599                 old_position = 1;
2600                 break;
2601         default:
2602                 old_position = -1;
2603                 break;
2604         }
2605
2606         g_object_set (G_OBJECT (dialog),
2607                       "bold", oldfmt.bold != FALSE,
2608                       "bold-set", !oldfmt.cs.bold,
2609                       "underline", oldfmt.underline != FALSE,
2610                       "underline-set", !oldfmt.cs.underline,
2611                       "italic", oldfmt.italic != FALSE,
2612                       "italic-set", !oldfmt.cs.italic,
2613                       "strikethrough", oldfmt.strikethrough != FALSE,
2614                       "strikethrough-set", !oldfmt.cs.strikethrough,
2615                       "color", &oldfmt.color,
2616                       "color-set", !oldfmt.cs.color,
2617                       "size", wp_font_size[oldfmt.font_size],
2618                       "size-set", !oldfmt.cs.font_size,
2619                       "position", old_position,
2620                       "position-set", !oldfmt.cs.text_position,
2621                       "family", wp_get_font_name (oldfmt.font),
2622                       "family-set", !oldfmt.cs.font,
2623                       NULL);
2624
2625         gtk_widget_show_all (dialog);
2626         response = gtk_dialog_run (GTK_DIALOG (dialog));
2627         if (response == GTK_RESPONSE_OK) {
2628
2629                 g_object_get( dialog,
2630                               "bold", &bold,
2631                               "bold-set", &bold_set,
2632                               "underline", &underline,
2633                               "underline-set", &underline_set,
2634                               "italic", &italic,
2635                               "italic-set", &italic_set,
2636                               "strikethrough", &strikethrough,
2637                               "strikethrough-set", &strikethrough_set,
2638                               "color", &color,
2639                               "color-set", &color_set,
2640                               "size", &font_size,
2641                               "size-set", &font_size_set,
2642                               "family", &font_name,
2643                               "family-set", &font_set,
2644                               "position", &position,
2645                               "position-set", &position_set,
2646                               NULL );
2647                 
2648         }       
2649
2650         if (response == GTK_RESPONSE_OK) {
2651                 memset(&fmt, 0, sizeof(fmt));
2652                 if (bold_set) {
2653                         fmt.bold = bold;
2654                         fmt.cs.bold = TRUE;
2655                 }
2656                 if (italic_set) {
2657                         fmt.italic = italic;
2658                         fmt.cs.italic = TRUE;
2659                 }
2660                 if (underline_set) {
2661                         fmt.underline = underline;
2662                         fmt.cs.underline = TRUE;
2663                 }
2664                 if (strikethrough_set) {
2665                         fmt.strikethrough = strikethrough;
2666                         fmt.cs.strikethrough = TRUE;
2667                 }
2668                 if (position_set) {
2669                         fmt.text_position =
2670                                 ( position == 0 )
2671                                 ? TEXT_POSITION_NORMAL
2672                                 : ( ( position == 1 )
2673                                     ? TEXT_POSITION_SUPERSCRIPT
2674                                     : TEXT_POSITION_SUBSCRIPT );
2675                         fmt.cs.text_position = TRUE;
2676                 }
2677                 if (color_set) {
2678                         fmt.color = *color;
2679                         fmt.cs.color = TRUE;
2680                 }
2681                 if (font_set) {
2682                         fmt.font = wp_get_font_index(font_name,
2683                                                      DEFAULT_FONT);
2684                         fmt.cs.font = TRUE;
2685                 }
2686                 g_free(font_name);
2687                 if (font_size_set) {
2688                         fmt.font_size = wp_get_font_size_index(
2689                                 font_size, DEFAULT_FONT_SIZE);
2690                         fmt.cs.font_size = TRUE;
2691                 }
2692                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2693                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2694         }
2695         gtk_widget_destroy (dialog);
2696         
2697         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2698 }
2699
2700 void
2701 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2702 {
2703         ModestMsgEditWindowPrivate *priv;
2704
2705         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2706         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2707         
2708         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
2709
2710         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2711
2712 }
2713
2714 void
2715 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
2716 {
2717         ModestMsgEditWindowPrivate *priv;
2718
2719         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2720         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2721         
2722         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
2723
2724         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2725
2726 }
2727
2728 static void  
2729 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
2730 {
2731         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2732
2733         priv->can_undo = can_undo;
2734 }
2735
2736 static void  
2737 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
2738 {
2739         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2740
2741         priv->can_redo = can_redo;
2742 }
2743
2744 gboolean            
2745 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
2746 {
2747         ModestMsgEditWindowPrivate *priv;
2748         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2749         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2750
2751         return priv->can_undo;
2752 }
2753
2754 gboolean            
2755 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
2756 {
2757         ModestMsgEditWindowPrivate *priv;
2758         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2759         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2760
2761         return priv->can_redo;
2762 }
2763
2764
2765 static void
2766 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
2767 {
2768         GtkTextIter iter;
2769         GtkTextIter match_start, match_end;
2770
2771         if (image_id == NULL)
2772                 return;
2773
2774         gtk_text_buffer_get_start_iter (buffer, &iter);
2775
2776         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
2777                 GSList *tags = gtk_text_iter_get_tags (&match_start);
2778                 GSList *node;
2779                 for (node = tags; node != NULL; node = g_slist_next (node)) {
2780                         GtkTextTag *tag = (GtkTextTag *) node->data;
2781                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
2782                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
2783                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
2784                                         gint offset;
2785                                         offset = gtk_text_iter_get_offset (&match_start);
2786                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
2787                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
2788                                 }
2789                         }
2790                 }
2791                 gtk_text_iter_forward_char (&iter);
2792         }
2793 }
2794
2795 gboolean
2796 message_is_empty (ModestMsgEditWindow *window)
2797 {
2798         ModestMsgEditWindowPrivate *priv = NULL;
2799
2800         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2801         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2802
2803         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
2804          * so we can ignore markup.
2805          */
2806         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
2807         gint count = 0;
2808         if (buf)
2809                 count = gtk_text_buffer_get_char_count (buf);
2810
2811         return count == 0;
2812 }
2813
2814 static gboolean
2815 msg_body_focus (GtkWidget *focus,
2816                 GdkEventFocus *event,
2817                 gpointer userdata)
2818 {
2819         
2820         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
2821         return FALSE;
2822 }
2823
2824 static void
2825 recpt_field_changed (GtkTextBuffer *buffer,
2826                   ModestMsgEditWindow *editor)
2827 {
2828         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2829 }
2830
2831 static void
2832 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
2833 {
2834         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
2835 }
2836
2837 void
2838 modest_msg_edit_window_reset_modified (ModestMsgEditWindow *editor)
2839 {
2840         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2841         GtkTextBuffer *buffer;
2842
2843         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2844         gtk_text_buffer_set_modified (buffer, FALSE);
2845         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2846         gtk_text_buffer_set_modified (buffer, FALSE);
2847         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2848         gtk_text_buffer_set_modified (buffer, FALSE);
2849         gtk_text_buffer_set_modified (priv->text_buffer, FALSE);
2850 }
2851
2852 gboolean
2853 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
2854 {
2855         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
2856         GtkTextBuffer *buffer;
2857
2858         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
2859         if (gtk_text_buffer_get_modified (buffer))
2860                 return TRUE;
2861         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
2862         if (gtk_text_buffer_get_modified (buffer))
2863                 return TRUE;
2864         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
2865         if (gtk_text_buffer_get_modified (buffer))
2866                 return TRUE;
2867         if (gtk_text_buffer_get_modified (priv->text_buffer))
2868                 return TRUE;
2869
2870         return FALSE;
2871 }
2872
2873
2874
2875
2876 gboolean
2877 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
2878 {
2879         ModestMsgEditWindowPrivate *priv = NULL;
2880         
2881         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2882         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2883
2884         /* check if there's no recipient added */
2885         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
2886             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
2887             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
2888                 /* no recipient contents, then select contacts */
2889                 modest_msg_edit_window_open_addressbook (window, NULL);
2890                 return FALSE;
2891         }
2892
2893         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
2894                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
2895                 return FALSE;
2896         }
2897         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
2898                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
2899                 return FALSE;
2900         }
2901         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
2902                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
2903                 return FALSE;
2904         }
2905
2906         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
2907             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
2908                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
2909
2910         return TRUE;
2911
2912 }
2913
2914 static void
2915 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
2916                                                ModestMsgEditWindow *window)
2917 {
2918         modest_msg_edit_window_offer_attach_file (window);
2919 }
2920
2921 const gchar *
2922 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
2923 {
2924         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
2925
2926         return priv->clipboard_text;
2927 }
2928
2929 static void
2930 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
2931                                                GdkEvent *event,
2932                                                ModestMsgEditWindow *window)
2933 {
2934         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2935         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2936         gchar *text = NULL;
2937         if (!GTK_WIDGET_VISIBLE (window))
2938                 return;
2939
2940         text = gtk_clipboard_wait_for_text (selection_clipboard);
2941
2942         if (priv->clipboard_text != NULL) {
2943                 g_free (priv->clipboard_text);
2944         }
2945         priv->clipboard_text = text;
2946
2947         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2948 }
2949 static void 
2950 subject_field_move_cursor (GtkEntry *entry,
2951                            GtkMovementStep step,
2952                            gint a1,
2953                            gboolean a2,
2954                            gpointer window)
2955 {
2956         if (!GTK_WIDGET_VISIBLE (window))
2957                 return;
2958
2959         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2960 }
2961
2962 static void 
2963 update_window_title (ModestMsgEditWindow *window)
2964 {
2965         ModestMsgEditWindowPrivate *priv = NULL;
2966         const gchar *subject;
2967
2968         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2969         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
2970         if (subject == NULL || subject[0] == '\0')
2971                 subject = _("mail_va_new_email");
2972
2973         gtk_window_set_title (GTK_WINDOW (window), subject);
2974
2975 }
2976
2977 static void  
2978 subject_field_changed (GtkEditable *editable, 
2979                        ModestMsgEditWindow *window)
2980 {
2981         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2982         update_window_title (window);
2983         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2984         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2985 }
2986
2987 static void  
2988 subject_field_insert_text (GtkEditable *editable, 
2989                            gchar *new_text,
2990                            gint new_text_length,
2991                            gint *position,
2992                            ModestMsgEditWindow *window)
2993 {
2994         GString *result = g_string_new ("");
2995         gchar *current;
2996         gint result_len = 0;
2997         const gchar *entry_text = NULL;
2998         gint old_length;
2999
3000         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3001         old_length = g_utf8_strlen (entry_text, -1);
3002
3003         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3004                 gunichar c = g_utf8_get_char_validated (current, 8);
3005                 /* Invalid unichar, stop */
3006                 if (c == -1)
3007                         break;
3008                 /* a bullet */
3009                 if (c == 0x2022)
3010                         continue;
3011                 result = g_string_append_unichar (result, c);
3012                 result_len++;
3013         }
3014
3015         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3016                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3017                 if (result_len > 0)
3018                 {
3019                         /* Prevent endless recursion */
3020                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3021                         g_signal_emit_by_name (editable, "insert-text", 
3022                                                (gpointer) result->str, (gpointer) result->len,
3023                                                (gpointer) position, (gpointer) window);
3024                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3025                 }
3026         }
3027
3028         if (result_len + old_length > 1000) {
3029                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3030                                                 dgettext("hildon-common-strings",
3031                                                          "ckdg_ib_maximum_characters_reached"));
3032         }
3033         
3034         g_string_free (result, TRUE);
3035 }
3036
3037 void
3038 modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window,
3039                                             gboolean show)
3040 {
3041         ModestMsgEditWindowPrivate *priv = NULL;
3042
3043         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3044         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3045
3046         gtk_widget_set_no_show_all (priv->find_toolbar, FALSE);
3047
3048         if (show) {
3049                 gtk_widget_show_all (priv->find_toolbar);
3050                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
3051         } else {
3052                 gtk_widget_hide_all (priv->find_toolbar);
3053                 gtk_widget_grab_focus (priv->msg_body);
3054         }
3055     
3056 }
3057
3058 static gboolean 
3059 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3060                                           const gchar *str,
3061                                           GtkTextIter *match_start,
3062                                           GtkTextIter *match_end)
3063 {
3064         GtkTextIter end_iter;
3065         gchar *str_casefold;
3066         gint str_chars_n;
3067         gchar *range_text;
3068         gchar *range_casefold;
3069         gint offset;
3070         gint range_chars_n;
3071         gboolean result = FALSE;
3072
3073         if (str == NULL)
3074                 return TRUE;
3075         
3076         /* get end iter */
3077         end_iter = *iter;
3078         gtk_text_iter_forward_to_end (&end_iter);
3079
3080         str_casefold = g_utf8_casefold (str, -1);
3081         str_chars_n = strlen (str);
3082
3083         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3084         range_casefold = g_utf8_casefold (range_text, -1);
3085         range_chars_n = strlen (range_casefold);
3086
3087         if (range_chars_n < str_chars_n) {
3088                 g_free (str_casefold);
3089                 g_free (range_text);
3090                 g_free (range_casefold);
3091                 return FALSE;
3092         }
3093
3094         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3095                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3096                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3097                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3098                         result = TRUE;
3099                         gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3100                                                       match_start, match_end, NULL);
3101                         g_free (found_text);
3102                 }
3103                 g_free (range_subtext);
3104                 if (result)
3105                         break;
3106         }
3107         g_free (str_casefold);
3108         g_free (range_text);
3109         g_free (range_casefold);
3110
3111         return result;
3112 }
3113
3114
3115 static void 
3116 modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
3117                                             ModestMsgEditWindow *window)
3118 {
3119         gchar *current_search = NULL;
3120         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3121         gboolean result;
3122         GtkTextIter selection_start, selection_end;
3123         GtkTextIter match_start, match_end;
3124         gboolean continue_search = FALSE;
3125
3126         if (message_is_empty (window)) {
3127                 g_free (priv->last_search);
3128                 priv->last_search = NULL;
3129                 hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3130                 return;
3131         }
3132
3133         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
3134         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3135                 g_free (current_search);
3136                 g_free (priv->last_search);
3137                 priv->last_search = NULL;
3138                 /* Information banner about empty search */
3139                 hildon_banner_show_information (NULL, NULL, dgettext ("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
3140                 return;
3141         }
3142
3143         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3144                 continue_search = TRUE;
3145         } else {
3146                 g_free (priv->last_search);
3147                 priv->last_search = g_strdup (current_search);
3148         }
3149
3150         if (continue_search) {
3151                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3152                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3153                                                                    &match_start, &match_end);
3154                 if (!result)
3155                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_search_complete"));
3156         } else {
3157                 GtkTextIter buffer_start;
3158                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3159                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3160                                                                    &match_start, &match_end);
3161                 if (!result)
3162                         hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_no_matches"));
3163         }
3164
3165         /* Mark as selected the string found in search */
3166         if (result) {
3167                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3168                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3169         } else {
3170                 g_free (priv->last_search);
3171                 priv->last_search = NULL;
3172         }
3173         g_free (current_search);
3174 }
3175
3176 static void
3177 modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
3178                                            ModestMsgEditWindow *window)
3179 {
3180         GtkToggleAction *toggle;
3181         ModestWindowPrivate *parent_priv;
3182         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3183
3184         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/FindInMessageMenu"));
3185         gtk_toggle_action_set_active (toggle, FALSE);
3186 }
3187
3188 gboolean 
3189 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3190 {
3191         ModestMsgEditWindowPrivate *priv;
3192
3193         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3194         return priv->sent;
3195 }
3196
3197 void 
3198 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3199                                  gboolean sent)
3200 {
3201         ModestMsgEditWindowPrivate *priv;
3202
3203         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3204         priv->sent = sent;
3205 }
3206
3207
3208 void            
3209 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3210                                   TnyMsg *draft)
3211 {
3212         ModestMsgEditWindowPrivate *priv;
3213         TnyHeader *header = NULL;
3214
3215         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3216         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3217
3218         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3219         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
3220
3221         if (priv->draft_msg != NULL) {
3222                 modest_window_mgr_unregister_window (mgr, MODEST_WINDOW (window));
3223                 g_object_unref (priv->draft_msg);
3224         }
3225
3226         if (draft != NULL) {
3227                 g_object_ref (draft);
3228                 header = tny_msg_get_header (draft);
3229                 if (priv->msg_uid) {
3230                         g_free (priv->msg_uid);
3231                         priv->msg_uid = NULL;
3232                 }
3233                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3234                 if (GTK_WIDGET_REALIZED (window))
3235                         modest_window_mgr_register_window (mgr, MODEST_WINDOW (window));
3236         }
3237
3238         priv->draft_msg = draft;
3239 }
3240
3241 static void  
3242 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3243                        GtkTextIter *start, GtkTextIter *end,
3244                        gpointer userdata)
3245 {
3246         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3247         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3248         gchar *tag_name;
3249
3250         if (tag == NULL+13) return;
3251         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3252         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3253                 replace_with_images (window, priv->images);
3254         }
3255 }
3256
3257 void                    
3258 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3259                                  TnyMimePart *part)
3260 {
3261         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3262
3263         g_return_if_fail (TNY_IS_MIME_PART (part));
3264         priv->attachments = g_list_prepend (priv->attachments, part);
3265         g_object_ref (part);
3266         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part);
3267         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3268         gtk_widget_show_all (priv->attachments_caption);
3269         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3270 }
3271
3272 const gchar*    
3273 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3274 {
3275         ModestMsgEditWindowPrivate *priv;
3276
3277         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3278         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3279
3280         return priv->msg_uid;
3281 }
3282
3283 GtkWidget *
3284 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3285                                          ModestMsgEditWindowWidgetType widget_type)
3286 {
3287         ModestMsgEditWindowPrivate *priv;
3288
3289         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3290         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3291
3292         switch (widget_type) {
3293         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3294                 return priv->msg_body;
3295                 break;
3296         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3297                 return priv->to_field;
3298                 break;
3299         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3300                 return priv->cc_field;
3301                 break;
3302         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3303                 return priv->bcc_field;
3304                 break;
3305         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3306                 return priv->subject_field;
3307                 break;
3308         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3309                 return priv->attachments_view;
3310                 break;
3311         default:
3312                 return NULL;
3313         }
3314 }
3315
3316 static void 
3317 remove_tags (WPTextBuffer *buffer)
3318 {
3319         GtkTextIter start, end;
3320
3321         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3322         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3323
3324         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3325 }
3326
3327 static void
3328 on_account_removed (TnyAccountStore *account_store, 
3329                     TnyAccount *account,
3330                     gpointer user_data)
3331 {
3332         /* Do nothing if it's a store account, because we use the
3333            transport to send the messages */
3334         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
3335                 const gchar *parent_acc = NULL;
3336                 const gchar *our_acc = NULL;
3337
3338                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
3339                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3340                 /* Close this window if I'm showing a message of the removed account */
3341                 if (strcmp (parent_acc, our_acc) == 0)
3342                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
3343         }
3344 }