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