Merge branch 'master' of 165.213.180.234:/git/slp/pkgs/elementary
[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    evas_object_smart_callback_call(ef_obj, SIG_IMPREGION_CHANGED, NULL);
208 }
209 #endif
210
211 static void
212 _signal_mouse_clicked(void *data, Evas_Object *obj, const char *emission, const char *source)
213 {
214    Widget_Data *wd = elm_widget_data_get(data);
215    if(!wd || !wd->base) return;
216
217    if(!strcmp(source, "eraser"))
218    {
219            elm_entry_entry_set(wd->entry, "");
220            if(wd->editing == EINA_FALSE) {
221                    if(wd->guide_text) 
222                    {
223                            edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
224                            edje_object_signal_emit(wd->base, "elm,state,guidetext,visible", "elm");
225                            wd->show_guide_text = EINA_TRUE;
226                    }
227            }
228    }       
229    else if(strcmp(source, "left_icon") && strcmp(source, "right_icon") && strcmp(source, "eraser"))
230      {
231         edje_object_signal_emit(wd->base, "elm,state,over,hide", "elm");
232         if(wd->single_line)
233                 elm_widget_focus_set(wd->scroller, EINA_TRUE);
234         else    
235                 elm_widget_focus_set(wd->entry, EINA_TRUE);                     
236
237         if(wd->editing == EINA_FALSE)
238           elm_entry_cursor_end_set(wd->entry);
239         
240         if(wd->guide_text) 
241            {
242                   edje_object_signal_emit(wd->base, "elm,state,guidetext,hidden", "elm");
243                   wd->show_guide_text = EINA_FALSE;
244            }
245         evas_object_smart_callback_call(data, "clicked", NULL);
246         wd->editing = EINA_TRUE;
247      }
248 }
249
250 static void
251 _resize_cb(void *data, Evas *evas, Evas_Object *obj, void *event)
252 {
253    Widget_Data *wd = elm_widget_data_get(data); 
254    Evas_Coord h;
255    if (!wd || !wd->base) return;
256    evas_object_geometry_get(obj, NULL, NULL, NULL, &h);
257 }
258
259 /**
260  * Add a new editfield object
261  *
262  * @param parent The parent object
263  * @return The new object or NULL if it cannot be created
264  *
265  * @ingroup Editfield
266  */
267 EAPI Evas_Object *
268 elm_editfield_add(Evas_Object *parent)
269 {
270    Evas_Object *obj;
271    Evas *e;
272    Widget_Data *wd;
273
274    e = evas_object_evas_get(parent);
275    if (e == NULL)
276      return NULL;
277    wd = ELM_NEW(Widget_Data);
278    obj = elm_widget_add(e);
279    ELM_SET_WIDTYPE(widtype, "editfield");
280    elm_widget_type_set(obj, "editfield");
281    elm_widget_sub_object_add(parent, obj);
282    elm_widget_data_set(obj, wd);
283    elm_widget_del_hook_set(obj, _del_hook);
284    elm_widget_theme_hook_set(obj, _theme_hook);
285    elm_widget_changed_hook_set(obj, _changed_hook);
286    elm_widget_on_focus_hook_set( obj, _on_focus_hook, NULL );
287    elm_widget_can_focus_set(obj, EINA_TRUE);
288 #ifdef HAVE_CONFORMANT_AUTOSCROLL
289    elm_widget_imp_region_get_hook_set(obj, _imp_region_get_hook, NULL);
290 #endif
291
292    wd->base = edje_object_add(e);
293    _elm_theme_object_set(obj, wd->base, "editfield", "base", "default");
294    elm_widget_resize_object_set(obj, wd->base);
295    edje_object_signal_callback_add(wd->base, "mouse,clicked,1", "*", 
296          _signal_mouse_clicked, obj);
297    evas_object_event_callback_add(wd->base, EVAS_CALLBACK_RESIZE, _resize_cb, obj);
298
299    wd->editing = EINA_FALSE;
300    wd->single_line = EINA_FALSE;
301    wd->eraser_show = EINA_FALSE;
302
303    wd->entry = elm_entry_add(obj);
304    elm_object_style_set(wd->entry, "editfield");
305    evas_object_size_hint_weight_set(wd->entry, 0, EVAS_HINT_EXPAND);
306    evas_object_size_hint_align_set(wd->entry, 0, EVAS_HINT_FILL);
307    evas_object_event_callback_add(wd->entry,
308          EVAS_CALLBACK_CHANGED_SIZE_HINTS,
309          _changed_size_hints, obj);
310    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
311    evas_object_smart_callback_add(wd->entry, "changed", _entry_changed_cb, obj);
312    elm_widget_sub_object_add(obj, wd->entry);
313 #ifdef HAVE_CONFORMANT_AUTOSCROLL
314    evas_object_smart_callback_add(wd->entry, SIG_CURSOR_CHANGED, _entry_cursor_changed_cb, obj); 
315 #endif
316
317    _sizing_eval(obj);
318
319    return obj;
320 }
321
322 /**
323  * Set the label of editfield
324  *
325  * @param obj The editfield object
326  * @param label The label text
327  *
328  * @ingroup Editfield
329  */
330 EAPI void
331 elm_editfield_label_set(Evas_Object *obj, const char *label)
332 {
333    Widget_Data *wd = elm_widget_data_get(obj);
334    ELM_CHECK_WIDTYPE(obj, widtype);
335    if (!wd || !wd->base) 
336      return;
337    if (wd->label) 
338      eina_stringshare_del(wd->label);
339    if (label) 
340      {
341         wd->label = eina_stringshare_add(label);
342         edje_object_signal_emit(wd->base, "elm,state,text,visible", "elm");
343         edje_object_signal_emit(wd->base, "elm,state,left,icon,hide", "elm");
344      }
345    else 
346      {
347         wd->label = NULL;
348         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
349         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
350      }
351    edje_object_message_signal_process(wd->base);
352    edje_object_part_text_set(wd->base, "elm.text", label);
353    _sizing_eval(obj);
354 }
355
356 /**
357  * Get the label used on the editfield object
358  *
359  * @param obj The editfield object
360  * @return label text
361  *
362  * @ingroup Editfield
363  */
364 EAPI const char*
365 elm_editfield_label_get(Evas_Object *obj)
366 {
367    Widget_Data *wd = elm_widget_data_get(obj);
368    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
369    if (!wd || !wd->base) 
370      return NULL;
371    return       wd->label;
372 }
373
374 /**
375  * Set the guidance text used on the editfield object
376  *
377  * @param obj The editfield object
378  * @return label text
379  *
380  * @ingroup Editfield
381  */
382 EAPI void
383 elm_editfield_guide_text_set(Evas_Object *obj, const char *text)
384 {
385    Widget_Data *wd = elm_widget_data_get(obj);
386    ELM_CHECK_WIDTYPE(obj, widtype);
387    if (!wd || !wd->base)
388      return;
389    if (wd->guide_text) 
390      eina_stringshare_del(wd->guide_text);
391    if (text) 
392      {
393         wd->guide_text = eina_stringshare_add(text);
394         edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
395         wd->show_guide_text = EINA_TRUE;
396      }
397    else
398      wd->guide_text = NULL;
399 }
400
401 /**
402  * Get the guidance text 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_guide_text_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->guide_text;
417 }
418
419 /**
420  * Get the entry of the editfield object
421  *
422  * @param obj The editfield object
423  * @return entry object
424  *
425  * @ingroup Editfield
426  */
427
428 EAPI Evas_Object *
429 elm_editfield_entry_get(Evas_Object *obj)
430 {
431    Widget_Data *wd = elm_widget_data_get(obj);
432    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
433    if (!wd)
434      return NULL;
435    return wd->entry;
436 }
437
438 /**
439  * Set the left side icon.
440  *
441  * @param obj The editfield object
442  * @param icon The icon object
443  * @return 1 if setting is done, 0 if there is no swallow part for the icon.
444  *
445  * @ingroup Editfield
446  */
447 EAPI Eina_Bool 
448 elm_editfield_left_icon_set(Evas_Object *obj, Evas_Object *icon)
449 {
450    Widget_Data *wd = elm_widget_data_get(obj);  
451    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;  
452    if (!wd || !wd->base || !icon)
453      return EINA_FALSE;
454    if ((wd->licon != icon) && (wd->licon))
455      elm_widget_sub_object_del(obj, wd->licon); 
456    if (icon)
457      {
458         if (!(edje_object_part_swallow(wd->base, "left_icon", icon)))
459           return EINA_FALSE;            
460         wd->licon = icon;
461         elm_widget_sub_object_add(obj, icon);
462         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
463               _changed_size_hints, obj);
464         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
465         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
466         _sizing_eval(obj);
467      }  
468    return EINA_TRUE;
469 }
470
471 /**
472  * Get the left side icon
473  *
474  * @param obj The editfield object
475  * @return icon object
476  *
477  * @ingroup Editfield
478  */
479 EAPI Evas_Object *
480 elm_editfield_left_icon_get(Evas_Object *obj)
481 {
482    Widget_Data *wd = elm_widget_data_get(obj);          
483    ELM_CHECK_WIDTYPE(obj, widtype) NULL;        
484    if (!wd || !wd->base || !wd->licon) 
485      return NULL;
486    return wd->licon;
487 }
488
489 /**
490  * Set the right side icon.
491  *
492  * @param obj The editfield object
493  * @param icon The icon object
494  * @return 1 if setting is done, 0 if there is no swallow part for the icon.
495  *
496  * @ingroup Editfield
497  */
498 EAPI Eina_Bool 
499 elm_editfield_right_icon_set(Evas_Object *obj, Evas_Object *icon)
500 {
501    Widget_Data *wd = elm_widget_data_get(obj);
502    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
503    if (!wd || !wd->base || !icon)
504      return EINA_FALSE;
505    if ((wd->ricon != icon) && (wd->ricon))
506      elm_widget_sub_object_del(obj, wd->ricon); 
507    if (icon)
508      {
509         if ( !(edje_object_part_swallow(wd->base, "right_icon", icon)) )
510           return EINA_FALSE;                            
511         wd->ricon = icon;
512         elm_widget_sub_object_add(obj, icon);
513         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
514               _changed_size_hints, obj);                
515         edje_object_signal_emit(wd->base, "elm,state,right,icon,show", "elm");
516         _sizing_eval(obj);
517      }  
518    return EINA_TRUE;
519 }
520
521 /**
522  * Get the right side icon
523  *
524  * @param obj The editfield object
525  * @return icon object
526  *
527  * @ingroup Editfield
528  */
529 EAPI Evas_Object *
530 elm_editfield_right_icon_get(Evas_Object *obj)
531 {
532    Widget_Data *wd = elm_widget_data_get(obj);
533    ELM_CHECK_WIDTYPE(obj, widtype) NULL;        
534    if (!wd || !wd->base || !wd->ricon)
535      return NULL;
536    return wd->ricon;
537 }
538
539 /**
540  * Set entry object style as single-line or multi-line.
541  *
542  * @param obj The editfield object  
543  * @param single_line 1 if single-line , 0 if multi-line
544  *
545  * @ingroup Editfield
546  */
547 EAPI void
548 elm_editfield_entry_single_line_set(Evas_Object *obj, Eina_Bool single_line)
549 {
550    Widget_Data *wd = elm_widget_data_get(obj);
551    Evas_Object *entry;
552    ELM_CHECK_WIDTYPE(obj, widtype);        
553    if (!wd || !wd->base || wd->single_line == single_line) 
554      return;
555    wd->single_line = single_line;
556    elm_entry_single_line_set(wd->entry, single_line);
557    if(single_line) {
558            if(!wd->scroller){
559                    wd->scroller = elm_scroller_add(obj);
560                    elm_scroller_bounce_set(wd->scroller, 0, 0);
561                    elm_scroller_policy_set(wd->scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
562                    elm_widget_sub_object_add(obj, wd->scroller);
563                    elm_scroller_content_min_limit(wd->scroller, 0, 1);
564            }
565            edje_object_part_unswallow(wd->base, wd->entry);
566            edje_object_part_swallow(wd->base, "elm.swallow.content", wd->scroller);
567            elm_scroller_content_set(wd->scroller, wd->entry);
568    }
569    else {
570            entry = elm_entry_add(obj);
571            elm_object_style_set(entry, "editfield");
572            evas_object_size_hint_weight_set(entry, 0, EVAS_HINT_EXPAND);
573            evas_object_size_hint_align_set(entry, 0, EVAS_HINT_FILL);
574            evas_object_event_callback_add(entry,
575                            EVAS_CALLBACK_CHANGED_SIZE_HINTS,
576                            _changed_size_hints, obj);
577            edje_object_part_swallow(wd->base, "elm.swallow.content", entry);
578            evas_object_smart_callback_add(entry, "changed", _entry_changed_cb, obj);
579            elm_widget_sub_object_add(obj, entry);
580            elm_entry_entry_set(entry, elm_entry_entry_get(wd->entry));
581            if(wd->scroller)
582                    edje_object_part_unswallow(wd->base, wd->scroller);
583            evas_object_del(wd->entry);
584            wd->entry = entry;
585            edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
586    }
587 }
588
589 /**
590  * Get the current entry object style(single-line or multi-line)
591  *
592  * @param obj The editfield object  
593  * @return 1 if single-line , 0 if multi-line
594  *
595  * @ingroup Editfield
596  */
597 EAPI Eina_Bool
598 elm_editfield_entry_single_line_get(Evas_Object *obj)
599 {
600    Widget_Data *wd = elm_widget_data_get(obj);
601    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;  
602    if (!wd || !wd->base)
603      return EINA_FALSE;
604    return wd->single_line;
605 }
606
607 /**
608  * Set enable user to clean all of text.
609  *
610  * @param obj The editfield object  
611  * @param visible If true, the eraser is visible and user can clean all of text by using eraser.  
612  * If false, the eraser is invisible.
613  *
614  * @ingroup Editfield
615  */
616 EAPI void
617 elm_editfield_eraser_set(Evas_Object *obj, Eina_Bool visible)
618 {
619    Widget_Data *wd = elm_widget_data_get(obj);
620    ELM_CHECK_WIDTYPE(obj, widtype);     
621    if (!wd || !wd->base)
622      return;
623    
624    wd->eraser_show = visible;
625
626    if (visible) 
627         edje_object_signal_emit(wd->base, "elm,state,eraser,show", "elm");
628    else 
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 }