elementary/win - [E-devel] [Patch][Elementary] Patch for elm_win to fix the focus...
[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 allow_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_allow_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->allow_events) evas_object_show(wd->block_events);
364    _timer_init(obj, wd);
365    elm_object_focus_set(obj, EINA_TRUE);
366 }
367
368 static void
369 _hide(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
370 {
371    Widget_Data *wd = elm_widget_data_get(obj);
372    if (!wd) return;
373    evas_object_hide(wd->notify);
374    if (!wd->allow_events) evas_object_hide(wd->block_events);
375    if (wd->timer)
376      {
377         ecore_timer_del(wd->timer);
378         wd->timer = NULL;
379      }
380 }
381
382 static void
383 _parent_del(void *data,  Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
384 {
385    Widget_Data *wd = elm_widget_data_get(data);
386    if (!wd) return;
387    wd->parent = NULL;
388    evas_object_hide(data);
389 }
390
391 static void
392 _parent_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
393 {
394    Widget_Data *wd = elm_widget_data_get(data);
395    if (!wd) return;
396    evas_object_hide(data);
397 }
398
399 static Eina_Bool
400 _elm_notify_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
401 {
402    Widget_Data *wd = elm_widget_data_get(obj);
403    Evas_Object *cur;
404
405    if ((!wd) || (!wd->content))
406      return EINA_FALSE;
407
408    cur = wd->content;
409
410    /* Try Focus cycle in subitem */
411    return elm_widget_focus_next_get(cur, dir, next);
412 }
413
414 static void
415 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
416 {
417    ELM_CHECK_WIDTYPE(obj, widtype);
418    Widget_Data *wd;
419    if (part && strcmp(part ,"default")) return;
420    wd = elm_widget_data_get(obj);
421    if (!wd) return;
422    if (wd->content == content) return;
423    if (wd->content) evas_object_del(wd->content);
424    wd->content = content;
425
426    if (content)
427      {
428         elm_widget_sub_object_add(obj, content);
429         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
430                                        _changed_size_hints, obj);
431         evas_object_event_callback_add(content, EVAS_CALLBACK_RESIZE,
432                                        _content_resize, obj);
433         edje_object_part_swallow(wd->notify, "elm.swallow.content", content);
434      }
435    _sizing_eval(obj);
436    _calc(obj);
437 }
438
439 static Evas_Object *
440 _content_get_hook(const Evas_Object *obj, const char *part)
441 {
442    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
443    Widget_Data *wd;
444    if (part && strcmp(part ,"default")) return NULL;
445    wd = elm_widget_data_get(obj);
446    if (!wd) return NULL;
447    return wd->content;
448 }
449
450 static Evas_Object *
451 _content_unset_hook(Evas_Object *obj, const char *part)
452 {
453    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
454    Widget_Data *wd;
455    Evas_Object *content;
456    if (part && strcmp(part ,"default")) return NULL;
457    wd = elm_widget_data_get(obj);
458    if (!wd) return NULL;
459    if (!wd->content) return NULL;
460    content = wd->content;
461    elm_widget_sub_object_del(obj, wd->content);
462    edje_object_part_unswallow(wd->notify, content);
463    return content;
464 }
465
466 EAPI Evas_Object *
467 elm_notify_add(Evas_Object *parent)
468 {
469    Evas_Object *obj;
470    Evas *e;
471    Widget_Data *wd;
472
473    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
474
475    ELM_SET_WIDTYPE(widtype, "notify");
476    elm_widget_type_set(obj, "notify");
477    elm_widget_sub_object_add(parent, obj);
478    elm_widget_data_set(obj, wd);
479    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
480    elm_widget_del_hook_set(obj, _del_hook);
481    elm_widget_theme_hook_set(obj, _theme_hook);
482    elm_widget_can_focus_set(obj, EINA_FALSE);
483    elm_widget_focus_next_hook_set(obj, _elm_notify_focus_next_hook);
484    elm_widget_content_set_hook_set(obj, _content_set_hook);
485    elm_widget_content_get_hook_set(obj, _content_get_hook);
486    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
487
488    wd->allow_events = EINA_TRUE;
489
490    wd->notify = edje_object_add(e);
491    wd->orient = -1;
492    elm_notify_orient_set(obj, ELM_NOTIFY_ORIENT_TOP);
493
494    elm_notify_parent_set(obj, parent);
495
496    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
497    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
498    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _resize, obj);
499    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
500    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _hide, obj);
501    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESTACK, _restack, obj);
502    _mirrored_set(obj, elm_widget_mirrored_get(obj));
503    _sizing_eval(obj);
504
505    evas_object_smart_callbacks_descriptions_set(obj, _signals);
506    return obj;
507 }
508
509 EAPI void
510 elm_notify_parent_set(Evas_Object *obj, Evas_Object *parent)
511 {
512    ELM_CHECK_WIDTYPE(obj, widtype);
513    Widget_Data *wd = elm_widget_data_get(obj);
514    if (!wd) return;
515    if (wd->parent)
516      {
517         evas_object_event_callback_del_full(wd->parent,
518                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
519                                             _changed_size_hints, obj);
520         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
521                                             _changed_size_hints, obj);
522         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
523                                             _changed_size_hints, obj);
524         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
525                                             _parent_del, obj);
526         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
527                                             _parent_hide, obj);
528         wd->parent = NULL;
529      }
530
531    if (parent)
532      {
533         wd->parent = parent;
534         evas_object_event_callback_add(parent,
535                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
536                                        _changed_size_hints, obj);
537         evas_object_event_callback_add(parent, EVAS_CALLBACK_RESIZE,
538                                        _changed_size_hints, obj);
539         evas_object_event_callback_add(parent, EVAS_CALLBACK_MOVE,
540                                        _changed_size_hints, obj);
541         evas_object_event_callback_add(parent, EVAS_CALLBACK_DEL,
542                                        _parent_del, obj);
543         evas_object_event_callback_add(parent, EVAS_CALLBACK_HIDE,
544                                        _parent_hide, obj);
545         _sizing_eval(obj);
546      }
547    _calc(obj);
548 }
549
550 EAPI Evas_Object *
551 elm_notify_parent_get(const Evas_Object *obj)
552 {
553    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
554    Widget_Data *wd = elm_widget_data_get(obj);
555    if (!wd) return NULL;
556    return wd->parent;
557 }
558
559 EAPI void
560 elm_notify_orient_set(Evas_Object *obj, Elm_Notify_Orient orient)
561 {
562    ELM_CHECK_WIDTYPE(obj, widtype);
563    Widget_Data *wd = elm_widget_data_get(obj);
564    if (!wd) return;
565    if (wd->orient == orient) return;
566    wd->orient = orient;
567    _notify_theme_apply(obj);
568    _resize(obj, NULL, obj, NULL);
569 }
570
571 EAPI Elm_Notify_Orient
572 elm_notify_orient_get(const Evas_Object *obj)
573 {
574    ELM_CHECK_WIDTYPE(obj, widtype) -1;
575    Widget_Data *wd = elm_widget_data_get(obj);
576    if (!wd) return -1;
577    return wd->orient;
578 }
579
580 EAPI void
581 elm_notify_timeout_set(Evas_Object *obj, double timeout)
582 {
583    ELM_CHECK_WIDTYPE(obj, widtype);
584    Widget_Data *wd = elm_widget_data_get(obj);
585    if (!wd) return;
586    wd->timeout = timeout;
587    _timer_init(obj, wd);
588 }
589
590 EAPI double
591 elm_notify_timeout_get(const Evas_Object *obj)
592 {
593    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
594    Widget_Data *wd = elm_widget_data_get(obj);
595    if (!wd) return 0.0;
596    return wd->timeout;
597 }
598
599 EAPI void
600 elm_notify_allow_events_set(Evas_Object *obj, Eina_Bool allow)
601 {
602    ELM_CHECK_WIDTYPE(obj, widtype);
603    Widget_Data *wd = elm_widget_data_get(obj);
604    if (!wd) return;
605    if (allow == wd->allow_events) return;
606    wd->allow_events = allow;
607    if (!allow)
608      {
609         wd->block_events = edje_object_add(evas_object_evas_get(obj));
610         _block_events_theme_apply(obj);
611         elm_widget_resize_object_set(obj, wd->block_events);
612         edje_object_signal_callback_add(wd->block_events, "elm,action,click",
613                                         "elm", _signal_block_clicked, obj);
614      }
615    else
616      evas_object_del(wd->block_events);
617 }
618
619 EAPI Eina_Bool
620 elm_notify_allow_events_get(const Evas_Object *obj)
621 {
622    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
623    Widget_Data *wd = elm_widget_data_get(obj);
624    if (!wd) return EINA_FALSE;
625    return wd->allow_events;
626 }