5314544b1da9a6f870219cf0d76c06fc86928165
[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 static void
1298 menu_tool_button_dont_expand (GtkMenuToolButton *item)
1299 {
1300         GtkWidget *box;
1301         GList *item_children, *node;
1302
1303         box = gtk_bin_get_child (GTK_BIN (item));
1304         gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
1305         item_children = gtk_container_get_children (GTK_CONTAINER (box));
1306         
1307         for (node = item_children; node != NULL; node = g_list_next (node)) {
1308                 gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START);
1309                 if (GTK_IS_TOGGLE_BUTTON (node->data))
1310                         gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5);
1311                 else if (GTK_IS_BUTTON (node->data))
1312                         gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5);
1313         }
1314         g_list_free (item_children);
1315 }
1316
1317
1318 static void
1319 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1320 {
1321         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1322         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1323         GtkWidget *placeholder;
1324         GtkWidget *tool_item;
1325         gint insert_index;
1326         gchar size_text[5];
1327         gint size_index;
1328         gint font_index;
1329         GtkWidget *sizes_menu;
1330         GtkWidget *fonts_menu;
1331         gchar *markup;
1332         gchar ldots[8];
1333         gint ldots_len;
1334
1335         ldots_len = g_unichar_to_utf8 (0x2026, ldots);
1336         ldots[ldots_len] = '\0';
1337
1338         /* Toolbar */
1339         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1340         gtk_toolbar_set_show_arrow (GTK_TOOLBAR (parent_priv->toolbar), FALSE);
1341         gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
1342         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1343
1344         /* Font color placeholder */
1345         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1346         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1347
1348         /* font color */
1349         priv->font_color_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1350         priv->font_color_button = hildon_color_button_new ();
1351         gtk_widget_set_size_request (priv->font_color_button, -1, 48);
1352         GTK_WIDGET_UNSET_FLAGS (priv->font_color_toolitem, GTK_CAN_FOCUS);
1353         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1354         gtk_container_add (GTK_CONTAINER (priv->font_color_toolitem), priv->font_color_button);
1355         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1356         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1357         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->font_color_toolitem), insert_index);
1358         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1359                                   "notify::color", 
1360                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1361                                   window);
1362
1363         /* Font size and face placeholder */
1364         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1365         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1366         /* font_size */
1367         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1368         priv->size_tool_button_label = gtk_label_new (NULL);
1369         hildon_helper_set_logical_color (GTK_WIDGET (priv->size_tool_button_label), GTK_RC_TEXT,
1370                                          GTK_STATE_INSENSITIVE, "SecondaryTextColor");
1371         hildon_helper_set_logical_color (GTK_WIDGET (priv->size_tool_button_label), GTK_RC_FG,
1372                                          GTK_STATE_INSENSITIVE, "SecondaryTextColor");
1373         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1374         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1375                               size_text, ldots, "</span>", NULL);
1376         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1377         g_free (markup);
1378         hildon_helper_set_logical_font (priv->size_tool_button_label, "LargeSystemFont");
1379         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label);
1380         sizes_menu = gtk_menu_new ();
1381         priv->sizes_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1382         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1383                 GtkTreeIter iter;
1384
1385                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1386                 gtk_list_store_append (GTK_LIST_STORE (priv->sizes_model), &iter);
1387
1388                 gtk_list_store_set (GTK_LIST_STORE (priv->sizes_model), &iter, 
1389                                     0, size_text,
1390                                     -1);
1391
1392                 if (wp_font_size[size_index] == 12)
1393                         priv->current_size_index = size_index;
1394                                         
1395         }
1396
1397         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_size_clicked), window);
1398         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1399         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), FALSE);
1400         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), FALSE);
1401         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1402         priv->font_size_toolitem = tool_item;
1403
1404         /* font face */
1405         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1406         priv->font_tool_button_label = gtk_label_new (NULL);
1407         hildon_helper_set_logical_color (GTK_WIDGET (priv->font_tool_button_label), GTK_RC_TEXT,
1408                                          GTK_STATE_INSENSITIVE, "SecondaryTextColor");
1409         hildon_helper_set_logical_color (GTK_WIDGET (priv->font_tool_button_label), GTK_RC_FG,
1410                                          GTK_STATE_INSENSITIVE, "SecondaryTextColor");
1411         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt",ldots,"</span>", NULL);
1412         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1413         g_free(markup);
1414         hildon_helper_set_logical_font (priv->font_tool_button_label, "LargeSystemFont");
1415         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label);
1416         fonts_menu = gtk_menu_new ();
1417         priv->faces_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1418         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1419                 GtkTreeIter iter;
1420
1421                 gtk_list_store_append (GTK_LIST_STORE (priv->faces_model), &iter);
1422
1423                 gtk_list_store_set (GTK_LIST_STORE (priv->faces_model), &iter, 
1424                                     0, wp_get_font_name (font_index),
1425                                     -1);
1426
1427                 if (font_index == DEFAULT_FONT)
1428                         priv->current_face_index = font_index;
1429         }
1430         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_face_clicked), window);
1431         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1432         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), FALSE);
1433         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), FALSE);
1434         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1435         priv->font_face_toolitem = tool_item;
1436
1437         /* Set expand and homogeneous for remaining items */
1438         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend");
1439         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1440         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1441         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1442         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1443         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1444         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1445         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1446         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1447
1448         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1449            will not show the tool items added to the placeholders) */
1450         gtk_widget_show_all (parent_priv->toolbar);
1451
1452         /* Set the no show all *after* showing all items. We do not
1453            want the toolbar to be shown with a show all because it
1454            could go agains the gconf setting regarding showing or not
1455            the toolbar of the editor window */
1456         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1457 }
1458
1459
1460
1461 ModestWindow*
1462 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich)
1463 {
1464         GObject *obj;
1465         ModestWindowPrivate *parent_priv;
1466         ModestMsgEditWindowPrivate *priv;
1467         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1468         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1469         ModestWindowMgr *mgr = NULL;
1470
1471         g_return_val_if_fail (msg, NULL);
1472         g_return_val_if_fail (account_name, NULL);
1473
1474         mgr = modest_runtime_get_window_mgr ();
1475         
1476         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1477
1478         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1479         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1480
1481         /* Menubar. Update the state of some toggles */
1482         priv->from_field_protos = get_transports ();
1483         modest_selector_picker_set_pair_list (MODEST_SELECTOR_PICKER (priv->from_field), priv->from_field_protos);
1484         modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field), (gpointer) account_name);
1485         priv->last_from_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1486         hildon_button_set_title (HILDON_BUTTON (priv->from_field),
1487                                  _("mail_va_from"));
1488         hildon_button_set_value (HILDON_BUTTON (priv->from_field), 
1489                                  hildon_touch_selector_get_current_text 
1490                                  (HILDON_TOUCH_SELECTOR (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (priv->from_field)))));
1491         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1492         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1493
1494         /* Init window */
1495         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1496
1497         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1498                 
1499         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1500
1501         priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL;
1502
1503         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1504         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1505         /* Add common dimming rules */
1506         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1507                                               modest_msg_edit_window_toolbar_dimming_entries,
1508                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1509                                               MODEST_WINDOW (obj));
1510         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_toolitem,
1511                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1512                                                     MODEST_WINDOW (obj));
1513         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1514                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1515                                                     MODEST_WINDOW (obj));
1516         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1517                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1518                                                     MODEST_WINDOW (obj));
1519         /* Insert dimming rules group for this window */
1520         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1521         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1522
1523         /* Setup app menu */
1524         setup_menu (MODEST_MSG_EDIT_WINDOW (obj));
1525
1526         /* Checks the dimming rules */
1527         g_object_unref (toolbar_rules_group);
1528         g_object_unref (clipboard_rules_group);
1529         gtk_widget_hide (priv->priority_icon);
1530         gtk_widget_queue_resize (priv->subject_box);
1531         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1532
1533         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1534
1535         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1536         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1537         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1538         priv->update_caption_visibility = TRUE;
1539
1540         modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE);
1541
1542         /* Track account-removed signal, this window should be closed
1543            in the case we're creating a mail associated to the account
1544            that is deleted */
1545         priv->account_removed_handler_id = 
1546                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1547                                   "account_removed",
1548                                   G_CALLBACK(on_account_removed),
1549                                   obj);
1550
1551         modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj));
1552
1553         return (ModestWindow*) obj;
1554 }
1555
1556 static gint
1557 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1558 {
1559         GString **string_buffer = (GString **) user_data;
1560
1561         *string_buffer = g_string_append (*string_buffer, buffer);
1562    
1563         return 0;
1564 }
1565
1566 /**
1567  * @result: A new string which should be freed with g_free().
1568  */
1569 static gchar *
1570 get_formatted_data (ModestMsgEditWindow *edit_window)
1571 {
1572         ModestMsgEditWindowPrivate *priv;
1573         GString *string_buffer = g_string_new ("");
1574         
1575         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1576
1577         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1578
1579         modest_text_utils_hyperlinkify (string_buffer);
1580
1581         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1582
1583         return g_string_free (string_buffer, FALSE);
1584                                                                         
1585 }
1586
1587 MsgData * 
1588 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1589 {
1590         MsgData *data;
1591         const gchar *account_name;
1592         ModestMsgEditWindowPrivate *priv;
1593         TnyIterator *att_iter;
1594         
1595         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1596
1597         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1598                                                                         
1599         account_name = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1600         g_return_val_if_fail (account_name, NULL);
1601         
1602         
1603         /* don't free these (except from) */
1604         data = g_slice_new0 (MsgData);
1605         data->from    =  modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(),
1606                                                              account_name);
1607         data->account_name = g_strdup (account_name);
1608         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1609         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1610         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1611         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1612         if (priv->draft_msg) {
1613                 data->draft_msg = g_object_ref (priv->draft_msg);
1614         } else if (priv->outbox_msg) {
1615                 data->draft_msg = g_object_ref (priv->outbox_msg);
1616         } else {
1617                 data->draft_msg = NULL;
1618         }
1619
1620         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1621         GtkTextIter b, e;
1622         gtk_text_buffer_get_bounds (buf, &b, &e);
1623         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1624
1625         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1626                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1627         else
1628                 data->html_body = NULL;
1629
1630         /* deep-copy the data */
1631         att_iter = tny_list_create_iterator (priv->attachments);
1632         data->attachments = NULL;
1633         while (!tny_iterator_is_done (att_iter)) {
1634                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1635                 if (!(TNY_IS_MIME_PART(part))) {
1636                         g_warning ("strange data in attachment list");
1637                         g_object_unref (part);
1638                         tny_iterator_next (att_iter);
1639                         continue;
1640                 }
1641                 data->attachments = g_list_append (data->attachments,
1642                                                    part);
1643                 tny_iterator_next (att_iter);
1644         }
1645         g_object_unref (att_iter);
1646
1647         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1648         att_iter = tny_list_create_iterator (priv->images);
1649         data->images = NULL;
1650         while (!tny_iterator_is_done (att_iter)) {
1651                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1652                 const gchar *cid;
1653                 if (!(TNY_IS_MIME_PART(part))) {
1654                         g_warning ("strange data in attachment list");
1655                         g_object_unref (part);
1656                         tny_iterator_next (att_iter);
1657                         continue;
1658                 }
1659                 cid = tny_mime_part_get_content_id (part);
1660                 if (cid) {                      
1661                         gchar *image_tag_id;
1662                         GtkTextTag *image_tag;
1663                         GtkTextIter iter;
1664                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1665                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1666                         g_free (image_tag_id);
1667                         
1668                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1669                         if (image_tag && 
1670                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1671                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1672                                 data->images = g_list_append (data->images,
1673                                                               g_object_ref (part));
1674                 }
1675                 g_object_unref (part);
1676                 tny_iterator_next (att_iter);
1677         }
1678         g_object_unref (att_iter);
1679         
1680         data->priority_flags = priv->priority_flags;
1681
1682         return data;
1683 }
1684
1685
1686 static void
1687 unref_gobject (GObject *obj, gpointer data)
1688 {
1689         if (!G_IS_OBJECT(obj))
1690                 return;
1691         g_object_unref (obj);
1692 }
1693
1694 void 
1695 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1696                                                       MsgData *data)
1697 {
1698         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1699
1700         if (!data)
1701                 return;
1702
1703         g_free (data->to);
1704         g_free (data->cc);
1705         g_free (data->bcc);
1706         g_free (data->from);
1707         g_free (data->subject);
1708         g_free (data->plain_body);
1709         g_free (data->html_body);
1710         g_free (data->account_name);
1711         
1712         if (data->draft_msg != NULL) {
1713                 g_object_unref (data->draft_msg);
1714                 data->draft_msg = NULL;
1715         }
1716         
1717         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1718         g_list_free (data->attachments);
1719         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1720         g_list_free (data->images);
1721         
1722         g_slice_free (MsgData, data);
1723 }
1724
1725 void                    
1726 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1727                                        gint *parts_count,
1728                                        guint64 *parts_size)
1729 {
1730         ModestMsgEditWindowPrivate *priv;
1731
1732         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1733
1734         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1735
1736         /* TODO: add images */
1737         *parts_size += priv->images_size;
1738         *parts_count += priv->images_count;
1739
1740 }
1741
1742 ModestMsgEditFormat
1743 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1744 {
1745         gboolean rich_text;
1746         ModestMsgEditWindowPrivate *priv = NULL;
1747         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1748
1749         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1750
1751         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1752         if (rich_text)
1753                 return MODEST_MSG_EDIT_FORMAT_HTML;
1754         else
1755                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1756 }
1757
1758 void
1759 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1760                                    ModestMsgEditFormat format)
1761 {
1762         ModestMsgEditWindowPrivate *priv;
1763
1764         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1765         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1766
1767         switch (format) {
1768         case MODEST_MSG_EDIT_FORMAT_HTML:
1769                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1770                 break;
1771         case MODEST_MSG_EDIT_FORMAT_TEXT:
1772                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1773                 break;
1774         default:
1775                 g_return_if_reached ();
1776         }
1777 }
1778
1779 ModestMsgEditFormatState *
1780 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1781 {
1782         ModestMsgEditFormatState *format_state = NULL;
1783         ModestMsgEditWindowPrivate *priv;
1784         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1785
1786         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
1787         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1788
1789         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
1790
1791         format_state = g_new0 (ModestMsgEditFormatState, 1);
1792         format_state->bold = buffer_format->bold&0x1;
1793         format_state->italics = buffer_format->italic&0x1;
1794         format_state->bullet = buffer_format->bullet&0x1;
1795         format_state->color = buffer_format->color;
1796         format_state->font_size = buffer_format->font_size;
1797         format_state->font_family = wp_get_font_name (buffer_format->font);
1798         format_state->justification = buffer_format->justification;
1799         g_free (buffer_format);
1800
1801         return format_state;
1802  
1803 }
1804
1805 void
1806 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
1807                                          const ModestMsgEditFormatState *format_state)
1808 {
1809         ModestMsgEditWindowPrivate *priv;
1810         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1811         WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1);
1812         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1813         g_return_if_fail (format_state != NULL);
1814
1815         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1816         gtk_widget_grab_focus (priv->msg_body);
1817         buffer_format->bold = (format_state->bold != FALSE);
1818         buffer_format->italic = (format_state->italics != FALSE);
1819         buffer_format->color = format_state->color;
1820         buffer_format->font_size = format_state->font_size;
1821         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
1822         buffer_format->justification = format_state->justification;
1823         buffer_format->bullet = format_state->bullet;
1824
1825         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
1826
1827         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
1828         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
1829         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
1830         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
1831         buffer_format->cs.font = (buffer_format->font != current_format->font);
1832         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
1833         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
1834
1835         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
1836         if (buffer_format->cs.bold) {
1837                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
1838                                               GINT_TO_POINTER (buffer_format->bold&0x1));
1839         }
1840         if (buffer_format->cs.italic) {
1841                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
1842                                               GINT_TO_POINTER (buffer_format->italic&0x1));
1843         }
1844         if (buffer_format->cs.color) {
1845                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1846                                               GINT_TO_POINTER (&(buffer_format->color)));
1847         }
1848         if (buffer_format->cs.font_size) {
1849                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
1850                                               GINT_TO_POINTER (buffer_format->font_size));
1851         }
1852         if (buffer_format->cs.justification) {
1853                 switch (buffer_format->justification) {
1854                 case GTK_JUSTIFY_LEFT:
1855                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
1856                                                       GINT_TO_POINTER(TRUE));
1857                         break;
1858                 case GTK_JUSTIFY_CENTER:
1859                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
1860                                                       GINT_TO_POINTER(TRUE));
1861                         break;
1862                 case GTK_JUSTIFY_RIGHT:
1863                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
1864                                                       GINT_TO_POINTER(TRUE));
1865                         break;
1866                 default:
1867                         break;
1868                 }
1869                         
1870         }
1871         if (buffer_format->cs.font) {
1872                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
1873                                               GINT_TO_POINTER (buffer_format->font));
1874         }
1875         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
1876         if (buffer_format->cs.bullet) {
1877                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
1878                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
1879         }
1880 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
1881         
1882         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
1883         
1884         g_free (buffer_format);
1885         g_free (current_format);
1886
1887         /* Check dimming rules */
1888         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1889         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1890 }
1891
1892 static void
1893 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
1894 {
1895         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1896         GtkAction *action;
1897         ModestWindowPrivate *parent_priv;
1898         ModestMsgEditWindowPrivate *priv;
1899         gchar ldots[8];
1900         gint ldots_len;
1901         
1902         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1903         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1904
1905         ldots_len = g_unichar_to_utf8 (0x2026, ldots);
1906         ldots[ldots_len] = '\0';
1907
1908         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
1909                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu");
1910                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1911                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1912         } else {
1913                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatPlainTextMenu");
1914                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1915                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1916         }
1917
1918         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1919
1920         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1921         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
1922
1923         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1924         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
1925
1926 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
1927 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
1928
1929         action = NULL;
1930         switch (buffer_format->justification)
1931         {
1932         case GTK_JUSTIFY_LEFT:
1933                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu");
1934                 break;
1935         case GTK_JUSTIFY_CENTER:
1936                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu");
1937                 break;
1938         case GTK_JUSTIFY_RIGHT:
1939                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu");
1940                 break;
1941         default:
1942                 break;
1943         }
1944         
1945         if (action != NULL)
1946                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1947         
1948         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
1949                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
1950                                          window);
1951         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
1952         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
1953                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
1954                                            window);
1955
1956         if (priv->current_size_index != buffer_format->font_size) {
1957                 GtkTreeIter iter;
1958                 GtkTreePath *path;
1959
1960                 path = gtk_tree_path_new_from_indices (buffer_format->font_size, -1);
1961                 if (gtk_tree_model_get_iter (priv->sizes_model, &iter, path)) {
1962                         gchar *size_text;
1963                         gchar *markup;
1964
1965                         priv->current_size_index = buffer_format->font_size;
1966
1967                         gtk_tree_model_get (priv->sizes_model, &iter, 0, &size_text, -1);
1968                         markup = g_strconcat ("<span font_family='Sans'>", 
1969                                               size_text, ldots, "</span>", NULL);
1970                         
1971                         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1972                         g_free (markup);
1973                         g_free (size_text);
1974                 }
1975                 gtk_tree_path_free (path);              
1976         }
1977
1978         if (priv->current_face_index != buffer_format->font) {
1979                 GtkTreeIter iter;
1980                 GtkTreePath *path;
1981
1982                 path = gtk_tree_path_new_from_indices (buffer_format->font, -1);
1983                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
1984                         gchar *face_name;
1985                         gchar *markup;
1986
1987                         priv->current_face_index = buffer_format->font;
1988                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
1989                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt", ldots, "</span>", NULL);
1990                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1991                         g_free (face_name);
1992                         g_free (markup);
1993                 }
1994
1995         }
1996
1997         g_free (buffer_format);
1998
1999 }
2000
2001 #ifdef MODEST_HILDON_VERSION_0
2002 void
2003 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2004 {
2005         
2006         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2007         ModestMsgEditWindowPrivate *priv;
2008         GtkWidget *dialog = NULL;
2009         gint response;
2010         GdkColor *new_color = NULL;
2011
2012         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2013         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2014         
2015         dialog = hildon_color_selector_new (GTK_WINDOW (window));
2016         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color));
2017         g_free (buffer_format);
2018
2019         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
2020                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
2021                 if (new_color != NULL) {
2022                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2023                                                       (gpointer) new_color);
2024                 }
2025         }
2026         gtk_widget_destroy (dialog);
2027 }
2028
2029
2030 void
2031 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2032 {
2033         
2034         ModestMsgEditWindowPrivate *priv;
2035         GtkWidget *dialog = NULL;
2036         gint response;
2037         GdkColor *old_color = NULL;
2038         const GdkColor *new_color = NULL;
2039         
2040         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2041         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2042         
2043         dialog = hildon_color_selector_new (GTK_WINDOW (window));
2044         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color);
2045
2046         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
2047                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
2048                 if (new_color != NULL)
2049                         wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color);
2050         }
2051         gtk_widget_destroy (dialog);
2052
2053 }
2054
2055 #else 
2056 void
2057 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2058 {
2059         
2060         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2061         ModestMsgEditWindowPrivate *priv;
2062         GtkWidget *dialog = NULL;
2063
2064         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2065         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2066                 
2067         dialog = hildon_color_chooser_new ();
2068         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2069         g_free (buffer_format);
2070
2071         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2072                 GdkColor col;
2073                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2074                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2075                                               (gpointer) &col);
2076         }
2077         gtk_widget_destroy (dialog);
2078 }
2079
2080
2081 void
2082 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2083 {
2084         
2085         ModestMsgEditWindowPrivate *priv;
2086         GtkWidget *dialog = NULL;
2087         GdkColor *old_color = NULL;
2088         
2089         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2090         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2091         
2092         dialog = hildon_color_chooser_new ();
2093         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2094
2095         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2096                 GdkColor col;
2097                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2098                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2099         }
2100         gtk_widget_destroy (dialog);
2101 }
2102
2103 #endif /*!MODEST_HILDON_VERSION_0*/
2104
2105
2106
2107 static TnyStream*
2108 create_stream_for_uri (const gchar* uri)
2109 {
2110         if (!uri)
2111                 return NULL;
2112                 
2113         TnyStream *result = NULL;
2114
2115         GnomeVFSHandle *handle = NULL;
2116         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2117         if (test == GNOME_VFS_OK) {
2118                 TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle));
2119                 /* Streams over OBEX (Bluetooth) are not seekable but
2120                  * we expect them to be (we might need to read them
2121                  * several times). So if this is a Bluetooth URI just
2122                  * read the whole file into memory (this is not a fast
2123                  * protocol so we can assume that these files are not
2124                  * going to be very big) */
2125                 if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)||
2126                     (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) {
2127                         TnyStream *memstream = tny_camel_mem_stream_new ();
2128                         tny_stream_write_to_stream (vfssstream, memstream);
2129                         g_object_unref (vfssstream);
2130                         result = memstream;
2131                 } else {
2132                         result = vfssstream;
2133                 }
2134         }
2135         
2136         return result;
2137 }
2138
2139 void
2140 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2141 {
2142         
2143         ModestMsgEditWindowPrivate *priv;
2144         GtkWidget *dialog = NULL;
2145         gint response = 0;
2146         GSList *uris = NULL;
2147         GSList *uri_node = NULL;
2148         
2149         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2150         
2151         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2152         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
2153         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2154
2155         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2156
2157         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2158                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2159
2160         response = gtk_dialog_run (GTK_DIALOG (dialog));
2161         switch (response) {
2162         case GTK_RESPONSE_OK:
2163                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2164                 break;
2165         default:
2166                 break;
2167         }
2168         gtk_widget_destroy (dialog);
2169
2170         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2171                 const gchar *uri;
2172                 GnomeVFSHandle *handle = NULL;
2173                 GnomeVFSResult result;
2174                 GtkTextIter position;
2175                 GtkTextMark *insert_mark;
2176
2177                 uri = (const gchar *) uri_node->data;
2178                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2179                 if (result == GNOME_VFS_OK) {
2180                         GdkPixbuf *pixbuf;
2181                         GnomeVFSFileInfo *info;
2182                         gchar *filename, *basename, *escaped_filename;
2183                         TnyMimePart *mime_part;
2184                         gchar *content_id;
2185                         const gchar *mime_type = NULL;
2186                         GnomeVFSURI *vfs_uri;
2187                         guint64 stream_size;
2188
2189                         gnome_vfs_close (handle);
2190                         vfs_uri = gnome_vfs_uri_new (uri);
2191
2192                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2193                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2194                         g_free (escaped_filename);
2195                         gnome_vfs_uri_unref (vfs_uri);
2196                         info = gnome_vfs_file_info_new ();
2197
2198                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2199                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2200                             == GNOME_VFS_OK)
2201                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2202
2203                         mime_part = tny_platform_factory_new_mime_part
2204                                 (modest_runtime_get_platform_factory ());
2205
2206                         TnyStream *stream = create_stream_for_uri (uri);
2207
2208                         if (stream == NULL) {
2209
2210                                 modest_platform_information_banner (NULL, NULL, 
2211                                                                     _FM("sfil_ib_opening_not_allowed"));
2212
2213                                 g_object_unref (mime_part);
2214                                 gnome_vfs_file_info_unref (info);
2215                                 continue;
2216                         }
2217
2218                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2219
2220                         content_id = g_strdup_printf ("%d", priv->next_cid);
2221                         tny_mime_part_set_content_id (mime_part, content_id);
2222                         g_free (content_id);
2223                         priv->next_cid++;
2224
2225                         basename = g_path_get_basename (filename);
2226                         tny_mime_part_set_filename (mime_part, basename);
2227                         g_free (basename);
2228
2229                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
2230
2231                         if (pixbuf != NULL) {
2232                                 priv->images_size += stream_size;
2233                                 priv->images_count ++;
2234                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2235                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2236                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
2237                                 g_object_unref (pixbuf);
2238                         } 
2239
2240                         tny_list_prepend (priv->images, (GObject *) mime_part);
2241                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2242                         g_free (filename);
2243                         g_object_unref (mime_part);
2244                         gnome_vfs_file_info_unref (info);
2245
2246                 }
2247         }
2248
2249
2250 }
2251
2252 static void
2253 on_attach_file_response (GtkDialog *dialog,
2254                          gint       arg1,
2255                          gpointer   user_data)
2256 {
2257         GSList *uris = NULL;
2258         GSList *uri_node;
2259         GnomeVFSFileSize total_size, allowed_size;
2260         ModestMsgEditWindow *window;
2261         ModestMsgEditWindowPrivate *priv;
2262         gint att_num;
2263         guint64 att_size;
2264
2265         switch (arg1) {
2266         case GTK_RESPONSE_OK:
2267                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2268                 break;
2269         default:
2270                 break;
2271         }
2272
2273         window = MODEST_MSG_EDIT_WINDOW (user_data);
2274         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2275
2276         /* allowed size is the maximum size - what's already there */
2277         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2278                                            &att_num, &att_size);
2279         allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size;
2280
2281         total_size = 0;
2282         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2283
2284                 const gchar *uri = (const gchar *) uri_node->data;
2285
2286                 total_size += 
2287                         modest_msg_edit_window_attach_file_one (window, uri, allowed_size);
2288
2289                 if (total_size > allowed_size) {
2290                         g_warning ("%s: total size: %u", 
2291                                    __FUNCTION__, (unsigned int)total_size);
2292                         break;
2293                 }
2294                 allowed_size -= total_size;
2295         }
2296         g_slist_foreach (uris, (GFunc) g_free, NULL);
2297         g_slist_free (uris);
2298
2299         gtk_widget_destroy (GTK_WIDGET (dialog));
2300 }
2301
2302 void
2303 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2304 {
2305         GtkWidget *dialog = NULL;
2306         ModestMsgEditWindowPrivate *priv;
2307
2308         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window));
2309
2310         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2311
2312         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2313                 return;
2314
2315         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2316                                                  GTK_FILE_CHOOSER_ACTION_OPEN);
2317         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2318         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2319         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2320                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2321
2322         /* Connect to response & show */
2323         g_signal_connect (dialog, "response", 
2324                           G_CALLBACK (on_attach_file_response), window);
2325         gtk_widget_show (dialog);
2326 }
2327
2328
2329 GnomeVFSFileSize
2330 modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window,
2331                                         const gchar *uri, 
2332                                         GnomeVFSFileSize allowed_size)
2333
2334 {
2335         GnomeVFSHandle *handle = NULL;
2336         ModestMsgEditWindowPrivate *priv;
2337         GnomeVFSResult result;
2338         GnomeVFSFileSize size = 0;
2339         g_return_val_if_fail (window, 0);
2340         g_return_val_if_fail (uri, 0);
2341
2342         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2343
2344         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2345         if (result == GNOME_VFS_OK) {
2346                 TnyMimePart *mime_part;
2347                 TnyStream *stream;
2348                 const gchar *mime_type = NULL;
2349                 gchar *basename;
2350                 gchar *escaped_filename;
2351                 gchar *filename;
2352                 gchar *content_id;
2353                 GnomeVFSFileInfo *info;
2354                 GnomeVFSURI *vfs_uri;
2355
2356                 gnome_vfs_close (handle);
2357                 vfs_uri = gnome_vfs_uri_new (uri);
2358                 
2359
2360                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2361                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2362                 g_free (escaped_filename);
2363                 gnome_vfs_uri_unref (vfs_uri);
2364
2365                 info = gnome_vfs_file_info_new ();
2366                 
2367                 if (gnome_vfs_get_file_info (uri, 
2368                                              info, 
2369                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2370                     == GNOME_VFS_OK)
2371                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2372                 mime_part = tny_platform_factory_new_mime_part
2373                         (modest_runtime_get_platform_factory ());
2374                 
2375                 /* try to get the attachment's size; this may fail for weird
2376                  * file systems, like obex, upnp... */
2377                 if (allowed_size != 0 &&
2378                     info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
2379                         size = info->size;
2380                         if (size > allowed_size) {
2381                                 modest_platform_information_banner (NULL, NULL, 
2382                                                                     _FM("sfil_ib_opening_not_allowed"));
2383                                 return 0;
2384                         }
2385                 } else
2386                         g_warning ("%s: could not get attachment size", __FUNCTION__);
2387                 
2388                 stream = create_stream_for_uri (uri);
2389                 
2390                 if (stream == NULL) {
2391
2392                         modest_platform_information_banner (NULL, NULL, _FM("sfil_ib_opening_not_allowed"));
2393
2394                         g_object_unref (mime_part);
2395                         gnome_vfs_file_info_unref (info);
2396                         return 0;
2397                 }
2398
2399                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2400                 g_object_unref (stream);
2401                 
2402                 content_id = g_strdup_printf ("%d", priv->next_cid);
2403                 tny_mime_part_set_content_id (mime_part, content_id);
2404                 g_free (content_id);
2405                 priv->next_cid++;
2406                 
2407                 basename = g_path_get_basename (filename);
2408                 tny_mime_part_set_filename (mime_part, basename);
2409                 g_free (basename);
2410                 
2411                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2412                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2413                                                         mime_part,
2414                                                         info->size == 0, info->size);
2415                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2416                 gtk_widget_show_all (priv->attachments_caption);
2417                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2418                 g_free (filename);
2419                 g_object_unref (mime_part);
2420                 gnome_vfs_file_info_unref (info);
2421         }
2422
2423         return size;
2424 }
2425
2426 void
2427 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2428                                            TnyList *att_list)
2429 {
2430         ModestMsgEditWindowPrivate *priv;
2431         TnyIterator *iter;
2432
2433         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2434         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2435
2436         if (att_list == NULL) {
2437                 att_list = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2438                 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), att_list, TRUE)) {
2439                         g_object_unref (att_list);
2440                         return;
2441                 }
2442                 
2443         } else {
2444                 g_object_ref (att_list);
2445         }
2446
2447         if (tny_list_get_length (att_list) == 0) {
2448                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2449         } else {
2450                 gboolean dialog_response;
2451                 gchar *message = NULL;
2452                 gchar *filename = NULL;
2453
2454                 if (tny_list_get_length (att_list) == 1) {
2455                         TnyMimePart *part;
2456                         iter = tny_list_create_iterator (att_list);
2457                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2458                         g_object_unref (iter);
2459                         if (TNY_IS_MSG (part)) {
2460                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2461                                 if (header) {
2462                                         filename = tny_header_dup_subject (header);
2463                                         g_object_unref (header);
2464                                 }
2465                                 if (filename == NULL) {
2466                                         filename = g_strdup (_("mail_va_no_subject"));
2467                                 }
2468                         } else {
2469                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2470                         }
2471                         g_object_unref (part);
2472                 } else {
2473                         filename = g_strdup ("");
2474                 }
2475                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", 
2476                                                     "emev_nc_delete_attachments",
2477                                                     tny_list_get_length (att_list)), filename);
2478                 g_free (filename);
2479
2480                 dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), 
2481                                                                            message);
2482                 g_free (message);
2483
2484                 if (dialog_response != GTK_RESPONSE_OK) {
2485                         g_object_unref (att_list);
2486                         return;
2487                 }
2488
2489                 for (iter = tny_list_create_iterator (att_list);
2490                      !tny_iterator_is_done (iter);
2491                      tny_iterator_next (iter)) {
2492                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2493                         const gchar *att_id;
2494                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2495
2496                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2497                                                                    mime_part);
2498                         if (tny_list_get_length (priv->attachments) == 0)
2499                                 gtk_widget_hide (priv->attachments_caption);
2500                         att_id = tny_mime_part_get_content_id (mime_part);
2501                         if (att_id != NULL)
2502                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2503                                                                  att_id);
2504                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2505                         g_object_unref (mime_part);
2506                 }
2507                 g_object_unref (iter);
2508         }
2509
2510         g_object_unref (att_list);
2511
2512         /* if the last attachment has been removed, focus the Subject: field */
2513         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) 
2514                 gtk_widget_grab_focus (priv->subject_field);
2515 }
2516
2517 static void
2518 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2519                                             gpointer userdata)
2520 {
2521         ModestMsgEditWindowPrivate *priv;
2522         GdkColor *new_color;
2523         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2524         
2525 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2526         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2527 #else 
2528         GdkColor col;
2529         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2530         new_color = &col;
2531 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2532
2533         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2534         
2535         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2536
2537 }
2538
2539 static void
2540 font_size_clicked (GtkToolButton *button,
2541                    ModestMsgEditWindow *window)
2542 {
2543         ModestMsgEditWindowPrivate *priv;
2544         GtkWidget *selector, *dialog;
2545         
2546         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2547
2548         selector = hildon_touch_selector_new ();
2549         hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector), priv->sizes_model, TRUE);
2550         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_size_index);
2551
2552         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2553         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2554
2555         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2556                 gint new_index;
2557                 gchar *size_text;
2558                 gchar *markup;
2559                 WPTextBufferFormat format;
2560                 gchar ldots[8];
2561                 gint ldots_len;
2562
2563                 new_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2564
2565                 memset (&format, 0, sizeof (format));
2566                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2567
2568                 format.cs.font_size = TRUE;
2569                 format.cs.text_position = TRUE;
2570                 format.cs.font = TRUE;
2571                 format.font_size = new_index;
2572 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2573
2574                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2575                                                    GINT_TO_POINTER (new_index)))
2576                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2577                 
2578                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2579                 size_text = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
2580                 ldots_len = g_unichar_to_utf8 (0x2026, ldots);
2581                 ldots[ldots_len] = '\0';
2582                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", 
2583                                       size_text, ldots, "</span>", NULL);
2584                 g_free (size_text);
2585                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2586                 g_free (markup);
2587
2588         }
2589         gtk_widget_destroy (dialog);
2590
2591         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2592
2593 }
2594
2595 static void
2596 font_face_clicked (GtkToolButton *button,
2597                    ModestMsgEditWindow *window)
2598 {
2599         ModestMsgEditWindowPrivate *priv;
2600         GtkWidget *selector, *dialog;
2601         GtkCellRenderer *renderer;
2602         
2603         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2604
2605         selector = hildon_touch_selector_new ();
2606         renderer = gtk_cell_renderer_text_new ();
2607         hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector), priv->faces_model, 
2608                                              renderer, "family", 0, "text", 0, NULL);
2609         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_face_index);
2610
2611         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2612         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2613
2614         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2615                 gint new_font_index;
2616                 GtkTreePath *path;
2617                 GtkTreeIter iter;
2618
2619                 new_font_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2620                 path = gtk_tree_path_new_from_indices (new_font_index, -1);
2621                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2622                         gchar *face_name;
2623                         gchar *markup;
2624                         gchar ldots[8];
2625                         gint ldots_len;
2626
2627                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2628
2629                         if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2630                                                            GINT_TO_POINTER(new_font_index)))
2631                                 wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2632
2633                         ldots_len = g_unichar_to_utf8 (0x2026, ldots);
2634                         ldots[ldots_len] = '\0';
2635                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt", ldots, "</span>", NULL);
2636                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2637
2638                         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2639                         g_free (face_name);
2640                         g_free (markup);
2641                 }
2642                 gtk_tree_path_free (path);
2643
2644         }
2645         gtk_widget_destroy (dialog);
2646
2647         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2648
2649 }
2650
2651 void
2652 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2653                                 gboolean show)
2654 {
2655         ModestMsgEditWindowPrivate *priv = NULL;
2656         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2657
2658         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2659         if (!priv->update_caption_visibility)
2660                 return;
2661
2662         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2663         if (show)
2664                 gtk_widget_show (priv->cc_caption);
2665         else
2666                 gtk_widget_hide (priv->cc_caption);
2667
2668         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2669 }
2670
2671 void
2672 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2673                                  gboolean show)
2674 {
2675         ModestMsgEditWindowPrivate *priv = NULL;
2676         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2677
2678         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2679         if (!priv->update_caption_visibility)
2680                 return;
2681
2682         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2683         if (show)
2684                 gtk_widget_show (priv->bcc_caption);
2685         else
2686                 gtk_widget_hide (priv->bcc_caption);
2687
2688         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2689 }
2690
2691 static void
2692 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2693                                          ModestRecptEditor *editor)
2694 {
2695         ModestMsgEditWindowPrivate *priv;
2696
2697         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2698         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2699         
2700         /* we check for low-mem; in that case, show a warning, and don't allow
2701          * for the addressbook
2702          */
2703         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2704                 return;
2705
2706         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2707
2708         if (editor == NULL) {
2709                 GtkWidget *view_focus;
2710                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2711
2712                 /* This code should be kept in sync with ModestRecptEditor. The
2713                    textview inside the recpt editor is the one that really gets the
2714                    focus. As it's inside a scrolled window, and this one inside the
2715                    hbox recpt editor inherits from, we'll need to go up in the 
2716                    hierarchy to know if the text view is part of the recpt editor
2717                    or if it's a different text entry */
2718
2719                 if (gtk_widget_get_parent (view_focus)) {
2720                         GtkWidget *first_parent;
2721
2722                         first_parent = gtk_widget_get_parent (view_focus);
2723                         if (gtk_widget_get_parent (first_parent) && 
2724                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2725                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2726                         }
2727                 }
2728
2729                 if (editor == NULL)
2730                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2731
2732         }
2733
2734         modest_address_book_select_addresses (editor);
2735
2736 }
2737
2738 void
2739 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2740 {
2741         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2742
2743         modest_msg_edit_window_open_addressbook (window, NULL);
2744 }
2745
2746 static void
2747 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2748                                      gboolean show_toolbar)
2749 {
2750         ModestWindowPrivate *parent_priv;
2751
2752         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2753         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2754
2755         /* We can not just use the code of
2756            modest_msg_edit_window_setup_toolbar because it has a
2757            mixture of both initialization and creation code. */
2758         if (show_toolbar)
2759                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2760         else
2761                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2762 }
2763
2764 void
2765 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2766                                            TnyHeaderFlags priority_flags)
2767 {
2768         ModestMsgEditWindowPrivate *priv;
2769         ModestWindowPrivate *parent_priv;
2770
2771         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2772
2773         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2774         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2775
2776         if (priv->priority_flags != priority_flags) {
2777                 GtkAction *priority_action = NULL;
2778
2779                 priv->priority_flags = priority_flags;
2780
2781                 switch (priority_flags) {
2782                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2783                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2784                                                       MODEST_HEADER_ICON_HIGH, 
2785                                                       HILDON_ICON_SIZE_SMALL);
2786                         gtk_widget_show (priv->priority_icon);
2787                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2788                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2789                         break;
2790                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2791                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2792                                                       MODEST_HEADER_ICON_LOW,
2793                                                       HILDON_ICON_SIZE_SMALL);
2794                         gtk_widget_show (priv->priority_icon);
2795                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2796                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2797                         break;
2798                 default:
2799                         gtk_widget_hide (priv->priority_icon);
2800                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2801                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2802                         break;
2803                 }
2804                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2805                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2806         }
2807         gtk_widget_queue_resize (priv->subject_box);
2808 }
2809
2810 void
2811 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2812                                         gint file_format)
2813 {
2814         ModestMsgEditWindowPrivate *priv;
2815         ModestWindowPrivate *parent_priv;
2816         gint current_format;
2817
2818         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2819
2820         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2821         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2822
2823         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2824                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2825
2826         if (current_format != file_format) {
2827                 switch (file_format) {
2828                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2829                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2830                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2831                         break;
2832                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2833                 {
2834                         GtkWidget *dialog;
2835                         gint response;
2836                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2837                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2838                         gtk_widget_destroy (dialog);
2839                         if (response == GTK_RESPONSE_OK) {
2840                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2841                         } else {
2842                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu"));
2843                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
2844                         }
2845                 }
2846                         break;
2847                 }
2848                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2849                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2850                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2851         }
2852 }
2853
2854 void
2855 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2856 {
2857         GtkWidget *dialog;
2858         ModestMsgEditWindowPrivate *priv;
2859         WPTextBufferFormat oldfmt, fmt;
2860         gint old_position = 0;
2861         gint response = 0;
2862         gint position = 0;
2863         gint font_size;
2864         GdkColor *color = NULL;
2865         gboolean bold, bold_set, italic, italic_set;
2866         gboolean underline, underline_set;
2867         gboolean strikethrough, strikethrough_set;
2868         gboolean position_set;
2869         gboolean font_size_set, font_set, color_set;
2870         gchar *font_name;
2871
2872         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2873         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2874         
2875         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2876         modest_window_mgr_set_modal (modest_runtime_get_window_mgr(),
2877                                      GTK_WINDOW(dialog), GTK_WINDOW (window));
2878
2879         /* First we get the currently selected font information */
2880         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2881
2882         switch (oldfmt.text_position) {
2883         case TEXT_POSITION_NORMAL:
2884                 old_position = 0;
2885                 break;
2886         case TEXT_POSITION_SUPERSCRIPT:
2887                 old_position = 1;
2888                 break;
2889         default:
2890                 old_position = -1;
2891                 break;
2892         }
2893
2894         g_object_set (G_OBJECT (dialog),
2895                       "bold", oldfmt.bold != FALSE,
2896                       "bold-set", !oldfmt.cs.bold,
2897                       "underline", oldfmt.underline != FALSE,
2898                       "underline-set", !oldfmt.cs.underline,
2899                       "italic", oldfmt.italic != FALSE,
2900                       "italic-set", !oldfmt.cs.italic,
2901                       "strikethrough", oldfmt.strikethrough != FALSE,
2902                       "strikethrough-set", !oldfmt.cs.strikethrough,
2903                       "color", &oldfmt.color,
2904                       "color-set", !oldfmt.cs.color,
2905                       "size", wp_font_size[oldfmt.font_size],
2906                       "size-set", !oldfmt.cs.font_size,
2907                       "position", old_position,
2908                       "position-set", !oldfmt.cs.text_position,
2909                       "family", wp_get_font_name (oldfmt.font),
2910                       "family-set", !oldfmt.cs.font,
2911                       NULL);
2912
2913         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2914                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2915         gtk_widget_show_all (dialog);
2916         priv->font_dialog = dialog;
2917         response = gtk_dialog_run (GTK_DIALOG (dialog));
2918         priv->font_dialog = NULL;
2919         if (response == GTK_RESPONSE_OK) {
2920
2921                 g_object_get( dialog,
2922                               "bold", &bold,
2923                               "bold-set", &bold_set,
2924                               "underline", &underline,
2925                               "underline-set", &underline_set,
2926                               "italic", &italic,
2927                               "italic-set", &italic_set,
2928                               "strikethrough", &strikethrough,
2929                               "strikethrough-set", &strikethrough_set,
2930                               "color", &color,
2931                               "color-set", &color_set,
2932                               "size", &font_size,
2933                               "size-set", &font_size_set,
2934                               "family", &font_name,
2935                               "family-set", &font_set,
2936                               "position", &position,
2937                               "position-set", &position_set,
2938                               NULL );
2939                 
2940         }       
2941
2942         if (response == GTK_RESPONSE_OK) {
2943                 memset(&fmt, 0, sizeof(fmt));
2944                 if (bold_set) {
2945                         fmt.bold = bold;
2946                         fmt.cs.bold = TRUE;
2947                 }
2948                 if (italic_set) {
2949                         fmt.italic = italic;
2950                         fmt.cs.italic = TRUE;
2951                 }
2952                 if (underline_set) {
2953                         fmt.underline = underline;
2954                         fmt.cs.underline = TRUE;
2955                 }
2956                 if (strikethrough_set) {
2957                         fmt.strikethrough = strikethrough;
2958                         fmt.cs.strikethrough = TRUE;
2959                 }
2960                 if (position_set) {
2961                         fmt.text_position =
2962                                 ( position == 0 )
2963                                 ? TEXT_POSITION_NORMAL
2964                                 : ( ( position == 1 )
2965                                     ? TEXT_POSITION_SUPERSCRIPT
2966                                     : TEXT_POSITION_SUBSCRIPT );
2967                         fmt.cs.text_position = TRUE;
2968                         fmt.font_size = oldfmt.font_size;
2969                 }
2970                 if (color_set) {
2971                         fmt.color = *color;
2972                         fmt.cs.color = TRUE;
2973                 }
2974                 if (font_set) {
2975                         fmt.font = wp_get_font_index(font_name,
2976                                                      DEFAULT_FONT);
2977                         fmt.cs.font = TRUE;
2978                 }
2979                 g_free(font_name);
2980                 if (font_size_set) {
2981                         fmt.cs.font_size = TRUE;
2982                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
2983                 }
2984                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2985                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2986         }
2987         gtk_widget_destroy (dialog);
2988         
2989         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2990 }
2991
2992 void
2993 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2994 {
2995         ModestMsgEditWindowPrivate *priv;
2996
2997         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2998         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2999         
3000         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
3001
3002         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3003         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3004 }
3005
3006 void
3007 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
3008 {
3009         ModestMsgEditWindowPrivate *priv;
3010
3011         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3012         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3013         
3014         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
3015
3016         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3017         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3018
3019 }
3020
3021 static void  
3022 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
3023 {
3024         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3025
3026         priv->can_undo = can_undo;
3027 }
3028
3029 static void  
3030 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
3031 {
3032         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3033
3034         priv->can_redo = can_redo;
3035 }
3036
3037 gboolean            
3038 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
3039 {
3040         ModestMsgEditWindowPrivate *priv;
3041         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3042         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3043
3044         return priv->can_undo;
3045 }
3046
3047 gboolean            
3048 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
3049 {
3050         ModestMsgEditWindowPrivate *priv;
3051         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3052         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3053
3054         return priv->can_redo;
3055 }
3056
3057
3058 static void
3059 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
3060 {
3061         GtkTextIter iter;
3062         GtkTextIter match_start, match_end;
3063
3064         if (image_id == NULL)
3065                 return;
3066
3067         gtk_text_buffer_get_start_iter (buffer, &iter);
3068
3069         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
3070                 GSList *tags = gtk_text_iter_get_tags (&match_start);
3071                 GSList *node;
3072                 for (node = tags; node != NULL; node = g_slist_next (node)) {
3073                         GtkTextTag *tag = (GtkTextTag *) node->data;
3074                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
3075                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
3076                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
3077                                         gint offset;
3078                                         offset = gtk_text_iter_get_offset (&match_start);
3079                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
3080                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
3081                                 }
3082                         }
3083                 }
3084                 gtk_text_iter_forward_char (&iter);
3085         }
3086 }
3087
3088 gboolean
3089 message_is_empty (ModestMsgEditWindow *window)
3090 {
3091         ModestMsgEditWindowPrivate *priv = NULL;
3092
3093         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3094         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3095
3096         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
3097          * so we can ignore markup.
3098          */
3099         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
3100         gint count = 0;
3101         if (buf)
3102                 count = gtk_text_buffer_get_char_count (buf);
3103
3104         return count == 0;
3105 }
3106
3107 static gboolean
3108 msg_body_focus (GtkWidget *focus,
3109                 GdkEventFocus *event,
3110                 gpointer userdata)
3111 {
3112         
3113         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
3114         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
3115         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
3116         return FALSE;
3117 }
3118
3119 static void
3120 recpt_field_changed (GtkTextBuffer *buffer,
3121                   ModestMsgEditWindow *editor)
3122 {
3123         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3124         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3125 }
3126
3127 static void
3128 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
3129 {
3130         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3131         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3132 }
3133
3134 void
3135 modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor,
3136                                      gboolean modified)
3137 {
3138         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3139         GtkTextBuffer *buffer;
3140
3141         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3142         gtk_text_buffer_set_modified (buffer, modified);
3143         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3144         gtk_text_buffer_set_modified (buffer, modified);
3145         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3146         gtk_text_buffer_set_modified (buffer, modified);
3147         gtk_text_buffer_set_modified (priv->text_buffer, modified);
3148 }
3149
3150 gboolean
3151 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
3152 {
3153         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3154         const char *account_name;
3155         GtkTextBuffer *buffer;
3156
3157         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3158         if (gtk_text_buffer_get_modified (buffer))
3159                 return TRUE;
3160         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3161         if (gtk_text_buffer_get_modified (buffer))
3162                 return TRUE;
3163         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3164         if (gtk_text_buffer_get_modified (buffer))
3165                 return TRUE;
3166         if (gtk_text_buffer_get_modified (priv->text_buffer))
3167                 return TRUE;
3168         account_name = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
3169         if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) {
3170                 return TRUE;
3171         }
3172
3173         return FALSE;
3174 }
3175
3176
3177
3178
3179 gboolean
3180 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
3181 {
3182         ModestMsgEditWindowPrivate *priv = NULL;
3183         
3184         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3185         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3186
3187         /* check if there's no recipient added */
3188         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3189             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3190             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3191                 /* no recipient contents, then select contacts */
3192                 modest_msg_edit_window_open_addressbook (window, NULL);
3193                 return FALSE;
3194         }
3195
3196         g_object_ref (window);
3197         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
3198                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3199                 g_object_unref (window);
3200                 return FALSE;
3201         }
3202         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
3203                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3204                 g_object_unref (window);
3205                 return FALSE;
3206         }
3207         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
3208                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3209                 g_object_unref (window);
3210                 return FALSE;
3211         }
3212
3213         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3214             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3215                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3216         g_object_unref (window);
3217
3218         return TRUE;
3219
3220 }
3221
3222 static void
3223 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3224                                                ModestMsgEditWindow *window)
3225 {
3226         modest_msg_edit_window_offer_attach_file (window);
3227 }
3228
3229 const gchar *
3230 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3231 {
3232         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3233
3234         return priv->clipboard_text;
3235 }
3236
3237 static void
3238 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3239                                                GdkEvent *event,
3240                                                ModestMsgEditWindow *window)
3241 {
3242         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3243         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3244         gchar *text = NULL;
3245         if (!GTK_WIDGET_VISIBLE (window))
3246                 return;
3247
3248         g_object_ref (window);
3249         text = gtk_clipboard_wait_for_text (selection_clipboard);
3250
3251         if (priv->clipboard_text != NULL) {
3252                 g_free (priv->clipboard_text);
3253         }
3254         priv->clipboard_text = text;
3255
3256         if (GTK_WIDGET_VISIBLE (window)) {
3257                 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3258         }
3259         g_object_unref (window);
3260 }
3261
3262 static gboolean clipboard_owner_change_idle (gpointer userdata)
3263 {
3264         ModestMsgEditWindow *window = (ModestMsgEditWindow *) userdata;
3265         ModestMsgEditWindowPrivate *priv;
3266
3267         gdk_threads_enter ();
3268         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3269         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3270
3271         priv->clipboard_owner_idle = 0;
3272         modest_msg_edit_window_clipboard_owner_change (NULL, NULL, window);
3273         gdk_threads_leave ();
3274
3275         return FALSE;
3276 }
3277
3278 static void
3279 modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window)
3280 {
3281         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3282         if (priv->clipboard_owner_idle == 0) {
3283                 priv->clipboard_owner_idle = g_idle_add (clipboard_owner_change_idle, window);
3284         }
3285 }
3286
3287 static void 
3288 subject_field_move_cursor (GtkEntry *entry,
3289                            GtkMovementStep step,
3290                            gint a1,
3291                            gboolean a2,
3292                            gpointer window)
3293 {
3294         if (!GTK_WIDGET_VISIBLE (window))
3295                 return;
3296
3297         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3298 }
3299
3300 static void 
3301 update_window_title (ModestMsgEditWindow *window)
3302 {
3303         ModestMsgEditWindowPrivate *priv = NULL;
3304         const gchar *subject;
3305
3306         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3307         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3308         if (subject == NULL || subject[0] == '\0')
3309                 subject = _("mail_va_new_email");
3310
3311         gtk_window_set_title (GTK_WINDOW (window), subject);
3312
3313 }
3314
3315 static void  
3316 subject_field_changed (GtkEditable *editable, 
3317                        ModestMsgEditWindow *window)
3318 {
3319         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3320         update_window_title (window);
3321         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3322         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3323         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3324 }
3325
3326 static void  
3327 subject_field_insert_text (GtkEditable *editable, 
3328                            gchar *new_text,
3329                            gint new_text_length,
3330                            gint *position,
3331                            ModestMsgEditWindow *window)
3332 {
3333         GString *result = g_string_new ("");
3334         gchar *current;
3335         gint result_len = 0;
3336         const gchar *entry_text = NULL;
3337         gint old_length;
3338
3339         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3340         old_length = g_utf8_strlen (entry_text, -1);
3341
3342         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3343                 gunichar c = g_utf8_get_char_validated (current, 8);
3344                 /* Invalid unichar, stop */
3345                 if (c == -1)
3346                         break;
3347                 /* a bullet */
3348                 if (c == 0x2022)
3349                         continue;
3350                 result = g_string_append_unichar (result, c);
3351                 result_len++;
3352         }
3353
3354         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3355                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3356                 if (result_len > 0)
3357                 {
3358                         /* Prevent endless recursion */
3359                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3360                         g_signal_emit_by_name (editable, "insert-text", 
3361                                                (gpointer) result->str, (gpointer) result->len,
3362                                                (gpointer) position, (gpointer) window);
3363                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3364                 }
3365         }
3366
3367         if (result_len + old_length > 1000) {
3368                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3369                                                 _CS("ckdg_ib_maximum_characters_reached"));
3370         }
3371         g_string_free (result, TRUE);
3372 }
3373
3374 void
3375 modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window,
3376                                             gboolean show)
3377 {
3378         ModestMsgEditWindowPrivate *priv = NULL;
3379
3380         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3381         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3382
3383         gtk_widget_set_no_show_all (priv->find_toolbar, FALSE);
3384
3385         if (show) {
3386                 gtk_widget_show_all (priv->find_toolbar);
3387                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
3388         } else {
3389                 gtk_widget_hide_all (priv->find_toolbar);
3390                 gtk_widget_grab_focus (priv->msg_body);
3391         }
3392 }
3393
3394 static gboolean 
3395 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3396                                           const gchar *str,
3397                                           GtkTextIter *match_start,
3398                                           GtkTextIter *match_end)
3399 {
3400         GtkTextIter end_iter;
3401         gchar *str_casefold;
3402         gint str_chars_n;
3403         gchar *range_text;
3404         gchar *range_casefold;
3405         gint offset;
3406         gint range_chars_n;
3407         gboolean result = FALSE;
3408
3409         if (str == NULL)
3410                 return TRUE;
3411         
3412         /* get end iter */
3413         end_iter = *iter;
3414         gtk_text_iter_forward_to_end (&end_iter);
3415
3416         str_casefold = g_utf8_casefold (str, -1);
3417         str_chars_n = strlen (str);
3418
3419         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3420         range_casefold = g_utf8_casefold (range_text, -1);
3421         range_chars_n = strlen (range_casefold);
3422
3423         if (range_chars_n < str_chars_n) {
3424                 g_free (str_casefold);
3425                 g_free (range_text);
3426                 g_free (range_casefold);
3427                 return FALSE;
3428         }
3429
3430         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3431                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3432                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3433                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3434                         result = TRUE;
3435                         gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3436                                                       match_start, match_end, NULL);
3437                         g_free (found_text);
3438                 }
3439                 g_free (range_subtext);
3440                 if (result)
3441                         break;
3442         }
3443         g_free (str_casefold);
3444         g_free (range_text);
3445         g_free (range_casefold);
3446
3447         return result;
3448 }
3449
3450
3451 static void 
3452 modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
3453                                             ModestMsgEditWindow *window)
3454 {
3455         gchar *current_search = NULL;
3456         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3457         gboolean result;
3458         GtkTextIter selection_start, selection_end;
3459         GtkTextIter match_start, match_end;
3460         gboolean continue_search = FALSE;
3461
3462         if (message_is_empty (window)) {
3463                 g_free (priv->last_search);
3464                 priv->last_search = NULL;
3465                 hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3466                 return;
3467         }
3468
3469         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
3470         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3471                 g_free (current_search);
3472                 g_free (priv->last_search);
3473                 priv->last_search = NULL;
3474                 /* Information banner about empty search */
3475                 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
3476                 return;
3477         }
3478
3479         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3480                 continue_search = TRUE;
3481         } else {
3482                 g_free (priv->last_search);
3483                 priv->last_search = g_strdup (current_search);
3484         }
3485
3486         if (continue_search) {
3487                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3488                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3489                                                                    &match_start, &match_end);
3490                 if (!result)
3491                         hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_search_complete"));
3492         } else {
3493                 GtkTextIter buffer_start;
3494                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3495                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3496                                                                    &match_start, &match_end);
3497                 if (!result)
3498                         hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_no_matches"));
3499         }
3500
3501         /* Mark as selected the string found in search */
3502         if (result) {
3503                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3504                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3505                 correct_scroll_without_drag_check (MODEST_MSG_EDIT_WINDOW (window), FALSE);
3506         } else {
3507                 g_free (priv->last_search);
3508                 priv->last_search = NULL;
3509         }
3510         g_free (current_search);
3511 }
3512
3513 gboolean 
3514 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3515 {
3516         ModestMsgEditWindowPrivate *priv;
3517
3518         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3519         return priv->sent;
3520 }
3521
3522 void 
3523 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3524                                  gboolean sent)
3525 {
3526         ModestMsgEditWindowPrivate *priv;
3527
3528         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3529         priv->sent = sent;
3530 }
3531
3532 static void
3533 modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
3534                                           ModestMsgEditWindow *window)
3535 {
3536         modest_msg_edit_window_toggle_find_toolbar (window, FALSE);
3537 }
3538
3539 void
3540 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3541                                   TnyMsg *draft)
3542 {
3543         ModestMsgEditWindowPrivate *priv;
3544         TnyHeader *header = NULL;
3545
3546         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3547         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3548
3549         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3550         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
3551
3552         if (priv->draft_msg != NULL) {
3553                 g_object_unref (priv->draft_msg);
3554         }
3555
3556         if (draft != NULL) {
3557                 g_object_ref (draft);
3558                 header = tny_msg_get_header (draft);
3559                 if (priv->msg_uid) {
3560                         g_free (priv->msg_uid);
3561                         priv->msg_uid = NULL;
3562                 }
3563                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3564                 if (GTK_WIDGET_REALIZED (window)) {
3565                         if (!modest_window_mgr_register_window (mgr, MODEST_WINDOW (window), NULL)) {
3566                                 gtk_widget_destroy (GTK_WIDGET (window));
3567                                 return;
3568                         }
3569                 }
3570         }
3571
3572         priv->draft_msg = draft;
3573 }
3574
3575 static void  
3576 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3577                        GtkTextIter *start, GtkTextIter *end,
3578                        gpointer userdata)
3579 {
3580         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3581         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3582         gchar *tag_name;
3583
3584         if (tag == NULL) return;
3585         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3586         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3587                 replace_with_images (window, priv->images);
3588         }
3589 }
3590
3591 void                    
3592 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3593                                  TnyMimePart *part)
3594 {
3595         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3596
3597         g_return_if_fail (TNY_IS_MIME_PART (part));
3598         tny_list_prepend (priv->attachments, (GObject *) part);
3599         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3600         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3601         gtk_widget_show_all (priv->attachments_caption);
3602         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3603 }
3604
3605 const gchar*    
3606 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3607 {
3608         ModestMsgEditWindowPrivate *priv;
3609
3610         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3611         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3612
3613         return priv->msg_uid;
3614 }
3615
3616 GtkWidget *
3617 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3618                                          ModestMsgEditWindowWidgetType widget_type)
3619 {
3620         ModestMsgEditWindowPrivate *priv;
3621
3622         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3623         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3624
3625         switch (widget_type) {
3626         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3627                 return priv->msg_body;
3628                 break;
3629         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3630                 return priv->to_field;
3631                 break;
3632         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3633                 return priv->cc_field;
3634                 break;
3635         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3636                 return priv->bcc_field;
3637                 break;
3638         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3639                 return priv->subject_field;
3640                 break;
3641         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3642                 return priv->attachments_view;
3643                 break;
3644         default:
3645                 return NULL;
3646         }
3647 }
3648
3649 static void 
3650 remove_tags (WPTextBuffer *buffer)
3651 {
3652         GtkTextIter start, end;
3653
3654         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3655         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3656
3657         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3658 }
3659
3660 static void
3661 on_account_removed (TnyAccountStore *account_store, 
3662                     TnyAccount *account,
3663                     gpointer user_data)
3664 {
3665         /* Do nothing if it's a store account, because we use the
3666            transport to send the messages */
3667         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
3668                 const gchar *parent_acc = NULL;
3669                 const gchar *our_acc = NULL;
3670
3671                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
3672                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3673                 /* Close this window if I'm showing a message of the removed account */
3674                 if (strcmp (parent_acc, our_acc) == 0)
3675                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
3676         }
3677 }
3678
3679 static void
3680 from_field_changed (HildonPickerButton *button,
3681                     ModestMsgEditWindow *self)
3682 {
3683         ModestMsgEditWindowPrivate *priv;
3684         gboolean has_old_signature, has_new_signature;
3685         GtkTextIter iter;
3686         GtkTextIter match_start, match_end;
3687         ModestAccountMgr *mgr;
3688         gchar *signature;
3689         gchar *full_signature;
3690
3691         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
3692
3693         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
3694         mgr = modest_runtime_get_account_mgr ();
3695         signature = modest_account_mgr_get_signature (mgr, priv->last_from_account, &has_old_signature);
3696         if (has_old_signature) {
3697                 full_signature = g_strconcat ("\n--\n", signature, NULL);
3698                 if (gtk_text_iter_forward_search (&iter, full_signature, 0, &match_start, &match_end, NULL)) {
3699                         gtk_text_buffer_delete (priv->text_buffer, &match_start, &match_end);
3700                         iter = match_start;
3701                 } else if (gtk_text_iter_forward_search (&iter, _("mcen_ia_editor_original_message"), 0,
3702                                                          &match_start, &match_end, NULL)) {
3703                         iter = match_start;
3704                 }
3705                 g_free (full_signature);
3706         }
3707         g_free (signature);
3708
3709         priv->last_from_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
3710         signature = modest_account_mgr_get_signature (mgr, priv->last_from_account, &has_new_signature);
3711         if (has_new_signature) {
3712                 full_signature = g_strconcat ("\n--\n", signature, NULL);
3713                 gtk_text_buffer_insert (priv->text_buffer, &iter, full_signature, -1);
3714                 g_free (full_signature);
3715         }
3716         g_free (signature);
3717 }
3718
3719 typedef struct _MessageSettingsHelper {
3720         ModestMsgEditWindow *window;
3721         GSList *priority_group;
3722         GSList *format_group;
3723         GtkToggleButton *current_priority;
3724         GtkToggleButton *current_format;
3725 } MessageSettingsHelper;
3726
3727 static void
3728 on_priority_toggle (GtkToggleButton *button, 
3729                     MessageSettingsHelper *helper)
3730 {
3731         GSList *node;
3732         ModestMsgEditWindowPrivate *priv;
3733
3734         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
3735         if (gtk_toggle_button_get_active (button)) {
3736
3737                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
3738                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
3739                         if ((node_button != button) &&
3740                             gtk_toggle_button_get_active (node_button)) {
3741                                 gtk_toggle_button_set_active (node_button, FALSE);
3742                         }
3743                 }
3744                 helper->current_priority = button;
3745         } else {
3746                 gboolean found = FALSE;
3747                 /* If no one is active, activate it again */
3748                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
3749                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
3750                         if (gtk_toggle_button_get_active (node_button)) {
3751                                 found = TRUE;
3752                                 break;
3753                         }
3754                 }
3755                 if (!found) {
3756                         gtk_toggle_button_set_active (button, TRUE);
3757                 }
3758         }
3759 }
3760
3761 static void
3762 on_format_toggle (GtkToggleButton *button,
3763                   MessageSettingsHelper *helper)
3764 {
3765         GSList *node;
3766         ModestMsgEditWindowPrivate *priv;
3767
3768         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
3769         if (gtk_toggle_button_get_active (button)) {
3770
3771                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
3772                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
3773                         if ((node_button != button) &&
3774                             gtk_toggle_button_get_active (node_button)) {
3775                                 gtk_toggle_button_set_active (node_button, FALSE);
3776                         }
3777                 }
3778                 helper->current_format = button;
3779         } else {
3780                 gboolean found = FALSE;
3781                 /* If no one is active, activate it again */
3782                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
3783                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
3784                         if (gtk_toggle_button_get_active (node_button)) {
3785                                 found = TRUE;
3786                                 break;
3787                         }
3788                 }
3789                 if (!found) {
3790                         gtk_toggle_button_set_active (button, TRUE);
3791                 }
3792         }
3793
3794 }
3795
3796 static void
3797 modest_msg_edit_window_show_msg_settings_dialog (ModestMsgEditWindow *window)
3798 {
3799         GtkWidget *dialog;
3800         GtkWidget *align;
3801         GtkWidget *vbox;
3802         GtkWidget *priority_hbox;
3803         GtkWidget *high_toggle, *medium_toggle, *low_toggle;
3804         GtkWidget *captioned;
3805         GtkSizeGroup *title_sizegroup, *value_sizegroup;
3806         GtkWidget *format_hbox;
3807         GtkWidget *html_toggle, *text_toggle;
3808         ModestMsgEditWindowPrivate *priv;
3809         MessageSettingsHelper helper = {0,};
3810
3811         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3812         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3813         helper.window = window;
3814         helper.priority_group = NULL;
3815         helper.format_group = NULL;
3816
3817         title_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
3818         value_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
3819
3820         dialog = gtk_dialog_new_with_buttons (_("mcen_me_message_settings"), NULL,
3821                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3822                                               _HL("wdgt_bd_done"), GTK_RESPONSE_ACCEPT, NULL);
3823         vbox = gtk_vbox_new (FALSE, 0);
3824         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
3825         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DOUBLE, 0);
3826         gtk_container_add (GTK_CONTAINER (align), vbox);
3827         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), align);
3828         gtk_widget_show (align);
3829         gtk_widget_show (vbox);
3830
3831         /* Priority toggles */
3832         priority_hbox = gtk_hbox_new (TRUE, 0);
3833         high_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
3834         gtk_button_set_label (GTK_BUTTON (high_toggle), _("mcen_me_editor_priority_high"));
3835         helper.priority_group = g_slist_prepend (helper.priority_group, high_toggle);
3836         g_object_set_data (G_OBJECT (high_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_HIGH_PRIORITY));
3837         medium_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
3838         gtk_button_set_label (GTK_BUTTON (medium_toggle), _("mcen_me_editor_priority_normal"));
3839         helper.priority_group = g_slist_prepend (helper.priority_group, medium_toggle);
3840         g_object_set_data (G_OBJECT (medium_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_NORMAL_PRIORITY));
3841         low_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
3842         gtk_button_set_label (GTK_BUTTON (low_toggle), _("mcen_me_editor_priority_low"));
3843         helper.priority_group = g_slist_prepend (helper.priority_group, low_toggle);
3844         g_object_set_data (G_OBJECT (low_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_LOW_PRIORITY));
3845         gtk_box_pack_start (GTK_BOX (priority_hbox), low_toggle, TRUE, TRUE, 0);
3846         gtk_box_pack_start (GTK_BOX (priority_hbox), medium_toggle, TRUE, TRUE, 0);
3847         gtk_box_pack_start (GTK_BOX (priority_hbox), high_toggle, TRUE, TRUE, 0);
3848         gtk_widget_show_all (priority_hbox);
3849         captioned = modest_maemo_utils_create_captioned (title_sizegroup, value_sizegroup,
3850                                                          _("mcen_me_editor_message_priority"), FALSE, priority_hbox);
3851         gtk_widget_show (captioned);
3852         gtk_box_pack_start (GTK_BOX (vbox), captioned, FALSE, FALSE, 0);
3853
3854         /* format toggles */
3855         format_hbox = gtk_hbox_new (TRUE, 0);
3856         html_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
3857         gtk_button_set_label (GTK_BUTTON (html_toggle), _("mcen_me_editor_formatted_text"));
3858         helper.format_group = g_slist_prepend (helper.format_group, html_toggle);
3859         g_object_set_data (G_OBJECT (html_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_HTML));
3860         g_object_set_data (G_OBJECT (html_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_FORMATTED_TEXT));
3861         text_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
3862         gtk_button_set_label (GTK_BUTTON (text_toggle), _("mcen_me_editor_plain_text"));
3863         helper.format_group = g_slist_prepend (helper.format_group, text_toggle);
3864         g_object_set_data (G_OBJECT (text_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_TEXT));
3865         g_object_set_data (G_OBJECT (text_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_PLAIN_TEXT));
3866         gtk_box_pack_start (GTK_BOX (format_hbox), html_toggle, TRUE, TRUE, 0);
3867         gtk_box_pack_start (GTK_BOX (format_hbox), text_toggle, TRUE, TRUE, 0);
3868         gtk_widget_show_all (format_hbox);
3869         gtk_widget_show (format_hbox);
3870         gtk_box_pack_start (GTK_BOX (vbox), format_hbox, FALSE, FALSE, 0);
3871
3872
3873         g_object_unref (title_sizegroup);
3874         g_object_unref (value_sizegroup);
3875
3876         /* Set current values */
3877         switch (priv->priority_flags) {
3878         case TNY_HEADER_FLAG_HIGH_PRIORITY:
3879                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (high_toggle), TRUE);
3880                 helper.current_priority = GTK_TOGGLE_BUTTON (high_toggle);
3881                 break;
3882         case TNY_HEADER_FLAG_LOW_PRIORITY:
3883                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (low_toggle), TRUE);
3884                 helper.current_priority = GTK_TOGGLE_BUTTON (low_toggle);
3885                 break;
3886         default:
3887                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (medium_toggle), TRUE);
3888                 helper.current_priority = GTK_TOGGLE_BUTTON (medium_toggle);
3889                 break;
3890         }
3891
3892         switch (modest_msg_edit_window_get_format (window)) {
3893         case MODEST_MSG_EDIT_FORMAT_TEXT:
3894                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_toggle), TRUE);
3895                 helper.current_format = GTK_TOGGLE_BUTTON (text_toggle);
3896                 break;
3897         case MODEST_MSG_EDIT_FORMAT_HTML:
3898         default:
3899                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (html_toggle), TRUE);
3900                 helper.current_format = GTK_TOGGLE_BUTTON (html_toggle);
3901                 break;
3902         }
3903
3904         /* Signal connects */
3905         g_signal_connect (G_OBJECT (high_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
3906         g_signal_connect (G_OBJECT (medium_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
3907         g_signal_connect (G_OBJECT (low_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
3908         g_signal_connect (G_OBJECT (html_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
3909         g_signal_connect (G_OBJECT (text_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
3910
3911         /* Save settings if the user clicked on done */
3912         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
3913                 TnyHeaderFlags flags;
3914                 ModestMsgEditFormat old_format, new_format;
3915
3916                 /* Set priority flags */
3917                 flags = (TnyHeaderFlags) g_object_get_data (G_OBJECT (helper.current_priority), "priority");
3918                 if (priv->priority_flags !=  flags)
3919                         modest_msg_edit_window_set_priority_flags (window, flags);
3920
3921                 /* Set edit format */
3922                 old_format = modest_msg_edit_window_get_format (window);
3923                 new_format = (ModestMsgEditFormat) g_object_get_data (G_OBJECT (helper.current_format), "format");
3924                 if (old_format != new_format) {
3925                         gint file_format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (helper.current_format), "file-format"));
3926                         modest_msg_edit_window_set_file_format (window, file_format);
3927                 }
3928         }
3929
3930         gtk_widget_destroy (dialog);
3931         g_slist_free (helper.priority_group);
3932 }
3933
3934 static void
3935 on_message_settings (GtkAction *action,
3936                      ModestMsgEditWindow *window)
3937 {
3938         modest_msg_edit_window_show_msg_settings_dialog (window);
3939 }
3940
3941 static void
3942 on_cc_button_toggled (HildonCheckButton *button,
3943                       ModestMsgEditWindow *window)
3944 {
3945         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
3946
3947         modest_msg_edit_window_show_cc (MODEST_MSG_EDIT_WINDOW (window),
3948                                         hildon_check_button_get_active (button));
3949 }
3950
3951 static void
3952 on_bcc_button_toggled (HildonCheckButton *button,
3953                       ModestMsgEditWindow *window)
3954 {
3955         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
3956
3957         modest_msg_edit_window_show_bcc (MODEST_MSG_EDIT_WINDOW (window),
3958                                         hildon_check_button_get_active (button));
3959 }
3960
3961 static void 
3962 setup_menu (ModestMsgEditWindow *self)
3963 {
3964         ModestMsgEditWindowPrivate *priv = NULL;
3965
3966         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
3967
3968         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
3969
3970         /* Settings menu buttons */
3971         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_editor_checknames"), NULL,
3972                                            APP_MENU_CALLBACK (modest_ui_actions_on_check_names),
3973                                            NULL);
3974         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_undo"), "<Ctrl>z",
3975                                            APP_MENU_CALLBACK (modest_ui_actions_on_undo),
3976                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_undo));
3977
3978         priv->cc_button = hildon_check_button_new (0);
3979         gtk_button_set_label (GTK_BUTTON (priv->cc_button), _("mcen_me_editor_showcc"));
3980         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->cc_button),
3981                                         FALSE);
3982         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->cc_button),
3983                                                   NULL);
3984         g_signal_connect (G_OBJECT (priv->cc_button), "toggled",
3985                           G_CALLBACK (on_cc_button_toggled), (gpointer) self);
3986         priv->bcc_button = hildon_check_button_new (0);
3987         gtk_button_set_label (GTK_BUTTON (priv->bcc_button), _("mcen_me_editor_showbcc"));
3988         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->bcc_button),
3989                                         FALSE);
3990         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->bcc_button),
3991                                                   NULL);
3992         g_signal_connect (G_OBJECT (priv->bcc_button), "toggled",
3993                           G_CALLBACK (on_bcc_button_toggled), (gpointer) self);
3994
3995         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_editor_attach_inlineimage"), NULL,
3996                                            APP_MENU_CALLBACK (modest_ui_actions_on_insert_image),
3997                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_set_style));
3998         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3999                                            APP_MENU_CALLBACK (modest_ui_actions_on_remove_attachments),
4000                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_editor_remove_attachment));
4001         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_message_settings"), NULL,
4002                                            APP_MENU_CALLBACK (on_message_settings),
4003                                            NULL);
4004         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), "<Ctrl>f",
4005                                            APP_MENU_CALLBACK (modest_ui_actions_on_toggle_find_in_page),
4006                                            NULL);
4007 }
4008