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