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
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.
22 #include "shared/report.h"
25 #include "menuscreens.h"
29 /* this is needed for verify_ipv4 and verify_ipv6. */
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 */
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
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
;
103 return MENURESULT_PREDECESSOR
;
106 /** returns default_result if successor_id is NULL or the respective value
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
;
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
;
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
) =
144 menuitem_destroy_ring
,
145 menuitem_destroy_slider
,
146 menuitem_destroy_numeric
,
147 menuitem_destroy_alpha
,
150 void (*reset_table
[NUM_ITEMTYPES
]) (MenuItem
*item
) =
157 menuitem_reset_numeric
,
158 menuitem_reset_alpha
,
161 void (*build_screen_table
[NUM_ITEMTYPES
]) (MenuItem
*item
, Screen
*s
) =
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
) =
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
) =
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
)
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__);
211 /* Allocate space and fill struct */
212 new_item
= malloc(sizeof(MenuItem
));
214 report(RPT_ERR
, "%s: Could not allocate memory", __FUNCTION__
);
217 new_item
->type
= type
;
218 new_item
->id
= strdup(id
);
220 report(RPT_ERR
, "%s: Could not allocate memory", __FUNCTION__
);
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__
);
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
));
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
)
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
)
258 case MENURESULT_NONE
:
259 new_item
->successor_id
= strdup("_none_");
261 case MENURESULT_CLOSE
:
262 new_item
->successor_id
= strdup("_close_");
264 case MENURESULT_QUIT
:
265 new_item
->successor_id
= strdup("_quit_");
268 assert(!"unexpected MENURESULT");
275 MenuItem
*menuitem_create_checkbox(char *id
, MenuEventFunc(*event_func
),
276 char *text
, Client
*client
, bool allow_gray
, bool value
)
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
;
292 MenuItem
*menuitem_create_ring(char *id
, MenuEventFunc(*event_func
),
293 char *text
, Client
*client
, char *strings
, short value
)
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
;
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
)
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
;
331 MenuItem
*menuitem_create_numeric(char *id
, MenuEventFunc(*event_func
),
332 char *text
, Client
*client
, int minvalue
, int maxvalue
, int value
)
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
;
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
)
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);
383 MenuItem
*menuitem_create_ip(char *id
, MenuEventFunc(*event_func
),
384 char *text
, Client
*client
, bool v6
, char *value
)
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
) {
408 while ((*skip
== ' ') || ((*skip
== '0') && (skip
[1] != ipinfo
->sep
) && (skip
[1] != '\0')))
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);
428 void menuitem_destroy(MenuItem
*item
)
430 debug(RPT_DEBUG
, "%s(item=[%s])", __FUNCTION__
,
431 ((item
!= NULL
) ? item
->id
: "(null)"));
434 void (*destructor
) (MenuItem
*);
436 /* First destroy type specific data */
437 destructor
= destructor_table
[item
->type
];
441 /* Following strings should always be allocated */
450 void menuitem_destroy_ring(MenuItem
*item
)
452 debug(RPT_DEBUG
, "%s(item=[%s])", __FUNCTION__
,
453 ((item
!= NULL
) ? item
->id
: "(null)"));
458 /* deallocate the strings */
459 for (s
= LL_GetFirst(item
->data
.ring
.strings
);
461 s
= LL_GetNext(item
->data
.ring
.strings
)) {
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)"));
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)"));
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)"));
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)"));
524 void (*func
) (MenuItem
*);
526 /* First destroy type specific data */
527 func
= reset_table
[item
->type
];
533 void menuitem_reset_numeric(MenuItem
*item
)
535 debug(RPT_DEBUG
, "%s(item=[%s])", __FUNCTION__
,
536 ((item
!= NULL
) ? item
->id
: "(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
);
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)"));
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
) {
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
;
588 tmpstr
[0] = ipinfo
->sep
;
590 strcat(item
->data
.ip
.edit_str
, tmpstr
);
596 /******** MENU SCREEN BUILD FUNCTIONS ********/
598 void menuitem_rebuild_screen(MenuItem
*item
, Screen
*s
)
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__
);
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
619 screen_remove_widget(s
, w
);
624 /* Call type specific screen building function */
625 build_screen
= build_screen_table
[item
->type
];
627 build_screen(item
, s
);
629 report(RPT_ERR
, "%s: given menuitem cannot be active", __FUNCTION__
);
633 /* Also always call update_screen */
634 menuitem_update_screen(item
, s
);
639 void menuitem_rebuild_screen_slider(MenuItem
*item
, Screen
*s
)
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
))
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
);
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.
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
);
675 if (display_props
->height
> 2) {
676 w
->y
= display_props
->height
/ 2 + 2;
678 w
->y
= display_props
->height
/ 2 + 1;
681 w
= widget_create("max", WID_STRING
, s
);
682 screen_add_widget(s
, w
);
685 if (display_props
->height
> 2) {
686 w
->y
= display_props
->height
/ 2 + 2;
688 w
->y
= display_props
->height
/ 2 + 1;
692 void menuitem_rebuild_screen_numeric(MenuItem
*item
, Screen
*s
)
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
))
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
);
712 w
= widget_create("value", WID_STRING
, s
);
713 screen_add_widget(s
, w
);
714 w
->text
= malloc(MAX_NUMERIC_LEN
);
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("");
724 w
->y
= display_props
->height
;
728 void menuitem_rebuild_screen_alpha(MenuItem
*item
, Screen
*s
)
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
))
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
);
748 w
= widget_create("value", WID_STRING
, s
);
749 screen_add_widget(s
, w
);
750 w
->text
= malloc(item
->data
.alpha
.maxlength
+1);
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("");
760 w
->y
= display_props
->height
;
764 void menuitem_rebuild_screen_ip(MenuItem
*item
, Screen
*s
)
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
))
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
);
784 w
= widget_create("value", WID_STRING
, s
);
785 screen_add_widget(s
, w
);
786 w
->text
= malloc(item
->data
.ip
.maxlength
+1);
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("");
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
))
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
];
819 update_screen(item
, s
);
821 report(RPT_ERR
, "%s: given menuitem cannot be active", __FUNCTION__
);
826 void menuitem_update_screen_slider(MenuItem
*item
, Screen
*s
)
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
))
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.
850 w
->y
= display_props
->height
;
851 w
->width
= display_props
->width
-
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
)
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
))
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
;
887 /* Only display error string if enough space... */
888 if (display_props
->height
> 2) {
889 w
= screen_find_widget(s
, "error");
891 w
->text
= strdup(error_strs
[item
->data
.numeric
.error_code
]);
895 void menuitem_update_screen_alpha(MenuItem
*item
, Screen
*s
)
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
))
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
);
910 int len
= strlen(item
->data
.alpha
.edit_str
) - item
->data
.alpha
.edit_offs
;
912 memset(w
->text
, item
->data
.alpha
.password_char
, len
);
916 s
->cursor
= CURSOR_DEFAULT_ON
;
917 s
->cursor_x
= w
->x
+ item
->data
.alpha
.edit_pos
- item
->data
.alpha
.edit_offs
;
920 /* Only display error string if enough space... */
921 if (display_props
->height
> 2) {
922 w
= screen_find_widget(s
, "error");
924 w
->text
= strdup(error_strs
[item
->data
.alpha
.error_code
]);
928 void menuitem_update_screen_ip(MenuItem
*item
, Screen
*s
)
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
))
939 w
= screen_find_widget(s
, "value");
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
;
947 /* Only display error string if enough space... */
948 if (display_props
->height
> 2) {
949 w
= screen_find_widget(s
, "error");
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
);
965 return MENURESULT_ERROR
;
967 /* Call type specific screen building function */
968 process_input
= process_input_table
[item
->type
];
970 return process_input(item
, token
, key
, extended
);
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
);
983 return MENURESULT_ERROR
;
987 return menuitem_predecessor2menuresult(
988 item
->predecessor_id
, MENURESULT_CLOSE
);
989 case MENUTOKEN_ENTER
:
990 return menuitem_successor2menuresult(
991 item
->successor_id
, MENURESULT_CLOSE
);
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
;
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
:
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
];
1021 debug(RPT_DEBUG
, "%s(item=[%s], token=%d, key=\"%s\")", __FUNCTION__
,
1022 ((item
!= NULL
) ? item
->id
: "(null)"), token
, key
);
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;
1040 case MENUTOKEN_MENU
:
1042 return menuitem_predecessor2menuresult(
1043 item
->predecessor_id
, MENURESULT_CLOSE
);
1047 menuitem_reset_numeric(item
);
1049 return MENURESULT_NONE
;
1050 case MENUTOKEN_ENTER
:
1051 if ((extended
) || (str
[pos
] == '\0')) {
1053 /* The user completed his input */
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
) {
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
;
1075 if (item
->event_func
)
1076 item
->event_func(item
, MENUEVENT_UPDATE
);
1078 return menuitem_successor2menuresult(
1079 item
->successor_id
, MENURESULT_CLOSE
);
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
;
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) {
1100 str
[0] = (str
[0] == '-') ? '+' : '-';
1103 if (str
[pos
] >= '0' && str
[pos
] < '9') {
1105 } else if (str
[pos
] == '9') {
1107 } else if (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) {
1122 str
[0] = (str
[0] == '-') ? '+' : '-';
1125 if (str
[pos
] > '0' && str
[pos
] <= '9') {
1127 } else if (str
[pos
] == '0') {
1129 } else if (str
[pos
] == '\0') {
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 */
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])) {
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
)
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
);
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;
1199 case MENUTOKEN_MENU
:
1201 return menuitem_predecessor2menuresult(
1202 item
->predecessor_id
, MENURESULT_CLOSE
);
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
;
1220 strcpy(item
->data
.alpha
.value
, item
->data
.alpha
.edit_str
);
1223 if (item
->event_func
)
1224 item
->event_func(item
, MENUEVENT_UPDATE
);
1226 return menuitem_successor2menuresult(
1227 item
->successor_id
, MENURESULT_CLOSE
);
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
;
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];
1250 /* We should have a symbol from our list */
1251 p
= strchr(chars
, str
[pos
]);
1253 str
[pos
] = *(++p
); /* next symbol on list */
1254 /* Might be '\0' now */
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];
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 */
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 */
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
)) {
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
;
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;
1336 case MENUTOKEN_MENU
:
1338 return menuitem_predecessor2menuresult(
1339 item
->predecessor_id
, MENURESULT_CLOSE
);
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'
1352 memccpy(tmp
, str
, '\0', sizeof(tmp
));
1354 while (start
!= NULL
) {
1357 while ((*skip
== ' ') || ((*skip
== '0') && (skip
[1] != ipinfo
->sep
) && (skip
[1] != '\0')))
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
;
1373 strcpy(item
->data
.ip
.value
, tmp
);
1376 if (item
->event_func
)
1377 item
->event_func(item
, MENUEVENT_UPDATE
);
1379 return menuitem_successor2menuresult(
1380 item
->successor_id
, MENURESULT_CLOSE
);
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
;
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
)
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
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 */
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
)
1464 if (strings
!= NULL
) {
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
);
1476 LL_Push(list
, new_s
);
1478 /* Go to next string */
1481 /* Add last string */
1483 LL_Push(list
, new_s
);
1489 MenuItemType
menuitem_typename_to_type(char *name
)
1494 for (type
= 0; type
< NUM_ITEMTYPES
; type
++) {
1495 if (strcmp(menuitemtypenames
[type
], name
) == 0) {
1503 char *menuitem_type_to_typename(MenuItemType type
)
1505 return ((type
>= 0 && type
< NUM_ITEMTYPES
)
1506 ? menuitemtypenames
[type
]
1510 MenuEventType
menuitem_eventtypename_to_eventtype(char *name
)
1515 for (type
= 0; type
< NUM_EVENTTYPES
; type
++) {
1516 if (strcmp(menueventtypenames
[type
], name
) == 0) {
1524 char *menuitem_eventtype_to_eventtypename(MenuEventType type
)
1526 return ((type
>= 0 && type
< NUM_EVENTTYPES
)
1527 ? menueventtypenames
[type
]