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