]> Pileus Git - lackey/blobdiff - src/view.c
Add option to show and hide weekends.
[lackey] / src / view.c
index 437e69c1ec693e260afedf6d14988e31b40a5c10..a66fb853dcfe17ae59af7305744dab1f0754cb6d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Andy Spencer <andy753421@gmail.com>
+ * Copyright (C) 2012-2013 Andy Spencer <andy753421@gmail.com>
  *
  * 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
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _XOPEN_SOURCE
 #define _XOPEN_SOURCE_EXTENDED
 
+#include <stdlib.h>
 #include <string.h>
+#include <locale.h>
 #include <ncurses.h>
 
 #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;
 
+/* 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 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', '?'} },
+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
 };
 
 /* Config data */
-int COMPACT = 0;
-int ACTIVE  = 0;
+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));
        if (!COMPACT)
                mvhline(1, 0, ACS_HLINE, COLS);
@@ -80,10 +142,129 @@ static int get_color(const char *cat)
               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);
@@ -100,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 (l<h && event->name) mvwprintw(win, y+l++, x+1, "%.*s",   w-2, event->name);
+       if (event == EVENT)     wattroff(win, WA_REVERSE);
        if (l<h && event->loc)  mvwprintw(win, y+l++, x+1, "@ %.*s", w-4, event->loc);
        if (l<h && event->desc) 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);
 
@@ -115,26 +303,31 @@ 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) {
+       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, " [all day]   -");
+                       mvwprintw(win, y, x+1, "[all day]   - ");
                else
-                       mvwprintw(win, y, x, " %2d:%02d-%2d:%02d -",
+                       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];
@@ -153,6 +346,11 @@ void todo_line(WINDOW *win, todo_t *todo, int y, int x, int w, int full)
        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]");
@@ -172,121 +370,128 @@ void todo_line(WINDOW *win, todo_t *todo, int y, int x, int w, int full)
 
        /* Print description */
        mvwprintw(win, y, x, "%s", desc);
-}
 
-/* 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);
+       /* Reset flags */
+       if (flags & SHOW_ACTIVE && todo == TODO)
+               wattroff(win, A_REVERSE | A_BOLD);
 }
 
 /* View init */
 void view_init(void)
 {
-       int hdr = COMPACT ? 1 : 2;
+       /* 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-hdr, COLS, hdr, 0);
-                       views[i].init(views[i].win);
-               }
-               if (views[i].size)
-                       views[i].size(LINES-hdr, COLS);
+               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)
 {
-       int hdr = COMPACT ? 1 : 2;
-       for (int i = 0; i < N_ELEMENTS(views); i++) {
-               if (views[i].win) {
-                       wresize(views[i].win, LINES-hdr, COLS);
-                       mvwin(views[i].win, hdr, 0);
+       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;
+                               }
+                       }
                }
-               if (views[i].size)
-                       views[i].size(LINES-hdr, COLS);
        }
 }
 
-/* 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 compact mode toggle */
-       if (key == 'c') {
-               COMPACT ^= 1;
-               view_resize();
-               view_draw();
-               return 1;
-       }
-
-       /* 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);
 }