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