X-Git-Url: http://pileus.org/git/?p=lackey;a=blobdiff_plain;f=src%2Fview.c;h=a66fb853dcfe17ae59af7305744dab1f0754cb6d;hp=ac33fc87f784b2c9c53359b69fde1cea0841aaf2;hb=9f9ede043dd04a20245f060bc6c89dd109ced36f;hpb=ef789a1e6c0fa2262690f7c19824d2829f1e2a3f diff --git a/src/view.c b/src/view.c index ac33fc8..a66fb85 100644 --- a/src/view.c +++ b/src/view.c @@ -1,86 +1,270 @@ /* - * Copyright (C) 2012 Andy Spencer - * + * Copyright (C) 2012-2013 Andy Spencer + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED +#include #include +#include #include #include "util.h" +#include "conf.h" #include "date.h" #include "cal.h" #include "view.h" /* Types */ typedef struct { - char *name; - void (*init)(WINDOW*); - void (*size)(int,int); - void (*draw)(void); - int (*run)(int,mmask_t,int,int); - int keys[8]; - WINDOW *win; + const char *name; + const char *title; + void (*init)(WINDOW*); + void (*size)(int,int); + void (*draw)(void); + int (*run)(int,mmask_t,int,int); + int keys[8]; + WINDOW *win; } view_t; -/* Data */ -view_t views[] = { - { "Day", day_init, day_size, day_draw, day_run, {KEY_F(1), '1', } }, - { "Week", week_init, week_size, week_draw, week_run, {KEY_F(2), '2', } }, - { "Month", month_init, month_size, month_draw, month_run, {KEY_F(3), '3', } }, - { "Year", year_init, year_size, year_draw, year_run, {KEY_F(4), '4', } }, - { "|", NULL, NULL, NULL, NULL, { } }, - { "Events", events_init, events_size, events_draw, events_run, {KEY_F(5), '5', } }, - { "Todo", todo_init, todo_size, todo_draw, todo_run, {KEY_F(6), '6', } }, - { "|", NULL, NULL, NULL, NULL, { } }, - { "Settings", settings_init, settings_size, settings_draw, settings_run, {KEY_F(7), '7', } }, - { "Help", help_init, help_size, help_draw, help_run, {KEY_F(8), '8', '?'} }, +/* Macros */ +#define VIEW(name, title, ...) \ + void name##_init(WINDOW *win); \ + void name##_size(int,int); \ + void name##_draw(void); \ + int name##_run(int,mmask_t,int,int); \ + view_t name##_view = { \ + #name, \ + title, \ + name##_init, \ + name##_size, \ + name##_draw, \ + name##_run, \ + { __VA_ARGS__ } \ + } + +/* Views */ +VIEW(day, "Day", KEY_F(1), '1'); +VIEW(week, "Week", KEY_F(2), '2'); +VIEW(month, "Month", KEY_F(3), '3'); +VIEW(year, "Year", KEY_F(4), '4'); +VIEW(events, "Events", KEY_F(5), '5'); +VIEW(todo, "Todo", KEY_F(6), '6'); +VIEW(settings, "Settings", KEY_F(7), '7'); +VIEW(help, "Help", KEY_F(8), '8'); +VIEW(edit, "Edit"); + +/* View data */ +view_t spacer = { "|", "|" }; + +view_t *views[] = { + &day_view, &week_view, &month_view, &year_view, + &events_view, &todo_view, + &settings_view, &help_view, + &edit_view +}; + +view_t *menu[] = { + &day_view, &week_view, &month_view, &year_view, + &spacer, &events_view, &todo_view, + &spacer, &settings_view, &help_view }; -int active = 6; +/* Config data */ +int COMPACT = 0; +int MORNING = 8; +int WEEKENDS = 0; + +/* Global data */ +edit_t EDIT = EDIT_NONE; + +/* Local data */ +view_t *view = &day_view; +view_t *active = &day_view; +view_t *popup = NULL; +int running = 0; /* Local functions */ static void draw_header(void) { move(0, 0); attron(COLOR_PAIR(COLOR_TITLE)); - for (int i = 0; i < N_ELEMENTS(views); i++) { - if (i == active) + clrtoeol(); + + /* Draw menu */ + for (int i = 0; i < N_ELEMENTS(menu); i++) { + if (menu[i] == active) attron(A_BOLD); - printw("%s ", views[i].name); - if (i == active) + printw("%s ", menu[i]->title); + if (menu[i] == active) attroff(A_BOLD); } + + /* Draw popup window */ + if (popup) { + printw("| "); + attron(A_BOLD); + printw("[%s]", popup->title); + attroff(A_BOLD); + } + + /* Draw date */ + move(0, COLS-19); + printw("%04d-%02d-%02d %02d:%02d:%02d", + NOW.year, NOW.month+1, NOW.day+1, + NOW.hour, NOW.min, NOW.sec); + attroff(COLOR_PAIR(COLOR_TITLE)); - mvhline(1, 0, ACS_HLINE, COLS); + if (!COMPACT) + mvhline(1, 0, ACS_HLINE, COLS); refresh(); } static int get_color(const char *cat) { return cat == NULL ? 0 : - !strcmp(cat, "class") ? COLOR_CLASS : - !strcmp(cat, "ec") ? COLOR_EC : - !strcmp(cat, "work") ? COLOR_WORK : COLOR_OTHER ; + match(cat, "class") ? COLOR_CLASS : + match(cat, "ec") ? COLOR_EC : + match(cat, "work") ? COLOR_WORK : COLOR_OTHER ; +} + +static void update_sizes(void) +{ + int hdr = COMPACT ? 1 : 2; + for (int i = 0; i < N_ELEMENTS(views); i++) { + wresize(views[i]->win, LINES-hdr, COLS); + mvwin(views[i]->win, hdr, 0); + views[i]->size(LINES-hdr, COLS); + } +} + +static void draw_view(void) +{ + draw_header(); + werase(view->win); + view->draw(); + wrefresh(view->win); +} + +static int set_view(view_t *_active, view_t *_popup) +{ + view = _popup ?: _active; + if (active != _active) { + active = _active; + set_string("view", 0, "active", active->name); + draw_view(); + } + if (popup != _popup) { + popup = _popup; + draw_view(); + } + return 1; +} + +static int process(int key, mmask_t btn, int row, int col) +{ + /* Refresh timestamp */ + draw_header(); + + /* Check for mouse events on the menu */ + if (key == KEY_MOUSE && row == 0) { + int start = 1; + for (int i = 0; i < N_ELEMENTS(menu); i++) { + int end = start + strlen(menu[i]->name) - 1; + if (start <= col && col <= end && menu[i]->draw) + return set_view(menu[i], NULL); + start = end + 2; + } + } + + /* Look though menu for hotkeys */ + for (int i = 0; i < N_ELEMENTS(menu); i++) { + for (int j = 0; j < N_ELEMENTS(menu[i]->keys); j++) + if (menu[i]->keys[j] == key) + return set_view(menu[i], NULL); + } + + /* Shift windows with left/right keys */ + int shift = key == KEY_RIGHT ? +1 : + key == KEY_LEFT ? -1 : 0; + if (shift) { + int num = 0; + for (int i = 0; i < N_ELEMENTS(menu); i++) + if (menu[i] == active) + num = i; + do { + num += shift; + num += N_ELEMENTS(menu); + num %= N_ELEMENTS(menu); + } while (menu[num] == &spacer); + return set_view(menu[num], NULL); + } + + /* Handle other keys */ + switch (key) { + case KEY_RESIZE: + endwin(); + refresh(); + update_sizes(); + draw_view(); + return 1; + case '\14': // Ctrl-L + clear(); + case '\7': // Ctrl-G + update_sizes(); + draw_view(); + return 1; + case '\033': // escape + return set_view(active, NULL); + case '?': // help + return set_view(active, &help_view); + } + + /* Pass key to active view */ + return view->run(key, btn, row, col); +} + +/* Curses functions */ +void wmvresize(WINDOW *win, int top, int left, int rows, int cols) +{ + int y = getpary(win); + if (top < y) + mvderwin(win, top, left); + wresize(win, rows, cols); + if (top > y) + mvderwin(win, top, left); +} + +void wshrink(WINDOW *win, int top) +{ + int x = getparx(win); + int y = getpary(win); + int r = getmaxy(win); + int c = getmaxx(win); + int rows = r + (y - top); + if (top < y) mvderwin(win, top, x); + if (rows != r) wresize(win, rows, c); + if (top > y) mvderwin(win, top, x); } /* Helper functions */ void event_box(WINDOW *win, event_t *event, int y, int x, int h, int w) { - int l = 0; + int i, l = 0; int s = y < 0 ? -y-1 : 0; int color = get_color(event->cat); @@ -97,14 +281,21 @@ void event_box(WINDOW *win, event_t *event, int y, int x, int h, int w) if (h >= 2) mvwadd_wch(win, y+h-1, x+w-1, WACS_T_LRCORNER); if (h >= 2) mvwhline_set(win, y+h-1, x+1, WACS_T_HLINE, w-2); + for (i = 1; i < h-1; i++) + mvwhline(win, y+i, x+1, ' ', w-2); + if (color) wattroff(win, COLOR_PAIR(color)); + if (event == EVENT) wattron(win, WA_BOLD | WA_REVERSE); + if (event == EVENT) mvwhline(win, y+s, x, ' ', w); if (lname) mvwprintw(win, y+l++, x+1, "%.*s", w-2, event->name); + if (event == EVENT) wattroff(win, WA_REVERSE); if (lloc) mvwprintw(win, y+l++, x+1, "@ %.*s", w-4, event->loc); if (ldesc) mvwprintw(win, y+l++, x+1, "%.*s", w-2, event->desc); + if (event == EVENT) wattroff(win, WA_BOLD); } -void event_line(WINDOW *win, event_t *event, int y, int x, int w, int full) +void event_line(WINDOW *win, event_t *event, int y, int x, int w, int flags) { int color = get_color(event->cat); @@ -112,115 +303,195 @@ void event_line(WINDOW *win, event_t *event, int y, int x, int w, int full) mvwaddch(win, y, x++, ACS_BLOCK); if (color) wattroff(win, COLOR_PAIR(color)); - if (full) { - mvwprintw(win, y, x, " %02d:%02d - ", event->start.hour, event->start.min); - x += 9; + if (flags & SHOW_ACTIVE && event == EVENT) + wattron(win, A_REVERSE | A_BOLD); + if (flags & SHOW_DETAILS) { + if (all_day(&event->start, &event->end)) + mvwprintw(win, y, x+1, "[all day] - "); + else + mvwprintw(win, y, x+1, "%2d:%02d-%2d:%02d - ", + event->start.hour, event->start.min, + event->end.hour, event->end.min); + x += 15; + w -= 15; } if (event->name) { const char *label = event->name ?: event->desc; mvwprintw(win, y, x, "%-*.*s", w-1, w-1, label); x += MIN(strlen(label), w-1); } - if (full && event->loc) { + if (flags & SHOW_DETAILS && event->loc) { mvwprintw(win, y, x, " @ %s", event->loc); } + if (flags & SHOW_ACTIVE && event == EVENT) + wattroff(win, A_REVERSE | A_BOLD); } -void todo_line(WINDOW *win, todo_t *todo, int y, int x, int w, int full) +void todo_line(WINDOW *win, todo_t *todo, int y, int x, int w, int flags) { char perc[16]; + char desc[LINES]; sprintf(perc, "%2d%%", todo->status); - int color = get_color(todo->cat); - if (color) wattron(win, COLOR_PAIR(color)); - mvwaddch(win, y, 2, ACS_BLOCK); - if (color) wattroff(win, COLOR_PAIR(color)); + int cat = get_color(todo->cat); + int status = todo->status == NEW ? COLOR_NEW : + todo->status == DONE ? COLOR_DONE : COLOR_WIP; + + sprintf(desc, "%s", todo->name ?: todo->desc ?: ""); + strsub(desc, '\n', ';'); - mvwprintw(win, y, 4, "%04d-%02d-%02d %2d:%02d", - todo->due.year, todo->due.month+1, todo->due.day+1, - todo->due.hour, todo->due.min); - mvwprintw(win, y, 22, "%s", - todo->status == NEW ? "new" : - todo->status == DONE ? "done" : perc); - mvwprintw(win, y, 30, "%s: %s", todo->name, todo->desc); + /* Print category */ + if (cat) wattron(win, COLOR_PAIR(cat)); + mvwaddch(win, y, x, ACS_BLOCK); + if (cat) wattroff(win, COLOR_PAIR(cat)); + x += 2; + + /* Set background */ + if (flags & SHOW_ACTIVE && todo == TODO) + wattron(win, A_REVERSE | A_BOLD); + mvwhline(win, y, x, ' ', COLS-x); + + /* Print time */ + if (no_date(&todo->due)) + mvwprintw(win, y, x, "[no due date]"); + else + mvwprintw(win, y, x, "%04d-%02d-%02d %2d:%02d", + todo->due.year, todo->due.month+1, todo->due.day+1, + todo->due.hour, todo->due.min); + x += 18; + + /* Print status */ + if (status) wattron(win, COLOR_PAIR(status)); + mvwprintw(win, y, x, "%s", + todo->status == NEW ? "new" : + todo->status == DONE ? "done" : perc); + if (status) wattroff(win, COLOR_PAIR(status)); + x += 6; + + /* Print description */ + mvwprintw(win, y, x, "%s", desc); + + /* Reset flags */ + if (flags & SHOW_ACTIVE && todo == TODO) + wattroff(win, A_REVERSE | A_BOLD); } /* View init */ void view_init(void) { + /* Set default escape timeout */ + if (!getenv("ESCDELAY")) + putenv("ESCDELAY=25"); + + /* Setup Curses */ + setlocale(LC_ALL, ""); + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + start_color(); + curs_set(false); + timeout(100); + use_default_colors(); + mousemask(ALL_MOUSE_EVENTS, NULL); + + init_pair(COLOR_TITLE, COLOR_GREEN, -1); + init_pair(COLOR_ERROR, COLOR_RED, -1); + + init_pair(COLOR_NEW, COLOR_RED, -1); + init_pair(COLOR_WIP, COLOR_YELLOW, -1); + init_pair(COLOR_DONE, COLOR_GREEN, -1); + + init_pair(COLOR_CLASS, COLOR_BLUE, -1); + init_pair(COLOR_EC, COLOR_GREEN, -1); + init_pair(COLOR_WORK, COLOR_MAGENTA, -1); + init_pair(COLOR_OTHER, COLOR_RED, -1); + + running = 1; + + /* Setup windows */ for (int i = 0; i < N_ELEMENTS(views); i++) { - if (views[i].init) { - views[i].win = newwin(LINES-2, COLS, 2, 0); - views[i].init(views[i].win); - } + int hdr = COMPACT ? 1 : 2; + views[i]->win = newwin(LINES-hdr, COLS, hdr, 0); + views[i]->init(views[i]->win); + views[i]->size(LINES-hdr, COLS); } } -/* View draw */ -void view_resize(void) +/* Config parser */ +void view_config(const char *group, const char *name, const char *key, const char *value) { - for (int i = 0; i < N_ELEMENTS(views); i++) { - if (views[i].win) - wresize(views[i].win, LINES-2, COLS); - if (views[i].size) - views[i].size(LINES-2, COLS); + if (match(group, "view")) { + if (match(key, "compact")) { + COMPACT = get_bool(value); + } else if (match(key, "morning")) { + MORNING = get_number(value); + } else if (match(key, "weekends")) { + WEEKENDS = get_bool(value); + } else if (match(key, "active")) { + for (int i = 0; i < N_ELEMENTS(views); i++) { + if (match(value, views[i]->name)) { + get_string(value); + view = active = views[i]; + break; + } + } + } } } -/* View draw */ -void view_draw(void) +/* View event */ +void view_edit(edit_t mode) { - draw_header(); - werase(views[active].win); - views[active].draw(); - wrefresh(views[active].win); + EDIT = mode; + set_view(active, &edit_view); } -/* View set */ -int view_set(int num) +void view_main(void) { - if (active != num) { - active = num; - view_draw(); + /* Draw initial view */ + draw_view(); + + /* Run */ + while (1) { + MEVENT btn; + conf_sync(); + int chr = getch(); + date_sync(); + if (chr == KEY_MOUSE) + if (getmouse(&btn) != OK) + continue; + if (process(chr, btn.bstate, btn.y, btn.x)) + continue; + if (chr == ERR) // timeout + continue; + if (chr == 'q') + break; + debug("main: Unhandled key - Dec %3d, Hex %02x, Oct %03o, Chr <%c>", + chr, chr, chr, chr); } - return 1; + + /* Cleanup window */ + view_exit(); } -/* View run */ -int view_run(int key, mmask_t btn, int row, int col) +void view_exit(void) { - /* Check for mouse events */ - if (key == KEY_MOUSE && row == 0) { - int start = 1; - for (int i = 0; i < N_ELEMENTS(views); i++) { - int end = start + strlen(views[i].name) - 1; - if (start <= col && col <= end && views[i].draw) - return view_set(i); - start = end + 2; - } - } - - /* Check for view change */ - for (int i = 0; i < N_ELEMENTS(views); i++) { - if (i == active) - continue; - for (int j = 0; j < N_ELEMENTS(views[i].keys); j++) - if (views[i].keys[j] == key) - return view_set(i); - } + if (running) + endwin(); +} - /* Shift windows */ - int num = active; - int shift = key == KEY_RIGHT ? +1 : - key == KEY_LEFT ? -1 : 0; - while (shift) { - num += shift; - num += N_ELEMENTS(views); - num %= N_ELEMENTS(views); - if (views[num].run) - return view_set(num); +void view_debug(const char *fmt, va_list ap) +{ + if (running) { + int rev = COMPACT ? A_BOLD : 0; + if (!COMPACT) + mvhline(LINES-2, 0, ACS_HLINE, COLS); + move(LINES-1, 0); + attron(COLOR_PAIR(COLOR_ERROR) | rev); + vwprintw(stdscr, fmt, ap); + attroff(COLOR_PAIR(COLOR_ERROR) | rev); + if (!COMPACT) + clrtoeol(); } - - /* Pass key to active view */ - return views[active].run(key, btn, row, col); }