From f6d49d377ac136fc29457b3b4501f0488b6412e3 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sun, 24 Jan 2021 19:27:49 -0500 Subject: copy/cut/paste --- Untitled | 6 ++--- buffer.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- command.c | 9 +++++++ command.h | 6 +++++ main.c | 1 + string32.c | 1 + ted.cfg | 3 +++ ted.h | 1 + 8 files changed, 111 insertions(+), 6 deletions(-) diff --git a/Untitled b/Untitled index 967045d..e988e21 100644 --- a/Untitled +++ b/Untitled @@ -1,4 +1,4 @@ 1 2 3 4 -t e s t -he ll o!! !! -t e s t +1 4 9 16 +1 8 27 64 +1 16 81 256 \ No newline at end of file diff --git a/buffer.c b/buffer.c index cc97e19..422f03c 100644 --- a/buffer.c +++ b/buffer.c @@ -1,4 +1,6 @@ // Text buffers - These store the contents of a file. +// NOTE: All text editing should be done through the two functions +// buffer_insert_text_at_pos and buffer_delete_chars_at_pos // this is a macro so we get -Wformat warnings #define buffer_seterr(buffer, ...) \ @@ -181,7 +183,7 @@ static u64 buffer_checksum(TextBuffer *buffer) { // Returns the number of characters gotten. // You can pass NULL for text if you just want to know how many characters *could* be accessed before the // end of the file. -static size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, size_t nchars) { +size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, size_t nchars) { if (!buffer_pos_valid(buffer, pos)) { return 0; // invalid position. no chars for you! } @@ -213,6 +215,32 @@ static size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t return nchars - chars_left; } +// returns a UTF-32 string of at most `nchars` code points from `buffer` starting at `pos` +// the string should be str32_free'd. +String32 buffer_get_str32_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars) { + String32 s32 = {0}; + size_t len = buffer_get_text_at_pos(buffer, pos, NULL, nchars); + if (len) { + char32_t *str = buffer_calloc(buffer, len, sizeof *str); + if (str) { + buffer_get_text_at_pos(buffer, pos, str, nchars); + s32.str = str; + s32.len = len; + } + } + return s32; +} + +// see buffer_get_str32_text_at_pos. returns NULL on failure (out of memory) +// the returned string should be free'd +char *buffer_get_utf8_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars) { + String32 s32 = buffer_get_str32_text_at_pos(buffer, pos, nchars); + char *ret = str32_to_utf8_cstr(s32); + if (!ret) buffer_out_of_mem(buffer); + str32_free(&s32); + return ret; +} + static BufferPos buffer_pos_advance(TextBuffer *buffer, BufferPos pos, size_t nchars) { buffer_pos_validate(buffer, &pos); size_t chars_left = nchars; @@ -619,6 +647,9 @@ bool buffer_save(TextBuffer *buffer) { } if (ferror(out)) success = false; if (fclose(out) != 0) success = false; + if (success) { + buffer->modified = false; + } return success; } else { buffer_seterr(buffer, "Couldn't create file %s.", buffer->filename); @@ -1280,6 +1311,8 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 // We need to put this after the end so the emptiness-checking is done after the edit is made. buffer_remove_last_edit_if_empty(buffer); + buffer->modified = true; + BufferPos b = {.line = line_idx, .index = index}; return b; } @@ -1522,6 +1555,8 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) // cursor position could have been invalidated by this edit buffer_validate_cursor(buffer); + + buffer->modified = true; } // Delete characters between the given buffer positions. Returns number of characters deleted. @@ -1683,6 +1718,49 @@ void buffer_redo(TextBuffer *buffer, i64 ntimes) { } } +void buffer_copy_or_cut(TextBuffer *buffer, bool cut) { + if (buffer->selection) { + BufferPos pos1 = buffer_pos_min(buffer->selection_pos, buffer->cursor_pos); + BufferPos pos2 = buffer_pos_max(buffer->selection_pos, buffer->cursor_pos); + i64 selection_len = buffer_pos_diff(buffer, pos1, pos2); + char *text = buffer_get_utf8_text_at_pos(buffer, pos1, (size_t)selection_len); + if (text) { + int err = SDL_SetClipboardText(text); + free(text); + if (err < 0) { + buffer_seterr(buffer, "Couldn't get clipboard contents: %s", SDL_GetError()); + } else { + // text copied successfully + if (cut) { + buffer_delete_selection(buffer); + } + } + } + } +} + +void buffer_copy(TextBuffer *buffer) { + buffer_copy_or_cut(buffer, false); +} + +void buffer_cut(TextBuffer *buffer) { + buffer_copy_or_cut(buffer, true); +} + +void buffer_paste(TextBuffer *buffer) { + if (SDL_HasClipboardText()) { + char *text = SDL_GetClipboardText(); + if (text) { + String32 str = str32_from_utf8(text); + if (str.len) { + buffer_insert_text_at_cursor(buffer, str); + str32_free(&str); + } + SDL_free(text); + } + } +} + // for debugging #if DEBUG static void buffer_pos_check_valid(TextBuffer *buffer, BufferPos p) { @@ -1752,8 +1830,11 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { if (!buffer->is_line_buffer) { // header glColor3f(1,1,1); float x = x1, y = y1; - if (buffer->filename) - text_render_with_state(font, &text_state, buffer->filename, x, y); + if (buffer->filename) { + char text[256] = {0}; + strbuf_printf(text, "%s%s%s", buffer->modified ? "*" : "", buffer->filename, buffer->modified ? "*" :""); + text_render_with_state(font, &text_state, text, x, y); + } #if DEBUG // show checksum char checksum[32] = {0}; @@ -1853,6 +1934,9 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { .render = true }; + // sel_pos >= scrolloff + // sel - scroll >= scrolloff + // scroll <= sel - scrolloff text_state.y -= (float)(buffer->scroll_y - start_line) * char_height; gl_color_rgba(colors[COLOR_TEXT]); diff --git a/command.c b/command.c index 74a624a..d90153d 100644 --- a/command.c +++ b/command.c @@ -129,6 +129,15 @@ void command_execute(Ted *ted, Command c, i64 argument) { case CMD_REDO: if (buffer) buffer_redo(buffer, argument); break; + case CMD_COPY: + if (buffer) buffer_copy(buffer); + break; + case CMD_CUT: + if (buffer) buffer_cut(buffer); + break; + case CMD_PASTE: + if (buffer) buffer_paste(buffer); + break; case CMD_TEXT_SIZE_INCREASE: { i64 new_text_size = settings->text_size + argument; diff --git a/command.h b/command.h index 50821b5..2372e37 100644 --- a/command.h +++ b/command.h @@ -38,6 +38,9 @@ ENUM_U16 { CMD_SAVE, // save current buffer CMD_UNDO, CMD_REDO, + CMD_COPY, + CMD_CUT, + CMD_PASTE, CMD_TEXT_SIZE_INCREASE, CMD_TEXT_SIZE_DECREASE, @@ -87,6 +90,9 @@ static CommandName const command_names[CMD_COUNT] = { {"save", CMD_SAVE}, {"undo", CMD_UNDO}, {"redo", CMD_REDO}, + {"copy", CMD_COPY}, + {"cut", CMD_CUT}, + {"paste", CMD_PASTE}, {"increase-text-size", CMD_TEXT_SIZE_INCREASE}, {"decrease-text-size", CMD_TEXT_SIZE_DECREASE}, {"escape", CMD_ESCAPE}, diff --git a/main.c b/main.c index b7ee115..8895c1f 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ // @TODO: // - Windows installation +// - error bar #include "base.h" no_warn_start #if _WIN32 diff --git a/string32.c b/string32.c index f678ad1..d7278d2 100644 --- a/string32.c +++ b/string32.c @@ -13,6 +13,7 @@ String32 str32_substr(String32 s, size_t from, size_t len) { return str32(s.str + from, len); } +// frees string and sets it to "" void str32_free(String32 *s) { free(s->str); s->str = NULL; diff --git a/ted.cfg b/ted.cfg index 623e914..c9608a5 100644 --- a/ted.cfg +++ b/ted.cfg @@ -60,6 +60,9 @@ Ctrl+o = :open Ctrl+s = :save Ctrl+z = :undo Ctrl+Shift+z = :redo +Ctrl+c = :copy +Ctrl+x = :cut +Ctrl+v = :paste Ctrl++ = 3 :increase-text-size Ctrl+- = 3 :decrease-text-size diff --git a/ted.h b/ted.h index 16fb131..8556d36 100644 --- a/ted.h +++ b/ted.h @@ -63,6 +63,7 @@ typedef struct { bool is_line_buffer; // "line buffers" are buffers which can only have one line of text (used for inputs) bool selection; bool store_undo_events; // set to false to disable undo events + bool modified; // has the buffer been modified since it was loaded/saved? float x1, y1, x2, y2; u32 nlines; u32 lines_capacity; -- cgit v1.2.3