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