Merge branch 'master' of 165.213.180.234:/git/slp/pkgs/elementary
[framework/uifw/elementary.git] / src / lib / elm_editfield.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4 #include <Elementary.h>
5 #include "elm_priv.h"
6
7 /**
8  * @defgroup Editfield Editfield
9  * @ingroup Elementary
10  *
11  * This is a editfield. It can contain a simple label and icon objects.
12  */
13
14 #define ERASER_PADDING (10)
15
16 typedef struct _Widget_Data Widget_Data;
17
18 struct _Widget_Data
19 {
20    Evas_Object *base;   
21    Evas_Object *entry;
22    Evas_Object *ricon;
23    Evas_Object *licon;
24    Evas_Object *eraser;
25    const char *label;
26    const char *guide_text;
27    Eina_Bool needs_size_calc:1;
28    Eina_Bool show_guide_text:1;
29    Eina_Bool editing:1;
30    Eina_Bool single_line : 1;
31    Eina_Bool eraser_visible : 1;
32    Evas_Event_Mouse_Down down_ev;
33 };
34
35 static const char *widtype = NULL;
36 static void _del_hook(Evas_Object *obj);
37 static void _theme_hook(Evas_Object *obj);
38 static void _sizing_eval(Evas_Object *obj);
39 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
40 static void _on_focus_hook(void *data, Evas_Object *obj);
41 static Eina_Bool _empty_entry(Evas_Object *entry);
42 static void _eraser_drag_end(void *data, Evas_Object *obj, const char *emission, const char *source);
43 static void _eraser_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
44 static void _eraser_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
45 static void _eraser_init(Evas_Object *obj);
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, 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         wd->editing = EINA_FALSE;
65         edje_object_signal_emit(wd->base, "elm,state,text,fix", "elm"); 
66 /*
67         edje_object_signal_emit(wd->base, "elm,action,unfocus", "elm");
68         edje_object_signal_emit(wd->base, "elm,state,over,show", "elm");                
69         text = elm_entry_entry_get(wd->entry);
70         edje_object_part_text_set(wd->base, "elm.content.no.edit", text);
71         edje_object_signal_emit(wd->base, "elm,action,no,edit", "elm");         
72 */
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    if (!wd || !wd->base)
90      return;
91    _elm_theme_object_set(obj, wd->base, "editfield", "base", elm_widget_style_get(obj));
92    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
93    if(!wd->editing)
94      edje_object_signal_emit(wd->base, "elm,state,text,fix", "elm");    
95    if(wd->show_guide_text)
96      {
97         if(_empty_entry(wd->entry)) 
98           {
99              if(wd->guide_text) 
100                {
101                   edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
102                   edje_object_signal_emit(wd->base, "elm,state,guidetext,visible", "elm");
103                }
104           }
105      }     
106    if(wd->ricon)        
107      edje_object_part_swallow(wd->base, "right_icon", wd->ricon);
108    if(wd->licon)
109      edje_object_part_swallow(wd->base, "left_icon", wd->licon);
110 //   if(wd->eraser)
111 //   edje_object_part_swallow(wd->base, "eraser", wd->eraser);  
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, Evas_Object *obj, void *event_info)
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 Eina_Bool
158 _empty_entry(Evas_Object *entry)
159 {
160    const char* text;
161    char *strip_text;
162    int len = 0;
163
164    text = elm_scrolled_entry_entry_get(entry);
165    if(!text) return EINA_FALSE;
166    strip_text = elm_entry_markup_to_utf8(text);
167    if (strip_text) {
168         len = strlen(strip_text);
169         free(strip_text);
170    }
171    if(len == 0)
172      return EINA_TRUE;
173    else 
174      return EINA_FALSE;
175 }
176
177 static void
178 _entry_changed_cb(void *data, Evas_Object *obj, void* event_info)
179 {
180    const char *text;
181    Evas_Object *ef_obj = (Evas_Object *)data;
182    Widget_Data *wd = elm_widget_data_get(ef_obj);
183
184    if(!wd || !wd->base) return;
185
186    if(!_empty_entry(wd->entry)) 
187      {
188 //      text = elm_entry_entry_get(wd->entry);
189 //      edje_object_part_text_set(wd->base, "elm.content.no.edit", text);               
190         if(wd->guide_text) 
191           {
192              edje_object_signal_emit(wd->base, "elm,state,guidetext,hidden", "elm");
193              wd->show_guide_text = EINA_FALSE;
194           }
195      }
196 }
197
198 static void
199 _signal_mouse_clicked(void *data, Evas_Object *obj, const char *emission, const char *source)
200 {
201    Widget_Data *wd = elm_widget_data_get(data);
202    if(!wd || !wd->base) return;
203
204    if(strcmp(source, "left_icon") && strcmp(source, "right_icon")) // && strcmp(source, "over_change_bg"))
205      {
206         edje_object_signal_emit(wd->base, "elm,state,text,edit", "elm");
207 //      edje_object_signal_emit(wd->base, "elm,action,focus", "elm");   
208 //      edje_object_signal_emit(wd->base, "elm,state,over,hide", "elm");
209 //      edje_object_signal_emit(wd->base, "elm,action,edit", "elm");    
210         elm_widget_focus_set(wd->entry, EINA_TRUE);                     
211         elm_scrolled_entry_cursor_end_set(wd->entry);
212         
213         if(!_empty_entry(wd->entry)) 
214           {
215              if(wd->guide_text) 
216                {
217                   edje_object_signal_emit(wd->base, "elm,state,guidetext,hidden", "elm");
218                   wd->show_guide_text = EINA_FALSE;
219                }
220           }
221         evas_object_smart_callback_call(data, "clicked", NULL);
222         wd->editing = EINA_TRUE;
223      }
224 }
225
226
227 static void
228 _resize_cb(void *data, Evas *evas, Evas_Object *obj, void *event)
229 {
230    Widget_Data *wd = elm_widget_data_get(data); 
231    Evas_Coord w, h;
232    if (!wd || !wd->base) return;
233    evas_object_geometry_get(obj, NULL, NULL, NULL, &h);
234    evas_object_geometry_get(wd->eraser, NULL, NULL, &w, NULL);
235    evas_object_size_hint_min_set(wd->eraser, w, h - ERASER_PADDING);
236    evas_object_resize(wd->eraser, w, h - ERASER_PADDING);
237 }
238
239 static void
240 _eraser_drag_end(void *data, Evas_Object *obj, const char *emission, const char *source)
241 {
242    Widget_Data *wd = elm_widget_data_get(data);
243    if (!wd || !wd->base || !wd->entry) 
244      return;
245    elm_scrolled_entry_entry_set(wd->entry, "");
246    edje_object_part_text_set(wd->base, "elm.content.no.edit", "");
247 }
248
249 static void
250 _eraser_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
251 {
252         Widget_Data *wd = elm_widget_data_get(data);
253         if (!wd || !wd->base) 
254                 return; 
255         memcpy(&wd->down_ev, event_info, sizeof(Evas_Event_Mouse_Down));
256 }
257
258 static void
259 _eraser_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info)
260 {
261    Widget_Data *wd = elm_widget_data_get(data);
262    Evas_Event_Mouse_Up *ev = event_info;
263    Evas_Coord distance;
264    unsigned int dur;
265    double pos = 0.0;
266    if (!wd || !wd->base) 
267      return;
268    edje_object_part_drag_value_get(wd->base, "eraser", &pos, NULL);
269    if (pos == 1.0) 
270      {
271         edje_object_signal_emit(wd->base, "elm,state,eraser,flick", "elm");
272         return;
273      }
274    dur = ev->timestamp - wd->down_ev.timestamp;
275    distance = wd->down_ev.canvas.x - ev->canvas.x;      
276    if ((dur && dur > 1000) || (distance < 10 && distance > -10)) 
277      {
278         edje_object_signal_emit(wd->base, "elm,state,eraser,drag", "elm");
279         return;
280      }
281    if(((float)distance / dur) > 0.5)    
282      edje_object_signal_emit(wd->base, "elm,state,eraser,flick", "elm");
283    else
284      edje_object_signal_emit(wd->base, "elm,state,eraser,drag", "elm");
285 }
286
287 static void
288 _eraser_init(Evas_Object *obj)
289 {
290    Widget_Data *wd = elm_widget_data_get(obj);
291    if (!wd || !wd->base)
292      return;
293    wd->eraser = edje_object_add(evas_object_evas_get(obj));
294    elm_widget_sub_object_add(obj, wd->eraser);
295    _elm_theme_object_set(obj, wd->eraser, "editfield/eraser", "base", "default");
296    edje_object_part_swallow(wd->base, "eraser", wd->eraser);
297    evas_object_event_callback_add(wd->eraser, EVAS_CALLBACK_MOUSE_DOWN, _eraser_mouse_down, obj);
298    evas_object_event_callback_add(wd->eraser, EVAS_CALLBACK_MOUSE_UP, _eraser_mouse_up, obj);
299    edje_object_signal_callback_add(wd->base, "drag", "end", _eraser_drag_end, obj);
300 }
301
302 /**
303  * Add a new editfield object
304  *
305  * @param parent The parent object
306  * @return The new object or NULL if it cannot be created
307  *
308  * @ingroup Editfield
309  */
310 EAPI Evas_Object *
311 elm_editfield_add(Evas_Object *parent)
312 {
313    Evas_Object *obj;
314    Evas *e;
315    Widget_Data *wd;
316
317    wd = ELM_NEW(Widget_Data);
318    e = evas_object_evas_get(parent);
319    if (e == NULL)
320      return NULL;
321    obj = elm_widget_add(e);
322    ELM_SET_WIDTYPE(widtype, "editfield");
323    elm_widget_type_set(obj, "editfield");
324    elm_widget_sub_object_add(parent, obj);
325    elm_widget_data_set(obj, wd);
326    elm_widget_del_hook_set(obj, _del_hook);
327    elm_widget_theme_hook_set(obj, _theme_hook);
328    elm_widget_changed_hook_set(obj, _changed_hook);
329    elm_widget_on_focus_hook_set( obj, _on_focus_hook, NULL );
330    elm_widget_can_focus_set(obj, EINA_TRUE);
331
332    wd->base = edje_object_add(e);
333    _elm_theme_object_set(obj, wd->base, "editfield", "base", "default");
334    elm_widget_resize_object_set(obj, wd->base);
335    edje_object_signal_callback_add(wd->base, "mouse,clicked,1", "*", 
336          _signal_mouse_clicked, obj);
337    evas_object_event_callback_add(wd->base, EVAS_CALLBACK_RESIZE, _resize_cb, obj);
338
339    wd->show_guide_text = EINA_FALSE;
340    wd->single_line = EINA_TRUE;
341 //   wd->editing = EINA_FALSE;
342 //   wd->eraser_visible = EINA_FALSE;
343
344    wd->entry = elm_scrolled_entry_add(obj);
345    elm_scrolled_entry_single_line_set(wd->entry, EINA_TRUE);
346    elm_object_style_set(wd->entry, "editfield");
347    evas_object_size_hint_weight_set(wd->entry, 0, EVAS_HINT_EXPAND);
348    evas_object_size_hint_align_set(wd->entry, 0, EVAS_HINT_FILL);
349    evas_object_event_callback_add(wd->entry,
350          EVAS_CALLBACK_CHANGED_SIZE_HINTS,
351          _changed_size_hints, obj);
352    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->entry);
353    evas_object_smart_callback_add(wd->entry, "changed", _entry_changed_cb, obj);
354    elm_widget_sub_object_add(obj, wd->entry);
355
356    _sizing_eval(obj);
357
358    return obj;
359 }
360
361 /**
362  * Set the label of editfield
363  *
364  * @param obj The editfield object
365  * @param label The label text
366  *
367  * @ingroup Editfield
368  */
369 EAPI void
370 elm_editfield_label_set(Evas_Object *obj, const char *label)
371 {
372    Widget_Data *wd = elm_widget_data_get(obj);
373    ELM_CHECK_WIDTYPE(obj, widtype);
374    if (!wd || !wd->base) 
375      return;
376    if (wd->label) 
377      eina_stringshare_del(wd->label);
378    if (label) 
379      {
380         wd->label = eina_stringshare_add(label);
381         edje_object_signal_emit(wd->base, "elm,state,text,visible", "elm");
382         edje_object_signal_emit(wd->base, "elm,state,left,icon,hide", "elm");
383      }
384    else 
385      {
386         wd->label = NULL;
387         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
388         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
389      }
390    edje_object_message_signal_process(wd->base);
391    edje_object_part_text_set(wd->base, "elm.text", label);
392    _sizing_eval(obj);
393 }
394
395 /**
396  * Get the label used on the editfield object
397  *
398  * @param obj The editfield object
399  * @return label text
400  *
401  * @ingroup Editfield
402  */
403 EAPI const char*
404 elm_editfield_label_get(Evas_Object *obj)
405 {
406    Widget_Data *wd = elm_widget_data_get(obj);
407    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
408    if (!wd || !wd->base) 
409      return NULL;
410    return       wd->label;
411 }
412
413 /**
414  * Get the label used on the editfield object
415  *
416  * @param obj The editfield object
417  * @return label text
418  *
419  * @ingroup Editfield
420  */
421 EAPI void
422 elm_editfield_guide_text_set(Evas_Object *obj, const char *text)
423 {
424    Widget_Data *wd = elm_widget_data_get(obj);
425    ELM_CHECK_WIDTYPE(obj, widtype);
426    if (!wd || !wd->base)
427      return;
428    if (wd->guide_text) 
429      eina_stringshare_del(wd->guide_text);
430    if (text) 
431      {
432         wd->guide_text = eina_stringshare_add(text);
433         edje_object_part_text_set(wd->base, "elm.guidetext", wd->guide_text);
434         wd->show_guide_text = EINA_TRUE;
435      }
436    else
437      wd->guide_text = NULL;
438 }
439
440 /**
441  * Get the guidance text used on the editfield object
442  *
443  * @param obj The editfield object
444  * @return label text
445  *
446  * @ingroup Editfield
447  */
448 EAPI const char*
449 elm_editfield_guide_text_get(Evas_Object *obj)
450 {
451    Widget_Data *wd = elm_widget_data_get(obj);
452    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
453    if (!wd || !wd->base)
454      return NULL;
455    return wd->guide_text;
456 }
457
458 /**
459  * Get the entry of the editfield object
460  *
461  * @param obj The editfield object
462  * @return entry object
463  *
464  * @ingroup Editfield
465  */
466
467 EAPI Evas_Object *
468 elm_editfield_entry_get(Evas_Object *obj)
469 {
470    Widget_Data *wd = elm_widget_data_get(obj);
471    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
472    if (!wd)
473      return NULL;
474    return wd->entry;
475 }
476
477 /**
478  * Set the left side icon.
479  *
480  * @param obj The editfield object
481  * @param icon The icon object
482  * @return 1 if setting is done, 0 if there is no swallow part for the icon.
483  *
484  * @ingroup Editfield
485  */
486 EAPI Eina_Bool 
487 elm_editfield_left_icon_set(Evas_Object *obj, Evas_Object *icon)
488 {
489    Widget_Data *wd = elm_widget_data_get(obj);  
490    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;  
491    if (!wd || !wd->base)
492      return EINA_FALSE;
493    if ((wd->licon != icon) && (wd->licon))
494      elm_widget_sub_object_del(obj, wd->licon); 
495    if (icon)
496      {
497         if (!(edje_object_part_swallow(wd->base, "left_icon", icon)))
498           return EINA_FALSE;            
499         wd->licon = icon;
500         elm_widget_sub_object_add(obj, icon);
501         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
502               _changed_size_hints, obj);
503         edje_object_signal_emit(wd->base, "elm,state,left,icon,show", "elm");
504         edje_object_signal_emit(wd->base, "elm,state,text,hidden", "elm");
505         _sizing_eval(obj);
506      }  
507    return EINA_TRUE;
508 }
509
510 /**
511  * Get the left side icon
512  *
513  * @param obj The editfield object
514  * @return icon object
515  *
516  * @ingroup Editfield
517  */
518 EAPI Evas_Object *
519 elm_editfield_left_icon_get(Evas_Object *obj)
520 {
521    Widget_Data *wd = elm_widget_data_get(obj);          
522    ELM_CHECK_WIDTYPE(obj, widtype) NULL;        
523    if (!wd || !wd->base || !wd->licon) 
524      return NULL;
525    return wd->licon;
526 }
527
528 /**
529  * Set the right side icon.
530  *
531  * @param obj The editfield object
532  * @param icon The icon object
533  * @return 1 if setting is done, 0 if there is no swallow part for the icon.
534  *
535  * @ingroup Editfield
536  */
537 EAPI Eina_Bool 
538 elm_editfield_right_icon_set(Evas_Object *obj, Evas_Object *icon)
539 {
540    Widget_Data *wd = elm_widget_data_get(obj);
541    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
542    if (!wd || !wd->base)
543      return EINA_FALSE;
544    if ((wd->ricon != icon) && (wd->ricon))
545      elm_widget_sub_object_del(obj, wd->ricon); 
546    if (icon)
547      {
548         if ( !(edje_object_part_swallow(wd->base, "right_icon", icon)) )
549           return EINA_FALSE;                            
550         wd->ricon = icon;
551         elm_widget_sub_object_add(obj, icon);
552         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
553               _changed_size_hints, obj);                
554         edje_object_signal_emit(wd->base, "elm,state,right,icon,show", "elm");
555         _sizing_eval(obj);
556      }  
557    return EINA_TRUE;
558 }
559
560 /**
561  * Get the right side icon
562  *
563  * @param obj The editfield object
564  * @return icon object
565  *
566  * @ingroup Editfield
567  */
568 EAPI Evas_Object *
569 elm_editfield_right_icon_get(Evas_Object *obj)
570 {
571    Widget_Data *wd = elm_widget_data_get(obj);
572    ELM_CHECK_WIDTYPE(obj, widtype) NULL;        
573    if (!wd || !wd->base || !wd->ricon)
574      return NULL;
575    return wd->ricon;
576 }
577
578 /**
579  * Set entry object style as single-line or multi-line.
580  *
581  * @param obj The editfield object  
582  * @param single_line 1 if single-line , 0 if multi-line
583  *
584  * @ingroup Editfield
585  */
586 EAPI void
587 elm_editfield_entry_single_line_set(Evas_Object *obj, Eina_Bool single_line)
588 {
589    Widget_Data *wd = elm_widget_data_get(obj);
590    ELM_CHECK_WIDTYPE(obj, widtype);     
591    if (!wd || !wd->base) 
592      return;
593    wd->single_line = single_line;
594    elm_scrolled_entry_single_line_set(wd->entry, single_line);
595 /*   if(wd->single_line && wd->eraser_visible)
596    edje_object_signal_emit(wd->base, "elm,state,eraser,show", "elm");
597    else
598      edje_object_signal_emit(wd->base, "elm,state,eraser,hide", "elm");
599 */
600 }
601
602 /**
603  * Set enable user to clean all of text.
604  *
605  * @param obj The editfield object  
606  * @param visible If true, the eraser is visible and user can clean all of text by using eraser.  
607  * If false, the eraser is invisible.
608  *
609  * @ingroup Editfield
610  */
611 EAPI void
612 elm_editfield_eraser_set(Evas_Object *obj, Eina_Bool visible)
613 {
614    Widget_Data *wd = elm_widget_data_get(obj);
615    ELM_CHECK_WIDTYPE(obj, widtype);     
616    if (!wd || !wd->base)
617      return;
618    wd->eraser_visible = visible;
619    _eraser_init(obj);   
620    if(wd->single_line && wd->eraser_visible)
621      edje_object_signal_emit(wd->base, "elm,state,eraser,show", "elm");
622    else
623      edje_object_signal_emit(wd->base, "elm,state,eraser,hide", "elm");
624 }