wide string refactor

This commit is contained in:
PoliEcho 2025-03-06 19:41:30 +01:00
parent af5252f732
commit a407d8c16a
5 changed files with 238 additions and 125 deletions

View File

@ -1,14 +1,17 @@
#include "helper_funcs.h"
#include "color.h"
#include "net.h" #include "net.h"
#include <codecvt>
#include <csignal> #include <csignal>
#include <curses.h> #include <curses.h>
#include <dirent.h> #include <dirent.h>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <locale>
#include <string> #include <string>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include "color.h"
void safe_exit(int code) { void safe_exit(int code) {
switch (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 SoRAuthFile(bool save, std::string data) {
std::string home = std::getenv("HOME"); std::string home = std::getenv("HOME");
if (home.empty()) { if (home.empty()) {
std::cerr << RED "[ERROR] " RESET << "HOME environment variable not set.\n"; std::cerr << RED "[ERROR] " RESET << "HOME environment variable not set.\n";
safe_exit(EXIT_FAILURE); 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; std::string authfile_path = savedir_path + "/auth";
savedir_path.append("/.local/share/bakatui");
if (save) {
if (!std::filesystem::exists(savedir_path)) { std::ofstream authfile(authfile_path);
if (!std::filesystem::create_directories(savedir_path)) { if (!authfile.is_open()) {
std::cerr << RED "[ERROR] " RESET << "Failed to create directory: " << savedir_path << "\n"; std::cerr << RED "[ERROR] " RESET
safe_exit(EXIT_FAILURE); << "Failed to open auth file for writing.\n";
} safe_exit(EXIT_FAILURE);
} }
authfile << data;
std::string authfile_path = savedir_path + "/auth"; authfile.close();
return "";
if (save) { } else {
std::ofstream authfile(authfile_path); std::ifstream authfile(authfile_path);
if (!authfile.is_open()) { if (!authfile.is_open()) {
std::cerr << RED "[ERROR] " RESET << "Failed to open auth file for writing.\n"; std::cerr << RED "[ERROR] " RESET
safe_exit(EXIT_FAILURE); << "Failed to open auth file for reading.\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<char>(authfile)),
std::istreambuf_iterator<char>());
authfile.close();
return data;
} }
data.assign((std::istreambuf_iterator<char>(authfile)),
std::istreambuf_iterator<char>());
authfile.close();
return data;
}
} }
void get_input_and_login() { void get_input_and_login() {
@ -99,6 +104,7 @@ void get_input_and_login() {
bakaapi::login(username, password); bakaapi::login(username, password);
} }
// Original function
void print_in_middle(WINDOW *win, int starty, int startx, int width, void print_in_middle(WINDOW *win, int starty, int startx, int width,
char *string, chtype color) { char *string, chtype color) {
int length, x, y; int length, x, y;
@ -123,19 +129,92 @@ void print_in_middle(WINDOW *win, int starty, int startx, int width,
refresh(); 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::string WHITESPACE = " \n\r\t\f\v";
const std::wstring WWHITESPACE = L" \n\r\t\f\v";
std::string ltrim(const std::string &s) { std::string ltrim(const std::string &s) {
size_t start = s.find_first_not_of(WHITESPACE); size_t start = s.find_first_not_of(WHITESPACE);
return (start == std::string::npos) ? "" : s.substr(start); return (start == std::string::npos) ? "" : s.substr(start);
} }
std::string rtrim(const std::string &s) { std::string rtrim(const std::string &s) {
size_t end = s.find_last_not_of(WHITESPACE); size_t end = s.find_last_not_of(WHITESPACE);
return (end == std::string::npos) ? "" : s.substr(0, end + 1); return (end == std::string::npos) ? "" : s.substr(0, end + 1);
} }
std::string rm_tr_le_whitespace(const std::string &s) { 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<std::codecvt_utf8<wchar_t>> converter;
return converter.from_bytes(str);
}
std::string wstring_to_string(const std::wstring &wstr) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.to_bytes(wstr);
} }

View File

@ -9,8 +9,22 @@ void safe_exit(int code);
std::string bool_to_string(bool bool_in); std::string bool_to_string(bool bool_in);
std::string SoRAuthFile(bool save, std::string data); std::string SoRAuthFile(bool save, std::string data);
void get_input_and_login(); void get_input_and_login();
// Original functions
void print_in_middle(WINDOW *win, int starty, int startx, int width, void print_in_middle(WINDOW *win, int starty, int startx, int width,
char *string, chtype color); char *string, chtype color);
std::string rm_tr_le_whitespace(const std::string &s); 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 #endif

View File

@ -5,16 +5,18 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <curses.h> #include <curses.h>
#include <locale>
#include <menu.h> #include <menu.h>
#include <string>
#include "marks.h" #include "marks.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD 4 #define CTRLD 4
char *choices[] = { wchar_t *choices[] = {
"login", "Marks", "timetable", "Komens", L"login", L"Marks", L"timetable", L"Komens",
"Homework", "Absence", "Exit", (char *)NULL, L"Homework", L"Absence", L"Exit", (wchar_t *)NULL,
}; };
void (*choicesFuncs[])() = {nullptr, marks_page, timetable_page, nullptr, void (*choicesFuncs[])() = {nullptr, marks_page, timetable_page, nullptr,
nullptr, nullptr, nullptr, nullptr}; nullptr, nullptr, nullptr, nullptr};
@ -27,6 +29,7 @@ void main_menu() {
int n_choices, i; int n_choices, i;
/* Initialize curses */ /* Initialize curses */
setlocale(LC_ALL, "");
initscr(); initscr();
start_color(); start_color();
cbreak(); cbreak();
@ -39,7 +42,8 @@ void main_menu() {
n_choices = ARRAY_SIZE(choices); n_choices = ARRAY_SIZE(choices);
my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
for (i = 0; i < n_choices; ++i) 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 */ /* Crate menu */
my_menu = new_menu((ITEM **)my_items); 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 */ /* Print a border around the main window and print a title */
box(my_menu_win, 0, 0); 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); mvwaddch(my_menu_win, 2, 0, ACS_LTEE);
mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38); mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);
mvwaddch(my_menu_win, 2, 39, ACS_RTEE); mvwaddch(my_menu_win, 2, 39, ACS_RTEE);
@ -106,4 +110,4 @@ void main_menu() {
for (i = 0; i < n_choices; ++i) for (i = 0; i < n_choices; ++i)
free_item(my_items[i]); free_item(my_items[i]);
endwin(); endwin();
} }

View File

@ -1,5 +1,7 @@
#include "marks.h" #include "marks.h"
#include "helper_funcs.h" #include "helper_funcs.h"
#include "net.h"
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@ -7,40 +9,17 @@
#include <curses.h> #include <curses.h>
#include <format> #include <format>
#include <iostream> #include <iostream>
#include <locale>
#include <menu.h> #include <menu.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <panel.h> #include <panel.h>
#include <string> #include <string>
#include <algorithm>
#include "net.h"
using nlohmann::json; 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 // https://github.com/tony/NCURSES-Programming-HOWTO-examples/blob/master/16-panels
/* // MIT License (see original file)
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.
*/
#define NLINES 10 #define NLINES 10
#define NCOLS 40 #define NCOLS 40
@ -51,8 +30,8 @@ SOFTWARE.
#define DEFAULT_PADDING 4 #define DEFAULT_PADDING 4
void init_wins(WINDOW **wins, int n, json marks_json); void init_wins(WINDOW **wins, int n, json marks_json);
void win_show(WINDOW *win, char *label, int label_color, int width, int height, void win_show(WINDOW *win, wchar_t *label, int label_color, int width,
json marks_json, int SubjectIndex); int height, json marks_json, int SubjectIndex);
void marks_page() { void marks_page() {
// DONT FORGET TO UNCOMMENT // DONT FORGET TO UNCOMMENT
@ -98,8 +77,8 @@ void marks_page() {
// Attach panels // Attach panels
for (size_t i = 0; i < size_my_panels; i++) { for (size_t i = 0; i < size_my_panels; i++) {
my_panels[i] = new_panel(my_wins[i]); my_panels[i] = new_panel(my_wins[i]);
set_panel_userptr(my_panels[i], set_panel_userptr(my_panels[i], (i + 1 < size_my_panels) ? my_panels[i + 1]
(i + 1 < size_my_panels) ? my_panels[i+1] : my_panels[0]); : my_panels[0]);
} }
update_panels(); update_panels();
@ -114,19 +93,19 @@ void marks_page() {
// Main loop // Main loop
while ((ch = getch()) != KEY_F(1)) { while ((ch = getch()) != KEY_F(1)) {
bool needs_update = false; bool needs_update = false;
switch (ch) {
case KEY_UP:
case 'k': // Vim-style up
y_offset--;
needs_update = true;
break;
case KEY_DOWN: switch (ch) {
case 'j': // Vim-style down case KEY_UP:
y_offset++; case 'k': // Vim-style up
needs_update = true; y_offset--;
break; needs_update = true;
break;
case KEY_DOWN:
case 'j': // Vim-style down
y_offset++;
needs_update = true;
break;
} }
// Update window positions if scrolled // Update window positions if scrolled
@ -136,7 +115,7 @@ void marks_page() {
int new_x = original_x[i]; int new_x = original_x[i];
move_panel(my_panels[i], new_y, new_x); move_panel(my_panels[i], new_y, new_x);
} }
update_panels(); update_panels();
doupdate(); doupdate();
} }
@ -154,36 +133,48 @@ void marks_page() {
/* Put all the windows */ /* Put all the windows */
void init_wins(WINDOW **wins, int n, json marks_json) { void init_wins(WINDOW **wins, int n, json marks_json) {
int x, y, i; int x, y, i;
char label[1500]; wchar_t label[1500];
y = DEFAULT_Y_OFFSET; y = DEFAULT_Y_OFFSET;
x = DEFAULT_X_OFFSET; x = DEFAULT_X_OFFSET;
uint8_t curent_color = 0; uint8_t curent_color = 0;
int MaxHight = 0; int MaxHight = 0;
// this loop true subjects // this loop through subjects
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
// Calculate label and max_text_length to determine window width // Calculate label and max_text_length to determine window width
std::string sub_name = marks_json["Subjects"][i]["Subject"]["Name"]; 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"]; 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++) { 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 caption =
std::string theme = rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Theme"]); rm_tr_le_whitespace(marks_json["Subjects"][i]["Marks"][j]["Caption"]);
caption = rm_tr_le_whitespace(caption); std::string theme =
theme = rm_tr_le_whitespace(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 = 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; int width = max_text_length + DEFAULT_PADDING;
// hanndle windows overflowing off screen // handle windows overflowing off screen
if (x + width > COLS) { if (x + width > COLS) {
x = DEFAULT_X_OFFSET; x = DEFAULT_X_OFFSET;
y += MaxHight + 2; 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 */ /* Show the window with a border and a label */
void win_show(WINDOW *win, char *label, int label_color, int width, int height, void win_show(WINDOW *win, wchar_t *label, int label_color, int width,
json marks_json, int SubjectIndex) { int height, json marks_json, int SubjectIndex) {
int startx, starty; int startx, starty;
wresize(win, height, width); 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); mvwhline(win, 2, 1, ACS_HLINE, width - 2);
mvwaddch(win, 2, width - 1, ACS_RTEE); 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]; wchar_t CaptionBuf[1500];
char ThemeBuf[1500]; wchar_t ThemeBuf[1500];
std::string Caption; std::wstring wCaption;
int AdditionalOffset = 0; int AdditionalOffset = 0;
for (size_t i = 0; i < marks_json["Subjects"][SubjectIndex]["Marks"].size(); for (size_t i = 0; i < marks_json["Subjects"][SubjectIndex]["Marks"].size();
i++) { 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 = rm_tr_le_whitespace(Caption);
Caption.append(std::format(" - {{{}}} [{}]",
marks_json["Subjects"][SubjectIndex]["Marks"][i]["MarkText"]
.get<std::string>(),marks_json["Subjects"][SubjectIndex]["Marks"][i]["Weight"].get<int>()));
strncpy(CaptionBuf, Caption.c_str(), sizeof(CaptionBuf)-1);
print_in_middle(win, 3 + i + AdditionalOffset, 0, width, CaptionBuf,COLOR_PAIR(label_color));
strncpy(ThemeBuf, std::string MarkText =
rm_tr_le_whitespace(marks_json["Subjects"][SubjectIndex]["Marks"][i]["Theme"] marks_json["Subjects"][SubjectIndex]["Marks"][i]["MarkText"];
.get<std::string>()) int Weight = marks_json["Subjects"][SubjectIndex]["Marks"][i]["Weight"];
.c_str(),
sizeof(ThemeBuf)-1); // Create formatted string with mark and weight
print_in_middle(win, 3 + i + 1 + AdditionalOffset, 0, width, ThemeBuf, std::string formattedCaption =
COLOR_PAIR(label_color)); 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++; AdditionalOffset++;
} }
} }

View File

@ -4,6 +4,7 @@
#include <format> #include <format>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <locale>
#include <ncurses.h> #include <ncurses.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -39,5 +40,10 @@ void timetable_page() {
noecho(); noecho();
keypad(stdscr, TRUE); 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();
} }