This is a manual merge of branch drop split view intro trunk.
[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 <wptextview.h>
65 #include <wptextbuffer.h>
66 #include <hildon/hildon-pannable-area.h>
67 #include "modest-msg-edit-window-ui-dimming.h"
68
69 #include "modest-hildon-includes.h"
70 #ifdef MODEST_HAVE_HILDON0_WIDGETS
71 #include <hildon-widgets/hildon-color-chooser.h>
72 #endif
73 #include "widgets/modest-msg-edit-window-ui.h"
74 #ifdef MODEST_HAVE_HILDON0_WIDGETS
75 #include <libgnomevfs/gnome-vfs-mime-utils.h>
76 #else
77 #include <libgnomevfs/gnome-vfs-mime.h>
78 #endif
79 #include <modest-utils.h>
80 #include "modest-maemo-utils.h"
81 #include <modest-ui-constants.h>
82
83
84 #define DEFAULT_FONT_SIZE 3
85 #define DEFAULT_FONT 2
86 #define DEFAULT_SIZE_BUTTON_FONT_FAMILY "Sans"
87 #define DEFAULT_MAIN_VBOX_SPACING 6
88 #define SUBJECT_MAX_LENGTH 1000
89 #define IMAGE_MAX_WIDTH 560
90 #define DEFAULT_FONT_SCALE 1.5
91
92 static gboolean is_wp_text_buffer_started = FALSE;
93
94 static void  modest_msg_edit_window_class_init   (ModestMsgEditWindowClass *klass);
95 static void  modest_msg_edit_window_init         (ModestMsgEditWindow *obj);
96 static void  modest_msg_edit_window_finalize     (GObject *obj);
97
98 static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata);
99 static void  body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
100 static void  recpt_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
101
102 static void  text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window);
103 static void  text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window);
104 static void  text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window);
105 static void  text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
106                                     GtkTextIter *start, GtkTextIter *end,
107                                     gpointer userdata);
108 static void  text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id);
109 static void  subject_field_changed (GtkEditable *editable, ModestMsgEditWindow *window);
110 static void  subject_field_insert_text (GtkEditable *editable, 
111                                         gchar *new_text,
112                                         gint new_text_length,
113                                         gint *position,
114                                         ModestMsgEditWindow *window);
115 static void  modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
116                                                          gpointer userdata);
117 static void  modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
118                                                  gpointer userdata);
119 static void  modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
120                                                  gpointer userdata);
121 static void  modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window);
122
123 static void modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
124                                                      ModestRecptEditor *editor);
125 static void modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
126                                                            ModestMsgEditWindow *window);
127
128 /* ModestWindow methods implementation */
129 static void modest_msg_edit_window_disconnect_signals (ModestWindow *window);
130 static void modest_msg_edit_window_show_toolbar   (ModestWindow *window,
131                                                    gboolean show_toolbar);
132 static void modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
133                                                            GdkEvent *event,
134                                                            ModestMsgEditWindow *window);
135 static void modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window);
136 static void subject_field_move_cursor (GtkEntry *entry,
137                                        GtkMovementStep step,
138                                        gint a1,
139                                        gboolean a2,
140                                        gpointer userdata);
141 static void update_window_title (ModestMsgEditWindow *window);
142
143 /* Find toolbar */
144 static void modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
145                                                         ModestMsgEditWindow *window);
146 static void modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
147                                                        ModestMsgEditWindow *window);
148 static gboolean gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
149                                                           const gchar *str,
150                                                           GtkTextIter *match_start,
151                                                           GtkTextIter *match_end);
152
153 static void remove_tags (WPTextBuffer *buffer);
154
155 static void on_account_removed (TnyAccountStore *account_store, 
156                                 TnyAccount *account,
157                                 gpointer user_data);
158
159 static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window);
160 static void set_zoom_do_nothing (ModestWindow *window, gdouble zoom);
161 static gdouble get_zoom_do_nothing (ModestWindow *window);
162
163 static void init_window (ModestMsgEditWindow *obj);
164
165 gboolean scroll_drag_timeout (gpointer userdata);
166 static void correct_scroll (ModestMsgEditWindow *w);
167 static void correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused);
168 static void text_buffer_end_user_action (GtkTextBuffer *buffer,
169                                          ModestMsgEditWindow *userdata);
170 static void text_buffer_mark_set (GtkTextBuffer *buffer,
171                                   GtkTextIter *iter,
172                                   GtkTextMark *mark,
173                                   ModestMsgEditWindow *userdata);
174
175 static void DEBUG_BUFFER (WPTextBuffer *buffer)
176 {
177 #ifdef DEBUG
178         GtkTextIter iter;
179         g_message ("BEGIN BUFFER OF SIZE %d", gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (buffer)));
180
181         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
182         while (!gtk_text_iter_is_end (&iter)) {
183                 GString *output = g_string_new ("");
184                 GSList *toggled_tags;
185                 GSList *node;
186
187                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
188                 g_string_append_printf (output, "%d: CLOSED [ ", gtk_text_iter_get_offset (&iter));
189                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
190                         GtkTextTag *tag = (GtkTextTag *) node->data;
191                         const gchar *name;
192                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
193                         output = g_string_append (output, name);
194                         g_string_append (output, " ");
195                 }
196                 output = g_string_append (output, "] OPENED [ ");
197                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
198                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
199                         GtkTextTag *tag = (GtkTextTag *) node->data;
200                         const gchar *name;
201                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
202                         output = g_string_append (output, name);
203                         g_string_append (output, " ");
204                 }
205                 output = g_string_append (output, "]\n");
206                 g_message ("%s", output->str);
207                 g_string_free (output, TRUE);
208                 gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
209         }
210         g_message ("END BUFFER");
211 #endif
212 }
213
214
215 /* static gboolean */
216 /* on_key_pressed (GtkWidget *self, */
217 /*              GdkEventKey *event, */
218 /*              gpointer user_data); */
219
220 /* list my signals */
221 enum {
222         /* MY_SIGNAL_1, */
223         /* MY_SIGNAL_2, */
224         LAST_SIGNAL
225 };
226
227 typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate;
228 struct _ModestMsgEditWindowPrivate {
229         GtkWidget   *msg_body;
230         GtkWidget   *frame;
231         GtkWidget   *header_box;
232         
233         ModestPairList *from_field_protos;
234         GtkWidget   *from_field;
235         gchar       *original_account_name;
236         
237         GtkWidget   *to_field;
238         GtkWidget   *cc_field;
239         GtkWidget   *bcc_field;
240         GtkWidget   *subject_field;
241         GtkWidget   *attachments_view;
242         GtkWidget   *priority_icon;
243         GtkWidget   *add_attachment_button;
244
245         GtkWidget   *cc_caption;
246         GtkWidget   *bcc_caption;
247         gboolean     update_caption_visibility;
248         GtkWidget   *attachments_caption;
249
250         GtkTextBuffer *text_buffer;
251
252         GtkWidget   *font_size_toolitem;
253         GtkWidget   *font_face_toolitem;
254         GtkWidget   *font_color_button;
255         GtkWidget   *font_color_toolitem;
256         GSList      *font_items_group;
257         GtkWidget   *font_tool_button_label;
258         GSList      *size_items_group;
259         GtkWidget   *size_tool_button_label;
260         
261         GtkWidget   *find_toolbar;
262         gchar       *last_search;
263
264         GtkWidget   *font_dialog;
265
266         GtkWidget   *pannable;
267         guint        scroll_drag_timeout_id;
268         gdouble      last_upper;
269
270         gint next_cid;
271         TnyList *attachments;
272         TnyList *images;
273         guint64 images_size;
274         gint images_count;
275
276         TnyHeaderFlags priority_flags;
277         
278         gboolean    can_undo, can_redo;
279         gulong      clipboard_change_handler_id;
280         gulong      default_clipboard_change_handler_id;
281         gulong      account_removed_handler_id;
282         guint       clipboard_owner_idle;
283         gchar       *clipboard_text;
284
285         TnyMsg      *draft_msg;
286         TnyMsg      *outbox_msg;
287         gchar       *msg_uid;
288
289         gboolean    sent;
290 };
291
292 #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
293                                                     MODEST_TYPE_MSG_EDIT_WINDOW, \
294                                                     ModestMsgEditWindowPrivate))
295 /* globals */
296 static GtkWindowClass *parent_class = NULL;
297
298 /* uncomment the following if you have defined any signals */
299 /* static guint signals[LAST_SIGNAL] = {0}; */
300
301 GType
302 modest_msg_edit_window_get_type (void)
303 {
304         static GType my_type = 0;
305         if (!my_type) {
306                 static const GTypeInfo my_info = {
307                         sizeof(ModestMsgEditWindowClass),
308                         NULL,           /* base init */
309                         NULL,           /* base finalize */
310                         (GClassInitFunc) modest_msg_edit_window_class_init,
311                         NULL,           /* class finalize */
312                         NULL,           /* class data */
313                         sizeof(ModestMsgEditWindow),
314                         1,              /* n_preallocs */
315                         (GInstanceInitFunc) modest_msg_edit_window_init,
316                         NULL
317                 };
318                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
319                                                   "ModestMsgEditWindow",
320                                                   &my_info, 0);
321
322         }
323         return my_type;
324 }
325
326 static void
327 save_state (ModestWindow *self)
328 {
329         modest_widget_memory_save (modest_runtime_get_conf(),
330                                    G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
331 }
332
333
334 static void
335 restore_settings (ModestMsgEditWindow *self)
336 {
337         ModestConf *conf = NULL;
338         GtkAction *action;
339         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
340
341         conf = modest_runtime_get_conf ();
342
343         /* Dim at start clipboard actions */
344         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CutMenu");
345         gtk_action_set_sensitive (action, FALSE);
346         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CopyMenu");
347         gtk_action_set_sensitive (action, FALSE);
348         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/RemoveAttachmentsMenu");
349         gtk_action_set_sensitive (action, FALSE);
350
351         modest_widget_memory_restore (conf, G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
352 }
353
354
355 static void
356 modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass)
357 {
358         GObjectClass *gobject_class;
359         ModestWindowClass *modest_window_class;
360         gobject_class = (GObjectClass*) klass;
361         modest_window_class = (ModestWindowClass*) klass;
362
363         parent_class            = g_type_class_peek_parent (klass);
364         gobject_class->finalize = modest_msg_edit_window_finalize;
365
366         modest_window_class->set_zoom_func = set_zoom_do_nothing;
367         modest_window_class->get_zoom_func = get_zoom_do_nothing;
368         modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented;
369         modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented;
370         modest_window_class->show_toolbar_func = modest_msg_edit_window_show_toolbar;
371         modest_window_class->save_state_func = save_state;
372         modest_window_class->disconnect_signals_func = modest_msg_edit_window_disconnect_signals;
373
374         g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate));
375 }
376
377 static void
378 modest_msg_edit_window_init (ModestMsgEditWindow *obj)
379 {
380         ModestMsgEditWindowPrivate *priv;
381         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
382
383         priv->msg_body      = NULL;
384         priv->frame         = NULL;
385         priv->from_field    = NULL;
386         priv->to_field      = NULL;
387         priv->cc_field      = NULL;
388         priv->bcc_field     = NULL;
389         priv->subject_field = NULL;
390         priv->attachments   = TNY_LIST (tny_simple_list_new ());
391         priv->images        = TNY_LIST (tny_simple_list_new ());
392         priv->images_size   = 0;
393         priv->images_count  = 0;
394         priv->next_cid      = 0;
395
396         priv->cc_caption    = NULL;
397         priv->bcc_caption    = NULL;
398         priv->update_caption_visibility = FALSE;
399
400         priv->priority_flags = 0;
401
402         priv->find_toolbar = NULL;
403         priv->last_search = NULL;
404
405         priv->draft_msg = NULL;
406         priv->outbox_msg = NULL;
407         priv->msg_uid = NULL;
408
409         priv->can_undo = FALSE;
410         priv->can_redo = FALSE;
411         priv->clipboard_change_handler_id = 0;
412         priv->default_clipboard_change_handler_id = 0;
413         priv->account_removed_handler_id = 0;
414         priv->clipboard_owner_idle = 0;
415         priv->clipboard_text = NULL;
416         priv->sent = FALSE;
417
418         priv->scroll_drag_timeout_id = 0;
419         priv->last_upper = 0.0;
420
421         priv->font_dialog = NULL;
422
423         if (!is_wp_text_buffer_started) {
424                 is_wp_text_buffer_started = TRUE;
425                 wp_text_buffer_library_init ();
426         }
427
428         init_window (obj);
429         
430         hildon_program_add_window (hildon_program_get_instance(),
431                                    HILDON_WINDOW(obj));
432 }
433
434
435 /** 
436  * @result: A ModestPairList, which must be freed with modest_pair_list_free().
437  */
438 static ModestPairList*
439 get_transports (void)
440 {
441         GSList *transports = NULL;
442         
443         ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr();
444         GSList *accounts = modest_account_mgr_account_names (account_mgr, 
445                                                              TRUE /* only enabled accounts. */); 
446                                                 
447         GSList *cursor = accounts;
448         while (cursor) {
449                 gchar *account_name = cursor->data;
450                 gchar *from_string  = NULL;
451                 if (account_name) {
452                         from_string = modest_account_mgr_get_from_string (account_mgr,
453                                                                           account_name);
454                 }
455                 
456                 if (from_string && account_name) {
457                         gchar *name = account_name;
458                         ModestPair *pair = modest_pair_new ((gpointer) name,
459                                                 (gpointer) from_string , TRUE);
460                         transports = g_slist_prepend (transports, pair);
461                 }
462                 
463                 cursor = cursor->next;
464         }
465         g_slist_free (accounts); /* only free the accounts, not the elements,
466                                   * because they are used in the pairlist */
467         return transports;
468 }
469
470 static void window_focus (GtkWindow *window,
471                           GtkWidget *widget,
472                           gpointer userdata)
473 {
474         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
475 }
476
477 gboolean
478 scroll_drag_timeout (gpointer userdata)
479 {
480         ModestMsgEditWindow *win = (ModestMsgEditWindow *) userdata;
481         ModestMsgEditWindowPrivate *priv;
482
483         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(win);
484
485         correct_scroll_without_drag_check (win, TRUE);
486
487         priv->scroll_drag_timeout_id = 0;
488
489         return FALSE;
490 }
491
492 static void
493 correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused)
494 {
495         ModestMsgEditWindowPrivate *priv;
496         GtkTextMark *insert;
497         GtkTextIter iter;
498         GdkRectangle rectangle;
499         gdouble new_value;
500         gint offset;
501
502         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
503
504         if (only_if_focused && !gtk_widget_is_focus (priv->msg_body))
505                 return;
506
507         insert = gtk_text_buffer_get_insert (priv->text_buffer);
508         gtk_text_buffer_get_iter_at_mark (priv->text_buffer, &iter, insert);
509
510         gtk_text_view_get_iter_location (GTK_TEXT_VIEW (priv->msg_body), &iter, &rectangle);
511         offset = priv->msg_body->allocation.y;
512
513         new_value = (offset + rectangle.y);
514
515         hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (priv->pannable), -1, new_value);
516 }
517
518 static void
519 correct_scroll (ModestMsgEditWindow *w)
520 {
521         ModestMsgEditWindowPrivate *priv;
522
523         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
524         if (gtk_grab_get_current () == priv->msg_body) {
525                 if (priv->scroll_drag_timeout_id == 0) {
526                         priv->scroll_drag_timeout_id = g_timeout_add (500, (GSourceFunc) scroll_drag_timeout,
527                                                                       (gpointer) w);
528                 }
529                 return;
530         }
531
532         correct_scroll_without_drag_check (w, TRUE);
533 }
534
535 static void
536 text_buffer_end_user_action (GtkTextBuffer *buffer,
537                              ModestMsgEditWindow *userdata)
538 {
539
540         correct_scroll (userdata);
541 }
542
543 static void
544 text_buffer_mark_set (GtkTextBuffer *buffer,
545                       GtkTextIter *iter,
546                       GtkTextMark *mark,
547                       ModestMsgEditWindow *userdata)
548 {
549         gtk_text_buffer_begin_user_action (buffer);
550         gtk_text_buffer_end_user_action (buffer);
551 }
552
553 static void
554 cut_clipboard_check (GtkTextView *text_view,
555                      gpointer userdata)
556 {
557         GtkTextBuffer *buffer;
558         
559         buffer = gtk_text_view_get_buffer (text_view);
560         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
561                 g_signal_stop_emission_by_name ((gpointer )text_view, "cut-clipboard");
562         }
563 }
564
565 static void
566 copy_clipboard_check (GtkTextView *text_view,
567                      gpointer userdata)
568 {
569         GtkTextBuffer *buffer;
570         
571         buffer = gtk_text_view_get_buffer (text_view);
572         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
573                 g_signal_stop_emission_by_name ((gpointer )text_view, "copy-clipboard");
574         }
575 }
576
577 static void
578 attachment_deleted (ModestAttachmentsView *attachments_view,
579                     gpointer user_data)
580 {
581         modest_msg_edit_window_remove_attachments (MODEST_MSG_EDIT_WINDOW (user_data),
582                                                    NULL);
583 }
584
585 static void
586 connect_signals (ModestMsgEditWindow *obj)
587 {
588         ModestMsgEditWindowPrivate *priv;
589
590         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
591
592         g_signal_connect (G_OBJECT (priv->text_buffer), "refresh_attributes",
593                           G_CALLBACK (text_buffer_refresh_attributes), obj);
594         g_signal_connect (G_OBJECT (priv->text_buffer), "can-undo",
595                           G_CALLBACK (text_buffer_can_undo), obj);
596         g_signal_connect (G_OBJECT (priv->text_buffer), "can-redo",
597                           G_CALLBACK (text_buffer_can_redo), obj);
598         g_signal_connect (G_OBJECT (priv->text_buffer), "changed",
599                           G_CALLBACK (body_changed), obj);
600         g_signal_connect (G_OBJECT (priv->text_buffer), "modified-changed",
601                           G_CALLBACK (body_changed), obj);
602         g_signal_connect (G_OBJECT (priv->text_buffer), "end-user-action",
603                           G_CALLBACK (text_buffer_end_user_action), obj);
604         g_signal_connect (G_OBJECT (priv->text_buffer), "mark-set",
605                           G_CALLBACK (text_buffer_mark_set), obj);
606         g_signal_connect_after (G_OBJECT (priv->text_buffer), "apply-tag",
607                                 G_CALLBACK (text_buffer_apply_tag), obj);
608         g_signal_connect_swapped (G_OBJECT (priv->to_field), "open-addressbook", 
609                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
610         g_signal_connect_swapped (G_OBJECT (priv->cc_field), "open-addressbook", 
611                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
612         g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", 
613                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
614
615         g_signal_connect (G_OBJECT (priv->add_attachment_button), "clicked",
616                           G_CALLBACK (modest_msg_edit_window_add_attachment_clicked), obj);
617
618         g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event",
619                           G_CALLBACK (msg_body_focus), obj);
620         g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event",
621                           G_CALLBACK (msg_body_focus), obj);
622         g_signal_connect (G_OBJECT (obj), "set-focus", G_CALLBACK (window_focus), obj);
623         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))),
624                           "changed", G_CALLBACK (recpt_field_changed), obj);
625         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))),
626                           "changed", G_CALLBACK (recpt_field_changed), obj);
627         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))),
628                           "changed", G_CALLBACK (recpt_field_changed), obj);
629         g_signal_connect (G_OBJECT (priv->subject_field), "changed", G_CALLBACK (subject_field_changed), obj);
630         g_signal_connect_after (G_OBJECT (priv->subject_field), "move-cursor", G_CALLBACK (subject_field_move_cursor), obj);
631         g_signal_connect (G_OBJECT (priv->subject_field), "insert-text", G_CALLBACK (subject_field_insert_text), obj);
632
633         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_edit_window_find_toolbar_close), obj);
634         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_edit_window_find_toolbar_search), obj);
635
636         priv->clipboard_change_handler_id = 
637                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change",
638                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
639         priv->default_clipboard_change_handler_id = 
640                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)), "owner-change",
641                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
642
643         g_signal_connect (G_OBJECT (priv->msg_body), "cut-clipboard", G_CALLBACK (cut_clipboard_check), NULL);
644         g_signal_connect (G_OBJECT (priv->msg_body), "copy-clipboard", G_CALLBACK (copy_clipboard_check), NULL);
645         g_signal_connect (G_OBJECT (priv->attachments_view), "delete", G_CALLBACK (attachment_deleted), obj);
646 }
647
648 static void
649 init_wp_text_view_style ()
650 {
651         static gboolean initialized = FALSE;
652
653         if (!initialized) {
654                 gtk_rc_parse_string ("class \"WPTextView\" style \"fremantle-textview\"");
655                 initialized = TRUE;
656         }
657 }       
658
659 static void
660 init_window (ModestMsgEditWindow *obj)
661 {
662         GtkWidget *to_caption, *subject_caption;
663         GtkWidget *main_vbox;
664         ModestMsgEditWindowPrivate *priv;
665         GtkActionGroup *action_group;
666         ModestWindowPrivate *parent_priv;
667         GdkPixbuf *window_icon = NULL;
668         GError *error = NULL;
669
670         GtkSizeGroup *title_size_group;
671         GtkSizeGroup *value_size_group;
672         GtkWidget *subject_box;
673         GtkWidget *attachment_icon;
674         GtkWidget *window_box;
675 #if (GTK_MINOR_VERSION >= 10)
676         GdkAtom deserialize_type;
677 #endif
678
679         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
680         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
681
682         parent_priv->ui_manager = gtk_ui_manager_new();
683         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
684         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
685
686         /* Add common actions */
687         gtk_action_group_add_actions (action_group,
688                                       modest_msg_edit_action_entries,
689                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
690                                       obj);
691         gtk_action_group_add_toggle_actions (action_group,
692                                              modest_msg_edit_toggle_action_entries,
693                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
694                                              obj);
695         gtk_action_group_add_radio_actions (action_group,
696                                             modest_msg_edit_alignment_radio_action_entries,
697                                             G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries),
698                                             GTK_JUSTIFY_LEFT,
699                                             G_CALLBACK (modest_ui_actions_on_change_justify),
700                                             obj);
701         gtk_action_group_add_radio_actions (action_group,
702                                             modest_msg_edit_priority_action_entries,
703                                             G_N_ELEMENTS (modest_msg_edit_priority_action_entries),
704                                             0,
705                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority),
706                                             obj);
707         gtk_action_group_add_radio_actions (action_group,
708                                             modest_msg_edit_file_format_action_entries,
709                                             G_N_ELEMENTS (modest_msg_edit_file_format_action_entries),
710                                             modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL),
711                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format),
712                                             obj);
713         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
714         g_object_unref (action_group);
715
716         /* Load the UI definition */
717         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml",
718                                          &error);
719         if (error != NULL) {
720                 g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message);
721                 g_clear_error (&error);
722         }
723
724         /* Add accelerators */
725         gtk_window_add_accel_group (GTK_WINDOW (obj), 
726                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
727
728         parent_priv->menubar = NULL;
729
730         title_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
731         value_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
732
733         /* Note: This ModestPairList* must exist for as long as the picker
734          * that uses it, because the ModestSelectorPicker uses the ID opaquely, 
735          * so it can't know how to manage its memory. */ 
736         priv->from_field    = modest_selector_picker_new (MODEST_EDITABLE_SIZE,
737                                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
738                                                           NULL, g_str_equal);
739         modest_maemo_utils_set_hbutton_layout (title_size_group, value_size_group, 
740                                                _("mail_va_from"), priv->from_field);
741
742         priv->to_field      = modest_recpt_editor_new ();
743         priv->cc_field      = modest_recpt_editor_new ();
744         priv->bcc_field     = modest_recpt_editor_new ();
745         subject_box = gtk_hbox_new (FALSE, 0);
746         priv->priority_icon = gtk_image_new ();
747         gtk_box_pack_start (GTK_BOX (subject_box), priv->priority_icon, FALSE, FALSE, 0);
748         priv->subject_field = hildon_entry_new (MODEST_EDITABLE_SIZE);
749         gtk_entry_set_max_length (GTK_ENTRY (priv->subject_field) ,SUBJECT_MAX_LENGTH);
750         g_object_set (G_OBJECT (priv->subject_field), "truncate-multiline", TRUE, NULL);
751         hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->subject_field), 
752                                          HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
753         gtk_box_pack_start (GTK_BOX (subject_box), priv->subject_field, TRUE, TRUE, 0);
754         priv->add_attachment_button = gtk_button_new ();
755         GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (priv->add_attachment_button), GTK_CAN_FOCUS);
756         gtk_button_set_relief (GTK_BUTTON (priv->add_attachment_button), GTK_RELIEF_NONE);
757         gtk_button_set_focus_on_click (GTK_BUTTON (priv->add_attachment_button), FALSE);
758         gtk_button_set_alignment (GTK_BUTTON (priv->add_attachment_button), 1.0, 0.5);
759         attachment_icon = gtk_image_new_from_icon_name (MODEST_HEADER_ICON_ATTACH, HILDON_ICON_SIZE_FINGER);
760         gtk_container_add (GTK_CONTAINER (priv->add_attachment_button), attachment_icon);
761         gtk_box_pack_start (GTK_BOX (subject_box), priv->add_attachment_button, FALSE, FALSE, 0);
762         priv->attachments_view = modest_attachments_view_new (NULL);
763         
764         priv->header_box = gtk_vbox_new (FALSE, 0);
765         
766         to_caption = modest_maemo_utils_create_captioned_with_size_type 
767                 (title_size_group, value_size_group,
768                  _("mail_va_to"), priv->to_field,
769                  HILDON_SIZE_AUTO_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
770         priv->cc_caption = modest_maemo_utils_create_captioned_with_size_type 
771                 (title_size_group, value_size_group,
772                  _("mail_va_cc"), priv->cc_field,
773                  HILDON_SIZE_AUTO_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
774         priv->bcc_caption = modest_maemo_utils_create_captioned_with_size_type
775                 (title_size_group, value_size_group,
776                  _("mail_va_hotfix1"), priv->bcc_field,
777                  HILDON_SIZE_AUTO_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
778         subject_caption = modest_maemo_utils_create_captioned (title_size_group, value_size_group,
779                                                                _("mail_va_subject"), subject_box);
780         priv->attachments_caption = modest_maemo_utils_create_captioned_with_size_type (title_size_group, value_size_group,
781                                                                                         _("mail_va_attachment"), 
782                                                                                         priv->attachments_view,
783                                                                                         HILDON_SIZE_AUTO_WIDTH |
784                                                                                         HILDON_SIZE_AUTO_HEIGHT);
785         /* modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->to_field), value_size_group); */
786         /* modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->cc_field), value_size_group); */
787         /* modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->bcc_field), value_size_group); */
788         g_object_unref (title_size_group);
789         g_object_unref (value_size_group);
790
791         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->from_field, FALSE, FALSE, 0);
792         gtk_box_pack_start (GTK_BOX (priv->header_box), to_caption, FALSE, FALSE, 0);
793         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->cc_caption, FALSE, FALSE, 0);
794         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->bcc_caption, FALSE, FALSE, 0);
795         gtk_box_pack_start (GTK_BOX (priv->header_box), subject_caption, FALSE, FALSE, 0);
796         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->attachments_caption, FALSE, FALSE, 0);
797         gtk_widget_set_no_show_all (priv->attachments_caption, TRUE);
798
799         init_wp_text_view_style ();
800
801         priv->msg_body = wp_text_view_new ();
802         
803
804         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->msg_body), GTK_WRAP_WORD_CHAR);
805         priv->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
806         g_object_set (priv->text_buffer, "font_scale", DEFAULT_FONT_SCALE, NULL);
807         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
808 #if (GTK_MINOR_VERSION >= 10)
809         gtk_text_buffer_register_serialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), NULL);
810         deserialize_type = gtk_text_buffer_register_deserialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), 
811                                                                        NULL);
812         gtk_text_buffer_deserialize_set_can_create_tags (GTK_TEXT_BUFFER (priv->text_buffer), 
813                                                          deserialize_type, TRUE);
814 #endif
815         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
816
817         priv->find_toolbar = hildon_find_toolbar_new (NULL);
818         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
819
820 /*      g_signal_connect (G_OBJECT (obj), "key_pressed", G_CALLBACK (on_key_pressed), NULL) */
821
822         priv->pannable = hildon_pannable_area_new ();
823         
824         main_vbox = gtk_vbox_new  (FALSE, DEFAULT_MAIN_VBOX_SPACING);
825
826         gtk_box_pack_start (GTK_BOX(main_vbox), priv->header_box, FALSE, FALSE, 0);
827         priv->frame = gtk_frame_new (NULL);
828         gtk_box_pack_start (GTK_BOX(main_vbox), priv->frame, TRUE, TRUE, 0);
829
830         hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (priv->pannable), main_vbox);
831         gtk_widget_show_all (GTK_WIDGET(priv->pannable));
832         
833         window_box = gtk_vbox_new (FALSE, 0);
834         gtk_container_add (GTK_CONTAINER(obj), window_box);
835
836         gtk_box_pack_start (GTK_BOX (window_box), priv->pannable, TRUE, TRUE, 0);
837
838         gtk_container_add (GTK_CONTAINER (priv->frame), priv->msg_body);
839
840         /* Set window icon */
841         window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON, MODEST_ICON_SIZE_BIG); 
842         if (window_icon) {
843                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
844                 g_object_unref (window_icon);
845         }       
846 }
847         
848 static void
849 modest_msg_edit_window_disconnect_signals (ModestWindow *window)
850 {
851         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
852
853         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
854             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
855                                            priv->clipboard_change_handler_id))
856                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
857                                              priv->clipboard_change_handler_id);
858         if (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD) &&
859             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
860                                            priv->default_clipboard_change_handler_id))
861                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
862                                              priv->default_clipboard_change_handler_id);
863
864         if (priv->account_removed_handler_id && 
865             g_signal_handler_is_connected (modest_runtime_get_account_store (), 
866                                            priv->account_removed_handler_id))
867                 g_signal_handler_disconnect(modest_runtime_get_account_store (), 
868                                            priv->account_removed_handler_id);
869 }
870
871 static void
872 modest_msg_edit_window_finalize (GObject *obj)
873 {
874         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
875         
876         /* Sanity check: shouldn't be needed, the window mgr should
877            call this function before */
878         modest_msg_edit_window_disconnect_signals (MODEST_WINDOW (obj));
879
880         if (priv->font_dialog != NULL) {
881                 gtk_dialog_response (GTK_DIALOG (priv->font_dialog), GTK_RESPONSE_NONE);
882         }
883
884         if (priv->clipboard_text != NULL) {
885                 g_free (priv->clipboard_text);
886                 priv->clipboard_text = NULL;
887         }
888         
889         if (priv->draft_msg != NULL) {
890                 TnyHeader *header = tny_msg_get_header (priv->draft_msg);
891                 if (TNY_IS_HEADER (header)) {
892                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
893                         modest_window_mgr_unregister_header (mgr, header);
894                 }
895                 g_object_unref (priv->draft_msg);
896                 priv->draft_msg = NULL;
897         }
898         if (priv->outbox_msg != NULL) {
899                 TnyHeader *header = tny_msg_get_header (priv->outbox_msg);
900                 if (TNY_IS_HEADER (header)) {
901                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
902                         modest_window_mgr_unregister_header (mgr, header);
903                 }
904                 g_object_unref (priv->outbox_msg);
905                 priv->outbox_msg = NULL;
906         }
907         if (priv->scroll_drag_timeout_id > 0) {
908                 g_source_remove (priv->scroll_drag_timeout_id);
909                 priv->scroll_drag_timeout_id = 0;
910         }
911         if (priv->clipboard_owner_idle > 0) {
912                 g_source_remove (priv->clipboard_owner_idle);
913                 priv->clipboard_owner_idle = 0;
914         }
915         if (priv->original_account_name)
916                 g_free (priv->original_account_name);
917         g_free (priv->msg_uid);
918         g_free (priv->last_search);
919         g_slist_free (priv->font_items_group);
920         g_slist_free (priv->size_items_group);
921         g_object_unref (priv->attachments);
922         g_object_unref (priv->images);
923
924         /* This had to stay alive for as long as the picker that used it: */
925         modest_pair_list_free (priv->from_field_protos);
926         
927         G_OBJECT_CLASS(parent_class)->finalize (obj);
928 }
929
930 static GdkPixbuf *
931 pixbuf_from_stream (TnyStream *stream, const gchar *mime_type, guint64 *stream_size)
932 {
933         GdkPixbufLoader *loader;
934         GdkPixbuf *pixbuf;
935         guint64 size;
936         
937         size = 0;
938
939         loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL);
940
941         if (loader == NULL) {
942                 if (stream_size)
943                         *stream_size = 0;
944                 return NULL;
945         }
946
947         tny_stream_reset (TNY_STREAM (stream));
948         while (!tny_stream_is_eos (TNY_STREAM (stream))) {
949                 GError *error = NULL;
950                 unsigned char read_buffer[128];
951                 gint readed;
952                 readed = tny_stream_read (TNY_STREAM (stream), (char *) read_buffer, 128);
953                 size += readed;
954                 if (!gdk_pixbuf_loader_write (loader, read_buffer, readed, &error)) {
955                         if (error)
956                                 g_error_free (error);
957                         break;
958                 }
959         }
960
961         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
962         if (pixbuf) 
963                 g_object_ref (pixbuf);
964         gdk_pixbuf_loader_close (loader, NULL);
965         g_object_unref (loader);
966
967         if (!pixbuf)
968                 return NULL;
969
970         if (gdk_pixbuf_get_width (pixbuf) > IMAGE_MAX_WIDTH) {
971                 GdkPixbuf *new_pixbuf;
972                 gint new_height;
973                 new_height = (gdk_pixbuf_get_height (pixbuf) * IMAGE_MAX_WIDTH) /
974                         gdk_pixbuf_get_width (pixbuf);
975                 new_pixbuf = gdk_pixbuf_scale_simple (pixbuf, IMAGE_MAX_WIDTH, new_height, GDK_INTERP_BILINEAR);
976                 g_object_unref (pixbuf);
977                 pixbuf = new_pixbuf;
978         }
979
980         if (stream_size)
981                 *stream_size = size;
982
983         return pixbuf;
984 }
985
986 static void
987 replace_with_images (ModestMsgEditWindow *self, TnyList *attachments)
988 {
989         ModestMsgEditWindowPrivate *priv;
990         TnyIterator *iter;
991
992         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
993
994         for (iter = tny_list_create_iterator (attachments);
995              !tny_iterator_is_done (iter);
996              tny_iterator_next (iter)) {
997                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
998                 const gchar *cid = tny_mime_part_get_content_id (part);
999                 const gchar *mime_type = tny_mime_part_get_content_type (part);
1000                 if ((cid != NULL)&&(mime_type != NULL)) {
1001                         guint64 stream_size;
1002                         TnyStream *stream = tny_mime_part_get_decoded_stream (part);
1003                         GdkPixbuf *pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
1004
1005
1006                         g_object_unref (stream);
1007
1008                         if (pixbuf != NULL) {
1009                                 priv->images_count ++;
1010                                 priv->images_size += stream_size;
1011                                 wp_text_buffer_replace_image (WP_TEXT_BUFFER (priv->text_buffer), cid, pixbuf);
1012                                 g_object_unref (pixbuf);
1013                         }
1014                 }
1015                 g_object_unref (part);
1016         }
1017         g_object_unref (iter);
1018 }
1019
1020 static void
1021 get_related_images (ModestMsgEditWindow *self, TnyMsg *msg)
1022 {
1023         TnyMimePart *parent = NULL;
1024         const gchar *content_type = NULL;
1025         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1026
1027         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
1028         
1029         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1030                 parent = g_object_ref (msg);
1031         } else if (content_type && !g_strcasecmp (content_type, "multipart/mixed")) {
1032                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1033                 TnyIterator *iter;
1034
1035                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
1036                 iter = tny_list_create_iterator (parts);
1037                 while (!tny_iterator_is_done (iter)) {
1038                         TnyMimePart *part;
1039                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1040                         content_type = tny_mime_part_get_content_type (part);
1041                         if (content_type && !g_strcasecmp (content_type, "multipart/related")) {
1042                                 parent = part;
1043                                 break;
1044                         } else {
1045                                 g_object_unref (part);
1046                         }
1047                         tny_iterator_next (iter);
1048                 }
1049                 g_object_unref (iter);
1050                 g_object_unref (parts);
1051         }
1052
1053         if (parent != NULL) {
1054                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1055                 TnyIterator *iter;
1056
1057                 tny_mime_part_get_parts (TNY_MIME_PART (parent), parts);
1058                 iter = tny_list_create_iterator (parts);
1059                 while (!tny_iterator_is_done (iter)) {
1060                         TnyMimePart *part;
1061                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1062                         content_type = tny_mime_part_get_content_type (part);
1063                         if (content_type && g_str_has_prefix (content_type, "image/")) {
1064                                 tny_list_prepend (priv->images, (GObject *) part);
1065                         } 
1066                         g_object_unref (part);
1067                         tny_iterator_next (iter);
1068                 }
1069                 g_object_unref (iter);
1070                 g_object_unref (parts);
1071                 g_object_unref (parent);
1072         }
1073 }
1074
1075 static void
1076 update_next_cid (ModestMsgEditWindow *self, TnyList *attachments)
1077 {
1078         TnyIterator *iter;
1079         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1080
1081         for (iter = tny_list_create_iterator (attachments) ; 
1082              !tny_iterator_is_done (iter);
1083              tny_iterator_next (iter)) {
1084                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1085                 const gchar *cid = tny_mime_part_get_content_id (part);
1086                 if (cid != NULL) {
1087                         char *invalid = NULL;
1088                         gint int_cid = strtol (cid, &invalid, 10);
1089                         if ((invalid != NULL) && (*invalid == '\0') && (int_cid >= priv->next_cid)) {
1090                                 priv->next_cid = int_cid + 1;
1091                         }
1092                 }
1093                 g_object_unref (part);
1094         }
1095         g_object_unref (iter);
1096 }
1097
1098 static void
1099 set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich)
1100 {
1101         TnyHeader *header;
1102         gchar *to, *cc, *bcc, *subject;
1103         gchar *body;
1104         ModestMsgEditWindowPrivate *priv;
1105         ModestWindowPrivate *parent_priv;
1106         GtkTextIter iter;
1107         TnyHeaderFlags priority_flags;
1108         TnyFolder *msg_folder;
1109         gboolean is_html = FALSE;
1110         GtkAction *action;
1111         gboolean field_view_set;
1112         
1113         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1114         g_return_if_fail (TNY_IS_MSG (msg));
1115
1116         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1117         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
1118
1119         header = tny_msg_get_header (msg);
1120         to      = tny_header_dup_to (header);
1121         cc      = tny_header_dup_cc (header);
1122         bcc     = tny_header_dup_bcc (header);
1123         subject = tny_header_dup_subject (header);
1124         priority_flags = tny_header_get_priority (header);
1125
1126         if (to)
1127                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field),  to);
1128
1129         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewCcFieldMenu");
1130         field_view_set = TRUE;
1131         if (cc) {
1132                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field),  cc);
1133                 gtk_widget_set_no_show_all (priv->cc_caption, FALSE);
1134                 gtk_widget_show (priv->cc_caption);
1135         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) {
1136                 gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
1137                 gtk_widget_hide (priv->cc_caption);
1138                 field_view_set = FALSE;
1139         }
1140         if (action)
1141                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), field_view_set);
1142
1143         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewBccFieldMenu");
1144         field_view_set = TRUE;
1145         if (bcc) {
1146                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), bcc);
1147                 gtk_widget_set_no_show_all (priv->bcc_caption, FALSE);
1148                 gtk_widget_show (priv->bcc_caption);
1149         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) {
1150                 gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
1151                 gtk_widget_hide (priv->bcc_caption);
1152                 field_view_set = FALSE;
1153         }
1154         if (action)
1155                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), field_view_set);
1156
1157
1158         if (subject)
1159                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
1160         modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self),
1161                                                    priority_flags);
1162
1163         update_window_title (self);
1164
1165         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1166         body = modest_tny_msg_get_body (msg, TRUE, &is_html);
1167
1168         if ((body == NULL)||(body[0] == '\0')) {
1169                 g_free (body);
1170                 body = modest_text_utils_convert_to_html ("");
1171                 is_html = FALSE;
1172         }
1173         wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1174         wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer),
1175                                             (gchar *) body,
1176                                             strlen (body));
1177         wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer));
1178         g_free (body);
1179
1180         /* Add attachments to the view */
1181         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg);
1182         priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1183         if (tny_list_get_length (priv->attachments) == 0) {
1184                 gtk_widget_hide (priv->attachments_caption);
1185         } else {
1186                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
1187                 gtk_widget_show_all (priv->attachments_caption);
1188         }
1189         get_related_images (self, msg);
1190         update_next_cid (self, priv->attachments);
1191         update_next_cid (self, priv->images);
1192         replace_with_images (self, priv->images);
1193
1194         if (preserve_is_rich && !is_html) {
1195                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1196         /* Get the default format required from configuration */
1197         } else if (!preserve_is_rich && !modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) {
1198                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1199         }
1200
1201         /* Set the default focus depending on having already a To: field or not */
1202         if ((!to)||(*to == '\0')) {
1203                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
1204         } else {
1205                 gtk_widget_grab_focus (priv->msg_body);
1206         }
1207
1208         DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer));
1209
1210         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1211         gtk_text_buffer_place_cursor (priv->text_buffer, &iter);
1212
1213         modest_msg_edit_window_set_modified (self, FALSE);
1214
1215         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1216         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1217         text_buffer_can_undo (priv->text_buffer, FALSE, self);
1218         text_buffer_can_redo (priv->text_buffer, FALSE, self);
1219
1220         if (priv->msg_uid) {
1221                 g_free (priv->msg_uid);
1222                 priv->msg_uid = NULL;
1223         }
1224
1225         /* we should set a reference to the incoming message if it is a draft */
1226         msg_folder = tny_msg_get_folder (msg);
1227         if (msg_folder) {               
1228                 if (modest_tny_folder_is_local_folder (msg_folder)) {
1229                         TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder);
1230                         if (type == TNY_FOLDER_TYPE_INVALID)
1231                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1232                         
1233                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
1234                                 priv->draft_msg = g_object_ref(msg);
1235                         if (type == TNY_FOLDER_TYPE_OUTBOX)
1236                                 priv->outbox_msg = g_object_ref(msg);
1237                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1238                 }
1239                 g_object_unref (msg_folder);
1240         }
1241
1242         g_free (to);
1243         g_free (subject);
1244         g_free (cc);
1245         g_free (bcc);
1246 }
1247
1248 static void
1249 menu_tool_button_clicked_popup (GtkMenuToolButton *item,
1250                                 gpointer data)
1251 {
1252         GList *item_children, *node;
1253         GtkWidget *bin_child;
1254
1255         bin_child = gtk_bin_get_child (GTK_BIN(item));
1256
1257         item_children = gtk_container_get_children (GTK_CONTAINER (bin_child));
1258         
1259         for (node = item_children; node != NULL; node = g_list_next (node)) {
1260                 if (GTK_IS_TOGGLE_BUTTON (node->data)) {
1261                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE);
1262                 }
1263         }
1264         g_list_free (item_children);
1265 }
1266
1267 static void
1268 menu_tool_button_dont_expand (GtkMenuToolButton *item)
1269 {
1270         GtkWidget *box;
1271         GList *item_children, *node;
1272
1273         box = gtk_bin_get_child (GTK_BIN (item));
1274         gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
1275         item_children = gtk_container_get_children (GTK_CONTAINER (box));
1276         
1277         for (node = item_children; node != NULL; node = g_list_next (node)) {
1278                 gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START);
1279                 if (GTK_IS_TOGGLE_BUTTON (node->data))
1280                         gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5);
1281                 else if (GTK_IS_BUTTON (node->data))
1282                         gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5);
1283         }
1284         g_list_free (item_children);
1285 }
1286
1287
1288 static void
1289 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1290 {
1291         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1292         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1293         GtkWidget *placeholder;
1294         GtkWidget *tool_item;
1295         gint insert_index;
1296         gchar size_text[5];
1297         gint size_index;
1298         gint font_index;
1299         GtkWidget *sizes_menu;
1300         GtkWidget *fonts_menu;
1301         GSList *radio_group = NULL;
1302         GSList *node = NULL;
1303         gchar *markup;
1304
1305         /* Toolbar */
1306         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1307         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1308
1309         /* Font color placeholder */
1310         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1311         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1312
1313         /* font color */
1314         priv->font_color_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1315         priv->font_color_button = hildon_color_button_new ();
1316         GTK_WIDGET_UNSET_FLAGS (tool_item, GTK_CAN_FOCUS);
1317         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1318         gtk_container_add (GTK_CONTAINER (priv->font_color_toolitem), priv->font_color_button);
1319         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1320         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1321         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->font_color_toolitem), insert_index);
1322         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1323                                   "notify::color", 
1324                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1325                                   window);
1326
1327         /* Font size and face placeholder */
1328         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1329         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1330         /* font_size */
1331         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1332         priv->size_tool_button_label = gtk_label_new (NULL);
1333         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1334         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1335                               size_text,"</span>", NULL);
1336         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1337         g_free (markup);
1338         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label);
1339         sizes_menu = gtk_menu_new ();
1340         priv->size_items_group = NULL;
1341         radio_group = NULL;
1342         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1343                 GtkWidget *size_menu_item;
1344
1345                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1346                 size_menu_item = gtk_radio_menu_item_new_with_label (radio_group, size_text);
1347                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (size_menu_item));
1348                 gtk_menu_shell_append (GTK_MENU_SHELL (sizes_menu), size_menu_item);
1349                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (size_menu_item), (wp_font_size[size_index] == 12));
1350                 gtk_widget_show (size_menu_item);
1351
1352                 priv->size_items_group = g_slist_prepend (priv->size_items_group, size_menu_item);
1353                         
1354         }
1355
1356         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1357                 GtkWidget *item = (GtkWidget *) node->data;
1358                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_size_change),
1359                                   window);
1360         }
1361
1362         priv->size_items_group = g_slist_reverse (priv->size_items_group);
1363         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), sizes_menu);
1364         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1365         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1366         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1367         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1368         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1369         priv->font_size_toolitem = tool_item;
1370
1371         /* font face */
1372         tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL));
1373         priv->font_tool_button_label = gtk_label_new (NULL);
1374         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1375         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1376         g_free(markup);
1377         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label);
1378         fonts_menu = gtk_menu_new ();
1379         priv->font_items_group = NULL;
1380         radio_group = NULL;
1381         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1382                 GtkWidget *font_menu_item;
1383                 GtkWidget *child_label;
1384
1385                 font_menu_item = gtk_radio_menu_item_new_with_label (radio_group, "");
1386                 child_label = gtk_bin_get_child (GTK_BIN (font_menu_item));
1387                 markup = g_strconcat ("<span font_family='", wp_get_font_name (font_index),"'>", 
1388                                       wp_get_font_name (font_index), "</span>", NULL);
1389                 gtk_label_set_markup (GTK_LABEL (child_label), markup);
1390                 g_free (markup);
1391                 
1392                 radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (font_menu_item));
1393                 gtk_menu_shell_append (GTK_MENU_SHELL (fonts_menu), font_menu_item);
1394                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (font_menu_item), (font_index == DEFAULT_FONT));
1395                 gtk_widget_show (font_menu_item);
1396
1397                 priv->font_items_group = g_slist_prepend (priv->font_items_group, font_menu_item);
1398                         
1399         }
1400         for (node = radio_group; node != NULL; node = g_slist_next (node)) {
1401                 GtkWidget *item = (GtkWidget *) node->data;
1402                 g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_font_change),
1403                                   window);
1404         }
1405         priv->font_items_group = g_slist_reverse (priv->font_items_group);
1406         gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), fonts_menu);
1407         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL);
1408         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1409         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1410         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1411         menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item));
1412         priv->font_face_toolitem = tool_item;
1413
1414         /* Set expand and homogeneous for remaining items */
1415         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend");
1416         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1417         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1418         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1419         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1420         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1421         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1422         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1423         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1424
1425         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1426            will not show the tool items added to the placeholders) */
1427         gtk_widget_show_all (parent_priv->toolbar);
1428
1429         /* Set the no show all *after* showing all items. We do not
1430            want the toolbar to be shown with a show all because it
1431            could go agains the gconf setting regarding showing or not
1432            the toolbar of the editor window */
1433         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1434 }
1435
1436
1437
1438 ModestWindow*
1439 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich)
1440 {
1441         GObject *obj;
1442         ModestWindowPrivate *parent_priv;
1443         ModestMsgEditWindowPrivate *priv;
1444         ModestDimmingRulesGroup *menu_rules_group = NULL;
1445         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1446         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1447         ModestWindowMgr *mgr = NULL;
1448
1449         g_return_val_if_fail (msg, NULL);
1450         g_return_val_if_fail (account_name, NULL);
1451
1452         mgr = modest_runtime_get_window_mgr ();
1453         
1454         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1455
1456         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1457         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1458
1459         /* Menubar. Update the state of some toggles */
1460         parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
1461         hildon_window_set_menu (HILDON_WINDOW (obj), GTK_MENU (parent_priv->menubar));
1462         priv->from_field_protos = get_transports ();
1463         modest_selector_picker_set_pair_list (MODEST_SELECTOR_PICKER (priv->from_field), priv->from_field_protos);
1464         modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field), (gpointer) account_name);
1465         hildon_button_set_title (HILDON_BUTTON (priv->from_field),
1466                                  _("mail_va_from"));
1467         hildon_button_set_value (HILDON_BUTTON (priv->from_field), 
1468                                  hildon_touch_selector_get_current_text 
1469                                  (HILDON_TOUCH_SELECTOR (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (priv->from_field)))));
1470         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1471         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1472
1473         /* Init window */
1474         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1475
1476         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1477                 
1478         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1479
1480         priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL;
1481
1482         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new ();
1483         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
1484         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1485         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1486         /* Add common dimming rules */
1487         modest_dimming_rules_group_add_rules (menu_rules_group, 
1488                                               modest_msg_edit_window_menu_dimming_entries,
1489                                               G_N_ELEMENTS (modest_msg_edit_window_menu_dimming_entries),
1490                                               MODEST_WINDOW (obj));
1491         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1492                                               modest_msg_edit_window_toolbar_dimming_entries,
1493                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1494                                               MODEST_WINDOW (obj));
1495         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_toolitem,
1496                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1497                                                     MODEST_WINDOW (obj));
1498         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1499                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1500                                                     MODEST_WINDOW (obj));
1501         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1502                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1503                                                     MODEST_WINDOW (obj));
1504         /* Insert dimming rules group for this window */
1505         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
1506         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1507         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1508         /* Checks the dimming rules */
1509         g_object_unref (menu_rules_group);
1510         g_object_unref (toolbar_rules_group);
1511         g_object_unref (clipboard_rules_group);
1512         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1513
1514         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1515
1516         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1517         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1518         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1519         priv->update_caption_visibility = TRUE;
1520
1521         modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE);
1522
1523         /* Track account-removed signal, this window should be closed
1524            in the case we're creating a mail associated to the account
1525            that is deleted */
1526         priv->account_removed_handler_id = 
1527                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1528                                   "account_removed",
1529                                   G_CALLBACK(on_account_removed),
1530                                   obj);
1531
1532         modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj));
1533
1534         return (ModestWindow*) obj;
1535 }
1536
1537 static gint
1538 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1539 {
1540         GString **string_buffer = (GString **) user_data;
1541
1542         *string_buffer = g_string_append (*string_buffer, buffer);
1543    
1544         return 0;
1545 }
1546
1547 /**
1548  * @result: A new string which should be freed with g_free().
1549  */
1550 static gchar *
1551 get_formatted_data (ModestMsgEditWindow *edit_window)
1552 {
1553         ModestMsgEditWindowPrivate *priv;
1554         GString *string_buffer = g_string_new ("");
1555         
1556         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1557
1558         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1559
1560         modest_text_utils_hyperlinkify (string_buffer);
1561
1562         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1563
1564         return g_string_free (string_buffer, FALSE);
1565                                                                         
1566 }
1567
1568 MsgData * 
1569 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1570 {
1571         MsgData *data;
1572         const gchar *account_name;
1573         ModestMsgEditWindowPrivate *priv;
1574         TnyIterator *att_iter;
1575         
1576         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1577
1578         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1579                                                                         
1580         account_name = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1581         g_return_val_if_fail (account_name, NULL);
1582         
1583         
1584         /* don't free these (except from) */
1585         data = g_slice_new0 (MsgData);
1586         data->from    =  modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(),
1587                                                              account_name);
1588         data->account_name = g_strdup (account_name);
1589         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1590         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1591         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1592         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1593         if (priv->draft_msg) {
1594                 data->draft_msg = g_object_ref (priv->draft_msg);
1595         } else if (priv->outbox_msg) {
1596                 data->draft_msg = g_object_ref (priv->outbox_msg);
1597         } else {
1598                 data->draft_msg = NULL;
1599         }
1600
1601         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1602         GtkTextIter b, e;
1603         gtk_text_buffer_get_bounds (buf, &b, &e);
1604         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1605
1606         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1607                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1608         else
1609                 data->html_body = NULL;
1610
1611         /* deep-copy the data */
1612         att_iter = tny_list_create_iterator (priv->attachments);
1613         data->attachments = NULL;
1614         while (!tny_iterator_is_done (att_iter)) {
1615                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1616                 if (!(TNY_IS_MIME_PART(part))) {
1617                         g_warning ("strange data in attachment list");
1618                         g_object_unref (part);
1619                         tny_iterator_next (att_iter);
1620                         continue;
1621                 }
1622                 data->attachments = g_list_append (data->attachments,
1623                                                    part);
1624                 tny_iterator_next (att_iter);
1625         }
1626         g_object_unref (att_iter);
1627
1628         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1629         att_iter = tny_list_create_iterator (priv->images);
1630         data->images = NULL;
1631         while (!tny_iterator_is_done (att_iter)) {
1632                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1633                 const gchar *cid;
1634                 if (!(TNY_IS_MIME_PART(part))) {
1635                         g_warning ("strange data in attachment list");
1636                         g_object_unref (part);
1637                         tny_iterator_next (att_iter);
1638                         continue;
1639                 }
1640                 cid = tny_mime_part_get_content_id (part);
1641                 if (cid) {                      
1642                         gchar *image_tag_id;
1643                         GtkTextTag *image_tag;
1644                         GtkTextIter iter;
1645                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1646                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1647                         g_free (image_tag_id);
1648                         
1649                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1650                         if (image_tag && 
1651                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1652                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1653                                 data->images = g_list_append (data->images,
1654                                                               g_object_ref (part));
1655                 }
1656                 g_object_unref (part);
1657                 tny_iterator_next (att_iter);
1658         }
1659         g_object_unref (att_iter);
1660         
1661         data->priority_flags = priv->priority_flags;
1662
1663         return data;
1664 }
1665
1666
1667 static void
1668 unref_gobject (GObject *obj, gpointer data)
1669 {
1670         if (!G_IS_OBJECT(obj))
1671                 return;
1672         g_object_unref (obj);
1673 }
1674
1675 void 
1676 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1677                                                       MsgData *data)
1678 {
1679         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1680
1681         if (!data)
1682                 return;
1683
1684         g_free (data->to);
1685         g_free (data->cc);
1686         g_free (data->bcc);
1687         g_free (data->from);
1688         g_free (data->subject);
1689         g_free (data->plain_body);
1690         g_free (data->html_body);
1691         g_free (data->account_name);
1692         
1693         if (data->draft_msg != NULL) {
1694                 g_object_unref (data->draft_msg);
1695                 data->draft_msg = NULL;
1696         }
1697         
1698         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1699         g_list_free (data->attachments);
1700         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1701         g_list_free (data->images);
1702         
1703         g_slice_free (MsgData, data);
1704 }
1705
1706 void                    
1707 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1708                                        gint *parts_count,
1709                                        guint64 *parts_size)
1710 {
1711         ModestMsgEditWindowPrivate *priv;
1712
1713         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1714
1715         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1716
1717         /* TODO: add images */
1718         *parts_size += priv->images_size;
1719         *parts_count += priv->images_count;
1720
1721 }
1722
1723 ModestMsgEditFormat
1724 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1725 {
1726         gboolean rich_text;
1727         ModestMsgEditWindowPrivate *priv = NULL;
1728         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1729
1730         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1731
1732         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1733         if (rich_text)
1734                 return MODEST_MSG_EDIT_FORMAT_HTML;
1735         else
1736                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1737 }
1738
1739 void
1740 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1741                                    ModestMsgEditFormat format)
1742 {
1743         ModestMsgEditWindowPrivate *priv;
1744
1745         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1746         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1747
1748         switch (format) {
1749         case MODEST_MSG_EDIT_FORMAT_HTML:
1750                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1751                 break;
1752         case MODEST_MSG_EDIT_FORMAT_TEXT:
1753                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1754                 break;
1755         default:
1756                 g_return_if_reached ();
1757         }
1758 }
1759
1760 ModestMsgEditFormatState *
1761 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1762 {
1763         ModestMsgEditFormatState *format_state = NULL;
1764         ModestMsgEditWindowPrivate *priv;
1765         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1766
1767         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
1768         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1769
1770         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
1771
1772         format_state = g_new0 (ModestMsgEditFormatState, 1);
1773         format_state->bold = buffer_format->bold&0x1;
1774         format_state->italics = buffer_format->italic&0x1;
1775         format_state->bullet = buffer_format->bullet&0x1;
1776         format_state->color = buffer_format->color;
1777         format_state->font_size = buffer_format->font_size;
1778         format_state->font_family = wp_get_font_name (buffer_format->font);
1779         format_state->justification = buffer_format->justification;
1780         g_free (buffer_format);
1781
1782         return format_state;
1783  
1784 }
1785
1786 void
1787 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
1788                                          const ModestMsgEditFormatState *format_state)
1789 {
1790         ModestMsgEditWindowPrivate *priv;
1791         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1792         WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1);
1793         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1794         g_return_if_fail (format_state != NULL);
1795
1796         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1797         gtk_widget_grab_focus (priv->msg_body);
1798         buffer_format->bold = (format_state->bold != FALSE);
1799         buffer_format->italic = (format_state->italics != FALSE);
1800         buffer_format->color = format_state->color;
1801         buffer_format->font_size = format_state->font_size;
1802         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
1803         buffer_format->justification = format_state->justification;
1804         buffer_format->bullet = format_state->bullet;
1805
1806         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
1807
1808         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
1809         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
1810         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
1811         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
1812         buffer_format->cs.font = (buffer_format->font != current_format->font);
1813         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
1814         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
1815
1816         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
1817         if (buffer_format->cs.bold) {
1818                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
1819                                               GINT_TO_POINTER (buffer_format->bold&0x1));
1820         }
1821         if (buffer_format->cs.italic) {
1822                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
1823                                               GINT_TO_POINTER (buffer_format->italic&0x1));
1824         }
1825         if (buffer_format->cs.color) {
1826                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1827                                               GINT_TO_POINTER (&(buffer_format->color)));
1828         }
1829         if (buffer_format->cs.font_size) {
1830                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
1831                                               GINT_TO_POINTER (buffer_format->font_size));
1832         }
1833         if (buffer_format->cs.justification) {
1834                 switch (buffer_format->justification) {
1835                 case GTK_JUSTIFY_LEFT:
1836                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
1837                                                       GINT_TO_POINTER(TRUE));
1838                         break;
1839                 case GTK_JUSTIFY_CENTER:
1840                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
1841                                                       GINT_TO_POINTER(TRUE));
1842                         break;
1843                 case GTK_JUSTIFY_RIGHT:
1844                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
1845                                                       GINT_TO_POINTER(TRUE));
1846                         break;
1847                 default:
1848                         break;
1849                 }
1850                         
1851         }
1852         if (buffer_format->cs.font) {
1853                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
1854                                               GINT_TO_POINTER (buffer_format->font));
1855         }
1856         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
1857         if (buffer_format->cs.bullet) {
1858                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
1859                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
1860         }
1861 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
1862         
1863         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
1864         
1865         g_free (buffer_format);
1866         g_free (current_format);
1867
1868         /* Check dimming rules */
1869         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1870         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1871 }
1872
1873 static void
1874 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
1875 {
1876         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1877         GtkAction *action;
1878         ModestWindowPrivate *parent_priv;
1879         ModestMsgEditWindowPrivate *priv;
1880         GtkWidget *new_size_menuitem;
1881         GtkWidget *new_font_menuitem;
1882         
1883         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1884         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1885
1886         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
1887                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu");
1888                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1889                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1890         } else {
1891                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatPlainTextMenu");
1892                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
1893                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
1894         }
1895
1896         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1897
1898         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1899         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
1900
1901         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1902         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
1903
1904 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
1905 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
1906
1907         action = NULL;
1908         switch (buffer_format->justification)
1909         {
1910         case GTK_JUSTIFY_LEFT:
1911                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu");
1912                 break;
1913         case GTK_JUSTIFY_CENTER:
1914                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu");
1915                 break;
1916         case GTK_JUSTIFY_RIGHT:
1917                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu");
1918                 break;
1919         default:
1920                 break;
1921         }
1922         
1923         if (action != NULL)
1924                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1925         
1926         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
1927                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
1928                                          window);
1929         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
1930         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
1931                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
1932                                            window);
1933
1934         new_size_menuitem = GTK_WIDGET ((g_slist_nth (priv->size_items_group, 
1935                                                       buffer_format->font_size))->data);
1936         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_size_menuitem))) {
1937                 GtkWidget *label;
1938                 gchar *markup;
1939
1940                 label = gtk_bin_get_child (GTK_BIN (new_size_menuitem));
1941                 markup = g_strconcat ("<span font_family='Sans'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
1942                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1943                 g_free (markup);
1944                 g_signal_handlers_block_by_func (G_OBJECT (new_size_menuitem),
1945                                                  G_CALLBACK (modest_msg_edit_window_size_change),
1946                                                  window);
1947                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_size_menuitem), TRUE);
1948                 g_signal_handlers_unblock_by_func (G_OBJECT (new_size_menuitem),
1949                                                    G_CALLBACK (modest_msg_edit_window_size_change),
1950                                                    window);
1951         }
1952
1953         new_font_menuitem = GTK_WIDGET ((g_slist_nth (priv->font_items_group, 
1954                                                       buffer_format->font))->data);
1955         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_font_menuitem))) {
1956                 GtkWidget *label;
1957                 gchar *markup;
1958
1959                 label = gtk_bin_get_child (GTK_BIN (new_font_menuitem));
1960                 markup = g_strconcat ("<span font_family='", gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
1961                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1962                 g_free (markup);
1963                 g_signal_handlers_block_by_func (G_OBJECT (new_font_menuitem),
1964                                                  G_CALLBACK (modest_msg_edit_window_font_change),
1965                                                  window);
1966                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_font_menuitem), TRUE);
1967                 g_signal_handlers_unblock_by_func (G_OBJECT (new_font_menuitem),
1968                                                    G_CALLBACK (modest_msg_edit_window_font_change),
1969                                                    window);
1970         }
1971
1972         g_free (buffer_format);
1973
1974 }
1975
1976 #ifdef MODEST_HILDON_VERSION_0
1977 void
1978 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
1979 {
1980         
1981         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
1982         ModestMsgEditWindowPrivate *priv;
1983         GtkWidget *dialog = NULL;
1984         gint response;
1985         GdkColor *new_color = NULL;
1986
1987         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1988         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
1989         
1990         dialog = hildon_color_selector_new (GTK_WINDOW (window));
1991         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color));
1992         g_free (buffer_format);
1993
1994         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
1995                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
1996                 if (new_color != NULL) {
1997                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
1998                                                       (gpointer) new_color);
1999                 }
2000         }
2001         gtk_widget_destroy (dialog);
2002 }
2003
2004
2005 void
2006 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2007 {
2008         
2009         ModestMsgEditWindowPrivate *priv;
2010         GtkWidget *dialog = NULL;
2011         gint response;
2012         GdkColor *old_color = NULL;
2013         const GdkColor *new_color = NULL;
2014         
2015         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2016         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2017         
2018         dialog = hildon_color_selector_new (GTK_WINDOW (window));
2019         hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color);
2020
2021         if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) {
2022                 new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog));
2023                 if (new_color != NULL)
2024                         wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color);
2025         }
2026         gtk_widget_destroy (dialog);
2027
2028 }
2029
2030 #else 
2031 void
2032 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2033 {
2034         
2035         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2036         ModestMsgEditWindowPrivate *priv;
2037         GtkWidget *dialog = NULL;
2038
2039         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2040         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2041                 
2042         dialog = hildon_color_chooser_new ();
2043         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2044         g_free (buffer_format);
2045
2046         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2047                 GdkColor col;
2048                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2049                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2050                                               (gpointer) &col);
2051         }
2052         gtk_widget_destroy (dialog);
2053 }
2054
2055
2056 void
2057 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2058 {
2059         
2060         ModestMsgEditWindowPrivate *priv;
2061         GtkWidget *dialog = NULL;
2062         GdkColor *old_color = NULL;
2063         
2064         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2065         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2066         
2067         dialog = hildon_color_chooser_new ();
2068         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2069
2070         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2071                 GdkColor col;
2072                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2073                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2074         }
2075         gtk_widget_destroy (dialog);
2076 }
2077
2078 #endif /*!MODEST_HILDON_VERSION_0*/
2079
2080
2081
2082 static TnyStream*
2083 create_stream_for_uri (const gchar* uri)
2084 {
2085         if (!uri)
2086                 return NULL;
2087                 
2088         TnyStream *result = NULL;
2089
2090         GnomeVFSHandle *handle = NULL;
2091         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2092         if (test == GNOME_VFS_OK) {
2093                 TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle));
2094                 /* Streams over OBEX (Bluetooth) are not seekable but
2095                  * we expect them to be (we might need to read them
2096                  * several times). So if this is a Bluetooth URI just
2097                  * read the whole file into memory (this is not a fast
2098                  * protocol so we can assume that these files are not
2099                  * going to be very big) */
2100                 if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)||
2101                     (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) {
2102                         TnyStream *memstream = tny_camel_mem_stream_new ();
2103                         tny_stream_write_to_stream (vfssstream, memstream);
2104                         g_object_unref (vfssstream);
2105                         result = memstream;
2106                 } else {
2107                         result = vfssstream;
2108                 }
2109         }
2110         
2111         return result;
2112 }
2113
2114 void
2115 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2116 {
2117         
2118         ModestMsgEditWindowPrivate *priv;
2119         GtkWidget *dialog = NULL;
2120         gint response = 0;
2121         GSList *uris = NULL;
2122         GSList *uri_node = NULL;
2123         
2124         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2125         
2126         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2127         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
2128         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2129
2130         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2131
2132         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2133                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2134
2135         response = gtk_dialog_run (GTK_DIALOG (dialog));
2136         switch (response) {
2137         case GTK_RESPONSE_OK:
2138                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2139                 break;
2140         default:
2141                 break;
2142         }
2143         gtk_widget_destroy (dialog);
2144
2145         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2146                 const gchar *uri;
2147                 GnomeVFSHandle *handle = NULL;
2148                 GnomeVFSResult result;
2149                 GtkTextIter position;
2150                 GtkTextMark *insert_mark;
2151
2152                 uri = (const gchar *) uri_node->data;
2153                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2154                 if (result == GNOME_VFS_OK) {
2155                         GdkPixbuf *pixbuf;
2156                         GnomeVFSFileInfo *info;
2157                         gchar *filename, *basename, *escaped_filename;
2158                         TnyMimePart *mime_part;
2159                         gchar *content_id;
2160                         const gchar *mime_type = NULL;
2161                         GnomeVFSURI *vfs_uri;
2162                         guint64 stream_size;
2163
2164                         gnome_vfs_close (handle);
2165                         vfs_uri = gnome_vfs_uri_new (uri);
2166
2167                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2168                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2169                         g_free (escaped_filename);
2170                         gnome_vfs_uri_unref (vfs_uri);
2171                         info = gnome_vfs_file_info_new ();
2172
2173                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2174                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2175                             == GNOME_VFS_OK)
2176                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2177
2178                         mime_part = tny_platform_factory_new_mime_part
2179                                 (modest_runtime_get_platform_factory ());
2180                                 
2181                         TnyStream *stream = create_stream_for_uri (uri);
2182
2183                         if (stream == NULL) {
2184
2185                                 modest_platform_information_banner (NULL, NULL, dgettext("hildon-fm", "sfil_ib_opening_not_allowed"));
2186                                 
2187                                 g_object_unref (mime_part);
2188                                 gnome_vfs_file_info_unref (info);
2189                                 continue;
2190                         }
2191
2192                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2193                         
2194                         content_id = g_strdup_printf ("%d", priv->next_cid);
2195                         tny_mime_part_set_content_id (mime_part, content_id);
2196                         g_free (content_id);
2197                         priv->next_cid++;
2198                         
2199                         basename = g_path_get_basename (filename);
2200                         tny_mime_part_set_filename (mime_part, basename);
2201                         g_free (basename);
2202
2203                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size);
2204                         
2205                         if (pixbuf != NULL) {
2206                                 priv->images_size += stream_size;
2207                                 priv->images_count ++;
2208                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2209                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2210                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf);
2211                                 g_object_unref (pixbuf);
2212                         } 
2213
2214                         tny_list_prepend (priv->images, (GObject *) mime_part);
2215                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2216                         g_free (filename);
2217                         g_object_unref (mime_part);
2218                         gnome_vfs_file_info_unref (info);
2219
2220                 }
2221         }
2222
2223
2224 }
2225
2226 void
2227 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2228 {       
2229         GtkWidget *dialog = NULL;
2230         gint response = 0;
2231         GSList *uris = NULL;
2232         GSList *uri_node;
2233         GnomeVFSFileSize total_size, allowed_size;
2234         ModestMsgEditWindowPrivate *priv;
2235         gint att_num;
2236         guint64 att_size;
2237
2238         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window));
2239                 
2240         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2241
2242         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2243                 return;
2244         
2245         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2246         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2247         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2248         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog), GTK_WINDOW (window));
2249
2250         response = gtk_dialog_run (GTK_DIALOG (dialog));
2251         switch (response) {
2252         case GTK_RESPONSE_OK:
2253                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2254                 break;
2255         default:
2256                 break;
2257         }
2258         gtk_widget_destroy (dialog);
2259
2260         /* allowed size is the maximum size - what's already there */
2261         modest_attachments_view_get_sizes (
2262                 MODEST_ATTACHMENTS_VIEW (priv->attachments_view), 
2263                 &att_num, &att_size);
2264         allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size;
2265
2266         total_size = 0;
2267         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2268
2269                 const gchar *uri = (const gchar *) uri_node->data;
2270                 
2271                 total_size += modest_msg_edit_window_attach_file_one 
2272                         (window, uri, allowed_size);
2273                 
2274                 if (total_size > allowed_size) {
2275                         g_warning ("%s: total size: %u", 
2276                                    __FUNCTION__, (unsigned int)total_size);
2277                         break;
2278                 }
2279
2280                 allowed_size -= total_size;
2281                 
2282
2283         }
2284         g_slist_foreach (uris, (GFunc) g_free, NULL);
2285         g_slist_free (uris);
2286 }
2287
2288
2289 GnomeVFSFileSize
2290 modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window,
2291                                         const gchar *uri, 
2292                                         GnomeVFSFileSize allowed_size)
2293
2294 {
2295         GnomeVFSHandle *handle = NULL;
2296         ModestMsgEditWindowPrivate *priv;
2297         GnomeVFSResult result;
2298         GnomeVFSFileSize size = 0;
2299         g_return_val_if_fail (window, 0);
2300         g_return_val_if_fail (uri, 0);
2301                 
2302         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2303         
2304         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2305         if (result == GNOME_VFS_OK) {
2306                 TnyMimePart *mime_part;
2307                 TnyStream *stream;
2308                 const gchar *mime_type = NULL;
2309                 gchar *basename;
2310                 gchar *escaped_filename;
2311                 gchar *filename;
2312                 gchar *content_id;
2313                 GnomeVFSFileInfo *info;
2314                 GnomeVFSURI *vfs_uri;
2315
2316                 gnome_vfs_close (handle);
2317                 vfs_uri = gnome_vfs_uri_new (uri);
2318                 
2319
2320                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2321                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2322                 g_free (escaped_filename);
2323                 gnome_vfs_uri_unref (vfs_uri);
2324
2325                 info = gnome_vfs_file_info_new ();
2326                 
2327                 if (gnome_vfs_get_file_info (uri, 
2328                                              info, 
2329                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2330                     == GNOME_VFS_OK)
2331                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2332                 mime_part = tny_platform_factory_new_mime_part
2333                         (modest_runtime_get_platform_factory ());
2334                 
2335                 /* try to get the attachment's size; this may fail for weird
2336                  * file systems, like obex, upnp... */
2337                 if (allowed_size != 0 &&
2338                     info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
2339                         size = info->size;
2340                         if (size > allowed_size) {
2341                                 modest_platform_information_banner (NULL, NULL, 
2342                                                                     dgettext("hildon-fm", "sfil_ib_opening_not_allowed"));
2343                                 return 0;
2344                         }
2345                 } else
2346                         g_warning ("%s: could not get attachment size", __FUNCTION__);
2347                 
2348                 stream = create_stream_for_uri (uri);
2349                 
2350                 if (stream == NULL) {
2351
2352                         modest_platform_information_banner (NULL, NULL, dgettext("hildon-fm", "sfil_ib_opening_not_allowed"));
2353
2354                         g_object_unref (mime_part);
2355                         gnome_vfs_file_info_unref (info);
2356                         return 0;
2357                 }
2358
2359                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2360                 g_object_unref (stream);
2361                 
2362                 content_id = g_strdup_printf ("%d", priv->next_cid);
2363                 tny_mime_part_set_content_id (mime_part, content_id);
2364                 g_free (content_id);
2365                 priv->next_cid++;
2366                 
2367                 basename = g_path_get_basename (filename);
2368                 tny_mime_part_set_filename (mime_part, basename);
2369                 g_free (basename);
2370                 
2371                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2372                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2373                                                         mime_part,
2374                                                         info->size == 0, info->size);
2375                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2376                 gtk_widget_show_all (priv->attachments_caption);
2377                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2378                 g_free (filename);
2379                 g_object_unref (mime_part);
2380                 gnome_vfs_file_info_unref (info);
2381         }
2382
2383         return size;
2384 }
2385
2386 void
2387 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2388                                            TnyList *att_list)
2389 {
2390         ModestMsgEditWindowPrivate *priv;
2391         TnyIterator *iter;
2392
2393         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2394         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2395
2396         if (att_list == NULL) {
2397                 att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2398         } else {
2399                 g_object_ref (att_list);
2400         }
2401
2402         if (tny_list_get_length (att_list) == 0) {
2403                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2404         } else {
2405                 gboolean dialog_response;
2406                 gchar *message = NULL;
2407                 gchar *filename = NULL;
2408
2409                 if (tny_list_get_length (att_list) == 1) {
2410                         TnyMimePart *part;
2411                         iter = tny_list_create_iterator (att_list);
2412                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2413                         g_object_unref (iter);
2414                         if (TNY_IS_MSG (part)) {
2415                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2416                                 if (header) {
2417                                         filename = tny_header_dup_subject (header);
2418                                         g_object_unref (header);
2419                                 }
2420                                 if (filename == NULL) {
2421                                         filename = g_strdup (_("mail_va_no_subject"));
2422                                 }
2423                         } else {
2424                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2425                         }
2426                         g_object_unref (part);
2427                 } else {
2428                         filename = g_strdup ("");
2429                 }
2430                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments",
2431                                                     tny_list_get_length (att_list)), filename);
2432                 g_free (filename);
2433
2434                 dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), message);
2435                 g_free (message);
2436
2437                 if (dialog_response != GTK_RESPONSE_OK) {
2438                         g_object_unref (att_list);
2439                         return;
2440                 }
2441                 hildon_banner_show_information (NULL, NULL, _("mcen_me_inbox_remove_attachments"));
2442                 
2443                 for (iter = tny_list_create_iterator (att_list);
2444                      !tny_iterator_is_done (iter);
2445                      tny_iterator_next (iter)) {
2446                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2447                         const gchar *att_id;
2448                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2449
2450                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2451                                                                    mime_part);
2452                         if (tny_list_get_length (priv->attachments) == 0)
2453                                 gtk_widget_hide (priv->attachments_caption);
2454                         att_id = tny_mime_part_get_content_id (mime_part);
2455                         if (att_id != NULL)
2456                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2457                                                                  att_id);
2458                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2459                         g_object_unref (mime_part);
2460                 }
2461                 g_object_unref (iter);
2462         }
2463
2464         g_object_unref (att_list);
2465
2466         /* if the last attachment has been removed, focus the Subject: field */
2467         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) 
2468                 gtk_widget_grab_focus (priv->subject_field);
2469 }
2470
2471 static void
2472 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2473                                             gpointer userdata)
2474 {
2475         ModestMsgEditWindowPrivate *priv;
2476         GdkColor *new_color;
2477         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2478         
2479 #ifdef MODEST_HAVE_HILDON0_WIDGETS      
2480         new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button));
2481 #else 
2482         GdkColor col;
2483         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col);
2484         new_color = &col;
2485 #endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/
2486
2487         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color);
2488         
2489         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2490
2491 }
2492
2493 static void
2494 modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item,
2495                                     gpointer userdata)
2496 {
2497         ModestMsgEditWindowPrivate *priv;
2498         gint new_size_index;
2499         ModestMsgEditWindow *window;
2500         GtkWidget *label;
2501         
2502         window = MODEST_MSG_EDIT_WINDOW (userdata);
2503         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2504         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2505
2506         if (gtk_check_menu_item_get_active (menu_item)) {
2507                 gchar *markup;
2508                 WPTextBufferFormat format;
2509
2510                 memset (&format, 0, sizeof (format));
2511                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2512
2513                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2514                 
2515                 new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label)));
2516                 format.cs.font_size = TRUE;
2517                 format.cs.text_position = TRUE;
2518                 format.cs.font = TRUE;
2519                 format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE);
2520 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2521
2522                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2523                                                    GINT_TO_POINTER (wp_get_font_size_index (new_size_index, 12))))
2524                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2525                 
2526                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2527                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", gtk_label_get_text (GTK_LABEL (label)), "</span>", NULL);
2528                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2529                 g_free (markup);
2530         }
2531 }
2532
2533 static void
2534 modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item,
2535                                     gpointer userdata)
2536 {
2537         ModestMsgEditWindowPrivate *priv;
2538         gint new_font_index;
2539         ModestMsgEditWindow *window;
2540         GtkWidget *label;
2541         
2542         window = MODEST_MSG_EDIT_WINDOW (userdata);
2543         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2544         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2545
2546         if (gtk_check_menu_item_get_active (menu_item)) {
2547                 gchar *markup;
2548
2549                 label = gtk_bin_get_child (GTK_BIN (menu_item));
2550                 
2551                 new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT);
2552
2553                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2554                                                    GINT_TO_POINTER(new_font_index)))
2555                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2556                 
2557                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2558                     markup = g_strconcat ("<span font_family='",gtk_label_get_text (GTK_LABEL (label)),"'>Tt</span>", NULL);
2559                 gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2560                 g_free (markup);
2561         }
2562 }
2563
2564 void
2565 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2566                                 gboolean show)
2567 {
2568         ModestMsgEditWindowPrivate *priv = NULL;
2569         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2570
2571         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2572         if (!priv->update_caption_visibility)
2573                 return;
2574
2575         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2576         if (show)
2577                 gtk_widget_show (priv->cc_caption);
2578         else
2579                 gtk_widget_hide (priv->cc_caption);
2580
2581         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2582 }
2583
2584 void
2585 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2586                                  gboolean show)
2587 {
2588         ModestMsgEditWindowPrivate *priv = NULL;
2589         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2590
2591         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2592         if (!priv->update_caption_visibility)
2593                 return;
2594
2595         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2596         if (show)
2597                 gtk_widget_show (priv->bcc_caption);
2598         else
2599                 gtk_widget_hide (priv->bcc_caption);
2600
2601         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2602 }
2603
2604 static void
2605 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2606                                          ModestRecptEditor *editor)
2607 {
2608         ModestMsgEditWindowPrivate *priv;
2609
2610         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2611         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2612         
2613         /* we check for low-mem; in that case, show a warning, and don't allow
2614          * for the addressbook
2615          */
2616         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2617                 return;
2618
2619         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2620
2621         if (editor == NULL) {
2622                 GtkWidget *view_focus;
2623                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2624
2625                 /* This code should be kept in sync with ModestRecptEditor. The
2626                    textview inside the recpt editor is the one that really gets the
2627                    focus. As it's inside a scrolled window, and this one inside the
2628                    hbox recpt editor inherits from, we'll need to go up in the 
2629                    hierarchy to know if the text view is part of the recpt editor
2630                    or if it's a different text entry */
2631
2632                 if (gtk_widget_get_parent (view_focus)) {
2633                         GtkWidget *first_parent;
2634
2635                         first_parent = gtk_widget_get_parent (view_focus);
2636                         if (gtk_widget_get_parent (first_parent) && 
2637                             MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) {
2638                                 editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent));
2639                         }
2640                 }
2641
2642                 if (editor == NULL)
2643                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2644
2645         }
2646
2647         modest_address_book_select_addresses (editor);
2648
2649 }
2650
2651 void
2652 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2653 {
2654         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2655
2656         modest_msg_edit_window_open_addressbook (window, NULL);
2657 }
2658
2659 static void
2660 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2661                                      gboolean show_toolbar)
2662 {
2663         ModestWindowPrivate *parent_priv;
2664
2665         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2666         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2667
2668         /* We can not just use the code of
2669            modest_msg_edit_window_setup_toolbar because it has a
2670            mixture of both initialization and creation code. */
2671         if (show_toolbar)
2672                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2673         else
2674                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2675 }
2676
2677 void
2678 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2679                                            TnyHeaderFlags priority_flags)
2680 {
2681         ModestMsgEditWindowPrivate *priv;
2682         ModestWindowPrivate *parent_priv;
2683
2684         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2685
2686         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2687         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2688
2689         if (priv->priority_flags != priority_flags) {
2690                 GtkAction *priority_action = NULL;
2691
2692                 priv->priority_flags = priority_flags;
2693
2694                 switch (priority_flags) {
2695                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2696                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2697                                                       MODEST_HEADER_ICON_HIGH, 
2698                                                       HILDON_ICON_SIZE_XSMALL);
2699                         gtk_widget_show (priv->priority_icon);
2700                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2701                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2702                         break;
2703                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2704                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2705                                                       MODEST_HEADER_ICON_LOW,
2706                                                       HILDON_ICON_SIZE_XSMALL);
2707                         gtk_widget_show (priv->priority_icon);
2708                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2709                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
2710                         break;
2711                 default:
2712                         gtk_widget_hide (priv->priority_icon);
2713                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2714                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
2715                         break;
2716                 }
2717                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
2718                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2719         }
2720 }
2721
2722 void
2723 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
2724                                         gint file_format)
2725 {
2726         ModestMsgEditWindowPrivate *priv;
2727         ModestWindowPrivate *parent_priv;
2728         gint current_format;
2729
2730         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2731
2732         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2733         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2734
2735         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2736                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2737
2738         if (current_format != file_format) {
2739                 switch (file_format) {
2740                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
2741                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
2742                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
2743                         break;
2744                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
2745                 {
2746                         GtkWidget *dialog;
2747                         gint response;
2748                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
2749                         response = gtk_dialog_run (GTK_DIALOG (dialog));
2750                         gtk_widget_destroy (dialog);
2751                         if (response == GTK_RESPONSE_OK) {
2752                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
2753                         } else {
2754                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu"));
2755                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
2756                         }
2757                 }
2758                         break;
2759                 }
2760                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2761                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2762                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2763         }
2764 }
2765
2766 void
2767 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
2768 {
2769         GtkWidget *dialog;
2770         ModestMsgEditWindowPrivate *priv;
2771         WPTextBufferFormat oldfmt, fmt;
2772         gint old_position = 0;
2773         gint response = 0;
2774         gint position = 0;
2775         gint font_size;
2776         GdkColor *color = NULL;
2777         gboolean bold, bold_set, italic, italic_set;
2778         gboolean underline, underline_set;
2779         gboolean strikethrough, strikethrough_set;
2780         gboolean position_set;
2781         gboolean font_size_set, font_set, color_set;
2782         gchar *font_name;
2783
2784         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2785         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2786         
2787         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
2788         modest_window_mgr_set_modal (modest_runtime_get_window_mgr(),
2789                                      GTK_WINDOW(dialog), GTK_WINDOW (window));
2790
2791         /* First we get the currently selected font information */
2792         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
2793
2794         switch (oldfmt.text_position) {
2795         case TEXT_POSITION_NORMAL:
2796                 old_position = 0;
2797                 break;
2798         case TEXT_POSITION_SUPERSCRIPT:
2799                 old_position = 1;
2800                 break;
2801         default:
2802                 old_position = -1;
2803                 break;
2804         }
2805
2806         g_object_set (G_OBJECT (dialog),
2807                       "bold", oldfmt.bold != FALSE,
2808                       "bold-set", !oldfmt.cs.bold,
2809                       "underline", oldfmt.underline != FALSE,
2810                       "underline-set", !oldfmt.cs.underline,
2811                       "italic", oldfmt.italic != FALSE,
2812                       "italic-set", !oldfmt.cs.italic,
2813                       "strikethrough", oldfmt.strikethrough != FALSE,
2814                       "strikethrough-set", !oldfmt.cs.strikethrough,
2815                       "color", &oldfmt.color,
2816                       "color-set", !oldfmt.cs.color,
2817                       "size", wp_font_size[oldfmt.font_size],
2818                       "size-set", !oldfmt.cs.font_size,
2819                       "position", old_position,
2820                       "position-set", !oldfmt.cs.text_position,
2821                       "family", wp_get_font_name (oldfmt.font),
2822                       "family-set", !oldfmt.cs.font,
2823                       NULL);
2824
2825         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2826                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2827         gtk_widget_show_all (dialog);
2828         priv->font_dialog = dialog;
2829         response = gtk_dialog_run (GTK_DIALOG (dialog));
2830         priv->font_dialog = NULL;
2831         if (response == GTK_RESPONSE_OK) {
2832
2833                 g_object_get( dialog,
2834                               "bold", &bold,
2835                               "bold-set", &bold_set,
2836                               "underline", &underline,
2837                               "underline-set", &underline_set,
2838                               "italic", &italic,
2839                               "italic-set", &italic_set,
2840                               "strikethrough", &strikethrough,
2841                               "strikethrough-set", &strikethrough_set,
2842                               "color", &color,
2843                               "color-set", &color_set,
2844                               "size", &font_size,
2845                               "size-set", &font_size_set,
2846                               "family", &font_name,
2847                               "family-set", &font_set,
2848                               "position", &position,
2849                               "position-set", &position_set,
2850                               NULL );
2851                 
2852         }       
2853
2854         if (response == GTK_RESPONSE_OK) {
2855                 memset(&fmt, 0, sizeof(fmt));
2856                 if (bold_set) {
2857                         fmt.bold = bold;
2858                         fmt.cs.bold = TRUE;
2859                 }
2860                 if (italic_set) {
2861                         fmt.italic = italic;
2862                         fmt.cs.italic = TRUE;
2863                 }
2864                 if (underline_set) {
2865                         fmt.underline = underline;
2866                         fmt.cs.underline = TRUE;
2867                 }
2868                 if (strikethrough_set) {
2869                         fmt.strikethrough = strikethrough;
2870                         fmt.cs.strikethrough = TRUE;
2871                 }
2872                 if (position_set) {
2873                         fmt.text_position =
2874                                 ( position == 0 )
2875                                 ? TEXT_POSITION_NORMAL
2876                                 : ( ( position == 1 )
2877                                     ? TEXT_POSITION_SUPERSCRIPT
2878                                     : TEXT_POSITION_SUBSCRIPT );
2879                         fmt.cs.text_position = TRUE;
2880                         fmt.font_size = oldfmt.font_size;
2881                 }
2882                 if (color_set) {
2883                         fmt.color = *color;
2884                         fmt.cs.color = TRUE;
2885                 }
2886                 if (font_set) {
2887                         fmt.font = wp_get_font_index(font_name,
2888                                                      DEFAULT_FONT);
2889                         fmt.cs.font = TRUE;
2890                 }
2891                 g_free(font_name);
2892                 if (font_size_set) {
2893                         fmt.cs.font_size = TRUE;
2894                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
2895                 }
2896                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
2897                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
2898         }
2899         gtk_widget_destroy (dialog);
2900         
2901         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
2902 }
2903
2904 void
2905 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
2906 {
2907         ModestMsgEditWindowPrivate *priv;
2908
2909         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2910         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2911         
2912         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
2913
2914         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2915         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2916 }
2917
2918 void
2919 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
2920 {
2921         ModestMsgEditWindowPrivate *priv;
2922
2923         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2924         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2925         
2926         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
2927
2928         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
2929         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
2930
2931 }
2932
2933 static void  
2934 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
2935 {
2936         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2937
2938         priv->can_undo = can_undo;
2939 }
2940
2941 static void  
2942 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
2943 {
2944         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2945
2946         priv->can_redo = can_redo;
2947 }
2948
2949 gboolean            
2950 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
2951 {
2952         ModestMsgEditWindowPrivate *priv;
2953         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2954         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2955
2956         return priv->can_undo;
2957 }
2958
2959 gboolean            
2960 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
2961 {
2962         ModestMsgEditWindowPrivate *priv;
2963         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
2964         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2965
2966         return priv->can_redo;
2967 }
2968
2969
2970 static void
2971 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
2972 {
2973         GtkTextIter iter;
2974         GtkTextIter match_start, match_end;
2975
2976         if (image_id == NULL)
2977                 return;
2978
2979         gtk_text_buffer_get_start_iter (buffer, &iter);
2980
2981         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
2982                 GSList *tags = gtk_text_iter_get_tags (&match_start);
2983                 GSList *node;
2984                 for (node = tags; node != NULL; node = g_slist_next (node)) {
2985                         GtkTextTag *tag = (GtkTextTag *) node->data;
2986                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
2987                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
2988                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
2989                                         gint offset;
2990                                         offset = gtk_text_iter_get_offset (&match_start);
2991                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
2992                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
2993                                 }
2994                         }
2995                 }
2996                 gtk_text_iter_forward_char (&iter);
2997         }
2998 }
2999
3000 gboolean
3001 message_is_empty (ModestMsgEditWindow *window)
3002 {
3003         ModestMsgEditWindowPrivate *priv = NULL;
3004
3005         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3006         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3007
3008         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
3009          * so we can ignore markup.
3010          */
3011         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
3012         gint count = 0;
3013         if (buf)
3014                 count = gtk_text_buffer_get_char_count (buf);
3015
3016         return count == 0;
3017 }
3018
3019 static gboolean
3020 msg_body_focus (GtkWidget *focus,
3021                 GdkEventFocus *event,
3022                 gpointer userdata)
3023 {
3024         
3025         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
3026         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
3027         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
3028         return FALSE;
3029 }
3030
3031 static void
3032 recpt_field_changed (GtkTextBuffer *buffer,
3033                   ModestMsgEditWindow *editor)
3034 {
3035         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3036         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3037 }
3038
3039 static void
3040 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
3041 {
3042         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3043         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3044 }
3045
3046 void
3047 modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor,
3048                                      gboolean modified)
3049 {
3050         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3051         GtkTextBuffer *buffer;
3052
3053         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3054         gtk_text_buffer_set_modified (buffer, modified);
3055         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3056         gtk_text_buffer_set_modified (buffer, modified);
3057         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3058         gtk_text_buffer_set_modified (buffer, modified);
3059         gtk_text_buffer_set_modified (priv->text_buffer, modified);
3060 }
3061
3062 gboolean
3063 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
3064 {
3065         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3066         const char *account_name;
3067         GtkTextBuffer *buffer;
3068
3069         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3070         if (gtk_text_buffer_get_modified (buffer))
3071                 return TRUE;
3072         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3073         if (gtk_text_buffer_get_modified (buffer))
3074                 return TRUE;
3075         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3076         if (gtk_text_buffer_get_modified (buffer))
3077                 return TRUE;
3078         if (gtk_text_buffer_get_modified (priv->text_buffer))
3079                 return TRUE;
3080         account_name = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
3081         if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) {
3082                 return TRUE;
3083         }
3084
3085         return FALSE;
3086 }
3087
3088
3089
3090
3091 gboolean
3092 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
3093 {
3094         ModestMsgEditWindowPrivate *priv = NULL;
3095         
3096         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3097         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3098
3099         /* check if there's no recipient added */
3100         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3101             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3102             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3103                 /* no recipient contents, then select contacts */
3104                 modest_msg_edit_window_open_addressbook (window, NULL);
3105                 return FALSE;
3106         }
3107
3108         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),  add_to_addressbook)) {
3109                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3110                 return FALSE;
3111         }
3112         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),  add_to_addressbook)) {
3113                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3114                 return FALSE;
3115         }
3116         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) {
3117                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3118                 return FALSE;
3119         }
3120
3121         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3122             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3123                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3124
3125         return TRUE;
3126
3127 }
3128
3129 static void