#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED
+#include <ctype.h>
#include <string.h>
#include <ncurses.h>
+#include "util.h"
#include "date.h"
+#include "cal.h"
+#include "view.h"
#include "form.h"
/* Constants */
-#define TEXT_WIDTH (20)
-#define NUMBER_WIDTH (10)
#define DATE_WIDTH (4+1+2+1+2 +1+ 2+1+2)
+#define TEXT_WIDTH (DATE_WIDTH)
+#define NUMBER_WIDTH (10)
/* Widget accessors */
#define FF_TEXT(f) (((form_text_t *)f)->text)
#define FF_LIST(f) (((form_list_t *)f))
/* Local variables */
-static WINDOW *form_win;
-static int form_row;
-static int form_col;
+static form_t *form; // current form
+static WINDOW *win; // current window
+static int arow; // active row
+static int acol; // active row
-/* Helpeer functions */
-int label_width(const char *label)
+/* Helper functions */
+static int label_width(const char *label)
{
int len = 0;
for (int i = 0; label[i]; i++)
return len;
}
-void label_print(WINDOW *win, const char *label)
+static void label_print(const char *label)
{
for (int i = 0; label[i]; i++) {
if (label[i] == '_' && label[i+1])
- waddch(form_win, label[++i]
+ waddch(win, label[++i]
| A_UNDERLINE | A_BOLD);
else
- waddch(form_win, label[i]);
+ waddch(win, label[i]);
}
}
-int field_width(form_field_t *field)
+static int field_width(form_field_t *field)
{
// Calculate list width
int list_size = 0;
return width;
}
-void field_sizes(form_t *form, int *col_size)
+static void field_sizes(form_t *form, int *col_size)
{
// Do this by column, and right to left so that we can add
// in the extra space available for blank spaces.
}
}
-void field_draw(WINDOW *win, form_field_t *field, int width, int hover)
+static void field_draw(form_field_t *field, int width, int hover)
{
char **map = FF_LIST(field)->map;
int idx = FF_LIST(field)->idx;
int boxed = field->type == FORM_TEXT ||
field->type == FORM_NUMBER;
+ int bold = field->attr.bold;
int under = 0;
int begin = getcurx(win);
int maxstr = width - strlen(before) - strlen(after);
+ if (bold) wattron(win, A_BOLD);
if (hover) wattron(win, A_REVERSE);
if (under) wattron(win, A_UNDERLINE);
if (boxed) waddch(win, '[');
wprintw(win, "%s", before);
switch (field->type) {
case FORM_LABEL:
- label_print(win, field->label);
+ label_print(field->label);
break;
case FORM_TEXT:
- wprintw(win, "%-.*s",
- maxstr, FF_TEXT(field));
+ if (FF_TEXT(field))
+ wprintw(win, "%-.*s",
+ maxstr, FF_TEXT(field));
break;
case FORM_DATE:
if (no_date(&FF_DATE(field)))
- wprintw(win, "%s", "undefined");
+ wprintw(win, "%s", "{undefined}");
else
wprintw(win, "%04d-%02d-%02d %02d:%02d",
FF_DATE(field).year, FF_DATE(field).month+1,
if (map)
wprintw(win, "%s", map[idx]);
else
- wprintw(win, "%s", "undefined");
+ wprintw(win, "%s", "{undefined}");
break;
}
int pad = width-(getcurx(win)-begin)-boxed;
if (boxed) waddch(win, ']');
if (under) wattroff(win, A_UNDERLINE);
if (hover) wattroff(win, A_REVERSE);
+ if (bold) wattroff(win, A_BOLD);
}
-int is_active(form_t *form, int r, int c)
+static int is_active(form_t *form, int r, int c)
{
- if (r == form_row && c == form_col)
- return 1;
- for (int i = c+1; i < form->cols && !form->fields[r][i]; i++)
- if (r == form_row && i == form_col)
+ for (int i = c; i >= 0; i--) {
+ if (r == arow && i == acol)
return 1;
+ if (form->fields[r][i])
+ break;
+ }
+ for (int i = c+1; i < form->cols; i++) {
+ if (form->fields[r][i])
+ break;
+ if (r == arow && i == acol)
+ return 1;
+ }
return 0;
}
-int can_active(form_t *form, int r, int c)
+static int can_active(form_t *form, int r, int c)
{
for (int i = c; i >= 0; i--) {
if (!form->fields[r][i])
continue;
- if (form->fields[r][i]->type != FORM_LABEL)
- return 1;
- else
+ if (form->fields[r][i]->type == FORM_LABEL)
return 0;
+ return 1;
}
return 0;
}
-void set_active(form_t *form, int ro, int co)
+static int set_active(form_t *form, int r, int c)
+{
+ if (can_active(form, r, c)) {
+ arow = r;
+ acol = c;
+ return 1;
+ }
+ return 0;
+}
+
+static void move_active(form_t *form, int ro, int co)
{
// Set first field
- if (ro==0 && co==0) {
+ if (ro==0 && co==0)
for (int r = 0; r < form->rows; r++)
- for (int c = 0; c < form->cols; c++) {
- if (can_active(form, r, c)) {
- form_row = r;
- form_col = c;
+ for (int c = 0; c < form->cols; c++)
+ if (set_active(form, r, c))
return;
- }
- }
- }
// Move up/down
if (ro) {
- for (int ri = form_row+ro; ri>=0 && ri<form->rows; ri+=ro) {
- if (can_active(form, ri, form_col)) {
- form_row = ri;
- return;
- }
+ for (int ri = arow+ro; ri>=0 && ri<form->rows; ri+=ro) {
+ if (is_active(form, ri, acol))
+ continue;
+ // Search for a row
+ for (int ci = acol; ci < form->cols; ci++)
+ if (set_active(form, ri, ci))
+ return;
+ for (int ci = acol; ci >= 0; ci--)
+ if (set_active(form, ri, ci))
+ return;
}
}
// Move left/right
if (co) {
- for (int ci = form_col+co; ci>=0 && ci<form->cols; ci+=co) {
- for (int ri = form_row; ri < form->rows; ri++)
- if (can_active(form, form_row, ci)) {
- form_row = ri;
- form_col = ci;
- return;
- }
- for (int ri = form_row; ri >= 0; ri--)
- if (can_active(form, form_row, ci)) {
- form_row = ri;
- form_col = ci;
- return;
- }
+ for (int ci = acol+co; ci>=0 && ci<form->cols; ci+=co) {
+ if (is_active(form, arow, ci))
+ continue;
+ // Simple move
+ if (set_active(form, arow, ci))
+ return;
}
}
}
-/* Initialize */
-void form_init(void)
+static void form_edit(void)
{
- form_win = newwin(LINES, COLS, 0, 0);
+ int cur=0, len=0;
+ char buf[512] = {};
+ int col_size[form->cols];
+
+ field_sizes(form, col_size);
+
+ int rrow = arow;
+ int rcol = 0;
+ int rlen = 0;
+ for (int c = 0; c < form->cols; c++) {
+ if (is_active(form, arow, c))
+ rlen += col_size[c];
+ else if (rlen == 0)
+ rcol += col_size[c];
+ else
+ break;
+ }
+
+ int done = 0;
+ curs_set(true);
+ while (!done) {
+ // Draw
+ int idx = MAX(0, len-(rlen-2));
+ wmove(win, rrow, rcol);
+ wprintw(win, "[%-*.*s]", rlen-2, rlen-2, &buf[idx]);
+ wmove(win, rrow, rcol+1+cur-idx);
+ wrefresh(win);
+
+ // Read next character
+ MEVENT btn;
+ int chr = getch();
+ date_sync();
+ if (chr == KEY_MOUSE)
+ if (getmouse(&btn) != OK)
+ continue;
+
+ // Line editing
+ switch (chr) {
+ case ERR:
+ case KEY_RESIZE:
+ case '\14':
+ case '\7':
+ //view_run(chr, btn.bstate, btn.y, btn.x);
+ continue;
+ case '\012': // Enter
+ done = 1;
+ break;
+ case '\033': // Escape
+ done = 2;
+ break;
+ case KEY_LEFT:
+ if (cur > 0)
+ cur--;
+ break;
+ case KEY_RIGHT:
+ if (cur < len)
+ cur++;
+ break;
+ case KEY_BACKSPACE:
+ if (cur > 0) {
+ memmove(&buf[cur-1], &buf[cur], (len-cur)+1);
+ cur--;
+ len--;
+ }
+ break;
+ case KEY_DC:
+ if (cur < len) {
+ memmove(&buf[cur], &buf[cur+1], (len-cur)+1);
+ len--;
+ }
+ break;
+ }
+
+ // Text input
+ if (isprint(chr)) {
+ if (len+1 < sizeof(buf)) {
+ memmove(&buf[cur+1], &buf[cur], (len-cur)+1);
+ buf[cur] = chr;
+ cur++;
+ len++;
+ //debug("form: cur=%d, len=%d", cur, len);
+ } else {
+ debug("form: out of space");
+ }
+ } else {
+ debug("form: Unhandled key - Dec %3d, Hex %02x, Oct %03o, Chr <%c>",
+ chr, chr, chr, chr);
+ }
+ }
+ curs_set(false);
}
-/* Resize */
-void form_resize(void)
+/* Form functions */
+void form_show(form_t *_form)
{
- mvwin(form_win, 0, 0);
- wresize(form_win, LINES, COLS);
+ // Save form
+ form = _form;
}
-/* Run */
-int form_draw(form_t *form)
+void form_draw(WINDOW *_win)
{
- int col_size[form->cols];
+ // Save window
+ win = _win;
+
+ // Validate everything
+ if (!win || !form)
+ return;
// Calculate column width
+ int col_size[form->cols];
field_sizes(form, col_size);
// Make sure we have an active field
- if (!can_active(form, form_row, form_col))
- set_active(form, 0, 0);
+ if (!can_active(form, arow, acol))
+ move_active(form, 0, 0);
// Display form
for (int r = 0; r < form->rows; r++) {
}
// Draw the field
if (field)
- field_draw(form_win, field, width,
- is_active(form, r, c));
+ field_draw(field, width, is_active(form, r, c));
else if (c == 0)
- wprintw(form_win, "%*s", width, "");
+ wprintw(win, "%*s", width, "");
}
- wprintw(form_win, "\n");
+ wprintw(win, "\n");
}
- return 0;
}
-int form_run(form_t *form, int key, mmask_t btn, int row, int col)
+int form_run(int key, mmask_t btn, int row, int col)
{
+ // Validate everything
+ if (!form)
+ return 0;
+
// Check movement keys
switch (key) {
- case 'h': set_active(form, 0, -1); goto redraw;
- case 'j': set_active(form, 1, 0); goto redraw;
- case 'k': set_active(form, -1, 0); goto redraw;
- case 'l': set_active(form, 0, 1); goto redraw;
+ case 'h': move_active(form, 0, -1); goto redraw;
+ case 'j': move_active(form, 1, 0); goto redraw;
+ case 'k': move_active(form, -1, 0); goto redraw;
+ case 'l': move_active(form, 0, 1); goto redraw;
}
// Handle mouse movement
for (int c = 0; c < form->cols; c++) {
if (pos < col && col < pos+col_size[c]) {
if (can_active(form, row, c)) {
- form_row = row;
- form_col = c;
+ arow = row;
+ acol = c;
goto redraw;
}
}
for (int c = 0; c < form->cols; c++)
if (form->fields[r][c] &&
form->fields[r][c]->hotkey == key) {
- form_row = r;
- form_col = c;
+ arow = r;
+ acol = c;
goto redraw;
}
+
+ // Check editing (enter)
+ if (key == '\012') {
+ form_edit();
+ goto redraw;
+ }
+
return 0;
redraw:
- werase(form_win);
- form_draw(form);
- wrefresh(form_win);
+ werase(win);
+ form_draw(win);
+ wrefresh(win);
return 1;
}
mousemask(ALL_MOUSE_EVENTS, NULL);
/* Init */
- form_init();
+ win = newwin(LINES, COLS, 0, 0);
+ form_show(&edit);
/* Run */
while (1) {
continue;
switch (chr) {
case KEY_RESIZE:
- form_resize();
+ mvwin(win, 0, 0);
+ wresize(win, LINES, COLS);
refresh();
- werase(form_win);
- form_draw(&edit);
- wrefresh(form_win);
+ werase(win);
+ form_draw(win);
+ wrefresh(win);
continue;
case '\14': // Ctrl-L
clear();
case '\7': // Ctrl-G
refresh();
- werase(form_win);
- form_draw(&edit);
- wrefresh(form_win);
+ werase(win);
+ form_draw(win);
+ wrefresh(win);
continue;
}
- if (form_run(&edit, chr, btn.bstate, btn.y, btn.x))
+ if (form_run(chr, btn.bstate, btn.y, btn.x))
continue;
if (chr == ERR) // timeout
continue;