Files
lcdproc/server/menuitem.c
T
boercher 164f4fbed0 - new option -prev, corrections for option -next
- adherence of LCDPROC_PERMISSIVE_MENU_GOTO corrected
2005-07-17 16:13:46 +00:00

1513 lines
42 KiB
C

/*
* menuitem.c
* This file is part of LCDd, the lcdproc server.
*
* This file is released under the GNU General Public License. Refer to the
* COPYING file distributed with this package.
*
* Copyright (c) 1999, William Ferrell, Scott Scriven
* 2002, Joris Robijn
* 2004, F5 Networks, Inc. - IP-address input
* 2005, Peter Marschall - error checks, ...
*
*
* Handles a menuitem and all actions that can be performed on it.
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "shared/report.h"
#include "menuitem.h"
#include "menuscreens.h"
#include "menu.h"
#include "drivers.h"
/* this is needed for verify_ipv4 and verify_ipv6. */
#include "sock.h"
#define MAX_NUMERIC_LEN 40
char *error_strs[] = {"", "Out of range", "Too long", "Too short", "Invalid Address"};
char *menuitemtypenames[] = {"menu", "action", "checkbox", "ring", "slider", "numeric", "alpha", "ip"};
char *menueventtypenames[] = {"select", "update", "plus", "minus", "enter", "leave"};
void menuitem_destroy_action (MenuItem *item);
void menuitem_destroy_checkbox (MenuItem *item);
void menuitem_destroy_ring (MenuItem *item);
void menuitem_destroy_slider (MenuItem *item);
void menuitem_destroy_numeric (MenuItem *item);
void menuitem_destroy_alpha (MenuItem *item);
void menuitem_destroy_ip (MenuItem *item);
void menuitem_reset_numeric (MenuItem *item);
void menuitem_reset_alpha (MenuItem *item);
void menuitem_reset_ip (MenuItem *item);
void menuitem_rebuild_screen_slider (MenuItem *item, Screen *s);
void menuitem_rebuild_screen_numeric (MenuItem *item, Screen *s);
void menuitem_rebuild_screen_alpha (MenuItem *item, Screen *s);
void menuitem_rebuild_screen_ip (MenuItem *item, Screen *s);
void menuitem_update_screen_slider (MenuItem *item, Screen *s);
void menuitem_update_screen_numeric (MenuItem *item, Screen *s);
void menuitem_update_screen_alpha (MenuItem *item, Screen *s);
void menuitem_update_screen_ip (MenuItem *item, Screen *s);
MenuResult menuitem_process_input_slider (MenuItem *item, MenuToken token, char * key, bool extended);
MenuResult menuitem_process_input_numeric (MenuItem *item, MenuToken token, char * key, bool extended);
MenuResult menuitem_process_input_alpha (MenuItem *item, MenuToken token, char * key, bool extended);
MenuResult menuitem_process_input_ip (MenuItem *item, MenuToken token, char * key, bool extended);
/* information about string representation of IP addresses */
typedef struct {
int maxlen; // max length of the string represenation;
char sep; // separators between numeric strings
int base; // numeric base
int width; // width of each numeric string
int limit; // upper limit of each numeric string
int posValue[5]; // digit-value of the digits at the approp. position in the numeric string
char format[5]; // printf-format string to print a numeric string
int (*verify)(const char *); // verify function: returns 1 = OK, 0 = error
char dummy[16]; // dummy value (if provided value is no IP address)
} IpSstringProperties;
const IpSstringProperties IPinfo[] = {
// IPv4: 15 char long '.'-separated sequence of 3-digit decimal strings in range 000 - 255
{ 15, '.', 10, 3, 255, { 100, 10, 1, 0, 0 }, "%03d", verify_ipv4, "0.0.0.0" },
// IPv6: 39 char long ':'-separated sequence of 4-digit hex strings in range 0000 - ffff
{ 39, ':', 16, 4, 65535, { 4096, 256, 16, 1, 0 }, "%04x", verify_ipv6, "0:0:0:0:0:0:0:0" }
};
/******** MENU UTILITY FUNCTIONS ********/
/** returns default_result if predecessor_id is NULL or the respective value
* otherwise. */
MenuResult menuitem_predecessor2menuresult(char *predecessor_id, MenuResult default_result)
{
if (predecessor_id == NULL)
return default_result;
if (strcmp("_quit_", predecessor_id) == 0)
return MENURESULT_QUIT;
else if (strcmp("_close_", predecessor_id) == 0)
return MENURESULT_CLOSE;
else if (strcmp("_none_", predecessor_id) == 0)
return MENURESULT_NONE;
else
return MENURESULT_PREDECESSOR;
}
/** returns default_result if successor_id is NULL or the respective value
* otherwise. */
MenuResult menuitem_successor2menuresult(char *successor_id, MenuResult default_result)
{
if (successor_id == NULL)
return default_result;
if (strcmp("_quit_", successor_id) == 0)
return MENURESULT_QUIT;
else if (strcmp("_close_", successor_id) == 0)
return MENURESULT_CLOSE;
else if (strcmp("_none_", successor_id) == 0)
return MENURESULT_NONE;
else
return MENURESULT_SUCCESSOR;
}
/** Returns the MenuItem with the specified id if found or NULL. The search
* for itemid is restricted to the client's menus if the preprocessor macro
* LCDPROC_PERMISSIVE_MENU_GOTO is *not* set. */
MenuItem *menuitem_search(char *menu_id, Client *client)
{
# ifdef LCDPROC_PERMISSIVE_MENU_GOTO
MenuItem *top = main_menu;
# else
MenuItem *top = client->menu;
# endif /* LCDPROC_PERMISSIVE_MENU_GOTO */
return menu_find_item (top, menu_id, true);
}
/******** FUNCTION TABLES ********/
/* Tables with functions to call for all different item types */
void (*destructor_table[NUM_ITEMTYPES] ) (MenuItem *item) =
{
menu_destroy,
NULL,
NULL,
menuitem_destroy_ring,
menuitem_destroy_slider,
menuitem_destroy_numeric,
menuitem_destroy_alpha,
menuitem_destroy_ip
};
void (*reset_table[NUM_ITEMTYPES] ) (MenuItem *item) =
{
menu_reset,
NULL,
NULL,
NULL,
NULL,
menuitem_reset_numeric,
menuitem_reset_alpha,
menuitem_reset_ip
};
void (*build_screen_table[NUM_ITEMTYPES] ) (MenuItem *item, Screen *s) =
{
menu_build_screen,
NULL,
NULL,
NULL,
menuitem_rebuild_screen_slider,
menuitem_rebuild_screen_numeric,
menuitem_rebuild_screen_alpha,
menuitem_rebuild_screen_ip
};
void (*update_screen_table[NUM_ITEMTYPES] ) (MenuItem *item, Screen *s) =
{
menu_update_screen,
NULL,
NULL,
NULL,
menuitem_update_screen_slider,
menuitem_update_screen_numeric,
menuitem_update_screen_alpha,
menuitem_update_screen_ip
};
MenuResult (*process_input_table[NUM_ITEMTYPES] ) (MenuItem *item, MenuToken token, char *key, bool extended) =
{
menu_process_input,
NULL,
NULL,
NULL,
menuitem_process_input_slider,
menuitem_process_input_numeric,
menuitem_process_input_alpha,
menuitem_process_input_ip
};
/******** METHODS ********/
MenuItem *menuitem_create (MenuItemType type, char *id, MenuEventFunc(*event_func),
char *text, Client *client)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( type=%d, id=\"%s\", event_func=%p, text=\"%s\" )",
__FUNCTION__, type, id, event_func, text);
if ((id == NULL) || (text == NULL)) {
// report (RPT_ERR, "%s: illegal id or text", __FUNCTION__);
return NULL;
}
/* Allocate space and fill struct */
new_item = malloc (sizeof(MenuItem));
if (!new_item) {
report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
return NULL;
}
new_item->type = type;
new_item->id = strdup (id);
if (!new_item->id) {
report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
free (new_item);
return NULL;
}
new_item->successor_id = NULL;
new_item->predecessor_id = NULL;
new_item->parent = NULL;
new_item->event_func = event_func;
new_item->text = strdup (text);
if (!new_item->text) {
report (RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
free (new_item->id);
free (new_item);
return NULL;
}
new_item->client = client;
new_item->is_hidden = false;
/* Clear the type specific data part */
memset ( &(new_item->data), '\0', sizeof(new_item->data));
return new_item;
}
// fixme: the menu_result arg is obsoleted (use char* successor_id)
MenuItem *menuitem_create_action (char *id, MenuEventFunc(*event_func),
char *text, Client *client, MenuResult menu_result)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=[%s], event_func=%p, text=\"%s\", close_menu=%d )",
__FUNCTION__, id, event_func, text, menu_result);
new_item = menuitem_create (MENUITEM_ACTION, id, event_func, text, client);
if (new_item != NULL)
{
switch (menu_result)
{
case MENURESULT_NONE:
new_item->successor_id = strdup("_none_");
break;
case MENURESULT_CLOSE:
new_item->successor_id = strdup("_close_");
break;
case MENURESULT_QUIT:
new_item->successor_id = strdup("_quit_");
break;
default:
assert(!"unexpected MENURESULT");
}
}
return new_item;
}
MenuItem *menuitem_create_checkbox (char *id, MenuEventFunc(*event_func),
char *text, Client *client, bool allow_gray, bool value)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=[%s], event_func=%p, text=\"%s\", allow_gray=%d, value=%d )",
__FUNCTION__, id, event_func, text, allow_gray, value);
new_item = menuitem_create (MENUITEM_CHECKBOX, id, event_func, text, client);
if (new_item != NULL) {
new_item->data.checkbox.allow_gray = allow_gray;
new_item->data.checkbox.value = value;
}
return new_item;
}
MenuItem *menuitem_create_ring (char *id, MenuEventFunc(*event_func),
char *text, Client *client, char *strings, short value)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=[%s], event_func=%p, text=\"%s\", strings=\"%s\", value=%d )",
__FUNCTION__, id, event_func, text, strings, value);
new_item = menuitem_create (MENUITEM_RING, id, event_func, text, client);
if (new_item != NULL) {
new_item->data.ring.strings = tablist2linkedlist (strings);
new_item->data.ring.value = value;
}
return new_item;
}
MenuItem *menuitem_create_slider (char *id, MenuEventFunc(*event_func),
char *text, Client *client, char *mintext, char *maxtext,
int minvalue, int maxvalue, int stepsize, int value)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=[%s], event_func=%p, text=\"%s\", mintext=\"%s\", maxtext=\"%s\", minvalue=%d, maxvalue=%d, stepsize=%d, value=%d )",
__FUNCTION__, id, event_func, text, mintext, maxtext, minvalue, maxvalue, stepsize, value);
new_item = menuitem_create (MENUITEM_SLIDER, id, event_func, text, client);
if (new_item != NULL) {
new_item->data.slider.mintext = strdup (mintext);
new_item->data.slider.maxtext = strdup (maxtext);
new_item->data.slider.minvalue = minvalue;
new_item->data.slider.maxvalue = maxvalue;
new_item->data.slider.stepsize = stepsize;
new_item->data.slider.value = value;
}
return new_item;
}
MenuItem *menuitem_create_numeric (char *id, MenuEventFunc(*event_func),
char *text, Client *client, int minvalue, int maxvalue, int value)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=[%s], event_func=%p, text=\"%s\", minvalue=%d, maxvalue=%d, value=%d )",
__FUNCTION__, id, event_func, text, minvalue, minvalue, value);
new_item = menuitem_create (MENUITEM_NUMERIC, id, event_func, text, client);
if (new_item != NULL) {
new_item->data.numeric.maxvalue = maxvalue;
new_item->data.numeric.minvalue = minvalue;
new_item->data.numeric.edit_str = malloc (MAX_NUMERIC_LEN);
new_item->data.numeric.value = value;
}
return new_item;
}
MenuItem *menuitem_create_alpha (char *id, MenuEventFunc(*event_func),
char *text, Client *client, char password_char, short minlength, short maxlength,
bool allow_caps, bool allow_noncaps, bool allow_numbers,
char *allowed_extra, char *value)
{
MenuItem *new_item;
debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", password_char=%d, maxlength=%d, value=\"%s\" )",
__FUNCTION__, id, event_func, text, password_char, maxlength, value);
new_item = menuitem_create (MENUITEM_ALPHA, id, event_func, text, client);
if (new_item != NULL) {
new_item->data.alpha.password_char = password_char;
new_item->data.alpha.minlength = minlength;
new_item->data.alpha.maxlength = maxlength;
new_item->data.alpha.allow_caps = allow_caps;
new_item->data.alpha.allow_noncaps = allow_noncaps;
new_item->data.alpha.allow_numbers = allow_numbers;
new_item->data.alpha.allowed_extra = strdup (allowed_extra);
new_item->data.alpha.value = malloc (maxlength + 1);
if (new_item->data.alpha.value != NULL) {
strncpy (new_item->data.alpha.value, value, maxlength);
new_item->data.alpha.value[maxlength] = 0;
}
new_item->data.alpha.edit_str = malloc (maxlength + 1);
}
return new_item;
}
MenuItem *menuitem_create_ip (char *id, MenuEventFunc(*event_func),
char *text, Client *client, bool v6, char *value)
{
MenuItem *new_item;
const IpSstringProperties *ipinfo;
debug (RPT_DEBUG, "%s( id=\"%s\", event_func=%p, text=\"%s\", v6=%d, value=\"%s\" )",
__FUNCTION__, id, event_func, text, v6, value);
new_item = menuitem_create (MENUITEM_IP, id, event_func, text, client);
new_item->data.ip.v6 = v6;
ipinfo = (v6) ? &IPinfo[1] : &IPinfo[0];
new_item->data.ip.maxlength = ipinfo->maxlen;;
new_item->data.ip.value = malloc (new_item->data.ip.maxlength + 1);
if (ipinfo->verify(value)) {
strncpy(new_item->data.ip.value, value, new_item->data.ip.maxlength);
new_item->data.ip.value[new_item->data.ip.maxlength] = '\0';
}
else {
strncpy(new_item->data.ip.value, ipinfo->dummy, new_item->data.ip.maxlength);
new_item->data.ip.value[new_item->data.ip.maxlength] = '\0';
}
new_item->data.ip.edit_str = malloc (new_item->data.ip.maxlength + 1);
return new_item;
}
void menuitem_destroy (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
void (*destructor) (MenuItem *);
/* First destroy type specific data */
destructor = destructor_table[item->type];
if (destructor)
destructor (item);
/* Following strings should always be allocated */
free (item->text);
free (item->id);
/* And finally...*/
free (item);
}
}
void menuitem_destroy_ring (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
char * s;
/* deallocate the strings */
for (s = LL_GetFirst(item->data.ring.strings);
s != NULL;
s = LL_GetNext(item->data.ring.strings)) {
free (s);
}
/* and the list */
LL_Destroy (item->data.ring.strings);
}
}
void menuitem_destroy_slider (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
/* These strings should always be allocated */
free (item->data.slider.mintext);
free (item->data.slider.maxtext);
}
}
void menuitem_destroy_numeric (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
/* This string should always be allocated */
free (item->data.numeric.edit_str);
}
}
void menuitem_destroy_alpha (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
/* These strings should always be allocated */
free (item->data.alpha.allowed_extra);
free (item->data.alpha.value);
free (item->data.alpha.edit_str);
}
}
void menuitem_destroy_ip (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
/* These strings should always be allocated */
free (item->data.ip.value);
free (item->data.ip.edit_str);
}
/******** MENU ITEM RESET FUNCTIONS ********/
void menuitem_reset (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
void (*func) (MenuItem *);
/* First destroy type specific data */
func = reset_table[item->type];
if (func)
func (item);
}
}
void menuitem_reset_numeric (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
item->data.numeric.edit_pos = 0;
item->data.numeric.edit_offs = 0;
memset (item->data.numeric.edit_str, '\0', MAX_NUMERIC_LEN);
if (item->data.numeric.minvalue < 0) {
snprintf (item->data.numeric.edit_str, MAX_NUMERIC_LEN,
"%+d", item->data.numeric.value);
} else {
snprintf (item->data.numeric.edit_str, MAX_NUMERIC_LEN,
"%d", item->data.numeric.value);
}
}
}
void menuitem_reset_alpha (MenuItem *item)
{
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
if (item != NULL) {
item->data.alpha.edit_pos = 0;
item->data.alpha.edit_offs = 0;
memset (item->data.alpha.edit_str, '\0', item->data.alpha.maxlength+1);
strcpy (item->data.alpha.edit_str, item->data.alpha.value);
}
}
void menuitem_reset_ip (MenuItem *item)
{
char *start = item->data.ip.value;
const IpSstringProperties *ipinfo = (item->data.ip.v6) ? &IPinfo[1] : &IPinfo[0];
debug (RPT_DEBUG, "%s( item=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"));
item->data.ip.edit_pos = 0;
item->data.ip.edit_offs = 0;
memset (item->data.ip.edit_str, '\0', item->data.ip.maxlength+1);
// normalize IP address string to e.g. 010.002.250.002 / 00:01:02:04:05:06:07:08:09:0a:0b:0c:0e:0f
while (start != NULL)
{
char *end;
char tmpstr[5];
int num = (int) strtol(start, (char **) NULL, ipinfo->base);
snprintf(tmpstr, 5, ipinfo->format, num);
strcat(item->data.ip.edit_str, tmpstr);
end = strchr(start, ipinfo->sep);
start = (end != NULL) ? (end + 1) : NULL;
if (start != NULL) {
tmpstr[0] = ipinfo->sep;
tmpstr[1] = '\0';
strcat(item->data.ip.edit_str, tmpstr);
}
}
}
/******** MENU SCREEN BUILD FUNCTIONS ********/
void menuitem_rebuild_screen (MenuItem *item, Screen *s)
{
Widget * w;
void (*build_screen) (MenuItem *item, Screen *s);
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if (!display_props) {
/* Nothing to build if no display size is known */
report (RPT_ERR, "%s: display size unknown", __FUNCTION__ );
return;
}
if (s != NULL) {
/* First remove all widgets from the screen */
while ( (w = screen_getfirst_widget(s)) != NULL) {
/* We know these widgets don't have subwidgets, so we can
* easily remove them
*/
screen_remove_widget (s, w);
widget_destroy (w);
}
if (item != NULL) {
/* Call type specific screen building function */
build_screen = build_screen_table [item->type];
if (build_screen) {
build_screen (item, s);
} else {
report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
return;
}
/* Also always call update_screen */
menuitem_update_screen (item, s);
}
}
}
void menuitem_rebuild_screen_slider (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
if (display_props->height >= 2 ) {
/* Only add a title if enough space... */
w = widget_create ("text", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup(item->text);
w->x = 1;
w->y = 1;
}
w = widget_create ("bar", WID_HBAR, s);
screen_add_widget (s, w);
w->width = display_props->width;
if (display_props->height > 2 ) {
/* This is option 1: we have enought space, so the bar and
* min/max texts can be on separate lines.
*/
w->x = 2;
w->y = display_props->height / 2 + 1;
w->width = display_props->width - 2;
}
w = widget_create ("min", WID_STRING, s);
screen_add_widget (s, w);
w->text = NULL;
w->x = 1;
if (display_props->height > 2 ) {
w->y = display_props->height / 2 + 2;
} else {
w->y = display_props->height / 2 + 1;
}
w = widget_create ("max", WID_STRING, s);
screen_add_widget (s, w);
w->text = NULL;
w->x = 1;
if (display_props->height > 2 ) {
w->y = display_props->height / 2 + 2;
} else {
w->y = display_props->height / 2 + 1;
}
}
void menuitem_rebuild_screen_numeric (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
if (display_props->height >= 2 ) {
/* Only add a title if enough space... */
w = widget_create ("text", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup(item->text);
w->x = 1;
w->y = 1;
}
w = widget_create ("value", WID_STRING, s);
screen_add_widget (s, w);
w->text = malloc (MAX_NUMERIC_LEN);
w->x = 2;
w->y = display_props->height / 2 + 1;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = widget_create ("error", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup("");
w->x = 1;
w->y = display_props->height;
}
}
void menuitem_rebuild_screen_alpha (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
if (display_props->height >= 2 ) {
/* Only add a title if enough space... */
w = widget_create ("text", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup(item->text);
w->x = 1;
w->y = 1;
}
w = widget_create ("value", WID_STRING, s);
screen_add_widget (s, w);
w->text = malloc (item->data.alpha.maxlength+1);
w->x = 2;
w->y = display_props->height / 2 + 1;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = widget_create ("error", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup("");
w->x = 1;
w->y = display_props->height;
}
}
void menuitem_rebuild_screen_ip (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
if (display_props->height >= 2 ) {
/* Only add a title if enough space... */
w = widget_create ("text", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup(item->text);
w->x = 1;
w->y = 1;
}
w = widget_create ("value", WID_STRING, s);
screen_add_widget (s, w);
w->text = malloc (item->data.ip.maxlength+1);
w->x = 2;
w->y = display_props->height / 2 + 1;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = widget_create ("error", WID_STRING, s);
screen_add_widget (s, w);
w->text = strdup("");
w->x = 1;
w->y = display_props->height;
}
}
/******** MENU SCREEN UPDATE FUNCTIONS ********/
void menuitem_update_screen (MenuItem *item, Screen *s)
{
void (*update_screen) (MenuItem *item, Screen *s);
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
/* Disable the cursor by default */
s->cursor = CURSOR_OFF;
/* Call type specific screen building function */
update_screen = update_screen_table [item->type];
if (update_screen) {
update_screen (item, s);
} else {
report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
return;
}
}
void menuitem_update_screen_slider (MenuItem *item, Screen *s)
{
Widget * w;
int min_len, max_len;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
/* Calculate the bar position and length by filling buffers */
min_len = strlen (item->data.slider.mintext);
max_len = strlen (item->data.slider.maxtext);
/* And adjust the data */
w = screen_find_widget (s, "bar");
if (display_props->height <= 2 ) {
/* This is option 2: we're tight on lines, so we put the bar
* and min/max texts on the same line.
*/
w->x = 1 + min_len;
w->y = display_props->height;
w->width = display_props->width -
min_len - max_len;
}
/* FUTURE: w->promille = 1000 * (item->data.slider.value - item->data.slider.minvalue) / (item->data.slider.maxvalue - item->data.slider.minvalue) */;
w->length = w->width * display_props->cellwidth
* (item->data.slider.value - item->data.slider.minvalue)
/ (item->data.slider.maxvalue - item->data.slider.minvalue);
w = screen_find_widget (s, "min");
if (w->text) free (w->text);
w->text = strdup(item->data.slider.mintext);
w = screen_find_widget (s, "max");
if (w->text) free (w->text);
w->x = 1 + display_props->width - max_len;
w->text = strdup(item->data.slider.maxtext);
}
void menuitem_update_screen_numeric (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
w = screen_find_widget (s, "value");
strcpy (w->text, item->data.numeric.edit_str + item->data.numeric.edit_offs);
s->cursor = CURSOR_DEFAULT_ON;
s->cursor_x = w->x + item->data.numeric.edit_pos - item->data.numeric.edit_offs;
s->cursor_y = w->y;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = screen_find_widget (s, "error");
free (w->text);
w->text = strdup (error_strs[item->data.numeric.error_code]);
}
}
void menuitem_update_screen_alpha (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
w = screen_find_widget (s, "value");
if (item->data.alpha.password_char == '\0') {
strcpy (w->text, item->data.alpha.edit_str + item->data.alpha.edit_offs);
} else {
int len = strlen(item->data.alpha.edit_str) - item->data.alpha.edit_offs;
memset (w->text, item->data.alpha.password_char, len);
w->text[len] = '\0';
}
s->cursor = CURSOR_DEFAULT_ON;
s->cursor_x = w->x + item->data.alpha.edit_pos - item->data.alpha.edit_offs;
s->cursor_y = w->y;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = screen_find_widget (s, "error");
free (w->text);
w->text = strdup (error_strs[item->data.alpha.error_code]);
}
}
void menuitem_update_screen_ip (MenuItem *item, Screen *s)
{
Widget * w;
debug (RPT_DEBUG, "%s( item=[%s], screen=[%s] )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"),
((s != NULL) ? s->id : "(null)"));
if ((item == NULL) || (s == NULL))
return;
w = screen_find_widget (s, "value");
if (w != NULL)
strcpy (w->text, item->data.ip.edit_str + item->data.ip.edit_offs);
s->cursor = CURSOR_DEFAULT_ON;
s->cursor_x = w->x + item->data.ip.edit_pos - item->data.ip.edit_offs;
s->cursor_y = w->y;
/* Only display error string if enough space... */
if (display_props->height > 2 ) {
w = screen_find_widget (s, "error");
free (w->text);
w->text = strdup (error_strs[item->data.ip.error_code]);
}
}
/******** MENU SCREEN INPUT HANDLING FUNCTIONS ********/
MenuResult menuitem_process_input (MenuItem *item, MenuToken token, char * key, bool extended)
{
MenuResult (*process_input) (MenuItem *item, MenuToken token, char * key, bool extended);
debug (RPT_DEBUG, "%s( item=[%s], token=%d, key=\"%s\" )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"), token, key);
if (item == NULL)
return MENURESULT_ERROR;
/* Call type specific screen building function */
process_input = process_input_table [item->type];
if (process_input ) {
return process_input (item, token, key, extended);
} else {
report (RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
return MENURESULT_ERROR;
}
}
MenuResult menuitem_process_input_slider (MenuItem *item, MenuToken token, char * key, bool extended)
{
debug (RPT_DEBUG, "%s( item=[%s], token=%d, key=\"%s\" )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"), token, key);
if (item == NULL)
return MENURESULT_ERROR;
switch (token) {
case MENUTOKEN_MENU:
return menuitem_predecessor2menuresult(
item->predecessor_id, MENURESULT_CLOSE);
case MENUTOKEN_ENTER:
return menuitem_successor2menuresult(
item->successor_id, MENURESULT_CLOSE);
case MENUTOKEN_UP:
case MENUTOKEN_RIGHT:
item->data.slider.value = min (item->data.slider.maxvalue,
item->data.slider.value + item->data.slider.stepsize);
if (item->event_func)
item->event_func (item, MENUEVENT_PLUS);
return MENURESULT_NONE;
case MENUTOKEN_DOWN:
case MENUTOKEN_LEFT:
item->data.slider.value = max (item->data.slider.minvalue,
item->data.slider.value - item->data.slider.stepsize);
if (item->event_func)
item->event_func (item, MENUEVENT_MINUS);
return MENURESULT_NONE;
case MENUTOKEN_OTHER:
default:
break;
}
return MENURESULT_ERROR;
}
MenuResult menuitem_process_input_numeric (MenuItem *item, MenuToken token, char * key, bool extended)
{
char buf1[MAX_NUMERIC_LEN];
char buf2[MAX_NUMERIC_LEN];
int max_len;
debug (RPT_DEBUG, "%s( item=[%s], token=%d, key=\"%s\" )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"), token, key);
if (item != NULL) {
/* To make life easy... */
char *str = item->data.numeric.edit_str;
int pos = item->data.numeric.edit_pos;
int allow_signed = (item->data.numeric.minvalue < 0);
char *format_str = (allow_signed) ? "%+d" : "%d";
snprintf (buf1, MAX_NUMERIC_LEN, format_str, item->data.numeric.minvalue);
snprintf (buf2, MAX_NUMERIC_LEN, format_str, item->data.numeric.maxvalue);
max_len = max (strlen(buf1), strlen(buf2));
/* Clear the error */
item->data.numeric.error_code = 0;
switch (token) {
case MENUTOKEN_MENU:
if (pos == 0) {
return menuitem_predecessor2menuresult(
item->predecessor_id, MENURESULT_CLOSE);
}
else {
/* Reset data */
menuitem_reset_numeric(item);
}
return MENURESULT_NONE;
case MENUTOKEN_ENTER:
if ((extended) || (str[pos] == '\0')) {
int value;
/* The user completed his input */
/* ...scan it */
if (sscanf (str, "%d", &value) != 1) {
return MENURESULT_ERROR;
}
/* Test the value */
if (value < item->data.numeric.minvalue
|| value > item->data.numeric.maxvalue) {
/* Out of range !
* We can't exit this screen now
*/
item->data.numeric.error_code = 1;
item->data.numeric.edit_pos = 0;
item->data.numeric.edit_offs = 0;
return MENURESULT_NONE;
}
/* OK, store value */
item->data.numeric.value = value;
/* Inform client */
if (item->event_func)
item->event_func (item, MENUEVENT_UPDATE);
return menuitem_successor2menuresult(
item->successor_id, MENURESULT_CLOSE);
}
else {
/* The user wants to go to next digit */
if (pos < max_len) {
item->data.numeric.edit_pos++;
if (pos >= display_props->width - 2)
item->data.numeric.edit_offs++;
}
}
return MENURESULT_NONE;
case MENUTOKEN_UP:
if (pos >= max_len) {
/* We're not allowed to add anything anymore */
item->data.numeric.error_code = 2;
item->data.numeric.edit_pos = 0;
item->data.numeric.edit_offs = 0;
return MENURESULT_NONE;
}
if (allow_signed && pos == 0) {
/* make negative */
str[0] = (str[0] == '-') ? '+' : '-';
}
else {
if (str[pos] >= '0' && str[pos] < '9') {
str[pos] ++;
} else if (str[pos] == '9') {
str[pos] = '\0';
} else if (str[pos] == '\0') {
str[pos] = '0';
}
}
return MENURESULT_NONE;
case MENUTOKEN_DOWN:
if (pos >= max_len) {
/* We're not allowed to add anything anymore */
item->data.numeric.error_code = 2;
item->data.numeric.edit_pos = 0;
item->data.numeric.edit_offs = 0;
return MENURESULT_NONE;
}
if (allow_signed && pos == 0) {
/* make negative */
str[0] = (str[0] == '-') ? '+' : '-';
}
else {
if (str[pos] > '0' && str[pos] <= '9') {
str[pos] --;
} else if (str[pos] == '0') {
str[pos] = '\0';
} else if (str[pos] == '\0') {
str[pos] = '9';
}
}
return MENURESULT_NONE;
case MENUTOKEN_RIGHT:
/* The user wants to go to next digit */
if (str[pos] != '\0' && pos < max_len) {
item->data.numeric.edit_pos++;
if (pos >= display_props->width - 2)
item->data.numeric.edit_offs++;
}
return MENURESULT_NONE;
case MENUTOKEN_LEFT:
/* The user wants to go to back a digit */
if (pos > 0) {
item->data.numeric.edit_pos--;
if (item->data.numeric.edit_offs > item->data.numeric.edit_pos)
item->data.numeric.edit_offs = item->data.numeric.edit_pos;
}
return MENURESULT_NONE;
case MENUTOKEN_OTHER:
if (pos >= max_len) {
/* We're not allowed to add anything anymore */
item->data.numeric.error_code = 2;
item->data.numeric.edit_pos = 0;
item->data.numeric.edit_offs = 0;
return MENURESULT_NONE;
}
/* process numeric keys */
if ((strlen(key) == 1) && isdigit(key[0])) {
str[pos] = key[0];
item->data.numeric.edit_pos++;
if (pos >= display_props->width - 2)
item->data.numeric.edit_offs++;
}
return MENURESULT_NONE;
}
}
return MENURESULT_ERROR;
}
MenuResult menuitem_process_input_alpha (MenuItem *item, MenuToken token, char * key, bool extended)
{
char * p;
static char * chars = NULL;
debug (RPT_DEBUG, "%s( item=[%s], token=%d, key=\"%s\" )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"), token, key);
if (item != NULL) {
/* To make life easy... */
char *str = item->data.alpha.edit_str;
int pos = item->data.alpha.edit_pos;
/* Create list of allowed chars */
chars = realloc (chars, 26 + 26 + 10 + strlen(item->data.alpha.allowed_extra) + 1);
chars[0] = '\0'; /* clear string */
if (item->data.alpha.allow_caps)
strcat (chars, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
if (item->data.alpha.allow_noncaps)
strcat (chars, "abcdefghijklmnopqrstuvwxyz");
if (item->data.alpha.allow_numbers)
strcat (chars, "0123456789");
strcat (chars, item->data.alpha.allowed_extra);
/* Clear the error */
item->data.alpha.error_code = 0;
switch (token) {
case MENUTOKEN_MENU:
if (pos == 0) {
return menuitem_predecessor2menuresult(
item->predecessor_id, MENURESULT_CLOSE);
}
else {
/* Reset data */
menuitem_reset_alpha(item);
}
return MENURESULT_NONE;
case MENUTOKEN_ENTER:
if ((extended) || (str[item->data.alpha.edit_pos] == '\0')) {
/* The user completed his input */
/* It's not too short ? */
if (strlen (item->data.alpha.edit_str) < item->data.alpha.minlength) {
item->data.alpha.error_code = 3;
return MENURESULT_NONE;
}
/* Store value */
strcpy (item->data.alpha.value, item->data.alpha.edit_str);
/* Inform client */
if (item->event_func)
item->event_func (item, MENUEVENT_UPDATE);
return menuitem_successor2menuresult(
item->successor_id, MENURESULT_CLOSE);
}
else {
/* The user wants to go to next digit */
if (pos < item->data.alpha.maxlength) {
item->data.alpha.edit_pos++;
if (pos >= display_props->width - 2)
item->data.alpha.edit_offs++;
}
}
return MENURESULT_NONE;
case MENUTOKEN_UP:
if (pos >= item->data.alpha.maxlength) {
/* We're not allowed to add anything anymore */
item->data.alpha.error_code = 2;
item->data.alpha.edit_pos = 0;
item->data.alpha.edit_offs = 0;
return MENURESULT_NONE;
}
if (str[pos] == '\0') {
/* User goes past EOL */
str[pos] = chars[0];
} else {
/* We should have a symbol from our list */
p = strchr (chars, str[pos]);
if (p != NULL) {
str[pos] = * (++p); /* next symbol on list */
/* Might be '\0' now */
} else {
str[pos] = '\0';
}
}
return MENURESULT_NONE;
case MENUTOKEN_DOWN:
if (pos >= item->data.alpha.maxlength) {
/* We're not allowed to add anything anymore */
item->data.alpha.error_code = 2;
item->data.alpha.edit_pos = 0;
item->data.alpha.edit_offs = 0;
return MENURESULT_NONE;
}
if (str[pos] == '\0') {
/* User goes past EOL */
str[pos] = chars[strlen(chars)-1];
} else {
/* We should have a symbol from our list */
p = strchr (chars, str[pos]);
if ((p != NULL) && (p != chars)) {
str[pos] = * (--p); /* previous symbol on list */
} else {
str[pos] = '\0';
}
}
return MENURESULT_NONE;
case MENUTOKEN_RIGHT:
/* The user wants to go to next digit */
if (str[item->data.alpha.edit_pos] != '\0' &&
pos < item->data.alpha.maxlength - 1) {
item->data.alpha.edit_pos++;
if (pos >= display_props->width - 2)
item->data.alpha.edit_offs++;
}
return MENURESULT_NONE;
case MENUTOKEN_LEFT:
/* The user wants to go to back a digit */
if (pos > 0) {
item->data.alpha.edit_pos--;
if (item->data.alpha.edit_offs > item->data.alpha.edit_pos)
item->data.alpha.edit_offs = item->data.alpha.edit_pos;
}
return MENURESULT_NONE;
case MENUTOKEN_OTHER:
if (pos >= item->data.alpha.maxlength) {
/* We're not allowed to add anything anymore */
item->data.alpha.error_code = 2;
item->data.alpha.edit_pos = 0;
item->data.alpha.edit_offs = 0;
return MENURESULT_NONE;
}
/* process other keys */
if ((strlen(key) == 1) && (key[0] >= ' ') && (strchr(chars, key[0]) != NULL)) {
str[pos] = key[0];
item->data.alpha.edit_pos++;
if (pos >= display_props->width - 2)
item->data.alpha.edit_offs++;
}
return MENURESULT_NONE;
}
}
return MENURESULT_ERROR;
}
MenuResult menuitem_process_input_ip (MenuItem *item, MenuToken token, char * key, bool extended)
{
/* To make life easy... */
char *str = item->data.ip.edit_str;
char numstr[5];
int num;
int pos = item->data.ip.edit_pos;
const IpSstringProperties *ipinfo = (item->data.ip.v6) ? &IPinfo[1] : &IPinfo[0];
debug (RPT_DEBUG, "%s( item=[%s], token=%d, key=\"%s\" )", __FUNCTION__,
((item != NULL) ? item->id : "(null)"), token, key);
/* Clear the error */
item->data.ip.error_code = 0;
switch (token) {
case MENUTOKEN_MENU:
if (pos == 0) {
return menuitem_predecessor2menuresult(
item->predecessor_id, MENURESULT_CLOSE);
}
else {
/* Reset data */
menuitem_reset_ip(item);
}
return MENURESULT_NONE;
case MENUTOKEN_ENTER:
if (extended || (pos >= item->data.ip.maxlength - 1)) {
/* ** we do not need this; the verify function is clever enough **
// remove the leading spaces/zeros in each octet-representing string
char *start = str;
while (start != NULL) {
char *skip = start;
while ((*skip == ' ') || ((*skip == '0') && (skip[1] != ipinfo->sep) && (skip[1] != '\0')))
skip++;
memccpy(start, skip, '\0', item->data.ip.maxlength + 1);
skip = strchr(start, ipinfo->sep);
start = (skip != NULL) ? (skip + 1) : NULL;
}
*/
// check IP address entered
if (!ipinfo->verify(item->data.ip.edit_str)) {
item->data.ip.error_code = 4;
return MENURESULT_NONE;
}
// store value
strcpy (item->data.ip.value, item->data.ip.edit_str);
// Inform client
if (item->event_func)
item->event_func (item, MENUEVENT_UPDATE);
return menuitem_successor2menuresult(
item->successor_id, MENURESULT_CLOSE);
}
else {
item->data.ip.edit_pos++;
if (str[item->data.ip.edit_pos] == ipinfo->sep)
item->data.ip.edit_pos++;
while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
item->data.ip.edit_offs++;
return MENURESULT_NONE;
}
case MENUTOKEN_UP:
// convert string starting at the beginning / previous dot into a number
num = (int) strtol(&str[pos - (pos % (ipinfo->width + 1))], (char **) NULL, ipinfo->base);
// increase the number depending on the position
num += ipinfo->posValue[(pos - (pos / (ipinfo->width + 1))) % ipinfo->width];
// wrap around upper limit
if (num > ipinfo->limit)
num = 0;
snprintf(numstr, 5, ipinfo->format, num);
memcpy(&str[pos - (pos % (ipinfo->width + 1))], numstr, ipinfo->width);
return MENURESULT_NONE;
case MENUTOKEN_DOWN:
// convert string starting at the beginning / previous dot into a number
num = (int) strtol(&str[pos - (pos % (ipinfo->width + 1))], (char **) NULL, ipinfo->base);
// decrease the number depending on the position
num -= ipinfo->posValue[(pos - (pos / (ipinfo->width + 1))) % ipinfo->width];
// wrap around lower limit 0
if (num < 0)
num = ipinfo->limit;
snprintf(numstr, 5, ipinfo->format, num);
memcpy(&str[pos - (pos % (ipinfo->width + 1))], numstr, ipinfo->width);
return MENURESULT_NONE;
case MENUTOKEN_RIGHT:
if (pos < item->data.ip.maxlength - 1) {
item->data.ip.edit_pos++;
if (str[item->data.ip.edit_pos] == ipinfo->sep)
item->data.ip.edit_pos++;
while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
item->data.ip.edit_offs++;
}
return MENURESULT_NONE;
case MENUTOKEN_LEFT:
/* The user wants to go to back a digit */
if (pos > 0) {
item->data.ip.edit_pos--;
if (str[item->data.ip.edit_pos] == ipinfo->sep)
item->data.ip.edit_pos--;
if (item->data.ip.edit_offs > item->data.ip.edit_pos)
item->data.ip.edit_offs = item->data.ip.edit_pos;
}
return MENURESULT_NONE;
case MENUTOKEN_OTHER:
/* process other keys */
if ((strlen(key) == 1) &&
((item->data.ip.v6) ? isxdigit(key[0]) : isdigit(key[0]))) {
str[pos] = tolower(key[0]);
if (pos < item->data.ip.maxlength - 1) {
item->data.ip.edit_pos++;
if (str[item->data.ip.edit_pos] == ipinfo->sep)
item->data.ip.edit_pos++;
while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
item->data.ip.edit_offs++;
}
}
return MENURESULT_NONE;
}
return MENURESULT_ERROR;
}
/**
* get the Client that owns item. extracted from menu_commands_handler().
*/
Client * menuitem_get_client(MenuItem * item)
{
return item->client;
}
LinkedList * tablist2linkedlist (char *strings)
{
LinkedList * list;
list = LL_new();
/* Parse strings */
if (strings != NULL) {
char *p = strings;
char *tabptr, *new_s;
while ((tabptr = strchr(p, '\t')) != NULL) {
int len = (int)(tabptr - p);
/* Alloc and copy substring */
new_s = malloc (len + 1);
strncpy (new_s, p, len);
new_s[len] = 0;
LL_Push (list, new_s);
/* Go to next string */
p = tabptr + 1;
}
/* Add last string */
new_s = strdup (p);
LL_Push (list, new_s);
}
return list;
}
MenuItemType menuitem_typename_to_type (char *name)
{
if (name != NULL) {
MenuItemType type;
for (type = 0; type < NUM_ITEMTYPES; type ++) {
if (strcmp (menuitemtypenames[type], name) == 0) {
return type;
}
}
}
return -1;
}
char *menuitem_type_to_typename (MenuItemType type)
{
return ((type >= 0 && type < NUM_ITEMTYPES)
? menuitemtypenames[type]
: NULL);
}
MenuEventType menuitem_eventtypename_to_eventtype (char *name)
{
if (name != NULL) {
MenuEventType type;
for (type = 0; type < NUM_EVENTTYPES; type ++) {
if (strcmp (menueventtypenames[type], name) == 0) {
return type;
}
}
}
return -1;
}
char *menuitem_eventtype_to_eventtypename (MenuEventType type)
{
return ((type >= 0 && type < NUM_EVENTTYPES)
? menueventtypenames[type]
: NULL);
}