* src/modest-ui-actions.[ch]:
[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 <glib/gi18n-lib.h>
33
34 #include <gtk/gtkscrolledwindow.h>
35 #include <gtk/gtktextview.h>
36 #include <gtk/gtkimage.h>
37 #include <gtk/gtkbutton.h>
38
39 #include <modest-text-utils.h>
40 #include <modest-recpt-editor.h>
41 #include <modest-scroll-text.h>
42 #include <pango/pango-attributes.h>
43 #include <string.h>
44 #include <gdk/gdkkeysyms.h>
45
46 static GObjectClass *parent_class = NULL;
47
48 #define RECIPIENT_TAG_ID "recpt-id"
49
50 /* signals */
51 enum {
52         OPEN_ADDRESSBOOK_SIGNAL,
53         LAST_SIGNAL
54 };
55
56 typedef struct _ModestRecptEditorPrivate ModestRecptEditorPrivate;
57
58 struct _ModestRecptEditorPrivate
59 {
60         GtkWidget *text_view;
61         GtkWidget *abook_button;
62         GtkWidget *scrolled_window;
63         gchar *recipients;
64
65 };
66
67 #define MODEST_RECPT_EDITOR_GET_PRIVATE(o)      \
68         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_RECPT_EDITOR, ModestRecptEditorPrivate))
69
70 static guint signals[LAST_SIGNAL] = {0};
71
72 /* static functions: GObject */
73 static void modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class);
74 static void modest_recpt_editor_finalize (GObject *object);
75 static void modest_recpt_editor_class_init (ModestRecptEditorClass *klass);
76
77 /* widget events */
78 static void modest_recpt_editor_on_abook_clicked (GtkButton *button,
79                                                   ModestRecptEditor *editor);
80 static gboolean modest_recpt_editor_on_button_release_event (GtkWidget *widget,
81                                                              GdkEventButton *event,
82                                                              ModestRecptEditor *editor);
83 static void modest_recpt_editor_move_cursor_to_end (ModestRecptEditor *editor);
84 static void modest_recpt_editor_on_focus_in (GtkTextView *text_view,
85                                              GdkEventFocus *event,
86                                              ModestRecptEditor *editor);
87 static void modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer,
88                                                 GtkTextIter *location,
89                                                 gchar *text,
90                                                 gint len,
91                                                 ModestRecptEditor *editor);
92 static gboolean modest_recpt_editor_on_key_press_event (GtkTextView *text_view,
93                                                           GdkEventKey *key,
94                                                           ModestRecptEditor *editor);
95 static GtkTextTag *iter_has_recipient (GtkTextIter *iter);
96 static gunichar iter_previous_char (GtkTextIter *iter);
97 /* static gunichar iter_next_char (GtkTextIter *iter); */
98 static GtkTextTag *prev_iter_has_recipient (GtkTextIter *iter);
99 /* static GtkTextTag *next_iter_has_recipient (GtkTextIter *iter); */
100 static void select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag);
101
102
103 /**
104  * modest_recpt_editor_new:
105  *
106  * Return value: a new #ModestRecptEditor instance implemented for Gtk+
107  **/
108 GtkWidget*
109 modest_recpt_editor_new (void)
110 {
111         ModestRecptEditor *self = g_object_new (MODEST_TYPE_RECPT_EDITOR, 
112                                                 "homogeneous", FALSE,
113                                                 "spacing", 1,
114                                                 NULL);
115
116         return GTK_WIDGET (self);
117 }
118
119 void
120 modest_recpt_editor_set_recipients (ModestRecptEditor *recpt_editor, const gchar *recipients)
121 {
122         ModestRecptEditorPrivate *priv;
123         GtkTextBuffer *buffer = NULL;
124
125         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
126         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
127
128         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
129
130         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
131         gtk_text_buffer_set_text (buffer, recipients, -1);
132         if (GTK_WIDGET_REALIZED (recpt_editor))
133                 gtk_widget_queue_resize (GTK_WIDGET (recpt_editor));
134         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
135
136 }
137
138 void
139 modest_recpt_editor_add_recipients (ModestRecptEditor *recpt_editor, const gchar *recipients)
140 {
141         ModestRecptEditorPrivate *priv;
142         GtkTextBuffer *buffer = NULL;
143         GtkTextIter iter;
144         gchar * string_to_add;
145
146         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
147         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
148
149         if (recipients == NULL)
150                 return;
151
152         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
153
154         if (gtk_text_buffer_get_char_count (buffer) > 0) {
155                 string_to_add = g_strconcat (";\n", recipients, NULL);
156         } else {
157                 string_to_add = g_strdup (recipients);
158         }
159
160         gtk_text_buffer_get_end_iter (buffer, &iter);
161
162         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
163
164         gtk_text_buffer_insert (buffer, &iter, recipients, -1);
165         
166         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
167
168         if (GTK_WIDGET_REALIZED (recpt_editor))
169                 gtk_widget_queue_resize (GTK_WIDGET (recpt_editor));
170
171 }
172
173 void 
174 modest_recpt_editor_add_resolved_recipient (ModestRecptEditor *recpt_editor, GSList *email_list, const gchar * recipient_id)
175 {
176         ModestRecptEditorPrivate *priv;
177         GtkTextBuffer *buffer = NULL;
178         GtkTextIter start, end, iter;
179         GtkTextTag *tag = NULL;
180         GSList *node;
181         gboolean is_first_recipient = TRUE;
182       
183         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
184         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
185
186         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
187
188         g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
189         gtk_text_buffer_get_bounds (buffer, &start, &end);
190         if (gtk_text_buffer_get_char_count (buffer) > 0) {
191                 gchar * buffer_contents;
192
193                 gtk_text_buffer_get_bounds (buffer, &start, &end);
194                 buffer_contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
195                 if (!g_str_has_suffix (buffer_contents, "\n")) {
196                         if (g_str_has_suffix (buffer_contents, ";")||(g_str_has_suffix (buffer_contents, ",")))
197                                 gtk_text_buffer_insert (buffer, &end, "\n", -1);
198                         else 
199                                 gtk_text_buffer_insert (buffer, &end, ";\n", -1);
200                 }
201                 g_free (buffer_contents);
202         }
203
204         gtk_text_buffer_get_end_iter (buffer, &iter);
205
206         tag = gtk_text_buffer_create_tag (buffer, NULL, 
207                                           "underline", PANGO_UNDERLINE_SINGLE,
208                                           "wrap-mode", GTK_WRAP_NONE,
209                                           "editable", TRUE, NULL);
210
211         g_object_set_data (G_OBJECT (tag), "recipient-tag-id", GINT_TO_POINTER (RECIPIENT_TAG_ID));
212         g_object_set_data_full (G_OBJECT (tag), "recipient-id", g_strdup (recipient_id), (GDestroyNotify) g_free);
213
214         for (node = email_list; node != NULL; node = g_slist_next (node)) {
215                 gchar *recipient = (gchar *) node->data;
216
217                 if ((recipient) && (strlen (recipient) != 0)) {
218
219                         if (!is_first_recipient)
220                         gtk_text_buffer_insert (buffer, &iter, "\n", -1);
221
222                         gtk_text_buffer_insert_with_tags (buffer, &iter, recipient, -1, tag, NULL);
223                         gtk_text_buffer_insert (buffer, &iter, ";", -1);
224                         is_first_recipient = FALSE;
225                 }
226         }
227         g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor);
228
229         modest_recpt_editor_move_cursor_to_end (recpt_editor);
230
231 }
232
233
234 const gchar *
235 modest_recpt_editor_get_recipients (ModestRecptEditor *recpt_editor)
236 {
237         ModestRecptEditorPrivate *priv;
238         GtkTextBuffer *buffer = NULL;
239         GtkTextIter start, end;
240         gchar *c;
241
242         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), NULL);
243         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
244
245         if (priv->recipients != NULL) {
246                 g_free (priv->recipients);
247                 priv->recipients = NULL;
248         }
249
250         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
251
252         gtk_text_buffer_get_start_iter (buffer, &start);
253         gtk_text_buffer_get_end_iter (buffer, &end);
254
255         priv->recipients = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
256         for (c = priv->recipients; *c == '\0'; c++) {
257                 if (*c == '\n')
258                         *c = ' ';
259         }
260
261         return priv->recipients;
262
263 }
264
265 static void
266 modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class)
267 {
268         ModestRecptEditorPrivate *priv;
269         GtkWidget *abook_icon;
270         GtkTextBuffer *buffer;
271
272         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (instance);
273
274         priv->abook_button = gtk_button_new ();
275         gtk_button_set_relief (GTK_BUTTON (priv->abook_button), GTK_RELIEF_NONE);
276         gtk_button_set_focus_on_click (GTK_BUTTON (priv->abook_button), FALSE);
277         GTK_WIDGET_UNSET_FLAGS (priv->abook_button, GTK_CAN_FOCUS);
278         gtk_button_set_alignment (GTK_BUTTON (priv->abook_button), 1.0, 1.0);
279         abook_icon = gtk_image_new_from_icon_name ("qgn_list_gene_contacts", GTK_ICON_SIZE_BUTTON);
280         gtk_container_add (GTK_CONTAINER (priv->abook_button), abook_icon);
281
282         priv->text_view = gtk_text_view_new ();
283         priv->scrolled_window = modest_scroll_text_new (GTK_TEXT_VIEW (priv->text_view), 5);
284         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
285         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window), GTK_SHADOW_IN);
286 /*      gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->text_view); */
287
288         gtk_box_pack_start (GTK_BOX (instance), priv->scrolled_window, TRUE, TRUE, 0);
289 /*      gtk_box_pack_start (GTK_BOX (instance), priv->text_view, TRUE, TRUE, 0); */
290         gtk_box_pack_end (GTK_BOX (instance), priv->abook_button, FALSE, FALSE, 0);
291
292         gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW (priv->text_view), FALSE);
293         gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (priv->text_view), TRUE);
294         gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->text_view), TRUE);
295
296         gtk_text_view_set_justification (GTK_TEXT_VIEW (priv->text_view), GTK_JUSTIFY_LEFT);
297         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->text_view), GTK_WRAP_CHAR);
298
299         gtk_widget_set_size_request (priv->text_view, 75, -1);
300
301         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
302         g_signal_connect (G_OBJECT (priv->abook_button), "clicked", G_CALLBACK (modest_recpt_editor_on_abook_clicked), instance);
303         g_signal_connect (G_OBJECT (priv->text_view), "button-release-event", G_CALLBACK (modest_recpt_editor_on_button_release_event), instance);
304         g_signal_connect (G_OBJECT (priv->text_view), "key-press-event", G_CALLBACK (modest_recpt_editor_on_key_press_event), instance);
305         g_signal_connect (G_OBJECT (priv->text_view), "focus-in-event", G_CALLBACK (modest_recpt_editor_on_focus_in), instance);
306         g_signal_connect (G_OBJECT (buffer), "insert-text", G_CALLBACK (modest_recpt_editor_on_insert_text), instance);
307
308 /*      gtk_container_set_focus_child (GTK_CONTAINER (instance), priv->text_view); */
309
310         return;
311 }
312
313 void
314 modest_recpt_editor_set_field_size_group (ModestRecptEditor *recpt_editor, GtkSizeGroup *size_group)
315 {
316         ModestRecptEditorPrivate *priv;
317
318         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
319         g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
320         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
321
322         gtk_size_group_add_widget (size_group, priv->scrolled_window);
323 }
324
325 GtkTextBuffer *
326 modest_recpt_editor_get_buffer (ModestRecptEditor *recpt_editor)
327 {
328         ModestRecptEditorPrivate *priv;
329
330         g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), NULL);
331         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
332
333         return gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
334 }
335
336 static void
337 modest_recpt_editor_on_abook_clicked (GtkButton *button, ModestRecptEditor *editor)
338 {
339         g_return_if_fail (MODEST_IS_RECPT_EDITOR (editor));
340
341         g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
342 }
343
344 static gboolean
345 modest_recpt_editor_on_button_release_event (GtkWidget *widget,
346                                              GdkEventButton *event,
347                                              ModestRecptEditor *recpt_editor)
348 {
349         ModestRecptEditorPrivate *priv;
350         GtkTextIter location, start, end;
351         GtkTextMark *mark;
352         GtkTextBuffer *buffer;
353         GtkTextTag *tag;
354         gboolean selection_changed = FALSE;
355         
356         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
357
358         buffer = modest_recpt_editor_get_buffer (recpt_editor);
359         mark = gtk_text_buffer_get_insert (buffer);
360         gtk_text_buffer_get_iter_at_mark (buffer, &location, mark);
361         g_message ("RELEASE OFFSET %d", gtk_text_iter_get_offset (&location));
362
363         gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
364
365         tag = iter_has_recipient (&start);
366         if (tag != NULL)
367                 if (!gtk_text_iter_begins_tag (&start, tag)) {
368                         gtk_text_iter_backward_to_tag_toggle (&start, tag);
369                         selection_changed = TRUE;
370                 } 
371
372         tag = iter_has_recipient (&end);
373         if (tag != NULL) 
374                 if (!gtk_text_iter_ends_tag (&end, tag)) {
375                         gtk_text_iter_forward_to_tag_toggle (&end, tag);
376                         selection_changed = TRUE;
377                 }
378
379         gtk_text_buffer_select_range (buffer, &start, &end);
380
381         return FALSE;
382 }
383
384 static void 
385 modest_recpt_editor_on_focus_in (GtkTextView *text_view,
386                                  GdkEventFocus *event,
387                                  ModestRecptEditor *editor)
388 {
389         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
390         modest_recpt_editor_move_cursor_to_end (editor);
391         gtk_text_view_place_cursor_onscreen (GTK_TEXT_VIEW (priv->text_view));
392 }
393
394 static void 
395 modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer,
396                                     GtkTextIter *location,
397                                     gchar *text,
398                                     gint len,
399                                     ModestRecptEditor *editor)
400 {
401         GtkTextIter prev;
402         gunichar prev_char;
403         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
404
405         if (iter_has_recipient (location)) {
406                 gtk_text_buffer_get_end_iter (buffer, location);
407                 gtk_text_buffer_place_cursor (buffer, location);
408         }
409
410         if (gtk_text_iter_is_start (location))
411                 return;
412
413         if (gtk_text_iter_is_end (location)) {
414                 prev = *location;
415                 if (!gtk_text_iter_backward_char (&prev))
416                         return;
417                 prev_char = gtk_text_iter_get_char (&prev);
418                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
419                 if (prev_char == ';'||prev_char == ',') {
420                         GtkTextMark *insert;
421                         gtk_text_buffer_insert (buffer, location, "\n",-1);
422                         insert = gtk_text_buffer_get_insert (buffer);
423                         gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->text_view), location, 0.0,TRUE, 0.0, 1.0);
424                 }
425                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
426                 
427         }
428
429 }
430
431 static GtkTextTag *
432 iter_has_recipient (GtkTextIter *iter)
433 {
434         GSList *tags, *node;
435         GtkTextTag *result = NULL;
436
437         tags = gtk_text_iter_get_tags (iter);
438
439         for (node = tags; node != NULL; node = g_slist_next (node)) {
440                 GtkTextTag *tag = GTK_TEXT_TAG (node->data);
441
442                 if (g_object_get_data (G_OBJECT (tag), "recipient-tag-id")) {
443                         result = tag;
444                         break;
445                 }
446         }
447         g_slist_free (tags);
448         return result;
449 }
450
451 static GtkTextTag *
452 prev_iter_has_recipient (GtkTextIter *iter)
453 {
454         GtkTextIter prev;
455
456         prev = *iter;
457         gtk_text_iter_backward_char (&prev);
458         return iter_has_recipient (&prev);
459 }
460
461 /* static GtkTextTag * */
462 /* next_iter_has_recipient (GtkTextIter *iter) */
463 /* { */
464 /*      GtkTextIter next; */
465
466 /*      next = *iter; */
467 /*      return iter_has_recipient (&next); */
468 /* } */
469
470 static gunichar 
471 iter_previous_char (GtkTextIter *iter)
472 {
473         GtkTextIter prev;
474
475         prev = *iter;
476         gtk_text_iter_backward_char (&prev);
477         return gtk_text_iter_get_char (&prev);
478 }
479
480 /* static gunichar  */
481 /* iter_next_char (GtkTextIter *iter) */
482 /* { */
483 /*      GtkTextIter next; */
484
485 /*      next = *iter; */
486 /*      gtk_text_iter_forward_char (&next); */
487 /*      return gtk_text_iter_get_char (&next); */
488 /* } */
489
490 static void
491 select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag)
492 {
493         GtkTextIter start, end;
494
495         start = *iter;
496         if (!gtk_text_iter_begins_tag (&start, tag))
497                 gtk_text_iter_backward_to_tag_toggle (&start, tag);
498         end = *iter;
499         if (!gtk_text_iter_ends_tag (&end, tag))
500                 gtk_text_iter_forward_to_tag_toggle (&end, tag);
501         gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &start, &end);
502 }
503
504 static gboolean
505 modest_recpt_editor_on_key_press_event (GtkTextView *text_view,
506                                           GdkEventKey *key,
507                                           ModestRecptEditor *editor)
508 {
509         GtkTextMark *insert;
510         GtkTextBuffer * buffer;
511         GtkTextIter location;
512         GtkTextTag *tag;
513      
514         buffer = gtk_text_view_get_buffer (text_view);
515         insert = gtk_text_buffer_get_insert (buffer);
516
517         /* cases to cover:
518          *    * cursor is on resolved recipient:
519          *        - right should go to first character after the recipient (usually ; or ,)
520          *        - left should fo to the first character before the recipient
521          *        - return should run check names on the recipient.
522          *    * cursor is just after a recipient:
523          *        - right should go to the next character. If it's a recipient, should select
524          *          it
525          *        - left should go to the previous character. If it's a recipient, should go
526          *          to the first character of the recipient, and select it.
527          *    * cursor is on arbitrary text:
528          *        - return should add a ; and go to the next line
529          *        - left or right standard ones.
530          *    * cursor is after a \n:
531          *        - left should go to the character before the \n (as if \n was not a character)
532          *    * cursor is before a \n:
533          *        - right should go to the character after the \n
534          */
535
536         gtk_text_buffer_get_iter_at_mark (buffer, &location, insert);
537
538         switch (key->keyval) {
539         case GDK_Left:
540         case GDK_KP_Left: 
541         {
542                 gboolean cursor_ready = FALSE;
543                 while (!cursor_ready) {
544                         if (iter_previous_char (&location) == '\n') {
545                                 g_message ("INTRO FOUND");
546                                 gtk_text_iter_backward_char (&location);
547                         } else {
548                                 cursor_ready = TRUE;
549                         }
550                 }
551                 tag = iter_has_recipient (&location);
552                 if ((tag != NULL)&& (gtk_text_iter_is_start (&location) || !(gtk_text_iter_begins_tag (&location, tag)))) {
553                         select_tag_of_iter (&location, tag);
554                         gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
555                         return TRUE;
556                 }
557                 gtk_text_iter_backward_char (&location);
558                 tag = iter_has_recipient (&location);
559                 if (tag != NULL)
560                         select_tag_of_iter (&location, tag);
561                 else {
562                         gtk_text_buffer_place_cursor (buffer, &location);
563                 }
564                 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
565
566                 return TRUE;
567         }
568         break;
569         case GDK_Right:
570         case GDK_KP_Right:
571         {
572                 gboolean cursor_moved = FALSE;
573
574                 tag = iter_has_recipient (&location);
575                 if ((tag != NULL)&&(!gtk_text_iter_ends_tag (&location, tag))) {
576                         gtk_text_iter_forward_to_tag_toggle (&location, tag);
577                         while (gtk_text_iter_get_char (&location) == '\n')
578                                 gtk_text_iter_forward_char (&location);
579                         gtk_text_buffer_place_cursor (buffer, &location);
580                         gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
581                         return TRUE;
582                 }
583
584                 while (gtk_text_iter_get_char (&location) == '\n') {
585                         gtk_text_iter_forward_char (&location);
586                         cursor_moved = TRUE;
587                 }
588                 if (!cursor_moved)
589                         gtk_text_iter_forward_char (&location);
590                 while (gtk_text_iter_get_char (&location) == '\n') {
591                         gtk_text_iter_forward_char (&location);
592                         cursor_moved = TRUE;
593                 }
594
595                 tag = iter_has_recipient (&location);
596                 if (tag != NULL)
597                         select_tag_of_iter (&location, tag);
598                 else
599                         gtk_text_buffer_place_cursor (buffer, &location);
600                 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0);
601                 return TRUE;
602         }
603         break;
604         case GDK_Return:
605         case GDK_KP_Enter:
606         {
607                 g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
608                 tag = iter_has_recipient (&location);
609                 if (tag != NULL) {
610                         gtk_text_buffer_get_end_iter (buffer, &location);
611                         gtk_text_buffer_place_cursor (buffer, &location);
612                         if ((iter_previous_char (&location) != ';')&&(iter_previous_char (&location) != ','))
613                                 gtk_text_buffer_insert_at_cursor (buffer, ";", -1);
614                         gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
615                 } else {
616                         gunichar prev_char = iter_previous_char (&location);
617                         if ((gtk_text_iter_is_start (&location))||(prev_char == '\n')
618                             ||(prev_char == ';')||(prev_char == ',')) 
619                                 g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook");
620                         else {
621                                 if ((prev_char != ';') && (prev_char != ','))
622                                         gtk_text_buffer_insert_at_cursor (buffer, ";", -1);
623                                 gtk_text_buffer_insert_at_cursor (buffer, "\n", -1);
624                         }
625                 }
626                 g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor);
627                 return TRUE;
628         }
629         break;
630         case GDK_BackSpace:
631         {
632                 if (gtk_text_buffer_get_has_selection (buffer)) {
633                         gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
634                         return TRUE;
635                 }
636                 tag = prev_iter_has_recipient (&location);
637                 if (tag != NULL) {
638                         GtkTextIter iter_in_tag;
639                         iter_in_tag = location;
640                         g_message ("DELETE PREV SELECTION");
641                         gtk_text_iter_backward_char (&iter_in_tag);
642                         select_tag_of_iter (&iter_in_tag, tag);
643                         gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
644                         return TRUE;
645                 }
646                 return FALSE;
647         }
648         break;
649         default:
650                 return FALSE;
651         }
652 }
653
654 static void 
655 modest_recpt_editor_move_cursor_to_end (ModestRecptEditor *editor)
656 {
657         ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor);
658         GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
659         GtkTextIter start, end;
660
661         gtk_text_buffer_get_end_iter (buffer, &start);
662         end = start;
663         gtk_text_buffer_select_range (buffer, &start, &end);
664
665
666 }
667
668 void
669 modest_recpt_editor_grab_focus (ModestRecptEditor *recpt_editor)
670 {
671         ModestRecptEditorPrivate *priv;
672         
673         g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor));
674         priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor);
675
676         gtk_widget_grab_focus (priv->text_view);
677 }
678
679 static void
680 modest_recpt_editor_finalize (GObject *object)
681 {
682         (*parent_class->finalize) (object);
683
684         return;
685 }
686
687 static void 
688 modest_recpt_editor_class_init (ModestRecptEditorClass *klass)
689 {
690         GObjectClass *object_class;
691         GtkWidgetClass *widget_class;
692
693         parent_class = g_type_class_peek_parent (klass);
694         object_class = (GObjectClass*) klass;
695         widget_class = GTK_WIDGET_CLASS (klass);
696
697         object_class->finalize = modest_recpt_editor_finalize;
698
699         g_type_class_add_private (object_class, sizeof (ModestRecptEditorPrivate));
700
701         signals[OPEN_ADDRESSBOOK_SIGNAL] = 
702                 g_signal_new ("open-addressbook",
703                               G_TYPE_FROM_CLASS (object_class),
704                               G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
705                               G_STRUCT_OFFSET (ModestRecptEditorClass, open_addressbook),
706                               NULL, NULL,
707                               g_cclosure_marshal_VOID__VOID,
708                               G_TYPE_NONE, 0);
709
710         return;
711 }
712
713 GType 
714 modest_recpt_editor_get_type (void)
715 {
716         static GType type = 0;
717
718         if (G_UNLIKELY(type == 0))
719         {
720                 static const GTypeInfo info = 
721                 {
722                   sizeof (ModestRecptEditorClass),
723                   NULL,   /* base_init */
724                   NULL,   /* base_finalize */
725                   (GClassInitFunc) modest_recpt_editor_class_init,   /* class_init */
726                   NULL,   /* class_finalize */
727                   NULL,   /* class_data */
728                   sizeof (ModestRecptEditor),
729                   0,      /* n_preallocs */
730                   modest_recpt_editor_instance_init    /* instance_init */
731                 };
732
733                 type = g_type_register_static (GTK_TYPE_HBOX,
734                         "ModestRecptEditor",
735                         &info, 0);
736
737         }
738
739         return type;
740 }