Example #1
0
/** This function changes the menuitem to the given one, and does necessary
 * actions.
 * To leave the menu system, specify NULL for new_menuitem.
 * The item will not be reset when the new item is a child of the last one.
 */
void
menuscreen_switch_item(MenuItem * new_menuitem)
{
	MenuItem *old_menuitem = active_menuitem;

	debug(RPT_DEBUG, "%s(item=[%s]) from active_menuitem=[%s]", __FUNCTION__,
	      ((new_menuitem != NULL) ? new_menuitem->id : "(null)"),
	      ((old_menuitem != NULL) ? old_menuitem->id : "(null)"));

	/* First we do the switch */
	active_menuitem = new_menuitem;

	/* What was the state change ? */
	if (!old_menuitem && !new_menuitem) {
		/* Nothing to be done */
	}
	else if (old_menuitem && !new_menuitem) {
		/* leave menu system */
		menuscreen->priority = PRI_HIDDEN;
	}
	else if (!old_menuitem && new_menuitem) {
		/* Menu is becoming active */
		menuitem_reset(active_menuitem);
		menuitem_rebuild_screen(active_menuitem, menuscreen);

		menuscreen->priority = PRI_INPUT;
	}
	else {
		/* We're left with the usual case: a menu level switch */
		if (old_menuitem->parent != new_menuitem) {
			menuitem_reset(new_menuitem);
		}
		menuitem_rebuild_screen(active_menuitem, menuscreen);
	}

	if (old_menuitem && old_menuitem->event_func)
		old_menuitem->event_func(old_menuitem, MENUEVENT_LEAVE);
	if (new_menuitem && new_menuitem->event_func)
		new_menuitem->event_func(new_menuitem, MENUEVENT_ENTER);

	return;
}
Example #2
0
/**************************************************************************
 * menu_set_item_func
 * Sets the info about a menu item
 *
 * For example, text displayed, value, etc...
 *
 * Usage: menu_set_item <menuid> <itemid> {<option>}+
 * The following parameters can be set per item:
 * (you should include the - in the option)
 *
 * For all types:
 * -text "text"			("")
 *	Sets the visible text.
 * -is_hidden false|true	(false)
 *	If the item currently should not appear in a menu.
 * -prev id			()
 *	Sets the predecessor of this item (what happens after "Escape")
 *
 * For all except menus:
 * -next id			()
 *	Sets the successor of this item (what happens after "Enter")
 *
 * action:
 * -menu_result none|close|quit	(none)
 *	Sets what to do with the menu when this action is selected:
 *	- none: the menu stays as it is.
 *	- close: the menu closes and returns to a higher level.
 *	- quit: quits the menu completely so you can foreground your app.
 *
 * checkbox:
 * -value off|on|gray		(off)
 *	Sets its current value.
 * -allow_gray false|true	(false)
 *	Sets if a grayed checkbox is allowed.
 *
 * ring:
 * -value <int>			(0)
 *	Sets the index in the stringlist that is currently selected.
 * -strings <string>		(empty)
 *	The subsequent strings that can be selected. They should be
 *	tab-separated in ONE string.
 *
 * slider:
 * -value <int>			(0)
 *	Sets its current value.
 * -mintext <string>		("")
 * -maxtex <string>		("")
 *	Text at the minimal and maximal side. On small displays these might
 *	not be displayed.
 * -minvalue <int>		(0)
 * -maxvalue <int>		(100)
 *	The minimum and maximum value of the slider.
 * -stepsize <int>		(1)
 *	The stepsize of the slider. If you use 0, you can control it yourself
 *	completely.
 *
 * numeric:
 * -value <int>			(0)
 *	Sets its current value.
 * -minvalue <int>		(0)
 * -maxvalue <int>		(100)
 *	The minimum and maximum value that are allowed. If you make one of
 *	them negative, the user will be able to enter negative numbers too.
 * Maybe floats will work too in the future.
 *
 * alpha:
 * -value <string>
 *	Sets its current value.	("")
 * -password_char <char>	(none)
 * -minlength <int>		(0)
 * -maxlength <int>		(10)
 *	Set the minimum and maximum allowed length.
 * -allow_caps false|true	(true)
 * -allow_noncaps false|true	(false)
 * -allow_numbers false|true	(true)
 *	Allows these groups of characters.
 * -allowed_extra <string>	("")
 *	The chars in this string are also allowed.
 *
 * ip:
 * -value <string>
 *	Sets its current value.	("")
 * -v6 false|true
 *
 * Hmm, this is getting very big. We might need a some real parser after all.
 */
int
menu_set_item_func (Client * c, int argc, char **argv)
{
	typedef enum AttrType { NOVALUE, BOOLEAN, CHECKBOX_VALUE, SHORT, INT,
				FLOAT, STRING } AttrType;

	/* This table generalizes the options.
	 * The table lists which options can exist for which menu items,
	 * what kind of parameter they should have and where this scanned
	 * parameter should be stored.
	 */
	struct OptionTable {
		MenuItemType menuitem_type;	/* For what MenuItem type is
						   the option ?
						   Use -1 for ALL types. */
		char * name;			/* The option concerned */
		AttrType attr_type;		/* Type of value */
		int attr_offset;		/* Where to put the value
						   in the structure.
						   Use -1 to process it
						   yourself. */
		/* Watch out with STRING, it will free() the current value
		 * and reallocate for the new value !! If you don't want
		 * that, use -1 for offset, to process it yourself. */
	} option_table[] = {
		{ -1,			"text",		STRING,		offsetof(MenuItem,text) },
		{ -1,			"is_hidden",	BOOLEAN,	offsetof(MenuItem,is_hidden) },
		{ -1,			"prev",	STRING,		-1 },
		{ -1,			"next",		STRING,		-1 },
		{ MENUITEM_ACTION,	"menu_result",	STRING,		-1 },
		{ MENUITEM_CHECKBOX,	"value",	CHECKBOX_VALUE,	offsetof(MenuItem,data.checkbox.value) },
		{ MENUITEM_CHECKBOX,	"allow_gray",	BOOLEAN,	offsetof(MenuItem,data.checkbox.allow_gray) },
		{ MENUITEM_RING,	"value",	SHORT,		offsetof(MenuItem,data.ring.value) },
		{ MENUITEM_RING,	"strings",	STRING,		-1 },
		{ MENUITEM_SLIDER,	"value",	INT,		offsetof(MenuItem,data.slider.value) },
		{ MENUITEM_SLIDER,	"minvalue",	INT,		offsetof(MenuItem,data.slider.minvalue) },
		{ MENUITEM_SLIDER,	"maxvalue",	INT,		offsetof(MenuItem,data.slider.maxvalue) },
		{ MENUITEM_SLIDER,	"stepsize",	INT,		offsetof(MenuItem,data.slider.stepsize) },
		{ MENUITEM_SLIDER,	"mintext",	STRING,		offsetof(MenuItem,data.slider.mintext) },
		{ MENUITEM_SLIDER,	"maxtext",	STRING,		offsetof(MenuItem,data.slider.maxtext) },
		{ MENUITEM_NUMERIC,	"value",	INT,		offsetof(MenuItem,data.numeric.value) },
		{ MENUITEM_NUMERIC,	"minvalue",	INT,		offsetof(MenuItem,data.numeric.minvalue) },
		{ MENUITEM_NUMERIC,	"maxvalue",	INT,		offsetof(MenuItem,data.numeric.maxvalue) },
		/*{ MENUITEM_NUMERIC,	"allow_decimals",BOOLEAN,	offsetof(MenuItem,data.numeric.allow_decimals) },*/
		{ MENUITEM_ALPHA,	"value",	STRING,		-1 /*offsetof(MenuItem,data.alpha.value)*/ },
		{ MENUITEM_ALPHA,	"minlength",	SHORT,		offsetof(MenuItem,data.alpha.minlength) },
		{ MENUITEM_ALPHA,	"maxlength",	SHORT,		offsetof(MenuItem,data.alpha.maxlength) },
		{ MENUITEM_ALPHA,	"password_char",STRING,		-1 },
		{ MENUITEM_ALPHA,	"allow_caps",	BOOLEAN,	offsetof(MenuItem,data.alpha.allow_caps) },
		{ MENUITEM_ALPHA,	"allow_noncaps",BOOLEAN,	offsetof(MenuItem,data.alpha.allow_noncaps) },
		{ MENUITEM_ALPHA,	"allow_numbers",BOOLEAN,	offsetof(MenuItem,data.alpha.allow_numbers) },
		{ MENUITEM_ALPHA,	"allowed_extra",STRING,		offsetof(MenuItem,data.alpha.allowed_extra) },
		{ MENUITEM_IP,		"v6",		BOOLEAN,	offsetof(MenuItem,data.ip.v6) },
		{ MENUITEM_IP,		"value",	STRING,		-1 /*offsetof(MenuItem,data.ip.value)*/ },
		{ -1,			NULL,		-1,		-1 }
	};

	debug (RPT_DEBUG, "%s( Client [%d]: %s)",
	       __FUNCTION__, c->sock, argv2string(argc, argv));
	bool bool_value = false;
	CheckboxValue checkbox_value = CHECKBOX_OFF;
	short short_value = 0;
	int int_value = 0;
	float float_value = 0;
	char * string_value = NULL;

	Menu * menu;
	MenuItem * item;
	char * menu_id;
	char * item_id;
	int argnr;

	if (!c->ack)
		return 1;

	if (argc < 4 ) {
		sock_send_error(c->sock, "Usage: menu_set_item <menuid> <itemid> {<option>}+\n");
		return 0;
	}

	menu_id = argv[1];
	item_id = argv[2];

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	} else {
		/* A specified menu */
		menu = menu_find_item (c->menu, menu_id, true);
	}
	if (!menu) {
		sock_send_error(c->sock, "Cannot find menu id\n");
		return 0;
	}

	item = menu_find_item (c->menu, item_id, true);
	if (!item) {
		sock_send_error(c->sock, "Cannot find item\n");
		return 0;
	}

	/* Scan all arguments */
	for( argnr = 3; argnr < argc; argnr ++) {
		int option_nr = -1;
		int found_option_name = 0;
		int error = 0;
		void * location;
		char * p;

		/* Find the option in the table */
		if( argv[argnr][0] == '-' ) {
			int i;
			for( i=0; option_table[i].name; i++ ) {
				if( strcmp( argv[argnr]+1, option_table[i].name ) == 0 ) {
					found_option_name = 1;
					if( item->type == option_table[i].menuitem_type
					|| option_table[i].menuitem_type == -1 ) {
						option_nr = i;
					}
				}
			}
		}
		else {
			sock_printf_error(c->sock, "Found non-option: \"%.40s\"\n", argv[argnr]);
			continue; /* Skip to next arg */
		}
		if( option_nr == -1 ) {
			if( found_option_name ) {
				sock_printf_error(c->sock, "Option not valid for menuitem type: \"%.40s\"\n", argv[argnr]);
			} else {
				sock_printf_error(c->sock, "Unknown option: \"%.40s\"\n", argv[argnr]);
			}
			continue; /* Skip to next arg */
		}

		/* OK, we now know we have an option that is valid for the item type. */

		/* Check for value */
		if( option_table[option_nr].attr_type != NOVALUE ) {
			if( argnr + 1 >= argc ) {
				sock_printf_error(c->sock, "Missing value at option: \"%.40s\"\n", argv[argnr]);
				continue; /* Skip to next arg (probably is not existing :) */
			}
		}
		/* Process the value that goes with the option */
		location = (void*)item + option_table[option_nr].attr_offset;
		switch( option_table[option_nr].attr_type ) {
		  case NOVALUE:
			break;
		  case BOOLEAN:
			if( strcmp( argv[argnr+1], "false" ) == 0 ) {
				bool_value = false;
			} else if( strcmp( argv[argnr+1], "true" ) == 0 ) {
				bool_value = true;
			} else {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(bool *)location = bool_value;
			}
			break;
		  case CHECKBOX_VALUE:
			if( strcmp( argv[argnr+1], "off" ) == 0 ) {
				checkbox_value = CHECKBOX_OFF;
			} else if( strcmp( argv[argnr+1], "on" ) == 0 ) {
				checkbox_value = CHECKBOX_ON;
			} else if( strcmp( argv[argnr+1], "gray" ) == 0 ) {
				checkbox_value = CHECKBOX_GRAY;
			} else {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(CheckboxValue *)location = checkbox_value;
			}
			break;
		  case SHORT:
			short_value = strtol( argv[argnr+1], &p, 0 );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(short*)location = short_value;
			}
			break;
		  case INT:
			int_value = strtol( argv[argnr+1], &p, 0 );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(int*)location = int_value;
			}
			break;
		  case FLOAT:
			float_value = strtod( argv[argnr+1], &p );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(float*)location = float_value;
			}
			break;
		  case STRING:
		  	string_value = argv[argnr+1];
			if( option_table[option_nr].attr_offset != -1 ) {
				free( *(char**)location );
				*(char**)location = strdup( string_value );
			}
			else if (strcmp(argv[argnr], "-prev") == 0) {
				set_predecessor(item, string_value, c);
			}
			else if (strcmp(argv[argnr], "-next") == 0) {
				set_successor(item, string_value, c);
			}
			break;
		}
		switch( error ) {
		  case 1:
			sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]);
			argnr ++;
			continue; /* Skip current option and the invalid value */
		}

		/* And at last process extra things for certain options.
		 * Most useful for the attr_offset==-1 stuff. */
		switch (item->type) {
		  case MENUITEM_ACTION:
			if( strcmp( argv[argnr]+1, "menu_result" ) == 0 ) {
				if( strcmp( argv[argnr+1], "none" ) == 0 ) {
					set_successor(item, "_none_", c);
				} else if( strcmp( argv[argnr+1], "close" ) == 0 ) {
					set_successor(item, "_close_", c);
				} else if( strcmp( argv[argnr+1], "quit" ) == 0 ) {
					set_successor(item, "_quit_", c);
				} else {
					error = 1;
				}
			}
			break;
		  case MENUITEM_SLIDER:
			if( item->data.slider.value < item->data.slider.minvalue ) {
				item->data.slider.value = item->data.slider.minvalue;
			} else if( item->data.slider.value > item->data.slider.maxvalue ) {
				item->data.slider.value = item->data.slider.maxvalue;
			}
			break;
		  case MENUITEM_RING:
		  	if( strcmp( argv[argnr]+1, "strings" ) == 0 ) {
		  		free (item->data.ring.strings);
		  		item->data.ring.strings = tablist2linkedlist (string_value);
		  	}
			item->data.ring.value %= LL_Length( item->data.ring.strings );
			break;
		  case MENUITEM_NUMERIC:
			menuitem_reset (item);
			break;
		  case MENUITEM_ALPHA:
			if( strcmp( argv[argnr]+1, "password_char" ) == 0 ) {
				item->data.alpha.password_char = string_value[0];
			}
			else if( strcmp( argv[argnr]+1, "maxlength" ) == 0 ) {
				char * new_buf;
				if( short_value < 0 || short_value > 1000 ) {
					error = 2;
					break;
				}
				new_buf = malloc( short_value + 1 );
				strncpy( new_buf, item->data.alpha.value, short_value );
				new_buf[short_value] = '\0'; /* terminate */
				free( item->data.alpha.value );
				item->data.alpha.value = new_buf;
				free( item->data.alpha.edit_str );
				item->data.alpha.edit_str = malloc( short_value + 1 );
				item->data.alpha.edit_str[0] = '\0';
			}
			else if( strcmp( argv[argnr]+1, "value" ) == 0 ) {
				strncpy( item->data.alpha.value, string_value, item->data.alpha.maxlength );
				item->data.alpha.value[ item->data.alpha.maxlength ] = 0; /* terminate */
			}
			menuitem_reset (item);
			break;
		  case MENUITEM_IP:
			if( strcmp( argv[argnr]+1, "v6" ) == 0 ) {
				char * new_buf;
                		/* set max lenth depending ob boolean option v6 */
				item->data.ip.maxlength = (bool_value == 0) ? 15 : 39;

				new_buf = malloc(item->data.ip.maxlength + 1 );
				strncpy( new_buf, item->data.ip.value, item->data.ip.maxlength);

				new_buf[item->data.ip.maxlength] = '\0'; /* terminate */
				free( item->data.ip.value );
				item->data.ip.value = new_buf;
				free( item->data.ip.edit_str );
				item->data.ip.edit_str = malloc( item->data.ip.maxlength +1);
				item->data.ip.edit_str[0] = '\0';
			}
			else if( strcmp( argv[argnr]+1, "value" ) == 0 ) {
				strncpy( item->data.ip.value, string_value, item->data.ip.maxlength );
				item->data.ip.value[item->data.ip.maxlength] = '\0'; /* terminate */
			}
			menuitem_reset (item);
			break;
		  default:
			break;
		}
		switch( error ) {
		  case 1:
			sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]);
			continue; /* Skip to next arg and retry it as an option */
		  case 2:
			sock_printf_error(c->sock, "Value out of range at option: \"%.40s\"\n", argv[argnr]);
			argnr ++;
			continue; /* Skip current option and the invalid value */
		}
		menuscreen_inform_item_modified (item);
		if( option_table[option_nr].attr_type != NOVALUE ) {
			/* Skip the now used argument */
			argnr ++;
		}
	}
	sock_send_string(c->sock, "success\n");
	return 0;
}