Tizen 2.1 release
[platform/core/uifw/e17.git] / src / modules / illume-keyboard / e_kbd_dict.c
1 #include "e.h"
2 #include "e_kbd_dict.h"
3
4 #include <fcntl.h>
5 #include <sys/mman.h>
6
7 #define MAXLATIN 0x100
8
9 static unsigned char _e_kbd_normalise_base[MAXLATIN];
10 static unsigned char _e_kbd_normalise_ready = 0;
11
12 static void
13 _e_kbd_normalise_init(void)
14 {
15    int i;
16    const char *table[][2] =
17      {
18           {"À", "a"},
19           {"Á", "a"},
20           {"Â", "a"},
21           {"Ã", "a"},
22           {"Ä", "a"},
23           {"Å", "a"},
24           {"Æ", "a"},
25           {"Ç", "c"},
26           {"È", "e"},
27           {"É", "e"},
28           {"Ê", "e"},
29           {"Ë", "e"},
30           {"Ì", "i"},
31           {"Í", "i"},
32           {"Î", "i"},
33           {"Ï", "i"},
34           {"Ð", "d"},
35           {"Ñ", "n"},
36           {"Ò", "o"},
37           {"Ó", "o"},
38           {"Ô", "o"},
39           {"Õ", "o"},
40           {"Ö", "o"},
41           {"×", "x"},
42           {"Ø", "o"},
43           {"Ù", "u"},
44           {"Ú", "u"},
45           {"Û", "u"},
46           {"Ü", "u"},
47           {"Ý", "y"},
48           {"Þ", "p"},
49           {"ß", "s"},
50           {"à", "a"},
51           {"á", "a"},
52           {"â", "a"},
53           {"ã", "a"},
54           {"ä", "a"},
55           {"å", "a"},
56           {"æ", "a"},
57           {"ç", "c"},
58           {"è", "e"},
59           {"é", "e"},
60           {"ê", "e"},
61           {"ë", "e"},
62           {"ì", "i"},
63           {"í", "i"},
64           {"î", "i"},
65           {"ï", "i"},
66           {"ð", "o"},
67           {"ñ", "n"},
68           {"ò", "o"},
69           {"ó", "o"},
70           {"ô", "o"},
71           {"õ", "o"},
72           {"ö", "o"},
73           {"ø", "o"},
74           {"ù", "u"},
75           {"ú", "u"},
76           {"û", "u"},
77           {"ü", "u"},
78           {"ý", "y"},
79           {"þ", "p"},
80           {"ÿ", "y"}
81      }; // 63 items
82
83    if (_e_kbd_normalise_ready) return;
84    _e_kbd_normalise_ready = 1;
85    for (i = 0; i < 128; i++)
86      _e_kbd_normalise_base[i] = tolower(i);
87    for (;i < MAXLATIN; i++)
88      {
89         int glyph, j;
90
91         for (j = 0; j < 63; j++)
92           {
93              evas_string_char_next_get(table[j][0], 0, &glyph);
94              if (glyph == i)
95                {
96                   _e_kbd_normalise_base[i] = *table[j][1];
97                   break;
98                }
99           }
100      }
101 }
102
103 static int
104 _e_kbd_dict_letter_normalise(int glyph)
105 {
106    // FIXME: ö -> o, ä -> a, Ó -> o etc. - ie normalise to latin-1
107    if (glyph < MAXLATIN) return _e_kbd_normalise_base[glyph];
108    return tolower(glyph) & 0x7f;
109 }
110
111 static int
112 _e_kbd_dict_normalized_strncmp(const char *a, const char *b, int len)
113 {
114    // FIXME: normalise 2 strings and then compare
115    if (len < 0) return strcasecmp(a, b);
116    return strncasecmp(a, b, len);
117 }
118
119 static int
120 _e_kbd_dict_normalized_strcmp(const char *a, const char *b)
121 {
122    return _e_kbd_dict_normalized_strncmp(a, b, -1);
123 }
124
125 static void
126 _e_kbd_dict_normalized_strcpy(char *dst, const char *src)
127 {
128    const char *p;
129    char *d;
130
131    for (p = src, d = dst; *p; p++, d++)
132      *d = _e_kbd_dict_letter_normalise(*p);
133    *d = 0;
134 }
135
136 static int
137 _e_kbd_dict_matches_lookup_cb_sort(const void *d1, const void *d2)
138 {
139    const E_Kbd_Dict_Word *kw1, *kw2;
140
141    kw1 = d1;
142    kw2 = d2;
143    if (kw1->usage < kw2->usage) return 1;
144    else if (kw1->usage > kw2->usage) return -1;
145    return 0;
146 }
147
148 static int
149 _e_kbd_dict_writes_cb_sort(const void *d1, const void *d2)
150 {
151    const E_Kbd_Dict_Word *kw1, *kw2;
152
153    kw1 = d1;
154    kw2 = d2;
155    return _e_kbd_dict_normalized_strcmp(kw1->word, kw2->word);
156    return 0;
157 }
158
159 static const char *
160 _e_kbd_dict_line_next(E_Kbd_Dict *kd, const char *p)
161 {
162    const char *e, *pp;
163
164    e = kd->file.dict + kd->file.size;
165    for (pp = p; pp < e; pp++)
166      if (*pp == '\n') return pp + 1;
167    return NULL;
168 }
169
170 static char *
171 _e_kbd_dict_line_parse(E_Kbd_Dict *kd __UNUSED__, const char *p, int *usage)
172 {
173    const char *ps;
174    char *wd = NULL;
175
176    for (ps = p; !isspace(*ps); ps++);
177    wd = malloc(ps - p + 1);
178    if (!wd) return NULL;
179    strncpy(wd, p, ps - p);
180    wd[ps - p] = 0;
181    if (*ps == '\n') *usage = 0;
182    else
183      {
184         ps++;
185         *usage = atoi(ps);
186      }
187    return wd;
188 }
189
190 static void
191 _e_kbd_dict_lookup_build_line(E_Kbd_Dict *kd __UNUSED__, const char *p, const char *eol, int *glyphs)
192 {
193    char *s;
194    int p2;
195
196    s = alloca(eol - p + 1);
197    strncpy(s, p, eol - p);
198    s[eol - p] = 0;
199    p2 = evas_string_char_next_get(s, 0, &(glyphs[0]));
200    if ((p2 > 0) && (glyphs[0] > 0))
201      evas_string_char_next_get(s, p2, &(glyphs[1]));
202 }
203
204 static void
205 _e_kbd_dict_lookup_build(E_Kbd_Dict *kd)
206 {
207    const char *p, *e, *eol;
208    int glyphs[2], pglyphs[2];
209
210    p = kd->file.dict;
211    e = p + kd->file.size;
212    pglyphs[0] = pglyphs[1] = 0;
213    while (p < e)
214      {
215         eol = strchr(p, '\n');
216         if (!eol) break;
217         if (eol > p)
218           {
219              glyphs[0] = glyphs[1] = 0;
220              _e_kbd_dict_lookup_build_line(kd, p, eol, glyphs);
221              if ((glyphs[1] != pglyphs[1]) || (glyphs[0] != pglyphs[0]))
222                {
223                   int v1, v2;
224
225                   if (isspace(glyphs[0]))
226                     {
227                        glyphs[0] = 0;
228                        glyphs[1] = 0;
229                     }
230                   else if (isspace(glyphs[1]))
231                     glyphs[1] = 0;
232                   if (glyphs[0] == 0)
233                     {
234                        pglyphs[0] = pglyphs[1] = 0;
235                        p = eol + 1;
236                        continue;
237                     }
238                   v1 = _e_kbd_dict_letter_normalise(glyphs[0]);
239                   v2 = _e_kbd_dict_letter_normalise(glyphs[1]);
240                   if (!kd->lookup.tuples[v1][v2])
241                     kd->lookup.tuples[v1][v2] = p;
242                   pglyphs[0] = v1;
243                   pglyphs[1] = v2;
244                }
245           }
246         p = eol + 1;
247      }
248 }
249
250 static int
251 _e_kbd_dict_open(E_Kbd_Dict *kd)
252 {
253    struct stat st;
254
255    kd->file.fd = open(kd->file.file, O_RDONLY);
256    if (kd->file.fd < 0) return 0;
257    if (fstat(kd->file.fd, &st) < 0)
258      {
259         close(kd->file.fd);
260         return 0;
261      }
262    kd->file.size = st.st_size;
263    
264    eina_mmap_safety_enabled_set(EINA_TRUE);
265    
266    kd->file.dict = mmap(NULL, kd->file.size, PROT_READ, MAP_SHARED,
267                         kd->file.fd, 0);
268    if ((kd->file.dict== MAP_FAILED) || (!kd->file.dict))
269      {
270         close(kd->file.fd);
271         return 0;
272      }
273    return 1;
274 }
275
276 static void
277 _e_kbd_dict_close(E_Kbd_Dict *kd)
278 {
279    if (kd->file.fd < 0) return;
280    memset(kd->lookup.tuples, 0, sizeof(kd->lookup.tuples));
281    munmap((void *)kd->file.dict, kd->file.size);
282    close(kd->file.fd);
283    kd->file.fd = -1;
284    kd->file.dict = NULL;
285    kd->file.size = 0;
286 }
287
288 EAPI E_Kbd_Dict *
289 e_kbd_dict_new(const char *file)
290 {
291    // alloc and load new dict - build quick-lookup table. words MUST be sorted
292    E_Kbd_Dict *kd;
293
294    _e_kbd_normalise_init();
295    kd = E_NEW(E_Kbd_Dict, 1);
296    if (!kd) return NULL;
297    kd->file.file = eina_stringshare_add(file);
298    if (!kd->file.file)
299      {
300         free(kd);
301         return NULL;
302      }
303    kd->file.fd = -1;
304    if (!_e_kbd_dict_open(kd))
305      {
306         eina_stringshare_del(kd->file.file);
307         free(kd);
308         return NULL;
309      }
310    _e_kbd_dict_lookup_build(kd);
311    return kd;
312 }
313
314 EAPI void
315 e_kbd_dict_free(E_Kbd_Dict *kd)
316 {
317    // free dict and anything in it
318    e_kbd_dict_word_letter_clear(kd);
319    e_kbd_dict_save(kd);
320    _e_kbd_dict_close(kd);
321    free(kd);
322 }
323
324 static E_Kbd_Dict_Word *
325 _e_kbd_dict_changed_write_find(E_Kbd_Dict *kd, const char *word)
326 {
327    Eina_List *l;
328
329    for (l = kd->changed.writes; l; l = l->next)
330      {
331         E_Kbd_Dict_Word *kw;
332
333         kw = l->data;
334         if (!strcmp(kw->word, word)) return kw;
335      }
336    return NULL;
337 }
338
339 EAPI void
340 e_kbd_dict_save(E_Kbd_Dict *kd)
341 {
342    FILE *f;
343
344    // save any changes (new words added, usage adjustments).
345    // all words MUST be sorted
346    if (!kd->changed.writes) return;
347    if (kd->changed.flush_timer)
348      {
349         ecore_timer_del(kd->changed.flush_timer);
350         kd->changed.flush_timer = NULL;
351      }
352    ecore_file_unlink(kd->file.file);
353    f = fopen(kd->file.file, "w");
354    kd->changed.writes = eina_list_sort(kd->changed.writes,
355                                        eina_list_count(kd->changed.writes),
356                                        _e_kbd_dict_writes_cb_sort);
357    if (f)
358      {
359         const char *p, *pn;
360
361         p = kd->file.dict;
362         while (p)
363           {
364              char *wd;
365              int usage = 0;
366
367              pn = _e_kbd_dict_line_next(kd, p);
368              if (!pn)
369                {
370                   fclose(f);
371                   return;
372                }
373              wd = _e_kbd_dict_line_parse(kd, p, &usage);
374              if ((wd) && (strlen(wd) > 0))
375                {
376                   if (kd->changed.writes)
377                     {
378                        int writeline = 0;
379
380                        while (kd->changed.writes)
381                          {
382                             E_Kbd_Dict_Word *kw;
383                             int cmp;
384
385                             kw = kd->changed.writes->data;
386                             cmp = _e_kbd_dict_normalized_strcmp(kw->word, wd);
387                             if (cmp < 0)
388                               {
389                                  fprintf(f, "%s %i\n", kw->word, kw->usage);
390                                  writeline = 1;
391                                  eina_stringshare_del(kw->word);
392                                  free(kw);
393                                  kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
394                               }
395                             else if (cmp == 0)
396                               {
397                                  fprintf(f, "%s %i\n", wd, kw->usage);
398                                  if (!strcmp(kw->word, wd))
399                                    writeline = 0;
400                                  else
401                                    writeline = 1;
402                                  eina_stringshare_del(kw->word);
403                                  free(kw);
404                                  kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
405                                  break;
406                               }
407                             else if (cmp > 0)
408                               {
409                                  writeline = 1;
410                                  break;
411                               }
412                          }
413                        if (writeline)
414                          fprintf(f, "%s %i\n", wd, usage);
415                     }
416                   else
417                     fprintf(f, "%s %i\n", wd, usage);
418                }
419              if (wd) free(wd);
420              p = pn;
421              if (p >= (kd->file.dict + kd->file.size)) break;
422           }
423         while (kd->changed.writes)
424           {
425              E_Kbd_Dict_Word *kw;
426
427              kw = kd->changed.writes->data;
428              fprintf(f, "%s %i\n", kw->word, kw->usage);
429              eina_stringshare_del(kw->word);
430              free(kw);
431              kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
432           }
433         fclose(f);
434      }
435    _e_kbd_dict_close(kd);
436    if (_e_kbd_dict_open(kd)) _e_kbd_dict_lookup_build(kd);
437 }
438
439 static Eina_Bool 
440 _e_kbd_dict_cb_save_flush(void *data)
441 {
442    E_Kbd_Dict *kd;
443
444    kd = data;
445    if ((kd->matches.list) || (kd->word.letters) || (kd->matches.deadends) ||
446        (kd->matches.leads))
447      return EINA_TRUE;
448    kd->changed.flush_timer = NULL;
449    e_kbd_dict_save(kd);
450    return EINA_FALSE;
451 }
452
453 static void
454 _e_kbd_dict_changed_write_add(E_Kbd_Dict *kd, const char *word, int usage)
455 {
456    E_Kbd_Dict_Word *kw;
457
458    kw = E_NEW(E_Kbd_Dict_Word, 1);
459    kw->word = eina_stringshare_add(word);
460    kw->usage = usage;
461    kd->changed.writes = eina_list_prepend(kd->changed.writes, kw);
462    if (eina_list_count(kd->changed.writes) > 64)
463      e_kbd_dict_save(kd);
464    else
465      {
466         if (kd->changed.flush_timer)
467           ecore_timer_del(kd->changed.flush_timer);
468         kd->changed.flush_timer = 
469           ecore_timer_add(5.0, _e_kbd_dict_cb_save_flush, kd);
470      }
471 }
472
473 static const char *
474 _e_kbd_dict_find_pointer(E_Kbd_Dict *kd, const char *p, int baselen, const char *word)
475 {
476    const char *pn;
477    int len;
478
479    if (!p) return NULL;
480    len = strlen(word);
481    while (p)
482      {
483         pn = _e_kbd_dict_line_next(kd, p);
484         if (!pn) return NULL;
485         if ((pn - p) > len)
486           {
487              if (!_e_kbd_dict_normalized_strncmp(p, word, len))
488                return p;
489           }
490         if (_e_kbd_dict_normalized_strncmp(p, word, baselen))
491           return NULL;
492         p = pn;
493         if (p >= (kd->file.dict + kd->file.size)) break;
494      }
495    return NULL;
496 }
497
498 static const char *
499 _e_kbd_dict_find(E_Kbd_Dict *kd, const char *word)
500 {
501    const char *p;
502    char *tword;
503    int glyphs[2], p2, v1, v2, i;
504
505    /* work backwards in leads. i.e.:
506     * going
507     * goin
508     * goi
509     * go
510     * g
511     */
512    tword = alloca(strlen(word) + 1);
513    _e_kbd_dict_normalized_strcpy(tword, word);
514    p = eina_hash_find(kd->matches.leads, tword);
515    if (p) return p;
516    p2 = strlen(tword);
517    while (tword[0])
518      {
519         p2 = evas_string_char_prev_get(tword, p2, &i);
520         if (p2 < 0) break;
521         tword[p2] = 0;
522         p = eina_hash_find(kd->matches.leads, tword);
523         if (p)
524           return _e_kbd_dict_find_pointer(kd, p, p2, word);
525      }
526    /* looking at leads going back letters didn't work */
527    p = kd->file.dict;
528    if ((p[0] == '\n') && (kd->file.size <= 1)) return NULL;
529    glyphs[0] = glyphs[1] = 0;
530    p2 = evas_string_char_next_get(word, 0, &(glyphs[0]));
531    if ((p2 > 0) && (glyphs[0] > 0))
532      p2 = evas_string_char_next_get(word, p2, &(glyphs[1]));
533    v1 = _e_kbd_dict_letter_normalise(glyphs[0]);
534    if (glyphs[1] != 0)
535      {
536         v2 = _e_kbd_dict_letter_normalise(glyphs[1]);
537         p = kd->lookup.tuples[v1][v2];
538      }
539    else
540      {
541         for (i = 0; i < 128; i++)
542           {
543              p = kd->lookup.tuples[v1][i];
544              if (p) break;
545           }
546      }
547    return _e_kbd_dict_find_pointer(kd, p, p2, word);
548 }
549
550 static const char *
551 _e_kbd_dict_find_full(E_Kbd_Dict *kd, const char *word)
552 {
553    const char *p;
554    int len;
555
556    p = _e_kbd_dict_find(kd, word);
557    if (!p) return NULL;
558    len = strlen(word);
559    if (isspace(p[len])) return p;
560    return NULL;
561 }
562
563 EAPI void
564 e_kbd_dict_word_usage_adjust(E_Kbd_Dict *kd, const char *word, int adjust)
565 {
566    // add "adjust" to word usage count
567    E_Kbd_Dict_Word *kw;
568
569    kw = _e_kbd_dict_changed_write_find(kd, word);
570    if (kw)
571      {
572         kw->usage += adjust;
573         if (kd->changed.flush_timer)
574           ecore_timer_del(kd->changed.flush_timer);
575         kd->changed.flush_timer = ecore_timer_add(5.0, _e_kbd_dict_cb_save_flush, kd);
576      }
577    else
578      {
579         const char *line;
580         int usage = 0;
581
582         line = _e_kbd_dict_find_full(kd, word);
583         if (line)
584           {
585              char *wd;
586
587              // FIXME: we need to find an EXACT line match - case and all
588              wd = _e_kbd_dict_line_parse(kd, line, &usage);
589              if (wd) free(wd);
590           }
591         usage += adjust;
592         _e_kbd_dict_changed_write_add(kd, word, usage);
593      }
594 }
595
596 EAPI void
597 e_kbd_dict_word_delete(E_Kbd_Dict *kd, const char *word)
598 {
599    // delete a word from the dictionary
600    E_Kbd_Dict_Word *kw;
601
602    kw = _e_kbd_dict_changed_write_find(kd, word);
603    if (kw)
604      kw->usage = -1;
605    else
606      {
607         if (_e_kbd_dict_find_full(kd, word))
608           _e_kbd_dict_changed_write_add(kd, word, -1);
609      }
610 }
611
612 EAPI void
613 e_kbd_dict_word_letter_clear(E_Kbd_Dict *kd)
614 {
615    // clear the current word buffer
616    while (kd->word.letters)
617      e_kbd_dict_word_letter_delete(kd);
618    if (kd->matches.deadends)
619      {
620         eina_hash_free(kd->matches.deadends);
621         kd->matches.deadends = NULL;
622      }
623    if (kd->matches.leads)
624      {
625         eina_hash_free(kd->matches.leads);
626         kd->matches.leads = NULL;
627      }
628    while (kd->matches.list)
629      {
630         E_Kbd_Dict_Word *kw;
631
632         kw = kd->matches.list->data;
633         eina_stringshare_del(kw->word);
634         free(kw);
635         kd->matches.list = eina_list_remove_list(kd->matches.list, kd->matches.list);
636     }
637 }
638
639 EAPI void
640 e_kbd_dict_word_letter_add(E_Kbd_Dict *kd, const char *letter, int dist)
641 {
642    // add a letter with a distance (0 == closest) as an option for the current
643    // letter position - advance starts a new letter position
644    Eina_List *l, *list;
645    E_Kbd_Dict_Letter *kl;
646
647    l = eina_list_last(kd->word.letters);
648    if (!l) return;
649    list = l->data;
650    kl = E_NEW(E_Kbd_Dict_Letter, 1);
651    if (!kl) return;
652    kl->letter = eina_stringshare_add(letter);
653    kl->dist = dist;
654    list = eina_list_append(list, kl);
655    l->data = list;
656 }
657
658 EAPI void
659 e_kbd_dict_word_letter_advance(E_Kbd_Dict *kd)
660 {
661    // start a new letter in the word
662    kd->word.letters = eina_list_append(kd->word.letters, NULL);
663 }
664
665 EAPI void
666 e_kbd_dict_word_letter_delete(E_Kbd_Dict *kd)
667 {
668    // delete the current letter completely
669    Eina_List *l, *list;
670
671    l = eina_list_last(kd->word.letters);
672    if (!l) return;
673    list = l->data;
674    while (list)
675      {
676         E_Kbd_Dict_Letter *kl;
677
678         kl = list->data;
679         eina_stringshare_del(kl->letter);
680         free(kl);
681         list = eina_list_remove_list(list, list);
682      }
683    kd->word.letters = eina_list_remove_list(kd->word.letters, l);
684 }
685
686 static void
687 _e_kbd_dict_matches_lookup_iter(E_Kbd_Dict *kd, Eina_List *word,
688                                 Eina_List *more)
689 {
690    Eina_List *l, *list;
691    const char *p;
692    char *base, *buf, *wd, *bufapp;
693    E_Kbd_Dict_Letter *kl;
694    int len = 0, dist = 0, d, baselen, maxdist = 0, md;
695    static int level = 0;
696
697    level++;
698    for (l = word; l; l = l->next)
699      {
700         kl = l->data;
701         len += strlen(kl->letter);
702         dist += kl->dist;
703         if (kl->dist > maxdist) maxdist = kl->dist;
704      }
705    if (maxdist < 1) maxdist = 1;
706    buf = alloca(len + 20); // 20 - just padding enough for 1 more utf8 char
707    base = alloca(len + 20);
708    base[0] = 0;
709    for (l = word; l; l = l->next)
710      {
711         kl = l->data;
712         strcat(base, kl->letter);
713      }
714    baselen = strlen(base);
715    strcpy(buf, base);
716    bufapp = buf + baselen;
717    list = more->data;
718    for (l = list; l; l = l->next)
719      {
720         kl = l->data;
721         if (kl->dist > maxdist) maxdist = kl->dist;
722      }
723    for (l = list; l; l = l->next)
724      {
725         kl = l->data;
726         strcpy(bufapp, kl->letter);
727         if ((kd->matches.deadends) && eina_hash_find(kd->matches.deadends, buf))
728           continue;
729         p = eina_hash_find(kd->matches.leads, buf);
730         if (p) p = _e_kbd_dict_find_pointer(kd, p, baselen, buf);
731         else p = _e_kbd_dict_find(kd, buf);
732         if (!p)
733           {
734              if (!kd->matches.deadends)
735                kd->matches.deadends = eina_hash_string_superfast_new(NULL);
736              eina_hash_add(kd->matches.deadends, buf, kd);
737              continue;
738           }
739         else
740           {
741              if (!kd->matches.leads)
742                kd->matches.leads = eina_hash_string_superfast_new(NULL);
743              eina_hash_add(kd->matches.leads, buf, p);
744           }
745         if ((!more->next) || (!more->next->data))
746           {
747              d = dist + kl->dist;
748              md = maxdist;
749              for (;;)
750                {
751                   E_Kbd_Dict_Word *kw;
752                   int usage = 0;
753
754                   wd = _e_kbd_dict_line_parse(kd, p, &usage);
755                   if (!wd) break;
756                   if (_e_kbd_dict_normalized_strcmp(wd, buf))
757                     {
758                        free(wd);
759                        break;
760                     }
761                   kw = E_NEW(E_Kbd_Dict_Word, 1);
762                   if (kw)
763                     {
764                        int accuracy;
765                        int w, b, w2, b2, wc, bc;
766
767                        // match any capitalisation
768                        for (w = 0, b = 0; wd[w] && buf[b];)
769                          {
770                             b2 = evas_string_char_next_get(buf, b, &bc);
771                             w2 = evas_string_char_next_get(wd, w, &wc);
772                             if (isupper(bc)) wd[w] = toupper(wc);
773                             w = w2;
774                             b = b2;
775                          }
776                        kw->word = eina_stringshare_add(wd);
777                        // FIXME: magic combination of distance metric and
778                        // frequency of usage. this is simple now, but could
779                        // be tweaked
780                        wc = eina_list_count(word);
781                        if (md < 1) md = 1;
782
783                        // basically a metric to see how far away the keys that
784                        // were actually pressed are away from the letters of
785                        // this word in a physical on-screen sense
786                        accuracy = md - (d / (wc + 1));
787                        // usage is the frequency of usage in the dictionary.
788                        // it its < 1 time, it's assumed to be 1.
789                        if (usage < 1) usage = 1;
790                        // multiply usage by a factor of 100 for better detailed
791                        // sorting. 10 == 1/10th factor
792                        usage = 100 + ((usage - 1) * 10);
793                        // and well just multiply and lets see. maybe this can
794                        // do with multiplication factors etc. but simple for
795                        // now.
796                        kw->usage = (usage * accuracy) / md;
797                        kd->matches.list = eina_list_append(kd->matches.list, kw);
798                     }
799                   free(wd);
800                   p = _e_kbd_dict_line_next(kd, p);
801                   if (p >= (kd->file.dict + kd->file.size)) break;
802                   if (!p) break;
803                }
804           }
805         else
806           {
807              word = eina_list_append(word, kl);
808              _e_kbd_dict_matches_lookup_iter(kd, word, more->next);
809              word = eina_list_remove_list(word, eina_list_last(word));
810           }
811      }
812    level--;
813 }
814
815 EAPI void
816 e_kbd_dict_matches_lookup(E_Kbd_Dict *kd)
817 {
818    // find all matches and sort them
819    while (kd->matches.list)
820      {
821         E_Kbd_Dict_Word *kw;
822
823         kw = kd->matches.list->data;
824         eina_stringshare_del(kw->word);
825         free(kw);
826         kd->matches.list = eina_list_remove_list(kd->matches.list, kd->matches.list);
827     }
828    if (kd->word.letters)
829      _e_kbd_dict_matches_lookup_iter(kd, NULL, kd->word.letters);
830    kd->matches.list = eina_list_sort(kd->matches.list,
831                                      eina_list_count(kd->matches.list),
832                                      _e_kbd_dict_matches_lookup_cb_sort);
833 }
834
835 EAPI void
836 e_kbd_dict_matches_first(E_Kbd_Dict *kd)
837 {
838    // jump to first match
839    kd->matches.list_ptr = kd->matches.list;
840 }
841
842 EAPI void
843 e_kbd_dict_matches_next(E_Kbd_Dict *kd)
844 {
845    // jump to next match
846    kd->matches.list_ptr = kd->matches.list_ptr->next;
847 }
848
849 EAPI const char *
850 e_kbd_dict_matches_match_get(E_Kbd_Dict *kd, int *pri_ret)
851 {
852    // return the word (string utf-8) for the current match
853    if (kd->matches.list_ptr)
854      {
855         E_Kbd_Dict_Word *kw;
856
857         kw = kd->matches.list_ptr->data;
858         if (kw)
859           {
860              *pri_ret = kw->usage;
861              return kw->word;
862           }
863      }
864    return NULL;
865 }