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