diff --git a/clients/editor.c b/clients/editor.c index 9299a050..29cab34a 100644 --- a/clients/editor.c +++ b/clients/editor.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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);