add ecore_imf_example code. I will add Makefile.am in example directory later
[framework/uifw/ecore.git] / src / examples / ecore_imf_example.c
1 /**
2  * Ecore example illustrating how to use ecore imf.
3  *
4  * @verbatim
5  * gcc -o ecore_imf_example ecore_imf_example.c `pkg-config --cflags --libs ecore evas ecore-evas ecore-imf ecore-imf-evas`
6  * @endverbatim
7  */
8
9 #include <Ecore.h>
10 #include <Ecore_Evas.h>
11 #include <Ecore_IMF.h>
12 #include <Ecore_IMF_Evas.h>
13 #include <Evas.h>
14 #include <stdio.h>
15
16 typedef struct _Entry Entry;
17
18 struct _Entry
19 {
20     Evas_Object *rect;
21     Evas_Object *txt_obj;
22     Evas_Textblock_Style *txt_style;
23     Evas_Textblock_Cursor *cursor;
24     Evas_Textblock_Cursor *preedit_start;
25     Evas_Textblock_Cursor *preedit_end;
26     Ecore_IMF_Context *imf_context;
27     Eina_Bool have_preedit : 1;
28 };
29
30 static void _mouse_up_cb(void *data, Evas *e, Evas_Object *o, void *event_info)
31 {
32     Entry *en = data;
33     if (!en) return;
34
35     evas_object_focus_set(en->rect, EINA_TRUE);
36 }
37
38 static void _entry_focus_in_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
39 {
40     Entry *en = data;
41     if (!en) return;
42
43     if (en->imf_context)
44         ecore_imf_context_focus_in(en->imf_context);
45 }
46
47 static void _entry_focus_out_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
48 {
49     Entry *en = data;
50     if (!en) return;
51
52     if (en->imf_context)
53     {
54         ecore_imf_context_reset(en->imf_context);
55         ecore_imf_context_focus_out(en->imf_context);
56     }
57 }
58
59 static void
60 _canvas_focus_in_cb(void *data, Evas *e, void *event_info)
61 {
62     Evas_Object *obj = evas_focus_get(e);
63     if (obj)
64         _entry_focus_in_cb(obj, NULL, NULL, NULL);
65 }
66
67 static void
68 _canvas_focus_out_cb(void *data, Evas *e, void *event_info)
69 {
70     Evas_Object *obj = evas_focus_get(e);
71     if (obj)
72         _entry_focus_out_cb(obj, NULL, NULL, NULL);
73 }
74
75 static void
76 _imf_cursor_info_set(Entry *en)
77 {
78     Evas_Coord x, y, w, h;
79     Evas_Coord cx, cy, cw, ch; // cursor geometry
80     int cursor_pos; // cursor position in chars (Not bytes)
81
82     if (!en) return;
83
84      // Get cursor geometry
85     evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h);
86     evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, NULL, EVAS_TEXTBLOCK_CURSOR_BEFORE);
87
88     // Get cursor position
89     cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
90
91     ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos);
92     ecore_imf_context_cursor_location_set(en->imf_context, x+cx, y+cy, cw, ch);
93 }
94
95 static void _preedit_del(Entry *en)
96 {
97     if (!en || !en->have_preedit) return;
98     if (!en->preedit_start || !en->preedit_end) return;
99     if (!evas_textblock_cursor_compare(en->preedit_start, en->preedit_end)) return;
100
101     /* delete the preedit characters */
102     evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end);
103 }
104
105 static void
106 _preedit_clear(Entry *en)
107 {
108     if (en->preedit_start)
109     {
110         evas_textblock_cursor_free(en->preedit_start);
111         en->preedit_start = NULL;
112     }
113
114     if (en->preedit_end)
115     {
116         evas_textblock_cursor_free(en->preedit_end);
117         en->preedit_end = NULL;
118     }
119
120     en->have_preedit = EINA_FALSE;
121 }
122
123 static Eina_Bool
124 _ecore_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx, char **text, int *cursor_pos)
125 {
126     /* This callback will be called when the Input Method Context module requests the surrounding context. */
127     Entry *en = data;
128     const char *str;
129
130     if (!en) return;
131
132     str = evas_object_textblock_text_markup_get(en->txt_obj);
133     *text = str ? strdup(str) : strdup("");
134
135     /* Get the current position of cursor */
136     if (cursor_pos)
137         *cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
138
139     return EINA_TRUE;
140 }
141
142 static void
143 _ecore_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx, void *event_info)
144 {
145     /* called when the input method needs to delete all or part of the context surrounding the cursor */
146     Entry *en = data;
147     Ecore_IMF_Event_Delete_Surrounding *ev = event_info;
148     Evas_Textblock_Cursor *del_start, *del_end;
149     int cursor_pos;
150
151     if (!en) return;
152
153     cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
154
155     del_start = evas_object_textblock_cursor_new(en->txt_obj);
156     evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset);
157
158     del_end = evas_object_textblock_cursor_new(en->txt_obj);
159     evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars);
160
161     /* implement function to delete character(s) from 'cursor_pos+ev->offset' cursor position to 'cursor_pos + ev->offset + ev->n_chars' */
162     evas_textblock_cursor_range_delete(del_start, del_end);
163
164     evas_textblock_cursor_free(del_start);
165     evas_textblock_cursor_free(del_end);
166 }
167
168 static void _ecore_imf_event_commit_cb (void *data, Ecore_IMF_Context *ctx, void *event_info)
169 {
170     Entry *en = data;
171     char *commit_str = (char *)event_info;
172     if (!en) return;
173
174     /* Delete preedit string */
175     _preedit_del(en);
176     _preedit_clear(en);
177
178     printf ("commit string : %s\n", commit_str);
179
180     evas_object_textblock_text_markup_prepend(en->cursor, commit_str);
181
182     /* Notify cursor information */
183     _imf_cursor_info_set(en);
184
185     return;
186 }
187
188 static void _ecore_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx, void *event_info)
189 {
190     /* example how to get preedit string */
191     Entry *en = data;
192     char *preedit_string;
193     int cursor_pos;
194     Eina_List *attrs = NULL;
195     Eina_List *l;
196     Ecore_IMF_Preedit_Attr *attr;
197     Ecore_IMF_Context * imf_context = ctx;
198     int preedit_start_pos, preedit_end_pos;
199     int i;
200     Eina_Bool preedit_end_state = EINA_FALSE;
201
202     if (!en) return;
203
204     /* Get preedit string, attributes */
205     ecore_imf_context_preedit_string_with_attributes_get(imf_context, &preedit_string, &attrs, &cursor_pos);
206     printf("preedit string : %s\n", preedit_string);
207
208     if (!strcmp(preedit_string, ""))
209         preedit_end_state = EINA_TRUE;
210
211     /* delete preedit */
212     _preedit_del(en);
213
214     preedit_start_pos = evas_textblock_cursor_pos_get(en->cursor);
215
216     /* insert preedit character(s) */
217     if (strlen(preedit_string) > 0)
218     {
219         if (attrs)
220         {
221             EINA_LIST_FOREACH(attrs, l, attr)
222             {
223                 if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB1) // style type
224                 {
225                     /* apply appropriate style such as underline */
226                 }
227                 else if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB2 || attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3)
228                 {
229                     /* apply appropriate style such as underline */
230                 }
231             }
232
233             /* insert code to display preedit string in your editor */
234             evas_object_textblock_text_markup_prepend(en->cursor, preedit_string);
235         }
236     }
237
238     if (!preedit_end_state)
239     {
240         /* set preedit start cursor */
241         if (!en->preedit_start)
242             en->preedit_start = evas_object_textblock_cursor_new(en->txt_obj);
243         evas_textblock_cursor_copy(en->cursor, en->preedit_start);
244
245         /* set preedit end cursor */
246         if (!en->preedit_end)
247             en->preedit_end = evas_object_textblock_cursor_new(en->txt_obj);
248         evas_textblock_cursor_copy(en->cursor, en->preedit_end);
249
250         preedit_end_pos = evas_textblock_cursor_pos_get(en->cursor);
251
252         for (i = 0; i < (preedit_end_pos - preedit_start_pos); i++)
253         {
254             evas_textblock_cursor_char_prev(en->preedit_start);
255         }
256
257         en->have_preedit = EINA_TRUE;
258
259         /* set cursor position */
260         evas_textblock_cursor_pos_set(en->cursor, preedit_start_pos + cursor_pos);
261     }
262
263     /* Notify cursor information */
264     _imf_cursor_info_set(en);
265
266     EINA_LIST_FREE(attrs, attr) free(attr);
267
268     free(preedit_string);
269 }
270
271 static void
272 _key_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
273 {
274     Entry *en = data;
275     Evas_Event_Key_Down *ev = event_info;
276     Eina_Bool control, alt, shift;
277     Eina_Bool multiline;
278     Eina_Bool cursor_changed;
279     if (!en) return;
280     if (!ev->key) return;
281
282     if (en->imf_context)
283     {
284         Ecore_IMF_Event_Key_Down ecore_ev;
285         ecore_imf_evas_event_key_down_wrap(ev, &ecore_ev);
286         if (ecore_imf_context_filter_event(en->imf_context,
287                     ECORE_IMF_EVENT_KEY_DOWN,
288                     (Ecore_IMF_Event *)&ecore_ev))
289             return;
290     }
291
292     control = evas_key_modifier_is_set(ev->modifiers, "Control");
293     alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
294     shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
295
296     if ((!strcmp(ev->keyname, "Escape")) ||
297             (!strcmp(ev->keyname, "Return")) || (!strcmp(ev->keyname, "KP_Enter")))
298         ecore_imf_context_reset(en->imf_context);
299
300     if (!strcmp(ev->key, "Escape"))
301     {
302         // dead keys here. Escape for now (should emit these)
303         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
304     }
305     else if (!strcmp(ev->key, "Up") || !strcmp(ev->key, "KP_Up"))
306     {
307         // FILLME
308     }
309     else if (!strcmp(ev->key, "Down") || !strcmp(ev->key, "KP_Down"))
310     {
311         // FILLME
312     }
313     else if (!strcmp(ev->key, "Left") || !strcmp(ev->key, "KP_Left"))
314     {
315         // FILLME
316     }
317     else if (!strcmp(ev->key, "Right") || !strcmp(ev->key, "KP_Right"))
318     {
319         // FILLME
320     }
321     else if (!strcmp(ev->key, "BackSpace"))
322     {
323         if (evas_textblock_cursor_char_prev(en->cursor))
324             evas_textblock_cursor_char_delete(en->cursor);
325
326         return;
327     }
328     else if (!strcmp(ev->key, "Delete") || !strcmp(ev->key, "KP_Delete"))
329     {
330         // FILLME
331     }
332     else if (!strcmp(ev->key, "Home") || !strcmp(ev->key, "KP_Home"))
333     {
334         // FILLME
335     }
336     else if (!strcmp(ev->key, "End") || !strcmp(ev->key, "KP_End"))
337     {
338         // FILLME
339     }
340     else if ((control) && (!strcmp(ev->key, "v")))
341     {
342         // ctrl + v
343         // FILLME
344     }
345     else if ((control) && (!strcmp(ev->key, "a")))
346     {
347         // ctrl + a
348         // FILLME
349     }
350     else if ((control) && (!strcmp(ev->key, "A")))
351     {
352         // ctrl + A
353         // FILLME
354     }
355     else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert")))))
356     {
357         // ctrl + c
358         // FILLME
359     }
360     else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m")))))
361     {
362         // ctrl + x
363     }
364     else if ((control) && (!strcmp(ev->key, "z")))
365     {
366         // ctrl + z (undo)
367         // FILLME
368     }
369     else if ((control) && (!strcmp(ev->key, "y")))
370     {
371
372         // ctrl + y (redo)
373         // FILLME
374     }
375     else if (!strcmp(ev->key, "Tab"))
376     {
377         // FILLME
378     }
379     else if ((!strcmp(ev->key, "ISO_Left_Tab")) && (multiline))
380     {
381         // remove a tab
382     }
383     else if (!strcmp(ev->key, "Prior") || !strcmp(ev->key, "KP_Prior"))
384     {
385         // FILLME
386     }
387     else if (!strcmp(ev->key, "Next") || !strcmp(ev->key, "KP_Next"))
388     {
389         // FILLME
390     }
391     else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
392     {
393         // FILLME
394     }
395     else
396     {
397         if (ev->string) {
398             printf("key down string : %s\n", ev->string);
399             evas_object_textblock_text_markup_prepend(en->cursor, ev->string);
400         }
401     }
402
403     /* Notify cursor information */
404     _imf_cursor_info_set(en);
405 }
406
407 static void
408 _key_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
409 {
410     Entry *en = data;
411     Evas_Event_Key_Up *ev = event_info;
412
413     if (!en) return;
414
415     if (en->imf_context)
416     {
417         Ecore_IMF_Event_Key_Up ecore_ev;
418
419         ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev);
420         if (ecore_imf_context_filter_event(en->imf_context,
421                     ECORE_IMF_EVENT_KEY_UP,
422                     (Ecore_IMF_Event *)&ecore_ev))
423             return;
424     }
425 }
426
427 static void
428 create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
429 {
430     if (!en) return;
431
432     /* Create background for text input field */
433     en->rect = evas_object_rectangle_add(evas);
434     evas_object_size_hint_weight_set(en->rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
435     evas_object_color_set(en->rect, 150, 150, 150, 255); /* gray */
436     evas_object_move(en->rect, x, y);
437     evas_object_resize(en->rect, w, h);
438     evas_object_show(en->rect);
439
440     /* Create text object for displaying text */
441     en->txt_obj = evas_object_textblock_add(evas);
442     evas_object_color_set(en->txt_obj, 0, 0, 0, 255);
443     evas_object_pass_events_set(en->txt_obj, EINA_TRUE);
444     evas_object_move(en->txt_obj, x, y);
445     evas_object_resize(en->txt_obj, w, h);
446     evas_object_show(en->txt_obj);
447
448     /* Set style on textblock */
449     static const char *style_buf =
450         "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'"
451         "newline='br'"
452         "b='+ font=Sans:style=bold'";
453     en->txt_style = evas_textblock_style_new();
454     evas_textblock_style_set(en->txt_style, style_buf);
455     evas_object_textblock_style_set(en->txt_obj, en->txt_style);
456
457     /* Create cursor */
458     en->cursor = evas_object_textblock_cursor_new(en->txt_obj);
459
460     /* Create input context */
461     const char* defaultContextID = ecore_imf_context_default_id_get();
462     if (!defaultContextID)
463         return;
464
465     en->imf_context = ecore_imf_context_add(defaultContextID);
466     ecore_imf_context_client_window_set(en->imf_context, (void *)ecore_evas_window_get(ecore_evas_ecore_evas_get(evas)));
467     ecore_imf_context_client_canvas_set(en->imf_context, evas);
468
469     evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en);
470     evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en);
471     evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en);
472     evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en);
473     evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en);
474
475     en->have_preedit = EINA_FALSE;
476     en->preedit_start = NULL;
477     en->preedit_end = NULL;
478
479     /* register retrieve surrounding callback */
480     /* This is used for autocapital or autoperiod */
481     ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en);
482
483     /* register commit event callback */
484     ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en);
485
486     /* register preedit changed event handler */
487     ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en);
488
489     /* register surrounding delete event callback */
490     /* This is used for autocapital or autoperiod */
491     ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en);
492 }
493
494 static void
495 delete_input_field(Entry *en)
496 {
497     if (!en) return;
498
499     if (en->rect)
500     {
501         evas_object_del(en->rect);
502         en->rect = NULL;
503     }
504
505     if (en->cursor)
506     {
507         evas_textblock_cursor_free(en->cursor);
508         en->cursor = NULL;
509     }
510
511     if (en->preedit_start)
512     {
513         evas_textblock_cursor_free(en->preedit_start);
514         en->preedit_start = NULL;
515     }
516
517     if (en->preedit_end)
518     {
519         evas_textblock_cursor_free(en->preedit_end);
520         en->preedit_end = NULL;
521     }
522
523     if (en->txt_obj)
524     {
525         evas_object_del(en->txt_obj);
526         en->txt_obj = NULL;
527     }
528
529     if (en->txt_style)
530     {
531         evas_textblock_style_free(en->txt_style);
532         en->txt_style = NULL;
533     }
534
535     if (en->imf_context)
536     {
537         ecore_imf_context_del(en->imf_context);
538         en->imf_context = NULL;
539     }
540 }
541
542 int main(int argc, char *argv[])
543 {
544     Ecore_Evas* ee;
545     Evas* evas;
546     Entry en1, en2;
547
548     if (!ecore_evas_init()) {
549         fprintf(stderr, "failed to call ecore_evas_init()\n");
550         return EXIT_FAILURE;
551     }
552
553     ecore_imf_init();
554     // Create a new window, with size=480x800 and default engine
555     ee = ecore_evas_new(NULL, 0, 0, 480, 800, NULL);
556
557     if (!ee) {
558         fprintf(stderr, "something went wrong... :(\n");
559         return EXIT_FAILURE;
560     }
561
562     ecore_evas_show(ee);
563
564     // Get the canvas off just-created window
565     evas = ecore_evas_get(ee);
566     if (!evas) {
567         fprintf(stderr, "something went wrong... :(\n");
568         return EXIT_FAILURE;
569     }
570
571     // Create input field rectangle
572     Evas_Object* bg = evas_object_rectangle_add(evas);
573     evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
574     evas_object_move(bg, 0, 0);
575     evas_object_resize(bg, 480, 800);
576     evas_object_color_set(bg, 255, 255, 255, 255);
577     evas_object_show(bg);
578
579     evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
580     evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
581
582     // create input field 1
583     create_input_field(evas, &en1, 40, 60, 400, 80);
584
585     // create input field 2
586     create_input_field(evas, &en2, 40, 180, 400, 80);
587
588     // Give focus to input field 1
589     evas_object_focus_set(en1.rect, EINA_TRUE);
590
591     ecore_main_loop_begin(); // begin mainloop
592
593     delete_input_field(&en1); // delete input field 1
594     delete_input_field(&en2); // delete input field 2
595
596     evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
597     evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
598
599     ecore_evas_free(ee);
600
601     ecore_imf_shutdown();
602     ecore_evas_shutdown();
603
604     return 0;
605 }