diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 391 |
1 files changed, 199 insertions, 192 deletions
@@ -17,6 +17,7 @@ #define MAX_TASK_NAME 58 // Maximum task name length, including null-terminator. #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0) +#define WEEK_DAYS 7 // Why not? #define LOG_FILE_NAME "log.txt" #define DB_BIN_PATH_NAME "./database.bin" @@ -25,7 +26,7 @@ typedef struct /*__attribute__((__packed__))*/ { int64_t times[7]; char name[MAX_TASK_NAME]; -} task_t; +} task_t; // TODO Rename to task_st typedef struct /*__attribute__((__packed__))*/ { task_t *tasks; @@ -35,7 +36,7 @@ typedef struct /*__attribute__((__packed__))*/ { ptrdiff_t selected_task; int64_t modified_on; int64_t total_times[7]; -} database_t; +} database_t; // TODO Rename to database_st #define DB_FILE_SIGN_STR "TTT:B:01" const char DB_FILE_SIGN[] = DB_FILE_SIGN_STR; @@ -49,6 +50,7 @@ const int64_t SECONDS_IN_YEAR = (int64_t)365*SECONDS_IN_DAY; database_t database; database_t archive; +database_t *db; // Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character. // The string should have capacity for at least length number of items. @@ -81,7 +83,7 @@ char *replace_char(char *string, char find, char replace) { return string; } -char* format_time(intmax_t time, char* string, int length) { +char *format_time(intmax_t time, char* string, int length) { int left_padding = (length - 5) / 2; int right_padding = length - 5 - left_padding; @@ -121,9 +123,12 @@ char* format_time(intmax_t time, char* string, int length) { intmax_t minutes = (time - (hours * SECONDS_IN_HOUR) ) / SECONDS_IN_MINUTE; sprintf(string, "%*s%02jd:%02jd%*s", left_padding, "", hours, minutes, right_padding, ""); } - else if (time >= 0) { + else if (time > 0) { sprintf(string, "%*s%4jds%*s", left_padding, "", time, right_padding, ""); } + else if (time == 0) { + sprintf(string, "%*s", length, ""); + } else { sprintf(string, "%*s - %*s", left_padding, "", right_padding, ""); } @@ -467,16 +472,74 @@ bool import_from_csv(database_t *db, const char *path_name) { return true; } +void update_timers(database_t *db) { + + // Get current UTC time. + time_t stop_time = time(NULL); + + // Get last modified on UTC time. + time_t start_time = db->modified_on; + + if (db->active_task < 0) { + return; + } + + task_t *active_task = db->tasks + db->active_task; + uint8_t start_week_day; + while (start_time < stop_time) { + + start_week_day = localtime(&start_time)->tm_wday; + + // Get next day in local time. + struct tm *start_of_day_tm = localtime(&start_time); + start_of_day_tm->tm_sec = 0; + start_of_day_tm->tm_min = 0; + start_of_day_tm->tm_hour = 0; + time_t start_of_day = mktime(start_of_day_tm); + time_t next_day = start_of_day + SECONDS_IN_DAY; + time_t next_start = next_day < stop_time ? next_day : stop_time; + time_t elapsed_time = next_start - start_time; + active_task->times[start_week_day] += elapsed_time; + db->total_times[start_week_day] += elapsed_time; + + start_time = next_start; + } + + db->modified_on = stop_time; +} + +void update_total_timers(database_t *db) { + + int64_t *d0 = &db->total_times[0]; + int64_t *d1 = &db->total_times[1]; + int64_t *d2 = &db->total_times[2]; + int64_t *d3 = &db->total_times[3]; + int64_t *d4 = &db->total_times[4]; + int64_t *d5 = &db->total_times[5]; + int64_t *d6 = &db->total_times[6]; + memset(db->total_times, 7, sizeof(int64_t)); + + for (size_t idx = 0; idx < db->count; idx++) { + int64_t *times = db->tasks[idx].times; + *d0 = add_time(*d0, times[0]); + *d1 = add_time(*d1, times[1]); + *d2 = add_time(*d2, times[2]); + *d3 = add_time(*d3, times[3]); + *d4 = add_time(*d4, times[4]); + *d5 = add_time(*d5, times[5]); + *d6 = add_time(*d6, times[6]); + } +} -enum TEST { +typedef enum { T_NONE = 0x00, T_TIME = 0x01, T_SOT = 0x02, T_TPF = 0x04, T_ALL = 0xFF, -}; +} test_et; -void prototype(int level) { +void prototype(test_et level) { const char *done = "# -- done -- -- -- /\n"; @@ -542,71 +605,8 @@ void prototype(int level) { } - - -void update_timers(database_t *db) { - - // Get current UTC time. - time_t stop_time = time(NULL); - - // Get last modified on UTC time. - time_t start_time = db->modified_on; - - if (db->active_task < 0) { - return; - } - - task_t *active_task = db->tasks + db->active_task; - uint8_t start_week_day; - while (start_time < stop_time) { - - start_week_day = localtime(&start_time)->tm_wday; - - // Get next day in local time. - struct tm *start_of_day_tm = localtime(&start_time); - start_of_day_tm->tm_sec = 0; - start_of_day_tm->tm_min = 0; - start_of_day_tm->tm_hour = 0; - time_t start_of_day = mktime(start_of_day_tm); - time_t next_day = start_of_day + SECONDS_IN_DAY; - time_t next_start = next_day < stop_time ? next_day : stop_time; - time_t elapsed_time = next_start - start_time; - active_task->times[start_week_day] += elapsed_time; - db->total_times[start_week_day] += elapsed_time; - - start_time = next_start; - } - - db->modified_on = stop_time; -} - -void update_total_timers(database_t *db) { - - int64_t *d0 = &db->total_times[0]; - int64_t *d1 = &db->total_times[1]; - int64_t *d2 = &db->total_times[2]; - int64_t *d3 = &db->total_times[3]; - int64_t *d4 = &db->total_times[4]; - int64_t *d5 = &db->total_times[5]; - int64_t *d6 = &db->total_times[6]; - memset(db->total_times, 7, sizeof(int64_t)); - - for (size_t idx = 0; idx < db->count; idx++) { - int64_t *times = db->tasks[idx].times; - *d0 = add_time(*d0, times[0]); - *d1 = add_time(*d1, times[1]); - *d2 = add_time(*d2, times[2]); - *d3 = add_time(*d3, times[3]); - *d4 = add_time(*d4, times[4]); - *d5 = add_time(*d5, times[5]); - *d6 = add_time(*d6, times[6]); - } -} - - char *line_buffer; int size_x, size_y, pos_x, pos_y; -uint8_t active_layout = 0; #define NUM_OF_COLUMNS 9 @@ -616,20 +616,36 @@ typedef struct { int column_widths[NUM_OF_COLUMNS]; char alignments[NUM_OF_COLUMNS]; int alignment_offsets[NUM_OF_COLUMNS]; - int title_offset; - int days_offset; - int total_offset; } layout_t; layout_t *layouts = NULL; +// typedef enum { +// L_NORMAL, +// L_COMPACT, +// } layout_type; + +#define L_NORMAL 0 +#define L_COMPACT 1 + +#define L_TITLE_IDX 0 +#define L_DAYS_IDX 1 +#define L_TOTAL_IDX 8 + + +#define THEME_A 1 +#define THEME_B 2 +#define THEME_C 3 +#define THEME_D 4 +#define THEME_E 5 + void initialize_tui() { layouts = calloc(2, sizeof(layout_t)); - // Layout : 0 : normal. - layouts[0] = (layout_t) { + // Normal layout. + layouts[L_NORMAL] = (layout_t) { .column_widths = { -1, 7, 7, 7, 7, 7, 7, 7, 9 }, .alignments = { 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C' }, .table_headers = { @@ -643,13 +659,10 @@ void initialize_tui() { " Sat ", " Total ", }, - .title_offset = 0, - .days_offset = 1, - .total_offset = 8, }; - // Layout : 1 : compact. - layouts[1] = (layout_t){ + // Compact layout. + layouts[L_COMPACT] = (layout_t){ .column_widths = { -1, 5, 5, 5, 5, 5, 5, 5, 5 }, .alignments = { 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C' }, .table_headers = { @@ -663,9 +676,6 @@ void initialize_tui() { " S ", " # ", }, - .title_offset = 0, - .days_offset = 1, - .total_offset = 8, }; // Calculate alignment_offsets. @@ -698,47 +708,16 @@ void initialize_tui() { // Initialize pairs of colors. start_color(); - init_pair(1, COLOR_BLUE, COLOR_BLACK); - init_pair(2, COLOR_BLACK, COLOR_CYAN); - init_pair(3, COLOR_WHITE, COLOR_BLUE); -} - -void free_memory() { - reset_database(&database); - reset_database(&archive); - - free(line_buffer); - line_buffer = NULL; - - free(layouts); - layouts = NULL; + init_pair(THEME_A, COLOR_BLUE, COLOR_BLACK); + init_pair(THEME_B, COLOR_BLACK, COLOR_CYAN); + init_pair(THEME_C, COLOR_WHITE, COLOR_BLUE); + init_pair(THEME_D, COLOR_CYAN, COLOR_BLACK); + init_pair(THEME_E, COLOR_BLUE, COLOR_BLACK); } - -void draw_tui(database_t *db) { - - const static int days_map[] = { - (0 + FIRST_DAY_OF_WEEK) % 7, - (1 + FIRST_DAY_OF_WEEK) % 7, - (2 + FIRST_DAY_OF_WEEK) % 7, - (3 + FIRST_DAY_OF_WEEK) % 7, - (4 + FIRST_DAY_OF_WEEK) % 7, - (5 + FIRST_DAY_OF_WEEK) % 7, - (6 + FIRST_DAY_OF_WEEK) % 7, - }; - const static int idx_map[] = { - 0, - 1 + days_map[0], - 1 + days_map[1], - 1 + days_map[2], - 1 + days_map[3], - 1 + days_map[4], - 1 + days_map[5], - 1 + days_map[6], - 8, - }; +void draw_tui(database_t *db, layout_t *layout) { - layout_t *layout = &layouts[active_layout]; + int x, y; // The first column expands to fill the remaining space dynamically. layout->column_widths[0] = size_x - (NUM_OF_COLUMNS - 1) - 2; @@ -753,11 +732,12 @@ void draw_tui(database_t *db) { box(stdscr, 0, 0); // Draw table grids. - int x = 0; + y = 0; + x = 0; for (int idx = 0; idx < NUM_OF_COLUMNS - 1; idx++) { x += 1 + layout->column_widths[idx]; - mvaddch(0, x, ACS_TTEE); - for (int y = 1; y < size_y - 1; y++) { + mvaddch(y, x, ACS_TTEE); + for (y = 1; y < size_y - 1; y++) { mvaddch(y, x, ACS_VLINE); } mvaddch(size_y - 1, x, ACS_BTEE); @@ -765,132 +745,159 @@ void draw_tui(database_t *db) { // Draw diamond symbol on top left corner when in archive mode. if (db == &archive) { + attron(COLOR_PAIR(THEME_D)); // Apply theme mvaddch(0, 0, ACS_DIAMOND); + attrset(A_NORMAL); // Reset theme. } + + /////////////////////////////////////////////////////////////////////////// // Draw headers. + y = 0; x = 0; - for (int counter = 0; counter < NUM_OF_COLUMNS; counter++) { + + // Headers : title + x++; + mvaddstr(y, x + layout->alignment_offsets[L_TITLE_IDX], layout->table_headers[L_TITLE_IDX]); + x += layout->column_widths[L_TITLE_IDX]; + + // Headers : days + + time_t now_utc = time(NULL); // Get current UTC time. + int week_day = localtime(&now_utc)->tm_wday; // Get current day of the week. + + for (int idx = 0; idx < WEEK_DAYS; idx++) { x++; - int idx = idx_map[counter]; - int header_position = x + layout->alignment_offsets[idx]; - mvaddstr(0, header_position, layout->table_headers[idx]); - x += layout->column_widths[idx]; + + int day_idx = (idx + FIRST_DAY_OF_WEEK) % WEEK_DAYS; + + // Apply theme. + if (day_idx == week_day && db->active_task >= 0) { + attron(COLOR_PAIR(THEME_E) | A_BOLD); + } + else if(day_idx == week_day) { + attron(COLOR_PAIR(THEME_D) | A_BOLD); + } + + mvaddstr(y, x + layout->alignment_offsets[day_idx + L_DAYS_IDX], layout->table_headers[day_idx + L_DAYS_IDX]); + x += layout->column_widths[day_idx + L_DAYS_IDX]; + + // Reset theme. + attrset(A_NORMAL); } + // Headers : total + x++; + mvaddstr(y, x + layout->alignment_offsets[L_TOTAL_IDX], layout->table_headers[L_TOTAL_IDX]); + x += layout->column_widths[L_TOTAL_IDX]; // Not needed. + + + /////////////////////////////////////////////////////////////////////////// // Draw tasks. + uint64_t total_time = 0; - int times_offset = 1; int column_width; - int y = 0; - - int available_rows = size_y - 2; + task_t *active_task = get_active_task(db); task_t *selected_task = get_selected_task(db); - // This is some sort of pagination to allow scrolling through the tasks. - task_t *start_task = db->tasks + (db->selected_task / available_rows) * available_rows; - for (task_t *task = start_task; available_rows > 0 && task < db->tasks + db->count; task++) { - available_rows--; + // TODO This is some sort of pagination to allow scrolling through the tasks. + y = 0; + size_t available_rows = size_y - 2; + size_t idx_start = (db->selected_task / available_rows) * available_rows; + size_t idx_stop = idx_start + (available_rows > db->count - idx_start ? db->count - idx_start : available_rows); + for (size_t idx = idx_start; idx < idx_stop; idx++) { + task_t *task = &db->tasks[idx]; y++; + x = 0; - - // TEST -- START - // Enable highlight color on selected entry. - int color_pair = 0; - { - if (task == active_task) { - color_pair = 1; - } - if (task == selected_task) { - color_pair = 2; - } - if (task == active_task && task == selected_task) { - color_pair = 3; - } - if (color_pair == 1 || color_pair == 3) { - attron(A_BOLD); - } - attron(COLOR_PAIR(color_pair)); + // Apply theme. + if (task == active_task && task == selected_task) { + attron(COLOR_PAIR(THEME_C) | A_BOLD); + } + else if (task == selected_task) { + attron(COLOR_PAIR(THEME_B)); + } + else if(task == active_task) { + attron(COLOR_PAIR(THEME_A) | A_BOLD); } - // TEST -- STOP - - int x = 0; - // Task name. + // Task title. x++; - - column_width = layout->column_widths[0]; + column_width = layout->column_widths[L_TITLE_IDX]; sprintf(line_buffer, "%*s", column_width, ""); mvaddnstr(y, x, line_buffer, column_width); mvaddnstr(y, x, task->name, column_width); - - x += layout->column_widths[0]; + x += column_width; // Task times. total_time = 0; - for (int idx = 0; idx < 7; idx++) { + for (int idx = 0; idx < WEEK_DAYS; idx++) { x++; - int idx_fdow = (idx + FIRST_DAY_OF_WEEK) % 7; + int day_idx = (idx + FIRST_DAY_OF_WEEK) % WEEK_DAYS; - int column_width = layout->column_widths[idx_fdow + times_offset]; - int64_t task_time = task->times[idx_fdow]; -// total_time += task_time; TODO + int column_width = layout->column_widths[L_DAYS_IDX + day_idx]; + int64_t task_time = task->times[day_idx]; total_time = add_time(total_time, task_time); - if (task_time > 0) { - format_time(task_time, line_buffer, column_width); - } - else { - sprintf(line_buffer, "%*s", column_width, ""); - } + format_time(task_time, line_buffer, column_width); mvaddstr(y, x, line_buffer); x += column_width; } // Task total. x++; - column_width = layout->column_widths[8]; // TODO + column_width = layout->column_widths[L_TOTAL_IDX]; // TODO format_time(total_time, line_buffer, column_width); mvaddstr(y, x, line_buffer); - // TEST -- START - attroff(COLOR_PAIR(color_pair)); - attroff(A_BOLD); - // TEST -- STOP + // Reset theme. + attrset(A_NORMAL); } - // Show current selected task and total number of tasks. + + /////////////////////////////////////////////////////////////////////////// + // Draw selected/total tasks. sprintf(line_buffer, " %td/%zd ", db->selected_task+1, db->count); - if (strlen(line_buffer) > layout->column_widths[0]) { + if (strlen(line_buffer) > layout->column_widths[L_TITLE_IDX]) { sprintf(line_buffer, "%td", db->selected_task+1); } mvaddstr(size_y-1, 1, line_buffer); - // Daily totals. + /////////////////////////////////////////////////////////////////////////// + // Draw daily totals. y = size_y-1; - x = 0 + 1 + layout->column_widths[0]; + x = 0 + 1 + layout->column_widths[L_TITLE_IDX]; total_time = 0; - for (int counter = 0; counter < 7; counter++) { + for (int idx = 0; idx < WEEK_DAYS; idx++) { x++; - int mapped_day = days_map[counter]; - int days_offset = layout->days_offset; - int64_t daily_total = db->total_times[mapped_day]; - column_width = layout->column_widths[mapped_day + days_offset]; + int day_idx = (idx + FIRST_DAY_OF_WEEK) % WEEK_DAYS; + int64_t daily_total = db->total_times[day_idx]; + column_width = layout->column_widths[day_idx + L_DAYS_IDX]; total_time = add_time(total_time, daily_total); format_time(daily_total, line_buffer, column_width); mvaddstr(y, x, line_buffer); x += column_width; } x++; - column_width = layout->column_widths[8]; + column_width = layout->column_widths[L_TOTAL_IDX]; format_time(total_time, line_buffer, column_width); mvaddstr(y, x, line_buffer); x += column_width; } -database_t *db; + +void free_memory() { + reset_database(&database); + reset_database(&archive); + + free(line_buffer); + line_buffer = NULL; + + free(layouts); + layouts = NULL; +} int main(int argc, char *argv[]) { @@ -1087,6 +1094,7 @@ int main(int argc, char *argv[]) { store_database(&archive, AR_BIN_PATH_NAME); } db = &archive; + db->active_task = -1; // TODO This should not be necessary. } else { store_database(&archive, AR_BIN_PATH_NAME); @@ -1141,8 +1149,7 @@ int main(int argc, char *argv[]) { } if (size_x >= 60 && size_y > 2) { - active_layout = size_x > 100 ? 0 : 1; - draw_tui(db); + draw_tui(db, &layouts[size_x > 100 ? L_NORMAL : L_COMPACT]); } else { const char *INVALID_WINDOW_MESSAGE = "Please expand window."; |
