Refactored some maemo utils to toolkit utils
[modest] / src / hildon2 / 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 #include <modest-account-protocol.h>
40
41 #include <config.h>
42
43 #include <modest-account-mgr.h>
44 #include <modest-account-mgr-helpers.h>
45
46 #include <widgets/modest-msg-edit-window.h>
47 #include <modest-selector-picker.h>
48 #include <widgets/modest-recpt-editor.h>
49 #include <widgets/modest-attachments-view.h>
50
51 #include <modest-runtime.h>
52
53 #include "modest-platform.h"
54 #include "modest-icon-names.h"
55 #include "modest-widget-memory.h"
56 #include "modest-window-priv.h"
57 #include "modest-mail-operation.h"
58 #include "modest-tny-platform-factory.h"
59 #include "modest-tny-msg.h"
60 #include "modest-tny-folder.h"
61 #include "modest-tny-account.h"
62 #include "modest-address-book.h"
63 #include "modest-text-utils.h"
64 #include <tny-simple-list.h>
65 #include <modest-wp-text-view.h>
66 #include <wptextbuffer.h>
67 #include <modest-scrollable.h>
68 #include <modest-isearch-toolbar.h>
69 #include <hildon/hildon-touch-selector.h>
70 #include <hildon/hildon-picker-dialog.h>
71 #include "modest-msg-edit-window-ui-dimming.h"
72
73 #include "modest-hildon-includes.h"
74 #include "widgets/modest-msg-edit-window-ui.h"
75 #include <libgnomevfs/gnome-vfs-mime.h>
76 #include <modest-utils.h>
77 #include "modest-maemo-utils.h"
78 #include <modest-ui-constants.h>
79 #include "modest-color-button.h"
80
81 #ifdef MODEST_USE_CALENDAR_WIDGETS
82 #include <calendar-ui-widgets.h>
83 #endif
84
85 #define DEFAULT_FONT_SIZE 3
86 #define DEFAULT_FONT 2
87 #define DEFAULT_SIZE_BUTTON_FONT_FAMILY "Sans"
88 #define DEFAULT_MAIN_VBOX_SPACING 0
89 #define SUBJECT_MAX_LENGTH 1000
90 #define IMAGE_MAX_WIDTH 560
91 #define DEFAULT_FONT_SCALE 1.5
92 #define ATTACHMENT_BUTTON_WIDTH 118
93 #define MAX_FROM_VALUE 36
94 #define MAX_BODY_LENGTH 128*1024
95 #define MAX_BODY_LINES 2048
96
97 static gboolean is_wp_text_buffer_started = FALSE;
98
99 static void  modest_msg_edit_window_class_init   (ModestMsgEditWindowClass *klass);
100 static void  modest_msg_edit_window_init         (ModestMsgEditWindow *obj);
101 static void  modest_msg_edit_window_finalize     (GObject *obj);
102
103 static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata);
104 static void  body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
105 static void  recpt_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
106
107 static void  text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window);
108 static void  text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window);
109 static void  text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window);
110 static void  text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
111                                     GtkTextIter *start, GtkTextIter *end,
112                                     gpointer userdata);
113 static void  text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id);
114 static void  body_insert_text (GtkTextBuffer *buffer, 
115                                       GtkTextIter *location,
116                                       gchar *text,
117                                       gint len,
118                                       ModestMsgEditWindow *window);
119 static void  subject_field_changed (GtkEditable *editable, ModestMsgEditWindow *window);
120 static void  subject_field_insert_text (GtkEditable *editable, 
121                                         gchar *new_text,
122                                         gint new_text_length,
123                                         gint *position,
124                                         ModestMsgEditWindow *window);
125 static void  modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
126                                                          gpointer userdata);
127 static void  modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window);
128
129 static void modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
130                                                      ModestRecptEditor *editor);
131 static void modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
132                                                            ModestMsgEditWindow *window);
133
134 /* ModestWindow methods implementation */
135 static void modest_msg_edit_window_disconnect_signals (ModestWindow *window);
136 static void modest_msg_edit_window_show_toolbar   (ModestWindow *window,
137                                                    gboolean show_toolbar);
138 static void modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
139                                                            GdkEvent *event,
140                                                            ModestMsgEditWindow *window);
141 static void modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window);
142 static void subject_field_move_cursor (GtkEntry *entry,
143                                        GtkMovementStep step,
144                                        gint a1,
145                                        gboolean a2,
146                                        gpointer userdata);
147 static void update_window_title (ModestMsgEditWindow *window);
148
149 /* Find toolbar */
150 static void modest_msg_edit_window_isearch_toolbar_search (GtkWidget *widget,
151                                                            ModestMsgEditWindow *window);
152 static void modest_msg_edit_window_isearch_toolbar_close (GtkWidget *widget,
153                                                           ModestMsgEditWindow *window);
154 static gboolean gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
155                                                           const gchar *str,
156                                                           GtkTextIter *match_start,
157                                                           GtkTextIter *match_end);
158
159 static void remove_tags (WPTextBuffer *buffer);
160
161 static void on_account_removed (TnyAccountStore *account_store, 
162                                 TnyAccount *account,
163                                 gpointer user_data);
164
165 static void init_window (ModestMsgEditWindow *obj);
166
167 gboolean scroll_drag_timeout (gpointer userdata);
168 static void correct_scroll (ModestMsgEditWindow *w);
169 static void correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused);
170 static void text_buffer_end_user_action (GtkTextBuffer *buffer,
171                                          ModestMsgEditWindow *userdata);
172 static void text_buffer_mark_set (GtkTextBuffer *buffer,
173                                   GtkTextIter *iter,
174                                   GtkTextMark *mark,
175                                   ModestMsgEditWindow *userdata);
176 static void on_message_settings (GtkAction *action,
177                                  ModestMsgEditWindow *window);
178 static void setup_menu (ModestMsgEditWindow *self);
179
180 static void from_field_changed (GtkWidget *button,
181                                 ModestMsgEditWindow *self);
182 static void font_size_clicked (GtkToolButton *button,
183                                ModestMsgEditWindow *window);
184 static void font_face_clicked (GtkToolButton *button,
185                                ModestMsgEditWindow *window);
186 static void update_signature (ModestMsgEditWindow *self,
187                               const gchar *old_account, 
188                               const gchar *new_account);
189 static void update_branding (ModestMsgEditWindow *self,
190                              const gchar *new_account);
191 static GtkWidget *_create_addressbook_box (GtkSizeGroup *title_size_group, GtkSizeGroup *value_size_group,
192                                            const gchar *label, GtkWidget *control);
193 static void max_chars_banner_unref (ModestMsgEditWindow *self, GObject *old_ref);
194 static void DEBUG_BUFFER (WPTextBuffer *buffer)
195 {
196 #ifdef DEBUG
197         GtkTextIter iter;
198         g_debug ("BEGIN BUFFER OF SIZE %d", gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (buffer)));
199
200         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
201         while (!gtk_text_iter_is_end (&iter)) {
202                 GString *output = g_string_new ("");
203                 GSList *toggled_tags;
204                 GSList *node;
205
206                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
207                 g_string_append_printf (output, "%d: CLOSED [ ", gtk_text_iter_get_offset (&iter));
208                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
209                         GtkTextTag *tag = (GtkTextTag *) node->data;
210                         const gchar *name;
211                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
212                         output = g_string_append (output, name);
213                         g_string_append (output, " ");
214                 }
215                 output = g_string_append (output, "] OPENED [ ");
216                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
217                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
218                         GtkTextTag *tag = (GtkTextTag *) node->data;
219                         const gchar *name;
220                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
221                         output = g_string_append (output, name);
222                         g_string_append (output, " ");
223                 }
224                 output = g_string_append (output, "]\n");
225                 g_debug ("%s", output->str);
226                 g_string_free (output, TRUE);
227                 gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
228         }
229         g_debug ("END BUFFER");
230 #endif
231 }
232
233 static const GtkActionEntry hildon2_msg_edit_action_entries [] = {
234         { "MessageSettings", NULL, N_("mcen_me_message_settings"), NULL, NULL, G_CALLBACK (on_message_settings)},
235 };
236
237
238 /* static gboolean */
239 /* on_key_pressed (GtkWidget *self, */
240 /*              GdkEventKey *event, */
241 /*              gpointer user_data); */
242
243 /* list my signals */
244 enum {
245         /* MY_SIGNAL_1, */
246         /* MY_SIGNAL_2, */
247         LAST_SIGNAL
248 };
249
250 typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate;
251 struct _ModestMsgEditWindowPrivate {
252         GtkWidget   *msg_body;
253         GtkWidget   *header_box;
254         
255         ModestPairList *from_field_protos;
256         GtkWidget   *from_field;
257         gchar       *last_from_account;
258         gchar       *original_account_name;
259
260         gchar       *references;
261         gchar       *in_reply_to;
262
263         gchar       *original_mailbox;
264         
265         GtkWidget   *to_field;
266         GtkWidget   *cc_field;
267         GtkWidget   *bcc_field;
268         GtkWidget   *subject_field;
269         GtkWidget   *attachments_view;
270         GtkWidget   *priority_icon;
271         GtkWidget   *subject_box;
272         GtkWidget   *send_button;
273
274         GtkWidget   *cc_caption;
275         GtkWidget   *bcc_caption;
276         gboolean     update_caption_visibility;
277         GtkWidget   *attachments_caption;
278
279         GtkTextBuffer *text_buffer;
280
281         GtkWidget   *font_size_toolitem;
282         GtkWidget   *font_face_toolitem;
283         GtkWidget   *font_color_button;
284         GtkWidget   *font_color_toolitem;
285         GSList      *font_items_group;
286         GtkTreeModel *faces_model;
287         gint         current_face_index;
288         GtkWidget   *font_tool_button_label;
289         GtkTreeModel *sizes_model;
290         gint         current_size_index;
291         GtkWidget   *size_tool_button_label;
292
293         GtkWidget   *isearch_toolbar;
294         gchar       *last_search;
295
296         GtkWidget   *font_dialog;
297
298         GtkWidget   *scrollable;
299         guint        correct_scroll_idle;
300         guint        scroll_drag_timeout_id;
301         gdouble      last_upper;
302
303         gint next_cid;
304         TnyList *attachments;
305         TnyList *images;
306         guint64 images_size;
307         gint images_count;
308
309         TnyHeaderFlags priority_flags;
310
311         gboolean    can_undo, can_redo;
312         gulong      clipboard_change_handler_id;
313         gulong      default_clipboard_change_handler_id;
314         gulong      account_removed_handler_id;
315         guint       clipboard_owner_idle;
316         gchar       *clipboard_text;
317
318         TnyMsg      *draft_msg;
319         TnyMsg      *outbox_msg;
320         gchar       *msg_uid;
321
322         gboolean    sent;
323
324         GtkWidget   *app_menu;
325         GtkWidget   *cc_button;
326         GtkWidget   *bcc_button;
327
328         GtkWidget   *max_chars_banner;
329
330         GtkWidget   *brand_icon;
331         GtkWidget   *brand_label;
332         GtkWidget   *brand_container;
333 };
334
335 #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
336                                                     MODEST_TYPE_MSG_EDIT_WINDOW, \
337                                                     ModestMsgEditWindowPrivate))
338 /* globals */
339 static GtkWindowClass *parent_class = NULL;
340
341 /* uncomment the following if you have defined any signals */
342 /* static guint signals[LAST_SIGNAL] = {0}; */
343
344 GType
345 modest_msg_edit_window_get_type (void)
346 {
347         static GType my_type = 0;
348         if (!my_type) {
349                 static const GTypeInfo my_info = {
350                         sizeof(ModestMsgEditWindowClass),
351                         NULL,           /* base init */
352                         NULL,           /* base finalize */
353                         (GClassInitFunc) modest_msg_edit_window_class_init,
354                         NULL,           /* class finalize */
355                         NULL,           /* class data */
356                         sizeof(ModestMsgEditWindow),
357                         1,              /* n_preallocs */
358                         (GInstanceInitFunc) modest_msg_edit_window_init,
359                         NULL
360                 };
361                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
362                                                   "ModestMsgEditWindow",
363                                                   &my_info, 0);
364
365         }
366         return my_type;
367 }
368
369 static void
370 save_state (ModestWindow *self)
371 {
372         modest_widget_memory_save (modest_runtime_get_conf(),
373                                    G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
374 }
375
376
377 static void
378 restore_settings (ModestMsgEditWindow *self)
379 {
380         ModestConf *conf = NULL;
381
382         conf = modest_runtime_get_conf ();
383
384         /* Dim at start clipboard actions */
385         modest_widget_memory_restore (conf, G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
386 }
387
388
389 static void
390 modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass)
391 {
392         GObjectClass *gobject_class;
393         ModestWindowClass *modest_window_class;
394         gobject_class = (GObjectClass*) klass;
395         modest_window_class = (ModestWindowClass*) klass;
396
397         parent_class            = g_type_class_peek_parent (klass);
398         gobject_class->finalize = modest_msg_edit_window_finalize;
399
400         modest_window_class->show_toolbar_func = modest_msg_edit_window_show_toolbar;
401         modest_window_class->save_state_func = save_state;
402         modest_window_class->disconnect_signals_func = modest_msg_edit_window_disconnect_signals;
403
404         g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate));
405 }
406
407 static void
408 modest_msg_edit_window_init (ModestMsgEditWindow *obj)
409 {
410         ModestMsgEditWindowPrivate *priv;
411         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
412
413         priv->msg_body      = NULL;
414         priv->from_field    = NULL;
415         priv->to_field      = NULL;
416         priv->cc_field      = NULL;
417         priv->bcc_field     = NULL;
418         priv->subject_field = NULL;
419         priv->attachments   = TNY_LIST (tny_simple_list_new ());
420         priv->images        = TNY_LIST (tny_simple_list_new ());
421         priv->images_size   = 0;
422         priv->images_count  = 0;
423         priv->next_cid      = 0;
424
425         priv->cc_caption    = NULL;
426         priv->bcc_caption    = NULL;
427         priv->update_caption_visibility = FALSE;
428
429         priv->priority_flags = 0;
430
431         priv->isearch_toolbar = NULL;
432         priv->last_search = NULL;
433
434         priv->draft_msg = NULL;
435         priv->outbox_msg = NULL;
436         priv->msg_uid = NULL;
437
438         priv->can_undo = FALSE;
439         priv->can_redo = FALSE;
440         priv->clipboard_change_handler_id = 0;
441         priv->default_clipboard_change_handler_id = 0;
442         priv->account_removed_handler_id = 0;
443         priv->clipboard_owner_idle = 0;
444         priv->clipboard_text = NULL;
445         priv->sent = FALSE;
446
447         priv->scroll_drag_timeout_id = 0;
448         priv->correct_scroll_idle = 0;
449         priv->last_upper = 0.0;
450
451         priv->font_dialog = NULL;
452         priv->app_menu = NULL;
453
454         priv->references = NULL;
455         priv->in_reply_to = NULL;
456         priv->max_chars_banner = NULL;
457
458         if (!is_wp_text_buffer_started) {
459                 is_wp_text_buffer_started = TRUE;
460                 wp_text_buffer_library_init ();
461         }
462
463         init_window (obj);
464         
465         hildon_program_add_window (hildon_program_get_instance(),
466                                    HILDON_WINDOW(obj));
467 }
468
469 static gchar *
470 multimailbox_get_default_mailbox (const gchar *account_name)
471 {
472         gchar *transport_account;
473         gchar *result = NULL;
474
475         transport_account = modest_account_mgr_get_server_account_name (modest_runtime_get_account_mgr (),
476                                                                         account_name,
477                                                                         TNY_ACCOUNT_TYPE_TRANSPORT);
478         if (transport_account) {
479                 gchar *proto;
480                 ModestProtocolRegistry *registry;
481
482                 registry = modest_runtime_get_protocol_registry ();
483
484                 proto = modest_account_mgr_get_string (modest_runtime_get_account_mgr (), transport_account, 
485                                                        MODEST_ACCOUNT_PROTO, TRUE);
486                 if (proto != NULL) {
487                         ModestProtocol *protocol = 
488                                 modest_protocol_registry_get_protocol_by_name (registry,
489                                                                                MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS,
490                                                                                proto);
491                         if (MODEST_ACCOUNT_PROTOCOL (protocol)) {
492                                 ModestPairList *pair_list;
493
494                                 pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
495                                                                                    account_name);
496                                 if (pair_list) {
497                                         ModestPair *pair = (ModestPair *) pair_list->data;
498                                         result = g_strdup ((const gchar *) pair->first);
499                                         modest_pair_list_free (pair_list);
500                                 }
501                         }
502                         
503                 }
504         }
505
506         return result;
507 }
508
509 /** 
510  * @result: A ModestPairList, which must be freed with modest_pair_list_free().
511  */
512 static ModestPairList*
513 get_transports (void)
514 {
515         GSList *transports = NULL;
516         
517         ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr();
518         GSList *accounts = modest_account_mgr_account_names (account_mgr, 
519                                                              TRUE /* only enabled accounts. */); 
520                                                 
521         GSList *cursor = accounts;
522         while (cursor) {
523                 gchar *account_name = cursor->data;
524                 if (account_name) {
525
526                         gchar *transport_account;
527                         gboolean multi_mailbox = FALSE;
528                         ModestProtocol *protocol = NULL;
529
530                         if (modest_account_mgr_account_is_multimailbox (account_mgr, account_name, &protocol)) {
531
532                                 transport_account = modest_account_mgr_get_server_account_name 
533                                         (modest_runtime_get_account_mgr (),
534                                          account_name,
535                                          TNY_ACCOUNT_TYPE_TRANSPORT);
536                                 if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
537                                         ModestPairList *pair_list;
538                                         pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
539                                                                                            account_name);
540                                         if (pair_list) {
541                                                 transports = g_slist_concat (transports, pair_list);
542                                                 multi_mailbox = TRUE;
543                                         }
544                                 }
545                         }
546
547                         if (!multi_mailbox) {
548                                 gchar *from_string  = NULL;
549
550                                 from_string = modest_account_mgr_get_from_string (account_mgr,
551                                                                                   account_name, NULL);
552                                 if (from_string && account_name) {
553                                         gchar *name = account_name;
554                                         ModestPair *pair = modest_pair_new ((gpointer) name,
555                                                                             (gpointer) from_string , TRUE);
556                                         transports = g_slist_prepend (transports, pair);
557                                 }
558                         }
559                 }
560                 
561                 cursor = cursor->next;
562         }
563         g_slist_free (accounts); /* only free the accounts, not the elements,
564                                   * because they are used in the pairlist */
565         return transports;
566 }
567
568 static void window_focus (GtkWindow *window,
569                           GtkWidget *widget,
570                           gpointer userdata)
571 {
572         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
573 }
574
575 gboolean
576 scroll_drag_timeout (gpointer userdata)
577 {
578         ModestMsgEditWindow *win = (ModestMsgEditWindow *) userdata;
579         ModestMsgEditWindowPrivate *priv;
580
581         /* It could happen that the window was already closed */
582         if (!GTK_WIDGET_VISIBLE (win))
583                 return FALSE;
584
585         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(win);
586
587         correct_scroll_without_drag_check (win, TRUE);
588
589         priv->scroll_drag_timeout_id = 0;
590
591         return FALSE;
592 }
593
594 static gboolean 
595 correct_scroll_without_drag_check_idle (gpointer userdata)
596 {
597         ModestMsgEditWindow *w = (ModestMsgEditWindow *) userdata;
598         ModestMsgEditWindowPrivate *priv;
599         GtkTextIter iter;
600         GdkRectangle rectangle;
601         gint offset_min, offset_max;
602         GtkTextMark *insert;
603         GtkAdjustment *vadj;
604
605         /* It could happen that the window was already closed */
606         if (!GTK_WIDGET_VISIBLE (w))
607                 return FALSE;
608
609         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
610
611         insert = gtk_text_buffer_get_insert (priv->text_buffer);
612         gtk_text_buffer_get_iter_at_mark (priv->text_buffer, &iter, insert);
613
614         gtk_text_view_get_iter_location (GTK_TEXT_VIEW (priv->msg_body), &iter, &rectangle);
615         offset_min = priv->msg_body->allocation.y + rectangle.y;
616         offset_max = offset_min + rectangle.height;
617
618         vadj = modest_scrollable_get_vadjustment (MODEST_SCROLLABLE (priv->scrollable));
619         offset_min = MAX (offset_min - 48, 0);
620         offset_max = MIN (offset_max + 48, vadj->upper);
621
622         gtk_adjustment_clamp_page (vadj, (gdouble) offset_min, (gdouble) offset_max);
623
624         priv->correct_scroll_idle = 0;
625         return FALSE;
626 }
627
628 static void
629 correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused)
630 {
631         ModestMsgEditWindowPrivate *priv;
632
633         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
634
635         if (only_if_focused && !gtk_widget_is_focus (priv->msg_body))
636                 return;
637
638         if (priv->correct_scroll_idle > 0) {
639                 return;
640         }
641
642         priv->correct_scroll_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
643                                                      (GSourceFunc) correct_scroll_without_drag_check_idle,
644                                                      g_object_ref (w),
645                                                      g_object_unref);
646 }
647
648 static void
649 correct_scroll (ModestMsgEditWindow *w)
650 {
651         ModestMsgEditWindowPrivate *priv;
652
653         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
654         if (gtk_grab_get_current () == priv->msg_body) {
655                 if (priv->scroll_drag_timeout_id == 0) {
656                         priv->scroll_drag_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
657                                                                            500,
658                                                                            (GSourceFunc) scroll_drag_timeout,
659                                                                            g_object_ref (w),
660                                                                            g_object_unref);
661                 }
662                 return;
663         }
664
665         correct_scroll_without_drag_check (w, TRUE);
666 }
667
668 static void
669 text_buffer_end_user_action (GtkTextBuffer *buffer,
670                              ModestMsgEditWindow *userdata)
671 {
672
673         correct_scroll (userdata);
674 }
675
676 static void
677 text_buffer_mark_set (GtkTextBuffer *buffer,
678                       GtkTextIter *iter,
679                       GtkTextMark *mark,
680                       ModestMsgEditWindow *userdata)
681 {
682         gtk_text_buffer_begin_user_action (buffer);
683         gtk_text_buffer_end_user_action (buffer);
684 }
685
686 static void
687 cut_clipboard_check (GtkTextView *text_view,
688                      gpointer userdata)
689 {
690         GtkTextBuffer *buffer;
691         
692         buffer = gtk_text_view_get_buffer (text_view);
693         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
694                 g_signal_stop_emission_by_name ((gpointer )text_view, "cut-clipboard");
695         }
696 }
697
698 static void
699 copy_clipboard_check (GtkTextView *text_view,
700                      gpointer userdata)
701 {
702         GtkTextBuffer *buffer;
703         
704         buffer = gtk_text_view_get_buffer (text_view);
705         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
706                 g_signal_stop_emission_by_name ((gpointer )text_view, "copy-clipboard");
707         }
708 }
709
710 static void
711 attachment_deleted (ModestAttachmentsView *attachments_view,
712                     gpointer user_data)
713 {
714         modest_msg_edit_window_remove_attachments (MODEST_MSG_EDIT_WINDOW (user_data),
715                                                    NULL);
716 }
717
718 static void
719 body_size_request (GtkWidget *body,
720                    GtkRequisition *req,
721                    gpointer user_data)
722 {
723         /* Make sure the body always get at least 70 pixels */
724         if (req->height < 70)
725                 req->height = 70;
726 }
727
728 static void
729 connect_signals (ModestMsgEditWindow *obj)
730 {
731         ModestMsgEditWindowPrivate *priv;
732
733         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
734
735         g_signal_connect (G_OBJECT (priv->text_buffer), "refresh_attributes",
736                           G_CALLBACK (text_buffer_refresh_attributes), obj);
737         g_signal_connect (G_OBJECT (priv->text_buffer), "can-undo",
738                           G_CALLBACK (text_buffer_can_undo), obj);
739         g_signal_connect (G_OBJECT (priv->text_buffer), "can-redo",
740                           G_CALLBACK (text_buffer_can_redo), obj);
741         g_signal_connect (G_OBJECT (priv->text_buffer), "changed",
742                           G_CALLBACK (body_changed), obj);
743         g_signal_connect (G_OBJECT (priv->text_buffer), "insert-text", 
744                           G_CALLBACK (body_insert_text), obj);
745         g_signal_connect (G_OBJECT (priv->text_buffer), "modified-changed",
746                           G_CALLBACK (body_changed), obj);
747         g_signal_connect (G_OBJECT (priv->text_buffer), "end-user-action",
748                           G_CALLBACK (text_buffer_end_user_action), obj);
749         g_signal_connect (G_OBJECT (priv->text_buffer), "mark-set",
750                           G_CALLBACK (text_buffer_mark_set), obj);
751         g_signal_connect_after (G_OBJECT (priv->text_buffer), "apply-tag",
752                                 G_CALLBACK (text_buffer_apply_tag), obj);
753         g_signal_connect_swapped (G_OBJECT (priv->to_field), "open-addressbook", 
754                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
755         g_signal_connect_swapped (G_OBJECT (priv->cc_field), "open-addressbook", 
756                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
757         g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", 
758                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
759
760         g_signal_connect (G_OBJECT (priv->send_button), "clicked",
761                           G_CALLBACK (modest_ui_actions_on_send), obj);
762
763         if (GTK_IS_COMBO_BOX (priv->from_field)) {
764                 g_signal_connect (G_OBJECT (priv->from_field), "changed",
765                                   G_CALLBACK (from_field_changed), obj);
766         } else {
767                 g_signal_connect (G_OBJECT (priv->from_field), "value-changed",
768                                   G_CALLBACK (from_field_changed), obj);
769         }
770
771         g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event",
772                           G_CALLBACK (msg_body_focus), obj);
773         g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event",
774                           G_CALLBACK (msg_body_focus), obj);
775         g_signal_connect (G_OBJECT (priv->msg_body), "size-request",
776                           G_CALLBACK (body_size_request), obj);
777         g_signal_connect (G_OBJECT (obj), "set-focus", G_CALLBACK (window_focus), obj);
778         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))),
779                           "changed", G_CALLBACK (recpt_field_changed), obj);
780         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))),
781                           "changed", G_CALLBACK (recpt_field_changed), obj);
782         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))),
783                           "changed", G_CALLBACK (recpt_field_changed), obj);
784         g_signal_connect (G_OBJECT (priv->subject_field), "changed", G_CALLBACK (subject_field_changed), obj);
785         g_signal_connect_after (G_OBJECT (priv->subject_field), "move-cursor", G_CALLBACK (subject_field_move_cursor), obj);
786         g_signal_connect (G_OBJECT (priv->subject_field), "insert-text", G_CALLBACK (subject_field_insert_text), obj);
787
788         g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
789                           G_CALLBACK (modest_msg_edit_window_isearch_toolbar_search), obj);
790         g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
791                           G_CALLBACK (modest_msg_edit_window_isearch_toolbar_close), obj);
792  
793         priv->clipboard_change_handler_id = 
794                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change",
795                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
796         priv->default_clipboard_change_handler_id = 
797                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)), "owner-change",
798                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
799
800         g_signal_connect (G_OBJECT (priv->msg_body), "cut-clipboard", G_CALLBACK (cut_clipboard_check), NULL);
801         g_signal_connect (G_OBJECT (priv->msg_body), "copy-clipboard", G_CALLBACK (copy_clipboard_check), NULL);
802         g_signal_connect (G_OBJECT (priv->attachments_view), "delete", G_CALLBACK (attachment_deleted), obj);
803 }
804
805 static void
806 init_wp_text_view_style ()
807 {
808         static gboolean initialized = FALSE;
809
810         if (!initialized) {
811                 gtk_rc_parse_string ("class \"WPTextView\" style \"fremantle-textview\"");
812                 initialized = TRUE;
813         }
814 }       
815
816 static void
817 init_window (ModestMsgEditWindow *obj)
818 {
819         GtkWidget *to_caption, *subject_caption;
820         GtkWidget *main_vbox;
821         ModestMsgEditWindowPrivate *priv;
822         GtkActionGroup *action_group;
823         ModestWindowPrivate *parent_priv;
824         GdkPixbuf *window_icon = NULL;
825         GError *error = NULL;
826
827         GtkSizeGroup *title_size_group;
828         GtkSizeGroup *value_size_group;
829         GtkWidget *window_box;
830         GtkWidget *window_align;
831 #if (GTK_MINOR_VERSION >= 10)
832         GdkAtom deserialize_type;
833 #endif
834         GtkWidget *from_send_hbox;
835         GtkWidget *send_icon;
836         GtkWidget *attachments_label;
837         GtkWidget *branding_box;
838         GtkWidget *from_caption;
839
840         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
841         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
842
843         parent_priv->ui_manager = gtk_ui_manager_new();
844         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
845         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
846
847         /* Add common actions */
848         gtk_action_group_add_actions (action_group,
849                                       modest_msg_edit_action_entries,
850                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
851                                       obj);
852         gtk_action_group_add_actions (action_group,
853                                       hildon2_msg_edit_action_entries,
854                                       G_N_ELEMENTS (hildon2_msg_edit_action_entries),
855                                       obj);
856         gtk_action_group_add_toggle_actions (action_group,
857                                              modest_msg_edit_toggle_action_entries,
858                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
859                                              obj);
860         gtk_action_group_add_radio_actions (action_group,
861                                             modest_msg_edit_alignment_radio_action_entries,
862                                             G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries),
863                                             GTK_JUSTIFY_LEFT,
864                                             G_CALLBACK (modest_ui_actions_on_change_justify),
865                                             obj);
866         gtk_action_group_add_radio_actions (action_group,
867                                             modest_msg_edit_priority_action_entries,
868                                             G_N_ELEMENTS (modest_msg_edit_priority_action_entries),
869                                             0,
870                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority),
871                                             obj);
872         gtk_action_group_add_radio_actions (action_group,
873                                             modest_msg_edit_file_format_action_entries,
874                                             G_N_ELEMENTS (modest_msg_edit_file_format_action_entries),
875                                             modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL),
876                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format),
877                                             obj);
878         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
879         g_object_unref (action_group);
880
881         /* Load the UI definition */
882         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml",
883                                          &error);
884         if (error != NULL) {
885                 g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message);
886                 g_clear_error (&error);
887         }
888
889         /* Add accelerators */
890         gtk_window_add_accel_group (GTK_WINDOW (obj), 
891                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
892
893         parent_priv->menubar = NULL;
894
895         title_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
896         value_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
897
898         /* Note: This ModestPairList* must exist for as long as the picker
899          * that uses it, because the ModestSelectorPicker uses the ID opaquely, 
900          * so it can't know how to manage its memory. */ 
901         priv->from_field    = modest_toolkit_factory_create_selector (modest_runtime_get_toolkit_factory (),
902                                                                       NULL, g_str_equal);
903         modest_selector_set_value_max_chars (priv->from_field, MAX_FROM_VALUE);
904         if (GTK_IS_COMBO_BOX (priv->from_field)) {
905                 from_caption = modest_toolkit_utils_create_captioned (title_size_group, NULL,
906                                                                       _("mail_va_from"), FALSE,
907                                                                       priv->from_field);
908                 gtk_widget_show (from_caption);
909         } else {
910                 modest_toolkit_utils_set_hbutton_layout (title_size_group, NULL, 
911                                                          _("mail_va_from"), priv->from_field);
912                 hildon_button_set_alignment (HILDON_BUTTON (priv->from_field), 0.0, 0.5, 1.0, 1.0);
913                 hildon_button_set_title_alignment (HILDON_BUTTON (priv->from_field), 0.0, 0.5);
914                 hildon_button_set_value_alignment (HILDON_BUTTON (priv->from_field), 1.0, 0.5);
915                 from_caption = priv->from_field;
916         }
917
918         priv->to_field      = modest_recpt_editor_new ();
919         priv->cc_field      = modest_recpt_editor_new ();
920         priv->bcc_field     = modest_recpt_editor_new ();
921         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->to_field), FALSE);
922         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->cc_field), FALSE);
923         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->bcc_field), FALSE);
924         priv->subject_box = gtk_hbox_new (FALSE, MODEST_MARGIN_NONE);
925         priv->priority_icon = gtk_image_new ();
926         gtk_box_pack_start (GTK_BOX (priv->subject_box), priv->priority_icon, FALSE, FALSE, 0);
927         priv->subject_field = modest_toolkit_factory_create_entry (modest_runtime_get_toolkit_factory ());
928         gtk_entry_set_max_length (GTK_ENTRY (priv->subject_field) ,SUBJECT_MAX_LENGTH);
929         g_object_set (G_OBJECT (priv->subject_field), "truncate-multiline", TRUE, NULL);
930         modest_entry_set_hint (priv->subject_field, _("mail_va_no_subject"));
931         hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->subject_field), 
932                                          HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
933         gtk_box_pack_start (GTK_BOX (priv->subject_box), priv->subject_field, TRUE, TRUE, 0);
934         priv->attachments_view = modest_attachments_view_new (NULL);
935         modest_attachments_view_set_style (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
936                                            MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS);
937         
938         priv->header_box = gtk_vbox_new (FALSE, 0);
939         
940         to_caption = _create_addressbook_box
941                 (title_size_group, value_size_group,
942                  _("mail_va_to"), priv->to_field);
943         priv->cc_caption = _create_addressbook_box
944                 (title_size_group, value_size_group,
945                  _("mail_va_cc"), priv->cc_field);
946         priv->bcc_caption = _create_addressbook_box
947                 (title_size_group, value_size_group,
948                  _("mail_va_hotfix1"), priv->bcc_field);
949         subject_caption = modest_toolkit_utils_create_captioned (title_size_group, value_size_group,
950                                                                  _("mail_va_subject"), FALSE, priv->subject_box);
951         priv->attachments_caption = modest_toolkit_utils_create_captioned_with_size_type (NULL, NULL,
952                                                                                           _("mail_va_attachment"), 
953                                                                                           FALSE,
954                                                                                           priv->attachments_view,
955                                                                                           HILDON_SIZE_AUTO_WIDTH |
956                                                                                           HILDON_SIZE_AUTO_HEIGHT);
957         attachments_label = modest_toolkit_utils_captioned_get_label_widget (priv->attachments_caption);
958         hildon_gtk_widget_set_theme_size (attachments_label, HILDON_SIZE_AUTO_HEIGHT);
959
960
961         priv->send_button = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT);
962         send_icon = gtk_image_new_from_icon_name (MODEST_TOOLBAR_ICON_MAIL_SEND, HILDON_ICON_SIZE_FINGER);
963         gtk_container_add (GTK_CONTAINER (priv->send_button), send_icon);
964         gtk_widget_set_size_request (GTK_WIDGET (priv->send_button), 148, -1);
965
966         g_object_unref (title_size_group);
967         g_object_unref (value_size_group);
968
969         priv->brand_icon = gtk_image_new ();
970         gtk_misc_set_alignment (GTK_MISC (priv->brand_icon), 0.5, 0.5);
971         priv->brand_label = gtk_label_new (NULL);
972         hildon_helper_set_logical_font (priv->brand_label, "SmallSystemFont");
973         gtk_misc_set_alignment (GTK_MISC (priv->brand_label), 0.0, 0.5);
974         gtk_widget_set_no_show_all (priv->brand_icon, TRUE);
975         gtk_widget_set_no_show_all (priv->brand_label, TRUE);
976
977         from_send_hbox = gtk_hbox_new (FALSE, 0);
978         gtk_box_pack_start (GTK_BOX (from_send_hbox), from_caption, TRUE, TRUE, 0);
979         gtk_box_pack_start (GTK_BOX (from_send_hbox), priv->send_button, FALSE, FALSE, 0);
980
981         branding_box = gtk_hbox_new (FALSE, MODEST_MARGIN_DEFAULT);
982         gtk_widget_show (branding_box);
983         gtk_box_pack_start (GTK_BOX (branding_box), priv->brand_label, FALSE, FALSE, 0);
984         gtk_box_pack_start (GTK_BOX (branding_box), priv->brand_icon, FALSE, FALSE, 0);
985
986         priv->brand_container = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
987         gtk_alignment_set_padding (GTK_ALIGNMENT (priv->brand_container), 0, 0, MODEST_MARGIN_DOUBLE, 0);
988         gtk_container_add (GTK_CONTAINER (priv->brand_container), branding_box);
989         gtk_widget_set_no_show_all (priv->brand_container, TRUE);
990
991
992         gtk_box_pack_start (GTK_BOX (priv->header_box), from_send_hbox, FALSE, FALSE, 0);
993         gtk_box_pack_start (GTK_BOX (priv->header_box), to_caption, FALSE, FALSE, 0);
994         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->cc_caption, FALSE, FALSE, 0);
995         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->bcc_caption, FALSE, FALSE, 0);
996         gtk_box_pack_start (GTK_BOX (priv->header_box), subject_caption, FALSE, FALSE, 0);
997         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->attachments_caption, FALSE, FALSE, 0);
998         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->brand_container, FALSE, FALSE, 0);
999         gtk_widget_set_no_show_all (priv->attachments_caption, TRUE);
1000
1001         init_wp_text_view_style ();
1002
1003         priv->msg_body = modest_wp_text_view_new ();
1004         
1005
1006         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->msg_body), GTK_WRAP_WORD_CHAR);
1007         priv->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1008         g_object_set (priv->text_buffer, "font_scale", DEFAULT_FONT_SCALE, NULL);
1009         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1010 #if (GTK_MINOR_VERSION >= 10)
1011         gtk_text_buffer_register_serialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), NULL);
1012         deserialize_type = gtk_text_buffer_register_deserialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), 
1013                                                                        NULL);
1014         gtk_text_buffer_deserialize_set_can_create_tags (GTK_TEXT_BUFFER (priv->text_buffer), 
1015                                                          deserialize_type, TRUE);
1016 #endif
1017         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1018
1019         priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
1020                                                                                NULL);
1021         gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
1022
1023 /*      g_signal_connect (G_OBJECT (obj), "key_pressed", G_CALLBACK (on_key_pressed), NULL) */
1024
1025         priv->scrollable = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
1026
1027         g_object_set (G_OBJECT (priv->scrollable), "horizontal-policy", GTK_POLICY_NEVER, NULL);
1028         
1029         main_vbox = gtk_vbox_new  (FALSE, DEFAULT_MAIN_VBOX_SPACING);
1030         window_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
1031         gtk_alignment_set_padding (GTK_ALIGNMENT (window_align), 0, 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DEFAULT);
1032
1033         gtk_box_pack_start (GTK_BOX(main_vbox), priv->header_box, FALSE, FALSE, 0);
1034         gtk_box_pack_start (GTK_BOX(main_vbox), priv->msg_body, TRUE, TRUE, 0);
1035         gtk_container_add (GTK_CONTAINER (window_align), main_vbox);
1036
1037         modest_scrollable_add_with_viewport (MODEST_SCROLLABLE (priv->scrollable), window_align);
1038         gtk_widget_show_all (GTK_WIDGET(priv->scrollable));
1039         
1040         window_box = gtk_vbox_new (FALSE, 0);
1041         gtk_container_add (GTK_CONTAINER(obj), window_box);
1042
1043         gtk_box_pack_start (GTK_BOX (window_box), priv->scrollable, TRUE, TRUE, 0);
1044
1045         /* Set window icon */
1046         window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON, MODEST_ICON_SIZE_BIG); 
1047         if (window_icon) {
1048                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
1049                 g_object_unref (window_icon);
1050         }       
1051 }
1052         
1053 static void
1054 modest_msg_edit_window_disconnect_signals (ModestWindow *window)
1055 {
1056         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1057
1058         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
1059             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
1060                                            priv->clipboard_change_handler_id))
1061                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
1062                                              priv->clipboard_change_handler_id);
1063         if (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD) &&
1064             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
1065                                            priv->default_clipboard_change_handler_id))
1066                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
1067                                              priv->default_clipboard_change_handler_id);
1068
1069         if (priv->account_removed_handler_id && 
1070             g_signal_handler_is_connected (modest_runtime_get_account_store (), 
1071                                            priv->account_removed_handler_id))
1072                 g_signal_handler_disconnect(modest_runtime_get_account_store (), 
1073                                            priv->account_removed_handler_id);
1074 }
1075
1076 static void
1077 modest_msg_edit_window_finalize (GObject *obj)
1078 {
1079         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1080
1081         if (priv->max_chars_banner) {
1082                 g_object_weak_unref (G_OBJECT (priv->max_chars_banner), (GWeakNotify) max_chars_banner_unref, obj);
1083                 priv->max_chars_banner = FALSE;
1084         }
1085
1086         /* Sanity check: shouldn't be needed, the window mgr should
1087            call this function before */
1088         modest_msg_edit_window_disconnect_signals (MODEST_WINDOW (obj));
1089
1090         if (priv->font_dialog != NULL) {
1091                 gtk_dialog_response (GTK_DIALOG (priv->font_dialog), GTK_RESPONSE_NONE);
1092         }
1093
1094         if (priv->clipboard_text != NULL) {
1095                 g_free (priv->clipboard_text);
1096                 priv->clipboard_text = NULL;
1097         }
1098         
1099         if (priv->draft_msg != NULL) {
1100                 TnyHeader *header = tny_msg_get_header (priv->draft_msg);
1101                 if (TNY_IS_HEADER (header)) {
1102                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1103                         modest_window_mgr_unregister_header (mgr, header);
1104                 }
1105                 g_object_unref (priv->draft_msg);
1106                 priv->draft_msg = NULL;
1107         }
1108         if (priv->outbox_msg != NULL) {
1109                 TnyHeader *header = tny_msg_get_header (priv->outbox_msg);
1110                 if (TNY_IS_HEADER (header)) {
1111                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1112                         modest_window_mgr_unregister_header (mgr, header);
1113                 }
1114                 g_object_unref (priv->outbox_msg);
1115                 priv->outbox_msg = NULL;
1116         }
1117         if (priv->correct_scroll_idle > 0) {
1118                 g_source_remove (priv->correct_scroll_idle);
1119                 priv->correct_scroll_idle = 0;
1120         }
1121         if (priv->scroll_drag_timeout_id > 0) {
1122                 g_source_remove (priv->scroll_drag_timeout_id);
1123                 priv->scroll_drag_timeout_id = 0;
1124         }
1125         if (priv->clipboard_owner_idle > 0) {
1126                 g_source_remove (priv->clipboard_owner_idle);
1127                 priv->clipboard_owner_idle = 0;
1128         }
1129         if (priv->original_account_name)
1130                 g_free (priv->original_account_name);
1131         if (priv->original_mailbox)
1132                 g_free (priv->original_mailbox);
1133         g_free (priv->msg_uid);
1134         g_free (priv->last_search);
1135         g_free (priv->references);
1136         g_free (priv->in_reply_to);
1137         g_object_unref (priv->attachments);
1138         g_object_unref (priv->images);
1139
1140         /* This had to stay alive for as long as the picker that used it: */
1141         modest_pair_list_free (priv->from_field_protos);
1142         
1143         G_OBJECT_CLASS(parent_class)->finalize (obj);
1144 }
1145
1146 static void
1147 pixbuf_size_prepared (GdkPixbufLoader *loader,
1148                       gint width,
1149                       gint height,
1150                       ModestMsgEditWindow *self)
1151 {
1152         gint new_height, new_width;
1153         gboolean set_size;
1154         
1155         new_height = height;
1156         new_width = width;
1157         set_size = FALSE;
1158
1159         if (width > IMAGE_MAX_WIDTH) {
1160                 new_height = height * IMAGE_MAX_WIDTH / width;
1161                 new_width = IMAGE_MAX_WIDTH;
1162         }
1163
1164         gdk_pixbuf_loader_set_size (loader, new_width, new_height);
1165 }
1166
1167 static GdkPixbuf *
1168 pixbuf_from_stream (TnyStream *stream,
1169                     const gchar *mime_type,
1170                     guint64 *stream_size,
1171                     ModestMsgEditWindow *self)
1172 {
1173         GdkPixbufLoader *loader;
1174         GdkPixbuf *pixbuf;
1175         guint64 size;
1176         GError *error = NULL;
1177
1178         size = 0;
1179
1180         loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL);
1181
1182         if (loader == NULL) {
1183                 if (stream_size)
1184                         *stream_size = 0;
1185                 return NULL;
1186         }
1187         g_signal_connect (G_OBJECT (loader), "size-prepared", G_CALLBACK (pixbuf_size_prepared), self);
1188
1189         hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
1190
1191         tny_stream_reset (TNY_STREAM (stream));
1192         while (!tny_stream_is_eos (TNY_STREAM (stream))) {
1193                 unsigned char read_buffer[128];
1194                 gint readed;
1195                 readed = tny_stream_read (TNY_STREAM (stream), (char *) read_buffer, 128);
1196                 size += readed;
1197                 if (!gdk_pixbuf_loader_write (loader, read_buffer, readed, &error)) {
1198                         break;
1199                 }
1200                 /* Allow some UI responsiveness */
1201                 while (gtk_events_pending ())
1202                         gtk_main_iteration ();
1203         }
1204         hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), FALSE);
1205
1206         gdk_pixbuf_loader_close (loader, &error);
1207
1208         if (error)
1209                 g_error_free (error);
1210         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1211         if (pixbuf) 
1212                 g_object_ref (pixbuf);
1213         g_object_unref (loader);
1214
1215         if (!pixbuf)
1216                 return NULL;
1217
1218         if (gdk_pixbuf_get_width (pixbuf) > IMAGE_MAX_WIDTH) {
1219                 GdkPixbuf *new_pixbuf;
1220                 gint new_height;
1221                 new_height = (gdk_pixbuf_get_height (pixbuf) * IMAGE_MAX_WIDTH) /
1222                         gdk_pixbuf_get_width (pixbuf);
1223                 new_pixbuf = gdk_pixbuf_scale_simple (pixbuf, IMAGE_MAX_WIDTH, new_height, GDK_INTERP_BILINEAR);
1224                 g_object_unref (pixbuf);
1225                 pixbuf = new_pixbuf;
1226         }
1227
1228         if (stream_size)
1229                 *stream_size = size;
1230
1231         return pixbuf;
1232 }
1233
1234 static void
1235 replace_with_images (ModestMsgEditWindow *self, TnyList *attachments)
1236 {
1237         ModestMsgEditWindowPrivate *priv;
1238         TnyIterator *iter;
1239
1240         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1241
1242         g_object_ref (self);
1243         for (iter = tny_list_create_iterator (attachments);
1244              !tny_iterator_is_done (iter);
1245              tny_iterator_next (iter)) {
1246                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1247                 const gchar *cid = tny_mime_part_get_content_id (part);
1248                 const gchar *mime_type = tny_mime_part_get_content_type (part);
1249                 if ((cid != NULL)&&(mime_type != NULL)) {
1250                         guint64 stream_size;
1251                         TnyStream *stream = tny_mime_part_get_decoded_stream (part);
1252                         GdkPixbuf *pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size, self);
1253
1254
1255                         g_object_unref (stream);
1256
1257                         if (pixbuf != NULL) {
1258                                 priv->images_count ++;
1259                                 priv->images_size += stream_size;
1260                                 wp_text_buffer_replace_image (WP_TEXT_BUFFER (priv->text_buffer), cid, pixbuf);
1261                                 g_object_unref (pixbuf);
1262                         }
1263                 }
1264                 g_object_unref (part);
1265         }
1266         g_object_unref (iter);
1267         g_object_unref (self);
1268 }
1269
1270 static void
1271 get_related_images (ModestMsgEditWindow *self, TnyMsg *msg)
1272 {
1273         TnyMimePart *parent = NULL;
1274         const gchar *content_type = NULL;
1275         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1276
1277         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
1278
1279         if (content_type && !g_ascii_strcasecmp (content_type, "multipart/related")) {
1280                 parent = g_object_ref (msg);
1281         } else if (content_type && !g_ascii_strcasecmp (content_type, "multipart/mixed")) {
1282                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1283                 TnyIterator *iter;
1284
1285                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
1286                 iter = tny_list_create_iterator (parts);
1287                 while (!tny_iterator_is_done (iter)) {
1288                         TnyMimePart *part;
1289                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1290                         content_type = tny_mime_part_get_content_type (part);
1291                         if (content_type && !g_ascii_strcasecmp (content_type, "multipart/related")) {
1292                                 parent = part;
1293                                 break;
1294                         } else {
1295                                 g_object_unref (part);
1296                         }
1297                         tny_iterator_next (iter);
1298                 }
1299                 g_object_unref (iter);
1300                 g_object_unref (parts);
1301         }
1302
1303         if (parent != NULL) {
1304                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1305                 TnyIterator *iter;
1306
1307                 tny_mime_part_get_parts (TNY_MIME_PART (parent), parts);
1308                 iter = tny_list_create_iterator (parts);
1309                 while (!tny_iterator_is_done (iter)) {
1310                         TnyMimePart *part;
1311                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1312                         content_type = tny_mime_part_get_content_type (part);
1313                         if (content_type && g_str_has_prefix (content_type, "image/")) {
1314                                 tny_list_prepend (priv->images, (GObject *) part);
1315                         } 
1316                         g_object_unref (part);
1317                         tny_iterator_next (iter);
1318                 }
1319                 g_object_unref (iter);
1320                 g_object_unref (parts);
1321                 g_object_unref (parent);
1322         }
1323 }
1324
1325 static void
1326 update_next_cid (ModestMsgEditWindow *self, TnyList *attachments)
1327 {
1328         TnyIterator *iter;
1329         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1330
1331         for (iter = tny_list_create_iterator (attachments) ; 
1332              !tny_iterator_is_done (iter);
1333              tny_iterator_next (iter)) {
1334                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1335                 const gchar *cid = tny_mime_part_get_content_id (part);
1336                 if (cid != NULL) {
1337                         char *invalid = NULL;
1338                         gint int_cid = strtol (cid, &invalid, 10);
1339                         if ((invalid != NULL) && (*invalid == '\0') && (int_cid >= priv->next_cid)) {
1340                                 priv->next_cid = int_cid + 1;
1341                         }
1342                 }
1343                 g_object_unref (part);
1344         }
1345         g_object_unref (iter);
1346 }
1347
1348 static void
1349 set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich)
1350 {
1351         TnyHeader *header;
1352         gchar *to, *cc, *bcc, *subject;
1353         gchar *body;
1354         ModestMsgEditWindowPrivate *priv;
1355         ModestWindowPrivate *parent_priv;
1356         GtkTextIter iter;
1357         TnyHeaderFlags priority_flags;
1358         TnyFolder *msg_folder;
1359         gboolean is_html = FALSE;
1360         gboolean field_view_set;
1361         
1362         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1363         g_return_if_fail (TNY_IS_MSG (msg));
1364
1365         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1366         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
1367
1368         header = tny_msg_get_header (msg);
1369         to      = tny_header_dup_to (header);
1370         cc      = tny_header_dup_cc (header);
1371         bcc     = tny_header_dup_bcc (header);
1372         subject = tny_header_dup_subject (header);
1373
1374         modest_tny_msg_get_references (TNY_MSG (msg), NULL, &(priv->references), &(priv->in_reply_to));
1375         priority_flags = tny_header_get_priority (header);
1376
1377         if (to) {
1378                 gchar *quoted_names = modest_text_utils_quote_names (to);
1379                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field), quoted_names);
1380                 g_free (quoted_names);
1381         }
1382
1383         field_view_set = TRUE;
1384         if (cc) {
1385                 gchar *quoted_names = modest_text_utils_quote_names (cc);
1386                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field),  quoted_names);
1387                 g_free (quoted_names);
1388                 gtk_widget_set_no_show_all (priv->cc_caption, FALSE);
1389                 gtk_widget_show (priv->cc_caption);
1390         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) {
1391                 gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
1392                 gtk_widget_hide (priv->cc_caption);
1393                 field_view_set = FALSE;
1394         }
1395         modest_togglable_set_active (priv->cc_button, field_view_set);
1396
1397         field_view_set = TRUE;
1398         if (bcc) {
1399                 gchar *quoted_names = modest_text_utils_quote_names (bcc);
1400                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), quoted_names);
1401                 g_free (quoted_names);
1402                 gtk_widget_set_no_show_all (priv->bcc_caption, FALSE);
1403                 gtk_widget_show (priv->bcc_caption);
1404         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) {
1405                 gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
1406                 gtk_widget_hide (priv->bcc_caption);
1407                 field_view_set = FALSE;
1408         }
1409         modest_togglable_set_active (priv->bcc_button, field_view_set);
1410
1411
1412         if (subject)
1413                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
1414         modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self),
1415                                                    priority_flags);
1416
1417         update_window_title (self);
1418
1419         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1420         body = modest_tny_msg_get_body (msg, TRUE, &is_html);
1421
1422         if ((body == NULL)||(body[0] == '\0')) {
1423                 g_free (body);
1424                 body = modest_text_utils_convert_to_html ("");
1425                 is_html = FALSE;
1426         }
1427         wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1428         wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer),
1429                                             (gchar *) body,
1430                                             strlen (body));
1431         wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer));
1432         g_free (body);
1433
1434         /* Add attachments to the view */
1435         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg);
1436         priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1437         if (tny_list_get_length (priv->attachments) == 0) {
1438                 gtk_widget_hide (priv->attachments_caption);
1439         } else {
1440                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
1441                 gtk_widget_show_all (priv->attachments_caption);
1442         }
1443         get_related_images (self, msg);
1444         update_next_cid (self, priv->attachments);
1445         update_next_cid (self, priv->images);
1446         replace_with_images (self, priv->images);
1447
1448         if (preserve_is_rich && !is_html) {
1449                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1450         /* Get the default format required from configuration */
1451         } else if (!preserve_is_rich && !modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) {
1452                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1453         }
1454
1455         /* Set the default focus depending on having already a To: field or not */
1456         if ((!to)||(*to == '\0')) {
1457                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
1458         } else {
1459                 gtk_widget_grab_focus (priv->msg_body);
1460         }
1461
1462         DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer));
1463
1464         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1465         gtk_text_buffer_place_cursor (priv->text_buffer, &iter);
1466
1467         modest_msg_edit_window_set_modified (self, FALSE);
1468
1469         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1470         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1471         text_buffer_can_undo (priv->text_buffer, FALSE, self);
1472         text_buffer_can_redo (priv->text_buffer, FALSE, self);
1473
1474         if (priv->msg_uid) {
1475                 g_free (priv->msg_uid);
1476                 priv->msg_uid = NULL;
1477         }
1478
1479         /* we should set a reference to the incoming message if it is a draft */
1480         msg_folder = tny_msg_get_folder (msg);
1481         if (msg_folder) {               
1482                 if (modest_tny_folder_is_local_folder (msg_folder)) {
1483                         TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder);
1484                         if (type == TNY_FOLDER_TYPE_INVALID)
1485                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1486                         
1487                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
1488                                 priv->draft_msg = g_object_ref(msg);
1489                         if (type == TNY_FOLDER_TYPE_OUTBOX)
1490                                 priv->outbox_msg = g_object_ref(msg);
1491                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1492                 }
1493                 g_object_unref (msg_folder);
1494         }
1495
1496         g_free (to);
1497         g_free (subject);
1498         g_free (cc);
1499         g_free (bcc);
1500 }
1501
1502 static void
1503 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1504 {
1505         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1506         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1507         GtkWidget *placeholder;
1508         GtkWidget *tool_item;
1509         gint insert_index;
1510         gchar size_text[5];
1511         gint size_index;
1512         gint font_index;
1513         GtkWidget *sizes_menu;
1514         GtkWidget *fonts_menu;
1515         gchar *markup;
1516         GtkWidget *arrow;
1517         GtkWidget *hbox;
1518
1519         /* Toolbar */
1520         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1521         gtk_toolbar_set_show_arrow (GTK_TOOLBAR (parent_priv->toolbar), FALSE);
1522         gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
1523         modest_window_add_toolbar (MODEST_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1524
1525         /* Font color placeholder */
1526         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1527         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1528
1529         /* font color */
1530         priv->font_color_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1531         priv->font_color_button = modest_color_button_new ();
1532         gtk_widget_set_size_request (priv->font_color_button, -1, 48);
1533         GTK_WIDGET_UNSET_FLAGS (priv->font_color_toolitem, GTK_CAN_FOCUS);
1534         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1535         gtk_container_add (GTK_CONTAINER (priv->font_color_toolitem), priv->font_color_button);
1536         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1537         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1538         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->font_color_toolitem), insert_index);
1539         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1540                                   "notify::color", 
1541                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1542                                   window);
1543
1544         /* Font size and face placeholder */
1545         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1546         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1547         /* font_size */
1548         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1549         priv->size_tool_button_label = gtk_label_new (NULL);
1550         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1551         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1552                               size_text, "</span>", NULL);
1553         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1554         gtk_misc_set_alignment (GTK_MISC (priv->size_tool_button_label), 1.0, 0.5);
1555         g_free (markup);
1556         hildon_helper_set_logical_font (priv->size_tool_button_label, "LargeSystemFont");
1557         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1558         gtk_box_pack_start (GTK_BOX (hbox), priv->size_tool_button_label, TRUE, TRUE, 0);
1559         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1560         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1561         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1562         gtk_widget_set_sensitive (arrow, FALSE);
1563         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1564         sizes_menu = gtk_menu_new ();
1565         priv->sizes_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1566         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1567                 GtkTreeIter iter;
1568
1569                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1570                 gtk_list_store_append (GTK_LIST_STORE (priv->sizes_model), &iter);
1571
1572                 gtk_list_store_set (GTK_LIST_STORE (priv->sizes_model), &iter, 
1573                                     0, size_text,
1574                                     -1);
1575
1576                 if (wp_font_size[size_index] == 12)
1577                         priv->current_size_index = size_index;
1578                                         
1579         }
1580
1581         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_size_clicked), window);
1582         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1583         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1584         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1585         priv->font_size_toolitem = tool_item;
1586
1587         /* font face */
1588         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1589         priv->font_tool_button_label = gtk_label_new (NULL);
1590         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1591         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1592         gtk_misc_set_alignment (GTK_MISC (priv->font_tool_button_label), 1.0, 0.5);
1593         g_free(markup);
1594         hildon_helper_set_logical_font (priv->font_tool_button_label, "LargeSystemFont");
1595         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1596         gtk_box_pack_start (GTK_BOX (hbox), priv->font_tool_button_label, TRUE, TRUE, 0);
1597         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1598         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1599         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1600         gtk_widget_set_sensitive (arrow, FALSE);
1601         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1602         fonts_menu = gtk_menu_new ();
1603         priv->faces_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1604         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1605                 GtkTreeIter iter;
1606
1607                 gtk_list_store_append (GTK_LIST_STORE (priv->faces_model), &iter);
1608
1609                 gtk_list_store_set (GTK_LIST_STORE (priv->faces_model), &iter, 
1610                                     0, wp_get_font_name (font_index),
1611                                     -1);
1612
1613                 if (font_index == DEFAULT_FONT)
1614                         priv->current_face_index = font_index;
1615         }
1616         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_face_clicked), window);
1617         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1618         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1619         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1620         priv->font_face_toolitem = tool_item;
1621
1622         /* Set expand and homogeneous for remaining items */
1623         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1624         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1625         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1626         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1627         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1628         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1629
1630         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1631            will not show the tool items added to the placeholders) */
1632         gtk_widget_show_all (parent_priv->toolbar);
1633
1634         /* Set the no show all *after* showing all items. We do not
1635            want the toolbar to be shown with a show all because it
1636            could go agains the gconf setting regarding showing or not
1637            the toolbar of the editor window */
1638         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1639 }
1640
1641
1642
1643 ModestWindow*
1644 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, const gchar *mailbox, gboolean preserve_is_rich)
1645 {
1646         GObject *obj;
1647         ModestWindowPrivate *parent_priv;
1648         ModestMsgEditWindowPrivate *priv;
1649         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1650         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1651         ModestWindowMgr *mgr = NULL;
1652
1653         g_return_val_if_fail (msg, NULL);
1654         g_return_val_if_fail (account_name, NULL);
1655
1656         mgr = modest_runtime_get_window_mgr ();
1657         
1658         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1659
1660         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1661         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1662
1663         /* Menubar. Update the state of some toggles */
1664         priv->from_field_protos = get_transports ();
1665         priv->original_mailbox = NULL;
1666         modest_selector_set_pair_list (priv->from_field, priv->from_field_protos);
1667         modest_selector_set_active_id (priv->from_field, (gpointer) account_name);
1668         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
1669         if (mailbox && modest_pair_list_find_by_first_as_string (priv->from_field_protos, mailbox)) {
1670                 modest_selector_set_active_id (priv->from_field, (gpointer) mailbox);
1671                 priv->original_mailbox = g_strdup (mailbox);
1672         } else if (modest_account_mgr_account_is_multimailbox (modest_runtime_get_account_mgr (), account_name, NULL)) {
1673                 /* We set the first mailbox as the active mailbox */
1674                 priv->original_mailbox = multimailbox_get_default_mailbox (account_name);
1675                 if (priv->original_mailbox != NULL)
1676                         modest_selector_set_active_id (priv->from_field,
1677                                                        (gpointer) priv->original_mailbox);
1678                 else
1679                         modest_selector_set_active_id (priv->from_field,
1680                                                        (gpointer) account_name);
1681         } else {
1682                 modest_selector_set_active_id (priv->from_field, (gpointer) account_name);
1683         }
1684         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
1685         update_branding (MODEST_MSG_EDIT_WINDOW (obj), priv->last_from_account);
1686         if (!GTK_IS_COMBO_BOX (priv->from_field)) {
1687 #ifdef HILDON_TOOLKIT_HILDON2
1688                 hildon_button_set_title (HILDON_BUTTON (priv->from_field),
1689                                          _("mail_va_from"));
1690                 hildon_button_set_value (HILDON_BUTTON (priv->from_field), 
1691                                          hildon_touch_selector_get_current_text 
1692                                          (HILDON_TOUCH_SELECTOR (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (priv->from_field)))));
1693 #endif
1694         }
1695         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1696         modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
1697
1698         /* Init window */
1699         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1700
1701         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1702                 
1703         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1704         modest_window_set_active_mailbox (MODEST_WINDOW(obj), priv->original_mailbox);
1705
1706         priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL;
1707
1708         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1709         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1710         /* Add common dimming rules */
1711         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1712                                               modest_msg_edit_window_toolbar_dimming_entries,
1713                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1714                                               MODEST_WINDOW (obj));
1715         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_toolitem,
1716                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1717                                                     MODEST_WINDOW (obj));
1718         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1719                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1720                                                     MODEST_WINDOW (obj));
1721         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1722                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1723                                                     MODEST_WINDOW (obj));
1724         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->send_button,
1725                                                     G_CALLBACK (modest_ui_dimming_rules_on_send),
1726                                                     MODEST_WINDOW (obj));
1727         /* Insert dimming rules group for this window */
1728         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1729         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1730
1731         /* Setup app menu */
1732         setup_menu (MODEST_MSG_EDIT_WINDOW (obj));
1733
1734         /* Checks the dimming rules */
1735         g_object_unref (toolbar_rules_group);
1736         g_object_unref (clipboard_rules_group);
1737         gtk_widget_hide (priv->priority_icon);
1738         gtk_widget_queue_resize (priv->subject_box);
1739         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1740
1741         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1742
1743         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1744         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1745         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1746         priv->update_caption_visibility = TRUE;
1747
1748         modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE);
1749
1750         /* Track account-removed signal, this window should be closed
1751            in the case we're creating a mail associated to the account
1752            that is deleted */
1753         priv->account_removed_handler_id = 
1754                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1755                                   "account_removed",
1756                                   G_CALLBACK(on_account_removed),
1757                                   obj);
1758
1759         modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj));
1760
1761         return (ModestWindow*) obj;
1762 }
1763
1764 static gint
1765 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1766 {
1767         GString **string_buffer = (GString **) user_data;
1768
1769         *string_buffer = g_string_append (*string_buffer, buffer);
1770    
1771         return 0;
1772 }
1773
1774 /**
1775  * @result: A new string which should be freed with g_free().
1776  */
1777 static gchar *
1778 get_formatted_data (ModestMsgEditWindow *edit_window)
1779 {
1780         ModestMsgEditWindowPrivate *priv;
1781         GString *string_buffer = g_string_new ("");
1782         
1783         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1784
1785         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1786
1787         modest_text_utils_hyperlinkify (string_buffer);
1788
1789         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1790
1791         return g_string_free (string_buffer, FALSE);
1792                                                                         
1793 }
1794
1795 MsgData * 
1796 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1797 {
1798         MsgData *data;
1799         const gchar *account_name;
1800         ModestMsgEditWindowPrivate *priv;
1801         TnyIterator *att_iter;
1802         const gchar *picker_active_id;
1803         
1804         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1805
1806         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1807         
1808         picker_active_id = modest_selector_get_active_id (priv->from_field);
1809         g_return_val_if_fail (picker_active_id, NULL);
1810         account_name = modest_utils_get_account_name_from_recipient (picker_active_id, NULL);
1811         
1812         /* don't free these (except from) */
1813         data = g_slice_new0 (MsgData);
1814         data->from    =  g_strdup ((gchar *) modest_selector_get_active_display_name (priv->from_field));
1815         data->account_name = g_strdup (account_name);
1816         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1817         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1818         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1819         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1820         data->references = g_strdup (priv->references);
1821         data->in_reply_to = g_strdup (priv->in_reply_to);
1822         if (priv->draft_msg) {
1823                 data->draft_msg = g_object_ref (priv->draft_msg);
1824         } else if (priv->outbox_msg) {
1825                 data->draft_msg = g_object_ref (priv->outbox_msg);
1826         } else {
1827                 data->draft_msg = NULL;
1828         }
1829
1830         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1831         GtkTextIter b, e;
1832         gtk_text_buffer_get_bounds (buf, &b, &e);
1833         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1834
1835         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1836                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1837         else
1838                 data->html_body = NULL;
1839
1840         /* deep-copy the data */
1841         att_iter = tny_list_create_iterator (priv->attachments);
1842         data->attachments = NULL;
1843         while (!tny_iterator_is_done (att_iter)) {
1844                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1845                 if (!(TNY_IS_MIME_PART(part))) {
1846                         g_warning ("strange data in attachment list");
1847                         g_object_unref (part);
1848                         tny_iterator_next (att_iter);
1849                         continue;
1850                 }
1851                 data->attachments = g_list_append (data->attachments,
1852                                                    part);
1853                 tny_iterator_next (att_iter);
1854         }
1855         g_object_unref (att_iter);
1856
1857         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1858         att_iter = tny_list_create_iterator (priv->images);
1859         data->images = NULL;
1860         while (!tny_iterator_is_done (att_iter)) {
1861                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1862                 const gchar *cid;
1863                 if (!(TNY_IS_MIME_PART(part))) {
1864                         g_warning ("strange data in attachment list");
1865                         g_object_unref (part);
1866                         tny_iterator_next (att_iter);
1867                         continue;
1868                 }
1869                 cid = tny_mime_part_get_content_id (part);
1870                 if (cid) {                      
1871                         gchar *image_tag_id;
1872                         GtkTextTag *image_tag;
1873                         GtkTextIter iter;
1874                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1875                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1876                         g_free (image_tag_id);
1877                         
1878                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1879                         if (image_tag && 
1880                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1881                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1882                                 data->images = g_list_append (data->images,
1883                                                               g_object_ref (part));
1884                 }
1885                 g_object_unref (part);
1886                 tny_iterator_next (att_iter);
1887         }
1888         g_object_unref (att_iter);
1889         
1890         data->priority_flags = priv->priority_flags;
1891
1892         return data;
1893 }
1894
1895
1896 static void
1897 unref_gobject (GObject *obj, gpointer data)
1898 {
1899         if (!G_IS_OBJECT(obj))
1900                 return;
1901         g_object_unref (obj);
1902 }
1903
1904 void 
1905 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1906                                                       MsgData *data)
1907 {
1908         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1909
1910         if (!data)
1911                 return;
1912
1913         g_free (data->to);
1914         g_free (data->cc);
1915         g_free (data->bcc);
1916         g_free (data->from);
1917         g_free (data->subject);
1918         g_free (data->plain_body);
1919         g_free (data->html_body);
1920         g_free (data->account_name);
1921         g_free (data->references);
1922         g_free (data->in_reply_to);
1923         
1924         if (data->draft_msg != NULL) {
1925                 g_object_unref (data->draft_msg);
1926                 data->draft_msg = NULL;
1927         }
1928         
1929         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1930         g_list_free (data->attachments);
1931         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1932         g_list_free (data->images);
1933         
1934         g_slice_free (MsgData, data);
1935 }
1936
1937 void                    
1938 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1939                                        gint *parts_count,
1940                                        guint64 *parts_size)
1941 {
1942         ModestMsgEditWindowPrivate *priv;
1943
1944         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1945
1946         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1947
1948         /* TODO: add images */
1949         *parts_size += priv->images_size;
1950         *parts_count += priv->images_count;
1951
1952 }
1953
1954 ModestMsgEditFormat
1955 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1956 {
1957         gboolean rich_text;
1958         ModestMsgEditWindowPrivate *priv = NULL;
1959         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1960
1961         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1962
1963         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1964         if (rich_text)
1965                 return MODEST_MSG_EDIT_FORMAT_HTML;
1966         else
1967                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1968 }
1969
1970 void
1971 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1972                                    ModestMsgEditFormat format)
1973 {
1974         ModestMsgEditWindowPrivate *priv;
1975         ModestWindowPrivate *parent_priv;
1976
1977         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1978         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1979         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
1980
1981         switch (format) {
1982         case MODEST_MSG_EDIT_FORMAT_HTML:
1983                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1984                 if (parent_priv->toolbar) gtk_widget_show (parent_priv->toolbar);
1985                 break;
1986         case MODEST_MSG_EDIT_FORMAT_TEXT:
1987                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1988                 if (parent_priv->toolbar) gtk_widget_hide (parent_priv->toolbar);
1989                 break;
1990         default:
1991                 g_return_if_reached ();
1992         }
1993 }
1994
1995 ModestMsgEditFormatState *
1996 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1997 {
1998         ModestMsgEditFormatState *format_state = NULL;
1999         ModestMsgEditWindowPrivate *priv;
2000         WPTextBufferFormat *buffer_format;
2001
2002         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
2003
2004         buffer_format = g_new0 (WPTextBufferFormat, 1);
2005         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2006
2007         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
2008
2009         format_state = g_new0 (ModestMsgEditFormatState, 1);
2010         format_state->bold = buffer_format->bold&0x1;
2011         format_state->italics = buffer_format->italic&0x1;
2012         format_state->bullet = buffer_format->bullet&0x1;
2013         format_state->color = buffer_format->color;
2014         format_state->font_size = buffer_format->font_size;
2015         format_state->font_family = wp_get_font_name (buffer_format->font);
2016         format_state->justification = buffer_format->justification;
2017         g_free (buffer_format);
2018
2019         return format_state;
2020  
2021 }
2022
2023 void
2024 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
2025                                          const ModestMsgEditFormatState *format_state)
2026 {
2027         ModestMsgEditWindowPrivate *priv;
2028         WPTextBufferFormat *buffer_format;
2029         WPTextBufferFormat *current_format;
2030
2031         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2032         g_return_if_fail (format_state != NULL);
2033
2034         buffer_format = g_new0 (WPTextBufferFormat, 1);
2035         current_format = g_new0 (WPTextBufferFormat, 1);
2036
2037         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2038         gtk_widget_grab_focus (priv->msg_body);
2039         buffer_format->bold = (format_state->bold != FALSE);
2040         buffer_format->italic = (format_state->italics != FALSE);
2041         buffer_format->color = format_state->color;
2042         buffer_format->font_size = format_state->font_size;
2043         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
2044         buffer_format->justification = format_state->justification;
2045         buffer_format->bullet = format_state->bullet;
2046
2047         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
2048
2049         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
2050         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
2051         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
2052         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
2053         buffer_format->cs.font = (buffer_format->font != current_format->font);
2054         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
2055         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
2056
2057         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
2058         if (buffer_format->cs.bold) {
2059                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
2060                                               GINT_TO_POINTER (buffer_format->bold&0x1));
2061         }
2062         if (buffer_format->cs.italic) {
2063                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
2064                                               GINT_TO_POINTER (buffer_format->italic&0x1));
2065         }
2066         if (buffer_format->cs.color) {
2067                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2068                                               GINT_TO_POINTER (&(buffer_format->color)));
2069         }
2070         if (buffer_format->cs.font_size) {
2071                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2072                                               GINT_TO_POINTER (buffer_format->font_size));
2073         }
2074         if (buffer_format->cs.justification) {
2075                 switch (buffer_format->justification) {
2076                 case GTK_JUSTIFY_LEFT:
2077                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
2078                                                       GINT_TO_POINTER(TRUE));
2079                         break;
2080                 case GTK_JUSTIFY_CENTER:
2081                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
2082                                                       GINT_TO_POINTER(TRUE));
2083                         break;
2084                 case GTK_JUSTIFY_RIGHT:
2085                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
2086                                                       GINT_TO_POINTER(TRUE));
2087                         break;
2088                 default:
2089                         break;
2090                 }
2091                         
2092         }
2093         if (buffer_format->cs.font) {
2094                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
2095                                               GINT_TO_POINTER (buffer_format->font));
2096         }
2097         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
2098         if (buffer_format->cs.bullet) {
2099                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
2100                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
2101         }
2102 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
2103         
2104         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
2105         
2106         g_free (buffer_format);
2107         g_free (current_format);
2108
2109         /* Check dimming rules */
2110         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2111         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2112 }
2113
2114 static void
2115 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
2116 {
2117         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2118         GtkAction *action;
2119         ModestWindowPrivate *parent_priv;
2120         ModestMsgEditWindowPrivate *priv;
2121         
2122         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2123         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2124
2125         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
2126                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu");
2127                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2128                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2129         } else {
2130                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatPlainTextMenu");
2131                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2132                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2133         }
2134
2135         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2136
2137         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
2138         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
2139
2140         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
2141         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
2142
2143 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
2144 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
2145
2146         action = NULL;
2147         switch (buffer_format->justification)
2148         {
2149         case GTK_JUSTIFY_LEFT:
2150                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu");
2151                 break;
2152         case GTK_JUSTIFY_CENTER:
2153                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu");
2154                 break;
2155         case GTK_JUSTIFY_RIGHT:
2156                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu");
2157                 break;
2158         default:
2159                 break;
2160         }
2161         
2162         if (action != NULL)
2163                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2164         
2165         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
2166                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
2167                                          window);
2168         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
2169         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
2170                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
2171                                            window);
2172
2173         if (priv->current_size_index != buffer_format->font_size) {
2174                 GtkTreeIter iter;
2175                 GtkTreePath *path;
2176
2177                 path = gtk_tree_path_new_from_indices (buffer_format->font_size, -1);
2178                 if (gtk_tree_model_get_iter (priv->sizes_model, &iter, path)) {
2179                         gchar *size_text;
2180                         gchar *markup;
2181
2182                         priv->current_size_index = buffer_format->font_size;
2183
2184                         gtk_tree_model_get (priv->sizes_model, &iter, 0, &size_text, -1);
2185                         markup = g_strconcat ("<span font_family='Sans'>", 
2186                                               size_text, "</span>", NULL);
2187                         
2188                         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2189                         g_free (markup);
2190                         g_free (size_text);
2191                 }
2192                 gtk_tree_path_free (path);              
2193         }
2194
2195         if (priv->current_face_index != buffer_format->font) {
2196                 GtkTreeIter iter;
2197                 GtkTreePath *path;
2198
2199                 path = gtk_tree_path_new_from_indices (buffer_format->font, -1);
2200                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2201                         gchar *face_name;
2202                         gchar *markup;
2203
2204                         priv->current_face_index = buffer_format->font;
2205                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2206                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2207                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2208                         g_free (face_name);
2209                         g_free (markup);
2210                 }
2211
2212         }
2213
2214         g_free (buffer_format);
2215
2216 }
2217
2218
2219 void
2220 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2221 {
2222         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2223         ModestMsgEditWindowPrivate *priv;
2224         GtkWidget *dialog = NULL;
2225
2226         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2227         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2228
2229         dialog = hildon_color_chooser_new ();
2230         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2231         g_free (buffer_format);
2232
2233         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2234                 GdkColor col;
2235                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2236                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2237                                               (gpointer) &col);
2238         }
2239         gtk_widget_destroy (dialog);
2240 }
2241
2242
2243 void
2244 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2245 {
2246         ModestMsgEditWindowPrivate *priv;
2247         GtkWidget *dialog = NULL;
2248         GdkColor *old_color = NULL;
2249         
2250         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2251         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2252         
2253         dialog = hildon_color_chooser_new ();
2254         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2255
2256         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2257                 GdkColor col;
2258                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2259                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2260         }
2261         gtk_widget_destroy (dialog);
2262 }
2263
2264
2265 static TnyStream*
2266 create_stream_for_uri (const gchar* uri)
2267 {
2268         if (!uri)
2269                 return NULL;
2270                 
2271         TnyStream *result = NULL;
2272
2273         GnomeVFSHandle *handle = NULL;
2274         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2275         if (test == GNOME_VFS_OK) {
2276                 TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle));
2277                 /* Streams over OBEX (Bluetooth) are not seekable but
2278                  * we expect them to be (we might need to read them
2279                  * several times). So if this is a Bluetooth URI just
2280                  * read the whole file into memory (this is not a fast
2281                  * protocol so we can assume that these files are not
2282                  * going to be very big) */
2283                 if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)||
2284                     (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) {
2285                         TnyStream *memstream = tny_camel_mem_stream_new ();
2286                         tny_stream_write_to_stream (vfssstream, memstream);
2287                         g_object_unref (vfssstream);
2288                         result = memstream;
2289                 } else {
2290                         result = vfssstream;
2291                 }
2292         }
2293         
2294         return result;
2295 }
2296
2297 void
2298 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2299 {
2300         
2301         ModestMsgEditWindowPrivate *priv;
2302         GtkWidget *dialog = NULL;
2303         gint response = 0;
2304         GSList *uris = NULL;
2305         GSList *uri_node = NULL;
2306
2307         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2308
2309         dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
2310                                                                     _("mcen_ia_select_inline_image_title"),
2311                                                                     (GtkWindow *) window,
2312                                                                     GTK_FILE_CHOOSER_ACTION_OPEN);
2313         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2314
2315         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2316
2317         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2318                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2319
2320         response = gtk_dialog_run (GTK_DIALOG (dialog));
2321         switch (response) {
2322         case GTK_RESPONSE_OK:
2323         {
2324                 gchar *current_folder;
2325                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2326                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2327                 if (current_folder && current_folder != '\0') {
2328                         GError *err = NULL;
2329                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_INSERT_IMAGE_PATH, 
2330                                                 current_folder, &err);
2331                         if (err != NULL) {
2332                                 g_debug ("Error storing latest used folder: %s", err->message);
2333                                 g_error_free (err);
2334                         }
2335                 }
2336                 g_free (current_folder);
2337         }
2338         break;
2339         default:
2340                 break;
2341         }
2342         gtk_widget_destroy (dialog);
2343
2344         g_object_ref (window);
2345         /* The operation could take some time so allow the dialog to be closed */
2346         while (gtk_events_pending ())
2347                 gtk_main_iteration ();
2348
2349         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2350                 const gchar *uri;
2351                 GnomeVFSHandle *handle = NULL;
2352                 GnomeVFSResult result;
2353                 GtkTextIter position;
2354                 GtkTextMark *insert_mark;
2355
2356                 uri = (const gchar *) uri_node->data;
2357                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2358                 if (result == GNOME_VFS_OK) {
2359                         GdkPixbuf *pixbuf;
2360                         GnomeVFSFileInfo *info;
2361                         gchar *filename, *basename, *escaped_filename;
2362                         TnyMimePart *mime_part;
2363                         gchar *content_id;
2364                         const gchar *mime_type = NULL;
2365                         GnomeVFSURI *vfs_uri;
2366                         guint64 stream_size;
2367
2368                         gnome_vfs_close (handle);
2369                         vfs_uri = gnome_vfs_uri_new (uri);
2370
2371                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2372                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2373                         g_free (escaped_filename);
2374                         gnome_vfs_uri_unref (vfs_uri);
2375                         info = gnome_vfs_file_info_new ();
2376
2377                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2378                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2379                             == GNOME_VFS_OK)
2380                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2381
2382                         mime_part = tny_platform_factory_new_mime_part
2383                                 (modest_runtime_get_platform_factory ());
2384
2385                         TnyStream *stream = create_stream_for_uri (uri);
2386
2387                         if (stream == NULL) {
2388
2389                                 modest_platform_information_banner (NULL, NULL, 
2390                                                                     _FM("sfil_ib_opening_not_allowed"));
2391                                 g_free (filename);
2392                                 g_object_unref (mime_part);
2393                                 gnome_vfs_file_info_unref (info);
2394                                 continue;
2395                         }
2396
2397                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2398
2399                         content_id = g_strdup_printf ("%d", priv->next_cid);
2400                         tny_mime_part_set_content_id (mime_part, content_id);
2401                         g_free (content_id);
2402                         priv->next_cid++;
2403
2404                         basename = g_path_get_basename (filename);
2405                         tny_mime_part_set_filename (mime_part, basename);
2406                         g_free (basename);
2407
2408                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size, window);
2409
2410                         if (pixbuf != NULL) {
2411                                 priv->images_size += stream_size;
2412                                 priv->images_count ++;
2413                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2414                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2415                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, 
2416                                                              tny_mime_part_get_content_id (mime_part), pixbuf);
2417                                 g_object_unref (pixbuf);
2418
2419                                 tny_list_prepend (priv->images, (GObject *) mime_part);
2420                                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2421                         } else {
2422                                 modest_platform_information_banner (NULL, NULL,
2423                                                                     _("mail_ib_file_operation_failed"));
2424                         }
2425
2426                         g_free (filename);
2427                         g_object_unref (mime_part);
2428                         gnome_vfs_file_info_unref (info);
2429
2430                 }
2431         }
2432         g_object_unref (window);
2433 }
2434
2435 static void
2436 on_attach_file_response (GtkDialog *dialog,
2437                          gint       arg1,
2438                          gpointer   user_data)
2439 {
2440         GSList *uris = NULL;
2441         GSList *uri_node;
2442         GnomeVFSFileSize total_size, allowed_size;
2443         ModestMsgEditWindow *window;
2444         ModestMsgEditWindowPrivate *priv;
2445         gint att_num;
2446         guint64 att_size;
2447
2448         switch (arg1) {
2449         case GTK_RESPONSE_OK:
2450         {
2451                 gchar *current_folder;
2452
2453                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2454                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2455                 if (current_folder && current_folder != '\0') {
2456                         GError *err = NULL;
2457                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_ATTACH_FILE_PATH, 
2458                                                 current_folder, &err);
2459                         if (err != NULL) {
2460                                 g_debug ("Error storing latest used folder: %s", err->message);
2461                                 g_error_free (err);
2462                         }
2463                 }
2464                 g_free (current_folder);
2465         }
2466         break;
2467         default:
2468                 break;
2469         }
2470
2471         window = MODEST_MSG_EDIT_WINDOW (user_data);
2472         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2473
2474         /* allowed size is the maximum size - what's already there */
2475         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2476                                            &att_num, &att_size);
2477         allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size;
2478
2479         total_size = 0;
2480         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2481
2482                 const gchar *uri = (const gchar *) uri_node->data;
2483
2484                 total_size += 
2485                         modest_msg_edit_window_attach_file_one (window, uri, allowed_size);
2486
2487                 if (total_size > allowed_size) {
2488                         g_debug ("%s: total size: %u", 
2489                                    __FUNCTION__, (unsigned int)total_size);
2490                         break;
2491                 }
2492                 allowed_size -= total_size;
2493         }
2494         g_slist_foreach (uris, (GFunc) g_free, NULL);
2495         g_slist_free (uris);
2496
2497         gtk_widget_destroy (GTK_WIDGET (dialog));
2498 }
2499
2500 void
2501 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2502 {
2503         GtkWidget *dialog = NULL;
2504         ModestMsgEditWindowPrivate *priv;
2505         gchar *conf_folder;
2506
2507         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window));
2508
2509         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2510
2511         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2512                 return;
2513
2514         dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
2515                                                                     _("mcen_ia_select_attachment_title"),
2516                                                                     (GtkWindow *) window,
2517                                                                     GTK_FILE_CHOOSER_ACTION_OPEN);
2518         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
2519                                               MODEST_CONF_LATEST_ATTACH_FILE_PATH, NULL);
2520         if (conf_folder && conf_folder[0] != '\0') {
2521                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), conf_folder);
2522         } else {
2523                 gchar *docs_folder;
2524                 /* Set the default folder to documents folder */
2525                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
2526                 if (!docs_folder) {
2527                         /* fallback */
2528                         docs_folder = g_build_filename (g_getenv (MODEST_MAEMO_UTILS_MYDOCS_ENV),
2529                                                         ".documents", NULL);
2530                 }
2531                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), docs_folder);
2532                 g_free (docs_folder);
2533         }
2534         g_free (conf_folder);
2535         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2536         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2537                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2538
2539         /* Connect to response & show */
2540         g_signal_connect (dialog, "response", 
2541                           G_CALLBACK (on_attach_file_response), window);
2542         gtk_widget_show (dialog);
2543 }
2544
2545
2546 GnomeVFSFileSize
2547 modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window,
2548                                         const gchar *uri, 
2549                                         GnomeVFSFileSize allowed_size)
2550
2551 {
2552         GnomeVFSHandle *handle = NULL;
2553         ModestMsgEditWindowPrivate *priv;
2554         GnomeVFSResult result;
2555         GnomeVFSFileSize size = 0;
2556         g_return_val_if_fail (window, 0);
2557         g_return_val_if_fail (uri, 0);
2558
2559         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2560
2561         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2562         if (result == GNOME_VFS_OK) {
2563                 TnyMimePart *mime_part;
2564                 TnyStream *stream;
2565                 const gchar *mime_type = NULL;
2566                 gchar *basename;
2567                 gchar *escaped_filename;
2568                 gchar *filename;
2569                 gchar *content_id;
2570                 GnomeVFSFileInfo *info;
2571                 GnomeVFSURI *vfs_uri;
2572
2573                 gnome_vfs_close (handle);
2574                 vfs_uri = gnome_vfs_uri_new (uri);
2575                 
2576
2577                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2578                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2579                 g_free (escaped_filename);
2580                 gnome_vfs_uri_unref (vfs_uri);
2581
2582                 info = gnome_vfs_file_info_new ();
2583                 
2584                 if (gnome_vfs_get_file_info (uri, 
2585                                              info, 
2586                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2587                     == GNOME_VFS_OK)
2588                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2589                 mime_part = tny_platform_factory_new_mime_part
2590                         (modest_runtime_get_platform_factory ());
2591                 
2592                 /* try to get the attachment's size; this may fail for weird
2593                  * file systems, like obex, upnp... */
2594                 if (allowed_size != 0 &&
2595                     info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
2596                         size = info->size;
2597                         if (size > allowed_size) {
2598                                 modest_platform_information_banner (NULL, NULL,
2599                                                                     _("mail_ib_error_attachment_size"));
2600                                 g_free (filename);
2601                                 return 0;
2602                         }
2603                 } else
2604                         g_debug ("%s: could not get attachment size", __FUNCTION__);
2605                 
2606                 stream = create_stream_for_uri (uri);
2607                 
2608                 if (stream == NULL) {
2609
2610                         modest_platform_information_banner (NULL, NULL, _FM("sfil_ib_opening_not_allowed"));
2611
2612                         g_object_unref (mime_part);
2613                         g_free (filename);
2614                         gnome_vfs_file_info_unref (info);
2615                         return 0;
2616                 }
2617
2618                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2619                 g_object_unref (stream);
2620                 
2621                 content_id = g_strdup_printf ("%d", priv->next_cid);
2622                 tny_mime_part_set_content_id (mime_part, content_id);
2623                 g_free (content_id);
2624                 priv->next_cid++;
2625                 
2626                 basename = g_path_get_basename (filename);
2627                 tny_mime_part_set_filename (mime_part, basename);
2628                 g_free (basename);
2629                 
2630                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2631                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2632                                                         mime_part,
2633                                                         info->size == 0, info->size);
2634                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2635                 gtk_widget_show_all (priv->attachments_caption);
2636                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2637                 g_free (filename);
2638                 g_object_unref (mime_part);
2639                 gnome_vfs_file_info_unref (info);
2640         }
2641
2642         return size;
2643 }
2644
2645 void
2646 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2647                                            TnyList *att_list)
2648 {
2649         ModestMsgEditWindowPrivate *priv;
2650         TnyIterator *iter;
2651
2652         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2653         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2654
2655         if (att_list == NULL) {
2656                 att_list = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2657                 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), att_list, TRUE)) {
2658                         g_object_unref (att_list);
2659                         return;
2660                 }
2661         } else {
2662                 g_object_ref (att_list);
2663         }
2664
2665         if (tny_list_get_length (att_list) == 0) {
2666                 modest_platform_system_banner (NULL, NULL, _("TODO: no attachments selected to remove"));
2667         } else {
2668                 gboolean dialog_response;
2669                 gchar *message = NULL;
2670                 gchar *filename = NULL;
2671
2672                 if (tny_list_get_length (att_list) == 1) {
2673                         TnyMimePart *part;
2674                         iter = tny_list_create_iterator (att_list);
2675                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2676                         g_object_unref (iter);
2677                         if (TNY_IS_MSG (part)) {
2678                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2679                                 if (header) {
2680                                         filename = tny_header_dup_subject (header);
2681                                         g_object_unref (header);
2682                                 }
2683                                 if (filename == NULL) {
2684                                         filename = g_strdup (_("mail_va_no_subject"));
2685                                 }
2686                         } else {
2687                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2688                         }
2689                         g_object_unref (part);
2690                 } else {
2691                         filename = g_strdup ("");
2692                 }
2693                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", 
2694                                                     "emev_nc_delete_attachments",
2695                                                     tny_list_get_length (att_list)), filename);
2696                 g_free (filename);
2697
2698                 dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), 
2699                                                                            message);
2700                 g_free (message);
2701
2702                 if (dialog_response != GTK_RESPONSE_OK) {
2703                         g_object_unref (att_list);
2704                         return;
2705                 }
2706
2707                 for (iter = tny_list_create_iterator (att_list);
2708                      !tny_iterator_is_done (iter);
2709                      tny_iterator_next (iter)) {
2710                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2711                         const gchar *att_id;
2712                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2713
2714                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2715                                                                    mime_part);
2716                         if (tny_list_get_length (priv->attachments) == 0)
2717                                 gtk_widget_hide (priv->attachments_caption);
2718                         att_id = tny_mime_part_get_content_id (mime_part);
2719                         if (att_id != NULL)
2720                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2721                                                                  att_id);
2722                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2723                         g_object_unref (mime_part);
2724                 }
2725                 g_object_unref (iter);
2726         }
2727
2728         g_object_unref (att_list);
2729
2730         /* if the last attachment has been removed, focus the Subject: field */
2731         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view)))
2732                 gtk_widget_grab_focus (priv->subject_field);
2733 }
2734
2735 static void
2736 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2737                                             gpointer userdata)
2738 {
2739         ModestMsgEditWindowPrivate *priv;
2740         GdkColor new_color;
2741
2742         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2743
2744         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &new_color);
2745
2746         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) &new_color);
2747
2748         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2749 }
2750
2751 static void
2752 font_size_clicked (GtkToolButton *button,
2753                    ModestMsgEditWindow *window)
2754 {
2755         ModestMsgEditWindowPrivate *priv;
2756         GtkWidget *selector, *dialog;
2757         
2758         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2759
2760         selector = hildon_touch_selector_new ();
2761         hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector), priv->sizes_model, TRUE);
2762         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_size_index);
2763
2764         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2765         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2766         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_size"));
2767
2768         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2769                 gint new_index;
2770                 gchar *size_text;
2771                 gchar *markup;
2772                 WPTextBufferFormat format;
2773
2774                 new_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2775
2776                 memset (&format, 0, sizeof (format));
2777                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2778
2779                 format.cs.font_size = TRUE;
2780                 format.cs.text_position = TRUE;
2781                 format.cs.font = TRUE;
2782                 format.font_size = new_index;
2783 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2784
2785                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2786                                                    GINT_TO_POINTER (new_index)))
2787                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2788                 
2789                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2790                 size_text = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
2791                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", 
2792                                       size_text, "</span>", NULL);
2793                 g_free (size_text);
2794                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2795                 g_free (markup);
2796
2797         }
2798         gtk_widget_destroy (dialog);
2799
2800         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2801
2802 }
2803
2804 static void
2805 font_face_clicked (GtkToolButton *button,
2806                    ModestMsgEditWindow *window)
2807 {
2808         ModestMsgEditWindowPrivate *priv;
2809         GtkWidget *selector, *dialog;
2810         GtkCellRenderer *renderer;
2811         
2812         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2813
2814         selector = hildon_touch_selector_new ();
2815         renderer = gtk_cell_renderer_text_new ();
2816         g_object_set (G_OBJECT (renderer), "alignment", PANGO_ALIGN_CENTER, "xalign", 0.5, NULL);
2817         hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector), priv->faces_model, 
2818                                              renderer, "family", 0, "text", 0, NULL);
2819         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_face_index);
2820
2821         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2822         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2823         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_face"));
2824
2825         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2826                 gint new_font_index;
2827                 GtkTreePath *path;
2828                 GtkTreeIter iter;
2829
2830                 new_font_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2831                 path = gtk_tree_path_new_from_indices (new_font_index, -1);
2832                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2833                         gchar *face_name;
2834                         gchar *markup;
2835
2836                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2837
2838                         if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2839                                                            GINT_TO_POINTER(new_font_index)))
2840                                 wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2841
2842                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2843                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2844
2845                         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2846                         g_free (face_name);
2847                         g_free (markup);
2848                 }
2849                 gtk_tree_path_free (path);
2850
2851         }
2852         gtk_widget_destroy (dialog);
2853
2854         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2855
2856 }
2857
2858 void
2859 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2860                                 gboolean show)
2861 {
2862         ModestMsgEditWindowPrivate *priv = NULL;
2863         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2864
2865         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2866         if (!priv->update_caption_visibility)
2867                 return;
2868
2869         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2870         if (show)
2871                 gtk_widget_show (priv->cc_caption);
2872         else
2873                 gtk_widget_hide (priv->cc_caption);
2874
2875         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2876 }
2877
2878 void
2879 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2880                                  gboolean show)
2881 {
2882         ModestMsgEditWindowPrivate *priv = NULL;
2883         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2884
2885         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2886         if (!priv->update_caption_visibility)
2887                 return;
2888
2889         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2890         if (show)
2891                 gtk_widget_show (priv->bcc_caption);
2892         else
2893                 gtk_widget_hide (priv->bcc_caption);
2894
2895         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2896 }
2897
2898 static void
2899 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2900                                          ModestRecptEditor *editor)
2901 {
2902         ModestMsgEditWindowPrivate *priv;
2903
2904         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2905         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2906         
2907         /* we check for low-mem; in that case, show a warning, and don't allow
2908          * for the addressbook
2909          */
2910         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2911                 return;
2912
2913         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2914
2915         if (editor == NULL) {
2916                 GtkWidget *view_focus, *parent;
2917                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2918
2919                 /* This code should be kept in sync with ModestRecptEditor. The
2920                    textview inside the recpt editor is the one that really gets the
2921                    focus. As it's inside a scrolled window, and this one inside the
2922                    hbox recpt editor inherits from, we'll need to go up in the 
2923                    hierarchy to know if the text view is part of the recpt editor
2924                    or if it's a different text entry */
2925                 parent = gtk_widget_get_parent (view_focus);
2926                 if (parent && MODEST_IS_RECPT_EDITOR (parent))
2927                         editor = MODEST_RECPT_EDITOR (parent);
2928
2929                 if (editor == NULL)
2930                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2931         }
2932
2933         modest_address_book_select_addresses (editor, GTK_WINDOW (window));
2934 }
2935
2936 void
2937 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2938 {
2939         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2940
2941         modest_msg_edit_window_open_addressbook (window, NULL);
2942 }
2943
2944 static void
2945 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2946                                      gboolean show_toolbar)
2947 {
2948         ModestWindowPrivate *parent_priv;
2949         ModestMsgEditWindowPrivate *priv;
2950
2951         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2952         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2953         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2954
2955         /* We can not just use the code of
2956            modest_msg_edit_window_setup_toolbar because it has a
2957            mixture of both initialization and creation code. */
2958         if (show_toolbar) {
2959                 gint current_format;
2960                 current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2961                         ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2962                 if (current_format == MODEST_FILE_FORMAT_PLAIN_TEXT) {
2963                         gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2964                 } else {
2965                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2966                 }
2967         } else {
2968                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2969         }
2970 }
2971
2972 void
2973 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2974                                            TnyHeaderFlags priority_flags)
2975 {
2976         ModestMsgEditWindowPrivate *priv;
2977         ModestWindowPrivate *parent_priv;
2978
2979         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2980
2981         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2982         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2983
2984         if (priv->priority_flags != priority_flags) {
2985                 GtkAction *priority_action = NULL;
2986
2987                 priv->priority_flags = priority_flags;
2988
2989                 switch (priority_flags) {
2990                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2991                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2992                                                       MODEST_HEADER_ICON_HIGH, 
2993                                                       HILDON_ICON_SIZE_SMALL);
2994                         gtk_widget_show (priv->priority_icon);
2995                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2996                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2997                         break;
2998                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2999                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
3000                                                       MODEST_HEADER_ICON_LOW,
3001                                                       HILDON_ICON_SIZE_SMALL);
3002                         gtk_widget_show (priv->priority_icon);
3003                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3004                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
3005                         break;
3006                 default:
3007                         gtk_widget_hide (priv->priority_icon);
3008                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3009                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
3010                         break;
3011                 }
3012                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
3013                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3014         }
3015         gtk_widget_queue_resize (priv->subject_box);
3016 }
3017
3018 void
3019 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
3020                                         gint file_format)
3021 {
3022         ModestMsgEditWindowPrivate *priv;
3023         ModestWindowPrivate *parent_priv;
3024         gint current_format;
3025
3026         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3027
3028         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3029         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3030
3031         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
3032                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
3033
3034         gtk_widget_set_no_show_all (GTK_WIDGET (parent_priv->toolbar), TRUE);
3035
3036         if (current_format != file_format) {
3037                 switch (file_format) {
3038                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
3039                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
3040                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
3041                         if (parent_priv->toolbar)
3042                                 gtk_widget_show (parent_priv->toolbar);
3043                         break;
3044                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
3045                 {
3046                         GtkWidget *dialog;
3047                         gint response;
3048                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
3049                         response = gtk_dialog_run (GTK_DIALOG (dialog));
3050                         gtk_widget_destroy (dialog);
3051                         if (response == GTK_RESPONSE_OK) {
3052                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
3053                                 if (parent_priv->toolbar)
3054                                         gtk_widget_hide (parent_priv->toolbar);
3055                         } else {
3056                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu"));
3057                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
3058                         }
3059                 }
3060                         break;
3061                 }
3062                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3063                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3064                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3065         }
3066 }
3067
3068 void
3069 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
3070 {
3071         GtkWidget *dialog;
3072         ModestMsgEditWindowPrivate *priv;
3073         WPTextBufferFormat oldfmt, fmt;
3074         gint old_position = 0;
3075         gint response = 0;
3076         gint position = 0;
3077         gint font_size;
3078         GdkColor *color = NULL;
3079         gboolean bold, bold_set, italic, italic_set;
3080         gboolean underline, underline_set;
3081         gboolean strikethrough, strikethrough_set;
3082         gboolean position_set;
3083         gboolean font_size_set, font_set, color_set;
3084         gchar *font_name;
3085
3086         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3087         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3088         
3089         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
3090         modest_window_mgr_set_modal (modest_runtime_get_window_mgr(),
3091                                      GTK_WINDOW(dialog), GTK_WINDOW (window));
3092
3093         /* First we get the currently selected font information */
3094         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
3095
3096         switch (oldfmt.text_position) {
3097         case TEXT_POSITION_NORMAL:
3098                 old_position = 0;
3099                 break;
3100         case TEXT_POSITION_SUPERSCRIPT:
3101                 old_position = 1;
3102                 break;
3103         default:
3104                 old_position = -1;
3105                 break;
3106         }
3107
3108         g_object_set (G_OBJECT (dialog),
3109                       "bold", oldfmt.bold != FALSE,
3110                       "bold-set", !oldfmt.cs.bold,
3111                       "underline", oldfmt.underline != FALSE,
3112                       "underline-set", !oldfmt.cs.underline,
3113                       "italic", oldfmt.italic != FALSE,
3114                       "italic-set", !oldfmt.cs.italic,
3115                       "strikethrough", oldfmt.strikethrough != FALSE,
3116                       "strikethrough-set", !oldfmt.cs.strikethrough,
3117                       "color", &oldfmt.color,
3118                       "color-set", !oldfmt.cs.color,
3119                       "size", wp_font_size[oldfmt.font_size],
3120                       "size-set", !oldfmt.cs.font_size,
3121                       "position", old_position,
3122                       "position-set", !oldfmt.cs.text_position,
3123                       "family", wp_get_font_name (oldfmt.font),
3124                       "family-set", !oldfmt.cs.font,
3125                       NULL);
3126
3127         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
3128                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
3129         gtk_widget_show_all (dialog);
3130         priv->font_dialog = dialog;
3131         response = gtk_dialog_run (GTK_DIALOG (dialog));
3132         priv->font_dialog = NULL;
3133         if (response == GTK_RESPONSE_OK) {
3134
3135                 g_object_get( dialog,
3136                               "bold", &bold,
3137                               "bold-set", &bold_set,
3138                               "underline", &underline,
3139                               "underline-set", &underline_set,
3140                               "italic", &italic,
3141                               "italic-set", &italic_set,
3142                               "strikethrough", &strikethrough,
3143                               "strikethrough-set", &strikethrough_set,
3144                               "color", &color,
3145                               "color-set", &color_set,
3146                               "size", &font_size,
3147                               "size-set", &font_size_set,
3148                               "family", &font_name,
3149                               "family-set", &font_set,
3150                               "position", &position,
3151                               "position-set", &position_set,
3152                               NULL );
3153                 
3154         }       
3155
3156         if (response == GTK_RESPONSE_OK) {
3157                 memset(&fmt, 0, sizeof(fmt));
3158                 if (bold_set) {
3159                         fmt.bold = bold;
3160                         fmt.cs.bold = TRUE;
3161                 }
3162                 if (italic_set) {
3163                         fmt.italic = italic;
3164                         fmt.cs.italic = TRUE;
3165                 }
3166                 if (underline_set) {
3167                         fmt.underline = underline;
3168                         fmt.cs.underline = TRUE;
3169                 }
3170                 if (strikethrough_set) {
3171                         fmt.strikethrough = strikethrough;
3172                         fmt.cs.strikethrough = TRUE;
3173                 }
3174                 if (position_set) {
3175                         fmt.text_position =
3176                                 ( position == 0 )
3177                                 ? TEXT_POSITION_NORMAL
3178                                 : ( ( position == 1 )
3179                                     ? TEXT_POSITION_SUPERSCRIPT
3180                                     : TEXT_POSITION_SUBSCRIPT );
3181                         fmt.cs.text_position = TRUE;
3182                         fmt.font_size = oldfmt.font_size;
3183                 }
3184                 if (color_set) {
3185                         fmt.color = *color;
3186                         fmt.cs.color = TRUE;
3187                 }
3188                 if (font_set) {
3189                         fmt.font = wp_get_font_index(font_name,
3190                                                      DEFAULT_FONT);
3191                         fmt.cs.font = TRUE;
3192                 }
3193                 g_free(font_name);
3194                 if (font_size_set) {
3195                         fmt.cs.font_size = TRUE;
3196                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
3197                 }
3198                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
3199                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3200         }
3201         gtk_widget_destroy (dialog);
3202         
3203         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
3204 }
3205
3206 void
3207 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
3208 {
3209         ModestMsgEditWindowPrivate *priv;
3210         ModestWindowPrivate *parent_priv;
3211         gboolean was_rich_text, is_rich_text;
3212
3213         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3214         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3215         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3216
3217         was_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3218
3219         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
3220
3221         is_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3222
3223         if (parent_priv->toolbar && was_rich_text != is_rich_text) {
3224                 if (is_rich_text)
3225                         gtk_widget_show (parent_priv->toolbar);
3226                 else
3227                         gtk_widget_hide (parent_priv->toolbar);
3228         }
3229
3230         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3231         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3232 }
3233
3234 void
3235 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
3236 {
3237         ModestMsgEditWindowPrivate *priv;
3238
3239         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3240         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3241         
3242         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
3243
3244         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3245         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3246
3247 }
3248
3249 static void  
3250 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
3251 {
3252         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3253
3254         priv->can_undo = can_undo;
3255 }
3256
3257 static void  
3258 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
3259 {
3260         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3261
3262         priv->can_redo = can_redo;
3263 }
3264
3265 gboolean            
3266 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
3267 {
3268         ModestMsgEditWindowPrivate *priv;
3269         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3270         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3271
3272         return priv->can_undo;
3273 }
3274
3275 gboolean            
3276 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
3277 {
3278         ModestMsgEditWindowPrivate *priv;
3279         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3280         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3281
3282         return priv->can_redo;
3283 }
3284
3285
3286 static void
3287 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
3288 {
3289         GtkTextIter iter;
3290         GtkTextIter match_start, match_end;
3291
3292         if (image_id == NULL)
3293                 return;
3294
3295         gtk_text_buffer_get_start_iter (buffer, &iter);
3296
3297         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
3298                 GSList *tags = gtk_text_iter_get_tags (&match_start);
3299                 GSList *node;
3300                 for (node = tags; node != NULL; node = g_slist_next (node)) {
3301                         GtkTextTag *tag = (GtkTextTag *) node->data;
3302                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
3303                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
3304                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
3305                                         gint offset;
3306                                         offset = gtk_text_iter_get_offset (&match_start);
3307                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
3308                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
3309                                 }
3310                         }
3311                 }
3312                 gtk_text_iter_forward_char (&iter);
3313         }
3314 }
3315
3316 gboolean
3317 message_is_empty (ModestMsgEditWindow *window)
3318 {
3319         ModestMsgEditWindowPrivate *priv = NULL;
3320
3321         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3322         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3323
3324         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
3325          * so we can ignore markup.
3326          */
3327         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
3328         gint count = 0;
3329         if (buf)
3330                 count = gtk_text_buffer_get_char_count (buf);
3331
3332         return count == 0;
3333 }
3334
3335 static gboolean
3336 msg_body_focus (GtkWidget *focus,
3337                 GdkEventFocus *event,
3338                 gpointer userdata)
3339 {
3340         
3341         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
3342         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
3343         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
3344         return FALSE;
3345 }
3346
3347 static void
3348 recpt_field_changed (GtkTextBuffer *buffer,
3349                   ModestMsgEditWindow *editor)
3350 {
3351         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3352         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3353 }
3354
3355 static void
3356 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
3357 {
3358         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3359         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3360 }
3361
3362 void
3363 modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor,
3364                                      gboolean modified)
3365 {
3366         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3367         GtkTextBuffer *buffer;
3368
3369         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3370         gtk_text_buffer_set_modified (buffer, modified);
3371         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3372         gtk_text_buffer_set_modified (buffer, modified);
3373         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3374         gtk_text_buffer_set_modified (buffer, modified);
3375         gtk_text_buffer_set_modified (priv->text_buffer, modified);
3376 }
3377
3378 gboolean
3379 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
3380 {
3381         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3382         const char *account_name;
3383         GtkTextBuffer *buffer;
3384
3385         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3386         if (gtk_text_buffer_get_modified (buffer))
3387                 return TRUE;
3388         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3389         if (gtk_text_buffer_get_modified (buffer))
3390                 return TRUE;
3391         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3392         if (gtk_text_buffer_get_modified (buffer))
3393                 return TRUE;
3394         if (gtk_text_buffer_get_modified (priv->text_buffer))
3395                 return TRUE;
3396
3397         account_name = modest_selector_get_active_id (priv->from_field);
3398         if (priv->original_mailbox) {
3399                 if (!account_name || strcmp (account_name, priv->original_mailbox))
3400                         return TRUE;
3401         } else if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) {
3402                 return TRUE;
3403         }
3404
3405         return FALSE;
3406 }
3407
3408
3409
3410
3411 gboolean
3412 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
3413 {
3414         ModestMsgEditWindowPrivate *priv = NULL;
3415         GSList *address_list = NULL;
3416
3417         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3418         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3419
3420         /* check if there's no recipient added */
3421         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3422             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3423             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3424                 /* no recipient contents, then select contacts */
3425                 modest_msg_edit_window_open_addressbook (window, NULL);
3426                 return FALSE;
3427         }
3428
3429         /* Check names */
3430         g_object_ref (window);
3431         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),
3432                                               (add_to_addressbook) ? &address_list : NULL)) {
3433                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3434                 g_object_unref (window);
3435                 return FALSE;
3436         }
3437         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),
3438                                               (add_to_addressbook) ? &address_list : NULL)) {
3439                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3440                 g_object_unref (window);
3441                 return FALSE;
3442         }
3443         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field),
3444                                               (add_to_addressbook) ? &address_list : NULL)) {
3445                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3446                 g_object_unref (window);
3447                 return FALSE;
3448         }
3449
3450         /* Add contacts to address book */
3451         if (add_to_addressbook && address_list)
3452                 modest_address_book_add_address_list (address_list);
3453
3454         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3455             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3456                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3457         g_object_unref (window);
3458
3459         return TRUE;
3460
3461 }
3462
3463 void
3464 modest_msg_edit_window_add_to_contacts (ModestMsgEditWindow *self)
3465 {
3466         GSList *recipients = NULL;
3467         ModestMsgEditWindowPrivate *priv;
3468         gchar *joined, *after_remove, *to, *cc, *bcc;
3469
3470         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
3471
3472         /* First of all check names */
3473         if (!modest_msg_edit_window_check_names (self, FALSE))
3474                 return;
3475
3476         if (!modest_msg_edit_window_has_pending_addresses (self))
3477                 return;
3478
3479         /* Don't add the from obviously */
3480         to  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->to_field));
3481         cc  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->cc_field));
3482         bcc =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->bcc_field));
3483
3484         joined = modest_text_utils_join_addresses (NULL, to, cc, bcc);
3485         g_free (to);
3486         g_free (cc);
3487         g_free (bcc);
3488
3489         after_remove = modest_text_utils_remove_duplicate_addresses (joined);
3490         g_free (joined);
3491
3492         recipients = modest_text_utils_split_addresses_list (after_remove);
3493         g_free (after_remove);
3494
3495         if (recipients) {
3496                 /* Offer the user to add recipients to the address book */
3497                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3498                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3499         }
3500 }
3501
3502 static void
3503 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3504                                                ModestMsgEditWindow *window)
3505 {
3506         modest_msg_edit_window_offer_attach_file (window);
3507 }
3508
3509 const gchar *
3510 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3511 {
3512         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3513
3514         return priv->clipboard_text;
3515 }
3516
3517 static void
3518 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3519                                                GdkEvent *event,
3520                                                ModestMsgEditWindow *window)
3521 {
3522         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3523         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3524         gchar *text = NULL;
3525
3526         /* It could happen that the window was already closed */
3527         if (!GTK_WIDGET_VISIBLE (window))
3528                 return;
3529
3530         g_object_ref (window);
3531         text = gtk_clipboard_wait_for_text (selection_clipboard);
3532
3533         if (priv->clipboard_text != NULL) {
3534                 g_free (priv->clipboard_text);
3535         }
3536         priv->clipboard_text = text;
3537
3538         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3539
3540         g_object_unref (window);
3541 }
3542
3543 static gboolean clipboard_owner_change_idle (gpointer userdata)
3544 {
3545         ModestMsgEditWindow *window = (ModestMsgEditWindow *) userdata;
3546         ModestMsgEditWindowPrivate *priv;
3547
3548         gdk_threads_enter ();
3549         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3550         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3551
3552         priv->clipboard_owner_idle = 0;
3553         modest_msg_edit_window_clipboard_owner_change (NULL, NULL, window);
3554         gdk_threads_leave ();
3555
3556         return FALSE;
3557 }
3558
3559 static void
3560 modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window)
3561 {
3562         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3563         if (priv->clipboard_owner_idle == 0) {
3564                 priv->clipboard_owner_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
3565                                                               clipboard_owner_change_idle, 
3566                                                               g_object_ref (window),
3567                                                               g_object_unref);
3568         }
3569 }
3570
3571 static void 
3572 subject_field_move_cursor (GtkEntry *entry,
3573                            GtkMovementStep step,
3574                            gint a1,
3575                            gboolean a2,
3576                            gpointer window)
3577 {
3578         /* It could happen that the window was already closed */
3579         if (!GTK_WIDGET_VISIBLE (window))
3580                 return;
3581
3582         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3583 }
3584
3585 static void 
3586 update_window_title (ModestMsgEditWindow *window)
3587 {
3588         ModestMsgEditWindowPrivate *priv = NULL;
3589         const gchar *subject;
3590
3591         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3592         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3593         if (subject == NULL || subject[0] == '\0')
3594                 subject = _("mail_va_new_email");
3595
3596         modest_window_set_title (MODEST_WINDOW (window), subject);
3597
3598 }
3599
3600
3601 static void  
3602 body_insert_text (GtkTextBuffer *buffer, 
3603                   GtkTextIter *location,
3604                   gchar *text,
3605                   gint len,
3606                   ModestMsgEditWindow *window)
3607 {
3608         GtkTextIter end_iter;
3609         gint offset;
3610         glong utf8_len;
3611         gint line;
3612         gchar *text_offset;
3613         gint text_lines;
3614
3615         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3616
3617         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end_iter);
3618
3619         offset = gtk_text_iter_get_offset (&end_iter);
3620         line = gtk_text_iter_get_line (&end_iter);
3621
3622         text_offset = text;
3623         text_lines = 0;
3624         while (text_offset < text + len) {
3625                 if (*text_offset == '\n')
3626                         text_lines++;
3627                 if (text_lines + line >= MAX_BODY_LINES) {
3628                         len = text_offset - text;
3629                         break;
3630                 }
3631                 text_offset++;
3632         }
3633
3634         utf8_len = g_utf8_strlen (text, len);
3635
3636         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3637                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
3638                 if (line <= MAX_BODY_LINES && offset < MAX_BODY_LENGTH)
3639                 {
3640                         gchar *result;
3641                         gchar *utf8_end;
3642
3643                         utf8_end = g_utf8_offset_to_pointer (text, MAX_BODY_LENGTH - offset);
3644
3645                         /* Prevent endless recursion */
3646                         result = g_strndup (text, utf8_end - text);
3647                         g_signal_handlers_block_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3648                         g_signal_emit_by_name (G_OBJECT (buffer), "insert-text", location,
3649                                                (gpointer) result, (gpointer) (utf8_end - text),
3650                                                (gpointer) window);
3651                         g_signal_handlers_unblock_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3652                 }
3653
3654         }
3655         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3656                 if (priv->max_chars_banner == NULL) {
3657                         priv->max_chars_banner = hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3658                                                                                  _CS("ckdg_ib_maximum_characters_reached"));
3659                         g_object_weak_ref (G_OBJECT (priv->max_chars_banner), (GWeakNotify) max_chars_banner_unref, window);
3660                 }
3661         }
3662 }
3663
3664 static void  
3665 subject_field_changed (GtkEditable *editable, 
3666                        ModestMsgEditWindow *window)
3667 {
3668         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3669         update_window_title (window);
3670         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3671         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3672         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3673 }
3674 static void  
3675 subject_field_insert_text (GtkEditable *editable, 
3676                            gchar *new_text,
3677                            gint new_text_length,
3678                            gint *position,
3679                            ModestMsgEditWindow *window)
3680 {
3681         GString *result = g_string_new ("");
3682         gchar *current;
3683         gint result_len = 0;
3684         const gchar *entry_text = NULL;
3685         gint old_length;
3686
3687         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3688         old_length = g_utf8_strlen (entry_text, -1);
3689
3690         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3691                 gunichar c = g_utf8_get_char_validated (current, 8);
3692                 /* Invalid unichar, stop */
3693                 if (c == -1)
3694                         break;
3695                 /* a bullet */
3696                 if (c == 0x2022)
3697                         continue;
3698                 result = g_string_append_unichar (result, c);
3699                 result_len++;
3700         }
3701
3702         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3703                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3704                 if (result_len > 0)
3705                 {
3706                         /* Prevent endless recursion */
3707                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3708                         g_signal_emit_by_name (editable, "insert-text", 
3709                                                (gpointer) result->str, (gpointer) result->len,
3710                                                (gpointer) position, (gpointer) window);
3711                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3712                 }
3713         }
3714
3715         if (result_len + old_length > 1000) {
3716                 modest_platform_system_banner (GTK_WIDGET (window), NULL, 
3717                                                 _CS("ckdg_ib_maximum_characters_reached"));
3718         }
3719         g_string_free (result, TRUE);
3720 }
3721
3722 void
3723 modest_msg_edit_window_toggle_isearch_toolbar (ModestMsgEditWindow *window,
3724                                                gboolean show)
3725 {
3726         ModestMsgEditWindowPrivate *priv = NULL;
3727
3728         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3729         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3730
3731         gtk_widget_set_no_show_all (priv->isearch_toolbar, FALSE);
3732
3733         if (show) {
3734                 gtk_widget_show_all (priv->isearch_toolbar);
3735                 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
3736         } else {
3737                 gtk_widget_hide_all (priv->isearch_toolbar);
3738                 gtk_widget_grab_focus (priv->msg_body);
3739         }
3740 }
3741
3742 static gboolean 
3743 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3744                                           const gchar *str,
3745                                           GtkTextIter *match_start,
3746                                           GtkTextIter *match_end)
3747 {
3748         GtkTextIter end_iter;
3749         gchar *str_casefold;
3750         gint str_chars_n;
3751         gchar *range_text;
3752         gchar *range_casefold;
3753         gint offset;
3754         gint range_chars_n;
3755         gboolean result = FALSE;
3756
3757         if (str == NULL)
3758                 return TRUE;
3759         
3760         /* get end iter */
3761         end_iter = *iter;
3762         gtk_text_iter_forward_to_end (&end_iter);
3763
3764         str_casefold = g_utf8_casefold (str, -1);
3765         str_chars_n = strlen (str);
3766
3767         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3768         range_casefold = g_utf8_casefold (range_text, -1);
3769         range_chars_n = strlen (range_casefold);
3770
3771         if (range_chars_n < str_chars_n) {
3772                 g_free (str_casefold);
3773                 g_free (range_text);
3774                 g_free (range_casefold);
3775                 return FALSE;
3776         }
3777
3778         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3779                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3780                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3781                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3782                         result = TRUE;
3783                         if (!gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3784                                                            match_start, match_end, NULL)) {
3785                                 g_debug ("Matched string with collate, but not matched in model");
3786                         }
3787                         g_free (found_text);
3788                 }
3789                 g_free (range_subtext);
3790                 if (result)
3791                         break;
3792         }
3793         g_free (str_casefold);
3794         g_free (range_text);
3795         g_free (range_casefold);
3796
3797         return result;
3798 }
3799
3800
3801 static void 
3802 modest_msg_edit_window_isearch_toolbar_search (GtkWidget *widget,
3803                                                ModestMsgEditWindow *window)
3804 {
3805         const gchar *current_search = NULL;
3806         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3807         gboolean result;
3808         GtkTextIter selection_start, selection_end;
3809         GtkTextIter match_start, match_end;
3810         gboolean continue_search = FALSE;
3811
3812         if (message_is_empty (window)) {
3813                 g_free (priv->last_search);
3814                 priv->last_search = NULL;
3815                 modest_platform_system_banner (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3816                 return;
3817         }
3818
3819         current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
3820         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3821                 g_free (priv->last_search);
3822                 priv->last_search = NULL;
3823                 /* Information banner about empty search */
3824                 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
3825                 return;
3826         }
3827
3828         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3829                 continue_search = TRUE;
3830         } else {
3831                 g_free (priv->last_search);
3832                 priv->last_search = g_strdup (current_search);
3833         }
3834
3835         if (continue_search) {
3836                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3837                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3838                                                                    &match_start, &match_end);
3839                 if (!result)
3840                         modest_platform_system_banner (NULL, NULL, _HL("ckct_ib_find_search_complete"));
3841         } else {
3842                 GtkTextIter buffer_start;
3843                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3844                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3845                                                                    &match_start, &match_end);
3846                 if (!result)
3847                         modest_platform_system_banner (NULL, NULL, _HL("ckct_ib_find_no_matches"));
3848         }
3849
3850         /* Mark as selected the string found in search */
3851         if (result) {
3852                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3853                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3854                 correct_scroll_without_drag_check (MODEST_MSG_EDIT_WINDOW (window), FALSE);
3855         } else {
3856                 g_free (priv->last_search);
3857                 priv->last_search = NULL;
3858         }
3859 }
3860
3861 gboolean 
3862 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3863 {
3864         ModestMsgEditWindowPrivate *priv;
3865
3866         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3867         return priv->sent;
3868 }
3869
3870 void 
3871 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3872                                  gboolean sent)
3873 {
3874         ModestMsgEditWindowPrivate *priv;
3875
3876         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3877         priv->sent = sent;
3878 }
3879
3880 static void
3881 modest_msg_edit_window_isearch_toolbar_close (GtkWidget *widget,
3882                                               ModestMsgEditWindow *window)
3883 {
3884         modest_msg_edit_window_toggle_isearch_toolbar (window, FALSE);
3885 }
3886
3887 void
3888 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3889                                   TnyMsg *draft)
3890 {
3891         ModestMsgEditWindowPrivate *priv;
3892         TnyHeader *header = NULL;
3893
3894         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3895         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3896
3897         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3898
3899         if (priv->draft_msg != NULL) {
3900                 g_object_unref (priv->draft_msg);
3901         }
3902
3903         if (draft != NULL) {
3904                 g_object_ref (draft);
3905                 header = tny_msg_get_header (draft);
3906                 if (priv->msg_uid) {
3907                         g_free (priv->msg_uid);
3908                         priv->msg_uid = NULL;
3909                 }
3910                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3911         }
3912
3913         priv->draft_msg = draft;
3914 }
3915
3916 static void  
3917 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3918                        GtkTextIter *start, GtkTextIter *end,
3919                        gpointer userdata)
3920 {
3921         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3922         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3923         gchar *tag_name;
3924
3925         if (tag == NULL) return;
3926         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3927         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3928                 replace_with_images (window, priv->images);
3929         }
3930 }
3931
3932 void                    
3933 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3934                                  TnyMimePart *part)
3935 {
3936         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3937
3938         g_return_if_fail (TNY_IS_MIME_PART (part));
3939         tny_list_prepend (priv->attachments, (GObject *) part);
3940         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3941         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3942         gtk_widget_show_all (priv->attachments_caption);
3943         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3944 }
3945
3946 const gchar*    
3947 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3948 {
3949         ModestMsgEditWindowPrivate *priv;
3950
3951         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3952         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3953
3954         return priv->msg_uid;
3955 }
3956
3957 GtkWidget *
3958 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3959                                          ModestMsgEditWindowWidgetType widget_type)
3960 {
3961         ModestMsgEditWindowPrivate *priv;
3962
3963         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3964         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3965
3966         switch (widget_type) {
3967         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3968                 return priv->msg_body;
3969                 break;
3970         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3971                 return priv->to_field;
3972                 break;
3973         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3974                 return priv->cc_field;
3975                 break;
3976         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3977                 return priv->bcc_field;
3978                 break;
3979         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3980                 return priv->subject_field;
3981                 break;
3982         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3983                 return priv->attachments_view;
3984                 break;
3985         default:
3986                 return NULL;
3987         }
3988 }
3989
3990 static void 
3991 remove_tags (WPTextBuffer *buffer)
3992 {
3993         GtkTextIter start, end;
3994
3995         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3996         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3997
3998         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3999 }
4000
4001 static void
4002 on_account_removed (TnyAccountStore *account_store, 
4003                     TnyAccount *account,
4004                     gpointer user_data)
4005 {
4006         /* Do nothing if it's a store account, because we use the
4007            transport to send the messages */
4008         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
4009                 const gchar *parent_acc = NULL;
4010                 const gchar *our_acc = NULL;
4011
4012                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
4013                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
4014                 /* Close this window if I'm showing a message of the removed account */
4015                 if (strcmp (parent_acc, our_acc) == 0)
4016                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
4017         }
4018 }
4019
4020 static void
4021 update_signature (ModestMsgEditWindow *self,
4022                   const gchar *old_account,
4023                   const gchar *new_account)
4024 {
4025         ModestMsgEditWindowPrivate *priv;
4026         gboolean has_old_signature, has_new_signature;
4027         GtkTextIter iter;
4028         ModestAccountMgr *mgr;
4029         gchar *signature;
4030
4031         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4032
4033         gtk_text_buffer_begin_user_action (priv->text_buffer);
4034
4035         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
4036         mgr = modest_runtime_get_account_mgr ();
4037
4038
4039         if (old_account) {
4040                 signature = modest_account_mgr_get_signature_from_recipient (mgr, old_account, &has_old_signature);
4041                 if (has_old_signature) {
4042                         GtkTextIter match_start, match_end;
4043                         /* We cannot use
4044                            MODEST_TEXT_UTILS_SIGNATURE_MARKER as it
4045                            seems that the search has some problems
4046                            with the blank space at the end */
4047                         if (gtk_text_iter_forward_search (&iter, "--",
4048                                                           GTK_TEXT_SEARCH_TEXT_ONLY,
4049                                                           &match_start, NULL, NULL)) {
4050                                 gtk_text_buffer_get_end_iter (priv->text_buffer ,&match_end);
4051                                 gtk_text_buffer_delete (priv->text_buffer, &match_start, &match_end);
4052                                 iter = match_start;
4053                         } else if (gtk_text_iter_forward_search (&iter, _("mcen_ia_editor_original_message"), 0,
4054                                                                  &match_start, &match_end, NULL)) {
4055                                 iter = match_start;
4056                         }
4057                 } else {
4058                         gtk_text_buffer_get_end_iter (priv->text_buffer, &iter);
4059                 }
4060                 g_free (signature);
4061         }
4062
4063         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
4064         signature = modest_account_mgr_get_signature_from_recipient (mgr, new_account, &has_new_signature);
4065         if (has_new_signature) {
4066
4067                 gchar *full_signature = g_strconcat ((gtk_text_iter_starts_line (&iter)) ? "" : "\n",
4068                                                      MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n",
4069                                                      signature, NULL);
4070                 gtk_text_buffer_insert (priv->text_buffer, &iter, full_signature, -1);
4071                 g_free (full_signature);
4072         }
4073         g_free (signature);
4074         gtk_text_buffer_end_user_action (priv->text_buffer);
4075 }
4076
4077 static void update_branding (ModestMsgEditWindow *self,
4078                              const gchar *new_account)
4079 {
4080         ModestMsgEditWindowPrivate *priv;
4081         ModestAccountMgr *mgr;
4082         const GdkPixbuf *new_icon = NULL;
4083         gchar *new_label = NULL;
4084         gboolean show = FALSE;
4085
4086         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4087
4088         mgr = modest_runtime_get_account_mgr ();
4089
4090         modest_account_mgr_get_branding_from_recipient (mgr, new_account, &new_label, &new_icon, MODEST_ICON_SIZE_SMALL);
4091         if (new_icon) {
4092                 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->brand_icon), (GdkPixbuf *) new_icon);
4093                 gtk_widget_show (priv->brand_icon);
4094                 show = TRUE;
4095         } else {
4096                 gtk_widget_hide (priv->brand_icon);
4097         }
4098         if (new_label) {
4099                 gtk_label_set_text (GTK_LABEL (priv->brand_label), new_label);
4100                 gtk_widget_show (priv->brand_label);
4101                 g_free (new_label);
4102                 show = TRUE;
4103         } else {
4104                 gtk_widget_hide (priv->brand_label);
4105         }
4106
4107         if (show)
4108                 gtk_widget_show (priv->brand_container);
4109         else
4110                 gtk_widget_hide (priv->brand_container);
4111 }
4112
4113 static void
4114 from_field_changed (GtkWidget *button,
4115                     ModestMsgEditWindow *self)
4116 {
4117         ModestMsgEditWindowPrivate *priv;
4118         gchar *old_account, *new_account;
4119
4120         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4121
4122         old_account = priv->last_from_account;
4123         new_account = modest_selector_get_active_id (priv->from_field);
4124
4125         if (!new_account) {
4126                 g_warning ("%s, could not get the new account", __FUNCTION__);
4127                 return;
4128         }
4129
4130         /* If the From is the same do nothing */
4131         if (old_account && new_account && !strcmp (old_account, new_account))
4132                 return;
4133
4134         priv->last_from_account = new_account;
4135
4136         update_signature (self, old_account, new_account);
4137         update_branding (self, new_account);
4138
4139 }
4140
4141 typedef struct _MessageSettingsHelper {
4142         ModestMsgEditWindow *window;
4143         GSList *priority_group;
4144         GSList *format_group;
4145         GtkToggleButton *current_priority;
4146         GtkToggleButton *current_format;
4147 } MessageSettingsHelper;
4148
4149 static void
4150 on_priority_toggle (GtkToggleButton *button, 
4151                     MessageSettingsHelper *helper)
4152 {
4153         GSList *node;
4154         ModestMsgEditWindowPrivate *priv;
4155
4156         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4157         if (gtk_toggle_button_get_active (button)) {
4158
4159                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4160                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4161                         if ((node_button != button) &&
4162                             gtk_toggle_button_get_active (node_button)) {
4163                                 gtk_toggle_button_set_active (node_button, FALSE);
4164                         }
4165                 }
4166                 helper->current_priority = button;
4167         } else {
4168                 gboolean found = FALSE;
4169                 /* If no one is active, activate it again */
4170                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4171                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4172                         if (gtk_toggle_button_get_active (node_button)) {
4173                                 found = TRUE;
4174                                 break;
4175                         }
4176                 }
4177                 if (!found) {
4178                         gtk_toggle_button_set_active (button, TRUE);
4179                 }
4180         }
4181 }
4182
4183 static void
4184 on_format_toggle (GtkToggleButton *button,
4185                   MessageSettingsHelper *helper)
4186 {
4187         GSList *node;
4188         ModestMsgEditWindowPrivate *priv;
4189
4190         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4191         if (gtk_toggle_button_get_active (button)) {
4192
4193                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4194                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4195                         if ((node_button != button) &&
4196                             gtk_toggle_button_get_active (node_button)) {
4197                                 gtk_toggle_button_set_active (node_button, FALSE);
4198                         }
4199                 }
4200                 helper->current_format = button;
4201         } else {
4202                 gboolean found = FALSE;
4203                 /* If no one is active, activate it again */
4204                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4205                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4206                         if (gtk_toggle_button_get_active (node_button)) {
4207                                 found = TRUE;
4208                                 break;
4209                         }
4210                 }
4211                 if (!found) {
4212                         gtk_toggle_button_set_active (button, TRUE);
4213                 }
4214         }
4215
4216 }
4217
4218 static void
4219 modest_msg_edit_window_show_msg_settings_dialog (ModestMsgEditWindow *window)
4220 {
4221         GtkWidget *dialog;
4222         GtkWidget *align;
4223         GtkWidget *vbox;
4224         GtkWidget *priority_hbox;
4225         GtkWidget *high_toggle, *medium_toggle, *low_toggle;
4226         GtkWidget *captioned;
4227         GtkSizeGroup *title_sizegroup, *value_sizegroup;
4228         GtkWidget *format_hbox;
4229         GtkWidget *html_toggle, *text_toggle;
4230         ModestMsgEditWindowPrivate *priv;
4231         MessageSettingsHelper helper = {0,};
4232
4233         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
4234         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
4235         helper.window = window;
4236         helper.priority_group = NULL;
4237         helper.format_group = NULL;
4238
4239         title_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4240         value_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4241
4242         dialog = gtk_dialog_new_with_buttons (_("mcen_me_message_settings"), NULL,
4243                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4244                                               _HL("wdgt_bd_done"), GTK_RESPONSE_ACCEPT, NULL);
4245         vbox = gtk_vbox_new (FALSE, 0);
4246         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
4247         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DOUBLE, 0);
4248         gtk_container_add (GTK_CONTAINER (align), vbox);
4249         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), align);
4250         gtk_widget_show (align);
4251         gtk_widget_show (vbox);
4252
4253         /* Priority toggles */
4254         priority_hbox = gtk_hbox_new (TRUE, 0);
4255         high_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4256         gtk_button_set_label (GTK_BUTTON (high_toggle), _("mcen_me_editor_priority_high"));
4257         helper.priority_group = g_slist_prepend (helper.priority_group, high_toggle);
4258         g_object_set_data (G_OBJECT (high_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_HIGH_PRIORITY));
4259         medium_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4260         gtk_button_set_label (GTK_BUTTON (medium_toggle), _("mcen_me_editor_priority_normal"));
4261         helper.priority_group = g_slist_prepend (helper.priority_group, medium_toggle);
4262         g_object_set_data (G_OBJECT (medium_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_NORMAL_PRIORITY));
4263         low_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4264         gtk_button_set_label (GTK_BUTTON (low_toggle), _("mcen_me_editor_priority_low"));
4265         helper.priority_group = g_slist_prepend (helper.priority_group, low_toggle);
4266         g_object_set_data (G_OBJECT (low_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_LOW_PRIORITY));
4267         gtk_box_pack_start (GTK_BOX (priority_hbox), low_toggle, TRUE, TRUE, 0);
4268         gtk_box_pack_start (GTK_BOX (priority_hbox), medium_toggle, TRUE, TRUE, 0);
4269         gtk_box_pack_start (GTK_BOX (priority_hbox), high_toggle, TRUE, TRUE, 0);
4270         gtk_widget_show_all (priority_hbox);
4271         captioned = modest_toolkit_utils_create_captioned (title_sizegroup, value_sizegroup,
4272                                                            _("mcen_me_editor_message_priority"), FALSE, priority_hbox);
4273         gtk_widget_show (captioned);
4274         gtk_box_pack_start (GTK_BOX (vbox), captioned, FALSE, FALSE, 0);
4275
4276         /* format toggles */
4277         format_hbox = gtk_hbox_new (TRUE, 0);
4278         html_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4279         gtk_button_set_label (GTK_BUTTON (html_toggle), _("mcen_me_editor_formatted_text"));
4280         helper.format_group = g_slist_prepend (helper.format_group, html_toggle);
4281         g_object_set_data (G_OBJECT (html_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_HTML));
4282         g_object_set_data (G_OBJECT (html_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_FORMATTED_TEXT));
4283         text_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4284         gtk_button_set_label (GTK_BUTTON (text_toggle), _("mcen_me_editor_plain_text"));
4285         helper.format_group = g_slist_prepend (helper.format_group, text_toggle);
4286         g_object_set_data (G_OBJECT (text_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_TEXT));
4287         g_object_set_data (G_OBJECT (text_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_PLAIN_TEXT));
4288         gtk_box_pack_start (GTK_BOX (format_hbox), html_toggle, TRUE, TRUE, 0);
4289         gtk_box_pack_start (GTK_BOX (format_hbox), text_toggle, TRUE, TRUE, 0);
4290         gtk_widget_show_all (format_hbox);
4291         gtk_widget_show (format_hbox);
4292         gtk_box_pack_start (GTK_BOX (vbox), format_hbox, FALSE, FALSE, 0);
4293
4294
4295         g_object_unref (title_sizegroup);
4296         g_object_unref (value_sizegroup);
4297
4298         /* Set current values */
4299         switch (priv->priority_flags) {
4300         case TNY_HEADER_FLAG_HIGH_PRIORITY:
4301                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (high_toggle), TRUE);
4302                 helper.current_priority = GTK_TOGGLE_BUTTON (high_toggle);
4303                 break;
4304         case TNY_HEADER_FLAG_LOW_PRIORITY:
4305                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (low_toggle), TRUE);
4306                 helper.current_priority = GTK_TOGGLE_BUTTON (low_toggle);
4307                 break;
4308         default:
4309                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (medium_toggle), TRUE);
4310                 helper.current_priority = GTK_TOGGLE_BUTTON (medium_toggle);
4311                 break;
4312         }
4313
4314         switch (modest_msg_edit_window_get_format (window)) {
4315         case MODEST_MSG_EDIT_FORMAT_TEXT:
4316                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_toggle), TRUE);
4317                 helper.current_format = GTK_TOGGLE_BUTTON (text_toggle);
4318                 break;
4319         case MODEST_MSG_EDIT_FORMAT_HTML:
4320         default:
4321                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (html_toggle), TRUE);
4322                 helper.current_format = GTK_TOGGLE_BUTTON (html_toggle);
4323                 break;
4324         }
4325
4326         /* Signal connects */
4327         g_signal_connect (G_OBJECT (high_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4328         g_signal_connect (G_OBJECT (medium_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4329         g_signal_connect (G_OBJECT (low_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4330         g_signal_connect (G_OBJECT (html_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4331         g_signal_connect (G_OBJECT (text_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4332
4333         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
4334                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
4335
4336         /* Save settings if the user clicked on done */
4337         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
4338                 TnyHeaderFlags flags;
4339                 ModestMsgEditFormat old_format, new_format;
4340
4341                 /* Set priority flags */
4342                 flags = (TnyHeaderFlags) g_object_get_data (G_OBJECT (helper.current_priority), "priority");
4343                 if (priv->priority_flags !=  flags)
4344                         modest_msg_edit_window_set_priority_flags (window, flags);
4345
4346                 /* Set edit format */
4347                 old_format = modest_msg_edit_window_get_format (window);
4348                 new_format = (ModestMsgEditFormat) g_object_get_data (G_OBJECT (helper.current_format), "format");
4349                 if (old_format != new_format) {
4350                         gint file_format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (helper.current_format), "file-format"));
4351                         modest_msg_edit_window_set_file_format (window, file_format);
4352                 }
4353         }
4354
4355         gtk_widget_destroy (dialog);
4356         g_slist_free (helper.priority_group);
4357 }
4358
4359 static void
4360 on_message_settings (GtkAction *action,
4361                      ModestMsgEditWindow *window)
4362 {
4363         modest_msg_edit_window_show_msg_settings_dialog (window);
4364 }
4365
4366 static void
4367 on_cc_button_toggled (GtkWidget *button,
4368                       ModestMsgEditWindow *window)
4369 {
4370         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4371
4372         modest_msg_edit_window_show_cc (MODEST_MSG_EDIT_WINDOW (window),
4373                                         modest_togglable_get_active (button));
4374 }
4375
4376 static void
4377 on_bcc_button_toggled (GtkWidget *button,
4378                       ModestMsgEditWindow *window)
4379 {
4380         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4381
4382         modest_msg_edit_window_show_bcc (MODEST_MSG_EDIT_WINDOW (window),
4383                                         modest_togglable_get_active (button));
4384 }
4385
4386 static void 
4387 setup_menu (ModestMsgEditWindow *self)
4388 {
4389         ModestMsgEditWindowPrivate *priv = NULL;
4390
4391         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4392
4393         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4394
4395         /* Settings menu buttons */
4396         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_checknames"), NULL,
4397                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_check_names),
4398                                    NULL);
4399         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
4400                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
4401                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
4402         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_undo"), "<Ctrl>z",
4403                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_undo),
4404                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_undo));
4405
4406         priv->cc_button = hildon_check_button_new (0);
4407         gtk_button_set_label (GTK_BUTTON (priv->cc_button), _("mcen_me_editor_showcc"));
4408         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->cc_button),
4409                                         FALSE);
4410         modest_window_add_item_to_menu (MODEST_WINDOW (self), priv->cc_button, NULL);
4411         g_signal_connect (G_OBJECT (priv->cc_button), "toggled",
4412                           G_CALLBACK (on_cc_button_toggled), (gpointer) self);
4413
4414         priv->bcc_button = modest_toolkit_factory_create_check_menu (modest_runtime_get_toolkit_factory (),
4415                                                                      _("mcen_me_editor_showbcc"));
4416         modest_togglable_set_active (priv->bcc_button,
4417                                      FALSE);
4418         modest_window_add_item_to_menu (MODEST_WINDOW (self), priv->bcc_button,
4419                                         NULL);
4420         g_signal_connect (G_OBJECT (priv->bcc_button), "toggled",
4421                           G_CALLBACK (on_bcc_button_toggled), (gpointer) self);
4422
4423         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_attach_inlineimage"), NULL,
4424                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_insert_image),
4425                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_set_style));
4426         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_add_attachment"), NULL,
4427                                    MODEST_WINDOW_MENU_CALLBACK (modest_msg_edit_window_add_attachment_clicked),
4428                                    NULL);
4429         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
4430                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_remove_attachments),
4431                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_editor_remove_attachment));
4432         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_message_settings"), NULL,
4433                                    MODEST_WINDOW_MENU_CALLBACK (on_message_settings),
4434                                    NULL);
4435         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), "<Ctrl>f",
4436                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_toggle_find_in_page),
4437                                    NULL);
4438 }
4439
4440 static void
4441 emit_open_addressbook (GtkButton *button,
4442                        ModestRecptEditor *editor)
4443 {
4444         g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
4445 }
4446
4447 static GtkWidget *
4448 _create_addressbook_box (GtkSizeGroup *title_size_group, GtkSizeGroup *value_size_group,
4449                          const gchar *label, GtkWidget *control)
4450 {
4451         GtkWidget *abook_button;
4452         GtkWidget *align;
4453         GtkWidget *box;
4454         GtkWidget *label_widget;
4455
4456         box = gtk_hbox_new (FALSE, 0);
4457
4458         align = gtk_alignment_new (0.0, 0.0, 1.0, 0.0);
4459         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 0, MODEST_MARGIN_DEFAULT);
4460
4461         abook_button = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT);
4462         label_widget = gtk_label_new (label);
4463         gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
4464         gtk_container_add (GTK_CONTAINER (abook_button), label_widget);
4465
4466         gtk_container_add (GTK_CONTAINER (align), abook_button);
4467         gtk_widget_set_size_request (label_widget, 148 - MODEST_MARGIN_DOUBLE, -1);
4468         gtk_box_pack_start (GTK_BOX (box), align, FALSE, FALSE, 0);
4469         gtk_box_pack_start (GTK_BOX (box), control, TRUE, TRUE, 0);
4470         if (title_size_group)
4471                 gtk_size_group_add_widget (title_size_group, label_widget);
4472         if (value_size_group)
4473                 gtk_size_group_add_widget (value_size_group, control);
4474
4475         g_signal_connect (G_OBJECT (abook_button), "clicked",
4476                           G_CALLBACK (emit_open_addressbook), control);
4477   
4478         return box;  
4479 }
4480
4481 static void 
4482 max_chars_banner_unref (ModestMsgEditWindow *self, GObject *old_ref)
4483 {
4484         ModestMsgEditWindowPrivate *priv = NULL;
4485
4486         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4487
4488         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4489         priv->max_chars_banner = NULL;
4490 }
4491
4492 static gboolean
4493 has_pending_addresses (ModestRecptEditor *recpt_editor)
4494 {
4495         const gchar *recipients = NULL;
4496         GSList *start_indexes = NULL, *end_indexes = NULL;
4497         GSList *current_start, *current_end;
4498         GtkTextBuffer *buffer;
4499         gint offset_delta = 0;
4500         gint last_length;
4501         gboolean has_recipients_to_add = FALSE;
4502
4503         recipients = modest_recpt_editor_get_recipients (recpt_editor);
4504         last_length = g_utf8_strlen (recipients, -1);
4505         modest_text_utils_get_addresses_indexes (recipients, &start_indexes, &end_indexes);
4506
4507         if (!start_indexes)
4508                 return FALSE;
4509
4510         current_start = start_indexes;
4511         current_end = end_indexes;
4512         buffer = modest_recpt_editor_get_buffer (recpt_editor);
4513
4514         while (current_start && !has_recipients_to_add) {
4515                 gchar *address;
4516                 gchar *start_ptr, *end_ptr;
4517                 gint start_pos, end_pos;
4518
4519                 start_pos = (*((gint*) current_start->data)) + offset_delta;
4520                 end_pos = (*((gint*) current_end->data)) + offset_delta;
4521
4522                 start_ptr = g_utf8_offset_to_pointer (recipients, start_pos);
4523                 end_ptr = g_utf8_offset_to_pointer (recipients, end_pos);
4524
4525                 address = g_strstrip (g_strndup (start_ptr, end_ptr - start_ptr));
4526
4527                 if (modest_text_utils_validate_recipient (address, NULL)) {
4528                         if (!modest_address_book_has_address (address)) {
4529                                 has_recipients_to_add = TRUE;
4530                         }
4531                 }
4532                 current_start = g_slist_next (current_start);
4533                 current_end = g_slist_next (current_end);
4534         }
4535         return has_recipients_to_add;
4536 }
4537
4538 gboolean
4539 modest_msg_edit_window_has_pending_addresses (ModestMsgEditWindow *self)
4540 {
4541         ModestMsgEditWindowPrivate *priv = NULL;
4542
4543         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self), FALSE);
4544
4545         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4546
4547         if (!has_pending_addresses ((ModestRecptEditor *) priv->to_field) &&
4548             !has_pending_addresses ((ModestRecptEditor *) priv->cc_field) &&
4549             !has_pending_addresses ((ModestRecptEditor *) priv->bcc_field))
4550                 return FALSE;
4551         else
4552                 return TRUE;
4553 }