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