blob: abb8d0327c85e591088f590186a5a080bc7cc103 [file] [log] [blame]
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001/*
2 * Copyright © 2012 Openismus GmbH
Jan Arne Petersen4c265182012-09-09 23:08:30 +02003 * Copyright © 2012 Intel Corporation
Jan Arne Petersencba9e472012-06-21 21:52:19 +02004 *
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
Philipp Brüschweiler591cfca2012-07-11 22:25:29 +020024#include <assert.h>
Jan Arne Petersencba9e472012-06-21 21:52:19 +020025#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <linux/input.h>
30#include <cairo.h>
31
32#include "window.h"
33#include "text-client-protocol.h"
34
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +020035static const char *font_name = "sans-serif";
36static int font_size = 14;
37
38struct text_layout {
39 cairo_glyph_t *glyphs;
40 int num_glyphs;
41 cairo_text_cluster_t *clusters;
42 int num_clusters;
43 cairo_text_cluster_flags_t cluster_flags;
44 cairo_scaled_font_t *font;
45};
46
Jan Arne Petersencba9e472012-06-21 21:52:19 +020047struct text_entry {
48 struct widget *widget;
Jan Arne Petersene829adc2012-08-10 16:47:22 +020049 struct window *window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020050 char *text;
51 int active;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +020052 uint32_t cursor;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +020053 uint32_t anchor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +020054 char *preedit_text;
55 uint32_t preedit_cursor;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020056 struct text_model *model;
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +020057 struct text_layout *layout;
Jan Arne Petersencd997062012-11-18 19:06:44 +010058 struct {
59 xkb_mod_mask_t shift_mask;
60 } keysym;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020061};
62
63struct editor {
Jan Arne Petersen51963742012-08-10 16:47:20 +020064 struct text_model_factory *text_model_factory;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020065 struct display *display;
66 struct window *window;
67 struct widget *widget;
68 struct text_entry *entry;
69 struct text_entry *editor;
70};
71
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010072static const char *
73utf8_start_char(const char *text, const char *p)
74{
75 for (; p >= text; --p) {
76 if ((*p & 0xc0) != 0x80)
77 return p;
78 }
79 return NULL;
80}
81
82static const char *
83utf8_prev_char(const char *text, const char *p)
84{
85 if (p > text)
86 return utf8_start_char(text, --p);
87 return NULL;
88}
89
90static const char *
91utf8_end_char(const char *p)
92{
93 while ((*p & 0xc0) == 0x80)
94 p++;
95 return p;
96}
97
98static const char *
99utf8_next_char(const char *p)
100{
101 if (*p != 0)
102 return utf8_end_char(++p);
103 return NULL;
104}
105
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200106static struct text_layout *
107text_layout_create(void)
108{
109 struct text_layout *layout;
110 cairo_surface_t *surface;
111 cairo_t *cr;
112
113 layout = malloc(sizeof *layout);
114 if (!layout)
115 return NULL;
116
117 layout->glyphs = NULL;
118 layout->num_glyphs = 0;
119
120 layout->clusters = NULL;
121 layout->num_clusters = 0;
122
123 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
124 cr = cairo_create(surface);
125 cairo_set_font_size(cr, font_size);
126 cairo_select_font_face(cr, font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
127 layout->font = cairo_get_scaled_font(cr);
128 cairo_scaled_font_reference(layout->font);
129
130 cairo_destroy(cr);
131 cairo_surface_destroy(surface);
132
133 return layout;
134}
135
136static void
137text_layout_destroy(struct text_layout *layout)
138{
139 if (layout->glyphs)
140 cairo_glyph_free(layout->glyphs);
141
142 if (layout->clusters)
143 cairo_text_cluster_free(layout->clusters);
144
145 cairo_scaled_font_destroy(layout->font);
146
147 free(layout);
148}
149
150static void
151text_layout_set_text(struct text_layout *layout,
152 const char *text)
153{
154 if (layout->glyphs)
155 cairo_glyph_free(layout->glyphs);
156
157 if (layout->clusters)
158 cairo_text_cluster_free(layout->clusters);
159
160 layout->glyphs = NULL;
161 layout->num_glyphs = 0;
162 layout->clusters = NULL;
163 layout->num_clusters = 0;
164
165 cairo_scaled_font_text_to_glyphs(layout->font, 0, 0, text, -1,
166 &layout->glyphs, &layout->num_glyphs,
167 &layout->clusters, &layout->num_clusters,
168 &layout->cluster_flags);
169}
170
171static void
172text_layout_draw(struct text_layout *layout, cairo_t *cr)
173{
174 cairo_save(cr);
175 cairo_set_scaled_font(cr, layout->font);
176 cairo_show_glyphs(cr, layout->glyphs, layout->num_glyphs);
177 cairo_restore(cr);
178}
179
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200180static void
181text_layout_extents(struct text_layout *layout, cairo_text_extents_t *extents)
182{
183 cairo_scaled_font_glyph_extents(layout->font,
184 layout->glyphs, layout->num_glyphs,
185 extents);
186}
187
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100188static uint32_t
189bytes_from_glyphs(struct text_layout *layout, uint32_t index)
190{
191 int i;
192 uint32_t glyphs = 0, bytes = 0;
193
194 for (i = 0; i < layout->num_clusters && glyphs < index; i++) {
195 bytes += layout->clusters[i].num_bytes;
196 glyphs += layout->clusters[i].num_glyphs;
197 }
198
199 return bytes;
200}
201
202static uint32_t
203glyphs_from_bytes(struct text_layout *layout, uint32_t index)
204{
205 int i;
206 uint32_t glyphs = 0, bytes = 0;
207
208 for (i = 0; i < layout->num_clusters && bytes < index; i++) {
209 bytes += layout->clusters[i].num_bytes;
210 glyphs += layout->clusters[i].num_glyphs;
211 }
212
213 return glyphs;
214}
215
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200216static int
217text_layout_xy_to_index(struct text_layout *layout, double x, double y)
218{
219 cairo_text_extents_t extents;
220 int i;
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200221 double d;
222
223 if (layout->num_glyphs == 0)
224 return 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200225
226 cairo_scaled_font_glyph_extents(layout->font,
227 layout->glyphs, layout->num_glyphs,
228 &extents);
229
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200230 if (x < 0)
231 return 0;
232
233 for (i = 0; i < layout->num_glyphs - 1; ++i) {
234 d = layout->glyphs[i + 1].x - layout->glyphs[i].x;
235 if (x < layout->glyphs[i].x + d/2)
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100236 return bytes_from_glyphs(layout, i);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200237 }
238
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200239 d = extents.width - layout->glyphs[layout->num_glyphs - 1].x;
240 if (x < layout->glyphs[layout->num_glyphs - 1].x + d/2)
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100241 return bytes_from_glyphs(layout, layout->num_glyphs - 1);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200242
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100243 return bytes_from_glyphs(layout, layout->num_glyphs);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200244}
245
246static void
247text_layout_index_to_pos(struct text_layout *layout, uint32_t index, cairo_rectangle_t *pos)
248{
249 cairo_text_extents_t extents;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100250 int glyph_index = glyphs_from_bytes(layout, index);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200251
252 if (!pos)
253 return;
254
255 cairo_scaled_font_glyph_extents(layout->font,
256 layout->glyphs, layout->num_glyphs,
257 &extents);
258
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100259 if (glyph_index >= layout->num_glyphs) {
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200260 pos->x = extents.x_advance;
261 pos->y = layout->num_glyphs ? layout->glyphs[layout->num_glyphs - 1].y : 0;
262 pos->width = 1;
263 pos->height = extents.height;
264 return;
265 }
266
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100267 pos->x = layout->glyphs[glyph_index].x;
268 pos->y = layout->glyphs[glyph_index].y;
269 pos->width = glyph_index < layout->num_glyphs - 1 ? layout->glyphs[glyph_index + 1].x : extents.x_advance - pos->x;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200270 pos->height = extents.height;
271}
272
273static void
274text_layout_get_cursor_pos(struct text_layout *layout, int index, cairo_rectangle_t *pos)
275{
276 text_layout_index_to_pos(layout, index, pos);
277 pos->width = 1;
278}
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200279
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200280static void text_entry_redraw_handler(struct widget *widget, void *data);
281static void text_entry_button_handler(struct widget *widget,
282 struct input *input, uint32_t time,
283 uint32_t button,
284 enum wl_pointer_button_state state, void *data);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200285static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200286static void text_entry_set_preedit(struct text_entry *entry,
287 const char *preedit_text,
288 int preedit_cursor);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200289static void text_entry_delete_text(struct text_entry *entry,
290 uint32_t index, uint32_t length);
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200291static void text_entry_delete_selected_text(struct text_entry *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200292
293static void
294text_model_commit_string(void *data,
295 struct text_model *text_model,
296 const char *text,
297 uint32_t index)
298{
299 struct text_entry *entry = data;
300
John Kåre Alsaker011a1ce2012-10-12 12:25:06 +0200301 if (index > strlen(text))
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200302 fprintf(stderr, "Invalid cursor index %d\n", index);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200303
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200304 text_entry_delete_selected_text(entry);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200305 text_entry_insert_at_cursor(entry, text);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200306
307 widget_schedule_redraw(entry->widget);
308}
309
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200310static void
311text_model_preedit_string(void *data,
312 struct text_model *text_model,
313 const char *text,
314 uint32_t index)
315{
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200316 struct text_entry *entry = data;
317
318 if (index > strlen(text)) {
319 fprintf(stderr, "Invalid cursor index %d\n", index);
320 index = strlen(text);
321 }
322
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200323 text_entry_delete_selected_text(entry);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200324 text_entry_set_preedit(entry, text, index);
325
326 widget_schedule_redraw(entry->widget);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200327}
328
329static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200330text_model_delete_surrounding_text(void *data,
331 struct text_model *text_model,
332 int32_t index,
333 uint32_t length)
334{
335 struct text_entry *entry = data;
336 uint32_t cursor_index = index + entry->cursor;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100337 const char *start, *end;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200338
339 if (cursor_index > strlen(entry->text)) {
340 fprintf(stderr, "Invalid cursor index %d\n", index);
341 return;
342 }
343
344 if (cursor_index + length > strlen(entry->text)) {
345 fprintf(stderr, "Invalid length %d\n", length);
346 return;
347 }
348
349 if (length == 0)
350 return;
351
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100352 start = utf8_start_char(entry->text, entry->text + cursor_index);
353 end = utf8_end_char(entry->text + cursor_index + length);
354
355 text_entry_delete_text(entry,
356 start - entry->text,
357 end - start);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200358}
359
360static void
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200361text_model_preedit_styling(void *data,
362 struct text_model *text_model)
363{
364}
365
366static void
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100367text_model_modifiers_map(void *data,
368 struct text_model *text_model,
369 struct wl_array *map)
370{
Jan Arne Petersencd997062012-11-18 19:06:44 +0100371 struct text_entry *entry = data;
372
373 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100374}
375
376static void
377text_model_keysym(void *data,
378 struct text_model *text_model,
379 uint32_t serial,
380 uint32_t time,
381 uint32_t key,
382 uint32_t state,
383 uint32_t modifiers)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200384{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200385 struct text_entry *entry = data;
Jan Arne Petersencd997062012-11-18 19:06:44 +0100386 const char *state_label = "release";
387 const char *key_label = "Unknown";
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100388 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200389
390 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
391 state_label = "pressed";
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200392 }
393
Jan Arne Petersencd997062012-11-18 19:06:44 +0100394 if (key == XKB_KEY_Left ||
395 key == XKB_KEY_Right) {
396 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
397 return;
398
399 if (key == XKB_KEY_Left)
400 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
401 else
402 new_char = utf8_next_char(entry->text + entry->cursor);
403
404 if (new_char != NULL) {
405 entry->cursor = new_char - entry->text;
406 if (!(modifiers & entry->keysym.shift_mask))
407 entry->anchor = entry->cursor;
408 widget_schedule_redraw(entry->widget);
409 }
410
411 return;
412 }
413
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200414 switch (key) {
415 case XKB_KEY_Tab:
416 key_label = "Tab";
417 break;
418 case XKB_KEY_KP_Enter:
Jan Arne Petersencd997062012-11-18 19:06:44 +0100419 case XKB_KEY_Return:
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200420 key_label = "Enter";
421 break;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200422 }
423
424 fprintf(stderr, "%s key was %s.\n", key_label, state_label);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200425}
426
427static void
428text_model_selection_replacement(void *data,
429 struct text_model *text_model)
430{
431}
432
433static void
434text_model_direction(void *data,
435 struct text_model *text_model)
436{
437}
438
439static void
440text_model_locale(void *data,
441 struct text_model *text_model)
442{
443}
444
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200445static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200446text_model_enter(void *data,
447 struct text_model *text_model,
448 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200449{
450 struct text_entry *entry = data;
451
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200452 if (surface != window_get_wl_surface(entry->window))
453 return;
454
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200455 entry->active = 1;
456
457 widget_schedule_redraw(entry->widget);
458}
459
460static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200461text_model_leave(void *data,
462 struct text_model *text_model)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200463{
464 struct text_entry *entry = data;
465
466 entry->active = 0;
467
468 widget_schedule_redraw(entry->widget);
469}
470
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200471static const struct text_model_listener text_model_listener = {
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200472 text_model_commit_string,
473 text_model_preedit_string,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200474 text_model_delete_surrounding_text,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200475 text_model_preedit_styling,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100476 text_model_modifiers_map,
477 text_model_keysym,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200478 text_model_selection_replacement,
479 text_model_direction,
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200480 text_model_locale,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200481 text_model_enter,
482 text_model_leave
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200483};
484
485static struct text_entry*
486text_entry_create(struct editor *editor, const char *text)
487{
488 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200489
Jan Arne Petersencd997062012-11-18 19:06:44 +0100490 entry = calloc(1, sizeof *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200491
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200492 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200493 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200494 entry->text = strdup(text);
495 entry->active = 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200496 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200497 entry->anchor = entry->cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200498 entry->preedit_text = NULL;
499 entry->preedit_cursor = 0;
Jan Arne Petersen4c265182012-09-09 23:08:30 +0200500 entry->model = text_model_factory_create_text_model(editor->text_model_factory);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200501 text_model_add_listener(entry->model, &text_model_listener, entry);
502
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200503 entry->layout = text_layout_create();
504 text_layout_set_text(entry->layout, entry->text);
505
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200506 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
507 widget_set_button_handler(entry->widget, text_entry_button_handler);
508
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200509 return entry;
510}
511
512static void
513text_entry_destroy(struct text_entry *entry)
514{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200515 widget_destroy(entry->widget);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200516 text_model_destroy(entry->model);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200517 text_layout_destroy(entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200518 free(entry->text);
519 free(entry);
520}
521
522static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200523redraw_handler(struct widget *widget, void *data)
524{
525 struct editor *editor = data;
526 cairo_surface_t *surface;
527 struct rectangle allocation;
528 cairo_t *cr;
529
530 surface = window_get_surface(editor->window);
531 widget_get_allocation(editor->widget, &allocation);
532
533 cr = cairo_create(surface);
534 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
535 cairo_clip(cr);
536
537 cairo_translate(cr, allocation.x, allocation.y);
538
539 /* Draw background */
540 cairo_push_group(cr);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200541 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200542 cairo_set_source_rgba(cr, 1, 1, 1, 1);
543 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
544 cairo_fill(cr);
545
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200546 cairo_pop_group_to_source(cr);
547 cairo_paint(cr);
548
549 cairo_destroy(cr);
550 cairo_surface_destroy(surface);
551}
552
553static void
554text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
555 int32_t width, int32_t height)
556{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200557 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200558}
559
560static void
561resize_handler(struct widget *widget,
562 int32_t width, int32_t height, void *data)
563{
564 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200565 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200566
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200567 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200568
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200569 text_entry_allocate(editor->entry,
570 allocation.x + 20, allocation.y + 20,
571 width - 40, height / 2 - 40);
572 text_entry_allocate(editor->editor,
573 allocation.x + 20, allocation.y + height / 2 + 20,
574 width - 40, height / 2 - 40);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200575}
576
577static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200578text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200579 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200580{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200581 struct wl_surface *surface = window_get_wl_surface(entry->window);
582
583 text_model_activate(entry->model,
584 seat,
585 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200586}
587
588static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200589text_entry_deactivate(struct text_entry *entry,
590 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200591{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200592 text_model_deactivate(entry->model,
593 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200594}
595
596static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200597text_entry_update_layout(struct text_entry *entry)
598{
599 char *text;
600
Philipp Brüschweiler237358b2012-10-02 11:06:51 +0200601 assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
602 (entry->preedit_text ? strlen(entry->preedit_text) : 0));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200603
604 if (!entry->preedit_text) {
605 text_layout_set_text(entry->layout, entry->text);
606 return;
607 }
608
609 text = malloc(strlen(entry->text) + strlen(entry->preedit_text) + 1);
610 strncpy(text, entry->text, entry->cursor);
611 strcpy(text + entry->cursor, entry->preedit_text);
612 strcpy(text + entry->cursor + strlen(entry->preedit_text),
613 entry->text + entry->cursor);
614
615 text_layout_set_text(entry->layout, text);
616 free(text);
617
618 widget_schedule_redraw(entry->widget);
Jan Arne Petersencb08f4d2012-09-09 23:08:40 +0200619
620 text_model_set_surrounding_text(entry->model,
621 entry->text,
622 entry->cursor,
623 entry->anchor);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200624}
625
626static void
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200627text_entry_insert_at_cursor(struct text_entry *entry, const char *text)
628{
629 char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
630
631 strncpy(new_text, entry->text, entry->cursor);
632 strcpy(new_text + entry->cursor, text);
633 strcpy(new_text + entry->cursor + strlen(text),
634 entry->text + entry->cursor);
635
636 free(entry->text);
637 entry->text = new_text;
638 entry->cursor += strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200639 entry->anchor += strlen(text);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200640
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200641 text_entry_update_layout(entry);
642}
643
644static void
645text_entry_set_preedit(struct text_entry *entry,
646 const char *preedit_text,
647 int preedit_cursor)
648{
649 if (entry->preedit_text) {
650 free(entry->preedit_text);
651 entry->preedit_text = NULL;
652 entry->preedit_cursor = 0;
653 }
654
655 if (!preedit_text)
656 return;
657
658 entry->preedit_text = strdup(preedit_text);
659 entry->preedit_cursor = preedit_cursor;
660
661 text_entry_update_layout(entry);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200662}
663
664static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200665text_entry_set_cursor_position(struct text_entry *entry,
666 int32_t x, int32_t y)
667{
668 entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
669
Jan Arne Petersenc1e481e2012-09-09 23:08:46 +0200670 text_model_reset(entry->model);
671
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200672 if (entry->cursor >= entry->preedit_cursor) {
673 entry->cursor -= entry->preedit_cursor;
674 }
675
676 text_entry_update_layout(entry);
677
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200678 widget_schedule_redraw(entry->widget);
679}
680
681static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200682text_entry_set_anchor_position(struct text_entry *entry,
683 int32_t x, int32_t y)
684{
685 entry->anchor = text_layout_xy_to_index(entry->layout, x, y);
686
687 widget_schedule_redraw(entry->widget);
688}
689
690static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200691text_entry_delete_text(struct text_entry *entry,
692 uint32_t index, uint32_t length)
693{
694 if (entry->cursor > index)
695 entry->cursor -= length;
696
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +0200697 entry->anchor = entry->cursor;
698
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200699 entry->text[index] = '\0';
700 strcat(entry->text, entry->text + index + length);
701
702 text_entry_update_layout(entry);
703
704 widget_schedule_redraw(entry->widget);
705}
706
707static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200708text_entry_delete_selected_text(struct text_entry *entry)
709{
710 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
711 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
712
713 if (entry->anchor == entry->cursor)
714 return;
715
716 text_entry_delete_text(entry, start_index, end_index - start_index);
717
718 entry->anchor = entry->cursor;
719}
720
721static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200722text_entry_draw_selection(struct text_entry *entry, cairo_t *cr)
723{
724 cairo_text_extents_t extents;
725 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
726 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
727 cairo_rectangle_t start;
728 cairo_rectangle_t end;
729
730 if (entry->anchor == entry->cursor)
731 return;
732
733 text_layout_extents(entry->layout, &extents);
734
735 text_layout_index_to_pos(entry->layout, start_index, &start);
736 text_layout_index_to_pos(entry->layout, end_index, &end);
737
738 cairo_save (cr);
739
Philipp Brüschweilerb8911dc2012-10-02 11:06:52 +0200740 cairo_set_source_rgba(cr, 0.3, 0.3, 1.0, 0.5);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200741 cairo_rectangle(cr,
742 start.x, extents.y_bearing + extents.height + 2,
743 end.x - start.x, -extents.height - 4);
744 cairo_fill(cr);
745
746 cairo_rectangle(cr,
747 start.x, extents.y_bearing + extents.height,
748 end.x - start.x, -extents.height);
749 cairo_clip(cr);
750 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
751 text_layout_draw(entry->layout, cr);
752
753 cairo_restore (cr);
754}
755
756static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200757text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
758{
759 cairo_text_extents_t extents;
760 cairo_rectangle_t cursor_pos;
761
762 text_layout_extents(entry->layout, &extents);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200763 text_layout_get_cursor_pos(entry->layout,
764 entry->cursor + entry->preedit_cursor,
765 &cursor_pos);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200766
767 cairo_set_line_width(cr, 1.0);
768 cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
769 cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
770 cairo_stroke(cr);
771}
772
773static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200774text_entry_draw_preedit(struct text_entry *entry, cairo_t *cr)
775{
776 cairo_text_extents_t extents;
777 cairo_rectangle_t start;
778 cairo_rectangle_t end;
779
780 if (!entry->preedit_text)
781 return;
782
783 text_layout_extents(entry->layout, &extents);
784
785 text_layout_index_to_pos(entry->layout, entry->cursor, &start);
786 text_layout_index_to_pos(entry->layout,
787 entry->cursor + strlen(entry->preedit_text),
788 &end);
789
790 cairo_save (cr);
791
792 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
793 cairo_rectangle(cr,
794 start.x, 0,
795 end.x - start.x, 1);
796 cairo_fill(cr);
797
798 cairo_restore (cr);
799}
800
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200801static const int text_offset_left = 10;
802
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200803static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200804text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200805{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200806 struct text_entry *entry = data;
807 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200808 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200809 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200810
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200811 surface = window_get_surface(entry->window);
812 widget_get_allocation(entry->widget, &allocation);
813
814 cr = cairo_create(surface);
815 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
816 cairo_clip(cr);
817
818 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
819
820 cairo_push_group(cr);
821 cairo_translate(cr, allocation.x, allocation.y);
822
823 cairo_set_source_rgba(cr, 1, 1, 1, 1);
824 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
825 cairo_fill(cr);
826
827 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
828
829 if (entry->active) {
830 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
831 cairo_set_line_width (cr, 3);
832 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
833 cairo_stroke(cr);
834 }
835
836 cairo_set_source_rgba(cr, 0, 0, 0, 1);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200837
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200838 cairo_translate(cr, text_offset_left, allocation.height / 2);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200839 text_layout_draw(entry->layout, cr);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200840
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200841 text_entry_draw_selection(entry, cr);
842
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200843 text_entry_draw_cursor(entry, cr);
844
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200845 text_entry_draw_preedit(entry, cr);
846
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200847 cairo_pop_group_to_source(cr);
848 cairo_paint(cr);
849
850 cairo_destroy(cr);
851 cairo_surface_destroy(surface);
852}
853
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200854static int
855text_entry_motion_handler(struct widget *widget,
856 struct input *input, uint32_t time,
857 float x, float y, void *data)
858{
859 struct text_entry *entry = data;
860 struct rectangle allocation;
861
862 widget_get_allocation(entry->widget, &allocation);
863
864 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200865 x - allocation.x - text_offset_left,
866 y - allocation.y - text_offset_left);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200867
868 return CURSOR_IBEAM;
869}
870
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200871static void
872text_entry_button_handler(struct widget *widget,
873 struct input *input, uint32_t time,
874 uint32_t button,
875 enum wl_pointer_button_state state, void *data)
876{
877 struct text_entry *entry = data;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200878 struct rectangle allocation;
879 int32_t x, y;
880
881 widget_get_allocation(entry->widget, &allocation);
882 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200883
884 if (button != BTN_LEFT) {
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200885 return;
886 }
887
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200888 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200889 x - allocation.x - text_offset_left,
890 y - allocation.y - text_offset_left);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200891
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200892 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
893 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200894
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200895 text_entry_activate(entry, seat);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200896
897 text_entry_set_anchor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200898 x - allocation.x - text_offset_left,
899 y - allocation.y - text_offset_left);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200900
901 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
902 } else {
903 widget_set_motion_handler(entry->widget, NULL);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200904 }
905}
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200906
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200907static void
908editor_button_handler(struct widget *widget,
909 struct input *input, uint32_t time,
910 uint32_t button,
911 enum wl_pointer_button_state state, void *data)
912{
913 struct editor *editor = data;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200914
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200915 if (button != BTN_LEFT) {
916 return;
917 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200918
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200919 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
920 struct wl_seat *seat = input_get_seat(input);
921
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200922 text_entry_deactivate(editor->entry, seat);
923 text_entry_deactivate(editor->editor, seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200924 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200925}
926
927static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400928global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200929 const char *interface, uint32_t version, void *data)
930{
931 struct editor *editor = data;
932
Jan Arne Petersen51963742012-08-10 16:47:20 +0200933 if (!strcmp(interface, "text_model_factory")) {
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400934 editor->text_model_factory =
935 display_bind(display, name,
936 &text_model_factory_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200937 }
938}
939
940int
941main(int argc, char *argv[])
942{
943 struct editor editor;
944
Jan Arne Petersen25f6db52012-11-05 03:26:40 +0100945 memset(&editor, 0, sizeof editor);
946
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200947 editor.display = display_create(argc, argv);
948 if (editor.display == NULL) {
949 fprintf(stderr, "failed to create display: %m\n");
950 return -1;
951 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200952
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400953 display_set_user_data(editor.display, &editor);
954 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200955
956 editor.window = window_create(editor.display);
957 editor.widget = frame_create(editor.window, &editor);
958
959 editor.entry = text_entry_create(&editor, "Entry");
960 editor.editor = text_entry_create(&editor, "Editor");
961
962 window_set_title(editor.window, "Text Editor");
963
964 widget_set_redraw_handler(editor.widget, redraw_handler);
965 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200966 widget_set_button_handler(editor.widget, editor_button_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200967
968 window_schedule_resize(editor.window, 500, 400);
969
970 display_run(editor.display);
971
972 text_entry_destroy(editor.entry);
973 text_entry_destroy(editor.editor);
974
975 return 0;
976}