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