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