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