summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-01-29 22:33:31 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-01-29 22:33:31 -0500
commit55fd631d86769e719f81206901bc1c3fb598fb5e (patch)
treeeca599d625d8d55d03c2d793e5f85d51f712a2c2
parent3cc173dd38e85c751bb1a9f9288ebe974e567ba4 (diff)
quitting, closing tabs
-rw-r--r--Untitled2
-rw-r--r--command.c21
-rw-r--r--command.h2
-rw-r--r--main.c13
-rw-r--r--math.c19
-rw-r--r--node.c51
-rw-r--r--ted.c (renamed from ted-base.c)93
-rw-r--r--ted.cfg1
-rw-r--r--ted.h1
9 files changed, 164 insertions, 39 deletions
diff --git a/Untitled b/Untitled
index 3af9812..6609522 100644
--- a/Untitled
+++ b/Untitled
@@ -1,3 +1,3 @@
-aa 1 2 3 4
+aaaa 1 2 3 4
56 78 910 1112
testing
diff --git a/command.c b/command.c
index 5c7cfe0..5acd922 100644
--- a/command.c
+++ b/command.c
@@ -147,17 +147,21 @@ void command_execute(Ted *ted, Command c, i64 argument) {
if (buffer) {
if (buffer->filename && streq(buffer->filename, TED_UNTITLED)) {
// don't worry, this won't catch files called "Untitled"; buffer->filename is the full path.
- goto save_as;
+ command_execute(ted, CMD_SAVE_AS, 1);
+ return;
}
buffer_save(buffer);
}
break;
case CMD_SAVE_AS:
- save_as:
if (buffer && !buffer->is_line_buffer) {
menu_open(ted, MENU_SAVE_AS);
}
break;
+ case CMD_QUIT:
+ // @TODO: check for unsaved changes in all buffers
+ ted->quit = true;
+ break;
case CMD_UNDO:
if (buffer) buffer_undo(buffer, argument);
break;
@@ -189,11 +193,20 @@ void command_execute(Ted *ted, Command c, i64 argument) {
}
} break;
+ case CMD_TAB_CLOSE: {
+ Node *node = ted->active_node;
+ if (node) {
+ node_tab_close(ted, node, node->active_tab);
+ } else {
+ command_execute(ted, CMD_QUIT, 1);
+ return;
+ }
+ } break;
case CMD_TAB_NEXT:
- node_tab_next(ted, ted->active_node, argument);
+ if (ted->active_node) node_tab_next(ted, ted->active_node, argument);
break;
case CMD_TAB_PREV:
- node_tab_prev(ted, ted->active_node, argument);
+ if (ted->active_node) node_tab_prev(ted, ted->active_node, argument);
break;
case CMD_ESCAPE:
diff --git a/command.h b/command.h
index 04a24f8..9c2fba3 100644
--- a/command.h
+++ b/command.h
@@ -47,6 +47,7 @@ ENUM_U16 {
CMD_COPY,
CMD_CUT,
CMD_PASTE,
+ CMD_QUIT,
CMD_TAB_CLOSE,
CMD_TAB_NEXT,
@@ -100,6 +101,7 @@ static CommandName const command_names[CMD_COUNT] = {
{"new", CMD_NEW},
{"save", CMD_SAVE},
{"save-as", CMD_SAVE_AS},
+ {"quit", CMD_QUIT},
{"undo", CMD_UNDO},
{"redo", CMD_REDO},
{"copy", CMD_COPY},
diff --git a/main.c b/main.c
index 7cb7f06..c7e12f7 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,8 @@
// @TODO:
// - when closing tabs/window, warn on unsaved changes
-// - try opening a file you don't have read permission for
+// - 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).
// - split
// - Windows installation
#include "base.h"
@@ -42,7 +44,7 @@ no_warn_end
#include "string32.c"
#include "arr.c"
#include "buffer.c"
-#include "ted-base.c"
+#include "ted.c"
#include "ui.c"
#include "node.c"
#include "command.c"
@@ -271,8 +273,7 @@ int main(int argc, char **argv) {
Uint32 time_at_last_frame = SDL_GetTicks();
- bool quit = false;
- while (!quit) {
+ while (!ted->quit) {
#if DEBUG
//printf("\033[H\033[2J");
#endif
@@ -312,7 +313,7 @@ int main(int argc, char **argv) {
| (u32)alt_down << KEY_MODIFIER_ALT_BIT;
switch (event.type) {
case SDL_QUIT:
- quit = true;
+ command_execute(ted, CMD_QUIT, 1);
break;
case SDL_MOUSEWHEEL: {
// scroll with mouse wheel
@@ -476,7 +477,7 @@ int main(int argc, char **argv) {
Font *font = ted->font;
{
- float x1 = 50, y1 = 50, x2 = window_width-50, y2 = window_height-50;
+ float x1 = 25, y1 = 25, x2 = window_width-25, y2 = window_height-25;
Node *node = ted->root;
node_frame(ted, node, rect4(x1, y1, x2, y2));
}
diff --git a/math.c b/math.c
index 71afabc..130aff7 100644
--- a/math.c
+++ b/math.c
@@ -46,12 +46,31 @@ static int clampi(int x, int a, int b) {
return x;
}
+static i16 clamp_i16(i16 x, i16 a, i16 b) {
+ if (x < a) return a;
+ if (x > b) return b;
+ return x;
+}
+
+static u16 clamp_u16(u16 x, u16 a, u16 b) {
+ if (x < a) return a;
+ if (x > b) return b;
+ return x;
+}
+
static i32 clamp_i32(i32 x, i32 a, i32 b) {
if (x < a) return a;
if (x > b) return b;
return x;
}
+static u32 clamp_u32(u32 x, u32 a, u32 b) {
+ if (x < a) return a;
+ if (x > b) return b;
+ return x;
+}
+
+
// remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval.
static float remapf(float x, float from_a, float from_b, float to_a, float to_b) {
float pos = (x - from_a) / (from_b - from_a);
diff --git a/node.c b/node.c
index 6681a84..f927f70 100644
--- a/node.c
+++ b/node.c
@@ -20,6 +20,55 @@ static void node_tab_prev(Ted *ted, Node *node, i64 n) {
node_tab_next(ted, node, -n);
}
+static void node_free(Node *node) {
+ arr_free(node->tabs);
+ memset(node, 0, sizeof *node);
+}
+
+static void node_close(Ted *ted, Node *node) {
+ // @TODO(split)
+
+ // delete all associated buffers
+ arr_foreach_ptr(node->tabs, u16, tab) {
+ u16 buffer_index = *tab;
+ ted_delete_buffer(ted, buffer_index);
+ }
+
+ node_free(node);
+
+ u16 node_index = (u16)(node - ted->nodes);
+ assert(node_index < TED_MAX_NODES);
+ ted->nodes_used[node_index] = false;
+ if (ted->active_node == node) {
+ ted->active_node = NULL;
+ }
+}
+
+// close tab, WITHOUT checking for unsaved changes!
+static void node_tab_close(Ted *ted, Node *node, u16 index) {
+ u16 ntabs = (u16)arr_len(node->tabs);
+ assert(index < ntabs);
+ if (ntabs == 1) {
+ // only 1 tab left, just close the node
+ node_close(ted, node);
+ } else {
+ u16 buffer_index = node->tabs[index];
+ // remove tab from array
+ memmove(&node->tabs[index], &node->tabs[index + 1], (ntabs - (index + 1)) * sizeof *node->tabs);
+ arr_remove_last(node->tabs);
+ ted_delete_buffer(ted, buffer_index);
+
+ ntabs = (u16)arr_len(node->tabs); // update ntabs
+ assert(ntabs);
+ // make sure active tab is valid
+ node->active_tab = clamp_u16(node->active_tab, 0, ntabs - 1);
+ if (ted->active_node == node) {
+ // fix active buffer if necessary
+ ted->active_buffer = &ted->buffers[node->tabs[node->active_tab]];
+ }
+ }
+}
+
static void node_frame(Ted *ted, Node *node, Rect r) {
if (node->tabs) {
bool is_active = node == ted->active_node;
@@ -75,7 +124,9 @@ static void node_frame(Ted *ted, Node *node, Rect r) {
}
TextBuffer *buffer = &ted->buffers[buffer_index];
+ assert(ted->buffers_used[buffer_index]);
Rect buffer_rect = rect_translate(r, V2(0, tab_bar_height));
+ buffer_rect.size.y -= tab_bar_height;
buffer_render(buffer, buffer_rect);
} else {
diff --git a/ted-base.c b/ted.c
index 8b0ab07..bc34123 100644
--- a/ted-base.c
+++ b/ted.c
@@ -102,7 +102,17 @@ static i32 ted_new_buffer(Ted *ted) {
return -1;
}
-// returns the index of an available node, or -1 if none are available
+// Opposite of ted_new_buffer
+// Make sure you set active_buffer to something else if you delete it!
+static void ted_delete_buffer(Ted *ted, u16 index) {
+ TextBuffer *buffer = &ted->buffers[index];
+ if (buffer == ted->active_buffer)
+ ted->active_buffer = NULL; // make sure we don't set the active buffer to something invalid
+ buffer_free(buffer);
+ ted->buffers_used[index] = false;
+}
+
+// Returns the index of an available node, or -1 if none are available
static i32 ted_new_node(Ted *ted) {
bool *nodes_used = ted->nodes_used;
for (i32 i = 0; i < TED_MAX_NODES; ++i) {
@@ -111,52 +121,79 @@ static i32 ted_new_node(Ted *ted) {
return i;
}
}
- ted_seterr(ted, "Too many buffers open!");
+ ted_seterr(ted, "Too many nodes.");
return -1;
}
-static void node_free(Node *node) {
- arr_free(node->tabs);
-}
+static void node_tab_close(Ted *ted, Node *node, u16 index);
-// returns true on success
-static bool ted_open_file(Ted *ted, char const *filename) {
+// Open a new buffer. Fills out *tab to the index of the tab used, and *buffer_idx to the index of the buffer.
+// Returns true on success.
+static Status ted_open_buffer(Ted *ted, u16 *buffer_idx, u16 *tab) {
i32 new_buffer_index = ted_new_buffer(ted);
if (new_buffer_index >= 0) {
Node *node = ted->active_node;
+ if (!node) {
+ // no active node; let's create one!
+ i32 node_idx = ted_new_node(ted);
+ if (node_idx >= 0) {
+ node = &ted->nodes[node_idx];
+ ted->active_node = node;
+ } else {
+ ted_delete_buffer(ted, (u16)new_buffer_index);
+ return false;
+ }
+ }
if (arr_len(node->tabs) < TED_MAX_TABS) {
arr_add(node->tabs, (u16)new_buffer_index);
TextBuffer *new_buffer = &ted->buffers[new_buffer_index];
- if (node->tabs && buffer_load_file(new_buffer, filename)) {
- ted->active_buffer = new_buffer;
- node->active_tab = (u16)(arr_len(node->tabs) - 1);
- return true;
- }
+ ted->active_buffer = new_buffer;
+ node->active_tab = (u16)(arr_len(node->tabs) - 1);
+ *buffer_idx = (u16)new_buffer_index;
+ *tab = node->active_tab;
+ return true;
} else {
ted_seterr(ted, "Too many tabs.");
+ ted_delete_buffer(ted, (u16)new_buffer_index);
+ return false;
}
+ } else {
+ return false;
}
- return false;
}
-static void ted_new_file(Ted *ted) {
- i32 new_buffer_index = ted_new_buffer(ted);
- if (new_buffer_index >= 0) {
- Node *node = ted->active_node;
- if (arr_len(node->tabs) < TED_MAX_TABS) {
- arr_add(node->tabs, (u16)new_buffer_index);
- TextBuffer *new_buffer = &ted->buffers[new_buffer_index];
- if (node->tabs) {
- buffer_new_file(new_buffer, TED_UNTITLED);
- if (!buffer_haserr(new_buffer)) {
- ted->active_buffer = new_buffer;
- node->active_tab = (u16)(arr_len(node->tabs) - 1);
- }
- }
+// Returns true on success
+static bool ted_open_file(Ted *ted, char const *filename) {
+ u16 buffer_idx, tab_idx;
+ if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) {
+ TextBuffer *buffer = &ted->buffers[buffer_idx];
+ if (buffer_load_file(buffer, filename)) {
+ return true;
} else {
- ted_seterr(ted, "Too many tabs.");
+ node_tab_close(ted, ted->active_node, tab_idx);
+ ted_delete_buffer(ted, (u16)buffer_idx);
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+static bool ted_new_file(Ted *ted) {
+ u16 buffer_idx, tab_idx;
+ if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) {
+ TextBuffer *buffer = &ted->buffers[buffer_idx];
+ buffer_new_file(buffer, TED_UNTITLED);
+ if (!buffer_haserr(buffer)) {
+ return true;
+ } else {
+ node_tab_close(ted, ted->active_node, tab_idx);
+ ted_delete_buffer(ted, (u16)buffer_idx);
+ return false;
}
+ } else {
+ return false;
}
}
diff --git a/ted.cfg b/ted.cfg
index bdc8c59..e7af61a 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -67,6 +67,7 @@ Ctrl+o = :open
Ctrl+n = :new
Ctrl+s = :save
Ctrl+Shift+s = :save-as
+Ctrl+q = :quit
Ctrl+z = :undo
Ctrl+Shift+z = :redo
Ctrl+c = :copy
diff --git a/ted.h b/ted.h
index d589365..6271a6d 100644
--- a/ted.h
+++ b/ted.h
@@ -138,6 +138,7 @@ typedef struct Ted {
double error_time; // time error box was opened (in seconds -- see time_get_seconds)
KeyAction key_actions[KEY_COMBO_COUNT];
bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed"
+ bool quit; // if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!!
char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite
char local_data_dir[TED_PATH_MAX];
char global_data_dir[TED_PATH_MAX];