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
28 #define TEXT_WIDTH (20)
29 #define NUMBER_WIDTH (10)
30 #define DATE_WIDTH (4+1+2+1+2 +1+ 2+1+2)
32 /* Widget accessors */
33 #define FF_TEXT(f) (((form_text_t *)f)->text)
34 #define FF_DATE(f) (((form_date_t *)f)->date)
35 #define FF_NUMBER(f) (((form_number_t*)f)->number)
36 #define FF_BUTTON(f) (((form_button_t*)f)->button)
37 #define FF_LIST(f) (((form_list_t *)f))
40 static form_t *form; // current form
41 static WINDOW *win; // current window
42 static int arow; // active row
43 static int acol; // active row
45 /* Helper functions */
46 static int label_width(const char *label)
49 for (int i = 0; label[i]; i++)
55 static void label_print(const char *label)
57 for (int i = 0; label[i]; i++) {
58 if (label[i] == '_' && label[i+1])
59 waddch(win, label[++i]
60 | A_UNDERLINE | A_BOLD);
62 waddch(win, label[i]);
66 static int field_width(form_field_t *field)
68 // Calculate list width
70 if (field->type == FORM_LIST) {
71 for (int i = 0; i < FF_LIST(field)->num; i++) {
72 int width = strlen(FF_LIST(field)->map[i]);
73 if (width > list_size)
78 // Calculate field size
81 width += strlen(field->before);
82 switch (field->type) {
83 case FORM_LABEL: width += label_width(field->label); break;
84 case FORM_TEXT: width += TEXT_WIDTH; break;
85 case FORM_DATE: width += DATE_WIDTH; break;
86 case FORM_NUMBER: width += NUMBER_WIDTH; break;
87 case FORM_BUTTON: width += label_width(field->label); break;
88 case FORM_LIST: width += list_size; break;
91 width += strlen(field->after);
95 static void field_sizes(form_t *form, int *col_size)
97 // Do this by column, and right to left so that we can add
98 // in the extra space available for blank spaces.
99 for (int c = form->cols-1; c >= 0; c--) {
101 for (int r = 0; r < form->rows; r++) {
102 form_field_t *field = form->fields[r][c];
103 if (form->fields[r][c]) {
104 int width = field_width(field);
105 for (int i = c+1; i < form->cols; i++) {
106 if (form->fields[r][i])
108 width -= col_size[i];
110 if (width > col_size[c])
117 static void field_draw(form_field_t *field, int width, int hover)
119 char **map = FF_LIST(field)->map;
120 int idx = FF_LIST(field)->idx;
122 char *before = field->before ?: "";
123 char *after = field->after ?: "";
125 int boxed = field->type == FORM_TEXT ||
126 field->type == FORM_NUMBER;
129 int begin = getcurx(win);
130 int maxstr = width - strlen(before) - strlen(after);
132 if (hover) wattron(win, A_REVERSE);
133 if (under) wattron(win, A_UNDERLINE);
134 if (boxed) waddch(win, '[');
135 if (boxed) maxstr -= 2;
137 wprintw(win, "%s", before);
138 switch (field->type) {
140 label_print(field->label);
143 wprintw(win, "%-.*s",
144 maxstr, FF_TEXT(field));
147 if (no_date(&FF_DATE(field)))
148 wprintw(win, "%s", "undefined");
150 wprintw(win, "%04d-%02d-%02d %02d:%02d",
151 FF_DATE(field).year, FF_DATE(field).month+1,
152 FF_DATE(field).day+1, FF_DATE(field).hour,
156 wprintw(win, "%d", FF_NUMBER(field));
159 wprintw(win, "%s", field->label);
163 wprintw(win, "%s", map[idx]);
165 wprintw(win, "%s", "undefined");
168 int pad = width-(getcurx(win)-begin)-boxed;
169 wprintw(win, "%-*s", pad, after);
171 if (boxed) waddch(win, ']');
172 if (under) wattroff(win, A_UNDERLINE);
173 if (hover) wattroff(win, A_REVERSE);
176 static int is_active(form_t *form, int r, int c)
178 for (int i = c; i >= 0; i--) {
179 if (r == arow && i == acol)
181 if (form->fields[r][i])
184 for (int i = c+1; i < form->cols; i++) {
185 if (form->fields[r][i])
187 if (r == arow && i == acol)
193 static int can_active(form_t *form, int r, int c)
195 for (int i = c; i >= 0; i--) {
196 if (!form->fields[r][i])
198 if (form->fields[r][i]->type == FORM_LABEL)
205 static int set_active(form_t *form, int r, int c)
207 if (can_active(form, r, c)) {
215 static void move_active(form_t *form, int ro, int co)
219 for (int r = 0; r < form->rows; r++)
220 for (int c = 0; c < form->cols; c++)
221 if (set_active(form, r, c))
226 for (int ri = arow+ro; ri>=0 && ri<form->rows; ri+=ro) {
227 if (is_active(form, ri, acol))
230 for (int ci = acol; ci < form->cols; ci++)
231 if (set_active(form, ri, ci))
233 for (int ci = acol; ci >= 0; ci--)
234 if (set_active(form, ri, ci))
241 for (int ci = acol+co; ci>=0 && ci<form->cols; ci+=co) {
242 if (is_active(form, arow, ci))
245 if (set_active(form, arow, ci))
252 void form_show(form_t *_form)
258 void form_draw(WINDOW *_win)
263 // Validate everything
267 // Calculate column width
268 int col_size[form->cols];
269 field_sizes(form, col_size);
271 // Make sure we have an active field
272 if (!can_active(form, arow, acol))
273 move_active(form, 0, 0);
276 for (int r = 0; r < form->rows; r++) {
277 for (int c = 0; c < form->cols; c++) {
278 form_field_t *field = form->fields[r][c];
279 // Calculate form field size
280 int width = col_size[c];
281 for (int i = c+1; i < form->cols; i++) {
282 if (form->fields[r][i])
284 width += col_size[i];
288 field_draw(field, width, is_active(form, r, c));
290 wprintw(win, "%*s", width, "");
296 int form_run(int key, mmask_t btn, int row, int col)
298 // Validate everything
302 // Check movement keys
304 case 'h': move_active(form, 0, -1); goto redraw;
305 case 'j': move_active(form, 1, 0); goto redraw;
306 case 'k': move_active(form, -1, 0); goto redraw;
307 case 'l': move_active(form, 0, 1); goto redraw;
310 // Handle mouse movement
311 if (key == KEY_MOUSE && row < form->rows) {
312 int pos=0, col_size[form->cols];
313 field_sizes(form, col_size);
314 for (int c = 0; c < form->cols; c++) {
315 if (pos < col && col < pos+col_size[c]) {
316 if (can_active(form, row, c)) {
326 // Search for hotkeys
327 for (int r = 0; r < form->rows; r++)
328 for (int c = 0; c < form->cols; c++)
329 if (form->fields[r][c] &&
330 form->fields[r][c]->hotkey == key) {
345 static form_text_t title = TEXT('t');
346 static form_text_t location = TEXT('o');
347 static form_date_t start = DATE('s');
348 static form_date_t end = DATE('e');
349 static form_date_t due_date = DATE('u');
350 static form_number_t completed = NUMBER('p', .f.after="%");
351 static form_list_t calendar = LIST('c');
352 static form_list_t category = LIST('g');
353 static form_list_t repeat = LIST('r');
354 static form_number_t frequency = NUMBER(0);
355 static form_button_t weekdays = BUTTONS("Su Mo Tu We Th Fr Sa");
356 static form_text_t details = TEXT('d');
358 static form_t edit = { 12, 4, {
359 { LABEL("_Title: "), &title.f },
360 { LABEL("L_ocation: "), &location.f },
362 { LABEL("_Start: "), &start.f, LABEL(" _End: "), &end.f },
363 { LABEL("D_ue Date: "), &due_date.f, LABEL(" Com_pleted: "), &completed.f },
364 { LABEL("_Calendar: "), &calendar.f, LABEL(" Cate_gory: "), &category.f },
366 { LABEL("_Repeat: "), &repeat.f, LABEL(" Every: "), &frequency.f },
368 { NULL, &weekdays.f },
370 { LABEL("_Details: "), &details.f },
379 keypad(stdscr, TRUE);
383 use_default_colors();
384 mousemask(ALL_MOUSE_EVENTS, NULL);
387 win = newwin(LINES, COLS, 0, 0);
396 if (chr == KEY_MOUSE)
397 if (getmouse(&btn) != OK)
402 wresize(win, LINES, COLS);
408 case '\14': // Ctrl-L
417 if (form_run(chr, btn.bstate, btn.y, btn.x))
419 if (chr == ERR) // timeout