#include #include #include #include #include namespace raleigh { void entry::set_contents(const char *s) { const uint32_t l = strlen(s); const uint32_t cl = l > rows * cols - 1 ? rows * cols - 1 : l; blockcpy(data, s, cl); data[cl] = '\0'; line_indices[0] = 0; get_indices(0, 0, 0); cur_x = end_x; cur_y = end_y; cur_d = end_d; next_paint_full = true; if (w) w->notify_needs_paint(*this); } entry::entry(uint32_t rows, uint32_t cols, const char *default_text, const char *font, _pixel_t bg, _pixel_t fg, _pixel_t border_color) : rows(rows), cols(cols), bg(bg), fg(fg), border_color(border_color), fi(get_font(font)), data(new char[rows * cols]), line_indices(new uint32_t[rows + 1]), has_focus(false) { size = coord(fi->space_width * (cols - 1) + fi->char_width + 6, fi->space_height * (rows - 1) + fi->char_height + 6); closest_opaque = this; set_contents(default_text); } void entry::get_indices(uint32_t from_y, uint32_t from_x, uint32_t from_d) { while (1) { const uint32_t ln = str_find_any(data + from_d, " \n"); bool quit_after = !data[from_d + ln]; if ((from_x + ln <= cols) || (ln >= cols)) { from_x += ln; from_d += ln; if (data[from_d] == '\n') { ++from_y; from_x = 0; line_indices[from_y] = from_d + 1; } else ++from_x; ++from_d; if (from_x >= cols) { ++from_y; from_x = 0; line_indices[from_y] = from_d; } if (quit_after) break; } else { ++from_y; from_x = 0; line_indices[from_y] = from_d; } } line_indices[from_y + 1] = from_d; end_y = from_y; end_x = from_x; end_d = from_d; } void entry::notify_window_change() {} void entry::paint_text(_pixel_t *pixbuf, uint32_t pitch) { for (uint32_t y = 0; y <= end_y; ++y) { const char *const line = data + line_indices[y]; const uint32_t len = line_indices[y + 1] - line_indices[y] >= cols ? cols : line_indices[y + 1] - line_indices[y]; for (uint32_t x = 0; x < len; ++x) if ((line[x] != '\n') && line[x]) put_char_no_bg(fi, line[x], pixbuf + (window_offset.y + 3 + y * fi->space_height) * pitch + (window_offset.x + 3 + x * fi->space_width), pitch, fg); } } void entry::paint(_pixel_t *pixbuf, uint32_t pitch) { _pixel_t *const cur_ptr = pixbuf + (window_offset.y + 3 + cur_y * fi->space_height) * pitch + (window_offset.x + 3 + cur_x * fi->space_width); _pixel_t *const old_cur_ptr = pixbuf + (window_offset.y + 3 + cur_y_last_paint * fi->space_height) * pitch + (window_offset.x + 3 + cur_x_last_paint * fi->space_width); if (next_paint_full) { next_paint_full = false; for (uint32_t x = 0; x < size.x; ++x) { pixbuf[(window_offset.y) * pitch + window_offset.x + x] = border_color; pixbuf[(window_offset.y + size.y - 1) * pitch + window_offset.x + x] = border_color; } for (uint32_t y = 1; y < size.y - 1; ++y) { pixbuf[(window_offset.y + y) * pitch + window_offset.x] = border_color; pixbuf[(window_offset.y + y) * pitch + window_offset.x + size.x - 1] = border_color; } for (uint32_t y = 1; y < size.y - 1; ++y) for (uint32_t x = 1; x < size.x - 1; ++x) pixbuf[(window_offset.y + y) * pitch + (window_offset.x + x)] = bg; paint_text(pixbuf, pitch); } else if (text_changed_since_last_paint) { for (uint32_t y = 3; y < size.y - 3; ++y) for (uint32_t x = 3; x < size.x - 3; ++x) pixbuf[(window_offset.y + y) * pitch + (window_offset.x + x)] = bg; paint_text(pixbuf, pitch); text_changed_since_last_paint = false; } else if (had_focus_last_paint) { for (uint32_t y = 0; y < fi->char_height; ++y) { old_cur_ptr[y * pitch] = bg; old_cur_ptr[y * pitch + 1] = bg; } if (data[cur_d_last_paint] && (data[cur_d_last_paint] != '\n') && (cur_d_last_paint < line_indices[cur_y_last_paint + 1])) put_char_no_bg(fi, data[cur_d_last_paint], old_cur_ptr, pitch, fg); } if (has_focus) for (uint32_t y = 0; y < fi->char_height; ++y) { cur_ptr[y * pitch] = fg; cur_ptr[y * pitch + 1] = fg; } cur_y_last_paint = cur_y; cur_x_last_paint = cur_x; cur_d_last_paint = cur_d; had_focus_last_paint = has_focus; } void entry::ensure_cursor_in_line() { cur_d = line_indices[cur_y] + cur_x; if (cur_d >= line_indices[cur_y + 1]) { cur_d = line_indices[cur_y + 1] - 1; if (data[cur_d] && (data[cur_d] != '\n')) ++cur_d; cur_x = cur_d - line_indices[cur_y]; } } void entry::handle_click(coord window_coords, enum mouse_packet::mouse_button click_type, bool up) { if (up || (click_type != mouse_packet::LEFT)) return; if (window_coords.x - window_offset.x < 3) window_coords.x = 3 + window_offset.x; else if (window_coords.x - window_offset.x > size.x - 4) window_coords.x = size.x - 4 + window_offset.x; if (window_coords.y - window_offset.y < 3) window_coords.y = 3 + window_offset.y; else if (window_coords.y - window_offset.y > size.y - 4) window_coords.y = size.y - 4 + window_offset.y; cur_y = (window_coords.y - window_offset.y - 3) / fi->space_height; cur_x = (window_coords.x - window_offset.x - 3) / fi->space_width; if (cur_y > end_y) { cur_y = end_y; cur_x = end_x; } ensure_cursor_in_line(); if (has_focus) w->notify_needs_paint(*this); else w->focus(*this); } void entry::notify_has_opaque_parent(widget *parent) {} bool entry::cursor_left() { if (cur_x) { --cur_x; --cur_d; return true; } if (!cur_y) return false; --cur_y; cur_x = line_indices[cur_y + 1] - line_indices[cur_y] - 1; if (cur_x == cols) --cur_x; cur_d = line_indices[cur_y] + cur_x; return true; } bool entry::cursor_right() { if (cur_d >= (end_d - 1)) return false; ++cur_d; ++cur_x; if (cur_d >= line_indices[cur_y + 1]) { cur_x = 0; ++cur_y; cur_d = line_indices[cur_y]; } return true; } bool entry::cursor_up() { if (!cur_y) return false; --cur_y; ensure_cursor_in_line(); return true; } bool entry::cursor_down() { if (cur_y == end_y) return false; ++cur_y; ensure_cursor_in_line(); return true; } void entry::handle_key(struct key_packet kp) { switch (kp.key_id) { case kp.KEY_LEFT_ARROW: if (cursor_left() && has_focus) w->notify_needs_paint(*this); break; case kp.KEY_RIGHT_ARROW: if (cursor_right() && has_focus) w->notify_needs_paint(*this); break; case kp.KEY_DOWN_ARROW: if (cursor_down() && has_focus) w->notify_needs_paint(*this); break; case kp.KEY_UP_ARROW: if (cursor_up() && has_focus) w->notify_needs_paint(*this); break; case kp.KEY_HOME: if (cur_x) { cur_x = 0; if (has_focus) w->notify_needs_paint(*this); } break; case kp.KEY_END: cur_x = cols; ensure_cursor_in_line(); if (has_focus) w->notify_needs_paint(*this); break; default: break; } } void entry::on_focus() { has_focus = true; w->notify_needs_paint(*this); } void entry::on_unfocus() { has_focus = false; w->notify_needs_paint(*this); } const char *entry::get_contents() { return data; } }