#include <sys/cdefs.h>
__RCSID("$NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $");
#include <menu.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "internals.h"
static void
_menui_calc_neighbours(MENU *menu, int item_no);
static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item);
int
_menui_stitch_items(MENU *menu)
{
int i, row_major;
row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
if (menu->posted == 1)
return E_POSTED;
if (menu->items == NULL)
return E_BAD_ARGUMENT;
menu->item_rows = menu->item_count / menu->cols;
menu->item_cols = menu->cols;
if (menu->item_count > (menu->item_rows * menu->item_cols))
menu->item_rows += 1;
_menui_max_item_size(menu);
for (i = 0; i < menu->item_count; i++) {
if (row_major) {
menu->items[i]->row = i / menu->item_cols;
menu->items[i]->col = i % menu->item_cols;
} else {
menu->items[i]->row = i % menu->item_rows;
menu->items[i]->col = i / menu->item_rows;
}
_menui_calc_neighbours(menu, i);
}
return E_OK;
}
static void
_menui_calc_neighbours(MENU *menu, int item_no)
{
int neighbour, cycle, row_major, edge;
ITEM *item;
row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC);
item = menu->items[item_no];
if (menu->item_rows < 2) {
if (cycle) {
item->up = item;
item->down = item;
} else {
item->up = NULL;
item->down = NULL;
}
} else {
if (menu->item_cols < 2) {
if (item_no == 0) {
if (cycle)
item->up =
menu->items[menu->item_count - 1];
else
item->up = NULL;
} else
item->up = menu->items[item_no - 1];
} else {
edge = 0;
if (row_major) {
if (item->row == 0) {
neighbour =
(menu->item_rows - 1) * menu->item_cols
+ item->col;
if (neighbour >= menu->item_count)
neighbour -= menu->item_cols;
edge = 1;
} else
neighbour = item_no - menu->item_cols;
} else {
if (item->row == 0) {
neighbour = menu->item_rows * item->col
+ menu->item_rows - 1;
if (neighbour >= menu->item_count)
neighbour = menu->item_count - 1;
edge = 1;
} else
neighbour = item_no - 1;
}
item->up = menu->items[neighbour];
if ((!cycle) && (edge == 1))
item->up = NULL;
}
if (menu->item_cols < 2) {
if (item_no == (menu->item_count - 1)) {
if (cycle)
item->down = menu->items[0];
else
item->down = NULL;
} else
item->down = menu->items[item_no + 1];
} else {
edge = 0;
if (row_major) {
if (item->row == menu->item_rows - 1) {
neighbour = item->col;
edge = 1;
} else {
neighbour = item_no + menu->item_cols;
if (neighbour >= menu->item_count) {
neighbour = item->col;
edge = 1;
}
}
} else {
if (item->row == menu->item_rows - 1) {
neighbour = item->col * menu->item_rows;
edge = 1;
} else {
neighbour = item_no + 1;
if (neighbour >= menu->item_count) {
neighbour = item->col
* menu->item_rows;
edge = 1;
}
}
}
item->down = menu->items[neighbour];
if ((!cycle) && (edge == 1))
item->down = NULL;
}
}
if (menu->item_cols < 2) {
if (cycle) {
item->left = item;
item->right = item;
} else {
item->left = NULL;
item->right = NULL;
}
} else {
if (menu->item_rows < 2) {
if (item_no == 0) {
if (cycle)
item->left =
menu->items[menu->item_count - 1];
else
item->left = NULL;
} else
item->left = menu->items[item_no - 1];
} else {
edge = 0;
if (row_major) {
if (item->col == 0) {
neighbour = item_no + menu->cols - 1;
if (neighbour >= menu->item_count)
neighbour = menu->item_count - 1;
edge = 1;
} else
neighbour = item_no - 1;
} else {
if (item->col == 0) {
neighbour = menu->item_rows
* (menu->item_cols - 1) + item->row;
if (neighbour >= menu->item_count)
neighbour -= menu->item_rows;
edge = 1;
} else
neighbour = item_no - menu->item_rows;
}
item->left = menu->items[neighbour];
if ((!cycle) && (edge == 1))
item->left = NULL;
}
if (menu->item_rows < 2) {
if (item_no == menu->item_count - 1) {
if (cycle)
item->right = menu->items[0];
else
item->right = NULL;
} else
item->right = menu->items[item_no + 1];
} else {
edge = 0;
if (row_major) {
if (item->col == menu->item_cols - 1) {
neighbour = item_no - menu->item_cols
+ 1;
edge = 1;
} else if (item_no == menu->item_count - 1) {
neighbour = item->row * menu->item_cols;
edge = 1;
} else
neighbour = item_no + 1;
} else {
if (item->col == menu->item_cols - 1) {
neighbour = item->row;
edge = 1;
} else {
neighbour = item_no + menu->item_rows;
if (neighbour >= menu->item_count) {
neighbour = item->row;
edge = 1;
}
}
}
item->right = menu->items[neighbour];
if ((!cycle) && (edge == 1))
item->right = NULL;
}
}
}
int
_menui_goto_item(MENU *menu, ITEM *item, int new_top_row)
{
int old_top_row = menu->top_row, old_cur_item = menu->cur_item;
if (item == NULL)
return E_REQUEST_DENIED;
menu->in_init = 1;
if (menu->top_row != new_top_row) {
if ((menu->posted == 1) && (menu->menu_term != NULL))
menu->menu_term(menu);
menu->top_row = new_top_row;
if ((menu->posted == 1) && (menu->menu_init != NULL))
menu->menu_init(menu);
}
if (menu->cur_item != item->index) {
if ((menu->posted == 1) && (menu->item_term != NULL))
menu->item_term(menu);
menu->cur_item = item->index;
menu->cur_row = item->row;
menu->cur_col = item->col;
if (menu->posted == 1)
_menui_redraw_menu(menu, old_top_row, old_cur_item);
if ((menu->posted == 1) && (menu->item_init != NULL))
menu->item_init(menu);
}
menu->in_init = 0;
return E_OK;
}
int
_menui_match_items(MENU *menu, int direction, int *item_matched)
{
int i, caseless;
caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE);
i = menu->cur_item;
if (direction == MATCH_NEXT_FORWARD) {
if (++i >= menu->item_count) i = 0;
} else if (direction == MATCH_NEXT_REVERSE) {
if (--i < 0) i = menu->item_count - 1;
}
do {
if (menu->items[i]->name.length >= menu->plen) {
if (caseless) {
if (strncasecmp(menu->items[i]->name.string,
menu->pattern,
(size_t) menu->plen) == 0) {
*item_matched = i;
menu->match_len = menu->plen;
return E_OK;
}
} else {
if (strncmp(menu->items[i]->name.string,
menu->pattern,
(size_t) menu->plen) == 0) {
*item_matched = i;
menu->match_len = menu->plen;
return E_OK;
}
}
}
if ((direction == MATCH_FORWARD) ||
(direction == MATCH_NEXT_FORWARD)) {
if (++i >= menu->item_count) i = 0;
} else {
if (--i <= 0) i = menu->item_count - 1;
}
} while (i != menu->cur_item);
menu->match_len = 0;
return E_NO_MATCH;
}
int
_menui_match_pattern(MENU *menu, int c, int direction, int *item_matched)
{
if (menu == NULL)
return E_BAD_ARGUMENT;
if (menu->items == NULL)
return E_BAD_ARGUMENT;
if (*menu->items == NULL)
return E_BAD_ARGUMENT;
if (isprint(c)) {
if ((menu->pattern = (char *)
realloc(menu->pattern,
menu->plen + sizeof(char) +
((menu->plen > 0)? 0 : 1)))
== NULL)
return E_SYSTEM_ERROR;
menu->pattern[menu->plen] = c;
menu->pattern[++menu->plen] = '\0';
if (menu->plen >= menu->max_item_width) {
menu->pattern[--menu->plen] = '\0';
return E_NO_MATCH;
}
if (_menui_match_items(menu, direction,
item_matched) == E_NO_MATCH) {
menu->pattern[--menu->plen] = '\0';
return E_NO_MATCH;
} else
return E_OK;
} else {
if (_menui_match_items(menu, direction,
item_matched) == E_OK) {
return E_OK;
} else {
return E_NO_MATCH;
}
}
}
void
_menui_draw_item(MENU *menu, int item)
{
int j, pad_len, mark_len;
mark_len = max(menu->mark.length, menu->unmark.length);
wmove(menu->scrwin,
menu->items[item]->row - menu->top_row,
menu->items[item]->col * (menu->col_width + 1));
if (menu->cur_item == item)
wattrset(menu->scrwin, menu->fore);
if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE)
wattron(menu->scrwin, menu->grey);
if ((menu->items[item]->selected == 1) ||
(((menu->opts & O_ONEVALUE) == O_ONEVALUE) &&
(menu->cur_item == item))) {
if (menu->mark.string != NULL) {
for (j = 0; j < menu->mark.length; j++) {
waddch(menu->scrwin,
menu->mark.string[j]);
}
}
for (j = menu->mark.length; j < mark_len; j++)
waddch(menu->scrwin, ' ');
} else {
if (menu->unmark.string != NULL) {
for (j = 0; j < menu->unmark.length; j++) {
waddch(menu->scrwin,
menu->unmark.string[j]);
}
}
for (j = menu->unmark.length; j < mark_len; j++)
waddch(menu->scrwin, ' ');
}
for (j=0; j < menu->items[item]->name.length; j++)
waddch(menu->scrwin,
menu->items[item]->name.string[j]);
pad_len = menu->col_width - menu->items[item]->name.length
- mark_len - 1;
if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) {
pad_len -= menu->items[item]->description.length - 1;
for (j = 0; j < pad_len; j++)
waddch(menu->scrwin, menu->pad);
for (j = 0; j < menu->items[item]->description.length; j++) {
waddch(menu->scrwin,
menu->items[item]->description.string[j]);
}
} else {
for (j = 0; j < pad_len; j++)
waddch(menu->scrwin, ' ');
}
menu->items[item]->visible = 1;
wattrset(menu->scrwin, menu->back);
if ((menu->items[item]->col > 0) &&
(menu->items[item]->col < (menu->item_cols - 1))) {
wmove(menu->scrwin,
menu->items[item]->row - menu->top_row,
menu->items[item]->col * (menu->col_width + 1) - 1);
waddch(menu->scrwin, ' ');
}
pos_menu_cursor(menu);
}
int
_menui_draw_menu(MENU *menu)
{
int rowmajor, i, j, k, row = -1, stride;
int incr, cur_row, offset, row_count;
rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
if (rowmajor) {
stride = 1;
incr = menu->item_cols;
} else {
stride = menu->item_rows;
incr = 1;
}
row_count = 0;
for (i = 0; i < menu->item_count; i += incr) {
if (menu->items[i]->row == menu->top_row)
break;
row_count++;
for (j = 0; j < menu->item_cols; j++) {
offset = j * stride + i;
if (offset >= menu->item_count)
break;
menu->items[offset]->visible = 0;
}
}
wmove(menu->scrwin, 0, 0);
menu->col_width = getmaxx(menu->scrwin) / menu->cols;
for (cur_row = 0; cur_row < menu->rows; cur_row++) {
for (j = 0; j < menu->cols; j++) {
offset = j * stride + i;
if (offset >= menu->item_count) {
wattrset(menu->scrwin, menu->back);
if (row < 0) {
row = menu->items[menu->item_count - 1]->row;
}
wmove(menu->scrwin, cur_row,
j * (menu->col_width + 1));
for (k = 0; k < menu->col_width; k++)
waddch(menu->scrwin, ' ');
} else {
_menui_draw_item(menu, offset);
}
}
i += incr;
row_count++;
}
if (row_count < menu->item_rows) {
for (cur_row = row_count; cur_row < menu->item_rows; cur_row++) {
for (j = 0; j < menu->item_cols; j++) {
offset = j * stride + i;
if (offset >= menu->item_count)
break;
menu->items[offset]->visible = 0;
}
i += incr;
}
}
return E_OK;
}
void
_menui_max_item_size(MENU *menu)
{
int i, with_desc, width;
with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC);
for (i = 0; i < menu->item_count; i++) {
width = menu->items[i]->name.length
+ max(menu->mark.length, menu->unmark.length);
if (with_desc)
width += menu->items[i]->description.length + 1;
menu->max_item_width = max(menu->max_item_width, width);
}
}
static void
_menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item)
{
if (menu->top_row != old_top_row) {
wclear(menu->scrwin);
_menui_draw_menu(menu);
} else {
if (menu->cur_item != old_cur_item) {
_menui_draw_item(menu, old_cur_item);
}
_menui_draw_item(menu, menu->cur_item);
}
}