From b35a780479bbf5038a825d415f9ca49c92f6b425 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sat, 30 Jan 2021 13:21:53 -0500 Subject: ctrl+q to quit (with unsaved changes dialog) --- buffer.c | 2 ++ command.c | 29 +++++++++++++++++++++++++++-- command.h | 2 ++ main.c | 8 +++++--- menu.c | 7 +++++-- ted.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ted.cfg | 1 + ted.h | 1 - util.c | 3 +++ 9 files changed, 103 insertions(+), 8 deletions(-) diff --git a/buffer.c b/buffer.c index 11b1b6c..d7699db 100644 --- a/buffer.c +++ b/buffer.c @@ -640,6 +640,8 @@ void buffer_new_file(TextBuffer *buffer, char const *filename) { buffer->nlines = 1; } +// Save the buffer to its current filename. This will rewrite the entire file, regardless of +// whether there are any unsaved changes. bool buffer_save(TextBuffer *buffer) { if (!buffer->is_line_buffer && buffer->filename) { FILE *out = fopen(buffer->filename, "wb"); diff --git a/command.c b/command.c index a7ff287..3a52746 100644 --- a/command.c +++ b/command.c @@ -159,9 +159,34 @@ void command_execute(Ted *ted, Command c, i64 argument) { menu_open(ted, MENU_SAVE_AS); } break; + case CMD_SAVE_ALL: + ted_save_all(ted); + break; case CMD_QUIT: - // @TODO: check for unsaved changes in all buffers - ted->quit = true; + // pass argument of 2 to override dialog + if (argument == 2) { + ted->quit = true; + } else { + *ted->warn_unsaved_names = 0; + bool *buffers_used = ted->buffers_used; + bool first = true; + for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) { + if (buffers_used[i]) { + buffer = &ted->buffers[i]; + if (buffer_unsaved_changes(buffer)) { + strbuf_catf(ted->warn_unsaved_names, "%s%s", first ? "" : ", ", path_filename(buffer->filename)); + first = false; + } + } + } + if (*ted->warn_unsaved_names) { + ted->warn_unsaved = CMD_QUIT; + menu_open(ted, MENU_WARN_UNSAVED); + } else { + // no unsaved changes + ted->quit = true; + } + } break; case CMD_UNDO: if (buffer) buffer_undo(buffer, argument); diff --git a/command.h b/command.h index 9c2fba3..63afdb2 100644 --- a/command.h +++ b/command.h @@ -41,6 +41,7 @@ ENUM_U16 { CMD_OPEN, // open a file CMD_SAVE, // save current buffer CMD_SAVE_AS, + CMD_SAVE_ALL, // save all open buffers with unsaved changes CMD_NEW, CMD_UNDO, CMD_REDO, @@ -101,6 +102,7 @@ static CommandName const command_names[CMD_COUNT] = { {"new", CMD_NEW}, {"save", CMD_SAVE}, {"save-as", CMD_SAVE_AS}, + {"save-all", CMD_SAVE_ALL}, {"quit", CMD_QUIT}, {"undo", CMD_UNDO}, {"redo", CMD_REDO}, diff --git a/main.c b/main.c index 8ef9aa2..1157ce4 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ // @TODO: -// - when closing tabs/window, warn on unsaved changes +// - middle click to close tab // - try opening a file you don't have read permission for -- check for memory leaks! // - show something informative when there's no nodes open (i.e. ted->active_node == NULL). @@ -570,9 +570,11 @@ int main(int argc, char **argv) { SDL_DestroyWindow(window); SDL_Quit(); for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) - buffer_free(&ted->buffers[i]); + if (ted->buffers_used[i]) + buffer_free(&ted->buffers[i]); for (u16 i = 0; i < TED_MAX_NODES; ++i) - node_free(&ted->nodes[i]); + if (ted->nodes_used[i]) + node_free(&ted->nodes[i]); buffer_free(&ted->line_buffer); text_font_free(ted->font); text_font_free(ted->font_bold); diff --git a/menu.c b/menu.c index df3c7a0..5cdf83f 100644 --- a/menu.c +++ b/menu.c @@ -129,13 +129,16 @@ static void menu_update(Ted *ted, Menu menu) { // save changes switch (ted->warn_unsaved) { case CMD_TAB_CLOSE: + menu_close(ted, true); if (buffer_save(ted->prev_active_buffer)) { - menu_close(ted, true); command_execute(ted, CMD_TAB_CLOSE, 1); } break; case CMD_QUIT: - assert(0); // @TODO! + menu_close(ted, true); + if (ted_save_all(ted)) { + command_execute(ted, CMD_QUIT, 1); + } break; default: assert(0); diff --git a/ted.c b/ted.c index 431768e..0e8be05 100644 --- a/ted.c +++ b/ted.c @@ -1,3 +1,5 @@ +static void menu_open(Ted *ted, Menu menu); + // this is a macro so we get -Wformat warnings #define ted_seterr(buffer, ...) \ snprintf(ted->error, sizeof ted->error - 1, __VA_ARGS__) @@ -196,3 +198,59 @@ static bool ted_new_file(Ted *ted) { return false; } } + +// sets the active buffer to this buffer, and updates active_node, etc. accordingly +static void ted_switch_to_buffer(Ted *ted, u16 buffer_idx) { + ted->active_buffer = &ted->buffers[buffer_idx]; + // now we need to figure out where this buffer is + bool *nodes_used = ted->nodes_used; + for (u16 i = 0; i < TED_MAX_NODES; ++i) { + if (nodes_used[i]) { + Node *node = &ted->nodes[i]; + arr_foreach_ptr(node->tabs, u16, tab) { + if (buffer_idx == *tab) { + node->active_tab = (u16)(tab - node->tabs); + ted->active_node = node; + return; + } + } + } + } + assert(0); +} + +// are there any unsaved changes in any buffers? +static bool ted_any_unsaved_changes(Ted *ted) { + bool *buffers_used = ted->buffers_used; + for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) { + if (buffers_used[i]) { + if (buffer_unsaved_changes(&ted->buffers[i])) + return true; + } + } + return false; +} + +// save all changes to all buffers with unsaved changes. +// returns true if all buffers were saved successfully +static bool ted_save_all(Ted *ted) { + bool success = true; + bool *buffers_used = ted->buffers_used; + for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) { + if (buffers_used[i]) { + TextBuffer *buffer = &ted->buffers[i]; + if (buffer_unsaved_changes(buffer)) { + if (buffer->filename && streq(buffer->filename, TED_UNTITLED)) { + ted_switch_to_buffer(ted, i); + menu_open(ted, MENU_SAVE_AS); + success = false; // we haven't saved this buffer yet; we've just opened the "save as" menu. + break; + } else { + if (!buffer_save(buffer)) + success = false; + } + } + } + } + return success; +} diff --git a/ted.cfg b/ted.cfg index e7af61a..c7d0307 100644 --- a/ted.cfg +++ b/ted.cfg @@ -66,6 +66,7 @@ Ctrl+PageDown = 10 :page-down Ctrl+o = :open Ctrl+n = :new Ctrl+s = :save +Ctrl+Alt+Shift+s = :save-all Ctrl+Shift+s = :save-as Ctrl+q = :quit Ctrl+z = :undo diff --git a/ted.h b/ted.h index 74b5145..7c546ae 100644 --- a/ted.h +++ b/ted.h @@ -155,5 +155,4 @@ typedef struct Ted { char error_shown[512]; // error display in box on screen } Ted; -// forward declarations void command_execute(Ted *ted, Command c, i64 argument); diff --git a/util.c b/util.c index 59ece04..7a970a2 100644 --- a/util.c +++ b/util.c @@ -57,6 +57,9 @@ static char *str_dup(char const *src) { // first, check that str is actually an array #define strbuf_printf(str, ...) assert(sizeof str != 4 && sizeof str != 8), \ str_printf(str, sizeof str, __VA_ARGS__) +#define str_catf(str, size, ...) str_printf((str) + strlen(str), (size) - strlen(str), __VA_ARGS__) +#define strbuf_catf(str, ...) assert(sizeof str != 4 && sizeof str != 8), \ + str_catf(str, sizeof str, __VA_ARGS__) // on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823 // it is unusual to have a string that long. -- cgit v1.2.3