#include #include #include #include #include "btk-cell.h" #include "btk-log.h" #include "btk-cell-render.c" void btk_cell_set_button(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, char *label, void (*func)(void)) { btk_cell_set_empty(c, cx, cy, cw, 1); c->group = BTK_CELL_GROUP_DYNAMIC; c->type = BTK_CELL_TYPE_BUTTON; c->lh = 1; c->label = label; NEW_PP(btk_pp_button_t, c->pp); PP_BUTTON(c)->func = func; } void btk_cell_set_editor(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, unsigned int ch, char *text, unsigned int text_w) { btk_cell_set_empty(c, cx, cy, cw, ch); c->group = BTK_CELL_GROUP_FIELD; c->type = BTK_CELL_TYPE_EDITOR; c->lh = 0; c->label = "no data"; NEW_PP(btk_pp_editor_t, c->pp); PP_EDITOR(c)->text = text; PP_EDITOR(c)->text_w = text_w; PP_EDITOR(c)->caret = 0; PP_EDITOR(c)->caret_2d.x = 0; PP_EDITOR(c)->caret_2d.y = 0; PP_EDITOR(c)->scroll = 0; PP_EDITOR(c)->par = NULL; } void btk_cell_set_empty(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, unsigned int ch) { c->group = BTK_CELL_GROUP_PASSIVE; c->type = BTK_CELL_TYPE_EMPTY; c->state = BTK_CELL_STATE_INITIAL; c->ca.x = cx; c->ca.y = cy; c->ca.w = cw; c->ca.h = ch; c->lh = 0; c->pp = NULL; c->fd = NULL; c->label = NULL; } void btk_cell_set_mark(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, int justify, char *label) { btk_cell_set_empty(c, cx, cy, cw, 1); c->type = BTK_CELL_TYPE_MARK; c->lh = 1; c->label = label; NEW_PP(btk_pp_mark_t, c->pp); PP_MARK(c)->justify = justify; } void btk_cell_set_input(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, char *text, unsigned int text_w) { btk_cell_set_empty(c, cx, cy, cw, 1); c->group = BTK_CELL_GROUP_DYNAMIC; c->type = BTK_CELL_TYPE_INPUT; c->lh = 1; c->label = "-/-"; NEW_PP(btk_pp_input_t, c->pp); PP_INPUT(c)->text = text; PP_INPUT(c)->text_w = text_w; PP_INPUT(c)->caret = 0; PP_INPUT(c)->scroll = 0; } void btk_cell_set_list(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, unsigned int ch, char **items, unsigned int *items_n, unsigned int items_w, int **filter, int **order, int **spot, void (*func_trigger)(int), void (*func_sel)(int)) { btk_cell_set_empty(c, cx, cy, cw, ch); c->group = BTK_CELL_GROUP_FIELD; c->type = BTK_CELL_TYPE_LIST; c->label = "no data"; NEW_PP(btk_pp_list_t, c->pp); PP_LIST(c)->items = items; PP_LIST(c)->items_n = items_n; PP_LIST(c)->items_w = items_w; PP_LIST(c)->item_sel = -1; PP_LIST(c)->filter = filter; PP_LIST(c)->order = order; PP_LIST(c)->spot = spot; PP_LIST(c)->scroll = 0; PP_LIST(c)->func_trigger = func_trigger; PP_LIST(c)->func_sel = func_sel; } void btk_cell_set_switch(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, char *label, void (*func)(int, btk_arg_t), btk_arg_t func_args) { btk_cell_set_empty(c, cx, cy, cw, 1); c->group = BTK_CELL_GROUP_DYNAMIC; c->type = BTK_CELL_TYPE_SWITCH; c->lh = 1; c->label = label; NEW_PP(btk_pp_switch_t, c->pp); PP_SWITCH(c)->func = func; PP_SWITCH(c)->func_args = func_args; } void btk_cell_set_table(btk_cell_t *c, unsigned int cx, unsigned int cy, unsigned int cw, unsigned int ch, char *header, /* must be same item_w as items */ char **items, unsigned int items_w, unsigned int cols_n, unsigned int *rows_n, int *cols_ena, unsigned int *cols_cw, unsigned int sc, int **filter, int **order, int **spot, void (*func_trigger)(int), void (*func_sel)(int)) { btk_cell_set_empty(c, cx, cy, cw, ch); c->group = BTK_CELL_GROUP_FIELD; c->type = BTK_CELL_TYPE_TABLE; c->label = "no data"; NEW_PP(btk_pp_table_t, c->pp); PP_TABLE(c)->header = header; PP_TABLE(c)->items = items; PP_TABLE(c)->items_w = items_w; PP_TABLE(c)->rows_n = rows_n; PP_TABLE(c)->cols_n = cols_n; PP_TABLE(c)->row_sel = -1; PP_TABLE(c)->cols_ena = cols_ena; PP_TABLE(c)->cols_cw = cols_cw; PP_TABLE(c)->sc = sc; PP_TABLE(c)->tw = 0; PP_TABLE(c)->filter = filter; PP_TABLE(c)->order = order; PP_TABLE(c)->spot = spot; PP_TABLE(c)->scroll.x = 0; PP_TABLE(c)->scroll.y = 0; PP_TABLE(c)->func_trigger = func_trigger; PP_TABLE(c)->func_sel = func_sel; PP_TABLE(c)->cols_pw = (unsigned int*)malloc(cols_n * sizeof(int)); PP_TABLE(c)->cols_px = (unsigned int*)malloc(cols_n * sizeof(int)); } /****************************************************************************************/ void btk_cell_button_trigger(btk_cell_t *c, int button) { if (button == 1 && PP_BUTTON(c)->func) PP_BUTTON(c)->func(); } void btk_cell_destroy(btk_cell_t *c) { if (c->pp) { switch (c->type) { case BTK_CELL_TYPE_TABLE: free(PP_TABLE(c)->cols_pw); free(PP_TABLE(c)->cols_px); break; case BTK_CELL_TYPE_EDITOR: btk_text_destroy_par(PP_EDITOR(c)->par); break; default: break; } free(c->pp); } free(c); } void btk_cell_draw(btk_cell_t *c, cairo_t *c_ctx) { if (!c) return; if ((c->state & BTK_CELL_STATE_HIDDEN)) return; switch(c->type) { case BTK_CELL_TYPE_MARK: render_mark(c, c_ctx); break; case BTK_CELL_TYPE_BUTTON: render_button(c, c_ctx); break; case BTK_CELL_TYPE_SWITCH: render_switch(c, c_ctx); break; case BTK_CELL_TYPE_LIST: render_list(c); break; case BTK_CELL_TYPE_TABLE: render_table(c); break; case BTK_CELL_TYPE_INPUT: render_input(c, c_ctx); break; case BTK_CELL_TYPE_EDITOR: render_editor(c); break; default: fill_cell(c, c_ctx, btk_cl_empty); break; }; } void btk_cell_editor_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt) { /* scrolling if appropriate button is clicked */ switch (button) { case 1: break; case 4: PP_EDITOR(c)->scroll -= btk_scroll_speed; return; case 5: PP_EDITOR(c)->scroll += btk_scroll_speed; return; default: return; } /* caret positioning */ if (pt.x < label_offset.x || pt.x >= c->pa.w - label_offset.x || pt.y < label_offset.y - glyph.h) return; PP_EDITOR(c)->caret_2d.x = (pt.x - label_offset.x) / glyph.w; PP_EDITOR(c)->caret_2d.y = (pt.y - label_offset.y + PP_EDITOR(c)->scroll + glyph.h) / (glyph.h + btk_text_spacing); PP_EDITOR(c)->caret_2d = btk_text_guard_2d_caret(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d); PP_EDITOR(c)->caret = btk_text_caret_2dto1d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d); //btk_text_caret_1dto2d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret); } void btk_cell_editor_input_key(btk_cell_t *c, uint32_t key) { btk_pp_editor_t *pp = PP_EDITOR(c); int caret_type, modif = 0; switch (key) { case 0xff08: pp->caret -= btk_text_del_char(pp->text, pp->caret, 0); caret_type = 0; modif = 1; break; case 0xff50: pp->caret_2d.x = 0; caret_type = 1; break; case 0xff51: pp->caret--; caret_type = 0; break; case 0xff52: pp->caret_2d.y--; caret_type = 1; break; case 0xff53: pp->caret++; caret_type = 0; break; case 0xff54: pp->caret_2d.y++; caret_type = 1; break; case 0xff57: pp->caret_2d.x = pp->text_w; caret_type = 1; break; case 0xffff: btk_text_del_char(pp->text, pp->caret, 1); caret_type = 0; modif = 1; break; case 0xff0d: pp->caret += btk_text_insert_char(pp->text, '\n', pp->caret, pp->text_w); caret_type = 0; modif = 1; break; case 0xff09: pp->caret += btk_text_insert_char(pp->text, '\t', pp->caret, pp->text_w); caret_type = 0; modif = 1; break; default : /* only allow ascii printable chars */ if (key < 32 || key >= 127) return; pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w); caret_type = 0; modif = 1; break; } if (modif) { btk_cell_editor_update_text(c); } if (caret_type) { pp->caret_2d = btk_text_guard_2d_caret(pp->par, pp->caret_2d); pp->caret = btk_text_caret_2dto1d(pp->par, pp->caret_2d); } else { pp->caret = btk_text_guard_1d_caret(pp->text, pp->caret); pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret); } /* modify scroll to show caret if needed */ if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll) pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing); if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - 2 * label_offset.y) { pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y; } } void btk_cell_editor_reset_caret(btk_cell_t*c) { PP_EDITOR(c)->caret = 0; PP_EDITOR(c)->caret_2d.x = 0; PP_EDITOR(c)->caret_2d.y = 0; PP_EDITOR(c)->scroll = 0; } void btk_cell_editor_update_text(btk_cell_t *c) { btk_pp_editor_t *pp = PP_EDITOR(c); int max_n = (c->pa.w - 2 * label_offset.x) / glyph.w + 1; if (!pp->text) return; if (pp->par) btk_text_destroy_par(pp->par); pp->par = btk_text_get_par(pp->text, max_n); pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret); /* modify scroll to show caret if needed */ if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll) pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing); if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - label_offset.y) { pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y; } } btk_field_t* btk_cell_field_create(cairo_surface_t *c_srf, btk_area_t a) { btk_field_t *f = (btk_field_t *)malloc(sizeof(btk_field_t)); f->c_srf = cairo_surface_create_for_rectangle(c_srf, a.x, a.y, a.w, a.h); f->c_ctx = cairo_create(f->c_srf); cairo_set_font_options(f->c_ctx, font_opt); cairo_set_font_size(f->c_ctx, btk_font_size); cairo_select_font_face(f->c_ctx, btk_font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); return f; } void btk_cell_field_destroy(btk_field_t *f) { if (!f) return; cairo_destroy(f->c_ctx); cairo_surface_destroy(f->c_srf); free(f); } void btk_cell_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt) { if (button != 1) return; if (pt.x >= 2 * label_offset.x + glyph.w && pt.x < c->pa.w - label_offset.x) PP_INPUT(c)->caret = (pt.x - 2 * label_offset.x - glyph.w) / glyph.w + PP_INPUT(c)->scroll; int text_n = strlen(PP_INPUT(c)->text); if (PP_INPUT(c)->caret < 0) PP_INPUT(c)->caret = 0; if (PP_INPUT(c)->caret > text_n) PP_INPUT(c)->caret = text_n; /* TODO combo clicks selections */ } void btk_cell_input_input_key(btk_cell_t *c, uint32_t key) { btk_pp_input_t *pp = PP_INPUT(c); switch (key) { case 0xff08: pp->caret -= btk_text_del_char(pp->text, pp->caret, 0); break; case 0xff50: pp->caret = 0; break; case 0xff51: pp->caret--; break; case 0xff53: pp->caret++; break; case 0xff57: pp->caret = pp->text_w; break; case 0xffff: btk_text_del_char(pp->text, pp->caret, 1); break; default : /* only allow ascii printable chars */ if (key < 32 || key >= 127) return; pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w); return; } int text_n = strlen(pp->text); if (pp->caret < 0) pp->caret = 0; if (pp->caret > text_n) pp->caret = text_n; } void btk_cell_input_reset_caret(btk_cell_t *c) { PP_INPUT(c)->caret = 0; } void btk_cell_list_deselect(btk_cell_t *c) { PP_LIST(c)->item_sel = -1; } void btk_cell_list_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt) { btk_pp_list_t *pp = PP_LIST(c); int prev_sel = pp->item_sel; /* scrolling if appropriate button is clicked */ switch (button) { case 4: pp->scroll -= btk_scroll_speed; return; case 5: pp->scroll += btk_scroll_speed; return; } /* filter out remaining unwanted inputs */ if (button != 1) return; /* find item under pointer */ pp->item_sel = (pt.y + pp->scroll) / (btk_cp.h + btk_frame); /* deselect if out of bounds */ if (pp->item_sel < 0 || pp->item_sel >= *(pp->items_n)) pp->item_sel = -1; /* adjust scrolling so that select item is displayed in full */ // TODO /* on selection change, exec sel func */ if (prev_sel != pp->item_sel && pp->func_sel) pp->func_sel(pp->item_sel); /* if double click, exec trigger func */ if (combo == 2 && pp->func_trigger) pp->func_trigger(pp->item_sel); } int btk_cell_list_get_sel(btk_cell_t *c) { return PP_LIST(c)->item_sel; } void btk_cell_setup_font(cairo_t *c_ctx) { font_opt = cairo_font_options_create(); cairo_font_options_set_subpixel_order(font_opt, CAIRO_SUBPIXEL_ORDER_DEFAULT); cairo_font_options_set_antialias(font_opt, CAIRO_ANTIALIAS_NONE); cairo_font_options_set_hint_style(font_opt, CAIRO_HINT_STYLE_SLIGHT); cairo_font_options_set_hint_metrics(font_opt, CAIRO_HINT_METRICS_ON); cairo_set_font_options(c_ctx, font_opt); cairo_set_font_size(c_ctx, btk_font_size); cairo_select_font_face(c_ctx, btk_font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_font_extents_t fe; cairo_font_extents(c_ctx, &fe); glyph.h = fe.height; glyph.w = fe.max_x_advance; label_offset.x = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.x; label_offset.y = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.y + fe.ascent; } void btk_cell_switch_toggle(btk_cell_t *c, int button) { if (button != 1) return; c->state ^= BTK_CELL_STATE_ON; if (PP_SWITCH(c)->func) PP_SWITCH(c)->func(((c->state & BTK_CELL_STATE_ON) >> 4), PP_SWITCH(c)->func_args); } void btk_cell_table_deselect(btk_cell_t *c) { PP_TABLE(c)->row_sel = -1; } char* btk_cell_table_get_item(btk_cell_t *c, int col, int row) { if (col < 0 || row < 0 || col >= PP_TABLE(c)->cols_n || row >= *(PP_TABLE(c)->rows_n)) return NULL; return *(PP_TABLE(c)->items) + PP_TABLE(c)->items_w * (col + row * PP_TABLE(c)->cols_n); } void btk_cell_table_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt) { btk_pp_table_t *pp = PP_TABLE(c); int prev_sel = pp->row_sel; /* scrolling if appropriate button is clicked */ switch (button) { case 4: pp->scroll.y -= btk_scroll_speed; return; case 5: pp->scroll.y += btk_scroll_speed; return; case 6: pp->scroll.x -= btk_scroll_speed; return; case 7: pp->scroll.x += btk_scroll_speed; return; } /* TODO ugly af block, to redo */ /* if click on header */ if (pt.y < btk_cp.h) { int ena; /* find clicked column */ for (int i = 0; i < pp->cols_n; i++) { ena = pp->cols_ena ? pp->cols_ena[i] : 1; if (ena && pt.x > pp->cols_px[i] && pt.x <= pp->cols_px[i] + pp->cols_pw[i]) { switch (button) { case 1: pp->cols_cw[i]++; break; case 2: /* TODO sorting ? */ break; case 3: pp->cols_cw[i] -= pp->cols_cw[i] > 1 ? 1 : 0; } btk_cell_table_update_geometry(c); break; } } return; } /* remaining item list actions */ /* filter out unwanted inputs */ if (button != 1) return; /* find item under pointer */ pp->row_sel = (pt.y + pp->scroll.y) / (btk_cp.h + btk_frame) -1; /* deselect if out of bounds */ if (pp->row_sel < 0 || pp->row_sel >= *(pp->rows_n)) pp->row_sel = -1; /* on selection change, exec sel func */ if (prev_sel != pp->row_sel && pp->func_sel) pp->func_sel(pp->row_sel); /* if double click, exec trigger func */ if (combo == 2 && pp->func_trigger) pp->func_trigger(pp->row_sel); } void btk_cell_table_update_geometry(btk_cell_t *c) { btk_pp_table_t *pp = PP_TABLE(c); unsigned int cw = 0; /* width in cell units */ unsigned int pw = 0; /* total minimum pixel width */ unsigned int ew = 0; /* extra width if tw > minimum width */ int ena; /* calculate extra width in pixels */ for (int i = 0; i < pp->cols_n; i++) { ena = pp->cols_ena ? pp->cols_ena[i] : 1; cw += ena ? pp->cols_cw[i] : 0; } pw = cw * (btk_cp.w + btk_frame) - btk_frame; ew = pw < c->pa.w ? c->pa.w - pw : 0; pp->tw = pw + ew; /* calculate each columns widths in pixels */ for (int i = 0; i < pp->cols_n; i++) { pp->cols_pw[i] = pp->cols_cw[i] * (btk_cp.w + btk_frame) - btk_frame; pp->cols_pw[i] += pp->sc == i ? ew : 0; } /* calculate columns positions */ int pos = 0; for (int i = 0; i < pp->cols_n; i++) { pp->cols_px[i] = pos; ena = pp->cols_ena ? pp->cols_ena[i] : 1; pos += ena ? pp->cols_pw[i] + btk_frame : 0; } }