575acb6d038e6116aa0e391f6cd639279ed2c74b
[framework/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    kd->file.dict = mmap(NULL, kd->file.size, PROT_READ, MAP_SHARED,
264                         kd->file.fd, 0);
265    if ((kd->file.dict== MAP_FAILED) || (!kd->file.dict))
266      {
267         close(kd->file.fd);
268         return 0;
269      }
270    return 1;
271 }
272
273 static void
274 _e_kbd_dict_close(E_Kbd_Dict *kd)
275 {
276    if (kd->file.fd < 0) return;
277    memset(kd->lookup.tuples, 0, sizeof(kd->lookup.tuples));
278    munmap((void *)kd->file.dict, kd->file.size);
279    close(kd->file.fd);
280    kd->file.fd = -1;
281    kd->file.dict = NULL;
282    kd->file.size = 0;
283 }
284
285 EAPI E_Kbd_Dict *
286 e_kbd_dict_new(const char *file)
287 {
288    // alloc and load new dict - build quick-lookup table. words MUST be sorted
289    E_Kbd_Dict *kd;
290
291    _e_kbd_normalise_init();
292    kd = E_NEW(E_Kbd_Dict, 1);
293    if (!kd) return NULL;
294    kd->file.file = eina_stringshare_add(file);
295    if (!kd->file.file)
296      {
297         free(kd);
298         return NULL;
299      }
300    kd->file.fd = -1;
301    if (!_e_kbd_dict_open(kd))
302      {
303         eina_stringshare_del(kd->file.file);
304         free(kd);
305         return NULL;
306      }
307    _e_kbd_dict_lookup_build(kd);
308    return kd;
309 }
310
311 EAPI void
312 e_kbd_dict_free(E_Kbd_Dict *kd)
313 {
314    // free dict and anything in it
315    e_kbd_dict_word_letter_clear(kd);
316    e_kbd_dict_save(kd);
317    _e_kbd_dict_close(kd);
318    free(kd);
319 }
320
321 static E_Kbd_Dict_Word *
322 _e_kbd_dict_changed_write_find(E_Kbd_Dict *kd, const char *word)
323 {
324    Eina_List *l;
325
326    for (l = kd->changed.writes; l; l = l->next)
327      {
328         E_Kbd_Dict_Word *kw;
329
330         kw = l->data;
331         if (!strcmp(kw->word, word)) return kw;
332      }
333    return NULL;
334 }
335
336 EAPI void
337 e_kbd_dict_save(E_Kbd_Dict *kd)
338 {
339    FILE *f;
340
341    // save any changes (new words added, usage adjustments).
342    // all words MUST be sorted
343    if (!kd->changed.writes) return;
344    if (kd->changed.flush_timer)
345      {
346         ecore_timer_del(kd->changed.flush_timer);
347         kd->changed.flush_timer = NULL;
348      }
349    ecore_file_unlink(kd->file.file);
350    f = fopen(kd->file.file, "w");
351    kd->changed.writes = eina_list_sort(kd->changed.writes,
352                                        eina_list_count(kd->changed.writes),
353                                        _e_kbd_dict_writes_cb_sort);
354    if (f)
355      {
356         const char *p, *pn;
357
358         p = kd->file.dict;
359         while (p)
360           {
361              char *wd;
362              int usage = 0;
363
364              pn = _e_kbd_dict_line_next(kd, p);
365              if (!pn)
366                {
367                   fclose(f);
368                   return;
369                }
370              wd = _e_kbd_dict_line_parse(kd, p, &usage);
371              if ((wd) && (strlen(wd) > 0))
372                {
373                   if (kd->changed.writes)
374                     {
375                        int writeline = 0;
376
377                        while (kd->changed.writes)
378                          {
379                             E_Kbd_Dict_Word *kw;
380                             int cmp;
381
382                             kw = kd->changed.writes->data;
383                             cmp = _e_kbd_dict_normalized_strcmp(kw->word, wd);
384                             if (cmp < 0)
385                               {
386                                  fprintf(f, "%s %i\n", kw->word, kw->usage);
387                                  writeline = 1;
388                                  eina_stringshare_del(kw->word);
389                                  free(kw);
390                                  kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
391                               }
392                             else if (cmp == 0)
393                               {
394                                  fprintf(f, "%s %i\n", wd, kw->usage);
395                                  if (!strcmp(kw->word, wd))
396                                    writeline = 0;
397                                  else
398                                    writeline = 1;
399                                  eina_stringshare_del(kw->word);
400                                  free(kw);
401                                  kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
402                                  break;
403                               }
404                             else if (cmp > 0)
405                               {
406                                  writeline = 1;
407                                  break;
408                               }
409                          }
410                        if (writeline)
411                          fprintf(f, "%s %i\n", wd, usage);
412                     }
413                   else
414                     fprintf(f, "%s %i\n", wd, usage);
415                }
416              if (wd) free(wd);
417              p = pn;
418              if (p >= (kd->file.dict + kd->file.size)) break;
419           }
420         while (kd->changed.writes)
421           {
422              E_Kbd_Dict_Word *kw;
423
424              kw = kd->changed.writes->data;
425              fprintf(f, "%s %i\n", kw->word, kw->usage);
426              eina_stringshare_del(kw->word);
427              free(kw);
428              kd->changed.writes  = eina_list_remove_list(kd->changed.writes, kd->changed.writes);
429           }
430         fclose(f);
431      }
432    _e_kbd_dict_close(kd);
433    if (_e_kbd_dict_open(kd)) _e_kbd_dict_lookup_build(kd);
434 }
435
436 static Eina_Bool 
437 _e_kbd_dict_cb_save_flush(void *data)
438 {
439    E_Kbd_Dict *kd;
440
441    kd = data;
442    if ((kd->matches.list) || (kd->word.letters) || (kd->matches.deadends) ||
443        (kd->matches.leads))
444      return EINA_TRUE;
445    kd->changed.flush_timer = NULL;
446    e_kbd_dict_save(kd);
447    return EINA_FALSE;
448 }
449
450 static void
451 _e_kbd_dict_changed_write_add(E_Kbd_Dict *kd, const char *word, int usage)
452 {
453    E_Kbd_Dict_Word *kw;
454
455    kw = E_NEW(E_Kbd_Dict_Word, 1);
456    kw->word = eina_stringshare_add(word);
457    kw->usage = usage;
458    kd->changed.writes = eina_list_prepend(kd->changed.writes, kw);
459    if (eina_list_count(kd->changed.writes) > 64)
460      e_kbd_dict_save(kd);
461    else
462      {
463         if (kd->changed.flush_timer)
464           ecore_timer_del(kd->changed.flush_timer);
465         kd->changed.flush_timer = 
466           ecore_timer_add(5.0, _e_kbd_dict_cb_save_flush, kd);
467      }
468 }
469
470 static const char *
471 _e_kbd_dict_find_pointer(E_Kbd_Dict *kd, const char *p, int baselen, const char *word)
472 {
473    const char *pn;
474    int len;
475
476    if (!p) return NULL;
477    len = strlen(word);
478    while (p)
479      {
480         pn = _e_kbd_dict_line_next(kd, p);
481         if (!pn) return NULL;
482         if ((pn - p) > len)
483           {
484              if (!_e_kbd_dict_normalized_strncmp(p, word, len))
485                return p;
486           }
487         if (_e_kbd_dict_normalized_strncmp(p, word, baselen))
488           return NULL;
489         p = pn;
490         if (p >= (kd->file.dict + kd->file.size)) break;
491      }
492    return NULL;
493 }
494
495 static const char *
496 _e_kbd_dict_find(E_Kbd_Dict *kd, const char *word)
497 {
498    const char *p;
499    char *tword;
500    int glyphs[2], p2, v1, v2, i;
501
502    /* work backwards in leads. i.e.:
503     * going
504     * goin
505     * goi
506     * go
507     * g
508     */
509    tword = alloca(strlen(word) + 1);
510    _e_kbd_dict_normalized_strcpy(tword, word);
511    p = eina_hash_find(kd->matches.leads, tword);
512    if (p) return p;
513    p2 = strlen(tword);
514    while (tword[0])
515      {
516         p2 = evas_string_char_prev_get(tword, p2, &i);
517         if (p2 < 0) break;
518         tword[p2] = 0;
519         p = eina_hash_find(kd->matches.leads, tword);
520         if (p)
521           return _e_kbd_dict_find_pointer(kd, p, p2, word);
522      }
523    /* looking at leads going back letters didn't work */
524    p = kd->file.dict;
525    if ((p[0] == '\n') && (kd->file.size <= 1)) return NULL;
526    glyphs[0] = glyphs[1] = 0;
527    p2 = evas_string_char_next_get(word, 0, &(glyphs[0]));
528    if ((p2 > 0) && (glyphs[0] > 0))
529      p2 = evas_string_char_next_get(word, p2, &(glyphs[1]));
530    v1 = _e_kbd_dict_letter_normalise(glyphs[0]);
531    if (glyphs[1] != 0)
532      {
533         v2 = _e_kbd_dict_letter_normalise(glyphs[1]);
534         p = kd->lookup.tuples[v1][v2];
535      }
536    else
537      {
538         for (i = 0; i < 128; i++)
539           {
540              p = kd->lookup.tuples[v1][i];
541              if (p) break;
542           }
543      }
544    return _e_kbd_dict_find_pointer(kd, p, p2, word);
545 }
546
547 static const char *
548 _e_kbd_dict_find_full(E_Kbd_Dict *kd, const char *word)
549 {
550    const char *p;
551    int len;
552
553    p = _e_kbd_dict_find(kd, word);
554    if (!p) return NULL;
555    len = strlen(word);
556    if (isspace(p[len])) return p;
557    return NULL;
558 }
559
560 EAPI void
561 e_kbd_dict_word_usage_adjust(E_Kbd_Dict *kd, const char *word, int adjust)
562 {
563    // add "adjust" to word usage count
564    E_Kbd_Dict_Word *kw;
565
566    kw = _e_kbd_dict_changed_write_find(kd, word);
567    if (kw)
568      {
569         kw->usage += adjust;
570         if (kd->changed.flush_timer)
571           ecore_timer_del(kd->changed.flush_timer);
572         kd->changed.flush_timer = ecore_timer_add(5.0, _e_kbd_dict_cb_save_flush, kd);
573      }
574    else
575      {
576         const char *line;
577         int usage = 0;
578
579         line = _e_kbd_dict_find_full(kd, word);
580         if (line)
581           {
582              char *wd;
583
584              // FIXME: we need to find an EXACT line match - case and all
585              wd = _e_kbd_dict_line_parse(kd, line, &usage);
586              if (wd) free(wd);
587           }
588         usage += adjust;
589         _e_kbd_dict_changed_write_add(kd, word, usage);
590      }
591 }
592
593 EAPI void
594 e_kbd_dict_word_delete(E_Kbd_Dict *kd, const char *word)
595 {
596    // delete a word from the dictionary
597    E_Kbd_Dict_Word *kw;
598
599    kw = _e_kbd_dict_changed_write_find(kd, word);
600    if (kw)
601      kw->usage = -1;
602    else
603      {
604         if (_e_kbd_dict_find_full(kd, word))
605           _e_kbd_dict_changed_write_add(kd, word, -1);
606      }
607 }
608
609 EAPI void
610 e_kbd_dict_word_letter_clear(E_Kbd_Dict *kd)
611 {
612    // clear the current word buffer
613    while (kd->word.letters)
614      e_kbd_dict_word_letter_delete(kd);
615    if (kd->matches.deadends)
616      {
617         eina_hash_free(kd->matches.deadends);
618         kd->matches.deadends = NULL;
619      }
620    if (kd->matches.leads)
621      {
622         eina_hash_free(kd->matches.leads);
623         kd->matches.leads = NULL;
624      }
625    while (kd->matches.list)
626      {
627         E_Kbd_Dict_Word *kw;
628
629         kw = kd->matches.list->data;
630         eina_stringshare_del(kw->word);
631         free(kw);
632         kd->matches.list = eina_list_remove_list(kd->matches.list, kd->matches.list);
633     }
634 }
635
636 EAPI void
637 e_kbd_dict_word_letter_add(E_Kbd_Dict *kd, const char *letter, int dist)
638 {
639    // add a letter with a distance (0 == closest) as an option for the current
640    // letter position - advance starts a new letter position
641    Eina_List *l, *list;
642    E_Kbd_Dict_Letter *kl;
643
644    l = eina_list_last(kd->word.letters);
645    if (!l) return;
646    list = l->data;
647    kl = E_NEW(E_Kbd_Dict_Letter, 1);
648    if (!kl) return;
649    kl->letter = eina_stringshare_add(letter);
650    kl->dist = dist;
651    list = eina_list_append(list, kl);
652    l->data = list;
653 }
654
655 EAPI void
656 e_kbd_dict_word_letter_advance(E_Kbd_Dict *kd)
657 {
658    // start a new letter in the word
659    kd->word.letters = eina_list_append(kd->word.letters, NULL);
660 }
661
662 EAPI void
663 e_kbd_dict_word_letter_delete(E_Kbd_Dict *kd)
664 {
665    // delete the current letter completely
666    Eina_List *l, *list;
667
668    l = eina_list_last(kd->word.letters);
669    if (!l) return;
670    list = l->data;
671    while (list)
672      {
673         E_Kbd_Dict_Letter *kl;
674
675         kl = list->data;
676         eina_stringshare_del(kl->letter);
677         free(kl);
678         list = eina_list_remove_list(list, list);
679      }
680    kd->word.letters = eina_list_remove_list(kd->word.letters, l);
681 }
682
683 static void
684 _e_kbd_dict_matches_lookup_iter(E_Kbd_Dict *kd, Eina_List *word,
685                                 Eina_List *more)
686 {
687    Eina_List *l, *list;
688    const char *p;
689    char *base, *buf, *wd, *bufapp;
690    E_Kbd_Dict_Letter *kl;
691    int len = 0, dist = 0, d, baselen, maxdist = 0, md;
692    static int level = 0;
693
694    level++;
695    for (l = word; l; l = l->next)
696      {
697         kl = l->data;
698         len += strlen(kl->letter);
699         dist += kl->dist;
700         if (kl->dist > maxdist) maxdist = kl->dist;
701      }
702    if (maxdist < 1) maxdist = 1;
703    buf = alloca(len + 20); // 20 - just padding enough for 1 more utf8 char
704    base = alloca(len + 20);
705    base[0] = 0;
706    for (l = word; l; l = l->next)
707      {
708         kl = l->data;
709         strcat(base, kl->letter);
710      }
711    baselen = strlen(base);
712    strcpy(buf, base);
713    bufapp = buf + baselen;
714    list = more->data;
715    for (l = list; l; l = l->next)
716      {
717         kl = l->data;
718         if (kl->dist > maxdist) maxdist = kl->dist;
719      }
720    for (l = list; l; l = l->next)
721      {
722         kl = l->data;
723         strcpy(bufapp, kl->letter);
724         if ((kd->matches.deadends) && eina_hash_find(kd->matches.deadends, buf))
725           continue;
726         p = eina_hash_find(kd->matches.leads, buf);
727         if (p) p = _e_kbd_dict_find_pointer(kd, p, baselen, buf);
728         else p = _e_kbd_dict_find(kd, buf);
729         if (!p)
730           {
731              if (!kd->matches.deadends)
732                kd->matches.deadends = eina_hash_string_superfast_new(NULL);
733              eina_hash_add(kd->matches.deadends, buf, kd);
734              continue;
735           }
736         else
737           {
738              if (!kd->matches.leads)
739                kd->matches.leads = eina_hash_string_superfast_new(NULL);
740              eina_hash_add(kd->matches.leads, buf, p);
741           }
742         if ((!more->next) || (!more->next->data))
743           {
744              d = dist + kl->dist;
745              md = maxdist;
746              for (;;)
747                {
748                   E_Kbd_Dict_Word *kw;
749                   int usage = 0;
750
751                   wd = _e_kbd_dict_line_parse(kd, p, &usage);
752                   if (!wd) break;
753                   if (_e_kbd_dict_normalized_strcmp(wd, buf))
754                     {
755                        free(wd);
756                        break;
757                     }
758                   kw = E_NEW(E_Kbd_Dict_Word, 1);
759                   if (kw)
760                     {
761                        int accuracy;
762                        int w, b, w2, b2, wc, bc;
763
764                        // match any capitalisation
765                        for (w = 0, b = 0; wd[w] && buf[b];)
766                          {
767                             b2 = evas_string_char_next_get(buf, b, &bc);
768                             w2 = evas_string_char_next_get(wd, w, &wc);
769                             if (isupper(bc)) wd[w] = toupper(wc);
770                             w = w2;
771                             b = b2;
772                          }
773                        kw->word = eina_stringshare_add(wd);
774                        // FIXME: magic combination of distance metric and
775                        // frequency of usage. this is simple now, but could
776                        // be tweaked
777                        wc = eina_list_count(word);
778                        if (md < 1) md = 1;
779
780                        // basically a metric to see how far away the keys that
781                        // were actually pressed are away from the letters of
782                        // this word in a physical on-screen sense
783                        accuracy = md - (d / (wc + 1));
784                        // usage is the frequency of usage in the dictionary.
785                        // it its < 1 time, it's assumed to be 1.
786                        if (usage < 1) usage = 1;
787                        // multiply usage by a factor of 100 for better detailed
788                        // sorting. 10 == 1/10th factor
789                        usage = 100 + ((usage - 1) * 10);
790                        // and well just multiply and lets see. maybe this can
791                        // do with multiplication factors etc. but simple for
792                        // now.
793                        kw->usage = (usage * accuracy) / md;
794                        kd->matches.list = eina_list_append(kd->matches.list, kw);
795                     }
796                   free(wd);
797                   p = _e_kbd_dict_line_next(kd, p);
798                   if (p >= (kd->file.dict + kd->file.size)) break;
799                   if (!p) break;
800                }
801           }
802         else
803           {
804              word = eina_list_append(word, kl);
805              _e_kbd_dict_matches_lookup_iter(kd, word, more->next);
806              word = eina_list_remove_list(word, eina_list_last(word));
807           }
808      }
809    level--;
810 }
811
812 EAPI void
813 e_kbd_dict_matches_lookup(E_Kbd_Dict *kd)
814 {
815    // find all matches and sort them
816    while (kd->matches.list)
817      {
818         E_Kbd_Dict_Word *kw;
819
820         kw = kd->matches.list->data;
821         eina_stringshare_del(kw->word);
822         free(kw);
823         kd->matches.list = eina_list_remove_list(kd->matches.list, kd->matches.list);
824     }
825    if (kd->word.letters)
826      _e_kbd_dict_matches_lookup_iter(kd, NULL, kd->word.letters);
827    kd->matches.list = eina_list_sort(kd->matches.list,
828                                      eina_list_count(kd->matches.list),
829                                      _e_kbd_dict_matches_lookup_cb_sort);
830 }
831
832 EAPI void
833 e_kbd_dict_matches_first(E_Kbd_Dict *kd)
834 {
835    // jump to first match
836    kd->matches.list_ptr = kd->matches.list;
837 }
838
839 EAPI void
840 e_kbd_dict_matches_next(E_Kbd_Dict *kd)
841 {
842    // jump to next match
843    kd->matches.list_ptr = kd->matches.list_ptr->next;
844 }
845
846 EAPI const char *
847 e_kbd_dict_matches_match_get(E_Kbd_Dict *kd, int *pri_ret)
848 {
849    // return the word (string utf-8) for the current match
850    if (kd->matches.list_ptr)
851      {
852         E_Kbd_Dict_Word *kw;
853
854         kw = kd->matches.list_ptr->data;
855         if (kw)
856           {
857              *pri_ret = kw->usage;
858              return kw->word;
859           }
860      }
861    return NULL;
862 }