aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-09-25 02:42:20 +0000
committerdam <dam@gudinoff>2022-09-25 02:42:20 +0000
commit2763fc6ad122f64cbd5023b4cbe7ff4d7101c805 (patch)
tree6e21029cc2919681ec37e6891f2c0c56575643ab /main.c
parenta7d15d8f11a735e81713df39597ff4b66d4e2e8c (diff)
downloadtask-time-tracker-2763fc6ad122f64cbd5023b4cbe7ff4d7101c805.tar.zst
task-time-tracker-2763fc6ad122f64cbd5023b4cbe7ff4d7101c805.zip
Improved theming in draw_tui.
Diffstat (limited to 'main.c')
-rw-r--r--main.c391
1 files changed, 199 insertions, 192 deletions
diff --git a/main.c b/main.c
index 5e1517f..f52f383 100644
--- a/main.c
+++ b/main.c
@@ -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.";