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