/************************************************************************* * menu_del_item_func * * Deletes an item from a menu * * Usage: menu_del_item <menuid> <itemid> * The given item in the given menu will be deleted. If you have deleted all * the items from your client menu, that menu will automatically be removed. */ int menu_del_item_func (Client * c, int argc, char **argv) { Menu * menu; MenuItem * item; char * menu_id; char * item_id; if (!c->ack) return 1; if (argc != 3 ) { sock_send_error(c->sock, "Usage: menu_del_item <menuid> <itemid>\n"); return 0; } menu_id = argv[1]; item_id = argv[2]; /* Does the client have a menu already ? */ if (!c->menu) { sock_send_error(c->sock, "Client has no menu\n"); return 0; } 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; } menuscreen_inform_item_destruction (item); menu_remove_item (menu, item); menuscreen_inform_item_modified (item->parent); menuitem_destroy (item); /* Was it the last item in the client's menu ? */ if (menu_getfirst_item(c->menu) == NULL) { menuscreen_inform_item_destruction (c->menu); menu_remove_item (main_menu, c->menu); menuscreen_inform_item_modified (main_menu); menu_destroy (c->menu); c->menu = NULL; } sock_send_string(c->sock, "success\n"); return 0; }
static GtkWidget * menu_find_path (GtkWidget *menu, char *path) { GtkMenuItem *item; char *s; char name[128]; int len; /* grab the next part of the path */ s = strchr (path, '/'); len = s - path; if (!s) len = strlen (path); len = MIN (len, sizeof (name) - 1); memcpy (name, path, len); name[len] = 0; item = menu_find_item (menu, name); if (!item) return NULL; menu = gtk_menu_item_get_submenu (item); if (!menu) return NULL; path += len; if (*path == 0) return menu; return menu_find_path (menu, path + 1); }
/** 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); }
static GtkWidget * menu_find (GtkWidget *menu, char *path, char *label) { GtkWidget *item = NULL; if (path[0] != 0) menu = menu_find_path (menu, path); if (menu) item = (GtkWidget *)menu_find_item (menu, label); return item; }
void menuscreen_remove_screen(Screen *s) { debug(RPT_DEBUG, "%s(s=[%s])", __FUNCTION__, (s != NULL) ? s->id : "(NULL)"); /* allow to remove the menuscreen itself */ if ((s == NULL) || (s == menuscreen)) return; if (screens_menu) { Menu *m = menu_find_item(screens_menu, s->id, false); menu_remove_item(screens_menu, m); menuitem_destroy(m); } }
static GtkWidget * menu_radio_item(char *label, GtkWidget *menu, void *callback, void *userdata, int state, char *groupname) { GtkWidget *item; GtkMenuItem *parent; GSList *grouplist = NULL; parent = menu_find_item(menu, groupname); if (parent) grouplist = gtk_radio_menu_item_get_group((GtkRadioMenuItem *) parent); item = gtk_radio_menu_item_new_with_label(grouplist, label); gtk_check_menu_item_set_active((GtkCheckMenuItem *) item, state); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(callback), userdata); gtk_widget_show(item); return item; }
/*************************************************************** * Requests the menu system to set the entry point into the menu system. * * Usage: menu_set_main <id> */ int menu_set_main_func (Client * c, int argc, char **argv) { char * menu_id; Menu * menu; debug (RPT_DEBUG, "%s( Client [%d], %s, %s )", __FUNCTION__, c->sock, (argc > 1 ? argv[1] : "<null>"), (argc > 2 ? argv[2] : "<null>") ); if (!c->ack) return 1; if (argc != 2 ) { sock_send_error(c->sock, "Usage: menu_set_main <menuid>\n"); return 0; } menu_id = argv[1]; if ( menu_id[0] == 0 ) { /* No menu specified = client's main menu */ menu = c->menu; } else if (strcmp(menu_id, "_main_") == 0) { menu = NULL; } 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; } } menuscreen_set_main(menu); sock_send_string(c->sock, "success\n"); return 0; }
/************************************************************************* * menu_add_item_func * * Adds an item to a menu * * Usage: menu_add_item <menuid> <newitemid> <type> [<text>] * You should use "" as id for the client's main menu. This menu will be * created automatically when you add an item to it the first time. * You (currently?) cannot create a menu in the main level yourself. * The names you use for items should be unique for your client. * The text is the visible text for the item. * * The following types are available: * - menu * - action * - checkbox * - ring (a kind of listbox of one line) * - slider * - numeric * - alpha * - ip */ int menu_add_item_func (Client * c, int argc, char **argv) { char * menu_id; char * item_id; char * text = NULL; Menu * menu = NULL; MenuItem * item; MenuItemType itemtype; char** argv_set = NULL; debug (RPT_DEBUG, "%s( Client [%d], %s, %s )", __FUNCTION__, c->sock, argv[1], argv[2]); if (!c->ack) return 1; if (!c->name) { sock_send_error(c->sock, "You need to give your client a name first\n"); return 0; } if ((argc < 4 )) { sock_send_error(c->sock, "Usage: menu_add_item <menuid> <newitemid> <type> [<text>]\n"); return 0; } menu_id = argv[1]; item_id = argv[2]; /* Does the client have a menu already ? */ if (!c->menu) { /* We need to create it */ report( RPT_INFO, "Client [%d] is using the menu", c->sock ); c->menu = menu_create ("_client_menu_", menu_commands_handler, c->name, c); menu_add_item (main_menu, c->menu); } 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, "Item id already in use\n"); return 0; } /* Find menuitem type */ itemtype = menuitem_typename_to_type (argv[3]); if (itemtype == -1) { sock_send_error(c->sock, "Invalid menuitem type\n"); return 0; } /* Is a text given (options don't count)? */ if (argc >= 5 && argv[4][0] != '-') { text = argv[4]; } else { text = ""; } /* Create the menuitem */ switch (itemtype) { case MENUITEM_MENU: item = menu_create (item_id, menu_commands_handler, text, c); break; case MENUITEM_ACTION: item = menuitem_create_action (item_id, menu_commands_handler, text, c, MENURESULT_NONE); break; case MENUITEM_CHECKBOX: item = menuitem_create_checkbox (item_id, menu_commands_handler, text, c, false, false); break; case MENUITEM_RING: item = menuitem_create_ring (item_id, menu_commands_handler, text, c, "", 0); break; case MENUITEM_SLIDER: item = menuitem_create_slider (item_id, menu_commands_handler, text, c, "", "", 0, 100, 1, 25); break; case MENUITEM_NUMERIC: item = menuitem_create_numeric (item_id, menu_commands_handler, text, c, 0, 100, 0); break; case MENUITEM_ALPHA: item = menuitem_create_alpha (item_id, menu_commands_handler, text, c, 0, 0, 10, true, false, true, "-./", ""); break; case MENUITEM_IP: item = menuitem_create_ip (item_id, menu_commands_handler, text, c, 0, "192.168.1.245"); break; default: assert(!"unexpected menuitem type"); } menu_add_item (menu, item); menuscreen_inform_item_modified (menu); sock_send_string(c->sock, "success\n"); /* are there any options (starting with '-')? * - create a temporary argv for menu_set_item() call */ if (argc > 5 || argv[4][0] == '-') { // menu_add_item <menuid> <newitemid> <type> [<text>] // menu_set_item <menuid> <itemid> {<option>}+ int i, j; argv_set = malloc(argc * sizeof(char*)); assert(argv_set); argv_set[0] = "menu_set_item"; for (i = j = 1; i < argc; ++i) { /* skip "type" */ if (i == 3) continue; /* skip "text" */ if (i == 4 && argv[4][0] != '-') continue; argv_set[j++] = argv[i]; } menu_set_item_func(c, j, argv_set); free(argv_set); } return 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; }