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