From a407d8c16a0469249b612a21d0ec1d0c57a53710 Mon Sep 17 00:00:00 2001 From: PoliEcho Date: Thu, 6 Mar 2025 19:41:30 +0100 Subject: [PATCH] wide string refactor --- src/helper_funcs.cpp | 165 ++++++++++++++++++++++++++++++++----------- src/helper_funcs.h | 14 ++++ src/main_menu.cpp | 16 +++-- src/marks.cpp | 160 +++++++++++++++++++++-------------------- src/timetable.cpp | 8 ++- 5 files changed, 238 insertions(+), 125 deletions(-) diff --git a/src/helper_funcs.cpp b/src/helper_funcs.cpp index 5c27262..80aafa2 100644 --- a/src/helper_funcs.cpp +++ b/src/helper_funcs.cpp @@ -1,14 +1,17 @@ +#include "helper_funcs.h" +#include "color.h" #include "net.h" +#include #include #include #include #include #include #include +#include #include #include #include -#include "color.h" void safe_exit(int code) { switch (code) { @@ -44,45 +47,47 @@ std::string bool_to_string(bool bool_in) { return bool_in ? "true" : "false"; } std::string SoRAuthFile(bool save, std::string data) { - std::string home = std::getenv("HOME"); - if (home.empty()) { - std::cerr << RED "[ERROR] " RESET << "HOME environment variable not set.\n"; - safe_exit(EXIT_FAILURE); + std::string home = std::getenv("HOME"); + if (home.empty()) { + std::cerr << RED "[ERROR] " RESET << "HOME environment variable not set.\n"; + safe_exit(EXIT_FAILURE); + } + + std::string savedir_path = home; + savedir_path.append("/.local/share/bakatui"); + + if (!std::filesystem::exists(savedir_path)) { + if (!std::filesystem::create_directories(savedir_path)) { + std::cerr << RED "[ERROR] " RESET + << "Failed to create directory: " << savedir_path << "\n"; + safe_exit(EXIT_FAILURE); } + } - std::string savedir_path = home; - savedir_path.append("/.local/share/bakatui"); + std::string authfile_path = savedir_path + "/auth"; - - if (!std::filesystem::exists(savedir_path)) { - if (!std::filesystem::create_directories(savedir_path)) { - std::cerr << RED "[ERROR] " RESET << "Failed to create directory: " << savedir_path << "\n"; - safe_exit(EXIT_FAILURE); - } + if (save) { + std::ofstream authfile(authfile_path); + if (!authfile.is_open()) { + std::cerr << RED "[ERROR] " RESET + << "Failed to open auth file for writing.\n"; + safe_exit(EXIT_FAILURE); } - - std::string authfile_path = savedir_path + "/auth"; - - if (save) { - std::ofstream authfile(authfile_path); - if (!authfile.is_open()) { - std::cerr << RED "[ERROR] " RESET << "Failed to open auth file for writing.\n"; - safe_exit(EXIT_FAILURE); - } - authfile << data; - authfile.close(); - return ""; - } else { - std::ifstream authfile(authfile_path); - if (!authfile.is_open()) { - std::cerr << RED "[ERROR] " RESET << "Failed to open auth file for reading.\n"; - safe_exit(EXIT_FAILURE); - } - data.assign((std::istreambuf_iterator(authfile)), - std::istreambuf_iterator()); - authfile.close(); - return data; + authfile << data; + authfile.close(); + return ""; + } else { + std::ifstream authfile(authfile_path); + if (!authfile.is_open()) { + std::cerr << RED "[ERROR] " RESET + << "Failed to open auth file for reading.\n"; + safe_exit(EXIT_FAILURE); } + data.assign((std::istreambuf_iterator(authfile)), + std::istreambuf_iterator()); + authfile.close(); + return data; + } } void get_input_and_login() { @@ -99,6 +104,7 @@ void get_input_and_login() { bakaapi::login(username, password); } +// Original function void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) { int length, x, y; @@ -123,19 +129,92 @@ void print_in_middle(WINDOW *win, int starty, int startx, int width, refresh(); } +// Wide character version +void wprint_in_middle(WINDOW *win, int starty, int startx, int width, + wchar_t *string, chtype color) { + int length, x, y; + float temp; + + if (win == NULL) + win = stdscr; + getyx(win, y, x); + if (startx != 0) + x = startx; + if (starty != 0) + y = starty; + if (width == 0) + width = 80; + + length = wcslen(string); + temp = (width - length) / 2; + x = startx + (int)temp; + wattron(win, color); + mvwaddwstr(win, y, x, string); + wattroff(win, color); + refresh(); +} const std::string WHITESPACE = " \n\r\t\f\v"; - +const std::wstring WWHITESPACE = L" \n\r\t\f\v"; + std::string ltrim(const std::string &s) { - size_t start = s.find_first_not_of(WHITESPACE); - return (start == std::string::npos) ? "" : s.substr(start); + size_t start = s.find_first_not_of(WHITESPACE); + return (start == std::string::npos) ? "" : s.substr(start); } - + std::string rtrim(const std::string &s) { - size_t end = s.find_last_not_of(WHITESPACE); - return (end == std::string::npos) ? "" : s.substr(0, end + 1); + size_t end = s.find_last_not_of(WHITESPACE); + return (end == std::string::npos) ? "" : s.substr(0, end + 1); } - + std::string rm_tr_le_whitespace(const std::string &s) { - return rtrim(ltrim(s)); + return rtrim(ltrim(s)); +} + +// Wide character versions +std::wstring wltrim(const std::wstring &s) { + size_t start = s.find_first_not_of(WWHITESPACE); + return (start == std::wstring::npos) ? L"" : s.substr(start); +} + +std::wstring wrtrim(const std::wstring &s) { + size_t end = s.find_last_not_of(WWHITESPACE); + return (end == std::wstring::npos) ? L"" : s.substr(0, end + 1); +} + +std::wstring wrm_tr_le_whitespace(const std::wstring &s) { + return wrtrim(wltrim(s)); +} + +// Conversion utilities +char *wchar_to_char(wchar_t *src) { + if (!src) + return nullptr; + + size_t len = wcslen(src) + 1; // +1 for null terminator + char *dest = new char[len * MB_CUR_MAX]; + + std::wcstombs(dest, src, len * MB_CUR_MAX); + return dest; +} + +wchar_t *char_to_wchar(char *src) { + if (!src) + return nullptr; + + size_t len = strlen(src) + 1; // +1 for null terminator + wchar_t *dest = new wchar_t[len]; + + std::mbstowcs(dest, src, len); + return dest; +} + +std::wstring string_to_wstring(const std::string &str) { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +std::string wstring_to_string(const std::wstring &wstr) { + std::wstring_convert> converter; + return converter.to_bytes(wstr); } \ No newline at end of file diff --git a/src/helper_funcs.h b/src/helper_funcs.h index 19c3fd8..7df8721 100644 --- a/src/helper_funcs.h +++ b/src/helper_funcs.h @@ -9,8 +9,22 @@ void safe_exit(int code); std::string bool_to_string(bool bool_in); std::string SoRAuthFile(bool save, std::string data); void get_input_and_login(); + +// Original functions void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); std::string rm_tr_le_whitespace(const std::string &s); +// Wide character support functions +void wprint_in_middle(WINDOW *win, int starty, int startx, int width, + wchar_t *string, chtype color); +std::wstring wrm_tr_le_whitespace(const std::wstring &s); + +// Conversion utilities +char *wchar_to_char(wchar_t *src); +wchar_t *char_to_wchar(char *src); + +std::wstring string_to_wstring(const std::string &str); +std::string wstring_to_string(const std::wstring &wstr); + #endif \ No newline at end of file diff --git a/src/main_menu.cpp b/src/main_menu.cpp index 7c5cd34..166f2e9 100644 --- a/src/main_menu.cpp +++ b/src/main_menu.cpp @@ -5,16 +5,18 @@ #include #include #include +#include #include +#include #include "marks.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define CTRLD 4 -char *choices[] = { - "login", "Marks", "timetable", "Komens", - "Homework", "Absence", "Exit", (char *)NULL, +wchar_t *choices[] = { + L"login", L"Marks", L"timetable", L"Komens", + L"Homework", L"Absence", L"Exit", (wchar_t *)NULL, }; void (*choicesFuncs[])() = {nullptr, marks_page, timetable_page, nullptr, nullptr, nullptr, nullptr, nullptr}; @@ -27,6 +29,7 @@ void main_menu() { int n_choices, i; /* Initialize curses */ + setlocale(LC_ALL, ""); initscr(); start_color(); cbreak(); @@ -39,7 +42,8 @@ void main_menu() { n_choices = ARRAY_SIZE(choices); my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); for (i = 0; i < n_choices; ++i) - my_items[i] = new_item(choices[i], choices[i]); + my_items[i] = + new_item(wchar_to_char(choices[i]), wchar_to_char(choices[i])); /* Crate menu */ my_menu = new_menu((ITEM **)my_items); @@ -59,7 +63,7 @@ void main_menu() { /* Print a border around the main window and print a title */ box(my_menu_win, 0, 0); - print_in_middle(my_menu_win, 1, 0, 40, "Main Menu", COLOR_PAIR(1)); + wprint_in_middle(my_menu_win, 1, 0, 40, L"Main Menu", COLOR_PAIR(1)); mvwaddch(my_menu_win, 2, 0, ACS_LTEE); mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); mvwaddch(my_menu_win, 2, 39, ACS_RTEE); @@ -106,4 +110,4 @@ void main_menu() { for (i = 0; i < n_choices; ++i) free_item(my_items[i]); endwin(); -} +} \ No newline at end of file diff --git a/src/marks.cpp b/src/marks.cpp index c55e8cf..211b7bc 100644 --- a/src/marks.cpp +++ b/src/marks.cpp @@ -1,5 +1,7 @@ #include "marks.h" #include "helper_funcs.h" +#include "net.h" +#include #include #include #include @@ -7,40 +9,17 @@ #include #include #include +#include #include #include #include #include -#include -#include "net.h" using nlohmann::json; -// Thsi code is based on +// This code is based on // https://github.com/tony/NCURSES-Programming-HOWTO-examples/blob/master/16-panels -/* -The MIT License (MIT) - -Copyright (c) 2016 Tony Narlock - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ +// MIT License (see original file) #define NLINES 10 #define NCOLS 40 @@ -51,8 +30,8 @@ SOFTWARE. #define DEFAULT_PADDING 4 void init_wins(WINDOW **wins, int n, json marks_json); -void win_show(WINDOW *win, char *label, int label_color, int width, int height, - json marks_json, int SubjectIndex); +void win_show(WINDOW *win, wchar_t *label, int label_color, int width, + int height, json marks_json, int SubjectIndex); void marks_page() { // DONT FORGET TO UNCOMMENT @@ -98,8 +77,8 @@ void marks_page() { // Attach panels for (size_t i = 0; i < size_my_panels; i++) { my_panels[i] = new_panel(my_wins[i]); - set_panel_userptr(my_panels[i], - (i + 1 < size_my_panels) ? my_panels[i+1] : my_panels[0]); + set_panel_userptr(my_panels[i], (i + 1 < size_my_panels) ? my_panels[i + 1] + : my_panels[0]); } update_panels(); @@ -114,19 +93,19 @@ void marks_page() { // Main loop while ((ch = getch()) != KEY_F(1)) { bool needs_update = false; - - switch (ch) { - case KEY_UP: - case 'k': // Vim-style up - y_offset--; - needs_update = true; - break; - case KEY_DOWN: - case 'j': // Vim-style down - y_offset++; - needs_update = true; - break; + switch (ch) { + case KEY_UP: + case 'k': // Vim-style up + y_offset--; + needs_update = true; + break; + + case KEY_DOWN: + case 'j': // Vim-style down + y_offset++; + needs_update = true; + break; } // Update window positions if scrolled @@ -136,7 +115,7 @@ void marks_page() { int new_x = original_x[i]; move_panel(my_panels[i], new_y, new_x); } - + update_panels(); doupdate(); } @@ -154,36 +133,48 @@ void marks_page() { /* Put all the windows */ void init_wins(WINDOW **wins, int n, json marks_json) { int x, y, i; - char label[1500]; + wchar_t label[1500]; y = DEFAULT_Y_OFFSET; x = DEFAULT_X_OFFSET; uint8_t curent_color = 0; int MaxHight = 0; - // this loop true subjects + // this loop through subjects for (i = 0; i < n; ++i) { // Calculate label and max_text_length to determine window width std::string sub_name = marks_json["Subjects"][i]["Subject"]["Name"]; - // DEBUG - // std::clog << BLUE"[LOG]" << RESET" procesing subject: " << sub_name << "\n"; std::string sub_avg_s = marks_json["Subjects"][i]["AverageText"]; - sprintf(label, "%s - avg: %s", sub_name.c_str(), sub_avg_s.c_str()); - size_t max_text_length = strlen(label); + // Convert to wchar_t + std::wstring wsub_name = string_to_wstring(sub_name); + std::wstring wsub_avg_s = string_to_wstring(sub_avg_s); + + // Using swprintf for wide character formatting + swprintf(label, sizeof(label) / sizeof(label[0]), L"%ls - avg: %ls", + wsub_name.c_str(), wsub_avg_s.c_str()); + + size_t max_text_length = wcslen(label); for (int j = 0; j < marks_json["Subjects"][i]["Marks"].size(); j++) { - std::string caption = rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Caption"]); - std::string theme = rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Theme"]); - caption = rm_tr_le_whitespace(caption); - theme = rm_tr_le_whitespace(theme); + std::string caption = + rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Caption"]); + std::string theme = + rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Theme"]); + + std::wstring wcaption = string_to_wstring(caption); + std::wstring wtheme = string_to_wstring(theme); + + // Some code that does something and fixes some edge cases + std::string testCaption = caption + std::format(" {{{}}} [{}]", "X", 0); + std::wstring wTestCaption = string_to_wstring(testCaption); max_text_length = - std::max({max_text_length, caption.length(), theme.length()}); + std::max({max_text_length, wTestCaption.length(), wtheme.length()}); } int width = max_text_length + DEFAULT_PADDING; - // hanndle windows overflowing off screen + // handle windows overflowing off screen if (x + width > COLS) { x = DEFAULT_X_OFFSET; y += MaxHight + 2; @@ -207,8 +198,8 @@ void init_wins(WINDOW **wins, int n, json marks_json) { } /* Show the window with a border and a label */ -void win_show(WINDOW *win, char *label, int label_color, int width, int height, - json marks_json, int SubjectIndex) { +void win_show(WINDOW *win, wchar_t *label, int label_color, int width, + int height, json marks_json, int SubjectIndex) { int startx, starty; wresize(win, height, width); @@ -221,30 +212,49 @@ void win_show(WINDOW *win, char *label, int label_color, int width, int height, mvwhline(win, 2, 1, ACS_HLINE, width - 2); mvwaddch(win, 2, width - 1, ACS_RTEE); - print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); + wprint_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); - char CaptionBuf[1500]; - char ThemeBuf[1500]; - std::string Caption; + wchar_t CaptionBuf[1500]; + wchar_t ThemeBuf[1500]; + std::wstring wCaption; int AdditionalOffset = 0; + for (size_t i = 0; i < marks_json["Subjects"][SubjectIndex]["Marks"].size(); i++) { - Caption = marks_json["Subjects"][SubjectIndex]["Marks"][i]["Caption"]; + std::string Caption = + marks_json["Subjects"][SubjectIndex]["Marks"][i]["Caption"]; Caption = rm_tr_le_whitespace(Caption); - Caption.append(std::format(" - {{{}}} [{}]", - marks_json["Subjects"][SubjectIndex]["Marks"][i]["MarkText"] - .get(),marks_json["Subjects"][SubjectIndex]["Marks"][i]["Weight"].get())); - - strncpy(CaptionBuf, Caption.c_str(), sizeof(CaptionBuf)-1); - print_in_middle(win, 3 + i + AdditionalOffset, 0, width, CaptionBuf,COLOR_PAIR(label_color)); - strncpy(ThemeBuf, - rm_tr_le_whitespace(marks_json["Subjects"][SubjectIndex]["Marks"][i]["Theme"] - .get()) - .c_str(), - sizeof(ThemeBuf)-1); - print_in_middle(win, 3 + i + 1 + AdditionalOffset, 0, width, ThemeBuf, - COLOR_PAIR(label_color)); + std::string MarkText = + marks_json["Subjects"][SubjectIndex]["Marks"][i]["MarkText"]; + int Weight = marks_json["Subjects"][SubjectIndex]["Marks"][i]["Weight"]; + + // Create formatted string with mark and weight + std::string formattedCaption = + Caption + std::format(" - {{{}}} [{}]", MarkText, Weight); + + // Convert to wide string + wCaption = string_to_wstring(formattedCaption); + + wcsncpy(CaptionBuf, wCaption.c_str(), + sizeof(CaptionBuf) / sizeof(CaptionBuf[0]) - 1); + CaptionBuf[sizeof(CaptionBuf) / sizeof(CaptionBuf[0]) - 1] = + L'\0'; // Ensure null termination + + wprint_in_middle(win, 3 + i + AdditionalOffset, 0, width, CaptionBuf, + COLOR_PAIR(label_color)); + + std::string Theme = + marks_json["Subjects"][SubjectIndex]["Marks"][i]["Theme"]; + std::wstring wTheme = string_to_wstring(rm_tr_le_whitespace(Theme)); + + wcsncpy(ThemeBuf, wTheme.c_str(), + sizeof(ThemeBuf) / sizeof(ThemeBuf[0]) - 1); + ThemeBuf[sizeof(ThemeBuf) / sizeof(ThemeBuf[0]) - 1] = + L'\0'; // Ensure null termination + + wprint_in_middle(win, 3 + i + 1 + AdditionalOffset, 0, width, ThemeBuf, + COLOR_PAIR(label_color)); AdditionalOffset++; } } \ No newline at end of file diff --git a/src/timetable.cpp b/src/timetable.cpp index 0e1cafe..95bc2c9 100644 --- a/src/timetable.cpp +++ b/src/timetable.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -39,5 +40,10 @@ void timetable_page() { noecho(); keypad(stdscr, TRUE); - printw("LINES: %d COLS: %d", LINES, COLS); + // Use wide character printing + std::wstring msg = std::format(L"LINES: {} COLS: {}", LINES, COLS); + mvaddwstr(0, 0, msg.c_str()); + refresh(); + getch(); // Wait for key press + endwin(); } \ No newline at end of file