b297ec966b3e25627c35ce6f078f6e9e1a415b42
[framework/uifw/e17.git] / src / modules / illume-keyboard / e_kbd_buf.c
1 #include "e.h"
2 #include "e_kbd_buf.h"
3 #include "e_kbd_dict.h"
4
5 static E_Kbd_Buf_Layout *
6 _e_kbd_buf_new(void)
7 {
8    E_Kbd_Buf_Layout *kbl;
9    
10    kbl = E_NEW(E_Kbd_Buf_Layout, 1);
11    kbl->ref =1;
12    return kbl;
13 }
14
15 static void
16 _e_kbd_buf_layout_ref(E_Kbd_Buf_Layout *kbl)
17 {
18    kbl->ref++;
19 }
20
21 static void
22 _e_kbd_buf_layout_unref(E_Kbd_Buf_Layout *kbl)
23 {
24    kbl->ref--;
25    if (kbl->ref > 0) return;
26    while (kbl->keys)
27      {
28         E_Kbd_Buf_Key *ky;
29         
30         ky = kbl->keys->data;
31         if (ky->key) eina_stringshare_del(ky->key);
32         if (ky->key_shift) eina_stringshare_del(ky->key_shift);
33         if (ky->key_capslock) eina_stringshare_del(ky->key_capslock);
34         free(ky);
35         kbl->keys = eina_list_remove_list(kbl->keys, kbl->keys);
36      }
37    free(kbl);
38 }
39
40 static void
41 _e_kbd_buf_string_matches_clear(E_Kbd_Buf *kb)
42 {
43    while (kb->string_matches)
44      {
45         if (kb->string_matches->data)
46           eina_stringshare_del(kb->string_matches->data);
47         kb->string_matches = eina_list_remove_list(kb->string_matches, kb->string_matches);
48      }
49 }
50
51 static void
52 _e_kbd_buf_actual_string_clear(E_Kbd_Buf *kb)
53 {
54    if (kb->actual_string) eina_stringshare_del(kb->actual_string);
55    kb->actual_string = NULL;
56 }
57
58 static E_Kbd_Buf_Key *
59 _e_kbd_buf_at_coord_get(E_Kbd_Buf *kb __UNUSED__, E_Kbd_Buf_Layout *kbl, int x, int y)
60 {
61    Eina_List *l;
62    
63    for (l = kbl->keys; l; l = l->next)
64      {
65         E_Kbd_Buf_Key *ky;
66         
67         ky = l->data;
68         if (ky->key)
69           {
70              if ((x >= ky->x) && (y >= ky->y) &&
71                  (x < (ky->x + ky->w)) && (y < (ky->y + ky->h)))
72                return ky;
73           }
74      }
75    return NULL;
76 }
77
78 static E_Kbd_Buf_Key *
79 _e_kbd_buf_closest_get(E_Kbd_Buf *kb __UNUSED__, E_Kbd_Buf_Layout *kbl, int x, int y)
80 {
81    Eina_List *l;
82    E_Kbd_Buf_Key *ky_closest = NULL;
83    int dist_closest = 0x7fffffff;
84    
85    for (l = kbl->keys; l; l = l->next)
86      {
87         E_Kbd_Buf_Key *ky;
88         int dist, dx, dy;
89         
90         ky = l->data;
91         if (ky->key)
92           {
93              dx = x - (ky->x + (ky->w / 2));
94              dy = y - (ky->y + (ky->h / 2));
95              dist = (dx * dx) + (dy * dy);
96              if (dist < dist_closest)
97                {
98                   ky_closest = ky;
99                   dist_closest = dist;
100                }
101           }
102      }
103    return ky_closest;
104 }
105
106 static const char *
107 _e_kbd_buf_keystroke_key_string_get(E_Kbd_Buf *kb __UNUSED__, E_Kbd_Buf_Keystroke *ks, E_Kbd_Buf_Key *ky)
108 {
109    const char *str = NULL;
110    
111    if ((ky) && (ky->key))
112      {
113         if (ks->shift)
114           {
115              if (ky->key_shift) str = ky->key_shift;
116              else str = ky->key;
117           }
118         else if (ks->capslock)
119           {
120              if (ky->key_capslock) str = ky->key_capslock;
121              else str = ky->key;
122           }
123         else str = ky->key;
124      }
125    return str;
126 }
127
128 static const char *
129 _e_kbd_buf_keystroke_string_get(E_Kbd_Buf *kb, E_Kbd_Buf_Keystroke *ks)
130 {
131    const char *str = NULL;
132    
133    if (ks->key) str = ks->key;
134    else
135      {
136         E_Kbd_Buf_Key *ky;
137         
138         ky = _e_kbd_buf_at_coord_get(kb, ks->layout, ks->x, ks->y);
139         if (!ky) ky = _e_kbd_buf_closest_get(kb, ks->layout, ks->x, ks->y);
140         str = _e_kbd_buf_keystroke_key_string_get(kb, ks, ky);
141      }
142    return str;
143 }
144
145 static void
146 _e_kbd_buf_actual_string_update(E_Kbd_Buf *kb)
147 {
148    Eina_List *l;
149    char *actual = NULL;
150    int actual_len = 0;
151    int actual_size = 0;
152    
153    _e_kbd_buf_actual_string_clear(kb);
154    for (l = kb->keystrokes; l; l = l->next)
155      {
156         E_Kbd_Buf_Keystroke *ks;
157         const char *str;
158         
159         ks = l->data;
160         str = _e_kbd_buf_keystroke_string_get(kb, ks);
161         if (str)
162           {
163              if ((actual_len + strlen(str) + 1) > actual_size)
164                {
165                   actual_size += 64;
166                   actual = realloc(actual, actual_size);
167                }
168              strcpy(actual + actual_len, str);
169              actual_len += strlen(str);
170           }
171      }
172    if (actual)
173      {
174         kb->actual_string = eina_stringshare_add(actual);
175         if (actual) free(actual);
176      }
177 }
178
179 static const char *
180 _e_kbd_buf_matches_find(Eina_List *matches, const char *s)
181 {
182    Eina_List *l;
183    
184    for (l = matches; l; l = l->next)
185      {
186         if (!strcmp(l->data, s)) return s;
187      }
188    return NULL;
189 }
190
191 static void
192 _e_kbd_buf_matches_update(E_Kbd_Buf *kb)
193 {
194    const char *word;
195    int pri, i;
196    E_Kbd_Dict *dicts[3];
197
198    _e_kbd_buf_string_matches_clear(kb);
199    dicts[0] = kb->dict.personal;
200    dicts[1] = kb->dict.sys;
201    dicts[2] = kb->dict.data;
202    for (i = 0; i < 3; i++)
203      {
204         if (!dicts[i]) continue;
205         e_kbd_dict_matches_lookup(dicts[i]);
206         e_kbd_dict_matches_first(dicts[i]);
207         for (;;)
208           {
209              word = e_kbd_dict_matches_match_get(dicts[i], &pri);
210              if (!word) break;
211              if (!_e_kbd_buf_matches_find(kb->string_matches, word))
212                kb->string_matches = eina_list_append(kb->string_matches,
213                                                      eina_stringshare_add(word));
214              e_kbd_dict_matches_next(dicts[i]);
215           }
216      }
217 }
218
219 static Eina_Bool
220 _e_kbd_buf_cb_data_dict_reload(void *data)
221 {
222    E_Kbd_Buf *kb;
223    char buf[PATH_MAX];
224
225    kb = data;
226    kb->dict.data_reload_delay = NULL;
227    e_kbd_buf_clear(kb);
228    if (kb->dict.data) e_kbd_dict_free(kb->dict.data);
229    e_user_dir_concat_static(buf, "dicts-dynamic/data.dic");
230    kb->dict.data = e_kbd_dict_new(buf);
231    return ECORE_CALLBACK_CANCEL;
232 }
233
234 static void
235 _e_kbd_buf_cb_data_dict_change(void *data, Ecore_File_Monitor *em __UNUSED__, Ecore_File_Event event __UNUSED__, const char *path __UNUSED__)
236 {
237    E_Kbd_Buf *kb;
238    
239    kb = data;
240    if (kb->dict.data_reload_delay) ecore_timer_del(kb->dict.data_reload_delay);
241    kb->dict.data_reload_delay = ecore_timer_add(2.0, _e_kbd_buf_cb_data_dict_reload, kb);
242 }
243     
244 EAPI E_Kbd_Buf *
245 e_kbd_buf_new(const char *sysdicts, const char *dict)
246 {
247    E_Kbd_Buf *kb;
248    char buf[PATH_MAX];
249
250    kb = E_NEW(E_Kbd_Buf, 1);
251    if (!kb) return NULL;
252    kb->sysdicts = eina_stringshare_add(sysdicts);
253
254    e_user_dir_concat_static(buf, "dicts");
255    if (!ecore_file_exists(buf)) ecore_file_mkpath(buf);
256
257    e_user_dir_snprintf(buf, sizeof(buf), "dicts/%s", dict);
258    kb->dict.sys = e_kbd_dict_new(buf);
259    if (!kb->dict.sys)
260      {
261         snprintf(buf, sizeof(buf), "%s/dicts/%s", kb->sysdicts, dict);
262         kb->dict.sys = e_kbd_dict_new(buf);
263      }
264
265    e_user_dir_concat_static(buf, "dicts-dynamic");
266    if (!ecore_file_exists(buf)) ecore_file_mkpath(buf);
267
268    e_user_dir_concat_static(buf, "dicts-dynamic/personal.dic");
269    kb->dict.personal = e_kbd_dict_new(buf);
270    if (!kb->dict.personal)
271      {
272         FILE *f;
273         
274         f = fopen(buf, "w");
275         if (f)
276           {
277              fprintf(f, "\n");
278              fclose(f);
279           }
280         kb->dict.personal = e_kbd_dict_new(buf);
281      }
282    e_user_dir_concat_static(buf, "dicts-dynamic/data.dic");
283    kb->dict.data = e_kbd_dict_new(buf);
284    kb->dict.data_monitor = 
285      ecore_file_monitor_add(buf, _e_kbd_buf_cb_data_dict_change, kb);
286    return kb;
287 }
288
289 EAPI void
290 e_kbd_buf_free(E_Kbd_Buf *kb)
291 {
292    e_kbd_buf_clear(kb);
293    e_kbd_buf_layout_clear(kb);
294    e_kbd_buf_lookup_cancel(kb);
295    eina_stringshare_del(kb->sysdicts);
296    if (kb->dict.sys) e_kbd_dict_free(kb->dict.sys);
297    if (kb->dict.personal) e_kbd_dict_free(kb->dict.personal);
298    if (kb->dict.data) e_kbd_dict_free(kb->dict.data);
299    if (kb->dict.data_monitor) ecore_file_monitor_del(kb->dict.data_monitor);
300    if (kb->dict.data_reload_delay) ecore_timer_del(kb->dict.data_reload_delay);
301    free(kb);
302 }
303
304 EAPI void
305 e_kbd_buf_dict_set(E_Kbd_Buf *kb, const char *dict)
306 {
307    char buf[PATH_MAX];
308
309    e_kbd_buf_clear(kb);
310
311    if (kb->dict.sys) e_kbd_dict_free(kb->dict.sys);
312
313    e_user_dir_concat_static(buf, "dicts");
314    if (!ecore_file_exists(buf)) ecore_file_mkpath(buf);
315
316    e_user_dir_snprintf(buf, sizeof(buf), "dicts/%s", dict);
317    kb->dict.sys = e_kbd_dict_new(buf);
318    if (!kb->dict.sys)
319      {
320         snprintf(buf, sizeof(buf), "%s/dicts/%s", kb->sysdicts, dict);
321         kb->dict.sys = e_kbd_dict_new(buf);
322      }
323 }
324
325 EAPI void
326 e_kbd_buf_clear(E_Kbd_Buf *kb)
327 {
328    e_kbd_buf_lookup_cancel(kb);
329    while (kb->keystrokes)
330      {
331         E_Kbd_Buf_Keystroke *ks;
332         
333         ks = kb->keystrokes->data;
334         if (ks->key) eina_stringshare_del(ks->key);
335         _e_kbd_buf_layout_unref(ks->layout);
336         free(ks);
337         kb->keystrokes = eina_list_remove_list(kb->keystrokes, kb->keystrokes);
338      }
339    _e_kbd_buf_string_matches_clear(kb);
340    if (kb->dict.sys) e_kbd_dict_word_letter_clear(kb->dict.sys);
341    if (kb->dict.personal) e_kbd_dict_word_letter_clear(kb->dict.personal);
342    if (kb->dict.data) e_kbd_dict_word_letter_clear(kb->dict.data);
343    _e_kbd_buf_actual_string_clear(kb);
344 }
345
346 EAPI void
347 e_kbd_buf_layout_clear(E_Kbd_Buf *kb)
348 {
349    if (kb->layout)
350      {
351         _e_kbd_buf_layout_unref(kb->layout);
352         kb->layout = NULL;
353      }
354 }
355
356 EAPI void
357 e_kbd_buf_layout_size_set(E_Kbd_Buf *kb, int w, int h)
358 {
359    if (!kb->layout) kb->layout = _e_kbd_buf_new();
360    if (!kb->layout) return;
361    kb->layout->w = w;
362    kb->layout->h = h;
363 }
364
365 EAPI void
366 e_kbd_buf_layout_fuzz_set(E_Kbd_Buf *kb, int fuzz)
367 {
368    if (!kb->layout) kb->layout = _e_kbd_buf_new();
369    if (!kb->layout) return;
370    kb->layout->fuzz = fuzz;
371 }
372
373 EAPI void
374 e_kbd_buf_layout_key_add(E_Kbd_Buf *kb, const char *key,  const char *key_shift, const char *key_capslock, int x, int y, int w, int h)
375 {
376    E_Kbd_Buf_Key *ky;
377
378    if (!key) return;
379    if (!kb->layout) kb->layout = _e_kbd_buf_new();
380    if (!kb->layout) return;
381    ky = E_NEW(E_Kbd_Buf_Key, 1);
382    if (!ky) return;
383    ky->key = eina_stringshare_add(key);
384    if (key_shift) ky->key_shift = eina_stringshare_add(key_shift);
385    if (key_capslock) ky->key_capslock = eina_stringshare_add(key_capslock);
386    ky->x = x;
387    ky->y = y;
388    ky->w = w;
389    ky->h = h;
390    kb->layout->keys = eina_list_append(kb->layout->keys, ky);
391 }
392
393 static void
394 _e_kbd_buf_keystroke_add(E_Kbd_Buf *kb, E_Kbd_Buf_Keystroke *ks)
395 {
396    const char *str;
397
398    str = _e_kbd_buf_keystroke_string_get(kb, ks);
399    if (str)
400      {
401         if (kb->dict.sys) e_kbd_dict_word_letter_add(kb->dict.sys, str, 0);
402         if (kb->dict.personal) e_kbd_dict_word_letter_add(kb->dict.personal, str, 0);
403         if (kb->dict.data) e_kbd_dict_word_letter_add(kb->dict.data, str, 0);
404      }
405 }
406
407 EAPI void
408 e_kbd_buf_pressed_key_add(E_Kbd_Buf *kb, const char *key, int shift, int capslock)
409 {
410    E_Kbd_Buf_Keystroke *ks;
411
412    e_kbd_buf_lookup_cancel(kb);
413    if (!key) return;
414    if (!kb->layout) kb->layout = _e_kbd_buf_new();
415    if (!kb->layout) return;
416    ks = E_NEW(E_Kbd_Buf_Keystroke, 1);
417    if (!ks) return;
418    ks->key = eina_stringshare_add(key);
419    if (shift) ks->shift = 1;
420    if (capslock) ks->capslock = 1;
421    ks->layout = kb->layout;
422    _e_kbd_buf_layout_ref(ks->layout);
423    kb->keystrokes = eina_list_append(kb->keystrokes, ks);
424    
425    if (kb->dict.sys) e_kbd_dict_word_letter_advance(kb->dict.sys);
426    if (kb->dict.personal) e_kbd_dict_word_letter_advance(kb->dict.personal);
427    if (kb->dict.data) e_kbd_dict_word_letter_advance(kb->dict.data);
428    _e_kbd_buf_keystroke_add(kb, ks);
429    
430    _e_kbd_buf_actual_string_update(kb);
431    _e_kbd_buf_matches_update(kb);
432 }
433
434 static void
435 _e_kbd_buf_keystroke_point_add(E_Kbd_Buf *kb, E_Kbd_Buf_Keystroke *ks)
436 {
437    Eina_List *l;
438    
439    for (l = ks->layout->keys; l; l = l->next)
440      {
441         E_Kbd_Buf_Key *ky;
442         const char *str;
443         int px, py, dx, dy, d;
444
445         ky = l->data;
446         px = ky->x + (ky->w / 2);
447         py = ky->y + (ky->h / 2);
448         dx = ks->x - px;
449         dy = ks->y - py;
450         d = sqrt((dx * dx) + (dy * dy));
451         if (d <= ks->layout->fuzz)
452           {
453              str = _e_kbd_buf_keystroke_key_string_get(kb, ks, ky);
454              if (str)
455                {
456                   if (kb->dict.sys) e_kbd_dict_word_letter_add(kb->dict.sys, str, d);
457                   if (kb->dict.personal) e_kbd_dict_word_letter_add(kb->dict.personal, str, d);
458                   if (kb->dict.data) e_kbd_dict_word_letter_add(kb->dict.data, str, d);
459                }
460           }
461      }
462 }
463
464 EAPI void
465 e_kbd_buf_pressed_point_add(E_Kbd_Buf *kb, int x, int y, int shift, int capslock)
466 {
467    E_Kbd_Buf_Keystroke *ks;
468    
469    e_kbd_buf_lookup_cancel(kb);
470    if (!kb->layout) kb->layout = _e_kbd_buf_new();
471    if (!kb->layout) return;
472    ks = E_NEW(E_Kbd_Buf_Keystroke, 1);
473    if (!ks) return;
474    ks->x = x;
475    ks->y = y;
476    if (shift) ks->shift = 1;
477    if (capslock) ks->capslock = 1;
478    ks->layout = kb->layout;
479    _e_kbd_buf_layout_ref(ks->layout);
480    kb->keystrokes = eina_list_append(kb->keystrokes, ks);
481    
482    if (kb->dict.sys) e_kbd_dict_word_letter_advance(kb->dict.sys);
483    if (kb->dict.personal) e_kbd_dict_word_letter_advance(kb->dict.personal);
484    if (kb->dict.data) e_kbd_dict_word_letter_advance(kb->dict.data);
485    
486    _e_kbd_buf_keystroke_point_add(kb, ks);
487    
488    _e_kbd_buf_actual_string_update(kb);
489    _e_kbd_buf_matches_update(kb);
490 }
491
492 EAPI const char *
493 e_kbd_buf_actual_string_get(E_Kbd_Buf *kb)
494 {
495    return kb->actual_string;
496 }
497
498 EAPI const Eina_List *
499 e_kbd_buf_string_matches_get(E_Kbd_Buf *kb)
500 {
501    return kb->string_matches;
502 }
503
504 EAPI void
505 e_kbd_buf_backspace(E_Kbd_Buf *kb)
506 {
507    Eina_List *l;
508    
509    l = eina_list_last(kb->keystrokes);
510    if (l)
511      {
512         E_Kbd_Buf_Keystroke *ks;
513         
514         ks = l->data;
515         if (ks->key) eina_stringshare_del(ks->key);
516         _e_kbd_buf_layout_unref(ks->layout);
517         free(ks);
518         kb->keystrokes = eina_list_remove_list(kb->keystrokes, l);
519         if (kb->dict.sys) e_kbd_dict_word_letter_delete(kb->dict.sys);
520         if (kb->dict.personal) e_kbd_dict_word_letter_delete(kb->dict.personal);
521         if (kb->dict.data) e_kbd_dict_word_letter_delete(kb->dict.data);
522         _e_kbd_buf_actual_string_update(kb);
523         _e_kbd_buf_matches_update(kb);
524      }
525 }
526
527 EAPI void
528 e_kbd_buf_word_use(E_Kbd_Buf *kb, const char *word)
529 {
530    if (kb->dict.personal)
531      e_kbd_dict_word_usage_adjust(kb->dict.personal, word, 1);
532 }
533
534 // FIXME: just faking delayed lookup with timer
535 static Eina_Bool
536 _e_kbd_buf_cb_faket(void *data)
537 {
538    E_Kbd_Buf *kb;
539    
540    kb = data;
541    kb->lookup.faket = NULL;
542    kb->lookup.func((void *)kb->lookup.data);
543    kb->lookup.func = NULL;
544    kb->lookup.data = NULL;
545    return ECORE_CALLBACK_CANCEL;
546 }
547
548 EAPI void
549 e_kbd_buf_lookup(E_Kbd_Buf *kb, void (*func) (void *data), const void *data)
550 {
551    e_kbd_buf_lookup_cancel(kb);
552    
553    kb->lookup.func = func;
554    kb->lookup.data = data;
555
556    // FIXME: just faking delayed lookup with timer
557    kb->lookup.faket = ecore_timer_add(0.1, _e_kbd_buf_cb_faket, kb);
558 }
559
560 EAPI void
561 e_kbd_buf_lookup_cancel(E_Kbd_Buf *kb)
562 {
563    // FIXME: just faking delayed lookup with timer
564    if (!kb->lookup.faket) return;
565    ecore_timer_del(kb->lookup.faket);
566    kb->lookup.faket = NULL;
567    
568    kb->lookup.func = NULL;
569    kb->lookup.data = NULL;
570 }