express/src/bin/grid.c

2546 lines
69 KiB
C

#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;
}