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