ecore_imf_example: fix formatting
[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, "BackSpace"))
301      {
302         if (evas_textblock_cursor_char_prev(en->cursor))
303           evas_textblock_cursor_char_delete(en->cursor);
304
305         return;
306      }
307    else if (!strcmp(ev->key, "Delete") || 
308                     (!strcmp(ev->key, "KP_Delete") && !ev->string))
309      {
310         // FILLME
311      }
312    else if ((control) && (!strcmp(ev->key, "v")))
313      {
314         // ctrl + v
315         // FILLME
316      }
317    else if ((control) && (!strcmp(ev->key, "a")))
318      {
319         // ctrl + a
320         // FILLME
321      }
322    else if ((control) && (!strcmp(ev->key, "A")))
323      {
324         // ctrl + A
325         // FILLME
326      }
327    else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert")))))
328      {
329         // ctrl + c
330         // FILLME
331      }
332    else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m")))))
333      {
334         // ctrl + x
335         // FILLME
336      }
337    else if ((control) && (!strcmp(ev->key, "z")))
338      {
339         // ctrl + z (undo)
340         // FILLME
341      }
342    else if ((control) && (!strcmp(ev->key, "y")))
343      {
344         // ctrl + y (redo)
345         // FILLME
346      }
347    else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
348      {
349         // FILLME
350      }
351    else
352      {
353         if (ev->string) 
354           {
355              printf("key down string : %s\n", ev->string);
356              evas_object_textblock_text_markup_prepend(en->cursor, ev->string);
357           }
358      }
359
360    /* notify cursor information */
361    _imf_cursor_info_set(en);
362 }
363
364 static void
365 _key_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
366 {
367    Entry *en = data;
368    Evas_Event_Key_Up *ev = event_info;
369
370    if (!en) return;
371
372    if (en->imf_context)
373      {
374         Ecore_IMF_Event_Key_Up ecore_ev;
375
376         ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev);
377         if (ecore_imf_context_filter_event(en->imf_context,
378                                            ECORE_IMF_EVENT_KEY_UP,
379                                            (Ecore_IMF_Event *)&ecore_ev))
380           return;
381      }
382 }
383
384 static void
385 create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
386 {
387    if (!en) return;
388
389    /* create background for text input field */
390    en->rect = evas_object_rectangle_add(evas);
391    evas_object_color_set(en->rect, 150, 150, 150, 255); /* gray */
392    evas_object_move(en->rect, x, y);
393    evas_object_resize(en->rect, w, h);
394    evas_object_show(en->rect);
395
396    /* create text object for displaying text */
397    en->txt_obj = evas_object_textblock_add(evas);
398    evas_object_color_set(en->txt_obj, 0, 0, 0, 255);
399    evas_object_pass_events_set(en->txt_obj, EINA_TRUE);
400    evas_object_move(en->txt_obj, x, y);
401    evas_object_resize(en->txt_obj, w, h);
402    evas_object_show(en->txt_obj);
403
404    /* set style on textblock */
405    static const char *style_buf =
406       "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'"
407       "newline='br'"
408       "b='+ font=Sans:style=bold'";
409    en->txt_style = evas_textblock_style_new();
410    evas_textblock_style_set(en->txt_style, style_buf);
411    evas_object_textblock_style_set(en->txt_obj, en->txt_style);
412
413    /* create cursor */
414    en->cursor = evas_object_textblock_cursor_new(en->txt_obj);
415
416    /* create input context */
417    const char* default_id = ecore_imf_context_default_id_get();
418    if (!default_id)
419      return;
420
421    en->imf_context = ecore_imf_context_add(default_id);
422    ecore_imf_context_client_window_set(en->imf_context, (void *)ecore_evas_window_get(ecore_evas_ecore_evas_get(evas)));
423    ecore_imf_context_client_canvas_set(en->imf_context, evas);
424
425    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en);
426    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en);
427    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en);
428    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en);
429    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en);
430
431    en->have_preedit = EINA_FALSE;
432    en->preedit_start = NULL;
433    en->preedit_end = NULL;
434
435    /* register retrieve surrounding callback */
436    ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en);
437
438    /* register commit event callback */
439    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en);
440
441    /* register preedit changed event handler */
442    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en);
443
444    /* register surrounding delete event callback */
445    ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en);
446 }
447
448 static void
449 delete_input_field(Entry *en)
450 {
451    if (!en) return;
452
453    if (en->rect)
454      {
455         evas_object_del(en->rect);
456         en->rect = NULL;
457      }
458
459    if (en->cursor)
460      {
461         evas_textblock_cursor_free(en->cursor);
462         en->cursor = NULL;
463      }
464
465    if (en->preedit_start)
466      {
467         evas_textblock_cursor_free(en->preedit_start);
468         en->preedit_start = NULL;
469      }
470
471    if (en->preedit_end)
472      {
473         evas_textblock_cursor_free(en->preedit_end);
474         en->preedit_end = NULL;
475      }
476
477    if (en->txt_obj)
478      {
479         evas_object_del(en->txt_obj);
480         en->txt_obj = NULL;
481      }
482
483    if (en->txt_style)
484      {
485         evas_textblock_style_free(en->txt_style);
486         en->txt_style = NULL;
487      }
488
489    if (en->imf_context)
490      {
491         ecore_imf_context_del(en->imf_context);
492         en->imf_context = NULL;
493      }
494 }
495
496 int main(int argc, char *argv[])
497 {
498    Ecore_Evas* ee;
499    Evas* evas;
500    Entry en1, en2;
501
502    if (!ecore_evas_init()) 
503      {
504         fprintf(stderr, "failed to call ecore_evas_init()\n");
505         return EXIT_FAILURE;
506      }
507
508    ecore_imf_init();
509
510    // create a new window, with size=480x800 and default engine
511    ee = ecore_evas_new(NULL, 0, 0, 480, 800, NULL);
512
513    if (!ee) 
514      {
515         fprintf(stderr, "failed to call ecore_evas_new\n");
516         return EXIT_FAILURE;
517      }
518
519    ecore_evas_show(ee);
520
521    // get the canvas off just-created window
522    evas = ecore_evas_get(ee);
523    if (!evas) 
524      {
525         fprintf(stderr, "failed to ccall ecore_evas_get\n");
526         return EXIT_FAILURE;
527      }
528
529    // create input field rectangle
530    Evas_Object* bg = evas_object_rectangle_add(evas);
531    evas_object_move(bg, 0, 0);
532    evas_object_resize(bg, 480, 800);
533    evas_object_color_set(bg, 255, 255, 255, 255);
534    evas_object_show(bg);
535
536    evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
537    evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
538
539    // create input field 1
540    create_input_field(evas, &en1, 40, 60, 400, 80);
541
542    // create input field 2
543    create_input_field(evas, &en2, 40, 180, 400, 80);
544
545    // give focus to input field 1
546    evas_object_focus_set(en1.rect, EINA_TRUE);
547
548    ecore_main_loop_begin(); // begin mainloop
549
550    delete_input_field(&en1); // delete input field 1
551    delete_input_field(&en2); // delete input field 2
552
553    evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL);
554    evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL);
555
556    ecore_evas_free(ee);
557
558    ecore_imf_shutdown();
559    ecore_evas_shutdown();
560
561    return 0;
562 }