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)
33 static WINDOW *form_win;
37 /* Helpeer functions */
38 int label_width(const char *label)
41 for (int i = 0; label[i]; i++)
47 void label_print(WINDOW *win, const char *label)
49 for (int i = 0; label[i]; i++) {
50 if (label[i] == '_' && label[i+1])
51 waddch(form_win, label[++i]
52 | A_UNDERLINE | A_BOLD);
54 waddch(form_win, label[i]);
58 int field_width(form_field_t *field)
60 // Calculate list width
62 if (field->type == FORM_LIST) {
63 for (int i = 0; i < field->list.num; i++) {
64 int width = strlen(field->list.map[i]);
65 if (width > list_size)
70 // Calculate field size
73 width += strlen(field->before);
74 switch (field->type) {
75 case FORM_LABEL: width += label_width(field->label); break;
76 case FORM_TEXT: width += TEXT_WIDTH; break;
77 case FORM_DATE: width += DATE_WIDTH; break;
78 case FORM_NUMBER: width += NUMBER_WIDTH; break;
79 case FORM_BUTTON: width += label_width(field->label); break;
80 case FORM_LIST: width += list_size; break;
83 width += strlen(field->after);
87 void field_sizes(form_t *form, int *col_size)
89 // Do this by column, and right to left so that we can add
90 // in the extra space available for blank spaces.
91 for (int c = form->cols-1; c >= 0; c--) {
93 for (int r = 0; r < form->rows; r++) {
94 form_field_t *field = form->fields[r][c];
95 if (form->fields[r][c]) {
96 int width = field_width(field);
97 for (int i = c+1; i < form->cols; i++) {
98 if (form->fields[r][i])
100 width -= col_size[i];
102 if (width > col_size[c])
109 void field_draw(WINDOW *win, form_field_t *field, int width, int hover)
111 char **map = field->list.map;
112 int idx = field->list.idx;
114 char *before = field->before ?: "";
115 char *after = field->after ?: "";
117 int boxed = field->type == FORM_TEXT ||
118 field->type == FORM_NUMBER;
121 int begin = getcurx(win);
122 int maxstr = width - strlen(before) - strlen(after);
124 if (hover) wattron(win, A_REVERSE);
125 if (under) wattron(win, A_UNDERLINE);
126 if (boxed) waddch(win, '[');
127 if (boxed) maxstr -= 2;
129 wprintw(win, "%s", before);
130 switch (field->type) {
132 label_print(win, field->label);
135 wprintw(win, "%-.*s",
136 maxstr, field->text);
139 if (no_date(&field->date))
140 wprintw(win, "%s", "undefined");
142 wprintw(win, "%04d-%02d-%02d %02d:%02d",
143 field->date.year, field->date.month+1,
144 field->date.day+1, field->date.hour,
148 wprintw(win, "%d", field->number);
151 wprintw(win, "%s", field->label);
155 wprintw(win, "%s", map[idx]);
157 wprintw(win, "%s", "undefined");
160 int pad = width-(getcurx(win)-begin)-boxed;
161 wprintw(win, "%-*s", pad, after);
163 if (boxed) waddch(win, ']');
164 if (under) wattroff(win, A_UNDERLINE);
165 if (hover) wattroff(win, A_REVERSE);
168 int is_active(form_t *form, int r, int c)
170 if (r == form_row && c == form_col)
172 for (int i = c+1; i < form->cols && !form->fields[r][i]; i++)
173 if (r == form_row && i == form_col)
178 int can_active(form_t *form, int r, int c)
180 for (int i = c; i >= 0; i--) {
181 if (!form->fields[r][i])
183 if (form->fields[r][i]->type != FORM_LABEL)
191 void set_active(form_t *form, int ro, int co)
194 if (ro==0 && co==0) {
195 for (int r = 0; r < form->rows; r++)
196 for (int c = 0; c < form->cols; c++) {
197 if (can_active(form, r, c)) {
207 for (int ri = form_row+ro; ri>=0 && ri<form->rows; ri+=ro) {
208 if (can_active(form, ri, form_col)) {
217 for (int ci = form_col+co; ci>=0 && ci<form->cols; ci+=co) {
218 for (int ri = form_row; ri < form->rows; ri++)
219 if (can_active(form, form_row, ci)) {
224 for (int ri = form_row; ri >= 0; ri--)
225 if (can_active(form, form_row, ci)) {
237 form_win = newwin(LINES, COLS, 0, 0);
241 void form_resize(void)
243 mvwin(form_win, 0, 0);
244 wresize(form_win, LINES, COLS);
248 int form_draw(form_t *form)
250 int col_size[form->cols];
252 // Calculate column width
253 field_sizes(form, col_size);
255 // Make sure we have an active field
256 if (!can_active(form, form_row, form_col))
257 set_active(form, 0, 0);
260 for (int r = 0; r < form->rows; r++) {
261 for (int c = 0; c < form->cols; c++) {
262 form_field_t *field = form->fields[r][c];
263 // Calculate form field size
264 int width = col_size[c];
265 for (int i = c+1; i < form->cols; i++) {
266 if (form->fields[r][i])
268 width += col_size[i];
272 field_draw(form_win, field, width,
273 is_active(form, r, c));
275 wprintw(form_win, "%*s", width, "");
277 wprintw(form_win, "\n");
282 int form_run(form_t *form, int key, mmask_t btn, int row, int col)
284 // Check movement keys
286 case 'h': set_active(form, 0, -1); goto redraw;
287 case 'j': set_active(form, 1, 0); goto redraw;
288 case 'k': set_active(form, -1, 0); goto redraw;
289 case 'l': set_active(form, 0, 1); goto redraw;
292 // Handle mouse movement
293 if (key == KEY_MOUSE && row < form->rows) {
294 int pos=0, col_size[form->cols];
295 field_sizes(form, col_size);
296 for (int c = 0; c < form->cols; c++) {
297 if (pos < col && col < pos+col_size[c]) {
298 if (can_active(form, row, c)) {
308 // Search for hotkeys
309 for (int r = 0; r < form->rows; r++)
310 for (int c = 0; c < form->cols; c++)
311 if (form->fields[r][c] &&
312 form->fields[r][c]->hotkey == key) {
327 static form_field_t title = TEXT('t');
328 static form_field_t location = TEXT('o');
329 static form_field_t start = DATE('s');
330 static form_field_t end = DATE('e');
331 static form_field_t due_date = DATE('u');
332 static form_field_t completed = NUMBER('p', .after="%");
333 static form_field_t calendar = LIST('c');
334 static form_field_t category = LIST('g');
335 static form_field_t repeat = LIST('r');
336 static form_field_t frequency = NUMBER(0);
337 static form_field_t weekdays = BUTTONS("Su Mo Tu We Th Fr Sa");
338 static form_field_t details = TEXT('d');
340 static form_t edit = { 12, 4, {
341 { LABEL("_Title: "), &title },
342 { LABEL("L_ocation: "), &location },
344 { LABEL("_Start: "), &start, LABEL(" _End: "), &end },
345 { LABEL("D_ue Date: "), &due_date, LABEL(" Com_pleted: "), &completed },
346 { LABEL("_Calendar: "), &calendar, LABEL(" Cate_gory: "), &category },
348 { LABEL("_Repeat: "), &repeat, LABEL(" Every: "), &frequency },
352 { LABEL("_Details: "), &details },
361 keypad(stdscr, TRUE);
365 use_default_colors();
366 mousemask(ALL_MOUSE_EVENTS, NULL);
377 if (chr == KEY_MOUSE)
378 if (getmouse(&btn) != OK)
388 case '\14': // Ctrl-L
397 if (form_run(&edit, chr, btn.bstate, btn.y, btn.x))
399 if (chr == ERR) // timeout