027b562fdd71e0576fe18a6e661e16bed4b58c80
[profile/ivi/weston-ivi-shell.git] / clients / editor.c
1 /*
2  * Copyright © 2012 Openismus GmbH
3  * Copyright © 2012 Intel Corporation
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and
6  * its documentation for any purpose is hereby granted without fee, provided
7  * that the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of the copyright holders not be used in
10  * advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission.  The copyright holders make
12  * no representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <linux/input.h>
32 #include <cairo.h>
33
34 #include <pango/pangocairo.h>
35
36 #include "window.h"
37 #include "text-client-protocol.h"
38
39 struct text_entry {
40         struct widget *widget;
41         struct window *window;
42         char *text;
43         int active;
44         uint32_t cursor;
45         uint32_t anchor;
46         struct {
47                 char *text;
48                 int32_t cursor;
49                 char *commit;
50                 PangoAttrList *attr_list;
51         } preedit;
52         struct {
53                 PangoAttrList *attr_list;
54                 int32_t cursor;
55         } preedit_info;
56         struct {
57                 int32_t cursor;
58                 int32_t anchor;
59         } pending_commit;
60         struct text_input *text_input;
61         PangoLayout *layout;
62         struct {
63                 xkb_mod_mask_t shift_mask;
64         } keysym;
65         uint32_t serial;
66         uint32_t content_purpose;
67         uint32_t click_to_show;
68         char *preferred_language;
69 };
70
71 struct editor {
72         struct text_input_manager *text_input_manager;
73         struct display *display;
74         struct window *window;
75         struct widget *widget;
76         struct text_entry *entry;
77         struct text_entry *editor;
78         struct text_entry *active_entry;
79 };
80
81 static const char *
82 utf8_start_char(const char *text, const char *p)
83 {
84         for (; p >= text; --p) {
85                 if ((*p & 0xc0) != 0x80)
86                         return p;
87         }
88         return NULL;
89 }
90
91 static const char *
92 utf8_prev_char(const char *text, const char *p)
93 {
94         if (p > text)
95                 return utf8_start_char(text, --p);
96         return NULL;
97 }
98
99 static const char *
100 utf8_end_char(const char *p)
101 {
102         while ((*p & 0xc0) == 0x80)
103                 p++;
104         return p;
105 }
106
107 static const char *
108 utf8_next_char(const char *p)
109 {
110         if (*p != 0)
111                 return utf8_end_char(++p);
112         return NULL;
113 }
114
115 static void text_entry_redraw_handler(struct widget *widget, void *data);
116 static void text_entry_button_handler(struct widget *widget,
117                                       struct input *input, uint32_t time,
118                                       uint32_t button,
119                                       enum wl_pointer_button_state state, void *data);
120 static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
121                                         int32_t cursor, int32_t anchor);
122 static void text_entry_set_preedit(struct text_entry *entry,
123                                    const char *preedit_text,
124                                    int preedit_cursor);
125 static void text_entry_delete_text(struct text_entry *entry,
126                                    uint32_t index, uint32_t length);
127 static void text_entry_delete_selected_text(struct text_entry *entry);
128 static void text_entry_reset_preedit(struct text_entry *entry);
129 static void text_entry_commit_and_reset(struct text_entry *entry);
130
131 static void
132 text_input_commit_string(void *data,
133                          struct text_input *text_input,
134                          uint32_t serial,
135                          const char *text)
136 {
137         struct text_entry *entry = data;
138
139         text_entry_reset_preedit(entry);
140
141         text_entry_delete_selected_text(entry);
142         text_entry_insert_at_cursor(entry, text,
143                                     entry->pending_commit.cursor,
144                                     entry->pending_commit.anchor);
145
146         memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
147
148         widget_schedule_redraw(entry->widget);
149 }
150
151 static void
152 text_input_preedit_string(void *data,
153                           struct text_input *text_input,
154                           uint32_t serial,
155                           const char *text,
156                           const char *commit)
157 {
158         struct text_entry *entry = data;
159
160         text_entry_delete_selected_text(entry);
161         text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
162         entry->preedit.commit = strdup(commit);
163         entry->preedit.attr_list = entry->preedit_info.attr_list;
164
165         entry->preedit_info.cursor = 0;
166         entry->preedit_info.attr_list = NULL;
167
168         widget_schedule_redraw(entry->widget);
169 }
170
171 static void
172 text_input_delete_surrounding_text(void *data,
173                                    struct text_input *text_input,
174                                    uint32_t serial,
175                                    int32_t index,
176                                    uint32_t length)
177 {
178         struct text_entry *entry = data;
179         uint32_t cursor_index = index + entry->cursor;
180         const char *start, *end;
181
182         if (cursor_index > strlen(entry->text)) {
183                 fprintf(stderr, "Invalid cursor index %d\n", index);
184                 return;
185         }
186
187         if (cursor_index + length > strlen(entry->text)) {
188                 fprintf(stderr, "Invalid length %d\n", length);
189                 return;
190         }
191
192         if (length == 0)
193                 return;
194
195         start = utf8_start_char(entry->text, entry->text + cursor_index);
196         end = utf8_end_char(entry->text + cursor_index + length);
197
198         text_entry_delete_text(entry,
199                                start - entry->text,
200                                end - start);
201 }
202
203 static void
204 text_input_cursor_position(void *data,
205                            struct text_input *text_input,
206                            uint32_t serial,
207                            int32_t index,
208                            int32_t anchor)
209 {
210         struct text_entry *entry = data;
211
212         entry->pending_commit.cursor = index;
213         entry->pending_commit.anchor = anchor;
214 }
215
216 static void
217 text_input_preedit_styling(void *data,
218                            struct text_input *text_input,
219                            uint32_t serial,
220                            uint32_t index,
221                            uint32_t length,
222                            uint32_t style)
223 {
224         struct text_entry *entry = data;
225         PangoAttribute *attr1 = NULL;
226         PangoAttribute *attr2 = NULL;
227
228         if (!entry->preedit_info.attr_list)
229                 entry->preedit_info.attr_list = pango_attr_list_new();
230
231         switch (style) {
232                 case TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
233                 case TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
234                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
235                         break;
236                 case TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
237                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
238                         attr2 = pango_attr_underline_color_new(65535, 0, 0);
239                         break;
240                 case TEXT_INPUT_PREEDIT_STYLE_SELECTION:
241                         attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
242                         attr2 = pango_attr_foreground_new(65535, 65535, 65535);
243                         break;
244                 case TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
245                 case TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
246                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
247                         attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
248                         break;
249                 case TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
250                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
251                         attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
252                         break;
253         }
254
255         if (attr1) {
256                 attr1->start_index = entry->cursor + index;
257                 attr1->end_index = entry->cursor + index + length;
258                 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
259         }
260
261         if (attr2) {
262                 attr2->start_index = entry->cursor + index;
263                 attr2->end_index = entry->cursor + index + length;
264                 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
265         }
266 }
267
268 static void
269 text_input_preedit_cursor(void *data,
270                           struct text_input *text_input,
271                           uint32_t serial,
272                           int32_t index)
273 {
274         struct text_entry *entry = data;
275
276         entry->preedit_info.cursor = index;
277 }
278
279 static void
280 text_input_modifiers_map(void *data,
281                          struct text_input *text_input,
282                          struct wl_array *map)
283 {
284         struct text_entry *entry = data;
285
286         entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
287 }
288
289 static void
290 text_input_keysym(void *data,
291                   struct text_input *text_input,
292                   uint32_t serial,
293                   uint32_t time,
294                   uint32_t key,
295                   uint32_t state,
296                   uint32_t modifiers)
297 {
298         struct text_entry *entry = data;
299         const char *state_label = "release";
300         const char *key_label = "Unknown";
301         const char *new_char;
302
303         if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
304                 state_label = "pressed";
305         }
306
307         if (key == XKB_KEY_Left ||
308             key == XKB_KEY_Right) {
309                 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
310                         return;
311
312                 if (key == XKB_KEY_Left)
313                         new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
314                 else
315                         new_char = utf8_next_char(entry->text + entry->cursor);
316
317                 if (new_char != NULL) {
318                         entry->cursor = new_char - entry->text;
319                         if (!(modifiers & entry->keysym.shift_mask))
320                                 entry->anchor = entry->cursor;
321                         widget_schedule_redraw(entry->widget);
322                 }
323
324                 return;
325         }
326
327         if (key == XKB_KEY_BackSpace) {
328                 const char *start, *end;
329
330                 text_entry_commit_and_reset(entry);
331
332                 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
333
334                 if (start == NULL)
335                         return;
336
337                 end = utf8_end_char(entry->text + entry->cursor);
338                 text_entry_delete_text(entry,
339                                        start - entry->text,
340                                        end - start);
341
342                 return;
343         }
344
345         switch (key) {
346                 case XKB_KEY_Tab:
347                         key_label = "Tab";
348                         break;
349                 case XKB_KEY_KP_Enter:
350                 case XKB_KEY_Return:
351                         key_label = "Enter";
352                         break;
353         }
354
355         fprintf(stderr, "%s key was %s.\n", key_label, state_label);
356 }
357
358 static void
359 text_input_enter(void *data,
360                  struct text_input *text_input,
361                  struct wl_surface *surface)
362 {
363         struct text_entry *entry = data;
364
365         if (surface != window_get_wl_surface(entry->window))
366                 return;
367
368         entry->active = 1;
369
370         widget_schedule_redraw(entry->widget);
371 }
372
373 static void
374 text_input_leave(void *data,
375                  struct text_input *text_input)
376 {
377         struct text_entry *entry = data;
378
379         text_entry_commit_and_reset(entry);
380
381         entry->active = 0;
382
383         text_input_hide_input_panel(text_input);
384
385         widget_schedule_redraw(entry->widget);
386 }
387
388 static void
389 text_input_input_panel_state(void *data,
390                              struct text_input *text_input,
391                              uint32_t state)
392 {
393 }
394
395 static void
396 text_input_language(void *data,
397                     struct text_input *text_input,
398                     uint32_t serial,
399                     const char *language)
400 {
401         fprintf(stderr, "input language is %s \n", language);
402 }
403
404 static void
405 text_input_text_direction(void *data,
406                           struct text_input *text_input,
407                           uint32_t serial,
408                           uint32_t direction)
409 {
410         struct text_entry *entry = data;
411         PangoContext *context = pango_layout_get_context(entry->layout);
412         PangoDirection pango_direction;
413
414
415         switch (direction) {
416                 case TEXT_INPUT_TEXT_DIRECTION_LTR:
417                         pango_direction = PANGO_DIRECTION_LTR;
418                         break;
419                 case TEXT_INPUT_TEXT_DIRECTION_RTL:
420                         pango_direction = PANGO_DIRECTION_RTL;
421                         break;
422                 case TEXT_INPUT_TEXT_DIRECTION_AUTO:
423                 default:
424                         pango_direction = PANGO_DIRECTION_NEUTRAL;
425         }
426         
427         pango_context_set_base_dir(context, pango_direction);
428 }
429
430 static const struct text_input_listener text_input_listener = {
431         text_input_enter,
432         text_input_leave,
433         text_input_modifiers_map,
434         text_input_input_panel_state,
435         text_input_preedit_string,
436         text_input_preedit_styling,
437         text_input_preedit_cursor,
438         text_input_commit_string,
439         text_input_cursor_position,
440         text_input_delete_surrounding_text,
441         text_input_keysym,
442         text_input_language,
443         text_input_text_direction
444 };
445
446 static struct text_entry*
447 text_entry_create(struct editor *editor, const char *text)
448 {
449         struct text_entry *entry;
450
451         entry = calloc(1, sizeof *entry);
452
453         entry->widget = widget_add_widget(editor->widget, entry);
454         entry->window = editor->window;
455         entry->text = strdup(text);
456         entry->active = 0;
457         entry->cursor = strlen(text);
458         entry->anchor = entry->cursor;
459         entry->text_input = text_input_manager_create_text_input(editor->text_input_manager);
460         text_input_add_listener(entry->text_input, &text_input_listener, entry);
461
462         widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
463         widget_set_button_handler(entry->widget, text_entry_button_handler);
464
465         return entry;
466 }
467
468 static void
469 text_entry_destroy(struct text_entry *entry)
470 {
471         widget_destroy(entry->widget);
472         text_input_destroy(entry->text_input);
473         g_clear_object(&entry->layout);
474         free(entry->text);
475         free(entry);
476 }
477
478 static void
479 redraw_handler(struct widget *widget, void *data)
480 {
481         struct editor *editor = data;
482         cairo_surface_t *surface;
483         struct rectangle allocation;
484         cairo_t *cr;
485
486         surface = window_get_surface(editor->window);
487         widget_get_allocation(editor->widget, &allocation);
488
489         cr = cairo_create(surface);
490         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
491         cairo_clip(cr);
492
493         cairo_translate(cr, allocation.x, allocation.y);
494
495         /* Draw background */
496         cairo_push_group(cr);
497         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
498         cairo_set_source_rgba(cr, 1, 1, 1, 1);
499         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
500         cairo_fill(cr);
501
502         cairo_pop_group_to_source(cr);
503         cairo_paint(cr);
504
505         cairo_destroy(cr);
506         cairo_surface_destroy(surface);
507 }
508
509 static void
510 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
511                     int32_t width, int32_t height)
512 {
513         widget_set_allocation(entry->widget, x, y, width, height);
514 }
515
516 static void
517 resize_handler(struct widget *widget,
518                int32_t width, int32_t height, void *data)
519 {
520         struct editor *editor = data;
521         struct rectangle allocation;
522
523         widget_get_allocation(editor->widget, &allocation);
524
525         text_entry_allocate(editor->entry,
526                             allocation.x + 20, allocation.y + 20,
527                             width - 40, height / 2 - 40);
528         text_entry_allocate(editor->editor,
529                             allocation.x + 20, allocation.y + height / 2 + 20,
530                             width - 40, height / 2 - 40);
531 }
532
533 static void
534 text_entry_activate(struct text_entry *entry,
535                     struct wl_seat *seat)
536 {
537         struct wl_surface *surface = window_get_wl_surface(entry->window);
538
539         if (entry->click_to_show && entry->active) {
540                 text_input_show_input_panel(entry->text_input);
541
542                 return;
543         }
544
545         if (!entry->click_to_show)
546                 text_input_show_input_panel(entry->text_input);
547
548         entry->serial++;
549
550         text_input_activate(entry->text_input,
551                             entry->serial,
552                             seat,
553                             surface);
554 }
555
556 static void
557 text_entry_deactivate(struct text_entry *entry,
558                       struct wl_seat *seat)
559 {
560         text_input_deactivate(entry->text_input,
561                               seat);
562 }
563
564 static void
565 text_entry_update_layout(struct text_entry *entry)
566 {
567         char *text;
568         PangoAttrList *attr_list;
569
570         assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
571                (entry->preedit.text ? strlen(entry->preedit.text) : 0));
572
573         if (entry->preedit.text) {
574                 text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
575                 strncpy(text, entry->text, entry->cursor);
576                 strcpy(text + entry->cursor, entry->preedit.text);
577                 strcpy(text + entry->cursor + strlen(entry->preedit.text),
578                        entry->text + entry->cursor);
579         } else {
580                 text = strdup(entry->text);
581         }
582
583         if (entry->cursor != entry->anchor) {
584                 int start_index = MIN(entry->cursor, entry->anchor);
585                 int end_index = MAX(entry->cursor, entry->anchor);
586                 PangoAttribute *attr;
587
588                 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
589
590                 if (!attr_list)
591                         attr_list = pango_attr_list_new();
592
593                 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
594                 attr->start_index = start_index;
595                 attr->end_index = end_index;
596                 pango_attr_list_insert(attr_list, attr);
597
598                 attr = pango_attr_foreground_new(65535, 65535, 65535);
599                 attr->start_index = start_index;
600                 attr->end_index = end_index;
601                 pango_attr_list_insert(attr_list, attr);
602         } else {
603                 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
604         }
605
606         if (entry->preedit.text && !entry->preedit.attr_list) {
607                 PangoAttribute *attr;
608
609                 if (!attr_list)
610                         attr_list = pango_attr_list_new();
611
612                 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
613                 attr->start_index = entry->cursor;
614                 attr->end_index = entry->cursor + strlen(entry->preedit.text);
615                 pango_attr_list_insert(attr_list, attr);
616         }
617
618         if (entry->layout) {
619                 pango_layout_set_text(entry->layout, text, -1);
620                 pango_layout_set_attributes(entry->layout, attr_list);
621         }
622
623         free(text);
624         pango_attr_list_unref(attr_list);
625 }
626
627 static void
628 text_entry_update(struct text_entry *entry)
629 {
630         text_input_set_content_type(entry->text_input,
631                                     TEXT_INPUT_CONTENT_HINT_NONE,
632                                     entry->content_purpose);
633
634         text_input_set_surrounding_text(entry->text_input,
635                                         entry->text,
636                                         entry->cursor,
637                                         entry->anchor);
638
639         if (entry->preferred_language)
640                 text_input_set_preferred_language(entry->text_input,
641                                                   entry->preferred_language);
642
643         text_input_commit_state(entry->text_input);
644 }
645
646 static void
647 text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
648                             int32_t cursor, int32_t anchor)
649 {
650         char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
651
652         strncpy(new_text, entry->text, entry->cursor);
653         strcpy(new_text + entry->cursor, text);
654         strcpy(new_text + entry->cursor + strlen(text),
655                entry->text + entry->cursor);
656
657         free(entry->text);
658         entry->text = new_text;
659         if (anchor >= 0)
660                 entry->anchor = entry->cursor + strlen(text) + anchor;
661         else
662                 entry->anchor = entry->cursor + 1 + anchor;
663         if (cursor >= 0)
664                 entry->cursor += strlen(text) + cursor;
665         else
666                 entry->cursor += 1 + cursor;
667
668         text_entry_update_layout(entry);
669
670         widget_schedule_redraw(entry->widget);
671
672         text_entry_update(entry);
673 }
674
675 static void
676 text_entry_reset_preedit(struct text_entry *entry)
677 {
678         entry->preedit.cursor = 0;
679
680         free(entry->preedit.text);
681         entry->preedit.text = NULL;
682
683         free(entry->preedit.commit);
684         entry->preedit.commit = NULL;
685
686         pango_attr_list_unref(entry->preedit.attr_list);
687         entry->preedit.attr_list = NULL;
688 }
689
690 static void
691 text_entry_commit_and_reset(struct text_entry *entry)
692 {
693         char *commit = NULL;
694
695         if (entry->preedit.commit)
696                 commit = strdup(entry->preedit.commit);
697
698         text_entry_reset_preedit(entry);
699         if (commit) {
700                 text_entry_insert_at_cursor(entry, commit, 0, 0);
701                 free(commit);
702         }
703
704         entry->serial++;
705         text_input_reset(entry->text_input, entry->serial);
706 }
707
708 static void
709 text_entry_set_preedit(struct text_entry *entry,
710                        const char *preedit_text,
711                        int preedit_cursor)
712 {
713         text_entry_reset_preedit(entry);
714
715         if (!preedit_text)
716                 return;
717
718         entry->preedit.text = strdup(preedit_text);
719         entry->preedit.cursor = preedit_cursor;
720
721         text_entry_update_layout(entry);
722
723         widget_schedule_redraw(entry->widget);
724 }
725
726 static uint32_t
727 text_entry_try_invoke_preedit_action(struct text_entry *entry,
728                                      int32_t x, int32_t y,
729                                      uint32_t button,
730                                      enum wl_pointer_button_state state)
731 {
732         int index, trailing;
733         uint32_t cursor;
734
735         if (!entry->preedit.text)
736                 return 0;
737
738         pango_layout_xy_to_index(entry->layout,
739                                  x * PANGO_SCALE, y * PANGO_SCALE,
740                                  &index, &trailing);
741         cursor = index + trailing;
742
743         if (cursor < entry->cursor ||
744             cursor > entry->cursor + strlen(entry->preedit.text)) {
745                 return 0;
746         }
747
748         if (state == WL_POINTER_BUTTON_STATE_RELEASED)
749                 text_input_invoke_action(entry->text_input,
750                                          button,
751                                          cursor - entry->cursor);
752
753         return 1;
754 }
755
756 static void
757 text_entry_set_cursor_position(struct text_entry *entry,
758                                int32_t x, int32_t y)
759 {
760         int index, trailing;
761
762         text_entry_commit_and_reset(entry);
763
764         pango_layout_xy_to_index(entry->layout,
765                                  x * PANGO_SCALE, y * PANGO_SCALE,
766                                  &index, &trailing);
767         entry->cursor = index + trailing;
768
769         text_entry_update_layout(entry);
770
771         widget_schedule_redraw(entry->widget);
772
773         text_entry_update(entry);
774 }
775
776 static void
777 text_entry_set_anchor_position(struct text_entry *entry,
778                                int32_t x, int32_t y)
779 {
780         int index, trailing;
781
782         pango_layout_xy_to_index(entry->layout,
783                                  x * PANGO_SCALE, y * PANGO_SCALE,
784                                  &index, &trailing);
785         entry->anchor = index + trailing;
786
787         text_entry_update_layout(entry);
788
789         widget_schedule_redraw(entry->widget);
790
791         text_entry_update(entry);
792 }
793
794 static void
795 text_entry_delete_text(struct text_entry *entry,
796                        uint32_t index, uint32_t length)
797 {
798         if (entry->cursor > index)
799                 entry->cursor -= length;
800
801         entry->anchor = entry->cursor;
802
803         entry->text[index] = '\0';
804         strcat(entry->text, entry->text + index + length);
805
806         text_entry_update_layout(entry);
807
808         widget_schedule_redraw(entry->widget);
809
810         text_entry_update(entry);
811 }
812
813 static void
814 text_entry_delete_selected_text(struct text_entry *entry)
815 {
816         uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
817         uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
818
819         if (entry->anchor == entry->cursor)
820                 return;
821
822         text_entry_delete_text(entry, start_index, end_index - start_index);
823
824         entry->anchor = entry->cursor;
825 }
826
827 static void
828 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
829 {
830         PangoRectangle extents;
831         PangoRectangle cursor_pos;
832
833         if (entry->preedit.text && entry->preedit.cursor < 0)
834                 return;
835
836         pango_layout_get_extents(entry->layout, &extents, NULL);
837         pango_layout_get_cursor_pos(entry->layout,
838                                     entry->cursor + entry->preedit.cursor,
839                                     &cursor_pos, NULL);
840
841         cairo_set_line_width(cr, 1.0);
842         cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(extents.height) + 2);
843         cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), - 2);
844         cairo_stroke(cr);
845 }
846
847 static const int text_offset_left = 10;
848
849 static void
850 text_entry_redraw_handler(struct widget *widget, void *data)
851 {
852         struct text_entry *entry = data;
853         cairo_surface_t *surface;
854         struct rectangle allocation;
855         cairo_t *cr;
856
857         surface = window_get_surface(entry->window);
858         widget_get_allocation(entry->widget, &allocation);
859
860         cr = cairo_create(surface);
861         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
862         cairo_clip(cr);
863
864         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
865
866         cairo_push_group(cr);
867         cairo_translate(cr, allocation.x, allocation.y);
868
869         cairo_set_source_rgba(cr, 1, 1, 1, 1);
870         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
871         cairo_fill(cr);
872
873         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
874
875         if (entry->active) {
876                 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
877                 cairo_set_line_width (cr, 3);
878                 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
879                 cairo_stroke(cr);
880         }
881
882         cairo_set_source_rgba(cr, 0, 0, 0, 1);
883
884         cairo_translate(cr, text_offset_left, allocation.height / 2);
885
886         if (!entry->layout)
887                 entry->layout = pango_cairo_create_layout(cr);
888         else
889                 pango_cairo_update_layout(cr, entry->layout);
890
891         text_entry_update_layout(entry);
892
893         pango_cairo_show_layout(cr, entry->layout);
894
895         text_entry_draw_cursor(entry, cr);
896
897         cairo_pop_group_to_source(cr);
898         cairo_paint(cr);
899
900         cairo_destroy(cr);
901         cairo_surface_destroy(surface);
902 }
903
904 static int
905 text_entry_motion_handler(struct widget *widget,
906                           struct input *input, uint32_t time,
907                           float x, float y, void *data)
908 {
909         struct text_entry *entry = data;
910         struct rectangle allocation;
911
912         widget_get_allocation(entry->widget, &allocation);
913
914         text_entry_set_cursor_position(entry,
915                                        x - allocation.x - text_offset_left,
916                                        y - allocation.y - text_offset_left);
917
918         return CURSOR_IBEAM;
919 }
920
921 static void
922 text_entry_button_handler(struct widget *widget,
923                           struct input *input, uint32_t time,
924                           uint32_t button,
925                           enum wl_pointer_button_state state, void *data)
926 {
927         struct text_entry *entry = data;
928         struct rectangle allocation;
929         struct editor *editor;
930         int32_t x, y;
931         uint32_t result;
932
933         widget_get_allocation(entry->widget, &allocation);
934         input_get_position(input, &x, &y);
935
936         x -= allocation.x + text_offset_left;
937         y -= allocation.y + text_offset_left;
938
939         editor = window_get_user_data(entry->window);
940
941         result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
942
943         if (result)
944                 return;
945
946         if (button != BTN_LEFT) {
947                 return;
948         }
949
950         text_entry_set_cursor_position(entry, x, y);
951
952         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
953                 struct wl_seat *seat = input_get_seat(input);
954
955                 text_entry_activate(entry, seat);
956                 editor->active_entry = entry;
957
958                 text_entry_set_anchor_position(entry, x, y);
959
960                 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
961         } else {
962                 widget_set_motion_handler(entry->widget, NULL);
963         }
964 }
965
966 static void
967 editor_button_handler(struct widget *widget,
968                       struct input *input, uint32_t time,
969                       uint32_t button,
970                       enum wl_pointer_button_state state, void *data)
971 {
972         struct editor *editor = data;
973
974         if (button != BTN_LEFT) {
975                 return;
976         }
977
978         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
979                 struct wl_seat *seat = input_get_seat(input);
980
981                 text_entry_deactivate(editor->entry, seat);
982                 text_entry_deactivate(editor->editor, seat);
983                 editor->active_entry = NULL;
984         }
985 }
986
987 static void
988 key_handler(struct window *window,
989             struct input *input, uint32_t time,
990             uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
991             void *data)
992 {
993         struct editor *editor = data;
994         struct text_entry *entry;
995         const char *start, *end, *new_char;
996         char text[16];
997
998         if (!editor->active_entry)
999                 return;
1000
1001         entry = editor->active_entry;
1002
1003         if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1004                 return;
1005
1006         switch (sym) {
1007                 case XKB_KEY_BackSpace:
1008                         text_entry_commit_and_reset(entry);
1009
1010                         start = utf8_prev_char(entry->text, entry->text + entry->cursor);
1011
1012                         if (start == NULL)
1013                                 break;
1014
1015                         end = utf8_end_char(entry->text + entry->cursor);
1016                         text_entry_delete_text(entry,
1017                                                start - entry->text,
1018                                                end - start);
1019                         break;
1020                 case XKB_KEY_Delete:
1021                         text_entry_commit_and_reset(entry);
1022
1023                         start = utf8_start_char(entry->text, entry->text + entry->cursor);
1024
1025                         if (start == NULL)
1026                                 break;
1027
1028                         end = utf8_next_char(start);
1029
1030                         if (end == NULL)
1031                                 break;
1032
1033                         text_entry_delete_text(entry,
1034                                                start - entry->text,
1035                                                end - start);
1036                         break;
1037                 case XKB_KEY_Left:
1038                         text_entry_commit_and_reset(entry);
1039
1040                         new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1041                         if (new_char != NULL) {
1042                                 entry->cursor = new_char - entry->text;
1043                                 entry->anchor = entry->cursor;
1044                                 widget_schedule_redraw(entry->widget);
1045                         }
1046                         break;
1047                 case XKB_KEY_Right:
1048                         text_entry_commit_and_reset(entry);
1049
1050                         new_char = utf8_next_char(entry->text + entry->cursor);
1051                         if (new_char != NULL) {
1052                                 entry->cursor = new_char - entry->text;
1053                                 entry->anchor = entry->cursor;
1054                                 widget_schedule_redraw(entry->widget);
1055                         }
1056                         break;
1057                 default:
1058                         if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1059                                 break;
1060
1061                         text_entry_commit_and_reset(entry);
1062
1063                         text_entry_insert_at_cursor(entry, text, 0, 0);
1064                         break;
1065         }
1066
1067         widget_schedule_redraw(entry->widget);
1068 }
1069
1070 static void
1071 global_handler(struct display *display, uint32_t name,
1072                const char *interface, uint32_t version, void *data)
1073 {
1074         struct editor *editor = data;
1075
1076         if (!strcmp(interface, "text_input_manager")) {
1077                 editor->text_input_manager =
1078                         display_bind(display, name,
1079                                      &text_input_manager_interface, 1);
1080         }
1081 }
1082
1083 int
1084 main(int argc, char *argv[])
1085 {
1086         struct editor editor;
1087         int i;
1088         uint32_t click_to_show = 0;
1089         const char *preferred_language = NULL;
1090
1091         for (i = 1; i < argc; i++) {
1092                 if (strcmp("--click-to-show", argv[i]) == 0)
1093                         click_to_show = 1;
1094                 else if (strcmp("--preferred-language", argv[i]) == 0) {
1095                         if (i + 1 < argc) {
1096                                 preferred_language = argv[i + 1];
1097                                 i++;
1098                         }
1099                 }
1100         }
1101
1102         memset(&editor, 0, sizeof editor);
1103
1104 #ifdef HAVE_PANGO
1105         g_type_init();
1106 #endif
1107
1108         editor.display = display_create(&argc, argv);
1109         if (editor.display == NULL) {
1110                 fprintf(stderr, "failed to create display: %m\n");
1111                 return -1;
1112         }
1113
1114         display_set_user_data(editor.display, &editor);
1115         display_set_global_handler(editor.display, global_handler);
1116
1117         editor.window = window_create(editor.display);
1118         editor.widget = frame_create(editor.window, &editor);
1119
1120         editor.entry = text_entry_create(&editor, "Entry");
1121         editor.entry->click_to_show = click_to_show;
1122         if (preferred_language)
1123                 editor.entry->preferred_language = strdup(preferred_language);
1124         editor.editor = text_entry_create(&editor, "Numeric");
1125         editor.editor->content_purpose = TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
1126         editor.editor->click_to_show = click_to_show;
1127
1128         window_set_title(editor.window, "Text Editor");
1129         window_set_key_handler(editor.window, key_handler);
1130         window_set_user_data(editor.window, &editor);
1131
1132         widget_set_redraw_handler(editor.widget, redraw_handler);
1133         widget_set_resize_handler(editor.widget, resize_handler);
1134         widget_set_button_handler(editor.widget, editor_button_handler);
1135
1136         window_schedule_resize(editor.window, 500, 400);
1137
1138         display_run(editor.display);
1139
1140         text_entry_destroy(editor.entry);
1141         text_entry_destroy(editor.editor);
1142
1143         return 0;
1144 }