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