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