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