diary

Text-based journaling program
git clone https://git.in0rdr.ch/diary.git
Log | Files | Refs | README | LICENSE

commit 7091d93fe18300c3bebff343ce1cb7164cfb2922
parent 9aada882f5a7cc8c268adcef42ca4963d5f66850
Author: in0rdr <andreas.gruhler@uzh.ch>
Date:   Wed, 16 Nov 2016 23:09:01 +0800

diary.c

Diffstat:
Adiary.c | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+), 0 deletions(-)

diff --git a/diary.c b/diary.c @@ -0,0 +1,244 @@ +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ncurses.h> + +#define YEAR_RANGE 1 +#define CAL_WIDTH 21 +#define ASIDE_WIDTH 4 +#define MAX_MONTH_HEIGHT 6 + +int cy, cx; +time_t raw_time; +struct tm cur_date; +struct tm curs_date; +char curs_date_str[70]; +struct tm cal_start; +struct tm cal_end; + +void draw_wdays(WINDOW* head) { + char* weekdays[] = {"Mo","Tu","We","Th","Fr","Sa","Su"}; + for (int wd = 0; wd < sizeof(weekdays)/sizeof(weekdays[0]); wd++) { + waddstr(head, weekdays[wd]); + waddch(head, ' '); + } + wrefresh(head); +} + +void draw_calendar(WINDOW* number_pad, WINDOW* month_pad) { + struct tm i = cal_start; + char month[10]; + + while (timelocal(&i) <= timelocal(&cal_end)) { + getyx(number_pad, cy, cx); + mvwprintw(number_pad, cy, cx, "%2i", i.tm_mday); + waddch(number_pad, ' '); + + if (i.tm_mday == 1) { + strftime(month, sizeof month, "%b", &i); + getyx(number_pad, cy, cx); + mvwprintw(month_pad, cy, 0, "%s ", month); + } + + i.tm_mday++; + mktime(&i); + } +} + +void update_date(WINDOW* dh) { + mktime(&curs_date); + strftime(curs_date_str, sizeof curs_date_str, "%Y-%m-%d", &curs_date); + mvwaddstr(dh, 0, 0, curs_date_str); + wrefresh(dh); +} + +char* curs_date_file_path(char* dir) { + static char path[100]; + + strcpy(path, dir); + if (dir[strlen(dir) - 1] != '/') + strcat(path, "/"); + strcat(path, curs_date_str); + + return path; +} + +char* curs_date_edit_cmd(char* dir) { + static char edit_cmd[150]; + char* editor = getenv("EDITOR"); + if (editor == NULL) + return NULL; + + strcpy(edit_cmd, editor); + strcat(edit_cmd, " "); + strcat(edit_cmd, curs_date_file_path(dir)); + + return edit_cmd; +} + +bool is_leap(int year) { + // normally leap is every 4 years, + // but is skipped every 100 years, + // unless it is divisible by 400 + if (year % 400 == 0) + return true; + if (year % 4 == 0 && year % 100 != 0) + return true; + + return false; +} + +void read_diary(char* dir) { + int width = COLS - ASIDE_WIDTH - CAL_WIDTH; + WINDOW* prev = newpad(LINES - 1, width); + + wclear(prev); + char buff[width]; + int i = 0; + char* path = curs_date_file_path(dir); + + FILE* fp = fopen(path, "r"); + if (fp != NULL) { + while(fgets(buff, width, fp) != NULL) { + mvwprintw(prev, i, 0, buff); + i++; + } + fclose(fp); + } + prefresh(prev, 0, 0, 1, CAL_WIDTH + ASIDE_WIDTH, LINES, COLS); +} + +bool go_to(WINDOW* calendar, WINDOW* month_sidebar, time_t date, int* cur_pad_pos) { + if (date < timelocal(&cal_start) || date > timelocal(&cal_end)) + return false; + + int diff_seconds = date - timelocal(&cal_start); + int diff_days = diff_seconds / 60 / 60 / 24; + int diff_weeks = diff_days / 7; + int diff_wdays = diff_days % 7; + + localtime_r(&date, &curs_date); + + getyx(calendar, cy, cx); + mvwchgat(calendar, cy , 0 , -1, A_NORMAL, 0, NULL); + mvwchgat(calendar, diff_weeks, diff_wdays * 3, 2, A_STANDOUT, 0, NULL); + + if (diff_weeks < *cur_pad_pos) + *cur_pad_pos = diff_weeks; + if (diff_weeks > *cur_pad_pos + LINES - 2) + *cur_pad_pos = diff_weeks - LINES + 2; + prefresh(month_sidebar, *cur_pad_pos, 0, 1, 0, LINES - 1, ASIDE_WIDTH); + prefresh(calendar, *cur_pad_pos, 0, 1, ASIDE_WIDTH, LINES - 1, ASIDE_WIDTH + CAL_WIDTH); + + return true; +} + +void setup_cal_timeframe() { + raw_time = time(NULL); + localtime_r(&raw_time, &cur_date); + curs_date = cur_date; + + cal_start = cur_date; + cal_start.tm_year -= YEAR_RANGE; + cal_start.tm_mon = 0; + cal_start.tm_mday = 1; + mktime(&cal_start); + + if (cal_start.tm_wday != 1) { + // adjust start date to first Mon before 01.01 + cal_start.tm_year--; + cal_start.tm_mon = 11; + cal_start.tm_mday = 31 - cal_start.tm_wday + 2; + mktime(&cal_start); + } + + cal_end = cur_date; + cal_end.tm_year += YEAR_RANGE; + cal_end.tm_mon = 11; + cal_end.tm_mday = 31; + mktime(&cal_end); +} + +int main(int argc, char** argv) +{ + if (argc < 2) { + printf("Expect diary directory as command line argument\n"); + return 0; + } + + setup_cal_timeframe(); + + initscr(); + raw(); + curs_set(0); + + WINDOW* date_header = newwin(1, 10, 0, ASIDE_WIDTH + CAL_WIDTH); + wattron(date_header, A_BOLD); + update_date(date_header); + WINDOW* wdays_header = newwin(1, 3 * 7, 0, ASIDE_WIDTH); + draw_wdays(wdays_header); + + WINDOW* aside = newpad((YEAR_RANGE * 2 + 1) * 12 * MAX_MONTH_HEIGHT, ASIDE_WIDTH); + WINDOW* cal = newpad((YEAR_RANGE * 2 + 1) * 12 * MAX_MONTH_HEIGHT, CAL_WIDTH); + keypad(cal, TRUE); + draw_calendar(cal, aside); + + int ch; + struct tm new_date; + char* diary_dir = argv[1]; + // init the current pad possition at the very end, + // such that the cursor is displayed top of screen + int pad_pos = 9999999; + + wmove(cal, 0, 0); + getyx(cal, cy, cx); + bool ret = go_to(cal, aside, raw_time, &pad_pos); + read_diary(diary_dir); + + do { + ch = wgetch(cal); + new_date = curs_date; + char* ecmd = curs_date_edit_cmd(diary_dir); + + switch(ch) { + case 'j': + case KEY_DOWN: + new_date.tm_mday += 7; + ret = go_to(cal, aside, timelocal(&new_date), &pad_pos); + break; + case 'k': + case KEY_UP: + new_date.tm_mday -= 7; + ret = go_to(cal, aside, timelocal(&new_date), &pad_pos); + break; + case 'l': + case KEY_RIGHT: + new_date.tm_mday++; + ret = go_to(cal, aside, timelocal(&new_date), &pad_pos); + break; + case 'h': + case KEY_LEFT: + new_date.tm_mday--; + ret = go_to(cal, aside, timelocal(&new_date), &pad_pos); + break; + case 't': + new_date = cur_date; + ret = go_to(cal, aside, raw_time, &pad_pos); + break; + case 'e': + if (ecmd) + system(ecmd); + curs_set(0); + break; + } + + if (ret) { + update_date(date_header); + read_diary(diary_dir); + } + } while (ch != 'q'); + + endwin(); + system("clear"); + return 0; +}