Issues deteced by Static Code Analysis fixed
[platform/core/uifw/eail.git] / eail / eail / eail_entry.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * @file eail_entry.c
22  * @brief EailEntry implementation
23  */
24
25 #include <Elementary.h>
26
27 #include "eail_entry.h"
28 #include "eail_utils.h"
29 #include "eail_priv.h"
30 #include "eail_clipboard.h"
31
32 static void atk_text_interface_init(AtkTextIface *iface);
33 static void atk_editable_text_interface_init(AtkEditableTextIface *iface);
34
35 /**
36  * @brief Definition of EailEntry as GObject
37  *
38  * EailEntry is extended EAIL_TYPE_TEXT with ATK_TYPE_TEXT and
39  * ATK_TYPE_EDITABLE_TEXT interfaces implemented.
40  *
41  */
42 G_DEFINE_TYPE_WITH_CODE(EailEntry,
43                         eail_entry,
44                         EAIL_TYPE_WIDGET,
45                         G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
46                                               atk_text_interface_init)
47                         G_IMPLEMENT_INTERFACE(ATK_TYPE_EDITABLE_TEXT,
48                                               atk_editable_text_interface_init));
49
50 /**
51  * @brief Handler for event which is raised when entry content has changed
52  *
53  * @param data data passed to callback
54  * @param obj Evas_Object that raised event
55  * @param event_info additional event info
56  */
57 void
58 _eail_entry_handle_changed_event(void *data,
59                                  Evas_Object *obj,
60                                  void *event_info)
61 {
62    eail_emit_atk_signal
63                   (ATK_OBJECT(data), "visible-data-changed", ATK_TYPE_OBJECT);
64 }
65
66 /**
67  * @brief handler for event which is raised when entry content is being pressed
68  *
69  * @param data passed to callback
70  * @param obj object that raised event
71  * @param event_info additional event info
72  */
73 void
74 _eail_entry_handle_pressed_event(void *data,
75                                  Evas_Object *obj,
76                                  void *event_info)
77 {
78    atk_object_notify_state_change(ATK_OBJECT(data), ATK_STATE_PRESSED, TRUE);
79 }
80
81 /**
82  * @brief Initializer for AtkObject
83  *
84  * @param obj AtkObject instance
85  * @param data initialization data
86  */
87 static void
88 eail_entry_initialize(AtkObject *obj, gpointer data)
89 {
90    Evas_Object *nested_widget = NULL;
91    ATK_OBJECT_CLASS(eail_entry_parent_class)->initialize(obj, data);
92
93    obj->role = ATK_ROLE_ENTRY;
94
95    g_return_if_fail(EAIL_IS_WIDGET(obj));
96    nested_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
97    if (!nested_widget)
98      {
99         ERR("No evas object inside EailWidget was found");
100         return;
101      }
102
103    evas_object_smart_callback_add(nested_widget, "changed",
104                                   _eail_entry_handle_changed_event, obj);
105    evas_object_smart_callback_add(nested_widget, "cursor,changed",
106                                   _eail_entry_handle_changed_event, obj);
107    evas_object_smart_callback_add(nested_widget, "press",
108                                   _eail_entry_handle_pressed_event, obj);
109 }
110
111 /**
112  * @brief Class destructor
113  *
114  * @param obj GObject instance
115  */
116 static void
117 eail_entry_finalize(GObject *obj)
118 {
119 }
120
121 /**
122  * @brief Gets a reference to the state set of the accessible
123  *
124  * The caller must unreference it when it is no longer needed.
125  *
126  * Implementation of AtkObject->ref_state_set callback.
127  *
128  * @param obj AtkObject instance
129  *
130  * @returns AtkStateSet representing the state set of the
131  * accessible
132  */
133 static AtkStateSet *
134 eail_entry_ref_state_set(AtkObject *obj)
135 {
136    AtkStateSet *state_set;
137    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(obj));
138
139    if (!widget)
140      return NULL;
141
142    state_set = ATK_OBJECT_CLASS(eail_entry_parent_class)->ref_state_set(obj);
143
144    if (elm_entry_editable_get(widget))
145      atk_state_set_add_state(state_set, ATK_STATE_EDITABLE);
146
147    if (elm_entry_single_line_get(widget))
148      atk_state_set_add_state(state_set, ATK_STATE_SINGLE_LINE);
149    else
150      atk_state_set_add_state(state_set, ATK_STATE_MULTI_LINE);
151
152    return state_set;
153 }
154
155 /**
156  * @brief Initializer for EailEntry GObject implementation
157  *
158  * @param entry EailEntry instance
159  */
160 static void
161 eail_entry_init(EailEntry *entry)
162 {
163    entry->selection_start = 0;
164    entry->selection_end = 0;
165 }
166
167 /**
168  * @brief Initializer for EailPopup GObject class
169  *
170  * Defines callbacks for base AtkObject.
171  *
172  * @param klass EailEntryClass instance
173  */
174 static void
175 eail_entry_class_init(EailEntryClass *klass)
176 {
177    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
178    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
179
180    atk_class->initialize = eail_entry_initialize;
181    atk_class->ref_state_set = eail_entry_ref_state_set;
182    gobject_class->finalize = eail_entry_finalize;
183 }
184
185 /**
186  * @brief Gets the caret offset
187  *
188  * @param text AtkText instance
189  * @return integer representing the caret offset
190  */
191 static gint
192 eail_entry_get_caret_offset(AtkText *text)
193 {
194    Evas_Object *widget;
195
196    widget = eail_widget_get_widget(EAIL_WIDGET(text));
197    if (!widget)
198      return 0;
199
200    return elm_entry_cursor_pos_get(widget);
201 }
202
203 /**
204  * @brief Sets the caret (cursor) position to the specified offset.
205  *
206  * Implementation of AtkTextIface->set_caret_offset callback.
207  *
208  * @param text AtkText instance
209  * @param offset starting position
210  *
211  * @returns TRUE on success, FALSE otherwise
212  */
213 static gboolean
214 eail_entry_set_caret_offset (AtkText *text,
215                              gint     offset)
216 {
217    Evas_Object *widget;
218
219    widget = eail_widget_get_widget(EAIL_WIDGET(text));
220    if (!widget)
221      return FALSE;
222
223    elm_entry_cursor_pos_set(widget, offset);
224
225    return TRUE;
226 }
227
228 /**
229  * @brief Gets the number of selected text regions
230  *
231  * @param text AtkText instance
232  * @returns integer representing the number of
233  * selected text regions
234  */
235 static gint
236 eail_entry_get_n_selections(AtkText *text)
237 {
238    Evas_Object *widget;
239
240    widget = eail_widget_get_widget(EAIL_WIDGET(text));
241    if (!widget) return 0;
242
243    if (elm_entry_selection_get(widget)) return 1;
244
245    return 0;
246 }
247
248 /**
249  * @brief Gets text selection from entry
250  *
251  * Use g_free() to free the returned string.
252  *
253  * The selected regions are assigned
254  * numbers that correspond to how far the region is from the start of the text.
255  * The selected region closest to the beginning of the text region is assigned
256  * the number 0, etc. Note that adding, moving or deleting a selected region can
257  * change the numbering.
258  *
259  * Implementation of AtkTextIface->get_selection callback.
260  *
261  * @param text AtkText instance
262  * @param selection_num selection number
263  * @param start_offset start position of the selected region
264  * @param end_offset end position of the selected region
265  *
266  * @returns newly allocated string containing the selected text
267  */
268 static gchar *
269 eail_entry_get_selection(AtkText *text,
270                          gint     selection_num,
271                          gint    *start_offset,
272                          gint    *end_offset)
273 {
274    const char* selection;
275    Evas_Object *widget;
276
277    widget = eail_widget_get_widget(EAIL_WIDGET(text));
278    if (!widget)
279      return g_strdup("");
280
281    if (0 != selection_num)
282      return g_strdup("");
283
284    selection = elm_entry_selection_get(widget);
285    if (selection)
286      {
287         *start_offset = EAIL_ENTRY(text)->selection_start;
288         *end_offset = EAIL_ENTRY(text)->selection_end;
289         return g_strdup(selection);
290      }
291
292    return g_strdup("");
293 }
294
295 /**
296  * @brief Adds a selection bounded by the specified offsets
297  *
298  * @param text AtkText instance
299  * @param start_offset start position of the selection
300  * @param end_offset offset of the first character after selection
301  * @returns TRUE on success, FALSE otherwise
302  */
303 static gboolean
304 eail_entry_add_selection(AtkText *text,
305                          gint start_offset,
306                          gint end_offset)
307 {
308    Evas_Object *widget;
309
310    widget = eail_widget_get_widget(EAIL_WIDGET(text));
311    if (!widget) return FALSE;
312
313    elm_entry_cursor_pos_set(widget, start_offset);
314    elm_entry_cursor_selection_begin(widget);
315    elm_entry_cursor_pos_set(widget, end_offset);
316    elm_entry_cursor_selection_end(widget);
317
318    EAIL_ENTRY(text)->selection_start = start_offset;
319    EAIL_ENTRY(text)->selection_end = end_offset;
320
321    return TRUE;
322 }
323
324 /**
325  * @brief Removes text selection
326  *
327  * Only one selection is supported, so
328  * selection number equals 0.
329  *
330  * @param text AtkText instance
331  * @param selection_num selection number
332  * @return TRUE on success, FALSE otherwise
333  */
334 static gboolean
335 eail_entry_remove_selection(AtkText *text,
336                             gint selection_num)
337 {
338    Evas_Object *widget;
339
340    widget = eail_widget_get_widget(EAIL_WIDGET(text));
341    if (!widget) return FALSE;
342
343    if (selection_num != 0 || !elm_entry_selection_get(widget)) return FALSE;
344
345    elm_entry_select_none(widget);
346    EAIL_ENTRY(text)->selection_start = 0;
347    EAIL_ENTRY(text)->selection_end = 0;
348
349    return TRUE;
350 }
351
352 /**
353  * @brief Sets text selection for entry
354  *
355  * The selected regions are assigned
356  * numbers that correspond to how far the region is from the start of the text.
357  * The selected region closest to the beginning of the text region is assigned
358  * the number 0, etc. Note that adding, moving or deleting a selected region can
359  * change the numbering.
360  *
361  * Implementation of AtkTextIface->set_selection callback.
362  *
363  * @param text AtkText instance
364  * @param selection_num selection number
365  * @param start_pos start position of the selected region
366  * @param end_pos end position of the selected region
367  *
368  * @returns TRUE on success, FALSE otherwise
369  */
370 static gboolean
371 eail_entry_set_selection(AtkText *text,
372                          gint     selection_num,
373                          gint     start_pos,
374                          gint     end_pos)
375 {
376    if (0 != selection_num)
377      return FALSE;
378
379    return eail_entry_add_selection(text, start_pos, end_pos);
380 }
381
382 /**
383  * @brief Gets text bounded by start_offset and end_offset
384  *
385  * Use g_free() to free the returned string.
386  *
387  * @param text AtkText instance
388  * @param start_offset start position
389  * @param end_offset end position, -1 for the end of the string
390  * @return string containing text from start_offset up to,
391  * but not including end_offset
392  */
393 static gchar*
394 eail_entry_get_text(AtkText   *text,
395                     gint       start_offset,
396                     gint       end_offset)
397 {
398    const gchar *string = NULL;
399    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
400
401    if (widget)
402      string = elm_entry_entry_get(widget);
403
404    return eail_get_substring(string, start_offset, end_offset);
405 }
406
407
408
409 /**
410  * @brief Returns the position that is obtained by adding count to the
411  * given offset
412  *
413  * Count may be positive or negative.
414  *
415  * @param textblock Evas textblock
416  * @param offset character offset
417  * @param count number of characters to move from offset
418  * @returns integer representing the new position
419  */
420 static gint
421 _eail_move_chars(const Evas_Object *textblock,
422                  gint offset,
423                  gint count)
424 {
425    Evas_Textblock_Cursor *cur;
426    gint dir;
427    gboolean res;
428
429    dir = count > 0 ? 1 : -1;
430    cur = evas_object_textblock_cursor_new(textblock);
431    evas_textblock_cursor_pos_set(cur, offset);
432
433    while(count)
434      {
435         res = dir > 0 ? evas_textblock_cursor_char_next(cur) :
436             evas_textblock_cursor_char_prev(cur);
437         if (!res) break;
438
439         count -= dir;
440      }
441
442    offset = evas_textblock_cursor_pos_get(cur);
443    evas_textblock_cursor_free(cur);
444
445    return offset;
446 }
447
448 /**
449  * @brief Gets the utf8 character at offset
450  *
451  * @param textblock Evas textblock
452  * @param offset character offset
453  * @returns char representing the utf8 character
454  */
455 static gunichar
456 _eail_get_unichar_at_offset(const Evas_Object *textblock,
457                             int offset)
458 {
459    Evas_Textblock_Cursor *cur;
460    gchar *s;
461    gunichar c;
462
463    cur = evas_object_textblock_cursor_new(textblock);
464    evas_textblock_cursor_pos_set(cur, offset);
465    s = evas_textblock_cursor_content_get(cur);
466    c = g_utf8_get_char(s);
467
468    evas_textblock_cursor_free(cur);
469    g_free(s);
470
471    return c;
472 }
473
474 /**
475  * @brief Checks whether the character at offset in textblock is a word's start
476  *
477  * @param textblock Evas textblock
478  * @param offset character offset
479  * @returns TRUE on success, FALSE otherwise
480  */
481 static gboolean
482 _eail_is_word_start(const Evas_Object *textblock,
483                     gint offset)
484 {
485    /* first character in a word */
486    gunichar c1, c2;
487
488    c1 = _eail_get_unichar_at_offset(textblock, offset);
489    c2 = _eail_get_unichar_at_offset(textblock, offset - 1);
490    if (g_unichar_isalnum(c1) && g_unichar_isspace(c2)) return TRUE;
491
492    return FALSE;
493 }
494
495 /**
496  * @brief Checks whether the character at offset in textblock is a word's end
497  *
498  * @param textblock Evas textblock
499  * @param offset character offset
500  * @return TRUE on success, FALSE otherwise
501  */
502 static gboolean
503 _eail_is_word_end(const Evas_Object *textblock,
504                   gint offset)
505 {
506    /* is first non-word char after a word */
507    gunichar c1, c2;
508
509    c1 = _eail_get_unichar_at_offset(textblock, offset - 1);
510    c2 = _eail_get_unichar_at_offset(textblock, offset);
511    if (g_unichar_isalnum(c1) && g_unichar_isspace(c2)) return TRUE;
512
513    return FALSE;
514 }
515
516 /**
517  * @brief Checks whether the character at offset in textblock is inside a word
518  *
519  * @param textblock Evas textblock
520  * @param offset character offset
521  * @returns TRUE on success, FALSE otherwise
522  */
523 static gboolean
524 _eail_is_inside_word(const Evas_Object *textblock,
525                      gint offset)
526 {
527    gunichar c1, c2, c3;
528
529    c1 = _eail_get_unichar_at_offset(textblock, offset - 1);
530    c2 = _eail_get_unichar_at_offset(textblock, offset);
531    c3 = _eail_get_unichar_at_offset(textblock, offset + 1);
532    if (g_unichar_isalnum(c1) && g_unichar_isalnum(c2) && g_unichar_isalnum(c3))
533      return TRUE;
534
535    if (g_unichar_isalnum(c1) && g_unichar_isalnum(c2) && g_unichar_isspace(c3))
536      return TRUE;
537
538    if (g_unichar_isspace(c1) && g_unichar_isalnum(c2) && g_unichar_isalnum(c3))
539      return TRUE;
540
541    if (g_unichar_isspace(c1) && g_unichar_isalnum(c2) && g_unichar_isspace(c3))
542      return TRUE;
543
544    return FALSE;
545 }
546
547 /**
548  * @brief Gets the texblock length
549  *
550  * @param textblock Evas textblock
551  * @returns integer representing the textblock length
552  */
553 static gint
554 _eail_get_len(const Evas_Object *textblock)
555 {
556    Evas_Textblock_Cursor *cur;
557    int ctr = 0;
558
559    cur = evas_object_textblock_cursor_new(textblock);
560    while (evas_textblock_cursor_char_next(cur))
561      ++ctr;
562
563    evas_textblock_cursor_free(cur);
564
565    return ctr;
566 }
567
568 /**
569  * @brief Returns the position that is count words from the given offset
570  *
571  * Count may  be positive or negative. If count is positive, the returned
572  * position will be a word end, otherwise it will be a word start.
573  *
574  * @param textblock Evas textblock
575  * @param offset a character offset
576  * @param count the number of words to move from offset
577  * @returns integer representing the new position
578  */
579 static gint
580 _eail_move_words(const Evas_Object *textblock,
581                  gint offset,
582                  gint count)
583 {
584    gint len = _eail_get_len(textblock);
585
586    while (count > 0 && offset < len)
587      {
588         do
589           offset++;
590         while (offset < len && !_eail_is_word_end(textblock, offset));
591
592         count--;
593      }
594    while (count < 0 && offset > 0)
595      {
596         do
597           offset--;
598         while (offset > 0 && !_eail_is_word_start(textblock, offset));
599
600         count++;
601      }
602
603    return offset;
604 }
605
606 /**
607  * @brief Gets the position of the first character in line
608  *
609  * @param cur Evas_Textblock_Cursor instance
610  * @returns TRUE on success, FALSE otherwise
611  */
612 static gint
613 _eail_get_line_start(Evas_Textblock_Cursor *cur)
614 {
615    gint start = 0;
616    gint pos;
617
618    pos = evas_textblock_cursor_pos_get(cur);
619    evas_textblock_cursor_line_char_first(cur);
620    start = evas_textblock_cursor_pos_get(cur);
621    evas_textblock_cursor_pos_set(cur, pos);
622
623    return start;
624 }
625
626 /**
627  * @brief Gets the position of the last character in line
628  *
629  * @param cur Evas_Textblock_Cursor instance
630  * @returns TRUE on success, FALSE otherwise
631  */
632 static gint
633 _eail_get_line_end(Evas_Textblock_Cursor *cur)
634 {
635    gint end = 0;
636    gint pos;
637
638    pos = evas_textblock_cursor_pos_get(cur);
639    evas_textblock_cursor_line_char_last(cur);
640    end = evas_textblock_cursor_pos_get(cur);
641    evas_textblock_cursor_pos_set(cur, pos);
642
643    return end;
644 }
645
646 /**
647  * @brief Moves the cursor to the beginning of the next line
648  *
649  * @param cur Evas_Textblock_Cursor instance
650  * @returns TRUE on success, FALSE otherwise
651  */
652 static gboolean
653 _eail_iter_next_line(Evas_Textblock_Cursor *cur)
654 {
655    evas_textblock_cursor_line_char_last(cur);
656
657    return evas_textblock_cursor_char_next(cur);
658 }
659
660 /**
661  * @brief Gets the length of the line shown by cursor cur
662  *
663  * @param cur Evas_Textblock_Cursor instance
664  * @return integer representing the length of the line
665  */
666 static gint
667 _eail_get_line_length(Evas_Textblock_Cursor *cur)
668 {
669    gint start;
670    gint end;
671
672    start = _eail_get_line_start(cur);
673    end = _eail_get_line_end(cur);
674
675    return end - start + 1;
676 }
677
678 /**
679  * @brief Gets the line before offset
680  *
681  * @param entry Evas_Object instance
682  * @param boundary_type AtkTextBoundary instance
683  * @param offset character offset
684  * @param [out] start_offset start offset of the returned string
685  * @param [out] end_offset offset of the first character after the
686  * returned substring
687  */
688 static void
689 _eail_get_line_before(Evas_Object     *entry,
690                       AtkTextBoundary  boundary_type,
691                       gint             offset,
692                       gint            *start_offset,
693                       gint            *end_offset)
694 {
695    Evas_Object *textblock;
696    Evas_Textblock_Cursor *cur, *prev_cur = NULL, *prev_prev_cur = NULL;
697    gint index, start_index, end_index;
698    const gchar *text;
699    gboolean found = FALSE;
700
701    textblock = elm_entry_textblock_get(entry);
702    cur = evas_object_textblock_cursor_new(textblock);
703
704    text = evas_textblock_text_markup_to_utf8(
705        textblock, evas_object_textblock_text_markup_get(textblock));
706    index = g_utf8_offset_to_pointer (text, offset) - text;
707    do
708      {
709         start_index = _eail_get_line_start(cur);
710         end_index = start_index + _eail_get_line_length(cur);
711
712         if (index >= start_index && index <= end_index)
713           {
714              /* Found line for offset */
715              if (prev_cur)
716                {
717                   switch (boundary_type)
718                     {
719                       case ATK_TEXT_BOUNDARY_LINE_START:
720                           end_index = start_index;
721                           start_index = _eail_get_line_start(prev_cur);
722                           break;
723                       case ATK_TEXT_BOUNDARY_LINE_END:
724                           if (prev_prev_cur)
725                             start_index = _eail_get_line_start(prev_prev_cur) +
726                                 _eail_get_line_length(prev_prev_cur);
727                           else
728                             start_index = 0;
729                           end_index = _eail_get_line_start(prev_cur) +
730                               _eail_get_line_length(prev_cur);
731                           break;
732                       default:
733                           g_assert_not_reached();
734                     }
735                }
736              else
737                start_index = end_index = 0;
738
739              found = TRUE;
740              break;
741           }
742
743         prev_prev_cur = prev_cur;
744         prev_cur = cur;
745      }
746    while (_eail_iter_next_line(cur));
747
748    if (!found)
749      {
750         start_index = _eail_get_line_start(prev_cur) +
751             _eail_get_line_length(prev_cur);
752         end_index = start_index;
753      }
754    evas_textblock_cursor_free(cur);
755
756    *start_offset = g_utf8_pointer_to_offset(text, text + start_index);
757    *end_offset = g_utf8_pointer_to_offset(text, text + end_index);
758 }
759
760 /**
761  * @brief Gets the line after offset
762  *
763  * @param entry Evas_Object instance
764  * @param boundary_type AtkTextBoundary instance
765  * @param offset character offset
766  * @param [out] start_offset start offset of the returned string
767  * @param [out] end_offset offset of the first character after the
768  * returned substring
769  */
770 static void
771 _eail_get_line_after (Evas_Object     *entry,
772                       AtkTextBoundary  boundary_type,
773                       gint             offset,
774                       gint            *start_offset,
775                       gint            *end_offset)
776 {
777    Evas_Object *textblock;
778    Evas_Textblock_Cursor *cur, *prev_cur = NULL;
779    gint index, start_index, end_index;
780    const gchar *text;
781    gboolean found = FALSE;
782
783    textblock = elm_entry_textblock_get(entry);
784    cur = evas_object_textblock_cursor_new(textblock);
785    text = evas_textblock_text_markup_to_utf8(
786        textblock, evas_object_textblock_text_markup_get(textblock));
787
788    index = g_utf8_offset_to_pointer (text, offset) - text;
789    do
790      {
791         start_index = _eail_get_line_start(cur);
792         end_index = start_index + _eail_get_line_length(cur);
793
794         if (index >= start_index && index <= end_index)
795           {
796              /* Found line for offset */
797              if (_eail_iter_next_line (cur))
798                {
799                   switch (boundary_type)
800                     {
801                       case ATK_TEXT_BOUNDARY_LINE_START:
802                           start_index = _eail_get_line_start(cur);
803                           if (_eail_iter_next_line (cur))
804                             end_index = _eail_get_line_start(cur);
805                           else
806                             end_index = start_index + _eail_get_line_length(cur);
807                           break;
808                       case ATK_TEXT_BOUNDARY_LINE_END:
809                           start_index = end_index;
810                           end_index = _eail_get_line_start(cur) +
811                               _eail_get_line_length(cur);
812                           break;
813                       default:
814                           g_assert_not_reached();
815                     }
816                }
817              else
818                start_index = end_index;
819
820              found = TRUE;
821              break;
822           }
823
824         prev_cur = cur;
825      }
826    while (_eail_iter_next_line (cur));
827
828    if (!found)
829      {
830         start_index = _eail_get_line_start(prev_cur) +
831             _eail_get_line_length(prev_cur);
832         end_index = start_index;
833      }
834    evas_textblock_cursor_free(cur);
835
836    *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
837    *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
838 }
839
840 /**
841  * @brief Gets the line at offset
842  *
843  * @param entry Evas_Object instance
844  * @param boundary_type AtkTextBoundary instance
845  * @param offset character offset
846  * @param [out] start_offset start offset of the returned string
847  * @param [out] end_offset offset of the first character after the
848  * returned substring
849  */
850 static void
851 _eail_get_line_at (Evas_Object *entry,
852                    AtkTextBoundary  boundary_type,
853                    gint             offset,
854                    gint            *start_offset,
855                    gint            *end_offset)
856 {
857    Evas_Object *textblock;
858    Evas_Textblock_Cursor *cur, *prev_cur = NULL;
859    gint index, start_index, end_index;
860    const gchar *text;
861    gboolean found = FALSE;
862
863    textblock = elm_entry_textblock_get(entry);
864    cur = evas_object_textblock_cursor_new(textblock);
865
866    text = evas_textblock_text_markup_to_utf8(
867        textblock, evas_object_textblock_text_markup_get(textblock));
868    index = g_utf8_offset_to_pointer (text, offset) - text;
869    do
870      {
871         start_index = _eail_get_line_start(cur);
872         end_index = start_index + _eail_get_line_end(cur);
873
874         if (index >= start_index && index <= end_index)
875           {
876              /* Found line for offset */
877              switch (boundary_type)
878                {
879                  case ATK_TEXT_BOUNDARY_LINE_START:
880                      if (_eail_iter_next_line (cur))
881                        end_index = _eail_get_line_start(cur);
882                      break;
883                  case ATK_TEXT_BOUNDARY_LINE_END:
884                      if (prev_cur)
885                        start_index = _eail_get_line_start(prev_cur) +
886                            _eail_get_line_length(prev_cur);
887                      break;
888                  default:
889                      g_assert_not_reached();
890                }
891
892              found = TRUE;
893              break;
894           }
895
896         prev_cur = cur;
897      }
898    while (_eail_iter_next_line (cur));
899
900    if (!found)
901      {
902         start_index = _eail_get_line_start(prev_cur) +
903             _eail_get_line_length(prev_cur);
904         end_index = start_index;
905      }
906    evas_textblock_cursor_free(cur);
907
908    *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
909    *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
910 }
911
912 /**
913  * @brief Gets a slice of the text from entry after offset
914  *
915  * Use g_free() to free the returned string.
916  *
917  * @param entry entry widget
918  * @param offset character offset
919  * @param boundary_type AtkTextBoundary instance
920  * @param [out] start_offset start position of the returned text
921  * @param [out] end_offset end position of the returned text
922  * @returns newly allocated string containg a slice of text from textblock
923  */
924 static gchar *
925 _eail_get_text_after(Evas_Object *entry,
926                      gint offset,
927                      AtkTextBoundary boundary_type,
928                      gint *start_offset,
929                      gint *end_offset)
930 {
931    const gchar *text;
932    int len;
933    gint start, end;
934    Evas_Object *textblock;
935
936    if (!entry) return g_strdup("");
937
938    textblock = elm_entry_textblock_get(entry);
939
940    text = evas_textblock_text_markup_to_utf8(
941        textblock, evas_object_textblock_text_markup_get(textblock));
942    if (!text)
943      {
944         *start_offset = 0;
945         *end_offset = 0;
946         return g_strdup("");
947      }
948
949    start = offset;
950    end = offset;
951    len = _eail_get_len(textblock);
952
953    switch (boundary_type)
954      {
955        case ATK_TEXT_BOUNDARY_CHAR:
956            start = _eail_move_chars(textblock, start, 1);
957            end = start;
958            end = _eail_move_chars(textblock, end, 1);
959            break;
960
961        case ATK_TEXT_BOUNDARY_WORD_START:
962            if (_eail_is_inside_word(textblock, end))
963              end = _eail_move_words(textblock, end, 1);
964            while (!_eail_is_word_start(textblock, end) && end < len)
965              end = _eail_move_chars(textblock, end, 1);
966            start = end;
967            if (end < len)
968              {
969                 end = _eail_move_words(textblock, end, 1);
970                 while (!_eail_is_word_start(textblock, end) && end < len)
971                   end = _eail_move_chars(textblock, end, 1);
972              }
973            break;
974
975        case ATK_TEXT_BOUNDARY_WORD_END:
976            end = _eail_move_words(textblock, end, 1);
977            start = end;
978            if (end < len)
979              end = _eail_move_words(textblock, end, 1);
980            break;
981
982        case ATK_TEXT_BOUNDARY_SENTENCE_START:
983        case ATK_TEXT_BOUNDARY_SENTENCE_END:
984            break;
985
986        case ATK_TEXT_BOUNDARY_LINE_START:
987        case ATK_TEXT_BOUNDARY_LINE_END:
988            _eail_get_line_after (entry, boundary_type, offset, &start, &end);
989            break;
990      }
991
992    *start_offset = start;
993    *end_offset = end;
994    g_assert(start <= end);
995
996    return g_utf8_substring(text, start, end);
997 }
998
999 /**
1000  * @brief Gets a slice of the text from entry at offset
1001  *
1002  * Use g_free() to free the returned string.
1003  *
1004  * @param entry entry widget instance
1005  * @param offset character offset
1006  * @param boundary_type AtkTextBoundary instance
1007  * @param [out] start_offset start position of the returned text
1008  * @param [out] end_offset end position of the return text
1009  * @returns newly allocated string containing a slice of text from entry
1010  */
1011 static gchar *
1012 _eail_get_text_at(Evas_Object *entry,
1013                   gint offset,
1014                   AtkTextBoundary boundary_type,
1015                   gint *start_offset,
1016                   gint *end_offset)
1017 {
1018    const gchar *text;
1019    int len;
1020    gint start, end;
1021    Evas_Object *textblock;
1022
1023    if (!entry) return g_strdup("");
1024
1025    textblock = elm_entry_textblock_get(entry);
1026
1027    text = evas_textblock_text_markup_to_utf8(
1028        textblock, evas_object_textblock_text_markup_get(textblock));
1029    if (!text)
1030      {
1031         *start_offset = 0;
1032         *end_offset = 0;
1033         return g_strdup("");
1034      }
1035
1036    start = offset;
1037    end = offset;
1038    len = _eail_get_len(textblock);
1039
1040    switch (boundary_type)
1041      {
1042        case ATK_TEXT_BOUNDARY_CHAR:
1043            end = _eail_move_chars(textblock, end, 1);
1044            break;
1045
1046        case ATK_TEXT_BOUNDARY_WORD_START:
1047            if (!_eail_is_word_start(textblock, start))
1048              start = _eail_move_words(textblock, start, -1);
1049            if (_eail_is_inside_word(textblock, end))
1050              end = _eail_move_words(textblock, end, 1);
1051            while (!_eail_is_word_start(textblock, end) && end < len)
1052              end = _eail_move_chars(textblock, end, 1);
1053            break;
1054
1055        case ATK_TEXT_BOUNDARY_WORD_END:
1056            if (_eail_is_inside_word(textblock, start) &&
1057                !_eail_is_word_start(textblock, start))
1058              start = _eail_move_words(textblock, start, -1);
1059            while (!_eail_is_word_end(textblock, start) && start > 0)
1060              start = _eail_move_chars(textblock, start, -1);
1061            end = _eail_move_words(textblock, end, 1);
1062            break;
1063
1064        case ATK_TEXT_BOUNDARY_SENTENCE_START:
1065        case ATK_TEXT_BOUNDARY_SENTENCE_END:
1066            break;
1067
1068        case ATK_TEXT_BOUNDARY_LINE_START:
1069        case ATK_TEXT_BOUNDARY_LINE_END:
1070            _eail_get_line_at (entry, boundary_type, offset, &start, &end);
1071            break;
1072      }
1073
1074    *start_offset = start;
1075    *end_offset = end;
1076    g_assert(start <= end);
1077
1078    return g_utf8_substring(text, start, end);
1079 }
1080
1081 /**
1082  * @brief Gets a slice of the text from entry before offset
1083  *
1084  * Use g_free() to free the returned string.
1085  *
1086  * @param entry entry widget instance
1087  * @param offset character offset
1088  * @param boundary_type AtkTextBoundary instance
1089  * @param [out] start_offset start position of the returned text
1090  * @param [out] end_offset end position of the returned text
1091  * @returns newly allocated string containing a slice of text from entry
1092  */
1093 static gchar *
1094 _eail_get_text_before(Evas_Object *entry,
1095                       gint offset,
1096                       AtkTextBoundary boundary_type,
1097                       gint *start_offset,
1098                       gint *end_offset)
1099 {
1100    const gchar *text;
1101    gint start, end;
1102    Evas_Object *textblock;
1103
1104    if (!entry) return g_strdup("");
1105
1106    textblock = elm_entry_textblock_get(entry);
1107
1108    text = evas_textblock_text_markup_to_utf8(
1109        textblock, evas_object_textblock_text_markup_get(textblock));
1110    if (!text)
1111      {
1112         *start_offset = 0;
1113         *end_offset = 0;
1114         return g_strdup("");
1115      }
1116
1117    start = offset;
1118    end = offset;
1119
1120    switch (boundary_type)
1121      {
1122        case ATK_TEXT_BOUNDARY_CHAR:
1123            start = _eail_move_chars(textblock, start, -1);
1124            break;
1125
1126        case ATK_TEXT_BOUNDARY_WORD_START:
1127            if (!_eail_is_word_start(textblock, start))
1128              start = _eail_move_words(textblock, start, -1);
1129            end = start;
1130            start = _eail_move_words(textblock, start, -1);
1131            break;
1132
1133        case ATK_TEXT_BOUNDARY_WORD_END:
1134            if (_eail_is_inside_word(textblock, start) &&
1135                !_eail_is_word_start(textblock, start))
1136              start = _eail_move_words(textblock, start, -1);
1137            while (!_eail_is_word_end(textblock, start) && start > 0)
1138              start = _eail_move_chars(textblock, start, -1);
1139            end = start;
1140            start = _eail_move_words(textblock, start, -1);
1141            while (!_eail_is_word_end(textblock, start) && start > 0)
1142              start = _eail_move_chars(textblock, start, -1);
1143            break;
1144
1145        case ATK_TEXT_BOUNDARY_SENTENCE_START:
1146        case ATK_TEXT_BOUNDARY_SENTENCE_END:
1147            break;
1148
1149        case ATK_TEXT_BOUNDARY_LINE_START:
1150        case ATK_TEXT_BOUNDARY_LINE_END:
1151            _eail_get_line_before (entry, boundary_type, offset, &start, &end);
1152            break;
1153      }
1154
1155    *start_offset = start;
1156    *end_offset = end;
1157    g_assert(start <= end);
1158
1159    return g_utf8_substring(text, start, end);
1160 }
1161
1162 /**
1163  * @brief Gets the specified text after offset
1164  *
1165  * Use g_free() to free the returned string.
1166  *
1167  * @param text AtkText instance
1168  * @param offset character offset
1169  * @param boundary_type AtkTextBoundary instance
1170  * @param [out] start_offset start offset of the returned string
1171  * @param [out] end_offset offset of the first character after the returned
1172  * substring
1173  * @returns newly allocated string containing the text after offset bounded
1174  * by the specified boundary_type
1175  */
1176 static gchar *
1177 eail_entry_get_text_after_offset(AtkText *text,
1178                                  gint offset,
1179                                  AtkTextBoundary boundary_type,
1180                                  gint *start_offset,
1181                                  gint *end_offset)
1182 {
1183    Evas_Object *widget;
1184
1185    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1186    if (!widget) return NULL;
1187
1188    return _eail_get_text_after(widget, offset, boundary_type, start_offset,
1189                                end_offset);
1190
1191 }
1192
1193 /**
1194  * @brief Gets the specified text at offset
1195  *
1196  * Use g_free() to free the returned string.
1197  *
1198  * @param text AtkText instance
1199  * @param offset character offset
1200  * @param boundary_type AtkTextBoundary instance
1201  * @param [out] start_offset start offset of the returned string
1202  * @param [out] end_offset offset of the first character after the returned
1203  * substring
1204  * @returns newly allocated string containing the text after offset bounded
1205  * by the specified boundary_type
1206  */
1207 static gchar *
1208 eail_entry_get_text_at_offset(AtkText *text,
1209                               gint offset,
1210                               AtkTextBoundary boundary_type,
1211                               gint *start_offset,
1212                               gint *end_offset)
1213 {
1214    Evas_Object *widget;
1215
1216    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1217    if (!widget) return NULL;
1218
1219    return _eail_get_text_at(widget, offset, boundary_type, start_offset,
1220                             end_offset);
1221 }
1222
1223 /**
1224  * @brief Gets the specified text before offset
1225  *
1226  * Use g_free() to free the returned string.
1227  *
1228  * @param text AtkText instance
1229  * @param offset character offset
1230  * @param boundary_type AtkTextBoundary instance
1231  * @param [out] start_offset start offset of the returned string
1232  * @param [out] end_offset offset of the first character after the returned
1233  * substring
1234  * @returns newly allocated string containing the text after offset bounded
1235  * by the specified boundary_type
1236  */
1237 static gchar *
1238 eail_entry_get_text_before_offset(AtkText *text,
1239                                   gint offset,
1240                                   AtkTextBoundary boundary_type,
1241                                   gint *start_offset,
1242                                   gint *end_offset)
1243 {
1244    Evas_Object *widget;
1245
1246    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1247    if (!widget) return NULL;
1248
1249    return _eail_get_text_before(widget, offset, boundary_type, start_offset,
1250                                 end_offset);
1251 }
1252
1253 /**
1254  * @brief Gets the character at offset
1255  *
1256  * @param text AtkText instance
1257  * @param offset character offset
1258  * @return char representing the character at offset
1259  */
1260 static gunichar
1261 eail_entry_get_character_at_offset(AtkText    *text,
1262                                    gint        offset)
1263 {
1264    gunichar character = '\0';
1265    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
1266
1267    if (widget)
1268      character = g_utf8_get_char(
1269          g_utf8_offset_to_pointer(elm_entry_entry_get(widget), offset));
1270
1271    return character;
1272 }
1273
1274 /**
1275  * @brief Gets the text's length
1276  *
1277  * @param text AtkText instance
1278  * @return integer representing the text length
1279  */
1280 static gint
1281 eail_entry_get_character_count(AtkText *text)
1282 {
1283    gint count = 0;
1284    const gchar *string_text = NULL;
1285
1286    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
1287
1288    if (!widget) return count;
1289
1290    string_text = elm_object_text_get(widget);
1291    if (!string_text) return count;
1292
1293    count = g_utf8_strlen(string_text, -1);
1294
1295    return count;
1296 }
1297
1298 /**
1299  * @brief Adds attribute to attribute set
1300  *
1301  * Creates an AtkAttribute from attr and value, and adds it
1302  * to attrib_set.
1303  *
1304  * @param attrib_set AtkAttributeSet instance to add the attribute to
1305  * @param attr AtkTextAttrribute instance which identifies the attribute to be added
1306  * @param value attribute value
1307  *
1308  * @returns: AtkAttributeSet representing the new attribute set
1309  **/
1310 AtkAttributeSet*
1311 eail_entry_add_attribute(AtkAttributeSet *attrib_set,
1312                          AtkTextAttribute attr,
1313                          gchar           *value)
1314 {
1315    AtkAttributeSet *return_set;
1316    AtkAttribute *at = g_malloc (sizeof (AtkAttribute));
1317    at->name = g_strdup (atk_text_attribute_get_name (attr));
1318    at->value = value;
1319    return_set = g_slist_prepend(attrib_set, at);
1320    return return_set;
1321 }
1322
1323 /**
1324  * @brief Creates an AtkAttributeSet which consists of the default values of
1325  * attributes for the text
1326  *
1327  * The returned AtkAttributeSet should be freed by a call to
1328  * atk_attribute_set_free().
1329  *
1330  * @param text AtkText instance
1331  *
1332  * @returns AtkAttributeSet which contains the default values of attributes
1333  * at offset
1334  */
1335 AtkAttributeSet *
1336 eail_entry_get_default_attributes(AtkText *text)
1337 {
1338    AtkAttributeSet *at_set = NULL;
1339
1340    at_set = eail_entry_add_attribute
1341        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
1342         g_strdup
1343         (atk_text_attribute_get_value(ATK_TEXT_ATTR_WRAP_MODE, 0)));
1344
1345    at_set = eail_entry_add_attribute
1346        (at_set, ATK_TEXT_ATTR_EDITABLE,
1347         g_strdup
1348         (atk_text_attribute_get_value
1349          (ATK_TEXT_ATTR_EDITABLE, TRUE)));
1350
1351    return at_set;
1352 }
1353
1354 /**
1355  * @brief Creates an AtkAttributeSet which consists of the attributes
1356  * explicitly set at the position offset in the text
1357  *
1358  * start_offset and end_offset are set to the start and end of the range
1359  * around offset where the attributes are invariant. Note that end_offset
1360  * is the offset of the first character after the range.
1361  *
1362  * The returned AtkAttributeSet should be freed by a call to
1363  * atk_attribute_set_free()
1364  *
1365  * @param text AtkText instance
1366  * @param offset offset at which to get the attributes
1367  * @param [out] start_offset start offset of the range
1368  * @param [out] end_offset end offset of the range
1369  *
1370  * @returns AtkAttributeSet which contains the attributes explicitly set at
1371  * offset
1372  */
1373 AtkAttributeSet *
1374 eail_entry_get_run_attributes(AtkText *text,
1375                               gint offset,
1376                               gint *start_offset,
1377                               gint *end_offset)
1378 {
1379    AtkAttributeSet *at_set = NULL;
1380    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
1381
1382    if (!widget || offset >= eail_entry_get_character_count(text))
1383      {
1384         *start_offset = -1;
1385         *end_offset = -1;
1386
1387         return NULL;
1388      }
1389
1390    *start_offset = 0;
1391    *end_offset = eail_entry_get_character_count(text);
1392
1393    /* NOTE: Elm_Wrap_Type value is in 100% compatible with ATK wrap modes, so
1394     * no additional conversion is needed*/
1395    Elm_Wrap_Type wrap_type = elm_entry_line_wrap_get(widget);
1396    at_set = eail_entry_add_attribute
1397        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
1398         g_strdup
1399         (atk_text_attribute_get_value
1400          (ATK_TEXT_ATTR_WRAP_MODE, wrap_type)));
1401
1402    Eina_Bool editable = elm_entry_editable_get(widget);
1403    at_set = eail_entry_add_attribute
1404        (at_set, ATK_TEXT_ATTR_EDITABLE,
1405         g_strdup
1406         (atk_text_attribute_get_value
1407          (ATK_TEXT_ATTR_EDITABLE, editable)));
1408
1409    return at_set;
1410 }
1411
1412 /**
1413  * @brief Initializes AtkTextIface interface
1414  *
1415  * @param iface AtkTextIface instance
1416  */
1417 static void
1418 atk_text_interface_init(AtkTextIface *iface)
1419 {
1420    iface->get_caret_offset = eail_entry_get_caret_offset;
1421    iface->set_caret_offset = eail_entry_set_caret_offset;
1422    iface->get_selection    = eail_entry_get_selection;
1423    iface->set_selection    = eail_entry_set_selection;
1424    iface->get_text         = eail_entry_get_text;
1425    iface->get_character_at_offset = eail_entry_get_character_at_offset;
1426    iface->get_character_count = eail_entry_get_character_count;
1427    iface->remove_selection = eail_entry_remove_selection;
1428    iface->get_n_selections = eail_entry_get_n_selections;
1429    iface->add_selection = eail_entry_add_selection;
1430    iface->get_run_attributes = eail_entry_get_run_attributes;
1431    iface->get_default_attributes = eail_entry_get_default_attributes;
1432    iface->get_text_after_offset = eail_entry_get_text_after_offset;
1433    iface->get_text_at_offset = eail_entry_get_text_at_offset;
1434    iface->get_text_before_offset = eail_entry_get_text_before_offset;
1435 }
1436
1437 /*
1438  * Implementation of the *AtkEditableText* interface
1439  */
1440
1441 /**
1442  * @brief Sets text content of entry
1443  *
1444  * @param text AtkEditableText instance
1445  * @param string string to be set as the content of entry
1446  */
1447 static void
1448 eail_entry_set_text_contents(AtkEditableText *text,
1449                              const gchar     *string)
1450 {
1451    Evas_Object *widget;
1452
1453    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1454    if (widget && elm_entry_editable_get(widget))
1455      elm_object_text_set(widget, string);
1456 }
1457
1458 /**
1459  * @brief Copies text content from entry to clipboard
1460  *
1461  * @param text AtkEditableText instance
1462  * @param start_pos start position of the cursor
1463  * @param end_pos end position of the cursor
1464  */
1465 static void
1466 eail_entry_copy_text(AtkEditableText *text,
1467                      gint             start_pos,
1468                      gint             end_pos)
1469 {
1470    Evas_Object *widget;
1471    const char *entry_text;
1472    char *tmp;
1473
1474    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1475    if (!widget) return;
1476
1477    entry_text = elm_entry_entry_get(widget);
1478    tmp = eail_get_substring(entry_text, start_pos, end_pos);
1479    eail_clipboard_set_text(tmp);
1480    g_free(tmp);
1481 }
1482
1483 /**
1484  * @brief Cuts text content from entry to clipboard
1485  *
1486  * @param text AtkEditableText instance
1487  * @param start_pos start position of the cursor
1488  * @param end_pos end position of the cursor
1489  */
1490 static void
1491 eail_entry_cut_text(AtkEditableText *text,
1492                     gint             start_pos,
1493                     gint             end_pos)
1494 {
1495    Evas_Object *widget;
1496    const char *entry_text;
1497    char *tmp;
1498    GString *s;
1499
1500    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1501    if (!widget || !elm_entry_editable_get(widget))
1502      return;
1503
1504    entry_text = elm_entry_entry_get(widget);
1505    tmp = eail_get_substring(entry_text, start_pos, end_pos);
1506    eail_clipboard_set_text(tmp);
1507    g_free(tmp);
1508
1509    s = g_string_new(entry_text);
1510    s = g_string_erase(s, start_pos, end_pos - start_pos);
1511
1512    elm_entry_entry_set(widget, s->str);
1513    g_string_free(s, TRUE);
1514 }
1515
1516 /**
1517  * @brief Pastes text content from clipboard into entry
1518  *
1519  * @param text AtkEditableText instance
1520  * @param position start position of the cursor
1521  */
1522 static void
1523 eail_entry_paste_text(AtkEditableText *text,
1524                       gint             position)
1525 {
1526    Evas_Object *widget;
1527    GString *s;
1528    const char *clip;
1529
1530    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1531    if (!widget || !elm_entry_editable_get(widget))
1532      return;
1533
1534    clip = eail_clipboard_get_text();
1535    if (!clip) return;
1536
1537    s = g_string_new(elm_entry_entry_get(widget));
1538    s = g_string_insert(s, position, clip);
1539    elm_entry_entry_set(widget, s->str);
1540    g_string_free(s, TRUE);
1541 }
1542
1543 /**
1544  * @brief Deletes text between start_pos and end_pos but not
1545  * including end_pos
1546  *
1547  * @param text AtkEditableText instance
1548  * @param start_pos start position
1549  * @param end_pos end position
1550  */
1551 static void
1552 eail_entry_delete_text(AtkEditableText *text,
1553                        gint start_pos,
1554                        gint end_pos)
1555 {
1556    Evas_Object *widget;
1557    GString *entry_text;
1558
1559    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1560    if (!widget || !elm_entry_editable_get(widget))
1561      return;
1562
1563    entry_text = g_string_new(elm_entry_entry_get(widget));
1564    entry_text = g_string_erase(entry_text, start_pos, end_pos-start_pos);
1565
1566    elm_entry_entry_set(widget, entry_text->str);
1567    g_string_free(entry_text, TRUE);
1568 }
1569
1570 /**
1571  * @brief Inserts text at the specified position
1572  *
1573  * After the call position points to the position after the newly inserted text.
1574  *
1575  * @param text AtkEditableText instance
1576  * @param string string to insert
1577  * @param length string's length
1578  * @param [out] position position at which text is inserted
1579  */
1580 static void
1581 eail_entry_insert_text(AtkEditableText *text,
1582                        const gchar *string,
1583                        gint length,
1584                        gint *position)
1585 {
1586    Evas_Object *widget;
1587    GString *entry_text;
1588
1589    widget = eail_widget_get_widget(EAIL_WIDGET(text));
1590    if (!widget || !elm_entry_editable_get(widget))
1591      return;
1592
1593    entry_text = g_string_new(elm_entry_entry_get(widget));
1594    entry_text = g_string_insert_len(entry_text, *position, string,
1595                                     length);
1596    elm_entry_entry_set(widget, entry_text->str);
1597    *position += length;
1598    g_string_free(entry_text, TRUE);
1599 }
1600
1601 /**
1602  * @brief Initialization for AtkEditableTextIface interface
1603  *
1604  * Function called upon instance creation. It initializes AtkText interface
1605  * implementation i.e hooks method pointers in the interface structure
1606  * to the implementing class's implementation.
1607  *
1608  * @param iface AtkEditableTextIface instance
1609  */
1610 static void
1611 atk_editable_text_interface_init(AtkEditableTextIface *iface)
1612 {
1613    iface->set_text_contents = eail_entry_set_text_contents;
1614    iface->copy_text = eail_entry_copy_text;
1615    iface->cut_text = eail_entry_cut_text;
1616    iface->paste_text = eail_entry_paste_text;
1617    iface->delete_text = eail_entry_delete_text;
1618    iface->insert_text = eail_entry_insert_text;
1619 }