Merge "navigationbar - fixed a to_content_pop API logic error."
[framework/uifw/elementary.git] / src / lib / elm_editfield.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Editfield Editfield
6  * @ingroup Elementary
7  *
8  * This is a editfield. It can contain a simple label and icon objects.
9  *
10  * Smart callbacks that you can add are:
11  *
12  * clicked - This signal is emitted when an editfield is clicked.
13  *
14  * unfocused - This signal is emitted when an editfield is unfocused.
15  *
16  */
17
18 //#define ERASER_PADDING (10)
19
20 typedef struct _Widget_Data Widget_Data;
21
22 struct _Widget_Data
23 {
24    Evas_Object *base;
25    Evas_Object *entry;
26    Evas_Object *ricon;
27    Evas_Object *licon;
28    const char *label;
29    const char *guide_text;
30    Eina_Bool needs_size_calc:1;
31    Eina_Bool show_guide_text:1;
32    Eina_Bool editing:1;
33    Eina_Bool single_line:1;
34    Eina_Bool eraser_show:1;
35 };
36
37 static const char *widtype = NULL;
38 static void _del_hook(Evas_Object *obj);
39 static void _theme_hook(Evas_Object *obj);
40 static void _sizing_eval(Evas_Object *obj);
41 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
42 static void _show_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
43 static void _on_focus_hook(void *data, Evas_Object *obj);
44 static Eina_Bool _empty_entry(Evas_Object *entry);
45
46 static void
47 _del_hook(Evas_Object *obj)
48 {
49    Widget_Data *wd = elm_widget_data_get(obj);
50    if (!wd) return;
51    if (wd->label) eina_stringshare_del(wd->label);
52    free(wd);
53 }
54
55 static void
56 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
57 {
58    Widget_Data *wd = elm_widget_data_get(obj);
59    if (!wd || !wd->base)
60       return;
61    if (!elm_widget_focus_get(obj) && !(elm_widget_disabled_get(obj)) )
62      {
63         evas_object_smart_callback_call(obj, "unfocused", NULL);
64         wd->editing = EINA_FALSE;
65         edje_object_signal_emit(wd->base, "elm,state,over,show", "elm");
66
67         if (!wd->single_line) // FIXME : if textblock works well, delete
68           edje_object_signal_emit(wd->base, "elm,state,entry,show", "elm"); // FIXME : if textblock works well, delete
69
70         edje_object_signal_emit(wd->base, "elm,state,eraser,hidden", "elm");
71         if(_empty_entry(wd->entry))
72           {
73              if(wd->guide_text)
74                {
75                   edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
76                   edje_object_signal_emit(wd->base, "elm,state,guidetext,visible", "elm");
77                   wd->show_guide_text = EINA_TRUE;
78                }
79           }
80      }
81 }
82
83 static void
84 _theme_hook(Evas_Object *obj)
85 {
86    Widget_Data *wd = elm_widget_data_get(obj);
87    char buf[4096];
88    if (!wd || !wd->base)
89       return;
90
91    _elm_theme_object_set(obj, wd->base, "editfield", "base", elm_widget_style_get(obj));
92    snprintf(buf, sizeof(buf), "editfield/%s", elm_widget_style_get(obj));
93    elm_object_style_set(wd->entry, buf);
94    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
95    if(!wd->editing)
96       edje_object_signal_emit(wd->base, "elm,state,over,show", "elm");
97    if(wd->show_guide_text)
98      {
99         if(_empty_entry(wd->entry))
100           {
101              if(wd->guide_text)
102                {
103                   edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
104                   edje_object_signal_emit(wd->base, "elm,state,guidetext,visible", "elm");
105                }
106           }
107      }
108    if(wd->ricon)
109       edje_object_part_swallow(wd->base, "right_icon", wd->ricon);
110    if(wd->licon)
111       edje_object_part_swallow(wd->base, "left_icon", wd->licon);
112    _sizing_eval(obj);
113 }
114
115 static void
116 _changed_hook(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    if (wd->needs_size_calc)
120      {
121         _sizing_eval(obj);
122         wd->needs_size_calc = EINA_FALSE;
123      }
124 }
125
126 static void
127 _sizing_eval(Evas_Object *obj)
128 {
129    Widget_Data *wd = elm_widget_data_get(obj);
130    Evas_Coord minw = -1, minh = -1;
131    edje_object_size_min_calc(wd->base, &minw, &minh);
132    evas_object_size_hint_min_set(obj, minw, minh);
133    evas_object_size_hint_max_set(obj, -1, -1);
134 }
135
136 static void
137 _request_sizing_eval(Evas_Object *obj)
138 {
139    Widget_Data *wd = elm_widget_data_get(obj);
140    if(!wd || !wd->base)
141       return;
142    if (wd->needs_size_calc)
143       return;
144    wd->needs_size_calc = EINA_TRUE;
145    evas_object_smart_changed(obj);
146 }
147
148 static void
149 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
150 {
151    double weight_x;
152    evas_object_size_hint_weight_get(data, &weight_x, NULL);
153    if (weight_x == EVAS_HINT_EXPAND)
154       _request_sizing_eval(data);
155 }
156
157 static void
158 _show_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
159 {
160    Widget_Data *wd = elm_widget_data_get(data);
161    if (!wd) return;
162    if ((wd->single_line) && (!wd->editing))  // FIXME : single_line is not needed for this conditional state after TEXTBLOCK fixing
163      {
164         elm_object_focus(wd->entry);
165         elm_entry_cursor_end_set(wd->entry);
166         wd->editing = EINA_TRUE;
167      }
168 }
169
170 static Eina_Bool
171 _empty_entry(Evas_Object *entry)
172 {
173    const char* text;
174    char *strip_text;
175    int len = 0;
176
177    text = elm_entry_entry_get(entry);
178    if(!text) return EINA_FALSE;
179    strip_text = elm_entry_markup_to_utf8(text);
180    if (strip_text) {
181         len = strlen(strip_text);
182         free(strip_text);
183    }
184    if(len == 0)
185       return EINA_TRUE;
186    else
187       return EINA_FALSE;
188 }
189
190 static void
191 _entry_changed_cb(void *data, Evas_Object *obj, void* event_info __UNUSED__)
192 {
193    Evas_Object *ef_obj = (Evas_Object *)data;
194    Widget_Data *wd = elm_widget_data_get(ef_obj);
195
196    if(!wd || !wd->base) return;
197
198    if(wd->single_line)
199      {
200         if(elm_entry_password_get(wd->entry))
201           {
202              edje_object_signal_emit(wd->base, "elm,state,password,set", "elm");
203              edje_object_part_text_set(wd->base, "elm.content.password", elm_entry_entry_get(wd->entry));
204           }
205         else
206           {
207              edje_object_signal_emit(wd->base, "elm,state,password,unset", "elm");
208              edje_object_part_text_set(wd->base, "elm.content.single", elm_entry_entry_get(wd->entry));
209           }
210      }
211 //   else     // Add after TEXTBLOCK fix
212 //     edje_object_part_text_set(wd->base, "elm.content.multi", elm_entry_entry_get(wd->entry));
213
214    if(!_empty_entry(wd->entry))
215      {
216         if(wd->eraser_show && elm_object_focus_get(obj))
217            edje_object_signal_emit(wd->base, "elm,state,eraser,show", "elm");
218         if(wd->guide_text)
219           {
220              edje_object_signal_emit(wd->base, "elm,state,guidetext,hidden", "elm");
221              wd->show_guide_text = EINA_FALSE;
222           }
223      }
224    else
225      {
226         if(wd->eraser_show)
227            edje_object_signal_emit(wd->base, "elm,state,eraser,hidden", "elm");
228      }
229
230    if (!wd->editing && wd->single_line)
231      edje_object_signal_emit(wd->base, "elm,state,over,show", "elm");
232 }
233
234 static void
235 _signal_mouse_clicked(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source)
236 {
237    Widget_Data *wd = elm_widget_data_get(data);
238    if(!wd || !wd->base) return;
239
240    if(!strcmp(source, "eraser"))
241      {
242         elm_entry_entry_set(wd->entry, "");
243         edje_object_signal_emit(wd->base, "elm,state,eraser,hidden", "elm");
244      }
245    else if(strcmp(source, "left_icon") && strcmp(source, "right_icon") && strcmp(source, "eraser"))
246      {
247         edje_object_signal_emit(wd->base, "elm,state,over,hide", "elm");
248
249         if ((!wd->single_line) && (!wd->editing)) //FIXME : after fixing TEXTBLOCK, this should be deleted
250           {
251              elm_object_focus(wd->entry);
252              elm_entry_cursor_end_set(wd->entry);
253              wd->editing = EINA_TRUE;
254           }
255
256         if(!(_empty_entry(wd->entry)) && (wd->eraser_show))
257            edje_object_signal_emit(wd->base, "elm,state,eraser,show", "elm");
258
259         if(wd->guide_text)
260           {
261              edje_object_signal_emit(wd->base, "elm,state,guidetext,hidden", "elm");
262              wd->show_guide_text = EINA_FALSE;
263           }
264         evas_object_smart_callback_call(data, "clicked", NULL);
265      }
266 }
267
268 static void
269 _resize_cb(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event __UNUSED__)
270 {
271    Widget_Data *wd = elm_widget_data_get(data);
272    Evas_Coord h;
273    if (!wd || !wd->base) return;
274    evas_object_geometry_get(obj, NULL, NULL, NULL, &h);
275 }
276
277 static void
278 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
279 {
280    Widget_Data *wd = elm_widget_data_get(obj);
281    if (!wd) return;
282    edje_object_signal_emit(wd->base, emission, source);
283 }
284
285
286 /**
287  * Add a new editfield object
288  *
289  * @param parent The parent object
290  * @return The new object or NULL if it cannot be created
291  *
292  * @ingroup Editfield
293  */
294 EAPI Evas_Object *
295 elm_editfield_add(Evas_Object *parent)
296 {
297    Evas_Object *obj;
298    Evas *e;
299    Widget_Data *wd;
300
301    e = evas_object_evas_get(parent);
302    if (e == NULL)
303       return NULL;
304    wd = ELM_NEW(Widget_Data);
305    obj = elm_widget_add(e);
306    ELM_SET_WIDTYPE(widtype, "editfield");
307    elm_widget_type_set(obj, "editfield");
308    elm_widget_sub_object_add(parent, obj);
309    elm_widget_data_set(obj, wd);
310    elm_widget_del_hook_set(obj, _del_hook);
311    elm_widget_theme_hook_set(obj, _theme_hook);
312    elm_widget_changed_hook_set(obj, _changed_hook);
313    elm_widget_on_focus_hook_set( obj, _on_focus_hook, NULL );
314    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
315    elm_widget_can_focus_set(obj, EINA_TRUE);
316
317    wd->base = edje_object_add(e);
318    _elm_theme_object_set(obj, wd->base, "editfield", "base", "default");
319    elm_widget_resize_object_set(obj, wd->base);
320    edje_object_signal_callback_add(wd->base, "mouse,clicked,1", "*",
321                                    _signal_mouse_clicked, obj);
322    edje_object_signal_callback_add(wd->base, "clicked", "*",
323                                    _signal_mouse_clicked, obj);
324
325    evas_object_event_callback_add(wd->base, EVAS_CALLBACK_RESIZE, _resize_cb, obj);
326
327    wd->editing = EINA_FALSE;
328    wd->single_line = EINA_FALSE;
329    wd->eraser_show = EINA_TRUE;
330
331    wd->entry = elm_entry_add(obj);
332    elm_object_style_set(wd->entry, "editfield");
333    evas_object_size_hint_weight_set(wd->entry, 0, EVAS_HINT_EXPAND);
334    evas_object_size_hint_align_set(wd->entry, 0, EVAS_HINT_FILL);
335    evas_object_event_callback_add(wd->entry, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
336    evas_object_event_callback_add(wd->entry, EVAS_CALLBACK_SHOW, _show_cb, obj);
337    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
338    evas_object_smart_callback_add(wd->entry, "changed", _entry_changed_cb, obj);
339    elm_widget_sub_object_add(obj, wd->entry);
340    evas_object_show(wd->entry);
341    _sizing_eval(obj);
342
343    return obj;
344 }
345
346 /**
347  * Set the label of editfield
348  *
349  * @param obj The editfield object
350  * @param label The label text
351  *
352  * @ingroup Editfield
353  */
354 EAPI void
355 elm_editfield_label_set(Evas_Object *obj, const char *label)
356 {
357    Widget_Data *wd = elm_widget_data_get(obj);
358    ELM_CHECK_WIDTYPE(obj, widtype);
359    if (!wd || !wd->base)
360       return;
361    if (wd->label)
362       eina_stringshare_del(wd->label);
363    if (label)
364      {
365         wd->label = eina_stringshare_add(label);
366         edje_object_signal_emit(wd->base, "elm,state,text,visible", "elm");
367         edje_object_signal_emit(wd->base, "elm,state,left,icon,hide", "elm");
368      }
369    else
370      {
371         wd->label = NULL;
372         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
373         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
374      }
375    edje_object_message_signal_process(wd->base);
376    edje_object_part_text_set(wd->base, "elm.text", label);
377    _sizing_eval(obj);
378 }
379
380 /**
381  * Get the label used on the editfield object
382  *
383  * @param obj The editfield object
384  * @return label text
385  *
386  * @ingroup Editfield
387  */
388 EAPI const char*
389 elm_editfield_label_get(Evas_Object *obj)
390 {
391    Widget_Data *wd = elm_widget_data_get(obj);
392    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
393    if (!wd || !wd->base)
394       return NULL;
395    return wd->label;
396 }
397
398 /**
399  * Set the guidance text used on the editfield object
400  *
401  * @param obj The editfield object
402  * @return label text
403  *
404  * @ingroup Editfield
405  */
406 EAPI void
407 elm_editfield_guide_text_set(Evas_Object *obj, const char *text)
408 {
409    Widget_Data *wd = elm_widget_data_get(obj);
410    ELM_CHECK_WIDTYPE(obj, widtype);
411    if (!wd || !wd->base)
412       return;
413    if (wd->guide_text)
414       eina_stringshare_del(wd->guide_text);
415    if (text)
416      {
417         wd->guide_text = eina_stringshare_add(text);
418         edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
419         wd->show_guide_text = EINA_TRUE;
420      }
421    else
422       wd->guide_text = NULL;
423 }
424
425 /**
426  * Get the guidance text used on the editfield object
427  *
428  * @param obj The editfield object
429  * @return label text
430  *
431  * @ingroup Editfield
432  */
433 EAPI const char*
434 elm_editfield_guide_text_get(Evas_Object *obj)
435 {
436    Widget_Data *wd = elm_widget_data_get(obj);
437    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
438    if (!wd || !wd->base)
439       return NULL;
440    return wd->guide_text;
441 }
442
443 /**
444  * Get the entry of the editfield object
445  *
446  * @param obj The editfield object
447  * @return entry object
448  *
449  * @ingroup Editfield
450  */
451
452 EAPI Evas_Object *
453 elm_editfield_entry_get(Evas_Object *obj)
454 {
455    Widget_Data *wd = elm_widget_data_get(obj);
456    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
457    if (!wd)
458       return NULL;
459    return wd->entry;
460 }
461
462 /**
463  * Set the left side icon.
464  *
465  * @param obj The editfield object
466  * @param icon The icon object
467  *
468  * @ingroup Editfield
469  */
470 EAPI void
471 elm_editfield_left_icon_set(Evas_Object *obj, Evas_Object *icon)
472 {
473    Widget_Data *wd = elm_widget_data_get(obj);
474    ELM_CHECK_WIDTYPE(obj, widtype) ;
475    if (!wd || !wd->base || !icon)
476       return;
477    if ((wd->licon != icon) && (wd->licon))
478       elm_widget_sub_object_del(obj, wd->licon);
479    if (icon)
480      {
481         if (!(edje_object_part_swallow(wd->base, "left_icon", icon)))
482            return;
483         wd->licon = icon;
484         elm_widget_sub_object_add(obj, icon);
485         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
486                                        _changed_size_hints, obj);
487         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
488         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
489         _sizing_eval(obj);
490      }
491    return;
492 }
493
494 /**
495  * Get the left side icon
496  *
497  * @param obj The editfield object
498  * @return icon object
499  *
500  * @ingroup Editfield
501  */
502 EAPI Evas_Object *
503 elm_editfield_left_icon_get(Evas_Object *obj)
504 {
505    Widget_Data *wd = elm_widget_data_get(obj);
506    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
507    if (!wd || !wd->base || !wd->licon)
508       return NULL;
509    return wd->licon;
510 }
511
512 /**
513  * Set the right side icon.
514  *
515  * @param obj The editfield object
516  * @param icon The icon object
517  *
518  * @ingroup Editfield
519  */
520 EAPI void
521 elm_editfield_right_icon_set(Evas_Object *obj, Evas_Object *icon)
522 {
523    Widget_Data *wd = elm_widget_data_get(obj);
524    ELM_CHECK_WIDTYPE(obj, widtype) ;
525    if (!wd || !wd->base || !icon)
526       return;
527    if ((wd->ricon != icon) && (wd->ricon))
528       elm_widget_sub_object_del(obj, wd->ricon);
529    if (icon)
530      {
531         if ( !(edje_object_part_swallow(wd->base, "right_icon", icon)) )
532            return;
533         wd->ricon = icon;
534         elm_widget_sub_object_add(obj, icon);
535         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
536                                        _changed_size_hints, obj);
537         edje_object_signal_emit(wd->base, "elm,state,right,icon,show", "elm");
538         _sizing_eval(obj);
539      }
540    return;
541 }
542
543 /**
544  * Get the right side icon
545  *
546  * @param obj The editfield object
547  * @return icon object
548  *
549  * @ingroup Editfield
550  */
551 EAPI Evas_Object *
552 elm_editfield_right_icon_get(Evas_Object *obj)
553 {
554    Widget_Data *wd = elm_widget_data_get(obj);
555    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
556    if (!wd || !wd->base || !wd->ricon)
557       return NULL;
558    return wd->ricon;
559 }
560
561 /**
562  * Set entry object style as single-line or multi-line.
563  *
564  * @param obj The editfield object
565  * @param single_line 1 if single-line , 0 if multi-line
566  *
567  * @ingroup Editfield
568  */
569 EAPI void
570 elm_editfield_entry_single_line_set(Evas_Object *obj, Eina_Bool single_line)
571 {
572    Widget_Data *wd = elm_widget_data_get(obj);
573    ELM_CHECK_WIDTYPE(obj, widtype);
574    if (!wd || !wd->base || wd->single_line == single_line)
575       return;
576    wd->single_line = !!single_line;
577    elm_entry_single_line_set(wd->entry, single_line);
578    if(single_line)
579      {
580         elm_entry_scrollable_set(wd->entry, EINA_TRUE);
581         elm_entry_single_line_set(wd->entry,EINA_TRUE);
582         edje_object_signal_emit(wd->base, "elm,state,text,singleline", "elm");
583      }
584    else
585      {
586         elm_entry_scrollable_set(wd->entry, EINA_FALSE);
587         elm_entry_single_line_set(wd->entry,EINA_FALSE);
588         edje_object_signal_emit(wd->base, "elm,state,text,multiline", "elm");
589      }
590    if (!wd->editing)
591      edje_object_signal_emit(wd->base, "elm,state,over,show", "elm");
592 }
593
594 /**
595  * Get the current entry object style(single-line or multi-line)
596  *
597  * @param obj The editfield object
598  * @return 1 if single-line , 0 if multi-line
599  *
600  * @ingroup Editfield
601  */
602 EAPI Eina_Bool
603 elm_editfield_entry_single_line_get(Evas_Object *obj)
604 {
605    Widget_Data *wd = elm_widget_data_get(obj);
606    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
607    if (!wd || !wd->base)
608       return EINA_FALSE;
609    return wd->single_line;
610 }
611
612 /**
613  * Set enable user to clean all of text.
614  *
615  * @param obj The editfield object
616  * @param visible If true, the eraser is visible and user can clean all of text by using eraser.
617  * If false, the eraser is invisible.
618  *
619  * @ingroup Editfield
620  */
621 EAPI void
622 elm_editfield_eraser_set(Evas_Object *obj, Eina_Bool visible)
623 {
624    Widget_Data *wd = elm_widget_data_get(obj);
625    ELM_CHECK_WIDTYPE(obj, widtype);
626    if (!wd || !wd->base)
627       return;
628
629    wd->eraser_show = !!visible;
630
631    if (!visible)
632       edje_object_signal_emit(wd->base, "elm,state,eraser,hidden", "elm");
633
634    return;
635 }
636
637 /**
638  * Get the current state of erase (visible/invisible)
639  *
640  * @param obj The editfield object
641  * @return 1 if visible, 0 if invisible
642  *
643  * @ingroup Editfield
644  */
645 EAPI Eina_Bool
646 elm_editfield_eraser_get(Evas_Object *obj)
647 {
648    Widget_Data *wd = elm_widget_data_get(obj);
649    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
650    if (!wd || !wd->base)
651       return EINA_FALSE;
652    return wd->eraser_show;
653 }
654
655 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/