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