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