blob: cbc72181c8eae58217e4afb8495cc9f1dd1978c9 [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
364text_model_key(void *data,
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200365 struct text_model *text_model,
366 uint32_t key,
367 uint32_t state)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200368{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200369 struct text_entry *entry = data;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200370 const char *state_label;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200371 const char *key_label = "released";
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100372 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200373
374 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
375 state_label = "pressed";
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200376 }
377
378 switch (key) {
379 case XKB_KEY_Tab:
380 key_label = "Tab";
381 break;
382 case XKB_KEY_KP_Enter:
383 key_label = "Enter";
384 break;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200385 case XKB_KEY_Left:
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100386 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
387 if (new_char != NULL) {
388 entry->cursor = new_char - entry->text;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200389 entry->anchor = entry->cursor;
390 widget_schedule_redraw(entry->widget);
391 }
392 break;
393 case XKB_KEY_Right:
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100394 new_char = utf8_next_char(entry->text + entry->cursor);
395 if (new_char != NULL) {
396 entry->cursor = new_char - entry->text;
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200397 entry->anchor = entry->cursor;
398 widget_schedule_redraw(entry->widget);
399 }
400 break;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200401 default:
402 key_label = "Unknown";
403 }
404
405 fprintf(stderr, "%s key was %s.\n", key_label, state_label);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200406}
407
408static void
409text_model_selection_replacement(void *data,
410 struct text_model *text_model)
411{
412}
413
414static void
415text_model_direction(void *data,
416 struct text_model *text_model)
417{
418}
419
420static void
421text_model_locale(void *data,
422 struct text_model *text_model)
423{
424}
425
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200426static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200427text_model_enter(void *data,
428 struct text_model *text_model,
429 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200430{
431 struct text_entry *entry = data;
432
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200433 if (surface != window_get_wl_surface(entry->window))
434 return;
435
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200436 entry->active = 1;
437
438 widget_schedule_redraw(entry->widget);
439}
440
441static void
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200442text_model_leave(void *data,
443 struct text_model *text_model)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200444{
445 struct text_entry *entry = data;
446
447 entry->active = 0;
448
449 widget_schedule_redraw(entry->widget);
450}
451
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200452static const struct text_model_listener text_model_listener = {
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200453 text_model_commit_string,
454 text_model_preedit_string,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200455 text_model_delete_surrounding_text,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200456 text_model_preedit_styling,
457 text_model_key,
458 text_model_selection_replacement,
459 text_model_direction,
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200460 text_model_locale,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200461 text_model_enter,
462 text_model_leave
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200463};
464
465static struct text_entry*
466text_entry_create(struct editor *editor, const char *text)
467{
468 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200469
470 entry = malloc(sizeof *entry);
471
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200472 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200473 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200474 entry->text = strdup(text);
475 entry->active = 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200476 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200477 entry->anchor = entry->cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200478 entry->preedit_text = NULL;
479 entry->preedit_cursor = 0;
Jan Arne Petersen4c265182012-09-09 23:08:30 +0200480 entry->model = text_model_factory_create_text_model(editor->text_model_factory);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200481 text_model_add_listener(entry->model, &text_model_listener, entry);
482
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200483 entry->layout = text_layout_create();
484 text_layout_set_text(entry->layout, entry->text);
485
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200486 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
487 widget_set_button_handler(entry->widget, text_entry_button_handler);
488
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200489 return entry;
490}
491
492static void
493text_entry_destroy(struct text_entry *entry)
494{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200495 widget_destroy(entry->widget);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200496 text_model_destroy(entry->model);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200497 text_layout_destroy(entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200498 free(entry->text);
499 free(entry);
500}
501
502static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200503redraw_handler(struct widget *widget, void *data)
504{
505 struct editor *editor = data;
506 cairo_surface_t *surface;
507 struct rectangle allocation;
508 cairo_t *cr;
509
510 surface = window_get_surface(editor->window);
511 widget_get_allocation(editor->widget, &allocation);
512
513 cr = cairo_create(surface);
514 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
515 cairo_clip(cr);
516
517 cairo_translate(cr, allocation.x, allocation.y);
518
519 /* Draw background */
520 cairo_push_group(cr);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200521 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200522 cairo_set_source_rgba(cr, 1, 1, 1, 1);
523 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
524 cairo_fill(cr);
525
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200526 cairo_pop_group_to_source(cr);
527 cairo_paint(cr);
528
529 cairo_destroy(cr);
530 cairo_surface_destroy(surface);
531}
532
533static void
534text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
535 int32_t width, int32_t height)
536{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200537 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200538}
539
540static void
541resize_handler(struct widget *widget,
542 int32_t width, int32_t height, void *data)
543{
544 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200545 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200546
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200547 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200548
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200549 text_entry_allocate(editor->entry,
550 allocation.x + 20, allocation.y + 20,
551 width - 40, height / 2 - 40);
552 text_entry_allocate(editor->editor,
553 allocation.x + 20, allocation.y + height / 2 + 20,
554 width - 40, height / 2 - 40);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200555}
556
557static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200558text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200559 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200560{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200561 struct wl_surface *surface = window_get_wl_surface(entry->window);
562
563 text_model_activate(entry->model,
564 seat,
565 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200566}
567
568static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200569text_entry_deactivate(struct text_entry *entry,
570 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200571{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200572 text_model_deactivate(entry->model,
573 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200574}
575
576static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200577text_entry_update_layout(struct text_entry *entry)
578{
579 char *text;
580
Philipp Brüschweiler237358b2012-10-02 11:06:51 +0200581 assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
582 (entry->preedit_text ? strlen(entry->preedit_text) : 0));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200583
584 if (!entry->preedit_text) {
585 text_layout_set_text(entry->layout, entry->text);
586 return;
587 }
588
589 text = malloc(strlen(entry->text) + strlen(entry->preedit_text) + 1);
590 strncpy(text, entry->text, entry->cursor);
591 strcpy(text + entry->cursor, entry->preedit_text);
592 strcpy(text + entry->cursor + strlen(entry->preedit_text),
593 entry->text + entry->cursor);
594
595 text_layout_set_text(entry->layout, text);
596 free(text);
597
598 widget_schedule_redraw(entry->widget);
Jan Arne Petersencb08f4d2012-09-09 23:08:40 +0200599
600 text_model_set_surrounding_text(entry->model,
601 entry->text,
602 entry->cursor,
603 entry->anchor);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200604}
605
606static void
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200607text_entry_insert_at_cursor(struct text_entry *entry, const char *text)
608{
609 char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
610
611 strncpy(new_text, entry->text, entry->cursor);
612 strcpy(new_text + entry->cursor, text);
613 strcpy(new_text + entry->cursor + strlen(text),
614 entry->text + entry->cursor);
615
616 free(entry->text);
617 entry->text = new_text;
618 entry->cursor += strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200619 entry->anchor += strlen(text);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200620
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200621 text_entry_update_layout(entry);
622}
623
624static void
625text_entry_set_preedit(struct text_entry *entry,
626 const char *preedit_text,
627 int preedit_cursor)
628{
629 if (entry->preedit_text) {
630 free(entry->preedit_text);
631 entry->preedit_text = NULL;
632 entry->preedit_cursor = 0;
633 }
634
635 if (!preedit_text)
636 return;
637
638 entry->preedit_text = strdup(preedit_text);
639 entry->preedit_cursor = preedit_cursor;
640
641 text_entry_update_layout(entry);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200642}
643
644static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200645text_entry_set_cursor_position(struct text_entry *entry,
646 int32_t x, int32_t y)
647{
648 entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
649
Jan Arne Petersenc1e481e2012-09-09 23:08:46 +0200650 text_model_reset(entry->model);
651
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200652 if (entry->cursor >= entry->preedit_cursor) {
653 entry->cursor -= entry->preedit_cursor;
654 }
655
656 text_entry_update_layout(entry);
657
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200658 widget_schedule_redraw(entry->widget);
659}
660
661static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200662text_entry_set_anchor_position(struct text_entry *entry,
663 int32_t x, int32_t y)
664{
665 entry->anchor = text_layout_xy_to_index(entry->layout, x, y);
666
667 widget_schedule_redraw(entry->widget);
668}
669
670static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200671text_entry_delete_text(struct text_entry *entry,
672 uint32_t index, uint32_t length)
673{
674 if (entry->cursor > index)
675 entry->cursor -= length;
676
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +0200677 entry->anchor = entry->cursor;
678
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200679 entry->text[index] = '\0';
680 strcat(entry->text, entry->text + index + length);
681
682 text_entry_update_layout(entry);
683
684 widget_schedule_redraw(entry->widget);
685}
686
687static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200688text_entry_delete_selected_text(struct text_entry *entry)
689{
690 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
691 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
692
693 if (entry->anchor == entry->cursor)
694 return;
695
696 text_entry_delete_text(entry, start_index, end_index - start_index);
697
698 entry->anchor = entry->cursor;
699}
700
701static void
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200702text_entry_draw_selection(struct text_entry *entry, cairo_t *cr)
703{
704 cairo_text_extents_t extents;
705 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
706 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
707 cairo_rectangle_t start;
708 cairo_rectangle_t end;
709
710 if (entry->anchor == entry->cursor)
711 return;
712
713 text_layout_extents(entry->layout, &extents);
714
715 text_layout_index_to_pos(entry->layout, start_index, &start);
716 text_layout_index_to_pos(entry->layout, end_index, &end);
717
718 cairo_save (cr);
719
Philipp Brüschweilerb8911dc2012-10-02 11:06:52 +0200720 cairo_set_source_rgba(cr, 0.3, 0.3, 1.0, 0.5);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200721 cairo_rectangle(cr,
722 start.x, extents.y_bearing + extents.height + 2,
723 end.x - start.x, -extents.height - 4);
724 cairo_fill(cr);
725
726 cairo_rectangle(cr,
727 start.x, extents.y_bearing + extents.height,
728 end.x - start.x, -extents.height);
729 cairo_clip(cr);
730 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
731 text_layout_draw(entry->layout, cr);
732
733 cairo_restore (cr);
734}
735
736static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200737text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
738{
739 cairo_text_extents_t extents;
740 cairo_rectangle_t cursor_pos;
741
742 text_layout_extents(entry->layout, &extents);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200743 text_layout_get_cursor_pos(entry->layout,
744 entry->cursor + entry->preedit_cursor,
745 &cursor_pos);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200746
747 cairo_set_line_width(cr, 1.0);
748 cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
749 cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
750 cairo_stroke(cr);
751}
752
753static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200754text_entry_draw_preedit(struct text_entry *entry, cairo_t *cr)
755{
756 cairo_text_extents_t extents;
757 cairo_rectangle_t start;
758 cairo_rectangle_t end;
759
760 if (!entry->preedit_text)
761 return;
762
763 text_layout_extents(entry->layout, &extents);
764
765 text_layout_index_to_pos(entry->layout, entry->cursor, &start);
766 text_layout_index_to_pos(entry->layout,
767 entry->cursor + strlen(entry->preedit_text),
768 &end);
769
770 cairo_save (cr);
771
772 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
773 cairo_rectangle(cr,
774 start.x, 0,
775 end.x - start.x, 1);
776 cairo_fill(cr);
777
778 cairo_restore (cr);
779}
780
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200781static const int text_offset_left = 10;
782
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200783static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200784text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200785{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200786 struct text_entry *entry = data;
787 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200788 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200789 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200790
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200791 surface = window_get_surface(entry->window);
792 widget_get_allocation(entry->widget, &allocation);
793
794 cr = cairo_create(surface);
795 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
796 cairo_clip(cr);
797
798 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
799
800 cairo_push_group(cr);
801 cairo_translate(cr, allocation.x, allocation.y);
802
803 cairo_set_source_rgba(cr, 1, 1, 1, 1);
804 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
805 cairo_fill(cr);
806
807 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
808
809 if (entry->active) {
810 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
811 cairo_set_line_width (cr, 3);
812 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
813 cairo_stroke(cr);
814 }
815
816 cairo_set_source_rgba(cr, 0, 0, 0, 1);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200817
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200818 cairo_translate(cr, text_offset_left, allocation.height / 2);
Jan Arne Petersenb9eb02c2012-09-09 23:08:35 +0200819 text_layout_draw(entry->layout, cr);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200820
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200821 text_entry_draw_selection(entry, cr);
822
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200823 text_entry_draw_cursor(entry, cr);
824
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200825 text_entry_draw_preedit(entry, cr);
826
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200827 cairo_pop_group_to_source(cr);
828 cairo_paint(cr);
829
830 cairo_destroy(cr);
831 cairo_surface_destroy(surface);
832}
833
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200834static int
835text_entry_motion_handler(struct widget *widget,
836 struct input *input, uint32_t time,
837 float x, float y, void *data)
838{
839 struct text_entry *entry = data;
840 struct rectangle allocation;
841
842 widget_get_allocation(entry->widget, &allocation);
843
844 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200845 x - allocation.x - text_offset_left,
846 y - allocation.y - text_offset_left);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200847
848 return CURSOR_IBEAM;
849}
850
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200851static void
852text_entry_button_handler(struct widget *widget,
853 struct input *input, uint32_t time,
854 uint32_t button,
855 enum wl_pointer_button_state state, void *data)
856{
857 struct text_entry *entry = data;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200858 struct rectangle allocation;
859 int32_t x, y;
860
861 widget_get_allocation(entry->widget, &allocation);
862 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200863
864 if (button != BTN_LEFT) {
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200865 return;
866 }
867
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200868 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200869 x - allocation.x - text_offset_left,
870 y - allocation.y - text_offset_left);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200871
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200872 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
873 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200874
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200875 text_entry_activate(entry, seat);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200876
877 text_entry_set_anchor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200878 x - allocation.x - text_offset_left,
879 y - allocation.y - text_offset_left);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200880
881 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
882 } else {
883 widget_set_motion_handler(entry->widget, NULL);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200884 }
885}
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200886
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200887static void
888editor_button_handler(struct widget *widget,
889 struct input *input, uint32_t time,
890 uint32_t button,
891 enum wl_pointer_button_state state, void *data)
892{
893 struct editor *editor = data;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200894
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200895 if (button != BTN_LEFT) {
896 return;
897 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200898
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200899 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
900 struct wl_seat *seat = input_get_seat(input);
901
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200902 text_entry_deactivate(editor->entry, seat);
903 text_entry_deactivate(editor->editor, seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200904 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200905}
906
907static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400908global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200909 const char *interface, uint32_t version, void *data)
910{
911 struct editor *editor = data;
912
Jan Arne Petersen51963742012-08-10 16:47:20 +0200913 if (!strcmp(interface, "text_model_factory")) {
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400914 editor->text_model_factory =
915 display_bind(display, name,
916 &text_model_factory_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200917 }
918}
919
920int
921main(int argc, char *argv[])
922{
923 struct editor editor;
924
925 editor.display = display_create(argc, argv);
926 if (editor.display == NULL) {
927 fprintf(stderr, "failed to create display: %m\n");
928 return -1;
929 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200930
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400931 display_set_user_data(editor.display, &editor);
932 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200933
934 editor.window = window_create(editor.display);
935 editor.widget = frame_create(editor.window, &editor);
936
937 editor.entry = text_entry_create(&editor, "Entry");
938 editor.editor = text_entry_create(&editor, "Editor");
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200939 text_entry_set_preedit(editor.editor, "preedit", strlen("preedit"));
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200940
941 window_set_title(editor.window, "Text Editor");
942
943 widget_set_redraw_handler(editor.widget, redraw_handler);
944 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200945 widget_set_button_handler(editor.widget, editor_button_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200946
947 window_schedule_resize(editor.window, 500, 400);
948
949 display_run(editor.display);
950
951 text_entry_destroy(editor.entry);
952 text_entry_destroy(editor.editor);
953
954 return 0;
955}