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
30 #define DATE_WIDTH (4+1+2+1+2 +1+ 2+1+2)
31 #define TEXT_WIDTH (DATE_WIDTH)
32 #define NUMBER_WIDTH (10)
34 /* Widget accessors */
35 #define FF_TEXT(f) (((form_text_t *)f)->text)
36 #define FF_DATE(f) (((form_date_t *)f)->date)
37 #define FF_NUMBER(f) (((form_number_t*)f)->number)
38 #define FF_BUTTON(f) (((form_button_t*)f)->button)
39 #define FF_LIST(f) (((form_list_t *)f))
42 static form_t *form; // current form
43 static WINDOW *win; // current window
44 static int arow; // active row
45 static int acol; // active row
47 /* Helper functions */
48 static int label_width(const char *label)
51 for (int i = 0; label[i]; i++)
57 static void label_print(const char *label)
59 for (int i = 0; label[i]; i++) {
60 if (label[i] == '_' && label[i+1])
61 waddch(win, label[++i]
62 | A_UNDERLINE | A_BOLD);
64 waddch(win, label[i]);
68 static int field_width(form_field_t *field)
70 // Calculate list width
72 if (field->type == FORM_LIST) {
73 for (int i = 0; i < FF_LIST(field)->num; i++) {
74 int width = strlen(FF_LIST(field)->map[i]);
75 if (width > list_size)
80 // Calculate field size
83 width += strlen(field->before);
84 switch (field->type) {
85 case FORM_LABEL: width += label_width(field->label); break;
86 case FORM_TEXT: width += TEXT_WIDTH; break;
87 case FORM_DATE: width += DATE_WIDTH; break;
88 case FORM_NUMBER: width += NUMBER_WIDTH; break;
89 case FORM_BUTTON: width += label_width(field->label); break;
90 case FORM_LIST: width += list_size; break;
93 width += strlen(field->after);
97 static void field_sizes(form_t *form, int *col_size)
99 // Do this by column, and right to left so that we can add
100 // in the extra space available for blank spaces.
101 for (int c = form->cols-1; c >= 0; c--) {
103 for (int r = 0; r < form->rows; r++) {
104 form_field_t *field = form->fields[r][c];
105 if (form->fields[r][c]) {
106 int width = field_width(field);
107 for (int i = c+1; i < form->cols; i++) {
108 if (form->fields[r][i])
110 width -= col_size[i];
112 if (width > col_size[c])
119 static void field_draw(form_field_t *field, int width, int hover)
121 char **map = FF_LIST(field)->map;
122 int idx = FF_LIST(field)->idx;
124 char *before = field->before ?: "";
125 char *after = field->after ?: "";
127 int boxed = field->type == FORM_TEXT ||
128 field->type == FORM_NUMBER;
129 int bold = field->attr.bold;
132 int begin = getcurx(win);
133 int maxstr = width - strlen(before) - strlen(after);
135 if (bold) wattron(win, A_BOLD);
136 if (hover) wattron(win, A_REVERSE);
137 if (under) wattron(win, A_UNDERLINE);
138 if (boxed) waddch(win, '[');
139 if (boxed) maxstr -= 2;
141 wprintw(win, "%s", before);
142 switch (field->type) {
144 label_print(field->label);
148 wprintw(win, "%-.*s",
149 maxstr, FF_TEXT(field));
152 if (no_date(&FF_DATE(field)))
153 wprintw(win, "%s", "{undefined}");
155 wprintw(win, "%04d-%02d-%02d %02d:%02d",
156 FF_DATE(field).year, FF_DATE(field).month+1,
157 FF_DATE(field).day+1, FF_DATE(field).hour,
161 wprintw(win, "%d", FF_NUMBER(field));
164 wprintw(win, "%s", field->label);
168 wprintw(win, "%s", map[idx]);
170 wprintw(win, "%s", "{undefined}");
173 int pad = width-(getcurx(win)-begin)-boxed;
174 wprintw(win, "%-*s", pad, after);
176 if (boxed) waddch(win, ']');
177 if (under) wattroff(win, A_UNDERLINE);
178 if (hover) wattroff(win, A_REVERSE);
179 if (bold) wattroff(win, A_BOLD);
182 static int is_active(form_t *form, int r, int c)
184 for (int i = c; i >= 0; i--) {
185 if (r == arow && i == acol)
187 if (form->fields[r][i])
190 for (int i = c+1; i < form->cols; i++) {
191 if (form->fields[r][i])
193 if (r == arow && i == acol)
199 static int can_active(form_t *form, int r, int c)
201 for (int i = c; i >= 0; i--) {
202 if (!form->fields[r][i])
204 if (form->fields[r][i]->type == FORM_LABEL)
211 static int set_active(form_t *form, int r, int c)
213 if (can_active(form, r, c)) {
221 static void move_active(form_t *form, int ro, int co)
225 for (int r = 0; r < form->rows; r++)
226 for (int c = 0; c < form->cols; c++)
227 if (set_active(form, r, c))
232 for (int ri = arow+ro; ri>=0 && ri<form->rows; ri+=ro) {
233 if (is_active(form, ri, acol))
236 for (int ci = acol; ci < form->cols; ci++)
237 if (set_active(form, ri, ci))
239 for (int ci = acol; ci >= 0; ci--)
240 if (set_active(form, ri, ci))
247 for (int ci = acol+co; ci>=0 && ci<form->cols; ci+=co) {
248 if (is_active(form, arow, ci))
251 if (set_active(form, arow, ci))
258 void form_show(form_t *_form)
264 void form_draw(WINDOW *_win)
269 // Validate everything
273 // Calculate column width
274 int col_size[form->cols];
275 field_sizes(form, col_size);
277 // Make sure we have an active field
278 if (!can_active(form, arow, acol))
279 move_active(form, 0, 0);
282 for (int r = 0; r < form->rows; r++) {
283 for (int c = 0; c < form->cols; c++) {
284 form_field_t *field = form->fields[r][c];
285 // Calculate form field size
286 int width = col_size[c];
287 for (int i = c+1; i < form->cols; i++) {
288 if (form->fields[r][i])
290 width += col_size[i];
294 field_draw(field, width, is_active(form, r, c));
296 wprintw(win, "%*s", width, "");
302 int form_run(int key, mmask_t btn, int row, int col)
304 // Validate everything
308 // Check movement keys
310 case 'h': move_active(form, 0, -1); goto redraw;
311 case 'j': move_active(form, 1, 0); goto redraw;
312 case 'k': move_active(form, -1, 0); goto redraw;
313 case 'l': move_active(form, 0, 1); goto redraw;
316 // Handle mouse movement
317 if (key == KEY_MOUSE && row < form->rows) {
318 int pos=0, col_size[form->cols];
319 field_sizes(form, col_size);
320 for (int c = 0; c < form->cols; c++) {
321 if (pos < col && col < pos+col_size[c]) {
322 if (can_active(form, row, c)) {
332 // Search for hotkeys
333 for (int r = 0; r < form->rows; r++)
334 for (int c = 0; c < form->cols; c++)
335 if (form->fields[r][c] &&
336 form->fields[r][c]->hotkey == key) {
351 static form_text_t title = TEXT('t');
352 static form_text_t location = TEXT('o');
353 static form_date_t start = DATE('s');
354 static form_date_t end = DATE('e');
355 static form_date_t due_date = DATE('u');
356 static form_number_t completed = NUMBER('p', .f.after="%");
357 static form_list_t calendar = LIST('c');
358 static form_list_t category = LIST('g');
359 static form_list_t repeat = LIST('r');
360 static form_number_t frequency = NUMBER(0);
361 static form_button_t weekdays = BUTTONS("Su Mo Tu We Th Fr Sa");
362 static form_text_t details = TEXT('d');
364 static form_t edit = { 12, 4, {
365 { LABEL("_Title: "), &title.f },
366 { LABEL("L_ocation: "), &location.f },
368 { LABEL("_Start: "), &start.f, LABEL(" _End: "), &end.f },
369 { LABEL("D_ue Date: "), &due_date.f, LABEL(" Com_pleted: "), &completed.f },
370 { LABEL("_Calendar: "), &calendar.f, LABEL(" Cate_gory: "), &category.f },
372 { LABEL("_Repeat: "), &repeat.f, LABEL(" Every: "), &frequency.f },
374 { NULL, &weekdays.f },
376 { LABEL("_Details: "), &details.f },
385 keypad(stdscr, TRUE);
389 use_default_colors();
390 mousemask(ALL_MOUSE_EVENTS, NULL);
393 win = newwin(LINES, COLS, 0, 0);
402 if (chr == KEY_MOUSE)
403 if (getmouse(&btn) != OK)
408 wresize(win, LINES, COLS);
414 case '\14': // Ctrl-L
423 if (form_run(chr, btn.bstate, btn.y, btn.x))
425 if (chr == ERR) // timeout