a81374cb12059bafd08405386d4b72a1ea162ab6
[modest] / src / hildon2 / 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 #include <tny-camel-mem-stream.h>
39 #include <modest-account-protocol.h>
40
41 #include <config.h>
42
43 #include <modest-account-mgr.h>
44 #include <modest-account-mgr-helpers.h>
45
46 #include <widgets/modest-msg-edit-window.h>
47 #include <modest-selector-picker.h>
48 #include <widgets/modest-recpt-editor.h>
49 #include <widgets/modest-attachments-view.h>
50
51 #include <modest-runtime.h>
52
53 #include "modest-platform.h"
54 #include "modest-icon-names.h"
55 #include "modest-widget-memory.h"
56 #include "modest-window-priv.h"
57 #include "modest-mail-operation.h"
58 #include "modest-tny-platform-factory.h"
59 #include "modest-tny-msg.h"
60 #include "modest-tny-folder.h"
61 #include "modest-tny-account.h"
62 #include "modest-address-book.h"
63 #include "modest-text-utils.h"
64 #include <tny-simple-list.h>
65 #include <modest-wp-text-view.h>
66 #include <wptextbuffer.h>
67 #include <hildon/hildon-pannable-area.h>
68 #include <hildon/hildon-touch-selector.h>
69 #include <hildon/hildon-picker-dialog.h>
70 #include "modest-msg-edit-window-ui-dimming.h"
71
72 #include "modest-hildon-includes.h"
73 #include "widgets/modest-msg-edit-window-ui.h"
74 #include <libgnomevfs/gnome-vfs-mime.h>
75 #include <modest-utils.h>
76 #include "modest-maemo-utils.h"
77 #include <modest-ui-constants.h>
78
79 #ifdef MODEST_USE_CALENDAR_WIDGETS
80 #include <calendar-ui-widgets.h>
81 #endif
82
83 #define DEFAULT_FONT_SIZE 3
84 #define DEFAULT_FONT 2
85 #define DEFAULT_SIZE_BUTTON_FONT_FAMILY "Sans"
86 #define DEFAULT_MAIN_VBOX_SPACING 0
87 #define SUBJECT_MAX_LENGTH 1000
88 #define IMAGE_MAX_WIDTH 560
89 #define DEFAULT_FONT_SCALE 1.5
90 #define ATTACHMENT_BUTTON_WIDTH 118
91 #define MAX_FROM_VALUE 36
92 #define MAX_BODY_LENGTH 128*1024
93 #define MAX_BODY_LINES 2048
94
95 static gboolean is_wp_text_buffer_started = FALSE;
96
97 static void  modest_msg_edit_window_class_init   (ModestMsgEditWindowClass *klass);
98 static void  modest_msg_edit_window_init         (ModestMsgEditWindow *obj);
99 static void  modest_msg_edit_window_finalize     (GObject *obj);
100
101 static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata);
102 static void  body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
103 static void  recpt_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor);
104
105 static void  text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window);
106 static void  text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window);
107 static void  text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window);
108 static void  text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
109                                     GtkTextIter *start, GtkTextIter *end,
110                                     gpointer userdata);
111 static void  text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id);
112 static void  body_insert_text (GtkTextBuffer *buffer, 
113                                       GtkTextIter *location,
114                                       gchar *text,
115                                       gint len,
116                                       ModestMsgEditWindow *window);
117 static void  subject_field_changed (GtkEditable *editable, ModestMsgEditWindow *window);
118 static void  subject_field_insert_text (GtkEditable *editable, 
119                                         gchar *new_text,
120                                         gint new_text_length,
121                                         gint *position,
122                                         ModestMsgEditWindow *window);
123 static void  modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
124                                                          gpointer userdata);
125 static void  modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window);
126
127 static void modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
128                                                      ModestRecptEditor *editor);
129 static void modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
130                                                            ModestMsgEditWindow *window);
131
132 /* ModestWindow methods implementation */
133 static void modest_msg_edit_window_disconnect_signals (ModestWindow *window);
134 static void modest_msg_edit_window_show_toolbar   (ModestWindow *window,
135                                                    gboolean show_toolbar);
136 static void modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
137                                                            GdkEvent *event,
138                                                            ModestMsgEditWindow *window);
139 static void modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window);
140 static void subject_field_move_cursor (GtkEntry *entry,
141                                        GtkMovementStep step,
142                                        gint a1,
143                                        gboolean a2,
144                                        gpointer userdata);
145 static void update_window_title (ModestMsgEditWindow *window);
146
147 /* Find toolbar */
148 static void modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
149                                                         ModestMsgEditWindow *window);
150 static void modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
151                                                        ModestMsgEditWindow *window);
152 static gboolean gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
153                                                           const gchar *str,
154                                                           GtkTextIter *match_start,
155                                                           GtkTextIter *match_end);
156
157 static void remove_tags (WPTextBuffer *buffer);
158
159 static void on_account_removed (TnyAccountStore *account_store, 
160                                 TnyAccount *account,
161                                 gpointer user_data);
162
163 static void init_window (ModestMsgEditWindow *obj);
164
165 gboolean scroll_drag_timeout (gpointer userdata);
166 static void correct_scroll (ModestMsgEditWindow *w);
167 static void correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused);
168 static void text_buffer_end_user_action (GtkTextBuffer *buffer,
169                                          ModestMsgEditWindow *userdata);
170 static void text_buffer_mark_set (GtkTextBuffer *buffer,
171                                   GtkTextIter *iter,
172                                   GtkTextMark *mark,
173                                   ModestMsgEditWindow *userdata);
174 static void on_message_settings (GtkAction *action,
175                                  ModestMsgEditWindow *window);
176 static void setup_menu (ModestMsgEditWindow *self);
177
178 static void from_field_changed (HildonPickerButton *button,
179                                 ModestMsgEditWindow *self);
180 static void font_size_clicked (GtkToolButton *button,
181                                ModestMsgEditWindow *window);
182 static void font_face_clicked (GtkToolButton *button,
183                                ModestMsgEditWindow *window);
184 static void update_signature (ModestMsgEditWindow *self,
185                               const gchar *old_account, 
186                               const gchar *new_account);
187 static void update_branding (ModestMsgEditWindow *self,
188                              const gchar *new_account);
189 static GtkWidget *_create_addressbook_box (GtkSizeGroup *title_size_group, GtkSizeGroup *value_size_group,
190                                            const gchar *label, GtkWidget *control);
191 static void max_chars_banner_unref (ModestMsgEditWindow *self, GObject *old_ref);
192 static void DEBUG_BUFFER (WPTextBuffer *buffer)
193 {
194 #ifdef DEBUG
195         GtkTextIter iter;
196         g_debug ("BEGIN BUFFER OF SIZE %d", gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (buffer)));
197
198         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
199         while (!gtk_text_iter_is_end (&iter)) {
200                 GString *output = g_string_new ("");
201                 GSList *toggled_tags;
202                 GSList *node;
203
204                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
205                 g_string_append_printf (output, "%d: CLOSED [ ", gtk_text_iter_get_offset (&iter));
206                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
207                         GtkTextTag *tag = (GtkTextTag *) node->data;
208                         const gchar *name;
209                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
210                         output = g_string_append (output, name);
211                         g_string_append (output, " ");
212                 }
213                 output = g_string_append (output, "] OPENED [ ");
214                 toggled_tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
215                 for (node = toggled_tags; node != NULL; node = g_slist_next (node)) {
216                         GtkTextTag *tag = (GtkTextTag *) node->data;
217                         const gchar *name;
218                         g_object_get (G_OBJECT (tag), "name", &name, NULL);
219                         output = g_string_append (output, name);
220                         g_string_append (output, " ");
221                 }
222                 output = g_string_append (output, "]\n");
223                 g_debug ("%s", output->str);
224                 g_string_free (output, TRUE);
225                 gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
226         }
227         g_debug ("END BUFFER");
228 #endif
229 }
230
231 static const GtkActionEntry hildon2_msg_edit_action_entries [] = {
232         { "MessageSettings", NULL, N_("mcen_me_message_settings"), NULL, NULL, G_CALLBACK (on_message_settings)},
233 };
234
235
236 /* static gboolean */
237 /* on_key_pressed (GtkWidget *self, */
238 /*              GdkEventKey *event, */
239 /*              gpointer user_data); */
240
241 /* list my signals */
242 enum {
243         /* MY_SIGNAL_1, */
244         /* MY_SIGNAL_2, */
245         LAST_SIGNAL
246 };
247
248 typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate;
249 struct _ModestMsgEditWindowPrivate {
250         GtkWidget   *msg_body;
251         GtkWidget   *header_box;
252         
253         ModestPairList *from_field_protos;
254         GtkWidget   *from_field;
255         gchar       *last_from_account;
256         gchar       *original_account_name;
257
258         gchar       *references;
259         gchar       *in_reply_to;
260
261         gchar       *original_mailbox;
262         
263         GtkWidget   *to_field;
264         GtkWidget   *cc_field;
265         GtkWidget   *bcc_field;
266         GtkWidget   *subject_field;
267         GtkWidget   *attachments_view;
268         GtkWidget   *priority_icon;
269         GtkWidget   *subject_box;
270         GtkWidget   *send_button;
271
272         GtkWidget   *cc_caption;
273         GtkWidget   *bcc_caption;
274         gboolean     update_caption_visibility;
275         GtkWidget   *attachments_caption;
276
277         GtkTextBuffer *text_buffer;
278
279         GtkWidget   *font_size_toolitem;
280         GtkWidget   *font_face_toolitem;
281         GtkWidget   *font_color_button;
282         GtkWidget   *font_color_toolitem;
283         GSList      *font_items_group;
284         GtkTreeModel *faces_model;
285         gint         current_face_index;
286         GtkWidget   *font_tool_button_label;
287         GtkTreeModel *sizes_model;
288         gint         current_size_index;
289         GtkWidget   *size_tool_button_label;
290
291         GtkWidget   *find_toolbar;
292         gchar       *last_search;
293
294         GtkWidget   *font_dialog;
295
296         GtkWidget   *pannable;
297         guint        correct_scroll_idle;
298         guint        scroll_drag_timeout_id;
299         gdouble      last_upper;
300
301         gint next_cid;
302         TnyList *attachments;
303         TnyList *images;
304         guint64 images_size;
305         gint images_count;
306
307         TnyHeaderFlags priority_flags;
308
309         gboolean    can_undo, can_redo;
310         gulong      clipboard_change_handler_id;
311         gulong      default_clipboard_change_handler_id;
312         gulong      account_removed_handler_id;
313         guint       clipboard_owner_idle;
314         gchar       *clipboard_text;
315
316         TnyMsg      *draft_msg;
317         TnyMsg      *outbox_msg;
318         gchar       *msg_uid;
319
320         gboolean    sent;
321
322         GtkWidget   *app_menu;
323         GtkWidget   *cc_button;
324         GtkWidget   *bcc_button;
325
326         GtkWidget   *max_chars_banner;
327
328         GtkWidget   *brand_icon;
329         GtkWidget   *brand_label;
330         GtkWidget   *brand_container;
331 };
332
333 #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
334                                                     MODEST_TYPE_MSG_EDIT_WINDOW, \
335                                                     ModestMsgEditWindowPrivate))
336 /* globals */
337 static GtkWindowClass *parent_class = NULL;
338
339 /* uncomment the following if you have defined any signals */
340 /* static guint signals[LAST_SIGNAL] = {0}; */
341
342 GType
343 modest_msg_edit_window_get_type (void)
344 {
345         static GType my_type = 0;
346         if (!my_type) {
347                 static const GTypeInfo my_info = {
348                         sizeof(ModestMsgEditWindowClass),
349                         NULL,           /* base init */
350                         NULL,           /* base finalize */
351                         (GClassInitFunc) modest_msg_edit_window_class_init,
352                         NULL,           /* class finalize */
353                         NULL,           /* class data */
354                         sizeof(ModestMsgEditWindow),
355                         1,              /* n_preallocs */
356                         (GInstanceInitFunc) modest_msg_edit_window_init,
357                         NULL
358                 };
359                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
360                                                   "ModestMsgEditWindow",
361                                                   &my_info, 0);
362
363         }
364         return my_type;
365 }
366
367 static void
368 save_state (ModestWindow *self)
369 {
370         modest_widget_memory_save (modest_runtime_get_conf(),
371                                    G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
372 }
373
374
375 static void
376 restore_settings (ModestMsgEditWindow *self)
377 {
378         ModestConf *conf = NULL;
379
380         conf = modest_runtime_get_conf ();
381
382         /* Dim at start clipboard actions */
383         modest_widget_memory_restore (conf, G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY);
384 }
385
386
387 static void
388 modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass)
389 {
390         GObjectClass *gobject_class;
391         ModestWindowClass *modest_window_class;
392         gobject_class = (GObjectClass*) klass;
393         modest_window_class = (ModestWindowClass*) klass;
394
395         parent_class            = g_type_class_peek_parent (klass);
396         gobject_class->finalize = modest_msg_edit_window_finalize;
397
398         modest_window_class->show_toolbar_func = modest_msg_edit_window_show_toolbar;
399         modest_window_class->save_state_func = save_state;
400         modest_window_class->disconnect_signals_func = modest_msg_edit_window_disconnect_signals;
401
402         g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate));
403 }
404
405 static void
406 modest_msg_edit_window_init (ModestMsgEditWindow *obj)
407 {
408         ModestMsgEditWindowPrivate *priv;
409         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
410
411         priv->msg_body      = NULL;
412         priv->from_field    = NULL;
413         priv->to_field      = NULL;
414         priv->cc_field      = NULL;
415         priv->bcc_field     = NULL;
416         priv->subject_field = NULL;
417         priv->attachments   = TNY_LIST (tny_simple_list_new ());
418         priv->images        = TNY_LIST (tny_simple_list_new ());
419         priv->images_size   = 0;
420         priv->images_count  = 0;
421         priv->next_cid      = 0;
422
423         priv->cc_caption    = NULL;
424         priv->bcc_caption    = NULL;
425         priv->update_caption_visibility = FALSE;
426
427         priv->priority_flags = 0;
428
429         priv->find_toolbar = NULL;
430         priv->last_search = NULL;
431
432         priv->draft_msg = NULL;
433         priv->outbox_msg = NULL;
434         priv->msg_uid = NULL;
435
436         priv->can_undo = FALSE;
437         priv->can_redo = FALSE;
438         priv->clipboard_change_handler_id = 0;
439         priv->default_clipboard_change_handler_id = 0;
440         priv->account_removed_handler_id = 0;
441         priv->clipboard_owner_idle = 0;
442         priv->clipboard_text = NULL;
443         priv->sent = FALSE;
444
445         priv->scroll_drag_timeout_id = 0;
446         priv->correct_scroll_idle = 0;
447         priv->last_upper = 0.0;
448
449         priv->font_dialog = NULL;
450         priv->app_menu = NULL;
451
452         priv->references = NULL;
453         priv->in_reply_to = NULL;
454         priv->max_chars_banner = NULL;
455
456         if (!is_wp_text_buffer_started) {
457                 is_wp_text_buffer_started = TRUE;
458                 wp_text_buffer_library_init ();
459         }
460
461         init_window (obj);
462         
463         hildon_program_add_window (hildon_program_get_instance(),
464                                    HILDON_WINDOW(obj));
465 }
466
467 static gchar *
468 multimailbox_get_default_mailbox (const gchar *account_name)
469 {
470         gchar *transport_account;
471         gchar *result = NULL;
472
473         transport_account = modest_account_mgr_get_server_account_name (modest_runtime_get_account_mgr (),
474                                                                         account_name,
475                                                                         TNY_ACCOUNT_TYPE_TRANSPORT);
476         if (transport_account) {
477                 gchar *proto;
478                 ModestProtocolRegistry *registry;
479
480                 registry = modest_runtime_get_protocol_registry ();
481
482                 proto = modest_account_mgr_get_string (modest_runtime_get_account_mgr (), transport_account, 
483                                                        MODEST_ACCOUNT_PROTO, TRUE);
484                 if (proto != NULL) {
485                         ModestProtocol *protocol = 
486                                 modest_protocol_registry_get_protocol_by_name (registry,
487                                                                                MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS,
488                                                                                proto);
489                         if (MODEST_ACCOUNT_PROTOCOL (protocol)) {
490                                 ModestPairList *pair_list;
491
492                                 pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
493                                                                                    account_name);
494                                 if (pair_list) {
495                                         ModestPair *pair = (ModestPair *) pair_list->data;
496                                         result = g_strdup ((const gchar *) pair->first);
497                                         modest_pair_list_free (pair_list);
498                                 }
499                         }
500                         
501                 }
502         }
503
504         return result;
505 }
506
507 /** 
508  * @result: A ModestPairList, which must be freed with modest_pair_list_free().
509  */
510 static ModestPairList*
511 get_transports (void)
512 {
513         GSList *transports = NULL;
514         
515         ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr();
516         GSList *accounts = modest_account_mgr_account_names (account_mgr, 
517                                                              TRUE /* only enabled accounts. */); 
518                                                 
519         GSList *cursor = accounts;
520         while (cursor) {
521                 gchar *account_name = cursor->data;
522                 if (account_name) {
523
524                         gchar *transport_account;
525                         gboolean multi_mailbox = FALSE;
526                         ModestProtocol *protocol = NULL;
527
528                         if (modest_account_mgr_account_is_multimailbox (account_mgr, account_name, &protocol)) {
529
530                                 transport_account = modest_account_mgr_get_server_account_name 
531                                         (modest_runtime_get_account_mgr (),
532                                          account_name,
533                                          TNY_ACCOUNT_TYPE_TRANSPORT);
534                                 if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
535                                         ModestPairList *pair_list;
536                                         pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
537                                                                                            account_name);
538                                         if (pair_list) {
539                                                 transports = g_slist_concat (transports, pair_list);
540                                                 multi_mailbox = TRUE;
541                                         }
542                                 }
543                         }
544
545                         if (!multi_mailbox) {
546                                 gchar *from_string  = NULL;
547
548                                 from_string = modest_account_mgr_get_from_string (account_mgr,
549                                                                                   account_name, NULL);
550                                 if (from_string && account_name) {
551                                         gchar *name = account_name;
552                                         ModestPair *pair = modest_pair_new ((gpointer) name,
553                                                                             (gpointer) from_string , TRUE);
554                                         transports = g_slist_prepend (transports, pair);
555                                 }
556                         }
557                 }
558                 
559                 cursor = cursor->next;
560         }
561         g_slist_free (accounts); /* only free the accounts, not the elements,
562                                   * because they are used in the pairlist */
563         return transports;
564 }
565
566 static void window_focus (GtkWindow *window,
567                           GtkWidget *widget,
568                           gpointer userdata)
569 {
570         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
571 }
572
573 gboolean
574 scroll_drag_timeout (gpointer userdata)
575 {
576         ModestMsgEditWindow *win = (ModestMsgEditWindow *) userdata;
577         ModestMsgEditWindowPrivate *priv;
578
579         /* It could happen that the window was already closed */
580         if (!GTK_WIDGET_VISIBLE (win))
581                 return FALSE;
582
583         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(win);
584
585         correct_scroll_without_drag_check (win, TRUE);
586
587         priv->scroll_drag_timeout_id = 0;
588
589         return FALSE;
590 }
591
592 static gboolean 
593 correct_scroll_without_drag_check_idle (gpointer userdata)
594 {
595         ModestMsgEditWindow *w = (ModestMsgEditWindow *) userdata;
596         ModestMsgEditWindowPrivate *priv;
597         GtkTextIter iter;
598         GdkRectangle rectangle;
599         gint offset_min, offset_max;
600         GtkTextMark *insert;
601         GtkAdjustment *vadj;
602
603         /* It could happen that the window was already closed */
604         if (!GTK_WIDGET_VISIBLE (w))
605                 return FALSE;
606
607         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
608
609         insert = gtk_text_buffer_get_insert (priv->text_buffer);
610         gtk_text_buffer_get_iter_at_mark (priv->text_buffer, &iter, insert);
611
612         gtk_text_view_get_iter_location (GTK_TEXT_VIEW (priv->msg_body), &iter, &rectangle);
613         offset_min = priv->msg_body->allocation.y + rectangle.y;
614         offset_max = offset_min + rectangle.height;
615
616         vadj = hildon_pannable_area_get_vadjustment (HILDON_PANNABLE_AREA (priv->pannable));
617         offset_min = MAX (offset_min - 48, 0);
618         offset_max = MIN (offset_max + 48, vadj->upper);
619
620         gtk_adjustment_clamp_page (vadj, (gdouble) offset_min, (gdouble) offset_max);
621
622         priv->correct_scroll_idle = 0;
623         return FALSE;
624 }
625
626 static void
627 correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused)
628 {
629         ModestMsgEditWindowPrivate *priv;
630
631         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
632
633         if (only_if_focused && !gtk_widget_is_focus (priv->msg_body))
634                 return;
635
636         if (priv->correct_scroll_idle > 0) {
637                 return;
638         }
639
640         priv->correct_scroll_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
641                                                      (GSourceFunc) correct_scroll_without_drag_check_idle,
642                                                      g_object_ref (w),
643                                                      g_object_unref);
644 }
645
646 static void
647 correct_scroll (ModestMsgEditWindow *w)
648 {
649         ModestMsgEditWindowPrivate *priv;
650
651         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w);
652         if (gtk_grab_get_current () == priv->msg_body) {
653                 if (priv->scroll_drag_timeout_id == 0) {
654                         priv->scroll_drag_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
655                                                                            500,
656                                                                            (GSourceFunc) scroll_drag_timeout,
657                                                                            g_object_ref (w),
658                                                                            g_object_unref);
659                 }
660                 return;
661         }
662
663         correct_scroll_without_drag_check (w, TRUE);
664 }
665
666 static void
667 text_buffer_end_user_action (GtkTextBuffer *buffer,
668                              ModestMsgEditWindow *userdata)
669 {
670
671         correct_scroll (userdata);
672 }
673
674 static void
675 text_buffer_mark_set (GtkTextBuffer *buffer,
676                       GtkTextIter *iter,
677                       GtkTextMark *mark,
678                       ModestMsgEditWindow *userdata)
679 {
680         gtk_text_buffer_begin_user_action (buffer);
681         gtk_text_buffer_end_user_action (buffer);
682 }
683
684 static void
685 cut_clipboard_check (GtkTextView *text_view,
686                      gpointer userdata)
687 {
688         GtkTextBuffer *buffer;
689         
690         buffer = gtk_text_view_get_buffer (text_view);
691         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
692                 g_signal_stop_emission_by_name ((gpointer )text_view, "cut-clipboard");
693         }
694 }
695
696 static void
697 copy_clipboard_check (GtkTextView *text_view,
698                      gpointer userdata)
699 {
700         GtkTextBuffer *buffer;
701         
702         buffer = gtk_text_view_get_buffer (text_view);
703         if (!modest_text_utils_buffer_selection_is_valid (buffer)) {
704                 g_signal_stop_emission_by_name ((gpointer )text_view, "copy-clipboard");
705         }
706 }
707
708 static void
709 attachment_deleted (ModestAttachmentsView *attachments_view,
710                     gpointer user_data)
711 {
712         modest_msg_edit_window_remove_attachments (MODEST_MSG_EDIT_WINDOW (user_data),
713                                                    NULL);
714 }
715
716 static void
717 body_size_request (GtkWidget *body,
718                    GtkRequisition *req,
719                    gpointer user_data)
720 {
721         /* Make sure the body always get at least 70 pixels */
722         if (req->height < 70)
723                 req->height = 70;
724 }
725
726 static void
727 connect_signals (ModestMsgEditWindow *obj)
728 {
729         ModestMsgEditWindowPrivate *priv;
730
731         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
732
733         g_signal_connect (G_OBJECT (priv->text_buffer), "refresh_attributes",
734                           G_CALLBACK (text_buffer_refresh_attributes), obj);
735         g_signal_connect (G_OBJECT (priv->text_buffer), "can-undo",
736                           G_CALLBACK (text_buffer_can_undo), obj);
737         g_signal_connect (G_OBJECT (priv->text_buffer), "can-redo",
738                           G_CALLBACK (text_buffer_can_redo), obj);
739         g_signal_connect (G_OBJECT (priv->text_buffer), "changed",
740                           G_CALLBACK (body_changed), obj);
741         g_signal_connect (G_OBJECT (priv->text_buffer), "insert-text", 
742                           G_CALLBACK (body_insert_text), obj);
743         g_signal_connect (G_OBJECT (priv->text_buffer), "modified-changed",
744                           G_CALLBACK (body_changed), obj);
745         g_signal_connect (G_OBJECT (priv->text_buffer), "end-user-action",
746                           G_CALLBACK (text_buffer_end_user_action), obj);
747         g_signal_connect (G_OBJECT (priv->text_buffer), "mark-set",
748                           G_CALLBACK (text_buffer_mark_set), obj);
749         g_signal_connect_after (G_OBJECT (priv->text_buffer), "apply-tag",
750                                 G_CALLBACK (text_buffer_apply_tag), obj);
751         g_signal_connect_swapped (G_OBJECT (priv->to_field), "open-addressbook", 
752                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
753         g_signal_connect_swapped (G_OBJECT (priv->cc_field), "open-addressbook", 
754                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
755         g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", 
756                                   G_CALLBACK (modest_msg_edit_window_open_addressbook), obj);
757
758         g_signal_connect (G_OBJECT (priv->send_button), "clicked",
759                           G_CALLBACK (modest_ui_actions_on_send), obj);
760
761         g_signal_connect (G_OBJECT (priv->from_field), "value-changed",
762                           G_CALLBACK (from_field_changed), obj);
763
764         g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event",
765                           G_CALLBACK (msg_body_focus), obj);
766         g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event",
767                           G_CALLBACK (msg_body_focus), obj);
768         g_signal_connect (G_OBJECT (priv->msg_body), "size-request",
769                           G_CALLBACK (body_size_request), obj);
770         g_signal_connect (G_OBJECT (obj), "set-focus", G_CALLBACK (window_focus), obj);
771         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))),
772                           "changed", G_CALLBACK (recpt_field_changed), obj);
773         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))),
774                           "changed", G_CALLBACK (recpt_field_changed), obj);
775         g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))),
776                           "changed", G_CALLBACK (recpt_field_changed), obj);
777         g_signal_connect (G_OBJECT (priv->subject_field), "changed", G_CALLBACK (subject_field_changed), obj);
778         g_signal_connect_after (G_OBJECT (priv->subject_field), "move-cursor", G_CALLBACK (subject_field_move_cursor), obj);
779         g_signal_connect (G_OBJECT (priv->subject_field), "insert-text", G_CALLBACK (subject_field_insert_text), obj);
780
781         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_edit_window_find_toolbar_search), obj);
782         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_edit_window_find_toolbar_close), obj);
783  
784         priv->clipboard_change_handler_id = 
785                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change",
786                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
787         priv->default_clipboard_change_handler_id = 
788                 g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)), "owner-change",
789                                   G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj);
790
791         g_signal_connect (G_OBJECT (priv->msg_body), "cut-clipboard", G_CALLBACK (cut_clipboard_check), NULL);
792         g_signal_connect (G_OBJECT (priv->msg_body), "copy-clipboard", G_CALLBACK (copy_clipboard_check), NULL);
793         g_signal_connect (G_OBJECT (priv->attachments_view), "delete", G_CALLBACK (attachment_deleted), obj);
794 }
795
796 static void
797 init_wp_text_view_style ()
798 {
799         static gboolean initialized = FALSE;
800
801         if (!initialized) {
802                 gtk_rc_parse_string ("class \"WPTextView\" style \"fremantle-textview\"");
803                 initialized = TRUE;
804         }
805 }       
806
807 static void
808 init_window (ModestMsgEditWindow *obj)
809 {
810         GtkWidget *to_caption, *subject_caption;
811         GtkWidget *main_vbox;
812         ModestMsgEditWindowPrivate *priv;
813         GtkActionGroup *action_group;
814         ModestWindowPrivate *parent_priv;
815         GdkPixbuf *window_icon = NULL;
816         GError *error = NULL;
817
818         GtkSizeGroup *title_size_group;
819         GtkSizeGroup *value_size_group;
820         GtkWidget *window_box;
821         GtkWidget *window_align;
822 #if (GTK_MINOR_VERSION >= 10)
823         GdkAtom deserialize_type;
824 #endif
825         GtkWidget *from_send_hbox;
826         GtkWidget *send_icon;
827         GtkWidget *attachments_label;
828         GtkWidget *branding_box;
829
830         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
831         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
832
833         parent_priv->ui_manager = gtk_ui_manager_new();
834         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
835         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
836
837         /* Add common actions */
838         gtk_action_group_add_actions (action_group,
839                                       modest_msg_edit_action_entries,
840                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
841                                       obj);
842         gtk_action_group_add_actions (action_group,
843                                       hildon2_msg_edit_action_entries,
844                                       G_N_ELEMENTS (hildon2_msg_edit_action_entries),
845                                       obj);
846         gtk_action_group_add_toggle_actions (action_group,
847                                              modest_msg_edit_toggle_action_entries,
848                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
849                                              obj);
850         gtk_action_group_add_radio_actions (action_group,
851                                             modest_msg_edit_alignment_radio_action_entries,
852                                             G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries),
853                                             GTK_JUSTIFY_LEFT,
854                                             G_CALLBACK (modest_ui_actions_on_change_justify),
855                                             obj);
856         gtk_action_group_add_radio_actions (action_group,
857                                             modest_msg_edit_priority_action_entries,
858                                             G_N_ELEMENTS (modest_msg_edit_priority_action_entries),
859                                             0,
860                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority),
861                                             obj);
862         gtk_action_group_add_radio_actions (action_group,
863                                             modest_msg_edit_file_format_action_entries,
864                                             G_N_ELEMENTS (modest_msg_edit_file_format_action_entries),
865                                             modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL),
866                                             G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format),
867                                             obj);
868         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
869         g_object_unref (action_group);
870
871         /* Load the UI definition */
872         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml",
873                                          &error);
874         if (error != NULL) {
875                 g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message);
876                 g_clear_error (&error);
877         }
878
879         /* Add accelerators */
880         gtk_window_add_accel_group (GTK_WINDOW (obj), 
881                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
882
883         parent_priv->menubar = NULL;
884
885         title_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
886         value_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
887
888         /* Note: This ModestPairList* must exist for as long as the picker
889          * that uses it, because the ModestSelectorPicker uses the ID opaquely, 
890          * so it can't know how to manage its memory. */ 
891         priv->from_field    = modest_selector_picker_new (MODEST_EDITABLE_SIZE,
892                                                           HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
893                                                           NULL, g_str_equal);
894         modest_selector_picker_set_value_max_chars (MODEST_SELECTOR_PICKER (priv->from_field), MAX_FROM_VALUE);
895         modest_maemo_utils_set_hbutton_layout (title_size_group, NULL, 
896                                                _("mail_va_from"), priv->from_field);
897         hildon_button_set_alignment (HILDON_BUTTON (priv->from_field), 0.0, 0.5, 1.0, 1.0);
898         hildon_button_set_title_alignment (HILDON_BUTTON (priv->from_field), 0.0, 0.5);
899         hildon_button_set_value_alignment (HILDON_BUTTON (priv->from_field), 1.0, 0.5);
900
901         priv->to_field      = modest_recpt_editor_new ();
902         priv->cc_field      = modest_recpt_editor_new ();
903         priv->bcc_field     = modest_recpt_editor_new ();
904         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->to_field), FALSE);
905         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->cc_field), FALSE);
906         modest_recpt_editor_set_show_abook_button (MODEST_RECPT_EDITOR (priv->bcc_field), FALSE);
907         priv->subject_box = gtk_hbox_new (FALSE, MODEST_MARGIN_NONE);
908         priv->priority_icon = gtk_image_new ();
909         gtk_box_pack_start (GTK_BOX (priv->subject_box), priv->priority_icon, FALSE, FALSE, 0);
910         priv->subject_field = hildon_entry_new (MODEST_EDITABLE_SIZE);
911         gtk_entry_set_max_length (GTK_ENTRY (priv->subject_field) ,SUBJECT_MAX_LENGTH);
912         g_object_set (G_OBJECT (priv->subject_field), "truncate-multiline", TRUE, NULL);
913         hildon_entry_set_placeholder ((HildonEntry *) priv->subject_field, _("mail_va_no_subject"));
914         hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->subject_field), 
915                                          HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
916         gtk_box_pack_start (GTK_BOX (priv->subject_box), priv->subject_field, TRUE, TRUE, 0);
917         priv->attachments_view = modest_attachments_view_new (NULL);
918         modest_attachments_view_set_style (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
919                                            MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS);
920         
921         priv->header_box = gtk_vbox_new (FALSE, 0);
922         
923         to_caption = _create_addressbook_box
924                 (title_size_group, value_size_group,
925                  _("mail_va_to"), priv->to_field);
926         priv->cc_caption = _create_addressbook_box
927                 (title_size_group, value_size_group,
928                  _("mail_va_cc"), priv->cc_field);
929         priv->bcc_caption = _create_addressbook_box
930                 (title_size_group, value_size_group,
931                  _("mail_va_hotfix1"), priv->bcc_field);
932         subject_caption = modest_maemo_utils_create_captioned (title_size_group, value_size_group,
933                                                                _("mail_va_subject"), FALSE, priv->subject_box);
934         priv->attachments_caption = modest_maemo_utils_create_captioned_with_size_type (NULL, NULL,
935                                                                                         _("mail_va_attachment"), 
936                                                                                         FALSE,
937                                                                                         priv->attachments_view,
938                                                                                         HILDON_SIZE_AUTO_WIDTH |
939                                                                                         HILDON_SIZE_AUTO_HEIGHT);
940         attachments_label = modest_maemo_utils_captioned_get_label_widget (priv->attachments_caption);
941         hildon_gtk_widget_set_theme_size (attachments_label, HILDON_SIZE_AUTO_HEIGHT);
942
943
944         priv->send_button = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT);
945         send_icon = gtk_image_new_from_icon_name (MODEST_TOOLBAR_ICON_MAIL_SEND, HILDON_ICON_SIZE_FINGER);
946         gtk_container_add (GTK_CONTAINER (priv->send_button), send_icon);
947         gtk_widget_set_size_request (GTK_WIDGET (priv->send_button), 148, -1);
948
949         g_object_unref (title_size_group);
950         g_object_unref (value_size_group);
951
952         priv->brand_icon = gtk_image_new ();
953         gtk_misc_set_alignment (GTK_MISC (priv->brand_icon), 0.5, 0.5);
954         priv->brand_label = gtk_label_new (NULL);
955         hildon_helper_set_logical_font (priv->brand_label, "SmallSystemFont");
956         gtk_misc_set_alignment (GTK_MISC (priv->brand_label), 0.0, 0.5);
957         gtk_widget_set_no_show_all (priv->brand_icon, TRUE);
958         gtk_widget_set_no_show_all (priv->brand_label, TRUE);
959
960         from_send_hbox = gtk_hbox_new (FALSE, 0);
961         gtk_box_pack_start (GTK_BOX (from_send_hbox), priv->from_field, TRUE, TRUE, 0);
962         gtk_box_pack_start (GTK_BOX (from_send_hbox), priv->send_button, FALSE, FALSE, 0);
963
964         branding_box = gtk_hbox_new (FALSE, MODEST_MARGIN_DEFAULT);
965         gtk_widget_show (branding_box);
966         gtk_box_pack_start (GTK_BOX (branding_box), priv->brand_label, FALSE, FALSE, 0);
967         gtk_box_pack_start (GTK_BOX (branding_box), priv->brand_icon, FALSE, FALSE, 0);
968
969         priv->brand_container = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
970         gtk_alignment_set_padding (GTK_ALIGNMENT (priv->brand_container), 0, 0, MODEST_MARGIN_DOUBLE, 0);
971         gtk_container_add (GTK_CONTAINER (priv->brand_container), branding_box);
972         gtk_widget_set_no_show_all (priv->brand_container, TRUE);
973
974
975         gtk_box_pack_start (GTK_BOX (priv->header_box), from_send_hbox, FALSE, FALSE, 0);
976         gtk_box_pack_start (GTK_BOX (priv->header_box), to_caption, FALSE, FALSE, 0);
977         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->cc_caption, FALSE, FALSE, 0);
978         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->bcc_caption, FALSE, FALSE, 0);
979         gtk_box_pack_start (GTK_BOX (priv->header_box), subject_caption, FALSE, FALSE, 0);
980         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->attachments_caption, FALSE, FALSE, 0);
981         gtk_box_pack_start (GTK_BOX (priv->header_box), priv->brand_container, FALSE, FALSE, 0);
982         gtk_widget_set_no_show_all (priv->attachments_caption, TRUE);
983
984         init_wp_text_view_style ();
985
986         priv->msg_body = modest_wp_text_view_new ();
987         
988
989         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->msg_body), GTK_WRAP_WORD_CHAR);
990         priv->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
991         g_object_set (priv->text_buffer, "font_scale", DEFAULT_FONT_SCALE, NULL);
992         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
993 #if (GTK_MINOR_VERSION >= 10)
994         gtk_text_buffer_register_serialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), NULL);
995         deserialize_type = gtk_text_buffer_register_deserialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), 
996                                                                        NULL);
997         gtk_text_buffer_deserialize_set_can_create_tags (GTK_TEXT_BUFFER (priv->text_buffer), 
998                                                          deserialize_type, TRUE);
999 #endif
1000         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1001
1002         priv->find_toolbar = hildon_find_toolbar_new (NULL);
1003         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
1004
1005 /*      g_signal_connect (G_OBJECT (obj), "key_pressed", G_CALLBACK (on_key_pressed), NULL) */
1006
1007         priv->pannable = hildon_pannable_area_new ();
1008
1009         g_object_set (G_OBJECT (priv->pannable), "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
1010         
1011         main_vbox = gtk_vbox_new  (FALSE, DEFAULT_MAIN_VBOX_SPACING);
1012         window_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
1013         gtk_alignment_set_padding (GTK_ALIGNMENT (window_align), 0, 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DEFAULT);
1014
1015         gtk_box_pack_start (GTK_BOX(main_vbox), priv->header_box, FALSE, FALSE, 0);
1016         gtk_box_pack_start (GTK_BOX(main_vbox), priv->msg_body, TRUE, TRUE, 0);
1017         gtk_container_add (GTK_CONTAINER (window_align), main_vbox);
1018
1019         hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (priv->pannable), window_align);
1020         gtk_widget_show_all (GTK_WIDGET(priv->pannable));
1021         
1022         window_box = gtk_vbox_new (FALSE, 0);
1023         gtk_container_add (GTK_CONTAINER(obj), window_box);
1024
1025         gtk_box_pack_start (GTK_BOX (window_box), priv->pannable, TRUE, TRUE, 0);
1026
1027         /* Set window icon */
1028         window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON, MODEST_ICON_SIZE_BIG); 
1029         if (window_icon) {
1030                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
1031                 g_object_unref (window_icon);
1032         }       
1033 }
1034         
1035 static void
1036 modest_msg_edit_window_disconnect_signals (ModestWindow *window)
1037 {
1038         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1039
1040         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
1041             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
1042                                            priv->clipboard_change_handler_id))
1043                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
1044                                              priv->clipboard_change_handler_id);
1045         if (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD) &&
1046             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
1047                                            priv->default_clipboard_change_handler_id))
1048                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), 
1049                                              priv->default_clipboard_change_handler_id);
1050
1051         if (priv->account_removed_handler_id && 
1052             g_signal_handler_is_connected (modest_runtime_get_account_store (), 
1053                                            priv->account_removed_handler_id))
1054                 g_signal_handler_disconnect(modest_runtime_get_account_store (), 
1055                                            priv->account_removed_handler_id);
1056 }
1057
1058 static void
1059 modest_msg_edit_window_finalize (GObject *obj)
1060 {
1061         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1062
1063         if (priv->max_chars_banner) {
1064                 g_object_weak_unref (G_OBJECT (priv->max_chars_banner), (GWeakNotify) max_chars_banner_unref, obj);
1065                 priv->max_chars_banner = FALSE;
1066         }
1067
1068         /* Sanity check: shouldn't be needed, the window mgr should
1069            call this function before */
1070         modest_msg_edit_window_disconnect_signals (MODEST_WINDOW (obj));
1071
1072         if (priv->font_dialog != NULL) {
1073                 gtk_dialog_response (GTK_DIALOG (priv->font_dialog), GTK_RESPONSE_NONE);
1074         }
1075
1076         if (priv->clipboard_text != NULL) {
1077                 g_free (priv->clipboard_text);
1078                 priv->clipboard_text = NULL;
1079         }
1080         
1081         if (priv->draft_msg != NULL) {
1082                 TnyHeader *header = tny_msg_get_header (priv->draft_msg);
1083                 if (TNY_IS_HEADER (header)) {
1084                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1085                         modest_window_mgr_unregister_header (mgr, header);
1086                 }
1087                 g_object_unref (priv->draft_msg);
1088                 priv->draft_msg = NULL;
1089         }
1090         if (priv->outbox_msg != NULL) {
1091                 TnyHeader *header = tny_msg_get_header (priv->outbox_msg);
1092                 if (TNY_IS_HEADER (header)) {
1093                         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1094                         modest_window_mgr_unregister_header (mgr, header);
1095                 }
1096                 g_object_unref (priv->outbox_msg);
1097                 priv->outbox_msg = NULL;
1098         }
1099         if (priv->correct_scroll_idle > 0) {
1100                 g_source_remove (priv->correct_scroll_idle);
1101                 priv->correct_scroll_idle = 0;
1102         }
1103         if (priv->scroll_drag_timeout_id > 0) {
1104                 g_source_remove (priv->scroll_drag_timeout_id);
1105                 priv->scroll_drag_timeout_id = 0;
1106         }
1107         if (priv->clipboard_owner_idle > 0) {
1108                 g_source_remove (priv->clipboard_owner_idle);
1109                 priv->clipboard_owner_idle = 0;
1110         }
1111         if (priv->original_account_name)
1112                 g_free (priv->original_account_name);
1113         if (priv->original_mailbox)
1114                 g_free (priv->original_mailbox);
1115         g_free (priv->msg_uid);
1116         g_free (priv->last_search);
1117         g_free (priv->references);
1118         g_free (priv->in_reply_to);
1119         g_object_unref (priv->attachments);
1120         g_object_unref (priv->images);
1121
1122         /* This had to stay alive for as long as the picker that used it: */
1123         modest_pair_list_free (priv->from_field_protos);
1124         
1125         G_OBJECT_CLASS(parent_class)->finalize (obj);
1126 }
1127
1128 static void
1129 pixbuf_size_prepared (GdkPixbufLoader *loader,
1130                       gint width,
1131                       gint height,
1132                       ModestMsgEditWindow *self)
1133 {
1134         gint new_height, new_width;
1135         gboolean set_size;
1136         
1137         new_height = height;
1138         new_width = width;
1139         set_size = FALSE;
1140
1141         if (width > IMAGE_MAX_WIDTH) {
1142                 new_height = height * IMAGE_MAX_WIDTH / width;
1143                 new_width = IMAGE_MAX_WIDTH;
1144         }
1145
1146         gdk_pixbuf_loader_set_size (loader, new_width, new_height);
1147 }
1148
1149 static GdkPixbuf *
1150 pixbuf_from_stream (TnyStream *stream,
1151                     const gchar *mime_type,
1152                     guint64 *stream_size,
1153                     ModestMsgEditWindow *self)
1154 {
1155         GdkPixbufLoader *loader;
1156         GdkPixbuf *pixbuf;
1157         guint64 size;
1158         GError *error = NULL;
1159
1160         size = 0;
1161
1162         loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL);
1163
1164         if (loader == NULL) {
1165                 if (stream_size)
1166                         *stream_size = 0;
1167                 return NULL;
1168         }
1169         g_signal_connect (G_OBJECT (loader), "size-prepared", G_CALLBACK (pixbuf_size_prepared), self);
1170
1171         hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
1172
1173         tny_stream_reset (TNY_STREAM (stream));
1174         while (!tny_stream_is_eos (TNY_STREAM (stream))) {
1175                 unsigned char read_buffer[128];
1176                 gint readed;
1177                 readed = tny_stream_read (TNY_STREAM (stream), (char *) read_buffer, 128);
1178                 size += readed;
1179                 if (!gdk_pixbuf_loader_write (loader, read_buffer, readed, &error)) {
1180                         break;
1181                 }
1182                 /* Allow some UI responsiveness */
1183                 while (gtk_events_pending ())
1184                         gtk_main_iteration ();
1185         }
1186         hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), FALSE);
1187
1188         gdk_pixbuf_loader_close (loader, &error);
1189
1190         if (error)
1191                 g_error_free (error);
1192         pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1193         if (pixbuf) 
1194                 g_object_ref (pixbuf);
1195         g_object_unref (loader);
1196
1197         if (!pixbuf)
1198                 return NULL;
1199
1200         if (gdk_pixbuf_get_width (pixbuf) > IMAGE_MAX_WIDTH) {
1201                 GdkPixbuf *new_pixbuf;
1202                 gint new_height;
1203                 new_height = (gdk_pixbuf_get_height (pixbuf) * IMAGE_MAX_WIDTH) /
1204                         gdk_pixbuf_get_width (pixbuf);
1205                 new_pixbuf = gdk_pixbuf_scale_simple (pixbuf, IMAGE_MAX_WIDTH, new_height, GDK_INTERP_BILINEAR);
1206                 g_object_unref (pixbuf);
1207                 pixbuf = new_pixbuf;
1208         }
1209
1210         if (stream_size)
1211                 *stream_size = size;
1212
1213         return pixbuf;
1214 }
1215
1216 static void
1217 replace_with_images (ModestMsgEditWindow *self, TnyList *attachments)
1218 {
1219         ModestMsgEditWindowPrivate *priv;
1220         TnyIterator *iter;
1221
1222         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1223
1224         g_object_ref (self);
1225         for (iter = tny_list_create_iterator (attachments);
1226              !tny_iterator_is_done (iter);
1227              tny_iterator_next (iter)) {
1228                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1229                 const gchar *cid = tny_mime_part_get_content_id (part);
1230                 const gchar *mime_type = tny_mime_part_get_content_type (part);
1231                 if ((cid != NULL)&&(mime_type != NULL)) {
1232                         guint64 stream_size;
1233                         TnyStream *stream = tny_mime_part_get_decoded_stream (part);
1234                         GdkPixbuf *pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size, self);
1235
1236
1237                         g_object_unref (stream);
1238
1239                         if (pixbuf != NULL) {
1240                                 priv->images_count ++;
1241                                 priv->images_size += stream_size;
1242                                 wp_text_buffer_replace_image (WP_TEXT_BUFFER (priv->text_buffer), cid, pixbuf);
1243                                 g_object_unref (pixbuf);
1244                         }
1245                 }
1246                 g_object_unref (part);
1247         }
1248         g_object_unref (iter);
1249         g_object_unref (self);
1250 }
1251
1252 static void
1253 get_related_images (ModestMsgEditWindow *self, TnyMsg *msg)
1254 {
1255         TnyMimePart *parent = NULL;
1256         const gchar *content_type = NULL;
1257         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1258
1259         content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg));
1260
1261         if (content_type && !g_ascii_strcasecmp (content_type, "multipart/related")) {
1262                 parent = g_object_ref (msg);
1263         } else if (content_type && !g_ascii_strcasecmp (content_type, "multipart/mixed")) {
1264                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1265                 TnyIterator *iter;
1266
1267                 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
1268                 iter = tny_list_create_iterator (parts);
1269                 while (!tny_iterator_is_done (iter)) {
1270                         TnyMimePart *part;
1271                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1272                         content_type = tny_mime_part_get_content_type (part);
1273                         if (content_type && !g_ascii_strcasecmp (content_type, "multipart/related")) {
1274                                 parent = part;
1275                                 break;
1276                         } else {
1277                                 g_object_unref (part);
1278                         }
1279                         tny_iterator_next (iter);
1280                 }
1281                 g_object_unref (iter);
1282                 g_object_unref (parts);
1283         }
1284
1285         if (parent != NULL) {
1286                 TnyList *parts = TNY_LIST (tny_simple_list_new ());
1287                 TnyIterator *iter;
1288
1289                 tny_mime_part_get_parts (TNY_MIME_PART (parent), parts);
1290                 iter = tny_list_create_iterator (parts);
1291                 while (!tny_iterator_is_done (iter)) {
1292                         TnyMimePart *part;
1293                         part = TNY_MIME_PART (tny_iterator_get_current (iter));
1294                         content_type = tny_mime_part_get_content_type (part);
1295                         if (content_type && g_str_has_prefix (content_type, "image/")) {
1296                                 tny_list_prepend (priv->images, (GObject *) part);
1297                         } 
1298                         g_object_unref (part);
1299                         tny_iterator_next (iter);
1300                 }
1301                 g_object_unref (iter);
1302                 g_object_unref (parts);
1303                 g_object_unref (parent);
1304         }
1305 }
1306
1307 static void
1308 update_next_cid (ModestMsgEditWindow *self, TnyList *attachments)
1309 {
1310         TnyIterator *iter;
1311         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1312
1313         for (iter = tny_list_create_iterator (attachments) ; 
1314              !tny_iterator_is_done (iter);
1315              tny_iterator_next (iter)) {
1316                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter);
1317                 const gchar *cid = tny_mime_part_get_content_id (part);
1318                 if (cid != NULL) {
1319                         char *invalid = NULL;
1320                         gint int_cid = strtol (cid, &invalid, 10);
1321                         if ((invalid != NULL) && (*invalid == '\0') && (int_cid >= priv->next_cid)) {
1322                                 priv->next_cid = int_cid + 1;
1323                         }
1324                 }
1325                 g_object_unref (part);
1326         }
1327         g_object_unref (iter);
1328 }
1329
1330 static void
1331 set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich)
1332 {
1333         TnyHeader *header;
1334         gchar *to, *cc, *bcc, *subject;
1335         gchar *body;
1336         ModestMsgEditWindowPrivate *priv;
1337         ModestWindowPrivate *parent_priv;
1338         GtkTextIter iter;
1339         TnyHeaderFlags priority_flags;
1340         TnyFolder *msg_folder;
1341         gboolean is_html = FALSE;
1342         gboolean field_view_set;
1343         
1344         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1345         g_return_if_fail (TNY_IS_MSG (msg));
1346
1347         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1348         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
1349
1350         header = tny_msg_get_header (msg);
1351         to      = tny_header_dup_to (header);
1352         cc      = tny_header_dup_cc (header);
1353         bcc     = tny_header_dup_bcc (header);
1354         subject = tny_header_dup_subject (header);
1355
1356         modest_tny_msg_get_references (TNY_MSG (msg), NULL, &(priv->references), &(priv->in_reply_to));
1357         priority_flags = tny_header_get_priority (header);
1358
1359         if (to) {
1360                 gchar *quoted_names = modest_text_utils_quote_names (to);
1361                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field), quoted_names);
1362                 g_free (quoted_names);
1363         }
1364
1365         field_view_set = TRUE;
1366         if (cc) {
1367                 gchar *quoted_names = modest_text_utils_quote_names (cc);
1368                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field),  quoted_names);
1369                 g_free (quoted_names);
1370                 gtk_widget_set_no_show_all (priv->cc_caption, FALSE);
1371                 gtk_widget_show (priv->cc_caption);
1372         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) {
1373                 gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
1374                 gtk_widget_hide (priv->cc_caption);
1375                 field_view_set = FALSE;
1376         }
1377         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->cc_button), field_view_set);
1378
1379         field_view_set = TRUE;
1380         if (bcc) {
1381                 gchar *quoted_names = modest_text_utils_quote_names (bcc);
1382                 modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), quoted_names);
1383                 g_free (quoted_names);
1384                 gtk_widget_set_no_show_all (priv->bcc_caption, FALSE);
1385                 gtk_widget_show (priv->bcc_caption);
1386         } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) {
1387                 gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
1388                 gtk_widget_hide (priv->bcc_caption);
1389                 field_view_set = FALSE;
1390         }
1391         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->bcc_button), field_view_set);
1392
1393
1394         if (subject)
1395                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
1396         modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self),
1397                                                    priority_flags);
1398
1399         update_window_title (self);
1400
1401         wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1402         body = modest_tny_msg_get_body (msg, TRUE, &is_html);
1403
1404         if ((body == NULL)||(body[0] == '\0')) {
1405                 g_free (body);
1406                 body = modest_text_utils_convert_to_html ("");
1407                 is_html = FALSE;
1408         }
1409         wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1410         wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer),
1411                                             (gchar *) body,
1412                                             strlen (body));
1413         wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer));
1414         g_free (body);
1415
1416         /* Add attachments to the view */
1417         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg);
1418         priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1419         if (tny_list_get_length (priv->attachments) == 0) {
1420                 gtk_widget_hide (priv->attachments_caption);
1421         } else {
1422                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
1423                 gtk_widget_show_all (priv->attachments_caption);
1424         }
1425         get_related_images (self, msg);
1426         update_next_cid (self, priv->attachments);
1427         update_next_cid (self, priv->images);
1428         replace_with_images (self, priv->images);
1429
1430         if (preserve_is_rich && !is_html) {
1431                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1432         /* Get the default format required from configuration */
1433         } else if (!preserve_is_rich && !modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) {
1434                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1435         }
1436
1437         /* Set the default focus depending on having already a To: field or not */
1438         if ((!to)||(*to == '\0')) {
1439                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
1440         } else {
1441                 gtk_widget_grab_focus (priv->msg_body);
1442         }
1443
1444         DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer));
1445
1446         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1447         gtk_text_buffer_place_cursor (priv->text_buffer, &iter);
1448
1449         modest_msg_edit_window_set_modified (self, FALSE);
1450
1451         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1452         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1453         text_buffer_can_undo (priv->text_buffer, FALSE, self);
1454         text_buffer_can_redo (priv->text_buffer, FALSE, self);
1455
1456         if (priv->msg_uid) {
1457                 g_free (priv->msg_uid);
1458                 priv->msg_uid = NULL;
1459         }
1460
1461         /* we should set a reference to the incoming message if it is a draft */
1462         msg_folder = tny_msg_get_folder (msg);
1463         if (msg_folder) {               
1464                 if (modest_tny_folder_is_local_folder (msg_folder)) {
1465                         TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder);
1466                         if (type == TNY_FOLDER_TYPE_INVALID)
1467                                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1468                         
1469                         if (type == TNY_FOLDER_TYPE_DRAFTS) 
1470                                 priv->draft_msg = g_object_ref(msg);
1471                         if (type == TNY_FOLDER_TYPE_OUTBOX)
1472                                 priv->outbox_msg = g_object_ref(msg);
1473                         priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1474                 }
1475                 g_object_unref (msg_folder);
1476         }
1477
1478         g_free (to);
1479         g_free (subject);
1480         g_free (cc);
1481         g_free (bcc);
1482 }
1483
1484 #ifdef MODEST_USE_CALENDAR_WIDGETS
1485 static void
1486 color_button_clicked (GtkButton *button)
1487 {
1488         /* Show ColorPicker dialog */
1489         PipCalendarColor color = pip_color_picker_select_color(PipTextColorRed, PipColorPickerText);
1490
1491         /* Check if some color is selected rather than dialog is dismissed */
1492         if (color != PipCalendarColorInvalid) {
1493                 GdkColor *gdk_color = (GdkColor *) pip_calendar_color_get_gdkcolor(color);
1494                 if (gdk_color)
1495                         hildon_color_button_set_color ((HildonColorButton *) button, gdk_color);
1496         }
1497 }
1498 #endif
1499
1500 static void
1501 modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window)
1502 {
1503         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1504         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1505         GtkWidget *placeholder;
1506         GtkWidget *tool_item;
1507         gint insert_index;
1508         gchar size_text[5];
1509         gint size_index;
1510         gint font_index;
1511         GtkWidget *sizes_menu;
1512         GtkWidget *fonts_menu;
1513         gchar *markup;
1514         GtkWidget *arrow;
1515         GtkWidget *hbox;
1516
1517         /* Toolbar */
1518         parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
1519         gtk_toolbar_set_show_arrow (GTK_TOOLBAR (parent_priv->toolbar), FALSE);
1520         gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
1521         hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar));
1522
1523         /* Font color placeholder */
1524         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor");
1525         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1526
1527         /* font color */
1528         priv->font_color_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1529         priv->font_color_button = hildon_color_button_new ();
1530         gtk_widget_set_size_request (priv->font_color_button, -1, 48);
1531         GTK_WIDGET_UNSET_FLAGS (priv->font_color_toolitem, GTK_CAN_FOCUS);
1532         GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS);
1533         gtk_container_add (GTK_CONTAINER (priv->font_color_toolitem), priv->font_color_button);
1534         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1535         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->font_color_toolitem), TRUE);
1536         gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->font_color_toolitem), insert_index);
1537         g_signal_connect_swapped (G_OBJECT (priv->font_color_button), 
1538                                   "notify::color", 
1539                                   G_CALLBACK (modest_msg_edit_window_color_button_change), 
1540                                   window);
1541
1542         /* Yes I know this is a horrible hack, but it works for the
1543            moment while we don't create a ModestColorButton */
1544 #ifdef MODEST_USE_CALENDAR_WIDGETS
1545         GtkButtonClass *button_class = GTK_BUTTON_CLASS (HILDON_COLOR_BUTTON_GET_CLASS (priv->font_color_button));
1546         button_class->clicked = color_button_clicked;
1547 #endif
1548
1549         /* Font size and face placeholder */
1550         placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes");
1551         insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1552         /* font_size */
1553         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1554         priv->size_tool_button_label = gtk_label_new (NULL);
1555         snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]);
1556         markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>",
1557                               size_text, "</span>", NULL);
1558         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
1559         gtk_misc_set_alignment (GTK_MISC (priv->size_tool_button_label), 1.0, 0.5);
1560         g_free (markup);
1561         hildon_helper_set_logical_font (priv->size_tool_button_label, "LargeSystemFont");
1562         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1563         gtk_box_pack_start (GTK_BOX (hbox), priv->size_tool_button_label, TRUE, TRUE, 0);
1564         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1565         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1566         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1567         gtk_widget_set_sensitive (arrow, FALSE);
1568         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1569         sizes_menu = gtk_menu_new ();
1570         priv->sizes_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1571         for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) {
1572                 GtkTreeIter iter;
1573
1574                 snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]);
1575                 gtk_list_store_append (GTK_LIST_STORE (priv->sizes_model), &iter);
1576
1577                 gtk_list_store_set (GTK_LIST_STORE (priv->sizes_model), &iter, 
1578                                     0, size_text,
1579                                     -1);
1580
1581                 if (wp_font_size[size_index] == 12)
1582                         priv->current_size_index = size_index;
1583                                         
1584         }
1585
1586         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_size_clicked), window);
1587         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1588         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1589         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1590         priv->font_size_toolitem = tool_item;
1591
1592         /* font face */
1593         tool_item = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
1594         priv->font_tool_button_label = gtk_label_new (NULL);
1595         markup = g_strconcat ("<span font_family='", wp_get_font_name(DEFAULT_FONT), "'>Tt</span>", NULL);
1596         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
1597         gtk_misc_set_alignment (GTK_MISC (priv->font_tool_button_label), 1.0, 0.5);
1598         g_free(markup);
1599         hildon_helper_set_logical_font (priv->font_tool_button_label, "LargeSystemFont");
1600         hbox = gtk_hbox_new (MODEST_MARGIN_DEFAULT, FALSE);
1601         gtk_box_pack_start (GTK_BOX (hbox), priv->font_tool_button_label, TRUE, TRUE, 0);
1602         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1603         gtk_misc_set_alignment (GTK_MISC (arrow), 0.0, 0.5);
1604         gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
1605         gtk_widget_set_sensitive (arrow, FALSE);
1606         gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), hbox);
1607         fonts_menu = gtk_menu_new ();
1608         priv->faces_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
1609         for (font_index = 0; font_index < wp_get_font_count (); font_index++) {
1610                 GtkTreeIter iter;
1611
1612                 gtk_list_store_append (GTK_LIST_STORE (priv->faces_model), &iter);
1613
1614                 gtk_list_store_set (GTK_LIST_STORE (priv->faces_model), &iter, 
1615                                     0, wp_get_font_name (font_index),
1616                                     -1);
1617
1618                 if (font_index == DEFAULT_FONT)
1619                         priv->current_face_index = font_index;
1620         }
1621         g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (font_face_clicked), window);
1622         gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index);
1623         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1624         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1625         priv->font_face_toolitem = tool_item;
1626
1627         /* Set expand and homogeneous for remaining items */
1628         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold");
1629         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1630         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1631         tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
1632         gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE);
1633         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE);
1634
1635         /* Explicitelly show all the toolbar (a normal gtk_widget_show
1636            will not show the tool items added to the placeholders) */
1637         gtk_widget_show_all (parent_priv->toolbar);
1638
1639         /* Set the no show all *after* showing all items. We do not
1640            want the toolbar to be shown with a show all because it
1641            could go agains the gconf setting regarding showing or not
1642            the toolbar of the editor window */
1643         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1644 }
1645
1646
1647
1648 ModestWindow*
1649 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, const gchar *mailbox, gboolean preserve_is_rich)
1650 {
1651         GObject *obj;
1652         ModestWindowPrivate *parent_priv;
1653         ModestMsgEditWindowPrivate *priv;
1654         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
1655         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
1656         ModestWindowMgr *mgr = NULL;
1657
1658         g_return_val_if_fail (msg, NULL);
1659         g_return_val_if_fail (account_name, NULL);
1660
1661         mgr = modest_runtime_get_window_mgr ();
1662         
1663         obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr));
1664
1665         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj);
1666         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1667
1668         /* Menubar. Update the state of some toggles */
1669         priv->from_field_protos = get_transports ();
1670         priv->original_mailbox = NULL;
1671         modest_selector_picker_set_pair_list (MODEST_SELECTOR_PICKER (priv->from_field), priv->from_field_protos);
1672         modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field), (gpointer) account_name);
1673         priv->last_from_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1674         if (mailbox && modest_pair_list_find_by_first_as_string (priv->from_field_protos, mailbox)) {
1675                 modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field), (gpointer) mailbox);
1676                 priv->original_mailbox = g_strdup (mailbox);
1677         } else if (modest_account_mgr_account_is_multimailbox (modest_runtime_get_account_mgr (), account_name, NULL)) {
1678                 /* We set the first mailbox as the active mailbox */
1679                 priv->original_mailbox = multimailbox_get_default_mailbox (account_name);
1680                 if (priv->original_mailbox != NULL)
1681                         modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field),
1682                                                               (gpointer) priv->original_mailbox);
1683                 else
1684                         modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field),
1685                                                               (gpointer) account_name);
1686         } else {
1687                 modest_selector_picker_set_active_id (MODEST_SELECTOR_PICKER (priv->from_field), (gpointer) account_name);
1688         }
1689         priv->last_from_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1690         update_branding (MODEST_MSG_EDIT_WINDOW (obj), priv->last_from_account);
1691         hildon_button_set_title (HILDON_BUTTON (priv->from_field),
1692                                  _("mail_va_from"));
1693         hildon_button_set_value (HILDON_BUTTON (priv->from_field), 
1694                                  hildon_touch_selector_get_current_text 
1695                                  (HILDON_TOUCH_SELECTOR (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (priv->from_field)))));
1696         modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj));
1697         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
1698
1699         /* Init window */
1700         connect_signals (MODEST_MSG_EDIT_WINDOW(obj));
1701
1702         restore_settings (MODEST_MSG_EDIT_WINDOW(obj));
1703                 
1704         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
1705         modest_window_set_active_mailbox (MODEST_WINDOW(obj), priv->original_mailbox);
1706
1707         priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL;
1708
1709         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
1710         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
1711         /* Add common dimming rules */
1712         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
1713                                               modest_msg_edit_window_toolbar_dimming_entries,
1714                                               G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries),
1715                                               MODEST_WINDOW (obj));
1716         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_toolitem,
1717                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1718                                                     MODEST_WINDOW (obj));
1719         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem,
1720                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1721                                                     MODEST_WINDOW (obj));
1722         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem,
1723                                                     G_CALLBACK (modest_ui_dimming_rules_on_set_style),
1724                                                     MODEST_WINDOW (obj));
1725         modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->send_button,
1726                                                     G_CALLBACK (modest_ui_dimming_rules_on_send),
1727                                                     MODEST_WINDOW (obj));
1728         /* Insert dimming rules group for this window */
1729         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
1730         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
1731
1732         /* Setup app menu */
1733         setup_menu (MODEST_MSG_EDIT_WINDOW (obj));
1734
1735         /* Checks the dimming rules */
1736         g_object_unref (toolbar_rules_group);
1737         g_object_unref (clipboard_rules_group);
1738         gtk_widget_hide (priv->priority_icon);
1739         gtk_widget_queue_resize (priv->subject_box);
1740         set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich);
1741
1742         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj));
1743
1744         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1745         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1746         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1747         priv->update_caption_visibility = TRUE;
1748
1749         modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE);
1750
1751         /* Track account-removed signal, this window should be closed
1752            in the case we're creating a mail associated to the account
1753            that is deleted */
1754         priv->account_removed_handler_id = 
1755                 g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
1756                                   "account_removed",
1757                                   G_CALLBACK(on_account_removed),
1758                                   obj);
1759
1760         modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj));
1761
1762         return (ModestWindow*) obj;
1763 }
1764
1765 static gint
1766 get_formatted_data_cb (const gchar *buffer, gpointer user_data)
1767 {
1768         GString **string_buffer = (GString **) user_data;
1769
1770         *string_buffer = g_string_append (*string_buffer, buffer);
1771    
1772         return 0;
1773 }
1774
1775 /**
1776  * @result: A new string which should be freed with g_free().
1777  */
1778 static gchar *
1779 get_formatted_data (ModestMsgEditWindow *edit_window)
1780 {
1781         ModestMsgEditWindowPrivate *priv;
1782         GString *string_buffer = g_string_new ("");
1783         
1784         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1785
1786         wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer);
1787
1788         modest_text_utils_hyperlinkify (string_buffer);
1789
1790         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
1791
1792         return g_string_free (string_buffer, FALSE);
1793                                                                         
1794 }
1795
1796 MsgData * 
1797 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
1798 {
1799         MsgData *data;
1800         const gchar *account_name;
1801         ModestMsgEditWindowPrivate *priv;
1802         TnyIterator *att_iter;
1803         const gchar *picker_active_id;
1804         
1805         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
1806
1807         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
1808         
1809         picker_active_id = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
1810         g_return_val_if_fail (picker_active_id, NULL);
1811         account_name = modest_utils_get_account_name_from_recipient (picker_active_id, NULL);
1812         
1813         /* don't free these (except from) */
1814         data = g_slice_new0 (MsgData);
1815         data->from    =  g_strdup ((gchar *) modest_selector_picker_get_active_display_name (MODEST_SELECTOR_PICKER (priv->from_field)));
1816         data->account_name = g_strdup (account_name);
1817         data->to      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field)));
1818         data->cc      =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field)));
1819         data->bcc     =  g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field)));
1820         data->subject =  g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field)));
1821         data->references = g_strdup (priv->references);
1822         data->in_reply_to = g_strdup (priv->in_reply_to);
1823         if (priv->draft_msg) {
1824                 data->draft_msg = g_object_ref (priv->draft_msg);
1825         } else if (priv->outbox_msg) {
1826                 data->draft_msg = g_object_ref (priv->outbox_msg);
1827         } else {
1828                 data->draft_msg = NULL;
1829         }
1830
1831         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
1832         GtkTextIter b, e;
1833         gtk_text_buffer_get_bounds (buf, &b, &e);
1834         data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */
1835
1836         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)))
1837                 data->html_body = get_formatted_data (edit_window); /* returns a copy. */
1838         else
1839                 data->html_body = NULL;
1840
1841         /* deep-copy the data */
1842         att_iter = tny_list_create_iterator (priv->attachments);
1843         data->attachments = NULL;
1844         while (!tny_iterator_is_done (att_iter)) {
1845                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1846                 if (!(TNY_IS_MIME_PART(part))) {
1847                         g_warning ("strange data in attachment list");
1848                         g_object_unref (part);
1849                         tny_iterator_next (att_iter);
1850                         continue;
1851                 }
1852                 data->attachments = g_list_append (data->attachments,
1853                                                    part);
1854                 tny_iterator_next (att_iter);
1855         }
1856         g_object_unref (att_iter);
1857
1858         GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer));
1859         att_iter = tny_list_create_iterator (priv->images);
1860         data->images = NULL;
1861         while (!tny_iterator_is_done (att_iter)) {
1862                 TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter);
1863                 const gchar *cid;
1864                 if (!(TNY_IS_MIME_PART(part))) {
1865                         g_warning ("strange data in attachment list");
1866                         g_object_unref (part);
1867                         tny_iterator_next (att_iter);
1868                         continue;
1869                 }
1870                 cid = tny_mime_part_get_content_id (part);
1871                 if (cid) {                      
1872                         gchar *image_tag_id;
1873                         GtkTextTag *image_tag;
1874                         GtkTextIter iter;
1875                         image_tag_id = g_strdup_printf ("image-tag-%s", cid);
1876                         image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id);
1877                         g_free (image_tag_id);
1878                         
1879                         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
1880                         if (image_tag && 
1881                             ((gtk_text_iter_has_tag (&iter, image_tag))||
1882                              (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag))))
1883                                 data->images = g_list_append (data->images,
1884                                                               g_object_ref (part));
1885                 }
1886                 g_object_unref (part);
1887                 tny_iterator_next (att_iter);
1888         }
1889         g_object_unref (att_iter);
1890         
1891         data->priority_flags = priv->priority_flags;
1892
1893         return data;
1894 }
1895
1896
1897 static void
1898 unref_gobject (GObject *obj, gpointer data)
1899 {
1900         if (!G_IS_OBJECT(obj))
1901                 return;
1902         g_object_unref (obj);
1903 }
1904
1905 void 
1906 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
1907                                                       MsgData *data)
1908 {
1909         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
1910
1911         if (!data)
1912                 return;
1913
1914         g_free (data->to);
1915         g_free (data->cc);
1916         g_free (data->bcc);
1917         g_free (data->from);
1918         g_free (data->subject);
1919         g_free (data->plain_body);
1920         g_free (data->html_body);
1921         g_free (data->account_name);
1922         g_free (data->references);
1923         g_free (data->in_reply_to);
1924         
1925         if (data->draft_msg != NULL) {
1926                 g_object_unref (data->draft_msg);
1927                 data->draft_msg = NULL;
1928         }
1929         
1930         g_list_foreach (data->attachments, (GFunc)unref_gobject,  NULL);
1931         g_list_free (data->attachments);
1932         g_list_foreach (data->images, (GFunc)unref_gobject,  NULL);
1933         g_list_free (data->images);
1934         
1935         g_slice_free (MsgData, data);
1936 }
1937
1938 void                    
1939 modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window,
1940                                        gint *parts_count,
1941                                        guint64 *parts_size)
1942 {
1943         ModestMsgEditWindowPrivate *priv;
1944
1945         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
1946
1947         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size);
1948
1949         /* TODO: add images */
1950         *parts_size += priv->images_size;
1951         *parts_count += priv->images_count;
1952
1953 }
1954
1955 ModestMsgEditFormat
1956 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
1957 {
1958         gboolean rich_text;
1959         ModestMsgEditWindowPrivate *priv = NULL;
1960         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML);
1961
1962         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1963
1964         rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
1965         if (rich_text)
1966                 return MODEST_MSG_EDIT_FORMAT_HTML;
1967         else
1968                 return MODEST_MSG_EDIT_FORMAT_TEXT;
1969 }
1970
1971 void
1972 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
1973                                    ModestMsgEditFormat format)
1974 {
1975         ModestMsgEditWindowPrivate *priv;
1976         ModestWindowPrivate *parent_priv;
1977
1978         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
1979         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
1980         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
1981
1982         switch (format) {
1983         case MODEST_MSG_EDIT_FORMAT_HTML:
1984                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
1985                 if (parent_priv->toolbar) gtk_widget_show (parent_priv->toolbar);
1986                 break;
1987         case MODEST_MSG_EDIT_FORMAT_TEXT:
1988                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
1989                 if (parent_priv->toolbar) gtk_widget_hide (parent_priv->toolbar);
1990                 break;
1991         default:
1992                 g_return_if_reached ();
1993         }
1994 }
1995
1996 ModestMsgEditFormatState *
1997 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
1998 {
1999         ModestMsgEditFormatState *format_state = NULL;
2000         ModestMsgEditWindowPrivate *priv;
2001         WPTextBufferFormat *buffer_format;
2002
2003         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
2004
2005         buffer_format = g_new0 (WPTextBufferFormat, 1);
2006         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2007
2008         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE);
2009
2010         format_state = g_new0 (ModestMsgEditFormatState, 1);
2011         format_state->bold = buffer_format->bold&0x1;
2012         format_state->italics = buffer_format->italic&0x1;
2013         format_state->bullet = buffer_format->bullet&0x1;
2014         format_state->color = buffer_format->color;
2015         format_state->font_size = buffer_format->font_size;
2016         format_state->font_family = wp_get_font_name (buffer_format->font);
2017         format_state->justification = buffer_format->justification;
2018         g_free (buffer_format);
2019
2020         return format_state;
2021  
2022 }
2023
2024 void
2025 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self,
2026                                          const ModestMsgEditFormatState *format_state)
2027 {
2028         ModestMsgEditWindowPrivate *priv;
2029         WPTextBufferFormat *buffer_format;
2030         WPTextBufferFormat *current_format;
2031
2032         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2033         g_return_if_fail (format_state != NULL);
2034
2035         buffer_format = g_new0 (WPTextBufferFormat, 1);
2036         current_format = g_new0 (WPTextBufferFormat, 1);
2037
2038         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2039         gtk_widget_grab_focus (priv->msg_body);
2040         buffer_format->bold = (format_state->bold != FALSE);
2041         buffer_format->italic = (format_state->italics != FALSE);
2042         buffer_format->color = format_state->color;
2043         buffer_format->font_size = format_state->font_size;
2044         buffer_format->font = wp_get_font_index (format_state->font_family, 0);
2045         buffer_format->justification = format_state->justification;
2046         buffer_format->bullet = format_state->bullet;
2047
2048         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE);
2049
2050         buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1));
2051         buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1));
2052         buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color));
2053         buffer_format->cs.font_size =  (buffer_format->font_size != current_format->font_size);
2054         buffer_format->cs.font = (buffer_format->font != current_format->font);
2055         buffer_format->cs.justification = (buffer_format->justification != current_format->justification);
2056         buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet);
2057
2058         wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer));
2059         if (buffer_format->cs.bold) {
2060                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD,
2061                                               GINT_TO_POINTER (buffer_format->bold&0x1));
2062         }
2063         if (buffer_format->cs.italic) {
2064                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC,
2065                                               GINT_TO_POINTER (buffer_format->italic&0x1));
2066         }
2067         if (buffer_format->cs.color) {
2068                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2069                                               GINT_TO_POINTER (&(buffer_format->color)));
2070         }
2071         if (buffer_format->cs.font_size) {
2072                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2073                                               GINT_TO_POINTER (buffer_format->font_size));
2074         }
2075         if (buffer_format->cs.justification) {
2076                 switch (buffer_format->justification) {
2077                 case GTK_JUSTIFY_LEFT:
2078                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT,
2079                                                       GINT_TO_POINTER(TRUE));
2080                         break;
2081                 case GTK_JUSTIFY_CENTER:
2082                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER,
2083                                                       GINT_TO_POINTER(TRUE));
2084                         break;
2085                 case GTK_JUSTIFY_RIGHT:
2086                         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT,
2087                                                       GINT_TO_POINTER(TRUE));
2088                         break;
2089                 default:
2090                         break;
2091                 }
2092                         
2093         }
2094         if (buffer_format->cs.font) {
2095                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT,
2096                                               GINT_TO_POINTER (buffer_format->font));
2097         }
2098         wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer));
2099         if (buffer_format->cs.bullet) {
2100                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET,
2101                                               GINT_TO_POINTER ((buffer_format->bullet)?1:0));
2102         }
2103 /*      wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */
2104         
2105         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self);
2106         
2107         g_free (buffer_format);
2108         g_free (current_format);
2109
2110         /* Check dimming rules */
2111         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2112         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2113 }
2114
2115 static void
2116 text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window)
2117 {
2118         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2119         GtkAction *action;
2120         ModestWindowPrivate *parent_priv;
2121         ModestMsgEditWindowPrivate *priv;
2122         
2123         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2124         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2125
2126         if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) {
2127                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu");
2128                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2129                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2130         } else {
2131                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatPlainTextMenu");
2132                 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
2133                         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE);
2134         }
2135
2136         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2137
2138         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold");
2139         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold);
2140
2141         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics");
2142         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic);
2143
2144 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */
2145 /*      modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */
2146
2147         action = NULL;
2148         switch (buffer_format->justification)
2149         {
2150         case GTK_JUSTIFY_LEFT:
2151                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu");
2152                 break;
2153         case GTK_JUSTIFY_CENTER:
2154                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu");
2155                 break;
2156         case GTK_JUSTIFY_RIGHT:
2157                 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu");
2158                 break;
2159         default:
2160                 break;
2161         }
2162         
2163         if (action != NULL)
2164                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2165         
2166         g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), 
2167                                          G_CALLBACK (modest_msg_edit_window_color_button_change),
2168                                          window);
2169         hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color));
2170         g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), 
2171                                            G_CALLBACK (modest_msg_edit_window_color_button_change),
2172                                            window);
2173
2174         if (priv->current_size_index != buffer_format->font_size) {
2175                 GtkTreeIter iter;
2176                 GtkTreePath *path;
2177
2178                 path = gtk_tree_path_new_from_indices (buffer_format->font_size, -1);
2179                 if (gtk_tree_model_get_iter (priv->sizes_model, &iter, path)) {
2180                         gchar *size_text;
2181                         gchar *markup;
2182
2183                         priv->current_size_index = buffer_format->font_size;
2184
2185                         gtk_tree_model_get (priv->sizes_model, &iter, 0, &size_text, -1);
2186                         markup = g_strconcat ("<span font_family='Sans'>", 
2187                                               size_text, "</span>", NULL);
2188                         
2189                         gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2190                         g_free (markup);
2191                         g_free (size_text);
2192                 }
2193                 gtk_tree_path_free (path);              
2194         }
2195
2196         if (priv->current_face_index != buffer_format->font) {
2197                 GtkTreeIter iter;
2198                 GtkTreePath *path;
2199
2200                 path = gtk_tree_path_new_from_indices (buffer_format->font, -1);
2201                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2202                         gchar *face_name;
2203                         gchar *markup;
2204
2205                         priv->current_face_index = buffer_format->font;
2206                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2207                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2208                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2209                         g_free (face_name);
2210                         g_free (markup);
2211                 }
2212
2213         }
2214
2215         g_free (buffer_format);
2216
2217 }
2218
2219
2220 void
2221 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
2222 {
2223         WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1);
2224         ModestMsgEditWindowPrivate *priv;
2225         GtkWidget *dialog = NULL;
2226
2227         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2228         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE);
2229
2230         dialog = hildon_color_chooser_new ();
2231         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color));
2232         g_free (buffer_format);
2233
2234         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2235                 GdkColor col;
2236                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2237                 wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR,
2238                                               (gpointer) &col);
2239         }
2240         gtk_widget_destroy (dialog);
2241 }
2242
2243
2244 void
2245 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
2246 {
2247         ModestMsgEditWindowPrivate *priv;
2248         GtkWidget *dialog = NULL;
2249         GdkColor *old_color = NULL;
2250         
2251         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2252         old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer));
2253         
2254         dialog = hildon_color_chooser_new ();
2255         hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color);
2256
2257         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { 
2258                 GdkColor col;
2259                 hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col);
2260                 wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col);
2261         }
2262         gtk_widget_destroy (dialog);
2263 }
2264
2265
2266 static TnyStream*
2267 create_stream_for_uri (const gchar* uri)
2268 {
2269         if (!uri)
2270                 return NULL;
2271                 
2272         TnyStream *result = NULL;
2273
2274         GnomeVFSHandle *handle = NULL;
2275         GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2276         if (test == GNOME_VFS_OK) {
2277                 TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle));
2278                 /* Streams over OBEX (Bluetooth) are not seekable but
2279                  * we expect them to be (we might need to read them
2280                  * several times). So if this is a Bluetooth URI just
2281                  * read the whole file into memory (this is not a fast
2282                  * protocol so we can assume that these files are not
2283                  * going to be very big) */
2284                 if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)||
2285                     (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) {
2286                         TnyStream *memstream = tny_camel_mem_stream_new ();
2287                         tny_stream_write_to_stream (vfssstream, memstream);
2288                         g_object_unref (vfssstream);
2289                         result = memstream;
2290                 } else {
2291                         result = vfssstream;
2292                 }
2293         }
2294         
2295         return result;
2296 }
2297
2298 void
2299 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
2300 {
2301         
2302         ModestMsgEditWindowPrivate *priv;
2303         GtkWidget *dialog = NULL;
2304         gint response = 0;
2305         GSList *uris = NULL;
2306         GSList *uri_node = NULL;
2307
2308         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2309
2310         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN);
2311         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title"));
2312         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2313
2314         modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog));
2315
2316         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2317                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2318
2319         response = gtk_dialog_run (GTK_DIALOG (dialog));
2320         switch (response) {
2321         case GTK_RESPONSE_OK:
2322         {
2323                 gchar *current_folder;
2324                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2325                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2326                 if (current_folder && current_folder != '\0') {
2327                         GError *err = NULL;
2328                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_INSERT_IMAGE_PATH, 
2329                                                 current_folder, &err);
2330                         if (err != NULL) {
2331                                 g_debug ("Error storing latest used folder: %s", err->message);
2332                                 g_error_free (err);
2333                         }
2334                 }
2335                 g_free (current_folder);
2336         }
2337         break;
2338         default:
2339                 break;
2340         }
2341         gtk_widget_destroy (dialog);
2342
2343         g_object_ref (window);
2344         /* The operation could take some time so allow the dialog to be closed */
2345         while (gtk_events_pending ())
2346                 gtk_main_iteration ();
2347
2348         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2349                 const gchar *uri;
2350                 GnomeVFSHandle *handle = NULL;
2351                 GnomeVFSResult result;
2352                 GtkTextIter position;
2353                 GtkTextMark *insert_mark;
2354
2355                 uri = (const gchar *) uri_node->data;
2356                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2357                 if (result == GNOME_VFS_OK) {
2358                         GdkPixbuf *pixbuf;
2359                         GnomeVFSFileInfo *info;
2360                         gchar *filename, *basename, *escaped_filename;
2361                         TnyMimePart *mime_part;
2362                         gchar *content_id;
2363                         const gchar *mime_type = NULL;
2364                         GnomeVFSURI *vfs_uri;
2365                         guint64 stream_size;
2366
2367                         gnome_vfs_close (handle);
2368                         vfs_uri = gnome_vfs_uri_new (uri);
2369
2370                         escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2371                         filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2372                         g_free (escaped_filename);
2373                         gnome_vfs_uri_unref (vfs_uri);
2374                         info = gnome_vfs_file_info_new ();
2375
2376                         if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE
2377                                                      | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) 
2378                             == GNOME_VFS_OK)
2379                                 mime_type = gnome_vfs_file_info_get_mime_type (info);
2380
2381                         mime_part = tny_platform_factory_new_mime_part
2382                                 (modest_runtime_get_platform_factory ());
2383
2384                         TnyStream *stream = create_stream_for_uri (uri);
2385
2386                         if (stream == NULL) {
2387
2388                                 modest_platform_information_banner (NULL, NULL, 
2389                                                                     _FM("sfil_ib_opening_not_allowed"));
2390                                 g_free (filename);
2391                                 g_object_unref (mime_part);
2392                                 gnome_vfs_file_info_unref (info);
2393                                 continue;
2394                         }
2395
2396                         tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2397
2398                         content_id = g_strdup_printf ("%d", priv->next_cid);
2399                         tny_mime_part_set_content_id (mime_part, content_id);
2400                         g_free (content_id);
2401                         priv->next_cid++;
2402
2403                         basename = g_path_get_basename (filename);
2404                         tny_mime_part_set_filename (mime_part, basename);
2405                         g_free (basename);
2406
2407                         pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size, window);
2408
2409                         if (pixbuf != NULL) {
2410                                 priv->images_size += stream_size;
2411                                 priv->images_count ++;
2412                                 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer));
2413                                 gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark);
2414                                 wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, 
2415                                                              tny_mime_part_get_content_id (mime_part), pixbuf);
2416                                 g_object_unref (pixbuf);
2417
2418                                 tny_list_prepend (priv->images, (GObject *) mime_part);
2419                                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2420                         } else {
2421                                 modest_platform_information_banner (NULL, NULL,
2422                                                                     _("mail_ib_file_operation_failed"));
2423                         }
2424
2425                         g_free (filename);
2426                         g_object_unref (mime_part);
2427                         gnome_vfs_file_info_unref (info);
2428
2429                 }
2430         }
2431         g_object_unref (window);
2432 }
2433
2434 static void
2435 on_attach_file_response (GtkDialog *dialog,
2436                          gint       arg1,
2437                          gpointer   user_data)
2438 {
2439         GSList *uris = NULL;
2440         GSList *uri_node;
2441         GnomeVFSFileSize total_size, allowed_size;
2442         ModestMsgEditWindow *window;
2443         ModestMsgEditWindowPrivate *priv;
2444         gint att_num;
2445         guint64 att_size;
2446
2447         switch (arg1) {
2448         case GTK_RESPONSE_OK:
2449         {
2450                 gchar *current_folder;
2451
2452                 uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2453                 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2454                 if (current_folder && current_folder != '\0') {
2455                         GError *err = NULL;
2456                         modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_ATTACH_FILE_PATH, 
2457                                                 current_folder, &err);
2458                         if (err != NULL) {
2459                                 g_debug ("Error storing latest used folder: %s", err->message);
2460                                 g_error_free (err);
2461                         }
2462                 }
2463                 g_free (current_folder);
2464         }
2465         break;
2466         default:
2467                 break;
2468         }
2469
2470         window = MODEST_MSG_EDIT_WINDOW (user_data);
2471         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2472
2473         /* allowed size is the maximum size - what's already there */
2474         modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2475                                            &att_num, &att_size);
2476         allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size;
2477
2478         total_size = 0;
2479         for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) {
2480
2481                 const gchar *uri = (const gchar *) uri_node->data;
2482
2483                 total_size += 
2484                         modest_msg_edit_window_attach_file_one (window, uri, allowed_size);
2485
2486                 if (total_size > allowed_size) {
2487                         g_debug ("%s: total size: %u", 
2488                                    __FUNCTION__, (unsigned int)total_size);
2489                         break;
2490                 }
2491                 allowed_size -= total_size;
2492         }
2493         g_slist_foreach (uris, (GFunc) g_free, NULL);
2494         g_slist_free (uris);
2495
2496         gtk_widget_destroy (GTK_WIDGET (dialog));
2497 }
2498
2499 void
2500 modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window)
2501 {
2502         GtkWidget *dialog = NULL;
2503         ModestMsgEditWindowPrivate *priv;
2504         gchar *conf_folder;
2505
2506         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window));
2507
2508         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2509
2510         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2511                 return;
2512
2513         dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2514                                                  GTK_FILE_CHOOSER_ACTION_OPEN);
2515         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title"));
2516         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
2517                                               MODEST_CONF_LATEST_ATTACH_FILE_PATH, NULL);
2518         if (conf_folder && conf_folder[0] != '\0') {
2519                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), conf_folder);
2520         } else {
2521                 gchar *docs_folder;
2522                 /* Set the default folder to documents folder */
2523                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
2524                 if (!docs_folder) {
2525                         /* fallback */
2526                         docs_folder = g_build_filename (g_getenv (MODEST_MAEMO_UTILS_MYDOCS_ENV),
2527                                                         ".documents", NULL);
2528                 }
2529                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), docs_folder);
2530                 g_free (docs_folder);
2531         }
2532         g_free (conf_folder);
2533         gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2534         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2535                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
2536
2537         /* Connect to response & show */
2538         g_signal_connect (dialog, "response", 
2539                           G_CALLBACK (on_attach_file_response), window);
2540         gtk_widget_show (dialog);
2541 }
2542
2543
2544 GnomeVFSFileSize
2545 modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window,
2546                                         const gchar *uri, 
2547                                         GnomeVFSFileSize allowed_size)
2548
2549 {
2550         GnomeVFSHandle *handle = NULL;
2551         ModestMsgEditWindowPrivate *priv;
2552         GnomeVFSResult result;
2553         GnomeVFSFileSize size = 0;
2554         g_return_val_if_fail (window, 0);
2555         g_return_val_if_fail (uri, 0);
2556
2557         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2558
2559         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2560         if (result == GNOME_VFS_OK) {
2561                 TnyMimePart *mime_part;
2562                 TnyStream *stream;
2563                 const gchar *mime_type = NULL;
2564                 gchar *basename;
2565                 gchar *escaped_filename;
2566                 gchar *filename;
2567                 gchar *content_id;
2568                 GnomeVFSFileInfo *info;
2569                 GnomeVFSURI *vfs_uri;
2570
2571                 gnome_vfs_close (handle);
2572                 vfs_uri = gnome_vfs_uri_new (uri);
2573                 
2574
2575                 escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri));
2576                 filename = gnome_vfs_unescape_string_for_display (escaped_filename);
2577                 g_free (escaped_filename);
2578                 gnome_vfs_uri_unref (vfs_uri);
2579
2580                 info = gnome_vfs_file_info_new ();
2581                 
2582                 if (gnome_vfs_get_file_info (uri, 
2583                                              info, 
2584                                              GNOME_VFS_FILE_INFO_GET_MIME_TYPE)
2585                     == GNOME_VFS_OK)
2586                         mime_type = gnome_vfs_file_info_get_mime_type (info);
2587                 mime_part = tny_platform_factory_new_mime_part
2588                         (modest_runtime_get_platform_factory ());
2589                 
2590                 /* try to get the attachment's size; this may fail for weird
2591                  * file systems, like obex, upnp... */
2592                 if (allowed_size != 0 &&
2593                     info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
2594                         size = info->size;
2595                         if (size > allowed_size) {
2596                                 modest_platform_information_banner (NULL, NULL,
2597                                                                     _("mail_ib_error_attachment_size"));
2598                                 g_free (filename);
2599                                 return 0;
2600                         }
2601                 } else
2602                         g_debug ("%s: could not get attachment size", __FUNCTION__);
2603                 
2604                 stream = create_stream_for_uri (uri);
2605                 
2606                 if (stream == NULL) {
2607
2608                         modest_platform_information_banner (NULL, NULL, _FM("sfil_ib_opening_not_allowed"));
2609
2610                         g_object_unref (mime_part);
2611                         g_free (filename);
2612                         gnome_vfs_file_info_unref (info);
2613                         return 0;
2614                 }
2615
2616                 tny_mime_part_construct (mime_part, stream, mime_type, "base64");
2617                 g_object_unref (stream);
2618                 
2619                 content_id = g_strdup_printf ("%d", priv->next_cid);
2620                 tny_mime_part_set_content_id (mime_part, content_id);
2621                 g_free (content_id);
2622                 priv->next_cid++;
2623                 
2624                 basename = g_path_get_basename (filename);
2625                 tny_mime_part_set_filename (mime_part, basename);
2626                 g_free (basename);
2627                 
2628                 tny_list_prepend (priv->attachments, (GObject *) mime_part);
2629                 modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2630                                                         mime_part,
2631                                                         info->size == 0, info->size);
2632                 gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
2633                 gtk_widget_show_all (priv->attachments_caption);
2634                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2635                 g_free (filename);
2636                 g_object_unref (mime_part);
2637                 gnome_vfs_file_info_unref (info);
2638         }
2639
2640         return size;
2641 }
2642
2643 void
2644 modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window,
2645                                            TnyList *att_list)
2646 {
2647         ModestMsgEditWindowPrivate *priv;
2648         TnyIterator *iter;
2649
2650         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2651         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2652
2653         if (att_list == NULL) {
2654                 att_list = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
2655                 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), att_list, TRUE)) {
2656                         g_object_unref (att_list);
2657                         return;
2658                 }
2659         } else {
2660                 g_object_ref (att_list);
2661         }
2662
2663         if (tny_list_get_length (att_list) == 0) {
2664                 hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove"));
2665         } else {
2666                 gboolean dialog_response;
2667                 gchar *message = NULL;
2668                 gchar *filename = NULL;
2669
2670                 if (tny_list_get_length (att_list) == 1) {
2671                         TnyMimePart *part;
2672                         iter = tny_list_create_iterator (att_list);
2673                         part = (TnyMimePart *) tny_iterator_get_current (iter);
2674                         g_object_unref (iter);
2675                         if (TNY_IS_MSG (part)) {
2676                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (part));
2677                                 if (header) {
2678                                         filename = tny_header_dup_subject (header);
2679                                         g_object_unref (header);
2680                                 }
2681                                 if (filename == NULL) {
2682                                         filename = g_strdup (_("mail_va_no_subject"));
2683                                 }
2684                         } else {
2685                                 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2686                         }
2687                         g_object_unref (part);
2688                 } else {
2689                         filename = g_strdup ("");
2690                 }
2691                 message = g_strdup_printf (ngettext("emev_nc_delete_attachment", 
2692                                                     "emev_nc_delete_attachments",
2693                                                     tny_list_get_length (att_list)), filename);
2694                 g_free (filename);
2695
2696                 dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), 
2697                                                                            message);
2698                 g_free (message);
2699
2700                 if (dialog_response != GTK_RESPONSE_OK) {
2701                         g_object_unref (att_list);
2702                         return;
2703                 }
2704
2705                 for (iter = tny_list_create_iterator (att_list);
2706                      !tny_iterator_is_done (iter);
2707                      tny_iterator_next (iter)) {
2708                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2709                         const gchar *att_id;
2710                         tny_list_remove (priv->attachments, (GObject *) mime_part);
2711
2712                         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
2713                                                                    mime_part);
2714                         if (tny_list_get_length (priv->attachments) == 0)
2715                                 gtk_widget_hide (priv->attachments_caption);
2716                         att_id = tny_mime_part_get_content_id (mime_part);
2717                         if (att_id != NULL)
2718                                 text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)),
2719                                                                  att_id);
2720                         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
2721                         g_object_unref (mime_part);
2722                 }
2723                 g_object_unref (iter);
2724         }
2725
2726         g_object_unref (att_list);
2727
2728         /* if the last attachment has been removed, focus the Subject: field */
2729         if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view)))
2730                 gtk_widget_grab_focus (priv->subject_field);
2731 }
2732
2733 static void
2734 modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window,
2735                                             gpointer userdata)
2736 {
2737         ModestMsgEditWindowPrivate *priv;
2738         GdkColor new_color;
2739
2740         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2741
2742         hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &new_color);
2743
2744         wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) &new_color);
2745
2746         gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body);
2747 }
2748
2749 static void
2750 font_size_clicked (GtkToolButton *button,
2751                    ModestMsgEditWindow *window)
2752 {
2753         ModestMsgEditWindowPrivate *priv;
2754         GtkWidget *selector, *dialog;
2755         
2756         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2757
2758         selector = hildon_touch_selector_new ();
2759         hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector), priv->sizes_model, TRUE);
2760         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_size_index);
2761
2762         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2763         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2764         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_size"));
2765
2766         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2767                 gint new_index;
2768                 gchar *size_text;
2769                 gchar *markup;
2770                 WPTextBufferFormat format;
2771
2772                 new_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2773
2774                 memset (&format, 0, sizeof (format));
2775                 wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE);
2776
2777                 format.cs.font_size = TRUE;
2778                 format.cs.text_position = TRUE;
2779                 format.cs.font = TRUE;
2780                 format.font_size = new_index;
2781 /*              wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */
2782
2783                 if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE,
2784                                                    GINT_TO_POINTER (new_index)))
2785                         wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2786                 
2787                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2788                 size_text = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
2789                 markup = g_strconcat ("<span font_family='", DEFAULT_SIZE_BUTTON_FONT_FAMILY, "'>", 
2790                                       size_text, "</span>", NULL);
2791                 g_free (size_text);
2792                 gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup);
2793                 g_free (markup);
2794
2795         }
2796         gtk_widget_destroy (dialog);
2797
2798         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2799
2800 }
2801
2802 static void
2803 font_face_clicked (GtkToolButton *button,
2804                    ModestMsgEditWindow *window)
2805 {
2806         ModestMsgEditWindowPrivate *priv;
2807         GtkWidget *selector, *dialog;
2808         GtkCellRenderer *renderer;
2809         
2810         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2811
2812         selector = hildon_touch_selector_new ();
2813         renderer = gtk_cell_renderer_text_new ();
2814         g_object_set (G_OBJECT (renderer), "alignment", PANGO_ALIGN_CENTER, "xalign", 0.5, NULL);
2815         hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector), priv->faces_model, 
2816                                              renderer, "family", 0, "text", 0, NULL);
2817         hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, priv->current_face_index);
2818
2819         dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
2820         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector));
2821         gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_font_face"));
2822
2823         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
2824                 gint new_font_index;
2825                 GtkTreePath *path;
2826                 GtkTreeIter iter;
2827
2828                 new_font_index = hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (selector), 0);
2829                 path = gtk_tree_path_new_from_indices (new_font_index, -1);
2830                 if (gtk_tree_model_get_iter (priv->faces_model, &iter, path)) {
2831                         gchar *face_name;
2832                         gchar *markup;
2833
2834                         gtk_tree_model_get (priv->faces_model, &iter, 0, &face_name, -1);
2835
2836                         if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, 
2837                                                            GINT_TO_POINTER(new_font_index)))
2838                                 wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body));
2839
2840                         markup = g_strconcat ("<span font_family='", face_name, "'>Tt</span>", NULL);
2841                         gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup);
2842
2843                         text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window));
2844                         g_free (face_name);
2845                         g_free (markup);
2846                 }
2847                 gtk_tree_path_free (path);
2848
2849         }
2850         gtk_widget_destroy (dialog);
2851
2852         gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body));
2853
2854 }
2855
2856 void
2857 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
2858                                 gboolean show)
2859 {
2860         ModestMsgEditWindowPrivate *priv = NULL;
2861         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2862
2863         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2864         if (!priv->update_caption_visibility)
2865                 return;
2866
2867         gtk_widget_set_no_show_all (priv->cc_caption, TRUE);
2868         if (show)
2869                 gtk_widget_show (priv->cc_caption);
2870         else
2871                 gtk_widget_hide (priv->cc_caption);
2872
2873         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL);
2874 }
2875
2876 void
2877 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
2878                                  gboolean show)
2879 {
2880         ModestMsgEditWindowPrivate *priv = NULL;
2881         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2882
2883         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2884         if (!priv->update_caption_visibility)
2885                 return;
2886
2887         gtk_widget_set_no_show_all (priv->bcc_caption, TRUE);
2888         if (show)
2889                 gtk_widget_show (priv->bcc_caption);
2890         else
2891                 gtk_widget_hide (priv->bcc_caption);
2892
2893         modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL);
2894 }
2895
2896 static void
2897 modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window,
2898                                          ModestRecptEditor *editor)
2899 {
2900         ModestMsgEditWindowPrivate *priv;
2901
2902         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2903         g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor)));
2904         
2905         /* we check for low-mem; in that case, show a warning, and don't allow
2906          * for the addressbook
2907          */
2908         if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE))
2909                 return;
2910
2911         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2912
2913         if (editor == NULL) {
2914                 GtkWidget *view_focus, *parent;
2915                 view_focus = gtk_window_get_focus (GTK_WINDOW (window));
2916
2917                 /* This code should be kept in sync with ModestRecptEditor. The
2918                    textview inside the recpt editor is the one that really gets the
2919                    focus. As it's inside a scrolled window, and this one inside the
2920                    hbox recpt editor inherits from, we'll need to go up in the 
2921                    hierarchy to know if the text view is part of the recpt editor
2922                    or if it's a different text entry */
2923                 parent = gtk_widget_get_parent (view_focus);
2924                 if (parent && MODEST_IS_RECPT_EDITOR (parent))
2925                         editor = MODEST_RECPT_EDITOR (parent);
2926
2927                 if (editor == NULL)
2928                         editor = MODEST_RECPT_EDITOR (priv->to_field);
2929         }
2930
2931         modest_address_book_select_addresses (editor, GTK_WINDOW (window));
2932 }
2933
2934 void
2935 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
2936 {
2937         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2938
2939         modest_msg_edit_window_open_addressbook (window, NULL);
2940 }
2941
2942 static void
2943 modest_msg_edit_window_show_toolbar (ModestWindow *self,
2944                                      gboolean show_toolbar)
2945 {
2946         ModestWindowPrivate *parent_priv;
2947         ModestMsgEditWindowPrivate *priv;
2948
2949         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
2950         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
2951         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2952
2953         /* We can not just use the code of
2954            modest_msg_edit_window_setup_toolbar because it has a
2955            mixture of both initialization and creation code. */
2956         if (show_toolbar) {
2957                 gint current_format;
2958                 current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
2959                         ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
2960                 if (current_format == MODEST_FILE_FORMAT_PLAIN_TEXT) {
2961                         gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2962                 } else {
2963                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2964                 }
2965         } else {
2966                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2967         }
2968 }
2969
2970 void
2971 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
2972                                            TnyHeaderFlags priority_flags)
2973 {
2974         ModestMsgEditWindowPrivate *priv;
2975         ModestWindowPrivate *parent_priv;
2976
2977         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
2978
2979         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
2980         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
2981
2982         if (priv->priority_flags != priority_flags) {
2983                 GtkAction *priority_action = NULL;
2984
2985                 priv->priority_flags = priority_flags;
2986
2987                 switch (priority_flags) {
2988                 case TNY_HEADER_FLAG_HIGH_PRIORITY:
2989                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2990                                                       MODEST_HEADER_ICON_HIGH, 
2991                                                       HILDON_ICON_SIZE_SMALL);
2992                         gtk_widget_show (priv->priority_icon);
2993                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
2994                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu");
2995                         break;
2996                 case TNY_HEADER_FLAG_LOW_PRIORITY:
2997                         gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon),
2998                                                       MODEST_HEADER_ICON_LOW,
2999                                                       HILDON_ICON_SIZE_SMALL);
3000                         gtk_widget_show (priv->priority_icon);
3001                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3002                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu");
3003                         break;
3004                 default:
3005                         gtk_widget_hide (priv->priority_icon);
3006                         priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager,
3007                                                                      "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu");
3008                         break;
3009                 }
3010                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE);
3011                 gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3012         }
3013         gtk_widget_queue_resize (priv->subject_box);
3014 }
3015
3016 void
3017 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
3018                                         gint file_format)
3019 {
3020         ModestMsgEditWindowPrivate *priv;
3021         ModestWindowPrivate *parent_priv;
3022         gint current_format;
3023
3024         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3025
3026         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3027         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3028
3029         current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))
3030                 ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT;
3031
3032         gtk_widget_set_no_show_all (GTK_WIDGET (parent_priv->toolbar), TRUE);
3033
3034         if (current_format != file_format) {
3035                 switch (file_format) {
3036                 case MODEST_FILE_FORMAT_FORMATTED_TEXT:
3037                         wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE);
3038                         remove_tags (WP_TEXT_BUFFER (priv->text_buffer));
3039                         if (parent_priv->toolbar)
3040                                 gtk_widget_show (parent_priv->toolbar);
3041                         break;
3042                 case MODEST_FILE_FORMAT_PLAIN_TEXT:
3043                 {
3044                         GtkWidget *dialog;
3045                         gint response;
3046                         dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost"));
3047                         response = gtk_dialog_run (GTK_DIALOG (dialog));
3048                         gtk_widget_destroy (dialog);
3049                         if (response == GTK_RESPONSE_OK) {
3050                                 wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE);
3051                                 if (parent_priv->toolbar)
3052                                         gtk_widget_hide (parent_priv->toolbar);
3053                         } else {
3054                                 GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatFormattedTextMenu"));
3055                                 modest_utils_toggle_action_set_active_block_notify (action, TRUE);
3056                         }
3057                 }
3058                         break;
3059                 }
3060                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3061                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3062                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3063         }
3064 }
3065
3066 void
3067 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
3068 {
3069         GtkWidget *dialog;
3070         ModestMsgEditWindowPrivate *priv;
3071         WPTextBufferFormat oldfmt, fmt;
3072         gint old_position = 0;
3073         gint response = 0;
3074         gint position = 0;
3075         gint font_size;
3076         GdkColor *color = NULL;
3077         gboolean bold, bold_set, italic, italic_set;
3078         gboolean underline, underline_set;
3079         gboolean strikethrough, strikethrough_set;
3080         gboolean position_set;
3081         gboolean font_size_set, font_set, color_set;
3082         gchar *font_name;
3083
3084         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3085         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3086         
3087         dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL);
3088         modest_window_mgr_set_modal (modest_runtime_get_window_mgr(),
3089                                      GTK_WINDOW(dialog), GTK_WINDOW (window));
3090
3091         /* First we get the currently selected font information */
3092         wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE);
3093
3094         switch (oldfmt.text_position) {
3095         case TEXT_POSITION_NORMAL:
3096                 old_position = 0;
3097                 break;
3098         case TEXT_POSITION_SUPERSCRIPT:
3099                 old_position = 1;
3100                 break;
3101         default:
3102                 old_position = -1;
3103                 break;
3104         }
3105
3106         g_object_set (G_OBJECT (dialog),
3107                       "bold", oldfmt.bold != FALSE,
3108                       "bold-set", !oldfmt.cs.bold,
3109                       "underline", oldfmt.underline != FALSE,
3110                       "underline-set", !oldfmt.cs.underline,
3111                       "italic", oldfmt.italic != FALSE,
3112                       "italic-set", !oldfmt.cs.italic,
3113                       "strikethrough", oldfmt.strikethrough != FALSE,
3114                       "strikethrough-set", !oldfmt.cs.strikethrough,
3115                       "color", &oldfmt.color,
3116                       "color-set", !oldfmt.cs.color,
3117                       "size", wp_font_size[oldfmt.font_size],
3118                       "size-set", !oldfmt.cs.font_size,
3119                       "position", old_position,
3120                       "position-set", !oldfmt.cs.text_position,
3121                       "family", wp_get_font_name (oldfmt.font),
3122                       "family-set", !oldfmt.cs.font,
3123                       NULL);
3124
3125         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
3126                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
3127         gtk_widget_show_all (dialog);
3128         priv->font_dialog = dialog;
3129         response = gtk_dialog_run (GTK_DIALOG (dialog));
3130         priv->font_dialog = NULL;
3131         if (response == GTK_RESPONSE_OK) {
3132
3133                 g_object_get( dialog,
3134                               "bold", &bold,
3135                               "bold-set", &bold_set,
3136                               "underline", &underline,
3137                               "underline-set", &underline_set,
3138                               "italic", &italic,
3139                               "italic-set", &italic_set,
3140                               "strikethrough", &strikethrough,
3141                               "strikethrough-set", &strikethrough_set,
3142                               "color", &color,
3143                               "color-set", &color_set,
3144                               "size", &font_size,
3145                               "size-set", &font_size_set,
3146                               "family", &font_name,
3147                               "family-set", &font_set,
3148                               "position", &position,
3149                               "position-set", &position_set,
3150                               NULL );
3151                 
3152         }       
3153
3154         if (response == GTK_RESPONSE_OK) {
3155                 memset(&fmt, 0, sizeof(fmt));
3156                 if (bold_set) {
3157                         fmt.bold = bold;
3158                         fmt.cs.bold = TRUE;
3159                 }
3160                 if (italic_set) {
3161                         fmt.italic = italic;
3162                         fmt.cs.italic = TRUE;
3163                 }
3164                 if (underline_set) {
3165                         fmt.underline = underline;
3166                         fmt.cs.underline = TRUE;
3167                 }
3168                 if (strikethrough_set) {
3169                         fmt.strikethrough = strikethrough;
3170                         fmt.cs.strikethrough = TRUE;
3171                 }
3172                 if (position_set) {
3173                         fmt.text_position =
3174                                 ( position == 0 )
3175                                 ? TEXT_POSITION_NORMAL
3176                                 : ( ( position == 1 )
3177                                     ? TEXT_POSITION_SUPERSCRIPT
3178                                     : TEXT_POSITION_SUBSCRIPT );
3179                         fmt.cs.text_position = TRUE;
3180                         fmt.font_size = oldfmt.font_size;
3181                 }
3182                 if (color_set) {
3183                         fmt.color = *color;
3184                         fmt.cs.color = TRUE;
3185                 }
3186                 if (font_set) {
3187                         fmt.font = wp_get_font_index(font_name,
3188                                                      DEFAULT_FONT);
3189                         fmt.cs.font = TRUE;
3190                 }
3191                 g_free(font_name);
3192                 if (font_size_set) {
3193                         fmt.cs.font_size = TRUE;
3194                         fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE);
3195                 }
3196                 wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt);
3197                 text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window);
3198         }
3199         gtk_widget_destroy (dialog);
3200         
3201         gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body));
3202 }
3203
3204 void
3205 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
3206 {
3207         ModestMsgEditWindowPrivate *priv;
3208         ModestWindowPrivate *parent_priv;
3209         gboolean was_rich_text, is_rich_text;
3210
3211         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3212         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3213         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
3214
3215         was_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3216
3217         wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer));
3218
3219         is_rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer));
3220
3221         if (parent_priv->toolbar && was_rich_text != is_rich_text) {
3222                 if (is_rich_text)
3223                         gtk_widget_show (parent_priv->toolbar);
3224                 else
3225                         gtk_widget_hide (parent_priv->toolbar);
3226         }
3227
3228         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3229         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3230 }
3231
3232 void
3233 modest_msg_edit_window_redo (ModestMsgEditWindow *window)
3234 {
3235         ModestMsgEditWindowPrivate *priv;
3236
3237         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3238         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3239         
3240         wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer));
3241
3242         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3243         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3244
3245 }
3246
3247 static void  
3248 text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window)
3249 {
3250         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3251
3252         priv->can_undo = can_undo;
3253 }
3254
3255 static void  
3256 text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window)
3257 {
3258         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3259
3260         priv->can_redo = can_redo;
3261 }
3262
3263 gboolean            
3264 modest_msg_edit_window_can_undo (ModestMsgEditWindow *window)
3265 {
3266         ModestMsgEditWindowPrivate *priv;
3267         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3268         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3269
3270         return priv->can_undo;
3271 }
3272
3273 gboolean            
3274 modest_msg_edit_window_can_redo (ModestMsgEditWindow *window)
3275 {
3276         ModestMsgEditWindowPrivate *priv;
3277         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3278         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3279
3280         return priv->can_redo;
3281 }
3282
3283
3284 static void
3285 text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id)
3286 {
3287         GtkTextIter iter;
3288         GtkTextIter match_start, match_end;
3289
3290         if (image_id == NULL)
3291                 return;
3292
3293         gtk_text_buffer_get_start_iter (buffer, &iter);
3294
3295         while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) {
3296                 GSList *tags = gtk_text_iter_get_tags (&match_start);
3297                 GSList *node;
3298                 for (node = tags; node != NULL; node = g_slist_next (node)) {
3299                         GtkTextTag *tag = (GtkTextTag *) node->data;
3300                         if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) {
3301                                 gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index");
3302                                 if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) {
3303                                         gint offset;
3304                                         offset = gtk_text_iter_get_offset (&match_start);
3305                                         gtk_text_buffer_delete (buffer, &match_start, &match_end);
3306                                         gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
3307                                 }
3308                         }
3309                 }
3310                 gtk_text_iter_forward_char (&iter);
3311         }
3312 }
3313
3314 gboolean
3315 message_is_empty (ModestMsgEditWindow *window)
3316 {
3317         ModestMsgEditWindowPrivate *priv = NULL;
3318
3319         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3320         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3321
3322         /** TODO: Add wpeditor API to tell us if there is any _visible_ text,
3323          * so we can ignore markup.
3324          */
3325         GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
3326         gint count = 0;
3327         if (buf)
3328                 count = gtk_text_buffer_get_char_count (buf);
3329
3330         return count == 0;
3331 }
3332
3333 static gboolean
3334 msg_body_focus (GtkWidget *focus,
3335                 GdkEventFocus *event,
3336                 gpointer userdata)
3337 {
3338         
3339         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata));
3340         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata));
3341         modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD);
3342         return FALSE;
3343 }
3344
3345 static void
3346 recpt_field_changed (GtkTextBuffer *buffer,
3347                   ModestMsgEditWindow *editor)
3348 {
3349         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3350         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3351 }
3352
3353 static void
3354 body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor)
3355 {
3356         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor));
3357         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor));
3358 }
3359
3360 void
3361 modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor,
3362                                      gboolean modified)
3363 {
3364         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3365         GtkTextBuffer *buffer;
3366
3367         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3368         gtk_text_buffer_set_modified (buffer, modified);
3369         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3370         gtk_text_buffer_set_modified (buffer, modified);
3371         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3372         gtk_text_buffer_set_modified (buffer, modified);
3373         gtk_text_buffer_set_modified (priv->text_buffer, modified);
3374 }
3375
3376 gboolean
3377 modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor)
3378 {
3379         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor);
3380         const char *account_name;
3381         GtkTextBuffer *buffer;
3382
3383         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field));
3384         if (gtk_text_buffer_get_modified (buffer))
3385                 return TRUE;
3386         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field));
3387         if (gtk_text_buffer_get_modified (buffer))
3388                 return TRUE;
3389         buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field));
3390         if (gtk_text_buffer_get_modified (buffer))
3391                 return TRUE;
3392         if (gtk_text_buffer_get_modified (priv->text_buffer))
3393                 return TRUE;
3394
3395         account_name = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
3396         if (priv->original_mailbox) {
3397                 if (!account_name || strcmp (account_name, priv->original_mailbox))
3398                         return TRUE;
3399         } else if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) {
3400                 return TRUE;
3401         }
3402
3403         return FALSE;
3404 }
3405
3406
3407
3408
3409 gboolean
3410 modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook)
3411 {
3412         ModestMsgEditWindowPrivate *priv = NULL;
3413         GSList *address_list = NULL;
3414
3415         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3416         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3417
3418         /* check if there's no recipient added */
3419         if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) &&
3420             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) &&
3421             (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) {
3422                 /* no recipient contents, then select contacts */
3423                 modest_msg_edit_window_open_addressbook (window, NULL);
3424                 return FALSE;
3425         }
3426
3427         /* Check names */
3428         g_object_ref (window);
3429         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field),
3430                                               (add_to_addressbook) ? &address_list : NULL)) {
3431                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3432                 g_object_unref (window);
3433                 return FALSE;
3434         }
3435         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field),
3436                                               (add_to_addressbook) ? &address_list : NULL)) {
3437                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field));
3438                 g_object_unref (window);
3439                 return FALSE;
3440         }
3441         if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field),
3442                                               (add_to_addressbook) ? &address_list : NULL)) {
3443                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field));
3444                 g_object_unref (window);
3445                 return FALSE;
3446         }
3447
3448         /* Add contacts to address book */
3449         if (add_to_addressbook && address_list)
3450                 modest_address_book_add_address_list (address_list);
3451
3452         if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) &&
3453             !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field)))
3454                 modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field));
3455         g_object_unref (window);
3456
3457         return TRUE;
3458
3459 }
3460
3461 void
3462 modest_msg_edit_window_add_to_contacts (ModestMsgEditWindow *self)
3463 {
3464         GSList *recipients = NULL;
3465         ModestMsgEditWindowPrivate *priv;
3466         gchar *joined, *after_remove, *to, *cc, *bcc;
3467
3468         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
3469
3470         /* First of all check names */
3471         if (!modest_msg_edit_window_check_names (self, FALSE))
3472                 return;
3473
3474         if (!modest_msg_edit_window_has_pending_addresses (self))
3475                 return;
3476
3477         /* Don't add the from obviously */
3478         to  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->to_field));
3479         cc  =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->cc_field));
3480         bcc =  g_strdup (modest_recpt_editor_get_recipients ((ModestRecptEditor *) priv->bcc_field));
3481
3482         joined = modest_text_utils_join_addresses (NULL, to, cc, bcc);
3483         g_free (to);
3484         g_free (cc);
3485         g_free (bcc);
3486
3487         after_remove = modest_text_utils_remove_duplicate_addresses (joined);
3488         g_free (joined);
3489
3490         recipients = modest_text_utils_split_addresses_list (after_remove);
3491         g_free (after_remove);
3492
3493         if (recipients) {
3494                 /* Offer the user to add recipients to the address book */
3495                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3496                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3497         }
3498 }
3499
3500 static void
3501 modest_msg_edit_window_add_attachment_clicked (GtkButton *button,
3502                                                ModestMsgEditWindow *window)
3503 {
3504         modest_msg_edit_window_offer_attach_file (window);
3505 }
3506
3507 const gchar *
3508 modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win)
3509 {
3510         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3511
3512         return priv->clipboard_text;
3513 }
3514
3515 static void
3516 modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard,
3517                                                GdkEvent *event,
3518                                                ModestMsgEditWindow *window)
3519 {
3520         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3521         GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3522         gchar *text = NULL;
3523
3524         /* It could happen that the window was already closed */
3525         if (!GTK_WIDGET_VISIBLE (window))
3526                 return;
3527
3528         g_object_ref (window);
3529         text = gtk_clipboard_wait_for_text (selection_clipboard);
3530
3531         if (priv->clipboard_text != NULL) {
3532                 g_free (priv->clipboard_text);
3533         }
3534         priv->clipboard_text = text;
3535
3536         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3537
3538         g_object_unref (window);
3539 }
3540
3541 static gboolean clipboard_owner_change_idle (gpointer userdata)
3542 {
3543         ModestMsgEditWindow *window = (ModestMsgEditWindow *) userdata;
3544         ModestMsgEditWindowPrivate *priv;
3545
3546         gdk_threads_enter ();
3547         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
3548         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3549
3550         priv->clipboard_owner_idle = 0;
3551         modest_msg_edit_window_clipboard_owner_change (NULL, NULL, window);
3552         gdk_threads_leave ();
3553
3554         return FALSE;
3555 }
3556
3557 static void
3558 modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window)
3559 {
3560         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3561         if (priv->clipboard_owner_idle == 0) {
3562                 priv->clipboard_owner_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
3563                                                               clipboard_owner_change_idle, 
3564                                                               g_object_ref (window),
3565                                                               g_object_unref);
3566         }
3567 }
3568
3569 static void 
3570 subject_field_move_cursor (GtkEntry *entry,
3571                            GtkMovementStep step,
3572                            gint a1,
3573                            gboolean a2,
3574                            gpointer window)
3575 {
3576         /* It could happen that the window was already closed */
3577         if (!GTK_WIDGET_VISIBLE (window))
3578                 return;
3579
3580         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
3581 }
3582
3583 static void 
3584 update_window_title (ModestMsgEditWindow *window)
3585 {
3586         ModestMsgEditWindowPrivate *priv = NULL;
3587         const gchar *subject;
3588
3589         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3590         subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field));
3591         if (subject == NULL || subject[0] == '\0')
3592                 subject = _("mail_va_new_email");
3593
3594         gtk_window_set_title (GTK_WINDOW (window), subject);
3595
3596 }
3597
3598
3599 static void  
3600 body_insert_text (GtkTextBuffer *buffer, 
3601                   GtkTextIter *location,
3602                   gchar *text,
3603                   gint len,
3604                   ModestMsgEditWindow *window)
3605 {
3606         GtkTextIter end_iter;
3607         gint offset;
3608         glong utf8_len;
3609         gint line;
3610         gchar *text_offset;
3611         gint text_lines;
3612
3613         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3614
3615         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end_iter);
3616
3617         offset = gtk_text_iter_get_offset (&end_iter);
3618         line = gtk_text_iter_get_line (&end_iter);
3619
3620         text_offset = text;
3621         text_lines = 0;
3622         while (text_offset < text + len) {
3623                 if (*text_offset == '\n')
3624                         text_lines++;
3625                 if (text_lines + line >= MAX_BODY_LINES) {
3626                         len = text_offset - text;
3627                         break;
3628                 }
3629                 text_offset++;
3630         }
3631
3632         utf8_len = g_utf8_strlen (text, len);
3633
3634         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3635                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
3636                 if (line <= MAX_BODY_LINES && offset < MAX_BODY_LENGTH)
3637                 {
3638                         gchar *result;
3639                         gchar *utf8_end;
3640
3641                         utf8_end = g_utf8_offset_to_pointer (text, MAX_BODY_LENGTH - offset);
3642
3643                         /* Prevent endless recursion */
3644                         result = g_strndup (text, utf8_end - text);
3645                         g_signal_handlers_block_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3646                         g_signal_emit_by_name (G_OBJECT (buffer), "insert-text", location,
3647                                                (gpointer) result, (gpointer) (utf8_end - text),
3648                                                (gpointer) window);
3649                         g_signal_handlers_unblock_by_func (G_OBJECT (buffer), G_CALLBACK (body_insert_text), window);
3650                 }
3651
3652         }
3653         if (line > MAX_BODY_LINES || offset + utf8_len > MAX_BODY_LENGTH) {
3654                 if (priv->max_chars_banner == NULL) {
3655                         priv->max_chars_banner = hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3656                                                                                  _CS("ckdg_ib_maximum_characters_reached"));
3657                         g_object_weak_ref (G_OBJECT (priv->max_chars_banner), (GWeakNotify) max_chars_banner_unref, window);
3658                 }
3659         }
3660 }
3661
3662 static void  
3663 subject_field_changed (GtkEditable *editable, 
3664                        ModestMsgEditWindow *window)
3665 {
3666         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3667         update_window_title (window);
3668         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3669         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
3670         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3671 }
3672 static void  
3673 subject_field_insert_text (GtkEditable *editable, 
3674                            gchar *new_text,
3675                            gint new_text_length,
3676                            gint *position,
3677                            ModestMsgEditWindow *window)
3678 {
3679         GString *result = g_string_new ("");
3680         gchar *current;
3681         gint result_len = 0;
3682         const gchar *entry_text = NULL;
3683         gint old_length;
3684
3685         entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
3686         old_length = g_utf8_strlen (entry_text, -1);
3687
3688         for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) {
3689                 gunichar c = g_utf8_get_char_validated (current, 8);
3690                 /* Invalid unichar, stop */
3691                 if (c == -1)
3692                         break;
3693                 /* a bullet */
3694                 if (c == 0x2022)
3695                         continue;
3696                 result = g_string_append_unichar (result, c);
3697                 result_len++;
3698         }
3699
3700         if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) {
3701                 g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
3702                 if (result_len > 0)
3703                 {
3704                         /* Prevent endless recursion */
3705                         g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3706                         g_signal_emit_by_name (editable, "insert-text", 
3707                                                (gpointer) result->str, (gpointer) result->len,
3708                                                (gpointer) position, (gpointer) window);
3709                        g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window);
3710                 }
3711         }
3712
3713         if (result_len + old_length > 1000) {
3714                 hildon_banner_show_information (GTK_WIDGET (window), NULL, 
3715                                                 _CS("ckdg_ib_maximum_characters_reached"));
3716         }
3717         g_string_free (result, TRUE);
3718 }
3719
3720 void
3721 modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window,
3722                                             gboolean show)
3723 {
3724         ModestMsgEditWindowPrivate *priv = NULL;
3725
3726         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3727         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3728
3729         gtk_widget_set_no_show_all (priv->find_toolbar, FALSE);
3730
3731         if (show) {
3732                 gtk_widget_show_all (priv->find_toolbar);
3733                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
3734         } else {
3735                 gtk_widget_hide_all (priv->find_toolbar);
3736                 gtk_widget_grab_focus (priv->msg_body);
3737         }
3738 }
3739
3740 static gboolean 
3741 gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter,
3742                                           const gchar *str,
3743                                           GtkTextIter *match_start,
3744                                           GtkTextIter *match_end)
3745 {
3746         GtkTextIter end_iter;
3747         gchar *str_casefold;
3748         gint str_chars_n;
3749         gchar *range_text;
3750         gchar *range_casefold;
3751         gint offset;
3752         gint range_chars_n;
3753         gboolean result = FALSE;
3754
3755         if (str == NULL)
3756                 return TRUE;
3757         
3758         /* get end iter */
3759         end_iter = *iter;
3760         gtk_text_iter_forward_to_end (&end_iter);
3761
3762         str_casefold = g_utf8_casefold (str, -1);
3763         str_chars_n = strlen (str);
3764
3765         range_text = gtk_text_iter_get_visible_text (iter, &end_iter);
3766         range_casefold = g_utf8_casefold (range_text, -1);
3767         range_chars_n = strlen (range_casefold);
3768
3769         if (range_chars_n < str_chars_n) {
3770                 g_free (str_casefold);
3771                 g_free (range_text);
3772                 g_free (range_casefold);
3773                 return FALSE;
3774         }
3775
3776         for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) {
3777                 gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n);
3778                 if (!g_utf8_collate (range_subtext, str_casefold)) {
3779                         gchar *found_text = g_strndup (range_text + offset, str_chars_n);
3780                         result = TRUE;
3781                         if (!gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY,
3782                                                            match_start, match_end, NULL)) {
3783                                 g_debug ("Matched string with collate, but not matched in model");
3784                         }
3785                         g_free (found_text);
3786                 }
3787                 g_free (range_subtext);
3788                 if (result)
3789                         break;
3790         }
3791         g_free (str_casefold);
3792         g_free (range_text);
3793         g_free (range_casefold);
3794
3795         return result;
3796 }
3797
3798
3799 static void 
3800 modest_msg_edit_window_find_toolbar_search (GtkWidget *widget,
3801                                             ModestMsgEditWindow *window)
3802 {
3803         gchar *current_search = NULL;
3804         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3805         gboolean result;
3806         GtkTextIter selection_start, selection_end;
3807         GtkTextIter match_start, match_end;
3808         gboolean continue_search = FALSE;
3809
3810         if (message_is_empty (window)) {
3811                 g_free (priv->last_search);
3812                 priv->last_search = NULL;
3813                 hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find"));
3814                 return;
3815         }
3816
3817         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
3818         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
3819                 g_free (current_search);
3820                 g_free (priv->last_search);
3821                 priv->last_search = NULL;
3822                 /* Information banner about empty search */
3823                 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
3824                 return;
3825         }
3826
3827         if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) {
3828                 continue_search = TRUE;
3829         } else {
3830                 g_free (priv->last_search);
3831                 priv->last_search = g_strdup (current_search);
3832         }
3833
3834         if (continue_search) {
3835                 gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end);
3836                 result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, 
3837                                                                    &match_start, &match_end);
3838                 if (!result)
3839                         hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_search_complete"));
3840         } else {
3841                 GtkTextIter buffer_start;
3842                 gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start);
3843                 result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, 
3844                                                                    &match_start, &match_end);
3845                 if (!result)
3846                         hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_no_matches"));
3847         }
3848
3849         /* Mark as selected the string found in search */
3850         if (result) {
3851                 gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end);
3852                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0);
3853                 correct_scroll_without_drag_check (MODEST_MSG_EDIT_WINDOW (window), FALSE);
3854         } else {
3855                 g_free (priv->last_search);
3856                 priv->last_search = NULL;
3857         }
3858         g_free (current_search);
3859 }
3860
3861 gboolean 
3862 modest_msg_edit_window_get_sent (ModestMsgEditWindow *window)
3863 {
3864         ModestMsgEditWindowPrivate *priv;
3865
3866         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3867         return priv->sent;
3868 }
3869
3870 void 
3871 modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, 
3872                                  gboolean sent)
3873 {
3874         ModestMsgEditWindowPrivate *priv;
3875
3876         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window);
3877         priv->sent = sent;
3878 }
3879
3880 static void
3881 modest_msg_edit_window_find_toolbar_close (GtkWidget *widget,
3882                                           ModestMsgEditWindow *window)
3883 {
3884         modest_msg_edit_window_toggle_find_toolbar (window, FALSE);
3885 }
3886
3887 void
3888 modest_msg_edit_window_set_draft (ModestMsgEditWindow *window,
3889                                   TnyMsg *draft)
3890 {
3891         ModestMsgEditWindowPrivate *priv;
3892         TnyHeader *header = NULL;
3893
3894         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
3895         g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft)));
3896
3897         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3898
3899         if (priv->draft_msg != NULL) {
3900                 g_object_unref (priv->draft_msg);
3901         }
3902
3903         if (draft != NULL) {
3904                 g_object_ref (draft);
3905                 header = tny_msg_get_header (draft);
3906                 if (priv->msg_uid) {
3907                         g_free (priv->msg_uid);
3908                         priv->msg_uid = NULL;
3909                 }
3910                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
3911         }
3912
3913         priv->draft_msg = draft;
3914 }
3915
3916 static void  
3917 text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, 
3918                        GtkTextIter *start, GtkTextIter *end,
3919                        gpointer userdata)
3920 {
3921         ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata);
3922         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata);
3923         gchar *tag_name;
3924
3925         if (tag == NULL) return;
3926         g_object_get (G_OBJECT (tag), "name", &tag_name, NULL);
3927         if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) {
3928                 replace_with_images (window, priv->images);
3929         }
3930 }
3931
3932 void                    
3933 modest_msg_edit_window_add_part (ModestMsgEditWindow *window,
3934                                  TnyMimePart *part)
3935 {
3936         ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3937
3938         g_return_if_fail (TNY_IS_MIME_PART (part));
3939         tny_list_prepend (priv->attachments, (GObject *) part);
3940         modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0);
3941         gtk_widget_set_no_show_all (priv->attachments_caption, FALSE);
3942         gtk_widget_show_all (priv->attachments_caption);
3943         gtk_text_buffer_set_modified (priv->text_buffer, TRUE);
3944 }
3945
3946 const gchar*    
3947 modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window)
3948 {
3949         ModestMsgEditWindowPrivate *priv;
3950
3951         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL);        
3952         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
3953
3954         return priv->msg_uid;
3955 }
3956
3957 GtkWidget *
3958 modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win,
3959                                          ModestMsgEditWindowWidgetType widget_type)
3960 {
3961         ModestMsgEditWindowPrivate *priv;
3962
3963         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL);
3964         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win);
3965
3966         switch (widget_type) {
3967         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY:
3968                 return priv->msg_body;
3969                 break;
3970         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO:
3971                 return priv->to_field;
3972                 break;
3973         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC:
3974                 return priv->cc_field;
3975                 break;
3976         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC:
3977                 return priv->bcc_field;
3978                 break;
3979         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT:
3980                 return priv->subject_field;
3981                 break;
3982         case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS:
3983                 return priv->attachments_view;
3984                 break;
3985         default:
3986                 return NULL;
3987         }
3988 }
3989
3990 static void 
3991 remove_tags (WPTextBuffer *buffer)
3992 {
3993         GtkTextIter start, end;
3994
3995         gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
3996         gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end);
3997
3998         gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end);
3999 }
4000
4001 static void
4002 on_account_removed (TnyAccountStore *account_store, 
4003                     TnyAccount *account,
4004                     gpointer user_data)
4005 {
4006         /* Do nothing if it's a store account, because we use the
4007            transport to send the messages */
4008         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) {
4009                 const gchar *parent_acc = NULL;
4010                 const gchar *our_acc = NULL;
4011
4012                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
4013                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
4014                 /* Close this window if I'm showing a message of the removed account */
4015                 if (strcmp (parent_acc, our_acc) == 0)
4016                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
4017         }
4018 }
4019
4020 static void
4021 update_signature (ModestMsgEditWindow *self,
4022                   const gchar *old_account,
4023                   const gchar *new_account)
4024 {
4025         ModestMsgEditWindowPrivate *priv;
4026         gboolean has_old_signature, has_new_signature;
4027         GtkTextIter iter;
4028         ModestAccountMgr *mgr;
4029         gchar *signature;
4030
4031         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4032
4033         gtk_text_buffer_begin_user_action (priv->text_buffer);
4034
4035         gtk_text_buffer_get_start_iter (priv->text_buffer, &iter);
4036         mgr = modest_runtime_get_account_mgr ();
4037
4038
4039         if (old_account) {
4040                 signature = modest_account_mgr_get_signature_from_recipient (mgr, old_account, &has_old_signature);
4041                 if (has_old_signature) {
4042                         GtkTextIter match_start, match_end;
4043                         /* We cannot use
4044                            MODEST_TEXT_UTILS_SIGNATURE_MARKER as it
4045                            seems that the search has some problems
4046                            with the blank space at the end */
4047                         if (gtk_text_iter_forward_search (&iter, "--",
4048                                                           GTK_TEXT_SEARCH_TEXT_ONLY,
4049                                                           &match_start, NULL, NULL)) {
4050                                 gtk_text_buffer_get_end_iter (priv->text_buffer ,&match_end);
4051                                 gtk_text_buffer_delete (priv->text_buffer, &match_start, &match_end);
4052                                 iter = match_start;
4053                         } else if (gtk_text_iter_forward_search (&iter, _("mcen_ia_editor_original_message"), 0,
4054                                                                  &match_start, &match_end, NULL)) {
4055                                 iter = match_start;
4056                         }
4057                 } else {
4058                         gtk_text_buffer_get_end_iter (priv->text_buffer, &iter);
4059                 }
4060                 g_free (signature);
4061         }
4062
4063         priv->last_from_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
4064         signature = modest_account_mgr_get_signature_from_recipient (mgr, new_account, &has_new_signature);
4065         if (has_new_signature) {
4066
4067                 gchar *full_signature = g_strconcat ((gtk_text_iter_starts_line (&iter)) ? "" : "\n",
4068                                                      MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n",
4069                                                      signature, NULL);
4070                 gtk_text_buffer_insert (priv->text_buffer, &iter, full_signature, -1);
4071                 g_free (full_signature);
4072         }
4073         g_free (signature);
4074         gtk_text_buffer_end_user_action (priv->text_buffer);
4075 }
4076
4077 static void update_branding (ModestMsgEditWindow *self,
4078                              const gchar *new_account)
4079 {
4080         ModestMsgEditWindowPrivate *priv;
4081         ModestAccountMgr *mgr;
4082         const GdkPixbuf *new_icon = NULL;
4083         gchar *new_label = NULL;
4084         gboolean show = FALSE;
4085
4086         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4087
4088         mgr = modest_runtime_get_account_mgr ();
4089
4090         modest_account_mgr_get_branding_from_recipient (mgr, new_account, &new_label, &new_icon, MODEST_ICON_SIZE_SMALL);
4091         if (new_icon) {
4092                 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->brand_icon), (GdkPixbuf *) new_icon);
4093                 gtk_widget_show (priv->brand_icon);
4094                 show = TRUE;
4095         } else {
4096                 gtk_widget_hide (priv->brand_icon);
4097         }
4098         if (new_label) {
4099                 gtk_label_set_text (GTK_LABEL (priv->brand_label), new_label);
4100                 gtk_widget_show (priv->brand_label);
4101                 g_free (new_label);
4102                 show = TRUE;
4103         } else {
4104                 gtk_widget_hide (priv->brand_label);
4105         }
4106
4107         if (show)
4108                 gtk_widget_show (priv->brand_container);
4109         else
4110                 gtk_widget_hide (priv->brand_container);
4111 }
4112
4113 static void
4114 from_field_changed (HildonPickerButton *button,
4115                     ModestMsgEditWindow *self)
4116 {
4117         ModestMsgEditWindowPrivate *priv;
4118         gchar *old_account, *new_account;
4119
4120         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4121
4122         old_account = priv->last_from_account;
4123         new_account = modest_selector_picker_get_active_id (MODEST_SELECTOR_PICKER (priv->from_field));
4124
4125         if (!new_account) {
4126                 g_warning ("%s, could not get the new account", __FUNCTION__);
4127                 return;
4128         }
4129
4130         /* If the From is the same do nothing */
4131         if (old_account && new_account && !strcmp (old_account, new_account))
4132                 return;
4133
4134         priv->last_from_account = new_account;
4135
4136         update_signature (self, old_account, new_account);
4137         update_branding (self, new_account);
4138
4139 }
4140
4141 typedef struct _MessageSettingsHelper {
4142         ModestMsgEditWindow *window;
4143         GSList *priority_group;
4144         GSList *format_group;
4145         GtkToggleButton *current_priority;
4146         GtkToggleButton *current_format;
4147 } MessageSettingsHelper;
4148
4149 static void
4150 on_priority_toggle (GtkToggleButton *button, 
4151                     MessageSettingsHelper *helper)
4152 {
4153         GSList *node;
4154         ModestMsgEditWindowPrivate *priv;
4155
4156         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4157         if (gtk_toggle_button_get_active (button)) {
4158
4159                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4160                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4161                         if ((node_button != button) &&
4162                             gtk_toggle_button_get_active (node_button)) {
4163                                 gtk_toggle_button_set_active (node_button, FALSE);
4164                         }
4165                 }
4166                 helper->current_priority = button;
4167         } else {
4168                 gboolean found = FALSE;
4169                 /* If no one is active, activate it again */
4170                 for (node = helper->priority_group; node != NULL; node = g_slist_next (node)) {
4171                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4172                         if (gtk_toggle_button_get_active (node_button)) {
4173                                 found = TRUE;
4174                                 break;
4175                         }
4176                 }
4177                 if (!found) {
4178                         gtk_toggle_button_set_active (button, TRUE);
4179                 }
4180         }
4181 }
4182
4183 static void
4184 on_format_toggle (GtkToggleButton *button,
4185                   MessageSettingsHelper *helper)
4186 {
4187         GSList *node;
4188         ModestMsgEditWindowPrivate *priv;
4189
4190         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (helper->window);
4191         if (gtk_toggle_button_get_active (button)) {
4192
4193                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4194                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4195                         if ((node_button != button) &&
4196                             gtk_toggle_button_get_active (node_button)) {
4197                                 gtk_toggle_button_set_active (node_button, FALSE);
4198                         }
4199                 }
4200                 helper->current_format = button;
4201         } else {
4202                 gboolean found = FALSE;
4203                 /* If no one is active, activate it again */
4204                 for (node = helper->format_group; node != NULL; node = g_slist_next (node)) {
4205                         GtkToggleButton *node_button = (GtkToggleButton *) node->data;
4206                         if (gtk_toggle_button_get_active (node_button)) {
4207                                 found = TRUE;
4208                                 break;
4209                         }
4210                 }
4211                 if (!found) {
4212                         gtk_toggle_button_set_active (button, TRUE);
4213                 }
4214         }
4215
4216 }
4217
4218 static void
4219 modest_msg_edit_window_show_msg_settings_dialog (ModestMsgEditWindow *window)
4220 {
4221         GtkWidget *dialog;
4222         GtkWidget *align;
4223         GtkWidget *vbox;
4224         GtkWidget *priority_hbox;
4225         GtkWidget *high_toggle, *medium_toggle, *low_toggle;
4226         GtkWidget *captioned;
4227         GtkSizeGroup *title_sizegroup, *value_sizegroup;
4228         GtkWidget *format_hbox;
4229         GtkWidget *html_toggle, *text_toggle;
4230         ModestMsgEditWindowPrivate *priv;
4231         MessageSettingsHelper helper = {0,};
4232
4233         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
4234         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window);
4235         helper.window = window;
4236         helper.priority_group = NULL;
4237         helper.format_group = NULL;
4238
4239         title_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4240         value_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
4241
4242         dialog = gtk_dialog_new_with_buttons (_("mcen_me_message_settings"), NULL,
4243                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4244                                               _HL("wdgt_bd_done"), GTK_RESPONSE_ACCEPT, NULL);
4245         vbox = gtk_vbox_new (FALSE, 0);
4246         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
4247         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_DOUBLE, 0);
4248         gtk_container_add (GTK_CONTAINER (align), vbox);
4249         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), align);
4250         gtk_widget_show (align);
4251         gtk_widget_show (vbox);
4252
4253         /* Priority toggles */
4254         priority_hbox = gtk_hbox_new (TRUE, 0);
4255         high_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4256         gtk_button_set_label (GTK_BUTTON (high_toggle), _("mcen_me_editor_priority_high"));
4257         helper.priority_group = g_slist_prepend (helper.priority_group, high_toggle);
4258         g_object_set_data (G_OBJECT (high_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_HIGH_PRIORITY));
4259         medium_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4260         gtk_button_set_label (GTK_BUTTON (medium_toggle), _("mcen_me_editor_priority_normal"));
4261         helper.priority_group = g_slist_prepend (helper.priority_group, medium_toggle);
4262         g_object_set_data (G_OBJECT (medium_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_NORMAL_PRIORITY));
4263         low_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4264         gtk_button_set_label (GTK_BUTTON (low_toggle), _("mcen_me_editor_priority_low"));
4265         helper.priority_group = g_slist_prepend (helper.priority_group, low_toggle);
4266         g_object_set_data (G_OBJECT (low_toggle), "priority", GINT_TO_POINTER (TNY_HEADER_FLAG_LOW_PRIORITY));
4267         gtk_box_pack_start (GTK_BOX (priority_hbox), low_toggle, TRUE, TRUE, 0);
4268         gtk_box_pack_start (GTK_BOX (priority_hbox), medium_toggle, TRUE, TRUE, 0);
4269         gtk_box_pack_start (GTK_BOX (priority_hbox), high_toggle, TRUE, TRUE, 0);
4270         gtk_widget_show_all (priority_hbox);
4271         captioned = modest_maemo_utils_create_captioned (title_sizegroup, value_sizegroup,
4272                                                          _("mcen_me_editor_message_priority"), FALSE, priority_hbox);
4273         gtk_widget_show (captioned);
4274         gtk_box_pack_start (GTK_BOX (vbox), captioned, FALSE, FALSE, 0);
4275
4276         /* format toggles */
4277         format_hbox = gtk_hbox_new (TRUE, 0);
4278         html_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4279         gtk_button_set_label (GTK_BUTTON (html_toggle), _("mcen_me_editor_formatted_text"));
4280         helper.format_group = g_slist_prepend (helper.format_group, html_toggle);
4281         g_object_set_data (G_OBJECT (html_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_HTML));
4282         g_object_set_data (G_OBJECT (html_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_FORMATTED_TEXT));
4283         text_toggle = hildon_gtk_toggle_button_new (HILDON_SIZE_FINGER_HEIGHT);
4284         gtk_button_set_label (GTK_BUTTON (text_toggle), _("mcen_me_editor_plain_text"));
4285         helper.format_group = g_slist_prepend (helper.format_group, text_toggle);
4286         g_object_set_data (G_OBJECT (text_toggle), "format", GINT_TO_POINTER (MODEST_MSG_EDIT_FORMAT_TEXT));
4287         g_object_set_data (G_OBJECT (text_toggle), "file-format", GINT_TO_POINTER (MODEST_FILE_FORMAT_PLAIN_TEXT));
4288         gtk_box_pack_start (GTK_BOX (format_hbox), html_toggle, TRUE, TRUE, 0);
4289         gtk_box_pack_start (GTK_BOX (format_hbox), text_toggle, TRUE, TRUE, 0);
4290         gtk_widget_show_all (format_hbox);
4291         gtk_widget_show (format_hbox);
4292         gtk_box_pack_start (GTK_BOX (vbox), format_hbox, FALSE, FALSE, 0);
4293
4294
4295         g_object_unref (title_sizegroup);
4296         g_object_unref (value_sizegroup);
4297
4298         /* Set current values */
4299         switch (priv->priority_flags) {
4300         case TNY_HEADER_FLAG_HIGH_PRIORITY:
4301                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (high_toggle), TRUE);
4302                 helper.current_priority = GTK_TOGGLE_BUTTON (high_toggle);
4303                 break;
4304         case TNY_HEADER_FLAG_LOW_PRIORITY:
4305                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (low_toggle), TRUE);
4306                 helper.current_priority = GTK_TOGGLE_BUTTON (low_toggle);
4307                 break;
4308         default:
4309                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (medium_toggle), TRUE);
4310                 helper.current_priority = GTK_TOGGLE_BUTTON (medium_toggle);
4311                 break;
4312         }
4313
4314         switch (modest_msg_edit_window_get_format (window)) {
4315         case MODEST_MSG_EDIT_FORMAT_TEXT:
4316                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_toggle), TRUE);
4317                 helper.current_format = GTK_TOGGLE_BUTTON (text_toggle);
4318                 break;
4319         case MODEST_MSG_EDIT_FORMAT_HTML:
4320         default:
4321                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (html_toggle), TRUE);
4322                 helper.current_format = GTK_TOGGLE_BUTTON (html_toggle);
4323                 break;
4324         }
4325
4326         /* Signal connects */
4327         g_signal_connect (G_OBJECT (high_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4328         g_signal_connect (G_OBJECT (medium_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4329         g_signal_connect (G_OBJECT (low_toggle), "toggled", G_CALLBACK (on_priority_toggle), &helper);
4330         g_signal_connect (G_OBJECT (html_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4331         g_signal_connect (G_OBJECT (text_toggle), "toggled", G_CALLBACK (on_format_toggle), &helper);
4332
4333         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
4334                                      GTK_WINDOW (dialog), GTK_WINDOW (window));
4335
4336         /* Save settings if the user clicked on done */
4337         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
4338                 TnyHeaderFlags flags;
4339                 ModestMsgEditFormat old_format, new_format;
4340
4341                 /* Set priority flags */
4342                 flags = (TnyHeaderFlags) g_object_get_data (G_OBJECT (helper.current_priority), "priority");
4343                 if (priv->priority_flags !=  flags)
4344                         modest_msg_edit_window_set_priority_flags (window, flags);
4345
4346                 /* Set edit format */
4347                 old_format = modest_msg_edit_window_get_format (window);
4348                 new_format = (ModestMsgEditFormat) g_object_get_data (G_OBJECT (helper.current_format), "format");
4349                 if (old_format != new_format) {
4350                         gint file_format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (helper.current_format), "file-format"));
4351                         modest_msg_edit_window_set_file_format (window, file_format);
4352                 }
4353         }
4354
4355         gtk_widget_destroy (dialog);
4356         g_slist_free (helper.priority_group);
4357 }
4358
4359 static void
4360 on_message_settings (GtkAction *action,
4361                      ModestMsgEditWindow *window)
4362 {
4363         modest_msg_edit_window_show_msg_settings_dialog (window);
4364 }
4365
4366 static void
4367 on_cc_button_toggled (HildonCheckButton *button,
4368                       ModestMsgEditWindow *window)
4369 {
4370         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4371
4372         modest_msg_edit_window_show_cc (MODEST_MSG_EDIT_WINDOW (window),
4373                                         hildon_check_button_get_active (button));
4374 }
4375
4376 static void
4377 on_bcc_button_toggled (HildonCheckButton *button,
4378                       ModestMsgEditWindow *window)
4379 {
4380         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
4381
4382         modest_msg_edit_window_show_bcc (MODEST_MSG_EDIT_WINDOW (window),
4383                                         hildon_check_button_get_active (button));
4384 }
4385
4386 static void 
4387 setup_menu (ModestMsgEditWindow *self)
4388 {
4389         ModestMsgEditWindowPrivate *priv = NULL;
4390
4391         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4392
4393         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4394
4395         /* Settings menu buttons */
4396         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_editor_checknames"), NULL,
4397                                            APP_MENU_CALLBACK (modest_ui_actions_on_check_names),
4398                                            NULL);
4399         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
4400                                            APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
4401                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
4402         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_undo"), "<Ctrl>z",
4403                                            APP_MENU_CALLBACK (modest_ui_actions_on_undo),
4404                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_undo));
4405
4406         priv->cc_button = hildon_check_button_new (0);
4407         gtk_button_set_label (GTK_BUTTON (priv->cc_button), _("mcen_me_editor_showcc"));
4408         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->cc_button),
4409                                         FALSE);
4410         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->cc_button),
4411                                                   NULL);
4412         g_signal_connect (G_OBJECT (priv->cc_button), "toggled",
4413                           G_CALLBACK (on_cc_button_toggled), (gpointer) self);
4414         gtk_button_set_alignment (GTK_BUTTON (priv->cc_button), 0.5, 0.5);
4415         gtk_button_set_alignment (GTK_BUTTON (priv->cc_button), 0.5, 0.5);
4416
4417         priv->bcc_button = hildon_check_button_new (0);
4418         gtk_button_set_label (GTK_BUTTON (priv->bcc_button), _("mcen_me_editor_showbcc"));
4419         hildon_check_button_set_active (HILDON_CHECK_BUTTON (priv->bcc_button),
4420                                         FALSE);
4421         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->bcc_button),
4422                                                   NULL);
4423         g_signal_connect (G_OBJECT (priv->bcc_button), "toggled",
4424                           G_CALLBACK (on_bcc_button_toggled), (gpointer) self);
4425         gtk_button_set_alignment (GTK_BUTTON (priv->bcc_button), 0.5, 0.5);
4426         gtk_button_set_alignment (GTK_BUTTON (priv->bcc_button), 0.5, 0.5);
4427
4428         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_editor_attach_inlineimage"), NULL,
4429                                            APP_MENU_CALLBACK (modest_ui_actions_on_insert_image),
4430                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_set_style));
4431         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_editor_add_attachment"), NULL,
4432                                            APP_MENU_CALLBACK (modest_msg_edit_window_add_attachment_clicked),
4433                                            NULL);
4434         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
4435                                            APP_MENU_CALLBACK (modest_ui_actions_on_remove_attachments),
4436                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_editor_remove_attachment));
4437         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_message_settings"), NULL,
4438                                            APP_MENU_CALLBACK (on_message_settings),
4439                                            NULL);
4440         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), "<Ctrl>f",
4441                                            APP_MENU_CALLBACK (modest_ui_actions_on_toggle_find_in_page),
4442                                            NULL);
4443 }
4444
4445 static void
4446 emit_open_addressbook (GtkButton *button,
4447                        ModestRecptEditor *editor)
4448 {
4449         g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
4450 }
4451
4452 static GtkWidget *
4453 _create_addressbook_box (GtkSizeGroup *title_size_group, GtkSizeGroup *value_size_group,
4454                          const gchar *label, GtkWidget *control)
4455 {
4456         GtkWidget *abook_button;
4457         GtkWidget *align;
4458         GtkWidget *box;
4459         GtkWidget *label_widget;
4460
4461         box = gtk_hbox_new (FALSE, 0);
4462
4463         align = gtk_alignment_new (0.0, 0.0, 1.0, 0.0);
4464         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 0, MODEST_MARGIN_DEFAULT);
4465
4466         abook_button = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT);
4467         label_widget = gtk_label_new (label);
4468         gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
4469         gtk_container_add (GTK_CONTAINER (abook_button), label_widget);
4470
4471         gtk_container_add (GTK_CONTAINER (align), abook_button);
4472         gtk_widget_set_size_request (label_widget, 148 - MODEST_MARGIN_DOUBLE, -1);
4473         gtk_box_pack_start (GTK_BOX (box), align, FALSE, FALSE, 0);
4474         gtk_box_pack_start (GTK_BOX (box), control, TRUE, TRUE, 0);
4475         if (title_size_group)
4476                 gtk_size_group_add_widget (title_size_group, label_widget);
4477         if (value_size_group)
4478                 gtk_size_group_add_widget (value_size_group, control);
4479
4480         g_signal_connect (G_OBJECT (abook_button), "clicked",
4481                           G_CALLBACK (emit_open_addressbook), control);
4482   
4483         return box;  
4484 }
4485
4486 static void 
4487 max_chars_banner_unref (ModestMsgEditWindow *self, GObject *old_ref)
4488 {
4489         ModestMsgEditWindowPrivate *priv = NULL;
4490
4491         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self));
4492
4493         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4494         priv->max_chars_banner = NULL;
4495 }
4496
4497 static gboolean
4498 has_pending_addresses (ModestRecptEditor *recpt_editor)
4499 {
4500         const gchar *recipients = NULL;
4501         GSList *start_indexes = NULL, *end_indexes = NULL;
4502         GSList *current_start, *current_end;
4503         GtkTextBuffer *buffer;
4504         gint offset_delta = 0;
4505         gint last_length;
4506         gboolean has_recipients_to_add = FALSE;
4507
4508         recipients = modest_recpt_editor_get_recipients (recpt_editor);
4509         last_length = g_utf8_strlen (recipients, -1);
4510         modest_text_utils_get_addresses_indexes (recipients, &start_indexes, &end_indexes);
4511
4512         if (!start_indexes)
4513                 return FALSE;
4514
4515         current_start = start_indexes;
4516         current_end = end_indexes;
4517         buffer = modest_recpt_editor_get_buffer (recpt_editor);
4518
4519         while (current_start && !has_recipients_to_add) {
4520                 gchar *address;
4521                 gchar *start_ptr, *end_ptr;
4522                 gint start_pos, end_pos;
4523
4524                 start_pos = (*((gint*) current_start->data)) + offset_delta;
4525                 end_pos = (*((gint*) current_end->data)) + offset_delta;
4526
4527                 start_ptr = g_utf8_offset_to_pointer (recipients, start_pos);
4528                 end_ptr = g_utf8_offset_to_pointer (recipients, end_pos);
4529
4530                 address = g_strstrip (g_strndup (start_ptr, end_ptr - start_ptr));
4531
4532                 if (modest_text_utils_validate_recipient (address, NULL)) {
4533                         if (!modest_address_book_has_address (address)) {
4534                                 has_recipients_to_add = TRUE;
4535                         }
4536                 }
4537                 current_start = g_slist_next (current_start);
4538                 current_end = g_slist_next (current_end);
4539         }
4540         return has_recipients_to_add;
4541 }
4542
4543 gboolean
4544 modest_msg_edit_window_has_pending_addresses (ModestMsgEditWindow *self)
4545 {
4546         ModestMsgEditWindowPrivate *priv = NULL;
4547
4548         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW(self), FALSE);
4549
4550         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
4551
4552         if (!has_pending_addresses ((ModestRecptEditor *) priv->to_field) &&
4553             !has_pending_addresses ((ModestRecptEditor *) priv->cc_field) &&
4554             !has_pending_addresses ((ModestRecptEditor *) priv->bcc_field))
4555                 return FALSE;
4556         else
4557                 return TRUE;
4558 }