2 * Copyright (C) 2015 Andy Spencer <andy753421@gmail.com>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #define _XOPEN_SOURCE_EXTENDED
32 #define DATE_WIDTH (4+1+2+1+2 +1+ 2+1+2)
33 #define TEXT_WIDTH (DATE_WIDTH)
34 #define NUMBER_WIDTH (10)
36 /* Widget accessors */
37 #define FF_TEXT(f) (((form_text_t *)f)->text)
38 #define FF_DATE(f) (((form_date_t *)f)->date)
39 #define FF_NUMBER(f) (((form_number_t*)f)->number)
40 #define FF_BUTTON(f) (((form_button_t*)f)->button)
41 #define FF_LIST(f) (((form_list_t *)f))
44 static form_t *form; // current form
45 static WINDOW *win; // current window
46 static int arow; // active row
47 static int acol; // active row
49 /* Helper functions */
50 static int label_width(const char *label)
53 for (int i = 0; label[i]; i++)
59 static void label_print(const char *label)
61 for (int i = 0; label[i]; i++) {
62 if (label[i] == '_' && label[i+1])
63 waddch(win, label[++i]
64 | A_UNDERLINE | A_BOLD);
66 waddch(win, label[i]);
70 static int field_width(form_field_t *field)
72 // Calculate list width
74 if (field->type == FORM_LIST) {
75 for (int i = 0; i < FF_LIST(field)->num; i++) {
76 int width = strlen(FF_LIST(field)->map[i]);
77 if (width > list_size)
82 // Calculate field size
85 width += strlen(field->before);
86 switch (field->type) {
87 case FORM_LABEL: width += label_width(field->label); break;
88 case FORM_TEXT: width += TEXT_WIDTH; break;
89 case FORM_DATE: width += DATE_WIDTH; break;
90 case FORM_NUMBER: width += NUMBER_WIDTH; break;
91 case FORM_BUTTON: width += label_width(field->label); break;
92 case FORM_LIST: width += list_size; break;
95 width += strlen(field->after);
99 static void field_sizes(form_t *form, int *col_size)
101 // Do this by column, and right to left so that we can add
102 // in the extra space available for blank spaces.
103 for (int c = form->cols-1; c >= 0; c--) {
105 for (int r = 0; r < form->rows; r++) {
106 form_field_t *field = form->fields[r][c];
107 if (form->fields[r][c]) {
108 int width = field_width(field);
109 for (int i = c+1; i < form->cols; i++) {
110 if (form->fields[r][i])
112 width -= col_size[i];
114 if (width > col_size[c])
121 static void field_draw(form_field_t *field, int width, int hover)
123 char **map = FF_LIST(field)->map;
124 int idx = FF_LIST(field)->idx;
126 char *before = field->before ?: "";
127 char *after = field->after ?: "";
129 int boxed = field->type == FORM_TEXT ||
130 field->type == FORM_NUMBER;
131 int bold = field->attr.bold;
134 int begin = getcurx(win);
135 int maxstr = width - strlen(before) - strlen(after);
137 if (bold) wattron(win, A_BOLD);
138 if (hover) wattron(win, A_REVERSE);
139 if (under) wattron(win, A_UNDERLINE);
140 if (boxed) waddch(win, '[');
141 if (boxed) maxstr -= 2;
143 wprintw(win, "%s", before);
144 switch (field->type) {
146 label_print(field->label);
150 wprintw(win, "%-.*s",
151 maxstr, FF_TEXT(field));
154 if (no_date(&FF_DATE(field)))
155 wprintw(win, "%s", "{undefined}");
157 wprintw(win, "%04d-%02d-%02d %02d:%02d",
158 FF_DATE(field).year, FF_DATE(field).month+1,
159 FF_DATE(field).day+1, FF_DATE(field).hour,
163 wprintw(win, "%d", FF_NUMBER(field));
166 wprintw(win, "%s", field->label);
170 wprintw(win, "%s", map[idx]);
172 wprintw(win, "%s", "{undefined}");
175 int pad = width-(getcurx(win)-begin)-boxed;
176 wprintw(win, "%-*s", pad, after);
178 if (boxed) waddch(win, ']');
179 if (under) wattroff(win, A_UNDERLINE);
180 if (hover) wattroff(win, A_REVERSE);
181 if (bold) wattroff(win, A_BOLD);
184 static int is_active(form_t *form, int r, int c)
186 for (int i = c; i >= 0; i--) {
187 if (r == arow && i == acol)
189 if (form->fields[r][i])
192 for (int i = c+1; i < form->cols; i++) {
193 if (form->fields[r][i])
195 if (r == arow && i == acol)
201 static int can_active(form_t *form, int r, int c)
203 for (int i = c; i >= 0; i--) {
204 if (!form->fields[r][i])
206 if (form->fields[r][i]->type == FORM_LABEL)
213 static int set_active(form_t *form, int r, int c)
215 if (can_active(form, r, c)) {
223 static void move_active(form_t *form, int ro, int co)
227 for (int r = 0; r < form->rows; r++)
228 for (int c = 0; c < form->cols; c++)
229 if (set_active(form, r, c))
234 for (int ri = arow+ro; ri>=0 && ri<form->rows; ri+=ro) {
235 if (is_active(form, ri, acol))
238 for (int ci = acol; ci < form->cols; ci++)
239 if (set_active(form, ri, ci))
241 for (int ci = acol; ci >= 0; ci--)
242 if (set_active(form, ri, ci))
249 for (int ci = acol+co; ci>=0 && ci<form->cols; ci+=co) {
250 if (is_active(form, arow, ci))
253 if (set_active(form, arow, ci))
259 static void form_edit(void)
263 int col_size[form->cols];
265 field_sizes(form, col_size);
270 for (int c = 0; c < form->cols; c++) {
271 if (is_active(form, arow, c))
283 int idx = MAX(0, len-(rlen-2));
284 wmove(win, rrow, rcol);
285 wprintw(win, "[%-*.*s]", rlen-2, rlen-2, &buf[idx]);
286 wmove(win, rrow, rcol+1+cur-idx);
289 // Read next character
293 if (chr == KEY_MOUSE)
294 if (getmouse(&btn) != OK)
303 //view_run(chr, btn.bstate, btn.y, btn.x);
305 case '\012': // Enter
308 case '\033': // Escape
321 memmove(&buf[cur-1], &buf[cur], (len-cur)+1);
328 memmove(&buf[cur], &buf[cur+1], (len-cur)+1);
336 if (len+1 < sizeof(buf)) {
337 memmove(&buf[cur+1], &buf[cur], (len-cur)+1);
341 //debug("form: cur=%d, len=%d", cur, len);
343 debug("form: out of space");
346 debug("form: Unhandled key - Dec %3d, Hex %02x, Oct %03o, Chr <%c>",
354 void form_show(form_t *_form)
360 void form_draw(WINDOW *_win)
365 // Validate everything
369 // Calculate column width
370 int col_size[form->cols];
371 field_sizes(form, col_size);
373 // Make sure we have an active field
374 if (!can_active(form, arow, acol))
375 move_active(form, 0, 0);
378 for (int r = 0; r < form->rows; r++) {
379 for (int c = 0; c < form->cols; c++) {
380 form_field_t *field = form->fields[r][c];
381 // Calculate form field size
382 int width = col_size[c];
383 for (int i = c+1; i < form->cols; i++) {
384 if (form->fields[r][i])
386 width += col_size[i];
390 field_draw(field, width, is_active(form, r, c));
392 wprintw(win, "%*s", width, "");
398 int form_run(int key, mmask_t btn, int row, int col)
400 // Validate everything
404 // Check movement keys
406 case 'h': move_active(form, 0, -1); goto redraw;
407 case 'j': move_active(form, 1, 0); goto redraw;
408 case 'k': move_active(form, -1, 0); goto redraw;
409 case 'l': move_active(form, 0, 1); goto redraw;
412 // Handle mouse movement
413 if (key == KEY_MOUSE && row < form->rows) {
414 int pos=0, col_size[form->cols];
415 field_sizes(form, col_size);
416 for (int c = 0; c < form->cols; c++) {
417 if (pos < col && col < pos+col_size[c]) {
418 if (can_active(form, row, c)) {
428 // Search for hotkeys
429 for (int r = 0; r < form->rows; r++)
430 for (int c = 0; c < form->cols; c++)
431 if (form->fields[r][c] &&
432 form->fields[r][c]->hotkey == key) {
438 // Check editing (enter)
454 static form_text_t title = TEXT('t');
455 static form_text_t location = TEXT('o');
456 static form_date_t start = DATE('s');
457 static form_date_t end = DATE('e');
458 static form_date_t due_date = DATE('u');
459 static form_number_t completed = NUMBER('p', .f.after="%");
460 static form_list_t calendar = LIST('c');
461 static form_list_t category = LIST('g');
462 static form_list_t repeat = LIST('r');
463 static form_number_t frequency = NUMBER(0);
464 static form_button_t weekdays = BUTTONS("Su Mo Tu We Th Fr Sa");
465 static form_text_t details = TEXT('d');
467 static form_t edit = { 12, 4, {
468 { LABEL("_Title: "), &title.f },
469 { LABEL("L_ocation: "), &location.f },
471 { LABEL("_Start: "), &start.f, LABEL(" _End: "), &end.f },
472 { LABEL("D_ue Date: "), &due_date.f, LABEL(" Com_pleted: "), &completed.f },
473 { LABEL("_Calendar: "), &calendar.f, LABEL(" Cate_gory: "), &category.f },
475 { LABEL("_Repeat: "), &repeat.f, LABEL(" Every: "), &frequency.f },
477 { NULL, &weekdays.f },
479 { LABEL("_Details: "), &details.f },
488 keypad(stdscr, TRUE);
492 use_default_colors();
493 mousemask(ALL_MOUSE_EVENTS, NULL);
496 win = newwin(LINES, COLS, 0, 0);
505 if (chr == KEY_MOUSE)
506 if (getmouse(&btn) != OK)
511 wresize(win, LINES, COLS);
517 case '\14': // Ctrl-L
526 if (form_run(chr, btn.bstate, btn.y, btn.x))
528 if (chr == ERR) // timeout