elm focus: Added elm_object_focus_set() API. Use this instead elm_object_focus/unfocus().
[framework/uifw/elementary.git] / src / lib / elm_notify.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *notify, *content, *parent;
9
10    Elm_Notify_Orient orient;
11    Eina_Bool repeat_events;
12    Evas_Object *block_events;
13
14    double timeout;
15    Ecore_Timer *timer;
16 };
17
18 static const char *widtype = NULL;
19 static void _del_hook(Evas_Object *obj);
20 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
21 static void _theme_hook(Evas_Object *obj);
22 static void _sizing_eval(Evas_Object *obj);
23 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
24 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
25 static void _signal_block_clicked(void *data, Evas_Object *obj, const char *emission, const char *source);
26 static void _calc(Evas_Object *obj);
27 static void _content_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
28 static void _show(void *data, Evas *e, Evas_Object *obj, void *event_info);
29 static void _hide(void *data, Evas *e, Evas_Object *obj, void *event_info);
30 static void _parent_del(void *data,  Evas *e, Evas_Object *obj, void *event_info);
31 static void _parent_hide(void *data,  Evas *e, Evas_Object *obj, void *event_info);
32
33 static void _resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static void _restack(void *data, Evas *e, Evas_Object *obj, void *event_info);
35
36 static const char SIG_BLOCK_CLICKED[] = "block,clicked";
37 static const char SIG_TIMEOUT[] = "timeout";
38 static const Evas_Smart_Cb_Description _signals[] = {
39        {SIG_BLOCK_CLICKED, ""},
40        {SIG_TIMEOUT, ""},
41        {NULL, NULL}
42 };
43
44 static void
45 _del_pre_hook(Evas_Object *obj)
46 {
47    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
48    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOVE, _resize, obj);
49    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_SHOW, _show, obj);
50    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_HIDE, _hide, obj);
51    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESTACK, _restack, obj);
52 }
53
54 static void
55 _del_hook(Evas_Object *obj)
56 {
57    Widget_Data *wd = elm_widget_data_get(obj);
58    if (!wd) return;
59    elm_notify_parent_set(obj, NULL);
60    elm_notify_repeat_events_set(obj, EINA_TRUE);
61    if (wd->timer)
62      {
63         ecore_timer_del(wd->timer);
64         wd->timer = NULL;
65      }
66    free(wd);
67 }
68
69 /**
70  * Return Notification orientation with RTL
71  *
72  * This function switches-sides of notification area when in RTL mode.
73  *
74  * @param obj notification object.
75  *
76  * @param orient Original notification orientation.
77  *
78  * @return notification orientation with respect to the object RTL mode.
79  *
80  * @internal
81  **/
82 static Elm_Notify_Orient
83 _notify_orientation_with_rtl(Evas_Object *obj, Elm_Notify_Orient orient)
84 {
85    if (elm_widget_mirrored_get(obj))
86      {
87         switch (orient)
88           {
89            case ELM_NOTIFY_ORIENT_LEFT:
90               orient = ELM_NOTIFY_ORIENT_RIGHT;
91               break;
92            case ELM_NOTIFY_ORIENT_RIGHT:
93               orient = ELM_NOTIFY_ORIENT_LEFT;
94               break;
95            case ELM_NOTIFY_ORIENT_TOP_LEFT:
96               orient = ELM_NOTIFY_ORIENT_TOP_RIGHT;
97               break;
98            case ELM_NOTIFY_ORIENT_TOP_RIGHT:
99               orient = ELM_NOTIFY_ORIENT_TOP_LEFT;
100               break;
101            case ELM_NOTIFY_ORIENT_BOTTOM_LEFT:
102               orient = ELM_NOTIFY_ORIENT_BOTTOM_RIGHT;
103               break;
104            case ELM_NOTIFY_ORIENT_BOTTOM_RIGHT:
105               orient = ELM_NOTIFY_ORIENT_BOTTOM_LEFT;
106               break;
107            default:
108               break;
109           }
110      }
111
112    return orient;
113 }
114
115 static void
116 _notify_theme_apply(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    const char *style = elm_widget_style_get(obj);
120
121    switch (wd->orient)
122      {
123       case ELM_NOTIFY_ORIENT_TOP:
124          _elm_theme_object_set(obj, wd->notify, "notify", "top", style);
125          break;
126       case ELM_NOTIFY_ORIENT_CENTER:
127          _elm_theme_object_set(obj, wd->notify, "notify", "center", style);
128          break;
129       case ELM_NOTIFY_ORIENT_BOTTOM:
130          _elm_theme_object_set(obj, wd->notify, "notify", "bottom", style);
131          break;
132       case ELM_NOTIFY_ORIENT_LEFT:
133          _elm_theme_object_set(obj, wd->notify, "notify", "left", style);
134          break;
135       case ELM_NOTIFY_ORIENT_RIGHT:
136          _elm_theme_object_set(obj, wd->notify, "notify", "right", style);
137          break;
138       case ELM_NOTIFY_ORIENT_TOP_LEFT:
139          _elm_theme_object_set(obj, wd->notify, "notify", "top_left", style);
140          break;
141       case ELM_NOTIFY_ORIENT_TOP_RIGHT:
142          _elm_theme_object_set(obj, wd->notify, "notify", "top_right", style);
143          break;
144       case ELM_NOTIFY_ORIENT_BOTTOM_LEFT:
145          _elm_theme_object_set(obj, wd->notify, "notify", "bottom_left", style);
146          break;
147       case ELM_NOTIFY_ORIENT_BOTTOM_RIGHT:
148          _elm_theme_object_set(obj, wd->notify, "notify", "bottom_right", style);
149          break;
150       case ELM_NOTIFY_ORIENT_LAST:
151          break;
152      }
153 }
154
155 /**
156  * Moves notification to orientation.
157  *
158  * This fucntion moves notification to orientation
159  * according to object RTL orientation.
160  *
161  * @param obj notification object.
162  *
163  * @param orient notification orientation.
164  *
165  * @internal
166  **/
167 static void
168 _notify_move_to_orientation(Evas_Object *obj)
169 {
170    Widget_Data *wd = elm_widget_data_get(obj);
171    int offx;
172    int offy;
173    Evas_Coord minw = -1, minh = -1;
174    Evas_Coord x, y, w, h;
175
176    if (!wd) return;
177    evas_object_geometry_get(obj, &x, &y, &w, &h);
178    edje_object_size_min_get(wd->notify, &minw, &minh);
179    edje_object_size_min_restricted_calc(wd->notify, &minw, &minh, minw, minh);
180    offx = (w - minw) / 2;
181    offy = (h - minh) / 2;
182
183    switch (_notify_orientation_with_rtl(obj, wd->orient))
184      {
185       case ELM_NOTIFY_ORIENT_TOP:
186          evas_object_move(wd->notify, x + offx, y);
187          break;
188       case ELM_NOTIFY_ORIENT_CENTER:
189          evas_object_move(wd->notify, x + offx, y + offy);
190          break;
191       case ELM_NOTIFY_ORIENT_BOTTOM:
192          evas_object_move(wd->notify, x + offx, y + h - minh);
193          break;
194       case ELM_NOTIFY_ORIENT_LEFT:
195          evas_object_move(wd->notify, x, y + offy);
196          break;
197       case ELM_NOTIFY_ORIENT_RIGHT:
198          evas_object_move(wd->notify, x + w - minw, y + offy);
199          break;
200       case ELM_NOTIFY_ORIENT_TOP_LEFT:
201          evas_object_move(wd->notify, x, y);
202          break;
203       case ELM_NOTIFY_ORIENT_TOP_RIGHT:
204          evas_object_move(wd->notify, x + w - minw, y);
205          break;
206       case ELM_NOTIFY_ORIENT_BOTTOM_LEFT:
207          evas_object_move(wd->notify, x, y + h - minh);
208          break;
209       case ELM_NOTIFY_ORIENT_BOTTOM_RIGHT:
210          evas_object_move(wd->notify, x + w - minw, y + h - minh);
211          break;
212       case ELM_NOTIFY_ORIENT_LAST:
213          break;
214      }
215 }
216
217 static void
218 _block_events_theme_apply(Evas_Object *obj)
219 {
220    Widget_Data *wd = elm_widget_data_get(obj);
221    const char *style = elm_widget_style_get(obj);
222    _elm_theme_object_set(obj, wd->block_events, "notify", "block_events", style);
223 }
224
225 static void
226 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
227 {
228    Widget_Data *wd = elm_widget_data_get(obj);
229    if (!wd) return;
230    edje_object_mirrored_set(wd->notify, rtl);
231    _notify_move_to_orientation(obj);
232 }
233
234 static void
235 _theme_hook(Evas_Object *obj)
236 {
237    Widget_Data *wd = elm_widget_data_get(obj);
238    if (!wd) return;
239    _elm_widget_mirrored_reload(obj);
240    _mirrored_set(obj, elm_widget_mirrored_get(obj));
241    _notify_theme_apply(obj);
242    if (wd->block_events) _block_events_theme_apply(obj);
243    edje_object_scale_set(wd->notify, elm_widget_scale_get(obj) *
244                          _elm_config->scale);
245    _sizing_eval(obj);
246 }
247
248 static void
249 _sizing_eval(Evas_Object *obj)
250 {
251    Widget_Data *wd = elm_widget_data_get(obj);
252    Evas_Coord x,y,w,h;
253    if (!wd) return;
254    if (!wd->parent) return;
255    evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
256    evas_object_move(obj, x, y);
257    evas_object_resize(obj, w, h);
258 }
259
260 static void
261 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
262 {
263    _calc(data);
264 }
265
266 static void
267 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
268 {
269    Widget_Data *wd = elm_widget_data_get(obj);
270    Evas_Object *sub = event_info;
271    if (!wd) return;
272
273    if (sub == wd->content)
274      {
275         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
276                                             _changed_size_hints, obj);
277         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_RESIZE,
278                                             _content_resize, obj);
279         wd->content = NULL;
280      }
281 }
282
283 static void
284 _signal_block_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
285 {
286    Widget_Data *wd = elm_widget_data_get(data);
287    if (!wd) return;
288    evas_object_smart_callback_call(data, SIG_BLOCK_CLICKED, NULL);
289 }
290
291 static void
292 _restack(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
293 {
294    Widget_Data *wd = elm_widget_data_get(obj);
295    if (!wd) return;
296    evas_object_layer_set(wd->notify,
297                          evas_object_layer_get(obj));
298 }
299
300 static void
301 _resize(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
302 {
303    _calc(obj);
304 }
305
306 static void
307 _content_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
308 {
309    _calc(data);
310 }
311
312 static void
313 _calc(Evas_Object *obj)
314 {
315    Widget_Data *wd = elm_widget_data_get(obj);
316    Evas_Coord minw = -1, minh = -1;
317    Evas_Coord x, y, w, h;
318
319    if (!wd) return;
320    _sizing_eval(obj);
321
322    evas_object_geometry_get(obj, &x, &y, &w, &h);
323    edje_object_size_min_get(wd->notify, &minw, &minh);
324    edje_object_size_min_restricted_calc(wd->notify, &minw, &minh, minw, minh);
325
326    if (wd->content)
327      {
328         _notify_move_to_orientation(obj);
329         evas_object_resize(wd->notify, minw, minh);
330      }
331 }
332
333 static Eina_Bool
334 _timer_cb(void *data)
335 {
336    Evas_Object *obj = data;
337    Widget_Data *wd = elm_widget_data_get(obj);
338    if (!wd) return ECORE_CALLBACK_CANCEL;
339    wd->timer = NULL;
340    evas_object_hide(obj);
341    evas_object_smart_callback_call(obj, SIG_TIMEOUT, NULL);
342    return ECORE_CALLBACK_CANCEL;
343 }
344
345 static void
346 _timer_init(Evas_Object *obj, Widget_Data *wd)
347 {
348    if (wd->timer)
349      {
350         ecore_timer_del(wd->timer);
351         wd->timer = NULL;
352      }
353    if ((evas_object_visible_get(obj)) && (wd->timeout > 0.0))
354      wd->timer = ecore_timer_add(wd->timeout, _timer_cb, obj);
355 }
356
357 static void
358 _show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
359 {
360    Widget_Data *wd = elm_widget_data_get(obj);
361    if (!wd) return;
362    evas_object_show(wd->notify);
363    if (!wd->repeat_events)
364      evas_object_show(wd->block_events);
365    _timer_init(obj, wd);
366    elm_object_focus_set(obj, EINA_TRUE);
367 }
368
369 static void
370 _hide(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
371 {
372    Widget_Data *wd = elm_widget_data_get(obj);
373    if (!wd) return;
374    evas_object_hide(wd->notify);
375    if (!wd->repeat_events)
376      evas_object_hide(wd->block_events);
377    if (wd->timer)
378      {
379         ecore_timer_del(wd->timer);
380         wd->timer = NULL;
381      }
382 }
383
384 static void
385 _parent_del(void *data,  Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
386 {
387    Widget_Data *wd = elm_widget_data_get(data);
388    if (!wd) return;
389    wd->parent = NULL;
390    evas_object_hide(data);
391 }
392
393 static void
394 _parent_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
395 {
396    Widget_Data *wd = elm_widget_data_get(data);
397    if (!wd) return;
398    evas_object_hide(data);
399 }
400
401 static Eina_Bool
402 _elm_notify_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
403 {
404    Widget_Data *wd = elm_widget_data_get(obj);
405    Evas_Object *cur;
406
407    if ((!wd) || (!wd->content))
408      return EINA_FALSE;
409
410    cur = wd->content;
411
412    /* Try Focus cycle in subitem */
413    return elm_widget_focus_next_get(cur, dir, next);
414 }
415
416 EAPI Evas_Object *
417 elm_notify_add(Evas_Object *parent)
418 {
419    Evas_Object *obj;
420    Evas *e;
421    Widget_Data *wd;
422
423    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
424
425    ELM_SET_WIDTYPE(widtype, "notify");
426    elm_widget_type_set(obj, "notify");
427    elm_widget_sub_object_add(parent, obj);
428    elm_widget_data_set(obj, wd);
429    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
430    elm_widget_del_hook_set(obj, _del_hook);
431    elm_widget_theme_hook_set(obj, _theme_hook);
432    elm_widget_can_focus_set(obj, EINA_FALSE);
433    elm_widget_focus_next_hook_set(obj, _elm_notify_focus_next_hook);
434
435    wd->repeat_events = EINA_TRUE;
436
437    wd->notify = edje_object_add(e);
438    wd->orient = -1;
439    elm_notify_orient_set(obj, ELM_NOTIFY_ORIENT_TOP);
440
441    elm_notify_parent_set(obj, parent);
442
443    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
444    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
445    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _resize, obj);
446    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
447    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _hide, obj);
448    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESTACK, _restack, obj);
449    _mirrored_set(obj, elm_widget_mirrored_get(obj));
450    _sizing_eval(obj);
451
452    evas_object_smart_callbacks_descriptions_set(obj, _signals);
453    return obj;
454 }
455
456 EAPI void
457 elm_notify_content_set(Evas_Object *obj, Evas_Object *content)
458 {
459    ELM_CHECK_WIDTYPE(obj, widtype);
460    Widget_Data *wd = elm_widget_data_get(obj);
461    if (!wd) return;
462    if (wd->content == content) return;
463    if (wd->content) evas_object_del(wd->content);
464    wd->content = content;
465
466    if (content)
467      {
468         elm_widget_sub_object_add(obj, content);
469         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
470                                        _changed_size_hints, obj);
471         evas_object_event_callback_add(content, EVAS_CALLBACK_RESIZE,
472                                        _content_resize, obj);
473         edje_object_part_swallow(wd->notify, "elm.swallow.content", content);
474      }
475    _sizing_eval(obj);
476    _calc(obj);
477 }
478
479 EAPI Evas_Object *
480 elm_notify_content_unset(Evas_Object *obj)
481 {
482    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
483    Widget_Data *wd = elm_widget_data_get(obj);
484    Evas_Object *content;
485    if (!wd) return NULL;
486    if (!wd->content) return NULL;
487    content = wd->content;
488    elm_widget_sub_object_del(obj, wd->content);
489    edje_object_part_unswallow(wd->notify, wd->content);
490    wd->content = NULL;
491    return content;
492 }
493
494 EAPI Evas_Object *
495 elm_notify_content_get(const Evas_Object *obj)
496 {
497    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
498    Widget_Data *wd = elm_widget_data_get(obj);
499
500    if (!wd) return NULL;
501    return wd->content;
502 }
503
504 EAPI void
505 elm_notify_parent_set(Evas_Object *obj, Evas_Object *parent)
506 {
507    ELM_CHECK_WIDTYPE(obj, widtype);
508    Widget_Data *wd = elm_widget_data_get(obj);
509    if (!wd) return;
510    if (wd->parent)
511      {
512         evas_object_event_callback_del_full(wd->parent,
513                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
514                                             _changed_size_hints, obj);
515         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
516                                             _changed_size_hints, obj);
517         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
518                                             _changed_size_hints, obj);
519         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
520                                             _parent_del, obj);
521         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
522                                             _parent_hide, obj);
523         wd->parent = NULL;
524      }
525
526    if (parent)
527      {
528         wd->parent = parent;
529         evas_object_event_callback_add(parent,
530                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
531                                        _changed_size_hints, obj);
532         evas_object_event_callback_add(parent, EVAS_CALLBACK_RESIZE,
533                                        _changed_size_hints, obj);
534         evas_object_event_callback_add(parent, EVAS_CALLBACK_MOVE,
535                                        _changed_size_hints, obj);
536         evas_object_event_callback_add(parent, EVAS_CALLBACK_DEL,
537                                        _parent_del, obj);
538         evas_object_event_callback_add(parent, EVAS_CALLBACK_HIDE,
539                                        _parent_hide, obj);
540         edje_object_part_swallow(wd->notify, "elm.swallow.parent", parent);
541         _sizing_eval(obj);
542      }
543    _calc(obj);
544 }
545
546 EAPI Evas_Object *
547 elm_notify_parent_get(const Evas_Object *obj)
548 {
549    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
550    Widget_Data *wd = elm_widget_data_get(obj);
551    if (!wd) return NULL;
552    return wd->parent;
553 }
554
555 EAPI void
556 elm_notify_orient_set(Evas_Object *obj, Elm_Notify_Orient orient)
557 {
558    ELM_CHECK_WIDTYPE(obj, widtype);
559    Widget_Data *wd = elm_widget_data_get(obj);
560    if (!wd) return;
561    if (wd->orient == orient) return;
562    wd->orient = orient;
563    _notify_theme_apply(obj);
564    _resize(obj, NULL, obj, NULL);
565 }
566
567 EAPI Elm_Notify_Orient
568 elm_notify_orient_get(const Evas_Object *obj)
569 {
570    ELM_CHECK_WIDTYPE(obj, widtype) -1;
571    Widget_Data *wd = elm_widget_data_get(obj);
572    if (!wd) return -1;
573    return wd->orient;
574 }
575
576 EAPI void
577 elm_notify_timeout_set(Evas_Object *obj, double timeout)
578 {
579    ELM_CHECK_WIDTYPE(obj, widtype);
580    Widget_Data *wd = elm_widget_data_get(obj);
581    if (!wd) return;
582    wd->timeout = timeout;
583    _timer_init(obj, wd);
584 }
585
586 EAPI double
587 elm_notify_timeout_get(const Evas_Object *obj)
588 {
589    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
590    Widget_Data *wd = elm_widget_data_get(obj);
591    if (!wd) return 0.0;
592    return wd->timeout;
593 }
594
595 EAPI void
596 elm_notify_repeat_events_set(Evas_Object *obj, Eina_Bool repeat)
597 {
598    ELM_CHECK_WIDTYPE(obj, widtype);
599    Widget_Data *wd = elm_widget_data_get(obj);
600    if (!wd) return;
601    if (repeat == wd->repeat_events) return;
602    wd->repeat_events = repeat;
603    if (!repeat)
604      {
605         wd->block_events = edje_object_add(evas_object_evas_get(obj));
606         _block_events_theme_apply(obj);
607         elm_widget_resize_object_set(obj, wd->block_events);
608         edje_object_signal_callback_add(wd->block_events, "elm,action,clicked",
609                                         "elm", _signal_block_clicked, obj);
610      }
611    else
612      evas_object_del(wd->block_events);
613 }
614
615 EAPI Eina_Bool
616 elm_notify_repeat_events_get(const Evas_Object *obj)
617 {
618    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
619    Widget_Data *wd = elm_widget_data_get(obj);
620    if (!wd) return EINA_FALSE;
621    return wd->repeat_events;
622 }