Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail_popup.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_popup.c
22  * @brief EailPopup implementation
23  */
24
25 #include <atk/atk.h>
26 #include <Elementary.h>
27
28 #include "eail_popup.h"
29 #include "eail_notify.h"
30 #include "eail_factory.h"
31 #include "eail_utils.h"
32 #include "eail_priv.h"
33 #include "eail_text.h"
34
35 static void atk_text_interface_init(AtkTextIface *iface);
36
37 /**
38  * @brief Definition of EailPopup as GObject
39  *
40  * EailPopup is extended EAIL_TYPE_NOTIFY with ATK_TYPE_TEXT interface
41  * implemented
42  */
43 G_DEFINE_TYPE_WITH_CODE(EailPopup, eail_popup, EAIL_TYPE_NOTIFY,
44                         G_IMPLEMENT_INTERFACE(ATK_TYPE_TEXT,
45                                              atk_text_interface_init));
46
47 /**
48  * @brief Part format
49  */
50 #define EAIL_POPUP_BUTTON_FORMAT "button%d"
51 /**
52  * @brief Number of popup buttons
53  */
54 #define EAIL_POPUP_NUM_BUTTONS 3
55 /**
56  * @brief Part name buffer size
57  */
58 #define EAIL_POPUP_CHAR_BUF_SIZE 30
59
60 /**
61  * @brief Initializer for AtkObject
62  *
63  * @param obj AtkObject instance
64  * @param data initialization data
65  */
66 static void
67 eail_popup_initialize(AtkObject *obj, gpointer data)
68 {
69    ATK_OBJECT_CLASS(eail_popup_parent_class) ->initialize(obj, data);
70    obj->role = ATK_ROLE_POPUP_MENU;
71 }
72
73 /**
74  * @brief EailPopup GObject instance initializer
75  *
76  * @param button EailPopup instance
77  */
78 static void
79 eail_popup_init(EailPopup *button)
80 {
81 }
82
83 /**
84  * @brief Helper function for getting elm_popup Evas_Object* from Atk EailPopup
85  *
86  * @param obj AtkObject instance
87  *
88  * @returns Evas_Object representing the nested elm_popup widget
89  */
90 static Evas_Object *
91 _eail_get_popup_widget_from_atkobj(AtkObject *obj)
92 {
93    Evas_Object *popup_widget = NULL;
94    popup_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
95    if (!popup_widget)
96      {
97         ERR("No widget found for notification object!");
98         return NULL;
99      }
100
101    return popup_widget;
102 }
103
104 /**
105  * @brief Helper func to get Textblock form popup widget
106  *
107  * @param text AtkText instance
108  * @returns Textblock part of popup widget
109  */
110 static const Evas_Object *
111 _eail_get_textblock(AtkText *text)
112 {
113    Evas_Object *widget;
114    const Evas_Object *textblock;
115    Evas_Object *label = NULL;
116    Evas_Object *layout = NULL;
117    Evas_Object *layout_edje_layer = NULL;
118    Evas_Object *label_edje_layer = NULL;
119    Evas_Object *popup_edje_layer = NULL;
120
121    widget = eail_widget_get_widget(EAIL_WIDGET(text));
122    if (!widget) return NULL;
123
124    popup_edje_layer = elm_layout_edje_get(widget);
125    if (!popup_edje_layer) return NULL;
126
127    layout = edje_object_part_swallow_get(popup_edje_layer, "elm.swallow.content");
128    if (!layout) return NULL;
129
130    layout_edje_layer = elm_layout_edje_get(layout);
131    if (!layout_edje_layer) return NULL;
132
133    label = edje_object_part_swallow_get(layout_edje_layer, "elm.swallow.content");
134    if (!label) return NULL;
135
136    label_edje_layer = elm_layout_edje_get(label);
137    if (!label_edje_layer) return NULL;
138
139    textblock = edje_object_part_object_get(label_edje_layer, "elm.text");
140    if (!textblock) return NULL;
141
142    return textblock;
143
144 }
145
146 /**
147  * @brief Helper function for getting nested content in elm_popup widget
148  *
149  * @param obj AtkObject instance
150  *
151  * @returns Evas_Object representing the nested widget content from elm_popup widget
152  */
153 static Evas_Object *
154 _eail_get_nested_widget(AtkObject *obj)
155 {
156    Evas_Object *popup_widget = NULL, *nested_widget = NULL;
157
158    /* getting widget of popup class */
159    popup_widget = _eail_get_popup_widget_from_atkobj(obj);
160    if (!popup_widget) return NULL;
161
162    nested_widget = elm_object_part_content_get(popup_widget, "default");
163
164    return nested_widget;
165 }
166
167 /**
168  * @brief Helper function for getting nested button content in elm_popup widget
169  *
170  * @param obj AtkObject instance
171  * @param index index of button in popup widget
172  *
173  * @returns Evas_Object representing the nested button widget
174  * from elm_popup widget or NULL in case of a failure
175  */
176 static Evas_Object *
177 _eail_get_nested_popup_button(AtkObject *obj, gint index)
178 {
179    gchar buf[EAIL_POPUP_CHAR_BUF_SIZE];
180    Evas_Object *ret_button = NULL, *popup_widget = NULL;
181
182    if (index >= EAIL_POPUP_NUM_BUTTONS)
183      {
184         ERR("Index of popup button cannot be >= %d", EAIL_POPUP_NUM_BUTTONS);
185         return NULL;
186      }
187
188    popup_widget = _eail_get_popup_widget_from_atkobj(obj);
189    if (!popup_widget) return NULL;
190
191    g_snprintf(buf, sizeof(buf), EAIL_POPUP_BUTTON_FORMAT, (index + 1));
192    ret_button = elm_object_part_content_get(popup_widget, buf);
193
194    return ret_button;
195 }
196
197 /**
198  * @brief Prepares Eina_List filled with Evas_Object * objects
199  * representing nested content in elm_popup widget
200  *
201  * Call eina_list_free on the returned list when results processing has been finished.
202  *
203  * @param obj AtkObject instance
204  *
205  * @return Eina_List representing the list of Evas_Object* objects
206  */
207 static Eina_List *
208 _eail_popup_get_items(AtkObject *obj)
209 {
210    Eina_List *items = NULL;
211    Evas_Object *widget = NULL;
212    gint i = 0;
213
214    /* nested widget */
215    widget = _eail_get_nested_widget(obj);
216    if (widget)
217      items = eina_list_append(items, widget);
218
219    /* action buttons below popup content */
220    for (i = 0; i < EAIL_POPUP_NUM_BUTTONS; ++i)
221      {
222         widget = _eail_get_nested_popup_button(obj, i);
223         if (widget)
224           items = eina_list_append(items, widget);
225      }
226
227    return items;
228 }
229
230 /**
231  * @brief Gets the number of accessible children of the accessible
232  *
233  * Implementation of AtkObject->get_n_children callback.
234  *
235  * @param obj AtkObject instance
236  *
237  * @returns integer representing the number of accessible children of
238  * the accessible
239  */
240 static gint
241 eail_popup_get_n_children(AtkObject *obj)
242 {
243    Eina_List *items = NULL;
244    gint num_of_children = 0;
245
246    items = _eail_popup_get_items(obj);
247    num_of_children = eina_list_count(items);
248
249    return num_of_children;
250 }
251
252 /**
253  * @brief Gets a reference to the specified accessible child of the object.
254  *
255  * The accessible children are 0-based so the first accessible child is at index 0,
256  * the second at index 1 and so on.
257  *
258  * Implementation of AtkObject->ref_child callback.
259  *
260  * @param obj AtkObject instance
261  * @param i index of a child
262  *
263  * @returns AtkObject representing the specified accessible child of the
264  * accessible
265  */
266 static AtkObject *
267 eail_popup_ref_child(AtkObject *obj, gint i)
268 {
269    Eina_List *items = NULL;
270    AtkObject *atk_obj = NULL;
271
272    items = _eail_popup_get_items(obj);
273    if (eina_list_count(items) > i)
274      atk_obj = eail_factory_get_accessible(eina_list_nth(items, i));
275
276    if (atk_obj)
277      g_object_ref(atk_obj);
278
279    return atk_obj;
280 }
281
282 /**
283  * @brief Gets the accessible name of EailItem
284  *
285  * Implementation of AtkObject->get_name callback.
286  *
287  * @param obj AtkObject instance
288  *
289  * @returns character string representing the accessible description of
290  * the accessible
291  */
292 static const gchar *
293 eail_popup_get_name(AtkObject *obj)
294 {
295    Evas_Object *popup_widget = NULL;
296    const char *atk_name;
297
298    atk_name = ATK_OBJECT_CLASS(eail_popup_parent_class)->get_name(obj);
299    if (atk_name) return atk_name;
300
301    popup_widget = eail_widget_get_widget(EAIL_WIDGET(obj));
302    if (!popup_widget)
303      {
304         ERR("No widget found for notification object!");
305         return NULL;
306      }
307
308    return elm_object_part_text_get(popup_widget, "title,text");
309 }
310
311 /**
312  * @brief Destructor for EailPopup class
313  * @param object GObject instance
314  */
315 static void
316 eail_popup_finalize(GObject *object)
317 {
318    G_OBJECT_CLASS(eail_popup_parent_class)->finalize(object);
319 }
320
321 /**
322  * @brief Initializer for EailPopup GObject class
323  *
324  * Defines callbacks for base AtkObject.
325  *
326  * @param klass EailPopupClass instance
327  */
328 static void
329 eail_popup_class_init(EailPopupClass *klass)
330 {
331    AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
332    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
333    class->initialize = eail_popup_initialize;
334    class->get_n_children = eail_popup_get_n_children;
335    class->ref_child = eail_popup_ref_child;
336    class->get_name = eail_popup_get_name;
337    gobject_class->finalize = eail_popup_finalize;
338 }
339
340 /*
341  * AtkText interface functions
342  */
343
344 /**
345  * @brief Helper function that gets text from popup as Eina_Strbuf*
346  *
347  * @param popup AtkText instance
348  *
349  * @returns Eina_Strbuf representing the text content of the popup
350  */
351 static Eina_Strbuf *
352 _eail_popup_get_strbuf(AtkText *popup)
353 {
354    Evas_Object *popup_widget = NULL;
355    Eina_Strbuf* str_buf = NULL;
356    const gchar *text_part = NULL;
357
358    g_return_val_if_fail (ATK_IS_OBJECT(popup), NULL);
359
360    /* getting widget of popup class */
361    popup_widget = _eail_get_popup_widget_from_atkobj(ATK_OBJECT(popup));
362    if (!popup_widget) return NULL;
363
364    text_part = elm_object_part_text_get(popup_widget, "default");
365    if (!text_part) return NULL;
366
367    str_buf = eina_strbuf_new();
368    eina_strbuf_append(str_buf, text_part);
369
370    return str_buf;
371 }
372
373 /**
374  * @brief Gets the text content from popup
375  *
376  * Use g_free() to free the returned string.
377  *
378  * Implementation of AtkTextIface->get_text callback.
379  *
380  * @param popup AtkText instance
381  * @param start_offset start position
382  * @param end_offset end position, or -1 for the end of the string.
383  *
384  * @returns newly allocated string containing the text from start_offset
385  * up to, but not including end_offset
386  */
387 static gchar*
388 eail_popup_get_text(AtkText          *popup,
389                     gint              start_offset,
390                     gint              end_offset)
391 {
392    Eina_Strbuf* str_buf = NULL;
393    gchar *ret_str = NULL;
394
395    g_return_val_if_fail(EAIL_IS_POPUP(popup), NULL);
396
397    str_buf = _eail_popup_get_strbuf(popup);
398    if (!str_buf) return NULL;
399
400    ret_str = eail_get_substring
401                (eina_strbuf_string_get(str_buf), start_offset, end_offset);
402
403    eina_strbuf_free(str_buf);
404
405    return ret_str;
406 }
407
408 /**
409  * @brief Gets the character from popup at given offset
410  *
411  * Implementation of AtkTextIface->get_character_at_offset callback,
412  *
413  * @param popup AtkText instance
414  * @param offset offset
415  *
416  * @returns char representing the character at offset
417  */
418 static gunichar
419 eail_popup_get_character_at_offset(AtkText *popup, gint offset)
420 {
421    gunichar character = '\0';
422    gchar* time_str = NULL;
423
424    g_return_val_if_fail(EAIL_IS_POPUP(popup), character);
425
426    time_str = eail_popup_get_text(popup, 0, -1);
427
428    if (time_str)
429      {
430          character = g_utf8_get_char
431                   (g_utf8_offset_to_pointer(time_str, offset));
432          g_free(time_str);
433      }
434
435    return character;
436 }
437
438 /**
439  * @brief Gets the character count from text content in popup
440  *
441  * Implementation of AtkTextIface->get_character_count callback.
442  *
443  * @param popup AtkText instance
444  *
445  * @returns char representing the the character count
446  */
447 static gint
448 eail_popup_get_character_count(AtkText *popup)
449 {
450    gint count = 0;
451    gchar* time_str = NULL;
452
453    g_return_val_if_fail(EAIL_IS_POPUP(popup), 0);
454
455    time_str = eail_popup_get_text(popup, 0, -1);
456
457    if (time_str)
458      {
459          count = g_utf8_strlen(time_str, -1);
460          g_free(time_str);
461      }
462
463    return count;
464 }
465
466 /**
467  * @brief Get the bounding box containing the glyph
468  *  representing the character at a particular text offset.
469  *
470  * @param text AtkText instance
471  * @param offset The offset of the text character for which
472  * bounding information is required.
473  * @param x Pointer for the x cordinate of the bounding box
474  * @param y Pointer for the y cordinate of the bounding box
475  * @param width Pointer for the width of the bounding box
476  * @param height Pointer for the height of the bounding box
477  * @param coords specify whether coordinates are relative to the
478  * screen or widget window
479  *
480  */
481 static void
482 eail_popup_get_character_extents(AtkText *text,
483                                  gint offset,
484                                  gint *x,
485                                  gint *y,
486                                  gint *width,
487                                  gint *height,
488                                  AtkCoordType coords)
489 {
490    int result = -1;
491    Evas_Textblock_Cursor *cur = NULL;
492    Evas_Object *widget;
493    const Evas_Object *textblock;
494
495    widget = eail_widget_get_widget(EAIL_WIDGET(text));
496    if (!widget) return;
497
498    textblock = _eail_get_textblock(text);
499    if (!textblock) return;
500
501    cur = evas_object_textblock_cursor_new(textblock);
502    if (!cur) return;
503
504    evas_textblock_cursor_pos_set(cur, offset);
505
506    result = evas_textblock_cursor_char_geometry_get(cur, x, y, width, height);
507
508    evas_textblock_cursor_free(cur);
509
510    if (-1 == result) return;
511
512    if (coords == ATK_XY_SCREEN)
513    {
514       int ee_x, ee_y;
515       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
516
517       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
518       *x += ee_x;
519       *y += ee_y;
520     }
521 }
522
523 /*
524  * @brief Creates an AtkAttributeSet which consists of the attributes
525  * explicitly set at the position offset in the text.
526  *
527  * start_offset and end_offset are set to the start and end of the range around offset
528  * where the attributes are invariant.
529  *
530  * Note that end_offset is the offset of the first character after the range.
531  *
532  * This AtkAttributeSet should be freed by a call to
533  * atk_attribute_set_free()
534  *
535  * @param text AtkText instance
536  * @param offset the offset at which to get the attributes
537  * @param [out] start_offset start offset of the range
538  * @param [out] end_offset end offset of the range
539  *
540  * @returns an AtkAttributeSet which contains the attributes explicitly set at
541  * offset.
542  */
543 static AtkAttributeSet *
544 eail_popup_get_run_attributes(AtkText *text,
545                               gint offset,
546                               gint *start_offset,
547                               gint *end_offset)
548 {
549    AtkAttributeSet *at_set = NULL;
550    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
551    gint len = eail_popup_get_character_count(ATK_TEXT(text));
552
553    if (!widget || offset >= len)
554      {
555         *start_offset = -1;
556         *end_offset = -1;
557
558         return NULL;
559      }
560
561    *start_offset = 0;
562    *end_offset = len;
563
564    /* NOTE: Elm_Wrap_Type value is in 100% compatible with ATK wrap modes, so
565     * no additional conversion is needed*/
566    Elm_Wrap_Type wrap_type = elm_popup_content_text_wrap_type_get(widget);
567    at_set = eail_utils_text_add_attribute
568        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
569         atk_text_attribute_get_value
570          (ATK_TEXT_ATTR_WRAP_MODE, wrap_type));
571
572    at_set = eail_utils_text_add_attribute
573        (at_set, ATK_TEXT_ATTR_EDITABLE,
574         atk_text_attribute_get_value
575          (ATK_TEXT_ATTR_EDITABLE, FALSE));
576
577    return at_set;
578 }
579
580 /**
581  * @brief Creates an AtkAttributeSet which consists of the default values of
582  * attributes for the text.
583  *
584  * This AtkAttributeSet should be freed by a call to
585  * atk_attribute_set_free()
586  *
587  * @param text AtkText instance
588  *
589  * @returns AtkAttributeSet containing default values of attributes
590  * at offset.
591  */
592 static AtkAttributeSet *
593 eail_popup_get_default_attributes(AtkText *text)
594 {
595    AtkAttributeSet *at_set = NULL;
596
597    at_set = eail_utils_text_add_attribute
598        (at_set, ATK_TEXT_ATTR_WRAP_MODE,
599         atk_text_attribute_get_value(ATK_TEXT_ATTR_WRAP_MODE, 0));
600
601    at_set = eail_utils_text_add_attribute
602        (at_set, ATK_TEXT_ATTR_EDITABLE,
603         atk_text_attribute_get_value
604          (ATK_TEXT_ATTR_EDITABLE, FALSE));
605
606    return at_set;
607 }
608
609 /**
610  * @brief Gets the specified text after offset
611  *
612  * Use g_free() to free the returned string.
613  *
614  * @param text AtkText instance
615  * @param offset character offset
616  * @param boundary_type AtkTextBoundary instance
617  * @param [out] start_offset start offset of the returned string
618  * @param [out] end_offset offset of the first character after the returned
619  * substring
620  * @returns newly allocated string containing the text after offset bounded
621  * by the specified boundary_type
622  */
623 static gchar *
624 eail_popup_get_text_after_offset(AtkText *text,
625                                  gint offset,
626                                  AtkTextBoundary boundary_type,
627                                  gint *start_offset,
628                                  gint *end_offset)
629 {
630    const Evas_Object *textblock;
631
632    textblock = _eail_get_textblock(text);
633    if (!textblock) return NULL;
634
635    return eail_get_text_after(textblock, offset, boundary_type, start_offset,
636                               end_offset);
637
638 }
639
640 /**
641  * @brief Gets the specified text at offset
642  *
643  * Use g_free() to free the returned string.
644  *
645  * @param text AtkText instance
646  * @param offset character offset
647  * @param boundary_type AtkTextBoundary instance
648  * @param [out] start_offset start offset of the returned string
649  * @param [out] end_offset offset of the first character after the returned
650  * substring
651  * @returns newly allocated string containing the text after offset bounded
652  * by the specified boundary_type
653  */
654 static gchar *
655 eail_popup_get_text_at_offset(AtkText *text,
656                               gint offset,
657                               AtkTextBoundary boundary_type,
658                               gint *start_offset,
659                               gint *end_offset)
660 {
661    const Evas_Object *textblock;
662
663    textblock = _eail_get_textblock(text);
664    if (!textblock) return NULL;
665
666    return eail_get_text_at(textblock, offset, boundary_type, start_offset,
667                            end_offset);
668 }
669
670 /**
671  * @brief Gets the specified text before offset
672  *
673  * Use g_free() to free the returned string.
674  *
675  * @param text AtkText instance
676  * @param offset character offset
677  * @param boundary_type AtkTextBoundary instance
678  * @param [out] start_offset start offset of the returned string
679  * @param [out] end_offset offset of the first character after the returned
680  * substring
681  * @returns newly allocated string containing the text after offset bounded
682  * by the specified boundary_type
683  */
684 static gchar *
685 eail_popup_get_text_before_offset(AtkText *text,
686                                   gint offset,
687                                   AtkTextBoundary boundary_type,
688                                   gint *start_offset,
689                                   gint *end_offset)
690 {
691    const Evas_Object *textblock;
692
693    textblock = _eail_get_textblock(text);
694    if (!textblock) return NULL;
695
696    return eail_get_text_before(textblock, offset, boundary_type, start_offset,
697                                end_offset);
698 }
699
700 /**
701  * @brief Gets the offset of the character located at coordinates
702  * x and y. x and y are interpreted as being relative to the
703  * screen or this widget's window depending on coords.
704  *
705  * @param text AtkText instance
706  * @param x screen x-position of character
707  * @param y screen y-position of character
708  * @param coords specify whether coordinates are relative to the
709  * screen or widget window
710  *
711  * @returns the offset to the character which is located at the
712  * specified x and y coordinates.
713  */
714 static gint
715 eail_popup_get_offset_at_point(AtkText *text,
716                                gint x,
717                                gint y,
718                                AtkCoordType coords)
719 {
720    const Evas_Object *textblock;
721
722    Eina_Bool result = EINA_FALSE;
723    Evas_Textblock_Cursor *cur = NULL;
724    gint offset = -1;
725    Evas_Object *widget = eail_widget_get_widget(EAIL_WIDGET(text));
726
727    if (!widget) return offset;
728
729    textblock = _eail_get_textblock(text);
730    if (!textblock) return offset;
731
732    cur = evas_object_textblock_cursor_new(textblock);
733    if (!cur) return offset;
734
735    if (coords == ATK_XY_SCREEN)
736    {
737       int ee_x, ee_y;
738       Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(widget));
739
740       ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
741       x -= ee_x;
742       y -= ee_y;
743     }
744
745    result = evas_textblock_cursor_char_coord_set(cur, x, y);
746
747    if (result == EINA_FALSE)
748    {
749       evas_textblock_cursor_free(cur);
750       return offset;
751    }
752
753    offset = evas_textblock_cursor_pos_get(cur);
754    evas_textblock_cursor_free(cur);
755
756    return offset;
757 }
758
759 /**
760  * @brief Initializer for AtkTextIface interface class
761  *
762  * Defines callbacks for AtkTextIface.
763  *
764  * @param iface AtkTextIface instance
765  */
766 static void
767 atk_text_interface_init(AtkTextIface *iface)
768 {
769     iface->get_text = eail_popup_get_text;
770     iface->get_character_at_offset = eail_popup_get_character_at_offset;
771     iface->get_character_count = eail_popup_get_character_count;
772     iface->get_character_extents = eail_popup_get_character_extents;
773     iface->get_run_attributes = eail_popup_get_run_attributes;
774     iface->get_default_attributes = eail_popup_get_default_attributes;
775     iface->get_text_after_offset = eail_popup_get_text_after_offset;
776     iface->get_text_at_offset = eail_popup_get_text_at_offset;
777     iface->get_text_before_offset = eail_popup_get_text_before_offset;
778     iface->get_offset_at_point = eail_popup_get_offset_at_point;
779 }