diff options
| -rw-r--r-- | curses.jai | 28 | ||||
| -rw-r--r-- | ttt.jai | 466 |
2 files changed, 208 insertions, 286 deletions
@@ -97,11 +97,15 @@ attrset :: (attrs: s32) -> s32 #foreign ncurses attron :: (attrs: s32) -> s32 #foreign ncurses; erase :: () -> s32 #foreign ncurses; curs_set :: (visibility: s32) -> s32 #foreign ncurses; +addstr :: (str: *u8) -> s32 #foreign ncurses; mvaddstr :: (y: s32, x: s32, str: *u8) -> s32 #foreign ncurses; -mvprintw :: (y: s32, x: s32, fmt: *u8, args: ..Any) -> s32 #foreign ncurses; -mvgetnstr :: (y: s32, x: s32, str: *u8, n: s32) -> s32 #foreign ncurses; +mvprintw :: (y: s32, x: s32, fmt: *u8, + args: ..Any) -> s32 #foreign ncurses; +mvgetnstr :: (y: s32, x: s32, str: *u8, + n: s32) -> s32 #foreign ncurses; noecho :: () -> s32 #foreign ncurses; -box :: (win: *WINDOW, verch: u8, horch: u8) -> s32 #foreign ncurses; +box :: (win: *WINDOW, verch: u8, + horch: u8) -> s32 #foreign ncurses; init_pair :: (pair: s16, f: s16, b: s16) -> s32 #foreign ncurses; timeout :: (delay: s32) -> void #foreign ncurses; addch :: (ch: u32) -> s32 #foreign ncurses; @@ -109,6 +113,24 @@ mvaddch :: (y: s32, x: s32, ch: u32) -> s32 #foreign ncurses clear :: () -> s32 #foreign ncurses; refresh :: () -> s32 #foreign ncurses; move :: (y: s32, x: s32) -> s32 #foreign ncurses; +isendwin :: () -> bool #foreign ncurses; +delwin :: (win: *WINDOW) -> s32 #foreign ncurses; +newwin :: (nlines: s32, ncols: s32, begin_y: s32, + begin_x: s32) -> *WINDOW #foreign ncurses; +wattron :: (win: *WINDOW, attrs: s32) -> s32 #foreign ncurses; +wborder :: (win: *WINDOW, ls: u32, rs: u32, + ts: u32, bs: u32, tl: u32, + tr: u32, bl: u32, br: u32) -> s32 #foreign ncurses; +mvwin :: (win: *WINDOW, y: s32, x: s32) -> s32 #foreign ncurses; +touchwin :: (win: *WINDOW) -> s32 #foreign ncurses; +wrefresh :: (win: *WINDOW) -> s32 #foreign ncurses; +mvwprintw :: (win: *WINDOW, y: s32, x: s32, + fmt: *u8, args: ..Any) -> s32 #foreign ncurses; +wmove :: (win: *WINDOW, y: s32, x: s32) -> s32 #foreign ncurses; +waddch :: (win: *WINDOW, ch: u32) -> s32 #foreign ncurses; +vw_printw :: (win: *WINDOW, fmt: *u8, + varglist: ..Any) -> s32 #foreign ncurses; + getmaxyx :: inline (win: *WINDOW, y: *s32, x: *s32) { <<y = getmaxy(win); <<x = getmaxx(win); } getmaxx :: inline (win: *WINDOW) -> s32 { return ifx win == null then ERR else win._maxx + 1; } @@ -29,10 +29,9 @@ VERSION :: "2.0"; // Use only 3 chars (to fit layouts). YEAR :: "2023"; -TASK_NAME_LENGTH :: 57; -TASK_NAME_BYTES :: #run TASK_NAME_LENGTH+1; // TODO Get rid of this! FIRST_DAY_OF_WEEK :: 1; // (0-6, Sunday = 0). -NUM_WEEK_DAYS :: 7; // Just to be more clear about what we're looping about. +NUM_WEEK_DAYS :: 7; // TODO This has to go - Just to be more clear about what we're looping about. +NAME_SIZE :: 72; // TODO Use this instead of Task.name.count ? APP_FOLDER_NAME :: ".task_time_tracker_v2"; // TODO Using _v2 to avoid erasing my work data. DB_FILE_NAME :: "database.bin"; @@ -48,8 +47,6 @@ MAX_DATABASE_TASKS :: S64_MAX; Task :: struct { times : [NUM_WEEK_DAYS] s64; name : [72] u8; - //name_data : [70] u8; - //name.data = cast(*~s8 u8) (0x80 ^ 0x01); // *name_data[0]; } Database :: struct { @@ -102,57 +99,54 @@ error_window : *WINDOW = null; error_time_limit := Apollo_Time.{0, 0}; print_error :: (format :string, args : .. Any) { - // NOTE Implement me please. -// if stdscr == null || isendwin() == true { // Not in ncurses mode? + if stdscr == null || isendwin() == true { // Not in ncurses mode? print(format, ..args); print("\n"); -// } -// else { -// int w_size_x = size_x > 120 ? 120 : size_x - 2; -// int w_size_y = 4; -// 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); -// wattron(error_window, COLOR_PAIR(ERROR)); -// wborder(error_window, ' ', ' ', 0, 0, ACS_HLINE, ACS_HLINE, ACS_HLINE, ACS_HLINE); -// mvwprintw(error_window, 0, 1, " Error "); -// wmove(error_window, 1, 0); -// } -// else { -// waddch(error_window, ' '); -// } -// vw_printw(error_window, format, args); -// error_time_limit = time(NULL) + 5; // NOTE Instead of time use the current_time_monotonic() -// } + } + else { + CHAR_SPACE :: #char " "; + w_size_x: s32 = ifx size_x > 120 then 120 else size_x - 2; + w_size_y: s32 = 4; + 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); + wattron(error_window, COLOR_PAIR(xx Styles.ERROR)); + wborder(error_window, CHAR_SPACE, CHAR_SPACE, 0, 0, ACS_HLINE, ACS_HLINE, ACS_HLINE, ACS_HLINE); + mvwprintw(error_window, 0, 1, " Error "); + wmove(error_window, 1, 0); + } + else { + waddch(error_window, CHAR_SPACE); + } + vw_printw(error_window, to_c_string(format), args); + error_time_limit = current_time_monotonic() + seconds_to_apollo(5); + } } draw_error_window :: () { - /* NOTE Implement me please. - if (error_window == NULL) { - return; - } + 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 + w_size_x: s32; + w_size_y: s32; + getmaxyx(error_window, *w_size_y, *w_size_x); + if (current_time_monotonic() >= error_time_limit || size_x - w_size_x < 2 || size_y - w_size_y < 2 ) { delwin(error_window); - error_window = NULL; + 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; + pos_x := (size_x - w_size_x) / 2; + 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); - */ } trigger_autosave :: () { @@ -240,12 +234,6 @@ is_empty_string :: (str: string) -> bool { return true; } -replace_char :: (str: string, find: u8, replace: u8) -> string { - assert(false, "Use modules/String/module.jai:replace_chars"); - // TODO Use modules/String/module.jai:replace_chars - return ""; -} - // Prints, on row y and column x, the time using 5 characters centered on space. // Returns the result of a call to mvprintw. mvprintw_time :: (y: s32, x: s32, time: s64, space: s32) -> int { @@ -306,23 +294,16 @@ sub_int64 :: (x :s64, y :s64) -> s64 { // Returns active task or NULL if none applies. get_active_task :: inline (db: Database) -> *Task { - task: *Task = null; - if (db.active_idx >= 0) { - task = *db.tasks[db.active_idx]; - } - return task; + return ifx db.active_idx >= 0 then *db.tasks[db.active_idx] else null; } // Returns selected task or NULL if none applies. get_selected_task :: inline (db: Database) -> *Task { - task: *Task = null; - if (db.selected_idx >= 0) { - task = *db.tasks[db.selected_idx]; - } - return task; + return ifx db.selected_idx >= 0 then *db.tasks[db.selected_idx] else null; } -contains_task :: inline (db: *Database, task: *Task) -> bool { +// TODO Add description. +contains_task :: inline (db: Database, task: *Task) -> bool { return task >= db.tasks.data && task - db.tasks.data < db.tasks.count; } @@ -331,37 +312,14 @@ contains_task :: inline (db: *Database, task: *Task) -> bool { add_task :: (db: *Database, task: Task = .{}) -> task: *Task { assert(db != null, "Parameter 'db' is null."); array_add(*db.tasks, task); - return *db.tasks[db.tasks.count-1]; -} -/* -// Duplicates the given task. Duplicated task is appended to the database. -// Returns success. -bool duplicate_task(database_st *db, task_st *task) { - assert(db != NULL); - assert(task != NULL); - - // Create new task and keep task_idx (relative pointer) of original task). - ptrdiff_t task_idx = task - db->tasks; - task_st *new_task; - if (create_task(db, &new_task) == false) { - return false; - } - - // If original task belonged to database, fix its pointer. - if (0 <= task_idx && task_idx < db->count - 1) { // Compensate '- 1' for the new task. - task = db->tasks + task_idx; - } - - memcpy(new_task, task, SIZEOF_TASK_ST); - // Add task time values to total times. - for (int idx = 0; idx < NUM_WEEK_DAYS; idx++) { - db->total_times[idx] = add_int64(db->total_times[idx], new_task->times[idx]); + for * db.total_times { + <<it = add_int64(<<it, task.times[it_index]); } - return true; + return *db.tasks[db.tasks.count-1]; } -*/ + // Deletes task from database. // If possible, shrinks the database capacity. // Returns success. @@ -425,88 +383,86 @@ delete_task :: (db: *Database, task: *Task) -> bool { return true; } -/* + // Moves task to index. // Index gets clamped to [0, db->count[. -void move_task_to_index(database_st *db, task_st *task, ptrdiff_t index) { - assert(db != NULL); - assert(task != NULL); - assert(task >= db->tasks && task - db->tasks < db->count); - - ptrdiff_t target_index = index < 0 ? 0 - : index >= db->count ? db->count - 1 - : index; +move_task_to_index :: (db: *Database, task: *Task, index: s64) { + assert(db != null, "Parameter 'db' is null."); + assert(task != null, "Parameter 'task' is null."); + assert(contains_task(db, task), "Database does not contain task."); - task_st *target_task = db->tasks + target_index; + target_index := clamp(index, 0, db.tasks.count-1); + target_task := *db.tasks[target_index]; - if (target_task == task) { - return; - } + if (target_task == task) return; // Move task to new location. - task_st temp_task; - memcpy(&temp_task, task, SIZEOF_TASK_ST); - if (target_task > task) { - memmove(task, task + 1, (target_task - task) * SIZEOF_TASK_ST); + temp_task := <<task; + if target_task > task { + //memmove(task, task + 1, (target_task - task) * SIZEOF_TASK_ST); TODO Maybe simplify this moves. + offset := task - db.tasks.data; + size := target_task - task; + for 0..size-1 + db.tasks[offset + it] = db.tasks[offset + it + 1]; } else { - memmove(target_task + 1, target_task, (task - target_task) * SIZEOF_TASK_ST); + //memmove(target_task + 1, target_task, (task - target_task) * SIZEOF_TASK_ST); TODO Maybe simplify this moves. + offset := target_task - db.tasks.data; + size := task - target_task; + for < size-1..0 + db.tasks[offset + it + 1] = db.tasks[offset + it]; } - memcpy(target_task, &temp_task, SIZEOF_TASK_ST); + <<target_task = temp_task; // Adjust active and selected tasks. - ptrdiff_t source_index = task - db->tasks; - if (db->active_task == source_index) { - db->active_task = target_index; + source_index := task - db.tasks.data; + if (db.active_idx == source_index) { + db.active_idx = target_index; } - else if (source_index < db->active_task && db->active_task <= target_index) { - db->active_task--; + else if (source_index < db.active_idx && db.active_idx <= target_index) { + db.active_idx -= 1; } - else if (target_index <= db->active_task && db->active_task < source_index) { - db->active_task++; + else if (target_index <= db.active_idx && db.active_idx < source_index) { + db.active_idx += 1; } - db->selected_task = target_index; + db.selected_idx = target_index; } -*/ + // Updates the times on the active task (and adjusts database totals). update_times :: (db: *Database) { assert(db != null); - return; - /* - // 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_idx < 0 return; - // Keep track of this update. - db->modified_on = stop_time; - - if (db->active_task < 0) { - return; - } + // Get time frame in UTC. + start_time := db.modified_on; + stop_time := current_time_consensus(); - task_st *active_task = db->tasks + db->active_task; - uint8_t start_week_day; + active_task := *db.tasks[db.active_idx]; + start_week_day: s8; while (start_time < stop_time) { - start_week_day = localtime(&start_time)->tm_wday; + start_week_day = to_calendar(start_time, .LOCAL).day_of_week_starting_at_0; // 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_of_day_cal := to_calendar(start_time, .LOCAL); + start_of_day_cal.hour = 0; + start_of_day_cal.minute = 0; + start_of_day_cal.second = 0; + start_of_day_cal.millisecond = 0; + start_of_day := calendar_to_apollo(start_of_day_cal); + + next_day := start_of_day + #run seconds_to_apollo(SECONDS_IN_DAY); + next_start := ifx next_day < stop_time then next_day else stop_time; + elapsed_time := to_seconds(next_start - start_time); + active_task.times[start_week_day] += elapsed_time; + db.total_times[start_week_day] += elapsed_time; start_time = next_start; } - */ + + // Keep track of this update. + db.modified_on = stop_time; } // Recalculates database totals. @@ -797,37 +753,35 @@ import_from_csv :: (db: *Database, path: string) -> bool { return true; } -/* + // Appends task to the end of the CSV file. // Returns success. -bool append_to_csv(task_st *task, const char *path) { - assert(task != NULL); - assert(path != NULL); +append_to_csv :: (task: Task, path: string) -> success: bool { + assert(xx path, "Parameter 'path' is empty."); - FILE *file = fopen(path, "a+"); - if (file == NULL) { - print_error("Failed to open file '%s' while appending to CSV: %s.", path, strerror(errno)); + file, file_success := file_open(path, true, true); + defer file_close(*file); + if file_success == false { + //print_error("Failed to open file '%s' while appending to CSV: %s.", path, strerror(errno)); // TODO Show internal error or something return false; } - char last_char; - fseek(file, -1, SEEK_END); - fread(&last_char, SIZEOF_CHAR, 1, file); - if (last_char != '\n') { - fprintf(file, "\n"); + file_size := file_length(file); + file_set_position(file, file_size-1); + last_char: u8; + file_read(file, *last_char, 1); + if (last_char != #char "\n") { + file_write(*file, "\n"); } - char name[TASK_NAME_BYTES]; - memcpy(name, task->name, TASK_NAME_BYTES); - replace_char(name, ',', ' '); - fprintf(file, "%s,%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 "\n", - name, task->times[0], task->times[1], task->times[2], task->times[3], task->times[4], task->times[5], task->times[6] - ); + task_name := copy_temporary_string(xx task.name); // TODO Cleanup this temp mess. + replace_chars(task_name, ",", #char " "); + csv_line := tprint("%,%,%,%,%,%,%,%\n", task_name, task.times[0], task.times[1], task.times[2], task.times[3], task.times[4], task.times[5], task.times[6]); + file_write(*file, csv_line); - fclose(file); return true; } -*/ + // Selects task by index. // Index gets clamped to [0, db->count[. select_task_by_index :: (db: *Database, index: s64) { @@ -1157,33 +1111,22 @@ draw_tui :: (db: *Database, layout: *Layout) { x += 1; mvprintw_time(xx y, xx x, total_time, xx layout.columns[L_TOTAL_IDX].width); } -/* -void *mem_alloc(size_t mem_size, const char *error_tag) { - void *mem_pointer = malloc(mem_size); - if (mem_pointer == NULL && mem_size > 0) { - print_error("Failed to allocate memory (%s): %s.", (error_tag == NULL ? "undefined" : error_tag), strerror(errno)); - exit(EXIT_FAILURE); - } - return mem_pointer; -} -*/ free_memory :: () { reset_database(*database); reset_database(*archive); - //free(string_buffer); string_buffer = NULL; free(app_directory); free(db_file_path); free(ar_file_path); //reset_temporary_storage(); } -read_input_with_space :: (row: int, column: int, style: s32, length: int, space: int) -> string { +read_input_string_padded :: (row: int, column: int, style: s32, length: int, padding: int) -> string { str := talloc_string(length); memset(str.data, 0, str.count); attron(style | A_UNDERLINE); - mvprintw(xx row, xx column, "%*s", space, ""); + mvprintw(xx row, xx column, "%*s", padding, ""); echo(); curs_set(1); mvgetnstr(xx row, xx column, str.data, xx length); @@ -1193,37 +1136,28 @@ read_input_with_space :: (row: int, column: int, style: s32, length: int, space: attrset(A_NORMAL); return str; } -/* -void read_input_to_string_buffer(int row, int column, int style, int length) { - read_input_to_string_buffer_with_space(row, column, style, length, length); + +read_input_string :: (row: int, column: int, style: s32, length: int) -> string { + return read_input_string_padded(row, column, style, length, length); } // Returns success. -bool read_input_to_int(int row, int style, const char *message, intmax_t *result) { - assert(message != NULL); - assert(result != NULL); - - attron(style); - move(row, 1); +read_input_int :: (row: int, style: s32, message: string) -> value: int, success: bool { + attron(xx style); + move(xx row, 1); addch(ACS_CKBOARD); - addstr(message); + addstr(message.data); // TODO Convert to C type attrset(A_NORMAL); // Get line number. - int input_pos_x = getcurx(stdscr); - int input_width = size_x - input_pos_x - 1; - read_input_to_string_buffer(row, input_pos_x, style, input_width); + input_pos_x := getcurx(stdscr); + input_width := size_x - input_pos_x - 1; + str := read_input_string(row, input_pos_x, style, input_width); - char *parser; - errno = 0; - *result = strtoimax(string_buffer, &parser, 10); - - bool success = (errno == 0 || errno == ERANGE) // No error OR value was clamped to limits (acceptable). - && parser != string_buffer; // If no digits are found, parser will return the address of the input string. - - return success; + value, success := parse_int(*str); + return value, success; } -*/ + // Retuns true if user presses enter, false otherwise. read_enter_confirmation :: (row: int, style: int, message: string) -> bool { assert(message.data != null); @@ -1465,17 +1399,6 @@ main :: () { clear(); getmaxyx(stdscr, *size_y, *size_x); is_terminal_too_small = size_x < 60 || size_y < 3; - new_size := 2047 | TASK_NAME_BYTES | (size_x + 1); - //if (string_buffer_size < new_size) { - //string_buffer_size = new_size; - //string_buffer = realloc(string_buffer, string_buffer_size); - //if (string_buffer == NULL && string_buffer_size > 0) { - //print_error("Failed to allocate memory for string buffer: %s.", strerror(errno)); - //flushinp(); - //ungetch(#char "q"); - //break; - //} - //} update_layout(); layout = *layouts[ifx size_x > 100 then Layouts.NORMAL else Layouts.COMPACT]; @@ -1506,8 +1429,7 @@ main :: () { if (selected_task == null) continue; // Change task name. - // TODO remove TASK_NAME_LENGTH - input := read_input_with_space(selected_task_row, 1, action_style, TASK_NAME_LENGTH, size_x - 2); + input := read_input_string_padded(selected_task_row, 1, action_style, Task.name.count, size_x - 2); if is_empty_string(input) == false { replace_chars(input, "\t\x0B\x0C\r", #char " "); memcpy(selected_task.name.data, input.data, min(Task.name.count, input.count)); @@ -1615,58 +1537,41 @@ main :: () { trigger_autosave(); break; } - - case 'm': - case 'M': { - if (selected_task == NULL) { - break; - } +*/ + case #char "m"; #through; + case #char "M"; + if selected_task == null continue; - intmax_t value; - if (read_input_to_int(selected_task_row, action_style, " Move to: ", &value) == false) { - break; - } + value, success := read_input_int(selected_task_row, action_style, " Move to: "); + if success == false continue; - ptrdiff_t target_index = (value < 1 ? 1 : value > MAX_DATABASE_TASKS ? MAX_DATABASE_TASKS : value) - 1; + target_index := clamp(value, 1, MAX_DATABASE_TASKS) - 1; move_task_to_index(db, selected_task, target_index); trigger_autosave(); - break; - } - - case 'g': - case 'G': { - if (selected_task == NULL) { - break; - } + + case #char "g"; #through; + case #char "G"; + if selected_task == null continue; - intmax_t value; - if (read_input_to_int(selected_task_row, action_style, " Go to: ", &value) == false) { - break; - } + value, success := read_input_int(selected_task_row, action_style, " Go to: "); + if success == false continue; - ptrdiff_t target_index = (value < 1 ? 1 : value > MAX_DATABASE_TASKS ? MAX_DATABASE_TASKS : value) - 1; + target_index := clamp(value, 1, MAX_DATABASE_TASKS) - 1; select_task_by_index(db, target_index); - break; - } - case 'd': - case 'D':{ - if (selected_task == NULL) { - break; - } + case #char "d"; #through; + case #char "D"; + if selected_task == null continue; - if (is_database_full(db)) { + if is_database_full(db) { read_enter_confirmation(selected_task_row, error_style, " Unable to duplicate entry: database is full. "); - break; + continue; } - if (duplicate_task(db, selected_task) == false) { - break; - } + if (add_task(db, selected_task) == null) continue; // TODO Show error? + trigger_autosave(); - break; - } - */ + case KEY_F5; update_total_times(db); trigger_autosave(); @@ -1700,39 +1605,34 @@ main :: () { reset_database(*archive); db = *database; } -/* - case 'a': - case 'A': { - if (db != &database || selected_task == NULL || selected_task == active_task) { - break; - } + + case #char "a"; #through; + case #char "A"; + if (db != *database || selected_task == null || selected_task == active_task) continue; + if (append_to_csv(selected_task, ar_file_path) == false) { print_error("Failed to archive entry."); - break; + continue; } - delete_task(&database, selected_task); + delete_task(*database, selected_task); trigger_autosave(); - break; - } - - case 'r': - case 'R': { - if (db != &archive || selected_task == NULL) { - break; - } - if (is_database_full(&database)) { + + case #char "r"; #through; + case #char "R"; + if (db != *archive || selected_task == null) continue; + + if is_database_full(*database) { read_enter_confirmation(selected_task_row, error_style, " Unable to restore entry: database is full. "); - break; + continue; } - if (duplicate_task(&database, selected_task) == false) { + + if (add_task(*database, selected_task) == null) { print_error("Failed to restore entry."); - break; + continue; } - delete_task(&archive, selected_task); + delete_task(*archive, selected_task); trigger_autosave(); - break; - } - */ + case KEY_HOME; select_task_by_index(db, 0); @@ -1754,26 +1654,26 @@ main :: () { } // Save any unsaved changes. - show_processing(); + show_processing(); error_saving := false; - if (db == *archive) { - if (export_to_csv(archive, ar_file_path) == false) { - print_error("Failed to save archive."); - error_saving |= true; - } - } - if (countdown_to_autosave > 0 || is_autosave_enabled == false) { - if (store_database(database, db_file_path) == false) { - print_error("Failed to save database."); - error_saving |= true; - } - } - if (error_saving) { - print_error("Press any key to close."); - draw_error_window(); - timeout(INPUT_AWAIT_INF); - getch(); - } + if (db == *archive) { + if (export_to_csv(archive, ar_file_path) == false) { + print_error("Failed to save archive."); + error_saving |= true; + } + } + if (countdown_to_autosave > 0 || is_autosave_enabled == false) { + if (store_database(database, db_file_path) == false) { + print_error("Failed to save database."); + error_saving |= true; + } + } + if (error_saving) { + print_error("Press any key to close."); + draw_error_window(); + timeout(INPUT_AWAIT_INF); + getch(); + } endwin(); exit(xx ifx error_saving then 1 else 0); |
