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