Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_label.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_label.c
22  * @brief EailLabel implementation.
23  */
24
25 #include <Elementary.h>
26
27 #include "eail_label.h"
28 #include "eail_utils.h"
29
30 static void atk_text_interface_init(AtkTextIface *iface);
31
32 /**
33  * @brief EailLabel type definition
34  */
35 G_DEFINE_TYPE_WITH_CODE(EailLabel,
36                         eail_label,
37                         EAIL_TYPE_WIDGET,
38                         G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
39                                               atk_text_interface_init));
40
41 /**
42  * @brief EailLabel initializer.
43  *
44  * @param obj EailLabel instance
45  * @param data initialization data
46  */
47 static void
48 eail_label_initialize(AtkObject *obj, gpointer data)
49 {
50    ATK_OBJECT_CLASS(eail_label_parent_class)->initialize(obj, data);
51    obj->role = ATK_ROLE_LABEL;
52 }
53
54 /**
55  * @brief EailLabel interface intializer.
56  *
57  * @param label EailLabel instance
58  */
59 static void
60 eail_label_init(EailLabel *label)
61 {
62 }
63
64 /**
65  * @brief Gets the state set of the accessible.
66  *
67  * The caller must unreference it when it is no longer needed.
68  *
69  * @param accessible AtkObject instance
70  * @return AtkStateSet containing the state set of the accessible
71  */
72 static AtkStateSet*
73 eail_label_ref_state_set(AtkObject *accessible)
74 {
75    AtkStateSet *state_set;
76    Evas_Object *widget;
77
78    state_set =
79        ATK_OBJECT_CLASS(eail_label_parent_class)->ref_state_set(accessible);
80
81    widget = eail_widget_get_widget(EAIL_WIDGET(accessible));
82    if (!widget) return state_set;
83
84    atk_state_set_add_state(state_set, ATK_STATE_MULTI_LINE);
85
86    return state_set;
87 }
88
89 /**
90  * @brief Gets obj's name
91  *
92  * @param obj AtkObject instance
93  * @returns string containing obj's name or its text content if the name is not set
94  */
95 static const gchar*
96 eail_label_get_name(AtkObject *obj)
97 {
98    const gchar *name;
99    Evas_Object *widget;
100
101    name = ATK_OBJECT_CLASS(eail_label_parent_class)->get_name(obj);
102    if (NULL != name)
103      return name;
104
105    widget = eail_widget_get_widget(EAIL_WIDGET(obj));
106    if (!widget) return NULL;
107
108    name = (const gchar*)elm_object_text_get(widget);
109    if (!name) return NULL;
110
111    return name;
112 }
113
114 /**
115  * @brief EailLabel class initializer.
116  *
117  * @param klass EailLabelClass instance
118  */
119 static void
120 eail_label_class_init(EailLabelClass *klass)
121 {
122    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
123
124    atk_class->initialize = eail_label_initialize;
125    atk_class->ref_state_set = eail_label_ref_state_set;
126    atk_class->get_name = eail_label_get_name;
127 }
128
129 /**
130  * @brief Gets text bounded by start_offset and end_offset
131  *
132  * Use g_free() to free the returned string
133  *
134  * @param text an AtkText
135  * @param start_offset start position
136  * @param end_offset end position, -1 for the end of the string
137  * @return string containing text from start_offset up to, but not including
138  * end_offset
139  */
140 static gchar*
141 eail_label_get_text(AtkText   *text,
142                     gint       start_offset,
143                     gint       end_offset)
144 {
145    gchar *string = NULL;
146    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
147
148    if (widget)
149      string = (gchar *)elm_object_text_get(widget);
150
151    if (!string)
152      return NULL;
153
154    return eail_get_substring(string, start_offset, end_offset);
155 }
156
157 /**
158  * @brief Gets the character at offset
159  *
160  * @param text AtkText instance
161  * @param offset character offset
162  * @return char located at the given offset
163  */
164 static gunichar
165 eail_label_get_character_at_offset(AtkText    *text,
166                                    gint        offset)
167 {
168    gunichar character = '\0';
169    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
170
171    if (widget)
172      character = g_utf8_get_char(
173          g_utf8_offset_to_pointer(elm_object_text_get(widget), offset));
174
175    return character;
176 }
177
178 /**
179  * @brief Gets the length of text
180  *
181  * @param text AtkText instance
182  * @return integer representing the length of text
183  */
184 static gint
185 eail_label_get_character_count(AtkText *text)
186 {
187    Evas_Object *widget;
188    const char *str;
189
190    widget = eail_widget_get_widget(EAIL_WIDGET(text));
191    if (!widget)
192      return 0;
193
194    str = elm_object_text_get(widget);
195    if (!str)
196      return 0;
197    return g_utf8_strlen(str, -1);
198 }
199
200 /**
201  * @brief Creates an AtkAttributeSet which consists of the default values of
202  * attributes for the text.
203  *
204  * This AtkAttributeSet should be freed by a call to
205  * atk_attribute_set_free()
206  *
207  * @param text AtkText instance
208  *
209  * @returns AtkAttributeSet containing default values of attributes
210  * at offset.
211  */
212 static AtkAttributeSet *
213 eail_label_get_default_attributes(AtkText *text)
214 {
215    AtkAttributeSet *at_set = NULL;
216
217    at_set = eail_utils_text_add_attribute
218        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
219         atk_text_attribute_get_value(ATK_TEXT_ATTR_WRAP_MODE, 0));
220
221    at_set = eail_utils_text_add_attribute
222        (at_set, ATK_TEXT_ATTR_EDITABLE,
223         atk_text_attribute_get_value
224          (ATK_TEXT_ATTR_EDITABLE, FALSE));
225
226    return at_set;
227 }
228
229 /**
230  * @brief Creates an AtkAttributeSet which consists of the attributes
231  * explicitly set at the position offset in the text.
232  *
233  * start_offset and end_offset are set to the start and end of the range around offset
234  * where the attributes are invariant.
235  *
236  * Note that end_offset is the offset of the first character after the range.
237  *
238  * This AtkAttributeSet should be freed by a call to
239  * atk_attribute_set_free()
240  *
241  * @param text AtkText instance
242  * @param offset the offset at which to get the attributes
243  * @param [out] start_offset start offset of the range
244  * @param [out] end_offset end offset of the range
245  *
246  * @returns an AtkAttributeSet which contains the attributes explicitly set at
247  * offset.
248  */
249 static AtkAttributeSet *
250 eail_label_get_run_attributes(AtkText *text,
251                               gint offset,
252                               gint *start_offset,
253                               gint *end_offset)
254 {
255    AtkAttributeSet *at_set = NULL;
256    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
257    gint len = eail_label_get_character_count(ATK_TEXT(text));
258
259    if (!widget || offset >= len)
260      {
261         *start_offset = -1;
262         *end_offset = -1;
263
264         return NULL;
265      }
266
267    *start_offset = 0;
268    *end_offset = len;
269
270    /* NOTE: Elm_Wrap_Type value is in 100% compatible with ATK wrap modes, so
271     * no additional conversion is needed*/
272    Elm_Wrap_Type wrap_type = elm_label_line_wrap_get(widget);
273    at_set = eail_utils_text_add_attribute
274        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
275         atk_text_attribute_get_value
276          (ATK_TEXT_ATTR_WRAP_MODE, wrap_type));
277
278    at_set = eail_utils_text_add_attribute
279        (at_set, ATK_TEXT_ATTR_EDITABLE,
280         atk_text_attribute_get_value
281          (ATK_TEXT_ATTR_EDITABLE, FALSE));
282
283    return at_set;
284 }
285
286 /**
287  * @brief Gets the specified text after offset
288  *
289  * Use g_free() to free the returned string.
290  *
291  * @param text AtkText instance
292  * @param offset character offset
293  * @param boundary_type AtkTextBoundary instance
294  * @param [out] start_offset start offset of the returned string
295  * @param [out] end_offset offset of the first character after the returned
296  * substring
297  * @returns newly allocated string containing the text after offset bounded
298  * by the specified boundary_type
299  */
300 static gchar *
301 eail_label_get_text_after_offset(AtkText *text,
302                                  gint offset,
303                                  AtkTextBoundary boundary_type,
304                                  gint *start_offset,
305                                  gint *end_offset)
306 {
307    Evas_Object *widget;
308    const Evas_Object *textblock;
309    Evas_Object *label_edje_layer;
310
311    widget = eail_widget_get_widget(EAIL_WIDGET(text));
312    if (!widget) return NULL;
313
314    label_edje_layer = elm_layout_edje_get(widget);
315    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
316
317    return eail_get_text_after(textblock, offset, boundary_type, start_offset,
318                               end_offset);
319
320 }
321
322 /**
323  * @brief Gets the specified text at offset
324  *
325  * Use g_free() to free the returned string.
326  *
327  * @param text AtkText instance
328  * @param offset character offset
329  * @param boundary_type AtkTextBoundary instance
330  * @param [out] start_offset start offset of the returned string
331  * @param [out] end_offset offset of the first character after the returned
332  * substring
333  * @returns newly allocated string containing the text after offset bounded
334  * by the specified boundary_type
335  */
336 static gchar *
337 eail_label_get_text_at_offset(AtkText *text,
338                               gint offset,
339                               AtkTextBoundary boundary_type,
340                               gint *start_offset,
341                               gint *end_offset)
342 {
343    Evas_Object *widget;
344    const Evas_Object *textblock;
345    Evas_Object *label_edje_layer;
346
347    widget = eail_widget_get_widget(EAIL_WIDGET(text));
348    if (!widget) return NULL;
349
350    label_edje_layer = elm_layout_edje_get(widget);
351    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
352
353    return eail_get_text_at(textblock, offset, boundary_type, start_offset,
354                            end_offset);
355 }
356
357 /**
358  * @brief Gets the specified text before offset
359  *
360  * Use g_free() to free the returned string.
361  *
362  * @param text AtkText instance
363  * @param offset character offset
364  * @param boundary_type AtkTextBoundary instance
365  * @param [out] start_offset start offset of the returned string
366  * @param [out] end_offset offset of the first character after the returned
367  * substring
368  * @returns newly allocated string containing the text after offset bounded
369  * by the specified boundary_type
370  */
371 static gchar *
372 eail_label_get_text_before_offset(AtkText *text,
373                                   gint offset,
374                                   AtkTextBoundary boundary_type,
375                                   gint *start_offset,
376                                   gint *end_offset)
377 {
378    Evas_Object *widget;
379    const Evas_Object *textblock;
380    Evas_Object *label_edje_layer;
381
382    widget = eail_widget_get_widget(EAIL_WIDGET(text));
383    if (!widget) return NULL;
384
385    label_edje_layer = elm_layout_edje_get(widget);
386    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
387
388    return eail_get_text_before(textblock, offset, boundary_type, start_offset,
389                                end_offset);
390 }
391
392 /**
393  * @brief Gets the offset of the character located at coordinates x and y.
394  *  x and y are interpreted as being relative to the screen or this widget's window depending on coords.
395  *
396  * @param text AtkText instance
397  * @param screen x-position of character
398  * @param screen y-position of character
399  * @param specify whether coordinates are relative to the screen or widget window
400  *
401  * @returns the offset to the character which is located at the specified x and y coordinates.
402  */
403 static gint
404 eail_label_get_offset_at_point(AtkText *text,
405                                  gint x,
406                                  gint y,
407                                  AtkCoordType coords)
408 {
409    int result = -1;
410    const Evas_Object *textblock = NULL;
411    Evas_Textblock_Cursor *cur = NULL;
412    Evas_Object *label_edje_layer = NULL;
413
414    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
415
416    if (!widget) return -1;
417
418    label_edje_layer = elm_layout_edje_get(widget);
419
420    if (!label_edje_layer) return-1 ;
421
422    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
423    cur = evas_object_textblock_cursor_new(textblock);
424
425    if (coords == ATK_XY_SCREEN)
426    {
427       int ee_x, ee_y;
428       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
429       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
430       x -= ee_x;
431       y -= ee_y;
432     }
433
434    evas_textblock_cursor_char_coord_set( cur, x, y);
435    result = evas_textblock_cursor_pos_get( cur );
436    evas_textblock_cursor_free(cur);
437
438    return result;
439 }
440
441 /*
442  * @brief Get the bounding box containing the glyph
443  *  representing the character at a particular text offset.
444  *
445  * @param text AtkText instance
446  * @param offset The offset of the text character for which
447  * bounding information is required.
448  * @param x Pointer for the x cordinate of the bounding box
449  * @param y Pointer for the y cordinate of the bounding box
450  * @param width Pointer for the width of the bounding box
451  * @param height Pointer for the height of the bounding box
452  * @param coords specify whether coordinates are relative to the
453  * screen or widget window
454  *
455  */
456 static void
457 eail_label_get_character_extents(AtkText *text,
458                                  gint offset,
459                                  gint *x,
460                                  gint *y,
461                                  gint *width,
462                                  gint *height,
463                                  AtkCoordType coords)
464 {
465    int result = -1;
466    const Evas_Object *textblock = NULL;
467    Evas_Textblock_Cursor *cur = NULL;
468    Evas_Object *label_edje_layer = NULL;
469
470    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
471
472    if (!widget) return;
473
474    label_edje_layer = elm_layout_edje_get(widget);
475
476    if (!label_edje_layer) return;
477
478    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
479
480    cur = evas_object_textblock_cursor_new(textblock);
481    evas_textblock_cursor_pos_set(cur, offset);
482
483    result = evas_textblock_cursor_char_geometry_get(cur, x, y, width, height);
484
485    evas_textblock_cursor_free(cur);
486
487    if (-1 == result) return;
488
489    if (coords == ATK_XY_SCREEN)
490    {
491       int ee_x, ee_y;
492       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
493
494       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
495       *x += ee_x;
496       *y += ee_y;
497     }
498 }
499
500 /**
501  * @brief Sum all rectangles on list
502  *
503  * (function for internal use ony)
504  *
505  * @param list list with Evas_Textblock_Rectangle
506  *
507  * @return an Evas_Textblock_Rectangle encloses all rectanges from list
508  */
509 Evas_Textblock_Rectangle _internal_sum_rects_on_list(Eina_List *list)
510 {
511     Evas_Textblock_Rectangle result;
512
513     Evas_Coord min_x = 0;
514     Evas_Coord min_y = 0;
515     Evas_Coord max_x = 0;
516     Evas_Coord max_y = 0;
517     int first_run = 1;
518
519     Eina_List *l = NULL;
520     Evas_Textblock_Rectangle *rectT = NULL;
521     EINA_LIST_FOREACH(list, l, rectT)
522     {
523         if (first_run)
524         {
525             min_x = rectT->x;
526             min_y = rectT->y;
527             max_x = rectT->x + rectT->w;
528             max_y = rectT->y + rectT->h;
529             first_run = 0;
530         }
531         else
532         {
533             Evas_Coord t_min_x = rectT->x;
534             Evas_Coord t_min_y = rectT->y;
535             Evas_Coord t_max_x = rectT->x + rectT->w;
536             Evas_Coord t_max_y = rectT->y + rectT->h;
537
538
539             if ( t_min_x < min_x )
540                 min_x = t_min_x;
541             if ( t_min_y < min_y )
542                 min_y = t_min_y;
543             if ( t_max_x > max_x )
544                 max_x = t_max_x;
545             if ( t_max_y > max_y )
546                 max_y = t_max_y;
547         }
548     }
549
550     result.x = min_x;
551     result.y = min_y;
552     result.w = max_x - min_x;
553     result.h = max_y - min_y;
554
555     return result;
556 }
557
558 /**
559  * Get the bounding box for text within the specified range.
560  *
561  * @param text an AtkText
562  * @param start_offset The offset of the first text character for which boundary information is required.
563  * @param end_offset The offset of the text character after the last character for which boundary information is required.
564  * @param coord_type Specify whether coordinates are relative to the screen or widget window.
565  * @param [out] rect A pointer to a AtkTextRectangle which is filled in by this function.
566  */
567 void eail_label_get_range_extents(AtkText *text,
568                                  gint start_offset,
569                                  gint end_offset,
570                                  AtkCoordType coord_type,
571                                  AtkTextRectangle *rect)
572 {
573     if (start_offset >= end_offset)
574        return;
575
576     Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
577     Evas_Textblock_Cursor *cur1 = NULL;
578     Evas_Textblock_Cursor *cur2 = NULL;
579     Evas_Object *label_edje_layer = NULL;
580     const Evas_Object *textblock = NULL;
581
582     label_edje_layer = elm_layout_edje_get(widget);
583     textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
584     cur1 = evas_object_textblock_cursor_new(textblock);
585     cur2 = evas_object_textblock_cursor_new(textblock);
586
587     evas_textblock_cursor_pos_set(cur1, start_offset);
588     evas_textblock_cursor_pos_set(cur2, end_offset);
589
590     Eina_List *list = NULL;
591     list = evas_textblock_cursor_range_geometry_get(cur1, cur2);
592
593     if (list)
594     {
595         Evas_Textblock_Rectangle rect_list = _internal_sum_rects_on_list(list);
596         rect->x = rect_list.x;
597         rect->y = rect_list.y;
598         rect->width = rect_list.w;
599         rect->height = rect_list.h;
600
601         if (coord_type == ATK_XY_SCREEN)
602         {
603             int ee_x, ee_y;
604             Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
605
606             ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
607             rect->x += ee_x;
608             rect->y += ee_y;
609         }
610     }
611
612     if (cur1)
613         evas_textblock_cursor_free(cur1);
614     if (cur2)
615         evas_textblock_cursor_free(cur2);
616 }
617
618 /**
619  * @brief Initializes AtkTextIface interface
620  *
621  * @param iface AtkTextIface instance
622  */
623 static void
624 atk_text_interface_init(AtkTextIface *iface)
625 {
626    iface->get_character_count = eail_label_get_character_count;
627    iface->get_character_at_offset = eail_label_get_character_at_offset;
628    iface->get_text = eail_label_get_text;
629    iface->get_run_attributes = eail_label_get_run_attributes;
630    iface->get_default_attributes = eail_label_get_default_attributes;
631    iface->get_text_after_offset = eail_label_get_text_after_offset;
632    iface->get_text_at_offset = eail_label_get_text_at_offset;
633    iface->get_text_before_offset = eail_label_get_text_before_offset;
634    iface->get_offset_at_point = eail_label_get_offset_at_point;
635    iface->get_character_extents = eail_label_get_character_extents;
636    iface->get_range_extents = eail_label_get_range_extents;
637 }
638