2546 lines
69 KiB
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;
|
|
}
|