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