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