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