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