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