editor: Add support for cursor
[profile/ivi/weston.git] / clients / editor.c
1 /*
2  * Copyright © 2012 Openismus GmbH
3  * Copyright © 2012 Intel Corporation
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and
6  * its documentation for any purpose is hereby granted without fee, provided
7  * that the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of the copyright holders not be used in
10  * advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission.  The copyright holders make
12  * no representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23
24 #include <assert.h>
25 #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
35 static const char *font_name = "sans-serif";
36 static int font_size = 14;
37
38 struct 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
47 struct text_entry {
48         struct widget *widget;
49         struct window *window;
50         char *text;
51         int active;
52         uint32_t cursor;
53         struct text_model *model;
54         struct text_layout *layout;
55 };
56
57 struct editor {
58         struct text_model_factory *text_model_factory;
59         struct display *display;
60         struct window *window;
61         struct widget *widget;
62         struct text_entry *entry;
63         struct text_entry *editor;
64 };
65
66 static struct text_layout *
67 text_layout_create(void)
68 {
69         struct text_layout *layout;
70         cairo_surface_t *surface;
71         cairo_t *cr;
72
73         layout = malloc(sizeof *layout);
74         if (!layout)
75                 return NULL;
76
77         layout->glyphs = NULL;
78         layout->num_glyphs = 0;
79
80         layout->clusters = NULL;
81         layout->num_clusters = 0;
82
83         surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
84         cr = cairo_create(surface);
85         cairo_set_font_size(cr, font_size);
86         cairo_select_font_face(cr, font_name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
87         layout->font = cairo_get_scaled_font(cr);
88         cairo_scaled_font_reference(layout->font);
89
90         cairo_destroy(cr);
91         cairo_surface_destroy(surface);
92
93         return layout;
94 }
95
96 static void
97 text_layout_destroy(struct text_layout *layout)
98 {
99         if (layout->glyphs)
100                 cairo_glyph_free(layout->glyphs);
101
102         if (layout->clusters)
103                 cairo_text_cluster_free(layout->clusters);
104
105         cairo_scaled_font_destroy(layout->font);
106
107         free(layout);
108 }
109
110 static void
111 text_layout_set_text(struct text_layout *layout,
112                      const char *text)
113 {
114         if (layout->glyphs)
115                 cairo_glyph_free(layout->glyphs);
116
117         if (layout->clusters)
118                 cairo_text_cluster_free(layout->clusters);
119
120         layout->glyphs = NULL;
121         layout->num_glyphs = 0;
122         layout->clusters = NULL;
123         layout->num_clusters = 0;
124
125         cairo_scaled_font_text_to_glyphs(layout->font, 0, 0, text, -1,
126                                          &layout->glyphs, &layout->num_glyphs,
127                                          &layout->clusters, &layout->num_clusters,
128                                          &layout->cluster_flags);
129 }
130
131 static void
132 text_layout_draw(struct text_layout *layout, cairo_t *cr)
133 {
134         cairo_save(cr);
135         cairo_set_scaled_font(cr, layout->font);
136         cairo_show_glyphs(cr, layout->glyphs, layout->num_glyphs);
137         cairo_restore(cr);
138 }
139
140 static void
141 text_layout_extents(struct text_layout *layout, cairo_text_extents_t *extents)
142 {
143         cairo_scaled_font_glyph_extents(layout->font,
144                                         layout->glyphs, layout->num_glyphs,
145                                         extents);
146 }
147
148 static int
149 text_layout_xy_to_index(struct text_layout *layout, double x, double y)
150 {
151         cairo_text_extents_t extents;
152         int i;
153
154         cairo_scaled_font_glyph_extents(layout->font,
155                                         layout->glyphs, layout->num_glyphs,
156                                         &extents);
157
158         for (i = 1; i < layout->num_glyphs; i++) {
159                 if (layout->glyphs[i].x >= x) {
160                         return i - 1;
161                 }
162         }
163
164         if (x >= layout->glyphs[layout->num_glyphs - 1].x && x < extents.width)
165                 return layout->num_glyphs - 1;
166
167         return layout->num_glyphs;
168 }
169
170 static void
171 text_layout_index_to_pos(struct text_layout *layout, uint32_t index, cairo_rectangle_t *pos)
172 {
173         cairo_text_extents_t extents;
174
175         if (!pos)
176                 return;
177
178         cairo_scaled_font_glyph_extents(layout->font,
179                                         layout->glyphs, layout->num_glyphs,
180                                         &extents);
181
182         if ((int)index >= layout->num_glyphs) {
183                 pos->x = extents.x_advance;
184                 pos->y = layout->num_glyphs ? layout->glyphs[layout->num_glyphs - 1].y : 0;
185                 pos->width = 1;
186                 pos->height = extents.height;
187                 return;
188         }
189
190         pos->x = layout->glyphs[index].x;
191         pos->y = layout->glyphs[index].y;
192         pos->width = (int)index < layout->num_glyphs - 1 ? layout->glyphs[index + 1].x : extents.x_advance - pos->x;
193         pos->height = extents.height;
194 }
195
196 static void
197 text_layout_get_cursor_pos(struct text_layout *layout, int index, cairo_rectangle_t *pos)
198 {
199         text_layout_index_to_pos(layout, index, pos);
200         pos->width = 1;
201 }
202
203 static void text_entry_redraw_handler(struct widget *widget, void *data);
204 static void text_entry_button_handler(struct widget *widget,
205                                       struct input *input, uint32_t time,
206                                       uint32_t button,
207                                       enum wl_pointer_button_state state, void *data);
208
209 static void
210 text_entry_append(struct text_entry *entry, const char *text)
211 {
212         entry->text = realloc(entry->text, strlen(entry->text) + strlen(text) + 1);
213         strcat(entry->text, text);
214         text_layout_set_text(entry->layout, entry->text);
215 }
216
217
218 static void
219 text_model_commit_string(void *data,
220                          struct text_model *text_model,
221                          const char *text,
222                          uint32_t index)
223 {
224         struct text_entry *entry = data;
225
226         text_entry_append(entry, text); 
227
228         widget_schedule_redraw(entry->widget);
229 }
230
231 static void
232 text_model_preedit_string(void *data,
233                           struct text_model *text_model,
234                           const char *text,
235                           uint32_t index)
236 {
237 }
238
239 static void
240 text_model_preedit_styling(void *data,
241                            struct text_model *text_model)
242 {
243 }
244
245 static void
246 text_model_key(void *data,
247                struct text_model *text_model)
248 {
249 }
250
251 static void
252 text_model_selection_replacement(void *data,
253                                  struct text_model *text_model)
254 {
255 }
256
257 static void
258 text_model_direction(void *data,
259                      struct text_model *text_model)
260 {
261 }
262
263 static void
264 text_model_locale(void *data,
265                   struct text_model *text_model)
266 {
267 }
268
269 static void
270 text_model_activated(void *data,
271                      struct text_model *text_model)
272 {
273         struct text_entry *entry = data;
274
275         entry->active = 1;
276
277         widget_schedule_redraw(entry->widget);
278 }
279
280 static void
281 text_model_deactivated(void *data,
282                        struct text_model *text_model)
283 {
284         struct text_entry *entry = data;
285
286         entry->active = 0;
287
288         widget_schedule_redraw(entry->widget);
289 }
290
291 static const struct text_model_listener text_model_listener = {
292         text_model_commit_string,
293         text_model_preedit_string,
294         text_model_preedit_styling,
295         text_model_key,
296         text_model_selection_replacement,
297         text_model_direction,
298         text_model_locale,
299         text_model_activated,
300         text_model_deactivated
301 };
302
303 static struct text_entry*
304 text_entry_create(struct editor *editor, const char *text)
305 {
306         struct text_entry *entry;
307
308         entry = malloc(sizeof *entry);
309
310         entry->widget = widget_add_widget(editor->widget, entry);
311         entry->window = editor->window;
312         entry->text = strdup(text);
313         entry->active = 0;
314         entry->cursor = strlen(text);
315         entry->model = text_model_factory_create_text_model(editor->text_model_factory);
316         text_model_add_listener(entry->model, &text_model_listener, entry);
317
318         entry->layout = text_layout_create();
319         text_layout_set_text(entry->layout, entry->text);
320
321         widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
322         widget_set_button_handler(entry->widget, text_entry_button_handler);
323
324         return entry;
325 }
326
327 static void
328 text_entry_destroy(struct text_entry *entry)
329 {
330         widget_destroy(entry->widget);
331         text_model_destroy(entry->model);
332         text_layout_destroy(entry->layout);
333         free(entry->text);
334         free(entry);
335 }
336
337 static void
338 redraw_handler(struct widget *widget, void *data)
339 {
340         struct editor *editor = data;
341         cairo_surface_t *surface;
342         struct rectangle allocation;
343         cairo_t *cr;
344
345         surface = window_get_surface(editor->window);
346         widget_get_allocation(editor->widget, &allocation);
347
348         cr = cairo_create(surface);
349         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
350         cairo_clip(cr);
351
352         cairo_translate(cr, allocation.x, allocation.y);
353
354         /* Draw background */
355         cairo_push_group(cr);
356         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
357         cairo_set_source_rgba(cr, 1, 1, 1, 1);
358         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
359         cairo_fill(cr);
360
361         cairo_pop_group_to_source(cr);
362         cairo_paint(cr);
363
364         cairo_destroy(cr);
365         cairo_surface_destroy(surface);
366 }
367
368 static void
369 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
370                     int32_t width, int32_t height)
371 {
372         widget_set_allocation(entry->widget, x, y, width, height);
373 }
374
375 static void
376 resize_handler(struct widget *widget,
377                int32_t width, int32_t height, void *data)
378 {
379         struct editor *editor = data;
380         struct rectangle allocation;
381
382         widget_get_allocation(editor->widget, &allocation);
383
384         text_entry_allocate(editor->entry,
385                             allocation.x + 20, allocation.y + 20,
386                             width - 40, height / 2 - 40);
387         text_entry_allocate(editor->editor,
388                             allocation.x + 20, allocation.y + height / 2 + 20,
389                             width - 40, height / 2 - 40);
390 }
391
392 static void
393 text_entry_activate(struct text_entry *entry,
394                     struct wl_seat *seat)
395 {
396         struct wl_surface *surface = window_get_wl_surface(entry->window);
397
398         text_model_activate(entry->model,
399                             seat,
400                             surface);
401 }
402
403 static void
404 text_entry_deactivate(struct text_entry *entry,
405                       struct wl_seat *seat)
406 {
407         text_model_deactivate(entry->model,
408                               seat);
409 }
410
411 static void
412 text_entry_set_cursor_position(struct text_entry *entry,
413                                int32_t x, int32_t y)
414 {
415         entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
416
417         widget_schedule_redraw(entry->widget);
418 }
419
420 static void
421 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
422 {
423         cairo_text_extents_t extents;
424         cairo_rectangle_t cursor_pos;
425
426         text_layout_extents(entry->layout, &extents);
427         text_layout_get_cursor_pos(entry->layout, entry->cursor, &cursor_pos);
428
429         cairo_set_line_width(cr, 1.0);
430         cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
431         cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
432         cairo_stroke(cr);
433 }
434
435 static void
436 text_entry_redraw_handler(struct widget *widget, void *data)
437 {
438         struct text_entry *entry = data;
439         cairo_surface_t *surface;
440         struct rectangle allocation;
441         cairo_t *cr;
442
443         surface = window_get_surface(entry->window);
444         widget_get_allocation(entry->widget, &allocation);
445
446         cr = cairo_create(surface);
447         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
448         cairo_clip(cr);
449
450         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
451
452         cairo_push_group(cr);
453         cairo_translate(cr, allocation.x, allocation.y);
454
455         cairo_set_source_rgba(cr, 1, 1, 1, 1);
456         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
457         cairo_fill(cr);
458
459         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
460
461         if (entry->active) {
462                 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
463                 cairo_set_line_width (cr, 3);
464                 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
465                 cairo_stroke(cr);
466         }
467
468         cairo_set_source_rgba(cr, 0, 0, 0, 1);
469
470         cairo_translate(cr, 10, allocation.height / 2);
471         text_layout_draw(entry->layout, cr);
472
473         text_entry_draw_cursor(entry, cr);
474
475         cairo_pop_group_to_source(cr);
476         cairo_paint(cr);
477
478         cairo_destroy(cr);
479         cairo_surface_destroy(surface);
480 }
481
482 static void
483 text_entry_button_handler(struct widget *widget,
484                           struct input *input, uint32_t time,
485                           uint32_t button,
486                           enum wl_pointer_button_state state, void *data)
487 {
488         struct text_entry *entry = data;
489         struct rectangle allocation;
490         int32_t x, y;
491
492         widget_get_allocation(entry->widget, &allocation);
493         input_get_position(input, &x, &y);
494
495         if (button != BTN_LEFT) {
496                 return;
497         }
498
499         text_entry_set_cursor_position(entry, 
500                                        x - allocation.x,
501                                        y - allocation.y);
502
503         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
504                 struct wl_seat *seat = input_get_seat(input);
505
506                 text_entry_activate(entry, seat);
507         }
508 }
509
510 static void
511 editor_button_handler(struct widget *widget,
512                       struct input *input, uint32_t time,
513                       uint32_t button,
514                       enum wl_pointer_button_state state, void *data)
515 {
516         struct editor *editor = data;
517
518         if (button != BTN_LEFT) {
519                 return;
520         }
521
522         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
523                 struct wl_seat *seat = input_get_seat(input);
524
525                 text_entry_deactivate(editor->entry, seat);
526                 text_entry_deactivate(editor->editor, seat);
527         }
528 }
529
530 static void
531 global_handler(struct wl_display *display, uint32_t id,
532                const char *interface, uint32_t version, void *data)
533 {
534         struct editor *editor = data;
535
536         if (!strcmp(interface, "text_model_factory")) {
537                 editor->text_model_factory = wl_display_bind(display, id,
538                                                              &text_model_factory_interface);
539         }
540 }
541
542 int
543 main(int argc, char *argv[])
544 {
545         struct editor editor;
546
547         editor.display = display_create(argc, argv);
548         if (editor.display == NULL) {
549                 fprintf(stderr, "failed to create display: %m\n");
550                 return -1;
551         }
552         wl_display_add_global_listener(display_get_display(editor.display),
553                                        global_handler, &editor);
554
555
556         editor.window = window_create(editor.display);
557         editor.widget = frame_create(editor.window, &editor);
558
559         editor.entry = text_entry_create(&editor, "Entry");
560         editor.editor = text_entry_create(&editor, "Editor");
561
562         window_set_title(editor.window, "Text Editor");
563
564         widget_set_redraw_handler(editor.widget, redraw_handler);
565         widget_set_resize_handler(editor.widget, resize_handler);
566         widget_set_button_handler(editor.widget, editor_button_handler);
567
568         window_schedule_resize(editor.window, 500, 400);
569
570         display_run(editor.display);
571
572         text_entry_destroy(editor.entry);
573         text_entry_destroy(editor.editor);
574
575         return 0;
576 }