diff --git a/src/bin/termio.c b/src/bin/termio.c index e82b7558..1303f711 100644 --- a/src/bin/termio.c +++ b/src/bin/termio.c @@ -54,6 +54,7 @@ struct _Termio char *string; int x1, y1, x2, y2; int suspend; + uint16_t id; Eina_List *objs; struct { Evas_Object *dndobj; @@ -1389,8 +1390,133 @@ _remove_links(Termio *sd, Evas_Object *obj) sd->link.y2 = -1; sd->link.suspend = EINA_FALSE; _update_link(obj, sd, same_link, same_geom); + sd->link.id = 0; } +static void +_hyperlink_end(Termio *sd, + Term_Link *hl, + Evas_Object *o, + Eina_Bool add_tooltip) +{ + Eina_Bool popup_exists; + + 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, sd->self); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, + _cb_link_up, sd->self); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, + _cb_link_move, sd->self); + popup_exists = main_term_popup_exists(sd->term); + if (!popup_exists && add_tooltip) + { + if (link_is_email(hl->url)) + { + gravatar_tooltip(o, sd->config, hl->url); + } + /* display tooltip */ + elm_object_tooltip_text_set(o, hl->url); + } +} + +static void +_hyperlink_mouseover(Evas_Object *obj, Termio *sd, + uint16_t link_id) +{ + Evas_Coord ox, oy, ow, oh; + Evas_Object *o; + int x, y; + Term_Link *hl; + + EINA_SAFETY_ON_NULL_RETURN(sd); + + /* If it's the same link, consider we already have the correct links + * displayed */ + if (sd->link.id == link_id) + return; + + if (sd->link.suspend) + return; + + evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); + EINA_LIST_FREE(sd->link.objs, o) + { + if (sd->link.down.dndobj == o) + { + sd->link.down.dndobjdel = EINA_TRUE; + evas_object_hide(o); + } + else + evas_object_del(o); + } + hl = &sd->pty->hl.links[link_id]; + if (!hl->url) + return; + + /* Scan the whole screen and display links as needed */ + termpty_backlog_lock(); + termpty_backscroll_adjust(sd->pty, &sd->scroll); + for (y = 0; y < sd->grid.h; y++) + { + ssize_t w = 0; + Termcell *cells; + int start_x = -1; + + o = NULL; + + cells = termpty_cellrow_get(sd->pty, y - sd->scroll, &w); + if (!cells) + continue; + for (x = 0; x < w; x++) + { + Termcell *c = cells + x; + if (term_link_eq(sd->pty, hl, c->att.link_id)) + { + if (!o) + { + o = elm_layout_add(sd->win); + evas_object_smart_member_add(o, obj); + theme_apply(elm_layout_edje_get(o), sd->config, + "terminology/link"); + evas_object_move(o, + ox + (x * sd->font.chw), + oy + (y * sd->font.chh)); + start_x = x; + } + } + else + { + if (o) + { + evas_object_resize(o, + (x - start_x) * sd->font.chw, + sd->font.chh); + _hyperlink_end(sd, hl, o, + (y == sd->mouse.cy) && + ((start_x <= sd->mouse.cx) && + (sd->mouse.cx <= x))); + o = NULL; + } + } + } + if (o) + { + evas_object_resize(o, + (x - start_x + 1) * sd->font.chw, + sd->font.chh); + _hyperlink_end(sd, hl, o, + (y == sd->mouse.cy) && + ((start_x <= sd->mouse.cx) && + (sd->mouse.cx <= x))); + } + } + termpty_backlog_unlock(); +} + + /* }}} */ /* {{{ Blocks */ @@ -3573,6 +3699,7 @@ _smart_mouseover_apply(Evas_Object *obj) Eina_Bool same_link = EINA_FALSE, same_geom = EINA_FALSE; Termio *sd = evas_object_smart_data_get(obj); Config *config; + Termcell *cell = NULL; EINA_SAFETY_ON_NULL_RETURN(sd); config = sd->config; @@ -3584,6 +3711,18 @@ _smart_mouseover_apply(Evas_Object *obj) _remove_links(sd, obj); return; } + cell = termpty_cell_get(sd->pty, sd->mouse.cy - sd->scroll, sd->mouse.cx); + if (!cell) + { + _remove_links(sd, obj); + return; + } + + if (cell->att.link_id) + { + _hyperlink_mouseover(obj, sd, cell->att.link_id); + return; + } s = termio_link_find(obj, sd->mouse.cx, sd->mouse.cy, &x1, &y1, &x2, &y2); diff --git a/src/bin/termiolink.c b/src/bin/termiolink.c index bbb818e1..bd96dd35 100644 --- a/src/bin/termiolink.c +++ b/src/bin/termiolink.c @@ -85,7 +85,7 @@ _txt_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp) cell = cells[*x]; } - if (cell.codepoint == 0) + if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); @@ -146,7 +146,7 @@ _txt_prev_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp) cell = cells[*x]; } - if (cell.codepoint == 0) + if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); @@ -210,7 +210,7 @@ _txt_next_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp) } cell = cells[*x]; - if (cell.codepoint == 0) + if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); @@ -235,7 +235,9 @@ termio_link_find(const Evas_Object *obj, int cx, int cy, char *s = NULL; char endmatch = 0; int x1, x2, y1, y2, w = 0, h = 0, sc; - Eina_Bool goback = EINA_TRUE, goforward = EINA_FALSE, escaped = EINA_FALSE; + Eina_Bool goback = EINA_TRUE, + goforward = EINA_FALSE, + escaped = EINA_FALSE; struct ty_sb sb = {.buf = NULL, .gap = 0, .len = 0, .alloc = 0}; Termpty *ty = termio_pty_get(obj); int res; @@ -525,9 +527,9 @@ term_link_free(Termpty *ty, Term_Link *link) return; uint16_t id = (link - ty->hl.links); - free(link->key); + eina_stringshare_del(link->key); link->key = NULL; - free(link->url); + eina_stringshare_del(link->url); link->url = NULL; /* Remove from bitmap */ diff --git a/src/bin/termpty.c b/src/bin/termpty.c index 6d93bd3d..49d4c7ec 100644 --- a/src/bin/termpty.c +++ b/src/bin/termpty.c @@ -817,7 +817,7 @@ termpty_free(Termpty *ty) size_t i; for (i = 0; i < ty->backsize; i++) - termpty_save_free(&ty->back[i]); + termpty_save_free(ty, &ty->back[i]); free(ty->back); } free(ty->screen); @@ -962,7 +962,7 @@ _backlog_remove_latest_nolock(Termpty *ty) ty->backlog_beacon.backlog_y = 0; verify_beacon(ty, 0); - termpty_save_free(ts); + termpty_save_free(ty, ts); } @@ -993,7 +993,7 @@ termpty_text_save_top(Termpty *ty, Termcell *cells, ssize_t w_max) if (ts->w && ts->cells[ts->w - 1].att.autowrapped) { int old_len = ts->w; - termpty_save_expand(ts, cells, w); + termpty_save_expand(ty, ts, cells, w); ty->backlog_beacon.screen_y += (ts->w + ty->w - 1) / ty->w - (old_len + ty->w - 1) / ty->w; verify_beacon(ty, 0); @@ -1003,7 +1003,7 @@ termpty_text_save_top(Termpty *ty, Termcell *cells, ssize_t w_max) add_new_ts: ts = BACKLOG_ROW_GET(ty, 0); - ts = termpty_save_new(ts, w); + ts = termpty_save_new(ty, ts, w); if (!ts) return; TERMPTY_CELL_COPY(ty, cells, ts->cells, w); @@ -1485,7 +1485,7 @@ termpty_backlog_size_set(Termpty *ty, size_t size) size_t i; for (i = 0; i < ty->backsize; i++) - termpty_save_free(&ty->back[i]); + termpty_save_free(ty, &ty->back[i]); free(ty->back); } if (size > 0) @@ -1677,6 +1677,9 @@ termpty_cells_att_fill_preserve_colors(Termpty *ty, Termcell *cells, int i; Termcell local = { .codepoint = codepoint, .att = ty->termstate.att}; + if (EINA_UNLIKELY(local.att.link_id)) + term_link_refcount_inc(ty, local.att.link_id, count); + for (i = 0; i < count; i++) { Termatt att = cells[i].att; @@ -1696,8 +1699,6 @@ termpty_cells_att_fill_preserve_colors(Termpty *ty, Termcell *cells, cells[i].att.bgintense = att.bgintense; } } - if (EINA_UNLIKELY(local.att.link_id)) - term_link_refcount_inc(ty, local.att.link_id, count); } @@ -1708,6 +1709,9 @@ termpty_cell_codepoint_att_fill(Termpty *ty, Eina_Unicode codepoint, Termcell local = { .codepoint = codepoint, .att = att }; int i; + if (EINA_UNLIKELY(local.att.link_id)) + term_link_refcount_inc(ty, local.att.link_id, n); + for (i = 0; i < n; i++) { HANDLE_BLOCK_CODEPOINT_OVERWRITE(ty, dst[i].codepoint, codepoint); @@ -1716,8 +1720,6 @@ termpty_cell_codepoint_att_fill(Termpty *ty, Eina_Unicode codepoint, dst[i] = local; } - if (EINA_UNLIKELY(local.att.link_id)) - term_link_refcount_inc(ty, local.att.link_id, n); } Config * diff --git a/src/bin/termpty.h b/src/bin/termpty.h index 7934dd76..70991a32 100644 --- a/src/bin/termpty.h +++ b/src/bin/termpty.h @@ -74,7 +74,7 @@ struct _Termatt #else unsigned short bit_padding : 12; #endif - uint16_t link_id; + uint16_t link_id; }; struct _Termpty @@ -311,10 +311,10 @@ do { \ HANDLE_BLOCK_CODEPOINT_OVERWRITE(Tpty, \ (Tdst)[__i].codepoint, \ (Tsrc)[__i].codepoint); \ - if (EINA_UNLIKELY((Tsrc)[__i].att.link_id)) \ - term_link_refcount_dec(ty, (Tsrc)[__i].att.link_id, 1); \ if (EINA_UNLIKELY((Tdst)[__i].att.link_id)) \ - term_link_refcount_inc(ty, (Tdst)[__i].att.link_id, 1); \ + term_link_refcount_dec(ty, (Tdst)[__i].att.link_id, 1); \ + if (EINA_UNLIKELY((Tsrc)[__i].att.link_id)) \ + term_link_refcount_inc(ty, (Tsrc)[__i].att.link_id, 1); \ } \ memcpy(Tdst, Tsrc, N * sizeof(Termcell)); \ } while (0) @@ -340,6 +340,25 @@ term_link_refcount_dec(Termpty *ty, uint16_t link_id, uint16_t count) term_link_free(ty, link); } +static inline Eina_Bool +term_link_eq(Termpty *ty, Term_Link *hl, uint16_t link_id) +{ + Term_Link *hl2; + uint16_t hl_id; + + if (link_id == 0) + return EINA_FALSE; + + hl_id = hl - ty->hl.links; + if (hl_id == link_id) + return EINA_TRUE; + hl2 = &ty->hl.links[link_id]; + if (!hl->key || !hl2->key || + strcmp(hl->key, hl2->key) != 0) + return EINA_FALSE; + return EINA_TRUE; +} + static inline void termpty_cell_fill(Termpty *ty, Termcell *src, Termcell *dst, int n) { @@ -356,7 +375,7 @@ termpty_cell_fill(Termpty *ty, Termcell *src, Termcell *dst, int n) dst[i] = src[0]; } if (src[0].att.link_id) - term_link_refcount_inc(ty, dst[i].att.link_id, n); + term_link_refcount_inc(ty, src[0].att.link_id, n); } else { diff --git a/src/bin/termptyesc.c b/src/bin/termptyesc.c index a182a851..190da61d 100644 --- a/src/bin/termptyesc.c +++ b/src/bin/termptyesc.c @@ -1852,7 +1852,7 @@ _handle_hyperlink(Termpty *ty, s += 3; len -= 3; - key = strndup(s, end_param - s); + key = eina_stringshare_add_length(s, end_param - s); } len -= end_param - s; s = end_param; @@ -1868,7 +1868,7 @@ _handle_hyperlink(Termpty *ty, s++; len--; - url = strndup(s, len); + url = eina_stringshare_add_length(s, len); if (!url) goto end; @@ -1885,8 +1885,8 @@ _handle_hyperlink(Termpty *ty, end: term_link_free(ty, hl); - free(url); - free(key); + eina_stringshare_del(url); + eina_stringshare_del(key); } static void diff --git a/src/bin/termptyops.c b/src/bin/termptyops.c index 3140a78c..c762f050 100644 --- a/src/bin/termptyops.c +++ b/src/bin/termptyops.c @@ -319,7 +319,7 @@ termpty_clear_backlog(Termpty *ty) { size_t i; for (i = 0; i < ty->backsize; i++) - termpty_save_free(&ty->back[i]); + termpty_save_free(ty, &ty->back[i]); free(ty->back); ty->back = NULL; } @@ -421,7 +421,6 @@ termpty_reset_att(Termatt *att) att->framed = 0; att->encircled = 0; att->overlined = 0; - att->link_id = 0; } void @@ -442,6 +441,7 @@ termpty_soft_reset_state(Termpty *ty) ty->termstate.had_cr_y = 0; ty->termstate.restrict_cursor = 0; termpty_reset_att(&(ty->termstate.att)); + ty->termstate.att.link_id = 0; ty->termstate.charset = 0; ty->termstate.charsetch = 'B'; ty->termstate.chset[0] = 'B'; diff --git a/src/bin/termptysave.c b/src/bin/termptysave.c index 56257fd4..afb9db4d 100644 --- a/src/bin/termptysave.c +++ b/src/bin/termptysave.c @@ -3,11 +3,6 @@ #include "termpty.h" #include "termptysave.h" -static void -_ts_free(void *ptr) -{ - free(ptr); -} static int ts_comp = 0; static int ts_uncomp = 0; @@ -38,9 +33,9 @@ termpty_save_extract(Termsave *ts) } Termsave * -termpty_save_new(Termsave *ts, int w) +termpty_save_new(Termpty *ty, Termsave *ts, int w) { - termpty_save_free(ts); + termpty_save_free(ty, ts); Termcell *cells = calloc(1, w * sizeof(Termcell)); if (!cells ) return NULL; @@ -50,7 +45,7 @@ termpty_save_new(Termsave *ts, int w) } Termsave * -termpty_save_expand(Termsave *ts, Termcell *cells, size_t delta) +termpty_save_expand(Termpty *ty, Termsave *ts, Termcell *cells, size_t delta) { Termcell *newcells; @@ -58,20 +53,29 @@ termpty_save_expand(Termsave *ts, Termcell *cells, size_t delta) if (!newcells) return NULL; - memcpy(&newcells[ts->w], cells, delta * sizeof(Termcell)); + memset(newcells + ts->w, + 0, delta * sizeof(Termcell)); + TERMPTY_CELL_COPY(ty, cells, &newcells[ts->w], (int)delta); + ts->w += delta; ts->cells = newcells; return ts; } void -termpty_save_free(Termsave *ts) +termpty_save_free(Termpty *ty, Termsave *ts) { + unsigned int i; if (!ts) return; if (ts->comp) ts_comp--; else ts_uncomp--; ts_freeops++; - _ts_free(ts->cells); + for (i = 0; i < ts->w; i++) + { + if (EINA_UNLIKELY(ts->cells[i].att.link_id)) + term_link_refcount_dec(ty, ts->cells[i].att.link_id, 1); + } + free(ts->cells); ts->cells = NULL; ts->w = 0; } diff --git a/src/bin/termptysave.h b/src/bin/termptysave.h index b285bca2..91132ef2 100644 --- a/src/bin/termptysave.h +++ b/src/bin/termptysave.h @@ -4,8 +4,9 @@ void termpty_save_register(Termpty *ty); void termpty_save_unregister(Termpty *ty); Termsave *termpty_save_extract(Termsave *ts); -Termsave *termpty_save_new(Termsave *ts, int w); -void termpty_save_free(Termsave *ts); -Termsave *termpty_save_expand(Termsave *ts, Termcell *cells, size_t delta); +Termsave *termpty_save_new(Termpty *ty, Termsave *ts, int w); +void termpty_save_free(Termpty *ty, Termsave *ts); +Termsave *termpty_save_expand(Termpty *ty, Termsave *ts, + Termcell *cells, size_t delta); #endif