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