Fixed several invalid #ifdef statements
[modest] / src / widgets / modest-recpt-editor.c
1 /* Copyright (c) 2007, 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 <config.h>
31
32 #include <gtk/gtk.h>
33
34 #include <modest-text-utils.h>
35 #include <modest-recpt-editor.h>
36 #include <modest-scroll-text.h>
37 #include <pango/pango-attributes.h>
38 #include <string.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <gtk/gtk.h>
41 #include <modest-ui-constants.h>
42 #include <modest-toolkit-factory.h>
43 #include <modest-runtime.h>
44
45 #ifdef MODEST_TOOLKIT_HILDON2
46 #include "modest-hildon-includes.h"
47 #endif
48
49 #define RECPT_BUTTON_WIDTH_HILDON2 118
50
51
52 static GObjectClass *parent_class = NULL;
53
54 #define RECIPIENT_TAG_ID "recpt-id"
55
56 /* signals */
57 enum {
58         OPEN_ADDRESSBOOK_SIGNAL,
59         LAST_SIGNAL
60 };
61
62 typedef struct _ModestRecptEditorPrivate ModestRecptEditorPrivate;
63
64 struct _ModestRecptEditorPrivate
65 {
66         GtkWidget *text_view;
67         GtkWidget *abook_button;
68         GtkWidget *scrolled_window;
69         gchar *recipients;
70         gulong on_mark_set_handler;
71         gboolean show_abook;
72 };
73
74 #define MODEST_RECPT_EDITOR_GET_PRIVATE(o)      \
75         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_RECPT_EDITOR, ModestRecptEditorPrivate))
76
77 static guint signals[LAST_SIGNAL] = {0};
78
79 /* static functions: GObject */
80 static void modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class);
81 static void modest_recpt_editor_finalize (GObject *object);
82 static void modest_recpt_editor_class_init (ModestRecptEditorClass *klass);
83
84 /* widget events */
85 static void modest_recpt_editor_on_abook_clicked (GtkButton *button,
86                                                   ModestRecptEditor *editor);
87 static void modest_recpt_editor_add_tags (ModestRecptEditor *editor,
88                                           const gchar * recipient_id);
89 static gboolean modest_recpt_editor_on_focus_in (GtkTextView *text_view,
90                                              GdkEventFocus *event,
91                                              ModestRecptEditor *editor);
92 static void modest_recpt_editor_on_mark_set (GtkTextBuffer *buffer,
93                                              GtkTextIter *iter,
94                                              GtkTextMark *mark,
95                                              ModestRecptEditor *editor);
96 static void modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer,
97                                                 GtkTextIter *location,
98                                                 gchar *text,
99                                                 gint len,
100                                                 ModestRecptEditor *editor);
101 static void modest_recpt_editor_on_insert_text_after (GtkTextBuffer *buffer,
102                                                       GtkTextIter *location,
103                                                       gchar *text,
104                                                       gint len,
105                                                       ModestRecptEditor *editor);
106 static gboolean modest_recpt_editor_on_key_press_event (GtkTextView *text_view,
107                                                           GdkEventKey *key,
108                                                           ModestRecptEditor *editor);
109 static GtkTextTag *iter_has_recipient (GtkTextIter *iter);
110 static gunichar iter_previous_char (GtkTextIter *iter);
111 /* static gunichar iter_next_char (GtkTextIter *iter); */
112 static GtkTextTag *prev_iter_has_recipient (GtkTextIter *iter);
113 /* static GtkTextTag *next_iter_has_recipient (GtkTextIter *iter); */
114 static void select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag, gboolean grow, gboolean left_not_right);
115 static gboolean quote_opened (GtkTextIter *iter);
116 static gboolean is_valid_insert (const gchar *text, gint len);
117 static gchar *create_valid_text (const gchar *text, gint len);
118
119 /**
120  * modest_recpt_editor_new:
121  *
122  * Return value: a new #ModestRecptEditor instance implemented for Gtk+
123  **/
124 GtkWidget*
125 modest_recpt_editor_new (void)
126 {
127         ModestRecptEditor *self = g_object_new (MODEST_TYPE_RECPT_EDITOR, 
128                                                 "homogeneous", FALSE,
129                                                 "spacing", MODEST_MARGIN_NONE,
130                                                 NULL);
131
132         return GTK_WIDGET (self);
133 }
134
135 void
136 modest_recpt_editor_set_recipients (ModestRecptEditor *recpt_editor, const gchar *recipients)
137 {
138         ModestRecptEditorPrivate *priv;
139         GtkTextBuffer *buffer = NULL;
140         gchar *valid_recipients = NULL;
141
142         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
143         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
144
145         buffer = modest_text_view_get_buffer (priv->text_view);
146
147         valid_recipients = create_valid_text (recipients, -1);
148         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
149         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
150         gtk_text_buffer_set_text (buffer, valid_recipients, -1);
151         g_free (valid_recipients);
152         if (GTK_WIDGET_REALIZED (recpt_editor))
153                 gtk_widget_queue_resize (GTK_WIDGET (recpt_editor));
154         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
155         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
156
157 }
158
159 void
160 modest_recpt_editor_add_recipients (ModestRecptEditor *recpt_editor, const gchar *recipients)
161 {
162         ModestRecptEditorPrivate *priv;
163         GtkTextBuffer *buffer = NULL;
164         GtkTextIter iter;
165         gchar *string_to_add;
166
167         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
168         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
169
170         if (recipients == NULL)
171                 return;
172
173         buffer = modest_text_view_get_buffer (priv->text_view);
174
175         if (gtk_text_buffer_get_char_count (buffer) > 0) {
176                 string_to_add = g_strconcat (";\n", recipients, NULL);
177         } else {
178                 string_to_add = g_strdup (recipients);
179         }
180
181         gtk_text_buffer_get_end_iter (buffer, &iter);
182
183         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
184         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
185
186         gtk_text_buffer_insert (buffer, &iter, string_to_add, -1);
187
188         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
189         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
190
191         if (GTK_WIDGET_REALIZED (recpt_editor))
192                 gtk_widget_queue_resize (GTK_WIDGET (recpt_editor));
193
194         g_free (string_to_add);
195 }
196
197 void 
198 modest_recpt_editor_add_resolved_recipient (ModestRecptEditor *recpt_editor, GSList *email_list, const gchar * recipient_id)
199 {
200         ModestRecptEditorPrivate *priv;
201         GtkTextBuffer *buffer = NULL;
202         GtkTextIter start, end, iter;
203         GSList *node;
204         gboolean is_first_recipient = TRUE;
205       
206         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
207         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
208
209         buffer = modest_text_view_get_buffer (priv->text_view);
210
211         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
212         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
213         gtk_text_buffer_get_bounds (buffer, &start, &end);
214         if (gtk_text_buffer_get_char_count (buffer) > 0) {
215                 gchar * buffer_contents;
216
217                 gtk_text_buffer_get_bounds (buffer, &start, &end);
218                 buffer_contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
219                 g_strstrip (buffer_contents);
220                 if (!g_str_has_suffix (buffer_contents, "\n")) {
221                         if (g_str_has_suffix (buffer_contents, ";")||(g_str_has_suffix (buffer_contents, ",")))
222                                 gtk_text_buffer_insert (buffer, &end, "\n", -1);
223                         else
224                                 gtk_text_buffer_insert (buffer, &end, ";\n", -1);
225                 }
226                 g_free (buffer_contents);
227         }
228
229         gtk_text_buffer_get_end_iter (buffer, &iter);
230
231         for (node = email_list; node != NULL; node = g_slist_next (node)) {
232                 gchar *recipient = (gchar *) node->data;
233
234                 if ((recipient) && (strlen (recipient) != 0)) {
235
236                         if (!is_first_recipient)
237                                 gtk_text_buffer_insert (buffer, &iter, "\n", -1);
238
239                         gtk_text_buffer_insert (buffer, &iter, recipient, -1);
240                         gtk_text_buffer_insert (buffer, &iter, ";", -1);
241                         is_first_recipient = FALSE;
242                 }
243         }
244         modest_recpt_editor_add_tags (recpt_editor, recipient_id);
245
246         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
247         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
248 }
249
250 void 
251 modest_recpt_editor_replace_with_resolved_recipient (ModestRecptEditor *recpt_editor, 
252                                                      GtkTextIter *start, GtkTextIter *end,
253                                                      GSList *email_list, const gchar * recipient_id)
254 {
255         GSList *email_lists_list;
256         GSList *recipient_ids_list;
257
258         email_lists_list = g_slist_append (NULL, email_list);
259         recipient_ids_list = g_slist_append (NULL, (gpointer) recipient_id);
260
261         modest_recpt_editor_replace_with_resolved_recipients (recpt_editor, start, end,
262                                                               email_lists_list, recipient_ids_list);
263
264         g_slist_free (email_lists_list);
265         g_slist_free (recipient_ids_list);
266
267 }
268
269 void 
270 modest_recpt_editor_replace_with_resolved_recipients (ModestRecptEditor *recpt_editor, 
271                                                      GtkTextIter *start, GtkTextIter *end,
272                                                      GSList *email_lists_list, GSList * recipient_ids_list)
273 {
274         ModestRecptEditorPrivate *priv;
275         GtkTextBuffer *buffer;
276         GtkTextTag *tag;
277         GSList *node;
278         gboolean is_first_recipient = TRUE;
279
280         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
281         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
282
283         buffer = modest_text_view_get_buffer (priv->text_view);
284         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
285         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
286
287         gtk_text_buffer_delete (buffer, start, end);
288
289         while (email_lists_list && recipient_ids_list) {
290                 gchar *recipient_id = (gchar *) recipient_ids_list->data;
291                 GSList *email_list = (GSList *) email_lists_list->data;
292
293                 tag = gtk_text_buffer_create_tag (buffer, NULL, 
294                                                   "underline", PANGO_UNDERLINE_SINGLE,
295                                                   "wrap-mode", GTK_WRAP_NONE,
296                                                   "editable", TRUE, NULL);
297
298                 g_object_set_data (G_OBJECT (tag), "recipient-tag-id", GINT_TO_POINTER (RECIPIENT_TAG_ID));
299                 g_object_set_data_full (G_OBJECT (tag), "recipient-id", g_strdup (recipient_id), (GDestroyNotify) g_free);
300
301                 for (node = email_list; node != NULL; node = g_slist_next (node)) {
302                         gchar *recipient = (gchar *) node->data;
303
304                         if ((recipient) && (strlen (recipient) != 0)) {
305
306                                 if (!is_first_recipient || !gtk_text_iter_is_start(start))
307                                         gtk_text_buffer_insert (buffer, start, "\n", -1);
308
309                                 gtk_text_buffer_insert_with_tags (buffer, start, recipient, -1, tag, NULL);
310
311                                 if (node->next != NULL)
312                                         gtk_text_buffer_insert (buffer, start, ";", -1);
313                                 is_first_recipient = FALSE;
314                         }
315                 }
316
317                 email_lists_list = g_slist_next (email_lists_list);
318                 recipient_ids_list = g_slist_next (recipient_ids_list);
319
320                 /* Add a separator between lists of emails*/
321                 if (recipient_ids_list)
322                         gtk_text_buffer_insert (buffer, start, ";", -1);
323         }
324         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
325         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, recpt_editor);
326
327 }
328
329
330 const gchar *
331 modest_recpt_editor_get_recipients (ModestRecptEditor *recpt_editor)
332 {
333         ModestRecptEditorPrivate *priv;
334         GtkTextBuffer *buffer = NULL;
335         GtkTextIter start, end;
336
337         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), NULL);
338         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
339
340         if (priv->recipients != NULL) {
341                 g_free (priv->recipients);
342                 priv->recipients = NULL;
343         }
344
345         buffer = modest_text_view_get_buffer (priv->text_view);
346
347         gtk_text_buffer_get_start_iter (buffer, &start);
348         gtk_text_buffer_get_end_iter (buffer, &end);
349
350         priv->recipients = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
351
352         return priv->recipients;
353
354 }
355
356 static void
357 modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class)
358 {
359         ModestRecptEditorPrivate *priv;
360         GtkWidget *abook_icon;
361         GtkWidget *abook_align;
362         GtkTextBuffer *buffer;
363
364         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (instance);
365
366         priv->show_abook = TRUE;
367         priv->abook_button = gtk_button_new ();
368         gtk_widget_set_no_show_all (GTK_WIDGET (priv->abook_button), TRUE);
369         gtk_widget_show (priv->abook_button);
370 #ifdef MODEST_TOOLKIT_HILDON2
371         gtk_widget_set_size_request (priv->abook_button, RECPT_BUTTON_WIDTH_HILDON2, -1);
372 #else
373         gtk_button_set_relief (GTK_BUTTON (priv->abook_button), GTK_RELIEF_NONE);
374 #endif
375         gtk_button_set_focus_on_click (GTK_BUTTON (priv->abook_button), FALSE);
376         GTK_WIDGET_UNSET_FLAGS (priv->abook_button, GTK_CAN_FOCUS);
377         gtk_button_set_alignment (GTK_BUTTON (priv->abook_button), 0.5, 0.5);
378 #ifdef MODEST_TOOLKIT_HILDON2
379         abook_icon = gtk_image_new_from_icon_name ("general_contacts", HILDON_ICON_SIZE_FINGER);
380 #else
381         abook_icon = gtk_image_new_from_icon_name ("qgn_list_addressbook", GTK_ICON_SIZE_BUTTON);
382 #endif
383         gtk_container_add (GTK_CONTAINER (priv->abook_button), abook_icon);
384
385         priv->text_view = modest_toolkit_factory_create_text_view  (modest_runtime_get_toolkit_factory ());
386         /* Auto-capitalization is the default, so let's turn it off: */
387 #ifdef MAEMO_CHANGES
388         hildon_gtk_text_view_set_input_mode (GTK_TEXT_VIEW (priv->text_view), 
389                 HILDON_GTK_INPUT_MODE_FULL);
390 #endif
391         
392         priv->recipients = NULL;
393
394 #ifdef MODEST_TOOLKIT_HILDON2
395         priv->scrolled_window = NULL;
396         gtk_box_pack_start (GTK_BOX (instance), priv->text_view, TRUE, TRUE, 0);
397 #else
398         priv->scrolled_window = modest_scroll_text_new (GTK_TEXT_VIEW (priv->text_view), 1024);
399         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window), GTK_POLICY_NEVER,
400                                         GTK_POLICY_AUTOMATIC);
401         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window), GTK_SHADOW_IN);
402 /*      gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->text_view); */
403
404         gtk_box_pack_start (GTK_BOX (instance), priv->scrolled_window, TRUE, TRUE, 0);
405 /*      gtk_box_pack_start (GTK_BOX (instance), priv->text_view, TRUE, TRUE, 0); */
406 #endif
407         abook_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
408         gtk_alignment_set_padding (GTK_ALIGNMENT (abook_align), 0, 0, MODEST_MARGIN_DEFAULT, 0);
409         gtk_container_add (GTK_CONTAINER (abook_align), priv->abook_button);
410         gtk_box_pack_end (GTK_BOX (instance), abook_align, FALSE, FALSE, 0);
411
412         gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW (priv->text_view), FALSE);
413         gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (priv->text_view), TRUE);
414         gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->text_view), TRUE);
415
416         gtk_text_view_set_justification (GTK_TEXT_VIEW (priv->text_view), GTK_JUSTIFY_LEFT);
417         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->text_view), GTK_WRAP_CHAR);
418
419         gtk_widget_set_size_request (priv->text_view, 75, -1);
420
421         buffer = modest_text_view_get_buffer (priv->text_view);
422         g_signal_connect (G_OBJECT (priv->abook_button), "clicked", G_CALLBACK (modest_recpt_editor_on_abook_clicked), instance);
423         g_signal_connect (G_OBJECT (priv->text_view), "key-press-event", G_CALLBACK (modest_recpt_editor_on_key_press_event), instance);
424         g_signal_connect (G_OBJECT (priv->text_view), "focus-in-event", G_CALLBACK (modest_recpt_editor_on_focus_in), instance);
425         g_signal_connect (G_OBJECT (buffer), "insert-text",
426                           G_CALLBACK (modest_recpt_editor_on_insert_text),
427                           instance);
428         g_signal_connect_after (G_OBJECT (buffer), "insert-text",
429                                 G_CALLBACK (modest_recpt_editor_on_insert_text_after),
430                                 instance);
431
432         priv->on_mark_set_handler = g_signal_connect (G_OBJECT (buffer), "mark-set", 
433                                                       G_CALLBACK (modest_recpt_editor_on_mark_set), 
434                                                       instance);
435
436         return;
437 }
438
439 void
440 modest_recpt_editor_set_field_size_group (ModestRecptEditor *recpt_editor, GtkSizeGroup *size_group)
441 {
442         ModestRecptEditorPrivate *priv;
443
444         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
445         g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
446         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
447
448 #ifdef MODEST_TOOLKIT_HILDON2
449         gtk_size_group_add_widget (size_group, priv->text_view);
450 #else
451         gtk_size_group_add_widget (size_group, priv->scrolled_window);
452 #endif
453 }
454
455 GtkTextBuffer *
456 modest_recpt_editor_get_buffer (ModestRecptEditor *recpt_editor)
457 {
458         ModestRecptEditorPrivate *priv;
459
460         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), NULL);
461         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
462
463         return modest_text_view_get_buffer (priv->text_view);
464 }
465
466 static void
467 modest_recpt_editor_on_abook_clicked (GtkButton *button, ModestRecptEditor *editor)
468 {
469         g_return_if_fail (MODEST_IS_RECPT_EDITOR (editor));
470
471         g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
472 }
473
474 static void
475 modest_recpt_editor_on_mark_set (GtkTextBuffer *buffer,
476                                  GtkTextIter *iter,
477                                  GtkTextMark *mark,
478                                  ModestRecptEditor *recpt_editor)
479 {
480         ModestRecptEditorPrivate *priv;
481         GtkTextIter insert_iter, selection_iter;
482         GtkTextMark *selection_bound;
483         GtkTextMark *insert;
484         GtkTextTag *tag;
485         gboolean selection_changed = FALSE;
486         gboolean select_to_left;
487
488         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
489
490         buffer = modest_recpt_editor_get_buffer (recpt_editor);
491         selection_bound = gtk_text_buffer_get_selection_bound (buffer);
492         insert = gtk_text_buffer_get_insert (buffer);
493
494         if (mark != selection_bound && mark != insert)
495                 return;
496
497         gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert);
498         gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection_bound);
499
500         select_to_left = gtk_text_iter_get_offset (&selection_iter) > gtk_text_iter_get_offset (&insert_iter);
501
502         tag = iter_has_recipient (&insert_iter);
503         if (tag) {
504                 if (select_to_left) {
505                         if (!gtk_text_iter_begins_tag (&insert_iter, tag)) {
506                                 gtk_text_iter_backward_to_tag_toggle (&insert_iter, tag);
507                                 selection_changed = TRUE;
508                         }
509                 } else {
510                         gtk_text_iter_forward_to_tag_toggle (&insert_iter, tag);
511                         selection_changed = TRUE;
512                 }
513         }
514                 
515         tag = iter_has_recipient (&selection_iter);
516         if (tag != NULL) {
517                 if (select_to_left) {
518                         gtk_text_iter_forward_to_tag_toggle (&selection_iter, tag);
519                         selection_changed = TRUE;
520                 } else {
521                         if (!gtk_text_iter_begins_tag (&selection_iter, tag)) {
522                                 gtk_text_iter_backward_to_tag_toggle (&selection_iter, tag);
523                                 selection_changed = TRUE;
524                         }
525                 }
526         }
527         
528         if (selection_changed) {
529                 /* We block this signal handler in order to prevent a
530                    stack overflow caused by recursive calls to this
531                    handler as the select_range call could issue a
532                    "mark-set" signal */
533                 g_signal_handler_block (buffer, priv->on_mark_set_handler);
534                 gtk_text_buffer_select_range (buffer, &insert_iter, &selection_iter);
535                 g_signal_handler_unblock (buffer, priv->on_mark_set_handler);
536         }
537 }
538
539 static gboolean 
540 modest_recpt_editor_on_focus_in (GtkTextView *text_view,
541                                  GdkEventFocus *event,
542                                  ModestRecptEditor *editor)
543 {
544         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
545         gtk_text_view_place_cursor_onscreen (GTK_TEXT_VIEW (priv->text_view));
546
547         return FALSE;
548 }
549
550 static gboolean
551 is_valid_insert (const gchar *text, gint len)
552 {
553         gunichar c;
554         gunichar next_c;
555         gint i= 0;
556         gboolean quoted = FALSE;
557         const gchar *current, *next_current;
558         if (text == NULL)
559                 return TRUE;
560         current = text;
561
562         while (((len == -1)||(i < len)) && (*current != '\0')) {
563                 c = g_utf8_get_char (current);
564                 next_current = g_utf8_next_char (current);
565                 if (next_current && *next_current != '\0')
566                         next_c = g_utf8_get_char (g_utf8_next_char (current));
567                 else
568                         next_c = 0;
569                 if (!quoted && ((c == g_utf8_get_char(",") || c == g_utf8_get_char (";")))) {
570                         if ((next_c != 0) && (next_c != g_utf8_get_char ("\n")))
571                                 return FALSE;
572                         else {
573                           current = g_utf8_next_char (next_current);
574                           continue;
575                         }
576                 }
577                 if (c == 0x2022 || c == 0xfffc ||
578                     c == g_utf8_get_char ("\n") ||
579                     c == g_utf8_get_char ("\t"))
580                         return FALSE;
581                 if (c == g_utf8_get_char ("\""))
582                         quoted = !quoted;
583                 current = g_utf8_next_char (current);
584                 i = current - text;
585         }
586         return TRUE;
587 }
588
589 static gchar *
590 create_valid_text (const gchar *text, gint len)
591 {
592         gunichar c;
593         gunichar next_c;
594         gint i= 0;
595         GString *str;
596         gboolean quoted = FALSE;
597         const gchar *current, *next_current;
598
599         if (text == NULL)
600                 return NULL;
601
602         str = g_string_new ("");
603         current = text;
604
605         while (((len == -1)||(i < len)) && (*current != '\0')) {
606                 c = g_utf8_get_char (current);
607                 next_current = g_utf8_next_char (current);
608                 if (next_current && *next_current != '\0')
609                         next_c = g_utf8_get_char (g_utf8_next_char (current));
610                 else
611                         next_c = 0;
612                 if (c != 0x2022 && c != 0xfffc &&
613                     c != g_utf8_get_char ("\n") &&
614                     c != g_utf8_get_char ("\t"))
615                         str = g_string_append_unichar (str, c);
616                 if (!quoted && ((c == g_utf8_get_char(",") || c == g_utf8_get_char (";")))) {
617                         if ((next_c != 0) && (next_c != g_utf8_get_char ("\n"))) {
618                                 gchar *last_separator = MAX (g_utf8_strrchr(str->str, -1, g_utf8_get_char (",")),
619                                                              g_utf8_strrchr(str->str, -1, g_utf8_get_char (";")));
620                                 if (last_separator) {
621                                         gchar *last_at = g_utf8_strrchr (str->str, -1, g_utf8_get_char ("@"));
622                                         if (last_at) {
623                                                 if (last_at < last_separator)
624                                                         str = g_string_append_c (str, '\n');
625                                         }
626                                 } else {
627                                         if (g_utf8_strrchr (str->str, -1, g_utf8_get_char ("@")))
628                                                 str = g_string_append_c (str, '\n');
629                                 }
630                         }
631                 }
632                 if (c == g_utf8_get_char ("\""))
633                         quoted = !quoted;
634                 current = g_utf8_next_char (current);
635                 i = current - text;
636         }
637
638         return g_string_free (str, FALSE);
639 }
640
641 /* Called after the default handler, and thus after the text was
642    inserted. We use this to insert a break after a ',' or a ';'*/
643 static void
644 modest_recpt_editor_on_insert_text_after (GtkTextBuffer *buffer,
645                                           GtkTextIter *location,
646                                           gchar *text,
647                                           gint len,
648                                           ModestRecptEditor *editor)
649 {
650         GtkTextIter prev;
651         gunichar prev_char;
652         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
653
654         prev = *location;
655         /* We must go backwards twice as location points to the next
656            valid position to insert text */
657         if (!gtk_text_iter_backward_chars (&prev, 2))
658                 return;
659
660         prev_char = gtk_text_iter_get_char (&prev);
661         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
662         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
663         if ((prev_char == ';'||prev_char == ',')&&(!quote_opened(&prev))) {
664                 GtkTextMark *insert;
665                 gtk_text_iter_forward_char (&prev);
666                 gtk_text_buffer_insert (buffer, &prev, "\n",-1);
667                 insert = gtk_text_buffer_get_insert (buffer);
668                 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->text_view), &prev, 0.0,TRUE, 0.0, 1.0);
669         }
670         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
671         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
672 }
673
674 /* Called before the default handler, we use it to validate the inputs */
675 static void
676 modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer,
677                                     GtkTextIter *location,
678                                     gchar *text,
679                                     gint len,
680                                     ModestRecptEditor *editor)
681 {
682         if (len > 1024)
683                 len = 1024;
684
685         if (!is_valid_insert (text, len)) {
686                 gchar *new_text = create_valid_text (text, len);
687                 g_signal_stop_emission_by_name (G_OBJECT (buffer), "insert-text");
688                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
689                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
690                 gtk_text_buffer_insert (buffer, location, new_text, -1);
691                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
692                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
693                 g_free (new_text);
694                 return;
695         }
696
697         if (iter_has_recipient (location)) {
698                 gtk_text_buffer_get_end_iter (buffer, location);
699                 gtk_text_buffer_place_cursor (buffer, location);
700         }
701 }
702
703 static GtkTextTag *
704 iter_has_recipient (GtkTextIter *iter)
705 {
706         GSList *tags, *node;
707         GtkTextTag *result = NULL;
708
709         tags = gtk_text_iter_get_tags (iter);
710
711         for (node = tags; node != NULL; node = g_slist_next (node)) {
712                 GtkTextTag *tag = GTK_TEXT_TAG (node->data);
713
714                 if (g_object_get_data (G_OBJECT (tag), "recipient-tag-id")) {
715                         result = tag;
716                         break;
717                 }
718         }
719         g_slist_free (tags);
720         return result;
721 }
722
723 static GtkTextTag *
724 prev_iter_has_recipient (GtkTextIter *iter)
725 {
726         GtkTextIter prev;
727
728         prev = *iter;
729         gtk_text_iter_backward_char (&prev);
730         return iter_has_recipient (&prev);
731 }
732
733 /* static GtkTextTag * */
734 /* next_iter_has_recipient (GtkTextIter *iter) */
735 /* { */
736 /*      GtkTextIter next; */
737
738 /*      next = *iter; */
739 /*      return iter_has_recipient (&next); */
740 /* } */
741
742 static gunichar 
743 iter_previous_char (GtkTextIter *iter)
744 {
745         GtkTextIter prev;
746
747         prev = *iter;
748         gtk_text_iter_backward_char (&prev);
749         return gtk_text_iter_get_char (&prev);
750 }
751
752 /* static gunichar  */
753 /* iter_next_char (GtkTextIter *iter) */
754 /* { */
755 /*      GtkTextIter next; */
756
757 /*      next = *iter; */
758 /*      gtk_text_iter_forward_char (&next); */
759 /*      return gtk_text_iter_get_char (&next); */
760 /* } */
761
762 static void
763 select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag, gboolean grow, gboolean left_not_right)
764 {
765         GtkTextIter start, end;
766
767         start = *iter;
768         if (!gtk_text_iter_begins_tag (&start, tag)) {
769                 gtk_text_iter_backward_to_tag_toggle (&start, tag);
770         } else {
771                 if (!left_not_right) {
772                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &start, &start);
773                         return;
774                 }
775         }
776         end = *iter;
777         if (!gtk_text_iter_ends_tag (&end, tag)) {
778                 gtk_text_iter_forward_to_tag_toggle (&end, tag);
779         } else {
780                 if (left_not_right) {
781                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &end, &end);
782                         return;
783                 }
784         }
785         if (grow) {
786                 if (left_not_right)
787                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &start, &end);
788                 else
789                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &end, &start);
790         } else {
791                 if (left_not_right)
792                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &start, &start);
793                 else
794                         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &end, &end);
795         }
796         *iter = left_not_right?start:end;
797 }
798
799 static gboolean 
800 quote_opened (GtkTextIter *iter)
801 {
802         GtkTextIter start;
803         GtkTextBuffer *buffer;
804         gboolean opened = FALSE;
805
806         buffer = gtk_text_iter_get_buffer (iter);
807         gtk_text_buffer_get_start_iter (buffer, &start);
808
809         while (!gtk_text_iter_equal (&start, iter)) {
810                 gunichar current_char = gtk_text_iter_get_char (&start);
811                 if (current_char == '"')
812                         opened = !opened;
813                 else if (current_char == '\\')
814                         gtk_text_iter_forward_char (&start);
815                 if (!gtk_text_iter_equal (&start, iter))
816                         gtk_text_iter_forward_char (&start);
817                         
818         }
819         return opened;
820
821 }
822
823
824 static gboolean
825 modest_recpt_editor_on_key_press_event (GtkTextView *text_view,
826                                           GdkEventKey *key,
827                                           ModestRecptEditor *editor)
828 {
829         GtkTextMark *insert;
830         GtkTextMark *selection;
831         GtkTextBuffer * buffer;
832         GtkTextIter location, selection_loc;
833         GtkTextTag *tag;
834         gboolean shift_pressed;
835         gboolean select_to_left;
836         gboolean has_selection;
837      
838         buffer = modest_text_view_get_buffer (GTK_WIDGET (text_view));
839         insert = gtk_text_buffer_get_insert (buffer);
840         selection = gtk_text_buffer_get_selection_bound (buffer);
841
842         /* cases to cover:
843          *    * cursor is on resolved recipient:
844          *        - right should go to first character after the recipient (usually ; or ,)
845          *        - left should fo to the first character before the recipient
846          *        - return should run check names on the recipient.
847          *    * cursor is just after a recipient:
848          *        - right should go to the next character. If it's a recipient, should select
849          *          it
850          *        - left should go to the previous character. If it's a recipient, should go
851          *          to the first character of the recipient, and select it.
852          *    * cursor is on arbitrary text:
853          *        - return should add a ; and go to the next line
854          *        - left or right standard ones.
855          *    * cursor is after a \n:
856          *        - left should go to the character before the \n (as if \n was not a character)
857          *    * cursor is before a \n:
858          *        - right should go to the character after the \n
859          */
860
861         gtk_text_buffer_get_iter_at_mark (buffer, &location, insert);
862         gtk_text_buffer_get_iter_at_mark (buffer, &selection_loc, selection);
863
864         select_to_left = gtk_text_iter_get_offset (&selection_loc) > gtk_text_iter_get_offset (&location);
865         has_selection = gtk_text_iter_get_offset (&selection_loc) != gtk_text_iter_get_offset (&location);
866         shift_pressed = key->state & GDK_SHIFT_MASK;
867
868         switch (key->keyval) {
869         case GDK_Left:
870         case GDK_KP_Left: 
871         {
872                 gboolean cursor_ready = FALSE;
873                 GtkTextIter prev_location;
874                 gboolean moved = TRUE;
875
876                 prev_location = location;
877                 while (!cursor_ready) {
878                         if (moved && (iter_previous_char (&location) == '\n')) {
879                                 moved = gtk_text_iter_backward_char (&location);
880                         } else {
881                                 cursor_ready = TRUE;
882                         }
883                 }
884                 tag = iter_has_recipient (&location);
885                 if (has_selection && gtk_text_iter_ends_tag (&prev_location, tag)) {
886                         gtk_text_iter_backward_to_tag_toggle (&prev_location, tag);
887                         location = prev_location;
888                         cursor_ready = FALSE;
889                         moved = TRUE;
890                         while (moved && !cursor_ready) {
891                                 if (iter_previous_char (&location) == '\n') {
892                                         moved = gtk_text_iter_backward_char (&location);
893                                 } else {
894                                         cursor_ready = TRUE;
895                                 }
896                         }
897                 }
898
899                 if ((tag != NULL)&& (gtk_text_iter_is_start (&location) || !(gtk_text_iter_begins_tag (&location, tag)))) {
900                         if (has_selection) {
901                                 gtk_text_buffer_select_range (buffer, &location, &location);
902                         } else {
903                                 select_tag_of_iter (&location, tag, select_to_left, TRUE);
904                         }
905                         gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
906
907                         if (shift_pressed) {
908                                 gtk_text_buffer_select_range (buffer, &location, &selection_loc);
909                         }
910                         return TRUE;
911                 }
912                 gtk_text_iter_backward_char (&location);
913                 tag = iter_has_recipient (&location);
914                 if (tag != NULL)
915                         select_tag_of_iter (&location, tag, select_to_left, TRUE);
916                 else {
917                         gtk_text_buffer_place_cursor (buffer, &location);
918                 }
919                 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
920
921                 if (shift_pressed) {
922                         gtk_text_buffer_select_range (buffer, &location, &selection_loc);
923                 }
924
925                 return TRUE;
926         }
927         break;
928         case GDK_Right:
929         case GDK_KP_Right:
930         {
931                 gboolean cursor_moved = FALSE;
932                 gboolean moved = TRUE;
933
934                 tag = iter_has_recipient (&location);
935                 if ((tag != NULL)&&(!gtk_text_iter_ends_tag (&location, tag))) {
936                         gtk_text_iter_forward_to_tag_toggle (&location, tag);
937                         moved = TRUE;
938                         while (moved && (gtk_text_iter_get_char (&location) == '\n'))
939                                 moved = gtk_text_iter_forward_char (&location);
940                         gtk_text_buffer_place_cursor (buffer, &location);
941                         gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
942
943                         if (shift_pressed) {
944                                 gtk_text_buffer_select_range (buffer, &location, &selection_loc);
945                         }
946                         return TRUE;
947                 }
948
949                 moved = TRUE;
950                 while (moved && (gtk_text_iter_get_char (&location) == '\n')) {
951                         moved = gtk_text_iter_forward_char (&location);
952                         cursor_moved = TRUE;
953                 }
954                 if (!cursor_moved)
955                         gtk_text_iter_forward_char (&location);
956
957                 moved = TRUE;
958                 while (moved && (gtk_text_iter_get_char (&location) == '\n')) {
959                         moved = gtk_text_iter_forward_char (&location);
960                         cursor_moved = TRUE;
961                 }
962
963                 tag = iter_has_recipient (&location);
964                 if (tag != NULL)
965                         select_tag_of_iter (&location, tag, !select_to_left, FALSE);
966                 else
967                         gtk_text_buffer_place_cursor (buffer, &location);
968                 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
969
970                 if (shift_pressed) {
971                         gtk_text_buffer_select_range (buffer, &location, &selection_loc);
972                 }
973                 return TRUE;
974         }
975         break;
976         case GDK_Return:
977         case GDK_KP_Enter:
978         {
979                 gint insert_offset, selection_offset;
980                 insert_offset = gtk_text_iter_get_offset (&location);
981                 selection_offset = gtk_text_iter_get_offset (&selection_loc);
982                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
983                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
984                 if (selection_offset > insert_offset)
985                         location = selection_loc;
986                 tag = iter_has_recipient (&location);
987                 if (tag != NULL) {
988                         gtk_text_buffer_get_end_iter (buffer, &location);
989                         gtk_text_buffer_place_cursor (buffer, &location);
990                         if ((iter_previous_char (&location) != ';')&&(iter_previous_char (&location) != ','))
991                                 gtk_text_buffer_insert_at_cursor (buffer, ";", -1);
992                         gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
993                 } else {
994                         gunichar prev_char = iter_previous_char (&location);
995                         if ((gtk_text_iter_is_start (&location))||(prev_char == '\n')
996                             ||(prev_char == ';')||(prev_char == ',')) 
997                                 g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
998                         else {
999                                 if ((prev_char != ';') && (prev_char != ','))
1000                                         gtk_text_buffer_insert_at_cursor (buffer, ";", -1);
1001                                 gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
1002                         }
1003                 }
1004                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
1005                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text_after, editor);
1006                 return TRUE;
1007         }
1008         break;
1009         case GDK_BackSpace:
1010         {
1011                 #if GTK_CHECK_VERSION(2, 10, 0) /* gtk_text_buffer_get_has_selection is only available in GTK+ 2.10 */
1012                 if (gtk_text_buffer_get_has_selection (buffer)) {
1013                         gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
1014                         return TRUE;
1015                 }
1016                 #else
1017                 if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
1018                         gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
1019                         return TRUE;
1020                 }
1021                 #endif
1022
1023                 tag = prev_iter_has_recipient (&location);
1024                 if (tag != NULL) {
1025                         GtkTextIter iter_in_tag;
1026                         iter_in_tag = location;
1027                         gtk_text_iter_backward_char (&iter_in_tag);
1028                         select_tag_of_iter (&iter_in_tag, tag, FALSE, TRUE);
1029                         gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
1030                         return TRUE;
1031                 }
1032                 return FALSE;
1033         }
1034         break;
1035         default:
1036                 return FALSE;
1037         }
1038 }
1039
1040 static void _discard_chars (GtkTextIter *start, GtkTextIter *end)
1041 {
1042         while (!gtk_text_iter_equal (start, end)) {
1043                 gunichar c = gtk_text_iter_get_char (start);
1044
1045                 if (c == '\n' || c == ';' || c == ',' || c == ' ') {
1046                         if (!gtk_text_iter_forward_char (start))
1047                                 break;
1048                 } else {
1049                         break;
1050                 }
1051
1052         }
1053 }
1054
1055 /* NOTE: before calling this function be sure that both
1056    modest_recpt_editor_on_insert_text and
1057    modest_recpt_editor_on_insert_text_after won't be triggered during
1058    the execution of the procedure. You'll have to block both signal
1059    handlers otherwise you'll get an infinite loop and most likely a
1060    SIGSEV caused by a stack overflow */
1061 static void
1062 modest_recpt_editor_add_tags (ModestRecptEditor *editor,
1063                               const gchar * recipient_id)
1064 {
1065
1066         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
1067         GtkTextBuffer *buffer = modest_text_view_get_buffer (priv->text_view);
1068         GtkTextTag *tag;
1069         GtkTextIter start, end;
1070         gchar * buffer_contents;
1071         GtkTextIter start_match, end_match;
1072
1073         /* This would move the cursor to the end of the buffer
1074            containing new line character. */
1075         gtk_text_buffer_get_bounds (buffer, &start, &end);
1076         buffer_contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1077         gtk_text_buffer_set_text(buffer,buffer_contents,strlen(buffer_contents));
1078
1079         tag = gtk_text_buffer_create_tag (buffer, NULL,
1080                                           "underline", PANGO_UNDERLINE_SINGLE,
1081                                           "wrap-mode", GTK_WRAP_NONE,
1082                                           "editable", TRUE, NULL);
1083
1084         g_object_set_data (G_OBJECT (tag), "recipient-tag-id", GINT_TO_POINTER (RECIPIENT_TAG_ID));
1085         g_object_set_data_full (G_OBJECT (tag), "recipient-id", g_strdup (recipient_id), (GDestroyNotify) g_free);
1086
1087         /* Formatting the buffer content by applying tag */
1088         gtk_text_buffer_get_bounds (buffer, &start, &end);
1089         _discard_chars (&start, &end);
1090         while (gtk_text_iter_forward_search(&start, ";",
1091                                             GTK_TEXT_SEARCH_TEXT_ONLY |
1092                                             GTK_TEXT_SEARCH_VISIBLE_ONLY,
1093                                             &start_match, &end_match, &end )) {
1094                 int offset;
1095
1096                 gtk_text_buffer_apply_tag(buffer, tag, &start, &start_match);
1097                 offset = gtk_text_iter_get_offset (&end_match);
1098                 gtk_text_buffer_get_iter_at_offset(buffer, &start, offset);
1099                 _discard_chars (&start, &end);
1100         }
1101         g_free (buffer_contents);
1102 }
1103
1104 void
1105 modest_recpt_editor_grab_focus (ModestRecptEditor *recpt_editor)
1106 {
1107         ModestRecptEditorPrivate *priv;
1108         
1109         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
1110         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
1111
1112         if (GTK_WIDGET_VISIBLE (recpt_editor)) {
1113                 gtk_widget_grab_focus (priv->text_view);
1114         }
1115 }
1116
1117 gboolean
1118 modest_recpt_editor_has_focus (ModestRecptEditor *recpt_editor)
1119 {
1120         ModestRecptEditorPrivate *priv;
1121         
1122         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), FALSE);
1123         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
1124
1125         return GTK_WIDGET_VISIBLE (priv->text_view) && 
1126                 gtk_widget_is_focus (priv->text_view);
1127 }
1128
1129 void 
1130 modest_recpt_editor_set_show_abook_button (ModestRecptEditor *recpt_editor, gboolean show)
1131 {
1132         ModestRecptEditorPrivate *priv;
1133         
1134         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
1135         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
1136
1137         priv->show_abook = show;
1138
1139         if (show)
1140                 gtk_widget_show (priv->abook_button);
1141         else
1142                 gtk_widget_hide (priv->abook_button);
1143 }
1144
1145 gboolean
1146 modest_recpt_editor_get_show_abook_button (ModestRecptEditor *recpt_editor, gboolean show)
1147 {
1148         ModestRecptEditorPrivate *priv;
1149         
1150         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), FALSE);
1151         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
1152
1153         return priv->show_abook;
1154         
1155 }
1156
1157 static void
1158 modest_recpt_editor_finalize (GObject *object)
1159 {
1160         ModestRecptEditorPrivate *priv;
1161         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (object);
1162
1163         if (g_signal_handler_is_connected (object, priv->on_mark_set_handler))
1164                 g_signal_handler_disconnect (object, priv->on_mark_set_handler);
1165         priv->on_mark_set_handler = 0;
1166
1167         if (priv->recipients) {
1168                 g_free (priv->recipients);
1169                 priv->recipients = NULL;
1170         }
1171
1172         (*parent_class->finalize) (object);
1173
1174         return;
1175 }
1176
1177 static void 
1178 modest_recpt_editor_class_init (ModestRecptEditorClass *klass)
1179 {
1180         GObjectClass *object_class;
1181
1182         parent_class = g_type_class_peek_parent (klass);
1183         object_class = (GObjectClass*) klass;
1184
1185         object_class->finalize = modest_recpt_editor_finalize;
1186
1187         g_type_class_add_private (object_class, sizeof (ModestRecptEditorPrivate));
1188
1189         signals[OPEN_ADDRESSBOOK_SIGNAL] = 
1190                 g_signal_new ("open-addressbook",
1191                               G_TYPE_FROM_CLASS (object_class),
1192                               G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
1193                               G_STRUCT_OFFSET (ModestRecptEditorClass, open_addressbook),
1194                               NULL, NULL,
1195                               g_cclosure_marshal_VOID__VOID,
1196                               G_TYPE_NONE, 0);
1197
1198         return;
1199 }
1200
1201 GType 
1202 modest_recpt_editor_get_type (void)
1203 {
1204         static GType type = 0;
1205
1206         if (G_UNLIKELY(type == 0))
1207         {
1208                 static const GTypeInfo info = 
1209                 {
1210                   sizeof (ModestRecptEditorClass),
1211                   NULL,   /* base_init */
1212                   NULL,   /* base_finalize */
1213                   (GClassInitFunc) modest_recpt_editor_class_init,   /* class_init */
1214                   NULL,   /* class_finalize */
1215                   NULL,   /* class_data */
1216                   sizeof (ModestRecptEditor),
1217                   0,      /* n_preallocs */
1218                   modest_recpt_editor_instance_init    /* instance_init */
1219                 };
1220
1221                 type = g_type_register_static (GTK_TYPE_HBOX,
1222                         "ModestRecptEditor",
1223                         &info, 0);
1224
1225         }
1226
1227         return type;
1228 }