/** 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; }
/************************************************************************** * 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; }