express/src/bin/grid.c

1439 lines
37 KiB
C
Raw Normal View History

#include "private.h"
#include "grid.h"
#include "grid_save.h"
#include "colors.h"
#include "utils.h"
#include "config.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 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 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;
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
_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->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_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(sd->o_event, EVAS_CALLBACK_KEY_DOWN,
_cb_key_down, obj);
}
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;
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);
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, 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
}
}
}
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)
{
Evas_Object *obj;
Grid *sd;
obj = data;
if (!(sd = evas_object_smart_data_get(obj))) return EINA_FALSE;
sd->anim = NULL;
_smart_apply(obj);
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, Grid_State *state)
{
state->wrapnext = 0;
if (state->cx >= sd->w) state->cx = sd->w - 1;
if (state->cy >= sd->h) state->cy = sd->h - 1;
if (state->had_cr_x >= sd->w) state->had_cr_x = sd->w - 1;
if (state->had_cr_y >= sd->h) state->had_cr_y = sd->h - 1;
}
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 EINA_UNUSED, int end_y EINA_UNUSED)
{
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)) */
/* { */
/* } */
/* } */
}
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);
}
}
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;
sd->state.cx += 8;
sd->state.cx = (sd->state.cx / 8) * 8;
if (sd->state.cx >= sd->w)
sd->state.cx = (sd->w - 1);
break;
default:
break;
}
}
static void
_text_append(Grid *sd, const Eina_Unicode *codepoints, int len)
{
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_FALSE);
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 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;
}
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;
return;
}
}
}
}
static int
_seq_handle(Grid *sd, Eina_Unicode *c, Eina_Unicode *ce)
{
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 = (int *)c;
while ((cc < ce) && (*cc >= 0x20) && (*cc != 0x7f))
{
cc++;
len++;
}
_text_append(sd, c, len);
sd->state.had_cr = 0;
return len;
}
static void
_buffer_handle(Grid *sd, const Eina_Unicode *codepoints, int len)
{
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);
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);
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;
_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 = nh;
/* sd->state.cy = (new_cy + nh - sd->circular_offset) % nh; */
if (altbuf) _cells_swap(sd);
_limit_coord(sd, &(sd->state));
_limit_coord(sd, &(sd->swap));
_limit_coord(sd, &(sd->save));
_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)
{
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);
_smart_queue_update(obj, sd);
}