#include "private.h" #include "grid.h" #include "grid_save.h" #include "colors.h" #include "utils.h" #include "config.h" #include "theme.h" #include "dbus.h" #include "gravatar.h" /* local function prototypes */ static void _smart_size(Evas_Object *obj, int w, int h, Eina_Bool force); static void _smart_queue_update(Evas_Object *obj, Grid *sd); Grid_Cell *_cellrow_get(Grid *sd, int y, int *wret); /* local variables */ static Evas_Smart *_smart = NULL; static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL; /* local functions */ static void _coord_to_cursor(Evas_Object *obj, Evas_Coord x, Evas_Coord y, int *cx, int *cy) { Grid *sd; Evas_Coord ox = 0, oy = 0; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); *cx = (x - ox) / sd->font.chw; *cy = (y - oy) / sd->font.chh; if (*cx < 0) *cx = 0; else if (*cx >= sd->grid.w) *cx = (sd->grid.w - 1); if (*cy < 0) *cy = 0; else if (*cy >= sd->grid.h) *cy = (sd->grid.h - 1); } static char * _selection_get(Evas_Object *obj, int c1x, int c1y, int c2x, int c2y, size_t *len) { Grid *sd; Eina_Strbuf *sb; char *s; int x = 0, y = 0; size_t len_backup; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return NULL; sb = eina_strbuf_new(); _grid_save_freeze(); for (y = c1y; y <= c2y; y++) { Grid_Cell *cells; int w, last0, v, start_x, end_x; w = 0; last0 = -1; cells = _cellrow_get(sd, y, &w); if ((!cells) || (!w)) { if (!eina_strbuf_append_char(sb, '\n')) break; continue; } if (w > sd->grid.w) w = sd->grid.w; if ((y == c1y) && (c1x >= w)) { if (!eina_strbuf_append_char(sb, '\n')) break; continue; } start_x = c1x; end_x = (c2x >= w) ? w - 1 : c2x; if (c1y != c2y) { if (y == c1y) end_x = w - 1; else if (y == c2y) start_x = 0; else { start_x = 0; end_x = w - 1; } } for (x = start_x; x <= end_x; x++) { #if defined(SUPPORT_DBLWIDTH) if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth)) { if (x < end_x) x++; else break; } #endif if (x >= w) break; if (cells[x].codepoint == 0) { if (last0 < 0) last0 = x; } else if (cells[x].att.newline) { last0 = -1; if ((y != c2y) || (x != end_x)) eina_strbuf_append_char(sb, '\n'); break; } else if (cells[x].att.tab) { eina_strbuf_append_char(sb, '\t'); while (x > sd->nicklen) x--; /* x = ((x + 8) / 8) * 8; */ /* x--; */ } else { char txt[8]; int txtlen; if (last0 >= 0) { v = x - last0 - 1; last0 = -1; while (v >= 0) { eina_strbuf_append_char(sb, ' '); v--; } } txtlen = _util_codepoint_to_utf8(cells[x].codepoint, txt); if (txtlen > 0) eina_strbuf_append_length(sb, txt, txtlen); if ((x == (w - 1)) && ((x != c2x) || (y != c2y))) { if (!cells[x].att.autowrapped) eina_strbuf_append_char(sb, '\n'); } } } if (last0 >= 0) { if (y == c2y) { Eina_Bool have_more = EINA_FALSE; for (x = end_x + 1; x < w; x++) { #if defined(SUPPORT_DBLWIDTH) if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth)) { if (x < (w - 1)) x++; else break; } #endif if (((cells[x].codepoint != 0) && (cells[x].codepoint != ' ')) || (cells[x].att.newline) || (cells[x].att.tab)) { have_more = EINA_TRUE; break; } } if (!have_more) eina_strbuf_append_char(sb, '\n'); else { for (x = last0; x <= end_x; x++) { #if defined(SUPPORT_DBLWIDTH) if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth)) { if (x < (w - 1)) x++; else break; } #endif if (x >= w) break; eina_strbuf_append_char(sb, ' '); } } } else eina_strbuf_append_char(sb, '\n'); } } _grid_save_thaw(); if (!len) len = &len_backup; *len = eina_strbuf_length_get(sb); if (!*len) { eina_strbuf_free(sb); return NULL; } s = eina_strbuf_string_steal(sb); eina_strbuf_free(sb); return s; } static void _cb_link_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Grid *sd; Evas_Event_Mouse_Down *ev; ev = event; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; if (ev->button == 1) { sd->link.down.down = EINA_TRUE; sd->link.down.x = ev->canvas.x; sd->link.down.y = ev->canvas.y; } /* else if (ev->button == 3) */ /* { */ /* } */ } static void _link_activate(Evas_Object *obj, Eina_Bool may_inline EINA_UNUSED) { Grid *sd; char buff[PATH_MAX], *s, *esc; const char *path = NULL, *cmd = NULL; Eina_Bool url = EINA_FALSE, email = EINA_FALSE, handled = EINA_FALSE; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if (!sd->link.str) return; if (_util_link_is_url(sd->link.str)) { if (eina_str_has_prefix(sd->link.str, "file://")) path = sd->link.str + sizeof("file://") - 1; else url = EINA_TRUE; } else if (sd->link.str[0] == '/') path = sd->link.str; else if (_util_link_is_email(sd->link.str)) email = EINA_TRUE; if ((url) && (eina_str_has_prefix(sd->link.str, "mailto:"))) { email = EINA_TRUE; url = EINA_FALSE; } if (!(s = eina_str_escape(sd->link.str))) return; if (email) { const char *p = s; // run mail client cmd = "xdg-email"; if (eina_str_has_prefix(s, "mailto:")) p += sizeof("mailto:") - 1; esc = ecore_file_escape_name(p); if (esc) { snprintf(buff, sizeof(buff), "%s %s", cmd, esc); free(esc); } } else if (path) { // locally accessible file cmd = "xdg-open"; esc = ecore_file_escape_name(s); if (esc) { /* int type; */ /* type = media_src_type_get(sd->link.string); */ /* if (may_inline && _should_inline(obj)) */ /* { */ /* if ((type == MEDIA_TYPE_IMG) || */ /* (type == MEDIA_TYPE_SCALE) || */ /* (type == MEDIA_TYPE_EDJE)) */ /* { */ /* evas_object_smart_callback_call(obj, "popup", NULL); */ /* handled = EINA_TRUE; */ /* } */ /* else if (type == MEDIA_TYPE_MOV) */ /* { */ /* evas_object_smart_callback_call(obj, "popup", NULL); */ /* handled = EINA_TRUE; */ /* } */ /* } */ if (!handled) { /* if ((type == MEDIA_TYPE_IMG) || */ /* (type == MEDIA_TYPE_SCALE) || */ /* (type == MEDIA_TYPE_EDJE)) */ /* { */ /* if ((config->helper.local.image) && */ /* (config->helper.local.image[0])) */ /* cmd = config->helper.local.image; */ /* } */ /* else if (type == MEDIA_TYPE_MOV) */ /* { */ /* if ((config->helper.local.video) && */ /* (config->helper.local.video[0])) */ /* cmd = config->helper.local.video; */ /* } */ /* else */ /* { */ /* if ((config->helper.local.general) && */ /* (config->helper.local.general[0])) */ /* cmd = config->helper.local.general; */ /* } */ snprintf(buff, sizeof(buff), "%s %s", cmd, esc); free(esc); } } } else if (url) { // remote file needs ecore-con-url cmd = "xdg-open"; esc = ecore_file_escape_name(s); if (esc) { /* int type; */ /* type = media_src_type_get(sd->link.string); */ /* if (may_inline && _should_inline(obj)) */ /* { */ /* if ((type == MEDIA_TYPE_IMG) || */ /* (type == MEDIA_TYPE_SCALE) || */ /* (type == MEDIA_TYPE_EDJE)) */ /* { */ /* evas_object_smart_callback_call(obj, "popup", NULL); */ /* handled = EINA_TRUE; */ /* } */ /* else if (type == MEDIA_TYPE_MOV) */ /* { */ /* evas_object_smart_callback_call(obj, "popup", NULL); */ /* handled = EINA_TRUE; */ /* } */ /* } */ if (!handled) { /* if ((type == MEDIA_TYPE_IMG) || */ /* (type == MEDIA_TYPE_SCALE) || */ /* (type == MEDIA_TYPE_EDJE)) */ /* { */ /* if ((config->helper.url.image) && */ /* (config->helper.url.image[0])) */ /* cmd = config->helper.url.image; */ /* } */ /* else if (type == MEDIA_TYPE_MOV) */ /* { */ /* if ((config->helper.url.video) && */ /* (config->helper.url.video[0])) */ /* cmd = config->helper.url.video; */ /* } */ /* else */ /* { */ /* if ((config->helper.url.general) && */ /* (config->helper.url.general[0])) */ /* cmd = config->helper.url.general; */ /* } */ snprintf(buff, sizeof(buff), "%s %s", cmd, esc); free(esc); } } } else { free(s); return; } free(s); if (!handled) ecore_exe_run(buff, NULL); } static Eina_Bool _cb_link_up_delay(void *data) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return EINA_FALSE; sd->delayed_link_up_tmr = NULL; if (!sd->link.clicked) _link_activate(data, EINA_TRUE); sd->link.clicked = EINA_FALSE; return EINA_FALSE; } static void _cb_link_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Grid *sd; Evas_Event_Mouse_Up *ev; ev = event; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; if ((ev->button == 1) && (sd->link.down.down)) { Evas_Coord dx, dy, size; dx = abs(ev->canvas.x - sd->link.down.x); dy = abs(ev->canvas.y - sd->link.down.y); size = elm_config_finger_size_get(); if ((dx <= size) && (dy <= size)) { if (sd->delayed_link_up_tmr) ecore_timer_reset(sd->delayed_link_up_tmr); else { sd->delayed_link_up_tmr = ecore_timer_add(0.2, _cb_link_up_delay, data); } } sd->link.down.down = EINA_FALSE; } } #if !((ELM_VERSION_MAJOR == 1) && (ELM_VERSION_MINOR < 8)) static void _cb_link_drag_move(void *data, Evas_Object *obj, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action) { const Evas_Modifier *em = NULL; Grid *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN(sd); DBG("dnd %i %i act %i", x, y, action); em = evas_key_modifier_get(evas_object_evas_get(sd->o_event)); if (em) { if (evas_key_modifier_is_set(em, "Control")) elm_drag_action_set(obj, ELM_XDND_ACTION_COPY); else elm_drag_action_set(obj, ELM_XDND_ACTION_MOVE); } } static void _cb_link_drag_accept(void *data, Evas_Object *obj EINA_UNUSED, Eina_Bool doaccept) { Grid *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN(sd); DBG("dnd accept: %i", doaccept); } static void _cb_link_drag_done(void *data, Evas_Object *obj EINA_UNUSED) { Grid *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN(sd); DBG("dnd done"); sd->link.down.dnd = EINA_FALSE; if ((sd->link.down.dndobjdel) && (sd->link.down.dndobj)) evas_object_del(sd->link.down.dndobj); sd->link.down.dndobj = NULL; } static Evas_Object * _cb_link_icon_new(void *data, Evas_Object *par, Evas_Coord *xoff, Evas_Coord *yoff) { Evas_Object *icon; Grid *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL); icon = elm_button_add(par); elm_object_text_set(icon, sd->link.str); *xoff = 0; *yoff = 0; return icon; } #endif static void _cb_link_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj #if ((ELM_VERSION_MAJOR == 1) && (ELM_VERSION_MINOR < 8)) EINA_UNUSED #endif , void *event) { Evas_Event_Mouse_Move *ev = event; Grid *sd = evas_object_smart_data_get(data); Evas_Coord dx, dy; EINA_SAFETY_ON_NULL_RETURN(sd); if (!sd->link.down.down) return; dx = abs(ev->cur.canvas.x - sd->link.down.x); dy = abs(ev->cur.canvas.y - sd->link.down.y); // if ((sd->config->drag_links) && if ((sd->link.str) && ((dx > elm_config_finger_size_get()) || (dy > elm_config_finger_size_get()))) { sd->link.down.down = EINA_FALSE; sd->link.down.dnd = EINA_TRUE; #if !((ELM_VERSION_MAJOR == 1) && (ELM_VERSION_MINOR < 8)) DBG("dnd start %s %i %i", sd->link.str, evas_key_modifier_is_set(ev->modifiers, "Control"), evas_key_modifier_is_set(ev->modifiers, "Shift")); if (evas_key_modifier_is_set(ev->modifiers, "Control")) elm_drag_start(obj, ELM_SEL_FORMAT_IMAGE, sd->link.str, ELM_XDND_ACTION_COPY, _cb_link_icon_new, data, _cb_link_drag_move, data, _cb_link_drag_accept, data, _cb_link_drag_done, data); else elm_drag_start(obj, ELM_SEL_FORMAT_IMAGE, sd->link.str, ELM_XDND_ACTION_MOVE, _cb_link_icon_new, data, _cb_link_drag_move, data, _cb_link_drag_accept, data, _cb_link_drag_done, data); sd->link.down.dndobj = obj; sd->link.down.dndobjdel = EINA_FALSE; #endif } } static void _links_update(Evas_Object *obj, Grid *sd, Eina_Bool same_link, Eina_Bool same_geom) { Evas_Coord ox, oy, ow, oh; Evas_Object *o; int y = 0; if (!sd) return; if (!same_link) { /* FIXME: check link and reprobe */ } if (same_geom) return; if (sd->link.suspend) return; evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); EINA_LIST_FREE(sd->link.objs, o) evas_object_del(o); if (!sd->link.str) return; if ((sd->link.str[0] == '/') || _util_link_is_url(sd->link.str)) { Evas_Coord _x = ox, _y = oy; uint64_t xwin; _x += sd->mouse.cx * sd->font.chw; _y += sd->mouse.cy * sd->font.chh; #if (ELM_VERSION_MAJOR > 1) || (ELM_VERSION_MINOR >= 8) xwin = elm_win_window_id_get(sd->win); # if (ELM_VERSION_MAJOR > 1) || ((ELM_VERSION_MAJOR == 1) && (ELM_VERSION_MINOR > 8)) // not a typo if (strstr(ecore_evas_engine_name_get(ecore_evas_ecore_evas_get(evas_object_evas_get(sd->win))), "wayland")) xwin = ((uint64_t)xwin << 32) + (uint64_t)getpid(); # endif #else xwin = elm_win_xwindow_get(sd->win); #endif _dbus_link_mousein(xwin, sd->link.str, _x, _y); } for (y = sd->link.y1; y <= sd->link.y2; y++) { o = elm_layout_add(obj); evas_object_smart_member_add(o, obj); _theme_apply(elm_layout_edje_get(o), "express/link"); if (y == sd->link.y1) { evas_object_move(o, ox + (sd->link.x1 * sd->font.chw), oy + (y * sd->font.chh)); if (sd->link.y1 == sd->link.y2) evas_object_resize(o, ((sd->link.x2 - sd->link.x1 + 1) * sd->font.chw), sd->font.chh); else evas_object_resize(o, ((sd->grid.w - sd->link.x1) * sd->font.chw), sd->font.chh); } else if (y == sd->link.y2) { evas_object_move(o, ox, oy + (y * sd->font.chh)); evas_object_resize(o, ((sd->link.x2 + 1) * sd->font.chw), sd->font.chh); } else { evas_object_move(o, ox, oy + (y * sd->font.chh)); evas_object_resize(o, (sd->grid.w * sd->font.chw), sd->font.chh); } sd->link.objs = eina_list_append(sd->link.objs, o); elm_object_cursor_set(o, "hand2"); evas_object_show(o); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _cb_link_down, obj); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _cb_link_up, obj); evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _cb_link_move, obj); if (_util_link_is_email(sd->link.str) && _ex_cfg->gui.use_gravatar) gravatar_tooltip(o, sd->link.str); } } static void _links_remove(Grid *sd, Evas_Object *obj) { if (sd->link.str) { if ((sd->link.str[0] == '/') || _util_link_is_url(sd->link.str)) { Evas_Coord ox, oy; uint64_t xwin; evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); ox += sd->mouse.cx * sd->font.chw; oy += sd->mouse.cy * sd->font.chh; #if (ELM_VERSION_MAJOR > 1) || (ELM_VERSION_MINOR >= 8) xwin = elm_win_window_id_get(sd->win); # if (ELM_VERSION_MAJOR > 1) || ((ELM_VERSION_MAJOR == 1) && (ELM_VERSION_MINOR > 8)) // not a typo if (strstr(ecore_evas_engine_name_get(ecore_evas_ecore_evas_get(evas_object_evas_get(sd->win))), "wayland")) xwin = ((uint64_t)xwin << 32) + (uint64_t)getpid(); # endif #else xwin = elm_win_xwindow_get(sd->win); #endif _dbus_link_mouseout(xwin, sd->link.str, ox, oy); } free(sd->link.str); sd->link.str = NULL; } sd->link.x1 = -1; sd->link.y1 = -1; sd->link.x2 = -1; sd->link.y2 = -1; sd->link.suspend = EINA_FALSE; _links_update(obj, sd, EINA_FALSE, EINA_FALSE); } static Eina_Bool coord_back(int *x, int *y, int w, int h EINA_UNUSED) { (*x)--; if ((*x) < 0) { if ((*y) <= 0) { (*x)++; return EINA_FALSE; } (*x) = (w - 1); (*y)--; } return EINA_TRUE; } static Eina_Bool coord_forward(int *x, int *y, int w, int h) { (*x)++; if ((*x) >= w) { if ((*y) >= (h - 1)) { (*x)--; return EINA_FALSE; } (*x) = 0; (*y)++; } return EINA_TRUE; } static char * _link_find(Evas_Object *obj, int cx, int cy, int *x1r, int *y1r, int *x2r, int *y2r) { Grid *sd; char *s, endmatch = 0; int x1 = 0, x2 = 0, y1 = 0, y2 = 0, w = 0, h = 0, sc; size_t len = 0, plen = 0; Eina_Bool back = EINA_TRUE, forward = EINA_FALSE, esc = EINA_FALSE; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return NULL; x1 = x2 = cx; y1 = y2 = cy; w = sd->grid.w; h = sd->grid.h; if ((w <= 0) || (h <= 0)) return NULL; sc = sd->scroll; for (;;) { plen = len; s = _selection_get(obj, x1, (y1 - sc), x2, (y2 - sc), &len); if (!s) break; if (back) { if (_util_link_is_protocol(s)) { back = EINA_FALSE; forward = EINA_TRUE; coord_back(&x1, &y1, w, h); free(s); s = _selection_get(obj, x1, (y1 - sc), x2, (y2 - sc), &len); if (!s) break; switch (s[0]) { case '"': endmatch = '"'; break; case '\'': endmatch = '\''; break; case '`': endmatch = '`'; break; case '<': endmatch = '>'; break; case '[': endmatch = ']'; break; case '{': endmatch = '}'; break; case '(': endmatch = ')'; break; } coord_forward(&x1, &y1, w, h); free(s); plen = len; s = _selection_get(obj, x1, (y1 - sc), x2, (y2 - sc), &len); if (!s) break; } else { switch (s[0]) { case '"': endmatch = '"'; break; case '\'': endmatch = '\''; break; case '`': endmatch = '`'; break; case '<': endmatch = '>'; break; case '[': endmatch = ']'; break; case '{': endmatch = '}'; break; case '(': endmatch = ')'; break; } if ((endmatch) || (isspace(s[0]))) { back = EINA_FALSE; coord_forward(&x1, &y1, w, h); forward = EINA_TRUE; free(s); s = _selection_get(obj, x1, (y1 - sc), x2, (y2 - sc), &len); if (!s) break; } } } if ((forward) && (len >= 1)) { char end; end = s[len - 1]; if (((len - plen) == 2) && (len >= 2)) end = s[len - 2]; if ((end == endmatch) || ((!esc) && (isspace(end)) && ((end != '\n') || (end != '\r')))) { forward = EINA_FALSE; coord_back(&x2, &y2, w, h); endmatch = 0; } esc = (end == '\\'); } if ((back) && (!coord_back(&x1, &y1, w, h))) { back = EINA_FALSE; forward = EINA_TRUE; } if ((forward) && (!coord_forward(&x2, &y2, w, h))) forward = EINA_FALSE; if ((!back) && (!forward)) { free(s); s = _selection_get(obj, x1, (y1 - sc), x2, (y2 - sc), &len); break; } free(s); s = NULL; } if (s) { if ((len > 1) && (!endmatch)) { Eina_Bool isf; isf = _util_is_file(s); if ((isf) || (_util_link_is_email(s)) || (_util_link_is_url(s))) { if (x1r) *x1r = x1; if (y1r) *y1r = y1; if (x2r) *x2r = x2; if (y2r) *y2r = y2; if ((isf) && (s[0] != '/')) { char *ret; ret = _util_local_path_get(obj, s); free(s); return ret; } return s; } } free(s); } return NULL; } static void _mouse_over_apply(Evas_Object *obj) { Grid *sd; char *str; int x1 = 0, y1 = 0, x2 = 0, y2 = 0; Eina_Bool same_link = EINA_FALSE, same_geom = EINA_FALSE; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if ((sd->mouse.cx < 0) || (sd->mouse.cy < 0)) return; str = _link_find(obj, sd->mouse.cx, sd->mouse.cy, &x1, &y1, &x2, &y2); if (!str) { _links_remove(sd, obj); return; } if ((sd->link.str) && (!strcmp(sd->link.str, str))) same_link = EINA_TRUE; if (sd->link.str) free(sd->link.str); sd->link.str = str; if ((x1 == sd->link.x1) && (y1 == sd->link.y1) && (x2 == sd->link.x2) && (y2 == sd->link.y2)) same_geom = EINA_TRUE; if (((sd->link.suspend != 0) && (sd->link.objs)) || ((sd->link.suspend == 0) && (!sd->link.objs))) same_geom = EINA_FALSE; sd->link.x1 = x1; sd->link.y1 = y1; sd->link.x2 = x2; sd->link.y2 = y2; _links_update(obj, sd, same_link, same_geom); } static void _mouse_over_suspend_pushpop(Evas_Object *obj, int dir) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; sd->link.suspend += dir; if (sd->link.suspend < 0) sd->link.suspend = 0; if (sd->link.suspend) { if (sd->anim) ecore_animator_del(sd->anim); sd->anim = NULL; } _smart_queue_update(obj, sd); } static Eina_Bool _cb_delayed_size(void *data) { Grid *sd; Evas_Object *obj; Evas_Coord w = 0, h = 0; obj = data; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return EINA_FALSE; sd->delayed_size_tmr = NULL; evas_object_geometry_get(obj, NULL, NULL, &w, &h); _smart_size(obj, (w / sd->font.chw), (h / sd->font.chh), EINA_FALSE); return EINA_FALSE; } static Eina_Bool _cb_mouse_over_delay(void *data) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return EINA_FALSE; sd->delayed_mouse_over_tmr = NULL; _mouse_over_apply(data); return EINA_FALSE; } static void _cb_mouse_move_job(void *data) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; sd->mouse_move_job = NULL; if (sd->delayed_mouse_over_tmr) ecore_timer_reset(sd->delayed_mouse_over_tmr); else { sd->delayed_mouse_over_tmr = ecore_timer_add(0.05, _cb_mouse_over_delay, data); } } static void _cb_mouse_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Grid *sd; Evas_Event_Mouse_In *ev; int cx = 0, cy = 0; ev = event; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; _coord_to_cursor(data, ev->canvas.x, ev->canvas.y, &cx, &cy); sd->mouse.cx = cx; sd->mouse.cy = cy; _mouse_over_suspend_pushpop(data, -1); } static void _cb_mouse_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Grid *sd; Evas_Event_Mouse_Out *ev; ev = event; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; _mouse_over_suspend_pushpop(data, 1); if ((ev->canvas.x == 0) || (ev->canvas.y == 0)) { sd->mouse.cx = -1; sd->mouse.cy = -1; sd->link.suspend = EINA_FALSE; } else { int cx = 0, cy = 0; _coord_to_cursor(data, ev->canvas.x, ev->canvas.y, &cx, &cy); sd->mouse.cx = cx; sd->mouse.cy = cy; } _links_remove(sd, data); if (sd->delayed_mouse_over_tmr) ecore_timer_del(sd->delayed_mouse_over_tmr); sd->delayed_mouse_over_tmr = NULL; } static void _cb_mouse_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Grid *sd; Evas_Event_Mouse_Move *ev; int cx = 0, cy = 0; ev = event; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; _coord_to_cursor(data, ev->cur.canvas.x, ev->cur.canvas.y, &cx, &cy); if ((sd->mouse.cx == cx) && (sd->mouse.cy == cy)) return; sd->mouse.cx = cx; sd->mouse.cy = cy; if (sd->mouse_move_job) ecore_job_del(sd->mouse_move_job); sd->mouse_move_job = ecore_job_add(_cb_mouse_move_job, data); } static void _cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Evas_Event_Mouse_Down *ev; ev = event; if (ev->button != 3) return; evas_object_smart_callback_call(data, "options", NULL); } static void _cb_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Evas_Event_Mouse_Wheel *ev; Grid *sd; ev = event; /* skip horizontal scrolling */ if (ev->direction) return; if (evas_key_modifier_is_set(ev->modifiers, "Control")) return; if (evas_key_modifier_is_set(ev->modifiers, "Alt")) return; if (evas_key_modifier_is_set(ev->modifiers, "Shift")) return; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; sd->scroll -= (ev->z * 4); if (sd->scroll > sd->backscroll_num) sd->scroll = sd->backscroll_num; else if (sd->scroll < 0) sd->scroll = 0; _smart_queue_update(data, sd); } static void _cb_key_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) { Evas_Event_Key_Down *ev; ev = event; if ((!evas_key_modifier_is_set(ev->modifiers, "Alt")) && (evas_key_modifier_is_set(ev->modifiers, "Control")) && (!evas_key_modifier_is_set(ev->modifiers, "Shift"))) { if ((!strcmp(ev->key, "Prior")) || (!strcmp(ev->key, "Up"))) evas_object_smart_callback_call(data, "prev", NULL); else if ((!strcmp(ev->key, "Next")) || (!strcmp(ev->key, "Down"))) evas_object_smart_callback_call(data, "next", NULL); } } static void _cb_focus_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return; _links_remove(sd, obj); } static void _selection_set(Evas_Object *obj, Eina_Bool enabled) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if (sd->selection.active == enabled) return; sd->selection.active = enabled; if (enabled) evas_object_smart_callback_call(sd->win, "selection,on", NULL); else evas_object_smart_callback_call(sd->win, "selection,off", NULL); } static void _smart_add(Evas_Object *obj) { Grid *sd; /* try to allocate space for smart data */ if (!(sd = calloc(1, sizeof(Grid)))) return; evas_object_smart_data_set(obj, sd); _parent_sc.add(obj); sd->obj = obj; sd->evas = evas_object_evas_get(obj); /* create textgrid */ sd->grid.obj = evas_object_textgrid_add(sd->evas); evas_object_pass_events_set(sd->grid.obj, EINA_TRUE); evas_object_propagate_events_set(sd->grid.obj, EINA_FALSE); evas_object_smart_member_add(sd->grid.obj, obj); evas_object_show(sd->grid.obj); sd->o_event = evas_object_rectangle_add(sd->evas); evas_object_repeat_events_set(sd->o_event, EINA_TRUE); evas_object_color_set(sd->o_event, 0, 0, 0, 0); evas_object_smart_member_add(sd->o_event, obj); evas_object_show(sd->o_event); /* TODO: finish callbacks */ evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_IN, _cb_mouse_in, obj); evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_OUT, _cb_mouse_out, obj); evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_MOVE, _cb_mouse_move, obj); evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_DOWN, _cb_mouse_down, obj); evas_object_event_callback_add(sd->o_event, EVAS_CALLBACK_MOUSE_WHEEL, _cb_mouse_wheel, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, _cb_key_down, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_OUT, _cb_focus_out, obj); sd->link.suspend = 1; } static void _smart_del(Evas_Object *obj) { Grid *sd; _grid_save_unregister(obj); /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; /* delete the size timer */ if (sd->delayed_size_tmr) ecore_timer_del(sd->delayed_size_tmr); /* delete animator */ if (sd->anim) ecore_animator_del(sd->anim); /* delete objects */ if (sd->grid.obj) evas_object_del(sd->grid.obj); if (sd->o_event) evas_object_del(sd->o_event); if (sd->font.name) eina_stringshare_del(sd->font.name); /* free grid cells */ if (sd->cells2) free(sd->cells2); if (sd->cells) free(sd->cells); if (sd->buff) free(sd->buff); _parent_sc.del(obj); } static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) { Grid *sd; Evas_Coord ow, oh; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; /* get geometry of the object */ evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); if ((ow == w) && (oh == h)) return; evas_object_smart_changed(obj); if (!sd->delayed_size_tmr) sd->delayed_size_tmr = ecore_timer_add(0.0, _cb_delayed_size, obj); else ecore_timer_reset(sd->delayed_size_tmr); evas_object_resize(sd->o_event, ow, oh); } static void _smart_move(Evas_Object *obj, Evas_Coord x EINA_UNUSED, Evas_Coord y EINA_UNUSED) { evas_object_smart_changed(obj); } static void _smart_calculate(Evas_Object *obj) { Grid *sd; Evas_Coord ox, oy, ow, oh; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); evas_object_move(sd->grid.obj, ox, oy); evas_object_resize(sd->grid.obj, sd->grid.w * sd->font.chw, sd->grid.h * sd->font.chh); evas_object_move(sd->o_event, ox, oy); evas_object_resize(sd->o_event, ow, oh); } static void _smart_init(void) { static Evas_Smart_Class sc; evas_object_smart_clipped_smart_set(&_parent_sc); sc = _parent_sc; sc.name = "grid"; sc.version = EVAS_SMART_CLASS_VERSION; sc.add = _smart_add; sc.del = _smart_del; sc.resize = _smart_resize; sc.move = _smart_move; sc.calculate = _smart_calculate; _smart = evas_smart_class_new(&sc); } static void _smart_apply(Evas_Object *obj) { Grid *sd; Evas_Coord ox, oy, ow, oh; int x, y, w;// h; int ch1 = 0, ch2 = 0, inv = 0; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); inv = sd->state.reverse; _grid_save_freeze(); for (y = 0; y < sd->grid.h; y++) { Grid_Cell *cells; Evas_Textgrid_Cell *tc; w = 0; cells = _cellrow_get(sd, y - sd->scroll, &w); /* if (!cells) */ /* { */ /* fprintf(stderr, "ERR: COULD NOT GET CELLS !!\n"); */ /* fprintf(stderr, "\tY: %d\tScroll: %d\tW: %d\n", y, sd->scroll, w); */ /* } */ tc = evas_object_textgrid_cellrow_get(sd->grid.obj, y); if (!tc) continue; ch1 = -1; for (x = 0; x < sd->grid.w; x++) { if ((!cells) || (x >= w)) { if ((tc[x].codepoint != 0) || (tc[x].bg != COL_INVIS) || (tc[x].bg_extended)) { if (ch1 < 0) ch1 = x; ch2 = x; } tc[x].codepoint = 0; if (inv) tc[x].bg = COL_INVERSEBG; else tc[x].bg = COL_INVIS; tc[x].bg_extended = 0; tc[x].double_width = 0; tc[x].underline = 0; tc[x].strikethrough = 0; } else { /* TODO: handle block id */ if (cells[x].att.invisible) { if ((tc[x].codepoint != 0) || (tc[x].bg != COL_INVIS) || (tc[x].bg_extended)) { if (ch1 < 0) ch1 = x; ch2 = x; } tc[x].codepoint = 0; if (inv) tc[x].bg = COL_INVERSEBG; else tc[x].bg = COL_INVIS; tc[x].bg_extended = 0; tc[x].underline = 0; tc[x].strikethrough = 0; #if defined(SUPPORT_DBLWIDTH) tc[x].double_width = cells[x].att.dblwidth; #endif if ((tc[x].double_width) && (tc[x].codepoint == 0) && (ch2 == x - 1)) ch2 = x; } else { int fg, bg, fgext, bgext; Eina_Unicode codepoint; // colors fg = cells[x].att.fg; bg = cells[x].att.bg; fgext = cells[x].att.fg256; bgext = cells[x].att.bg256; codepoint = cells[x].codepoint; if ((fg == COL_DEF) && (cells[x].att.inverse ^ inv)) fg = COL_INVERSEBG; if (bg == COL_DEF) { if (cells[x].att.inverse ^ inv) bg = COL_INVERSE; else if (!bgext) bg = COL_INVIS; } if ((cells[x].att.fgintense) && (!fgext)) fg += 48; if ((cells[x].att.bgintense) && (!bgext)) bg += 48; if (cells[x].att.inverse ^ inv) { int t; t = fgext; fgext = bgext; bgext = t; t = fg; fg = bg; bg = t; } if ((cells[x].att.bold) && (!fgext)) fg += 12; if ((cells[x].att.faint) && (!fgext)) fg += 24; if ((tc[x].codepoint != codepoint) || (tc[x].fg != fg) || (tc[x].bg != bg) || (tc[x].fg_extended != fgext) || (tc[x].bg_extended != bgext) || (tc[x].underline != cells[x].att.underline) || (tc[x].strikethrough != cells[x].att.strike)) { if (ch1 < 0) ch1 = x; ch2 = x; } tc[x].fg_extended = fgext; tc[x].bg_extended = bgext; tc[x].underline = cells[x].att.underline; tc[x].strikethrough = cells[x].att.strike; tc[x].fg = fg; tc[x].bg = bg; tc[x].codepoint = codepoint; #if defined(SUPPORT_DBLWIDTH) tc[x].double_width = cells[x].att.dblwidth; #endif if ((tc[x].double_width) && (tc[x].codepoint == 0) && (ch2 == x - 1)) ch2 = x; // cells[x].att.italic // never going 2 support // cells[x].att.blink // cells[x].att.blink2 } } /* fprintf(stderr, "Textgrid Cell %d\n", x); */ /* fprintf(stderr, "\tCodepoint: %d\tFG: %d\tBG: %d\n", */ /* tc[x].codepoint, tc[x].fg, tc[x].bg); */ } evas_object_textgrid_cellrow_set(sd->grid.obj, y, tc); /* only bothering to keep 1 change span per row - not worth doing * more really */ if (ch1 >= 0) evas_object_textgrid_update_add(sd->grid.obj, ch1, y, ch2 - ch1 + 1, 1); } _grid_save_thaw(); } static void _smart_size(Evas_Object *obj, int w, int h, Eina_Bool force) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if (w < 1) w = 1; if (h < 1) h = 1; if (!force) { if ((w == sd->grid.w) && (h == sd->grid.h)) return; } evas_event_freeze(sd->evas); sd->grid.w = w; sd->grid.h = h; evas_object_textgrid_size_set(sd->grid.obj, sd->grid.w, sd->grid.h); evas_object_size_hint_min_set(obj, sd->font.chw, sd->font.chh); evas_object_size_hint_request_set(obj, sd->font.chw * sd->grid.w, sd->font.chh * sd->grid.h); _selection_set(obj, EINA_FALSE); /* call grid resize function to allocate cells */ _grid_resize(obj, w, h); _smart_calculate(obj); _smart_apply(obj); evas_event_thaw(sd->evas); } static Eina_Bool _smart_change(void *data) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(data))) return EINA_FALSE; sd->anim = NULL; _smart_apply(data); return EINA_FALSE; } static void _smart_queue_update(Evas_Object *obj, Grid *sd) { if (sd->anim) return; sd->anim = ecore_animator_add(_smart_change, obj); } static void _codepoint_overwrite_heavy(Grid *sd EINA_UNUSED, int oldc, int newc) { int ido = 0, idn = 0; if (oldc & 0x80000000) ido = (oldc >> 18) & 0x1fff; if (newc & 0x80000000) idn = (newc >> 18) & 0x1fff; if (((oldc & 0x80000000) && (newc & 0x80000000)) && (idn == ido)) return; /* TODO: Commented out until we support blocks */ /* if (oldc & 0x80000000) */ /* { */ /* } */ /* if (newc & 0x80000000) */ /* { */ /* } */ } static inline void _codepoint_overwrite(Grid *sd, Eina_Unicode oldc, Eina_Unicode newc) { if (!((oldc | newc) & 0x80000000)) return; _codepoint_overwrite_heavy(sd, oldc, newc); } Grid_Cell * _cellrow_get(Grid *sd, int y, int *wret) { Grid_Save *gs, **gssrc; if (y >= 0) { if (y >= sd->h) return NULL; *wret = sd->w; return &(GRID_CELLS(sd, 0, y)); } if ((y < -sd->backmax) || (!sd->back)) return NULL; gssrc = &(sd->back[(sd->backmax + sd->backpos + y) % sd->backmax]); gs = _grid_save_extract(*gssrc); if (!gs) return NULL; *gssrc = gs; *wret = gs->w; return gs->cell; } static void _cell_copy(Grid *sd, Grid_Cell *src, Grid_Cell *dst, int n) { int i = 0; for (; i < n; i++) { _codepoint_overwrite(sd, dst[i].codepoint, src[i].codepoint); dst[i] = src[i]; } } static void _cell_codepoint_att_fill(Grid *sd, Eina_Unicode codepoint, Grid_Att att, Grid_Cell *dst, int n) { Grid_Cell local = { .codepoint = codepoint, .att = att }; int i; for (i = 0; i < n; i++) { _codepoint_overwrite(sd, dst[i].codepoint, codepoint); dst[i] = local; } } static void _cell_fill(Grid *sd, Grid_Cell *src, Grid_Cell *dst, int n) { int i; if (src) { for (i = 0; i < n; i++) { _codepoint_overwrite(sd, dst[i].codepoint, src[0].codepoint); dst[i] = src[0]; } } else { for (i = 0; i < n; i++) { _codepoint_overwrite(sd, dst[i].codepoint, 0); memset(&(dst[i]), 0, sizeof(*dst)); } } } static void _cells_swap(Grid *sd) { Grid_Cell *tmp; int tmp_circular_offset; tmp = sd->cells; sd->cells =sd->cells2; sd->cells2 = tmp; /* if (sd->altbuf) */ /* sd->state = sd->swap; */ /* else */ /* sd->swap = sd->state; */ tmp_circular_offset = sd->circular_offset; sd->circular_offset = sd->circular_offset2; sd->circular_offset2 = tmp_circular_offset; sd->altbuf = !sd->altbuf; } static void _limit_coord(Grid *sd) { if (sd->state.had_cr_x >= sd->w) sd->state.had_cr_x = (sd->w - 1); else if (sd->state.had_cr_x < 0) sd->state.had_cr_x = 0; if (sd->state.had_cr_y >= sd->h) sd->state.had_cr_y = (sd->h - 1); else if (sd->state.had_cr_y < 0) sd->state.had_cr_y = 0; /* state->wrapnext = 0; */ if (sd->state.cx >= sd->w) sd->state.cx = sd->w - 1; else if (sd->state.cx < 0) sd->state.cx = 0; if (sd->state.cy >= sd->h) sd->state.cy = sd->h - 1; else if (sd->state.cy < 0) sd->state.cy = 0; if (sd->save.cx >= sd->w) sd->save.cx = (sd->w - 1); else if (sd->save.cx < 0) sd->save.cx = 0; if (sd->save.cy >= sd->h) sd->save.cy = (sd->h - 1); else if (sd->save.cy < 0) sd->save.cy = 0; } ssize_t _line_length(const Grid_Cell *cells, ssize_t nb_cells) { ssize_t len = nb_cells; for (len = nb_cells - 1; len >= 0; len--) { const Grid_Cell *cell; cell = cells + len; if ((cell->codepoint != 0) && (cell->att.bg != COL_INVIS)) return len + 1; } return 0; } static int _line_find_top(Grid *sd, int y_end, int *top) { int y_start = y_end; while (y_start > 0) { if (GRID_CELLS(sd, sd->w - 1, y_start - 1).att.autowrapped) y_start--; else { *top = y_start; return 0; } } while (-y_start < sd->backscroll_num) { Grid_Save *gs; gs = sd->back[(y_start + sd->backpos - 1 + sd->backmax) % sd->backmax]; if (gs) gs = _grid_save_extract(gs); if (!gs) return -1; sd->back[(y_start + sd->backpos - 1 + sd->backmax) % sd->backmax] = gs; if (gs->cell[gs->w - 1].att.autowrapped) y_start--; else { *top = y_start; return 0; } } *top = y_start; return 0; } static int _line_rewrap(Grid *sd, int y_start, int y_end, Grid_Cell *cells2, Grid_Save **back2, int w2, int y2_end, int *new_y2_start, int *new_cyp) { int x, x2, y, y2, y2_start; int len, len_last, len_remaining, copy_width, ts2_width; Grid_Save *gs, *gs2; Grid_Cell *line, *line2 = NULL; if (y_end >= 0) len_last = _line_length(&GRID_CELLS(sd, 0, y_end), sd->w); else { gs = _grid_save_extract(sd->back[(y_end + sd->backpos + sd->backmax) % sd->backmax]); if (!gs) return -1; sd->back[(y_end + sd->backpos + sd->backmax) % sd->backmax] = gs; len_last = gs->w; } len_remaining = len_last + (y_end - y_start) * sd->w; y2_start = y2_end; if (len_remaining) y2_start -= (len_remaining + w2 - 1) / w2 - 1; else { if (y2_start < 0) back2[y2_start + sd->backmax] = _grid_save_new(0); *new_y2_start = y2_start; return 0; } if (-y2_start > sd->backmax) { y_start += ((-y2_start - sd->backmax) * w2) / sd->w; x = ((-y2_start - sd->backmax) * w2) % sd->w; len_remaining -= (-y2_start - sd->backmax) * w2; y2_start = -sd->backmax; } else x = 0; y = y_start; x2 = 0; y2 = y2_start; while (y <= y_end) { if (y >= 0) line = &GRID_CELLS(sd, 0, y); else { gs = _grid_save_extract(sd->back[(y + sd->backpos + sd->backmax) % sd->backmax]); if (!gs) return -1; sd->back[(y + sd->backpos + sd->backmax) % sd->backmax] = gs; line = gs->cell; } if (y == y_end) len = len_last; else len = sd->w; line[len - 1].att.autowrapped = 0; while (x < len) { copy_width = MIN(len - x, w2 - x2); if (x2 == 0) { if (y2 >= 0) line2 = cells2 + (y2 * w2); else { ts2_width = MIN(len_remaining, w2); gs2 = _grid_save_new(ts2_width); if (!gs2) return -1; line2 = gs2->cell; back2[y2 + sd->backmax] = gs2; } } if (y == sd->state.cy) { *new_cyp = y2_start; } if (line2) { _cell_copy(sd, line + x, line2 + x2, copy_width); x += copy_width; x2 += copy_width; len_remaining -= copy_width; if ((x2 == w2) && (y2 != y2_end)) { line2[x2 - 1].att.autowrapped = 1; x2 = 0; y2++; } } } x = 0; y++; } *new_y2_start = y2_start; return 0; } static void _scroll(Grid *sd, int direction, int start_y, int end_y) { if (sd->scroll > 0) { sd->scroll -= direction; if (sd->scroll > sd->backscroll_num) sd->scroll = sd->backscroll_num; } /* if (sd->selection.active) */ /* { */ /* if ((start_y <= sd->selection.start.y) && */ /* (end_y >= sd->selection.end.y)) */ /* { */ /* } */ /* } */ if (sd->link.str) { if ((sd->link.y1 <= end_y) && (sd->link.y2 >= start_y)) _links_remove(sd, sd->obj); } } static void _text_clear(Grid *sd, Grid_Cell *cells, int count, int val, Eina_Bool inherit_att) { Grid_Cell src; memset(&src, 0, sizeof(src)); src.codepoint = val; if (inherit_att) src.att = sd->state.att; _cell_fill(sd, &src, cells, count); } static void _text_save_top(Grid *sd, Grid_Cell *cells, ssize_t w_max) { Grid_Save *gs; ssize_t w; if (sd->backmax <= 0) return; _grid_save_freeze(); w = _line_length(cells, w_max); gs = _grid_save_new(w); if (!gs) return; _cell_copy(sd, cells, gs->cell, w); if (!sd->back) sd->back = calloc(1, sizeof(Grid_Cell *) * sd->backmax); if (sd->back[sd->backpos]) { _grid_save_free(sd->back[sd->backpos]); sd->back[sd->backpos] = NULL; } sd->back[sd->backpos] = gs; sd->backpos++; if (sd->backpos >= sd->backmax) sd->backpos = 0; sd->backscroll_num++; if (sd->backscroll_num >= sd->backmax) sd->backscroll_num = sd->backmax; _grid_save_thaw(); } static void _text_scroll(Grid *sd, Eina_Bool clear) { Grid_Cell *cells, *cells2 = NULL; int y, start_y = 0, end_y = sd->h - 1; if (sd->state.scroll_y2 != 0) { start_y = sd->state.scroll_y1; end_y = sd->state.scroll_y2 - 1; } else { if (!sd->altbuf) _text_save_top(sd, &(GRID_CELLS(sd, 0, 0)), sd->w); } _scroll(sd, -1, start_y, end_y); if ((start_y == 0) && (end_y == (sd->h - 1))) { cells = &(sd->cells[sd->circular_offset * sd->w]); if (clear) _text_clear(sd, cells, sd->w, 0, EINA_TRUE); sd->circular_offset++; if (sd->circular_offset >= sd->h) sd->circular_offset = 0; } else { cells = &(GRID_CELLS(sd, 0, end_y)); for (y = start_y; y < end_y; y++) { cells = &(GRID_CELLS(sd, 0, (y + 1))); cells2 = &(GRID_CELLS(sd, 0, y)); _cell_copy(sd, cells, cells2, sd->w); } if (clear) _text_clear(sd, cells, sd->w, 0, EINA_TRUE); } } static void _text_scroll_test(Grid *sd, Eina_Bool clear) { int e; e = sd->h; if (sd->state.scroll_y2 != 0) e = sd->state.scroll_y2; if (sd->state.cy >= e) { _text_scroll(sd, clear); sd->state.cy = (e - 1); if (sd->state.cy >= sd->h) sd->state.cy = (sd->h - 1); else if (sd->state.cy < 0) sd->state.cy = 0; } } static void _cursor_handle(Grid *sd, const Eina_Unicode *cc) { switch (*cc) { case 0x0a: // LF '\n' (new line); if (sd->state.had_cr) { GRID_CELLS(sd, sd->state.had_cr_x, sd->state.had_cr_y).att.newline = 1; } sd->state.had_cr = 0; sd->state.wrapnext = 0; if (sd->state.crlf) sd->state.cx = 0; sd->state.cy++; _text_scroll_test(sd, EINA_TRUE); break; case 0x0d: // CR '\r' (carriage return) if (sd->state.cx != 0) { sd->state.had_cr_x = sd->state.cx; sd->state.had_cr_y = sd->state.cy; sd->state.wrapnext = 0; } sd->state.cx = 0; // sd->state.had_cr = 1; break; case 0x09: // HT '\t' (horizontal tab) sd->state.had_cr = 0; GRID_CELLS(sd, sd->state.cx, sd->state.cy).att.tab = 1; sd->state.wrapnext = 0; while (sd->state.cx < (sd->nicklen + 1)) sd->state.cx += 1; /* sd->state.cx += 8; */ /* sd->state.cx = (sd->state.cx / 8) * 8; */ if (sd->state.cx >= sd->w) sd->state.cx = (sd->w - 1); else if (sd->state.cx < 0) sd->state.cx = 0; break; default: break; } } static void _text_append(Grid *sd, const Eina_Unicode *codepoints, int len, Row_Color *color) { Grid_Cell *cells; int i, j; cells = &(GRID_CELLS(sd, 0, sd->state.cy)); for (i = 0; i < len; i++) { Eina_Unicode g; if (sd->state.wrapnext) { cells[sd->w - 1].att.autowrapped = 1; sd->state.wrapnext = 0; sd->state.cx = 0; sd->state.cy++; _text_scroll_test(sd, EINA_TRUE); cells = &(GRID_CELLS(sd, 0, sd->state.cy)); } if (sd->state.insert) { for (j = (sd->w - 1); j > sd->state.cx; j--) _cell_copy(sd, &(cells[j - 1]), &(cells[j]), 1); } g = _util_charset_translate(codepoints[i], &sd->state); _cell_codepoint_att_fill(sd, g, sd->state.att, &(cells[sd->state.cx]), 1); if (color) { cells[sd->state.cx].att.fg = color->fg_primary_color; cells[sd->state.cx].att.bg = color->bg_primary_color; } #if defined(SUPPORT_DBLWIDTH) cells[sd->state.cx].att.dblwidth = _util_dblwidth_get(sd, g); if (EINA_UNLIKELY((cells[sd->state.cx].att.dblwidth) && (sd->state.cx < (sd->w - 1)))) { GRID_FMTCLR(cells[sd->state.cx].att); _cell_codepoint_att_fill(sd, 0, cells[sd->state.cx].att, &(cells[sd->state.cx + 1]), 1); } #endif if (sd->state.wrap) { unsigned char offset = 1; sd->state.wrapnext = 0; #if defined(SUPPORT_DBLWIDTH) if (EINA_UNLIKELY(cells[sd->state.cx].att.dblwidth)) offset = 2; #endif if (EINA_UNLIKELY(sd->state.cx >= (sd->w - offset))) sd->state.wrapnext = 1; else { sd->state.cx += offset; if (sd->state.cx >= sd->w) sd->state.cx = (sd->w - 1); else if (sd->state.cx < 0) sd->state.cx = 0; } } else { unsigned char offset = 1; sd->state.wrapnext = 0; #if defined(SUPPORT_DBLWIDTH) if (EINA_UNLIKELY(cells[sd->state.cx].att.dblwidth)) offset = 2; #endif sd->state.cx += offset; if (sd->state.cx > (sd->w - offset)) { sd->state.cx = sd->w - offset; if (sd->state.cx >= sd->w) sd->state.cx = (sd->w - 1); else if (sd->state.cx < 0) sd->state.cx = 0; return; } if (sd->state.cx >= sd->w) sd->state.cx = (sd->w - 1); else if (sd->state.cx < 0) sd->state.cx = 0; } } } static int _seq_handle(Grid *sd, Eina_Unicode *c, Eina_Unicode *ce, Row_Color *color) { Eina_Unicode *cc; int len = 0; if (c[0] < 0x20) { switch (c[0]) { case 0x0a: // LF '\n' (new line); case 0x0d: // CR '\r' (carriage return) case 0x09: // HT '\t' (horizontal tab) _cursor_handle(sd, c); return 1; default: sd->state.had_cr = 0; return 1; } } else sd->state.had_cr = 0; cc = c; while ((cc < ce) && (*cc >= 0x20) && (*cc != 0x7f)) { cc++; len++; } _text_append(sd, c, len, color); sd->state.had_cr = 0; return len; } static void _buffer_handle(Grid *sd, const Eina_Unicode *codepoints, int len, Row_Color *color) { Eina_Unicode *c, *ce, *b; int n, bytes; c = (Eina_Unicode *)codepoints; ce = &(c[len]); if (sd->buff) { bytes = (sd->bufflen + len + 1) * sizeof(int); b = realloc(sd->buff, bytes); if (!b) { ERR("Memerr: %s", strerror(errno)); return; } bytes = len * sizeof(Eina_Unicode); memcpy(&(b[sd->bufflen]), codepoints, bytes); sd->buff = b; sd->bufflen += len; sd->buff[sd->bufflen] = 0; c = sd->buff; ce = c + sd->bufflen; while (c < ce) { n = _seq_handle(sd, c, ce, color); if (n == 0) { Eina_Unicode *tmp; tmp = sd->buff; sd->buff = NULL; sd->bufflen = 0; bytes = ((char *)ce - (char *)c) + sizeof(Eina_Unicode); sd->buff = malloc(bytes); if (!sd->buff) { ERR("Memerr: %s", strerror(errno)); return; } bytes = (char *)ce - (char *)c; memcpy(sd->buff, c, bytes); sd->bufflen = bytes / sizeof(Eina_Unicode); sd->buff[sd->bufflen] = 0; free(tmp); break; } c += n; } if (c == ce) { if (sd->buff) { free(sd->buff); sd->buff = NULL; } sd->bufflen = 0; } } else { while (c < ce) { n = _seq_handle(sd, c, ce, color); if (n == 0) { bytes = ((char *)ce - (char *)c) + sizeof(Eina_Unicode); sd->buff = malloc(bytes); if (!sd->buff) { ERR("Memerr: %s", strerror(errno)); } else { bytes = (char *)ce - (char *)c; memcpy(sd->buff, c, bytes); sd->bufflen = bytes / sizeof(Eina_Unicode); sd->buff[sd->bufflen] = 0; } break; } c += n; } } } static void _att_reset(Grid_Att *att) { att->fg = COL_DEF; att->bg = COL_DEF; att->bold = 0; att->faint = 0; #if defined(SUPPORT_ITALIC) att->italic = 0; #elif defined(SUPPORT_DBLWIDTH) att->dblwidth = 0; #endif att->underline = 0; att->blink = 0; att->blink2 = 0; att->inverse = 0; att->invisible = 0; att->strike = 0; att->fg256 = 0; att->bg256 = 0; att->fgintense = 0; att->bgintense = 0; att->autowrapped = 0; att->newline = 0; att->tab = 0; att->fraktur = 0; } static void _state_reset(Grid *sd) { sd->state.cx = 0; sd->state.cy = 0; sd->state.scroll_y1 = 0; sd->state.scroll_y2 = 0; sd->state.had_cr_x = 0; sd->state.had_cr_y = 0; _att_reset(&(sd->state.att)); sd->state.charset = 0; sd->state.charsetch = 'B'; sd->state.chset[0] = 'B'; sd->state.chset[1] = 'B'; sd->state.chset[2] = 'B'; sd->state.chset[3] = 'B'; sd->state.multibyte = 0; sd->state.alt_kp = 0; sd->state.insert = 0; sd->state.appcursor = 0; sd->state.wrap = 1; sd->state.wrapnext = 0; sd->state.hidecursor = 0; sd->state.crlf = 0; sd->state.had_cr = 0; /* sd->mouse_mode = MOUSE_OFF; */ /* sd->mouse_ext = MOUSE_EXT_NONE; */ /* sd->bracketed_paste = 0; */ _grid_save_freeze(); if (sd->back) { int i; for (i = 0; i < sd->backmax; i++) if (sd->back[i]) _grid_save_free(sd->back[i]); free(sd->back); sd->back = NULL; } sd->backscroll_num = 0; sd->backpos = 0; if (sd->backmax) sd->back = calloc(1, sizeof(Grid_Save *) * sd->backmax); _grid_save_thaw(); } static void _backscroll_set(Grid *sd, int size) { int i; if (sd->backmax == size) return; _grid_save_freeze(); if (sd->back) { for (i = 0; i < sd->backmax; i++) if (sd->back[i]) _grid_save_free(sd->back[i]); free(sd->back); } if (size > 0) sd->back = calloc(1, sizeof(Grid_Save *) * size); else sd->back = NULL; sd->backscroll_num = 0; sd->backpos = 0; sd->backmax = size; _grid_save_thaw(); } /* external functions */ Evas_Object * _grid_add(Evas *evas) { Evas_Object *obj; Grid *sd; if (!_smart) _smart_init(); obj = evas_object_smart_add(evas, _smart); /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return obj; sd->w = 80; sd->h = 24; sd->backmax = 2000; _state_reset(sd); sd->save = sd->state; sd->swap = sd->state; sd->cells = calloc(1, sizeof(Grid_Cell) * sd->w * sd->h); if (!sd->cells) goto err; sd->cells2 = calloc(1, sizeof(Grid_Cell) * sd->w * sd->h); if (!sd->cells2) goto err; _smart_size(obj, sd->w, sd->h, EINA_TRUE); _grid_save_register(obj); return obj; err: if (sd->cells) free(sd->cells); if (sd->cells2) free(sd->cells2); free(sd); return obj; } void _grid_update(Evas_Object *obj) { Grid *sd; Evas_Coord w = 0, h = 0; Evas_Coord ow = 0, oh = 0; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if (sd->font.name) eina_stringshare_del(sd->font.name); sd->font.name = NULL; if (_ex_cfg->font.bitmap) { char buff[PATH_MAX]; snprintf(buff, sizeof(buff), "%s/fonts/%s", elm_app_data_dir_get(), _ex_cfg->font.name); sd->font.name = eina_stringshare_add(buff); } else sd->font.name = eina_stringshare_add(_ex_cfg->font.name); sd->font.size = _ex_cfg->font.size; _backscroll_set(sd, _ex_cfg->gui.scrollback); sd->scroll = 0; _colors_init(sd->grid.obj, sd->o_theme); evas_object_scale_set(sd->grid.obj, elm_config_scale_get()); evas_object_textgrid_font_set(sd->grid.obj, sd->font.name, sd->font.size); evas_object_textgrid_size_get(sd->grid.obj, &w, &h); if (w < 1) w = 1; if (h < 1) h = 1; evas_object_textgrid_size_set(sd->grid.obj, w, h); evas_object_textgrid_cell_size_get(sd->grid.obj, &w, &h); if (w < 1) w = 1; if (h < 1) h = 1; sd->font.chw = w; sd->font.chh = h; evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); _smart_size(obj, ow / w, oh / h, EINA_TRUE); } void _grid_window_set(Evas_Object *obj, Evas_Object *win) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; sd->win = win; } void _grid_theme_set(Evas_Object *obj, Evas_Object *theme) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; sd->o_theme = theme; } void _grid_resize(Evas_Object *obj, int nw, int nh) { Grid *sd; Grid_Cell *new_cells = NULL; Grid_Save **new_back = NULL; int y_start, y_end, new_y_start = 0, new_y_end, i; int altbuf = 0, new_cy; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; if ((sd->w == nw) && (sd->h == nh)) return; if ((nw == nh) && (nw == 1)) return; new_cy = sd->state.cy; _grid_save_freeze(); if (sd->altbuf) { _cells_swap(sd); altbuf = 1; } new_cells = calloc(1, sizeof(Grid_Cell) * nw * nh); if (!new_cells) goto err; free(sd->cells2); sd->cells2 = calloc(1, sizeof(Grid_Cell) * nw * nh); if (!sd->cells2) goto err; new_back = calloc(sizeof(Grid_Save *), sd->backmax); y_end = sd->state.cy; new_y_end = nh - 1; while ((y_end >= -sd->backscroll_num) && (new_y_end >= -sd->backmax)) { if (_line_find_top(sd, y_end, &y_start) < 0) goto err; if (_line_rewrap(sd, y_start, y_end, new_cells, new_back, nw, new_y_end, &new_y_start, &new_cy) < 0) goto err; y_end = y_start - 1; new_y_end = new_y_start - 1; } free(sd->cells); sd->cells = new_cells; for (i = 1; i <= sd->backscroll_num; i++) _grid_save_free(sd->back[(sd->backpos - i + sd->backmax) % sd->backmax]); free(sd->back); sd->back = new_back; sd->w = nw; sd->h = nh; sd->circular_offset = MAX(new_y_start, 0); sd->backpos = 0; sd->backscroll_num = MAX(-new_y_start, 0); sd->state.had_cr = 0; sd->state.cy = (new_cy + nh - sd->circular_offset) % nh; if (altbuf) _cells_swap(sd); sd->state.wrapnext = 0; _limit_coord(sd); _grid_save_thaw(); return; err: _grid_save_thaw(); free(new_cells); free(new_back); } void _grid_text_append(Evas_Object *obj, const char *txt, int len, Row_Color *color) { Grid *sd; char buff[len]; Eina_Unicode codepoint[len]; int i = 0, j = 0, k = 0; // fprintf(stdout, "Append Len: %d\n", len); /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; /* copy txt into a buffer we can manipulate */ memcpy(buff, txt, len); buff[len] = 0;//'\0'; // fprintf(stdout, "BUFFER: [START]%s[END]", buff); // fprintf(stdout, "\tLen: %zu\n", strlen(buff)); for (i = 0; i < (int)sizeof(sd->oldbuff); i++) sd->oldbuff[i] = 0; for (i = 0; i < len;) { int g = 0, prev_i = i; if (buff[i]) { #if (EINA_VERSION_MAJOR > 1) || (EINA_VERSION_MINOR >= 8) g = eina_unicode_utf8_next_get(buff, &i); if ((0xdc80 <= g) && (g <= 0xdcff) && (len - prev_i) <= (int)sizeof(sd->oldbuff)) #else i = evas_string_char_next_get(buff, i, &g); if (i < 0 && (len - prev_i) <= (int)sizeof(sd->oldbuff)) #endif { for (k = 0; (k < (int)sizeof(sd->oldbuff)) && (k < (len - prev_i)); k++) { sd->oldbuff[k] = buff[prev_i + k]; } DBG("failure at %d/%d/%d", prev_i, i, len); break; } } else { g = 0; i++; } codepoint[j] = g; j++; } codepoint[j] = 0; _buffer_handle(sd, codepoint, j, color); _smart_queue_update(obj, sd); } void _grid_nicklen_set(Evas_Object *obj, int len) { Grid *sd; /* try to get the objects smart data */ if (!(sd = evas_object_smart_data_get(obj))) return; sd->nicklen = len; } Row_Color * _row_color_simple_create(int fg_primary_color) { Row_Color *ret = NULL; ret = malloc(sizeof(Row_Color)); if (ret) { ret->fg_primary_color = fg_primary_color; ret->bg_primary_color = 0; } return ret; }