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 <codecvt>
#include <csignal>
#include <curses.h>
#include <dirent.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <locale>
#include <string>
#include <termios.h>
#include <unistd.h>
#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<char>(authfile)),
std::istreambuf_iterator<char>());
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<char>(authfile)),
std::istreambuf_iterator<char>());
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<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 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

View File

@ -5,16 +5,18 @@
#include <cstdlib>
#include <cstring>
#include <curses.h>
#include <locale>
#include <menu.h>
#include <string>
#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();
}
}

View File

@ -1,5 +1,7 @@
#include "marks.h"
#include "helper_funcs.h"
#include "net.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
@ -7,40 +9,17 @@
#include <curses.h>
#include <format>
#include <iostream>
#include <locale>
#include <menu.h>
#include <nlohmann/json.hpp>
#include <panel.h>
#include <string>
#include <algorithm>
#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<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,
rm_tr_le_whitespace(marks_json["Subjects"][SubjectIndex]["Marks"][i]["Theme"]
.get<std::string>())
.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++;
}
}

View File

@ -4,6 +4,7 @@
#include <format>
#include <fstream>
#include <iostream>
#include <locale>
#include <ncurses.h>
#include <nlohmann/json.hpp>
@ -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();
}