blob: 24ccace31a8ce04cd53635d4e0075815e74d932c [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 Petersencba9e472012-06-21 21:52:19 +020058};
59
60struct editor {
Jan Arne Petersen51963742012-08-10 16:47:20 +020061 struct text_model_factory *text_model_factory;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020062 struct display *display;
63 struct window *window;
64 struct widget *widget;
65 struct text_entry *entry;
66 struct text_entry *editor;
67};
68
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010069static const char *
70utf8_start_char(const char *text, const char *p)
71{
72 for (; p >= text; --p) {
73 if ((*p & 0xc0) != 0x80)
74 return p;
75 }
76 return NULL;
77}
78
79static const char *
80utf8_prev_char(const char *text, const char *p)
81{
82 if (p > text)
83 return utf8_start_char(text, --p);
84 return NULL;
85}
86
87static const char *
88utf8_end_char(const char *p)
89{
90 while ((*p & 0xc0) == 0x80)
91 p++;
92 return p;
93}
94
95static const char *
96utf8_next_char(const char *p)
97{
98 if (*p != 0)
99 return utf8_end_char(++p);
100 return NULL;
101}
102
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200103static struct text_layout *
104text_layout_create(void)
105{
106 struct text_layout *layout;
107 cairo_surface_t *surface;
108 cairo_t *cr;
109
110 layout = malloc(sizeof *layout);
111 if (!layout)
112 return NULL;
113
114 layout->glyphs = NULL;
115 layout->num_glyphs = 0;
116
117 layout->clusters = NULL;
118 layout->num_clusters = 0;
119
120 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
121 cr = cairo_create(surface);
122 cairo_set_font_size(cr, font_size);
123 cairo_select_font_face(cr, font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
124 layout->font = cairo_get_scaled_font(cr);
125 cairo_scaled_font_reference(layout->font);
126
127 cairo_destroy(cr);
128 cairo_surface_destroy(surface);
129
130 return layout;
131}
132
133static void
134text_layout_destroy(struct text_layout *layout)
135{
136 if (layout->glyphs)
137 cairo_glyph_free(layout->glyphs);
138
139 if (layout->clusters)
140 cairo_text_cluster_free(layout->clusters);
141
142 cairo_scaled_font_destroy(layout->font);
143
144 free(layout);
145}
146
147static void
148text_layout_set_text(struct text_layout *layout,
149 const char *text)
150{
151 if (layout->glyphs)
152 cairo_glyph_free(layout->glyphs);
153
154 if (layout->clusters)
155 cairo_text_cluster_free(layout->clusters);
156
157 layout->glyphs = NULL;
158 layout->num_glyphs = 0;
159 layout->clusters = NULL;
160 layout->num_clusters = 0;
161
162 cairo_scaled_font_text_to_glyphs(layout->font, 0, 0, text, -1,
163 &layout->glyphs, &layout->num_glyphs,
164 &layout->clusters, &layout->num_clusters,
165 &layout->cluster_flags);
166}
167
168static void
169text_layout_draw(struct text_layout *layout, cairo_t *cr)
170{
171 cairo_save(cr);
172 cairo_set_scaled_font(cr, layout->font);
173 cairo_show_glyphs(cr, layout->glyphs, layout->num_glyphs);
174 cairo_restore(cr);
175}
176
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200177static void
178text_layout_extents(struct text_layout *layout, cairo_text_extents_t *extents)
179{
180 cairo_scaled_font_glyph_extents(layout->font,
181 layout->glyphs, layout->num_glyphs,
182 extents);
183}
184
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100185static uint32_t
186bytes_from_glyphs(struct text_layout *layout, uint32_t index)
187{
188 int i;
189 uint32_t glyphs = 0, bytes = 0;
190
191 for (i = 0; i < layout->num_clusters && glyphs < index; i++) {
192 bytes += layout->clusters[i].num_bytes;
193 glyphs += layout->clusters[i].num_glyphs;
194 }
195
196 return bytes;
197}
198
199static uint32_t
200glyphs_from_bytes(struct text_layout *layout, uint32_t index)
201{
202 int i;
203 uint32_t glyphs = 0, bytes = 0;
204
205 for (i = 0; i < layout->num_clusters && bytes < index; i++) {
206 bytes += layout->clusters[i].num_bytes;
207 glyphs += layout->clusters[i].num_glyphs;
208 }
209
210 return glyphs;
211}
212
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200213static int
214text_layout_xy_to_index(struct text_layout *layout, double x, double y)
215{
216 cairo_text_extents_t extents;
217 int i;
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200218 double d;
219
220 if (layout->num_glyphs == 0)
221 return 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200222
223 cairo_scaled_font_glyph_extents(layout->font,
224 layout->glyphs, layout->num_glyphs,
225 &extents);
226
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200227 if (x < 0)
228 return 0;
229
230 for (i = 0; i < layout->num_glyphs - 1; ++i) {
231 d = layout->glyphs[i + 1].x - layout->glyphs[i].x;
232 if (x < layout->glyphs[i].x + d/2)
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100233 return bytes_from_glyphs(layout, i);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200234 }
235
Philipp Brüschweiler70f83672012-10-02 11:06:54 +0200236 d = extents.width - layout->glyphs[layout->num_glyphs - 1].x;
237 if (x < layout->glyphs[layout->num_glyphs - 1].x + d/2)
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100238 return bytes_from_glyphs(layout, layout->num_glyphs - 1);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200239
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100240 return bytes_from_glyphs(layout, layout->num_glyphs);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200241}
242
243static void
244text_layout_index_to_pos(struct text_layout *layout, uint32_t index, cairo_rectangle_t *pos)
245{
246 cairo_text_extents_t extents;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100247 int glyph_index = glyphs_from_bytes(layout, index);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200248
249 if (!pos)
250 return;
251
252 cairo_scaled_font_glyph_extents(layout->font,
253 layout->glyphs, layout->num_glyphs,
254 &extents);
255
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100256 if (glyph_index >= layout->num_glyphs) {
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200257 pos->x = extents.x_advance;
258 pos->y = layout->num_glyphs ? layout->glyphs[layout->num_glyphs - 1].y : 0;
259 pos->width = 1;
260 pos->height = extents.height;
261 return;
262 }
263
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100264 pos->x = layout->glyphs[glyph_index].x;
265 pos->y = layout->glyphs[glyph_index].y;
266 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 +0200267 pos->height = extents.height;
268}
269
270static void
271text_layout_get_cursor_pos(struct text_layout *layout, int index, cairo_rectangle_t *pos)
272{
273 text_layout_index_to_pos(layout, index, pos);
274 pos->width = 1;
275}
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200276
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200277static void text_entry_redraw_handler(struct widget *widget, void *data);
278static void text_entry_button_handler(struct widget *widget,
279 struct input *input, uint32_t time,
280 uint32_t button,
281 enum wl_pointer_button_state state, void *data);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200282static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200283static void text_entry_set_preedit(struct text_entry *entry,
284 const char *preedit_text,
285 int preedit_cursor);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200286static void text_entry_delete_text(struct text_entry *entry,
287 uint32_t index, uint32_t length);
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200288static void text_entry_delete_selected_text(struct text_entry *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200289
290static void
291text_model_commit_string(void *data,
292 struct text_model *text_model,
293 const char *text,
294 uint32_t index)
295{
296 struct text_entry *entry = data;
297
John Kåre Alsaker011a1ce2012-10-12 12:25:06 +0200298 if (index > strlen(text))
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200299 fprintf(stderr, "Invalid cursor index %d\n", index);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200300
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200301 text_entry_delete_selected_text(entry);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200302 text_entry_insert_at_cursor(entry, text);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200303
304 widget_schedule_redraw(entry->widget);
305}
306
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200307static void
308text_model_preedit_string(void *data,
309 struct text_model *text_model,
310 const char *text,
311 uint32_t index)
312{
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200313 struct text_entry *entry = data;
314
315 if (index > strlen(text)) {
316 fprintf(stderr, "Invalid cursor index %d\n", index);
317 index = strlen(text);
318 }
319
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200320 text_entry_delete_selected_text(entry);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200321 text_entry_set_preedit(entry, text, index);
322
323 widget_schedule_redraw(entry->widget);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200324}
325
326static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200327text_model_delete_surrounding_text(void *data,
328 struct text_model *text_model,
329 int32_t index,
330 uint32_t length)
331{
332 struct text_entry *entry = data;
333 uint32_t cursor_index = index + entry->cursor;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100334 const char *start, *end;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200335
336 if (cursor_index > strlen(entry->text)) {
337 fprintf(stderr, "Invalid cursor index %d\n", index);
338 return;
339 }
340
341 if (cursor_index + length > strlen(entry->text)) {
342 fprintf(stderr, "Invalid length %d\n", length);
343 return;
344 }
345
346 if (length == 0)
347 return;
348
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100349 start = utf8_start_char(entry->text, entry->text + cursor_index);
350 end = utf8_end_char(entry->text + cursor_index + length);
351
352 text_entry_delete_text(entry,
353 start - entry->text,
354 end - start);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200355}
356
357static void
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200358text_model_preedit_styling(void *data,
359 struct text_model *text_model)
360{
361}
362
363static void
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100364text_model_modifiers_map(void *data,
365 struct text_model *text_model,
366 struct wl_array *map)
367{
368}
369
370static void
371text_model_keysym(void *data,
372 struct text_model *text_model,
373 uint32_t serial,
374 uint32_t time,
375 uint32_t key,
376 uint32_t state,
377 uint32_t modifiers)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200378{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200379 struct text_entry *entry = data;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200380 const char *state_label;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200381 const char *key_label = "released";
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100382 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200383
384 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
385 state_label = "pressed";
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200386 }
387
388 switch (key) {
389 case XKB_KEY_Tab:
390 key_label = "Tab";
391 break;
392 case XKB_KEY_KP_Enter:
393 key_label = "Enter";
394 break;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200395 case XKB_KEY_Left:
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100396 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
397 if (new_char != NULL) {
398 entry->cursor = new_char - entry->text;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200399 entry->anchor = entry->cursor;
400 widget_schedule_redraw(entry->widget);
401 }
402 break;
403 case XKB_KEY_Right:
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100404 new_char = utf8_next_char(entry->text + entry->cursor);
405 if (new_char != NULL) {
406 entry->cursor = new_char - entry->text;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200407 entry->anchor = entry->cursor;
408 widget_schedule_redraw(entry->widget);
409 }
410 break;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200411 default:
412 key_label = "Unknown";
413 }
414
415 fprintf(stderr, "%s key was %s.\n", key_label, state_label);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200416}
417
418static void
419text_model_selection_replacement(void *data,
420 struct text_model *text_model)
421{
422}
423
424static void
425text_model_direction(void *data,
426 struct text_model *text_model)
427{
428}
429
430static void
431text_model_locale(void *data,
432 struct text_model *text_model)
433{
434}
435
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200436static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200437text_model_enter(void *data,
438 struct text_model *text_model,
439 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200440{
441 struct text_entry *entry = data;
442
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200443 if (surface != window_get_wl_surface(entry->window))
444 return;
445
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200446 entry->active = 1;
447
448 widget_schedule_redraw(entry->widget);
449}
450
451static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200452text_model_leave(void *data,
453 struct text_model *text_model)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200454{
455 struct text_entry *entry = data;
456
457 entry->active = 0;
458
459 widget_schedule_redraw(entry->widget);
460}
461
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200462static const struct text_model_listener text_model_listener = {
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200463 text_model_commit_string,
464 text_model_preedit_string,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200465 text_model_delete_surrounding_text,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200466 text_model_preedit_styling,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100467 text_model_modifiers_map,
468 text_model_keysym,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200469 text_model_selection_replacement,
470 text_model_direction,
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200471 text_model_locale,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200472 text_model_enter,
473 text_model_leave
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200474};
475
476static struct text_entry*
477text_entry_create(struct editor *editor, const char *text)
478{
479 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200480
481 entry = malloc(sizeof *entry);
482
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200483 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200484 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200485 entry->text = strdup(text);
486 entry->active = 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200487 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200488 entry->anchor = entry->cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200489 entry->preedit_text = NULL;
490 entry->preedit_cursor = 0;
Jan Arne Petersen4c265182012-09-09 23:08:30 +0200491 entry->model = text_model_factory_create_text_model(editor->text_model_factory);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200492 text_model_add_listener(entry->model, &text_model_listener, entry);
493
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200494 entry->layout = text_layout_create();
495 text_layout_set_text(entry->layout, entry->text);
496
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200497 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
498 widget_set_button_handler(entry->widget, text_entry_button_handler);
499
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200500 return entry;
501}
502
503static void
504text_entry_destroy(struct text_entry *entry)
505{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200506 widget_destroy(entry->widget);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200507 text_model_destroy(entry->model);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200508 text_layout_destroy(entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200509 free(entry->text);
510 free(entry);
511}
512
513static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200514redraw_handler(struct widget *widget, void *data)
515{
516 struct editor *editor = data;
517 cairo_surface_t *surface;
518 struct rectangle allocation;
519 cairo_t *cr;
520
521 surface = window_get_surface(editor->window);
522 widget_get_allocation(editor->widget, &allocation);
523
524 cr = cairo_create(surface);
525 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
526 cairo_clip(cr);
527
528 cairo_translate(cr, allocation.x, allocation.y);
529
530 /* Draw background */
531 cairo_push_group(cr);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200532 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200533 cairo_set_source_rgba(cr, 1, 1, 1, 1);
534 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
535 cairo_fill(cr);
536
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200537 cairo_pop_group_to_source(cr);
538 cairo_paint(cr);
539
540 cairo_destroy(cr);
541 cairo_surface_destroy(surface);
542}
543
544static void
545text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
546 int32_t width, int32_t height)
547{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200548 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200549}
550
551static void
552resize_handler(struct widget *widget,
553 int32_t width, int32_t height, void *data)
554{
555 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200556 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200557
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200558 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200559
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200560 text_entry_allocate(editor->entry,
561 allocation.x + 20, allocation.y + 20,
562 width - 40, height / 2 - 40);
563 text_entry_allocate(editor->editor,
564 allocation.x + 20, allocation.y + height / 2 + 20,
565 width - 40, height / 2 - 40);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200566}
567
568static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200569text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200570 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200571{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200572 struct wl_surface *surface = window_get_wl_surface(entry->window);
573
574 text_model_activate(entry->model,
575 seat,
576 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200577}
578
579static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200580text_entry_deactivate(struct text_entry *entry,
581 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200582{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200583 text_model_deactivate(entry->model,
584 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200585}
586
587static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200588text_entry_update_layout(struct text_entry *entry)
589{
590 char *text;
591
Philipp Brüschweiler237358b2012-10-02 11:06:51 +0200592 assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
593 (entry->preedit_text ? strlen(entry->preedit_text) : 0));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200594
595 if (!entry->preedit_text) {
596 text_layout_set_text(entry->layout, entry->text);
597 return;
598 }
599
600 text = malloc(strlen(entry->text) + strlen(entry->preedit_text) + 1);
601 strncpy(text, entry->text, entry->cursor);
602 strcpy(text + entry->cursor, entry->preedit_text);
603 strcpy(text + entry->cursor + strlen(entry->preedit_text),
604 entry->text + entry->cursor);
605
606 text_layout_set_text(entry->layout, text);
607 free(text);
608
609 widget_schedule_redraw(entry->widget);
Jan Arne Petersencb08f4d2012-09-09 23:08:40 +0200610
611 text_model_set_surrounding_text(entry->model,
612 entry->text,
613 entry->cursor,
614 entry->anchor);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200615}
616
617static void
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200618text_entry_insert_at_cursor(struct text_entry *entry, const char *text)
619{
620 char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
621
622 strncpy(new_text, entry->text, entry->cursor);
623 strcpy(new_text + entry->cursor, text);
624 strcpy(new_text + entry->cursor + strlen(text),
625 entry->text + entry->cursor);
626
627 free(entry->text);
628 entry->text = new_text;
629 entry->cursor += strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200630 entry->anchor += strlen(text);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200631
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200632 text_entry_update_layout(entry);
633}
634
635static void
636text_entry_set_preedit(struct text_entry *entry,
637 const char *preedit_text,
638 int preedit_cursor)
639{
640 if (entry->preedit_text) {
641 free(entry->preedit_text);
642 entry->preedit_text = NULL;
643 entry->preedit_cursor = 0;
644 }
645
646 if (!preedit_text)
647 return;
648
649 entry->preedit_text = strdup(preedit_text);
650 entry->preedit_cursor = preedit_cursor;
651
652 text_entry_update_layout(entry);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200653}
654
655static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200656text_entry_set_cursor_position(struct text_entry *entry,
657 int32_t x, int32_t y)
658{
659 entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
660
Jan Arne Petersenc1e481e2012-09-09 23:08:46 +0200661 text_model_reset(entry->model);
662
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200663 if (entry->cursor >= entry->preedit_cursor) {
664 entry->cursor -= entry->preedit_cursor;
665 }
666
667 text_entry_update_layout(entry);
668
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200669 widget_schedule_redraw(entry->widget);
670}
671
672static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200673text_entry_set_anchor_position(struct text_entry *entry,
674 int32_t x, int32_t y)
675{
676 entry->anchor = text_layout_xy_to_index(entry->layout, x, y);
677
678 widget_schedule_redraw(entry->widget);
679}
680
681static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200682text_entry_delete_text(struct text_entry *entry,
683 uint32_t index, uint32_t length)
684{
685 if (entry->cursor > index)
686 entry->cursor -= length;
687
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +0200688 entry->anchor = entry->cursor;
689
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200690 entry->text[index] = '\0';
691 strcat(entry->text, entry->text + index + length);
692
693 text_entry_update_layout(entry);
694
695 widget_schedule_redraw(entry->widget);
696}
697
698static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200699text_entry_delete_selected_text(struct text_entry *entry)
700{
701 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
702 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
703
704 if (entry->anchor == entry->cursor)
705 return;
706
707 text_entry_delete_text(entry, start_index, end_index - start_index);
708
709 entry->anchor = entry->cursor;
710}
711
712static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200713text_entry_draw_selection(struct text_entry *entry, cairo_t *cr)
714{
715 cairo_text_extents_t extents;
716 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
717 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
718 cairo_rectangle_t start;
719 cairo_rectangle_t end;
720
721 if (entry->anchor == entry->cursor)
722 return;
723
724 text_layout_extents(entry->layout, &extents);
725
726 text_layout_index_to_pos(entry->layout, start_index, &start);
727 text_layout_index_to_pos(entry->layout, end_index, &end);
728
729 cairo_save (cr);
730
Philipp Brüschweilerb8911dc2012-10-02 11:06:52 +0200731 cairo_set_source_rgba(cr, 0.3, 0.3, 1.0, 0.5);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200732 cairo_rectangle(cr,
733 start.x, extents.y_bearing + extents.height + 2,
734 end.x - start.x, -extents.height - 4);
735 cairo_fill(cr);
736
737 cairo_rectangle(cr,
738 start.x, extents.y_bearing + extents.height,
739 end.x - start.x, -extents.height);
740 cairo_clip(cr);
741 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
742 text_layout_draw(entry->layout, cr);
743
744 cairo_restore (cr);
745}
746
747static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200748text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
749{
750 cairo_text_extents_t extents;
751 cairo_rectangle_t cursor_pos;
752
753 text_layout_extents(entry->layout, &extents);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200754 text_layout_get_cursor_pos(entry->layout,
755 entry->cursor + entry->preedit_cursor,
756 &cursor_pos);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200757
758 cairo_set_line_width(cr, 1.0);
759 cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
760 cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
761 cairo_stroke(cr);
762}
763
764static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200765text_entry_draw_preedit(struct text_entry *entry, cairo_t *cr)
766{
767 cairo_text_extents_t extents;
768 cairo_rectangle_t start;
769 cairo_rectangle_t end;
770
771 if (!entry->preedit_text)
772 return;
773
774 text_layout_extents(entry->layout, &extents);
775
776 text_layout_index_to_pos(entry->layout, entry->cursor, &start);
777 text_layout_index_to_pos(entry->layout,
778 entry->cursor + strlen(entry->preedit_text),
779 &end);
780
781 cairo_save (cr);
782
783 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
784 cairo_rectangle(cr,
785 start.x, 0,
786 end.x - start.x, 1);
787 cairo_fill(cr);
788
789 cairo_restore (cr);
790}
791
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200792static const int text_offset_left = 10;
793
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200794static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200795text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200796{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200797 struct text_entry *entry = data;
798 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200799 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200800 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200801
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200802 surface = window_get_surface(entry->window);
803 widget_get_allocation(entry->widget, &allocation);
804
805 cr = cairo_create(surface);
806 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
807 cairo_clip(cr);
808
809 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
810
811 cairo_push_group(cr);
812 cairo_translate(cr, allocation.x, allocation.y);
813
814 cairo_set_source_rgba(cr, 1, 1, 1, 1);
815 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
816 cairo_fill(cr);
817
818 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
819
820 if (entry->active) {
821 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
822 cairo_set_line_width (cr, 3);
823 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
824 cairo_stroke(cr);
825 }
826
827 cairo_set_source_rgba(cr, 0, 0, 0, 1);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200828
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200829 cairo_translate(cr, text_offset_left, allocation.height / 2);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200830 text_layout_draw(entry->layout, cr);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200831
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200832 text_entry_draw_selection(entry, cr);
833
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200834 text_entry_draw_cursor(entry, cr);
835
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200836 text_entry_draw_preedit(entry, cr);
837
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200838 cairo_pop_group_to_source(cr);
839 cairo_paint(cr);
840
841 cairo_destroy(cr);
842 cairo_surface_destroy(surface);
843}
844
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200845static int
846text_entry_motion_handler(struct widget *widget,
847 struct input *input, uint32_t time,
848 float x, float y, void *data)
849{
850 struct text_entry *entry = data;
851 struct rectangle allocation;
852
853 widget_get_allocation(entry->widget, &allocation);
854
855 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200856 x - allocation.x - text_offset_left,
857 y - allocation.y - text_offset_left);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200858
859 return CURSOR_IBEAM;
860}
861
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200862static void
863text_entry_button_handler(struct widget *widget,
864 struct input *input, uint32_t time,
865 uint32_t button,
866 enum wl_pointer_button_state state, void *data)
867{
868 struct text_entry *entry = data;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200869 struct rectangle allocation;
870 int32_t x, y;
871
872 widget_get_allocation(entry->widget, &allocation);
873 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200874
875 if (button != BTN_LEFT) {
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200876 return;
877 }
878
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200879 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200880 x - allocation.x - text_offset_left,
881 y - allocation.y - text_offset_left);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200882
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200883 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
884 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200885
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200886 text_entry_activate(entry, seat);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200887
888 text_entry_set_anchor_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 Petersen0e5bd452012-09-09 23:08:38 +0200891
892 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
893 } else {
894 widget_set_motion_handler(entry->widget, NULL);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200895 }
896}
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200897
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200898static void
899editor_button_handler(struct widget *widget,
900 struct input *input, uint32_t time,
901 uint32_t button,
902 enum wl_pointer_button_state state, void *data)
903{
904 struct editor *editor = data;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200905
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200906 if (button != BTN_LEFT) {
907 return;
908 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200909
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200910 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
911 struct wl_seat *seat = input_get_seat(input);
912
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200913 text_entry_deactivate(editor->entry, seat);
914 text_entry_deactivate(editor->editor, seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200915 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200916}
917
918static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400919global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200920 const char *interface, uint32_t version, void *data)
921{
922 struct editor *editor = data;
923
Jan Arne Petersen51963742012-08-10 16:47:20 +0200924 if (!strcmp(interface, "text_model_factory")) {
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400925 editor->text_model_factory =
926 display_bind(display, name,
927 &text_model_factory_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200928 }
929}
930
931int
932main(int argc, char *argv[])
933{
934 struct editor editor;
935
Jan Arne Petersen25f6db52012-11-05 03:26:40 +0100936 memset(&editor, 0, sizeof editor);
937
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200938 editor.display = display_create(argc, argv);
939 if (editor.display == NULL) {
940 fprintf(stderr, "failed to create display: %m\n");
941 return -1;
942 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200943
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400944 display_set_user_data(editor.display, &editor);
945 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200946
947 editor.window = window_create(editor.display);
948 editor.widget = frame_create(editor.window, &editor);
949
950 editor.entry = text_entry_create(&editor, "Entry");
951 editor.editor = text_entry_create(&editor, "Editor");
952
953 window_set_title(editor.window, "Text Editor");
954
955 widget_set_redraw_handler(editor.widget, redraw_handler);
956 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200957 widget_set_button_handler(editor.widget, editor_button_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200958
959 window_schedule_resize(editor.window, 500, 400);
960
961 display_run(editor.display);
962
963 text_entry_destroy(editor.entry);
964 text_entry_destroy(editor.editor);
965
966 return 0;
967}