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