From e51934316474cdce794cdc575bbbdc4683a3097b Mon Sep 17 00:00:00 2001 From: Chee Yee Date: Thu, 23 Feb 2023 04:16:49 -0800 Subject: [PATCH] add a simple text editor --- CMakeLists.txt | 3 +- data/assets/langs/English.ini | 5 + source/actions.h | 4 +- source/config.cpp | 4 + source/config.h | 5 + source/fs.cpp | 61 ++++++ source/fs.h | 3 + source/gui.cpp | 2 +- source/ime_dialog.cpp | 10 +- source/lang.cpp | 6 + source/lang.h | 10 +- source/main.cpp | 12 +- source/remote_client.h | 3 +- source/windows.cpp | 191 ++++++++++++++++- source/windows.h | 2 + source/xt_editor/array.h | 40 ---- source/xt_editor/editor.cpp | 385 ---------------------------------- source/xt_editor/editor.h | 10 - source/xt_editor/getline.c | 56 ----- source/xt_editor/types.h | 215 ------------------- 20 files changed, 302 insertions(+), 725 deletions(-) delete mode 100644 source/xt_editor/array.h delete mode 100644 source/xt_editor/editor.cpp delete mode 100644 source/xt_editor/editor.h delete mode 100644 source/xt_editor/getline.c delete mode 100644 source/xt_editor/types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3775bed..c152e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,6 @@ add_executable(ezremote_client source/http/iis.cpp source/http/nginx.cpp source/http/npxserve.cpp - source/xt_editor/editor.cpp - source/xt_editor/getline.c source/actions.cpp source/config.cpp source/fs.cpp @@ -59,6 +57,7 @@ add_self(ezremote_client) add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.02" 32 0) target_link_libraries(ezremote_client + dbglogger c c++ png diff --git a/data/assets/langs/English.ini b/data/assets/langs/English.ini index ef358a8..919b25d 100644 --- a/data/assets/langs/English.ini +++ b/data/assets/langs/English.ini @@ -115,3 +115,8 @@ STR_ENABLE_RPI=RPI STR_ENABLE_RPI_FTP_SMB_MSG=This option enables Remote Package Installation. This requires a HTTP Server setup on the same host sharing the same folder with anonymous access. STR_ENABLE_RPI_WEBDAV_MSG=This option enables Remote Package Installation. This requires the Server with anonymous access that does not need username/password. STR_FILES=Files +STR_EDITOR=Editor +STR_SAVE=Save +STR_MAX_EDIT_FILE_SIZE_MSG=Cannot edit files bigger than 32KB +STR_DELETE_LINE=Delete Selected Line +STR_INSERT_LINE=Insert Below Selected Line diff --git a/source/actions.h b/source/actions.h index 6258867..7c2b7c6 100644 --- a/source/actions.h +++ b/source/actions.h @@ -44,9 +44,11 @@ enum ACTIONS ACTION_LOCAL_CUT, ACTION_LOCAL_COPY, ACTION_LOCAL_PASTE, + ACTION_LOCAL_EDIT, ACTION_REMOTE_CUT, ACTION_REMOTE_COPY, - ACTION_REMOTE_PASTE + ACTION_REMOTE_PASTE, + ACTION_REMOTE_EDIT }; enum OverWriteType diff --git a/source/config.cpp b/source/config.cpp index 9c889a2..524b510 100644 --- a/source/config.cpp +++ b/source/config.cpp @@ -27,6 +27,7 @@ std::map site_settings; PackageUrlInfo install_pkg_url; char favorite_urls[MAX_FAVORITE_URLS][512]; bool auto_delete_tmp_pkg; +int max_edit_file_size; RemoteClient *remoteclient; namespace CONFIG @@ -91,6 +92,9 @@ namespace CONFIG auto_delete_tmp_pkg = ReadBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, true); WriteBool(CONFIG_GLOBAL, CONFIG_AUTO_DELETE_TMP_PKG, auto_delete_tmp_pkg); + max_edit_file_size = ReadInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, MAX_EDIT_FILE_SIZE); + WriteInt(CONFIG_GLOBAL, CONFIG_MAX_EDIT_FILE_SIZE, max_edit_file_size); + for (int i = 0; i < sites.size(); i++) { RemoteSettings setting; diff --git a/source/config.h b/source/config.h index 7cb32fe..eef9d8a 100644 --- a/source/config.h +++ b/source/config.h @@ -12,6 +12,7 @@ #define DATA_PATH "/data/" APP_ID #define CONFIG_INI_FILE DATA_PATH "/config.ini" #define COOKIE_FILE DATA_PATH "/cookies.txt" +#define TMP_EDITOR_FILE DATA_PATH "/tmp_editor.txt" #define CONFIG_GLOBAL "Global" @@ -28,6 +29,7 @@ #define CONFIG_FAVORITE_URLS "favorite_urls" #define MAX_FAVORITE_URLS 30 +#define CONFIG_MAX_EDIT_FILE_SIZE "max_edit_file_size" #define CONFIG_LAST_SITE "last_site" #define CONFIG_AUTO_DELETE_TMP_PKG "auto_delete_tmp_pkg" @@ -42,6 +44,8 @@ #define HTTP_SERVER_NGINX "Nginx" #define HTTP_SERVER_NPX_SERVE "Serve" +#define MAX_EDIT_FILE_SIZE 262144 + struct RemoteSettings { char site_name[32]; @@ -76,6 +80,7 @@ extern RemoteClient *remoteclient; extern PackageUrlInfo install_pkg_url; extern char favorite_urls[MAX_FAVORITE_URLS][512]; extern bool auto_delete_tmp_pkg; +extern int max_edit_file_size; namespace CONFIG { diff --git a/source/fs.cpp b/source/fs.cpp index c4b3dce..69e574d 100644 --- a/source/fs.cpp +++ b/source/fs.cpp @@ -150,6 +150,67 @@ namespace FS return data; } + bool LoadText(std::vector *lines, const std::string &path) + { + FILE *fd = fopen(path.c_str(), "r"); + if (fd == nullptr) + return false; + + lines->clear(); + + char buffer[1024]; + short bytes_read; + std::vector line = std::vector(0); + do + { + bytes_read = fread(buffer, sizeof(char), 1024, fd); + if (bytes_read < 0) + { + fclose(fd); + return false; + } + + for (short i = 0; i < bytes_read; i++) + { + if (buffer[i] != '\r' && buffer[i] != '\n') + { + line.push_back(buffer[i]); + } + else + { + lines->push_back(std::string(line.data(), line.size())); + line = std::vector(0); + if (buffer[i] == '\r' && buffer[i+1] == '\n') + i++; + } + } + } while (bytes_read == 1024); + if (line.size()>0) + lines->push_back(std::string(line.data(), line.size())); + + fclose(fd); + + return true; + } + + bool SaveText(std::vector *lines, const std::string &path) + { + FILE *fd = OpenRW(path); + if (fd == nullptr) + return false; + + char nl[1] = {'\n'}; + for (int i=0; i < lines->size(); i++) + { + Write(fd, lines->at(i).c_str(), lines->at(i).length()); + Write(fd, nl, 1); + } + + fclose(fd); + + return true; + } + void Save(const std::string &path, const void *data, uint32_t size) { FILE *fd = fopen(path.c_str(), "w+"); diff --git a/source/fs.h b/source/fs.h index 50cbab2..04649bb 100644 --- a/source/fs.h +++ b/source/fs.h @@ -51,6 +51,9 @@ namespace FS int Write(FILE *f, const void *buffer, uint32_t size); std::vector Load(const std::string &path); + bool LoadText(std::vector *lines, const std::string &path); + bool SaveText(std::vector *lines, const std::string &path); + void Save(const std::string &path, const void *data, uint32_t size); std::vector ListFiles(const std::string &path); diff --git a/source/gui.cpp b/source/gui.cpp index 0e814fc..791adec 100644 --- a/source/gui.cpp +++ b/source/gui.cpp @@ -23,7 +23,7 @@ namespace GUI { ImGui_ImplSDL2_ProcessEvent(&event); } - + GImGui->GcCompactAll = true; ImGui_ImplSDLRenderer_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); diff --git a/source/ime_dialog.cpp b/source/ime_dialog.cpp index 3bfca73..d328877 100644 --- a/source/ime_dialog.cpp +++ b/source/ime_dialog.cpp @@ -10,9 +10,9 @@ #include "ime_dialog.h" static int ime_dialog_running = 0; -static uint16_t inputTextBuffer[512+1]; -static uint8_t storebuffer[512]; -static char initial_ime_text[512]; +static uint16_t inputTextBuffer[1024+1]; +static uint8_t storebuffer[1024]; +static char initial_ime_text[1024]; static int max_text_length; static void utf16_to_utf8(const uint16_t *src, uint8_t *dst) @@ -83,7 +83,7 @@ namespace Dialog uint16_t title[100]; - if ((initialTextBuffer && strlen(initialTextBuffer) > 511) || (Title && strlen(Title) > 99)) + if ((initialTextBuffer && strlen(initialTextBuffer) > 1023) || (Title && strlen(Title) > 99)) { ime_dialog_running = 0; return -1; @@ -95,7 +95,7 @@ namespace Dialog if (initialTextBuffer) { - snprintf(initial_ime_text, 511, "%s", initialTextBuffer); + snprintf(initial_ime_text, 1023, "%s", initialTextBuffer); } // converts the multibyte string src to a wide-character string starting at dest. diff --git a/source/lang.cpp b/source/lang.cpp index 6925912..3b73b6b 100644 --- a/source/lang.cpp +++ b/source/lang.cpp @@ -129,6 +129,12 @@ char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = { "This option enables Remote Package Installation. " "This requires the Server with anonymous access that does not need username/password.", // STR_ENABLE_RPI_WEBDAV_MSG "Files", // STR_FILES + "Editor", // STR_EDITOR + "Save", // STR_SAVE + "Cannot edit files bigger than", // STR_MAX_EDIT_FILE_SIZE_MSG + "Delete Selected Line", // STR_DELETE_LINE + "Insert Below Selected Line", // STR_INSERT_LINE + "Modified", // STR_MODIFIED }; bool needs_extended_font = false; diff --git a/source/lang.h b/source/lang.h index b82e21e..dc8e5bc 100644 --- a/source/lang.h +++ b/source/lang.h @@ -120,7 +120,13 @@ FUNC(STR_ENABLE_RPI) \ FUNC(STR_ENABLE_RPI_FTP_SMB_MSG) \ FUNC(STR_ENABLE_RPI_WEBDAV_MSG) \ - FUNC(STR_FILES) + FUNC(STR_FILES) \ + FUNC(STR_EDITOR) \ + FUNC(STR_SAVE) \ + FUNC(STR_MAX_EDIT_FILE_SIZE_MSG) \ + FUNC(STR_DELETE_LINE) \ + FUNC(STR_INSERT_LINE) \ + FUNC(STR_MODIFIED) #define GET_VALUE(x) x, #define GET_STRING(x) #x, @@ -130,7 +136,7 @@ enum FOREACH_STR(GET_VALUE) }; -#define LANG_STRINGS_NUM 117 +#define LANG_STRINGS_NUM 123 #define LANG_ID_SIZE 64 #define LANG_STR_SIZE 384 extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE]; diff --git a/source/main.cpp b/source/main.cpp index 3f83406..eb747c0 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -11,7 +11,7 @@ #include #include #include -// #include +#include #include "imgui.h" #include "SDL2/SDL.h" @@ -118,6 +118,10 @@ void InitImgui() 0xF56F, 0xF56F, // compress 0xF0F6, 0xF0F6, // properties 0xF112, 0xF112, // cancel + 0xF0DA, 0xF0DA, // arrow right + 0x0031, 0x0031, // 1 + 0x004C, 0x004C, // L + 0x0052, 0x0052, // R 0, }; @@ -240,7 +244,7 @@ void InitImgui() colors[ImGuiCol_PlotHistogramHovered] = panelHoverColor; colors[ImGuiCol_ModalWindowDimBg] = bgColorBlur; colors[ImGuiCol_DragDropTarget] = bgColor; - colors[ImGuiCol_NavHighlight] = bgColor; + colors[ImGuiCol_NavHighlight] = titleColor; colors[ImGuiCol_Tab] = bgColor; colors[ImGuiCol_TabActive] = panelActiveColor; colors[ImGuiCol_TabUnfocused] = bgColor; @@ -257,8 +261,8 @@ static void terminate() int main() { - // dbglogger_init(); - // dbglogger_log("If you see this you've set up dbglogger correctly."); + dbglogger_init(); + dbglogger_log("If you see this you've set up dbglogger correctly."); int rc; // No buffering setvbuf(stdout, NULL, _IONBF, 0); diff --git a/source/remote_client.h b/source/remote_client.h index eae4dfd..22b0fa6 100644 --- a/source/remote_client.h +++ b/source/remote_client.h @@ -17,7 +17,8 @@ enum RemoteActions REMOTE_ACTION_DOWNLOAD = 64, REMOTE_ACTION_UPLOAD = 128, REMOTE_ACTION_INSTALL = 256, - REMOTE_ACTION_ALL = 511 + REMOTE_ACTION_EDIT = 512, + REMOTE_ACTION_ALL = 1023 }; enum ClientType diff --git a/source/windows.cpp b/source/windows.cpp index f160260..47cd704 100644 --- a/source/windows.cpp +++ b/source/windows.cpp @@ -64,8 +64,18 @@ bool select_url_inprogress = false; int favorite_url_idx = 0; char extract_zip_folder[256]; char zip_file_path[384]; -char label[256]; +// Editor variables +std::vector edit_buffer; +bool editor_inprogress = false; +char edit_line[1024]; +int edit_line_num = 0; +char label[256]; +bool editor_modified = false; +char edit_file[256]; +int edit_line_to_select = -1; + +// Overwrite dialog variables bool dont_prompt_overwrite = false; bool dont_prompt_overwrite_cb = false; int confirm_transfer_state = -1; @@ -789,7 +799,7 @@ namespace Windows { ImGui::SetNextWindowPos(ImVec2(1330, 300)); } - ImGui::SetNextWindowSizeConstraints(ImVec2(230, 150), ImVec2(230, 550), NULL, NULL); + ImGui::SetNextWindowSizeConstraints(ImVec2(230, 150), ImVec2(230, 600), NULL, NULL); if (ImGui::BeginPopupModal(lang_strings[STR_ACTIONS], NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::PushID("Select All##settings"); @@ -922,6 +932,46 @@ namespace Windows ImGui::PopID(); ImGui::Separator(); + ImGui::PushID("Edit##settings"); + flags = ImGuiSelectableFlags_None; + if (remote_browser_selected && remoteclient != nullptr && !(remoteclient->SupportedActions() & REMOTE_ACTION_EDIT)) + { + flags = ImGuiSelectableFlags_Disabled; + } + if (ImGui::Selectable(lang_strings[STR_EDIT], false, flags | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0))) + { + bool can_edit = true; + if (local_browser_selected) + { + if (selected_local_file.file_size > max_edit_file_size) + can_edit = false; + else + { + snprintf(edit_file, 255, "%s", selected_local_file.path); + FS::LoadText(&edit_buffer, selected_local_file.path); + } + } + else + { + if (selected_remote_file.file_size > max_edit_file_size) + can_edit = false; + else if (remoteclient != nullptr && remoteclient->Get(TMP_EDITOR_FILE, selected_remote_file.path)) + { + snprintf(edit_file, 255, "%s", selected_remote_file.path); + FS::LoadText(&edit_buffer, TMP_EDITOR_FILE); + } + } + if (can_edit) + editor_inprogress = true; + else + sprintf(status_message, "%s %d", lang_strings[STR_MAX_EDIT_FILE_SIZE_MSG], max_edit_file_size); + editor_modified = false; + SetModalMode(false); + ImGui::CloseCurrentPopup(); + } + ImGui::PopID(); + ImGui::Separator(); + if (local_browser_selected) { ImGui::PushID("Extract##settings"); @@ -1329,6 +1379,131 @@ namespace Windows } } + void ShowEditorDialog() + { + if (editor_inprogress) + { + ImGuiIO &io = ImGui::GetIO(); + (void)io; + ImGuiStyle *style = &ImGui::GetStyle(); + ImVec4 *colors = style->Colors; + + SetModalMode(true); + ImGui::OpenPopup(lang_strings[STR_EDITOR]); + + ImGui::SetNextWindowPos(ImVec2(320, 115)); + ImGui::SetNextWindowSizeConstraints(ImVec2(1280, 80), ImVec2(1280, 850), NULL, NULL); + if (ImGui::BeginPopupModal(lang_strings[STR_EDITOR], NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImVec2 cur_pos = ImGui::GetCursorPos(); + char id[128]; + sprintf(id, "%s##editor", lang_strings[STR_CANCEL]); + if (ImGui::Button(id, ImVec2(635, 0))) + { + editor_inprogress = false; + SetModalMode(false); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + sprintf(id, "%s##editor", lang_strings[STR_SAVE]); + if (ImGui::Button(id, ImVec2(635, 0))) + { + bool local_browser_selected = saved_selected_browser & LOCAL_BROWSER; + bool remote_browser_selected = saved_selected_browser & REMOTE_BROWSER; + if (local_browser_selected) + { + FS::SaveText(&edit_buffer, selected_local_file.path); + selected_action = ACTION_REFRESH_LOCAL_FILES; + } + else + { + FS::SaveText(&edit_buffer, TMP_EDITOR_FILE); + if (remoteclient != nullptr) + { + remoteclient->Put(TMP_EDITOR_FILE, selected_remote_file.path); + selected_action = ACTION_REFRESH_REMOTE_FILES; + } + } + editor_inprogress = false; + SetModalMode(false); + ImGui::CloseCurrentPopup(); + } + + ImGui::Separator(); + ImGui::BeginChild("Editor##ChildWindow", ImVec2(1275, 680)); + int j = 0; + static int insert_item = -1; + for (std::vector::iterator it = edit_buffer.begin(); it != edit_buffer.end(); it++) + { + ImGui::Text("%s", ICON_FA_CARET_RIGHT); + ImGui::SameLine(); + + sprintf(id, "%d##editor", j); + ImGui::PushID(id); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 1.0f)); + if (ImGui::Selectable(it->c_str(), false, ImGuiSelectableFlags_DontClosePopups, ImVec2(1275, 0))) + { + edit_line_num = j; + snprintf(edit_line, 1023, "%s", it->c_str()); + ResetImeCallbacks(); + ime_single_field = edit_line; + ime_field_size = 1023; + ime_after_update = AfterEditorCallback; + ime_callback = SingleValueImeCallback; + Dialog::initImeDialog(lang_strings[STR_EDIT], edit_line, 1023, ORBIS_TYPE_BASIC_LATIN, 420, 290); + gui_mode = GUI_MODE_IME; + } + ImGui::PopStyleVar(); + ImGui::PopID(); + if ((gui_mode != GUI_MODE_IME && j == edit_line_num) || edit_line_to_select == j) + { + SetNavFocusHere(); + edit_line_num = -1; + edit_line_to_select = -1; + } + if (ImGui::IsItemHovered()) + { + if (ImGui::CalcTextSize(it->c_str()).x > 1275) + { + ImGui::BeginTooltip(); + ImGui::Text("%s", it->c_str()); + ImGui::EndTooltip(); + } + } + if (ImGui::IsItemFocused()) + { + if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1, false)) + { + insert_item = j; + editor_modified = true; + } + else if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1, false)) + { + edit_buffer.erase(it--); + editor_modified = true; + edit_line_to_select = j; + } + } + j++; + } + if (insert_item > -1) + { + if (insert_item == edit_buffer.size() - 1) + edit_buffer.push_back(std::string()); + else + edit_buffer.insert(edit_buffer.begin() + insert_item + 1, std::string()); + } + insert_item = -1; + ImGui::EndChild(); + + ImGui::Text("%s%s", (editor_modified? "**" : ""), edit_file); + ImGui::Separator(); + ImGui::Text("L1 - %s R1 - %s", lang_strings[STR_DELETE_LINE], lang_strings[STR_INSERT_LINE]); + ImGui::EndPopup(); + } + } + } + void MainWindow() { Windows::SetupWindow(); @@ -1346,6 +1521,7 @@ namespace Windows ShowProgressDialog(); ShowActionsDialog(); ShowFavoriteUrlsDialog(); + ShowEditorDialog(); } ImGui::End(); } @@ -1626,7 +1802,6 @@ namespace Windows { ime_callback(ime_result); } - if (ime_after_update != nullptr) { ime_after_update(ime_result); @@ -1754,4 +1929,14 @@ namespace Windows remote_settings->http_port = atoi(txt_http_port); } } + + void AfterEditorCallback(int ime_result) + { + if (ime_result == IME_DIALOG_RESULT_FINISHED) + { + std::string str = std::string(edit_line); + edit_buffer[edit_line_num] = str; + editor_modified = true; + } + } } diff --git a/source/windows.h b/source/windows.h index 1d5b836..941f155 100644 --- a/source/windows.h +++ b/source/windows.h @@ -41,6 +41,7 @@ extern ACTIONS action_to_take; extern bool file_transfering; extern char extract_zip_folder[]; extern char zip_file_path[]; +extern std::vector edit_buffer; static ImVector s_GroupPanelLabelStack; @@ -208,6 +209,7 @@ namespace Windows void AfterZipFileCallback(int ime_result); void AferServerChangeCallback(int ime_result); void AfterHttpPortChangeCallback(int ime_result); + void AfterEditorCallback(int ime_result); } #endif diff --git a/source/xt_editor/array.h b/source/xt_editor/array.h deleted file mode 100644 index 0c1ee19..0000000 --- a/source/xt_editor/array.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef ARRAY_H -#define ARRAY_H - -#define ARRAY_INITIAL_CAPACITY 256 - -// note: An array whose length can dynamically change at run-time -template -struct Array { - T& operator[](int Index) { - return Data[Index]; - } - - T* Data; - size_t Capacity; - size_t Index; -}; - -template -inline Array array_init(size_t Capacity = ARRAY_INITIAL_CAPACITY) { - Array Result; - - Result.Data = (T*)malloc(Capacity * sizeof(T)); - Result.Capacity = Capacity; - Result.Index = 0; - - return Result; -} - -template -inline void array_add(Array* _Array, T Value) { - if (_Array->Index >= _Array->Capacity) { - _Array->Capacity *= 2; - _Array->Data = (T*)realloc(_Array->Data, _Array->Capacity * sizeof(T)); - } - - _Array->Data[_Array->Index] = Value; - _Array->Index++; -} - -#endif \ No newline at end of file diff --git a/source/xt_editor/editor.cpp b/source/xt_editor/editor.cpp deleted file mode 100644 index c115542..0000000 --- a/source/xt_editor/editor.cpp +++ /dev/null @@ -1,385 +0,0 @@ -#include "xt_editor/types.h" -#include "xt_editor/editor.h" -struct EditorRow { - char* Chars; - size_t Size; -}; - -struct EditorState { - int CPosX; - int CPosY; - - int Rows; - int Columns; - - EditorRow* Row; - int RowCount; - int CharCount; - - bool IsFileDirty; - char* FileName; -}; - -enum CursorStyle_ { - CursorStyle_Block, - CursorStyle_Block_Outline, - CursorStyle_Line, - CursorStyle_Underline, -}; - -typedef int CursorStyle; - -struct EditorConfig { - CursorStyle Style; - bool LineBlink; -}; - -static EditorState State; -static EditorConfig Config; -static bool IsInitialized = false; -static int TextStart = 7; -static char LeftBuffer[16]; -static float BlinkStart = 0; -static float BlinkEnd = 0; - -#ifdef BUILD_WIN32 -#include "editor_input_win32.cpp" -#else -void Editor_HandleInput() -{}; -#endif - - -void Editor_OpenFile(char* Filename) { - State.FileName = Filename; - State.IsFileDirty = false; - - FILE* File = fopen(Filename, "r"); - if (!File) - return; - - char* Line = 0; - size_t LineCapacity = 0; - ssize_t LineLength; - while ((LineLength = getline(&Line, &LineCapacity, File)) != -1) { - while (LineLength > 0 && (Line[LineLength - 1] == '\n' || Line[LineLength - 1] == '\r')) { - LineLength--; - Editor_AppendRow(Line, LineLength); - State.CharCount += LineLength; - } - } - free(Line); - fclose(File); -} - -void Editor_Init() { - State.CPosX = 0; - State.CPosY = 0; - - ImVec2 WindowSize = ImGui::GetWindowContentRegionMax(); - float FontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; - ImVec2 CharAdvance = ImVec2(FontSize, ImGui::GetTextLineHeightWithSpacing() * 1.0f); - - State.Rows = WindowSize.y / CharAdvance.y; - State.Columns = WindowSize.x / CharAdvance.x; - - State.Row = (EditorRow*)malloc(sizeof(EditorRow)); - - Editor_OpenFile("../src/editor.cpp"); - - IsInitialized = true; - - Config.Style = CursorStyle_Block; - Config.LineBlink = true; -} -// https://en.wikipedia.org/wiki/UTF-8 -// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code) -static int UTF8CharLength(char c) { - if ((c & 0xFE) == 0xFC) - return 6; - if ((c & 0xFC) == 0xF8) - return 5; - if ((c & 0xF8) == 0xF0) - return 4; - else if ((c & 0xF0) == 0xE0) - return 3; - else if ((c & 0xE0) == 0xC0) - return 2; - return 1; -} - -int Editor_GetCharacterIndexByCursor(int X, int Y) { - int Index = 0; - int Column = 0; - EditorRow* Line = &State.Row[Y]; - if (Line == 0) - return 0; - - for (; Index < Line->Size && Column < X;) { - Index += UTF8CharLength(Line->Chars[Index]); - ++Column; - } - - return Index; -} - -void Editor_RenderRows(ImVec2 WindowSize, ImVec2 Pos) { - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyle().Colors[ImGuiCol_FrameBg]); - //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - ImGui::BeginChild("Editor", WindowSize, true); - ImGui::PushAllowKeyboardFocus(true); - - // Handle input here or else we can't grab the childs input - Editor_HandleInput(); - - if (State.CPosX < 0) - State.CPosX = 0; - - if (State.CPosY < 0) - State.CPosY = 0; - - static float FontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; // Get the size of the tallest char - ImVec2 CharAdvance = ImVec2(FontSize, ImGui::GetTextLineHeightWithSpacing() * 1.0f); - - bool Focused = ImGui::IsWindowFocused(); - float ScrollX = ImGui::GetScrollX(); - float ScrollY = ImGui::GetScrollY(); - - int LineNum = (int)floor(ScrollY / CharAdvance.y); - int LineMax = Maximum(0, Minimum(State.RowCount - 1, LineNum + (int)floor((ScrollY + WindowSize.y) / CharAdvance.y))); - - int ActualTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x + TextStart; - - ImDrawList* Draw = ImGui::GetWindowDrawList(); - - while (LineNum <= LineMax) { - ImVec2 LineStartPos = ImVec2(Pos.x, Pos.y + LineNum * CharAdvance.y); - ImVec2 TextPos = ImVec2(LineStartPos.x + ActualTextStart, LineStartPos.y); - - snprintf(LeftBuffer, 16, "%*d ", (TextStart - 1), LineNum + 1); - int LineNumWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x; - Draw->AddText(ImVec2(LineStartPos.x, LineStartPos.y), IM_COL32(255, 255, 255, 255), LeftBuffer); - - ImVec2 Start = ImVec2(LineStartPos.x + ScrollX, LineStartPos.y); - ImVec2 End = ImVec2(Start.x + ScrollX + WindowSize.x, Start.y + CharAdvance.y); - Draw->AddRectFilled(Start, End, 0x141414); - Draw->AddRect(Start, End, 0x40808080, 1.0f); - - EditorRow* Row = &State.Row[LineNum]; - - size_t Len = Row->Size; - if (Len > State.Columns) { - Len = State.Columns; - } - - if (Config.Style != (CursorStyle_Line)) - Draw->AddText(TextPos, IM_COL32(255, 255, 255, 255), Row->Chars); - - // Draw the cursor - if (State.CPosY == LineNum && Focused) { - float CursorWidth = CharAdvance.x; - if (Config.Style == CursorStyle_Line || Config.Style == CursorStyle_Underline) - CursorWidth = 1.f; - - int Index = Editor_GetCharacterIndexByCursor(State.CPosX, State.CPosY); - int ScaledCurX = (Index * CharAdvance.x); - int ScaledCurY = (State.CPosY * CharAdvance.y); - ImVec2 TextStartPos = ImVec2(Pos.x + ActualTextStart, Pos.y); - - ImVec2 CursorStart, CursorEnd; - if (Config.Style == CursorStyle_Underline) { // We are doing underline style - CursorStart = ImVec2(TextStartPos.x + ScaledCurX, ((ScaledCurY + TextStartPos.y + CharAdvance.y) - CursorWidth) - 1); - CursorEnd = ImVec2(TextStartPos.x + ScaledCurX + CharAdvance.x, (ScaledCurY + TextStartPos.y + CharAdvance.y) - 1); - } else { - CursorStart = ImVec2(TextStartPos.x + ScaledCurX, ScaledCurY + TextStartPos.y); - CursorEnd = ImVec2(TextStartPos.x + ScaledCurX + CursorWidth, ScaledCurY + TextStartPos.y + CharAdvance.y); - } - - BlinkEnd++; - float Elapsed = (BlinkEnd - BlinkStart); - - static int OldCPosX = 0; - static int OldCPosY = 0; - if ((OldCPosX != State.CPosX || OldCPosY != State.CPosY) || Config.LineBlink == false) { - // Constantly render the cursor if we're in motion - (Config.Style == CursorStyle_Block_Outline) ? Draw->AddRect(CursorStart, CursorEnd, 0xffffffff, 1.0f) : Draw->AddRectFilled(CursorStart, CursorEnd, 0xffffffff); - - // Draw the char of text at the cursors location in the opposite color - char* Char = (char*)malloc(sizeof(char) * 1); - Char[0] = Row->Chars[Index]; - Draw->AddText(CursorStart, IM_COL32(0, 0, 0, 255), Char); - } else { - // Blink the cursor rendering - static float InitStart = 108; - if (Elapsed > InitStart) { - (Config.Style == CursorStyle_Block_Outline) ? Draw->AddRect(CursorStart, CursorEnd, 0xffffffff, 1.0f) : Draw->AddRectFilled(CursorStart, CursorEnd, 0xffffffff); - - // Draw the char of text at the cursors location in the opposite color - char* Char = (char*)malloc(sizeof(char) * 1); - Char[0] = Row->Chars[Index]; - Draw->AddText(CursorStart, IM_COL32(0, 0, 0, 255), Char); - - if (Elapsed > (InitStart + 40)) - BlinkStart = BlinkEnd; - } - } - - OldCPosX = State.CPosX; - OldCPosY = State.CPosY; - } - - // AddRect doesn't allow for a transparent rectangle so we need to write the character again :/ - // When we use CursorStyle_Line the original text gets overwritten in a similar way. - if (Config.Style == CursorStyle_Block_Outline || Config.Style == CursorStyle_Line) - Draw->AddText(TextPos, IM_COL32(255, 255, 255, 255), Row->Chars); - - { - // DEBUG DRAW: Cursor position and index - int Index = Editor_GetCharacterIndexByCursor(State.CPosX, State.CPosY); - char Temp[64]; - sprintf(Temp, "%d, %d, %d", State.CPosX, State.CPosY, Index); - Draw->AddText(ImVec2(Pos.x + 550, Pos.y), IM_COL32(255, 255, 255, 255), Temp); - } - - LineNum++; - } - - while (LineNum >= LineMax) { - ImVec2 LineStartPos = ImVec2(Pos.x, Pos.y + LineNum * CharAdvance.y); - ImVec2 TextPos = ImVec2(LineStartPos.x + CharAdvance.x * (TextStart + 1), LineStartPos.y); - - snprintf(LeftBuffer, 16, "%*s ", (TextStart - 1), "~"); - int LineNumWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, LeftBuffer, nullptr, nullptr).x; - Draw->AddText(ImVec2(LineStartPos.x, LineStartPos.y), IM_COL32(255, 255, 255, 255), LeftBuffer); - - ImVec2 Start = ImVec2(LineStartPos.x + ScrollX, LineStartPos.y); - ImVec2 End = ImVec2(Start.x + ScrollX + WindowSize.x, Start.y + CharAdvance.y); - Draw->AddRectFilled(Start, End, 0x141414); - Draw->AddRect(Start, End, 0x40808080, 1.0f); - - LineNum++; - - if (LineNum >= (LineMax)) break; // Impose some limit so that we don't render infinitly - } - - ImGui::PopAllowKeyboardFocus(); - ImGui::EndChild(); - //ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - - ImGui::Text("xt editor -- \"%s\"%s %2s %dL %dC %8s (%d, %d)", State.FileName, (State.IsFileDirty) ? "*" : "", "", State.RowCount, State.CharCount, "", State.CPosX, State.CPosY); -} - -void Editor_AppendRow(char* String, size_t Length) { - State.Row = (EditorRow*)realloc(State.Row, sizeof(EditorRow) * (State.RowCount + 1)); - - int At = State.RowCount; - State.Row[At].Size = Length; - State.Row[At].Chars = (char*)malloc(Length + 1); - memcpy(State.Row[At].Chars, String, Length); - State.Row[At].Chars[Length] = 0; - State.RowCount++; -} - -bool StringsMatch(char* A, char* B) { - while (*A && *B) { - if (*A != *B){ - return false; - } - - ++A; - ++B; - } - - if (*A != *B){ - return false; - } else { - return true; - } -} - -int StringLength(const char* String) { - int Count = 0; - while (*String++) { - ++Count; - } - return Count; -} - -char* StringCopy(const char* String) { - char* Result = (char*)malloc(sizeof(char) * (StringLength(String) + 1)); - memcpy(Result, String, sizeof(char) * (StringLength(String) + 1)); - - return Result; -} - -void Editor_Render() { - ImGui::Begin("xt Demo Panel", nullptr, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar); - ImGui::SetWindowSize(ImVec2(1280, 720), ImGuiCond_FirstUseEver); - - if (!IsInitialized) { - Editor_Init(); - } - if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Quit", "Alt-F4, CTRL-Q")) { - exit(0); - } - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("View")) { - if (ImGui::MenuItem("Cursor Blink", NULL, &Config.LineBlink)) { - //ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true); - //ImGui::PopItemFlag(); - } - - { - const char* Items[] = { "Block", "Block Outline", "Line", "Underline" }; - static char* CurrentItem = NULL; - ImGuiComboFlags flags = ImGuiComboFlags_NoArrowButton; - - ImGuiStyle& Style = ImGui::GetStyle(); - - ImGui::Text("Cursor Style"); - ImGui::SameLine(0, Style.ItemInnerSpacing.x); - ImGui::PushItemWidth(ImGui::CalcItemWidth() - Style.ItemInnerSpacing.x- ImGui::GetFrameHeight()); - if (ImGui::BeginCombo("##custom combo", CurrentItem, ImGuiComboFlags_NoArrowButton)) { - for (int n = 0; n < IM_ARRAYSIZE(Items); n++) { - bool is_selected = (CurrentItem == Items[n]); - if (ImGui::Selectable(Items[n], is_selected)) { - CurrentItem = StringCopy(Items[n]); - if (StringsMatch(CurrentItem, "Block")) { - Config.Style = CursorStyle_Block; - } - else if (StringsMatch(CurrentItem, "Block Outline")) { - Config.Style = CursorStyle_Block_Outline; - } - else if (StringsMatch(CurrentItem, "Line")) { - Config.Style = CursorStyle_Line; - } - else if (StringsMatch(CurrentItem, "Underline")) { - Config.Style = CursorStyle_Underline; - } - } - } - ImGui::EndCombo(); - } - ImGui::PopItemWidth(); - } - - ImGui::EndMenu(); - } - - ImGui::EndMenuBar(); - } - - ImVec2 WindowSize = ImGui::GetWindowContentRegionMax(); - WindowSize.y -= 215; - ImVec2 Pos = ImGui::GetCursorScreenPos(); - - Editor_RenderRows(WindowSize, Pos); - - ImGui::End(); -} \ No newline at end of file diff --git a/source/xt_editor/editor.h b/source/xt_editor/editor.h deleted file mode 100644 index 95d1bd6..0000000 --- a/source/xt_editor/editor.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef EDITOR_H -#define EDITOR_H -#include "imgui.h" - -void Editor_Init(); -void Editor_AppendRow(char* String, size_t Length); -void Editor_RenderRows(ImVec2 WindowSize, ImVec2 Pos); -void Editor_Render(); - -#endif \ No newline at end of file diff --git a/source/xt_editor/getline.c b/source/xt_editor/getline.c deleted file mode 100644 index f6c154f..0000000 --- a/source/xt_editor/getline.c +++ /dev/null @@ -1,56 +0,0 @@ -// getline() is written to POSIX spec so that it can be used on Windows platforms -// This is taken from a stackexchange answer, link has since been lost. -#include -#include -#include -#include - -typedef intptr_t ssize_t; - -ssize_t getline(char** lineptr, size_t* n, FILE* stream) { - size_t pos; - int c; - - if (lineptr == NULL || stream == NULL || n == NULL) { - errno = EINVAL; - return -1; - } - - c = getc(stream); - if (c == EOF) { - return -1; - } - - if (*lineptr == NULL) { - *lineptr = (char*)malloc(128); - if (*lineptr == NULL) { - return -1; - } - *n = 128; - } - - pos = 0; - while (c != EOF) { - if (pos + 1 >= *n) { - size_t new_size = *n + (*n >> 2); - if (new_size < 128) { - new_size = 128; - } - char* new_ptr = (char*)realloc(*lineptr, new_size); - if (new_ptr == NULL) { - return -1; - } - *n = new_size; - *lineptr = new_ptr; - } - - ((unsigned char*)(*lineptr))[pos++] = c; - if (c == '\n') { - break; - } - c = getc(stream); - } - - (*lineptr)[pos] = '\0'; - return pos; -} \ No newline at end of file diff --git a/source/xt_editor/types.h b/source/xt_editor/types.h deleted file mode 100644 index 24f18e0..0000000 --- a/source/xt_editor/types.h +++ /dev/null @@ -1,215 +0,0 @@ -#ifndef TYPES_H -#define TYPES_H - -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_WIN32 // MSVC -#define API_EXPORT extern "C" __declspec(dllexport) -#define API_IMPORT extern "C" __declspec(dllimport) -#elif BUILD_LINUX | BUILD_MACOS // GCC or Clang -#define API_EXPORT extern "C" __attribute__((visibility("default"))) -#define API_IMPORT -#endif - -#define global static - -typedef unsigned int uint; - -typedef int8_t int8; -typedef int16_t int16; -typedef int32_t int32; -typedef int64_t int64; -typedef int32 bool32; - -typedef uint8_t uint8; -typedef uint16_t uint16; -typedef uint32_t uint32; -typedef uint64_t uint64; - -typedef float real32; -typedef double real64; - -typedef int8 s8; -typedef int16 s16; -typedef int32 s32; -typedef int64 s64; -typedef bool32 b32; - -typedef uint8 u8; -typedef uint16 u16; -typedef uint32 u32; -typedef uint64 u64; - -typedef real32 f32; -typedef real64 f64; - -typedef uintptr_t umm; -typedef intptr_t smm; - -#define S32Min ((s32)0x80000000) -#define S32Max ((s32)0x7fffffff) -#define U16Max 65535 -#define U32Max ((u32)-1) -#define U64Max ((u64)-1) -#define F32Max FLT_MAX -#define F32Min -FLT_MAX - -#define Minimum(A, B) ((A < B) ? (A) : (B)) -#define Maximum(A, B) ((A > B) ? (A) : (B)) - -#define For(Value) For_e((Value), ArrayCount(Value)) -#define For_e(Value, End) For_se((Value), 0, (End)) -#define For_se(Value, Start, End) for (int (Value) = (Start); (Value) < (End); ++(Value)) - -// todo(jax): Should these always be 64-bit? -#define Kilobytes(Value) (((uint64)Value) * 1024LL) -#define Megabytes(Value) (Kilobytes((uint64)Value) * 1024LL) -#define Gigabytes(Value) (Megabytes((uint64)Value) * 1024LL) -#define Terabytes(Value) (Gigabytes((uint64)Value) * 1024LL) - -#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0])) -// todo(jax): swap, min, max ... macros??? - -#define AlignPow2(Value, Alignment) ((Value + ((Alignment) - 1)) & ~((Alignment) - 1)) -#define Align4(Value) ((Value + 3) & ~3) -#define Align8(Value) ((Value + 7) & ~7) -#define Align16(Value) ((Value + 15) & ~15) - -#define Stringize(x) PrimitiveStringize(x) -#define PrimitiveStringize(x) #x - -inline b32 IsPow2(u32 Value) { - return ((Value & ~(Value - 1)) == Value); -} - -// ANSI Color Codes -#define BLACK "\33[0;30m" -#define RED "\33[0;31m" -#define GREEN "\33[0;32m" -#define YELLOW "\33[0;33m" -#define BLUE "\33[0;34m" -#define MAGENTA "\33[0;35m" -#define CYAN "\33[0;36m" -#define WHITE "\33[0;37m" -#define LIGHT_GRAY "\33[0;37m" -#define DARK_GRAY "\33[1;30m" -#define BOLD_BLACK "\33[1;30m" -#define BOLD_RED "\33[1;31m" -#define BOLD_GREEN "\33[1;32m" -#define BOLD_YELLOW "\33[1;33m" -#define BOLD_BLUE "\33[1;34m" -#define BOLD_MAGENTA "\33[1;35m" -#define BOLD_CYAN "\33[1;36m" -#define BOLD_WHITE "\33[1;37m" -#define RESET "\33[0m" - -// note(jax): Platform-independent way to perform an assertion. -// Flat out writes to zero memory to crash the program. -// todo(jax): Create some sort of assert function that creates a message box -// like in previous engines I've worked on! -#if ENGINE_SLOW -#define Assert(Expression) if (!(Expression)) { *(int*)0=0; } -#else -#define Assert(Expression) -#endif - -#define InvalidCodePath Assert(!"InvalidCodePath") - -// A structure that encapsulates a non-terminated buffer -struct string { - u8* Data; - umm Count; -}; - -inline u32 SafeTruncateU32(u64 Value) { - // todo(jax): Defines for min/max values - Assert(Value <= 0xFFFFFFFF); - u32 Result = (u32)Value; - return Result; -} - -inline u16 SafeTruncateU16(u32 Value) { - // todo(jax): Defines for min/max values - Assert(Value <= 0xFFFF); - u16 Result = (u16)Value; - return Result; -} - -inline u8 SafeTruncateU8(u64 Value) { - Assert(Value <= 0xFF); - u8 Result = (u8)Value; - return Result; -} - -inline s32 SafeTruncateS32(s64 Value) { - if (Value >> 63) { - b32 IsSafeOperation = !(!(Value >> 32) && 0xffffffff); - if (!IsSafeOperation) { - printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value); - } - return (s32)Value; - } else { - b32 IsSafeOperation = !((Value >> 32) && 0xffffffff); - if (!IsSafeOperation) { - printf("SafeTruncateS32: Performing unsafe truncation on '%lld'\n", Value); - } - return (s32)Value; - } -} - -inline s16 SafeTruncateS16(s32 Value) { - if (Value >> 31) { - b32 IsSafeOperation = !(!(Value >> 16) && 0xffff); - if (!IsSafeOperation) { - printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value); - } - return (s16)Value; - } else { - b32 IsSafeOperation = !((Value >> 16) && 0xffff); - if (!IsSafeOperation) { - printf("SafeTruncateS16: Performing unsafe truncation on '%d'\n", Value); - } - return (s16)Value; - } -} - -inline s8 SafeTruncateS8(s16 Value) { - if (Value >> 15) { - b32 IsSafeOperation = !(!(Value >> 8) && 0xff); - if (!IsSafeOperation) { - printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value); - } - return (s8)Value; - } else { - b32 IsSafeOperation = !((Value >> 8) && 0xff); - if (!IsSafeOperation) { - printf("SafeTruncateS8: Performing unsafe truncation on '%d'\n", Value); - } - return (s8)Value; - } -} - -// -// note: Scalar operations -// - -// todo(jax): These will eventually go into mathlib - -inline real32 Square(real32 A) { - real32 Result = A*A; - - return Result; -} - -inline real32 Lerp(real32 A, real32 t, real32 B){ - real32 Result = (1.0f - t)*A + t*B; - - return Result; -} - -#endif \ No newline at end of file