editor: Insert commit-string at 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 static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text);
209
210 static void
211 text_model_commit_string(void *data,
212                          struct text_model *text_model,
213                          const char *text,
214                          uint32_t index)
215 {
216         struct text_entry *entry = data;
217
218         if (index > strlen(text)) {
219                 fprintf(stderr, "Invalid cursor index %d\n", index);
220                 index = strlen(text);
221         }
222
223         text_entry_insert_at_cursor(entry, text);
224
225         widget_schedule_redraw(entry->widget);
226 }
227
228 static void
229 text_model_preedit_string(void *data,
230                           struct text_model *text_model,
231                           const char *text,
232                           uint32_t index)
233 {
234 }
235
236 static void
237 text_model_preedit_styling(void *data,
238                            struct text_model *text_model)
239 {
240 }
241
242 static void
243 text_model_key(void *data,
244                struct text_model *text_model)
245 {
246 }
247
248 static void
249 text_model_selection_replacement(void *data,
250                                  struct text_model *text_model)
251 {
252 }
253
254 static void
255 text_model_direction(void *data,
256                      struct text_model *text_model)
257 {
258 }
259
260 static void
261 text_model_locale(void *data,
262                   struct text_model *text_model)
263 {
264 }
265
266 static void
267 text_model_activated(void *data,
268                      struct text_model *text_model)
269 {
270         struct text_entry *entry = data;
271
272         entry->active = 1;
273
274         widget_schedule_redraw(entry->widget);
275 }
276
277 static void
278 text_model_deactivated(void *data,
279                        struct text_model *text_model)
280 {
281         struct text_entry *entry = data;
282
283         entry->active = 0;
284
285         widget_schedule_redraw(entry->widget);
286 }
287
288 static const struct text_model_listener text_model_listener = {
289         text_model_commit_string,
290         text_model_preedit_string,
291         text_model_preedit_styling,
292         text_model_key,
293         text_model_selection_replacement,
294         text_model_direction,
295         text_model_locale,
296         text_model_activated,
297         text_model_deactivated
298 };
299
300 static struct text_entry*
301 text_entry_create(struct editor *editor, const char *text)
302 {
303         struct text_entry *entry;
304
305         entry = malloc(sizeof *entry);
306
307         entry->widget = widget_add_widget(editor->widget, entry);
308         entry->window = editor->window;
309         entry->text = strdup(text);
310         entry->active = 0;
311         entry->cursor = strlen(text);
312         entry->model = text_model_factory_create_text_model(editor->text_model_factory);
313         text_model_add_listener(entry->model, &text_model_listener, entry);
314
315         entry->layout = text_layout_create();
316         text_layout_set_text(entry->layout, entry->text);
317
318         widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
319         widget_set_button_handler(entry->widget, text_entry_button_handler);
320
321         return entry;
322 }
323
324 static void
325 text_entry_destroy(struct text_entry *entry)
326 {
327         widget_destroy(entry->widget);
328         text_model_destroy(entry->model);
329         text_layout_destroy(entry->layout);
330         free(entry->text);
331         free(entry);
332 }
333
334 static void
335 redraw_handler(struct widget *widget, void *data)
336 {
337         struct editor *editor = data;
338         cairo_surface_t *surface;
339         struct rectangle allocation;
340         cairo_t *cr;
341
342         surface = window_get_surface(editor->window);
343         widget_get_allocation(editor->widget, &allocation);
344
345         cr = cairo_create(surface);
346         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
347         cairo_clip(cr);
348
349         cairo_translate(cr, allocation.x, allocation.y);
350
351         /* Draw background */
352         cairo_push_group(cr);
353         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
354         cairo_set_source_rgba(cr, 1, 1, 1, 1);
355         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
356         cairo_fill(cr);
357
358         cairo_pop_group_to_source(cr);
359         cairo_paint(cr);
360
361         cairo_destroy(cr);
362         cairo_surface_destroy(surface);
363 }
364
365 static void
366 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
367                     int32_t width, int32_t height)
368 {
369         widget_set_allocation(entry->widget, x, y, width, height);
370 }
371
372 static void
373 resize_handler(struct widget *widget,
374                int32_t width, int32_t height, void *data)
375 {
376         struct editor *editor = data;
377         struct rectangle allocation;
378
379         widget_get_allocation(editor->widget, &allocation);
380
381         text_entry_allocate(editor->entry,
382                             allocation.x + 20, allocation.y + 20,
383                             width - 40, height / 2 - 40);
384         text_entry_allocate(editor->editor,
385                             allocation.x + 20, allocation.y + height / 2 + 20,
386                             width - 40, height / 2 - 40);
387 }
388
389 static void
390 text_entry_activate(struct text_entry *entry,
391                     struct wl_seat *seat)
392 {
393         struct wl_surface *surface = window_get_wl_surface(entry->window);
394
395         text_model_activate(entry->model,
396                             seat,
397                             surface);
398 }
399
400 static void
401 text_entry_deactivate(struct text_entry *entry,
402                       struct wl_seat *seat)
403 {
404         text_model_deactivate(entry->model,
405                               seat);
406 }
407
408 static void
409 text_entry_insert_at_cursor(struct text_entry *entry, const char *text)
410 {
411         char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
412
413         strncpy(new_text, entry->text, entry->cursor);
414         strcpy(new_text + entry->cursor, text);
415         strcpy(new_text + entry->cursor + strlen(text),
416                entry->text + entry->cursor);
417
418         free(entry->text);
419         entry->text = new_text;
420         entry->cursor += strlen(text);
421
422         text_layout_set_text(entry->layout, entry->text);
423 }
424
425 static void
426 text_entry_set_cursor_position(struct text_entry *entry,
427                                int32_t x, int32_t y)
428 {
429         entry->cursor = text_layout_xy_to_index(entry->layout, x, y);
430
431         widget_schedule_redraw(entry->widget);
432 }
433
434 static void
435 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
436 {
437         cairo_text_extents_t extents;
438         cairo_rectangle_t cursor_pos;
439
440         text_layout_extents(entry->layout, &extents);
441         text_layout_get_cursor_pos(entry->layout, entry->cursor, &cursor_pos);
442
443         cairo_set_line_width(cr, 1.0);
444         cairo_move_to(cr, cursor_pos.x, extents.y_bearing + extents.height + 2);
445         cairo_line_to(cr, cursor_pos.x, extents.y_bearing - 2);
446         cairo_stroke(cr);
447 }
448
449 static void
450 text_entry_redraw_handler(struct widget *widget, void *data)
451 {
452         struct text_entry *entry = data;
453         cairo_surface_t *surface;
454         struct rectangle allocation;
455         cairo_t *cr;
456
457         surface = window_get_surface(entry->window);
458         widget_get_allocation(entry->widget, &allocation);
459
460         cr = cairo_create(surface);
461         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
462         cairo_clip(cr);
463
464         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
465
466         cairo_push_group(cr);
467         cairo_translate(cr, allocation.x, allocation.y);
468
469         cairo_set_source_rgba(cr, 1, 1, 1, 1);
470         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
471         cairo_fill(cr);
472
473         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
474
475         if (entry->active) {
476                 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
477                 cairo_set_line_width (cr, 3);
478                 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
479                 cairo_stroke(cr);
480         }
481
482         cairo_set_source_rgba(cr, 0, 0, 0, 1);
483
484         cairo_translate(cr, 10, allocation.height / 2);
485         text_layout_draw(entry->layout, cr);
486
487         text_entry_draw_cursor(entry, cr);
488
489         cairo_pop_group_to_source(cr);
490         cairo_paint(cr);
491
492         cairo_destroy(cr);
493         cairo_surface_destroy(surface);
494 }
495
496 static void
497 text_entry_button_handler(struct widget *widget,
498                           struct input *input, uint32_t time,
499                           uint32_t button,
500                           enum wl_pointer_button_state state, void *data)
501 {
502         struct text_entry *entry = data;
503         struct rectangle allocation;
504         int32_t x, y;
505
506         widget_get_allocation(entry->widget, &allocation);
507         input_get_position(input, &x, &y);
508
509         if (button != BTN_LEFT) {
510                 return;
511         }
512
513         text_entry_set_cursor_position(entry, 
514                                        x - allocation.x,
515                                        y - allocation.y);
516
517         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
518                 struct wl_seat *seat = input_get_seat(input);
519
520                 text_entry_activate(entry, seat);
521         }
522 }
523
524 static void
525 editor_button_handler(struct widget *widget,
526                       struct input *input, uint32_t time,
527                       uint32_t button,
528                       enum wl_pointer_button_state state, void *data)
529 {
530         struct editor *editor = data;
531
532         if (button != BTN_LEFT) {
533                 return;
534         }
535
536         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
537                 struct wl_seat *seat = input_get_seat(input);
538
539                 text_entry_deactivate(editor->entry, seat);
540                 text_entry_deactivate(editor->editor, seat);
541         }
542 }
543
544 static void
545 global_handler(struct wl_display *display, uint32_t id,
546                const char *interface, uint32_t version, void *data)
547 {
548         struct editor *editor = data;
549
550         if (!strcmp(interface, "text_model_factory")) {
551                 editor->text_model_factory = wl_display_bind(display, id,
552                                                              &text_model_factory_interface);
553         }
554 }
555
556 int
557 main(int argc, char *argv[])
558 {
559         struct editor editor;
560
561         editor.display = display_create(argc, argv);
562         if (editor.display == NULL) {
563                 fprintf(stderr, "failed to create display: %m\n");
564                 return -1;
565         }
566         wl_display_add_global_listener(display_get_display(editor.display),
567                                        global_handler, &editor);
568
569
570         editor.window = window_create(editor.display);
571         editor.widget = frame_create(editor.window, &editor);
572
573         editor.entry = text_entry_create(&editor, "Entry");
574         editor.editor = text_entry_create(&editor, "Editor");
575
576         window_set_title(editor.window, "Text Editor");
577
578         widget_set_redraw_handler(editor.widget, redraw_handler);
579         widget_set_resize_handler(editor.widget, resize_handler);
580         widget_set_button_handler(editor.widget, editor_button_handler);
581
582         window_schedule_resize(editor.window, 500, 400);
583
584         display_run(editor.display);
585
586         text_entry_destroy(editor.entry);
587         text_entry_destroy(editor.editor);
588
589         return 0;
590 }