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