editor.c 38.5 KB
Newer Older
1 2
/*
 * Copyright © 2012 Openismus GmbH
3
 * Copyright © 2012 Intel Corporation
4
 *
5 6 7 8 9 10
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
11
 *
12 13 14 15 16 17 18 19 20 21 22
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
23 24
 */

25 26
#include "config.h"

27
#include <assert.h>
28 29 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Jan Arne Petersen's avatar
Jan Arne Petersen committed
31
#include <stdbool.h>
32
#include <unistd.h>
33 34 35 36

#include <linux/input.h>
#include <cairo.h>

37 38
#include <pango/pangocairo.h>

39 40 41 42 43
#include "window.h"
#include "text-client-protocol.h"

struct text_entry {
	struct widget *widget;
44
	struct window *window;
45 46
	char *text;
	int active;
47
	uint32_t cursor;
48
	uint32_t anchor;
49 50 51 52
	struct {
		char *text;
		int32_t cursor;
		char *commit;
53
		PangoAttrList *attr_list;
54 55
	} preedit;
	struct {
56
		PangoAttrList *attr_list;
57 58
		int32_t cursor;
	} preedit_info;
59 60 61
	struct {
		int32_t cursor;
		int32_t anchor;
62 63
		uint32_t delete_index;
		uint32_t delete_length;
64
		bool invalid_delete;
65
	} pending_commit;
66
	struct wl_text_input *text_input;
67
	PangoLayout *layout;
68 69 70
	struct {
		xkb_mod_mask_t shift_mask;
	} keysym;
71
	uint32_t serial;
Jan Arne Petersen's avatar
Jan Arne Petersen committed
72
	uint32_t reset_serial;
73
	uint32_t content_purpose;
74
	uint32_t click_to_show;
75
	char *preferred_language;
Jan Arne Petersen's avatar
Jan Arne Petersen committed
76
	bool button_pressed;
77 78 79
};

struct editor {
80
	struct wl_text_input_manager *text_input_manager;
81 82
	struct wl_data_source *selection;
	char *selected_text;
83 84 85 86 87
	struct display *display;
	struct window *window;
	struct widget *widget;
	struct text_entry *entry;
	struct text_entry *editor;
88
	struct text_entry *active_entry;
89 90
};

91
static const char *
92
utf8_end_char(const char *p)
93
{
94 95 96
	while ((*p & 0xc0) == 0x80)
		p++;
	return p;
97 98 99
}

static const char *
100
utf8_prev_char(const char *s, const char *p)
101
{
102 103 104 105
	for (--p; p >= s; --p) {
		if ((*p & 0xc0) != 0x80)
			return p;
	}
106 107 108 109 110 111 112 113 114 115 116
	return NULL;
}

static const char *
utf8_next_char(const char *p)
{
	if (*p != 0)
		return utf8_end_char(++p);
	return NULL;
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
static void
move_up(const char *p, uint32_t *cursor)
{
	const char *posr, *posr_i;
	char text[16];

	xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));

	posr = strstr(p, text);
	while (posr) {
		if (*cursor > (unsigned)(posr-p)) {
			posr_i = strstr(posr+1, text);
			if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
				*cursor = posr-p;
				break;
			}
			posr = posr_i;
		} else {
			break;
		}
	}
}

static void
move_down(const char *p, uint32_t *cursor)
{
	const char *posr;
	char text[16];

	xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));

	posr = strstr(p, text);
	while (posr) {
		if (*cursor <= (unsigned)(posr-p)) {
			*cursor = posr-p + 1;
			break;
		}
		posr = strstr(posr+1, text);
	}
}

158 159 160 161 162
static void text_entry_redraw_handler(struct widget *widget, void *data);
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);
163 164 165
static void text_entry_touch_handler(struct widget *widget, struct input *input,
				     uint32_t serial, uint32_t time, int32_t id,
				     float tx, float ty, void *data);
Jan Arne Petersen's avatar
Jan Arne Petersen committed
166 167 168
static int text_entry_motion_handler(struct widget *widget,
				     struct input *input, uint32_t time,
				     float x, float y, void *data);
169 170
static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
					int32_t cursor, int32_t anchor);
171 172 173
static void text_entry_set_preedit(struct text_entry *entry,
				   const char *preedit_text,
				   int preedit_cursor);
174 175
static void text_entry_delete_text(struct text_entry *entry,
				   uint32_t index, uint32_t length);
176
static void text_entry_delete_selected_text(struct text_entry *entry);
177 178
static void text_entry_reset_preedit(struct text_entry *entry);
static void text_entry_commit_and_reset(struct text_entry *entry);
179 180
static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
static void text_entry_update(struct text_entry *entry);
181 182

static void
183
text_input_commit_string(void *data,
184
			 struct wl_text_input *text_input,
185
			 uint32_t serial,
186
			 const char *text)
187 188 189
{
	struct text_entry *entry = data;

Jan Arne Petersen's avatar
Jan Arne Petersen committed
190 191 192 193 194 195
	if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
		fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
			serial, entry->serial, entry->reset_serial);
		return;
	}

196 197 198 199 200
	if (entry->pending_commit.invalid_delete) {
		fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
		memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
		return;
	}
201

202
	text_entry_reset_preedit(entry);
203 204 205 206 207

	if (entry->pending_commit.delete_length) {
		text_entry_delete_text(entry,
				       entry->pending_commit.delete_index,
				       entry->pending_commit.delete_length);
208 209
	} else {
		text_entry_delete_selected_text(entry);
210 211
	}

212 213 214 215 216
	text_entry_insert_at_cursor(entry, text,
				    entry->pending_commit.cursor,
				    entry->pending_commit.anchor);

	memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
217 218 219 220

	widget_schedule_redraw(entry->widget);
}

221 222 223 224 225 226 227 228 229 230 231 232 233
static void
clear_pending_preedit(struct text_entry *entry)
{
	memset(&entry->pending_commit, 0, sizeof entry->pending_commit);

	pango_attr_list_unref(entry->preedit_info.attr_list);

	entry->preedit_info.cursor = 0;
	entry->preedit_info.attr_list = NULL;

	memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
}

234
static void
235
text_input_preedit_string(void *data,
236
			  struct wl_text_input *text_input,
237
			  uint32_t serial,
238
			  const char *text,
239
			  const char *commit)
240
{
241 242
	struct text_entry *entry = data;

243 244 245 246 247 248 249 250 251 252 253 254 255
	if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
		fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
			serial, entry->serial, entry->reset_serial);
		clear_pending_preedit(entry);
		return;
	}

	if (entry->pending_commit.invalid_delete) {
		fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
		clear_pending_preedit(entry);
		return;
	}

256 257 258 259 260 261 262 263
	if (entry->pending_commit.delete_length) {
		text_entry_delete_text(entry,
				       entry->pending_commit.delete_index,
				       entry->pending_commit.delete_length);
	} else {
		text_entry_delete_selected_text(entry);
	}

264
	text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
265
	entry->preedit.commit = strdup(commit);
266
	entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
267

268
	clear_pending_preedit(entry);
269

270
	text_entry_update(entry);
271

272
	widget_schedule_redraw(entry->widget);
273 274
}

275
static void
276
text_input_delete_surrounding_text(void *data,
277
				   struct wl_text_input *text_input,
278 279 280 281
				   int32_t index,
				   uint32_t length)
{
	struct text_entry *entry = data;
282 283 284 285
	uint32_t text_length;

	entry->pending_commit.delete_index = entry->cursor + index;
	entry->pending_commit.delete_length = length;
286
	entry->pending_commit.invalid_delete = false;
287

288
	text_length = strlen(entry->text);
289

290 291
	if (entry->pending_commit.delete_index > text_length ||
	    length > text_length ||
292
	    entry->pending_commit.delete_index + length > text_length) {
293 294 295
		fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
			"length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
		entry->pending_commit.invalid_delete = true;
296 297 298 299
		return;
	}
}

300
static void
301
text_input_cursor_position(void *data,
302
			   struct wl_text_input *text_input,
303 304 305 306 307 308 309 310 311
			   int32_t index,
			   int32_t anchor)
{
	struct text_entry *entry = data;

	entry->pending_commit.cursor = index;
	entry->pending_commit.anchor = anchor;
}

312
static void
313
text_input_preedit_styling(void *data,
314
			   struct wl_text_input *text_input,
315 316 317 318
			   uint32_t index,
			   uint32_t length,
			   uint32_t style)
{
319 320 321 322 323 324 325 326
	struct text_entry *entry = data;
	PangoAttribute *attr1 = NULL;
	PangoAttribute *attr2 = NULL;

	if (!entry->preedit_info.attr_list)
		entry->preedit_info.attr_list = pango_attr_list_new();

	switch (style) {
327 328
		case WL_TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
		case WL_TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
329 330
			attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
			break;
331
		case WL_TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
332 333 334
			attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
			attr2 = pango_attr_underline_color_new(65535, 0, 0);
			break;
335
		case WL_TEXT_INPUT_PREEDIT_STYLE_SELECTION:
336 337 338
			attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
			attr2 = pango_attr_foreground_new(65535, 65535, 65535);
			break;
339 340
		case WL_TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
		case WL_TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
341 342 343
			attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
			attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
			break;
344
		case WL_TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
			attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
			attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
			break;
	}

	if (attr1) {
		attr1->start_index = entry->cursor + index;
		attr1->end_index = entry->cursor + index + length;
		pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
	}

	if (attr2) {
		attr2->start_index = entry->cursor + index;
		attr2->end_index = entry->cursor + index + length;
		pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
	}
361 362 363
}

static void
364
text_input_preedit_cursor(void *data,
365
			  struct wl_text_input *text_input,
366
			  int32_t index)
367
{
368 369 370
	struct text_entry *entry = data;

	entry->preedit_info.cursor = index;
371 372 373
}

static void
374
text_input_modifiers_map(void *data,
375
			 struct wl_text_input *text_input,
376 377
			 struct wl_array *map)
{
378 379 380
	struct text_entry *entry = data;

	entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
381 382 383
}

static void
384
text_input_keysym(void *data,
385
		  struct wl_text_input *text_input,
386 387 388 389 390
		  uint32_t serial,
		  uint32_t time,
		  uint32_t key,
		  uint32_t state,
		  uint32_t modifiers)
391
{
392
	struct text_entry *entry = data;
393
	const char *new_char;
394

395 396 397 398 399 400 401 402 403 404 405 406 407 408
	if (key == XKB_KEY_Left ||
	    key == XKB_KEY_Right) {
		if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
			return;

		if (key == XKB_KEY_Left)
			new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
		else
			new_char = utf8_next_char(entry->text + entry->cursor);

		if (new_char != NULL) {
			entry->cursor = new_char - entry->text;
		}

409 410 411 412
		if (!(modifiers & entry->keysym.shift_mask))
			entry->anchor = entry->cursor;
		widget_schedule_redraw(entry->widget);

413 414 415
		return;
	}

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
	if (key == XKB_KEY_Up ||
	    key == XKB_KEY_Down) {
		if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
			return;

		if (key == XKB_KEY_Up)
			move_up(entry->text, &entry->cursor);
		else
			move_down(entry->text, &entry->cursor);

		if (!(modifiers & entry->keysym.shift_mask))
			entry->anchor = entry->cursor;
		widget_schedule_redraw(entry->widget);

		return;
	}

433 434 435
	if (key == XKB_KEY_BackSpace) {
		const char *start, *end;

436 437 438
		if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
			return;

439 440 441 442 443 444
		text_entry_commit_and_reset(entry);

		start = utf8_prev_char(entry->text, entry->text + entry->cursor);
		if (start == NULL)
			return;

445 446
		end = utf8_next_char(start);

447 448 449 450 451 452 453
		text_entry_delete_text(entry,
				       start - entry->text,
				       end - start);

		return;
	}

454 455 456 457
	if (key == XKB_KEY_Tab ||
	    key == XKB_KEY_KP_Enter ||
	    key == XKB_KEY_Return) {
		char text[16];
458

459 460 461 462 463 464 465 466 467
		if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
			return;

		xkb_keysym_to_utf8(key, text, sizeof(text));

		text_entry_insert_at_cursor(entry, text, 0, 0);

		return;
	}
468 469
}

470
static void
471
text_input_enter(void *data,
472
		 struct wl_text_input *text_input,
473
		 struct wl_surface *surface)
474 475 476
{
	struct text_entry *entry = data;

477 478 479
	if (surface != window_get_wl_surface(entry->window))
		return;

480
	entry->active++;
481

Jan Arne Petersen's avatar
Jan Arne Petersen committed
482 483 484
	text_entry_update(entry);
	entry->reset_serial = entry->serial;

485 486 487 488
	widget_schedule_redraw(entry->widget);
}

static void
489
text_input_leave(void *data,
490
		 struct wl_text_input *text_input)
491 492 493
{
	struct text_entry *entry = data;

494
	text_entry_commit_and_reset(entry);
495
	entry->active--;
496

497 498
	if (!entry->active)
		wl_text_input_hide_input_panel(text_input);
499

500 501 502
	widget_schedule_redraw(entry->widget);
}

503
static void
504
text_input_input_panel_state(void *data,
505
			     struct wl_text_input *text_input,
506 507 508 509
			     uint32_t state)
{
}

510
static void
511
text_input_language(void *data,
512
		    struct wl_text_input *text_input,
513 514 515
		    uint32_t serial,
		    const char *language)
{
516
	fprintf(stderr, "input language is %s \n", language);
517 518 519
}

static void
520
text_input_text_direction(void *data,
521
			  struct wl_text_input *text_input,
522 523 524
			  uint32_t serial,
			  uint32_t direction)
{
525 526 527 528 529 530
	struct text_entry *entry = data;
	PangoContext *context = pango_layout_get_context(entry->layout);
	PangoDirection pango_direction;


	switch (direction) {
531
		case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
532 533
			pango_direction = PANGO_DIRECTION_LTR;
			break;
534
		case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
535 536
			pango_direction = PANGO_DIRECTION_RTL;
			break;
537
		case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
538 539 540
		default:
			pango_direction = PANGO_DIRECTION_NEUTRAL;
	}
541

542
	pango_context_set_base_dir(context, pango_direction);
543 544
}

545
static const struct wl_text_input_listener text_input_listener = {
546 547 548 549 550 551 552 553 554 555 556 557 558
	text_input_enter,
	text_input_leave,
	text_input_modifiers_map,
	text_input_input_panel_state,
	text_input_preedit_string,
	text_input_preedit_styling,
	text_input_preedit_cursor,
	text_input_commit_string,
	text_input_cursor_position,
	text_input_delete_surrounding_text,
	text_input_keysym,
	text_input_language,
	text_input_text_direction
559 560
};

561 562 563 564 565 566 567 568 569 570 571 572 573
static void
data_source_target(void *data,
		   struct wl_data_source *source, const char *mime_type)
{
}

static void
data_source_send(void *data,
		 struct wl_data_source *source,
		 const char *mime_type, int32_t fd)
{
	struct editor *editor = data;

574 575
	if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
		fprintf(stderr, "write failed: %m\n");
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
}

static void
data_source_cancelled(void *data, struct wl_data_source *source)
{
	wl_data_source_destroy(source);
}

static const struct wl_data_source_listener data_source_listener = {
	data_source_target,
	data_source_send,
	data_source_cancelled
};

static void
paste_func(void *buffer, size_t len,
	   int32_t x, int32_t y, void *data)
{
	struct editor *editor = data;
	struct text_entry *entry = editor->active_entry;
	char *pasted_text;

	if (!entry)
		return;

	pasted_text = malloc(len + 1);
	strncpy(pasted_text, buffer, len);
	pasted_text[len] = '\0';

	text_entry_insert_at_cursor(entry, pasted_text, 0, 0);

	free(pasted_text);
}

static void
editor_copy_cut(struct editor *editor, struct input *input, bool cut)
{
	struct text_entry *entry = editor->active_entry;

	if (!entry)
		return;
Michael Vetter's avatar
Michael Vetter committed
617

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
	if (entry->cursor != entry->anchor) {
		int start_index = MIN(entry->cursor, entry->anchor);
		int end_index = MAX(entry->cursor, entry->anchor);
		int len = end_index - start_index;

		editor->selected_text = realloc(editor->selected_text, len + 1);
		strncpy(editor->selected_text, &entry->text[start_index], len);
		editor->selected_text[len] = '\0';

		if (cut)
			text_entry_delete_text(entry, start_index, len);

		editor->selection =
			display_create_data_source(editor->display);
		wl_data_source_offer(editor->selection,
				     "text/plain;charset=utf-8");
		wl_data_source_add_listener(editor->selection,
					    &data_source_listener, editor);
		input_set_selection(input, editor->selection,
				    display_get_serial(editor->display));
	}
}

static void
editor_paste(struct editor *editor, struct input *input)
{
	input_receive_selection_data(input,
				     "text/plain;charset=utf-8",
				     paste_func, editor);
}

static void
menu_func(void *data, struct input *input, int index)
{
	struct window *window = data;
	struct editor *editor = window_get_user_data(window);

	fprintf(stderr, "picked entry %d\n", index);

	switch (index) {
	case 0:
		editor_copy_cut(editor, input, true);
		break;
	case 1:
		editor_copy_cut(editor, input, false);
		break;
	case 2:
		editor_paste(editor, input);
		break;
	}
}

static void
show_menu(struct editor *editor, struct input *input, uint32_t time)
{
	int32_t x, y;
	static const char *entries[] = {
		"Cut", "Copy", "Paste"
	};

	input_get_position(input, &x, &y);
	window_show_menu(editor->display, input, time, editor->window,
			 x + 10, y + 20, menu_func,
			 entries, ARRAY_LENGTH(entries));
}

684 685 686 687 688
static struct text_entry*
text_entry_create(struct editor *editor, const char *text)
{
	struct text_entry *entry;

689 690
	entry = xmalloc(sizeof *entry);
	memset(entry, 0, sizeof *entry);
691

692
	entry->widget = widget_add_widget(editor->widget, entry);
693
	entry->window = editor->window;
694 695
	entry->text = strdup(text);
	entry->active = 0;
696
	entry->cursor = strlen(text);
697
	entry->anchor = entry->cursor;
698 699
	entry->text_input = wl_text_input_manager_create_text_input(editor->text_input_manager);
	wl_text_input_add_listener(entry->text_input, &text_input_listener, entry);
700

701 702
	widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
	widget_set_button_handler(entry->widget, text_entry_button_handler);
Jan Arne Petersen's avatar
Jan Arne Petersen committed
703
	widget_set_motion_handler(entry->widget, text_entry_motion_handler);
704
	widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
705

706 707 708 709 710 711
	return entry;
}

static void
text_entry_destroy(struct text_entry *entry)
{
712
	widget_destroy(entry->widget);
713
	wl_text_input_destroy(entry->text_input);
714
	g_clear_object(&entry->layout);
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
	free(entry->text);
	free(entry);
}

static void
redraw_handler(struct widget *widget, void *data)
{
	struct editor *editor = data;
	cairo_surface_t *surface;
	struct rectangle allocation;
	cairo_t *cr;

	surface = window_get_surface(editor->window);
	widget_get_allocation(editor->widget, &allocation);

	cr = cairo_create(surface);
	cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
	cairo_clip(cr);

	cairo_translate(cr, allocation.x, allocation.y);

	/* Draw background */
	cairo_push_group(cr);
738
	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
	cairo_set_source_rgba(cr, 1, 1, 1, 1);
	cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
	cairo_fill(cr);

	cairo_pop_group_to_source(cr);
	cairo_paint(cr);

	cairo_destroy(cr);
	cairo_surface_destroy(surface);
}

static void
text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
		    int32_t width, int32_t height)
{
754
	widget_set_allocation(entry->widget, x, y, width, height);
755 756 757 758 759 760 761
}

static void
resize_handler(struct widget *widget,
	       int32_t width, int32_t height, void *data)
{
	struct editor *editor = data;
762
	struct rectangle allocation;
763

764
	widget_get_allocation(editor->widget, &allocation);
765

766 767 768 769 770 771
	text_entry_allocate(editor->entry,
			    allocation.x + 20, allocation.y + 20,
			    width - 40, height / 2 - 40);
	text_entry_allocate(editor->editor,
			    allocation.x + 20, allocation.y + height / 2 + 20,
			    width - 40, height / 2 - 40);
772 773 774
}

static void
775
text_entry_activate(struct text_entry *entry,
776
		    struct wl_seat *seat)
777
{
778 779
	struct wl_surface *surface = window_get_wl_surface(entry->window);

780
	if (entry->click_to_show && entry->active) {
781
		wl_text_input_show_input_panel(entry->text_input);
782 783 784 785 786

		return;
	}

	if (!entry->click_to_show)
787
		wl_text_input_show_input_panel(entry->text_input);
788

789 790 791
	wl_text_input_activate(entry->text_input,
			       seat,
			       surface);
792 793 794
}

static void
795 796
text_entry_deactivate(struct text_entry *entry,
		      struct wl_seat *seat)
797
{
798 799
	wl_text_input_deactivate(entry->text_input,
				 seat);
800 801
}

802 803 804 805
static void
text_entry_update_layout(struct text_entry *entry)
{
	char *text;
806
	PangoAttrList *attr_list;
807

808 809
	assert(entry->cursor <= (strlen(entry->text) +
	       (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
810

811
	if (entry->preedit.text) {
812
		text = xmalloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
813 814 815 816 817 818
		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);
	} else {
		text = strdup(entry->text);
819 820
	}

821 822 823 824
	if (entry->cursor != entry->anchor) {
		int start_index = MIN(entry->cursor, entry->anchor);
		int end_index = MAX(entry->cursor, entry->anchor);
		PangoAttribute *attr;
825

826
		attr_list = pango_attr_list_copy(entry->preedit.attr_list);
827

828 829
		if (!attr_list)
			attr_list = pango_attr_list_new();
830

831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
		attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
		attr->start_index = start_index;
		attr->end_index = end_index;
		pango_attr_list_insert(attr_list, attr);

		attr = pango_attr_foreground_new(65535, 65535, 65535);
		attr->start_index = start_index;
		attr->end_index = end_index;
		pango_attr_list_insert(attr_list, attr);
	} else {
		attr_list = pango_attr_list_ref(entry->preedit.attr_list);
	}

	if (entry->preedit.text && !entry->preedit.attr_list) {
		PangoAttribute *attr;

		if (!attr_list)
			attr_list = pango_attr_list_new();

		attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
		attr->start_index = entry->cursor;
		attr->end_index = entry->cursor + strlen(entry->preedit.text);
		pango_attr_list_insert(attr_list, attr);
	}

	if (entry->layout) {
		pango_layout_set_text(entry->layout, text, -1);
		pango_layout_set_attributes(entry->layout, attr_list);
	}

	free(text);
	pango_attr_list_unref(attr_list);
863 864
}

865 866 867
static void
text_entry_update(struct text_entry *entry)
{
868 869
	struct rectangle cursor_rectangle;

870 871 872
	wl_text_input_set_content_type(entry->text_input,
				       WL_TEXT_INPUT_CONTENT_HINT_NONE,
				       entry->content_purpose);
873

874 875 876 877
	wl_text_input_set_surrounding_text(entry->text_input,
					   entry->text,
					   entry->cursor,
					   entry->anchor);
Jan Arne Petersen's avatar
Jan Arne Petersen committed
878

879
	if (entry->preferred_language)
880 881
		wl_text_input_set_preferred_language(entry->text_input,
						     entry->preferred_language);
882

883
	text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
884 885
	wl_text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
					   cursor_rectangle.width, cursor_rectangle.height);
886

887
	wl_text_input_commit_state(entry->text_input, ++entry->serial);
888 889
}

890
static void
891 892
text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
			    int32_t cursor, int32_t anchor)
893
{
894
	char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
895 896 897 898 899 900 901 902

	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;
903 904 905 906
	if (anchor >= 0)
		entry->anchor = entry->cursor + strlen(text) + anchor;
	else
		entry->anchor = entry->cursor + 1 + anchor;
907

908 909 910 911
	if (cursor >= 0)
		entry->cursor += strlen(text) + cursor;
	else
		entry->cursor += 1 + cursor;
912

913
	text_entry_update_layout(entry);
914 915 916

	widget_schedule_redraw(entry->widget);

917
	text_entry_update(entry);
918 919
}

920 921 922 923 924 925 926 927 928 929
static void
text_entry_reset_preedit(struct text_entry *entry)
{
	entry->preedit.cursor = 0;

	free(entry->preedit.text);
	entry->preedit.text = NULL;

	free(entry->preedit.commit);
	entry->preedit.commit = NULL;
930 931 932

	pango_attr_list_unref(entry->preedit.attr_list);
	entry->preedit.attr_list = NULL;
933 934 935 936 937 938 939 940 941 942 943 944
}

static void
text_entry_commit_and_reset(struct text_entry *entry)
{
	char *commit = NULL;

	if (entry->preedit.commit)
		commit = strdup(entry->preedit.commit);

	text_entry_reset_preedit(entry);
	if (commit) {
945
		text_entry_insert_at_cursor(entry, commit, 0, 0);
946 947
		free(commit);
	}
948

949
	wl_text_input_reset(entry->text_input);
Jan Arne Petersen's avatar
Jan Arne Petersen committed
950 951
	text_entry_update(entry);
	entry->reset_serial = entry->serial;
952 953
}

954 955 956 957 958
static void
text_entry_set_preedit(struct text_entry *entry,
		       const char *preedit_text,
		       int preedit_cursor)
{
959
	text_entry_reset_preedit(entry);
960 961 962 963

	if (!preedit_text)
		return;

964 965
	entry->preedit.text = strdup(preedit_text);
	entry->preedit.cursor = preedit_cursor;
966 967

	text_entry_update_layout(entry);
968 969

	widget_schedule_redraw(entry->widget);
970 971
}

972 973 974 975 976 977 978 979
static uint32_t
text_entry_try_invoke_preedit_action(struct text_entry *entry,
				     int32_t x, int32_t y,
				     uint32_t button,
				     enum wl_pointer_button_state state)
{
	int index, trailing;
	uint32_t cursor;
980
	const char *text;
981 982 983 984 985 986 987

	if (!entry->preedit.text)
		return 0;

	pango_layout_xy_to_index(entry->layout,
				 x * PANGO_SCALE, y * PANGO_SCALE,
				 &index, &trailing);
988 989 990

	text = pango_layout_get_text(entry->layout);
	cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
991 992 993 994 995 996 997

	if (cursor < entry->cursor ||
	    cursor > entry->cursor + strlen(entry->preedit.text)) {
		return 0;
	}

	if (state == WL_POINTER_BUTTON_STATE_RELEASED)
998 999 1000
		wl_text_input_invoke_action(entry->text_input,
					    button,
					    cursor - entry->cursor);
1001 1002 1003 1004

	return 1;
}

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1005 1006 1007 1008 1009 1010
static bool
text_entry_has_preedit(struct text_entry *entry)
{
	return entry->preedit.text && (strlen(entry->preedit.text) > 0);
}

1011 1012
static void
text_entry_set_cursor_position(struct text_entry *entry,
Jan Arne Petersen's avatar
Jan Arne Petersen committed
1013 1014
			       int32_t x, int32_t y,
			       bool move_anchor)
1015
{
1016
	int index, trailing;
1017
	const char *text;
Jan Arne Petersen's avatar
Jan Arne Petersen committed
1018
	uint32_t cursor;
1019

1020 1021 1022
	pango_layout_xy_to_index(entry->layout,
				 x * PANGO_SCALE, y * PANGO_SCALE,
				 &index, &trailing);
1023 1024

	text = pango_layout_get_text(entry->layout);
1025

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1026
	cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
1027

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1028 1029
	if (move_anchor)
		entry->anchor = cursor;
1030

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1031 1032
	if (text_entry_has_preedit(entry)) {
		text_entry_commit_and_reset(entry);
1033

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1034 1035
		assert(!text_entry_has_preedit(entry));
	}
1036

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1037 1038
	if (entry->cursor == cursor)
		return;
1039

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1040
	entry->cursor = cursor;
1041 1042

	text_entry_update_layout(entry);
1043 1044

	widget_schedule_redraw(entry->widget);
1045

1046
	text_entry_update(entry);
1047 1048
}

1049 1050 1051 1052
static void
text_entry_delete_text(struct text_entry *entry,
		       uint32_t index, uint32_t length)
{
1053 1054
	uint32_t l;

1055 1056 1057
	assert(index <= strlen(entry->text));
	assert(index + length <= strlen(entry->text));
	assert(index + length >= length);
1058

1059 1060 1061 1062
	l = strlen(entry->text + index + length);
	memmove(entry->text + index,
		entry->text + index + length,
		l + 1);
1063

1064 1065 1066 1067 1068 1069 1070
	if (entry->cursor > (index + length))
		entry->cursor -= length;
	else if (entry->cursor > index)
		entry->cursor = index;

	entry->anchor = entry->cursor;

1071 1072 1073
	text_entry_update_layout(entry);

	widget_schedule_redraw(entry->widget);
1074

1075
	text_entry_update(entry);
1076 1077
}

1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
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;
}

1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
static void
text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
{
	struct rectangle allocation;
	PangoRectangle extents;
	PangoRectangle cursor_pos;

	widget_get_allocation(entry->widget, &allocation);

	if (entry->preedit.text && entry->preedit.cursor < 0) {
		rectangle->x = 0;
		rectangle->y = 0;
		rectangle->width = 0;
		rectangle->height = 0;
		return;
	}

1109

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
	pango_layout_get_extents(entry->layout, &extents, NULL);
	pango_layout_get_cursor_pos(entry->layout,
				    entry->cursor + entry->preedit.cursor,
				    &cursor_pos, NULL);

	rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
	rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
	rectangle->width = PANGO_PIXELS(cursor_pos.width);
	rectangle->height = PANGO_PIXELS(cursor_pos.height);
}

1121 1122 1123
static void
text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
{
1124 1125
	PangoRectangle extents;
	PangoRectangle cursor_pos;
1126

1127 1128 1129
	if (entry->preedit.text && entry->preedit.cursor < 0)
		return;

1130 1131 1132 1133
	pango_layout_get_extents(entry->layout, &extents, NULL);
	pango_layout_get_cursor_pos(entry->layout,
				    entry->cursor + entry->preedit.cursor,
				    &cursor_pos, NULL);
1134 1135

	cairo_set_line_width(cr, 1.0);
1136 1137
	cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
	cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
1138 1139 1140
	cairo_stroke(cr);
}

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
static int
text_offset_left(struct rectangle *allocation)
{
	return 10;
}

static int
text_offset_top(struct rectangle *allocation)
{
	return allocation->height / 2;
}
1152

1153
static void
1154
text_entry_redraw_handler(struct widget *widget, void *data)
1155
{
1156 1157
	struct text_entry *entry = data;
	cairo_surface_t *surface;
1158
	struct rectangle allocation;
1159 1160 1161 1162 1163 1164 1165 1166
	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);
1167

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
	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);

1188 1189 1190
	cairo_translate(cr,
			text_offset_left(&allocation),
			text_offset_top(&allocation));
1191

1192 1193 1194 1195
	if (!entry->layout)
		entry->layout = pango_cairo_create_layout(cr);
	else
		pango_cairo_update_layout(cr, entry->layout);
1196

1197
	text_entry_update_layout(entry);
1198

1199 1200 1201
	pango_cairo_show_layout(cr, entry->layout);

	text_entry_draw_cursor(entry, cr);
1202

1203 1204 1205 1206 1207 1208 1209
	cairo_pop_group_to_source(cr);
	cairo_paint(cr);

	cairo_destroy(cr);
	cairo_surface_destroy(surface);
}

1210 1211 1212 1213 1214 1215 1216
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;
1217
	int tx, ty;
1218

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1219 1220 1221 1222
	if (!entry->button_pressed) {
		return CURSOR_IBEAM;
	}

1223 1224
	widget_get_allocation(entry->widget, &allocation);

1225 1226 1227 1228
	tx = x - allocation.x - text_offset_left(&allocation);
	ty = y - allocation.y - text_offset_top(&allocation);

	text_entry_set_cursor_position(entry, tx, ty, false);
1229 1230 1231 1232

	return CURSOR_IBEAM;
}

1233 1234 1235 1236 1237 1238 1239
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;
1240
	struct rectangle allocation;
1241
	struct editor *editor;
1242
	int32_t x, y;
1243
	uint32_t result;
1244 1245 1246

	widget_get_allocation(entry->widget, &allocation);
	input_get_position(input, &x, &y);
1247

1248 1249
	x -= allocation.x + text_offset_left(&allocation);
	y -= allocation.y + text_offset_top(&allocation);
1250

1251 1252
	editor = window_get_user_data(entry->window);

1253 1254
	switch (button) {
	case BTN_LEFT:
Jan Arne Petersen's avatar
Jan Arne Petersen committed
1255 1256 1257 1258 1259
		entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
		if (state == WL_POINTER_BUTTON_STATE_PRESSED)
			input_grab(input, entry->widget, button);
		else
			input_ungrab(input);
1260 1261 1262 1263 1264
		break;
	case BTN_RIGHT:
		if (state == WL_POINTER_BUTTON_STATE_PRESSED)
			show_menu(editor, input, time);
		break;
1265 1266
	}

Jan Arne Petersen's avatar
Jan Arne Petersen committed
1267 1268 1269 1270 1271 1272
	if (text_entry_has_preedit(entry)) {
		result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);

		if (result)
			return;
	}
1273