Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_bubble.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_bubble.c
22  * @brief EailBubble implementation
23  */
24
25 #include <Elementary.h>
26
27 #include "eail_bubble.h"
28 #include "eail_utils.h"
29
30 static void atk_action_interface_init(AtkActionIface *iface);
31 static void atk_text_interface_init(AtkTextIface *iface);
32
33 /**
34  * @brief Definition of EailBubble type
35  */
36 G_DEFINE_TYPE_WITH_CODE(EailBubble,
37                         eail_bubble,
38                         EAIL_TYPE_TEXT,
39                         G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
40                                               atk_text_interface_init)
41                         G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION,
42                                               atk_action_interface_init));
43
44 /*
45  * Implementation of the *AtkObject* interface
46  */
47
48 /**
49  * @brief EailBubble object initialization
50  *
51  * @param obj EailBubble instance
52  * @param data user set additional initialization data
53  */
54 static void
55 eail_bubble_initialize(AtkObject *obj, gpointer data)
56 {
57    ATK_OBJECT_CLASS(eail_bubble_parent_class)->initialize(obj, data);
58
59    obj->role = ATK_ROLE_FILLER;
60 }
61
62 /**
63  * @brief Class destructor
64  *
65  * @param object GObject instance
66  */
67 static void
68 eail_bubble_finalize(GObject *object)
69 {
70    EailBubble *bubble = EAIL_BUBBLE(object);
71
72    if (bubble->click_description) free(bubble->click_description);
73
74    G_OBJECT_CLASS(eail_bubble_parent_class)->finalize(object);
75 }
76
77 /**
78  * @brief EailBubble instance initialization
79  *
80  * @param bubble EailBubble instance
81  */
82 static void
83 eail_bubble_init(EailBubble *bubble)
84 {
85    bubble->click_description = NULL;
86 }
87
88 /**
89  * @brief Gets the list of widget's accessible children
90  *
91  * @param widget EailWidget instance
92  *
93  * @return Eina_List representing the list of accessible children
94  * or NULL if widget has no children
95  */
96 static Eina_List *
97 eail_bubble_get_widget_children(EailWidget *widget)
98 {
99    Eina_List *list = NULL;
100    Evas_Object *child, *obj;
101
102    obj = eail_widget_get_widget(EAIL_WIDGET(widget));
103
104    if (obj)
105      {
106         child = elm_object_part_content_get(obj, "default");
107         if (child && elm_object_widget_check(child))
108           list = eina_list_append(list, child);
109
110         child = elm_object_part_content_get(obj, "icon");
111         if (child && elm_object_widget_check(child))
112           list = eina_list_append(list, child);
113      }
114
115    return list;
116 }
117
118 /**
119  * @brief GObject type initialization function
120  *
121  * @param klass EailBubbleClass instance
122  */
123 static void
124 eail_bubble_class_init(EailBubbleClass *klass)
125 {
126    AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
127    EailWidgetClass *widget_class = EAIL_WIDGET_CLASS(klass);
128    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
129
130    atk_class->initialize = eail_bubble_initialize;
131    widget_class->get_widget_children = eail_bubble_get_widget_children;
132    gobject_class->finalize = eail_bubble_finalize;
133 }
134
135 /*
136  * Implementation of the *AtkAction* interface
137  */
138
139 /**
140  * @brief Gets the number of accessible actions available on the object
141  *
142  * Implementation of get_n_actions from AtkAction interface.
143  *
144  * @param action EailBubble instance
145  *
146  * @returns integer representing the number of available actions
147  */
148 static int
149 eail_bubble_n_actions_get(AtkAction *action)
150 {
151    return 1;
152 }
153
154 /**
155  * @brief Gets the description string of the specified action
156  *
157  * Implementation of get_description from AtkAction interface.
158  *
159  * @param action EailBubble instance
160  * @param i action index
161  *
162  * @return string representing the specified action's description
163  */
164 static const char*
165 eail_bubble_description_get(AtkAction *action,
166                             gint i)
167 {
168    EailBubble *bubble;
169    const char *action_description;
170
171    bubble = EAIL_BUBBLE(action);
172    if (!bubble) return NULL;
173
174    switch (i)
175      {
176       case 0:
177          action_description = bubble->click_description;
178          break;
179       default:
180          action_description = NULL;
181          break;
182      }
183
184    return action_description;
185 }
186
187 /**
188  * @brief Sets a description of the specified action of the object
189  *
190  * Implementation of set_description from AtkAction interface.
191  *
192  * @param action AtkAction instance
193  * @param i action index
194  * @param description action description
195  *
196  * @return TRUE on success, FALSE otherwise
197  */
198 static gboolean
199 eail_bubble_description_set(AtkAction *action,
200                             gint i,
201                             const char *description)
202 {
203    EailBubble *bubble;
204    char **value;
205
206    bubble = EAIL_BUBBLE(action);
207    if (!bubble) return FALSE;
208
209    switch (i)
210      {
211       case 0:
212          value = &bubble->click_description;
213          break;
214       default:
215          value = NULL;
216          break;
217      }
218
219    if (value)
220      {
221         free(*value);
222         *value = g_strdup(description);
223         return TRUE;
224      }
225
226    return FALSE;
227 }
228
229 /**
230  * @brief Gets the name string of the specified action
231  *
232  * Implementation of get_name from AtkAction interface.
233  *
234  * @param action AtkAction instance
235  * @param i action index
236  *
237  * @return string representing the specified action's name
238  */
239 static const char*
240 eail_bubble_action_name_get(AtkAction *action,
241                             int i)
242 {
243    const char* action_name;
244
245    switch (i)
246      {
247       case 0:
248          action_name = "click";
249          break;
250       default:
251          action_name = NULL;
252          break;
253      }
254
255    return action_name;
256 }
257
258 /**
259  * @brief Performs the specified action on the object
260  *
261  * Implementation of do_action from AtkAction interface.
262  *
263  * @param action AtkAction instance
264  * @param i action index
265  *
266  * @return TRUE on success, FALSE otherwise
267  */
268 static gboolean
269 eail_bubble_do_action(AtkAction *action,
270                       int i)
271 {
272    Evas_Object *widget;
273
274    widget = eail_widget_get_widget(EAIL_WIDGET(action));
275    if (!widget) return FALSE;
276
277    if ((elm_object_disabled_get(widget)) || (!evas_object_visible_get(widget)))
278      return FALSE;
279
280    const char *action_name = atk_action_get_name(action, i);
281    if (!action_name) return FALSE;
282
283    evas_object_smart_callback_call(widget, "clicked", NULL);
284
285    return TRUE;
286 }
287
288 /**
289  * @brief AtkAction interface initializer
290  *
291  * @param iface action interface to be filled
292  **/
293 static void
294 atk_action_interface_init(AtkActionIface *iface)
295 {
296    g_return_if_fail(iface != NULL);
297
298    iface->get_n_actions   = eail_bubble_n_actions_get;
299    iface->get_description = eail_bubble_description_get;
300    iface->set_description = eail_bubble_description_set;
301    iface->get_name        = eail_bubble_action_name_get;
302    iface->do_action       = eail_bubble_do_action;
303 }
304
305
306 /**
307  * @brief Helper func to get Textblock form bubble widget
308  *
309  * @param text AtkText instance
310  * @returns Textblock part of bubble widget
311  */
312 static const Evas_Object *
313 _eail_get_textblock(AtkText *text)
314 {
315    Evas_Object *widget= NULL;
316    const Evas_Object *textblock = NULL;
317    Evas_Object *label = NULL;
318    Evas_Object *label_edje_layer = NULL;
319    Evas_Object *bubble_edje_layer = NULL;
320
321    widget = eail_widget_get_widget(EAIL_WIDGET(text));
322    if (!widget) return NULL;
323
324    bubble_edje_layer = elm_layout_edje_get(widget);
325    if (!bubble_edje_layer) return NULL;
326
327    label= edje_object_part_swallow_get(bubble_edje_layer, "elm.swallow.content");
328    if (!label) return NULL;
329
330    label_edje_layer = elm_layout_edje_get(label);
331    if (!label_edje_layer) return NULL;
332
333    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
334    if (!textblock) return NULL;
335
336    return textblock;
337 }
338
339 /**
340  * @brief Gets text bounded by start_offset and end_offset
341  *
342  * Use g_free() to free the returned string
343  *
344  * @param text AtkText instance
345  * @param start_offset start position
346  * @param end_offset end position, -1 for the end of the string
347  * @return string containing text from start_offset up to, but not including
348  * end_offset
349  */
350 static gchar*
351 eail_bubble_get_text(AtkText   *text,
352                      gint       start_offset,
353                      gint       end_offset)
354 {
355    gchar *string = NULL;
356    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
357    Evas_Object *label = NULL;
358
359    if (!widget)
360       return NULL;
361    label = elm_object_content_get(widget);
362
363    if (label)
364      string = (gchar *)elm_object_text_get(label);
365
366    return eail_get_substring(string, start_offset, end_offset);
367 }
368
369 /**
370  * @brief Gets the character at offset
371  *
372  * @param text AtkText instance
373  * @param offset character offset
374  * @return char representing the character at offset
375  */
376 static gunichar
377 eail_bubble_get_character_at_offset(AtkText    *text,
378                                   gint        offset)
379 {
380    gunichar character = '\0';
381    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
382    Evas_Object *label = NULL;
383
384    if (!widget)
385       return character;
386
387    label = elm_object_content_get(widget);
388
389    if (label)
390      character = g_utf8_get_char(
391          g_utf8_offset_to_pointer(elm_object_text_get(label), offset));
392
393    return character;
394 }
395
396 /**
397  * @brief Gets the text's length
398  *
399  * @param text AtkText instance
400  * @return integer representing the text length
401  */
402 static gint
403 eail_bubble_get_character_count(AtkText *text)
404 {
405    gint count = 0;
406    const gchar *string_text = NULL;
407
408    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
409    Evas_Object *label = NULL;
410
411    if (!widget) return count;
412
413    label = elm_object_content_get(widget);
414    if (!label) return count;
415
416    string_text = elm_object_text_get(label);
417    if (!string_text) return count;
418
419    count = g_utf8_strlen(string_text, -1);
420
421    return count;
422 }
423
424
425 /**
426  * @brief Get the bounding box containing the glyph
427  *  representing the character at a particular text offset.
428  *
429  * @param text AtkText instance
430  * @param offset The offset of the text character for which
431  * bounding information is required.
432  * @param x Pointer for the x cordinate of the bounding box
433  * @param y Pointer for the y cordinate of the bounding box
434  * @param width Pointer for the width of the bounding box
435  * @param height Pointer for the height of the bounding box
436  * @param coords specify whether coordinates are relative to the
437  * screen or widget window
438  *
439  */
440 static void
441 eail_bubble_get_character_extents(AtkText *text,
442                                   gint offset,
443                                   gint *x,
444                                   gint *y,
445                                   gint *width,
446                                   gint *height,
447                                   AtkCoordType coords)
448 {
449    int result = -1;
450    const Evas_Object *textblock = NULL;
451    Evas_Textblock_Cursor *cur = NULL;
452
453    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
454    if (!widget) return;
455
456    textblock = _eail_get_textblock(text);
457    if (!textblock) return;
458
459    cur = evas_object_textblock_cursor_new(textblock);
460    if (!cur) return;
461
462    evas_textblock_cursor_pos_set(cur, offset);
463
464    result = evas_textblock_cursor_char_geometry_get(cur, x, y, width, height);
465
466    evas_textblock_cursor_free(cur);
467
468    if (-1 == result) return;
469
470    if (coords == ATK_XY_SCREEN)
471    {
472       int ee_x, ee_y;
473       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
474
475       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
476       *x += ee_x;
477       *y += ee_y;
478     }
479 }
480
481 /**
482  * @brief Gets the specified text after offset
483  *
484  * Use g_free() to free the returned string.
485  *
486  * @param text AtkText instance
487  * @param offset character offset
488  * @param boundary_type AtkTextBoundary instance
489  * @param [out] start_offset start offset of the returned string
490  * @param [out] end_offset offset of the first character after the returned
491  * substring
492  * @returns newly allocated string containing the text after offset bounded
493  * by the specified boundary_type
494  */
495 static gchar *
496 eail_bubble_get_text_after_offset(AtkText *text,
497                                   gint offset,
498                                   AtkTextBoundary boundary_type,
499                                   gint *start_offset,
500                                   gint *end_offset)
501 {
502    const Evas_Object *textblock;
503
504    textblock = _eail_get_textblock(text);
505    if (!textblock) return NULL;
506
507    return eail_get_text_after(textblock, offset, boundary_type, start_offset,
508                               end_offset);
509
510 }
511
512 /**
513  * @brief Gets the specified text at offset
514  *
515  * Use g_free() to free the returned string.
516  *
517  * @param text AtkText instance
518  * @param offset character offset
519  * @param boundary_type AtkTextBoundary instance
520  * @param [out] start_offset start offset of the returned string
521  * @param [out] end_offset offset of the first character after the returned
522  * substring
523  * @returns newly allocated string containing the text after offset bounded
524  * by the specified boundary_type
525  */
526 static gchar *
527 eail_bubble_get_text_at_offset(AtkText *text,
528                                gint offset,
529                                AtkTextBoundary boundary_type,
530                                gint *start_offset,
531                                gint *end_offset)
532 {
533    const Evas_Object *textblock;
534
535    textblock = _eail_get_textblock(text);
536    if (!textblock) return NULL;
537
538    return eail_get_text_at(textblock, offset, boundary_type, start_offset,
539                            end_offset);
540 }
541
542 /**
543  * @brief Gets the specified text before offset
544  *
545  * Use g_free() to free the returned string.
546  *
547  * @param text AtkText instance
548  * @param offset character offset
549  * @param boundary_type AtkTextBoundary instance
550  * @param [out] start_offset start offset of the returned string
551  * @param [out] end_offset offset of the first character after the returned
552  * substring
553  * @returns newly allocated string containing the text after offset bounded
554  * by the specified boundary_type
555  */
556 static gchar *
557 eail_bubble_get_text_before_offset(AtkText *text,
558                                    gint offset,
559                                    AtkTextBoundary boundary_type,
560                                    gint *start_offset,
561                                    gint *end_offset)
562 {
563    const Evas_Object *textblock;
564
565    textblock = _eail_get_textblock(text);
566    if (!textblock) return NULL;
567
568    return eail_get_text_before(textblock, offset, boundary_type, start_offset,
569                                end_offset);
570 }
571
572 /**
573  * @brief Gets the offset of the character located at coordinates x and y.
574  *  x and y are interpreted as being relative to the screen or this
575  *  widget's window depending on coords.
576  *
577  * @param text AtkText instance
578  * @param screen x-position of character
579  * @param screen y-position of character
580  * @param specify whether coordinates are relative to the screen or widget window
581  *
582  * @returns the offset to the character which is located at the specified x and y coordinates.
583  */
584 static gint
585 eail_bubble_get_offset_at_point(AtkText *text,
586                                 gint x,
587                                 gint y,
588                                 AtkCoordType coords)
589 {
590    int result = -1;
591    const Evas_Object *textblock = NULL;
592    Evas_Textblock_Cursor *cur = NULL;
593
594    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
595
596    if (!widget) return -1;
597
598    textblock = _eail_get_textblock(text);
599    if (!textblock) return -1;
600
601    cur = evas_object_textblock_cursor_new(textblock);
602
603    if (coords == ATK_XY_SCREEN)
604    {
605       int ee_x, ee_y;
606       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
607       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
608       x -= ee_x;
609       y -= ee_y;
610     }
611
612    evas_textblock_cursor_char_coord_set( cur, x, y);
613    result = evas_textblock_cursor_pos_get( cur );
614    evas_textblock_cursor_free(cur);
615
616    return result;
617 }
618
619
620 /**
621  * @brief Initializes AtkTextIface interface
622  *
623  * @param iface AtkTextIface instance
624  */
625 static void
626 atk_text_interface_init(AtkTextIface *iface)
627 {
628    iface->get_text = eail_bubble_get_text;
629    iface->get_character_at_offset = eail_bubble_get_character_at_offset;
630    iface->get_character_count = eail_bubble_get_character_count;
631    iface->get_character_extents = eail_bubble_get_character_extents;
632    iface->get_text_after_offset = eail_bubble_get_text_after_offset;
633    iface->get_text_at_offset = eail_bubble_get_text_at_offset;
634    iface->get_text_before_offset = eail_bubble_get_text_before_offset;
635    iface->get_offset_at_point = eail_bubble_get_offset_at_point;
636 }