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 WINDOW *form_win;
44 /* Helpeer functions */
45 int label_width(const char *label)
48 for (int i = 0; label[i]; i++)
54 void label_print(WINDOW *win, const char *label)
56 for (int i = 0; label[i]; i++) {
57 if (label[i] == '_' && label[i+1])
58 waddch(form_win, label[++i]
59 | A_UNDERLINE | A_BOLD);
61 waddch(form_win, label[i]);
65 int field_width(form_field_t *field)
67 // Calculate list width
69 if (field->type == FORM_LIST) {
70 for (int i = 0; i < FF_LIST(field)->num; i++) {
71 int width = strlen(FF_LIST(field)->map[i]);
72 if (width > list_size)
77 // Calculate field size
80 width += strlen(field->before);
81 switch (field->type) {
82 case FORM_LABEL: width += label_width(field->label); break;
83 case FORM_TEXT: width += TEXT_WIDTH; break;
84 case FORM_DATE: width += DATE_WIDTH; break;
85 case FORM_NUMBER: width += NUMBER_WIDTH; break;
86 case FORM_BUTTON: width += label_width(field->label); break;
87 case FORM_LIST: width += list_size; break;
90 width += strlen(field->after);
94 void field_sizes(form_t *form, int *col_size)
96 // Do this by column, and right to left so that we can add
97 // in the extra space available for blank spaces.
98 for (int c = form->cols-1; c >= 0; c--) {
100 for (int r = 0; r < form->rows; r++) {
101 form_field_t *field = form->fields[r][c];
102 if (form->fields[r][c]) {
103 int width = field_width(field);
104 for (int i = c+1; i < form->cols; i++) {
105 if (form->fields[r][i])
107 width -= col_size[i];
109 if (width > col_size[c])
116 void field_draw(WINDOW *win, form_field_t *field, int width, int hover)
118 char **map = FF_LIST(field)->map;
119 int idx = FF_LIST(field)->idx;
121 char *before = field->before ?: "";
122 char *after = field->after ?: "";
124 int boxed = field->type == FORM_TEXT ||
125 field->type == FORM_NUMBER;
128 int begin = getcurx(win);
129 int maxstr = width - strlen(before) - strlen(after);
131 if (hover) wattron(win, A_REVERSE);
132 if (under) wattron(win, A_UNDERLINE);
133 if (boxed) waddch(win, '[');
134 if (boxed) maxstr -= 2;
136 wprintw(win, "%s", before);
137 switch (field->type) {
139 label_print(win, field->label);
142 wprintw(win, "%-.*s",
143 maxstr, FF_TEXT(field));
146 if (no_date(&FF_DATE(field)))
147 wprintw(win, "%s", "undefined");
149 wprintw(win, "%04d-%02d-%02d %02d:%02d",
150 FF_DATE(field).year, FF_DATE(field).month+1,
151 FF_DATE(field).day+1, FF_DATE(field).hour,
155 wprintw(win, "%d", FF_NUMBER(field));
158 wprintw(win, "%s", field->label);
162 wprintw(win, "%s", map[idx]);
164 wprintw(win, "%s", "undefined");
167 int pad = width-(getcurx(win)-begin)-boxed;
168 wprintw(win, "%-*s", pad, after);
170 if (boxed) waddch(win, ']');
171 if (under) wattroff(win, A_UNDERLINE);
172 if (hover) wattroff(win, A_REVERSE);
175 int is_active(form_t *form, int r, int c)
177 if (r == form_row && c == form_col)
179 for (int i = c+1; i < form->cols && !form->fields[r][i]; i++)
180 if (r == form_row && i == form_col)
185 int can_active(form_t *form, int r, int c)
187 for (int i = c; i >= 0; i--) {
188 if (!form->fields[r][i])
190 if (form->fields[r][i]->type != FORM_LABEL)
198 void set_active(form_t *form, int ro, int co)
201 if (ro==0 && co==0) {
202 for (int r = 0; r < form->rows; r++)
203 for (int c = 0; c < form->cols; c++) {
204 if (can_active(form, r, c)) {
214 for (int ri = form_row+ro; ri>=0 && ri<form->rows; ri+=ro) {
215 if (can_active(form, ri, form_col)) {
224 for (int ci = form_col+co; ci>=0 && ci<form->cols; ci+=co) {
225 for (int ri = form_row; ri < form->rows; ri++)
226 if (can_active(form, form_row, ci)) {
231 for (int ri = form_row; ri >= 0; ri--)
232 if (can_active(form, form_row, ci)) {
244 form_win = newwin(LINES, COLS, 0, 0);
248 void form_resize(void)
250 mvwin(form_win, 0, 0);
251 wresize(form_win, LINES, COLS);
255 int form_draw(form_t *form)
257 int col_size[form->cols];
259 // Calculate column width
260 field_sizes(form, col_size);
262 // Make sure we have an active field
263 if (!can_active(form, form_row, form_col))
264 set_active(form, 0, 0);
267 for (int r = 0; r < form->rows; r++) {
268 for (int c = 0; c < form->cols; c++) {
269 form_field_t *field = form->fields[r][c];
270 // Calculate form field size
271 int width = col_size[c];
272 for (int i = c+1; i < form->cols; i++) {
273 if (form->fields[r][i])
275 width += col_size[i];
279 field_draw(form_win, field, width,
280 is_active(form, r, c));
282 wprintw(form_win, "%*s", width, "");
284 wprintw(form_win, "\n");
289 int form_run(form_t *form, int key, mmask_t btn, int row, int col)
291 // Check movement keys
293 case 'h': set_active(form, 0, -1); goto redraw;
294 case 'j': set_active(form, 1, 0); goto redraw;
295 case 'k': set_active(form, -1, 0); goto redraw;
296 case 'l': set_active(form, 0, 1); goto redraw;
299 // Handle mouse movement
300 if (key == KEY_MOUSE && row < form->rows) {
301 int pos=0, col_size[form->cols];
302 field_sizes(form, col_size);
303 for (int c = 0; c < form->cols; c++) {
304 if (pos < col && col < pos+col_size[c]) {
305 if (can_active(form, row, c)) {
315 // Search for hotkeys
316 for (int r = 0; r < form->rows; r++)
317 for (int c = 0; c < form->cols; c++)
318 if (form->fields[r][c] &&
319 form->fields[r][c]->hotkey == key) {
334 static form_text_t title = TEXT('t');
335 static form_text_t location = TEXT('o');
336 static form_date_t start = DATE('s');
337 static form_date_t end = DATE('e');
338 static form_date_t due_date = DATE('u');
339 static form_number_t completed = NUMBER('p', .f.after="%");
340 static form_list_t calendar = LIST('c');
341 static form_list_t category = LIST('g');
342 static form_list_t repeat = LIST('r');
343 static form_number_t frequency = NUMBER(0);
344 static form_button_t weekdays = BUTTONS("Su Mo Tu We Th Fr Sa");
345 static form_text_t details = TEXT('d');
347 static form_t edit = { 12, 4, {
348 { LABEL("_Title: "), &title.f },
349 { LABEL("L_ocation: "), &location.f },
351 { LABEL("_Start: "), &start.f, LABEL(" _End: "), &end.f },
352 { LABEL("D_ue Date: "), &due_date.f, LABEL(" Com_pleted: "), &completed.f },
353 { LABEL("_Calendar: "), &calendar.f, LABEL(" Cate_gory: "), &category.f },
355 { LABEL("_Repeat: "), &repeat.f, LABEL(" Every: "), &frequency.f },
357 { NULL, &weekdays.f },
359 { LABEL("_Details: "), &details.f },
368 keypad(stdscr, TRUE);
372 use_default_colors();
373 mousemask(ALL_MOUSE_EVENTS, NULL);
384 if (chr == KEY_MOUSE)
385 if (getmouse(&btn) != OK)
395 case '\14': // Ctrl-L
404 if (form_run(&edit, chr, btn.bstate, btn.y, btn.x))
406 if (chr == ERR) // timeout