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