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