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