aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-12-22 02:44:59 +0000
committerdam <dam@gudinoff>2022-12-22 02:44:59 +0000
commit298cfb1a4dbd58029066c3af09a48fd5ffbdb5ef (patch)
treef3c5c306ee8f81c904ef8e9568ea80a4b277b60b
parented4c07fec790af982dbbfc40695da38da438bfb8 (diff)
downloadtask-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.c150
-rw-r--r--misc.c48
2 files changed, 158 insertions, 40 deletions
diff --git a/main.c b/main.c
index 6ef30f6..6b38f2d 100644
--- a/main.c
+++ b/main.c
@@ -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);
diff --git a/misc.c b/misc.c
index 35a602c..32e4360 100644
--- a/misc.c
+++ b/misc.c
@@ -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) {