diff options
| author | dam <dam@gudinoff> | 2022-12-22 02:44:59 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2022-12-22 02:44:59 +0000 |
| commit | 298cfb1a4dbd58029066c3af09a48fd5ffbdb5ef (patch) | |
| tree | f3c5c306ee8f81c904ef8e9568ea80a4b277b60b | |
| parent | ed4c07fec790af982dbbfc40695da38da438bfb8 (diff) | |
| download | task-time-tracker-298cfb1a4dbd58029066c3af09a48fd5ffbdb5ef.tar.zst task-time-tracker-298cfb1a4dbd58029066c3af09a48fd5ffbdb5ef.zip | |
Reduced usage of snprintf and string_buffer by replacing them with mvprintw. Prototype code for print_error.
| -rw-r--r-- | main.c | 150 | ||||
| -rw-r--r-- | misc.c | 48 |
2 files changed, 158 insertions, 40 deletions
@@ -27,8 +27,10 @@ #include <locale.h> #include <ncurses.h> #include <signal.h> +#include <stdarg.h> #include <stdbool.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> @@ -91,6 +93,87 @@ char *string_buffer = NULL; // A temporary buffer for localized actions. Pleas size_t string_buffer_size = 0; int size_x, size_y, pos_x, pos_y; + + +typedef enum { + STYLE_SELECTED = 1, + STYLE_SELECTED_INVERTED, + STYLE_ACTIVE, + STYLE_ACTIVE_SELECTED, + STYLE_ERROR, +} styles_et; + + + +WINDOW *error_window = NULL; +time_t error_time_limit = 0; + +void print_error(const char *format, ...) { + va_list args; + va_start(args, format); + + if (stdscr != NULL) { // If ncurses is active. + + int w_size_x = size_x > 120 ? 120 : size_x - 2; + int w_size_y = 3; + if (error_window == NULL) { + error_window = newwin(w_size_y, w_size_x, (size_y - w_size_y) / 2, (size_x - w_size_x) / 2); + } + else { + wresize(error_window, w_size_y, w_size_x); + werase(error_window); + } + + // Apply theme. + wattron(error_window, COLOR_PAIR(STYLE_ERROR)); + + box(error_window, 0, 0); + mvwprintw(error_window, 0, 1, " Error "); + wmove(error_window, 1, 1); + vw_printw(error_window, format, args); +// wrefresh(error_window); + error_time_limit = time(NULL) + 30; + + // Reset theme. + wattrset(error_window, A_NORMAL); + } + else { + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + } + va_end(args); +} + +void update_error_window() { + if (error_window == NULL) { + return; + } + + // Hide error window after time-limit or if terminal is shrank. + int w_size_x, w_size_y; + getmaxyx(error_window, w_size_y, w_size_x); + if (time(NULL) >= error_time_limit + || size_x - w_size_x < 2 + || size_y - w_size_y < 2 + ) { + delwin(error_window); + error_window = NULL; + return; + } + + // Adjust error window position. + int pos_x = (size_x - w_size_x) / 2; + int pos_y = (size_y - w_size_y) / 2; + mvwin(error_window, pos_y, pos_x); + + // Avoid being overwritten by main window content. + refresh(); + touchwin(error_window); + wrefresh(error_window); +} + + + void trigger_autosave() { countdown_to_autosave = 13375; // ms } @@ -180,29 +263,28 @@ char *replace_char(char *string, char find, char replace) { return string; } -// Prints time into string using 5 characters centered on space. -// The string buffer should be able to store space UTF8 characters plus '\0'. -char *sprint_time5_utf8(char *string, intmax_t time, int space) { +// Prints, on row y and column x, the time using 5 characters centered on space. +// Returns the result of a call to mvprintw. +int mvprintw_time(int y, int x, intmax_t time, int space) { const int TIME_CHARS = 5; assert(space >= TIME_CHARS); - int buffer_space = space * 4 + 1; // Each that UTF8 char can have 4 bytes. int left_padding = (space - TIME_CHARS) / 2; int right_padding = space - TIME_CHARS - left_padding; if (time < 0) { - snprintf(string, buffer_space, "%*s - %*s", left_padding, "", right_padding, ""); + return mvprintw(y, x, "%*s - %*s", left_padding, "", right_padding, ""); } else if (time == 0) { - snprintf(string, buffer_space, "%*s 0 %*s", left_padding, "", right_padding, ""); + return mvprintw(y, x, "%*s 0 %*s", left_padding, "", right_padding, ""); } else if (time < SECONDS_IN_MINUTE) { - snprintf(string, buffer_space, "%*s%3jds %*s", left_padding, "", time, right_padding, ""); + return mvprintw(y, x, "%*s%3jds %*s", left_padding, "", time, right_padding, ""); } else if (time < (intmax_t)100 * SECONDS_IN_HOUR) { intmax_t hours = (double)time / (double)SECONDS_IN_HOUR; intmax_t minutes = (time - (hours * SECONDS_IN_HOUR) ) / SECONDS_IN_MINUTE; - snprintf(string, buffer_space, "%*s%02jd:%02jd%*s", left_padding, "", hours, minutes, right_padding, ""); + return mvprintw(y, x, "%*s%02jd:%02jd%*s", left_padding, "", hours, minutes, right_padding, ""); } else if (time < (intmax_t)(9999.5 * SECONDS_IN_DAY)) { double value = (double)time / (double)SECONDS_IN_DAY; @@ -210,7 +292,7 @@ char *sprint_time5_utf8(char *string, intmax_t time, int space) { time >= 99.95 * SECONDS_IN_DAY ? 0 : time >= 9.995 * SECONDS_IN_DAY ? 1 : 2; - snprintf(string, buffer_space, "%*s%4.*fd%*s", left_padding, "", decimals, value, right_padding, ""); + return mvprintw(y, x, "%*s%4.*fd%*s", left_padding, "", decimals, value, right_padding, ""); } else if (time < (intmax_t)(9999.5 * SECONDS_IN_YEAR)) { double value = (double)time / (double)SECONDS_IN_YEAR; @@ -218,13 +300,11 @@ char *sprint_time5_utf8(char *string, intmax_t time, int space) { time >= 99.95 * SECONDS_IN_YEAR ? 0 : time >= 9.995 * SECONDS_IN_YEAR ? 1 : 2; - snprintf(string, buffer_space, "%*s%4.*fy%*s", left_padding, "", decimals, value, right_padding, ""); + return mvprintw(y, x, "%*s%4.*fy%*s", left_padding, "", decimals, value, right_padding, ""); } else { - snprintf(string, buffer_space, "%*s ∞ %*s", left_padding, "", right_padding, ""); + return mvprintw(y, x, "%*s ∞ %*s", left_padding, "", right_padding, ""); } - - return string; } int64_t add_int64(int64_t x, int64_t y) { @@ -789,13 +869,6 @@ void set_active_task(database_st *db, task_st *task) { #define L_TOTAL_IDX 8 typedef enum { - STYLE_SELECTED = 1, - STYLE_SELECTED_INVERTED, - STYLE_ACTIVE, - STYLE_ACTIVE_SELECTED, -} styles_et; - -typedef enum { L_NORMAL, L_COMPACT, NUM_LAYOUTS, @@ -888,6 +961,7 @@ void initialize_tui() { init_pair(STYLE_SELECTED_INVERTED, COLOR_CYAN, -1); init_pair(STYLE_ACTIVE, COLOR_BLUE, -1); init_pair(STYLE_ACTIVE_SELECTED, COLOR_WHITE, COLOR_BLUE); + init_pair(STYLE_ERROR, COLOR_RED, -1); } void update_layout() { @@ -1012,9 +1086,7 @@ void draw_tui(database_st *db, layout_st *layout) { // Task title. x++; column_width = layout->columns[L_TITLE_IDX].width; - snprintf(string_buffer, string_buffer_size, "%*s", column_width, ""); - mvaddnstr(y, x, string_buffer, column_width); - mvaddnstr(y, x, task->name, column_width); + mvprintw(y, x, "%-*.*s", column_width, column_width, task->name); x += column_width; // Task times. @@ -1027,15 +1099,13 @@ void draw_tui(database_st *db, layout_st *layout) { column_width = layout->columns[L_DAYS_IDX + day_idx].width; int64_t task_stime = task->times[day_idx]; total_time = add_int64(total_time, task_stime); - sprint_time5_utf8(string_buffer, task_stime, column_width); - mvaddstr(y, x, string_buffer); + mvprintw_time(y, x, task_stime, column_width); x += column_width; } // Task total. x++; - sprint_time5_utf8(string_buffer, total_time, layout->columns[L_TOTAL_IDX].width); - mvaddstr(y, x, string_buffer); + mvprintw_time(y, x, total_time, layout->columns[L_TOTAL_IDX].width); // Reset theme. attrset(A_NORMAL); @@ -1044,11 +1114,13 @@ void draw_tui(database_st *db, layout_st *layout) { /////////////////////////////////////////////////////////////////////////// // Draw selected/total tasks. - snprintf(string_buffer, string_buffer_size, " %td/%zd ", db->selected_task + 1, db->count); - if (strlen(string_buffer) > layout->columns[L_TITLE_IDX].width) { - snprintf(string_buffer, string_buffer_size, "%td", db->selected_task + 1); + int size = snprintf(NULL, 0, " %td/%zd ", db->selected_task + 1, db->count); + if (size <= layout->columns[L_TITLE_IDX].width) { + mvprintw(size_y - 1, 1, " %td/%zd ", db->selected_task + 1, db->count); + } + else { + mvprintw(size_y - 1, 1, "%td", db->selected_task + 1); } - mvaddstr(size_y - 1, 1, string_buffer); /////////////////////////////////////////////////////////////////////////// // Draw daily totals. @@ -1060,10 +1132,6 @@ void draw_tui(database_st *db, layout_st *layout) { int64_t daily_total = db->total_times[idx]; x++; - column_width = layout->columns[L_DAYS_IDX + idx].width; - total_time = add_int64(total_time, daily_total); - sprint_time5_utf8(string_buffer, daily_total, column_width); - // Apply theme. if (idx == now_week_day && active_task != NULL) { attron(COLOR_PAIR(STYLE_ACTIVE) | A_BOLD); @@ -1072,15 +1140,16 @@ void draw_tui(database_st *db, layout_st *layout) { attron(COLOR_PAIR(STYLE_SELECTED_INVERTED) | A_BOLD); } - mvaddstr(y, x, string_buffer); + column_width = layout->columns[L_DAYS_IDX + idx].width; + total_time = add_int64(total_time, daily_total); + mvprintw_time(y, x, daily_total, column_width); x += column_width; // Reset theme. attrset(A_NORMAL); } x++; - sprint_time5_utf8(string_buffer, total_time, layout->columns[L_TOTAL_IDX].width); - mvaddstr(y, x, string_buffer); + mvprintw_time(y, x, total_time, layout->columns[L_TOTAL_IDX].width); } void *mem_alloc(size_t mem_size, const char *error_tag) { @@ -1142,11 +1211,11 @@ void read_input_to_string_buffer_with_space(int row, int column, int style, int assert(length < string_buffer_size); assert(space < string_buffer_size); - snprintf(string_buffer, string_buffer_size, "%*s", space, ""); attron(style | A_UNDERLINE); - mvaddstr(row, column, string_buffer); + mvprintw(row, column, "%*s", space, ""); echo(); curs_set(1); + memset(string_buffer, 0, string_buffer_size); mvgetnstr(row, column, string_buffer, length); truncate_string_utf8(string_buffer, length); noecho(); @@ -1711,6 +1780,7 @@ int main(int argc, char *argv[]) { } else { draw_tui(db, layout); + update_error_window(); } timeout(INPUT_TIMEOUT_MS); @@ -35,6 +35,54 @@ */ +// Prints time into string using 5 characters centered on space. +// The string buffer should be able to store space UTF8 characters plus '\0'. +char *sprint_time5_utf8(char *string, intmax_t time, int space) { + const int TIME_CHARS = 5; + assert(space >= TIME_CHARS); + + int buffer_space = space * 4 + 1; // Each that UTF8 char can have 4 bytes. + int left_padding = (space - TIME_CHARS) / 2; + int right_padding = space - TIME_CHARS - left_padding; + + if (time < 0) { + snprintf(string, buffer_space, "%*s - %*s", left_padding, "", right_padding, ""); + } + else if (time == 0) { + snprintf(string, buffer_space, "%*s 0 %*s", left_padding, "", right_padding, ""); + } + else if (time < SECONDS_IN_MINUTE) { + snprintf(string, buffer_space, "%*s%3jds %*s", left_padding, "", time, right_padding, ""); + } + else if (time < (intmax_t)100 * SECONDS_IN_HOUR) { + intmax_t hours = (double)time / (double)SECONDS_IN_HOUR; + intmax_t minutes = (time - (hours * SECONDS_IN_HOUR) ) / SECONDS_IN_MINUTE; + snprintf(string, buffer_space, "%*s%02jd:%02jd%*s", left_padding, "", hours, minutes, right_padding, ""); + } + else if (time < (intmax_t)(9999.5 * SECONDS_IN_DAY)) { + double value = (double)time / (double)SECONDS_IN_DAY; + int decimals = + time >= 99.95 * SECONDS_IN_DAY ? 0 : + time >= 9.995 * SECONDS_IN_DAY ? 1 : + 2; + snprintf(string, buffer_space, "%*s%4.*fd%*s", left_padding, "", decimals, value, right_padding, ""); + } + else if (time < (intmax_t)(9999.5 * SECONDS_IN_YEAR)) { + double value = (double)time / (double)SECONDS_IN_YEAR; + int decimals = + time >= 99.95 * SECONDS_IN_YEAR ? 0 : + time >= 9.995 * SECONDS_IN_YEAR ? 1 : + 2; + snprintf(string, buffer_space, "%*s%4.*fy%*s", left_padding, "", decimals, value, right_padding, ""); + } + else { + snprintf(string, buffer_space, "%*s ∞ %*s", left_padding, "", right_padding, ""); + } + + return string; +} + + // Writes only the database core structure and the provided task if not null. // Returns success. bool store_database_partial(const database_st *db, const task_st *task, const char *path) { |
