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