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