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