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