From c38d6ce47b671af78e88498e484a7e2fbc6b2662 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Mon, 12 Oct 2015 12:15:11 -0400 Subject: [PATCH] API started --- 4coder_custom.cpp | 188 +-- 4coder_custom.h | 56 +- 4coder_custom_vim.cpp | 135 ++ 4coder_helper.h | 228 ++++ 4cpp_lexer.h | 8 +- 4cpp_string.h | 18 +- 4ed.cpp | 651 ++++------ 4ed_color_view.cpp | 435 +------ 4ed_command.cpp | 8 +- 4ed_debug_view.cpp | 4 +- 4ed_file_view.cpp | 2520 ++++++++++++++++++++++++++++---------- 4ed_interactive_view.cpp | 110 +- 4ed_layout.cpp | 6 +- 13 files changed, 2751 insertions(+), 1616 deletions(-) create mode 100644 4coder_custom_vim.cpp create mode 100644 4coder_helper.h diff --git a/4coder_custom.cpp b/4coder_custom.cpp index dfbdea9a..923f15d2 100644 --- a/4coder_custom.cpp +++ b/4coder_custom.cpp @@ -2,113 +2,14 @@ * Example use of customization API */ +// NOTE(allen): NEW THINGS TO LOOK FOR: +// MAPID_USER_CUSTOM - define maps other than the built in GLOBAL/FILE maps +// inherit_map +// +// get_settings + #include "4coder_custom.h" - -// NOTE(allen): All this helper stuff is here to make the new API -// work a lot like the old API -struct Bind_Helper{ - Binding_Unit *cursor, *start, *end; - Binding_Unit *header, *map; - int write_total; - int error; -}; - -#define BH_ERR_NONE 0 -#define BH_ERR_MISSING_END_MAP 1 -#define BH_ERR_MISSING_BEGIN_MAP 2 - -inline Binding_Unit* -write_unit(Bind_Helper *helper, Binding_Unit unit){ - Binding_Unit *p = 0; - helper->write_total += sizeof(Binding_Unit); - if (helper->error == 0 && helper->cursor != helper->end){ - p = helper->cursor++; - *p = unit; - } - return p; -} - -inline Bind_Helper -begin_bind_helper(void *data, int size){ - Bind_Helper result; - - result.header = 0; - result.map = 0; - result.write_total = 0; - result.error = 0; - - result.cursor = (Binding_Unit*)data; - result.start = result.cursor; - result.end = result.start + size / sizeof(Binding_Unit); - - Binding_Unit unit; - unit.type = UNIT_HEADER; - unit.header.total_size = sizeof(Binding_Unit); - result.header = write_unit(&result, unit); - - return result; -} - -inline void -begin_map(Bind_Helper *helper, int mapid){ - if (helper->map != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END_MAP; - - Binding_Unit unit; - unit.type = UNIT_MAP_BEGIN; - unit.map_begin.mapid = mapid; - helper->map = write_unit(helper, unit); -} - -inline void -end_map(Bind_Helper *helper){ - if (helper->map == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN_MAP; - - helper->map = 0; -} - -inline void -bind(Bind_Helper *helper, short code, unsigned char modifiers, int cmdid){ - if (helper->map == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN_MAP; - - Binding_Unit unit; - unit.type = UNIT_BINDING; - unit.binding.command_id = cmdid; - unit.binding.code = code; - unit.binding.modifiers = modifiers; - - write_unit(helper, unit); -} - -inline void -bind_me(Bind_Helper *helper, short code, unsigned char modifiers, Custom_Command_Function *func){ - if (helper->map == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN_MAP; - - Binding_Unit unit; - unit.type = UNIT_CALLBACK; - unit.callback.func = func; - unit.callback.code = code; - unit.callback.modifiers = modifiers; - - write_unit(helper, unit); -} - -inline void -bind_vanilla_keys(Bind_Helper *helper, int cmdid){ - bind(helper, 0, 0, cmdid); -} - -inline void -bind_me_vanilla_keys(Bind_Helper *helper, Custom_Command_Function *func){ - bind_me(helper, 0, 0, func); -} - -inline void -end_bind_helper(Bind_Helper *helper){ - if (helper->header){ - helper->header->header.total_size = (int)(helper->cursor - helper->start); - helper->header->header.error = helper->error; - } -} +#include "4coder_helper.h" #define exec_command app.exec_command #define fulfill_interaction app.fulfill_interaction @@ -123,37 +24,81 @@ CUSTOM_COMMAND_SIG(open_in_other){ exec_command(cmd_context, cmdid_interactive_open); } -extern "C" GET_BINDING_DATA(get_bindings){ +extern "C" GET_BINDING_DATA(get_binding){ Bind_Helper context_actual = begin_bind_helper(data, size); Bind_Helper *context = &context_actual; + begin_settings_group(context); + + use_when(context, when_default, 1); + + set(context, set_lex_as_cpp_file, 0); + set(context, set_wrap_lines, 1); + set(context, set_key_mapid, MAPID_FILE); + + // NOTE(allen): options include EOL_USE_CRLF, EOL_USE_CR_USE_LF, EOL_SHOW_CR_USE_LF + // EOL_USE_CRLF - treats a crlf and lf as newline markers, renders lone cr as special character "\r" + // EOL_USE_CR_USE_LF - treats both as separate newline markers + // EOL_SHOW_CR_USE_LF - treats lf as newline marker, renders cr as special character "\r" + set(context, set_end_line_mode, EOL_USE_CRLF); + + end_group(context); + + + begin_settings_group(context); + + use_when(context, when_extension, "cpp"); + use_when(context, when_extension, "hpp"); + use_when(context, when_extension, "c"); + use_when(context, when_extension, "h"); + + set(context, set_lex_as_cpp_file, 1); + set(context, set_key_mapid, MAPID_USER_CUSTOM + 0); + begin_map(context, MAPID_GLOBAL); - // NOTE(allen): Here put the contents of your set_global_bindings bind(context, 'p', MDFR_CTRL, cmdid_open_panel_vsplit); bind(context, '-', MDFR_CTRL, cmdid_open_panel_hsplit); bind(context, 'P', MDFR_CTRL, cmdid_close_panel); bind(context, 'n', MDFR_CTRL, cmdid_interactive_new); bind(context, 'o', MDFR_CTRL, cmdid_interactive_open); bind(context, ',', MDFR_CTRL, cmdid_change_active_panel); - bind(context, 'k', MDFR_CTRL, cmdid_interactive_kill_file); - bind(context, 'i', MDFR_CTRL, cmdid_interactive_switch_file); + bind(context, 'k', MDFR_CTRL, cmdid_interactive_kill_buffer); + bind(context, 'i', MDFR_CTRL, cmdid_interactive_switch_buffer); bind(context, 'c', MDFR_ALT, cmdid_open_color_tweaker); bind(context, 'x', MDFR_ALT, cmdid_open_menu); bind_me(context, 'o', MDFR_ALT, open_in_other); end_map(context); + + begin_map(context, MAPID_USER_CUSTOM + 0); + + // NOTE(allen): Set this map (MAPID_USER_CUSTOM + 0) to + // inherit from MAPID_FILE. When searching if a key is bound + // in this map, if it is not found here it will then search MAPID_FILE. + // + // If this is not set, it defaults to MAPID_GLOBAL. + inherit_map(context, MAPID_FILE); + + bind(context, codes->right, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_right); + bind(context, codes->left, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_left); + + // NOTE(allen): Not currently functional + bind(context, '\t', MDFR_CTRL, cmdid_auto_tab); + + end_map(context); + begin_map(context, MAPID_FILE); - // NOTE(allen): This is a new concept in the API. Binding this can be thought of as binding - // all combos which have an ascii code (shifted or not) and unmodified by CTRL or ALT. - // As of now, if this is used it cannot be overriden for particular combos; this overrides - // normal bindings. + // NOTE(allen): Binding this essentially binds all key combos that + // would normally insert a character into a buffer. + // Or apply this rule (which always works): if the code for the key + // is not in the codes struct, it is a vanilla key. + // It is possible to override this binding for individual keys. bind_vanilla_keys(context, cmdid_write_character); - // NOTE(allen): Here put the contents of your set_file_bindings bind(context, codes->left, MDFR_NONE, cmdid_move_left); bind(context, codes->right, MDFR_NONE, cmdid_move_right); bind(context, codes->del, MDFR_NONE, cmdid_delete); @@ -165,8 +110,8 @@ extern "C" GET_BINDING_DATA(get_bindings){ bind(context, codes->page_up, MDFR_NONE, cmdid_page_up); bind(context, codes->page_down, MDFR_NONE, cmdid_page_down); - bind(context, codes->right, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_right); - bind(context, codes->left, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_left); + bind(context, codes->right, MDFR_CTRL, cmdid_seek_whitespace_right); + bind(context, codes->left, MDFR_CTRL, cmdid_seek_whitespace_left); bind(context, codes->up, MDFR_CTRL, cmdid_seek_whitespace_up); bind(context, codes->down, MDFR_CTRL, cmdid_seek_whitespace_down); @@ -176,23 +121,26 @@ extern "C" GET_BINDING_DATA(get_bindings){ bind(context, 'x', MDFR_CTRL, cmdid_cut); bind(context, 'v', MDFR_CTRL, cmdid_paste); bind(context, 'V', MDFR_CTRL, cmdid_paste_next); + bind(context, 'z', MDFR_CTRL, cmdid_undo); + bind(context, 'y', MDFR_CTRL, cmdid_redo); bind(context, 'd', MDFR_CTRL, cmdid_delete_chunk); bind(context, 'l', MDFR_CTRL, cmdid_toggle_line_wrap); bind(context, 'L', MDFR_CTRL, cmdid_toggle_endline_mode); bind(context, 'u', MDFR_CTRL, cmdid_to_uppercase); bind(context, 'j', MDFR_CTRL, cmdid_to_lowercase); bind(context, '?', MDFR_CTRL, cmdid_toggle_show_whitespace); + + // NOTE(allen): These whitespace manipulators are not currently functional bind(context, '`', MDFR_CTRL, cmdid_clean_line); bind(context, '~', MDFR_CTRL, cmdid_clean_all_lines); bind(context, '1', MDFR_CTRL, cmdid_eol_dosify); bind(context, '!', MDFR_CTRL, cmdid_eol_nixify); + bind(context, 'f', MDFR_CTRL, cmdid_search); bind(context, 'r', MDFR_CTRL, cmdid_rsearch); bind(context, 'g', MDFR_CTRL, cmdid_goto_line); - bind(context, '\t', MDFR_CTRL, cmdid_auto_tab); - - bind(context, 'K', MDFR_CTRL, cmdid_kill_file); + bind(context, 'K', MDFR_CTRL, cmdid_kill_buffer); bind(context, 'O', MDFR_CTRL, cmdid_reopen); bind(context, 'w', MDFR_CTRL, cmdid_interactive_save_as); bind(context, 's', MDFR_CTRL, cmdid_save); diff --git a/4coder_custom.h b/4coder_custom.h index 94aa4e2f..d26117a9 100644 --- a/4coder_custom.h +++ b/4coder_custom.h @@ -70,15 +70,17 @@ enum Command_ID{ cmdid_paste, cmdid_paste_next, cmdid_delete_chunk, + cmdid_undo, + cmdid_redo, cmdid_interactive_new, cmdid_interactive_open, cmdid_reopen, cmdid_save, cmdid_interactive_save_as, cmdid_change_active_panel, - cmdid_interactive_switch_file, - cmdid_interactive_kill_file, - cmdid_kill_file, + cmdid_interactive_switch_buffer, + cmdid_interactive_kill_buffer, + cmdid_kill_buffer, cmdid_toggle_line_wrap, cmdid_toggle_endline_mode, cmdid_to_uppercase, @@ -141,36 +143,72 @@ struct Application_Links{ Fulfill_Interaction_Function *fulfill_interaction; }; +enum Settings_Unit_Type{ + SUNIT_HEADER, + SUNIT_GROUP, + SUNIT_USE_CLAUSE, + SUNIT_USE_CLAUSE_STRING, + SUNIT_SETTING +}; + +enum Setting_ID{ + set_lex_as_cpp_file, + set_wrap_lines, + set_key_mapid, + set_end_line_mode +}; + +enum Setting_When_Type{ + when_default, + when_extension +}; + +enum End_Of_Line_Options{ + EOL_USE_CRLF, + EOL_USE_CR_USE_LF, + EOL_SHOW_CR_USE_LF +}; + enum Binding_Unit_Type{ UNIT_HEADER, UNIT_MAP_BEGIN, UNIT_BINDING, - UNIT_CALLBACK + UNIT_CALLBACK, + UNIT_INHERIT, + UNIT_SETTINGS_BEGIN, + UNIT_USE_CLAUSE, + UNIT_USE_CLAUSE_STRING, + UNIT_SETTING }; enum Map_ID{ MAPID_GLOBAL, - MAPID_FILE + MAPID_FILE, + MAPID_USER_CUSTOM }; struct Binding_Unit{ Binding_Unit_Type type; union{ - struct{ int total_size; int error; } header; - - struct{ int mapid; } map_begin; + struct{ int total_size; int map_count; int group_count; int error; } header; + struct{ int mapid; int bind_count; } map_begin; + struct{ int mapid; } map_inherit; struct{ int command_id; short code; unsigned char modifiers; } binding; - struct{ Custom_Command_Function *func; short code; unsigned char modifiers; } callback; + + struct{ int clause_type; int value; } use_clause; + struct{ int clause_type; int len; char *value; } use_clause_string; + struct{ int setting_id; int value; } setting; }; }; + diff --git a/4coder_custom_vim.cpp b/4coder_custom_vim.cpp new file mode 100644 index 00000000..81a0348d --- /dev/null +++ b/4coder_custom_vim.cpp @@ -0,0 +1,135 @@ +/* + * This is an example of how vim-keys might start + * to work in 4coder, through the customization API. + */ + +#include "4coder_custom.h" +#include "4coder_helper.h" + +#define exec_command app.exec_command +#define fulfill_interaction app.fulfill_interaction + +extern "C" START_HOOK_SIG(start_hook){ + exec_command(cmd_context, cmdid_open_panel_vsplit); + exec_command(cmd_context, cmdid_change_active_panel); +} + + +extern "C" GET_BINDING_DATA(get_binding){ + Bind_Helper context_actual = begin_bind_helper(data, size); + Bind_Helper *context = &context_actual; + + begin_settings_group(context); + + use_when(context, when_default, 1); + + set(context, set_lex_as_cpp_file, 0); + set(context, set_wrap_lines, 1); + set(context, set_key_mapid, MAPID_FILE); + + // NOTE(allen): options include EOL_USE_CRLF, EOL_USE_CR_USE_LF, EOL_SHOW_CR_USE_LF + // EOL_USE_CRLF - treats a crlf and lf as newline markers, renders lone cr as special character "\r" + // EOL_USE_CR_USE_LF - treats both as separate newline markers + // EOL_SHOW_CR_USE_LF - treats lf as newline marker, renders cr as special character "\r" + set(context, set_end_line_mode, EOL_USE_CRLF); + + end_group(context); + + + begin_settings_group(context); + + use_when(context, when_extension, "cpp"); + use_when(context, when_extension, "hpp"); + use_when(context, when_extension, "c"); + use_when(context, when_extension, "h"); + + set(context, set_lex_as_cpp_file, 1); + set(context, set_key_mapid, MAPID_USER_CUSTOM + 0); + + begin_map(context, MAPID_GLOBAL); + + bind(context, 'p', MDFR_CTRL, cmdid_open_panel_vsplit); + bind(context, '-', MDFR_CTRL, cmdid_open_panel_hsplit); + bind(context, 'P', MDFR_CTRL, cmdid_close_panel); + bind(context, 'n', MDFR_CTRL, cmdid_interactive_new); + bind(context, 'o', MDFR_CTRL, cmdid_interactive_open); + bind(context, ',', MDFR_CTRL, cmdid_change_active_panel); + bind(context, 'k', MDFR_CTRL, cmdid_interactive_kill_buffer); + bind(context, 'i', MDFR_CTRL, cmdid_interactive_switch_buffer); + bind(context, 'c', MDFR_ALT, cmdid_open_color_tweaker); + bind(context, 'x', MDFR_ALT, cmdid_open_menu); + bind_me(context, 'o', MDFR_ALT, open_in_other); + + end_map(context); + + + begin_map(context, MAPID_USER_CUSTOM + 0); + inherit_map(context, MAPID_FILE); + end_map(context); + + int sub_id; + begin_map(context, MAPID_FILE); + + sub_id = begin_sub_map(context); + { + bind_vanilla_keys(context, cmdid_write_character); + + bind(context, codes->left, MDFR_NONE, cmdid_move_left); + bind(context, codes->right, MDFR_NONE, cmdid_move_right); + bind(context, codes->del, MDFR_NONE, cmdid_delete); + bind(context, codes->back, MDFR_NONE, cmdid_backspace); + bind(context, codes->up, MDFR_NONE, cmdid_move_up); + bind(context, codes->down, MDFR_NONE, cmdid_move_down); + bind(context, codes->end, MDFR_NONE, cmdid_seek_end_of_line); + bind(context, codes->home, MDFR_NONE, cmdid_seek_beginning_of_line); + bind(context, codes->page_up, MDFR_NONE, cmdid_page_up); + bind(context, codes->page_down, MDFR_NONE, cmdid_page_down); + } + end_sub_map(context); + + bind_params(context, 'i', MDFR_NONE, cmdid_use_sub_map); + fill_param(context, "sub_id", sub_id); + end_params(context); + + bind_multi(context, 'a', MDFR_NONE); + { + bind(context, 'a', MDFR_NONE, cmdid_move_left); + bind_params(context, 'a', MDFR_NONE, cmdid_use_sub_map); + fill_param(context, "sub_id", sub_id); + end_params(context); + } + end_multi(context); + + bind(context, 'b', cmdid_seek_alphanumeric_left); + bind(context, 'w', cmdid_seek_alphanumeric_right); + bind(context, 'e', cmdid_seek_white_or_token_right); + + // ???? this seems a bit off + bind_compound(context, 'g'); + { + bind(context, 'e', cmdid_seek_white_or_token_left); + } + end_compound(context); + + end_map(context); + end_bind_helper(context); + + return context->write_total; +} + +inline void +strset_(char *dst, char *src){ + do{ + *dst++ = *src++; + }while (*src); +} + +#define strset(d,s) if (sizeof(s) <= sizeof(d)) strset_(d,s) + +extern "C" SET_EXTRA_FONT_SIG(set_extra_font){ + strset(font_out->file_name, "liberation-mono.ttf"); + strset(font_out->font_name, "BIG"); + font_out->size = 25; +} + + diff --git a/4coder_helper.h b/4coder_helper.h new file mode 100644 index 00000000..be5c3ac0 --- /dev/null +++ b/4coder_helper.h @@ -0,0 +1,228 @@ +/* + * Bind helper struct and functions + */ + +struct Bind_Helper{ + Binding_Unit *cursor, *start, *end; + Binding_Unit *header, *group; + int write_total; + int error; +}; + +#define BH_ERR_NONE 0 +#define BH_ERR_MISSING_END 1 +#define BH_ERR_MISSING_BEGIN 2 +#define BH_ERR_OUT_OF_MEMORY 3 + +inline int +seek_null(char *str){ + char *start = str; + while (*str) ++str; + return (int)(str - start); +} + +inline void +copy(char *dest, char *src, int len){ + for (int i = 0; i < len; ++i){ + *dest++ = *src++; + } +} + +inline Binding_Unit* +write_unit(Bind_Helper *helper, Binding_Unit unit){ + Binding_Unit *p = 0; + helper->write_total += sizeof(*p); + if (helper->error == 0 && helper->cursor != helper->end){ + p = helper->cursor++; + *p = unit; + } + return p; +} + +inline char* +write_inline_string(Bind_Helper *helper, char *value, int len){ + char *dest = 0; + helper->write_total += len; + if (helper->error == 0){ + dest = (char*)helper->cursor; + int cursor_advance = len + sizeof(*helper->cursor) - 1; + cursor_advance /= sizeof(*helper->cursor); + cursor_advance *= sizeof(*helper->cursor); + helper->cursor += cursor_advance; + if (helper->cursor < helper->end){ + copy(dest, value, len); + } + else{ + helper->error = BH_ERR_OUT_OF_MEMORY; + } + } + return dest; +} + +inline Bind_Helper +begin_bind_helper(void *data, int size){ + Bind_Helper result; + + result.header = 0; + result.group = 0; + result.write_total = 0; + result.error = 0; + + result.cursor = (Binding_Unit*)data; + result.start = result.cursor; + result.end = result.start + size / sizeof(*result.cursor); + + Binding_Unit unit; + unit.type = UNIT_HEADER; + unit.header.total_size = sizeof(*result.header); + result.header = write_unit(&result, unit); + result.header->header.map_count = 0; + result.header->header.group_count = 0; + + return result; +} + +inline void +begin_map(Bind_Helper *helper, int mapid){ + if (helper->group != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END; + if (!helper->error) ++helper->header->header.map_count; + + Binding_Unit unit; + unit.type = UNIT_MAP_BEGIN; + unit.map_begin.mapid = mapid; + helper->group = write_unit(helper, unit); + helper->group->map_begin.bind_count = 0; +} + +inline void +end_map(Bind_Helper *helper){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + helper->group = 0; +} + +inline void +bind(Bind_Helper *helper, short code, unsigned char modifiers, int cmdid){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error) ++helper->group->map_begin.bind_count; + + Binding_Unit unit; + unit.type = UNIT_BINDING; + unit.binding.command_id = cmdid; + unit.binding.code = code; + unit.binding.modifiers = modifiers; + + write_unit(helper, unit); +} + +inline void +bind_me(Bind_Helper *helper, short code, unsigned char modifiers, Custom_Command_Function *func){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error) ++helper->group->map_begin.bind_count; + + Binding_Unit unit; + unit.type = UNIT_CALLBACK; + unit.callback.func = func; + unit.callback.code = code; + unit.callback.modifiers = modifiers; + + write_unit(helper, unit); +} + +inline void +bind_vanilla_keys(Bind_Helper *helper, int cmdid){ + bind(helper, 0, 0, cmdid); +} + +inline void +bind_me_vanilla_keys(Bind_Helper *helper, Custom_Command_Function *func){ + bind_me(helper, 0, 0, func); +} + +inline void +inherit_map(Bind_Helper *helper, int mapid){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + + Binding_Unit unit; + unit.type = UNIT_INHERIT; + unit.map_inherit.mapid = mapid; + + write_unit(helper, unit); +} + +inline void +end_bind_helper(Bind_Helper *helper){ + if (helper->header){ + helper->header->header.total_size = (int)(helper->cursor - helper->start); + helper->header->header.error = helper->error; + } +} + +inline void +begin_settings_group(Bind_Helper *helper){ + if (helper->group != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END; + if (!helper->error) ++helper->header->header.group_count; + + Binding_Unit unit; + unit.type = UNIT_SETTINGS_BEGIN; + helper->group = write_unit(helper, unit); +} + +inline void +end_group(Bind_Helper *helper){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + helper->group = 0; +} + +inline void +use_when(Bind_Helper *helper, int clause_type, int value){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + + Binding_Unit unit; + unit.type = UNIT_USE_CLAUSE; + unit.use_clause.clause_type = clause_type; + unit.use_clause.value = value; + write_unit(helper, unit); +} + +inline void +use_when(Bind_Helper *helper, int clause_type, char *value){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + + Binding_Unit unit; + unit.type = UNIT_USE_CLAUSE; + unit.use_clause_string.clause_type = clause_type; + unit.use_clause_string.len = seek_null(value); + Binding_Unit *u = write_unit(helper, unit); + u->use_clause_string.value = write_inline_string(helper, value, unit.use_clause_string.len); +} + +inline void +use_when(Bind_Helper *helper, int clause_type, char *value, int len){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + + Binding_Unit unit; + unit.type = UNIT_USE_CLAUSE; + unit.use_clause_string.clause_type = clause_type; + unit.use_clause_string.len = len; + unit.use_clause_string.value = value; + write_unit(helper, unit); +} + +inline void +set(Bind_Helper *helper, int setting_id, int value){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + + Binding_Unit unit; + unit.type = UNIT_SETTING; + unit.setting.setting_id = setting_id; + unit.setting.value = value; + write_unit(helper, unit); +} + +inline void +end_settings_helper(Bind_Helper *helper){ + if (helper->header){ + helper->header->header.error = helper->error; + } +} + diff --git a/4cpp_lexer.h b/4cpp_lexer.h index 11607b41..14d509fe 100644 --- a/4cpp_lexer.h +++ b/4cpp_lexer.h @@ -1661,10 +1661,10 @@ cpp_relex_nonalloc_main(Cpp_Relex_State *state, Cpp_Token_Stack *relex_stack, in for (;;){ Cpp_Read_Result read = cpp_lex_step(state->file, &lex); if (read.has_result){ - if (read.token.start == match_token.start && - read.token.size == match_token.size && - read.token.flags == match_token.flags && - read.token.state_flags == match_token.state_flags){ + if (read.token.start == end_token.start && + read.token.size == end_token.size && + read.token.flags == end_token.flags && + read.token.state_flags == end_token.state_flags){ break; } cpp_push_token_nonalloc(relex_stack, read.token); diff --git a/4cpp_string.h b/4cpp_string.h index 92f77d70..752b96bf 100644 --- a/4cpp_string.h +++ b/4cpp_string.h @@ -111,13 +111,13 @@ FCPP_LINK int hexchar_to_int(char c); FCPP_LINK int int_to_hexchar(char c); FCPP_LINK int hexstr_to_int(String s); -FCPP_LINK void copy_fast_unsafe(char *dest, char *src); +FCPP_LINK int copy_fast_unsafe(char *dest, char *src); FCPP_LINK void copy_fast_unsafe(char *dest, String src); FCPP_LINK bool copy_checked(String *dest, String src); FCPP_LINK bool copy_partial(String *dest, char *src); FCPP_LINK bool copy_partial(String *dest, String src); -inline void copy(char *dest, char *src) { copy_fast_unsafe(dest, src); } +inline int copy(char *dest, char *src) { return copy_fast_unsafe(dest, src); } inline void copy(String *dest, String src) { copy_checked(dest, src); } inline void copy(String *dest, char *src) { copy_partial(dest, src); } @@ -671,14 +671,15 @@ hexstr_to_int(String str){ return x; } -FCPP_LINK void +FCPP_LINK int copy_fast_unsafe(char *dest, char *src){ - int i = 0; - while (src[i] != 0){ - dest[i] = src[i]; - ++i; + char *start = dest; + while (*src != 0){ + *dest = *src; + ++dest; + ++src; } - dest[i] = 0; + return (int)(dest - start); } FCPP_LINK void @@ -688,7 +689,6 @@ copy_fast_unsafe(char *dest, String src){ dest[i] = src.str[i]; ++i; } - dest[i] = 0; } FCPP_LINK bool diff --git a/4ed.cpp b/4ed.cpp index 56e9a8ae..a9fe27f7 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -35,8 +35,6 @@ // // - nav links // -// - undo / redo -// // - on file reopen diff and try to find place for cursor // // TOOLS @@ -123,7 +121,7 @@ struct App_Vars{ char query_[256]; char dest_[256]; - + Delay delay; String mini_str; @@ -179,6 +177,7 @@ COMMAND_DECL(write_character){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); + USE_LAYOUT(layout); USE_MEM(mem); u8 character = (u8)command->key.key.character; @@ -194,38 +193,13 @@ COMMAND_DECL(write_character){ string.size = 1; } - i32 pos = pos_adjust_to_left(view->cursor.pos, file->data); - buffer_replace_range(mem, file, pos, pos, (u8*)string.str, string.size); - -#if 0 - switch ((u8)command->key.key.character){ - case '{': case '}': - case '(': case ')': - case ';': case ':': - case '#': case '\n': - { - view_auto_tab(view, view->cursor.pos, view->cursor.pos); - }break; - } -#endif - - Editing_Layout *layout = command->layout; - Panel *current_panel = layout->panels; - i32 panel_count = layout->panel_count; - for (i32 i = 0; i < panel_count; ++i, ++current_panel){ - File_View *current_view = view_to_file_view(current_panel->view); - if (current_view && current_view->file == file){ - view_measure_wraps(&mem->general, current_view); - if (current_view->cursor.pos >= pos){ - view_cursor_move(current_view, current_view->cursor.pos+string.size); - } - if (current_view->mark >= pos){ - current_view->mark += string.size; - } - current_view->preferred_x = view_get_cursor_x(current_view); - } - } - file->cursor_pos = view->cursor.pos; + i32 pos; + pos = view->cursor.pos; + i32 next_cursor_pos = view->cursor.pos + string.size; + view_replace_range(mem, view, layout, pos, pos, (u8*)string.str, string.size, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + if (view->mark >= pos) view->mark += string.size; + view->file->cursor_pos = view->cursor.pos; } internal i32 @@ -347,7 +321,7 @@ COMMAND_DECL(seek_whitespace_down){ } if (prev_endline == -1 || prev_endline+1 >= size){ - pos = size-1; + pos = size; } else{ pos = prev_endline+1; @@ -533,7 +507,7 @@ COMMAND_DECL(search){ REQ_FILE(file, view); USE_VARS(vars); - view->state = FVIEW_STATE_SEARCH; + view_set_widget(view, FWIDG_SEARCH); view->isearch.str = vars->mini_str; view->isearch.reverse = 0; view->isearch.pos = view->cursor.pos; @@ -545,7 +519,7 @@ COMMAND_DECL(rsearch){ REQ_FILE(file, view); USE_VARS(vars); - view->state = FVIEW_STATE_SEARCH; + view_set_widget(view, FWIDG_SEARCH); view->isearch.str = vars->mini_str; view->isearch.reverse = 1; view->isearch.pos = view->cursor.pos; @@ -557,7 +531,7 @@ COMMAND_DECL(goto_line){ REQ_FILE(file, view); USE_VARS(vars); - view->state = FVIEW_STATE_GOTO_LINE; + view_set_widget(view, FWIDG_GOTO_LINE); view->isearch.str = vars->mini_str; view->isearch.reverse = 1; view->isearch.pos = view->cursor.pos; @@ -579,11 +553,8 @@ COMMAND_DECL(copy){ USE_MEM(mem); Range range = get_range(view->cursor.pos, view->mark); - if (range.smaller < range.larger){ + if (range.start < range.end){ u8 *data = file->data; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - range = range_adjust_to_left(range, data); - } clipboard_copy(&mem->general, working_set, data, range); } } @@ -593,67 +564,31 @@ COMMAND_DECL(cut){ REQ_FILE_VIEW(view); REQ_FILE(file, view); USE_WORKING_SET(working_set); + USE_LAYOUT(layout); USE_MEM(mem); Range range = get_range(view->cursor.pos, view->mark); - if (range.smaller < range.larger){ + if (range.start < range.end){ u8 *data = file->data; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - range = range_adjust_to_left(range, data); - } - clipboard_copy(&mem->general, working_set, data, range); - buffer_delete_range(mem, file, range); - view->mark = pos_universal_fix(range.smaller, + i32 next_cursor_pos = range.start; + clipboard_copy(&mem->general, working_set, data, range); + view_replace_range(mem, view, layout, range.start, range.end, 0, 0, next_cursor_pos); + + view->mark = pos_universal_fix(range.start, file->data, file->size, file->endline_mode); view_measure_wraps(&mem->general, view); - view_cursor_move(view, view->mark); - - Editing_Layout *layout = command->layout; - Panel *panels = layout->panels; - i32 panel_count = layout->panel_count; - i32 shift_amount = range.end - range.start; - for (i32 i = 0; i < panel_count; ++i){ - Panel *current_panel = panels + i; - File_View *current_view = view_to_file_view(current_panel->view); - - if (current_view && current_view != view && current_view->file == view->file){ - if (current_view->mark >= range.end){ - current_view->mark -= shift_amount; - } - else if (current_view->mark > range.start){ - current_view->mark = range.start; - } - - view_measure_wraps(&mem->general, current_view); - - i32 cursor_pos = current_view->cursor.pos; - if (cursor_pos >= range.end){ - view_cursor_move(current_view, cursor_pos - shift_amount); - } - else if (cursor_pos > range.start){ - view_cursor_move(current_view, range.start); - } - } - } + view_cursor_move(view, next_cursor_pos); } } -internal void -view_post_paste_effect(File_View *view, i32 ticks, i32 start, i32 size, u32 color){ - view->paste_effect.start = start; - view->paste_effect.end = start + size; - view->paste_effect.color = color; - view->paste_effect.tick_down = ticks; - view->paste_effect.tick_max = ticks; -} - COMMAND_DECL(paste){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); USE_WORKING_SET(working_set); + USE_LAYOUT(layout); USE_MEM(mem); if (working_set->clipboard_size > 0){ @@ -661,14 +596,11 @@ COMMAND_DECL(paste){ String *src = working_set_clipboard_head(working_set); i32 pos_left = view->cursor.pos; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - pos_left = pos_adjust_to_left(pos_left, file->data); - } - buffer_replace_range(mem, file, pos_left, pos_left, (u8*)src->str, src->size); + i32 next_cursor_pos = pos_left+src->size; + view_replace_range(mem, view, layout, pos_left, pos_left, (u8*)src->str, src->size, next_cursor_pos); - view_measure_wraps(&mem->general, view); - view_cursor_move(view, pos_left+src->size); + view_cursor_move(view, next_cursor_pos); view->mark = pos_universal_fix(pos_left, file->data, file->size, file->endline_mode); @@ -681,18 +613,6 @@ COMMAND_DECL(paste){ File_View *current_view = view_to_file_view(current_panel->view); if (current_view && current_view->file == file){ - if (current_view != view){ - view_measure_wraps(&mem->general, current_view); - if (current_view->cursor.pos > pos_left){ - view_cursor_move(current_view, - current_view->cursor.pos+src->size); - } - - if (current_view->mark > pos_left){ - current_view->mark += src->size; - } - } - view_post_paste_effect(current_view, 20, pos_left, src->size, current_view->style->main.paste_color); } @@ -705,59 +625,33 @@ COMMAND_DECL(paste_next){ REQ_FILE_VIEW(view); REQ_FILE(file, view); USE_WORKING_SET(working_set); + USE_LAYOUT(layout); USE_MEM(mem); if (working_set->clipboard_size > 0 && view->mode.rewrite){ view->next_mode.rewrite = 1; Range range = get_range(view->mark, view->cursor.pos); + String *src = working_set_clipboard_roll_down(working_set); + i32 next_cursor_pos = range.start+src->size; + view_replace_range(mem, view, layout, range.start, range.end, + (u8*)src->str, src->size, next_cursor_pos); - if (range.smaller < range.larger || 1){ - if (file->endline_mode == ENDLINE_RN_COMBINED){ - range = range_adjust_to_left(range, file->data); - } - - String *src = working_set_clipboard_roll_down(working_set); - buffer_replace_range(mem, file, range.smaller, range.larger, - (u8*)src->str, src->size); - - view_measure_wraps(&mem->general, view); - - view_cursor_move(view, range.smaller+src->size); - view->mark = pos_universal_fix(range.smaller, - file->data, file->size, - file->endline_mode); - - Editing_Layout *layout = command->layout; - Panel *panels = layout->panels; - i32 panel_count = layout->panel_count; - i32 shift_amount = range.start - range.end + src->size; - for (i32 i = 0; i < panel_count; ++i){ - Panel *current_panel = panels + i; - File_View *current_view = view_to_file_view(current_panel->view); + view_cursor_move(view, next_cursor_pos); + view->mark = pos_universal_fix(range.start, + file->data, file->size, + file->endline_mode); + + Editing_Layout *layout = command->layout; + Panel *panels = layout->panels; + i32 panel_count = layout->panel_count; + for (i32 i = 0; i < panel_count; ++i){ + Panel *current_panel = panels + i; + File_View *current_view = view_to_file_view(current_panel->view); - if (current_view && current_view->file == file){ - if (current_view != view){ - view_measure_wraps(&mem->general, current_view); - if (current_view->cursor.pos >= range.larger){ - view_cursor_move(current_view, - current_view->cursor.pos + shift_amount); - } - else if (current_view->cursor.pos > range.smaller){ - view_cursor_move(current_view, range.smaller); - } - - if (current_view->mark >= range.larger){ - current_view->mark += shift_amount; - } - else if (current_view->cursor.pos > range.smaller){ - current_view->mark = range.smaller; - } - } - - view_post_paste_effect(current_view, 20, range.smaller, src->size, - current_view->style->main.paste_color); - } + if (current_view && current_view->file == file){ + view_post_paste_effect(current_view, 20, range.start, src->size, + current_view->style->main.paste_color); } } } @@ -770,91 +664,106 @@ COMMAND_DECL(delete_chunk){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); + USE_LAYOUT(layout); USE_MEM(mem); Range range = get_range(view->cursor.pos, view->mark); - if (range.smaller < range.larger){ - if (file->endline_mode == ENDLINE_RN_COMBINED){ - range = range_adjust_to_left(range, file->data); - } - buffer_delete_range(mem, file, range); + if (range.start < range.end){ + i32 next_cursor_pos = range.start; + view_replace_range(mem, view, layout, range.start, range.end, 0, 0, next_cursor_pos); view_measure_wraps(&mem->general, view); - view_cursor_move(view, range.smaller); - view->mark = pos_universal_fix(range.smaller, + view_cursor_move(view, next_cursor_pos); + view->mark = pos_universal_fix(range.start, file->data, file->size, file->endline_mode); - - Editing_Layout *layout = command->layout; - Panel *panels = layout->panels; - i32 panel_count = layout->panel_count; - i32 shift_amount = range.smaller - range.larger; - for (i32 i = 0; i < panel_count; ++i){ - Panel *current_panel = panels + i; - File_View *current_view = view_to_file_view(current_panel->view); - - if (current_view && current_view->file == file && current_view != view){ - view_measure_wraps(&mem->general, current_view); - - if (current_view->cursor.pos >= range.larger){ - view_cursor_move(current_view, - current_view->cursor.pos + shift_amount); - } - else if (current_view->cursor.pos > range.smaller){ - view_cursor_move(current_view, range.smaller); - } - - if (current_view->mark >= range.larger){ - current_view->mark += shift_amount; - } - else if (current_view->cursor.pos > range.smaller){ - current_view->mark = range.smaller; - } - } - } } } +COMMAND_DECL(undo_scrub){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + REQ_FILE(file, view); + + view_set_widget(view, FWIDG_UNDOING); +} + COMMAND_DECL(undo){ ProfileMomentFunction(); REQ_FILE_VIEW(view); - REQ_FILE(file, view); + USE_LAYOUT(layout); USE_MEM(mem); - if (file->undo.edit_count > 0){ - Edit_Step step = file->undo.edits[--file->undo.edit_count]; - - buffer_post_redo(&mem->general, file, step.range.start, step.range.end, step.replaced.size); - buffer_replace_range(mem, file, step.range.start, step.range.end, - (u8*)step.replaced.str, step.replaced.size, 0); - view_cursor_move(view, step.cursor_pos); - view->mark = view->cursor.pos; - - view_post_paste_effect(view, 10, step.range.start, step.replaced.size, - view->style->main.undo_color); - - file->undo.str_size -= step.replaced.size; - } + view_undo(mem, layout, view); } COMMAND_DECL(redo){ ProfileMomentFunction(); REQ_FILE_VIEW(view); - REQ_FILE(file, view); + USE_LAYOUT(layout); USE_MEM(mem); - if (file->undo.edit_redo < file->undo.edit_max){ - Edit_Step step = file->undo.edits[file->undo.edit_redo++]; - - buffer_replace_range(mem, file, step.range.start, step.range.end, - (u8*)step.replaced.str, step.replaced.size, 2); - view_cursor_move(view, step.cursor_pos); - view->mark = view->cursor.pos; - - view_post_paste_effect(view, 10, step.range.start, step.replaced.size, - view->style->main.undo_color); - - file->undo.str_redo += step.replaced.size; - } + view_redo(mem, layout, view); +} + +COMMAND_DECL(increase_rewind_speed){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + + i32 rewind_speed = ROUND32(view->rewind_speed * 4.f); + if (rewind_speed > 1) rewind_speed >>= 1; + else if (rewind_speed == 1) rewind_speed = 0; + else if (rewind_speed == 0) rewind_speed = -1; + else rewind_speed *= 2; + + view->rewind_speed = rewind_speed * 0.25f; +} + +COMMAND_DECL(increase_fastforward_speed){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + + i32 neg_rewind_speed = -ROUND32(view->rewind_speed * 4.f); + if (neg_rewind_speed > 1) neg_rewind_speed >>= 1; + else if (neg_rewind_speed == 1) neg_rewind_speed = 0; + else if (neg_rewind_speed == 0) neg_rewind_speed = -1; + else neg_rewind_speed *= 2; + + view->rewind_speed = -neg_rewind_speed * 0.25f; +} + +COMMAND_DECL(stop_rewind_and_fastforward){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + + view->rewind_speed = 0; +} + +COMMAND_DECL(history_scrub){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + REQ_FILE(file, view); + + view_set_widget(view, FWIDG_RESTORING); +} + +COMMAND_DECL(history_backward){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + REQ_FILE(file, view); + USE_LAYOUT(layout); + USE_MEM(mem); + + view_history_step(mem, layout, view, 1); +} + +COMMAND_DECL(history_forward){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + REQ_FILE(file, view); + USE_LAYOUT(layout); + USE_MEM(mem); + + view_history_step(mem, layout, view, 2); } COMMAND_DECL(interactive_new){ @@ -867,7 +776,7 @@ COMMAND_DECL(interactive_new){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; @@ -889,14 +798,13 @@ COMMAND_DECL(interactive_open){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; Interactive_View *int_view = interactive_view_init(new_view, &vars->hot_directory, style, working_set, delay); - int_view->style = command->style; int_view->interaction = INTV_SYS_FILE_LIST; int_view->action = INTV_OPEN; copy(&int_view->query, "Open: "); @@ -957,14 +865,13 @@ COMMAND_DECL(interactive_save_as){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; Interactive_View *int_view = interactive_view_init(new_view, &vars->hot_directory, style, working_set, delay); - int_view->style = command->style; int_view->interaction = INTV_SYS_FILE_LIST; int_view->action = INTV_SAVE_AS; copy(&int_view->query, "Save As: "); @@ -981,7 +888,7 @@ COMMAND_DECL(change_active_panel){ } } -COMMAND_DECL(interactive_switch_file){ +COMMAND_DECL(interactive_switch_buffer){ ProfileMomentFunction(); USE_VARS(vars); USE_LIVE_SET(live_set); @@ -991,20 +898,19 @@ COMMAND_DECL(interactive_switch_file){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; Interactive_View *int_view = interactive_view_init(new_view, &vars->hot_directory, style, working_set, delay); - int_view->style = command->style; int_view->interaction = INTV_LIVE_FILE_LIST; int_view->action = INTV_SWITCH; copy(&int_view->query, "Switch File: "); } -COMMAND_DECL(interactive_kill_file){ +COMMAND_DECL(interactive_kill_buffer){ ProfileMomentFunction(); USE_VARS(vars); USE_LIVE_SET(live_set); @@ -1014,30 +920,25 @@ COMMAND_DECL(interactive_kill_file){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; Interactive_View *int_view = interactive_view_init(new_view, &vars->hot_directory, style, working_set, delay); - int_view->style = command->style; int_view->interaction = INTV_LIVE_FILE_LIST; int_view->action = INTV_KILL; copy(&int_view->query, "Kill File: "); } -COMMAND_DECL(kill_file){ +COMMAND_DECL(kill_buffer){ ProfileMomentFunction(); - USE_MEM(mem); REQ_FILE_VIEW(view); REQ_FILE(file, view); - USE_LIVE_SET(live_set); - USE_LAYOUT(layout); - USE_WORKING_SET(working_set); + USE_DELAY(delay); - table_remove(&working_set->table, file->source_path); - kill_file(&mem->general, file, live_set, layout); + delayed_action(delay, DACT_TRY_KILL, file->live_name, view->view_base.panel); } COMMAND_DECL(toggle_line_wrap){ @@ -1063,32 +964,30 @@ COMMAND_DECL(toggle_endline_mode){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); + USE_MEM(mem); switch (file->endline_mode){ - case ENDLINE_RN_COMBINED: - { - view->cursor.pos = pos_adjust_to_left(view->cursor.pos, view->file->data); - file->endline_mode = ENDLINE_RN_SEPARATE; - view->cursor = - view_compute_cursor_from_pos(view, view->cursor.pos); - }break; - - case ENDLINE_RN_SEPARATE: - { - file->endline_mode = ENDLINE_RN_SHOWALLR; - view->cursor = - view_compute_cursor_from_pos(view, view->cursor.pos); - }break; - - case ENDLINE_RN_SHOWALLR: - { - view->cursor.pos = pos_adjust_to_self(view->cursor.pos, view->file->data, - view->file->size); - file->endline_mode = ENDLINE_RN_COMBINED; - view->cursor = - view_compute_cursor_from_pos(view, view->cursor.pos); - }break; + case ENDLINE_RN_COMBINED: + { + file->endline_mode = ENDLINE_RN_SEPARATE; + }break; + + case ENDLINE_RN_SEPARATE: + { + file->endline_mode = ENDLINE_RN_SHOWALLR; + }break; + + case ENDLINE_RN_SHOWALLR: + { + view->cursor.pos = pos_adjust_to_self(view->cursor.pos, view->file->data, + view->file->size); + file->endline_mode = ENDLINE_RN_COMBINED; + }break; } + + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + buffer_measure_starts(&mem->general, view->file); } COMMAND_DECL(toggle_show_whitespace){ @@ -1118,20 +1017,20 @@ COMMAND_DECL(to_uppercase){ USE_MEM(mem); Range range = get_range(view->cursor.pos, view->mark); - if (range.smaller < range.larger){ + if (range.start < range.end){ if (file->still_lexing){ system_cancel_job(BACKGROUND_THREADS, file->lex_job); } u8 *data = file->data; - for (i32 i = range.smaller; i < range.larger; ++i){ + for (i32 i = range.start; i < range.end; ++i){ if (data[i] >= 'a' && data[i] <= 'z'){ data[i] += (u8)('A' - 'a'); } } if (file->token_stack.tokens){ - buffer_relex_parallel(mem, file, range.smaller, range.larger, 0); + buffer_relex_parallel(mem, file, range.start, range.end, 0); } } } @@ -1143,20 +1042,20 @@ COMMAND_DECL(to_lowercase){ USE_MEM(mem); Range range = get_range(view->cursor.pos, view->mark); - if (range.smaller < range.larger){ + if (range.start < range.end){ if (file->still_lexing){ system_cancel_job(BACKGROUND_THREADS, file->lex_job); } u8 *data = file->data; - for (i32 i = range.smaller; i < range.larger; ++i){ + for (i32 i = range.start; i < range.end; ++i){ if (data[i] >= 'A' && data[i] <= 'Z'){ data[i] -= (u8)('A' - 'a'); } } if (file->token_stack.tokens){ - buffer_relex_parallel(mem, file, range.smaller, range.larger, 0); + buffer_relex_parallel(mem, file, range.start, range.end, 0); } } } @@ -1215,7 +1114,7 @@ view_clean_line(Mem_Options *mem, File_View *view, Editing_File *file, i32 line_ } } else{ - //view_auto_tab(mem, view, pos, pos); + view_auto_tab(mem, view, pos, pos); } } #endif @@ -1416,17 +1315,9 @@ COMMAND_DECL(move_left){ u8 *data = file->data; i32 pos = view->cursor.pos; if (pos > 0){ + --pos; if (file->endline_mode == ENDLINE_RN_COMBINED){ - pos = pos_adjust_to_left(pos, data); - if (pos > 0){ - --pos; - } - else{ - pos = pos_adjust_to_self(pos, data, file->size); - } - } - else{ - --pos; + pos = pos_adjust_to_self(pos, data, file->size); } } @@ -1442,9 +1333,12 @@ COMMAND_DECL(move_right){ u8* data = file->data; i32 pos = view->cursor.pos; if (pos < size){ - ++pos; if (file->endline_mode == ENDLINE_RN_COMBINED){ - pos = pos_adjust_to_self(pos, data, size); + pos = pos_adjust_to_right(pos, data, size); + ++pos; + } + else{ + ++pos; } } @@ -1460,58 +1354,24 @@ COMMAND_DECL(delete){ i32 cursor_pos = view->cursor.pos; u8 *data = file->data; - if (file->size > 0){ - Range range = {}; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - if (cursor_pos > 0 && - data[cursor_pos-1] == '\r' && - data[cursor_pos] == '\n'){ - range.start = cursor_pos-1; - range.end = cursor_pos+1; - buffer_delete_range(mem, file, range); - } - - else{ - range.start = cursor_pos; - range.end = cursor_pos+1; - buffer_delete(mem, file, cursor_pos); - } + if (file->size > 0 && cursor_pos < file->size){ + i32 start, end; + start = cursor_pos; + if (file->endline_mode == ENDLINE_RN_COMBINED && + data[cursor_pos] == '\r' && data[cursor_pos+1] == '\n'){ + end = cursor_pos+2; } - else{ - range.start = cursor_pos; - range.end = cursor_pos+1; - buffer_delete(mem, file, cursor_pos); + end = cursor_pos+1; } - Panel *panels = layout->panels; - i32 panel_count = layout->panel_count; - i32 shift_amount = range.end - range.start; - for (i32 i = 0; i < panel_count; ++i){ - Panel *current_panel = panels + i; - File_View *current_view = view_to_file_view(current_panel->view); - if (!current_view) continue; - - if (current_view->file == file){ - view_measure_wraps(&mem->general, current_view); - - if (current_view->mark >= range.end){ - current_view->mark -= shift_amount; - } - else if (current_view->mark > range.start){ - current_view->mark = range.start; - } - - if (current_view->cursor.pos >= range.end){ - current_view->cursor.pos -= shift_amount; - view_cursor_move(current_view, current_view->cursor.pos); - } - else if (current_view->cursor.pos > range.start){ - current_view->cursor.pos = range.start; - view_cursor_move(current_view, current_view->cursor.pos); - } - } - } + i32 shift = (end - start); + Assert(shift > 0); + + i32 next_cursor_pos = start; + view_replace_range(mem, view, layout, start, end, 0, 0, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + if (view->mark >= end) view->mark -= shift; } } @@ -1519,56 +1379,32 @@ COMMAND_DECL(backspace){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); + USE_LAYOUT(layout); USE_MEM(mem); i32 cursor_pos = view->cursor.pos; u8 *data = file->data; if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ - i32 shift_amount = 0; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - i32 target_pos = cursor_pos; - if (cursor_pos > 1 && - data[cursor_pos] == '\n' && - data[cursor_pos-1] == '\r'){ - --target_pos; - } - - if (target_pos > 1 && - data[target_pos-1] == '\n' && - data[target_pos-2] == '\r'){ - buffer_delete_range(mem, file, target_pos-2, target_pos); - shift_amount = 2; - } - else{ - if (target_pos > 0){ - buffer_delete(mem, file, target_pos-1); - shift_amount = 1; - } - } + i32 start, end; + end = cursor_pos; + + if (file->endline_mode == ENDLINE_RN_COMBINED && + cursor_pos > 1 && + data[cursor_pos-1] == '\n' && + data[cursor_pos-2] == '\r'){ + start = cursor_pos-2; } else{ - buffer_delete(mem, file, cursor_pos-1); - shift_amount = 1; + start = cursor_pos-1; } - Editing_Layout *layout = command->layout; - Panel *panels = layout->panels; - i32 panel_count = layout->panel_count; + i32 shift = (end - start); + Assert(shift > 0); - for (i32 i = 0; i < panel_count; ++i){ - Panel *current_panel = panels + i; - File_View *current_view = view_to_file_view(current_panel->view); - if (current_view && current_view->file == view->file){ - if (current_view->mark >= cursor_pos){ - current_view->mark -= shift_amount; - } - if (current_view->cursor.pos >= cursor_pos){ - current_view->cursor.pos -= shift_amount; - } - view_measure_wraps(&mem->general, current_view); - view_cursor_move(current_view, current_view->cursor.pos); - } - } + i32 next_cursor_pos = view->cursor.pos - shift; + view_replace_range(mem, view, layout, start, end, 0, 0, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + if (view->mark >= end) view->mark -= shift; } } @@ -1648,8 +1484,8 @@ COMMAND_DECL(page_up){ } inline void -open_theme_options(App_Vars *vars, Live_Views *live_set, General_Memory *general, Panel *panel){ - View *new_view = live_set_alloc_view(live_set, general); +open_theme_options(App_Vars *vars, Live_Views *live_set, Mem_Options *mem, Panel *panel){ + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; @@ -1669,7 +1505,7 @@ COMMAND_DECL(open_color_tweaker){ USE_MEM(mem); USE_PANEL(panel); - open_theme_options(vars, live_set, &mem->general, panel); + open_theme_options(vars, live_set, mem, panel); } COMMAND_DECL(open_menu){ @@ -1681,7 +1517,7 @@ COMMAND_DECL(open_menu){ USE_WORKING_SET(working_set); USE_STYLE(style); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_minor(new_view, panel, live_set); new_view->map = &vars->map_ui; @@ -1698,7 +1534,7 @@ COMMAND_DECL(open_debug_view){ USE_PANEL(panel); USE_MEM(mem); - View *new_view = live_set_alloc_view(live_set, &mem->general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_major(new_view, panel, live_set); new_view->map = &vars->map_debug; @@ -1877,6 +1713,13 @@ setup_file_commands(Command_Map *commands, Key_Codes *codes){ map_add(commands, 'V', MDFR_CTRL, command_paste_next); map_add(commands, 'z', MDFR_CTRL, command_undo); map_add(commands, 'y', MDFR_CTRL, command_redo); + map_add(commands, 'Z', MDFR_CTRL, command_undo_scrub); + map_add(commands, codes->left, MDFR_ALT, command_increase_rewind_speed); + map_add(commands, codes->right, MDFR_ALT, command_increase_fastforward_speed); + map_add(commands, codes->down, MDFR_ALT, command_stop_rewind_and_fastforward); + map_add(commands, 'h', MDFR_CTRL, command_history_backward); + map_add(commands, 'H', MDFR_CTRL, command_history_forward); + map_add(commands, 'J', MDFR_CTRL, command_history_scrub); map_add(commands, 'd', MDFR_CTRL, command_delete_chunk); map_add(commands, 'l', MDFR_CTRL, command_toggle_line_wrap); map_add(commands, 'L', MDFR_CTRL, command_toggle_endline_mode); @@ -1894,7 +1737,7 @@ setup_file_commands(Command_Map *commands, Key_Codes *codes){ map_add(commands, '\t', MDFR_CTRL, command_auto_tab); - map_add(commands, 'K', MDFR_CTRL, command_kill_file); + map_add(commands, 'K', MDFR_CTRL, command_kill_buffer); map_add(commands, 'O', MDFR_CTRL, command_reopen); map_add(commands, 's', MDFR_CTRL, command_save); map_add(commands, 'w', MDFR_CTRL, command_interactive_save_as); @@ -1915,8 +1758,8 @@ setup_top_commands(Command_Map *commands, Key_Codes *codes){ map_add(commands, 'n', MDFR_CTRL, command_interactive_new); map_add(commands, 'o', MDFR_CTRL, command_interactive_open); map_add(commands, ',', MDFR_CTRL, command_change_active_panel); - map_add(commands, 'k', MDFR_CTRL, command_interactive_kill_file); - map_add(commands, 'i', MDFR_CTRL, command_interactive_switch_file); + map_add(commands, 'k', MDFR_CTRL, command_interactive_kill_buffer); + map_add(commands, 'i', MDFR_CTRL, command_interactive_switch_buffer); map_add(commands, 'c', MDFR_ALT, command_open_color_tweaker); map_add(commands, 'x', MDFR_ALT, command_open_menu); } @@ -1964,15 +1807,17 @@ setup_command_table(){ SET(paste); SET(paste_next); SET(delete_chunk); + SET(undo); + SET(redo); SET(interactive_new); SET(interactive_open); SET(reopen); SET(save); SET(interactive_save_as); SET(change_active_panel); - SET(interactive_switch_file); - SET(interactive_kill_file); - SET(kill_file); + SET(interactive_switch_buffer); + SET(interactive_kill_buffer); + SET(kill_buffer); SET(toggle_line_wrap); SET(toggle_endline_mode); SET(to_uppercase); @@ -2976,6 +2821,7 @@ app_step(Thread_Context *thread, Key_Codes *codes, Style *style = &vars->style; Working_Set *working_set = &vars->working_set; Live_Views *live_set = &vars->live_set; + Mem_Options *mem = &vars->mem; General_Memory *general = &vars->mem.general; i32 count = vars->delay.count; @@ -3006,12 +2852,12 @@ app_step(Thread_Context *thread, Key_Codes *codes, } if (target_file){ - View *new_view = live_set_alloc_view(live_set, &vars->mem.general); + View *new_view = live_set_alloc_view(live_set, &vars->mem); new_view->map = &vars->map_file; view_replace_major(new_view, panel, live_set); - File_View *file_view = file_view_init(new_view); + File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); view_set_file(file_view, target_file, style); if (created_file && target_file->tokens_exist) buffer_first_lex_parallel(general, target_file); @@ -3032,17 +2878,25 @@ app_step(Thread_Context *thread, Key_Codes *codes, } }break; + case DACT_SAVE: + { + Editing_File *file = working_set_lookup_file(working_set, *string); + if (file && !file->is_dummy){ + buffer_save(file, (u8*)file->source_path.str); + } + }break; + case DACT_NEW: { Get_File_Result file = working_set_get_available_file(working_set); buffer_create_empty(general, file.file, (u8*)string->str, style->font); table_add(&working_set->table, file.file->source_path, file.index); - View *new_view = live_set_alloc_view(live_set, general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_major(new_view, panel, live_set); new_view->map = &vars->map_file; - File_View *file_view = file_view_init(new_view); + File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); view_set_file(file_view, file.file, style); if (file.file->tokens_exist) buffer_first_lex_parallel(general, file.file); }break; @@ -3051,11 +2905,11 @@ app_step(Thread_Context *thread, Key_Codes *codes, { Editing_File *file = working_set_lookup_file(working_set, *string); if (file){ - View *new_view = live_set_alloc_view(live_set, general); + View *new_view = live_set_alloc_view(live_set, mem); view_replace_major(new_view, panel, live_set); new_view->map = &vars->map_file; - File_View *file_view = file_view_init(new_view); + File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); view_set_file(file_view, file, style); } }break; @@ -3065,7 +2919,40 @@ app_step(Thread_Context *thread, Key_Codes *codes, Editing_File *file = working_set_lookup_file(working_set, *string); if (file){ table_remove(&working_set->table, file->source_path); - kill_file(general, file, live_set, &vars->layout); + kill_buffer(general, file, live_set, &vars->layout); + } + }break; + + case DACT_TRY_KILL: + { + Editing_File *file = working_set_lookup_file(working_set, *string); + if (file){ + switch (buffer_get_sync(file)){ + case SYNC_BEHIND_OS: + case SYNC_GOOD: + { + table_remove(&working_set->table, file->source_path); + kill_buffer(general, file, live_set, &vars->layout); + view_remove_minor(panel, live_set); + }break; + + case SYNC_UNSAVED: + { + View *new_view = live_set_alloc_view(live_set, mem); + view_replace_minor(new_view, panel, live_set); + + new_view->map = &vars->map_ui; + Interactive_View *int_view = + interactive_view_init(new_view, &vars->hot_directory, style, + working_set, &vars->delay); + int_view->interaction = INTV_SURE_TO_KILL_INTER; + int_view->action = INTV_SURE_TO_KILL; + copy(&int_view->query, "Are you sure?"); + copy(&int_view->dest, file->live_name); + }break; + + default: Assert(!"invalid path"); + } } }break; @@ -3081,7 +2968,7 @@ app_step(Thread_Context *thread, Key_Codes *codes, case DACT_THEME_OPTIONS: { - open_theme_options(vars, live_set, &vars->mem.general, panel); + open_theme_options(vars, live_set, mem, panel); }break; } } diff --git a/4ed_color_view.cpp b/4ed_color_view.cpp index 9ad104a9..7c002712 100644 --- a/4ed_color_view.cpp +++ b/4ed_color_view.cpp @@ -71,66 +71,6 @@ struct Color_Highlight{ i32 ids[4]; }; -struct Widget_ID{ - i32 id; - i32 sub_id0; - i32 sub_id1; - i32 sub_id2; -}; - -inline bool32 -widget_match(Widget_ID s1, Widget_ID s2){ - return (s1.id == s2.id && s1.sub_id0 == s2.sub_id0 && - s1.sub_id1 == s2.sub_id1 && s1.sub_id2 == s2.sub_id2); -} - -struct UI_State{ - Render_Target *target; - Style *style; - Font *font; - Mouse_Summary *mouse; - Key_Summary *keys; - Key_Codes *codes; - Working_Set *working_set; - - Widget_ID selected, hover, hot; - bool32 activate_me; - bool32 redraw; - bool32 input_stage; - i32 sub_id1_change; - - real32 height, view_y; -}; - -inline bool32 -is_selected(UI_State *state, Widget_ID id){ - return widget_match(state->selected, id); -} - -inline bool32 -is_hot(UI_State *state, Widget_ID id){ - return widget_match(state->hot, id); -} - -inline bool32 -is_hover(UI_State *state, Widget_ID id){ - return widget_match(state->hover, id); -} - -struct UI_Layout{ - i32 row_count; - i32 row_item_width; - i32 row_max_item_height; - - i32_Rect rect; - i32 x, y; -}; - -struct UI_Layout_Restore{ - UI_Layout layout; - UI_Layout *dest; -}; - struct Library_UI{ UI_State state; UI_Layout layout; @@ -183,58 +123,6 @@ view_to_color_view(View *view){ return (Color_View*)view; } -inline void -begin_layout(UI_Layout *layout, i32_Rect rect){ - layout->rect = rect; - layout->x = rect.x0; - layout->y = rect.y0; - layout->row_count = 0; - layout->row_max_item_height = 0; -} - -inline void -begin_row(UI_Layout *layout, i32 count){ - layout->row_count = count; - layout->row_item_width = (layout->rect.x1 - layout->x) / count; -} - -inline i32_Rect -layout_rect(UI_Layout *layout, i32 height){ - i32_Rect rect; - rect.x0 = layout->x; - rect.y0 = layout->y; - rect.x1 = rect.x0; - rect.y1 = rect.y0 + height; - if (layout->row_count > 0){ - --layout->row_count; - rect.x1 = rect.x0 + layout->row_item_width; - layout->x += layout->row_item_width; - layout->row_max_item_height = Max(height, layout->row_max_item_height); - } - if (layout->row_count == 0){ - rect.x1 = layout->rect.x1; - layout->row_max_item_height = Max(height, layout->row_max_item_height); - layout->y += layout->row_max_item_height; - layout->x = layout->rect.x0; - layout->row_max_item_height = 0; - } - return rect; -} - -inline UI_Layout_Restore -begin_sub_layout(UI_Layout *layout, i32_Rect area){ - UI_Layout_Restore restore; - restore.layout = *layout; - restore.dest = layout; - begin_layout(layout, area); - return restore; -} - -inline void -end_sub_layout(UI_Layout_Restore restore){ - *restore.dest = restore.layout; -} - internal real32 font_string_width(Font *font, char *str){ real32 x = 0; @@ -308,203 +196,6 @@ draw_rgb_slider(Render_Target *target, Vec4 base, i32 channel, i32 steps, real32 draw_gradient_slider(target, base, channel, steps, top, slider, 0); } -inline Widget_ID -make_id(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.id = id; - return r; -} - -inline Widget_ID -make_sub0(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id0 = id; - return r; -} - -inline Widget_ID -make_sub1(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id1 = id; - return r; -} - -inline Widget_ID -make_sub2(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id2 = id; - return r; -} - -internal bool32 -ui_do_button_input(UI_State *state, i32_Rect rect, Widget_ID id, bool32 activate, bool32 *right = 0){ - bool32 result = 0; - Mouse_Summary *mouse = state->mouse; - bool32 hover = hit_check(mouse->mx, mouse->my, rect); - if (hover){ - state->hover = id; - if (activate) state->activate_me = 1; - if (mouse->press_l || (mouse->press_r && right)) state->hot = id; - if (mouse->l && mouse->r) state->hot = {}; - } - bool32 is_match = widget_match(state->hot, id); - if (mouse->release_l && is_match){ - if (hover) result = 1; - state->redraw = 1; - } - if (right && mouse->release_r && is_match){ - if (hover) *right = 1; - state->redraw = 1; - } - return result; -} - -internal bool32 -ui_do_subdivided_button_input(UI_State *state, i32_Rect rect, i32 parts, Widget_ID id, bool32 activate, i32 *indx_out, bool32 *right = 0){ - bool32 result = 0; - real32 x0, x1; - i32_Rect sub_rect; - Widget_ID sub_widg = id; - real32 sub_width = (rect.x1 - rect.x0) / (real32)parts; - sub_rect.y0 = rect.y0; - sub_rect.y1 = rect.y1; - x1 = (real32)rect.x0; - - for (i32 i = 0; i < parts; ++i){ - x0 = x1; - x1 = x1 + sub_width; - sub_rect.x0 = TRUNC32(x0); - sub_rect.x1 = TRUNC32(x1); - sub_widg.sub_id2 = i; - if (ui_do_button_input(state, sub_rect, sub_widg, activate, right)){ - *indx_out = i; - break; - } - } - - return result; -} - -internal real32 -ui_do_vscroll_input(UI_State *state, i32_Rect top, i32_Rect bottom, i32_Rect slider, - Widget_ID id, real32 val, real32 step_amount, - real32 smin, real32 smax, real32 vmin, real32 vmax){ - Mouse_Summary *mouse = state->mouse; - i32 mx = mouse->mx; - i32 my = mouse->my; - if (hit_check(mx, my, top)){ - state->hover = id; - state->hover.sub_id2 = 1; - } - if (hit_check(mx, my, bottom)){ - state->hover = id; - state->hover.sub_id2 = 2; - } - if (hit_check(mx, my, slider)){ - state->hover = id; - state->hover.sub_id2 = 3; - } - if (mouse->press_l) state->hot = state->hover; - if (id.id == state->hot.id){ - if (mouse->release_l){ - Widget_ID wid1, wid2; - wid1 = wid2 = id; - wid1.sub_id2 = 1; - wid2.sub_id2 = 2; - if (state->hot.sub_id2 == 1 && is_hover(state, wid1)) val -= step_amount; - if (state->hot.sub_id2 == 2 && is_hover(state, wid2)) val += step_amount; - state->redraw = 1; - } - if (state->hot.sub_id2 == 3){ - real32 S, L; - S = (real32)mouse->my - (slider.y1 - slider.y0) / 2; - if (S < smin) S = smin; - if (S > smax) S = smax; - L = unlerp(smin, S, smax); - val = lerp(vmin, L, vmax); - state->redraw = 1; - } - } - return val; -} - -internal bool32 -ui_do_text_field_input(UI_State *state, String *str){ - bool32 result = 0; - Key_Summary *keys = state->keys; - for (i32 key_i = 0; key_i < keys->count; ++key_i){ - Key_Single key = get_single_key(keys, key_i); - char c = (char)key.key.character; - if (char_is_basic(c) && str->size < str->memory_size-1){ - str->str[str->size++] = c; - str->str[str->size] = 0; - } - else if (c == '\n'){ - result = 1; - } - else if (key.key.keycode == state->codes->back && str->size > 0){ - str->str[--str->size] = 0; - } - } - return result; -} - -internal bool32 -ui_do_file_field_input(UI_State *state, Hot_Directory *hot_dir){ - bool32 result = 0; - Key_Summary *keys = state->keys; - for (i32 key_i = 0; key_i < keys->count; ++key_i){ - Key_Single key = get_single_key(keys, key_i); - String *str = &hot_dir->string; - terminate_with_null(str); - Single_Line_Input_Step step = - app_single_file_input_step(state->codes, state->working_set, key, str, hot_dir, 1); - if (step.hit_newline || step.hit_ctrl_newline) result = 1; - } - return result; -} - -internal bool32 -ui_do_line_field_input(UI_State *state, String *string){ - bool32 result = 0; - Key_Summary *keys = state->keys; - for (i32 key_i = 0; key_i < keys->count; ++key_i){ - Key_Single key = get_single_key(keys, key_i); - terminate_with_null(string); - Single_Line_Input_Step step = - app_single_line_input_step(state->codes, key, string); - if (step.hit_newline || step.hit_ctrl_newline) result = 1; - } - return result; -} - -internal bool32 -ui_do_slider_input(UI_State *state, i32_Rect rect, Widget_ID wid, - real32 min, real32 max, real32 *v){ - bool32 result = 0; - ui_do_button_input(state, rect, wid, 0); - Mouse_Summary *mouse = state->mouse; - if (is_hot(state, wid)){ - result = 1; - *v = unlerp(min, (real32)mouse->mx, max); - state->redraw = 1; - } - return result; -} - -struct UI_Style{ - u32 dark, dim, bright; -}; - -internal UI_Style -get_ui_style(Style *style){ - UI_Style ui_style; - ui_style.dark = style->main.back_color; - ui_style.dim = style->main.margin_color; - ui_style.bright = style->main.margin_active_color; - return ui_style; -} - internal void do_label(UI_State *state, UI_Layout *layout, char *text, real32 height = 2.f){ Style *style = state->style; @@ -522,27 +213,6 @@ do_label(UI_State *state, UI_Layout *layout, char *text, real32 height = 2.f){ } } -internal void -get_colors(UI_State *state, u32 *back, u32 *fore, Widget_ID wid, UI_Style style){ - bool32 hover = is_hover(state, wid); - bool32 hot = is_hot(state, wid); - i32 level = hot + hover; - switch (level){ - case 2: - *back = style.bright; - *fore = style.dark; - break; - case 1: - *back = style.dim; - *fore = style.bright; - break; - case 0: - *back = style.dark; - *fore = style.bright; - break; - } -} - internal bool32 do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, bool32 is_toggle = 0, bool32 on = 0){ @@ -550,7 +220,7 @@ do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, Font *font = state->font; i32 character_h = font->height; - i32_Rect btn_rect = layout_rect(layout, font->height * 2); + i32_Rect btn_rect = layout_rect(layout, character_h * 2); btn_rect = get_inner_rect(btn_rect, 2); Widget_ID wid = make_id(state, id); @@ -1350,75 +1020,6 @@ do_font_switch(Color_UI *ui){ } } -internal UI_State -ui_state_init(UI_State *state_in, Render_Target *target, Input_Summary *user_input, - Style *style, Working_Set *working_set, bool32 input_stage){ - UI_State state; - state.target = target; - state.style = style; - state.font = style->font; - state.working_set = working_set; - state.mouse = &user_input->mouse; - state.keys = &user_input->keys; - state.codes = user_input->codes; - state.selected = state_in->selected; - state.hot = state_in->hot; - if (input_stage) state.hover = {}; - else state.hover = state_in->hover; - state.redraw = 0; - state.activate_me = 0; - state.input_stage = input_stage; - state.height = state_in->height; - state.view_y = state_in->view_y; - return state; -} - -inline bool32 -ui_state_match(UI_State a, UI_State b){ - return (widget_match(a.selected, b.selected) && - widget_match(a.hot, b.hot) && - widget_match(a.hover, b.hover)); -} - -internal bool32 -ui_finish_frame(UI_State *persist_state, UI_State *state, UI_Layout *layout, i32_Rect rect, - bool32 do_wheel, bool32 *did_activation){ - bool32 result = 0; - real32 h = layout->y + persist_state->view_y - rect.y0; - real32 max_y = h - (rect.y1 - rect.y0); - - persist_state->height = h; - persist_state->view_y = state->view_y; - - if (state->input_stage){ - Mouse_Summary *mouse = state->mouse; - if (mouse->wheel_used && do_wheel){ - persist_state->view_y += mouse->wheel_amount*state->font->height; - result = 1; - } - if (mouse->release_l && widget_match(state->hot, state->hover)){ - if (did_activation) *did_activation = 1; - if (state->activate_me){ - state->selected = state->hot; - } - } - if (!mouse->l && !mouse->r){ - state->hot = {}; - } - - if (!ui_state_match(*persist_state, *state) || state->redraw){ - result = 1; - } - - *persist_state = *state; - } - - if (persist_state->view_y >= max_y) persist_state->view_y = max_y; - if (persist_state->view_y < 0) persist_state->view_y = 0; - - return result; -} - internal i32 step_draw_adjusting(Color_View *color_view, i32_Rect rect, View_Message message, Render_Target *target, Input_Summary *user_input){ @@ -1860,11 +1461,11 @@ do_file_list_box(UI_State *state, UI_Layout *layout, Hot_Directory *hot_dir, boo terminate_with_null(&hot_dir->string); } else{ - String p4c_extension = make_lit_string("p4c"); - String message_loaded = make_lit_string(" LOADED"); - String message_unsaved = make_lit_string(" LOADED *"); - String message_unsynced = make_lit_string(" LOADED BEHIND OS"); - String message_nothing = {}; + persist String p4c_extension = make_lit_string("p4c"); + persist String message_loaded = make_lit_string(" LOADED"); + persist String message_unsaved = make_lit_string(" LOADED *"); + persist String message_unsynced = make_lit_string(" LOADED BEHIND OS"); + persist String message_nothing = {}; char front_name_space[256]; String front_name = make_fixed_width_string(front_name_space); @@ -1896,14 +1497,10 @@ do_file_list_box(UI_State *state, UI_Layout *layout, Hot_Directory *hot_dir, boo String message = message_nothing; if (is_loaded){ - if (file->last_4ed_write_time != file->last_sys_write_time){ - message = message_unsynced; - } - else if (file->last_4ed_edit_time > file->last_sys_write_time){ - message = message_unsaved; - } - else{ - message = message_loaded; + switch (buffer_get_sync(file)){ + case SYNC_GOOD: message = message_loaded; break; + case SYNC_BEHIND_OS: message = message_unsynced; break; + case SYNC_UNSAVED: message = message_unsaved; break; } } @@ -1938,6 +1535,10 @@ do_live_file_list_box(UI_State *state, UI_Layout *layout, Working_Set *working_s terminate_with_null(string); } else{ + persist String message_unsaved = make_lit_string(" *"); + persist String message_unsynced = make_lit_string(" BEHIND OS"); + persist String message_nothing = {}; + Absolutes absolutes; get_absolutes(*string, &absolutes, 1, 1); @@ -1947,8 +1548,14 @@ do_live_file_list_box(UI_State *state, UI_Layout *layout, Working_Set *working_s Editing_File *file = files + i; if (!file->is_dummy){ + String message = message_nothing; + switch (buffer_get_sync(file)){ + case SYNC_BEHIND_OS: message = message_unsynced; break; + case SYNC_UNSAVED: message = message_unsaved; break; + } + if (filename_match(*string, &absolutes, file->live_name)){ - if (do_file_option(100+i, state, layout, file->live_name, 0, {})){ + if (do_file_option(100+i, state, layout, file->live_name, 0, message)){ result = 1; *selected = 1; copy(string, file->live_name); diff --git a/4ed_command.cpp b/4ed_command.cpp index e4ff3991..cf7f0eff 100644 --- a/4ed_command.cpp +++ b/4ed_command.cpp @@ -121,11 +121,11 @@ map_extract(Command_Map *map, Key_Single key){ if (alt) command |= MDFR_ALT; u16 code = key.key.character_no_caps_lock; - if (code != 0) map_get_vanilla_keyboard_default(map, command, &bind); + if (code == 0) code = key.key.keycode; + map_find(map, code, command, &bind); - if (bind.function == 0){ - if (code == 0) code = key.key.keycode; - map_find(map, code, command, &bind); + if (bind.function == 0 && key.key.character_no_caps_lock != 0){ + map_get_vanilla_keyboard_default(map, command, &bind); } return bind; diff --git a/4ed_debug_view.cpp b/4ed_debug_view.cpp index e757458a..f7cd88dc 100644 --- a/4ed_debug_view.cpp +++ b/4ed_debug_view.cpp @@ -41,7 +41,7 @@ internal i32 draw_general_memory(Debug_View *view, i32_Rect rect, Render_Target *target, i32 y){ Font *font = view->font; i32 y_advance = font->height; - Bubble *sentinel = &view->view_base.general->sentinel; + Bubble *sentinel = &view->view_base.mem->general.sentinel; for (Bubble *bubble = sentinel->next; bubble != sentinel; @@ -62,6 +62,8 @@ draw_general_memory(Debug_View *view, i32_Rect rect, Render_Target *target, i32 case BUBBLE_TOKENS: append(&s, "tokens"); break; case BUBBLE_UNDO_STRING: append(&s, "undo string"); break; case BUBBLE_UNDO: append(&s, "undo"); break; + case BUBBLE_HISTORY_STRING: append(&s, "history string"); break; + case BUBBLE_HISTORY: append(&s, "history"); break; default: append(&s, "unknown"); break; } } diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index 83974d58..758eeb95 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -16,30 +16,42 @@ enum Endline_Mode{ }; struct Range{ - union{ - struct{ - i32 smaller, larger; - }; - struct{ - i32 start, end; - }; - }; - bool32 swapped; + i32 start, end; +}; + +enum Edit_Type{ + ED_NORMAL, + ED_REVERSE_NORMAL, + ED_UNDO, + ED_REDO }; struct Edit_Step{ - String replaced; + i32 str_start; + i32 str_len; Range range; - i32 cursor_pos; + i32 pre_pos; + i32 post_pos; bool32 can_merge; + Edit_Type reverse_type; + i32 next_block, prev_block; }; struct Undo_Data{ - i32 str_size, str_redo, str_max; u8 *strings; + i32 str_size, str_redo, str_max; i32 edit_count, edit_redo, edit_max; Edit_Step *edits; + + i32 str_history, str_history_max; + u8 *history_strings; + + i32 history_block_count, history_head_block; + Edit_Step *history; + i32 edit_history, edit_history_max; + i32 edit_history_cursor; + i32 pending_block; }; struct Editing_File{ @@ -186,15 +198,7 @@ struct View_Cursor_Data{ }; struct File_View_Mode{ - bool32 rewrite; -}; - -enum File_View_State{ - FVIEW_STATE_EDIT, - FVIEW_STATE_SEARCH, - FVIEW_STATE_GOTO_LINE, - // never below this - FVIEW_STATE_COUNT + bool8 rewrite; }; struct Incremental_Search{ @@ -203,8 +207,805 @@ struct Incremental_Search{ i32 pos; }; +enum Action_Type{ + DACT_OPEN, + DACT_SAVE_AS, + DACT_SAVE, + DACT_NEW, + DACT_SWITCH, + DACT_TRY_KILL, + DACT_KILL, + DACT_CLOSE_MINOR, + DACT_CLOSE_MAJOR, + DACT_THEME_OPTIONS +}; + +struct Delayed_Action{ + Action_Type type; + String string; + Panel *panel; +}; + +struct Delay{ + Delayed_Action acts[8]; + i32 count, max; +}; + +inline void +delayed_action(Delay *delay, Action_Type type, + String string, Panel *panel){ + Assert(delay->count < delay->max); + Delayed_Action action; + action.type = type; + action.string = string; + action.panel = panel; + delay->acts[delay->count++] = action; +} + +// Hot Directory + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory, String base){ + hot_directory->string = base; + hot_directory->string.str[255] = 0; + i32 dir_size = system_get_working_directory((u8*)hot_directory->string.str, + hot_directory->string.memory_size); + if (dir_size <= 0){ + dir_size = system_get_easy_directory((u8*)hot_directory->string.str); + } + hot_directory->string.size = dir_size; + append(&hot_directory->string, "\\"); +} + +internal void +hot_directory_clean_end(Hot_Directory *hot_directory){ + String *str = &hot_directory->string; + if (str->size != 0 && str->str[str->size-1] != '\\'){ + str->size = reverse_seek_slash(*str) + 1; + str->str[str->size] = 0; + } +} + +internal i32 +hot_directory_quick_partition(File_Info *infos, i32 start, i32 pivot){ + File_Info *p = infos + pivot; + File_Info *a = infos + start; + for (i32 i = start; i < pivot; ++i, ++a){ + i32 comp = 0; + comp = p->folder - a->folder; + if (comp == 0) comp = compare(a->filename, p->filename); + if (comp < 0){ + Swap(*a, infos[start]); + ++start; + } + } + Swap(*p, infos[start]); + return start; +} + +internal void +hot_directory_quick_sort(File_Info *infos, i32 start, i32 pivot){ + i32 mid = hot_directory_quick_partition(infos, start, pivot); + if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1); + if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot); +} + +inline void +hot_directory_fixup(Hot_Directory *hot_directory, Working_Set *working_set){ + File_List *files = &hot_directory->file_list; + if (files->count >= 2) + hot_directory_quick_sort(files->infos, 0, files->count - 1); +} + +inline void +hot_directory_set(Hot_Directory *hot_directory, String str, Working_Set *working_set){ + bool32 success = copy_checked(&hot_directory->string, str); + terminate_with_null(&hot_directory->string); + if (success){ + system_free_file_list(hot_directory->file_list); + hot_directory->file_list = system_get_files(str); + } + hot_directory_fixup(hot_directory, working_set); +} + +inline void +hot_directory_reload(Hot_Directory *hot_directory, Working_Set *working_set){ + if (hot_directory->file_list.block){ + system_free_file_list(hot_directory->file_list); + } + hot_directory->file_list = system_get_files(hot_directory->string); + hot_directory_fixup(hot_directory, working_set); +} + +struct Hot_Directory_Match{ + String filename; + bool32 is_folder; +}; + +internal bool32 +filename_match(String query, Absolutes *absolutes, String filename){ + bool32 result; + result = (query.size == 0); + if (!result) result = wildcard_match(absolutes, filename); + return result; +} + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + bool32 include_files, + bool32 exact_match){ + Hot_Directory_Match result = {}; + + Absolutes absolutes; + if (!exact_match) + get_absolutes(str, &absolutes, 1, 1); + + File_List *files = &hot_directory->file_list; + File_Info *info, *end; + end = files->infos + files->count; + for (info = files->infos; info != end; ++info){ + String filename = info->filename; + bool32 is_match = 0; + if (exact_match){ + if (match(filename, str)) is_match = 1; + } + else{ + if (filename_match(str, &absolutes, filename)) is_match = 1; + } + + if (is_match){ + result.is_folder = info->folder; + result.filename = filename; + break; + } + } + + return result; +} + +struct Single_Line_Input_Step{ + bool32 hit_newline; + bool32 hit_ctrl_newline; + bool32 hit_a_character; + bool32 hit_backspace; + bool32 hit_esc; + bool32 made_a_change; + bool32 did_command; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + bool32 fast_folder_select; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(Key_Codes *codes, Working_Set *working_set, + Key_Single key, Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (key.key.keycode == codes->back){ + result.hit_backspace = 1; + if (mode.string->size > 0){ + result.made_a_change = 1; + --mode.string->size; + switch (mode.type){ + case SINGLE_LINE_STRING: + mode.string->str[mode.string->size] = 0; break; + + case SINGLE_LINE_FILE: + { + char end_character = mode.string->str[mode.string->size]; + if (char_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + mode.string->str[mode.string->size] = 0; + hot_directory_set(mode.hot_directory, *mode.string, working_set); + } + else{ + mode.string->str[mode.string->size] = 0; + } + }break; + } + } + } + + else if (key.key.character == '\n' || key.key.character == '\t'){ + result.made_a_change = 1; + if (key.modifiers[CONTROL_KEY_CONTROL] || + key.modifiers[CONTROL_KEY_ALT]){ + result.hit_ctrl_newline = 1; + } + else{ + result.hit_newline = 1; + if (mode.fast_folder_select){ + char front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *mode.string); + Hot_Directory_Match match; + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); + if (!match.filename.str){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); + } + if (match.filename.str){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(mode.hot_directory, *mode.string, working_set); + result.hit_newline = 0; + } + else{ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + result.hit_newline = 1; + } + } + } + } + } + + else if (key.key.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (key.key.character){ + result.hit_a_character = 1; + if (!key.modifiers[CONTROL_KEY_CONTROL] && + !key.modifiers[CONTROL_KEY_ALT]){ + if (mode.string->size+1 < mode.string->memory_size){ + u8 new_character = (u8)key.key.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + mode.string->str[mode.string->size] = 0; + if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ + hot_directory_set(mode.hot_directory, *mode.string, working_set); + } + result.made_a_change = 1; + } + } + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + + return result; +} + +inline Single_Line_Input_Step +app_single_line_input_step(Key_Codes *codes, Key_Single key, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(codes, 0, key, mode); +} + +inline Single_Line_Input_Step +app_single_file_input_step(Key_Codes *codes, Working_Set *working_set, Key_Single key, + String *string, Hot_Directory *hot_directory, + bool32 fast_folder_select){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + return app_single_line_input_core(codes, working_set, key, mode); +} + +inline Single_Line_Input_Step +app_single_number_input_step(Key_Codes *codes, Key_Single key, String *string){ + Single_Line_Input_Step result = {}; + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + + char c = (char)key.key.character; + if (c == 0 || c == '\n' || char_is_numeric(c)) + result = app_single_line_input_core(codes, 0, key, mode); + return result; +} + +struct Widget_ID{ + i32 id; + i32 sub_id0; + i32 sub_id1; + i32 sub_id2; +}; + +inline bool32 +widget_match(Widget_ID s1, Widget_ID s2){ + return (s1.id == s2.id && s1.sub_id0 == s2.sub_id0 && + s1.sub_id1 == s2.sub_id1 && s1.sub_id2 == s2.sub_id2); +} + +struct UI_State{ + Render_Target *target; + Style *style; + Font *font; + Mouse_Summary *mouse; + Key_Summary *keys; + Key_Codes *codes; + Working_Set *working_set; + + Widget_ID selected, hover, hot; + bool32 activate_me; + bool32 redraw; + bool32 input_stage; + i32 sub_id1_change; + + real32 height, view_y; +}; + +inline Widget_ID +make_id(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.id = id; + return r; +} + +inline Widget_ID +make_sub0(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id0 = id; + return r; +} + +inline Widget_ID +make_sub1(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id1 = id; + return r; +} + +inline Widget_ID +make_sub2(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id2 = id; + return r; +} + +inline bool32 +is_selected(UI_State *state, Widget_ID id){ + return widget_match(state->selected, id); +} + +inline bool32 +is_hot(UI_State *state, Widget_ID id){ + return widget_match(state->hot, id); +} + +inline bool32 +is_hover(UI_State *state, Widget_ID id){ + return widget_match(state->hover, id); +} + +struct UI_Layout{ + i32 row_count; + i32 row_item_width; + i32 row_max_item_height; + + i32_Rect rect; + i32 x, y; +}; + +struct UI_Layout_Restore{ + UI_Layout layout; + UI_Layout *dest; +}; + +inline void +begin_layout(UI_Layout *layout, i32_Rect rect){ + layout->rect = rect; + layout->x = rect.x0; + layout->y = rect.y0; + layout->row_count = 0; + layout->row_max_item_height = 0; +} + +inline void +begin_row(UI_Layout *layout, i32 count){ + layout->row_count = count; + layout->row_item_width = (layout->rect.x1 - layout->x) / count; +} + +inline i32_Rect +layout_rect(UI_Layout *layout, i32 height){ + i32_Rect rect; + rect.x0 = layout->x; + rect.y0 = layout->y; + rect.x1 = rect.x0; + rect.y1 = rect.y0 + height; + if (layout->row_count > 0){ + --layout->row_count; + rect.x1 = rect.x0 + layout->row_item_width; + layout->x += layout->row_item_width; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + } + if (layout->row_count == 0){ + rect.x1 = layout->rect.x1; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + layout->y += layout->row_max_item_height; + layout->x = layout->rect.x0; + layout->row_max_item_height = 0; + } + return rect; +} + +inline UI_Layout_Restore +begin_sub_layout(UI_Layout *layout, i32_Rect area){ + UI_Layout_Restore restore; + restore.layout = *layout; + restore.dest = layout; + begin_layout(layout, area); + return restore; +} + +inline void +end_sub_layout(UI_Layout_Restore restore){ + *restore.dest = restore.layout; +} + +struct UI_Style{ + u32 dark, dim, bright; + u32 base, pop1, pop2; +}; + +internal UI_Style +get_ui_style(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.back_color; + ui_style.dim = style->main.margin_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +internal UI_Style +get_ui_style_upper(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.margin_color; + ui_style.dim = style->main.margin_hover_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +inline void +get_colors(UI_State *state, u32 *back, u32 *fore, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *back = style.bright; + *fore = style.dark; + break; + case 1: + *back = style.dim; + *fore = style.bright; + break; + case 0: + *back = style.dark; + *fore = style.bright; + break; + } +} + +inline void +get_pop_color(UI_State *state, u32 *pop, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *pop = style.pop1; + break; + case 1: + *pop = style.pop1; + break; + case 0: + *pop = style.pop1; + break; + } +} + +internal UI_State +ui_state_init(UI_State *state_in, Render_Target *target, Input_Summary *user_input, + Style *style, Working_Set *working_set, bool32 input_stage){ + UI_State state = {}; + state.target = target; + state.style = style; + state.font = style->font; + state.working_set = working_set; + if (user_input){ + state.mouse = &user_input->mouse; + state.keys = &user_input->keys; + state.codes = user_input->codes; + } + state.selected = state_in->selected; + state.hot = state_in->hot; + if (input_stage) state.hover = {}; + else state.hover = state_in->hover; + state.redraw = 0; + state.activate_me = 0; + state.input_stage = input_stage; + state.height = state_in->height; + state.view_y = state_in->view_y; + return state; +} + +inline bool32 +ui_state_match(UI_State a, UI_State b){ + return (widget_match(a.selected, b.selected) && + widget_match(a.hot, b.hot) && + widget_match(a.hover, b.hover)); +} + +internal bool32 +ui_finish_frame(UI_State *persist_state, UI_State *state, UI_Layout *layout, i32_Rect rect, + bool32 do_wheel, bool32 *did_activation){ + bool32 result = 0; + real32 h = layout->y + persist_state->view_y - rect.y0; + real32 max_y = h - (rect.y1 - rect.y0); + + persist_state->height = h; + persist_state->view_y = state->view_y; + + if (state->input_stage){ + Mouse_Summary *mouse = state->mouse; + if (mouse->wheel_used && do_wheel){ + persist_state->view_y += mouse->wheel_amount*state->font->height; + result = 1; + } + if (mouse->release_l && widget_match(state->hot, state->hover)){ + if (did_activation) *did_activation = 1; + if (state->activate_me){ + state->selected = state->hot; + } + } + if (!mouse->l && !mouse->r){ + state->hot = {}; + } + + if (!ui_state_match(*persist_state, *state) || state->redraw){ + result = 1; + } + + *persist_state = *state; + } + + if (persist_state->view_y >= max_y) persist_state->view_y = max_y; + if (persist_state->view_y < 0) persist_state->view_y = 0; + + return result; +} + +internal bool32 +ui_do_button_input(UI_State *state, i32_Rect rect, Widget_ID id, bool32 activate, bool32 *right = 0){ + bool32 result = 0; + Mouse_Summary *mouse = state->mouse; + bool32 hover = hit_check(mouse->mx, mouse->my, rect); + if (hover){ + state->hover = id; + if (activate) state->activate_me = 1; + if (mouse->press_l || (mouse->press_r && right)) state->hot = id; + if (mouse->l && mouse->r) state->hot = {}; + } + bool32 is_match = is_hot(state, id); + if (mouse->release_l && is_match){ + if (hover) result = 1; + state->redraw = 1; + } + if (right && mouse->release_r && is_match){ + if (hover) *right = 1; + state->redraw = 1; + } + return result; +} + +internal bool32 +ui_do_subdivided_button_input(UI_State *state, i32_Rect rect, i32 parts, Widget_ID id, bool32 activate, i32 *indx_out, bool32 *right = 0){ + bool32 result = 0; + real32 x0, x1; + i32_Rect sub_rect; + Widget_ID sub_widg = id; + real32 sub_width = (rect.x1 - rect.x0) / (real32)parts; + sub_rect.y0 = rect.y0; + sub_rect.y1 = rect.y1; + x1 = (real32)rect.x0; + + for (i32 i = 0; i < parts; ++i){ + x0 = x1; + x1 = x1 + sub_width; + sub_rect.x0 = TRUNC32(x0); + sub_rect.x1 = TRUNC32(x1); + sub_widg.sub_id2 = i; + if (ui_do_button_input(state, sub_rect, sub_widg, activate, right)){ + *indx_out = i; + break; + } + } + + return result; +} + +internal real32 +ui_do_vscroll_input(UI_State *state, i32_Rect top, i32_Rect bottom, i32_Rect slider, + Widget_ID id, real32 val, real32 step_amount, + real32 smin, real32 smax, real32 vmin, real32 vmax){ + Mouse_Summary *mouse = state->mouse; + i32 mx = mouse->mx; + i32 my = mouse->my; + if (hit_check(mx, my, top)){ + state->hover = id; + state->hover.sub_id2 = 1; + } + if (hit_check(mx, my, bottom)){ + state->hover = id; + state->hover.sub_id2 = 2; + } + if (hit_check(mx, my, slider)){ + state->hover = id; + state->hover.sub_id2 = 3; + } + if (mouse->press_l) state->hot = state->hover; + if (id.id == state->hot.id){ + if (mouse->release_l){ + Widget_ID wid1, wid2; + wid1 = wid2 = id; + wid1.sub_id2 = 1; + wid2.sub_id2 = 2; + if (state->hot.sub_id2 == 1 && is_hover(state, wid1)) val -= step_amount; + if (state->hot.sub_id2 == 2 && is_hover(state, wid2)) val += step_amount; + state->redraw = 1; + } + if (state->hot.sub_id2 == 3){ + real32 S, L; + S = (real32)mouse->my - (slider.y1 - slider.y0) / 2; + if (S < smin) S = smin; + if (S > smax) S = smax; + L = unlerp(smin, S, smax); + val = lerp(vmin, L, vmax); + state->redraw = 1; + } + } + return val; +} + +internal bool32 +ui_do_text_field_input(UI_State *state, String *str){ + bool32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Single key = get_single_key(keys, key_i); + char c = (char)key.key.character; + if (char_is_basic(c) && str->size < str->memory_size-1){ + str->str[str->size++] = c; + str->str[str->size] = 0; + } + else if (c == '\n'){ + result = 1; + } + else if (key.key.keycode == state->codes->back && str->size > 0){ + str->str[--str->size] = 0; + } + } + return result; +} + +internal bool32 +ui_do_file_field_input(UI_State *state, Hot_Directory *hot_dir){ + bool32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Single key = get_single_key(keys, key_i); + String *str = &hot_dir->string; + terminate_with_null(str); + Single_Line_Input_Step step = + app_single_file_input_step(state->codes, state->working_set, key, str, hot_dir, 1); + if (step.hit_newline || step.hit_ctrl_newline) result = 1; + } + return result; +} + +internal bool32 +ui_do_line_field_input(UI_State *state, String *string){ + bool32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Single key = get_single_key(keys, key_i); + terminate_with_null(string); + Single_Line_Input_Step step = + app_single_line_input_step(state->codes, key, string); + if (step.hit_newline || step.hit_ctrl_newline) result = 1; + } + return result; +} + +internal bool32 +ui_do_slider_input(UI_State *state, i32_Rect rect, Widget_ID wid, + real32 min, real32 max, real32 *v){ + bool32 result = 0; + ui_do_button_input(state, rect, wid, 0); + Mouse_Summary *mouse = state->mouse; + if (is_hot(state, wid)){ + result = 1; + *v = unlerp(min, (real32)mouse->mx, max); + state->redraw = 1; + } + return result; +} + +internal bool32 +do_text_field(Widget_ID wid, UI_State *state, UI_Layout *layout, + String prompt, String dest){ + bool32 result = 0; + Font *font = state->font; + i32 character_h = font->height; + + i32_Rect rect = layout_rect(layout, character_h); + + if (state->input_stage){ + ui_do_button_input(state, rect, wid, 1); + if (is_selected(state, wid)){ + if (ui_do_text_field_input(state, &dest)){ + result = 1; + } + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style_upper(state->style); + u32 back, fore, prompt_pop; + get_colors(state, &back, &fore, wid, ui_style); + get_pop_color(state, &prompt_pop, wid, ui_style); + + draw_rectangle(target, rect, back); + i32 x = rect.x0; + x = draw_string(target, font, prompt, x, rect.y0, prompt_pop); + draw_string(target, font, dest, x, rect.y0, ui_style.base); + } + + return result; +} + +enum File_View_Widget_Type{ + FWIDG_NONE, + FWIDG_SEARCH, + FWIDG_GOTO_LINE, + FWIDG_UNDOING, + FWIDG_RESTORING, + // never below this + FWIDG_TYPE_COUNT +}; + +struct File_View_Widget{ + File_View_Widget_Type type; + i32 height; + UI_State state; +}; + struct File_View{ View view_base; + + Delay *delay; + Editing_Layout *layout; Editing_File *file; Style *style; @@ -212,7 +1013,6 @@ struct File_View{ i32 font_advance; i32 font_height; - File_View_State state; View_Cursor_Data cursor; i32 mark; real32 scroll_y, target_y, vel_y; @@ -231,6 +1031,9 @@ struct File_View{ bool32 show_temp_highlight; File_View_Mode mode, next_mode; + File_View_Widget widget; + real32 rewind_amount, rewind_speed; + i32 rewind_max, scrub_max; bool32 unwrapped_lines; bool32 show_whitespace; @@ -253,44 +1056,18 @@ internal Range get_range(i32 a, i32 b){ Range result = {}; if (a < b){ - result.smaller = a; - result.larger = b; + result.start = a; + result.end = b; } else{ - result.smaller = b; - result.larger = a; - result.swapped = 1; + result.start = b; + result.end = a; } return result; } -internal Range -range_adjust_to_left(Range range, u8 *data){ - Range result = range; - if (result.smaller > 0 && - data[result.smaller] == '\n' && - data[result.smaller-1] == '\r'){ - --result.smaller; - } - if (data[result.larger] == '\n' && - data[result.larger-1] == '\r'){ - --result.larger; - } - return result; -} - -internal i32 -pos_adjust_to_left(i32 pos, u8 *data){ - if (pos > 0 && - data[pos] == '\n' && - data[pos-1] == '\r'){ - --pos; - } - return pos; -} - -internal i32 -pos_adjust_to_self(i32 pos, u8 *data, i32 size){ +inline i32 +pos_adjust_to_right(i32 pos, u8 *data, i32 size){ if (pos+1 < size && data[pos] == '\r' && data[pos+1] == '\n'){ @@ -299,14 +1076,23 @@ pos_adjust_to_self(i32 pos, u8 *data, i32 size){ return pos; } -internal i32 -pos_universal_fix(i32 pos, u8 *data, i32 size, Endline_Mode mode){ - if (mode == ENDLINE_RN_COMBINED){ - pos = pos_adjust_to_self(pos, data, size); +inline i32 +pos_adjust_to_self(i32 pos, u8 *data, i32 size){ + if (pos > 0 && + data[pos-1] == '\r' && + data[pos] == '\n'){ + --pos; } return pos; } +inline i32 +pos_universal_fix(i32 pos, u8 *data, i32 size, Endline_Mode mode){ + if (mode == ENDLINE_RN_COMBINED) + pos = pos_adjust_to_self(pos, data, size); + return pos; +} + inline i32 starts_new_line(u8 character, Endline_Mode mode){ return (character == '\n' || (mode == ENDLINE_RN_SEPARATE && character == '\r')); @@ -403,6 +1189,8 @@ enum File_Bubble_Type{ BUBBLE_TOKENS, BUBBLE_UNDO_STRING, BUBBLE_UNDO, + BUBBLE_HISTORY_STRING, + BUBBLE_HISTORY, // FILE_BUBBLE_TYPE_END, }; @@ -737,6 +1525,16 @@ buffer_create_from_string(General_Memory *general, Editing_File *file, u8 *filen file->undo.edit_redo = file->undo.edit_max; file->undo.edits = (Edit_Step*)general_memory_allocate(general, request_size, BUBBLE_UNDO); + file->undo.str_history_max = request_size; + file->undo.history_strings = (u8*)general_memory_allocate(general, request_size, BUBBLE_HISTORY_STRING); + + file->undo.edit_history_max = request_size / sizeof(Edit_Step); + file->undo.history = (Edit_Step*)general_memory_allocate(general, request_size, BUBBLE_HISTORY); + + file->undo.history_block_count = 1; + file->undo.history_head_block = 0; + file->undo.pending_block = 0; + if (!match(file->extension, make_lit_string("txt"))){ file->tokens_exist = 1; } @@ -997,7 +1795,7 @@ buffer_relex_parallel(Mem_Options *mem, Editing_File *file, internal bool32 buffer_grow_as_needed(General_Memory *general, Editing_File *file, i32 additional_size){ bool32 result = 1; - i32 target_size = file->size + additional_size; + i32 target_size = file->size + additional_size + 1; if (target_size >= file->max_size){ i32 request_size = LargeRoundUp(target_size*2, Kbytes(256)); u8 *new_data = (u8*)general_memory_reallocate(general, file->data, file->size, request_size, BUBBLE_BUFFER); @@ -1054,9 +1852,34 @@ buffer_grow_undo_edits(General_Memory *general, Editing_File *file){ } inline void +buffer_grow_history_string(General_Memory *general, Editing_File *file, i32 extra_size){ + i32 old_max = file->undo.str_max; + u8 *old_str = file->undo.strings; + i32 new_max = old_max*2 + extra_size; + u8 *new_str = (u8*) + general_memory_reallocate(general, old_str, old_max, new_max, BUBBLE_HISTORY_STRING); + + file->undo.history_strings = new_str; + file->undo.str_history_max = new_max; +} + +inline void +buffer_grow_history_edits(General_Memory *general, Editing_File *file){ + i32 old_max = file->undo.edit_max; + Edit_Step *old_eds = file->undo.edits; + i32 new_max = old_max*2 + 2; + Edit_Step *new_eds = (Edit_Step*) + general_memory_reallocate(general, old_eds, old_max*sizeof(Edit_Step), + new_max*sizeof(Edit_Step), BUBBLE_UNDO); + + file->undo.history = new_eds; + file->undo.edit_history_max = new_max; +} + +internal Edit_Step* buffer_post_undo(General_Memory *general, Editing_File *file, i32 start, i32 end, i32 str_len, bool32 do_merge, bool32 can_merge, - bool32 clear_redo){ + bool32 clear_redo, i32 pre_pos = -1, i32 post_pos = -1){ String replaced = make_string((char*)file->data + start, end - start); if (clear_redo){ @@ -1068,38 +1891,56 @@ buffer_post_undo(General_Memory *general, Editing_File *file, buffer_grow_undo_string(general, file, replaced.size); Edit_Step edit; - edit.replaced = make_string((char*)file->undo.strings + file->undo.str_size, replaced.size); - file->undo.str_size += replaced.size; - copy(&edit.replaced, replaced); - edit.range = {start, start + str_len, 0}; - edit.cursor_pos = file->cursor_pos; + edit.str_start = file->undo.str_size; + edit.str_len = replaced.size; + edit.range = {start, start + str_len}; + if (pre_pos != -1) edit.pre_pos = pre_pos; + else edit.pre_pos = file->cursor_pos; + if (post_pos != -1) edit.post_pos = post_pos; edit.can_merge = can_merge; + edit.reverse_type = ED_REDO; + + file->undo.str_size += replaced.size; + copy_fast_unsafe((char*)file->undo.strings + edit.str_start, replaced); bool32 did_merge = 0; if (do_merge && file->undo.edit_count > 0){ Edit_Step prev = file->undo.edits[file->undo.edit_count-1]; - if (prev.can_merge && edit.replaced.size == 0 && prev.replaced.size == 0){ + if (prev.can_merge && edit.str_len == 0 && prev.str_len == 0){ if (prev.range.end == edit.range.start){ did_merge = 1; edit.range.start = prev.range.start; - edit.cursor_pos = prev.cursor_pos; + edit.pre_pos = prev.pre_pos; } } } + Edit_Step *result = 0; if (did_merge){ - file->undo.edits[file->undo.edit_count-1] = edit; + result = file->undo.edits + (file->undo.edit_count-1); + *result = edit; } else{ if (file->undo.edit_redo <= file->undo.edit_count) buffer_grow_undo_edits(general, file); - file->undo.edits[file->undo.edit_count++] = edit; + result = file->undo.edits + (file->undo.edit_count++); + *result = edit; } + + return result; } inline void +buffer_unpost_undo(Editing_File *file){ + if (file->undo.edit_count > 0){ + Edit_Step *edit = file->undo.edits + (--file->undo.edit_count); + file->undo.str_size -= edit->str_len; + } +} + +internal void buffer_post_redo(General_Memory *general, Editing_File *file, - i32 start, i32 end, i32 str_len){ + i32 start, i32 end, i32 str_len, i32 pre_pos, i32 post_pos){ String replaced = make_string((char*)file->data + start, end - start); if (file->undo.str_redo - replaced.size < file->undo.str_size) @@ -1109,10 +1950,14 @@ buffer_post_redo(General_Memory *general, Editing_File *file, TentativeAssert(file->undo.str_redo >= file->undo.str_size); Edit_Step edit; - edit.replaced = make_string((char*)file->undo.strings + file->undo.str_redo, replaced.size); - copy(&edit.replaced, replaced); - edit.range = {start, start + str_len, 0}; - edit.cursor_pos = file->cursor_pos; + edit.str_start = file->undo.str_redo; + edit.str_len = replaced.size; + edit.range = {start, start + str_len}; + edit.pre_pos = pre_pos; + edit.post_pos = post_pos; + edit.reverse_type = ED_UNDO; + + copy_fast_unsafe((char*)file->undo.strings + edit.str_start, replaced); if (file->undo.edit_redo <= file->undo.edit_count) buffer_grow_undo_edits(general, file); @@ -1120,18 +1965,89 @@ buffer_post_redo(General_Memory *general, Editing_File *file, file->undo.edits[file->undo.edit_redo] = edit; } +inline void +buffer_post_history_block(Editing_File *file, i32 pos){ + Assert(file->undo.history_head_block < pos); + Assert(pos < file->undo.edit_history); + + Edit_Step *history = file->undo.history; + Edit_Step *step = history + file->undo.history_head_block; + step->next_block = pos; + step = history + pos; + step->prev_block = file->undo.history_head_block; + file->undo.history_head_block = pos; + ++file->undo.history_block_count; +} + +inline void +buffer_unpost_history_block(Editing_File *file){ + Assert(file->undo.history_block_count > 1); + +} + +internal Edit_Step* +buffer_post_history(General_Memory *general, Editing_File *file, + i32 start, i32 end, i32 str_len, + bool32 do_merge, bool32 can_merge, Edit_Type reverse_type, + i32 pre_pos = -1, i32 post_pos = -1){ + String replaced = make_string((char*)file->data + start, end - start); + + if (file->undo.str_history_max < file->undo.str_history + replaced.size) + buffer_grow_history_string(general, file, replaced.size); + + Edit_Step edit; + edit.str_start = file->undo.str_history; + edit.str_len = replaced.size; + edit.range = {start, start + str_len}; + if (pre_pos != -1) edit.pre_pos = pre_pos; + else edit.pre_pos = file->cursor_pos; + if (post_pos != -1) edit.post_pos = post_pos; + edit.can_merge = can_merge; + edit.reverse_type = reverse_type; + + file->undo.str_history += replaced.size; + copy_fast_unsafe((char*)file->undo.history_strings + edit.str_start, replaced); + + bool32 did_merge = 0; + if (do_merge && file->undo.edit_history > 0){ + Edit_Step prev = file->undo.edits[file->undo.edit_count-1]; + if (prev.can_merge && edit.str_len == 0 && prev.str_len == 0){ + if (prev.range.end == edit.range.start && reverse_type == edit.reverse_type){ + did_merge = 1; + edit.range.start = prev.range.start; + edit.pre_pos = prev.pre_pos; + } + } + } + + Edit_Step *result = 0; + if (did_merge){ + result = file->undo.history + (file->undo.edit_history-1); + } + else{ + if (file->undo.edit_history_max <= file->undo.edit_history) + buffer_grow_history_edits(general, file); + result = file->undo.history + (file->undo.edit_history++); + } + + *result = edit; + + return result; +} + inline i32 buffer_text_replace(General_Memory *general, Editing_File *file, i32 start, i32 end, u8 *str, i32 str_len){ i32 shift_amount = (str_len - (end - start)); buffer_grow_as_needed(general, file, shift_amount); - Assert(shift_amount + file->size < file->max_size); + Assert(shift_amount + file->size + 1 <= file->max_size); i32 size = file->size; u8 *data = (u8*)file->data; memmove(data + end + shift_amount, data + end, size - end); file->size += shift_amount; + file->data[file->size] = 0; memcpy(data + start, str, str_len); @@ -1141,23 +2057,15 @@ buffer_text_replace(General_Memory *general, Editing_File *file, // TODO(allen): Proper strings? internal Shift_Information buffer_replace_range(Mem_Options *mem, Editing_File *file, - i32 start, i32 end, u8 *str, i32 str_len, - i32 save_undo = 1){ + i32 start, i32 end, u8 *str, i32 str_len){ ProfileMomentFunction(); if (file->still_lexing) system_cancel_job(BACKGROUND_THREADS, file->lex_job); file->last_4ed_edit_time = system_get_now(); - + General_Memory *general = &mem->general; - if (save_undo){ - bool32 can_merge = 0, do_merge = 0; - if (str_len == 1 && char_is_alpha_numeric(*str)) can_merge = 1; - if (str_len == 1 && (can_merge || char_is_whitespace(*str))) do_merge = 1; - buffer_post_undo(general, file, start, end, str_len, do_merge, can_merge, save_undo == 1); - } - i32 shift_amount = buffer_text_replace(general, file, start, end, str, str_len); if (file->tokens_exist) @@ -1181,6 +2089,7 @@ buffer_replace_range(Mem_Options *mem, Editing_File *file, return shift; } +#if 0 inline internal void buffer_delete(Mem_Options *mem, Editing_File *file, i32 pos){ buffer_replace_range(mem, file, pos, pos+1, 0, 0); @@ -1195,6 +2104,7 @@ inline internal void buffer_delete_range(Mem_Options *mem, Editing_File *file, Range range){ buffer_replace_range(mem, file, range.smaller, range.larger, 0, 0); } +#endif enum Endline_Convert_Type{ ENDLINE_RN, @@ -1270,18 +2180,6 @@ view_cursor_seek(u8 *data, i32 size, Panel_Seek seek, View_Cursor_Data hint = {}){ View_Cursor_Data cursor = hint; -#if 0 - if (size == 0 || (seek.type == SEEK_POS && seek.pos == cursor.pos) || - (seek.type == SEEK_WRAPPED_XY && seek.x == cursor.wrapped_x && seek.y == cursor.wrapped_y) || - (seek.type == SEEK_UNWRAPPED_XY && seek.x == cursor.unwrapped_x && seek.y == cursor.unwrapped_y) || - (seek.type == SEEK_LINE_CHAR && seek.line == cursor.line && seek.character == cursor.character)){ - if (endline_mode == ENDLINE_RN_COMBINED){ - cursor.pos = pos_adjust_to_self(cursor.pos, data, size); - } - return cursor; - } -#endif - while (1){ View_Cursor_Data prev_cursor = cursor; bool32 do_newline = 0; @@ -1382,8 +2280,7 @@ view_cursor_seek(u8 *data, i32 size, Panel_Seek seek, } if (xy_seek){ if (seek.round_down){ - if (y > seek.y || - (y > seek.y - font->height && x > seek.x)){ + if (y > seek.y || (y > seek.y - font->height && x > seek.x)){ cursor = prev_cursor; break; } @@ -1595,7 +2492,7 @@ view_set_file(File_View *view, Editing_File *file, Style *style){ Panel *panel = view->view_base.panel; view->file = file; - General_Memory *general = view->view_base.general; + General_Memory *general = &view->view_base.mem->general; Font *font = style->font; view->style = style; view->font_advance = font->advance; @@ -1694,6 +2591,263 @@ view_cursor_move(File_View *view, real32 x, real32 y, bool32 round_down = 0){ view->show_temp_highlight = 0; } +inline void +view_set_widget(File_View *view, File_View_Widget_Type type){ + view->widget.type = type; +} + +inline i32 +view_widget_height(File_View *view, Font *font){ + i32 result = 0; + switch (view->widget.type){ + case FWIDG_NONE: break; + case FWIDG_SEARCH: result = font->height + 2; break; + case FWIDG_GOTO_LINE: result = font->height + 2; break; + case FWIDG_UNDOING: result = font->height*2; break; + case FWIDG_RESTORING: result = font->height*2; break; + } + return result; +} + +inline i32_Rect +view_widget_rect(File_View *view, Font *font){ + Panel *panel = view->view_base.panel; + i32_Rect whole = panel->inner; + i32_Rect result; + result.x0 = whole.x0; + result.x1 = whole.x1; + result.y0 = whole.y0 + font->height + 2; + result.y1 = result.y0 + view_widget_height(view, font); + + return result; +} + +struct Edit_Spec{ + Edit_Type type; + i32 start, end; + u8 *str; + i32 str_len; + i32 pre_pos, post_pos; + i32 next_cursor_pos; + i32 in_history; +}; + +internal void +view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, + Editing_Layout *layout, Edit_Spec spec){ + General_Memory *general = &mem->general; + + bool32 can_merge = 0, do_merge = 0; + switch (spec.type){ + case ED_NORMAL: + { + if (spec.str_len == 1 && char_is_alpha_numeric(*spec.str)) can_merge = 1; + if (spec.str_len == 1 && (can_merge || char_is_whitespace(*spec.str))) do_merge = 1; + + Edit_Step *step = 0; + if (spec.in_history != 2){ + step = buffer_post_history(general, file, spec.start, spec.end, spec.str_len, + do_merge, can_merge, ED_REVERSE_NORMAL); + step->post_pos = spec.next_cursor_pos; + } + + step = buffer_post_undo(general, file, spec.start, spec.end, spec.str_len, + do_merge, can_merge, 1); + step->post_pos = spec.next_cursor_pos; + }break; + + case ED_REVERSE_NORMAL: + { + if (spec.in_history != 2) + buffer_post_history(general, file, spec.start, spec.end, spec.str_len, + do_merge, can_merge, ED_NORMAL, spec.pre_pos, spec.post_pos); + + buffer_unpost_undo(file); + + if (spec.in_history == 1 && file->undo.edit_history_cursor > 0){ + Edit_Step *redo_end = file->undo.history + (file->undo.edit_history_cursor - 1); + Edit_Step *redo_start = redo_end; + while (redo_start->reverse_type == ED_REDO) --redo_start; + ++redo_start; + ++redo_end; + + if (redo_start < redo_end){ + i32 steps_of_redo = (i32)(redo_end - redo_start); + + u8 *str_dest_base = file->undo.strings; + u8 *str_src = file->undo.history_strings + redo_start->str_start; + i32 str_redo_pos = file->undo.str_redo; + + Edit_Step *edit_dest = file->undo.edits + file->undo.edit_redo; + Edit_Step *edit_src = redo_start; + + for (i32 i = 0; i < steps_of_redo; ++i){ + --edit_dest; + edit_dest->range = edit_src->range; + edit_dest->pre_pos = edit_src->pre_pos; + edit_dest->post_pos = edit_src->post_pos; + edit_dest->can_merge = edit_src->can_merge; + edit_dest->reverse_type = edit_src->reverse_type; + + edit_dest->str_len = edit_src->str_len; + str_redo_pos -= edit_dest->str_len; + edit_dest->str_start = str_redo_pos; + + memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->str_len); + str_src += edit_dest->str_len; + ++edit_src; + } + + file->undo.str_redo = str_redo_pos; + file->undo.edit_redo -= steps_of_redo; + + Assert(file->undo.str_redo >= file->undo.str_size); + Assert(file->undo.edit_redo >= file->undo.edit_count); + } + } + }break; + + case ED_UNDO: + { + if (spec.in_history != 2) + buffer_post_history(general, file, spec.start, spec.end, spec.str_len, + do_merge, can_merge, ED_REDO, spec.pre_pos, spec.post_pos); + buffer_post_redo(general, file, spec.start, spec.end, spec.str_len, spec.pre_pos, spec.post_pos); + }break; + + case ED_REDO: + { + if (spec.str_len == 1 && char_is_alpha_numeric(*spec.str)) can_merge = 1; + if (spec.str_len == 1 && (can_merge || char_is_whitespace(*spec.str))) do_merge = 1; + + if (spec.in_history != 2) + buffer_post_history(general, file, spec.start, spec.end, spec.str_len, + do_merge, can_merge, ED_UNDO, spec.pre_pos, spec.post_pos); + + buffer_post_undo(general, file, spec.start, spec.end, spec.str_len, do_merge, can_merge, 0, + spec.pre_pos, spec.post_pos); + }break; + } + + if (spec.in_history != 2){ + if (spec.type == ED_UNDO){ + if (!file->undo.pending_block){ + file->undo.pending_block = file->undo.edit_history - 1; + buffer_post_history_block(file, file->undo.pending_block); + } + } + else{ + if (file->undo.pending_block){ + buffer_post_history_block(file, file->undo.edit_history - 1); + file->undo.pending_block = 0; + } + } + } + else{ + if (file->undo.pending_block == file->undo.edit_history){ + buffer_unpost_history_block(file); + file->undo.pending_block = 0; + } + } + + if (spec.in_history == 0) file->undo.edit_history_cursor = file->undo.edit_history; +} + +internal void +view_post_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, + Editing_Layout *layout, Edit_Spec spec){ + Panel *current_panel = layout->panels; + i32 panel_count = layout->panel_count; + for (i32 i = 0; i < panel_count; ++i, ++current_panel){ + File_View *current_view = view_to_file_view(current_panel->view); + if (current_view && current_view->file == file){ + view_measure_wraps(&mem->general, current_view); + if (current_view != view){ + if (current_view->cursor.pos >= spec.end){ + view_cursor_move(current_view, current_view->cursor.pos+shift.amount); + } + else if (current_view->cursor.pos > spec.start){ + view_cursor_move(current_view, spec.start); + } + + if (current_view->mark >= spec.end){ + current_view->mark += shift.amount; + } + else if (current_view->mark > spec.start){ + current_view->mark += spec.start; + } + current_view->preferred_x = view_get_cursor_x(current_view); + } + } + } +} + +internal void +view_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, + Editing_Layout *layout, Edit_Spec spec){ + Assert(file); + + view_pre_replace_range(mem, view, file, layout, spec); + + Shift_Information shift; + if (!spec.skip_text_replace){ + shift = + buffer_replace_range(mem, file, spec.start, spec.end, spec.str, spec.str_len); + } + else{ + shift = spec.override_replace; + } + + view_post_replace_range(mem, view, file, layout, spec, shift); +} + +inline void +view_replace_range(Mem_Options *mem, File_View *view, Editing_Layout *layout, + i32 start, i32 end, u8 *str, i32 len, i32 next_cursor_pos){ + Edit_Spec spec = {}; + spec.type = ED_NORMAL; + spec.start = start; + spec.end = end; + spec.str = str; + spec.str_len = len; + spec.next_cursor_pos = next_cursor_pos; + view_replace_range(mem, view, view->file, layout, spec); +} + +inline void +view_range_undo(Mem_Options *mem, File_View *view, Editing_Layout *layout, + Edit_Step step){ + Editing_File *file = view->file; + + Edit_Spec spec = {}; + spec.type = ED_UNDO; + spec.start = step.range.start; + spec.end = step.range.end; + spec.str = file->undo.strings + step.str_start; + spec.str_len = step.str_len; + spec.pre_pos = step.pre_pos; + spec.post_pos = step.post_pos; + spec.in_history = 0; + view_replace_range(mem, view, file, layout, spec); +} + +inline void +view_range_redo(Mem_Options *mem, File_View *view, Editing_Layout *layout, + Edit_Step step){ + Editing_File *file = view->file; + + Edit_Spec spec = {}; + spec.type = ED_REDO; + spec.start = step.range.start; + spec.end = step.range.end; + spec.str = file->undo.strings + step.str_start; + spec.str_len = step.str_len; + spec.pre_pos = step.pre_pos; + spec.post_pos = step.post_pos; + spec.in_history = 0; + view_replace_range(mem, view, file, layout, spec); +} + // TODO(allen): should these still be view operations? internal i32 view_find_end_of_line(File_View *view, i32 pos){ @@ -1829,9 +2983,9 @@ working_set_lookup_file(Working_Set *working_set, String string){ internal void clipboard_copy(General_Memory *general, Working_Set *working, u8 *data, Range range){ - i32 size = range.larger - range.smaller; + i32 size = range.end - range.start; String *dest = working_set_next_clipboard_string(general, working, size); - copy(dest, make_string((char*)data + range.smaller, size)); + copy(dest, make_string((char*)data + range.start, size)); system_post_clipboard(*dest); } @@ -2476,17 +3630,266 @@ internal void remeasure_file_view(View *view_, i32_Rect rect){ File_View *view = (File_View*)view_; Relative_Scrolling relative = view_get_relative_scrolling(view); - view_measure_wraps(view->view_base.general, view); + view_measure_wraps(&view->view_base.mem->general, view); view_cursor_move(view, view->cursor.pos); view->preferred_x = view_get_cursor_x(view); view_set_relative_scrolling(view, relative); } +internal bool32 +do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v, Undo_Data *undo, i32 *out){ + bool32 result = 0; + Font *font = state->font; + i32 character_h = font->height; + + i32_Rect containing_rect = layout_rect(layout, character_h * 2); + + i32_Rect click_rect; + click_rect.x0 = containing_rect.x0 + character_h - 1; + click_rect.x1 = containing_rect.x1 - character_h + 1; + click_rect.y0 = containing_rect.y0 + (character_h/2) - 1; + click_rect.y1 = containing_rect.y1 - DIVCEIL32(character_h, 2) + 1; + + if (state->input_stage){ + real32 l; + if (ui_do_slider_input(state, click_rect, wid, (real32)click_rect.x0, (real32)click_rect.x1, &l)){ + real32 v_new = lerp(0.f, l, (real32)max); + v = ROUND32(v_new); + result = 1; + if (out) *out = v; + } + } + else{ + if (max > 0){ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style_upper(state->style); + + real32 L = unlerp(0.f, (real32)v, (real32)max); + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + i32 bar_top = containing_rect.y0 + character_h - 1; + i32 bar_bottom = containing_rect.y1 - character_h + 1; + + bool32 show_bar = 1; + real32 tick_step = (click_rect.x1 - click_rect.x0) / (real32)max; + bool32 show_ticks = 1; + if (tick_step <= 5.f) show_ticks = 0; + + if (undo == 0){ + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.x1 = x; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + draw_rectangle(target, slider_rect, ui_style.dim); + + slider_rect.x0 = x; + slider_rect.x1 = click_rect.x1; + draw_rectangle(target, slider_rect, ui_style.pop1); + } + + if (show_ticks){ + real32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + for (i32 i = 0; i < v; ++i){ + draw_rectangle(target, tick, ui_style.dim); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + + for (i32 i = v; i <= max; ++i){ + draw_rectangle(target, tick, ui_style.pop1); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + else{ + + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + Edit_Step *history = undo->history; + i32 block_count = undo->history_block_count; + Edit_Step *step = history; + for (i32 i = 0; i < block_count; ++i){ + u32 color; + if (step->reverse_type == ED_REDO) color = ui_style.pop1; + else color = ui_style.dim; + + real32 L; + if (i + 1 == block_count){ + L = 1.f; + }else{ + step = history + step->next_block; + L = unlerp(0.f, (real32)(step - history), (real32)max); + } + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + slider_rect.x1 = x; + draw_rectangle(target, slider_rect, color); + slider_rect.x0 = slider_rect.x1; + } + } + + if (show_ticks){ + real32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + Edit_Step *history = undo->history; + u32 color = ui_style.dim; + for (i32 i = 0; i <= max; ++i){ + if (i != max){ + if (history[i].reverse_type == ED_REDO) color = ui_style.pop1; + else color = ui_style.dim; + } + draw_rectangle(target, tick, color); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + + i32_Rect slider_handle; + slider_handle.x0 = x - 2; + slider_handle.x1 = x + 2; + slider_handle.y0 = bar_top - (character_h/2); + slider_handle.y1 = bar_bottom + (character_h/2); + + draw_rectangle(target, slider_handle, ui_style.bright); + } + } + + return result; +} + +internal void +view_post_paste_effect(File_View *view, i32 ticks, i32 start, i32 size, u32 color){ + view->paste_effect.start = start; + view->paste_effect.end = start + size; + view->paste_effect.color = color; + view->paste_effect.tick_down = ticks; + view->paste_effect.tick_max = ticks; +} + +internal void +view_undo(Mem_Options *mem, Editing_Layout *layout, File_View *view){ + Editing_File *file = view->file; + + if (file && file->undo.edit_count > 0){ + Edit_Step step = file->undo.edits[--file->undo.edit_count]; + + view_range_undo(mem, view, layout, step); + view_cursor_move(view, step.pre_pos); + view->mark = view->cursor.pos; + + view_post_paste_effect(view, 10, step.range.start, step.str_len, + view->style->main.undo_color); + file->undo.str_size -= step.str_len; + } +} + +internal void +view_redo(Mem_Options *mem, Editing_Layout *layout, File_View *view){ + Editing_File *file = view->file; + + if (file->undo.edit_redo < file->undo.edit_max){ + Edit_Step step = file->undo.edits[file->undo.edit_redo++]; + + view_range_redo(mem, view, layout, step); + view_cursor_move(view, step.post_pos); + view->mark = view->cursor.pos; + + view_post_paste_effect(view, 10, step.range.start, step.str_len, + view->style->main.undo_color); + + file->undo.str_redo += step.str_len; + } +} + +internal void +view_history_step(Mem_Options *mem, Editing_Layout *layout, File_View *view, i32 direction){ + Assert(direction == 1 || direction == 2); + + Editing_File *file = view->file; + bool32 do_history_step = 0; + Edit_Step step = {}; + if (direction == 1){ + if (file->undo.edit_history_cursor > 0){ + do_history_step = 1; + step = file->undo.history[--file->undo.edit_history_cursor]; + } + } + else{ + if (file->undo.edit_history_cursor < file->undo.edit_history){ + Assert(((file->undo.edit_history - file->undo.edit_history_cursor) & 1) == 0); + step = file->undo.history[--file->undo.edit_history]; + file->undo.str_history -= step.str_len; + ++file->undo.edit_history_cursor; + do_history_step = 1; + } + } + + if (do_history_step){ + Edit_Spec spec = {}; + spec.type = step.reverse_type; + spec.start = step.range.start; + spec.end = step.range.end; + spec.str = file->undo.history_strings + step.str_start; + spec.str_len = step.str_len; + spec.pre_pos = step.pre_pos; + spec.post_pos = step.post_pos; + spec.next_cursor_pos = step.post_pos; + spec.in_history = direction; + + switch (spec.type){ + case ED_UNDO: + Assert(file->undo.edit_count > 0); + --file->undo.edit_count; + break; + + case ED_REDO: + Assert(file->undo.edit_redo < file->undo.edit_max); + ++file->undo.edit_redo; + break; + } + + view_replace_range(mem, view, file, layout, spec); + + switch (spec.type){ + case ED_NORMAL: + case ED_REDO: + view_cursor_move(view, step.post_pos); + break; + + case ED_REVERSE_NORMAL: + case ED_UNDO: + view_cursor_move(view, step.pre_pos); + break; + } + view->mark = view->cursor.pos; + } +} + internal i32 step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_active, Input_Summary *user_input){ + view_->mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; i32 result = 0; File_View *view = (File_View*)view_; + Editing_File *file = view->file; Style *style = view->style; Font *font = style->font; @@ -2497,18 +3900,28 @@ step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, i32 lowest_line = view_compute_lowest_line(view); real32 max_target_y = view_compute_max_target_y(lowest_line, font->height, max_y); real32 delta_y = 3.f*line_height; + real32 extra_top = 0.f; + extra_top += view_widget_height(view, font); + real32 taken_top_space = line_height + extra_top; + + if (user_input->mouse.my < rect.y0 + taken_top_space){ + view_->mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + } + else{ + view_->mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; + } if (user_input->mouse.wheel_used){ real32 wheel_multiplier = 3.f; real32 delta_target_y = delta_y*user_input->mouse.wheel_amount*wheel_multiplier; target_y += delta_target_y; - if (target_y < 0) target_y = 0; + if (target_y < -taken_top_space) target_y = -taken_top_space; if (target_y > max_target_y) target_y = max_target_y; real32 old_cursor_y = cursor_y; if (cursor_y >= target_y + max_y) cursor_y = target_y + max_y; - if (cursor_y < target_y + line_height) cursor_y = target_y + line_height; + if (cursor_y < target_y + taken_top_space) cursor_y = target_y + taken_top_space; if (cursor_y != old_cursor_y){ view->cursor = @@ -2520,15 +3933,11 @@ step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, result = 1; } - while (cursor_y > target_y + max_y){ - target_y += delta_y; - } - while (cursor_y < target_y){ - target_y -= delta_y; - } + while (cursor_y > target_y + max_y) target_y += delta_y; + while (cursor_y < target_y + taken_top_space) target_y -= delta_y; if (target_y > max_target_y) target_y = max_target_y; - if (target_y < 0) target_y = 0; + if (target_y < -extra_top) target_y = -extra_top; view->target_y = target_y; real32 cursor_x = view_get_cursor_x(view); @@ -2555,30 +3964,124 @@ step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, } if (is_active && user_input->mouse.press_l){ - rect.y0 += font->height + 2; - real32 max_y = view_compute_height(view); real32 rx = (real32)(user_input->mouse.mx - rect.x0); - real32 ry = (real32)(user_input->mouse.my - rect.y0); + real32 ry = (real32)(user_input->mouse.my - rect.y0 - line_height - 2); - if (rx >= 0 && rx < max_x && ry >= 0 && ry < max_y){ - view_cursor_move(view, rx + view->scroll_x, ry + view->scroll_y, 1); - view->mode = {}; + if (ry >= extra_top){ + view_set_widget(view, FWIDG_NONE); + if (rx >= 0 && rx < max_x && ry >= 0 && ry < max_y){ + view_cursor_move(view, rx + view->scroll_x, ry + view->scroll_y, 1); + view->mode = {}; + } } result = 1; } - if (!is_active && view->state != FVIEW_STATE_EDIT){ - view->state = FVIEW_STATE_EDIT; + if (!is_active) view_set_widget(view, FWIDG_NONE); + + // NOTE(allen): framely undo stuff + { + i32 scrub_max = view->scrub_max; + i32 undo_count, redo_count, total_count; + undo_count = file->undo.edit_count; + redo_count = file->undo.edit_max - file->undo.edit_redo; + total_count = undo_count + redo_count; + + switch (view->widget.type){ + case FWIDG_UNDOING: + case FWIDG_RESTORING: + { + i32_Rect widg_rect = view_widget_rect(view, font); + + UI_State state = + ui_state_init(&view->widget.state, 0, user_input, + view->style, 0, 1); + + UI_Layout layout; + begin_layout(&layout, widg_rect); + + Widget_ID wid = make_id(&state, 1); + + if (view->widget.type == FWIDG_UNDOING){ + i32 new_count; + if (do_undo_slider(wid, &state, &layout, total_count, undo_count, 0, &new_count)){ + view->rewind_speed = 0; + view->rewind_amount = 0; + + for (i32 i = 0; i < scrub_max && new_count < undo_count; ++i){ + view_undo(view_->mem, view->layout, view); + --undo_count; + } + for (i32 i = 0; i < scrub_max && new_count > undo_count; ++i){ + view_redo(view_->mem, view->layout, view); + ++undo_count; + } + } + } + else{ + i32 new_count; + i32 mid = ((file->undo.edit_history + file->undo.edit_history_cursor) >> 1); + i32 count = file->undo.edit_history_cursor; + if (do_undo_slider(wid, &state, &layout, mid, count, &file->undo, &new_count)){ + for (i32 i = 0; i < scrub_max && new_count < count; ++i){ + view_history_step(view_->mem, view->layout, view, 1); + } + for (i32 i = 0; i < scrub_max && new_count > count; ++i){ + view_history_step(view_->mem, view->layout, view, 2); + } + } + } + + if (ui_finish_frame(&view->widget.state, &state, &layout, widg_rect, 0, 0)){ + result = 1; + } + }break; + } + + i32 rewind_max = view->rewind_max; + view->rewind_amount += view->rewind_speed; + for (i32 i = 0; view->rewind_amount <= -1.f; ++i, view->rewind_amount += 1.f){ + if (i < rewind_max) view_undo(view_->mem, view->layout, view); + } + + for (i32 i = 0; view->rewind_amount >= 1.f; ++i, view->rewind_amount -= 1.f){ + if (i < rewind_max) view_redo(view_->mem, view->layout, view); + } + + if (view->rewind_speed < 0.f && undo_count == 0){ + view->rewind_speed = 0.f; + view->rewind_amount = 0.f; + } + + if (view->rewind_speed > 0.f && redo_count == 0){ + view->rewind_speed = 0.f; + view->rewind_amount = 0.f; + } } return result; } +enum File_Sync_State{ + SYNC_GOOD, + SYNC_BEHIND_OS, + SYNC_UNSAVED +}; + +inline File_Sync_State +buffer_get_sync(Editing_File *file){ + File_Sync_State result = SYNC_GOOD; + if (file->last_4ed_write_time != file->last_sys_write_time) + result = SYNC_BEHIND_OS; + else if (file->last_4ed_edit_time > file->last_sys_write_time) + result = SYNC_UNSAVED; + return result; +} + internal i32 draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_active, Render_Target *target){ - view_->mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; File_View *view = (File_View*)view_; Editing_File *file = view->file; Style *style = view->style; @@ -2598,270 +4101,283 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act i32 max_x = rect.x1 - rect.x0; i32 max_y = rect.y1 - rect.y0 + font->height; - if (!file || !file->data || file->is_dummy){ - Assert(!"Displaying this file is no longer allowed - 26.08.2015"); + Assert(file && file->data && !file->is_dummy); + + i32 size = (i32)file->size; + u8 *data = (u8*)file->data; + + View_Cursor_Data start_cursor; + start_cursor = view_compute_cursor_from_xy(view, 0, view->scroll_y); + view->scroll_y_cursor = start_cursor; + + i32 start_character = start_cursor.pos; + + real32 shift_x = rect.x0 - view->scroll_x; + real32 shift_y = rect.y0 - view->scroll_y; + if (view->unwrapped_lines) shift_y += start_cursor.unwrapped_y; + else shift_y += start_cursor.wrapped_y; + + real32 pos_x = 0; + real32 pos_y = 0; + + Cpp_Token_Stack token_stack = file->token_stack; + u32 highlight_color = 0; + u32 main_color = style->main.default_color; + i32 token_i = 0; + bool32 tokens_use = file->tokens_complete && (file->token_stack.count > 0); + + if (tokens_use){ + Cpp_Get_Token_Result result = cpp_get_token(&token_stack, start_character); + token_i = result.token_index; + + main_color = *style_get_color(style, token_stack.tokens[token_i]); + ++token_i; } - else{ - i32 size = (i32)file->size; - u8 *data = (u8*)file->data; - - View_Cursor_Data start_cursor; - start_cursor = view_compute_cursor_from_xy(view, 0, view->scroll_y); - view->scroll_y_cursor = start_cursor; - - i32 start_character = start_cursor.pos; - - real32 shift_x = rect.x0 - view->scroll_x; - real32 shift_y = rect.y0 - view->scroll_y; - if (view->unwrapped_lines){ - shift_y += start_cursor.unwrapped_y; + for (i32 i = start_character; i <= size; ++i){ + u8 to_render, next_to_render; + if (i < size){ + to_render = data[i]; } else{ - shift_y += start_cursor.wrapped_y; + to_render = 0; + } + if (i+1 < size){ + next_to_render = data[i+1]; + } + else{ + next_to_render = 0; + } + + if (!view->unwrapped_lines && pos_x + font_get_glyph_width(font, to_render) > max_x){ + pos_x = 0; + pos_y += font->height; + } + + u32 fade_color = 0xFFFF00FF; + real32 fade_amount = 0.f; + if (view->paste_effect.tick_down > 0 && + view->paste_effect.start <= i && i < view->paste_effect.end){ + fade_color = view->paste_effect.color; + fade_amount = (real32)(view->paste_effect.tick_down) / view->paste_effect.tick_max; } - real32 pos_x = 0; - real32 pos_y = 0; - - Cpp_Token_Stack token_stack = file->token_stack; - u32 highlight_color = 0; - u32 main_color = style->main.default_color; - i32 token_i = 0; - bool32 tokens_use = file->tokens_complete && (file->token_stack.count > 0); - + highlight_color = 0; if (tokens_use){ - Cpp_Get_Token_Result result = cpp_get_token(&token_stack, start_character); - token_i = result.token_index; - - main_color = *style_get_color(style, token_stack.tokens[token_i]); - ++token_i; + Cpp_Token current_token = token_stack.tokens[token_i-1]; + + if (token_i < token_stack.count){ + if (i >= token_stack.tokens[token_i].start){ + main_color = + *style_get_color(style, token_stack.tokens[token_i]); + current_token = token_stack.tokens[token_i]; + ++token_i; + } + else if (i >= current_token.start + current_token.size){ + main_color = 0xFFFFFFFF; + } + } + + if (current_token.type == CPP_TOKEN_JUNK && + i >= current_token.start && i <= current_token.start + current_token.size){ + highlight_color = style->main.highlight_junk_color; + } } - - for (i32 i = start_character; i <= size; ++i){ - u8 to_render, next_to_render; - if (i < size){ - to_render = data[i]; + if (highlight_color == 0 && view->show_whitespace && char_is_whitespace(data[i])){ + highlight_color = style->main.highlight_white_color; + } + + i32 cursor_mode = 0; + if (view->show_temp_highlight){ + if (view->temp_highlight.pos <= i && i < view->temp_highlight_end_pos){ + cursor_mode = 2; + } + } + else{ + if (view->cursor.pos == i){ + cursor_mode = 1; + } + } + + real32 to_render_width = font_get_glyph_width(font, to_render); + real32_Rect cursor_rect = + real32XYWH(shift_x + pos_x, shift_y + pos_y, + 1 + to_render_width, (real32)font->height); + + if (to_render == '\t' || to_render == 0){ + cursor_rect.x1 = cursor_rect.x0 + font->chardata[' '].xadvance; + } + + if (highlight_color != 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED && + to_render == '\r' && next_to_render == '\n'){ + // DO NOTHING + // NOTE(allen): relevant durring whitespace highlighting } else{ - to_render = 0; - } - if (i+1 < size){ - next_to_render = data[i+1]; - } - else{ - next_to_render = 0; + real32_Rect highlight_rect = cursor_rect; + highlight_rect.x0 += 1; + draw_rectangle(target, highlight_rect, highlight_color); } + } - if (!view->unwrapped_lines && pos_x + font_get_glyph_width(font, to_render) > max_x){ - pos_x = 0; - pos_y += font->height; - } + u32 cursor_color = 0; + switch (cursor_mode){ + case 1: + cursor_color = style->main.cursor_color; break; + case 2: + cursor_color = style->main.highlight_color; break; + } - u32 fade_color = 0xFFFF00FF; - real32 fade_amount = 0.f; - if (view->paste_effect.tick_down > 0 && - view->paste_effect.start <= i && i < view->paste_effect.end){ - fade_color = view->paste_effect.color; - fade_amount = (real32)(view->paste_effect.tick_down) / view->paste_effect.tick_max; - } + if (is_active){ + if (cursor_color & 0xFF000000) draw_rectangle(target, cursor_rect, cursor_color); + } + else{ + if (cursor_color & 0xFF000000)draw_rectangle_outline(target, cursor_rect, cursor_color); + } + if (i == view->mark){ + draw_rectangle_outline(target, cursor_rect, style->main.mark_color); + } - highlight_color = 0; - if (tokens_use){ - Cpp_Token current_token = token_stack.tokens[token_i-1]; - - if (token_i < token_stack.count){ - if (i >= token_stack.tokens[token_i].start){ - main_color = - *style_get_color(style, token_stack.tokens[token_i]); - current_token = token_stack.tokens[token_i]; - ++token_i; - } - else if (i >= current_token.start + current_token.size){ - main_color = 0xFFFFFFFF; - } - } - - if (current_token.type == CPP_TOKEN_JUNK && - i >= current_token.start && i <= current_token.start + current_token.size){ - highlight_color = style->main.highlight_junk_color; - } - } - if (highlight_color == 0 && view->show_whitespace && char_is_whitespace(data[i])){ - highlight_color = style->main.highlight_white_color; - } - - i32 cursor_mode = 0; - if (view->show_temp_highlight){ - if (view->temp_highlight.pos <= i && i < view->temp_highlight_end_pos){ - cursor_mode = 2; - } - } - else{ - if (view->cursor.pos == i){ - cursor_mode = 1; - } - } - - real32 to_render_width = font_get_glyph_width(font, to_render); - real32_Rect cursor_rect = - real32XYWH(shift_x + pos_x, shift_y + pos_y, - 1 + to_render_width, (real32)font->height); - - if (to_render == '\t' || to_render == 0){ - cursor_rect.x1 = cursor_rect.x0 + font->chardata[' '].xadvance; - } - - if (highlight_color != 0){ - if (file->endline_mode == ENDLINE_RN_COMBINED && - to_render == '\r' && next_to_render == '\n'){ - // DO NOTHING - // NOTE(allen): relevant durring whitespace highlighting - } - else{ - real32_Rect highlight_rect = cursor_rect; - highlight_rect.x0 += 1; - draw_rectangle(target, highlight_rect, highlight_color); - } - } - - u32 cursor_color = 0; + u32 char_color = main_color; + u32 special_char_color = main_color; + if (to_render == '\r'){ + special_char_color = char_color = style->main.special_character_color; + } + if (is_active){ switch (cursor_mode){ case 1: - cursor_color = style->main.cursor_color; break; + char_color = style->main.at_cursor_color; break; case 2: - cursor_color = style->main.highlight_color; break; + special_char_color = char_color = style->main.at_highlight_color; break; } + } - if (is_active){ - if (cursor_color & 0xFF000000) draw_rectangle(target, cursor_rect, cursor_color); - } - else{ - if (cursor_color & 0xFF000000)draw_rectangle_outline(target, cursor_rect, cursor_color); - } - if (i == view->mark){ - draw_rectangle_outline(target, cursor_rect, style->main.mark_color); - } + char_color = color_blend(char_color, fade_amount, fade_color); + special_char_color = color_blend(special_char_color, fade_amount, fade_color); - u32 char_color = main_color; - u32 special_char_color = main_color; - if (to_render == '\r'){ - special_char_color = char_color = style->main.special_character_color; - } - if (is_active){ - switch (cursor_mode){ - case 1: - char_color = style->main.at_cursor_color; break; - case 2: - special_char_color = char_color = style->main.at_highlight_color; break; - } - } - - char_color = color_blend(char_color, fade_amount, fade_color); - special_char_color = color_blend(special_char_color, fade_amount, fade_color); - - if (to_render == '\r'){ - bool32 show_slashr = 0; - switch (file->endline_mode){ - case ENDLINE_RN_COMBINED: - { - if (next_to_render != '\n'){ - show_slashr = 1; - } - }break; - - case ENDLINE_RN_SEPARATE: - { - pos_x = 0; - pos_y += font->height; - }break; - - case ENDLINE_RN_SHOWALLR: - { + if (to_render == '\r'){ + bool32 show_slashr = 0; + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + if (next_to_render != '\n'){ show_slashr = 1; - }break; } + }break; - if (show_slashr){ - font_draw_glyph(target, font, '\\', - shift_x + pos_x, - shift_y + pos_y, - char_color); - pos_x += font_get_glyph_width(font, '\\'); - - real32 cw = font_get_glyph_width(font, 'r'); - draw_rectangle(target, - real32XYWH(shift_x + pos_x, - shift_y + pos_y, - cw, (real32)font->height), - highlight_color); - font_draw_glyph(target, font, 'r', - shift_x + pos_x, - shift_y + pos_y, - special_char_color); - pos_x += cw; - } - } - - else if (to_render == '\n'){ + case ENDLINE_RN_SEPARATE: + { pos_x = 0; pos_y += font->height; + }break; + + case ENDLINE_RN_SHOWALLR: + { + show_slashr = 1; + }break; } - - else if (font->glyphs[to_render].exists){ - font_draw_glyph(target, font, to_render, + + if (show_slashr){ + font_draw_glyph(target, font, '\\', shift_x + pos_x, shift_y + pos_y, char_color); - pos_x += to_render_width; + pos_x += font_get_glyph_width(font, '\\'); + + real32 cw = font_get_glyph_width(font, 'r'); + draw_rectangle(target, + real32XYWH(shift_x + pos_x, + shift_y + pos_y, + cw, (real32)font->height), + highlight_color); + font_draw_glyph(target, font, 'r', + shift_x + pos_x, + shift_y + pos_y, + special_char_color); + pos_x += cw; } + } - else{ - pos_x += to_render_width; - } + else if (to_render == '\n'){ + pos_x = 0; + pos_y += font->height; + } - if (pos_y > max_y){ - break; - } + else if (font->glyphs[to_render].exists){ + font_draw_glyph(target, font, to_render, + shift_x + pos_x, + shift_y + pos_y, + char_color); + pos_x += to_render_width; + } + + else{ + pos_x += to_render_width; + } + + if (pos_y > max_y){ + break; } } - { - Interactive_Bar bar2; - bar2.style = style->main.file_info_style; - bar2.font = style->font; - bar2.pos_x = (real32)rect.x0; - bar2.pos_y = (real32)rect.y0 + 3; - bar2.text_shift_y = 0; - bar2.text_shift_x = 0; - bar2.rect = rect; - bar2.rect.y1 = bar2.rect.y0 + font->height + 2; + if (view->widget.type != FWIDG_NONE){ + UI_Style ui_style = get_ui_style_upper(style); - switch (view->state){ - case FVIEW_STATE_SEARCH: + Font *font = style->font; + i32_Rect widg_rect = view_widget_rect(view, font); + + draw_rectangle(target, widg_rect, ui_style.dark); + draw_rectangle_outline(target, widg_rect, ui_style.dim); + + UI_State state = + ui_state_init(&view->widget.state, target, 0, + view->style, 0, 0); + + UI_Layout layout; + begin_layout(&layout, widg_rect); + + switch (view->widget.type){ + case FWIDG_UNDOING: + case FWIDG_RESTORING: { - draw_rectangle(target, bar2.rect, bar2.style.bar_active_color); - + Widget_ID wid = make_id(&state, 1); + if (view->widget.type == FWIDG_UNDOING){ + i32 undo_count, redo_count, total_count; + undo_count = file->undo.edit_count; + redo_count = file->undo.edit_max - file->undo.edit_redo; + total_count = undo_count + redo_count; + do_undo_slider(wid, &state, &layout, total_count, undo_count, 0, 0); + } + else{ + i32 new_count; + i32 mid = ((file->undo.edit_history + file->undo.edit_history_cursor) >> 1); + do_undo_slider(wid, &state, &layout, mid, file->undo.edit_history_cursor, &file->undo, &new_count); + } + }break; + + case FWIDG_SEARCH: + { + Widget_ID wid = make_id(&state, 1); persist String search_str = make_lit_string("I-Search: "); persist String rsearch_str = make_lit_string("Reverse-I-Search: "); if (view->isearch.reverse){ - intbar_draw_string(target, &bar2, rsearch_str, bar2.style.pop1_color); + do_text_field(wid, &state, &layout, rsearch_str, view->isearch.str); } else{ - intbar_draw_string(target, &bar2, search_str, bar2.style.pop1_color); + do_text_field(wid, &state, &layout, search_str, view->isearch.str); } - intbar_draw_string(target, &bar2, view->isearch.str, bar2.style.base_color); }break; - case FVIEW_STATE_GOTO_LINE: + case FWIDG_GOTO_LINE: { - draw_rectangle(target, bar2.rect, bar2.style.bar_active_color); - + Widget_ID wid = make_id(&state, 1); persist String gotoline_str = make_lit_string("Goto Line: "); - intbar_draw_string(target, &bar2, gotoline_str, bar2.style.pop1_color); - intbar_draw_string(target, &bar2, view->isearch.str, bar2.style.base_color); + do_text_field(wid, &state, &layout, gotoline_str, view->isearch.str); }break; } + + ui_finish_frame(&view->widget.state, &state, &layout, widg_rect, 0, 0); } { @@ -2878,306 +4394,36 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act append_int_to_str(view->cursor.line, &line_number); intbar_draw_string(target, &bar, line_number, base_color); - - if (file->last_4ed_write_time != file->last_sys_write_time){ + + switch (buffer_get_sync(file)){ + case SYNC_BEHIND_OS: + { persist String out_of_sync = make_lit_string(" BEHIND OS"); intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); - } - else if (file->last_4ed_edit_time > file->last_sys_write_time){ + }break; + + case SYNC_UNSAVED: + { persist String out_of_sync = make_lit_string(" *"); intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); + }break; } } return 0; } -// Hot Directory - -struct Hot_Directory{ - String string; - File_List file_list; -}; - internal void -hot_directory_init(Hot_Directory *hot_directory, String base){ - hot_directory->string = base; - hot_directory->string.str[255] = 0; - i32 dir_size = system_get_working_directory((u8*)hot_directory->string.str, - hot_directory->string.memory_size); - if (dir_size <= 0){ - dir_size = system_get_easy_directory((u8*)hot_directory->string.str); - } - hot_directory->string.size = dir_size; - append(&hot_directory->string, "\\"); -} - -internal void -hot_directory_clean_end(Hot_Directory *hot_directory){ - String *str = &hot_directory->string; - if (str->size != 0 && str->str[str->size-1] != '\\'){ - str->size = reverse_seek_slash(*str) + 1; - str->str[str->size] = 0; - } -} - -internal i32 -hot_directory_quick_partition(File_Info *infos, i32 start, i32 pivot){ - File_Info *p = infos + pivot; - File_Info *a = infos + start; - for (i32 i = start; i < pivot; ++i, ++a){ - i32 comp = 0; - comp = p->folder - a->folder; - if (comp == 0) comp = compare(a->filename, p->filename); - if (comp < 0){ - Swap(*a, infos[start]); - ++start; - } - } - Swap(*p, infos[start]); - return start; -} - -internal void -hot_directory_quick_sort(File_Info *infos, i32 start, i32 pivot){ - i32 mid = hot_directory_quick_partition(infos, start, pivot); - if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1); - if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot); -} - -inline void -hot_directory_fixup(Hot_Directory *hot_directory, Working_Set *working_set){ - File_List *files = &hot_directory->file_list; - if (files->count >= 2) - hot_directory_quick_sort(files->infos, 0, files->count - 1); -} - -inline void -hot_directory_set(Hot_Directory *hot_directory, String str, Working_Set *working_set){ - bool32 success = copy_checked(&hot_directory->string, str); - terminate_with_null(&hot_directory->string); - if (success){ - system_free_file_list(hot_directory->file_list); - hot_directory->file_list = system_get_files(str); - } - hot_directory_fixup(hot_directory, working_set); -} - -inline void -hot_directory_reload(Hot_Directory *hot_directory, Working_Set *working_set){ - if (hot_directory->file_list.block){ - system_free_file_list(hot_directory->file_list); - } - hot_directory->file_list = system_get_files(hot_directory->string); - hot_directory_fixup(hot_directory, working_set); -} - -struct Hot_Directory_Match{ - String filename; - bool32 is_folder; -}; - -internal bool32 -filename_match(String query, Absolutes *absolutes, String filename){ - bool32 result; - result = (query.size == 0); - if (!result) result = wildcard_match(absolutes, filename); - return result; -} - -internal Hot_Directory_Match -hot_directory_first_match(Hot_Directory *hot_directory, - String str, - bool32 include_files, - bool32 exact_match){ - Hot_Directory_Match result = {}; - - Absolutes absolutes; - if (!exact_match) - get_absolutes(str, &absolutes, 1, 1); - - File_List *files = &hot_directory->file_list; - File_Info *info, *end; - end = files->infos + files->count; - for (info = files->infos; info != end; ++info){ - String filename = info->filename; - bool32 is_match = 0; - if (exact_match){ - if (match(filename, str)) is_match = 1; - } - else{ - if (filename_match(str, &absolutes, filename)) is_match = 1; - } - - if (is_match){ - result.is_folder = info->folder; - result.filename = filename; - break; - } - } - - return result; -} - -struct Single_Line_Input_Step{ - bool32 hit_newline; - bool32 hit_ctrl_newline; - bool32 hit_a_character; - bool32 hit_backspace; - bool32 hit_esc; - bool32 made_a_change; - bool32 did_command; -}; - -enum Single_Line_Input_Type{ - SINGLE_LINE_STRING, - SINGLE_LINE_FILE -}; - -struct Single_Line_Mode{ - Single_Line_Input_Type type; - String *string; - Hot_Directory *hot_directory; - bool32 fast_folder_select; -}; - -internal Single_Line_Input_Step -app_single_line_input_core(Key_Codes *codes, Working_Set *working_set, - Key_Single key, Single_Line_Mode mode){ - Single_Line_Input_Step result = {}; - - if (key.key.keycode == codes->back){ - result.hit_backspace = 1; - if (mode.string->size > 0){ - result.made_a_change = 1; - --mode.string->size; - switch (mode.type){ - case SINGLE_LINE_STRING: - mode.string->str[mode.string->size] = 0; break; - - case SINGLE_LINE_FILE: - { - char end_character = mode.string->str[mode.string->size]; - if (char_is_slash(end_character)){ - mode.string->size = reverse_seek_slash(*mode.string) + 1; - mode.string->str[mode.string->size] = 0; - hot_directory_set(mode.hot_directory, *mode.string, working_set); - } - else{ - mode.string->str[mode.string->size] = 0; - } - }break; - } - } - } - - else if (key.key.character == '\n' || key.key.character == '\t'){ - result.made_a_change = 1; - if (key.modifiers[CONTROL_KEY_CONTROL] || - key.modifiers[CONTROL_KEY_ALT]){ - result.hit_ctrl_newline = 1; - } - else{ - result.hit_newline = 1; - if (mode.fast_folder_select){ - char front_name_space[256]; - String front_name = - make_string(front_name_space, 0, ArrayCount(front_name_space)); - get_front_of_directory(&front_name, *mode.string); - Hot_Directory_Match match; - match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); - if (!match.filename.str){ - match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); - } - if (match.filename.str){ - if (match.is_folder){ - set_last_folder(mode.string, match.filename); - hot_directory_set(mode.hot_directory, *mode.string, working_set); - result.hit_newline = 0; - } - else{ - mode.string->size = reverse_seek_slash(*mode.string) + 1; - append(mode.string, match.filename); - result.hit_newline = 1; - } - } - } - } - } - - else if (key.key.keycode == codes->esc){ - result.hit_esc = 1; - result.made_a_change = 1; - } - - else if (key.key.character){ - result.hit_a_character = 1; - if (!key.modifiers[CONTROL_KEY_CONTROL] && - !key.modifiers[CONTROL_KEY_ALT]){ - if (mode.string->size+1 < mode.string->memory_size){ - u8 new_character = (u8)key.key.character; - mode.string->str[mode.string->size] = new_character; - mode.string->size++; - mode.string->str[mode.string->size] = 0; - if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ - hot_directory_set(mode.hot_directory, *mode.string, working_set); - } - result.made_a_change = 1; - } - } - else{ - result.did_command = 1; - result.made_a_change = 1; - } - } - - return result; -} - -inline Single_Line_Input_Step -app_single_line_input_step(Key_Codes *codes, Key_Single key, String *string){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - return app_single_line_input_core(codes, 0, key, mode); -} - -inline Single_Line_Input_Step -app_single_file_input_step(Key_Codes *codes, Working_Set *working_set, Key_Single key, - String *string, Hot_Directory *hot_directory, - bool32 fast_folder_select){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_FILE; - mode.string = string; - mode.hot_directory = hot_directory; - mode.fast_folder_select = fast_folder_select; - return app_single_line_input_core(codes, working_set, key, mode); -} - -inline Single_Line_Input_Step -app_single_number_input_step(Key_Codes *codes, Key_Single key, String *string){ - Single_Line_Input_Step result = {}; - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - - char c = (char)key.key.character; - if (c == 0 || c == '\n' || char_is_numeric(c)) - result = app_single_line_input_core(codes, 0, key, mode); - return result; -} - -internal void -kill_file(General_Memory *general, Editing_File *file, Live_Views *live_set, Editing_Layout *layout){ +kill_buffer(General_Memory *general, Editing_File *file, Live_Views *live_set, Editing_Layout *layout){ i32 panel_count = layout->panel_count; Panel *panels = layout->panels, *panel; panel = panels; + for (i32 i = 0; i < panel_count; ++i){ View *view = panel->view; if (view){ View *to_kill = view; if (view->is_minor) to_kill = view->major; - File_View *fview = view_to_file_view(to_kill); if (fview && fview->file == file){ live_set_free_view(live_set, &fview->view_base); @@ -3187,6 +4433,7 @@ kill_file(General_Memory *general, Editing_File *file, Live_Views *live_set, Edi } ++panel; } + buffer_close(general, file); buffer_get_dummy(file); } @@ -3199,15 +4446,22 @@ command_rsearch(Command_Data*,Command_Binding); internal HANDLE_COMMAND_SIG(handle_command_file_view){ File_View *file_view = (File_View*)(view); - switch (file_view->state){ - case FVIEW_STATE_EDIT: + Editing_File *file = file_view->file; + + switch (file_view->widget.type){ + case FWIDG_NONE: + case FWIDG_UNDOING: + case FWIDG_RESTORING: { file_view->next_mode = {}; if (binding.function) binding.function(command, binding); file_view->mode = file_view->next_mode; + + if (key.key.keycode == codes->esc) + view_set_widget(file_view, FWIDG_NONE); }break; - case FVIEW_STATE_SEARCH: + case FWIDG_SEARCH: { String *string = &file_view->isearch.str; Single_Line_Input_Step result = @@ -3216,7 +4470,6 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ if (result.made_a_change || binding.function == command_search || binding.function == command_rsearch){ - Editing_File *file = file_view->file; bool32 step_forward = 0; bool32 step_backward = 0; @@ -3290,16 +4543,16 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ if (result.hit_newline || result.hit_ctrl_newline){ view_cursor_move(file_view, file_view->temp_highlight); - file_view->state = FVIEW_STATE_EDIT; + view_set_widget(file_view, FWIDG_NONE); } if (result.hit_esc){ file_view->show_temp_highlight = 0; - file_view->state = FVIEW_STATE_EDIT; + view_set_widget(file_view, FWIDG_NONE); } }break; - case FVIEW_STATE_GOTO_LINE: + case FWIDG_GOTO_LINE: { String *string = &file_view->gotoline.str; Single_Line_Input_Step result = @@ -3313,23 +4566,23 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ view_compute_cursor_from_unwrapped_xy(file_view, 0, (real32)(line_number-1)*font->height); file_view->preferred_x = view_get_cursor_x(file_view); - file_view->state = FVIEW_STATE_EDIT; + view_set_widget(file_view, FWIDG_NONE); } if (result.hit_esc){ - file_view->state = FVIEW_STATE_EDIT; + view_set_widget(file_view, FWIDG_NONE); } }break; } } -internal void +inline void free_file_view(View *view_){ File_View *view = (File_View*)view_; - general_memory_free(view_->general, view->line_wrap_y); + general_memory_free(&view_->mem->general, view->line_wrap_y); } -inline +internal DO_VIEW_SIG(do_file_view){ i32 result = 0; switch (message){ @@ -3355,11 +4608,16 @@ DO_VIEW_SIG(do_file_view){ } internal File_View* -file_view_init(View *view){ - File_View *result = (File_View*)view; +file_view_init(View *view, Delay *delay, Editing_Layout *layout){ view->type = VIEW_TYPE_FILE; view->do_view = do_file_view; view->handle_command = handle_command_file_view; + + File_View *result = (File_View*)view; + result->delay = delay; + result->layout = layout; + result->rewind_max = 4; + result->scrub_max = 1; return result; } diff --git a/4ed_interactive_view.cpp b/4ed_interactive_view.cpp index 36892744..cb1068ec 100644 --- a/4ed_interactive_view.cpp +++ b/4ed_interactive_view.cpp @@ -9,50 +9,19 @@ // TOP -enum Action_Type{ - DACT_OPEN, - DACT_SAVE_AS, - DACT_NEW, - DACT_SWITCH, - DACT_KILL, - DACT_CLOSE_MINOR, - DACT_CLOSE_MAJOR, - DACT_THEME_OPTIONS -}; - -struct Delayed_Action{ - Action_Type type; - String string; - Panel *panel; -}; - -struct Delay{ - Delayed_Action acts[8]; - i32 count, max; -}; - -inline void -delayed_action(Delay *delay, Action_Type type, - String string, Panel *panel){ - Assert(delay->count < delay->max); - Delayed_Action action; - action.type = type; - action.string = string; - action.panel = panel; - delay->acts[delay->count++] = action; -} - enum Interactive_View_Action{ INTV_OPEN, INTV_SAVE_AS, INTV_NEW, INTV_SWITCH, INTV_KILL, + INTV_SURE_TO_KILL }; enum Interactive_View_Interaction{ INTV_SYS_FILE_LIST, INTV_LIVE_FILE_LIST, + INTV_SURE_TO_KILL_INTER }; struct Interactive_View{ @@ -64,10 +33,12 @@ struct Interactive_View{ UI_State state; Interactive_View_Interaction interaction; Interactive_View_Action action; + char query_[256]; String query; char dest_[256]; String dest; + i32 user_action; }; inline Interactive_View* @@ -101,8 +72,26 @@ interactive_view_complete(Interactive_View *view){ break; case INTV_KILL: - delayed_action(view->delay, DACT_KILL, view->dest, panel); - delayed_action(view->delay, DACT_CLOSE_MINOR, {}, panel); + delayed_action(view->delay, DACT_TRY_KILL, view->dest, panel); + break; + + case INTV_SURE_TO_KILL: + switch (view->user_action){ + case 0: + delayed_action(view->delay, DACT_KILL, view->dest, panel); + delayed_action(view->delay, DACT_CLOSE_MINOR, {}, panel); + break; + + case 1: + delayed_action(view->delay, DACT_CLOSE_MINOR, {}, panel); + break; + + case 2: + delayed_action(view->delay, DACT_SAVE, view->dest, panel); + delayed_action(view->delay, DACT_KILL, view->dest, panel); + delayed_action(view->delay, DACT_CLOSE_MINOR, {}, panel); + break; + } break; } } @@ -119,7 +108,7 @@ step_draw_int_view(Interactive_View *view, Render_Target *target, i32_Rect rect, begin_layout(&layout, rect); bool32 new_dir = 0; - bool32 file_selected = 0; + bool32 complete = 0; terminate_with_null(&view->query); do_label(&state, &layout, view->query.str, 1.f); @@ -127,7 +116,7 @@ step_draw_int_view(Interactive_View *view, Render_Target *target, i32_Rect rect, switch (view->interaction){ case INTV_SYS_FILE_LIST: if (do_file_list_box(&state, &layout, view->hot_directory, 0, - &new_dir, &file_selected, 0)){ + &new_dir, &complete, 0)){ result = 1; } if (new_dir){ @@ -136,13 +125,55 @@ step_draw_int_view(Interactive_View *view, Render_Target *target, i32_Rect rect, break; case INTV_LIVE_FILE_LIST: - if (do_live_file_list_box(&state, &layout, view->working_set, &view->dest, &file_selected)){ + if (do_live_file_list_box(&state, &layout, view->working_set, &view->dest, &complete)){ result = 1; } break; + + case INTV_SURE_TO_KILL_INTER: + { + i32 action = -1; + char s_[256]; + String s = make_fixed_width_string(s_); + append(&s, view->dest); + append(&s, " has unsaved changes, kill it?"); + do_label(&state, &layout, s.str, 1.f); + + i32 id = 0; + if (do_list_option(++id, &state, &layout, make_lit_string("(Y)es"))){ + action = 0; + } + + if (do_list_option(++id, &state, &layout, make_lit_string("(N)o"))){ + action = 1; + } + + if (do_list_option(++id, &state, &layout, make_lit_string("(S)ave and kill"))){ + action = 2; + } + + if (action == -1 && input_stage){ + i32 key_count = user_input->keys.count; + for (i32 i = 0; i < key_count; ++i){ + Key_Event_Data key = user_input->keys.keys[i]; + switch (key.character){ + case 'y': case 'Y': action = 0; break; + case 'n': case 'N': action = 1; break; + case 's': case 'S': action = 2; break; + } + if (action == -1 && key.keycode == state.codes->esc) action = 1; + if (action != -1) break; + } + } + + if (action != -1){ + complete = 1; + view->user_action = action; + } + }break; } - if (file_selected){ + if (complete){ interactive_view_complete(view); } @@ -156,6 +187,7 @@ step_draw_int_view(Interactive_View *view, Render_Target *target, i32_Rect rect, DO_VIEW_SIG(do_interactive_view){ i32 result = 0; + view->mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; Interactive_View *int_view = (Interactive_View*)view; switch (message){ case VMSG_STEP: case VMSG_DRAW: diff --git a/4ed_layout.cpp b/4ed_layout.cpp index d96f6c40..834c44ef 100644 --- a/4ed_layout.cpp +++ b/4ed_layout.cpp @@ -69,7 +69,7 @@ struct View{ Command_Map *map; Do_View_Function *do_view; Handle_Command_Function *handle_command; - General_Memory *general; + Mem_Options *mem; i32 type; i32 block_size; Application_Mouse_Cursor mouse_cursor_type; @@ -173,7 +173,7 @@ live_set_get_view(Live_Views *live_set, i32 i){ } internal View* -live_set_alloc_view(Live_Views *live_set, General_Memory *general){ +live_set_alloc_view(Live_Views *live_set, Mem_Options *mem){ Assert(live_set->count < live_set->max); View *result = 0; result = live_set->free_view; @@ -181,7 +181,7 @@ live_set_alloc_view(Live_Views *live_set, General_Memory *general){ memset(result, 0, live_set->stride); ++live_set->count; result->is_active = 1; - result->general = general; + result->mem = mem; return result; }