Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / menuitem.c
blobe2001d8a6a5563e138e7b4c45bcc143a0c0a5d4d
1 /*
2 * menuitem.c
3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
9 * 2002, Joris Robijn
10 * 2004, F5 Networks, Inc. - IP-address input
11 * 2005, Peter Marschall - error checks, ...
14 * Handles a menuitem and all actions that can be performed on it.
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <assert.h>
22 #include "shared/report.h"
24 #include "menuitem.h"
25 #include "menuscreens.h"
26 #include "menu.h"
27 #include "drivers.h"
29 /* this is needed for verify_ipv4 and verify_ipv6. */
30 #include "sock.h"
32 #define MAX_NUMERIC_LEN 40
34 char *error_strs[] = {"", "Out of range", "Too long", "Too short", "Invalid Address"};
35 char *menuitemtypenames[] = {"menu", "action", "checkbox", "ring", "slider", "numeric", "alpha", "ip"};
36 char *menueventtypenames[] = {"select", "update", "plus", "minus", "enter", "leave"};
38 void menuitem_destroy_action(MenuItem *item);
39 void menuitem_destroy_checkbox(MenuItem *item);
40 void menuitem_destroy_ring(MenuItem *item);
41 void menuitem_destroy_slider(MenuItem *item);
42 void menuitem_destroy_numeric(MenuItem *item);
43 void menuitem_destroy_alpha(MenuItem *item);
44 void menuitem_destroy_ip(MenuItem *item);
46 void menuitem_reset_numeric(MenuItem *item);
47 void menuitem_reset_alpha(MenuItem *item);
48 void menuitem_reset_ip(MenuItem *item);
50 void menuitem_rebuild_screen_slider(MenuItem *item, Screen *s);
51 void menuitem_rebuild_screen_numeric(MenuItem *item, Screen *s);
52 void menuitem_rebuild_screen_alpha(MenuItem *item, Screen *s);
53 void menuitem_rebuild_screen_ip(MenuItem *item, Screen *s);
55 void menuitem_update_screen_slider(MenuItem *item, Screen *s);
56 void menuitem_update_screen_numeric(MenuItem *item, Screen *s);
57 void menuitem_update_screen_alpha(MenuItem *item, Screen *s);
58 void menuitem_update_screen_ip(MenuItem *item, Screen *s);
60 MenuResult menuitem_process_input_slider(MenuItem *item, MenuToken token, const char *key, bool extended);
61 MenuResult menuitem_process_input_numeric(MenuItem *item, MenuToken token, const char *key, bool extended);
62 MenuResult menuitem_process_input_alpha(MenuItem *item, MenuToken token, const char *key, bool extended);
63 MenuResult menuitem_process_input_ip(MenuItem *item, MenuToken token, const char *key, bool extended);
66 /* information about string representation of IP addresses */
67 typedef struct {
68 int maxlen; // max length of the string represenation;
69 char sep; // separators between numeric strings
70 int base; // numeric base
71 int width; // width of each numeric string
72 int limit; // upper limit of each numeric string
73 int posValue[5]; // digit-value of the digits at the approp. position in the numeric string
74 char format[5]; // printf-format string to print a numeric string
75 int (*verify)(const char *); // verify function: returns 1 = OK, 0 = error
76 char dummy[16]; // dummy value (if provided value is no IP address)
77 } IpSstringProperties;
80 const IpSstringProperties IPinfo[] = {
81 // IPv4: 15 char long '.'-separated sequence of 3-digit decimal strings in range 000 - 255
82 { 15, '.', 10, 3, 255, { 100, 10, 1, 0, 0 }, "%03d", verify_ipv4, "0.0.0.0" },
83 // IPv6: 39 char long ':'-separated sequence of 4-digit hex strings in range 0000 - ffff
84 { 39, ':', 16, 4, 65535, { 4096, 256, 16, 1, 0 }, "%04x", verify_ipv6, "0:0:0:0:0:0:0:0" }
88 /******** MENU UTILITY FUNCTIONS ********/
90 /** returns default_result if predecessor_id is NULL or the respective value
91 * otherwise. */
92 MenuResult menuitem_predecessor2menuresult(char *predecessor_id, MenuResult default_result)
94 if (predecessor_id == NULL)
95 return default_result;
96 if (strcmp("_quit_", predecessor_id) == 0)
97 return MENURESULT_QUIT;
98 else if (strcmp("_close_", predecessor_id) == 0)
99 return MENURESULT_CLOSE;
100 else if (strcmp("_none_", predecessor_id) == 0)
101 return MENURESULT_NONE;
102 else
103 return MENURESULT_PREDECESSOR;
106 /** returns default_result if successor_id is NULL or the respective value
107 * otherwise. */
108 MenuResult menuitem_successor2menuresult(char *successor_id, MenuResult default_result)
110 if (successor_id == NULL)
111 return default_result;
112 if (strcmp("_quit_", successor_id) == 0)
113 return MENURESULT_QUIT;
114 else if (strcmp("_close_", successor_id) == 0)
115 return MENURESULT_CLOSE;
116 else if (strcmp("_none_", successor_id) == 0)
117 return MENURESULT_NONE;
118 else
119 return MENURESULT_SUCCESSOR;
122 /** Returns the MenuItem with the specified id if found or NULL. The search
123 * for itemid is restricted to the client's menus if the preprocessor macro
124 * LCDPROC_PERMISSIVE_MENU_GOTO is *not* set. */
125 MenuItem *menuitem_search(char *menu_id, Client *client)
127 # ifdef LCDPROC_PERMISSIVE_MENU_GOTO
128 MenuItem *top = main_menu;
129 # else
130 MenuItem *top = client->menu;
131 # endif /* LCDPROC_PERMISSIVE_MENU_GOTO */
133 return menu_find_item(top, menu_id, true);
136 /******** FUNCTION TABLES ********/
137 /* Tables with functions to call for all different item types */
139 void (*destructor_table[NUM_ITEMTYPES]) (MenuItem *item) =
141 menu_destroy,
142 NULL,
143 NULL,
144 menuitem_destroy_ring,
145 menuitem_destroy_slider,
146 menuitem_destroy_numeric,
147 menuitem_destroy_alpha,
148 menuitem_destroy_ip
150 void (*reset_table[NUM_ITEMTYPES]) (MenuItem *item) =
152 menu_reset,
153 NULL,
154 NULL,
155 NULL,
156 NULL,
157 menuitem_reset_numeric,
158 menuitem_reset_alpha,
159 menuitem_reset_ip
161 void (*build_screen_table[NUM_ITEMTYPES]) (MenuItem *item, Screen *s) =
163 menu_build_screen,
164 NULL,
165 NULL,
166 NULL,
167 menuitem_rebuild_screen_slider,
168 menuitem_rebuild_screen_numeric,
169 menuitem_rebuild_screen_alpha,
170 menuitem_rebuild_screen_ip
172 void (*update_screen_table[NUM_ITEMTYPES]) (MenuItem *item, Screen *s) =
174 menu_update_screen,
175 NULL,
176 NULL,
177 NULL,
178 menuitem_update_screen_slider,
179 menuitem_update_screen_numeric,
180 menuitem_update_screen_alpha,
181 menuitem_update_screen_ip
184 MenuResult (*process_input_table[NUM_ITEMTYPES]) (MenuItem *item, MenuToken token, const char *key, bool extended) =
186 menu_process_input,
187 NULL,
188 NULL,
189 NULL,
190 menuitem_process_input_slider,
191 menuitem_process_input_numeric,
192 menuitem_process_input_alpha,
193 menuitem_process_input_ip
196 /******** METHODS ********/
198 MenuItem *menuitem_create(MenuItemType type, char *id, MenuEventFunc(*event_func),
199 char *text, Client *client)
201 MenuItem *new_item;
203 debug(RPT_DEBUG, "%s(type=%d, id=\"%s\", event_func=%p, text=\"%s\")",
204 __FUNCTION__, type, id, event_func, text);
206 if ((id == NULL) || (text == NULL)) {
207 // report(RPT_ERR, "%s: illegal id or text", __FUNCTION__);
208 return NULL;
211 /* Allocate space and fill struct */
212 new_item = malloc(sizeof(MenuItem));
213 if (!new_item) {
214 report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
215 return NULL;
217 new_item->type = type;
218 new_item->id = strdup(id);
219 if (!new_item->id) {
220 report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
221 free(new_item);
222 return NULL;
224 new_item->successor_id = NULL;
225 new_item->predecessor_id = NULL;
226 new_item->parent = NULL;
227 new_item->event_func = event_func;
228 new_item->text = strdup(text);
229 if (!new_item->text) {
230 report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
231 free(new_item->id);
232 free(new_item);
233 return NULL;
235 new_item->client = client;
236 new_item->is_hidden = false;
238 /* Clear the type specific data part */
239 memset(&(new_item->data), '\0', sizeof(new_item->data));
241 return new_item;
244 // fixme: the menu_result arg is obsoleted (use char* successor_id)
245 MenuItem *menuitem_create_action(char *id, MenuEventFunc(*event_func),
246 char *text, Client *client, MenuResult menu_result)
248 MenuItem *new_item;
250 debug(RPT_DEBUG, "%s(id=[%s], event_func=%p, text=\"%s\", close_menu=%d)",
251 __FUNCTION__, id, event_func, text, menu_result);
253 new_item = menuitem_create(MENUITEM_ACTION, id, event_func, text, client);
254 if (new_item != NULL)
256 switch (menu_result)
258 case MENURESULT_NONE:
259 new_item->successor_id = strdup("_none_");
260 break;
261 case MENURESULT_CLOSE:
262 new_item->successor_id = strdup("_close_");
263 break;
264 case MENURESULT_QUIT:
265 new_item->successor_id = strdup("_quit_");
266 break;
267 default:
268 assert(!"unexpected MENURESULT");
272 return new_item;
275 MenuItem *menuitem_create_checkbox(char *id, MenuEventFunc(*event_func),
276 char *text, Client *client, bool allow_gray, bool value)
278 MenuItem *new_item;
280 debug(RPT_DEBUG, "%s(id=[%s], event_func=%p, text=\"%s\", allow_gray=%d, value=%d)",
281 __FUNCTION__, id, event_func, text, allow_gray, value);
283 new_item = menuitem_create(MENUITEM_CHECKBOX, id, event_func, text, client);
284 if (new_item != NULL) {
285 new_item->data.checkbox.allow_gray = allow_gray;
286 new_item->data.checkbox.value = value;
289 return new_item;
292 MenuItem *menuitem_create_ring(char *id, MenuEventFunc(*event_func),
293 char *text, Client *client, char *strings, short value)
295 MenuItem *new_item;
297 debug(RPT_DEBUG, "%s(id=[%s], event_func=%p, text=\"%s\", strings=\"%s\", value=%d)",
298 __FUNCTION__, id, event_func, text, strings, value);
300 new_item = menuitem_create(MENUITEM_RING, id, event_func, text, client);
301 if (new_item != NULL) {
302 new_item->data.ring.strings = tablist2linkedlist(strings);
303 new_item->data.ring.value = value;
306 return new_item;
309 MenuItem *menuitem_create_slider(char *id, MenuEventFunc(*event_func),
310 char *text, Client *client, char *mintext, char *maxtext,
311 int minvalue, int maxvalue, int stepsize, int value)
313 MenuItem *new_item;
315 debug(RPT_DEBUG, "%s(id=[%s], event_func=%p, text=\"%s\", mintext=\"%s\", maxtext=\"%s\", minvalue=%d, maxvalue=%d, stepsize=%d, value=%d)",
316 __FUNCTION__, id, event_func, text, mintext, maxtext, minvalue, maxvalue, stepsize, value);
318 new_item = menuitem_create(MENUITEM_SLIDER, id, event_func, text, client);
319 if (new_item != NULL) {
320 new_item->data.slider.mintext = strdup(mintext);
321 new_item->data.slider.maxtext = strdup(maxtext);
322 new_item->data.slider.minvalue = minvalue;
323 new_item->data.slider.maxvalue = maxvalue;
324 new_item->data.slider.stepsize = stepsize;
325 new_item->data.slider.value = value;
328 return new_item;
331 MenuItem *menuitem_create_numeric(char *id, MenuEventFunc(*event_func),
332 char *text, Client *client, int minvalue, int maxvalue, int value)
334 MenuItem *new_item;
336 debug(RPT_DEBUG, "%s(id=[%s], event_func=%p, text=\"%s\", minvalue=%d, maxvalue=%d, value=%d)",
337 __FUNCTION__, id, event_func, text, minvalue, minvalue, value);
339 new_item = menuitem_create(MENUITEM_NUMERIC, id, event_func, text, client);
340 if (new_item != NULL) {
341 new_item->data.numeric.maxvalue = maxvalue;
342 new_item->data.numeric.minvalue = minvalue;
343 new_item->data.numeric.edit_str = malloc(MAX_NUMERIC_LEN);
344 new_item->data.numeric.value = value;
347 return new_item;
350 MenuItem *menuitem_create_alpha(char *id, MenuEventFunc(*event_func),
351 char *text, Client *client, char password_char, short minlength, short maxlength,
352 bool allow_caps, bool allow_noncaps, bool allow_numbers,
353 char *allowed_extra, char *value)
355 MenuItem *new_item;
357 debug(RPT_DEBUG, "%s(id=\"%s\", event_func=%p, text=\"%s\", password_char=%d, maxlength=%d, value=\"%s\")",
358 __FUNCTION__, id, event_func, text, password_char, maxlength, value);
360 new_item = menuitem_create(MENUITEM_ALPHA, id, event_func, text, client);
361 if (new_item != NULL) {
362 new_item->data.alpha.password_char = password_char;
363 new_item->data.alpha.minlength = minlength;
364 new_item->data.alpha.maxlength = maxlength;
366 new_item->data.alpha.allow_caps = allow_caps;
367 new_item->data.alpha.allow_noncaps = allow_noncaps;
368 new_item->data.alpha.allow_numbers = allow_numbers;
369 new_item->data.alpha.allowed_extra = strdup(allowed_extra);
371 new_item->data.alpha.value = malloc(maxlength + 1);
372 if (new_item->data.alpha.value != NULL) {
373 strncpy(new_item->data.alpha.value, value, maxlength);
374 new_item->data.alpha.value[maxlength] = 0;
377 new_item->data.alpha.edit_str = malloc(maxlength + 1);
380 return new_item;
383 MenuItem *menuitem_create_ip(char *id, MenuEventFunc(*event_func),
384 char *text, Client *client, bool v6, char *value)
386 MenuItem *new_item;
387 const IpSstringProperties *ipinfo;
389 debug(RPT_DEBUG, "%s(id=\"%s\", event_func=%p, text=\"%s\", v6=%d, value=\"%s\")",
390 __FUNCTION__, id, event_func, text, v6, value);
392 new_item = menuitem_create(MENUITEM_IP, id, event_func, text, client);
393 new_item->data.ip.v6 = v6;
394 ipinfo = (v6) ? &IPinfo[1] : &IPinfo[0];
396 new_item->data.ip.maxlength = ipinfo->maxlen;;
397 new_item->data.ip.value = malloc(new_item->data.ip.maxlength + 1);
399 strncpy(new_item->data.ip.value, value, new_item->data.ip.maxlength);
400 new_item->data.ip.value[new_item->data.ip.maxlength] = '\0';
402 if (ipinfo->verify != NULL) {
403 char *start = new_item->data.ip.value;
405 while (start != NULL) {
406 char *skip = start;
408 while ((*skip == ' ') || ((*skip == '0') && (skip[1] != ipinfo->sep) && (skip[1] != '\0')))
409 skip++;
410 memccpy(start, skip, '\0', new_item->data.ip.maxlength + 1);
411 skip = strchr(start, ipinfo->sep);
412 start = (skip != NULL) ? (skip + 1) : NULL;
415 if (!ipinfo->verify(new_item->data.ip.value)) {
416 report(RPT_WARNING, "%s(id=\"%s\") ip address not verified: \"%s\"",
417 __FUNCTION__, id, value);
418 strncpy(new_item->data.ip.value, ipinfo->dummy, new_item->data.ip.maxlength);
419 new_item->data.ip.value[new_item->data.ip.maxlength] = '\0';
423 new_item->data.ip.edit_str = malloc(new_item->data.ip.maxlength + 1);
425 return new_item;
428 void menuitem_destroy(MenuItem *item)
430 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
431 ((item != NULL) ? item->id : "(null)"));
433 if (item != NULL) {
434 void (*destructor) (MenuItem *);
436 /* First destroy type specific data */
437 destructor = destructor_table[item->type];
438 if (destructor)
439 destructor(item);
441 /* Following strings should always be allocated */
442 free(item->text);
443 free(item->id);
445 /* And finally...*/
446 free(item);
450 void menuitem_destroy_ring(MenuItem *item)
452 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
453 ((item != NULL) ? item->id : "(null)"));
455 if (item != NULL) {
456 char *s;
458 /* deallocate the strings */
459 for (s = LL_GetFirst(item->data.ring.strings);
460 s != NULL;
461 s = LL_GetNext(item->data.ring.strings)) {
462 free(s);
464 /* and the list */
465 LL_Destroy(item->data.ring.strings);
469 void menuitem_destroy_slider(MenuItem *item)
471 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
472 ((item != NULL) ? item->id : "(null)"));
474 if (item != NULL) {
475 /* These strings should always be allocated */
476 free(item->data.slider.mintext);
477 free(item->data.slider.maxtext);
481 void menuitem_destroy_numeric(MenuItem *item)
483 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
484 ((item != NULL) ? item->id : "(null)"));
486 if (item != NULL) {
487 /* This string should always be allocated */
488 free(item->data.numeric.edit_str);
492 void menuitem_destroy_alpha(MenuItem *item)
494 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
495 ((item != NULL) ? item->id : "(null)"));
497 if (item != NULL) {
498 /* These strings should always be allocated */
499 free(item->data.alpha.allowed_extra);
500 free(item->data.alpha.value);
501 free(item->data.alpha.edit_str);
505 void menuitem_destroy_ip(MenuItem *item)
507 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
508 ((item != NULL) ? item->id : "(null)"));
510 /* These strings should always be allocated */
511 free(item->data.ip.value);
512 free(item->data.ip.edit_str);
516 /******** MENU ITEM RESET FUNCTIONS ********/
518 void menuitem_reset(MenuItem *item)
520 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
521 ((item != NULL) ? item->id : "(null)"));
523 if (item != NULL) {
524 void (*func) (MenuItem *);
526 /* First destroy type specific data */
527 func = reset_table[item->type];
528 if (func)
529 func(item);
533 void menuitem_reset_numeric(MenuItem *item)
535 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
536 ((item != NULL) ? item->id : "(null)"));
538 if (item != NULL) {
539 item->data.numeric.edit_pos = 0;
540 item->data.numeric.edit_offs = 0;
541 memset(item->data.numeric.edit_str, '\0', MAX_NUMERIC_LEN);
542 if (item->data.numeric.minvalue < 0) {
543 snprintf(item->data.numeric.edit_str, MAX_NUMERIC_LEN,
544 "%+d", item->data.numeric.value);
545 } else {
546 snprintf(item->data.numeric.edit_str, MAX_NUMERIC_LEN,
547 "%d", item->data.numeric.value);
552 void menuitem_reset_alpha(MenuItem *item)
554 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
555 ((item != NULL) ? item->id : "(null)"));
557 if (item != NULL) {
558 item->data.alpha.edit_pos = 0;
559 item->data.alpha.edit_offs = 0;
560 memset(item->data.alpha.edit_str, '\0', item->data.alpha.maxlength+1);
561 strcpy(item->data.alpha.edit_str, item->data.alpha.value);
565 void menuitem_reset_ip(MenuItem *item)
567 char *start = item->data.ip.value;
568 const IpSstringProperties *ipinfo = (item->data.ip.v6) ? &IPinfo[1] : &IPinfo[0];
570 debug(RPT_DEBUG, "%s(item=[%s])", __FUNCTION__,
571 ((item != NULL) ? item->id : "(null)"));
573 item->data.ip.edit_pos = 0;
574 item->data.ip.edit_offs = 0;
575 memset(item->data.ip.edit_str, '\0', item->data.ip.maxlength+1);
577 // normalize IP address string to e.g. 010.002.250.002 / 0001:0203:0405:0607:0809:0a0b:0c0d:0e0f
578 while (start != NULL) {
579 char *end;
580 char tmpstr[5];
581 int num = (int) strtol(start, (char **) NULL, ipinfo->base);
583 snprintf(tmpstr, 5, ipinfo->format, num);
584 strcat(item->data.ip.edit_str, tmpstr);
585 end = strchr(start, ipinfo->sep);
586 start = (end != NULL) ? (end + 1) : NULL;
587 if (start != NULL) {
588 tmpstr[0] = ipinfo->sep;
589 tmpstr[1] = '\0';
590 strcat(item->data.ip.edit_str, tmpstr);
596 /******** MENU SCREEN BUILD FUNCTIONS ********/
598 void menuitem_rebuild_screen(MenuItem *item, Screen *s)
600 Widget *w;
601 void (*build_screen) (MenuItem *item, Screen *s);
603 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
604 ((item != NULL) ? item->id : "(null)"),
605 ((s != NULL) ? s->id : "(null)"));
607 if (!display_props) {
608 /* Nothing to build if no display size is known */
609 report(RPT_ERR, "%s: display size unknown", __FUNCTION__);
610 return;
613 if (s != NULL) {
614 /* First remove all widgets from the screen */
615 while ((w = screen_getfirst_widget(s)) != NULL) {
616 /* We know these widgets don't have subwidgets, so we can
617 * easily remove them
619 screen_remove_widget(s, w);
620 widget_destroy(w);
623 if (item != NULL) {
624 /* Call type specific screen building function */
625 build_screen = build_screen_table [item->type];
626 if (build_screen) {
627 build_screen(item, s);
628 } else {
629 report(RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
630 return;
633 /* Also always call update_screen */
634 menuitem_update_screen(item, s);
639 void menuitem_rebuild_screen_slider(MenuItem *item, Screen *s)
641 Widget *w;
643 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
644 ((item != NULL) ? item->id : "(null)"),
645 ((s != NULL) ? s->id : "(null)"));
647 if ((item == NULL) || (s == NULL))
648 return;
650 if (display_props->height >= 2) {
651 /* Only add a title if enough space... */
652 w = widget_create("text", WID_STRING, s);
653 screen_add_widget(s, w);
654 w->text = strdup(item->text);
655 w->x = 1;
656 w->y = 1;
659 w = widget_create("bar", WID_HBAR, s);
660 screen_add_widget(s, w);
661 w->width = display_props->width;
662 if (display_props->height > 2) {
663 /* This is option 1: we have enought space, so the bar and
664 * min/max texts can be on separate lines.
666 w->x = 2;
667 w->y = display_props->height / 2 + 1;
668 w->width = display_props->width - 2;
671 w = widget_create("min", WID_STRING, s);
672 screen_add_widget(s, w);
673 w->text = NULL;
674 w->x = 1;
675 if (display_props->height > 2) {
676 w->y = display_props->height / 2 + 2;
677 } else {
678 w->y = display_props->height / 2 + 1;
681 w = widget_create("max", WID_STRING, s);
682 screen_add_widget(s, w);
683 w->text = NULL;
684 w->x = 1;
685 if (display_props->height > 2) {
686 w->y = display_props->height / 2 + 2;
687 } else {
688 w->y = display_props->height / 2 + 1;
692 void menuitem_rebuild_screen_numeric(MenuItem *item, Screen *s)
694 Widget *w;
696 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
697 ((item != NULL) ? item->id : "(null)"),
698 ((s != NULL) ? s->id : "(null)"));
700 if ((item == NULL) || (s == NULL))
701 return;
703 if (display_props->height >= 2) {
704 /* Only add a title if enough space... */
705 w = widget_create("text", WID_STRING, s);
706 screen_add_widget(s, w);
707 w->text = strdup(item->text);
708 w->x = 1;
709 w->y = 1;
712 w = widget_create("value", WID_STRING, s);
713 screen_add_widget(s, w);
714 w->text = malloc(MAX_NUMERIC_LEN);
715 w->x = 2;
716 w->y = display_props->height / 2 + 1;
718 /* Only display error string if enough space... */
719 if (display_props->height > 2) {
720 w = widget_create("error", WID_STRING, s);
721 screen_add_widget(s, w);
722 w->text = strdup("");
723 w->x = 1;
724 w->y = display_props->height;
728 void menuitem_rebuild_screen_alpha(MenuItem *item, Screen *s)
730 Widget *w;
732 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
733 ((item != NULL) ? item->id : "(null)"),
734 ((s != NULL) ? s->id : "(null)"));
736 if ((item == NULL) || (s == NULL))
737 return;
739 if (display_props->height >= 2) {
740 /* Only add a title if enough space... */
741 w = widget_create("text", WID_STRING, s);
742 screen_add_widget(s, w);
743 w->text = strdup(item->text);
744 w->x = 1;
745 w->y = 1;
748 w = widget_create("value", WID_STRING, s);
749 screen_add_widget(s, w);
750 w->text = malloc(item->data.alpha.maxlength+1);
751 w->x = 2;
752 w->y = display_props->height / 2 + 1;
754 /* Only display error string if enough space... */
755 if (display_props->height > 2) {
756 w = widget_create("error", WID_STRING, s);
757 screen_add_widget(s, w);
758 w->text = strdup("");
759 w->x = 1;
760 w->y = display_props->height;
764 void menuitem_rebuild_screen_ip(MenuItem *item, Screen *s)
766 Widget *w;
768 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
769 ((item != NULL) ? item->id : "(null)"),
770 ((s != NULL) ? s->id : "(null)"));
772 if ((item == NULL) || (s == NULL))
773 return;
775 if (display_props->height >= 2) {
776 /* Only add a title if enough space... */
777 w = widget_create("text", WID_STRING, s);
778 screen_add_widget(s, w);
779 w->text = strdup(item->text);
780 w->x = 1;
781 w->y = 1;
784 w = widget_create("value", WID_STRING, s);
785 screen_add_widget(s, w);
786 w->text = malloc(item->data.ip.maxlength+1);
787 w->x = 2;
788 w->y = display_props->height / 2 + 1;
790 /* Only display error string if enough space... */
791 if (display_props->height > 2) {
792 w = widget_create("error", WID_STRING, s);
793 screen_add_widget(s, w);
794 w->text = strdup("");
795 w->x = 1;
796 w->y = display_props->height;
800 /******** MENU SCREEN UPDATE FUNCTIONS ********/
802 void menuitem_update_screen(MenuItem *item, Screen *s)
804 void (*update_screen) (MenuItem *item, Screen *s);
806 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
807 ((item != NULL) ? item->id : "(null)"),
808 ((s != NULL) ? s->id : "(null)"));
810 if ((item == NULL) || (s == NULL))
811 return;
813 /* Disable the cursor by default */
814 s->cursor = CURSOR_OFF;
816 /* Call type specific screen building function */
817 update_screen = update_screen_table [item->type];
818 if (update_screen) {
819 update_screen(item, s);
820 } else {
821 report(RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
822 return;
826 void menuitem_update_screen_slider(MenuItem *item, Screen *s)
828 Widget *w;
829 int min_len, max_len;
831 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
832 ((item != NULL) ? item->id : "(null)"),
833 ((s != NULL) ? s->id : "(null)"));
835 if ((item == NULL) || (s == NULL))
836 return;
838 /* Calculate the bar position and length by filling buffers */
839 min_len = strlen(item->data.slider.mintext);
840 max_len = strlen(item->data.slider.maxtext);
842 /* And adjust the data */
844 w = screen_find_widget(s, "bar");
845 if (display_props->height <= 2) {
846 /* This is option 2: we're tight on lines, so we put the bar
847 * and min/max texts on the same line.
849 w->x = 1 + min_len;
850 w->y = display_props->height;
851 w->width = display_props->width -
852 min_len - max_len;
854 /* FUTURE: w->promille = 1000 * (item->data.slider.value - item->data.slider.minvalue) / (item->data.slider.maxvalue - item->data.slider.minvalue) */;
855 w->length = w->width * display_props->cellwidth
856 * (item->data.slider.value - item->data.slider.minvalue)
857 / (item->data.slider.maxvalue - item->data.slider.minvalue);
859 w = screen_find_widget(s, "min");
860 if (w->text) free(w->text);
861 w->text = strdup(item->data.slider.mintext);
863 w = screen_find_widget(s, "max");
864 if (w->text) free(w->text);
865 w->x = 1 + display_props->width - max_len;
866 w->text = strdup(item->data.slider.maxtext);
869 void menuitem_update_screen_numeric(MenuItem *item, Screen *s)
871 Widget *w;
873 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
874 ((item != NULL) ? item->id : "(null)"),
875 ((s != NULL) ? s->id : "(null)"));
877 if ((item == NULL) || (s == NULL))
878 return;
880 w = screen_find_widget(s, "value");
881 strcpy(w->text, item->data.numeric.edit_str + item->data.numeric.edit_offs);
883 s->cursor = CURSOR_DEFAULT_ON;
884 s->cursor_x = w->x + item->data.numeric.edit_pos - item->data.numeric.edit_offs;
885 s->cursor_y = w->y;
887 /* Only display error string if enough space... */
888 if (display_props->height > 2) {
889 w = screen_find_widget(s, "error");
890 free(w->text);
891 w->text = strdup(error_strs[item->data.numeric.error_code]);
895 void menuitem_update_screen_alpha(MenuItem *item, Screen *s)
897 Widget *w;
899 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
900 ((item != NULL) ? item->id : "(null)"),
901 ((s != NULL) ? s->id : "(null)"));
903 if ((item == NULL) || (s == NULL))
904 return;
906 w = screen_find_widget(s, "value");
907 if (item->data.alpha.password_char == '\0') {
908 strcpy(w->text, item->data.alpha.edit_str + item->data.alpha.edit_offs);
909 } else {
910 int len = strlen(item->data.alpha.edit_str) - item->data.alpha.edit_offs;
912 memset(w->text, item->data.alpha.password_char, len);
913 w->text[len] = '\0';
916 s->cursor = CURSOR_DEFAULT_ON;
917 s->cursor_x = w->x + item->data.alpha.edit_pos - item->data.alpha.edit_offs;
918 s->cursor_y = w->y;
920 /* Only display error string if enough space... */
921 if (display_props->height > 2) {
922 w = screen_find_widget(s, "error");
923 free(w->text);
924 w->text = strdup(error_strs[item->data.alpha.error_code]);
928 void menuitem_update_screen_ip(MenuItem *item, Screen *s)
930 Widget *w;
932 debug(RPT_DEBUG, "%s(item=[%s], screen=[%s])", __FUNCTION__,
933 ((item != NULL) ? item->id : "(null)"),
934 ((s != NULL) ? s->id : "(null)"));
936 if ((item == NULL) || (s == NULL))
937 return;
939 w = screen_find_widget(s, "value");
940 if (w != NULL)
941 strcpy(w->text, item->data.ip.edit_str + item->data.ip.edit_offs);
943 s->cursor = CURSOR_DEFAULT_ON;
944 s->cursor_x = w->x + item->data.ip.edit_pos - item->data.ip.edit_offs;
945 s->cursor_y = w->y;
947 /* Only display error string if enough space... */
948 if (display_props->height > 2) {
949 w = screen_find_widget(s, "error");
950 free(w->text);
951 w->text = strdup(error_strs[item->data.ip.error_code]);
955 /******** MENU SCREEN INPUT HANDLING FUNCTIONS ********/
957 MenuResult menuitem_process_input(MenuItem *item, MenuToken token, const char *key, bool extended)
959 MenuResult (*process_input) (MenuItem *item, MenuToken token, const char *key, bool extended);
961 debug(RPT_DEBUG, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__,
962 ((item != NULL) ? item->id : "(null)"), token, key);
964 if (item == NULL)
965 return MENURESULT_ERROR;
967 /* Call type specific screen building function */
968 process_input = process_input_table [item->type];
969 if (process_input) {
970 return process_input(item, token, key, extended);
971 } else {
972 report(RPT_ERR, "%s: given menuitem cannot be active", __FUNCTION__);
973 return MENURESULT_ERROR;
977 MenuResult menuitem_process_input_slider(MenuItem *item, MenuToken token, const char *key, bool extended)
979 debug(RPT_DEBUG, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__,
980 ((item != NULL) ? item->id : "(null)"), token, key);
982 if (item == NULL)
983 return MENURESULT_ERROR;
985 switch (token) {
986 case MENUTOKEN_MENU:
987 return menuitem_predecessor2menuresult(
988 item->predecessor_id, MENURESULT_CLOSE);
989 case MENUTOKEN_ENTER:
990 return menuitem_successor2menuresult(
991 item->successor_id, MENURESULT_CLOSE);
992 case MENUTOKEN_UP:
993 case MENUTOKEN_RIGHT:
994 item->data.slider.value = min(item->data.slider.maxvalue,
995 item->data.slider.value + item->data.slider.stepsize);
996 if (item->event_func)
997 item->event_func(item, MENUEVENT_PLUS);
998 return MENURESULT_NONE;
999 case MENUTOKEN_DOWN:
1000 case MENUTOKEN_LEFT:
1001 item->data.slider.value = max(item->data.slider.minvalue,
1002 item->data.slider.value - item->data.slider.stepsize);
1003 if (item->event_func)
1004 item->event_func(item, MENUEVENT_MINUS);
1005 return MENURESULT_NONE;
1006 case MENUTOKEN_OTHER:
1007 default:
1008 break;
1011 return MENURESULT_ERROR;
1014 MenuResult menuitem_process_input_numeric(MenuItem *item, MenuToken token, const char *key, bool extended)
1016 char buf1[MAX_NUMERIC_LEN];
1017 char buf2[MAX_NUMERIC_LEN];
1019 int max_len;
1021 debug(RPT_DEBUG, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__,
1022 ((item != NULL) ? item->id : "(null)"), token, key);
1024 if (item != NULL) {
1025 /* To make life easy... */
1026 char *str = item->data.numeric.edit_str;
1027 int pos = item->data.numeric.edit_pos;
1028 int allow_signed = (item->data.numeric.minvalue < 0);
1029 char *format_str = (allow_signed) ? "%+d" : "%d";
1031 snprintf(buf1, MAX_NUMERIC_LEN, format_str, item->data.numeric.minvalue);
1032 snprintf(buf2, MAX_NUMERIC_LEN, format_str, item->data.numeric.maxvalue);
1034 max_len = max(strlen(buf1), strlen(buf2));
1036 /* Clear the error */
1037 item->data.numeric.error_code = 0;
1039 switch (token) {
1040 case MENUTOKEN_MENU:
1041 if (pos == 0) {
1042 return menuitem_predecessor2menuresult(
1043 item->predecessor_id, MENURESULT_CLOSE);
1045 else {
1046 /* Reset data */
1047 menuitem_reset_numeric(item);
1049 return MENURESULT_NONE;
1050 case MENUTOKEN_ENTER:
1051 if ((extended) || (str[pos] == '\0')) {
1052 int value;
1053 /* The user completed his input */
1055 /* ...scan it */
1056 if (sscanf(str, "%d", &value) != 1) {
1057 return MENURESULT_ERROR;
1059 /* Test the value */
1060 if (value < item->data.numeric.minvalue
1061 || value > item->data.numeric.maxvalue) {
1062 /* Out of range !
1063 * We can't exit this screen now
1065 item->data.numeric.error_code = 1;
1066 item->data.numeric.edit_pos = 0;
1067 item->data.numeric.edit_offs = 0;
1068 return MENURESULT_NONE;
1071 /* OK, store value */
1072 item->data.numeric.value = value;
1074 /* Inform client */
1075 if (item->event_func)
1076 item->event_func(item, MENUEVENT_UPDATE);
1078 return menuitem_successor2menuresult(
1079 item->successor_id, MENURESULT_CLOSE);
1081 else {
1082 /* The user wants to go to next digit */
1083 if (pos < max_len) {
1084 item->data.numeric.edit_pos++;
1085 if (pos >= display_props->width - 2)
1086 item->data.numeric.edit_offs++;
1089 return MENURESULT_NONE;
1090 case MENUTOKEN_UP:
1091 if (pos >= max_len) {
1092 /* We're not allowed to add anything anymore */
1093 item->data.numeric.error_code = 2;
1094 item->data.numeric.edit_pos = 0;
1095 item->data.numeric.edit_offs = 0;
1096 return MENURESULT_NONE;
1098 if (allow_signed && pos == 0) {
1099 /* make negative */
1100 str[0] = (str[0] == '-') ? '+' : '-';
1102 else {
1103 if (str[pos] >= '0' && str[pos] < '9') {
1104 str[pos] ++;
1105 } else if (str[pos] == '9') {
1106 str[pos] = '\0';
1107 } else if (str[pos] == '\0') {
1108 str[pos] = '0';
1111 return MENURESULT_NONE;
1112 case MENUTOKEN_DOWN:
1113 if (pos >= max_len) {
1114 /* We're not allowed to add anything anymore */
1115 item->data.numeric.error_code = 2;
1116 item->data.numeric.edit_pos = 0;
1117 item->data.numeric.edit_offs = 0;
1118 return MENURESULT_NONE;
1120 if (allow_signed && pos == 0) {
1121 /* make negative */
1122 str[0] = (str[0] == '-') ? '+' : '-';
1124 else {
1125 if (str[pos] > '0' && str[pos] <= '9') {
1126 str[pos] --;
1127 } else if (str[pos] == '0') {
1128 str[pos] = '\0';
1129 } else if (str[pos] == '\0') {
1130 str[pos] = '9';
1133 return MENURESULT_NONE;
1134 case MENUTOKEN_RIGHT:
1135 /* The user wants to go to next digit */
1136 if (str[pos] != '\0' && pos < max_len) {
1137 item->data.numeric.edit_pos++;
1138 if (pos >= display_props->width - 2)
1139 item->data.numeric.edit_offs++;
1141 return MENURESULT_NONE;
1142 case MENUTOKEN_LEFT:
1143 /* The user wants to go to back a digit */
1144 if (pos > 0) {
1145 item->data.numeric.edit_pos--;
1146 if (item->data.numeric.edit_offs > item->data.numeric.edit_pos)
1147 item->data.numeric.edit_offs = item->data.numeric.edit_pos;
1149 return MENURESULT_NONE;
1150 case MENUTOKEN_OTHER:
1151 if (pos >= max_len) {
1152 /* We're not allowed to add anything anymore */
1153 item->data.numeric.error_code = 2;
1154 item->data.numeric.edit_pos = 0;
1155 item->data.numeric.edit_offs = 0;
1156 return MENURESULT_NONE;
1158 /* process numeric keys */
1159 if ((strlen(key) == 1) && isdigit(key[0])) {
1160 str[pos] = key[0];
1161 item->data.numeric.edit_pos++;
1162 if (pos >= display_props->width - 2)
1163 item->data.numeric.edit_offs++;
1165 return MENURESULT_NONE;
1168 return MENURESULT_ERROR;
1171 MenuResult menuitem_process_input_alpha(MenuItem *item, MenuToken token, const char *key, bool extended)
1173 char *p;
1174 static char *chars = NULL;
1176 debug(RPT_DEBUG, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__,
1177 ((item != NULL) ? item->id : "(null)"), token, key);
1179 if (item != NULL) {
1180 /* To make life easy... */
1181 char *str = item->data.alpha.edit_str;
1182 int pos = item->data.alpha.edit_pos;
1184 /* Create list of allowed chars */
1185 chars = realloc(chars, 26 + 26 + 10 + strlen(item->data.alpha.allowed_extra) + 1);
1186 chars[0] = '\0'; /* clear string */
1187 if (item->data.alpha.allow_caps)
1188 strcat(chars, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
1189 if (item->data.alpha.allow_noncaps)
1190 strcat(chars, "abcdefghijklmnopqrstuvwxyz");
1191 if (item->data.alpha.allow_numbers)
1192 strcat(chars, "0123456789");
1193 strcat(chars, item->data.alpha.allowed_extra);
1195 /* Clear the error */
1196 item->data.alpha.error_code = 0;
1198 switch (token) {
1199 case MENUTOKEN_MENU:
1200 if (pos == 0) {
1201 return menuitem_predecessor2menuresult(
1202 item->predecessor_id, MENURESULT_CLOSE);
1204 else {
1205 /* Reset data */
1206 menuitem_reset_alpha(item);
1208 return MENURESULT_NONE;
1209 case MENUTOKEN_ENTER:
1210 if ((extended) || (str[item->data.alpha.edit_pos] == '\0')) {
1211 /* The user completed his input */
1213 /* It's not too short ? */
1214 if (strlen(item->data.alpha.edit_str) < item->data.alpha.minlength) {
1215 item->data.alpha.error_code = 3;
1216 return MENURESULT_NONE;
1219 /* Store value */
1220 strcpy(item->data.alpha.value, item->data.alpha.edit_str);
1222 /* Inform client */
1223 if (item->event_func)
1224 item->event_func(item, MENUEVENT_UPDATE);
1226 return menuitem_successor2menuresult(
1227 item->successor_id, MENURESULT_CLOSE);
1229 else {
1230 /* The user wants to go to next digit */
1231 if (pos < item->data.alpha.maxlength) {
1232 item->data.alpha.edit_pos++;
1233 if (pos >= display_props->width - 2)
1234 item->data.alpha.edit_offs++;
1237 return MENURESULT_NONE;
1238 case MENUTOKEN_UP:
1239 if (pos >= item->data.alpha.maxlength) {
1240 /* We're not allowed to add anything anymore */
1241 item->data.alpha.error_code = 2;
1242 item->data.alpha.edit_pos = 0;
1243 item->data.alpha.edit_offs = 0;
1244 return MENURESULT_NONE;
1246 if (str[pos] == '\0') {
1247 /* User goes past EOL */
1248 str[pos] = chars[0];
1249 } else {
1250 /* We should have a symbol from our list */
1251 p = strchr(chars, str[pos]);
1252 if (p != NULL) {
1253 str[pos] = *(++p); /* next symbol on list */
1254 /* Might be '\0' now */
1255 } else {
1256 str[pos] = '\0';
1259 return MENURESULT_NONE;
1260 case MENUTOKEN_DOWN:
1261 if (pos >= item->data.alpha.maxlength) {
1262 /* We're not allowed to add anything anymore */
1263 item->data.alpha.error_code = 2;
1264 item->data.alpha.edit_pos = 0;
1265 item->data.alpha.edit_offs = 0;
1266 return MENURESULT_NONE;
1268 if (str[pos] == '\0') {
1269 /* User goes past EOL */
1270 str[pos] = chars[strlen(chars)-1];
1271 } else {
1272 /* We should have a symbol from our list */
1273 p = strchr(chars, str[pos]);
1274 if ((p != NULL) && (p != chars)) {
1275 str[pos] = *(--p); /* previous symbol on list */
1276 } else {
1277 str[pos] = '\0';
1280 return MENURESULT_NONE;
1281 case MENUTOKEN_RIGHT:
1282 /* The user wants to go to next digit */
1283 if (str[item->data.alpha.edit_pos] != '\0' &&
1284 pos < item->data.alpha.maxlength - 1) {
1285 item->data.alpha.edit_pos++;
1286 if (pos >= display_props->width - 2)
1287 item->data.alpha.edit_offs++;
1289 return MENURESULT_NONE;
1290 case MENUTOKEN_LEFT:
1291 /* The user wants to go to back a digit */
1292 if (pos > 0) {
1293 item->data.alpha.edit_pos--;
1294 if (item->data.alpha.edit_offs > item->data.alpha.edit_pos)
1295 item->data.alpha.edit_offs = item->data.alpha.edit_pos;
1297 return MENURESULT_NONE;
1298 case MENUTOKEN_OTHER:
1299 if (pos >= item->data.alpha.maxlength) {
1300 /* We're not allowed to add anything anymore */
1301 item->data.alpha.error_code = 2;
1302 item->data.alpha.edit_pos = 0;
1303 item->data.alpha.edit_offs = 0;
1304 return MENURESULT_NONE;
1306 /* process other keys */
1307 if ((strlen(key) == 1) && (key[0] >= ' ') && (strchr(chars, key[0]) != NULL)) {
1308 str[pos] = key[0];
1309 item->data.alpha.edit_pos++;
1310 if (pos >= display_props->width - 2)
1311 item->data.alpha.edit_offs++;
1313 return MENURESULT_NONE;
1316 return MENURESULT_ERROR;
1320 MenuResult menuitem_process_input_ip(MenuItem *item, MenuToken token, const char *key, bool extended)
1322 /* To make life easy... */
1323 char *str = item->data.ip.edit_str;
1324 char numstr[5];
1325 int num;
1326 int pos = item->data.ip.edit_pos;
1327 const IpSstringProperties *ipinfo = (item->data.ip.v6) ? &IPinfo[1] : &IPinfo[0];
1329 debug(RPT_DEBUG, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__,
1330 ((item != NULL) ? item->id : "(null)"), token, key);
1332 /* Clear the error */
1333 item->data.ip.error_code = 0;
1335 switch (token) {
1336 case MENUTOKEN_MENU:
1337 if (pos == 0) {
1338 return menuitem_predecessor2menuresult(
1339 item->predecessor_id, MENURESULT_CLOSE);
1341 else {
1342 /* Reset data */
1343 menuitem_reset_ip(item);
1345 return MENURESULT_NONE;
1346 case MENUTOKEN_ENTER:
1347 if (extended || (pos >= item->data.ip.maxlength - 1)) {
1348 // remove the leading spaces/zeros in each octet-representing string
1349 char tmp[40]; // 40 = max. length of IPv4 & IPv6 addresses incl. '\0'
1350 char *start = tmp;
1352 memccpy(tmp, str, '\0', sizeof(tmp));
1354 while (start != NULL) {
1355 char *skip = start;
1357 while ((*skip == ' ') || ((*skip == '0') && (skip[1] != ipinfo->sep) && (skip[1] != '\0')))
1358 skip++;
1359 memccpy(start, skip, '\0', item->data.ip.maxlength + 1);
1360 skip = strchr(start, ipinfo->sep);
1361 start = (skip != NULL) ? (skip + 1) : NULL;
1364 // check IP address entered
1365 if ((ipinfo->verify != NULL) && (!ipinfo->verify(tmp))) {
1366 report(RPT_WARNING, "%s(id=\"%s\") ip address not verified: \"%s\"",
1367 __FUNCTION__, item->id, tmp);
1368 item->data.ip.error_code = 4;
1369 return MENURESULT_NONE;
1372 // store value
1373 strcpy(item->data.ip.value, tmp);
1375 // Inform client
1376 if (item->event_func)
1377 item->event_func(item, MENUEVENT_UPDATE);
1379 return menuitem_successor2menuresult(
1380 item->successor_id, MENURESULT_CLOSE);
1382 else {
1383 item->data.ip.edit_pos++;
1384 if (str[item->data.ip.edit_pos] == ipinfo->sep)
1385 item->data.ip.edit_pos++;
1386 while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
1387 item->data.ip.edit_offs++;
1388 return MENURESULT_NONE;
1390 case MENUTOKEN_UP:
1391 // convert string starting at the beginning / previous dot into a number
1392 num = (int) strtol(&str[pos - (pos % (ipinfo->width + 1))], (char **) NULL, ipinfo->base);
1393 // increase the number depending on the position
1394 num += ipinfo->posValue[(pos - (pos / (ipinfo->width + 1))) % ipinfo->width];
1395 // wrap around upper limit
1396 if (num > ipinfo->limit)
1397 num = 0;
1398 snprintf(numstr, 5, ipinfo->format, num);
1399 memcpy(&str[pos - (pos % (ipinfo->width + 1))], numstr, ipinfo->width);
1400 return MENURESULT_NONE;
1401 case MENUTOKEN_DOWN:
1402 // convert string starting at the beginning / previous dot into a number
1403 num = (int) strtol(&str[pos - (pos % (ipinfo->width + 1))], (char **) NULL, ipinfo->base);
1404 // decrease the number depending on the position
1405 num -= ipinfo->posValue[(pos - (pos / (ipinfo->width + 1))) % ipinfo->width];
1406 // wrap around lower limit 0
1407 if (num < 0)
1408 num = ipinfo->limit;
1409 snprintf(numstr, 5, ipinfo->format, num);
1410 memcpy(&str[pos - (pos % (ipinfo->width + 1))], numstr, ipinfo->width);
1411 return MENURESULT_NONE;
1412 case MENUTOKEN_RIGHT:
1413 if (pos < item->data.ip.maxlength - 1) {
1414 item->data.ip.edit_pos++;
1415 if (str[item->data.ip.edit_pos] == ipinfo->sep)
1416 item->data.ip.edit_pos++;
1417 while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
1418 item->data.ip.edit_offs++;
1420 return MENURESULT_NONE;
1421 case MENUTOKEN_LEFT:
1422 /* The user wants to go to back a digit */
1423 if (pos > 0) {
1424 item->data.ip.edit_pos--;
1425 if (str[item->data.ip.edit_pos] == ipinfo->sep)
1426 item->data.ip.edit_pos--;
1427 if (item->data.ip.edit_offs > item->data.ip.edit_pos)
1428 item->data.ip.edit_offs = item->data.ip.edit_pos;
1430 return MENURESULT_NONE;
1431 case MENUTOKEN_OTHER:
1432 /* process other keys */
1433 if ((strlen(key) == 1) &&
1434 ((item->data.ip.v6) ? isxdigit(key[0]) : isdigit(key[0]))) {
1435 str[pos] = tolower(key[0]);
1436 if (pos < item->data.ip.maxlength - 1) {
1437 item->data.ip.edit_pos++;
1438 if (str[item->data.ip.edit_pos] == ipinfo->sep)
1439 item->data.ip.edit_pos++;
1440 while (item->data.ip.edit_pos - item->data.ip.edit_offs > display_props->width - 2)
1441 item->data.ip.edit_offs++;
1444 return MENURESULT_NONE;
1446 return MENURESULT_ERROR;
1450 * get the Client that owns item. extracted from menu_commands_handler().
1452 Client *menuitem_get_client(MenuItem *item)
1454 return item->client;
1457 LinkedList *tablist2linkedlist(char *strings)
1459 LinkedList *list;
1461 list = LL_new();
1463 /* Parse strings */
1464 if (strings != NULL) {
1465 char *p = strings;
1466 char *tabptr, *new_s;
1468 while ((tabptr = strchr(p, '\t')) != NULL) {
1469 int len = (int)(tabptr - p);
1471 /* Alloc and copy substring */
1472 new_s = malloc(len + 1);
1473 strncpy(new_s, p, len);
1474 new_s[len] = 0;
1476 LL_Push(list, new_s);
1478 /* Go to next string */
1479 p = tabptr + 1;
1481 /* Add last string */
1482 new_s = strdup(p);
1483 LL_Push(list, new_s);
1486 return list;
1489 MenuItemType menuitem_typename_to_type(char *name)
1491 if (name != NULL) {
1492 MenuItemType type;
1494 for (type = 0; type < NUM_ITEMTYPES; type ++) {
1495 if (strcmp(menuitemtypenames[type], name) == 0) {
1496 return type;
1500 return -1;
1503 char *menuitem_type_to_typename(MenuItemType type)
1505 return ((type >= 0 && type < NUM_ITEMTYPES)
1506 ? menuitemtypenames[type]
1507 : NULL);
1510 MenuEventType menuitem_eventtypename_to_eventtype(char *name)
1512 if (name != NULL) {
1513 MenuEventType type;
1515 for (type = 0; type < NUM_EVENTTYPES; type ++) {
1516 if (strcmp(menueventtypenames[type], name) == 0) {
1517 return type;
1521 return -1;
1524 char *menuitem_eventtype_to_eventtypename(MenuEventType type)
1526 return ((type >= 0 && type < NUM_EVENTTYPES)
1527 ? menueventtypenames[type]
1528 : NULL);