+ char *text;
+
+ assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
+ (entry->preedit_text ? strlen(entry->preedit_text) : 0));
+
+ if (!entry->preedit_text) {
+ text_layout_set_text(entry->layout, entry->text);
+ return;
+ }
+
+ text = malloc(strlen(entry->text) + strlen(entry->preedit_text) + 1);
+ strncpy(text, entry->text, entry->cursor);
+ strcpy(text + entry->cursor, entry->preedit_text);
+ strcpy(text + entry->cursor + strlen(entry->preedit_text),
+ entry->text + entry->cursor);
+
+ text_layout_set_text(entry->layout, text);
+ free(text);
+
+ widget_schedule_redraw(entry->widget);
+
+ text_model_set_surrounding_text(entry->model,
+ entry->text,
+ entry->cursor,
+ entry->anchor);
+}
+
+static void
+text_entry_insert_at_cursor(struct text_entry *entry, const char *text)
+{
+ char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
+
+ strncpy(new_text, entry->text, entry->cursor);
+ strcpy(new_text + entry->cursor, text);
+ strcpy(new_text + entry->cursor + strlen(text),
+ entry->text + entry->cursor);
+
+ free(entry->text);
+ entry->text = new_text;
+ entry->cursor += strlen(text);
+ entry->anchor += strlen(text);
+
+ text_entry_update_layout(entry);
+}
+
+static void
+text_entry_set_preedit(struct text_entry *entry,
+ const char *preedit_text,
+ int preedit_cursor)
+{
+ if (entry->preedit_text) {
+ free(entry->preedit_text);
+ entry->preedit_text = NULL;
+ entry->preedit_cursor = 0;
+ }
+
+ if (!preedit_text)
+ return;
+
+ entry->preedit_text = strdup(preedit_text);
+ entry->preedit_cursor = preedit_cursor;
+
+ text_entry_update_layout(entry);
+}
+
+static void
+text_entry_set_cursor_position(struct text_entry *entry,
+ int32_t x, int32_t y)
+{
+ entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
+
+ text_model_reset(entry->model);
+
+ if (entry->cursor >= entry->preedit_cursor) {
+ entry->cursor -= entry->preedit_cursor;
+ }
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_entry_set_anchor_position(struct text_entry *entry,
+ int32_t x, int32_t y)
+{
+ entry->anchor = text_layout_xy_to_index(entry->layout, x, y);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_entry_delete_text(struct text_entry *entry,
+ uint32_t index, uint32_t length)
+{
+ if (entry->cursor > index)
+ entry->cursor -= length;
+
+ entry->anchor = entry->cursor;
+
+ entry->text[index] = '\0';
+ strcat(entry->text, entry->text + index + length);
+
+ text_entry_update_layout(entry);
+
+ widget_schedule_redraw(entry->widget);
+}
+
+static void
+text_entry_delete_selected_text(struct text_entry *entry)
+{
+ uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
+ uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
+
+ if (entry->anchor == entry->cursor)
+ return;
+
+ text_entry_delete_text(entry, start_index, end_index - start_index);
+
+ entry->anchor = entry->cursor;
+}
+
+static void
+text_entry_draw_selection(struct text_entry *entry, cairo_t *cr)
+{
+ cairo_text_extents_t extents;
+ uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
+ uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
+ cairo_rectangle_t start;
+ cairo_rectangle_t end;
+
+ if (entry->anchor == entry->cursor)
+ return;
+
+ text_layout_extents(entry->layout, &extents);
+
+ text_layout_index_to_pos(entry->layout, start_index, &start);
+ text_layout_index_to_pos(entry->layout, end_index, &end);
+
+ cairo_save (cr);
+
+ cairo_set_source_rgba(cr, 0.3, 0.3, 1.0, 0.5);
+ cairo_rectangle(cr,
+ start.x, extents.y_bearing + extents.height + 2,
+ end.x - start.x, -extents.height - 4);
+ cairo_fill(cr);
+
+ cairo_rectangle(cr,
+ start.x, extents.y_bearing + extents.height,
+ end.x - start.x, -extents.height);
+ cairo_clip(cr);
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+ text_layout_draw(entry->layout, cr);
+
+ cairo_restore (cr);
+}
+
+static void
+text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
+{
+ cairo_text_extents_t extents;
+ cairo_rectangle_t cursor_pos;
+
+ text_layout_extents(entry->layout, &extents);
+ text_layout_get_cursor_pos(entry->layout,
+ entry->cursor + entry->preedit_cursor,
+ &cursor_pos);
+
+ cairo_set_line_width(cr, 1.0);
+ cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
+ cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
+ cairo_stroke(cr);
+}
+
+static void
+text_entry_draw_preedit(struct text_entry *entry, cairo_t *cr)
+{
+ cairo_text_extents_t extents;
+ cairo_rectangle_t start;
+ cairo_rectangle_t end;
+
+ if (!entry->preedit_text)
+ return;
+
+ text_layout_extents(entry->layout, &extents);
+
+ text_layout_index_to_pos(entry->layout, entry->cursor, &start);
+ text_layout_index_to_pos(entry->layout,
+ entry->cursor + strlen(entry->preedit_text),
+ &end);
+
+ cairo_save (cr);
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+ cairo_rectangle(cr,
+ start.x, 0,
+ end.x - start.x, 1);
+ cairo_fill(cr);
+
+ cairo_restore (cr);
+}
+
+static const int text_offset_left = 10;
+
+static void
+text_entry_redraw_handler(struct widget *widget, void *data)
+{
+ struct text_entry *entry = data;
+ cairo_surface_t *surface;
+ struct rectangle allocation;
+ cairo_t *cr;
+
+ surface = window_get_surface(entry->window);
+ widget_get_allocation(entry->widget, &allocation);
+
+ cr = cairo_create(surface);
+ cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_source_rgba(cr, 1, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+
+ if (entry->active) {
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_set_line_width (cr, 3);
+ cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
+ cairo_stroke(cr);
+ }
+
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+
+ cairo_translate(cr, text_offset_left, allocation.height / 2);
+ text_layout_draw(entry->layout, cr);
+
+ text_entry_draw_selection(entry, cr);
+
+ text_entry_draw_cursor(entry, cr);
+
+ text_entry_draw_preedit(entry, cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+static int
+text_entry_motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct text_entry *entry = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(entry->widget, &allocation);
+
+ text_entry_set_cursor_position(entry,
+ x - allocation.x - text_offset_left,
+ y - allocation.y - text_offset_left);
+
+ return CURSOR_IBEAM;
+}
+
+static void
+text_entry_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ struct text_entry *entry = data;