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