505678aaf451f8921bed0a4d9ab16663f053ca69
[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         /* Show ColorPicker dialog */
1506         PipCalendarColor color = pip_color_picker_select_color(PipTextColorRed, PipColorPickerText);
1507
1508         /* Check if some color is selected rather than dialog is dismissed */
1509         if (color != PipCalendarColorInvalid) {
1510                 GdkColor *gdk_color = (GdkColor *) pip_calendar_color_get_gdkcolor(color);
1511                 if (gdk_color)
1512                         hildon_color_button_set_color ((HildonColorButton *) button, gdk_color);
1513         }
1514 }
1515 #endif
1516
1517 static void
1518 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1519 {
1520         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1521         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1522         GtkWidget *placeholder;
1523         GtkWidget *tool_item;
1524         gint insert_index;
1525         gchar size_text[5];
1526         gint size_index;
1527         gint font_index;
1528         GtkWidget *sizes_menu;
1529         GtkWidget *fonts_menu;
1530         gchar *markup;
1531         GtkWidget *arrow;
1532         GtkWidget *hbox;
1533
1534         /* Toolbar */
1535         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1536         gtk_toolbar_set_show_arrow (GTK_TOOLBAR (parent_priv->toolbar), FALSE);
1537         gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
1538         modest_window_add_toolbar (MODEST_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1539
1540         /* Font color placeholder */
1541         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1542         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1543
1544         /* font color */
1545         priv->font_color_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1546         priv->font_color_button = hildon_color_button_new ();
1547         gtk_widget_set_size_request (priv->font_color_button, -1, 48);
1548         GTK_WIDGET_UNSET_FLAGS (priv->font_color_toolitem, GTK_CAN_FOCUS);
1549         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1550         gtk_container_add (GTK_CONTAINER (priv->font_color_toolitem), priv->font_color_button);
1551         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1552         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1553         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->font_color_toolitem), insert_index);
1554         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1555                                   "notify::color", 
1556                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1557                                   window);
1558
1559         /* Yes I know this is a horrible hack, but it works for the
1560            moment while we don't create a ModestColorButton */
1561 #ifdef MODEST_USE_CALENDAR_WIDGETS
1562         GtkButtonClass *button_class = GTK_BUTTON_CLASS (HILDON_COLOR_BUTTON_GET_CLASS (priv->font_color_button));
1563         button_class->clicked = color_button_clicked;
1564 #endif
1565
1566         /* Font size and face placeholder */
1567         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1568         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1569         /* font_size */
1570         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1571         priv->size_tool_button_label = gtk_label_new (NULL);
1572         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1573         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1574                               size_text, "</span>", NULL);
1575         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1576         gtk_misc_set_alignment (GTK_MISC (priv->size_tool_button_label), 1.0, 0.5);
1577         g_free (markup);
1578         hildon_helper_set_logical_font (priv->size_tool_button_label, "LargeSystemFont");
1579         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1580         gtk_box_pack_start (GTK_BOX (hbox), priv->size_tool_button_label, TRUE, TRUE, 0);
1581         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1582         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1583         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1584         gtk_widget_set_sensitive (arrow, FALSE);
1585         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1586         sizes_menu = gtk_menu_new ();
1587         priv->sizes_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1588         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1589                 GtkTreeIter iter;
1590
1591                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1592                 gtk_list_store_append (GTK_LIST_STORE (priv->sizes_model), &iter);
1593
1594                 gtk_list_store_set (GTK_LIST_STORE (priv->sizes_model), &iter, 
1595                                     0, size_text,
1596                                     -1);
1597
1598                 if (wp_font_size[size_index] == 12)
1599                         priv->current_size_index = size_index;
1600                                         
1601         }
1602
1603         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_size_clicked), window);
1604         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1605         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1606         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1607         priv->font_size_toolitem = tool_item;
1608
1609         /* font face */
1610         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1611         priv->font_tool_button_label = gtk_label_new (NULL);
1612         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1613         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1614         gtk_misc_set_alignment (GTK_MISC (priv->font_tool_button_label), 1.0, 0.5);
1615         g_free(markup);
1616         hildon_helper_set_logical_font (priv->font_tool_button_label, "LargeSystemFont");
1617         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1618         gtk_box_pack_start (GTK_BOX (hbox), priv->font_tool_button_label, TRUE, TRUE, 0);
1619         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1620         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1621         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1622         gtk_widget_set_sensitive (arrow, FALSE);
1623         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1624         fonts_menu = gtk_menu_new ();
1625         priv->faces_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1626         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1627                 GtkTreeIter iter;
1628
1629                 gtk_list_store_append (GTK_LIST_STORE (priv->faces_model), &iter);
1630
1631                 gtk_list_store_set (GTK_LIST_STORE (priv->faces_model), &iter, 
1632                                     0, wp_get_font_name (font_index),
1633                                     -1);
1634
1635                 if (font_index == DEFAULT_FONT)
1636                         priv->current_face_index = font_index;
1637         }
1638         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_face_clicked), window);
1639         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1640         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1641         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1642         priv->font_face_toolitem = tool_item;
1643
1644         /* Set expand and homogeneous for remaining items */
1645         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1646         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1647         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1648         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1649         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1650         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1651
1652         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1653            will not show the tool items added to the placeholders) */
1654         gtk_widget_show_all (parent_priv->toolbar);
1655
1656         /* Set the no show all *after* showing all items. We do not
1657            want the toolbar to be shown with a show all because it
1658            could go agains the gconf setting regarding showing or not
1659            the toolbar of the editor window */
1660         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1661 }
1662
1663
1664
1665 ModestWindow*
1666 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, const gchar *mailbox, gboolean preserve_is_rich)
1667 {
1668         GObject *obj;
1669         ModestWindowPrivate *parent_priv;
1670         ModestMsgEditWindowPrivate *priv;
1671         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1672         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1673         ModestWindowMgr *mgr = NULL;
1674
1675         g_return_val_if_fail (msg, NULL);
1676         g_return_val_if_fail (account_name, NULL);
1677
1678         mgr = modest_runtime_get_window_mgr ();
1679         
1680         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1681
1682         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1683         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1684
1685         /* Menubar. Update the state of some toggles */
1686         priv->from_field_protos = get_transports ();
1687         priv->original_mailbox = NULL;
1688         modest_selector_set_pair_list (priv->from_field, priv->from_field_protos);
1689         modest_selector_set_active_id (priv->from_field, (gpointer) account_name);
1690         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
1691         if (mailbox && modest_pair_list_find_by_first_as_string (priv->from_field_protos, mailbox)) {
1692                 modest_selector_set_active_id (priv->from_field, (gpointer) mailbox);
1693                 priv->original_mailbox = g_strdup (mailbox);
1694         } else if (modest_account_mgr_account_is_multimailbox (modest_runtime_get_account_mgr (), account_name, NULL)) {
1695                 /* We set the first mailbox as the active mailbox */
1696                 priv->original_mailbox = multimailbox_get_default_mailbox (account_name);
1697                 if (priv->original_mailbox != NULL)
1698                         modest_selector_set_active_id (priv->from_field,
1699                                                        (gpointer) priv->original_mailbox);
1700                 else
1701                         modest_selector_set_active_id (priv->from_field,
1702                                                        (gpointer) account_name);
1703         } else {
1704                 modest_selector_set_active_id (priv->from_field, (gpointer) account_name);
1705         }
1706         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
1707         update_branding (MODEST_MSG_EDIT_WINDOW (obj), priv->last_from_account);
1708         if (!GTK_IS_COMBO_BOX (priv->from_field)) {
1709 #ifdef HILDON_TOOLKIT_HILDON2
1710                 hildon_button_set_title (HILDON_BUTTON (priv->from_field),
1711                                          _("mail_va_from"));
1712                 hildon_button_set_value (HILDON_BUTTON (priv->from_field), 
1713                                          hildon_touch_selector_get_current_text 
1714                                          (HILDON_TOUCH_SELECTOR (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (priv->from_field)))));
1715 #endif
1716         }
1717         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1718         modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
1719
1720         /* Init window */
1721         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1722
1723         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1724                 
1725         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1726         modest_window_set_active_mailbox (MODEST_WINDOW(obj), priv->original_mailbox);
1727
1728         priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL;
1729
1730         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1731         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1732         /* Add common dimming rules */
1733         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1734                                               modest_msg_edit_window_toolbar_dimming_entries,
1735                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1736                                               MODEST_WINDOW (obj));
1737         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_toolitem,
1738                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1739                                                     MODEST_WINDOW (obj));
1740         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1741                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1742                                                     MODEST_WINDOW (obj));
1743         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1744                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1745                                                     MODEST_WINDOW (obj));
1746         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->send_button,
1747                                                     G_CALLBACK (modest_ui_dimming_rules_on_send),
1748                                                     MODEST_WINDOW (obj));
1749         /* Insert dimming rules group for this window */
1750         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1751         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1752
1753         /* Setup app menu */
1754         setup_menu (MODEST_MSG_EDIT_WINDOW (obj));
1755
1756         /* Checks the dimming rules */
1757         g_object_unref (toolbar_rules_group);
1758         g_object_unref (clipboard_rules_group);
1759         gtk_widget_hide (priv->priority_icon);
1760         gtk_widget_queue_resize (priv->subject_box);
1761         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1762
1763         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1764
1765         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1766         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1767         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1768         priv->update_caption_visibility = TRUE;
1769
1770         modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE);
1771
1772         /* Track account-removed signal, this window should be closed
1773            in the case we're creating a mail associated to the account
1774            that is deleted */
1775         priv->account_removed_handler_id = 
1776                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1777                                   "account_removed",
1778                                   G_CALLBACK(on_account_removed),
1779                                   obj);
1780
1781         modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj));
1782
1783         return (ModestWindow*) obj;
1784 }
1785
1786 static gint
1787 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1788 {
1789         GString **string_buffer = (GString **) user_data;
1790
1791         *string_buffer = g_string_append (*string_buffer, buffer);
1792    
1793         return 0;
1794 }
1795
1796 /**
1797  * @result: A new string which should be freed with g_free().
1798  */
1799 static gchar *
1800 get_formatted_data (ModestMsgEditWindow *edit_window)
1801 {
1802         ModestMsgEditWindowPrivate *priv;
1803         GString *string_buffer = g_string_new ("");
1804         
1805         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1806
1807         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1808
1809         modest_text_utils_hyperlinkify (string_buffer);
1810
1811         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1812
1813         return g_string_free (string_buffer, FALSE);
1814                                                                         
1815 }
1816
1817 MsgData * 
1818 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1819 {
1820         MsgData *data;
1821         const gchar *account_name;
1822         ModestMsgEditWindowPrivate *priv;
1823         TnyIterator *att_iter;
1824         const gchar *picker_active_id;
1825         
1826         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1827
1828         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1829         
1830         picker_active_id = modest_selector_get_active_id (priv->from_field);
1831         g_return_val_if_fail (picker_active_id, NULL);
1832         account_name = modest_utils_get_account_name_from_recipient (picker_active_id, NULL);
1833         
1834         /* don't free these (except from) */
1835         data = g_slice_new0 (MsgData);
1836         data->from    =  g_strdup ((gchar *) modest_selector_get_active_display_name (priv->from_field));
1837         data->account_name = g_strdup (account_name);
1838         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1839         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1840         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1841         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1842         data->references = g_strdup (priv->references);
1843         data->in_reply_to = g_strdup (priv->in_reply_to);
1844         if (priv->draft_msg) {
1845                 data->draft_msg = g_object_ref (priv->draft_msg);
1846         } else if (priv->outbox_msg) {
1847                 data->draft_msg = g_object_ref (priv->outbox_msg);
1848         } else {
1849                 data->draft_msg = NULL;
1850         }
1851
1852         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1853         GtkTextIter b, e;
1854         gtk_text_buffer_get_bounds (buf, &b, &e);
1855         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1856
1857         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1858                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1859         else
1860                 data->html_body = NULL;
1861
1862         /* deep-copy the data */
1863         att_iter = tny_list_create_iterator (priv->attachments);
1864         data->attachments = NULL;
1865         while (!tny_iterator_is_done (att_iter)) {
1866                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1867                 if (!(TNY_IS_MIME_PART(part))) {
1868                         g_warning ("strange data in attachment list");
1869                         g_object_unref (part);
1870                         tny_iterator_next (att_iter);
1871                         continue;
1872                 }
1873                 data->attachments = g_list_append (data->attachments,
1874                                                    part);
1875                 tny_iterator_next (att_iter);
1876         }
1877         g_object_unref (att_iter);
1878
1879         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1880         att_iter = tny_list_create_iterator (priv->images);
1881         data->images = NULL;
1882         while (!tny_iterator_is_done (att_iter)) {
1883                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1884                 const gchar *cid;
1885                 if (!(TNY_IS_MIME_PART(part))) {
1886                         g_warning ("strange data in attachment list");
1887                         g_object_unref (part);
1888                         tny_iterator_next (att_iter);
1889                         continue;
1890                 }
1891                 cid = tny_mime_part_get_content_id (part);
1892                 if (cid) {                      
1893                         gchar *image_tag_id;
1894                         GtkTextTag *image_tag;
1895                         GtkTextIter iter;
1896                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1897                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1898                         g_free (image_tag_id);
1899                         
1900                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1901                         if (image_tag && 
1902                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1903                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1904                                 data->images = g_list_append (data->images,
1905                                                               g_object_ref (part));
1906                 }
1907                 g_object_unref (part);
1908                 tny_iterator_next (att_iter);
1909         }
1910         g_object_unref (att_iter);
1911         
1912         data->priority_flags = priv->priority_flags;
1913
1914         return data;
1915 }
1916
1917
1918 static void
1919 unref_gobject (GObject *obj, gpointer data)
1920 {
1921         if (!G_IS_OBJECT(obj))
1922                 return;
1923         g_object_unref (obj);
1924 }
1925
1926 void 
1927 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1928                                                       MsgData *data)
1929 {
1930         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1931
1932         if (!data)
1933                 return;
1934
1935         g_free (data->to);
1936         g_free (data->cc);
1937         g_free (data->bcc);
1938         g_free (data->from);
1939         g_free (data->subject);
1940         g_free (data->plain_body);
1941         g_free (data->html_body);
1942         g_free (data->account_name);
1943         g_free (data->references);
1944         g_free (data->in_reply_to);
1945         
1946         if (data->draft_msg != NULL) {
1947                 g_object_unref (data->draft_msg);
1948                 data->draft_msg = NULL;
1949         }
1950         
1951         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1952         g_list_free (data->attachments);
1953         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1954         g_list_free (data->images);
1955         
1956         g_slice_free (MsgData, data);
1957 }
1958
1959 void                    
1960 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1961                                        gint *parts_count,
1962                                        guint64 *parts_size)
1963 {
1964         ModestMsgEditWindowPrivate *priv;
1965
1966         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1967
1968         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1969
1970         /* TODO: add images */
1971         *parts_size += priv->images_size;
1972         *parts_count += priv->images_count;
1973
1974 }
1975
1976 ModestMsgEditFormat
1977 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1978 {
1979         gboolean rich_text;
1980         ModestMsgEditWindowPrivate *priv = NULL;
1981         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1982
1983         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1984
1985         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1986         if (rich_text)
1987                 return MODEST_MSG_EDIT_FORMAT_HTML;
1988         else
1989                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1990 }
1991
1992 void
1993 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1994                                    ModestMsgEditFormat format)
1995 {
1996         ModestMsgEditWindowPrivate *priv;
1997         ModestWindowPrivate *parent_priv;
1998
1999         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2000         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2001         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
2002
2003         switch (format) {
2004         case MODEST_MSG_EDIT_FORMAT_HTML:
2005                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2006                 if (parent_priv->toolbar) gtk_widget_show (parent_priv->toolbar);
2007                 break;
2008         case MODEST_MSG_EDIT_FORMAT_TEXT:
2009                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2010                 if (parent_priv->toolbar) gtk_widget_hide (parent_priv->toolbar);
2011                 break;
2012         default:
2013                 g_return_if_reached ();
2014         }
2015 }
2016
2017 ModestMsgEditFormatState *
2018 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
2019 {
2020         ModestMsgEditFormatState *format_state = NULL;
2021         ModestMsgEditWindowPrivate *priv;
2022         WPTextBufferFormat *buffer_format;
2023
2024         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
2025
2026         buffer_format = g_new0 (WPTextBufferFormat, 1);
2027         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2028
2029         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
2030
2031         format_state = g_new0 (ModestMsgEditFormatState, 1);
2032         format_state->bold = buffer_format->bold&0x1;
2033         format_state->italics = buffer_format->italic&0x1;
2034         format_state->bullet = buffer_format->bullet&0x1;
2035         format_state->color = buffer_format->color;
2036         format_state->font_size = buffer_format->font_size;
2037         format_state->font_family = wp_get_font_name (buffer_format->font);
2038         format_state->justification = buffer_format->justification;
2039         g_free (buffer_format);
2040
2041         return format_state;
2042  
2043 }
2044
2045 void
2046 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
2047                                          const ModestMsgEditFormatState *format_state)
2048 {
2049         ModestMsgEditWindowPrivate *priv;
2050         WPTextBufferFormat *buffer_format;
2051         WPTextBufferFormat *current_format;
2052
2053         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2054         g_return_if_fail (format_state != NULL);
2055
2056         buffer_format = g_new0 (WPTextBufferFormat, 1);
2057         current_format = g_new0 (WPTextBufferFormat, 1);
2058
2059         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2060         gtk_widget_grab_focus (priv->msg_body);
2061         buffer_format->bold = (format_state->bold != FALSE);
2062         buffer_format->italic = (format_state->italics != FALSE);
2063         buffer_format->color = format_state->color;
2064         buffer_format->font_size = format_state->font_size;
2065         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
2066         buffer_format->justification = format_state->justification;
2067         buffer_format->bullet = format_state->bullet;
2068
2069         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
2070
2071         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
2072         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
2073         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
2074         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
2075         buffer_format->cs.font = (buffer_format->font != current_format->font);
2076         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
2077         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
2078
2079         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
2080         if (buffer_format->cs.bold) {
2081                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
2082                                               GINT_TO_POINTER (buffer_format->bold&0x1));
2083         }
2084         if (buffer_format->cs.italic) {
2085                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
2086                                               GINT_TO_POINTER (buffer_format->italic&0x1));
2087         }
2088         if (buffer_format->cs.color) {
2089                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2090                                               GINT_TO_POINTER (&(buffer_format->color)));
2091         }
2092         if (buffer_format->cs.font_size) {
2093                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2094                                               GINT_TO_POINTER (buffer_format->font_size));
2095         }
2096         if (buffer_format->cs.justification) {
2097                 switch (buffer_format->justification) {
2098                 case GTK_JUSTIFY_LEFT:
2099                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
2100                                                       GINT_TO_POINTER(TRUE));
2101                         break;
2102                 case GTK_JUSTIFY_CENTER:
2103                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
2104                                                       GINT_TO_POINTER(TRUE));
2105                         break;
2106                 case GTK_JUSTIFY_RIGHT:
2107                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
2108                                                       GINT_TO_POINTER(TRUE));
2109                         break;
2110                 default:
2111                         break;
2112                 }
2113                         
2114         }
2115         if (buffer_format->cs.font) {
2116                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
2117                                               GINT_TO_POINTER (buffer_format->font));
2118         }
2119         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
2120         if (buffer_format->cs.bullet) {
2121                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
2122                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
2123         }
2124 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
2125         
2126         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
2127         
2128         g_free (buffer_format);
2129         g_free (current_format);
2130
2131         /* Check dimming rules */
2132         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2133         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2134 }
2135
2136 static void
2137 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
2138 {
2139         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2140         GtkAction *action;
2141         ModestWindowPrivate *parent_priv;
2142         ModestMsgEditWindowPrivate *priv;
2143         
2144         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2145         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2146
2147         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
2148                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu");
2149                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2150                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2151         } else {
2152                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatPlainTextMenu");
2153                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2154                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2155         }
2156
2157         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2158
2159         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
2160         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
2161
2162         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
2163         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
2164
2165 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
2166 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
2167
2168         action = NULL;
2169         switch (buffer_format->justification)
2170         {
2171         case GTK_JUSTIFY_LEFT:
2172                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu");
2173                 break;
2174         case GTK_JUSTIFY_CENTER:
2175                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu");
2176                 break;
2177         case GTK_JUSTIFY_RIGHT:
2178                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu");
2179                 break;
2180         default:
2181                 break;
2182         }
2183         
2184         if (action != NULL)
2185                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2186         
2187         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
2188                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
2189                                          window);
2190         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
2191         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
2192                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
2193                                            window);
2194
2195         if (priv->current_size_index != buffer_format->font_size) {
2196                 GtkTreeIter iter;
2197                 GtkTreePath *path;
2198
2199                 path = gtk_tree_path_new_from_indices (buffer_format->font_size, -1);
2200                 if (gtk_tree_model_get_iter (priv->sizes_model, &iter, path)) {
2201                         gchar *size_text;
2202                         gchar *markup;
2203
2204                         priv->current_size_index = buffer_format->font_size;
2205
2206                         gtk_tree_model_get (priv->sizes_model, &iter, 0, &size_text, -1);
2207                         markup = g_strconcat ("<span font_family='Sans'>", 
2208                                               size_text, "</span>", NULL);
2209                         
2210                         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2211                         g_free (markup);
2212                         g_free (size_text);
2213                 }
2214                 gtk_tree_path_free (path);              
2215         }
2216
2217         if (priv->current_face_index != buffer_format->font) {
2218                 GtkTreeIter iter;
2219                 GtkTreePath *path;
2220
2221                 path = gtk_tree_path_new_from_indices (buffer_format->font, -1);
2222                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2223                         gchar *face_name;
2224                         gchar *markup;
2225
2226                         priv->current_face_index = buffer_format->font;
2227                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2228                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2229                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2230                         g_free (face_name);
2231                         g_free (markup);
2232                 }
2233
2234         }
2235
2236         g_free (buffer_format);
2237
2238 }
2239
2240
2241 void
2242 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2243 {
2244         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2245         ModestMsgEditWindowPrivate *priv;
2246         GtkWidget *dialog = NULL;
2247
2248         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2249         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2250
2251         dialog = hildon_color_chooser_new ();
2252         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2253         g_free (buffer_format);
2254
2255         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2256                 GdkColor col;
2257                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2258                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2259                                               (gpointer) &col);
2260         }
2261         gtk_widget_destroy (dialog);
2262 }
2263
2264
2265 void
2266 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2267 {
2268         ModestMsgEditWindowPrivate *priv;
2269         GtkWidget *dialog = NULL;
2270         GdkColor *old_color = NULL;
2271         
2272         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2273         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2274         
2275         dialog = hildon_color_chooser_new ();
2276         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2277
2278         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2279                 GdkColor col;
2280                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2281                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2282         }
2283         gtk_widget_destroy (dialog);
2284 }
2285
2286
2287 static TnyStream*
2288 create_stream_for_uri (const gchar* uri)
2289 {
2290         if (!uri)
2291                 return NULL;
2292                 
2293         TnyStream *result = NULL;
2294
2295         GnomeVFSHandle *handle = NULL;
2296         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2297         if (test == GNOME_VFS_OK) {
2298                 TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle));
2299                 /* Streams over OBEX (Bluetooth) are not seekable but
2300                  * we expect them to be (we might need to read them
2301                  * several times). So if this is a Bluetooth URI just
2302                  * read the whole file into memory (this is not a fast
2303                  * protocol so we can assume that these files are not
2304                  * going to be very big) */
2305                 if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)||
2306                     (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) {
2307                         TnyStream *memstream = tny_camel_mem_stream_new ();
2308                         tny_stream_write_to_stream (vfssstream, memstream);
2309                         g_object_unref (vfssstream);
2310                         result = memstream;
2311                 } else {
2312                         result = vfssstream;
2313                 }
2314         }
2315         
2316         return result;
2317 }
2318
2319 void
2320 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2321 {
2322         
2323         ModestMsgEditWindowPrivate *priv;
2324         GtkWidget *dialog = NULL;
2325         gint response = 0;
2326         GSList *uris = NULL;
2327         GSList *uri_node = NULL;
2328
2329         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2330
2331         dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
2332                                                                     _("mcen_ia_select_inline_image_title"),
2333                                                                     (GtkWindow *) window,
2334                                                                     GTK_FILE_CHOOSER_ACTION_OPEN);
2335         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2336
2337         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2338
2339         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2340                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2341
2342         response = gtk_dialog_run (GTK_DIALOG (dialog));
2343         switch (response) {
2344         case GTK_RESPONSE_OK:
2345         {
2346                 gchar *current_folder;
2347                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2348                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2349                 if (current_folder && current_folder != '\0') {
2350                         GError *err = NULL;
2351                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_INSERT_IMAGE_PATH, 
2352                                                 current_folder, &err);
2353                         if (err != NULL) {
2354                                 g_debug ("Error storing latest used folder: %s", err->message);
2355                                 g_error_free (err);
2356                         }
2357                 }
2358                 g_free (current_folder);
2359         }
2360         break;
2361         default:
2362                 break;
2363         }
2364         gtk_widget_destroy (dialog);
2365
2366         g_object_ref (window);
2367         /* The operation could take some time so allow the dialog to be closed */
2368         while (gtk_events_pending ())
2369                 gtk_main_iteration ();
2370
2371         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2372                 const gchar *uri;
2373                 GnomeVFSHandle *handle = NULL;
2374                 GnomeVFSResult result;
2375                 GtkTextIter position;
2376                 GtkTextMark *insert_mark;
2377
2378                 uri = (const gchar *) uri_node->data;
2379                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2380                 if (result == GNOME_VFS_OK) {
2381                         GdkPixbuf *pixbuf;
2382                         GnomeVFSFileInfo *info;
2383                         gchar *filename, *basename, *escaped_filename;
2384                         TnyMimePart *mime_part;
2385                         gchar *content_id;
2386                         const gchar *mime_type = NULL;
2387                         GnomeVFSURI *vfs_uri;
2388                         guint64 stream_size;
2389
2390                         gnome_vfs_close (handle);
2391                         vfs_uri = gnome_vfs_uri_new (uri);
2392
2393                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2394                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2395                         g_free (escaped_filename);
2396                         gnome_vfs_uri_unref (vfs_uri);
2397                         info = gnome_vfs_file_info_new ();
2398
2399                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2400                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2401                             == GNOME_VFS_OK)
2402                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2403
2404                         mime_part = tny_platform_factory_new_mime_part
2405                                 (modest_runtime_get_platform_factory ());
2406
2407                         TnyStream *stream = create_stream_for_uri (uri);
2408
2409                         if (stream == NULL) {
2410
2411                                 modest_platform_information_banner (NULL, NULL, 
2412                                                                     _FM("sfil_ib_opening_not_allowed"));
2413                                 g_free (filename);
2414                                 g_object_unref (mime_part);
2415                                 gnome_vfs_file_info_unref (info);
2416                                 continue;
2417                         }
2418
2419                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2420
2421                         content_id = g_strdup_printf ("%d", priv->next_cid);
2422                         tny_mime_part_set_content_id (mime_part, content_id);
2423                         g_free (content_id);
2424                         priv->next_cid++;
2425
2426                         basename = g_path_get_basename (filename);
2427                         tny_mime_part_set_filename (mime_part, basename);
2428                         g_free (basename);
2429
2430                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size, window);
2431
2432                         if (pixbuf != NULL) {
2433                                 priv->images_size += stream_size;
2434                                 priv->images_count ++;
2435                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2436                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2437                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, 
2438                                                              tny_mime_part_get_content_id (mime_part), pixbuf);
2439                                 g_object_unref (pixbuf);
2440
2441                                 tny_list_prepend (priv->images, (GObject *) mime_part);
2442                                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2443                         } else {
2444                                 modest_platform_information_banner (NULL, NULL,
2445                                                                     _("mail_ib_file_operation_failed"));
2446                         }
2447
2448                         g_free (filename);
2449                         g_object_unref (mime_part);
2450                         gnome_vfs_file_info_unref (info);
2451
2452                 }
2453         }
2454         g_object_unref (window);
2455 }
2456
2457 static void
2458 on_attach_file_response (GtkDialog *dialog,
2459                          gint       arg1,
2460                          gpointer   user_data)
2461 {
2462         GSList *uris = NULL;
2463         GSList *uri_node;
2464         GnomeVFSFileSize total_size, allowed_size;
2465         ModestMsgEditWindow *window;
2466         ModestMsgEditWindowPrivate *priv;
2467         gint att_num;
2468         guint64 att_size;
2469
2470         switch (arg1) {
2471         case GTK_RESPONSE_OK:
2472         {
2473                 gchar *current_folder;
2474
2475                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2476                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2477                 if (current_folder && current_folder != '\0') {
2478                         GError *err = NULL;
2479                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_ATTACH_FILE_PATH, 
2480                                                 current_folder, &err);
2481                         if (err != NULL) {
2482                                 g_debug ("Error storing latest used folder: %s", err->message);
2483                                 g_error_free (err);
2484                         }
2485                 }
2486                 g_free (current_folder);
2487         }
2488         break;
2489         default:
2490                 break;
2491         }
2492
2493         window = MODEST_MSG_EDIT_WINDOW (user_data);
2494         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2495
2496         /* allowed size is the maximum size - what's already there */
2497         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2498                                            &att_num, &att_size);
2499         allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size;
2500
2501         total_size = 0;
2502         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2503
2504                 const gchar *uri = (const gchar *) uri_node->data;
2505
2506                 total_size += 
2507                         modest_msg_edit_window_attach_file_one (window, uri, allowed_size);
2508
2509                 if (total_size > allowed_size) {
2510                         g_debug ("%s: total size: %u", 
2511                                    __FUNCTION__, (unsigned int)total_size);
2512                         break;
2513                 }
2514                 allowed_size -= total_size;
2515         }
2516         g_slist_foreach (uris, (GFunc) g_free, NULL);
2517         g_slist_free (uris);
2518
2519         gtk_widget_destroy (GTK_WIDGET (dialog));
2520 }
2521
2522 void
2523 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2524 {
2525         GtkWidget *dialog = NULL;
2526         ModestMsgEditWindowPrivate *priv;
2527         gchar *conf_folder;
2528
2529         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window));
2530
2531         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2532
2533         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2534                 return;
2535
2536         dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
2537                                                                     _("mcen_ia_select_attachment_title"),
2538                                                                     (GtkWindow *) window,
2539                                                                     GTK_FILE_CHOOSER_ACTION_OPEN);
2540         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
2541                                               MODEST_CONF_LATEST_ATTACH_FILE_PATH, NULL);
2542         if (conf_folder && conf_folder[0] != '\0') {
2543                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), conf_folder);
2544         } else {
2545                 gchar *docs_folder;
2546                 /* Set the default folder to documents folder */
2547                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
2548                 if (!docs_folder) {
2549                         /* fallback */
2550                         docs_folder = g_build_filename (g_getenv (MODEST_MAEMO_UTILS_MYDOCS_ENV),
2551                                                         ".documents", NULL);
2552                 }
2553                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), docs_folder);
2554                 g_free (docs_folder);
2555         }
2556         g_free (conf_folder);
2557         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2558         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2559                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2560
2561         /* Connect to response & show */
2562         g_signal_connect (dialog, "response", 
2563                           G_CALLBACK (on_attach_file_response), window);
2564         gtk_widget_show (dialog);
2565 }
2566
2567
2568 GnomeVFSFileSize
2569 modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window,
2570                                         const gchar *uri, 
2571                                         GnomeVFSFileSize allowed_size)
2572
2573 {
2574         GnomeVFSHandle *handle = NULL;
2575         ModestMsgEditWindowPrivate *priv;
2576         GnomeVFSResult result;
2577         GnomeVFSFileSize size = 0;
2578         g_return_val_if_fail (window, 0);
2579         g_return_val_if_fail (uri, 0);
2580
2581         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2582
2583         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2584         if (result == GNOME_VFS_OK) {
2585                 TnyMimePart *mime_part;
2586                 TnyStream *stream;
2587                 const gchar *mime_type = NULL;
2588                 gchar *basename;
2589                 gchar *escaped_filename;
2590                 gchar *filename;
2591                 gchar *content_id;
2592                 GnomeVFSFileInfo *info;
2593                 GnomeVFSURI *vfs_uri;
2594
2595                 gnome_vfs_close (handle);
2596                 vfs_uri = gnome_vfs_uri_new (uri);
2597                 
2598
2599                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2600                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2601                 g_free (escaped_filename);
2602                 gnome_vfs_uri_unref (vfs_uri);
2603
2604                 info = gnome_vfs_file_info_new ();
2605                 
2606                 if (gnome_vfs_get_file_info (uri, 
2607                                              info, 
2608                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2609                     == GNOME_VFS_OK)
2610                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2611                 mime_part = tny_platform_factory_new_mime_part
2612                         (modest_runtime_get_platform_factory ());
2613                 
2614                 /* try to get the attachment's size; this may fail for weird
2615                  * file systems, like obex, upnp... */
2616                 if (allowed_size != 0 &&
2617                     info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
2618                         size = info->size;
2619                         if (size > allowed_size) {
2620                                 modest_platform_information_banner (NULL, NULL,
2621                                                                     _("mail_ib_error_attachment_size"));
2622                                 g_free (filename);
2623                                 return 0;
2624                         }
2625                 } else
2626                         g_debug ("%s: could not get attachment size", __FUNCTION__);
2627                 
2628                 stream = create_stream_for_uri (uri);
2629                 
2630                 if (stream == NULL) {
2631
2632                         modest_platform_information_banner (NULL, NULL, _FM("sfil_ib_opening_not_allowed"));
2633
2634                         g_object_unref (mime_part);
2635                         g_free (filename);
2636                         gnome_vfs_file_info_unref (info);
2637                         return 0;
2638                 }
2639
2640                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2641                 g_object_unref (stream);
2642                 
2643                 content_id = g_strdup_printf ("%d", priv->next_cid);
2644                 tny_mime_part_set_content_id (mime_part, content_id);
2645                 g_free (content_id);
2646                 priv->next_cid++;
2647                 
2648                 basename = g_path_get_basename (filename);
2649                 tny_mime_part_set_filename (mime_part, basename);
2650                 g_free (basename);
2651                 
2652                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2653                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2654                                                         mime_part,
2655                                                         info->size == 0, info->size);
2656                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2657                 gtk_widget_show_all (priv->attachments_caption);
2658                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2659                 g_free (filename);
2660                 g_object_unref (mime_part);
2661                 gnome_vfs_file_info_unref (info);
2662         }
2663
2664         return size;
2665 }
2666
2667 void
2668 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2669                                            TnyList *att_list)
2670 {
2671         ModestMsgEditWindowPrivate *priv;
2672         TnyIterator *iter;
2673
2674         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2675         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2676
2677         if (att_list == NULL) {
2678                 att_list = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2679                 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), att_list, TRUE)) {
2680                         g_object_unref (att_list);
2681                         return;
2682                 }
2683         } else {
2684                 g_object_ref (att_list);
2685         }
2686
2687         if (tny_list_get_length (att_list) == 0) {
2688                 modest_platform_system_banner (NULL, NULL, _("TODO: no attachments selected to remove"));
2689         } else {
2690                 gboolean dialog_response;
2691                 gchar *message = NULL;
2692                 gchar *filename = NULL;
2693
2694                 if (tny_list_get_length (att_list) == 1) {
2695                         TnyMimePart *part;
2696                         iter = tny_list_create_iterator (att_list);
2697                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2698                         g_object_unref (iter);
2699                         if (TNY_IS_MSG (part)) {
2700                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2701                                 if (header) {
2702                                         filename = tny_header_dup_subject (header);
2703                                         g_object_unref (header);
2704                                 }
2705                                 if (filename == NULL) {
2706                                         filename = g_strdup (_("mail_va_no_subject"));
2707                                 }
2708                         } else {
2709                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2710                         }
2711                         g_object_unref (part);
2712                 } else {
2713                         filename = g_strdup ("");
2714                 }
2715                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", 
2716                                                     "emev_nc_delete_attachments",
2717                                                     tny_list_get_length (att_list)), filename);
2718                 g_free (filename);
2719
2720                 dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), 
2721                                                                            message);
2722                 g_free (message);
2723
2724                 if (dialog_response != GTK_RESPONSE_OK) {
2725                         g_object_unref (att_list);
2726                         return;
2727                 }
2728
2729                 for (iter = tny_list_create_iterator (att_list);
2730                      !tny_iterator_is_done (iter);
2731                      tny_iterator_next (iter)) {
2732                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2733                         const gchar *att_id;
2734                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2735
2736                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2737                                                                    mime_part);
2738                         if (tny_list_get_length (priv->attachments) == 0)
2739                                 gtk_widget_hide (priv->attachments_caption);
2740                         att_id = tny_mime_part_get_content_id (mime_part);
2741                         if (att_id != NULL)
2742                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2743                                                                  att_id);
2744                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2745                         g_object_unref (mime_part);
2746                 }
2747                 g_object_unref (iter);
2748         }
2749
2750         g_object_unref (att_list);
2751
2752         /* if the last attachment has been removed, focus the Subject: field */
2753         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view)))
2754                 gtk_widget_grab_focus (priv->subject_field);
2755 }
2756
2757 static void
2758 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2759                                             gpointer userdata)
2760 {
2761         ModestMsgEditWindowPrivate *priv;
2762         GdkColor new_color;
2763
2764         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2765
2766         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &new_color);
2767
2768         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) &new_color);
2769
2770         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2771 }
2772
2773 static void
2774 font_size_clicked (GtkToolButton *button,
2775                    ModestMsgEditWindow *window)
2776 {
2777         ModestMsgEditWindowPrivate *priv;
2778         GtkWidget *selector, *dialog;
2779         
2780         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2781
2782         selector = hildon_touch_selector_new ();
2783         hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector), priv->sizes_model, TRUE);
2784         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_size_index);
2785
2786         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2787         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2788         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_size"));
2789
2790         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2791                 gint new_index;
2792                 gchar *size_text;
2793                 gchar *markup;
2794                 WPTextBufferFormat format;
2795
2796                 new_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2797
2798                 memset (&format, 0, sizeof (format));
2799                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2800
2801                 format.cs.font_size = TRUE;
2802                 format.cs.text_position = TRUE;
2803                 format.cs.font = TRUE;
2804                 format.font_size = new_index;
2805 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2806
2807                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2808                                                    GINT_TO_POINTER (new_index)))
2809                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2810                 
2811                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2812                 size_text = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
2813                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", 
2814                                       size_text, "</span>", NULL);
2815                 g_free (size_text);
2816                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2817                 g_free (markup);
2818
2819         }
2820         gtk_widget_destroy (dialog);
2821
2822         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2823
2824 }
2825
2826 static void
2827 font_face_clicked (GtkToolButton *button,
2828                    ModestMsgEditWindow *window)
2829 {
2830         ModestMsgEditWindowPrivate *priv;
2831         GtkWidget *selector, *dialog;
2832         GtkCellRenderer *renderer;
2833         
2834         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2835
2836         selector = hildon_touch_selector_new ();
2837         renderer = gtk_cell_renderer_text_new ();
2838         g_object_set (G_OBJECT (renderer), "alignment", PANGO_ALIGN_CENTER, "xalign", 0.5, NULL);
2839         hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector), priv->faces_model, 
2840                                              renderer, "family", 0, "text", 0, NULL);
2841         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_face_index);
2842
2843         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2844         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2845         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_face"));
2846
2847         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2848                 gint new_font_index;
2849                 GtkTreePath *path;
2850                 GtkTreeIter iter;
2851
2852                 new_font_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2853                 path = gtk_tree_path_new_from_indices (new_font_index, -1);
2854                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2855                         gchar *face_name;
2856                         gchar *markup;
2857
2858                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2859
2860                         if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2861                                                            GINT_TO_POINTER(new_font_index)))
2862                                 wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2863
2864                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2865                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2866
2867                         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2868                         g_free (face_name);
2869                         g_free (markup);
2870                 }
2871                 gtk_tree_path_free (path);
2872
2873         }
2874         gtk_widget_destroy (dialog);
2875
2876         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2877
2878 }
2879
2880 void
2881 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2882                                 gboolean show)
2883 {
2884         ModestMsgEditWindowPrivate *priv = NULL;
2885         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2886
2887         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2888         if (!priv->update_caption_visibility)
2889                 return;
2890
2891         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2892         if (show)
2893                 gtk_widget_show (priv->cc_caption);
2894         else
2895                 gtk_widget_hide (priv->cc_caption);
2896
2897         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2898 }
2899
2900 void
2901 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2902                                  gboolean show)
2903 {
2904         ModestMsgEditWindowPrivate *priv = NULL;
2905         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2906
2907         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2908         if (!priv->update_caption_visibility)
2909                 return;
2910
2911         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2912         if (show)
2913                 gtk_widget_show (priv->bcc_caption);
2914         else
2915                 gtk_widget_hide (priv->bcc_caption);
2916
2917         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2918 }
2919
2920 static void
2921 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2922                                          ModestRecptEditor *editor)
2923 {
2924         ModestMsgEditWindowPrivate *priv;
2925
2926         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2927         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2928         
2929         /* we check for low-mem; in that case, show a warning, and don't allow
2930          * for the addressbook
2931          */
2932         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2933                 return;
2934
2935         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2936
2937         if (editor == NULL) {
2938                 GtkWidget *view_focus, *parent;
2939                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2940
2941                 /* This code should be kept in sync with ModestRecptEditor. The
2942                    textview inside the recpt editor is the one that really gets the
2943                    focus. As it's inside a scrolled window, and this one inside the
2944                    hbox recpt editor inherits from, we'll need to go up in the 
2945                    hierarchy to know if the text view is part of the recpt editor
2946                    or if it's a different text entry */
2947                 parent = gtk_widget_get_parent (view_focus);
2948                 if (parent && MODEST_IS_RECPT_EDITOR (parent))
2949                         editor = MODEST_RECPT_EDITOR (parent);
2950
2951                 if (editor == NULL)
2952                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2953         }
2954
2955         modest_address_book_select_addresses (editor, GTK_WINDOW (window));
2956 }
2957
2958 void
2959 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2960 {
2961         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2962
2963         modest_msg_edit_window_open_addressbook (window, NULL);
2964 }
2965
2966 static void
2967 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2968                                      gboolean show_toolbar)
2969 {
2970         ModestWindowPrivate *parent_priv;
2971         ModestMsgEditWindowPrivate *priv;
2972
2973         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2974         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2975         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2976
2977         /* We can not just use the code of
2978            modest_msg_edit_window_setup_toolbar because it has a
2979            mixture of both initialization and creation code. */
2980         if (show_toolbar) {
2981                 gint current_format;
2982                 current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2983                         ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2984                 if (current_format == MODEST_FILE_FORMAT_PLAIN_TEXT) {
2985                         gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2986                 } else {
2987                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2988                 }
2989         } else {
2990                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2991         }
2992 }
2993
2994 void
2995 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2996                                            TnyHeaderFlags priority_flags)
2997 {
2998         ModestMsgEditWindowPrivate *priv;
2999         ModestWindowPrivate *parent_priv;
3000
3001         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3002
3003         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3004         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3005
3006         if (priv->priority_flags != priority_flags) {
3007                 GtkAction *priority_action = NULL;
3008
3009                 priv->priority_flags = priority_flags;
3010
3011                 switch (priority_flags) {
3012                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
3013                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
3014                                                       MODEST_HEADER_ICON_HIGH, 
3015                                                       HILDON_ICON_SIZE_SMALL);
3016                         gtk_widget_show (priv->priority_icon);
3017                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3018                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
3019                         break;
3020                 case TNY_HEADER_FLAG_LOW_PRIORITY:
3021                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
3022                                                       MODEST_HEADER_ICON_LOW,
3023                                                       HILDON_ICON_SIZE_SMALL);
3024                         gtk_widget_show (priv->priority_icon);
3025                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3026                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
3027                         break;
3028                 default:
3029                         gtk_widget_hide (priv->priority_icon);
3030                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3031                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
3032                         break;
3033                 }
3034                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
3035                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3036         }
3037         gtk_widget_queue_resize (priv->subject_box);
3038 }
3039
3040 void
3041 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
3042                                         gint file_format)
3043 {
3044         ModestMsgEditWindowPrivate *priv;
3045         ModestWindowPrivate *parent_priv;
3046         gint current_format;
3047
3048         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3049
3050         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3051         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3052
3053         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
3054                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
3055
3056         gtk_widget_set_no_show_all (GTK_WIDGET (parent_priv->toolbar), TRUE);
3057
3058         if (current_format != file_format) {
3059                 switch (file_format) {
3060                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
3061                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
3062                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
3063                         if (parent_priv->toolbar)
3064                                 gtk_widget_show (parent_priv->toolbar);
3065                         break;
3066                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
3067                 {
3068                         GtkWidget *dialog;
3069                         gint response;
3070                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
3071                         response = gtk_dialog_run (GTK_DIALOG (dialog));
3072                         gtk_widget_destroy (dialog);
3073                         if (response == GTK_RESPONSE_OK) {
3074                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
3075                                 if (parent_priv->toolbar)
3076                                         gtk_widget_hide (parent_priv->toolbar);
3077                         } else {
3078                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu"));
3079                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
3080                         }
3081                 }
3082                         break;
3083                 }
3084                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3085                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3086                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3087         }
3088 }
3089
3090 void
3091 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
3092 {
3093         GtkWidget *dialog;
3094         ModestMsgEditWindowPrivate *priv;
3095         WPTextBufferFormat oldfmt, fmt;
3096         gint old_position = 0;
3097         gint response = 0;
3098         gint position = 0;
3099         gint font_size;
3100         GdkColor *color = NULL;
3101         gboolean bold, bold_set, italic, italic_set;
3102         gboolean underline, underline_set;
3103         gboolean strikethrough, strikethrough_set;
3104         gboolean position_set;
3105         gboolean font_size_set, font_set, color_set;
3106         gchar *font_name;
3107
3108         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3109         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3110         
3111         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
3112         modest_window_mgr_set_modal (modest_runtime_get_window_mgr(),
3113                                      GTK_WINDOW(dialog), GTK_WINDOW (window));
3114
3115         /* First we get the currently selected font information */
3116         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
3117
3118         switch (oldfmt.text_position) {
3119         case TEXT_POSITION_NORMAL:
3120                 old_position = 0;
3121                 break;
3122         case TEXT_POSITION_SUPERSCRIPT:
3123                 old_position = 1;
3124                 break;
3125         default:
3126                 old_position = -1;
3127                 break;
3128         }
3129
3130         g_object_set (G_OBJECT (dialog),
3131                       "bold", oldfmt.bold != FALSE,
3132                       "bold-set", !oldfmt.cs.bold,
3133                       "underline", oldfmt.underline != FALSE,
3134                       "underline-set", !oldfmt.cs.underline,
3135                       "italic", oldfmt.italic != FALSE,
3136                       "italic-set", !oldfmt.cs.italic,
3137                       "strikethrough", oldfmt.strikethrough != FALSE,
3138                       "strikethrough-set", !oldfmt.cs.strikethrough,
3139                       "color", &oldfmt.color,
3140                       "color-set", !oldfmt.cs.color,
3141                       "size", wp_font_size[oldfmt.font_size],
3142                       "size-set", !oldfmt.cs.font_size,
3143                       "position", old_position,
3144                       "position-set", !oldfmt.cs.text_position,
3145                       "family", wp_get_font_name (oldfmt.font),
3146                       "family-set", !oldfmt.cs.font,
3147                       NULL);
3148
3149         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
3150                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
3151         gtk_widget_show_all (dialog);
3152         priv->font_dialog = dialog;
3153         response = gtk_dialog_run (GTK_DIALOG (dialog));
3154         priv->font_dialog = NULL;
3155         if (response == GTK_RESPONSE_OK) {
3156
3157                 g_object_get( dialog,
3158                               "bold", &bold,
3159                               "bold-set", &bold_set,
3160                               "underline", &underline,
3161                               "underline-set", &underline_set,
3162                               "italic", &italic,
3163                               "italic-set", &italic_set,
3164                               "strikethrough", &strikethrough,
3165                               "strikethrough-set", &strikethrough_set,
3166                               "color", &color,
3167                               "color-set", &color_set,
3168                               "size", &font_size,
3169                               "size-set", &font_size_set,
3170                               "family", &font_name,
3171                               "family-set", &font_set,
3172                               "position", &position,
3173                               "position-set", &position_set,
3174                               NULL );
3175                 
3176         }       
3177
3178         if (response == GTK_RESPONSE_OK) {
3179                 memset(&fmt, 0, sizeof(fmt));
3180                 if (bold_set) {
3181                         fmt.bold = bold;
3182                         fmt.cs.bold = TRUE;
3183                 }
3184                 if (italic_set) {
3185                         fmt.italic = italic;
3186                         fmt.cs.italic = TRUE;
3187                 }
3188                 if (underline_set) {
3189                         fmt.underline = underline;
3190                         fmt.cs.underline = TRUE;
3191                 }
3192                 if (strikethrough_set) {
3193                         fmt.strikethrough = strikethrough;
3194                         fmt.cs.strikethrough = TRUE;
3195                 }
3196                 if (position_set) {
3197                         fmt.text_position =
3198                                 ( position == 0 )
3199                                 ? TEXT_POSITION_NORMAL
3200                                 : ( ( position == 1 )
3201                                     ? TEXT_POSITION_SUPERSCRIPT
3202                                     : TEXT_POSITION_SUBSCRIPT );
3203                         fmt.cs.text_position = TRUE;
3204                         fmt.font_size = oldfmt.font_size;
3205                 }
3206                 if (color_set) {
3207                         fmt.color = *color;
3208                         fmt.cs.color = TRUE;
3209                 }
3210                 if (font_set) {
3211                         fmt.font = wp_get_font_index(font_name,
3212                                                      DEFAULT_FONT);
3213                         fmt.cs.font = TRUE;
3214                 }
3215                 g_free(font_name);
3216                 if (font_size_set) {
3217                         fmt.cs.font_size = TRUE;
3218                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
3219                 }
3220                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
3221                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3222         }
3223         gtk_widget_destroy (dialog);
3224         
3225         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
3226 }
3227
3228 void
3229 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
3230 {
3231         ModestMsgEditWindowPrivate *priv;
3232         ModestWindowPrivate *parent_priv;
3233         gboolean was_rich_text, is_rich_text;
3234
3235         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3236         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3237         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3238
3239         was_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3240
3241         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
3242
3243         is_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3244
3245         if (parent_priv->toolbar && was_rich_text != is_rich_text) {
3246                 if (is_rich_text)
3247                         gtk_widget_show (parent_priv->toolbar);
3248                 else
3249                         gtk_widget_hide (parent_priv->toolbar);
3250         }
3251
3252         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3253         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3254 }
3255
3256 void
3257 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
3258 {
3259         ModestMsgEditWindowPrivate *priv;
3260
3261         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3262         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3263         
3264         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
3265
3266         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3267         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3268
3269 }
3270
3271 static void  
3272 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
3273 {
3274         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3275
3276         priv->can_undo = can_undo;
3277 }
3278
3279 static void  
3280 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
3281 {
3282         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3283
3284         priv->can_redo = can_redo;
3285 }
3286
3287 gboolean            
3288 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
3289 {
3290         ModestMsgEditWindowPrivate *priv;
3291         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3292         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3293
3294         return priv->can_undo;
3295 }
3296
3297 gboolean            
3298 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
3299 {
3300         ModestMsgEditWindowPrivate *priv;
3301         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3302         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3303
3304         return priv->can_redo;
3305 }
3306
3307
3308 static void
3309 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
3310 {
3311         GtkTextIter iter;
3312         GtkTextIter match_start, match_end;
3313
3314         if (image_id == NULL)
3315                 return;
3316
3317         gtk_text_buffer_get_start_iter (buffer, &iter);
3318
3319         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
3320                 GSList *tags = gtk_text_iter_get_tags (&match_start);
3321                 GSList *node;
3322                 for (node = tags; node != NULL; node = g_slist_next (node)) {
3323                         GtkTextTag *tag = (GtkTextTag *) node->data;
3324                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
3325                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
3326                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
3327                                         gint offset;
3328                                         offset = gtk_text_iter_get_offset (&match_start);
3329                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
3330                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
3331                                 }
3332                         }
3333                 }
3334                 gtk_text_iter_forward_char (&iter);
3335         }
3336 }
3337
3338 gboolean
3339 message_is_empty (ModestMsgEditWindow *window)
3340 {
3341         ModestMsgEditWindowPrivate *priv = NULL;
3342
3343         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3344         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3345
3346         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
3347          * so we can ignore markup.
3348          */
3349         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
3350         gint count = 0;
3351         if (buf)
3352                 count = gtk_text_buffer_get_char_count (buf);
3353
3354         return count == 0;
3355 }
3356
3357 static gboolean
3358 msg_body_focus (GtkWidget *focus,
3359                 GdkEventFocus *event,
3360                 gpointer userdata)
3361 {
3362         
3363         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
3364         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
3365         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
3366         return FALSE;
3367 }
3368
3369 static void
3370 recpt_field_changed (GtkTextBuffer *buffer,
3371                   ModestMsgEditWindow *editor)
3372 {
3373         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3374         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3375 }
3376
3377 static void
3378 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
3379 {
3380         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3381         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3382 }
3383
3384 void
3385 modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor,
3386                                      gboolean modified)
3387 {
3388         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3389         GtkTextBuffer *buffer;
3390
3391         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3392         gtk_text_buffer_set_modified (buffer, modified);
3393         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3394         gtk_text_buffer_set_modified (buffer, modified);
3395         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3396         gtk_text_buffer_set_modified (buffer, modified);
3397         gtk_text_buffer_set_modified (priv->text_buffer, modified);
3398 }
3399
3400 gboolean
3401 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
3402 {
3403         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3404         const char *account_name;
3405         GtkTextBuffer *buffer;
3406
3407         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3408         if (gtk_text_buffer_get_modified (buffer))
3409                 return TRUE;
3410         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3411         if (gtk_text_buffer_get_modified (buffer))
3412                 return TRUE;
3413         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3414         if (gtk_text_buffer_get_modified (buffer))
3415                 return TRUE;
3416         if (gtk_text_buffer_get_modified (priv->text_buffer))
3417                 return TRUE;
3418
3419         account_name = modest_selector_get_active_id (priv->from_field);
3420         if (priv->original_mailbox) {
3421                 if (!account_name || strcmp (account_name, priv->original_mailbox))
3422                         return TRUE;
3423         } else if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) {
3424                 return TRUE;
3425         }
3426
3427         return FALSE;
3428 }
3429
3430
3431
3432
3433 gboolean
3434 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
3435 {
3436         ModestMsgEditWindowPrivate *priv = NULL;
3437         GSList *address_list = NULL;
3438
3439         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3440         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3441
3442         /* check if there's no recipient added */
3443         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3444             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3445             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3446                 /* no recipient contents, then select contacts */
3447                 modest_msg_edit_window_open_addressbook (window, NULL);
3448                 return FALSE;
3449         }
3450
3451         /* Check names */
3452         g_object_ref (window);
3453         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),
3454                                               (add_to_addressbook) ? &address_list : NULL)) {
3455                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3456                 g_object_unref (window);
3457                 return FALSE;
3458         }
3459         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),
3460                                               (add_to_addressbook) ? &address_list : NULL)) {
3461                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3462                 g_object_unref (window);
3463                 return FALSE;
3464         }
3465         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field),
3466                                               (add_to_addressbook) ? &address_list : NULL)) {
3467                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3468                 g_object_unref (window);
3469                 return FALSE;
3470         }
3471
3472         /* Add contacts to address book */
3473         if (add_to_addressbook && address_list)
3474                 modest_address_book_add_address_list (address_list);
3475
3476         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3477             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3478                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3479         g_object_unref (window);
3480
3481         return TRUE;
3482
3483 }
3484
3485 void
3486 modest_msg_edit_window_add_to_contacts (ModestMsgEditWindow *self)
3487 {
3488         GSList *recipients = NULL;
3489         ModestMsgEditWindowPrivate *priv;
3490         gchar *joined, *after_remove, *to, *cc, *bcc;
3491
3492         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
3493
3494         /* First of all check names */
3495         if (!modest_msg_edit_window_check_names (self, FALSE))
3496                 return;
3497
3498         if (!modest_msg_edit_window_has_pending_addresses (self))
3499                 return;
3500
3501         /* Don't add the from obviously */
3502         to  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->to_field));
3503         cc  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->cc_field));
3504         bcc =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->bcc_field));
3505
3506         joined = modest_text_utils_join_addresses (NULL, to, cc, bcc);
3507         g_free (to);
3508         g_free (cc);
3509         g_free (bcc);
3510
3511         after_remove = modest_text_utils_remove_duplicate_addresses (joined);
3512         g_free (joined);
3513
3514         recipients = modest_text_utils_split_addresses_list (after_remove);
3515         g_free (after_remove);
3516
3517         if (recipients) {
3518                 /* Offer the user to add recipients to the address book */
3519                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3520                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3521         }
3522 }
3523
3524 static void
3525 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3526                                                ModestMsgEditWindow *window)
3527 {
3528         modest_msg_edit_window_offer_attach_file (window);
3529 }
3530
3531 const gchar *
3532 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3533 {
3534         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3535
3536         return priv->clipboard_text;
3537 }
3538
3539 static void
3540 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3541                                                GdkEvent *event,
3542                                                ModestMsgEditWindow *window)
3543 {
3544         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3545         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3546         gchar *text = NULL;
3547
3548         /* It could happen that the window was already closed */
3549         if (!GTK_WIDGET_VISIBLE (window))
3550                 return;
3551
3552         g_object_ref (window);
3553         text = gtk_clipboard_wait_for_text (selection_clipboard);
3554
3555         if (priv->clipboard_text != NULL) {
3556                 g_free (priv->clipboard_text);
3557         }
3558         priv->clipboard_text = text;
3559
3560         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3561
3562         g_object_unref (window);
3563 }
3564
3565 static gboolean clipboard_owner_change_idle (gpointer userdata)
3566 {
3567         ModestMsgEditWindow *window = (ModestMsgEditWindow *) userdata;
3568         ModestMsgEditWindowPrivate *priv;
3569
3570         gdk_threads_enter ();
3571         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3572         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3573
3574         priv->clipboard_owner_idle = 0;
3575         modest_msg_edit_window_clipboard_owner_change (NULL, NULL, window);
3576         gdk_threads_leave ();
3577
3578         return FALSE;
3579 }
3580
3581 static void
3582 modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window)
3583 {
3584         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3585         if (priv->clipboard_owner_idle == 0) {
3586                 priv->clipboard_owner_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
3587                                                               clipboard_owner_change_idle, 
3588                                                               g_object_ref (window),
3589                                                               g_object_unref);
3590         }
3591 }
3592
3593 static void 
3594 subject_field_move_cursor (GtkEntry *entry,
3595                            GtkMovementStep step,
3596                            gint a1,
3597                            gboolean a2,
3598                            gpointer window)
3599 {
3600         /* It could happen that the window was already closed */
3601         if (!GTK_WIDGET_VISIBLE (window))
3602                 return;
3603
3604         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3605 }
3606
3607 static void 
3608 update_window_title (ModestMsgEditWindow *window)
3609 {
3610         ModestMsgEditWindowPrivate *priv = NULL;
3611         const gchar *subject;
3612
3613         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3614         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3615         if (subject == NULL || subject[0] == '\0')
3616                 subject = _("mail_va_new_email");
3617
3618         modest_window_set_title (MODEST_WINDOW (window), subject);
3619
3620 }
3621
3622
3623 static void  
3624 body_insert_text (GtkTextBuffer *buffer, 
3625                   GtkTextIter *location,
3626                   gchar *text,
3627                   gint len,
3628                   ModestMsgEditWindow *window)
3629 {
3630         GtkTextIter end_iter;
3631         gint offset;
3632         glong utf8_len;
3633         gint line;
3634         gchar *text_offset;
3635         gint text_lines;
3636
3637         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3638
3639         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end_iter);
3640
3641         offset = gtk_text_iter_get_offset (&end_iter);
3642         line = gtk_text_iter_get_line (&end_iter);
3643
3644         text_offset = text;
3645         text_lines = 0;
3646         while (text_offset < text + len) {
3647                 if (*text_offset == '\n')
3648                         text_lines++;
3649                 if (text_lines + line >= MAX_BODY_LINES) {
3650                         len = text_offset - text;
3651                         break;
3652                 }
3653                 text_offset++;
3654         }
3655
3656         utf8_len = g_utf8_strlen (text, len);
3657
3658         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3659                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
3660                 if (line <= MAX_BODY_LINES && offset < MAX_BODY_LENGTH)
3661                 {
3662                         gchar *result;
3663                         gchar *utf8_end;
3664
3665                         utf8_end = g_utf8_offset_to_pointer (text, MAX_BODY_LENGTH - offset);
3666
3667                         /* Prevent endless recursion */
3668                         result = g_strndup (text, utf8_end - text);
3669                         g_signal_handlers_block_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3670                         g_signal_emit_by_name (G_OBJECT (buffer), "insert-text", location,
3671                                                (gpointer) result, (gpointer) (utf8_end - text),
3672                                                (gpointer) window);
3673                         g_signal_handlers_unblock_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3674                 }
3675
3676         }
3677         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3678                 if (priv->max_chars_banner == NULL) {
3679                         priv->max_chars_banner = hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3680                                                                                  _CS("ckdg_ib_maximum_characters_reached"));
3681                         g_object_weak_ref (G_OBJECT (priv->max_chars_banner), (GWeakNotify) max_chars_banner_unref, window);
3682                 }
3683         }
3684 }
3685
3686 static void  
3687 subject_field_changed (GtkEditable *editable, 
3688                        ModestMsgEditWindow *window)
3689 {
3690         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3691         update_window_title (window);
3692         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3693         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3694         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3695 }
3696 static void  
3697 subject_field_insert_text (GtkEditable *editable, 
3698                            gchar *new_text,
3699                            gint new_text_length,
3700                            gint *position,
3701                            ModestMsgEditWindow *window)
3702 {
3703         GString *result = g_string_new ("");
3704         gchar *current;
3705         gint result_len = 0;
3706         const gchar *entry_text = NULL;
3707         gint old_length;
3708
3709         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3710         old_length = g_utf8_strlen (entry_text, -1);
3711
3712         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3713                 gunichar c = g_utf8_get_char_validated (current, 8);
3714                 /* Invalid unichar, stop */
3715                 if (c == -1)
3716                         break;
3717                 /* a bullet */
3718                 if (c == 0x2022)
3719                         continue;
3720                 result = g_string_append_unichar (result, c);
3721                 result_len++;
3722         }
3723
3724         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3725                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3726                 if (result_len > 0)
3727                 {
3728                         /* Prevent endless recursion */
3729                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3730                         g_signal_emit_by_name (editable, "insert-text", 
3731                                                (gpointer) result->str, (gpointer) result->len,
3732                                                (gpointer) position, (gpointer) window);
3733                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3734                 }
3735         }
3736
3737         if (result_len + old_length > 1000) {
3738                 modest_platform_system_banner (GTK_WIDGET (window), NULL, 
3739                                                 _CS("ckdg_ib_maximum_characters_reached"));
3740         }
3741         g_string_free (result, TRUE);
3742 }
3743
3744 void
3745 modest_msg_edit_window_toggle_isearch_toolbar (ModestMsgEditWindow *window,
3746                                                gboolean show)
3747 {
3748         ModestMsgEditWindowPrivate *priv = NULL;
3749
3750         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3751         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3752
3753         gtk_widget_set_no_show_all (priv->isearch_toolbar, FALSE);
3754
3755         if (show) {
3756                 gtk_widget_show_all (priv->isearch_toolbar);
3757                 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
3758         } else {
3759                 gtk_widget_hide_all (priv->isearch_toolbar);
3760                 gtk_widget_grab_focus (priv->msg_body);
3761         }
3762 }
3763
3764 static gboolean 
3765 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3766                                           const gchar *str,
3767                                           GtkTextIter *match_start,
3768                                           GtkTextIter *match_end)
3769 {
3770         GtkTextIter end_iter;
3771         gchar *str_casefold;
3772         gint str_chars_n;
3773         gchar *range_text;
3774         gchar *range_casefold;
3775         gint offset;
3776         gint range_chars_n;
3777         gboolean result = FALSE;
3778
3779         if (str == NULL)
3780                 return TRUE;
3781         
3782         /* get end iter */
3783         end_iter = *iter;
3784         gtk_text_iter_forward_to_end (&end_iter);
3785
3786         str_casefold = g_utf8_casefold (str, -1);
3787         str_chars_n = strlen (str);
3788
3789         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3790         range_casefold = g_utf8_casefold (range_text, -1);
3791         range_chars_n = strlen (range_casefold);
3792
3793         if (range_chars_n < str_chars_n) {
3794                 g_free (str_casefold);
3795                 g_free (range_text);
3796                 g_free (range_casefold);
3797                 return FALSE;
3798         }
3799
3800         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3801                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3802                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3803                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3804                         result = TRUE;
3805                         if (!gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3806                                                            match_start, match_end, NULL)) {
3807                                 g_debug ("Matched string with collate, but not matched in model");
3808                         }
3809                         g_free (found_text);
3810                 }
3811                 g_free (range_subtext);
3812                 if (result)
3813                         break;
3814         }
3815         g_free (str_casefold);
3816         g_free (range_text);
3817         g_free (range_casefold);
3818
3819         return result;
3820 }
3821
3822
3823 static void 
3824 modest_msg_edit_window_isearch_toolbar_search (GtkWidget *widget,
3825                                                ModestMsgEditWindow *window)
3826 {
3827         const gchar *current_search = NULL;
3828         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3829         gboolean result;
3830         GtkTextIter selection_start, selection_end;
3831         GtkTextIter match_start, match_end;
3832         gboolean continue_search = FALSE;
3833
3834         if (message_is_empty (window)) {
3835                 g_free (priv->last_search);
3836                 priv->last_search = NULL;
3837                 modest_platform_system_banner (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3838                 return;
3839         }
3840
3841         current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
3842         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3843                 g_free (priv->last_search);
3844                 priv->last_search = NULL;
3845                 /* Information banner about empty search */
3846                 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
3847                 return;
3848         }
3849
3850         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3851                 continue_search = TRUE;
3852         } else {
3853                 g_free (priv->last_search);
3854                 priv->last_search = g_strdup (current_search);
3855         }
3856
3857         if (continue_search) {
3858                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3859                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3860                                                                    &match_start, &match_end);
3861                 if (!result)
3862                         modest_platform_system_banner (NULL, NULL, _HL("ckct_ib_find_search_complete"));
3863         } else {
3864                 GtkTextIter buffer_start;
3865                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3866                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3867                                                                    &match_start, &match_end);
3868                 if (!result)
3869                         modest_platform_system_banner (NULL, NULL, _HL("ckct_ib_find_no_matches"));
3870         }
3871
3872         /* Mark as selected the string found in search */
3873         if (result) {
3874                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3875                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3876                 correct_scroll_without_drag_check (MODEST_MSG_EDIT_WINDOW (window), FALSE);
3877         } else {
3878                 g_free (priv->last_search);
3879                 priv->last_search = NULL;
3880         }
3881 }
3882
3883 gboolean 
3884 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3885 {
3886         ModestMsgEditWindowPrivate *priv;
3887
3888         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3889         return priv->sent;
3890 }
3891
3892 void 
3893 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3894                                  gboolean sent)
3895 {
3896         ModestMsgEditWindowPrivate *priv;
3897
3898         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3899         priv->sent = sent;
3900 }
3901
3902 static void
3903 modest_msg_edit_window_isearch_toolbar_close (GtkWidget *widget,
3904                                               ModestMsgEditWindow *window)
3905 {
3906         modest_msg_edit_window_toggle_isearch_toolbar (window, FALSE);
3907 }
3908
3909 void
3910 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3911                                   TnyMsg *draft)
3912 {
3913         ModestMsgEditWindowPrivate *priv;
3914         TnyHeader *header = NULL;
3915
3916         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3917         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3918
3919         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3920
3921         if (priv->draft_msg != NULL) {
3922                 g_object_unref (priv->draft_msg);
3923         }
3924
3925         if (draft != NULL) {
3926                 g_object_ref (draft);
3927                 header = tny_msg_get_header (draft);
3928                 if (priv->msg_uid) {
3929                         g_free (priv->msg_uid);
3930                         priv->msg_uid = NULL;
3931                 }
3932                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3933         }
3934
3935         priv->draft_msg = draft;
3936 }
3937
3938 static void  
3939 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3940                        GtkTextIter *start, GtkTextIter *end,
3941                        gpointer userdata)
3942 {
3943         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3944         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3945         gchar *tag_name;
3946
3947         if (tag == NULL) return;
3948         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3949         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3950                 replace_with_images (window, priv->images);
3951         }
3952 }
3953
3954 void                    
3955 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3956                                  TnyMimePart *part)
3957 {
3958         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3959
3960         g_return_if_fail (TNY_IS_MIME_PART (part));
3961         tny_list_prepend (priv->attachments, (GObject *) part);
3962         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3963         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3964         gtk_widget_show_all (priv->attachments_caption);
3965         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3966 }
3967
3968 const gchar*    
3969 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3970 {
3971         ModestMsgEditWindowPrivate *priv;
3972
3973         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3974         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3975
3976         return priv->msg_uid;
3977 }
3978
3979 GtkWidget *
3980 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3981                                          ModestMsgEditWindowWidgetType widget_type)
3982 {
3983         ModestMsgEditWindowPrivate *priv;
3984
3985         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3986         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3987
3988         switch (widget_type) {
3989         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3990                 return priv->msg_body;
3991                 break;
3992         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3993                 return priv->to_field;
3994                 break;
3995         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3996                 return priv->cc_field;
3997                 break;
3998         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3999                 return priv->bcc_field;
4000                 break;
4001         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
4002                 return priv->subject_field;
4003                 break;
4004         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
4005                 return priv->attachments_view;
4006                 break;
4007         default:
4008                 return NULL;
4009         }
4010 }
4011
4012 static void 
4013 remove_tags (WPTextBuffer *buffer)
4014 {
4015         GtkTextIter start, end;
4016
4017         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
4018         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
4019
4020         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
4021 }
4022
4023 static void
4024 on_account_removed (TnyAccountStore *account_store, 
4025                     TnyAccount *account,
4026                     gpointer user_data)
4027 {
4028         /* Do nothing if it's a store account, because we use the
4029            transport to send the messages */
4030         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
4031                 const gchar *parent_acc = NULL;
4032                 const gchar *our_acc = NULL;
4033
4034                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
4035                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
4036                 /* Close this window if I'm showing a message of the removed account */
4037                 if (strcmp (parent_acc, our_acc) == 0)
4038                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
4039         }
4040 }
4041
4042 static void
4043 update_signature (ModestMsgEditWindow *self,
4044                   const gchar *old_account,
4045                   const gchar *new_account)
4046 {
4047         ModestMsgEditWindowPrivate *priv;
4048         gboolean has_old_signature, has_new_signature;
4049         GtkTextIter iter;
4050         ModestAccountMgr *mgr;
4051         gchar *signature;
4052
4053         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4054
4055         gtk_text_buffer_begin_user_action (priv->text_buffer);
4056
4057         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
4058         mgr = modest_runtime_get_account_mgr ();
4059
4060
4061         if (old_account) {
4062                 signature = modest_account_mgr_get_signature_from_recipient (mgr, old_account, &has_old_signature);
4063                 if (has_old_signature) {
4064                         GtkTextIter match_start, match_end;
4065                         /* We cannot use
4066                            MODEST_TEXT_UTILS_SIGNATURE_MARKER as it
4067                            seems that the search has some problems
4068                            with the blank space at the end */
4069                         if (gtk_text_iter_forward_search (&iter, "--",
4070                                                           GTK_TEXT_SEARCH_TEXT_ONLY,
4071                                                           &match_start, NULL, NULL)) {
4072                                 gtk_text_buffer_get_end_iter (priv->text_buffer ,&match_end);
4073                                 gtk_text_buffer_delete (priv->text_buffer, &match_start, &match_end);
4074                                 iter = match_start;
4075                         } else if (gtk_text_iter_forward_search (&iter, _("mcen_ia_editor_original_message"), 0,
4076                                                                  &match_start, &match_end, NULL)) {
4077                                 iter = match_start;
4078                         }
4079                 } else {
4080                         gtk_text_buffer_get_end_iter (priv->text_buffer, &iter);
4081                 }
4082                 g_free (signature);
4083         }
4084
4085         priv->last_from_account = modest_selector_get_active_id (priv->from_field);
4086         signature = modest_account_mgr_get_signature_from_recipient (mgr, new_account, &has_new_signature);
4087         if (has_new_signature) {
4088
4089                 gchar *full_signature = g_strconcat ((gtk_text_iter_starts_line (&iter)) ? "" : "\n",
4090                                                      MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n",
4091                                                      signature, NULL);
4092                 gtk_text_buffer_insert (priv->text_buffer, &iter, full_signature, -1);
4093                 g_free (full_signature);
4094         }
4095         g_free (signature);
4096         gtk_text_buffer_end_user_action (priv->text_buffer);
4097 }
4098
4099 static void update_branding (ModestMsgEditWindow *self,
4100                              const gchar *new_account)
4101 {
4102         ModestMsgEditWindowPrivate *priv;
4103         ModestAccountMgr *mgr;
4104         const GdkPixbuf *new_icon = NULL;
4105         gchar *new_label = NULL;
4106         gboolean show = FALSE;
4107
4108         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4109
4110         mgr = modest_runtime_get_account_mgr ();
4111
4112         modest_account_mgr_get_branding_from_recipient (mgr, new_account, &new_label, &new_icon, MODEST_ICON_SIZE_SMALL);
4113         if (new_icon) {
4114                 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->brand_icon), (GdkPixbuf *) new_icon);
4115                 gtk_widget_show (priv->brand_icon);
4116                 show = TRUE;
4117         } else {
4118                 gtk_widget_hide (priv->brand_icon);
4119         }
4120         if (new_label) {
4121                 gtk_label_set_text (GTK_LABEL (priv->brand_label), new_label);
4122                 gtk_widget_show (priv->brand_label);
4123                 g_free (new_label);
4124                 show = TRUE;
4125         } else {
4126                 gtk_widget_hide (priv->brand_label);
4127         }
4128
4129         if (show)
4130                 gtk_widget_show (priv->brand_container);
4131         else
4132                 gtk_widget_hide (priv->brand_container);
4133 }
4134
4135 static void
4136 from_field_changed (GtkWidget *button,
4137                     ModestMsgEditWindow *self)
4138 {
4139         ModestMsgEditWindowPrivate *priv;
4140         gchar *old_account, *new_account;
4141
4142         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4143
4144         old_account = priv->last_from_account;
4145         new_account = modest_selector_get_active_id (priv->from_field);
4146
4147         if (!new_account) {
4148                 g_warning ("%s, could not get the new account", __FUNCTION__);
4149                 return;
4150         }
4151
4152         /* If the From is the same do nothing */
4153         if (old_account && new_account && !strcmp (old_account, new_account))
4154                 return;
4155
4156         priv->last_from_account = new_account;
4157
4158         update_signature (self, old_account, new_account);
4159         update_branding (self, new_account);
4160
4161 }
4162
4163 typedef struct _MessageSettingsHelper {
4164         ModestMsgEditWindow *window;
4165         GSList *priority_group;
4166         GSList *format_group;
4167         GtkToggleButton *current_priority;
4168         GtkToggleButton *current_format;
4169 } MessageSettingsHelper;
4170
4171 static void
4172 on_priority_toggle (GtkToggleButton *button, 
4173                     MessageSettingsHelper *helper)
4174 {
4175         GSList *node;
4176         ModestMsgEditWindowPrivate *priv;
4177
4178         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4179         if (gtk_toggle_button_get_active (button)) {
4180
4181                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4182                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4183                         if ((node_button != button) &&
4184                             gtk_toggle_button_get_active (node_button)) {
4185                                 gtk_toggle_button_set_active (node_button, FALSE);
4186                         }
4187                 }
4188                 helper->current_priority = button;
4189         } else {
4190                 gboolean found = FALSE;
4191                 /* If no one is active, activate it again */
4192                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4193                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4194                         if (gtk_toggle_button_get_active (node_button)) {
4195                                 found = TRUE;
4196                                 break;
4197                         }
4198                 }
4199                 if (!found) {
4200                         gtk_toggle_button_set_active (button, TRUE);
4201                 }
4202         }
4203 }
4204
4205 static void
4206 on_format_toggle (GtkToggleButton *button,
4207                   MessageSettingsHelper *helper)
4208 {
4209         GSList *node;
4210         ModestMsgEditWindowPrivate *priv;
4211
4212         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4213         if (gtk_toggle_button_get_active (button)) {
4214
4215                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4216                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4217                         if ((node_button != button) &&
4218                             gtk_toggle_button_get_active (node_button)) {
4219                                 gtk_toggle_button_set_active (node_button, FALSE);
4220                         }
4221                 }
4222                 helper->current_format = button;
4223         } else {
4224                 gboolean found = FALSE;
4225                 /* If no one is active, activate it again */
4226                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4227                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4228                         if (gtk_toggle_button_get_active (node_button)) {
4229                                 found = TRUE;
4230                                 break;
4231                         }
4232                 }
4233                 if (!found) {
4234                         gtk_toggle_button_set_active (button, TRUE);
4235                 }
4236         }
4237
4238 }
4239
4240 static void
4241 modest_msg_edit_window_show_msg_settings_dialog (ModestMsgEditWindow *window)
4242 {
4243         GtkWidget *dialog;
4244         GtkWidget *align;
4245         GtkWidget *vbox;
4246         GtkWidget *priority_hbox;
4247         GtkWidget *high_toggle, *medium_toggle, *low_toggle;
4248         GtkWidget *captioned;
4249         GtkSizeGroup *title_sizegroup, *value_sizegroup;
4250         GtkWidget *format_hbox;
4251         GtkWidget *html_toggle, *text_toggle;
4252         ModestMsgEditWindowPrivate *priv;
4253         MessageSettingsHelper helper = {0,};
4254
4255         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
4256         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
4257         helper.window = window;
4258         helper.priority_group = NULL;
4259         helper.format_group = NULL;
4260
4261         title_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4262         value_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4263
4264         dialog = gtk_dialog_new_with_buttons (_("mcen_me_message_settings"), NULL,
4265                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4266                                               _HL("wdgt_bd_done"), GTK_RESPONSE_ACCEPT, NULL);
4267         vbox = gtk_vbox_new (FALSE, 0);
4268         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
4269         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DOUBLE, 0);
4270         gtk_container_add (GTK_CONTAINER (align), vbox);
4271         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), align);
4272         gtk_widget_show (align);
4273         gtk_widget_show (vbox);
4274
4275         /* Priority toggles */
4276         priority_hbox = gtk_hbox_new (TRUE, 0);
4277         high_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4278         gtk_button_set_label (GTK_BUTTON (high_toggle), _("mcen_me_editor_priority_high"));
4279         helper.priority_group = g_slist_prepend (helper.priority_group, high_toggle);
4280         g_object_set_data (G_OBJECT (high_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_HIGH_PRIORITY));
4281         medium_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4282         gtk_button_set_label (GTK_BUTTON (medium_toggle), _("mcen_me_editor_priority_normal"));
4283         helper.priority_group = g_slist_prepend (helper.priority_group, medium_toggle);
4284         g_object_set_data (G_OBJECT (medium_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_NORMAL_PRIORITY));
4285         low_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4286         gtk_button_set_label (GTK_BUTTON (low_toggle), _("mcen_me_editor_priority_low"));
4287         helper.priority_group = g_slist_prepend (helper.priority_group, low_toggle);
4288         g_object_set_data (G_OBJECT (low_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_LOW_PRIORITY));
4289         gtk_box_pack_start (GTK_BOX (priority_hbox), low_toggle, TRUE, TRUE, 0);
4290         gtk_box_pack_start (GTK_BOX (priority_hbox), medium_toggle, TRUE, TRUE, 0);
4291         gtk_box_pack_start (GTK_BOX (priority_hbox), high_toggle, TRUE, TRUE, 0);
4292         gtk_widget_show_all (priority_hbox);
4293         captioned = modest_maemo_utils_create_captioned (title_sizegroup, value_sizegroup,
4294                                                          _("mcen_me_editor_message_priority"), FALSE, priority_hbox);
4295         gtk_widget_show (captioned);
4296         gtk_box_pack_start (GTK_BOX (vbox), captioned, FALSE, FALSE, 0);
4297
4298         /* format toggles */
4299         format_hbox = gtk_hbox_new (TRUE, 0);
4300         html_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4301         gtk_button_set_label (GTK_BUTTON (html_toggle), _("mcen_me_editor_formatted_text"));
4302         helper.format_group = g_slist_prepend (helper.format_group, html_toggle);
4303         g_object_set_data (G_OBJECT (html_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_HTML));
4304         g_object_set_data (G_OBJECT (html_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_FORMATTED_TEXT));
4305         text_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4306         gtk_button_set_label (GTK_BUTTON (text_toggle), _("mcen_me_editor_plain_text"));
4307         helper.format_group = g_slist_prepend (helper.format_group, text_toggle);
4308         g_object_set_data (G_OBJECT (text_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_TEXT));
4309         g_object_set_data (G_OBJECT (text_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_PLAIN_TEXT));
4310         gtk_box_pack_start (GTK_BOX (format_hbox), html_toggle, TRUE, TRUE, 0);
4311         gtk_box_pack_start (GTK_BOX (format_hbox), text_toggle, TRUE, TRUE, 0);
4312         gtk_widget_show_all (format_hbox);
4313         gtk_widget_show (format_hbox);
4314         gtk_box_pack_start (GTK_BOX (vbox), format_hbox, FALSE, FALSE, 0);
4315
4316
4317         g_object_unref (title_sizegroup);
4318         g_object_unref (value_sizegroup);
4319
4320         /* Set current values */
4321         switch (priv->priority_flags) {
4322         case TNY_HEADER_FLAG_HIGH_PRIORITY:
4323                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (high_toggle), TRUE);
4324                 helper.current_priority = GTK_TOGGLE_BUTTON (high_toggle);
4325                 break;
4326         case TNY_HEADER_FLAG_LOW_PRIORITY:
4327                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (low_toggle), TRUE);
4328                 helper.current_priority = GTK_TOGGLE_BUTTON (low_toggle);
4329                 break;
4330         default:
4331                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (medium_toggle), TRUE);
4332                 helper.current_priority = GTK_TOGGLE_BUTTON (medium_toggle);
4333                 break;
4334         }
4335
4336         switch (modest_msg_edit_window_get_format (window)) {
4337         case MODEST_MSG_EDIT_FORMAT_TEXT:
4338                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_toggle), TRUE);
4339                 helper.current_format = GTK_TOGGLE_BUTTON (text_toggle);
4340                 break;
4341         case MODEST_MSG_EDIT_FORMAT_HTML:
4342         default:
4343                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (html_toggle), TRUE);
4344                 helper.current_format = GTK_TOGGLE_BUTTON (html_toggle);
4345                 break;
4346         }
4347
4348         /* Signal connects */
4349         g_signal_connect (G_OBJECT (high_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4350         g_signal_connect (G_OBJECT (medium_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4351         g_signal_connect (G_OBJECT (low_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4352         g_signal_connect (G_OBJECT (html_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4353         g_signal_connect (G_OBJECT (text_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4354
4355         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
4356                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
4357
4358         /* Save settings if the user clicked on done */
4359         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
4360                 TnyHeaderFlags flags;
4361                 ModestMsgEditFormat old_format, new_format;
4362
4363                 /* Set priority flags */
4364                 flags = (TnyHeaderFlags) g_object_get_data (G_OBJECT (helper.current_priority), "priority");
4365                 if (priv->priority_flags !=  flags)
4366                         modest_msg_edit_window_set_priority_flags (window, flags);
4367
4368                 /* Set edit format */
4369                 old_format = modest_msg_edit_window_get_format (window);
4370                 new_format = (ModestMsgEditFormat) g_object_get_data (G_OBJECT (helper.current_format), "format");
4371                 if (old_format != new_format) {
4372                         gint file_format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (helper.current_format), "file-format"));
4373                         modest_msg_edit_window_set_file_format (window, file_format);
4374                 }
4375         }
4376
4377         gtk_widget_destroy (dialog);
4378         g_slist_free (helper.priority_group);
4379 }
4380
4381 static void
4382 on_message_settings (GtkAction *action,
4383                      ModestMsgEditWindow *window)
4384 {
4385         modest_msg_edit_window_show_msg_settings_dialog (window);
4386 }
4387
4388 static void
4389 on_cc_button_toggled (GtkWidget *button,
4390                       ModestMsgEditWindow *window)
4391 {
4392         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4393
4394         modest_msg_edit_window_show_cc (MODEST_MSG_EDIT_WINDOW (window),
4395                                         modest_togglable_get_active (button));
4396 }
4397
4398 static void
4399 on_bcc_button_toggled (GtkWidget *button,
4400                       ModestMsgEditWindow *window)
4401 {
4402         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4403
4404         modest_msg_edit_window_show_bcc (MODEST_MSG_EDIT_WINDOW (window),
4405                                         modest_togglable_get_active (button));
4406 }
4407
4408 static void 
4409 setup_menu (ModestMsgEditWindow *self)
4410 {
4411         ModestMsgEditWindowPrivate *priv = NULL;
4412
4413         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4414
4415         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4416
4417         /* Settings menu buttons */
4418         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_checknames"), NULL,
4419                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_check_names),
4420                                    NULL);
4421         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
4422                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
4423                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
4424         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_undo"), "<Ctrl>z",
4425                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_undo),
4426                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_undo));
4427
4428         priv->cc_button = hildon_check_button_new (0);
4429         gtk_button_set_label (GTK_BUTTON (priv->cc_button), _("mcen_me_editor_showcc"));
4430         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->cc_button),
4431                                         FALSE);
4432         modest_window_add_item_to_menu (MODEST_WINDOW (self), priv->cc_button, NULL);
4433         g_signal_connect (G_OBJECT (priv->cc_button), "toggled",
4434                           G_CALLBACK (on_cc_button_toggled), (gpointer) self);
4435
4436         priv->bcc_button = modest_toolkit_factory_create_check_menu (modest_runtime_get_toolkit_factory (),
4437                                                                      _("mcen_me_editor_showbcc"));
4438         modest_togglable_set_active (priv->bcc_button,
4439                                      FALSE);
4440         modest_window_add_item_to_menu (MODEST_WINDOW (self), priv->bcc_button,
4441                                         NULL);
4442         g_signal_connect (G_OBJECT (priv->bcc_button), "toggled",
4443                           G_CALLBACK (on_bcc_button_toggled), (gpointer) self);
4444
4445         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_attach_inlineimage"), NULL,
4446                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_insert_image),
4447                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_set_style));
4448         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_editor_add_attachment"), NULL,
4449                                    MODEST_WINDOW_MENU_CALLBACK (modest_msg_edit_window_add_attachment_clicked),
4450                                    NULL);
4451         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
4452                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_remove_attachments),
4453                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_editor_remove_attachment));
4454         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_message_settings"), NULL,
4455                                    MODEST_WINDOW_MENU_CALLBACK (on_message_settings),
4456                                    NULL);
4457         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), "<Ctrl>f",
4458                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_toggle_find_in_page),
4459                                    NULL);
4460 }
4461
4462 static void
4463 emit_open_addressbook (GtkButton *button,
4464                        ModestRecptEditor *editor)
4465 {
4466         g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
4467 }
4468
4469 static GtkWidget *
4470 _create_addressbook_box (GtkSizeGroup *title_size_group, GtkSizeGroup *value_size_group,
4471                          const gchar *label, GtkWidget *control)
4472 {
4473         GtkWidget *abook_button;
4474         GtkWidget *align;
4475         GtkWidget *box;
4476         GtkWidget *label_widget;
4477
4478         box = gtk_hbox_new (FALSE, 0);
4479
4480         align = gtk_alignment_new (0.0, 0.0, 1.0, 0.0);
4481         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 0, MODEST_MARGIN_DEFAULT);
4482
4483         abook_button = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT);
4484         label_widget = gtk_label_new (label);
4485         gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
4486         gtk_container_add (GTK_CONTAINER (abook_button), label_widget);
4487
4488         gtk_container_add (GTK_CONTAINER (align), abook_button);
4489         gtk_widget_set_size_request (label_widget, 148 - MODEST_MARGIN_DOUBLE, -1);
4490         gtk_box_pack_start (GTK_BOX (box), align, FALSE, FALSE, 0);
4491         gtk_box_pack_start (GTK_BOX (box), control, TRUE, TRUE, 0);
4492         if (title_size_group)
4493                 gtk_size_group_add_widget (title_size_group, label_widget);
4494         if (value_size_group)
4495                 gtk_size_group_add_widget (value_size_group, control);
4496
4497         g_signal_connect (G_OBJECT (abook_button), "clicked",
4498                           G_CALLBACK (emit_open_addressbook), control);
4499   
4500         return box;  
4501 }
4502
4503 static void 
4504 max_chars_banner_unref (ModestMsgEditWindow *self, GObject *old_ref)
4505 {
4506         ModestMsgEditWindowPrivate *priv = NULL;
4507
4508         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4509
4510         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4511         priv->max_chars_banner = NULL;
4512 }
4513
4514 static gboolean
4515 has_pending_addresses (ModestRecptEditor *recpt_editor)
4516 {
4517         const gchar *recipients = NULL;
4518         GSList *start_indexes = NULL, *end_indexes = NULL;
4519         GSList *current_start, *current_end;
4520         GtkTextBuffer *buffer;
4521         gint offset_delta = 0;
4522         gint last_length;
4523         gboolean has_recipients_to_add = FALSE;
4524
4525         recipients = modest_recpt_editor_get_recipients (recpt_editor);
4526         last_length = g_utf8_strlen (recipients, -1);
4527         modest_text_utils_get_addresses_indexes (recipients, &start_indexes, &end_indexes);
4528
4529         if (!start_indexes)
4530                 return FALSE;
4531
4532         current_start = start_indexes;
4533         current_end = end_indexes;
4534         buffer = modest_recpt_editor_get_buffer (recpt_editor);
4535
4536         while (current_start && !has_recipients_to_add) {
4537                 gchar *address;
4538                 gchar *start_ptr, *end_ptr;
4539                 gint start_pos, end_pos;
4540
4541                 start_pos = (*((gint*) current_start->data)) + offset_delta;
4542                 end_pos = (*((gint*) current_end->data)) + offset_delta;
4543
4544                 start_ptr = g_utf8_offset_to_pointer (recipients, start_pos);
4545                 end_ptr = g_utf8_offset_to_pointer (recipients, end_pos);
4546
4547                 address = g_strstrip (g_strndup (start_ptr, end_ptr - start_ptr));
4548
4549                 if (modest_text_utils_validate_recipient (address, NULL)) {
4550                         if (!modest_address_book_has_address (address)) {
4551                                 has_recipients_to_add = TRUE;
4552                         }
4553                 }
4554                 current_start = g_slist_next (current_start);
4555                 current_end = g_slist_next (current_end);
4556         }
4557         return has_recipients_to_add;
4558 }
4559
4560 gboolean
4561 modest_msg_edit_window_has_pending_addresses (ModestMsgEditWindow *self)
4562 {
4563         ModestMsgEditWindowPrivate *priv = NULL;
4564
4565         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self), FALSE);
4566
4567         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4568
4569         if (!has_pending_addresses ((ModestRecptEditor *) priv->to_field) &&
4570             !has_pending_addresses ((ModestRecptEditor *) priv->cc_field) &&
4571             !has_pending_addresses ((ModestRecptEditor *) priv->bcc_field))
4572                 return FALSE;
4573         else
4574                 return TRUE;
4575 }