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