move elementary to trunk base. out of TMP/st.
[framework/uifw/elementary.git] / src / lib / elm_bubble.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Bubble Bubble
6  *
7  * The Bubble is an widget used to show a text in a frame as speech is
8  * represented in comics.
9  *
10  * Signals that you can add callbacks for are:
11  *
12  * clicked - This is called when a user has clicked the bubble.
13  */
14
15 typedef struct _Widget_Data Widget_Data;
16
17 struct _Widget_Data
18 {
19    Evas_Object *bbl;
20    Evas_Object *content, *icon;
21    const char *label, *info, *corner;
22 };
23
24 static const char *widtype = NULL;
25 static void _del_hook(Evas_Object *obj);
26 static void _theme_hook(Evas_Object *obj);
27 static void _sizing_eval(Evas_Object *obj);
28 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
29 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
30
31 #define SIG_CLICKED "clicked"
32 static const Evas_Smart_Cb_Description _signals[] =
33 {
34   {SIG_CLICKED, ""},
35   {NULL, NULL}
36 };
37
38 static void
39 _del_hook(Evas_Object *obj)
40 {
41    Widget_Data *wd = elm_widget_data_get(obj);
42    if (!wd) return;
43    if (wd->label) eina_stringshare_del(wd->label);
44    if (wd->info) eina_stringshare_del(wd->info);
45    if (wd->corner) eina_stringshare_del(wd->corner);
46    free(wd);
47 }
48
49 static void
50 _theme_hook(Evas_Object *obj)
51 {
52    Widget_Data *wd = elm_widget_data_get(obj);
53    if (!wd) return;
54    _elm_theme_object_set(obj, wd->bbl, "bubble", wd->corner,
55                          elm_widget_style_get(obj));
56    edje_object_part_text_set(wd->bbl, "elm.text", wd->label);
57    if (wd->label) edje_object_signal_emit(wd->bbl, "elm,state,text,visible", "elm");
58    else edje_object_signal_emit(wd->bbl, "elm,state,text,hidden", "elm");
59    edje_object_part_text_set(wd->bbl, "elm.info", wd->info);
60    if (wd->info) edje_object_signal_emit(wd->bbl, "elm,state,info,visible", "elm");
61    else edje_object_signal_emit(wd->bbl, "elm,state,info,hidden", "elm");
62    if (wd->content)
63      {
64         edje_object_part_swallow(wd->bbl, "elm.swallow.content", wd->content);
65         edje_object_message_signal_process(wd->bbl);
66      }
67    if (wd->icon)
68      edje_object_signal_emit(wd->bbl, "elm,state,icon,visible", "elm");
69    else
70      edje_object_signal_emit(wd->bbl, "elm,state,icon,hidden", "elm");
71    edje_object_scale_set(wd->bbl,
72                          elm_widget_scale_get(obj) * _elm_config->scale);
73    _sizing_eval(obj);
74 }
75
76 static Eina_Bool
77 _elm_bubble_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
78 {
79    Widget_Data *wd = elm_widget_data_get(obj);
80    Evas_Object *cur;
81
82    if ((!wd) || (!wd->content))
83      return EINA_FALSE;
84
85    cur = wd->content;
86
87    /* Try Focus cycle in subitem */
88    return elm_widget_focus_next_get(cur, dir, next);
89 }
90
91 static void
92 _sizing_eval(Evas_Object *obj)
93 {
94    Widget_Data *wd = elm_widget_data_get(obj);
95    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
96    if (!wd) return;
97    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
98    edje_object_size_min_restricted_calc(wd->bbl, &minw, &minh, minw, minh);
99    evas_object_size_hint_min_set(obj, minw, minh);
100    evas_object_size_hint_max_set(obj, maxw, maxh);
101 }
102
103 static void
104 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
105 {
106    Widget_Data *wd = elm_widget_data_get(data);
107    if (!wd) return;
108    _sizing_eval(data);
109 }
110
111 static void
112 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
113 {
114    Widget_Data *wd = elm_widget_data_get(obj);
115    Evas_Object *sub = event_info;
116    if (!wd) return;
117    evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
118                                   _changed_size_hints, obj);
119    if (sub == wd->content) wd->content = NULL;
120    else if (sub == wd->icon)
121      {
122         edje_object_signal_emit(wd->bbl, "elm,state,icon,hidden", "elm");
123         wd->icon = NULL;
124         edje_object_message_signal_process(wd->bbl);
125      }
126    _sizing_eval(obj);
127 }
128
129 static void
130 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
131 {
132    Evas_Event_Mouse_Up *ev = event_info;
133    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
134      return;
135    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
136 }
137
138 /**
139  * Add a new bubble to the parent
140  *
141  * @param parent The parent object
142  * @return The new object or NULL if it cannot be created
143  *
144  * This function adds a text bubble to the given parent evas object.
145  *
146  * @ingroup Bubble
147  */
148 EAPI Evas_Object *
149 elm_bubble_add(Evas_Object *parent)
150 {
151    Evas_Object *obj;
152    Evas *e;
153    Widget_Data *wd;
154
155    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
156
157    wd = ELM_NEW(Widget_Data);
158    e = evas_object_evas_get(parent);
159    if (!e) return NULL;
160    obj = elm_widget_add(e);
161    ELM_SET_WIDTYPE(widtype, "bubble");
162    elm_widget_type_set(obj, "bubble");
163    elm_widget_sub_object_add(parent, obj);
164    elm_widget_data_set(obj, wd);
165    elm_widget_del_hook_set(obj, _del_hook);
166    elm_widget_theme_hook_set(obj, _theme_hook);
167    elm_widget_focus_next_hook_set(obj, _elm_bubble_focus_next_hook);
168    elm_widget_can_focus_set(obj, EINA_FALSE);
169
170    wd->corner = eina_stringshare_add("base");
171
172    wd->bbl = edje_object_add(e);
173    _elm_theme_object_set(obj, wd->bbl, "bubble", "base", "default");
174    elm_widget_resize_object_set(obj, wd->bbl);
175
176    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
177    evas_object_event_callback_add(wd->bbl, EVAS_CALLBACK_MOUSE_UP,
178                                   _mouse_up, obj);
179
180    evas_object_smart_callbacks_descriptions_set(obj, _signals);
181    _sizing_eval(obj);
182    return obj;
183 }
184
185 /**
186  * Set the label of the bubble
187  *
188  * @param obj The bubble object
189  * @param label The string to set in the label
190  *
191  * This function sets the title of the bubble that is shown on top of
192  * the bubble.
193  *
194  * @ingroup Bubble
195  */
196 EAPI void
197 elm_bubble_label_set(Evas_Object *obj, const char *label)
198 {
199    ELM_CHECK_WIDTYPE(obj, widtype);
200    Widget_Data *wd = elm_widget_data_get(obj);
201    if (!wd) return;
202    eina_stringshare_replace(&wd->label, label);
203    edje_object_part_text_set(wd->bbl, "elm.text", label);
204    if (label) edje_object_signal_emit(wd->bbl, "elm,state,text,visible", "elm");
205    else edje_object_signal_emit(wd->bbl, "elm,state,text,hidden", "elm");
206    _sizing_eval(obj);
207 }
208
209 /**
210  * Get the label of the bubble
211  *
212  * @param obj The bubble object
213  * @return The string of set in the label
214  *
215  * This function gets the title of the bubble that is shown on top of
216  * the bubble.
217  *
218  * @ingroup Bubble
219  */
220 EAPI const char*
221 elm_bubble_label_get(const Evas_Object *obj)
222 {
223    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
224    Widget_Data *wd = elm_widget_data_get(obj);
225    if (!wd) return NULL;
226    return wd->label;
227 }
228
229 /**
230  * Set the info of the bubble
231  *
232  * @param obj The bubble object
233  * @param info The given info about the bubble
234  *
235  * This function sets the text shown on the top right of bubble.
236  * In the Anchorblock example of the Elementary tests application it
237  * shows time.
238  *
239  * @ingroup Bubble
240  *
241  */
242 EAPI void
243 elm_bubble_info_set(Evas_Object *obj, const char *info)
244 {
245    ELM_CHECK_WIDTYPE(obj, widtype);
246    Widget_Data *wd = elm_widget_data_get(obj);
247    if (!wd) return;
248    eina_stringshare_replace(&wd->info, info);
249    edje_object_part_text_set(wd->bbl, "elm.info", info);
250    if (info) edje_object_signal_emit(wd->bbl, "elm,state,info,visible", "elm");
251    else edje_object_signal_emit(wd->bbl, "elm,state,info,hidden", "elm");
252    _sizing_eval(obj);
253 }
254
255 /**
256  * Get the info of the bubble
257  *
258  * @param obj The bubble object
259  *
260  * @return The "info" string of the bubble
261  *
262  * This function gets the text set to be displayed at the top right of
263  * the bubble.
264  *
265  * @ingroup Bubble
266  *
267  */
268 EAPI const char *
269 elm_bubble_info_get(const Evas_Object *obj)
270 {
271    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
272    Widget_Data *wd = elm_widget_data_get(obj);
273    if (!wd) return NULL;
274    return wd->info;
275 }
276
277 /**
278  * Set the content to be shown in the bubble
279  *
280  * Once the content object is set, a previously set one will be deleted.
281  * If you want to keep the old content object, use the
282  * elm_bubble_content_unset() function.
283  *
284  * @param obj The bubble object
285  * @param content The given content of the bubble
286  *
287  * This function sets the content shown on the middle of the bubble.
288  * In the Anchorblock example of the Elementary tests application it
289  * shows time.
290  *
291  * @ingroup Bubble
292  */
293 EAPI void
294 elm_bubble_content_set(Evas_Object *obj, Evas_Object *content)
295 {
296    ELM_CHECK_WIDTYPE(obj, widtype);
297    Widget_Data *wd = elm_widget_data_get(obj);
298    if (!wd) return;
299    if (wd->content == content) return;
300    if (wd->content) evas_object_del(wd->content);
301    wd->content = content;
302    if (content)
303      {
304         elm_widget_sub_object_add(obj, content);
305         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
306                                        _changed_size_hints, obj);
307         edje_object_part_swallow(wd->bbl, "elm.swallow.content", content);
308      }
309    _sizing_eval(obj);
310 }
311
312 /**
313  * Get the content shown in the bubble
314  *
315  * Return the content object which is set for this widget.
316  *
317  * @param obj The bubble object
318  * @return The content that is being used
319  *
320  * @ingroup Bubble
321  */
322 EAPI Evas_Object *
323 elm_bubble_content_get(const Evas_Object *obj)
324 {
325    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
326    Widget_Data *wd = elm_widget_data_get(obj);
327    if (!wd) return NULL;
328    return wd->content;
329 }
330
331 /**
332  * Unset the content shown in the bubble
333  *
334  * Unparent and return the content object which was set for this widget.
335  *
336  * @param obj The bubble object
337  * @return The content that was being used
338  *
339  * @ingroup Bubble
340  */
341 EAPI Evas_Object *
342 elm_bubble_content_unset(Evas_Object *obj)
343 {
344    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
345    Widget_Data *wd = elm_widget_data_get(obj);
346    Evas_Object *content;
347    if (!wd) return NULL;
348    if (!wd->content) return NULL;
349    content = wd->content;
350    elm_widget_sub_object_del(obj, content);
351    edje_object_part_unswallow(wd->bbl, content);
352    wd->content = NULL;
353    return content;
354 }
355
356 /**
357  * Set the icon of the bubble
358  *
359  * Once the icon object is set, a previously set one will be deleted.
360  * If you want to keep the old content object, use the
361  * elm_icon_content_unset() function.
362  *
363  * @param obj The bubble object
364  * @param icon The given icon for the bubble
365  *
366  * @ingroup Bubble
367  */
368 EAPI void
369 elm_bubble_icon_set(Evas_Object *obj, Evas_Object *icon)
370 {
371    ELM_CHECK_WIDTYPE(obj, widtype);
372    Widget_Data *wd = elm_widget_data_get(obj);
373    if (!wd) return;
374    if (wd->icon == icon) return;
375    if (wd->icon) evas_object_del(wd->icon);
376    wd->icon = icon;
377    if (icon)
378      {
379         elm_widget_sub_object_add(obj, icon);
380         edje_object_part_swallow(wd->bbl, "elm.swallow.icon", icon);
381         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
382                                        _changed_size_hints, obj);
383         edje_object_signal_emit(wd->bbl, "elm,state,icon,visible", "elm");
384         edje_object_message_signal_process(wd->bbl);
385      }
386    _sizing_eval(obj);
387 }
388
389 /**
390  * Get the icon of the bubble
391  *
392  * @param obj The bubble object
393  * @return The icon for the bubble
394  *
395  * This function gets the icon shown on the top left of bubble.
396  *
397  * @ingroup Bubble
398  */
399 EAPI Evas_Object *
400 elm_bubble_icon_get(const Evas_Object *obj)
401 {
402    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
403    Widget_Data *wd = elm_widget_data_get(obj);
404    if (!wd) return NULL;
405    return wd->icon;
406 }
407
408 /**
409  * Unset the icon of the bubble
410  *
411  * Unparent and return the icon object which was set for this widget.
412  *
413  * @param obj The bubble object
414  * @return The icon that was being used
415  *
416  * @ingroup Bubble
417  */
418 EAPI Evas_Object *
419 elm_bubble_icon_unset(Evas_Object *obj)
420 {
421    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
422    Widget_Data *wd = elm_widget_data_get(obj);
423    Evas_Object *icon;
424    if (!wd) return NULL;
425    if (!wd->icon) return NULL;
426    icon = wd->icon;
427    elm_widget_sub_object_del(obj, icon);
428    edje_object_part_unswallow(wd->bbl, icon);
429    wd->icon = NULL;
430    return icon;
431 }
432
433 /**
434  * Set the corner of the bubble
435  *
436  * @param obj The bubble object.
437  * @param corner The given corner for the bubble.
438  *
439  * This function sets the corner of the bubble.
440  * The corner will be used to find the group in the theme
441  * For example, if you set the corner to "bottom_right",
442  * the following group will be searched:
443  * "elm/bubble/bottom_right/default",
444  * considering default style.
445  *
446  * @ingroup Bubble
447  */
448 EAPI void
449 elm_bubble_corner_set(Evas_Object *obj, const char *corner)
450 {
451    ELM_CHECK_WIDTYPE(obj, widtype);
452    Widget_Data *wd = elm_widget_data_get(obj);
453    if (!wd) return;
454    EINA_SAFETY_ON_NULL_RETURN(corner);
455    eina_stringshare_replace(&wd->corner, corner);
456    _theme_hook(obj);
457 }
458
459 /**
460  * Get the corner of the bubble
461  *
462  * @param obj The bubble object.
463  * @return The given corner for the bubble.
464  *
465  * This function gets the corner of the bubble.
466  *
467  * @ingroup Bubble
468  */
469 EAPI const char*
470 elm_bubble_corner_get(const Evas_Object *obj)
471 {
472    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
473    Widget_Data *wd = elm_widget_data_get(obj);
474    if (!wd) return NULL;
475    return wd->corner;
476 }