#include #include "e_kbd_buf.h" #include "e_kbd_int.h" #include "e_kbd_send.h" #include "e_cfg.h" enum { NORMAL = 0, SHIFT = (1 << 0), CAPSLOCK = (1 << 1), CTRL = (1 << 2), ALT = (1 << 3) }; static Evas_Object *_theme_obj_new(Evas *e, const char *custom_dir, const char *group); static void _e_kbd_int_layout_next(E_Kbd_Int *ki); static void _e_kbd_int_matches_update(E_Kbd_Int *ki); static void _e_kbd_int_cb_resize(E_Win *win) { E_Kbd_Int *ki; ki = win->data; evas_object_resize(ki->base_obj, ki->win->w, ki->win->h); } static const char * _e_kbd_int_str_unquote(const char *str) { static char buf[256]; char *p; snprintf(buf, sizeof(buf), "%s", str + 1); p = strrchr(buf, '"'); if (p) *p = 0; return buf; } static E_Kbd_Int_Key * _e_kbd_int_at_coord_get(E_Kbd_Int *ki, Evas_Coord x, Evas_Coord y) { Evas_List *l; for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; ky = l->data; if ((x >= ky->x) && (y >= ky->y) && (x < (ky->x + ky->w)) && (y < (ky->y + ky->h))) return ky; } return NULL; } static E_Kbd_Int_Key_State * _e_kbd_int_key_state_get(E_Kbd_Int *ki, E_Kbd_Int_Key *ky) { Evas_List *l; for (l = ky->states; l; l = l->next) { E_Kbd_Int_Key_State *st; st = l->data; if (st->state & ki->layout.state) return st; } for (l = ky->states; l; l = l->next) { E_Kbd_Int_Key_State *st; st = l->data; if (st->state == NORMAL) return st; } return NULL; } static void _e_kbd_int_layout_buf_update(E_Kbd_Int *ki) { Evas_List *l, *l2; e_kbd_buf_layout_clear(ki->kbuf); e_kbd_buf_layout_size_set(ki->kbuf, ki->layout.w, ki->layout.h); e_kbd_buf_layout_fuzz_set(ki->kbuf, ki->layout.fuzz); for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; E_Kbd_Int_Key_State *st; const char *out, *out_shift, *out_capslock; ky = l->data; out = NULL; out_shift = NULL; out_capslock = NULL; for (l2 = ky->states; l2; l2 = l2->next) { st = l2->data; if (st->state == NORMAL) out = st->out; else if (st->state == SHIFT) out_shift = st->out; else if (st->state == CAPSLOCK) out_capslock = st->out; } if (out) { char *s1 = NULL, *s2 = NULL, *s3 = NULL; if ((out) && (out[0] == '"')) s1 = strdup(_e_kbd_int_str_unquote(out)); if ((out_shift) && (out_shift[0] == '"')) s2 = strdup(_e_kbd_int_str_unquote(out_shift)); if ((out_capslock) && (out_capslock[0] == '"')) s3 = strdup(_e_kbd_int_str_unquote(out_capslock)); e_kbd_buf_layout_key_add(ki->kbuf, s1, s2, s3, ky->x, ky->y, ky->w, ky->h); if (s1) free(s1); if (s2) free(s2); if (s3) free(s3); } } } static void _e_kbd_int_layout_state_update(E_Kbd_Int *ki) { Evas_List *l; for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; E_Kbd_Int_Key_State *st; int selected; ky = l->data; st = _e_kbd_int_key_state_get(ki, ky); if (st) { if (st->label) edje_object_part_text_set(ky->obj, "e.text.label", st->label); else edje_object_part_text_set(ky->obj, "e.text.label", ""); if (st->icon) { char buf[PATH_MAX]; char *p; snprintf(buf, sizeof(buf), "%s/%s", ki->layout.directory, st->icon); p = strrchr(st->icon, '.'); if (!strcmp(p, ".edj")) e_icon_file_edje_set(ky->icon_obj, buf, "icon"); else e_icon_file_set(ky->icon_obj, buf); } else e_icon_file_set(ky->icon_obj, NULL); } selected = 0; if ((ki->layout.state & SHIFT) && (ky->is_shift)) selected = 1; if ((ki->layout.state & CTRL) && (ky->is_ctrl)) selected = 1; if ((ki->layout.state & ALT) && (ky->is_alt)) selected = 1; if ((ki->layout.state & CAPSLOCK) && (ky->is_capslock)) selected = 1; if (selected) { if (!ky->selected) { edje_object_signal_emit(ky->obj, "e,state,selected", "e"); ky->selected = 1; } } if (!selected) { if (ky->selected) { edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); ky->selected = 0; } } } } static void _e_kbd_int_string_send(E_Kbd_Int *ki, const char *str) { int pos, newpos, glyph; pos = 0; for (;;) { char buf[16]; newpos = evas_string_char_next_get(str, pos, &glyph); if (glyph <= 0) return; strncpy(buf, str + pos, newpos - pos); buf[newpos - pos] = 0; e_kbd_send_string_press(buf, 0); pos = newpos; } } static void _e_kbd_int_buf_send(E_Kbd_Int *ki) { const char *str = NULL; const Evas_List *matches; matches = e_kbd_buf_string_matches_get(ki->kbuf); if (matches) str = matches->data; else str = e_kbd_buf_actual_string_get(ki->kbuf); if (str) _e_kbd_int_string_send(ki, str); } static void _e_kbd_int_cb_match_select(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd_Int_Match *km; km = data; _e_kbd_int_string_send(km->ki, km->str); e_kbd_buf_clear(km->ki->kbuf); e_kbd_send_keysym_press("space", 0); if (km->ki->layout.state & (SHIFT | CTRL | ALT)) { km->ki->layout.state &= (~(SHIFT | CTRL | ALT)); _e_kbd_int_layout_state_update(km->ki); } _e_kbd_int_matches_update(km->ki); } static void _e_kbd_int_matches_add(E_Kbd_Int *ki, const char *str, int num) { E_Kbd_Int_Match *km; Evas_Object *o; Evas_Coord mw, mh; km = E_NEW(E_Kbd_Int_Match, 1); if (!km) return; o = _theme_obj_new(ki->win->evas, ki->themedir, "e/modules/kbd/match/word"); km->ki = ki; km->str = evas_stringshare_add(str); km->obj = o; ki->matches = evas_list_append(ki->matches, km); edje_object_part_text_set(o, "e.text.label", str); edje_object_size_min_calc(o, &mw, &mh); if (mw < 32) mw = 32; if (num & 0x1) e_box_pack_start(ki->box_obj, o); else e_box_pack_end(ki->box_obj, o); e_box_pack_options_set(o, 1, 1, 1, 1, 0.5, 0.5, mw, mh, mw, mh); edje_object_signal_callback_add(o, "e,action,do,select", "", _e_kbd_int_cb_match_select, km); evas_object_show(o); } static void _e_kbd_int_matches_free(E_Kbd_Int *ki) { while (ki->matches) { E_Kbd_Int_Match *km; km = ki->matches->data; if (km->str) evas_stringshare_del(km->str); evas_object_del(km->obj); free(km); ki->matches = evas_list_remove_list(ki->matches, ki->matches); } } static void _e_kbd_int_matches_update(E_Kbd_Int *ki) { const Evas_List *l, *matches; Evas_Coord mw, mh; evas_event_freeze(ki->win->evas); e_box_freeze(ki->box_obj); _e_kbd_int_matches_free(ki); matches = e_kbd_buf_string_matches_get(ki->kbuf); if (!matches) { const char *actual; actual = e_kbd_buf_actual_string_get(ki->kbuf); if (actual) _e_kbd_int_matches_add(ki, actual, 0); } else { int i = 0; for (i = 0, l = matches; l; l = l->next, i++) _e_kbd_int_matches_add(ki, l->data, i); } e_box_thaw(ki->box_obj); e_box_min_size_get(ki->box_obj, &mw, &mh); edje_extern_object_min_size_set(ki->box_obj, 0, mh); edje_object_part_swallow(ki->base_obj, "e.swallow.label", ki->box_obj); evas_event_thaw(ki->win->evas); } static void _e_kbd_int_key_press_handle(E_Kbd_Int *ki, Evas_Coord dx, Evas_Coord dy) { E_Kbd_Int_Key *ky; E_Kbd_Int_Key_State *st; const char *out = NULL; ky = _e_kbd_int_at_coord_get(ki, dx, dy); if (!ky) return; if (ky->is_shift) { if (ki->layout.state & SHIFT) ki->layout.state &= (~(SHIFT)); else ki->layout.state |= SHIFT; _e_kbd_int_layout_state_update(ki); return; } if (ky->is_ctrl) { if (ki->layout.state & CTRL) ki->layout.state &= (~(CTRL)); else ki->layout.state |= CTRL; if (e_kbd_buf_actual_string_get(ki->kbuf)) _e_kbd_int_buf_send(ki); e_kbd_buf_clear(ki->kbuf); ki->layout.state |= CTRL; _e_kbd_int_layout_state_update(ki); _e_kbd_int_matches_update(ki); return; } if (ky->is_alt) { if (ki->layout.state & ALT) ki->layout.state &= (~(ALT)); else ki->layout.state |= ALT; if (e_kbd_buf_actual_string_get(ki->kbuf)) _e_kbd_int_buf_send(ki); e_kbd_buf_clear(ki->kbuf); ki->layout.state |= ALT; _e_kbd_int_layout_state_update(ki); _e_kbd_int_matches_update(ki); return; } if (ky->is_capslock) { if (ki->layout.state & CAPSLOCK) ki->layout.state &= (~CAPSLOCK); else ki->layout.state |= CAPSLOCK; _e_kbd_int_layout_state_update(ki); return; } st = _e_kbd_int_key_state_get(ki, ky); if (st) out = st->out; if (ki->layout.state & (CTRL | ALT)) { if (out) { Kbd_Mod mods = 0; if (ki->layout.state & CTRL) mods |= KBD_MOD_CTRL; if (ki->layout.state & ALT) mods |= KBD_MOD_ALT; if (out[0] == '"') { e_kbd_send_string_press(_e_kbd_int_str_unquote(out), mods); } else e_kbd_send_keysym_press(out, mods); } ki->layout.state &= (~(SHIFT | CTRL | ALT)); _e_kbd_int_layout_state_update(ki); e_kbd_buf_lookup(ki->kbuf, _e_kbd_int_matches_update, ki); return; } if (out) { if (out[0] == '"') { e_kbd_buf_pressed_point_add(ki->kbuf, dx, dy, ki->layout.state & SHIFT, ki->layout.state & CAPSLOCK); e_kbd_buf_lookup(ki->kbuf, _e_kbd_int_matches_update, ki); } else { if (e_kbd_buf_actual_string_get(ki->kbuf)) _e_kbd_int_buf_send(ki); e_kbd_buf_clear(ki->kbuf); e_kbd_send_keysym_press(out, 0); _e_kbd_int_matches_update(ki); } } if (ki->layout.state & (SHIFT | CTRL | ALT)) { ki->layout.state &= (~(SHIFT | CTRL | ALT)); _e_kbd_int_layout_state_update(ki); } } static void _e_kbd_int_stroke_handle(E_Kbd_Int *ki, int dir) { if (dir == 4) // up { _e_kbd_int_layout_next(ki); } else if (dir == 3) // left { if (e_kbd_buf_actual_string_get(ki->kbuf)) { e_kbd_buf_backspace(ki->kbuf); e_kbd_buf_lookup(ki->kbuf, _e_kbd_int_matches_update, ki); } else e_kbd_send_keysym_press("BackSpace", 0); } else if (dir == 2) // down { if (e_kbd_buf_actual_string_get(ki->kbuf)) _e_kbd_int_buf_send(ki); e_kbd_buf_clear(ki->kbuf); e_kbd_send_keysym_press("Return", 0); _e_kbd_int_matches_update(ki); } else if (dir == 1) // right { if (e_kbd_buf_actual_string_get(ki->kbuf)) _e_kbd_int_buf_send(ki); e_kbd_buf_clear(ki->kbuf); e_kbd_send_keysym_press("space", 0); _e_kbd_int_matches_update(ki); } if (ki->layout.state) { ki->layout.state = 0; _e_kbd_int_layout_state_update(ki); } } static void _e_kbd_int_zoomkey_down(E_Kbd_Int *ki) { const Evas_List *l; if (!ki->zoomkey.popup) return; e_object_del(E_OBJECT(ki->zoomkey.popup)); ki->zoomkey.popup = NULL; ki->zoomkey.layout_obj = NULL; ki->zoomkey.sublayout_obj = NULL; for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; ky = l->data; ky->zoom_obj = NULL; ky->zoom_icon_obj = NULL; } } static void _e_kbd_int_zoomkey_up(E_Kbd_Int *ki) { const Evas_List *l; Evas_Object *o, *o2; Evas_Coord w, h, mw, mh, vw, vh; int sx, sy, sw, sh; if (ki->zoomkey.popup) return; ki->zoomkey.popup = e_popup_new(ki->win->border->zone, -1, -1, 1, 1); e_popup_layer_set(ki->zoomkey.popup, 190); o = _theme_obj_new(ki->zoomkey.popup->evas, ki->themedir, "e/modules/kbd/zoom/default"); ki->zoomkey.base_obj = o; o = e_layout_add(ki->zoomkey.popup->evas); e_layout_virtual_size_set(o, 100, 100); edje_object_part_swallow(ki->zoomkey.base_obj, "e.swallow.content", o); evas_object_show(o); ki->zoomkey.layout_obj = o; e_layout_virtual_size_get(ki->layout_obj, &vw, &vh); o = e_layout_add(ki->zoomkey.popup->evas); e_layout_virtual_size_set(o, vw, vh); e_layout_pack(ki->zoomkey.layout_obj, o); e_layout_child_move(o, 0, 0); // FIXME dimension * 4 is a magic number - make config e_layout_child_resize(o, vw * 4, vh * 4); evas_object_show(o); ki->zoomkey.sublayout_obj = o; for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; E_Kbd_Int_Key_State *st; const char *label, *icon; int selected; ky = l->data; o = _theme_obj_new(ki->zoomkey.popup->evas, ki->themedir, "e/modules/kbd/zoomkey/default"); label = ""; icon = NULL; st = _e_kbd_int_key_state_get(ki, ky); if (st) { label = st->label; icon = st->icon; } edje_object_part_text_set(o, "e.text.label", label); o2 = e_icon_add(ki->zoomkey.popup->evas); e_icon_fill_inside_set(o2, 1); // e_icon_scale_up_set(o2, 0); edje_object_part_swallow(o, "e.swallow.content", o2); evas_object_show(o2); if (icon) { char buf[PATH_MAX]; char *p; snprintf(buf, sizeof(buf), "%s/%s", ki->layout.directory, icon); p = strrchr(icon, '.'); if (!strcmp(p, ".edj")) e_icon_file_edje_set(o2, buf, "icon"); else e_icon_file_set(o2, buf); } selected = 0; if ((ki->layout.state & SHIFT) && (ky->is_shift)) selected = 1; if ((ki->layout.state & CTRL) && (ky->is_ctrl)) selected = 1; if ((ki->layout.state & ALT) && (ky->is_alt)) selected = 1; if ((ki->layout.state & CAPSLOCK) && (ky->is_capslock)) selected = 1; if (selected) edje_object_signal_emit(o, "e,state,selected", "e"); if (!selected) edje_object_signal_emit(o, "e,state,unselected", "e"); e_layout_pack(ki->zoomkey.sublayout_obj, o); e_layout_child_move(o, ky->x, ky->y); e_layout_child_resize(o, ky->w, ky->h); evas_object_show(o); ky->zoom_obj = o; ky->zoom_icon_obj = o2; } edje_object_size_max_get(ki->zoomkey.base_obj, &vw, &vh); e_slipshelf_safe_app_region_get(ki->win->border->zone, &sx, &sy, &sw, &sh); sh -= ki->win->h; mw = sw; if ((vw > 0) && (mw > vw)) mw = vw; mh = sh; if ((vh > 0) && (mh > vh)) mh = vh; e_popup_move_resize(ki->zoomkey.popup, sx + ((sw - mw) / 2), sy + ((sh - mh) / 2), mw, mh); evas_object_resize(ki->zoomkey.base_obj, ki->zoomkey.popup->w, ki->zoomkey.popup->h); evas_object_show(ki->zoomkey.base_obj); e_popup_edje_bg_object_set(ki->zoomkey.popup, ki->zoomkey.base_obj); e_popup_show(ki->zoomkey.popup); } static void _e_kbd_int_zoomkey_update(E_Kbd_Int *ki) { Evas_Coord w, h, ww, hh; E_Kbd_Int_Key *ky; evas_object_geometry_get(ki->zoomkey.layout_obj, NULL, NULL, &w, &h); evas_object_geometry_get(ki->layout_obj, NULL, NULL, &ww, &hh); e_layout_virtual_size_set(ki->zoomkey.layout_obj, w, h); // FIXME dimension * 4 is a magic number - make config e_layout_child_resize(ki->zoomkey.sublayout_obj, ww * 4, hh * 4); e_layout_child_move(ki->zoomkey.sublayout_obj, (w / 2) - (ki->down.cx * 4), (h / 2) - (ki->down.cy * 4)); ky = _e_kbd_int_at_coord_get(ki, ki->down.clx, ki->down.cly); if (ky != ki->zoomkey.pressed) { if (ki->zoomkey.pressed) { ki->zoomkey.pressed->pressed = 0; edje_object_signal_emit(ki->zoomkey.pressed->zoom_obj, "e,state,released", "e"); edje_object_signal_emit(ki->zoomkey.pressed->obj, "e,state,released", "e"); } ki->zoomkey.pressed = ky; if (ki->zoomkey.pressed) { ki->zoomkey.pressed->pressed = 1; edje_object_signal_emit(ki->zoomkey.pressed->zoom_obj, "e,state,pressed", "e"); edje_object_signal_emit(ki->zoomkey.pressed->obj, "e,state,pressed", "e"); } } } static int _e_kbd_int_cb_hold_timeout(void *data) { E_Kbd_Int *ki; ki = data; ki->down.hold_timer = NULL; ki->down.zoom = 1; if (ki->layout.pressed) { ki->layout.pressed->pressed = 0; edje_object_signal_emit(ki->layout.pressed->obj, "e,state,released", "e"); ki->layout.pressed = NULL; } _e_kbd_int_zoomkey_up(ki); _e_kbd_int_zoomkey_update(ki); return 0; } static void _e_kbd_int_cb_mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Down *ev; E_Kbd_Int *ki; Evas_Coord x, y, w, h; E_Kbd_Int_Key *ky; ev = event_info; if (ev->button != 1) return; ki = data; ki->down.x = ev->canvas.x; ki->down.y = ev->canvas.y; ki->down.down = 1; ki->down.stroke = 0; evas_object_geometry_get(ki->event_obj, &x, &y, &w, &h); x = ev->canvas.x - x; y = ev->canvas.y - y; ki->down.cx = x; ki->down.cy = y; x = (x * ki->layout.w) / w; y = (y * ki->layout.h) / h; ki->down.lx = x; ki->down.ly = y; ki->down.clx = x; ki->down.cly = y; if (ki->down.hold_timer) ecore_timer_del(ki->down.hold_timer); // FIXME 0.25 - magic value. make config. ki->down.hold_timer = ecore_timer_add(0.25, _e_kbd_int_cb_hold_timeout, ki); ky = _e_kbd_int_at_coord_get(ki, x, y); ki->layout.pressed = ky; if (ky) { ky->pressed = 1; edje_object_signal_emit(ky->obj, "e,state,pressed", "e"); } } static void _e_kbd_int_cb_mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Move *ev; E_Kbd_Int *ki; Evas_Coord dx, dy, x, w, y, h; ev = event_info; ki = data; if (ki->down.zoom) { evas_object_geometry_get(ki->event_obj, &x, &y, &w, &h); x = ev->cur.canvas.x - x; y = ev->cur.canvas.y - y; ki->down.cx = x; ki->down.cy = y; x = (x * ki->layout.w) / w; y = (y * ki->layout.h) / h; ki->down.clx = x; ki->down.cly = y; _e_kbd_int_zoomkey_update(ki); return; } if (ki->down.stroke) return; dx = ev->cur.canvas.x - ki->down.x; dy = ev->cur.canvas.y - ki->down.y; evas_object_geometry_get(ki->event_obj, &x, &y, &w, &h); dx = (dx * ki->layout.w) / w; dy = (dy * ki->layout.h) / h; // FIXME: slide 1/4 of dimension is a magic number - make config if ((dx > 0) && (dx > (ki->layout.w / 4))) ki->down.stroke = 1; else if ((dx < 0) && (-dx > (ki->layout.w / 4))) ki->down.stroke = 1; if ((dy > 0) && (dy > (ki->layout.h / 4))) ki->down.stroke = 1; else if ((dy < 0) && (-dy > (ki->layout.w / 4))) ki->down.stroke = 1; if ((ki->down.stroke) && (ki->down.hold_timer)) { ecore_timer_del(ki->down.hold_timer); ki->down.hold_timer = NULL; } } static void _e_kbd_int_cb_mouse_up(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Up *ev; E_Kbd_Int *ki; Evas_Coord x, y; E_Kbd_Int_Key *ky; int dir = 0; ev = event_info; if (ev->button != 1) return; ki = data; if (ki->down.zoom) { _e_kbd_int_zoomkey_down(ki); ki->down.zoom = 0; _e_kbd_int_key_press_handle(ki, ki->down.clx, ki->down.cly); } else if (!ki->down.stroke) _e_kbd_int_key_press_handle(ki, ki->down.lx, ki->down.ly); else { Evas_Coord dx, dy; dx = ev->canvas.x - ki->down.x; dy = ev->canvas.y - ki->down.y; if (dx > 0) { if (dy > 0) { if (dx > dy) dir = 1; // right else dir = 2; // down } else { if (dx > -dy) dir = 1; // right else dir = 4; // up } } else { if (dy > 0) { if (-dx > dy) dir = 3; // left else dir = 2; // down } else { if (-dx > -dy) dir = 3; // left else dir = 4; // up } } } ky = ki->layout.pressed; if (ky) { ky->pressed = 0; edje_object_signal_emit(ky->obj, "e,state,released", "e"); ki->layout.pressed = NULL; } ky = ki->zoomkey.pressed; if (ky) { ky->pressed = 0; edje_object_signal_emit(ky->obj, "e,state,released", "e"); ki->zoomkey.pressed = NULL; } ki->down.down = 0; ki->down.stroke = 0; if (ki->down.hold_timer) { ecore_timer_del(ki->down.hold_timer); ki->down.hold_timer = NULL; } if (dir > 0) _e_kbd_int_stroke_handle(ki, dir); } static E_Kbd_Int_Layout * _e_kbd_int_layouts_list_default_get(E_Kbd_Int *ki) { Evas_List *l; for (l = ki->layouts; l; l = l->next) { E_Kbd_Int_Layout *kil; kil = l->data; if ((!strcmp(ecore_file_file_get(kil->path), "Default.kbd"))) return kil; } return NULL; } static E_Kbd_Int_Layout * _e_kbd_int_layouts_type_get(E_Kbd_Int *ki, int type) { Evas_List *l; for (l = ki->layouts; l; l = l->next) { E_Kbd_Int_Layout *kil; kil = l->data; if (kil->type == type) return kil; } return NULL; } static void _e_kbd_int_layout_free(E_Kbd_Int *ki) { if (ki->layout.directory) free(ki->layout.directory); if (ki->layout.file) evas_stringshare_del(ki->layout.file); ki->layout.directory = NULL; ki->layout.file = NULL; while (ki->layout.keys) { E_Kbd_Int_Key *ky; ky = ki->layout.keys->data; while (ky->states) { E_Kbd_Int_Key_State *st; st = ky->states->data; if (st->label) evas_stringshare_del(st->label); if (st->icon) evas_stringshare_del(st->icon); if (st->out) evas_stringshare_del(st->out); free(st); ky->states = evas_list_remove_list(ky->states, ky->states); } if (ky->obj) evas_object_del(ky->obj); if (ky->icon_obj) evas_object_del(ky->icon_obj); free(ky); ki->layout.keys = evas_list_remove_list(ki->layout.keys, ki->layout.keys); } if (ki->event_obj) evas_object_del(ki->event_obj); ki->event_obj = NULL; } static void _e_kbd_int_layout_parse(E_Kbd_Int *ki, const char *layout) { FILE *f; char buf[4096]; int isok = 0; E_Kbd_Int_Key *ky = NULL; E_Kbd_Int_Key_State *st = NULL; f = fopen(layout, "r"); if (!f) return; ki->layout.directory = ecore_file_dir_get(layout); ki->layout.file = evas_stringshare_add(layout); while (fgets(buf, sizeof(buf), f)) { int len; char str[4096]; if (!isok) { if (!strcmp(buf, "##KBDCONF-1.0\n")) isok = 1; } if (!isok) break; if (buf[0] == '#') continue; len = strlen(buf); if (len > 0) { if (buf[len - 1] == '\n') buf[len - 1] = 0; } if (sscanf(buf, "%4000s", str) != 1) continue; if (!strcmp(str, "kbd")) { if (sscanf(buf, "%*s %i %i\n", &(ki->layout.w), &(ki->layout.h)) != 2) continue; } if (!strcmp(str, "fuzz")) { sscanf(buf, "%*s %i\n", &(ki->layout.fuzz)); continue; } if (!strcmp(str, "key")) { ky = calloc(1, sizeof(E_Kbd_Int_Key)); if (!ky) continue; if (sscanf(buf, "%*s %i %i %i %i\n", &(ky->x), &(ky->y), &(ky->w), &(ky->h)) != 4) { free(ky); ky = NULL; continue; } ki->layout.keys = evas_list_append(ki->layout.keys, ky); } if (!ky) continue; if ((!strcmp(str, "normal")) || (!strcmp(str, "shift")) || (!strcmp(str, "capslock"))) { char *p; char label[4096]; int xx; if (sscanf(buf, "%*s %4000s", label) != 1) continue; st = calloc(1, sizeof(E_Kbd_Int_Key_State)); if (!st) continue; ky->states = evas_list_append(ky->states, st); if (!strcmp(str, "normal")) st->state = NORMAL; if (!strcmp(str, "shift")) st->state = SHIFT; if (!strcmp(str, "capslock")) st->state = CAPSLOCK; p = strrchr(label, '.'); if ((p) && (!strcmp(p, ".png"))) st->icon = evas_stringshare_add(label); else if ((p) && (!strcmp(p, ".edj"))) st->icon = evas_stringshare_add(label); else st->label = evas_stringshare_add(label); if (sscanf(buf, "%*s %*s %4000s", str) != 1) continue; st->out = evas_stringshare_add(str); } if (!strcmp(str, "is_shift")) ky->is_shift = 1; if (!strcmp(str, "is_ctrl")) ky->is_ctrl = 1; if (!strcmp(str, "is_alt")) ky->is_alt = 1; if (!strcmp(str, "is_capslock")) ky->is_capslock = 1; } fclose(f); } static void _e_kbd_int_layout_build(E_Kbd_Int *ki) { Evas_Object *o, *o2; Evas_Coord lw, lh; Evas_List *l; evas_event_freeze(ki->win->evas); e_layout_virtual_size_set(ki->layout_obj, ki->layout.w, ki->layout.h); edje_extern_object_min_size_set(ki->layout_obj, ki->layout.w, ki->layout.h); edje_object_part_swallow(ki->base_obj, "e.swallow.content", ki->layout_obj); evas_object_resize(ki->base_obj, ki->win->w, ki->win->h); evas_object_geometry_get(ki->layout_obj, NULL, NULL, &lw, &lh); lh = (ki->layout.h * lw) / ki->layout.w; edje_extern_object_min_size_set(ki->layout_obj, lw, lh); edje_object_part_swallow(ki->base_obj, "e.swallow.content", ki->layout_obj); for (l = ki->layout.keys; l; l = l->next) { E_Kbd_Int_Key *ky; E_Kbd_Int_Key_State *st; const char *label, *icon; ky = l->data; o = _theme_obj_new(ki->win->evas, ki->themedir, "e/modules/kbd/key/default"); ky->obj = o; label = ""; icon = NULL; st = _e_kbd_int_key_state_get(ki, ky); if (st) { label = st->label; icon = st->icon; } edje_object_part_text_set(o, "e.text.label", label); o2 = e_icon_add(ki->win->evas); e_icon_fill_inside_set(o2, 1); e_icon_scale_up_set(o2, 0); ky->icon_obj = o2; edje_object_part_swallow(o, "e.swallow.content", o2); evas_object_show(o2); if (icon) { char buf[PATH_MAX]; char *p; snprintf(buf, sizeof(buf), "%s/%s", ki->layout.directory, icon); p = strrchr(icon, '.'); if (!strcmp(p, ".edj")) e_icon_file_edje_set(o2, buf, "icon"); else e_icon_file_set(o2, buf); } e_layout_pack(ki->layout_obj, o); e_layout_child_move(o, ky->x, ky->y); e_layout_child_resize(o, ky->w, ky->h); evas_object_show(o); } o = evas_object_rectangle_add(ki->win->evas); e_layout_pack(ki->layout_obj, o); e_layout_child_move(o, 0, 0); e_layout_child_resize(o, ki->layout.w, ki->layout.h); evas_object_color_set(o, 0, 0, 0, 0); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _e_kbd_int_cb_mouse_down, ki); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _e_kbd_int_cb_mouse_up, ki); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _e_kbd_int_cb_mouse_move, ki); evas_object_show(o); ki->event_obj = o; evas_event_thaw(ki->win->evas); } static void _e_kbd_int_layouts_free(E_Kbd_Int *ki) { while (ki->layouts) { E_Kbd_Int_Layout *kil; kil = ki->layouts->data; evas_stringshare_del(kil->path); evas_stringshare_del(kil->dir); evas_stringshare_del(kil->icon); evas_stringshare_del(kil->name); free(kil); ki->layouts = evas_list_remove_list(ki->layouts, ki->layouts); } } static void _e_kbd_int_layouts_list_update(E_Kbd_Int *ki) { Ecore_List *files; char buf[PATH_MAX], *p, *file; const char *homedir, *fl; Evas_List *kbs = NULL, *l, *layouts = NULL; int ok; homedir = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/keyboards", homedir); files = ecore_file_ls(buf); if (files) { ecore_list_first_goto(files); while ((file = ecore_list_current(files))) { p = strrchr(file, '.'); if ((p) && (!strcmp(p, ".kbd"))) { snprintf(buf, sizeof(buf), "%s/.e/e/keyboards/%s", homedir, file); kbs = evas_list_append(kbs, evas_stringshare_add(buf)); } ecore_list_next(files); } ecore_list_destroy(files); } snprintf(buf, sizeof(buf), "%s/keyboards", ki->syskbds); files = ecore_file_ls(buf); if (files) { ecore_list_first_goto(files); while ((file = ecore_list_current(files))) { p = strrchr(file, '.'); if ((p) && (!strcmp(p, ".kbd"))) { ok = 1; for (l = kbs; l; l = l->next) { fl = ecore_file_file_get(l->data); if (!strcmp(file, fl)) { ok = 0; break; } } if (ok) { snprintf(buf, sizeof(buf), "%s/keyboards/%s", ki->syskbds, file); kbs = evas_list_append(kbs, evas_stringshare_add(buf)); } } ecore_list_next(files); } ecore_list_destroy(files); } for (l = kbs; l; l = l->next) { E_Kbd_Int_Layout *kil; kil = E_NEW(E_Kbd_Int_Layout, 1); if (kil) { char *s, *p; FILE *f; kil->path = l->data; l->data = NULL; s = strdup(ecore_file_file_get(kil->path)); if (s) { p = strrchr(s, '.'); if (p) *p = 0; kil->name = evas_stringshare_add(s); free(s); } s = ecore_file_dir_get(kil->path); if (s) { kil->dir = evas_stringshare_add(s); free(s); } f = fopen(kil->path, "r"); if (f) { int isok = 0; while (fgets(buf, sizeof(buf), f)) { int len; char str[4096]; if (!isok) { if (!strcmp(buf, "##KBDCONF-1.0\n")) isok = 1; } if (!isok) break; if (buf[0] == '#') continue; len = strlen(buf); if (len > 0) { if (buf[len - 1] == '\n') buf[len - 1] = 0; } if (sscanf(buf, "%4000s", str) != 1) continue; if (!strcmp(str, "type")) { sscanf(buf, "%*s %4000s\n", str); if (!strcmp(str, "ALPHA")) kil->type = E_KBD_INT_TYPE_ALPHA; else if (!strcmp(str, "NUMERIC")) kil->type = E_KBD_INT_TYPE_NUMERIC; else if (!strcmp(str, "FULL")) kil->type = E_KBD_INT_TYPE_FULL; continue; } if (!strcmp(str, "icon")) { sscanf(buf, "%*s %4000s\n", str); snprintf(buf, sizeof(buf), "%s/%s", kil->dir, str); kil->icon = evas_stringshare_add(buf); continue; } } fclose(f); } layouts = evas_list_append(layouts, kil); } } evas_list_free(kbs); _e_kbd_int_layouts_free(ki); ki->layouts = layouts; } static void _e_kbd_int_layout_select(E_Kbd_Int *ki, E_Kbd_Int_Layout *kil) { _e_kbd_int_layout_free(ki); _e_kbd_int_layout_parse(ki, kil->path); _e_kbd_int_layout_build(ki); _e_kbd_int_layout_buf_update(ki); _e_kbd_int_layout_state_update(ki); if (!kil->icon) e_icon_file_set(ki->icon_obj, kil->icon); else { const char *p; p = strrchr(kil->icon, '.'); if (!p) e_icon_file_set(ki->icon_obj, kil->icon); else { if (!strcmp(p, ".edj")) e_icon_file_edje_set(ki->icon_obj, kil->icon, "icon"); else e_icon_file_set(ki->icon_obj, kil->icon); } } } static void _e_kbd_int_layout_next(E_Kbd_Int *ki) { Evas_List *l, *ln = NULL; const char *nextlay = NULL; E_Kbd_Int_Layout *kil; for (l = ki->layouts; l; l = l->next) { kil = l->data; if (!strcmp(kil->path, ki->layout.file)) { ln = l->next; break; } } if (!ln) ln = ki->layouts; if (!ln) return; kil = ln->data; _e_kbd_int_layout_select(ki, kil); } static int _e_kbd_int_cb_client_message(void *data, int type, void *event) { Ecore_X_Event_Client_Message *ev; E_Kbd_Int *ki; ev = event; ki = data; if ((ev->win == ki->win->evas_win) && (ev->message_type == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE)) { E_Kbd_Int_Layout *kil; if (ev->data.l[0] == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_ALPHA) { kil = _e_kbd_int_layouts_type_get(ki, E_KBD_INT_TYPE_ALPHA); if (kil) _e_kbd_int_layout_select(ki, kil); } else if (ev->data.l[0] == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_NUMERIC) { kil = _e_kbd_int_layouts_type_get(ki, E_KBD_INT_TYPE_NUMERIC); if (kil) _e_kbd_int_layout_select(ki, kil); } } return 1; } static void _e_kbd_int_matchlist_down(E_Kbd_Int *ki) { if (!ki->matchlist.popup) return; e_object_del(E_OBJECT(ki->matchlist.popup)); ki->matchlist.popup = NULL; while (ki->matchlist.matches) { evas_stringshare_del(ki->matchlist.matches->data); ki->matchlist.matches = evas_list_remove_list(ki->matchlist.matches, ki->matchlist.matches); } } static void _e_kbd_int_cb_matchlist_item_sel(void *data, void *data2) { E_Kbd_Int *ki; const char *str; ki = data; str = data2; _e_kbd_int_string_send(ki, str); e_kbd_buf_clear(ki->kbuf); e_kbd_send_keysym_press("space", 0); if (ki->layout.state & (SHIFT | CTRL | ALT)) { ki->layout.state &= (~(SHIFT | CTRL | ALT)); _e_kbd_int_layout_state_update(ki); } _e_kbd_int_matches_update(ki); _e_kbd_int_matchlist_down(ki); } static void _e_kbd_int_matchlist_up(E_Kbd_Int *ki) { const Evas_List *l; Evas_Object *o; Evas_Coord w, h, mw, mh, vw, vh; int sx, sy, sw, sh; if (!e_kbd_buf_string_matches_get(ki->kbuf)) return; if (ki->matchlist.popup) return; ki->matchlist.popup = e_popup_new(ki->win->border->zone, -1, -1, 1, 1); e_popup_layer_set(ki->matchlist.popup, 190); o = _theme_obj_new(ki->matchlist.popup->evas, ki->themedir, "e/modules/kbd/match/default"); ki->matchlist.base_obj = o; o = e_scrollframe_add(ki->matchlist.popup->evas); edje_object_part_swallow(ki->matchlist.base_obj, "e.swallow.content", o); evas_object_show(o); ki->matchlist.scrollframe_obj = o; o = e_ilist_add(ki->matchlist.popup->evas); e_ilist_selector_set(o, 1); e_ilist_freeze(o); ki->matchlist.ilist_obj = o; for (l = e_kbd_buf_string_matches_get(ki->kbuf); l; l = l->next) { const char *str; str = l->data; str = evas_stringshare_add(str); ki->matchlist.matches = evas_list_append(ki->matchlist.matches, str); e_ilist_append(o, NULL, str, 0, _e_kbd_int_cb_matchlist_item_sel, NULL, ki, (char *)str); } e_ilist_thaw(o); e_ilist_min_size_get(o, &mw, &mh); evas_object_resize(o, ki->win->w, mh); e_scrollframe_child_set(ki->matchlist.scrollframe_obj, o); e_scrollframe_child_viewport_size_get(ki->matchlist.scrollframe_obj, &vw, &vh); evas_object_geometry_get(ki->matchlist.scrollframe_obj, NULL, NULL, &w, &h); if (mw > vw) mw = mw + (w - vw); else if (mw < vw) evas_object_resize(o, vw, mh); evas_object_show(o); edje_extern_object_min_size_set(ki->matchlist.scrollframe_obj, mw, mh); edje_object_part_swallow(ki->matchlist.base_obj, "e.swallow.content", ki->matchlist.scrollframe_obj); edje_object_size_min_calc(ki->matchlist.base_obj, &mw, &mh); edje_extern_object_min_size_set(ki->matchlist.scrollframe_obj, 0, 0); edje_object_part_swallow(ki->matchlist.base_obj, "e.swallow.content", ki->matchlist.scrollframe_obj); e_slipshelf_safe_app_region_get(ki->win->border->zone, &sx, &sy, &sw, &sh); mw = ki->win->w; if (mh > (sh - ki->win->h)) mh = sh - ki->win->h; e_popup_move_resize(ki->matchlist.popup, ki->win->x, ki->win->y - mh, mw, mh); evas_object_resize(ki->matchlist.base_obj, ki->matchlist.popup->w, ki->matchlist.popup->h); evas_object_show(ki->matchlist.base_obj); e_popup_edje_bg_object_set(ki->matchlist.popup, ki->matchlist.base_obj); e_popup_show(ki->matchlist.popup); } static void _e_kbd_int_cb_matches(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd_Int *ki; ki = data; if (ki->matchlist.popup) _e_kbd_int_matchlist_down(ki); else _e_kbd_int_matchlist_up(ki); } static void _e_kbd_int_layoutlist_down(E_Kbd_Int *ki) { if (!ki->layoutlist.popup) return; e_object_del(E_OBJECT(ki->layoutlist.popup)); ki->layoutlist.popup = NULL; } static void _e_kbd_int_cb_layoutlist_item_sel(void *data, void *data2) { E_Kbd_Int *ki; E_Kbd_Int_Layout *kil; ki = data; kil = data2; _e_kbd_int_layout_select(ki, kil); _e_kbd_int_layoutlist_down(ki); } static void _e_kbd_int_layoutlist_up(E_Kbd_Int *ki) { Evas_List *l; Evas_Object *o, *o2; Evas_Coord w, h, mw, mh, vw, vh; int sx, sy, sw, sh; if (ki->layoutlist.popup) return; ki->layoutlist.popup = e_popup_new(ki->win->border->zone, -1, -1, 1, 1); e_popup_layer_set(ki->layoutlist.popup, 190); o = _theme_obj_new(ki->layoutlist.popup->evas, ki->themedir, "e/modules/kbd/match/default"); ki->layoutlist.base_obj = o; o = e_scrollframe_add(ki->layoutlist.popup->evas); edje_object_part_swallow(ki->layoutlist.base_obj, "e.swallow.content", o); evas_object_show(o); ki->layoutlist.scrollframe_obj = o; o = e_ilist_add(ki->layoutlist.popup->evas); e_ilist_selector_set(o, 1); e_ilist_freeze(o); ki->layoutlist.ilist_obj = o; for (l = ki->layouts; l; l = l->next) { E_Kbd_Int_Layout *kil; kil = l->data; o2 = e_icon_add(ki->layoutlist.popup->evas); e_icon_fill_inside_set(o2, 1); e_icon_scale_up_set(o2, 0); if (kil->icon) { char *p; p = strrchr(kil->icon, '.'); if (!strcmp(p, ".edj")) e_icon_file_edje_set(o2, kil->icon, "icon"); else e_icon_file_set(o2, kil->icon); } evas_object_show(o2); e_ilist_append(o, o2, kil->name, 0, _e_kbd_int_cb_layoutlist_item_sel, NULL, ki, kil); } e_ilist_thaw(o); e_ilist_min_size_get(o, &mw, &mh); evas_object_resize(o, ki->win->w, mh); e_scrollframe_child_set(ki->layoutlist.scrollframe_obj, o); e_scrollframe_child_viewport_size_get(ki->layoutlist.scrollframe_obj, &vw, &vh); evas_object_geometry_get(ki->layoutlist.scrollframe_obj, NULL, NULL, &w, &h); if (mw > vw) mw = mw + (w - vw); else if (mw < vw) evas_object_resize(o, vw, mh); evas_object_show(o); edje_extern_object_min_size_set(ki->layoutlist.scrollframe_obj, mw, mh); edje_object_part_swallow(ki->layoutlist.base_obj, "e.swallow.content", ki->layoutlist.scrollframe_obj); edje_object_size_min_calc(ki->layoutlist.base_obj, &mw, &mh); edje_extern_object_min_size_set(ki->layoutlist.scrollframe_obj, 0, 0); edje_object_part_swallow(ki->layoutlist.base_obj, "e.swallow.content", ki->layoutlist.scrollframe_obj); e_slipshelf_safe_app_region_get(ki->win->border->zone, &sx, &sy, &sw, &sh); mw = ki->win->w; if (mh > (sh - ki->win->h)) mh = sh - ki->win->h; e_popup_move_resize(ki->layoutlist.popup, ki->win->x, ki->win->y - mh, mw, mh); evas_object_resize(ki->layoutlist.base_obj, ki->layoutlist.popup->w, ki->layoutlist.popup->h); evas_object_show(ki->layoutlist.base_obj); e_popup_edje_bg_object_set(ki->layoutlist.popup, ki->layoutlist.base_obj); e_popup_show(ki->layoutlist.popup); } static void _e_kbd_int_cb_layouts(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd_Int *ki; ki = data; if (ki->layoutlist.popup) _e_kbd_int_layoutlist_down(ki); else _e_kbd_int_layoutlist_up(ki); } EAPI E_Kbd_Int * e_kbd_int_new(const char *themedir, const char *syskbds, const char *sysdicts) { E_Kbd_Int *ki; unsigned int one = 1; Evas_Object *o; Evas_Coord mw, mh; const char *deflay; E_Zone *zone; E_Kbd_Int_Layout *kil; ki = E_NEW(E_Kbd_Int, 1); if (!ki) return NULL; if (themedir) ki->themedir = evas_stringshare_add(themedir); if (syskbds) ki->syskbds = evas_stringshare_add(syskbds); if (sysdicts) ki->sysdicts = evas_stringshare_add(sysdicts); ki->win = e_win_new(e_util_container_number_get(0)); zone = e_util_container_zone_number_get(0, 0); e_win_no_remember_set(ki->win, 1); e_win_resize(ki->win, zone->w, zone->h); e_win_resize_callback_set(ki->win, _e_kbd_int_cb_resize); ki->win->data = ki; e_win_name_class_set(ki->win, "E", "Virtual-Keyboard"); e_win_title_set(ki->win, "Virtual Keyboard"); ki->base_obj = _theme_obj_new(ki->win->evas, ki->themedir, "e/modules/kbd/base/default"); edje_object_signal_callback_add(ki->base_obj, "e,action,do,matches", "", _e_kbd_int_cb_matches, ki); edje_object_signal_callback_add(ki->base_obj, "e,action,do,layouts", "", _e_kbd_int_cb_layouts, ki); o = e_layout_add(ki->win->evas); edje_object_part_swallow(ki->base_obj, "e.swallow.content", o); evas_object_show(o); ki->layout_obj = o; o = e_icon_add(ki->win->evas); evas_object_pass_events_set(o, 1); e_icon_fill_inside_set(o, 1); e_icon_scale_up_set(o, 0); edje_object_part_swallow(ki->base_obj, "e.swallow.layout", o); evas_object_show(o); ki->icon_obj = o; o = e_box_add(ki->win->evas); e_box_orientation_set(o, 1); e_box_homogenous_set(o, 1); edje_object_part_swallow(ki->base_obj, "e.swallow.label", o); evas_object_show(o); ki->box_obj = o; ki->kbuf = e_kbd_buf_new(ki->sysdicts); _e_kbd_int_layouts_list_update(ki); kil = _e_kbd_int_layouts_list_default_get(ki); if ((!kil) && (ki->layouts)) kil = ki->layouts->data; if (kil) _e_kbd_int_layout_select(ki, kil); edje_object_size_min_calc(ki->base_obj, &mw, &mh); if (mw < 48) mw = 48; if (mh < 48) mh = 48; evas_object_move(ki->base_obj, 0, 0); evas_object_resize(ki->base_obj, mw, mh); evas_object_show(ki->base_obj); e_win_size_min_set(ki->win, mw, mh); e_win_resize(ki->win, mw, mh); ecore_x_e_virtual_keyboard_set(ki->win->evas_win, 1); ki->client_message_handler = ecore_event_handler_add (ECORE_X_EVENT_CLIENT_MESSAGE, _e_kbd_int_cb_client_message, ki); e_win_show(ki->win); return ki; } EAPI void e_kbd_int_free(E_Kbd_Int *ki) { e_object_del(E_OBJECT(ki->win)); if (ki->themedir) evas_stringshare_del(ki->themedir); if (ki->syskbds) evas_stringshare_del(ki->syskbds); if (ki->sysdicts) evas_stringshare_del(ki->sysdicts); _e_kbd_int_layouts_free(ki); _e_kbd_int_matches_free(ki); _e_kbd_int_layout_free(ki); ecore_event_handler_del(ki->client_message_handler); if (ki->down.hold_timer) ecore_timer_del(ki->down.hold_timer); _e_kbd_int_layoutlist_down(ki); _e_kbd_int_matchlist_down(ki); _e_kbd_int_zoomkey_down(ki); e_kbd_buf_free(ki->kbuf); free(ki); } static Evas_Object * _theme_obj_new(Evas *e, const char *custom_dir, const char *group) { Evas_Object *o; o = edje_object_add(e); if (!e_theme_edje_object_set(o, "base/theme/modules/illume", group)) { if (custom_dir) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/illume.edj", custom_dir); if (edje_object_file_set(o, buf, group)) { printf("OK FALLBACK %s\n", buf); } } } return o; } #if 0 #include #include #include #include #include /* FIXME: this keyboard does a LOT. it has a lot of features but was * written in a very short timeframe. it can do with LOTS of love and cleaning * up so it is better structured */ /* this code sucks. it EATS through memory. 25mb or so... * the tree is very inefficient memory-wise. this works, but needs to be * cleaned up. it is NOT acceptable */ /* note: probably need to write a file that can be mmap()ed directly as * the dictionary tree */ enum { NORMAL, SHIFT, CAPSLOCK }; typedef struct _Kbd Kbd; typedef struct _Key Key; typedef struct _State State; typedef struct _Seq Seq; typedef struct _Letter Letter; typedef struct _Match Match; typedef struct _Match_Point Match_Point; struct _Kbd { int w, h; int fuzz; Evas_List *keys; int capslock_mode; int shift_mode; int alt_mode; int ctrl_mode; Key *pressed; }; struct _Key { int x, y, w, h; Evas_List *states; int pressed; int selected; Evas_Object *obj; int is_shift; int is_ctrl; int is_alt; int is_capslock; }; struct _State { int state; const char *label; // FIXME: possible icon Evas_List *sequence; }; struct _Seq { const char *keysym; int up; }; struct _Letter { Evas_Coord x, y; const char *letter, *rawletter; Evas_List *matches; unsigned char shift : 1; unsigned char capslock : 1; }; struct _Match { int distance; int len; Match_Point *points; }; struct _Match_Point { const char *letter; int distance; unsigned char shift : 1; unsigned char capslock : 1; }; /* internal calls */ static Kbd *_e_kbd_config_parse(E_Kbd *kbd, const char *config); static Evas_Object *_theme_obj_new(Evas *e, const char *custom_dir, const char *group); /* state */ static Evas_List *kbds = NULL; static Ecore_Event_Handler *client_message_handler = NULL; static Ecore_X_Atom atom_mb_im_invoker_command = 0; static Ecore_X_Atom atom_mtp_im_invoker_command = 0; static int _e_kbd_cb_delayed_hide(void *data) { E_Kbd *kbd; kbd = data; e_kbd_hide(kbd); kbd->delay_hide= NULL; return 0; } static int _e_kbd_cb_client_message(void *data, int type, void *event) { Ecore_X_Event_Client_Message *ev; ev = event; if (ev->win == ecore_x_window_root_first_get()) { if ((ev->message_type == atom_mb_im_invoker_command) || (ev->message_type == atom_mtp_im_invoker_command)) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if (ev->data.l[0] == 1) { if (!kbd->out) e_kbd_show(kbd); } else if (ev->data.l[0] == 2) { if (kbd->out) { if (kbd->delay_hide) ecore_timer_del(kbd->delay_hide); kbd->delay_hide = ecore_timer_add(0.2, _e_kbd_cb_delayed_hide, kbd); } } else if (ev->data.l[0] == 3) { if (kbd->out) { if (kbd->delay_hide) ecore_timer_del(kbd->delay_hide); kbd->delay_hide = ecore_timer_add(0.2, _e_kbd_cb_delayed_hide, kbd); } else e_kbd_show(kbd); } } } } return 1; } /* called from the module core */ EAPI int e_kbd_init(void) { /* steal matchbox stuff... :) */ /* ATOM: _MB_IM_INVOKER_COMMAND * format: 32 * win: root * data.l[0] = ... * 0 == none * 1 == show * 2 == hide * 3 == toggle * 4 == operation * * ATOM: _MTP_IM_INVOKER_COMMAND * format: 32 * win: root * data.l[0] = ... * 0 == none * 1 == show * 2 == hide * 3 == toggle */ atom_mb_im_invoker_command = ecore_x_atom_get("_MB_IM_INVOKER_COMMAND"); atom_mtp_im_invoker_command = ecore_x_atom_get("_MTP_IM_INVOKER_COMMAND"); client_message_handler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, _e_kbd_cb_client_message, NULL); return 1; } EAPI int e_kbd_shutdown(void) { if (client_message_handler) ecore_event_handler_del(client_message_handler); client_message_handler = NULL; return 1; } /////////////////////////////////////////////////////////////////////////////// /* internal calls */ E_Kbd *_e_kbd_new(E_Zone *zone, const char *themedir); static void _e_kbd_free(E_Kbd *ess); static int _e_kbd_cb_animate(void *data); static void _e_kbd_slide(E_Kbd *ess, int out, double len); static int _e_kbd_cb_zone_move_resize(void *data, int type, void *event); static void _e_kbd_mode_eval(E_Kbd *kbd) { Kbd *k; Evas_List *l, *l2; int state; if (!kbd->kbd) return; k = kbd->kbd; state = NORMAL; if (k->capslock_mode) state = CAPSLOCK; if (k->shift_mode) state = SHIFT; for (l = k->keys; l; l = l->next) { Key *ky; const char *label; int found; ky = l->data; label = ""; found = 0; for (l2 = ky->states; l2; l2 = l2->next) { State *st; st = l2->data; if (st->state == state) { label = st->label; found = 1; break; } } if (!found) { for (l2 = ky->states; l2; l2 = l2->next) { State *st; st = l2->data; if (st->state == NORMAL) { label = st->label; break; } } } edje_object_part_text_set(ky->obj, "e.text.label", label); } } static Key * _e_kbd_at_get(E_Kbd *kbd, Evas_Coord x, Evas_Coord y) { Kbd *k; Evas_List *l, *l2; Evas_Coord w, h; k = kbd->kbd; for (l = k->keys; l; l = l->next) { Key *ky; ky = l->data; if ((x >= ky->x) && (y >= ky->y) && (x < (ky->x + ky->w)) && (y < (ky->y + ky->h))) return ky; } return NULL; } static void _e_kbd_unset_all_mode(E_Kbd *kbd) { Kbd *k; Evas_List *l, *l2; k = kbd->kbd; for (l = k->keys; l; l = l->next) { Key *ky; ky = l->data; if (ky->is_shift) { if (ky->selected) { ky->selected = 0; // ecore_x_test_fake_key_up("Shift_L"); edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); } } else if (ky->is_ctrl) { if (ky->selected) { ky->selected = 0; ecore_x_test_fake_key_up("Control_L"); edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); } } else if (ky->is_alt) { if (ky->selected) { ky->selected = 0; ecore_x_test_fake_key_up("Alt_L"); edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); } } } return; } static int _e_kbd_is_word_letter(E_Kbd *kbd, const char *letter) { /* FIXME: these SHOULD come from config of kbd */ const char *validletters[] = { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a", "s", "d", "f", "g", "h", "j", "k", "l", "z", "x", "c", "v", "b", "n", "m", "minus", NULL }; int i; for (i = 0; validletters[i]; i++) { if (!strcasecmp(letter, validletters[i])) return 1; } return 0; } static const char * _e_kbd_key_string_get(E_Kbd *kbd, Key *ky) { Evas_List *l, *l2; for (l = ky->states; l; l = l->next) { State *st; st = l->data; for (l2 = st->sequence; l2; l2 = l2->next) { Seq *sq; sq = l2->data; if (!sq->up) { if (_e_kbd_is_word_letter(kbd, sq->keysym)) return sq->keysym; } } } return NULL; } static int _e_kbd_cb_match_sort(void *data1, void *data2) { Match_Point *mp1, *mp2; mp1 = data1; mp2 = data2; return mp1->distance - mp2->distance; } /*****************************/ /* magic dictionary matching */ /*****************************/ typedef struct _Dict_Node Dict_Node; /* FIXME: can't do multiple languages */ struct _Dict_Node { char ch; Dict_Node *subs[27]; const char *str; /* ptr mmaped file */ int str_len; int freq; /* frequency use. < 0 == usr word, > 0 == system word */ }; /* system dictionary */ static int dict_words_fd = -1; static int dict_words_size = 0; static const char *dict_words = NULL; /* user dictionary - words added not in system */ static int user_dict_words_fd = -1; static int user_dict_words_size = 0; static const char *user_dict_words = NULL; static int user_dict_changed = 0; static Dict_Node *dict = NULL; static const char *dict_exists = ""; static Evas_List *new_words = NULL; static int _e_kbd_dict_char_simplify(char *word) { int i; i = tolower(word[0]); if ((i < 'a') || (i > 'z')) i = 26; else i = i - 'a'; return i; } static void _e_kbd_dict_word_add_internal(Dict_Node *n, const char *word, const char *str, int str_len, int freq, int usr) { Dict_Node *n2; int i; i = _e_kbd_dict_char_simplify(word); n2 = n->subs[i]; if (!n2) { n2 = calloc(1, sizeof(Dict_Node)); n2->ch = word[0]; n->subs[i] = n2; } if (!word[1]) { /* final node */ n2->str = str; n2->str_len = str_len; if (usr) n2->freq = -freq; else n2->freq = freq; } else _e_kbd_dict_word_add_internal(n2, word + 1, str, str_len, freq, usr); } static void _e_kbd_dict_word_add(const char *word, const char *str, int str_len, int freq, int usr) { const char *p; if (!dict) dict = calloc(1, sizeof(Dict_Node)); _e_kbd_dict_word_add_internal(dict, word, str, str_len, freq, usr); } static void _e_kbd_dict_wordtree_build(const char *dict_ptr, int dict_size, int usr) { int freq; char *p, *end; char *ws = NULL, *we = NULL; end = dict_ptr + dict_size; for (p = dict_ptr; p < end; p++) { if (!ws) ws = p; if ((*p == ' ') || (*p == '\n')) { we = p; if ((we - ws) > 0) { char *wd, *wp; wd = alloca(we - ws); strncpy(wd, ws, we - ws); wd[we - ws] = 0; for (wp = wd; *wp; wp++) *wp = tolower(*wp); freq = 1; if ((*p == ' ') && (p < (end - 1))) { freq = atoi(p + 1); while ((p < end) && (*p != '\n')) p++; } // printf("WD: %s | %i\n", wd, freq); _e_kbd_dict_word_add(wd, ws, we - ws, freq, usr); } we = NULL; ws = NULL; } } } static void _e_kbd_dict_wordtree_load(E_Kbd *kbd) { char buf[4096]; const char *homedir; struct stat st; if (dict_words_fd >= 0) return; /* open words */ if (kbd->sysdicts) { snprintf(buf, sizeof(buf), "%s/dicts/Default.dic", kbd->sysdicts); printf("LOAd %s\n", buf); dict_words_fd = open(buf, O_RDONLY); if (dict_words_fd >= 0) { fstat(dict_words_fd, &st); dict_words_size = st.st_size; dict_words = mmap(NULL, dict_words_size, PROT_READ, MAP_SHARED, dict_words_fd, 0); if ((dict_words == MAP_FAILED) || (dict_words == NULL)) { close(dict_words_fd); dict_words_fd = -1; dict_words = NULL; dict_words_size = 0; } else { printf("OK\n"); _e_kbd_dict_wordtree_build(dict_words, dict_words_size, 0); } } } /* get user dict */ homedir = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/user-words", homedir); user_dict_words_fd = open(buf, O_RDONLY); if (user_dict_words_fd < 0) return; fstat(user_dict_words_fd, &st); user_dict_words_size = st.st_size; user_dict_words = mmap(NULL, user_dict_words_size, PROT_READ, MAP_SHARED, user_dict_words_fd, 0); if ((user_dict_words == MAP_FAILED) || (user_dict_words == NULL)) { close(user_dict_words_fd); user_dict_words_fd = -1; user_dict_words = NULL; user_dict_words_size = 0; return; } _e_kbd_dict_wordtree_build(user_dict_words, user_dict_words_size, 1); } static char * _e_kbd_dict_word_get(const char *str) { char *p, *s; if (!str) return NULL; p = strchr(str, '\n'); if (!p) return strdup(str); s = malloc(p - str + 1); strncpy(s, str, p - str); s[p - str] = 0 ; p = strchr(s, ' '); if (p) *p = 0; return s; } static void _e_kbd_dict_user_write_internal(Dict_Node *n, FILE *f) { int i; if (!n) return; if ((n->str) && (n->freq < 0)) { char *s; s = _e_kbd_dict_word_get(n->str); if (s) { fprintf(f, "%s %i\n", s, -n->freq); free(s); } } for (i = 0; i < 27; i++) _e_kbd_dict_user_write_internal(n->subs[i], f); } static void _e_kbd_dict_user_write(void) { char buf[4096]; const char *homedir; FILE *f; homedir = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/user-words", homedir); ecore_file_unlink(buf); f = fopen(buf, "w"); if (!f) return; printf("WRITE.... %s\n", buf); _e_kbd_dict_user_write_internal(dict, f); fclose(f); } static void _e_kbd_dict_free_internal(Dict_Node *n) { int i; if (!n) return; for (i = 0; i < 27; i++) _e_kbd_dict_free_internal(n->subs[i]); free(n); } static void _e_kbd_dict_free(void) { if (dict_words_fd >= 0) { munmap(dict_words, dict_words_size); close(dict_words_fd); dict_words_fd = -1; dict_words = NULL; dict_words_size = 0; } if (user_dict_words_fd >= 0) { munmap(user_dict_words, user_dict_words_size); close(user_dict_words_fd); user_dict_words_fd = -1; user_dict_words = NULL; user_dict_words_size = 0; } while (new_words) { evas_stringshare_del(new_words->data); new_words = evas_list_remove_list(new_words, new_words); } _e_kbd_dict_free_internal(dict); dict = NULL; } static const char * _e_kbd_dict_match_firt_n_internal(Dict_Node *n, const char *start, int len, int *freq) { int i; if (!start[0]) { *freq = n->freq; if (!n->str) return dict_exists; return n->str; } i = _e_kbd_dict_char_simplify(start); // printf("chk: %c\n", 'a' + i); if (!n->subs[i]) return NULL; n = n->subs[i]; return _e_kbd_dict_match_firt_n_internal(n, start + 1, len, freq); } static const char * _e_kbd_dict_match_first_n(E_Kbd *kbd, const char *start, int len, int *freq) { Dict_Node *n; _e_kbd_dict_wordtree_load(kbd); if (!dict) return NULL; n = dict; return _e_kbd_dict_match_firt_n_internal(n, start, len, freq); } static void _e_kbd_dict_match_freq_adjust_internal(Dict_Node *n, const char *word, const char *fullword, int adj) { int i; if (!word[0]) { char *s; if (n->str) { s = _e_kbd_dict_word_get(n->str); if (s) { if (n->freq >= 0) n->freq = -n->freq; n->freq -= adj; printf("word %s [%s] [%p] -> %i\n", fullword, s, n->str, n->freq); free(s); user_dict_changed++; return; } } } i = _e_kbd_dict_char_simplify(word); if (!n->subs[i]) { char *s; s = evas_stringshare_add(fullword); new_words = evas_list_append(new_words, s); printf("new word %s %s\n", fullword, s); _e_kbd_dict_word_add(s, s, strlen(s), 2, 1); user_dict_changed++; return; } n = n->subs[i]; _e_kbd_dict_match_freq_adjust_internal(n, word + 1, fullword, adj); } static void _e_kbd_dict_freq_adjust(E_Kbd *kbd, const char *word, int adj) { if (!dict) return; printf("... FIND %s adj by %i\n", word, adj); _e_kbd_dict_match_freq_adjust_internal(dict, word, word, adj); } static void _e_kbd_dict_flush(void) { if (user_dict_changed > 0) { char buf[4096]; const char *homedir; user_dict_changed = 0; homedir = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/user-words", homedir); _e_kbd_dict_user_write(); } _e_kbd_dict_free(); } /*****************************/ /*****************************/ /*****************************/ static void _e_kbd_letter_commit(E_Kbd *kbd); static void _e_kbd_layout_next(E_Kbd *kbd); static void _e_kbd_layout_prev(E_Kbd *kbd); typedef struct _Result Result; struct _Result { Match_Point *mp; Evas_List *results; }; typedef struct _Matched Matched; struct _Matched { int distance; int freq; char *match; }; static int _e_kbd_cb_matched_sort(void *data1, void *data2) { Matched *mt1, *mt2; int d1, d2; mt1 = data1; mt2 = data2; // Frequency adust based on freq /* FIXME: 50 is a magic multiplier */ d1 = (50 * mt1->distance) / (50 + mt1->freq); d2 = (50 * mt2->distance) / (50 + mt2->freq); return d1 - d2; } static void _e_kbd_match_tree_build_internal(E_Kbd *kbd, Result *res, char *wbuf, char *wbufend, Evas_List *lnode) { Result *r; Evas_List *l2; Letter *let; if (!lnode) return; let = lnode->data; for (l2 = let->matches; l2; l2 = l2->next) { Match_Point *mp; const char *match; int freq; mp = l2->data; wbufend[0] = mp->letter[0]; wbufend[1] = 0; match = _e_kbd_dict_match_first_n(kbd, wbuf, strlen(wbuf), &freq); if (match) { // printf("-- OK %s @ %i\n", wbuf, freq); r = calloc(1, sizeof(Result)); r->mp = mp; res->results = evas_list_append(res->results, r); _e_kbd_match_tree_build_internal(kbd, r, wbuf, wbufend + 1, lnode->next); } // else // printf(".. NO %s\n", wbuf); } } static void _e_kbd_results_free(Result *res) { if (!res) return; while (res->results) { _e_kbd_results_free(res->results->data); res->results = evas_list_remove_list(res->results, res->results); } free(res); } static Result * _e_kbd_match_tree_build(E_Kbd *kbd) { Result *res = NULL; char *wbuf; wbuf = alloca(evas_list_count(kbd->letters) + 2); wbuf[0] = 0; res = calloc(1, sizeof(Result)); _e_kbd_match_tree_build_internal(kbd, res, wbuf, wbuf, kbd->letters); if (!res->results) { _e_kbd_results_free(res); return NULL; } return res; } static void _e_kbd_list_results(E_Kbd *kbd, Result *res, Evas_List **matched, int dist, int len, int needlen, char *tbuf, char *tbufend) { Evas_List *l; dist += res->mp->distance; tbufend[0] = res->mp->letter[0]; tbufend[1] = 0; // printf("%s: %i / %i\n", tbuf, len, needlen); if (!res->results) { if (len == needlen - 1) { const char *match; int freq; match = _e_kbd_dict_match_first_n(kbd, tbuf, needlen, &freq); if ((match) && (match != dict_exists)) { Matched *mt; char *wmatch; wmatch = _e_kbd_dict_word_get(match); mt = calloc(1, sizeof(Matched)); mt->distance = dist; mt->match = wmatch; if (freq < 0) mt->freq = -freq; else mt->freq = freq; *matched = evas_list_append(*matched, mt); // printf("have @ %i '%s' match: '%s'\n", dist, tbuf, wmatch); } } } else { for (l = res->results; l; l = l->next) _e_kbd_list_results(kbd, l->data, matched, dist, len + 1, needlen, tbuf, tbufend + 1); } } static void _e_kbd_match_clear(E_Kbd *kbd) { while (kbd->match.matches) { evas_stringshare_del(kbd->match.matches->data); kbd->match.matches = evas_list_remove_list(kbd->match.matches, kbd->match.matches); } } static void _e_kbd_match_down(E_Kbd *kbd) { if (!kbd->match.popup) return; e_object_del(E_OBJECT(kbd->match.popup)); kbd->match.popup = NULL; } static void _e_kbd_letter_from_buf(E_Kbd *kbd, char *buf) { Evas_List *l2; int i; for (i = 0, l2 = kbd->letters; l2; l2 = l2->next, i++) { Letter *let; char lbuf[4096]; let = l2->data; evas_stringshare_del(let->letter); lbuf[0] = buf[i]; lbuf[1] = 0; if (buf[i] == '-') let->letter = evas_stringshare_add("minus"); else let->letter = evas_stringshare_add(lbuf); } } static void _e_kbd_cb_item_sel(void *data, void *data2) { E_Kbd *kbd; kbd = data; printf("adjust... %s\n", data2); _e_kbd_dict_freq_adjust(kbd, data2, 1); _e_kbd_letter_from_buf(kbd, data2); _e_kbd_letter_commit(kbd); ecore_x_test_fake_key_down("space"); ecore_x_test_fake_key_up("space"); } static void _e_kbd_match_list_show(E_Kbd *kbd) { Evas_List *l; Evas_Object *o; Evas_Coord w, h, mw, mh, vw, vh; int sx, sy, sw, sh; kbd->match.popup = e_popup_new(kbd->zone, -1, -1, 1, 1); e_popup_layer_set(kbd->match.popup, 190); kbd->match.base_obj = _theme_obj_new(kbd->match.popup->evas, kbd->themedir, "e/modules/kbd/match/default"); evas_object_show(kbd->match.base_obj); o = e_scrollframe_add(kbd->match.popup->evas); edje_object_part_swallow(kbd->match.base_obj, "e.swallow.content", o); evas_object_show(o); kbd->match.scrollframe_obj = o; o = e_ilist_add(kbd->match.popup->evas); e_ilist_selector_set(o, 1); e_ilist_freeze(o); for (l = kbd->match.matches; l; l = l->next) e_ilist_append(o, NULL/*icon*/, l->data, 0, _e_kbd_cb_item_sel, NULL, kbd, l->data); e_ilist_thaw(o); e_ilist_min_size_get(o, &mw, &mh); evas_object_resize(o, kbd->zone->w, mh); e_scrollframe_child_set(kbd->match.scrollframe_obj, o); e_scrollframe_child_viewport_size_get(kbd->match.scrollframe_obj, &vw, &vh); evas_object_geometry_get(kbd->match.scrollframe_obj, NULL, NULL, &w, &h); if (mw > vw) mw = mw + (w - vw); else if (mw < vw) evas_object_resize(o, vw, mh); evas_object_show(o); edje_extern_object_min_size_set(kbd->match.scrollframe_obj, mw, mh); edje_object_part_swallow(kbd->match.base_obj, "e.swallow.content", kbd->match.scrollframe_obj); edje_object_size_min_calc(kbd->match.base_obj, &mw, &mh); edje_extern_object_min_size_set(kbd->match.scrollframe_obj, 0, 0); edje_object_part_swallow(kbd->match.base_obj, "e.swallow.content", kbd->match.scrollframe_obj); e_slipshelf_safe_app_region_get( kbd->zone, &sx, &sy, &sw, &sh); mw = kbd->zone->w; if (mh > (sh - kbd->popup->h)) mh = sh - kbd->popup->h; e_popup_move_resize(kbd->match.popup, kbd->popup->x, kbd->popup->y - mh, mw, mh); evas_object_resize(kbd->match.base_obj, kbd->match.popup->w, kbd->match.popup->h); evas_object_show(kbd->match.base_obj); e_popup_show(kbd->match.popup); } typedef struct _Guess_Item Guess_Item; struct _Guess_Item { E_Kbd *kbd; Evas_Object *obj; const char *string; }; static void _e_kbd_cb_guess_select(void *data, Evas_Object *obj, const char *emission, const char *source) { Guess_Item *gi; E_Kbd *kbd; gi = data; kbd = gi->kbd; _e_kbd_dict_freq_adjust(kbd, gi->string, 1); _e_kbd_letter_from_buf(kbd, gi->string); _e_kbd_letter_commit(kbd); // e_kbd_send_keysym_press("space", 0); e_kbd_send_string_press(" ", 0); // e_kbd_send_string_press("รค", 0); // e_kbd_send_string_press(" ", 0); // ecore_x_test_fake_key_down("space"); // ecore_x_test_fake_key_up("space"); } static void _e_kbd_letter_buf_guesses_clear(E_Kbd *kbd) { evas_event_freeze(kbd->popup->evas); while (kbd->hbox_items) { Guess_Item *gi; gi = kbd->hbox_items->data; evas_object_del(gi->obj); evas_stringshare_del(gi->string); free(gi); kbd->hbox_items = evas_list_remove_list(kbd->hbox_items, kbd->hbox_items); } if (kbd->hbox_obj) { evas_object_del(kbd->hbox_obj); kbd->hbox_obj = NULL; } evas_event_thaw(kbd->popup->evas); } static void _e_kbd_letter_buf_guesses_update_box_item_add(E_Kbd *kbd, const char *str, int num) { Evas_Object *o; Guess_Item *gi; Evas_Coord mw, mh; o = _theme_obj_new(kbd->popup->evas, kbd->themedir, "e/modules/kbd/match/word"); gi = calloc(1, sizeof(Guess_Item)); gi->kbd = kbd; gi->obj = o; gi->string = evas_stringshare_add(str); kbd->hbox_items = evas_list_append(kbd->hbox_items, gi); edje_object_part_text_set(o, "e.text.label", gi->string); edje_object_size_min_calc(o, &mw, &mh); /* start/end adds alternately */ if (num & 0x1) e_box_pack_start(kbd->hbox_obj, o); else e_box_pack_end(kbd->hbox_obj, o); if (mw < 32) mw = 32; e_box_pack_options_set(o, 1, 1, 1, 1, 0.5, 0.5, mw, mh, mw, mh); edje_object_signal_callback_add(o, "e,action,do,select", "", _e_kbd_cb_guess_select, gi); evas_object_show(o); } static void _e_kbd_letter_buf_guesses_update(E_Kbd *kbd) { Evas_Object *o; Evas_List *l; int num; Evas_Coord mw, mh; _e_kbd_letter_buf_guesses_clear(kbd); evas_event_freeze(kbd->popup->evas); o = e_box_add(kbd->popup->evas); e_box_orientation_set(o, 1); e_box_homogenous_set(o, 1); kbd->hbox_obj = o; if (kbd->match.matches) { for (num = 0, l = kbd->match.matches->next; l; l = l->next, num++) _e_kbd_letter_buf_guesses_update_box_item_add(kbd, l->data, num); _e_kbd_letter_buf_guesses_update_box_item_add(kbd, kbd->match.matches->data, num); } e_box_min_size_get(kbd->hbox_obj, &mw, &mh); edje_extern_object_min_size_set(kbd->hbox_obj, 0, mh); edje_object_part_swallow(kbd->base_obj, "e.swallow.label", kbd->hbox_obj); evas_object_show(kbd->hbox_obj); evas_event_thaw(kbd->popup->evas); } static void _e_kbd_letter_buf_update(E_Kbd *kbd) { Evas_List *l, *l2, *ml, *combos = NULL; char buf[4096], *buf2, *p; int i, len; Evas_List *matches = NULL; Match *m; Match_Point *mp; Kbd *k; k = kbd->kbd; if (!kbd->letters) { printf("BUF: [EMPTY]\n"); edje_object_part_text_set(kbd->base_obj, "e.text.label", ""); _e_kbd_letter_buf_guesses_clear(kbd); return; } for (i = 0, l = kbd->letters; l && (i < (sizeof(buf) - 1)); l = l->next, i++) { Letter *let; let = l->data; if ((let->capslock) || (let->shift)) { if (!strcmp(let->letter, "minus")) buf[i] = '_'; else { buf[i] = toupper(let->letter[0]); } } else { if (!strcmp(let->letter, "minus")) buf[i] = '-'; else { buf[i] = let->letter[0]; } } buf[i + 1] = 0; if (!let->matches) { ml = NULL; for (l2 = k->keys; l2; l2 = l2->next) { Key *ky; int x, y, dx, dy; int dist; const char *letter; ky = l2->data; letter = _e_kbd_key_string_get(kbd, ky); if (letter) { x = ky->x + (ky->w / 2); y = ky->y + (ky->h / 2); dx = x - let->x; dy = y - let->y; dist = sqrt((dx * dx) + (dy * dy)); if (dist < k->fuzz) { mp = calloc(1, sizeof(Match_Point)); mp->shift = let->shift; mp->capslock = let->capslock; mp->letter = evas_stringshare_add(letter); mp->distance = dist; let->matches = evas_list_append(let->matches, mp); } } } let->matches = evas_list_sort(let->matches, evas_list_count(let->matches), _e_kbd_cb_match_sort); } } buf[i] = 0; { Result *res = NULL, *r; int len; char tbuf[4096], *tbufend; Evas_List *matched = NULL; len = evas_list_count(kbd->letters); res = _e_kbd_match_tree_build(kbd); printf("RES::::::\n"); if (res) { tbufend = tbuf; tbuf[0] = 0; for (l = res->results; l; l = l->next) { r = l->data; _e_kbd_list_results(kbd, r, &matched, 0, 0, len, tbuf, tbufend); } _e_kbd_results_free(res); } tbuf[0] = 0; for (i = 0, l = kbd->letters; l; l = l->next, i++) { Letter *let; char buf2[4096]; let = l->data; if (buf[i] == '-') { strcpy(buf2, "minus"); } else { buf2[0] = buf[i]; buf2[1] = 0; } evas_stringshare_del(let->letter); let->letter = evas_stringshare_add(buf2); if (!strcmp(let->rawletter, "minus")) tbuf[i] = '-'; else { if ((let->shift) || (let->capslock)) tbuf[i] = toupper(let->rawletter[0]); else tbuf[i] = let->rawletter[0]; } tbuf[i + 1] = 0; } for (l = matched; l; l = l->next) { Matched *mt; mt = l->data; if (!strcasecmp(mt->match, tbuf)) mt->distance = 0; } printf("R: '%s'\n", tbuf); matched = evas_list_sort(matched, evas_list_count(matched), _e_kbd_cb_matched_sort); for (l = matched; l;) { Evas_List *tl; if (l->next) { if (!strcmp(l->next->data, l->data)) { Matched *mt; tl = l->next; mt = l->data; if (mt->match) free(mt->match); free(mt); matched = evas_list_remove_list(matched, l); l = tl; } else l = l->next; } else break; } _e_kbd_match_clear(kbd); _e_kbd_match_down(kbd); kbd->match.matches = evas_list_append(kbd->match.matches, evas_stringshare_add(tbuf)); for (l = matched; l; l = l->next) { Matched *mt; mt = l->data; if (l == matched) strcpy(buf, mt->match); kbd->match.matches = evas_list_append(kbd->match.matches, evas_stringshare_add(mt->match)); printf("M: '%s' @ %i\n", mt->match, mt->distance); } while (matched) { Matched *mt; mt = matched->data; if (mt->match) free(mt->match); free(mt); matched = evas_list_remove_list(matched, matched); } } printf("buf-> %s\n", buf); edje_object_part_text_set(kbd->base_obj, "e.text.label", buf); _e_kbd_letter_buf_guesses_update(kbd); } static void _e_kbd_letter_commit(E_Kbd *kbd) { printf("---- COMMIT\n"); if (!kbd->letters) return; while (kbd->letters) { Letter *let; let = kbd->letters->data; kbd->letters = evas_list_remove_list(kbd->letters, kbd->letters); if ((let->shift) || (let->capslock)) ecore_x_test_fake_key_down("Shift_L"); printf("COMMIT %s [sh:%i cp:%i]\n", let->letter, let->shift, let->capslock); ecore_x_test_fake_key_down(let->letter); ecore_x_test_fake_key_up(let->letter); if (let->capslock) { ecore_x_test_fake_key_down("Caps_Lock"); ecore_x_test_fake_key_up("Caps_Lock"); } if ((let->shift) || (let->capslock)) ecore_x_test_fake_key_up("Shift_L"); while (let->matches) { Match_Point *mp; mp = let->matches->data; evas_stringshare_del(mp->letter); free(mp); let->matches = evas_list_remove_list(let->matches, let->matches); } evas_stringshare_del(let->rawletter); evas_stringshare_del(let->letter); free(let); } _e_kbd_letter_buf_update(kbd); _e_kbd_match_clear(kbd); _e_kbd_match_down(kbd); } static void _e_kbd_letter_del(E_Kbd *kbd) { Evas_List *l; Letter *let; if (!kbd->letters) return; l = evas_list_last(kbd->letters); if (!l) return; let = l->data; kbd->letters = evas_list_remove_list(kbd->letters, l); while (let->matches) { Match_Point *mp; mp = let->matches->data; evas_stringshare_del(mp->letter); free(mp); let->matches = evas_list_remove_list(let->matches, let->matches); } if (let->rawletter) evas_stringshare_del(let->rawletter); if (let->letter) evas_stringshare_del(let->letter); free(let); _e_kbd_letter_buf_update(kbd); } static int _e_kbd_letter_add(E_Kbd *kbd, const char *letter, Evas_Coord x, Evas_Coord y) { Kbd *k; Letter *let; if (!_e_kbd_is_word_letter(kbd, letter)) return 0; let = calloc(1, sizeof(Letter)); k = kbd->kbd; let->shift = k->shift_mode; let->capslock = k->capslock_mode; let->rawletter = evas_stringshare_add(letter); let->letter = evas_stringshare_add(letter); let->x = x; let->y = y; kbd->letters = evas_list_append(kbd->letters, let); _e_kbd_letter_buf_update(kbd); return 1; } static void _e_kbd_handle_stroke(E_Kbd *kbd, int dir) { Kbd *k; k = kbd->kbd; if (dir == 1) // right { if (!_e_kbd_letter_add(kbd, "space", 0, 0)) { _e_kbd_letter_commit(kbd); ecore_x_test_fake_key_down("space"); ecore_x_test_fake_key_up("space"); } if ((k->shift_mode) || (k->ctrl_mode) || (k->alt_mode)) { printf("reset shift mode to 0\n"); k->shift_mode = 0; k->ctrl_mode = 0; k->alt_mode = 0; _e_kbd_unset_all_mode(kbd); _e_kbd_mode_eval(kbd); } } else if (dir == 3) // left { if (kbd->letters) _e_kbd_letter_del(kbd); else { ecore_x_test_fake_key_down("BackSpace"); ecore_x_test_fake_key_up("BackSpace"); } if ((k->shift_mode) || (k->ctrl_mode) || (k->alt_mode)) { printf("reset shift mode to 0\n"); k->shift_mode = 0; k->ctrl_mode = 0; k->alt_mode = 0; _e_kbd_unset_all_mode(kbd); _e_kbd_mode_eval(kbd); } } else if (dir == 2) // down { if (!_e_kbd_letter_add(kbd, "Return", 0, 0)) { _e_kbd_letter_commit(kbd); ecore_x_test_fake_key_down("Return"); ecore_x_test_fake_key_up("Return"); } if ((k->shift_mode) || (k->ctrl_mode) || (k->alt_mode)) { printf("reset shift mode to 0\n"); k->shift_mode = 0; k->ctrl_mode = 0; k->alt_mode = 0; _e_kbd_unset_all_mode(kbd); _e_kbd_mode_eval(kbd); } } else if (dir == 4) // up { _e_kbd_layout_next(kbd); // k->shift_mode = !k->shift_mode; // _e_kbd_mode_eval(kbd); } } static void _e_kbd_handle_press(E_Kbd *kbd, Evas_Coord dx, Evas_Coord dy) { Kbd *k; Evas_Coord x, y, w, h; Key *ky; k = kbd->kbd; evas_object_geometry_get(kbd->event_obj, &x, &y, &w, &h); x = dx - x; y = dy - y; x = (x * k->w) / w; y = (y * k->h) / h; ky = _e_kbd_at_get(kbd, x, y); if (ky) { if (ky->is_capslock) { k->capslock_mode = !k->capslock_mode; ky->selected = k->capslock_mode; if (ky->selected) { edje_object_signal_emit(ky->obj, "e,state,selected", "e"); // ecore_x_test_fake_key_down("Caps_Lock"); // ecore_x_test_fake_key_up("Caps_Lock"); } else { edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); // ecore_x_test_fake_key_down("Caps_Lock"); // ecore_x_test_fake_key_up("Caps_Lock"); } _e_kbd_mode_eval(kbd); } else if (ky->is_shift) { k->shift_mode = !k->shift_mode; ky->selected = k->shift_mode; if (ky->selected) { edje_object_signal_emit(ky->obj, "e,state,selected", "e"); // ecore_x_test_fake_key_down("Shift_L"); } else { edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); // ecore_x_test_fake_key_up("Shift_L"); } _e_kbd_mode_eval(kbd); } else if (ky->is_ctrl) { k->ctrl_mode = !k->ctrl_mode; ky->selected = k->ctrl_mode; _e_kbd_letter_commit(kbd); if (ky->selected) { edje_object_signal_emit(ky->obj, "e,state,selected", "e"); ecore_x_test_fake_key_down("Control_L"); } else { edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); ecore_x_test_fake_key_up("Control_L"); } _e_kbd_mode_eval(kbd); } else if (ky->is_alt) { k->alt_mode = !k->alt_mode; ky->selected = k->alt_mode; _e_kbd_letter_commit(kbd); if (ky->selected) { edje_object_signal_emit(ky->obj, "e,state,selected", "e"); ecore_x_test_fake_key_down("Alt_L"); } else { edje_object_signal_emit(ky->obj, "e,state,unselected", "e"); ecore_x_test_fake_key_up("Alt_L"); } _e_kbd_mode_eval(kbd); } else { Evas_List *l, *l2; int state; // // handle key // state = NORMAL; // if (k->shift_mode) state = SHIFT; // else if (k->capslock_mode) state = CAPSLOCK; for (l = ky->states; l; l = l->next) { State *st; st = l->data; if (st->state == state) { for (l2 = st->sequence; l2; l2 = l2->next) { Seq *sq; sq = l2->data; if (!sq->up) { if (!_e_kbd_letter_add(kbd, sq->keysym, x, y)) { if (!strcmp(sq->keysym, "BackSpace")) { if (kbd->letters) _e_kbd_letter_del(kbd); else { ecore_x_test_fake_key_down(sq->keysym); ecore_x_test_fake_key_up(sq->keysym); } } else { _e_kbd_letter_commit(kbd); if (k->shift_mode) { ecore_x_test_fake_key_down("Shift_L"); } if (k->capslock_mode) { ecore_x_test_fake_key_down("Caps_Lock"); ecore_x_test_fake_key_up("Caps_Lock"); } ecore_x_test_fake_key_down(sq->keysym); ecore_x_test_fake_key_up(sq->keysym); if (k->capslock_mode) { ecore_x_test_fake_key_down("Caps_Lock"); ecore_x_test_fake_key_up("Caps_Lock"); } if (k->shift_mode) { ecore_x_test_fake_key_up("Shift_L"); } } } } /* if (sq->up) ecore_x_test_fake_key_up(sq->keysym); else ecore_x_test_fake_key_down(sq->keysym); */ } break; } if (st->state == NORMAL) { for (l2 = st->sequence; l2; l2 = l2->next) { Seq *sq; sq = l2->data; if (!sq->up) { if (!_e_kbd_letter_add(kbd, sq->keysym, x, y)) { if (!strcmp(sq->keysym, "BackSpace")) { if (kbd->letters) _e_kbd_letter_del(kbd); else { ecore_x_test_fake_key_down(sq->keysym); ecore_x_test_fake_key_up(sq->keysym); } } else { _e_kbd_letter_commit(kbd); if (k->shift_mode) { ecore_x_test_fake_key_down("Shift_L"); } if (k->capslock_mode) { ecore_x_test_fake_key_down("Caps_Lock"); ecore_x_test_fake_key_up("Caps_Lock"); } ecore_x_test_fake_key_down(sq->keysym); ecore_x_test_fake_key_up(sq->keysym); if (k->capslock_mode) { ecore_x_test_fake_key_down("Caps_Lock"); ecore_x_test_fake_key_up("Caps_Lock"); } if (k->shift_mode) { ecore_x_test_fake_key_up("Shift_L"); } } } } /* if (sq->up) ecore_x_test_fake_key_up(sq->keysym); else ecore_x_test_fake_key_down(sq->keysym); */ } break; } } if ((k->shift_mode) || (k->ctrl_mode) || (k->alt_mode)) { printf("reset shift mode to 0\n"); k->shift_mode = 0; k->ctrl_mode = 0; k->alt_mode = 0; _e_kbd_unset_all_mode(kbd); _e_kbd_mode_eval(kbd); } } } } static void _e_kbd_cb_mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Down *ev; E_Kbd *kbd; Evas_Coord x, y, w, h; Key *ky; Kbd *k; ev = event_info; if (ev->button != 1) return; kbd = data; k = kbd->kbd; kbd->down.x = ev->canvas.x; kbd->down.y = ev->canvas.y; kbd->down.down = 1; kbd->down.stroke = 0; evas_object_geometry_get(kbd->event_obj, &x, &y, &w, &h); x = ev->canvas.x - x; y = ev->canvas.y - y; x = (x * k->w) / w; y = (y * k->h) / h; ky = _e_kbd_at_get(kbd, x, y); k->pressed = ky; if (ky) { ky->pressed = 1; edje_object_signal_emit(ky->obj, "e,state,pressed", "e"); } } static void _e_kbd_cb_mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Move *ev; E_Kbd *kbd; Evas_Coord dx, dy, x, w, y, h; Kbd *k; ev = event_info; kbd = data; if (kbd->down.stroke) return; k = kbd->kbd; dx = ev->cur.canvas.x - kbd->down.x; dy = ev->cur.canvas.y - kbd->down.y; evas_object_geometry_get(kbd->event_obj, &x, &y, &w, &h); dx = (dx * k->w) / w; dy = (dy * k->h) / h; /* FIXME / 4 is a magic number - make config */ if ((dx > 0) && (dx > (k->w / 4))) kbd->down.stroke = 1; else if ((dx < 0) && (-dx > (k->w / 4))) kbd->down.stroke = 1; if ((dy > 0) && (dy > (k->h / 4))) kbd->down.stroke = 1; else if ((dy < 0) && (-dy > (k->w / 4))) kbd->down.stroke = 1; } static void _e_kbd_cb_mouse_up(void *data, Evas *evas, Evas_Object *obj, void *event_info) { Evas_Event_Mouse_Up *ev; E_Kbd *kbd; Kbd *k; Evas_Coord x, y; Key *ky; ev = event_info; if (ev->button != 1) return; kbd = data; if (!kbd->down.stroke) _e_kbd_handle_press(kbd, kbd->down.x, kbd->down.y); else { Evas_Coord dx, dy; int dir = 0; dx = ev->canvas.x - kbd->down.x; dy = ev->canvas.y - kbd->down.y; printf("STROKE: %i %i\n", dx, dy); if (dx > 0) { if (dy > 0) { if (dx > dy) dir = 1; else dir = 2; } else { if (dx > -dy) dir = 1; else dir = 4; } } else { if (dy > 0) { if (-dx > dy) dir = 3; else dir = 2; } else { if (-dx > -dy) dir = 3; else dir = 4; } } printf("DIR (r,d,l,u) %i\n", dir); if (dir > 0) _e_kbd_handle_stroke(kbd, dir); } k = kbd->kbd; ky = k->pressed; if (ky) { ky->pressed = 0; edje_object_signal_emit(ky->obj, "e,state,released", "e"); k->pressed = NULL; } kbd->down.down = 0; kbd->down.stroke = 0; } static void _e_kbd_layout_build(E_Kbd *kbd) { Kbd *k; Evas_List *l, *l2; Evas_Object *o; Evas_Coord lw, lh; if (!kbd->kbd) return; k = kbd->kbd; e_layout_virtual_size_set(kbd->layout_obj, k->w, k->h); edje_extern_object_min_size_set(kbd->layout_obj, k->w, k->h); edje_object_part_swallow(kbd->base_obj, "e.swallow.content", kbd->layout_obj); evas_object_resize(kbd->base_obj, kbd->zone->w, kbd->popup->h); evas_object_geometry_get(kbd->layout_obj, NULL, NULL, &lw, &lh); printf("%i %i\n", lw, lh); lh = (k->h * lw) / k->w; edje_extern_object_min_size_set(kbd->layout_obj, lw, lh); edje_object_part_swallow(kbd->base_obj, "e.swallow.content", kbd->layout_obj); evas_event_freeze(kbd->popup->evas); edje_freeze(); e_layout_freeze(kbd->layout_obj); printf("build objects\n"); for (l = k->keys; l; l = l->next) { Key *ky; const char *label; ky = l->data; o = _theme_obj_new(kbd->popup->evas, kbd->themedir, "e/modules/kbd/key/default"); label = ""; for (l2 = ky->states; l2; l2 = l2->next) { State *st; st = l2->data; if (st->state == NORMAL) { label = st->label; break; } } edje_object_part_text_set(o, "e.text.label", label); e_layout_pack(kbd->layout_obj, o); e_layout_child_move(o, ky->x, ky->y); e_layout_child_resize(o, ky->w, ky->h); evas_object_show(o); ky->obj = o; } printf("done\n"); e_layout_thaw(kbd->layout_obj); edje_thaw(); evas_event_thaw(kbd->popup->evas); o = evas_object_rectangle_add(kbd->popup->evas); e_layout_pack(kbd->layout_obj, o); e_layout_child_move(o, 0, 0); e_layout_child_resize(o, k->w, k->h); evas_object_color_set(o, 0, 0, 0, 0); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _e_kbd_cb_mouse_down, kbd); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _e_kbd_cb_mouse_up, kbd); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _e_kbd_cb_mouse_move, kbd); evas_object_show(o); kbd->event_obj = o; } static void _e_kbd_layout_unbuild(E_Kbd *kbd) { Kbd *k; Evas_List *l, *l2; Evas_Object *o; Evas_Coord lw, lh; if (!kbd->kbd) return; k = kbd->kbd; evas_event_freeze(kbd->popup->evas); edje_freeze(); e_layout_freeze(kbd->layout_obj); for (l = k->keys; l; l = l->next) { Key *ky; ky = l->data; evas_object_del(ky->obj); ky->obj = NULL; } e_layout_thaw(kbd->layout_obj); edje_thaw(); evas_event_thaw(kbd->popup->evas); evas_object_del(kbd->event_obj); kbd->event_obj = NULL; } static Evas_List * _e_kbd_layouts_get(E_Kbd *kbd) { Ecore_List *files; char buf[PATH_MAX]; const char *homedir; Evas_List *kbs = NULL, *l; homedir = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/keyboards", homedir); files = ecore_file_ls(buf); if (files) { char *file; ecore_list_first_goto(files); while ((file = ecore_list_current(files))) { snprintf(buf, sizeof(buf), "%s/.e/e/keyboards/%s", homedir, file); kbs = evas_list_append(kbs, evas_stringshare_add(buf)); ecore_list_next(files); } ecore_list_destroy(files); } snprintf(buf, sizeof(buf), "%s/keyboards", kbd->syskbds); files = ecore_file_ls(buf); if (files) { char *file; ecore_list_first_goto(files); while ((file = ecore_list_current(files))) { int ok; ok = 1; for (l = kbs; l; l = l->next) { const char *fl; fl = ecore_file_file_get(l->data); if (!strcmp(file, fl)) { ok = 0; break; } } if (ok) { snprintf(buf, sizeof(buf), "%s/keyboards/%s", kbd->syskbds, file); kbs = evas_list_append(kbs, evas_stringshare_add(buf)); } ecore_list_next(files); } ecore_list_destroy(files); } return kbs; } static void _e_kbd_cb_matches(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd *kbd; kbd = data; if (kbd->match.popup) _e_kbd_match_down(kbd); else _e_kbd_match_list_show(kbd); } static void _e_kbd_layout_next(E_Kbd *kbd) { Evas_List *l; _e_kbd_layout_unbuild(kbd); for (l = kbd->kbds; l; l = l->next) { if (kbd->kbd == l->data) { if (!l->next) l = kbd->kbds; else l = l->next; kbd->kbd = l->data; break; } } _e_kbd_layout_build(kbd); } static void _e_kbd_layout_prev(E_Kbd *kbd) { Evas_List *l; _e_kbd_layout_unbuild(kbd); for (l = kbd->kbds; l; l = l->next) { if (kbd->kbd == l->data) { if (!l->prev) l = evas_list_last(kbd->kbds); else l = l->prev; kbd->kbd = l->data; break; } } _e_kbd_layout_build(kbd); } static void _e_kbd_cb_layout_next(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd *kbd; kbd = data; _e_kbd_layout_next(kbd); } static void _e_kbd_cb_layout_prev(void *data, Evas_Object *obj, const char *emission, const char *source) { E_Kbd *kbd; kbd = data; _e_kbd_layout_prev(kbd); } /* called from the module core */ EAPI E_Kbd * e_kbd_new(E_Zone *zone, const char *themedir, const char *syskbds, const char *sysdicts) { E_Kbd *kbd; Evas_Coord mw, mh; int x, y; Evas_Object *o; Evas_List *kbs, *l; kbd = E_OBJECT_ALLOC(E_Kbd, E_KBD_TYPE, _e_kbd_free); if (!kbd) return NULL; kbd->zone = zone; if (themedir) kbd->themedir = evas_stringshare_add(themedir); if (syskbds) kbd->syskbds = evas_stringshare_add(syskbds); if (sysdicts) kbd->sysdicts = evas_stringshare_add(sysdicts); kbd->popup = e_popup_new(kbd->zone, -1, -1, 1, 1); e_popup_layer_set(kbd->popup, 190); kbd->base_obj = _theme_obj_new(kbd->popup->evas, kbd->themedir, "e/modules/kbd/base/default"); edje_object_signal_callback_add(kbd->base_obj, "e,action,do,matches", "", _e_kbd_cb_matches, kbd); edje_object_signal_callback_add(kbd->base_obj, "e,action,do,layout,prev", "", _e_kbd_cb_layout_prev, kbd); edje_object_signal_callback_add(kbd->base_obj, "e,action,do,layout,next", "", _e_kbd_cb_layout_next, kbd); o = e_layout_add(kbd->popup->evas); edje_object_part_swallow(kbd->base_obj, "e.swallow.content", o); evas_object_show(o); kbd->layout_obj = o; kbs = _e_kbd_layouts_get(kbd); for (l = kbs; l; l = l->next) { Kbd *k; k = _e_kbd_config_parse(kbd, l->data); kbd->kbds = evas_list_append(kbd->kbds, k); if ((!strcmp(ecore_file_file_get(l->data), "Default.kbd"))) kbd->kbd = k; } _e_kbd_layout_build(kbd); edje_object_size_min_calc(kbd->base_obj, &mw, &mh); x = zone->x; y = zone->y + zone->h; mw = zone->w; e_popup_move_resize(kbd->popup, x, y, mw, mh); evas_object_resize(kbd->base_obj, kbd->popup->w, kbd->popup->h); e_popup_edje_bg_object_set(kbd->popup, kbd->base_obj); evas_object_show(kbd->base_obj); e_popup_show(kbd->popup); kbds = evas_list_append(kbds, kbd); return kbd; } EAPI void e_kbd_show(E_Kbd *kbd) { Evas_Object *o; Evas_Coord mw, mh, vw, vh, w, h; E_OBJECT_CHECK(kbd); E_OBJECT_TYPE_CHECK(kbd, E_KBD_TYPE); if (kbd->delay_hide) { ecore_timer_del(kbd->delay_hide); kbd->delay_hide = NULL; } // edje_extern_object_min_size_set(kbd->scrollframe_obj, mw, mh); // edje_object_part_swallow(kbd->base_obj, "e.swallow.content", kbd->scrollframe_obj); // edje_object_size_min_calc(kbd->base_obj, &mw, &mh); // // edje_extern_object_min_size_set(kbd->scrollframe_obj, 0, 0); // edje_object_part_swallow(kbd->base_obj, "e.swallow.content", kbd->scrollframe_obj); mh = kbd->popup->h; mw = kbd->zone->w; if (mh > kbd->zone->h) mh = kbd->zone->h; e_popup_resize(kbd->popup, mw, mh); evas_object_resize(kbd->base_obj, kbd->popup->w, kbd->popup->h); _e_kbd_slide(kbd, 1, (double)illume_cfg->sliding.kbd.duration / 1000.0); } EAPI void e_kbd_hide(E_Kbd *kbd) { E_OBJECT_CHECK(kbd); E_OBJECT_TYPE_CHECK(kbd, E_KBD_TYPE); _e_kbd_match_down(kbd); _e_kbd_slide(kbd, 0, (double)illume_cfg->sliding.kbd.duration / 1000.0); } EAPI void e_kbd_safe_app_region_get(E_Zone *zone, int *x, int *y, int *w, int *h) { Evas_List *l; if (x) *x = zone->x; if (y) *y = zone->y; if (w) *w = zone->w; if (h) *h = zone->h; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if (kbd->zone == zone) { if ((kbd->out) && (!kbd->animator)) /* out finished */ { if (h) { *h -= kbd->popup->h; if (*h < 0) *h = 0; } } else /* on its way in or in */ { } return; } } } /* internal calls */ static void _e_kbd_free(E_Kbd *kbd) { kbds = evas_list_remove(kbds, kbd); while (kbd->handlers) { if (kbd->handlers->data) ecore_event_handler_del(kbd->handlers->data); kbd->handlers = evas_list_remove_list(kbd->handlers, kbd->handlers); } if (kbd->animator) ecore_animator_del(kbd->animator); if (kbd->themedir) evas_stringshare_del(kbd->themedir); if (kbd->syskbds) evas_stringshare_del(kbd->syskbds); if (kbd->sysdicts) evas_stringshare_del(kbd->sysdicts); e_object_del(E_OBJECT(kbd->popup)); free(kbd); } static int _e_kbd_cb_animate(void *data) { E_Kbd *kbd; double t, v; kbd = data; t = ecore_time_get() - kbd->start; if (t > kbd->len) t = kbd->len; if (kbd->len > 0.0) { v = t / kbd->len; v = 1.0 - v; v = v * v * v * v; v = 1.0 - v; } else { t = kbd->len; v = 1.0; } kbd->adjust = (kbd->adjust_target * v) + (kbd->adjust_start * (1.0 - v)); e_popup_move(kbd->popup, kbd->zone->x, kbd->zone->y + kbd->zone->h - kbd->adjust); if (t == kbd->len) { kbd->animator = NULL; if (kbd->out) { edje_object_signal_emit(kbd->base_obj, "e,state,out,end", "e"); _e_mod_layout_apply_all(); } else edje_object_signal_emit(kbd->base_obj, "e,state,in,end", "e"); _e_kbd_dict_flush(); return 0; } return 1; } static void _e_kbd_slide(E_Kbd *kbd, int out, double len) { if (out == kbd->out) return; kbd->start = ecore_time_get(); kbd->len = len; kbd->out = out; kbd->adjust_start = kbd->adjust; if (kbd->out) kbd->adjust_target = kbd->popup->h; else kbd->adjust_target = 0; if (kbd->out) edje_object_signal_emit(kbd->base_obj, "e,state,out,begin", "e"); else { edje_object_signal_emit(kbd->base_obj, "e,state,in,begin", "e"); _e_mod_layout_apply_all(); } if (len <= 0.0) { _e_kbd_cb_animate(kbd); return; } if (!kbd->animator) kbd->animator = ecore_animator_add(_e_kbd_cb_animate, kbd); } static int _e_kbd_cb_zone_move_resize(void *data, int type, void *event) { E_Event_Zone_Move_Resize *ev; E_Kbd *kbd; ev = event; kbd = data; if (kbd->zone == ev->zone) { /* FIXME: handle new size pants */ } return 1; } /////////////////////////////////////////////////////////////////////////////// /* internal calls */ static Kbd * _e_kbd_config_parse(E_Kbd *kbd, const char *config) { FILE *f; char buf[4096]; int isok = 0; Kbd *k = NULL; Key *ky = NULL; State *st = NULL; Seq *sq = NULL; /* FIXME: this parsing code sucks. i hate writing parsing code. rush rush */ f = fopen(config, "r"); if (!f) return NULL; while (fgets(buf, sizeof(buf), f)) { int len; char str[4096]; if (!isok) { if (!strcmp(buf, "##KBDCONF-1.0\n")) isok = 1; } if (!isok) break; if (buf[0] == '#') continue; len = strlen(buf); if (len > 0) { if (buf[len - 1] == '\n') buf[len - 1] = 0; } if (sscanf(buf, "%s", str) != 1) continue; if (!strcmp(str, "kbd")) { k = calloc(1, sizeof(Kbd)); if (!k) continue; if (sscanf(buf, "%*s %i %i\n", &(k->w), &(k->h)) != 2) { free(k); k = NULL; continue; } } if (!k) continue; if (!strcmp(str, "fuzz")) { sscanf(buf, "%*s %i\n", &(k->fuzz)); continue; } if (!strcmp(str, "key")) { ky = NULL; ky = calloc(1, sizeof(Key)); if (!ky) continue; if (sscanf(buf, "%*s %i %i %i %i\n", &(ky->x), &(ky->y), &(ky->w), &(ky->h)) != 4) { free(ky); ky = NULL; continue; } k->keys = evas_list_append(k->keys, ky); } if (!ky) continue; if ((!strcmp(str, "normal")) || (!strcmp(str, "shift")) || (!strcmp(str, "capslock"))) { char *p; char label[4096]; int xx; if (sscanf(buf, "%*s %s", label) != 1) continue; st = calloc(1, sizeof(State)); if (!st) continue; ky->states = evas_list_append(ky->states, st); if (!strcmp(str, "normal")) st->state = NORMAL; if (!strcmp(str, "shift")) st->state = SHIFT; if (!strcmp(str, "capslock")) st->state = CAPSLOCK; st->label = evas_stringshare_add(label); p = strstr(buf, str); p += strlen(str); while ((*p) && (isblank(*p))) p++; while ((*p) && (!isblank(*p))) p++; while ((*p) && (sscanf(p, "%s", &str) == 1)) { p += strlen(str); if (strlen(str) < 3) continue; sq = calloc(1, sizeof(Seq)); if (!sq) continue; st->sequence = evas_list_append(st->sequence, sq); if (str[0] == 'u') sq->up = 1; sq->keysym = evas_stringshare_add(str + 2); } } if (!strcmp(str, "is_shift")) ky->is_shift = 1; if (!strcmp(str, "is_ctrl")) ky->is_ctrl = 1; if (!strcmp(str, "is_alt")) ky->is_alt = 1; if (!strcmp(str, "is_capslock")) ky->is_capslock = 1; } fclose(f); return k; } static Evas_Object * _theme_obj_new(Evas *e, const char *custom_dir, const char *group) { Evas_Object *o; o = edje_object_add(e); if (!e_theme_edje_object_set(o, "base/theme/modules/illume", group)) { if (custom_dir) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/illume.edj", custom_dir); if (edje_object_file_set(o, buf, group)) { printf("OK FALLBACK %s\n", buf); } } } return o; } #endif