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