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