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