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