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