editor: implement Cut,Copy,Paste
authorManuel Bachmann <manuel.bachmann@open.eurogiciel.org>
Sun, 29 Mar 2015 23:57:44 +0000 (01:57 +0200)
committerBryce Harrington <bryce@osg.samsung.com>
Thu, 7 May 2015 23:27:30 +0000 (16:27 -0700)
weston-editor is the only stock client spawning the virtual
keyboard ; which means it may be the only client able to
obtain some special characters (depending on the user's
keyboard layout).

If we implement Cut, Copy and Paste, the user has now a way
to copy such characters to other useful clients (such as
weston-terminal). Plus, it demonstrates text data exchange
between two clients of different nature.

Functionality is implemented in a right-click menu and the
Ctrl+Shift+X/C/V bindings, just as in weston-terminal.

Signed-off-by: Manuel Bachmann <manuel.bachmann@open.eurogiciel.org>
Reviewed-by: Bryce Harrington <bryce@osg.samsung.com>
Tested-by: Bryce Harrington <bryce@osg.samsung.com>
clients/editor.c

index 9299a05..29cab34 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
+#include <unistd.h>
 
 #include <linux/input.h>
 #include <cairo.h>
@@ -76,6 +77,8 @@ struct text_entry {
 
 struct editor {
        struct wl_text_input_manager *text_input_manager;
+       struct wl_data_source *selection;
+       char *selected_text;
        struct display *display;
        struct window *window;
        struct widget *widget;
@@ -554,6 +557,128 @@ static const struct wl_text_input_listener text_input_listener = {
        text_input_text_direction
 };
 
+static void
+data_source_target(void *data,
+                  struct wl_data_source *source, const char *mime_type)
+{
+}
+
+static void
+data_source_send(void *data,
+                struct wl_data_source *source,
+                const char *mime_type, int32_t fd)
+{
+       struct editor *editor = data;
+
+       write(fd, editor->selected_text, strlen(editor->selected_text) + 1);
+}
+
+static void
+data_source_cancelled(void *data, struct wl_data_source *source)
+{
+       wl_data_source_destroy(source);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+       data_source_target,
+       data_source_send,
+       data_source_cancelled
+};
+
+static void
+paste_func(void *buffer, size_t len,
+          int32_t x, int32_t y, void *data)
+{
+       struct editor *editor = data;
+       struct text_entry *entry = editor->active_entry;
+       char *pasted_text;
+
+       if (!entry)
+               return;
+
+       pasted_text = malloc(len + 1);
+       strncpy(pasted_text, buffer, len);
+       pasted_text[len] = '\0';
+
+       text_entry_insert_at_cursor(entry, pasted_text, 0, 0);
+
+       free(pasted_text);
+}
+
+static void
+editor_copy_cut(struct editor *editor, struct input *input, bool cut)
+{
+       struct text_entry *entry = editor->active_entry;
+
+       if (!entry)
+               return;
+       
+       if (entry->cursor != entry->anchor) {
+               int start_index = MIN(entry->cursor, entry->anchor);
+               int end_index = MAX(entry->cursor, entry->anchor);
+               int len = end_index - start_index;
+
+               editor->selected_text = realloc(editor->selected_text, len + 1);
+               strncpy(editor->selected_text, &entry->text[start_index], len);
+               editor->selected_text[len] = '\0';
+
+               if (cut)
+                       text_entry_delete_text(entry, start_index, len);
+
+               editor->selection =
+                       display_create_data_source(editor->display);
+               wl_data_source_offer(editor->selection,
+                                    "text/plain;charset=utf-8");
+               wl_data_source_add_listener(editor->selection,
+                                           &data_source_listener, editor);
+               input_set_selection(input, editor->selection,
+                                   display_get_serial(editor->display));
+       }
+}
+
+static void
+editor_paste(struct editor *editor, struct input *input)
+{
+       input_receive_selection_data(input,
+                                    "text/plain;charset=utf-8",
+                                    paste_func, editor);
+}
+
+static void
+menu_func(void *data, struct input *input, int index)
+{
+       struct window *window = data;
+       struct editor *editor = window_get_user_data(window);
+
+       fprintf(stderr, "picked entry %d\n", index);
+
+       switch (index) {
+       case 0:
+               editor_copy_cut(editor, input, true);
+               break;
+       case 1:
+               editor_copy_cut(editor, input, false);
+               break;
+       case 2:
+               editor_paste(editor, input);
+               break;
+       }
+}
+
+static void
+show_menu(struct editor *editor, struct input *input, uint32_t time)
+{
+       int32_t x, y;
+       static const char *entries[] = {
+               "Cut", "Copy", "Paste"
+       };
+
+       input_get_position(input, &x, &y);
+       window_show_menu(editor->display, input, time, editor->window,
+                        x + 10, y + 20, menu_func,
+                        entries, ARRAY_LENGTH(entries));
+}
+
 static struct text_entry*
 text_entry_create(struct editor *editor, const char *text)
 {
@@ -1123,13 +1248,18 @@ text_entry_button_handler(struct widget *widget,
 
        editor = window_get_user_data(entry->window);
 
-       if (button == BTN_LEFT) {
+       switch (button) {
+       case BTN_LEFT:
                entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
-
                if (state == WL_POINTER_BUTTON_STATE_PRESSED)
                        input_grab(input, entry->widget, button);
                else
                        input_ungrab(input);
+               break;
+       case BTN_RIGHT:
+               if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+                       show_menu(editor, input, time);
+               break;
        }
 
        if (text_entry_has_preedit(entry)) {
@@ -1139,7 +1269,8 @@ text_entry_button_handler(struct widget *widget,
                        return;
        }
 
-       if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+       if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+           button == BTN_LEFT) {
                struct wl_seat *seat = input_get_seat(input);
 
                text_entry_activate(entry, seat);
@@ -1216,6 +1347,25 @@ keyboard_focus_handler(struct window *window,
        window_schedule_redraw(editor->window);
 }
 
+static int
+handle_bound_key(struct editor *editor,
+                struct input *input, uint32_t sym, uint32_t time)
+{
+       switch (sym) {
+       case XKB_KEY_X:
+               editor_copy_cut(editor, input, true);
+               return 1;
+       case XKB_KEY_C:
+               editor_copy_cut(editor, input, false);
+               return 1;
+       case XKB_KEY_V:
+               editor_paste(editor, input);
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 static void
 key_handler(struct window *window,
            struct input *input, uint32_t time,
@@ -1226,6 +1376,7 @@ key_handler(struct window *window,
        struct text_entry *entry;
        const char *new_char;
        char text[16];
+       uint32_t modifiers;
 
        if (!editor->active_entry)
                return;
@@ -1235,6 +1386,12 @@ key_handler(struct window *window,
        if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
                return;
 
+       modifiers = input_get_modifiers(input);
+       if ((modifiers & MOD_CONTROL_MASK) &&
+           (modifiers & MOD_SHIFT_MASK) &&
+           handle_bound_key(editor, input, sym, time))
+               return;
+
        switch (sym) {
                case XKB_KEY_BackSpace:
                        text_entry_commit_and_reset(entry);
@@ -1374,6 +1531,8 @@ main(int argc, char *argv[])
        editor.editor = text_entry_create(&editor, "Numeric");
        editor.editor->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
        editor.editor->click_to_show = click_to_show;
+       editor.selection = NULL;
+       editor.selected_text = NULL;
 
        window_set_title(editor.window, "Text Editor");
        window_set_key_handler(editor.window, key_handler);
@@ -1390,6 +1549,10 @@ main(int argc, char *argv[])
 
        display_run(editor.display);
 
+       if (editor.selected_text)
+               free(editor.selected_text);
+       if (editor.selection)
+               wl_data_source_destroy(editor.selection);
        text_entry_destroy(editor.entry);
        text_entry_destroy(editor.editor);
        widget_destroy(editor.widget);