Home | History | Annotate | Line # | Download | only in libmenu
      1 /*	$NetBSD: menu.c,v 1.18 2012/12/30 12:27:09 blymn Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998-1999 Brett Lymn (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au)
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. The name of the author may not be used to endorse or promote products
     13  *    derived from this software without specific prior written permission
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  *
     26  *
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: menu.c,v 1.18 2012/12/30 12:27:09 blymn Exp $");
     31 
     32 #include <ctype.h>
     33 #include <menu.h>
     34 #include <string.h>
     35 #include <stdlib.h>
     36 #include "internals.h"
     37 
     38 MENU _menui_default_menu = {
     39 	16,         /* number of item rows that will fit in window */
     40         1,          /* number of columns of items that will fit in window */
     41 	0,          /* number of rows of items we have */
     42 	0,          /* number of columns of items we have */
     43         0,          /* current cursor row */
     44         0,          /* current cursor column */
     45         {NULL, 0},  /* mark string */
     46         {NULL, 0},  /* unmark string */
     47         O_ONEVALUE, /* menu options */
     48         NULL,       /* the pattern buffer */
     49 	0,          /* length of pattern buffer */
     50 	0,          /* the length of matched buffer */
     51         0,          /* is the menu posted? */
     52         A_REVERSE, /* menu foreground */
     53         A_NORMAL,   /* menu background */
     54         A_UNDERLINE,      /* unselectable menu item */
     55         ' ',        /* filler between name and description */
     56         NULL,       /* user defined pointer */
     57 	0,          /* top row of menu */
     58 	0,          /* widest item in the menu */
     59 	0,          /* the width of a menu column */
     60 	0,          /* number of items attached to the menu */
     61         NULL,       /* items in the menu */
     62         0,          /* current menu item */
     63 	0,          /* currently in a hook function */
     64         NULL,       /* function called when menu posted */
     65         NULL,       /* function called when menu is unposted */
     66         NULL,       /* function called when current item changes */
     67         NULL,       /* function called when current item changes */
     68         NULL,       /* the menu window */
     69 	NULL,       /* the menu subwindow */
     70 	NULL,       /* the window to write to */
     71 };
     72 
     73 
     74 
     75 /*
     76  * Set the menu mark character
     77  */
     78 int
     79 set_menu_mark(MENU *m, char *mark)
     80 {
     81 	MENU *menu = m;
     82 
     83 	if (m == NULL) menu = &_menui_default_menu;
     84 
     85           /* if there was an old mark string, free it first */
     86         if (menu->mark.string != NULL) free(menu->mark.string);
     87 
     88         if ((menu->mark.string = (char *) malloc(strlen(mark) + 1)) == NULL)
     89                 return E_SYSTEM_ERROR;
     90 
     91         strcpy(menu->mark.string, mark);
     92 	menu->mark.length = strlen(mark);
     93 
     94 	  /* max item size may have changed - recalculate. */
     95 	_menui_max_item_size(menu);
     96         return E_OK;
     97 }
     98 
     99 /*
    100  * Return the menu mark string for the menu.
    101  */
    102 char *
    103 menu_mark(MENU *menu)
    104 {
    105 	if (menu == NULL)
    106 		return _menui_default_menu.mark.string;
    107 	else
    108 		return menu->mark.string;
    109 }
    110 
    111 /*
    112  * Set the menu unmark character
    113  */
    114 int
    115 set_menu_unmark(MENU *m, char *mark)
    116 {
    117 	MENU *menu = m;
    118 
    119 	if (m == NULL) menu = &_menui_default_menu;
    120 
    121           /* if there was an old mark string, free it first */
    122         if (menu->unmark.string != NULL) free(menu->unmark.string);
    123 
    124         if ((menu->unmark.string = (char *) malloc(strlen(mark) + 1)) == NULL)
    125                 return E_SYSTEM_ERROR;
    126 
    127         strcpy(menu->unmark.string, mark);
    128 	menu->unmark.length = strlen(mark);
    129 	  /* max item size may have changed - recalculate. */
    130 	_menui_max_item_size(menu);
    131         return E_OK;
    132 }
    133 
    134 /*
    135  * Return the menu unmark string for the menu.
    136  */
    137 char *
    138 menu_unmark(MENU *menu)
    139 {
    140 	if (menu == NULL)
    141 		return _menui_default_menu.unmark.string;
    142 	else
    143 		return menu->unmark.string;
    144 }
    145 
    146 /*
    147  * Set the menu window to the window passed.
    148  */
    149 int
    150 set_menu_win(MENU *menu, WINDOW *win)
    151 {
    152 	if (menu == NULL) {
    153 		_menui_default_menu.menu_win = win;
    154 		_menui_default_menu.scrwin = win;
    155 	} else {
    156 		if (menu->posted == TRUE) {
    157 			return E_POSTED;
    158 		} else {
    159 			menu->menu_win = win;
    160 			menu->scrwin = win;
    161 		}
    162 	}
    163 
    164         return E_OK;
    165 }
    166 
    167 /*
    168  * Return the pointer to the menu window
    169  */
    170 WINDOW *
    171 menu_win(MENU *menu)
    172 {
    173 	if (menu == NULL)
    174 		return _menui_default_menu.menu_win;
    175 	else
    176 		return menu->menu_win;
    177 }
    178 
    179 /*
    180  * Set the menu subwindow for the menu.
    181  */
    182 int
    183 set_menu_sub(MENU *menu, WINDOW *sub)
    184 {
    185 	if (menu == NULL) {
    186 		_menui_default_menu.menu_subwin = sub;
    187 		_menui_default_menu.scrwin = sub;
    188 	} else {
    189 		if (menu->posted == TRUE)
    190 			return E_POSTED;
    191 
    192 		menu->menu_subwin = sub;
    193 		menu->scrwin = sub;
    194 	}
    195 
    196         return E_OK;
    197 }
    198 
    199 /*
    200  * Return the subwindow pointer for the menu
    201  */
    202 WINDOW *
    203 menu_sub(MENU *menu)
    204 {
    205 	if (menu == NULL)
    206 		return _menui_default_menu.menu_subwin;
    207 	else
    208 		return menu->menu_subwin;
    209 }
    210 
    211 /*
    212  * Set the maximum number of rows and columns of items that may be displayed.
    213  */
    214 int
    215 set_menu_format(MENU *param_menu, int rows, int cols)
    216 {
    217 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    218 
    219         menu->rows = rows;
    220         menu->cols = cols;
    221 
    222 	if (menu->items != NULL)
    223 		  /* recalculate the item neighbours */
    224 		return _menui_stitch_items(menu);
    225 
    226 	return E_OK;
    227 }
    228 
    229 /*
    230  * Return the max number of rows and cols that may be displayed.
    231  */
    232 void
    233 menu_format(MENU *param_menu, int *rows, int *cols)
    234 {
    235 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    236 
    237         *rows = menu->rows;
    238         *cols = menu->cols;
    239 }
    240 
    241 /*
    242  * Set the user defined function to call when a menu is posted.
    243  */
    244 int
    245 set_menu_init(MENU *menu, Menu_Hook func)
    246 {
    247 	if (menu == NULL)
    248 		_menui_default_menu.menu_init = func;
    249 	else
    250 		menu->menu_init = func;
    251         return E_OK;
    252 }
    253 
    254 /*
    255  * Return the pointer to the menu init function.
    256  */
    257 Menu_Hook
    258 menu_init(MENU *menu)
    259 {
    260 	if (menu == NULL)
    261 		return _menui_default_menu.menu_init;
    262 	else
    263 		return menu->menu_init;
    264 }
    265 
    266 /*
    267  * Set the user defined function called when a menu is unposted.
    268  */
    269 int
    270 set_menu_term(MENU *menu, Menu_Hook func)
    271 {
    272 	if (menu == NULL)
    273 		_menui_default_menu.menu_term = func;
    274 	else
    275 		menu->menu_term = func;
    276         return E_OK;
    277 }
    278 
    279 /*
    280  * Return the user defined menu termination function pointer.
    281  */
    282 Menu_Hook
    283 menu_term(MENU *menu)
    284 {
    285 	if (menu == NULL)
    286 		return _menui_default_menu.menu_term;
    287 	else
    288 		return menu->menu_term;
    289 }
    290 
    291 /*
    292  * Return the current menu options set.
    293  */
    294 OPTIONS
    295 menu_opts(MENU *menu)
    296 {
    297 	if (menu == NULL)
    298 		return _menui_default_menu.opts;
    299 	else
    300 		return menu->opts;
    301 }
    302 
    303 /*
    304  * Set the menu options to the given options.
    305  */
    306 int
    307 set_menu_opts(MENU *param_menu, OPTIONS opts)
    308 {
    309 	int i, seen;
    310 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    311 	OPTIONS old_opts = menu->opts;
    312 
    313         menu->opts = opts;
    314 
    315 	  /*
    316 	   * If the radio option is selected then make sure only one
    317 	   * item is actually selected in the items.
    318 	   */
    319 	if (((opts & O_RADIO) == O_RADIO) && (menu->items != NULL) &&
    320 	    (menu->items[0] != NULL)) {
    321 		seen = 0;
    322 		for (i = 0; i < menu->item_count; i++) {
    323 			if (menu->items[i]->selected == 1) {
    324 				if (seen == 0) {
    325 					seen = 1;
    326 				} else {
    327 					menu->items[i]->selected = 0;
    328 				}
    329 			}
    330 		}
    331 
    332 		  /* if none selected, select the first item */
    333 		if (seen == 0)
    334 			menu->items[0]->selected = 1;
    335 	}
    336 
    337  	if ((menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    338 		  /* changed menu layout - need to recalc neighbours */
    339 		_menui_stitch_items(menu);
    340 
    341         return E_OK;
    342 }
    343 
    344 /*
    345  * Turn on the options in menu given by opts.
    346  */
    347 int
    348 menu_opts_on(MENU *param_menu, OPTIONS opts)
    349 {
    350 	int i, seen;
    351 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    352 	OPTIONS old_opts = menu->opts;
    353 
    354         menu->opts |= opts;
    355 
    356 	  /*
    357 	   * If the radio option is selected then make sure only one
    358 	   * item is actually selected in the items.
    359 	   */
    360 	if (((opts & O_RADIO) == O_RADIO) && (menu->items != NULL) &&
    361 	    (menu->items[0] != NULL)) {
    362 		seen = 0;
    363 		for (i = 0; i < menu->item_count; i++) {
    364 			if (menu->items[i]->selected == 1) {
    365 				if (seen == 0) {
    366 					seen = 1;
    367 				} else {
    368 					menu->items[i]->selected = 0;
    369 				}
    370 			}
    371 		}
    372 		  /* if none selected then select the top item */
    373 		if (seen == 0)
    374 			menu->items[0]->selected = 1;
    375 	}
    376 
    377 	if ((menu->items != NULL) &&
    378 	    (menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    379 		  /* changed menu layout - need to recalc neighbours */
    380 		_menui_stitch_items(menu);
    381 
    382         return E_OK;
    383 }
    384 
    385 /*
    386  * Turn off the menu options given in opts.
    387  */
    388 int
    389 menu_opts_off(MENU *param_menu, OPTIONS opts)
    390 {
    391 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    392 	OPTIONS old_opts = menu->opts;
    393 
    394         menu->opts &= ~(opts);
    395 
    396 	if ((menu->items != NULL ) &&
    397 	    (menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    398 		  /* changed menu layout - need to recalc neighbours */
    399 		_menui_stitch_items(menu);
    400 
    401         return E_OK;
    402 }
    403 
    404 /*
    405  * Return the menu pattern buffer.
    406  */
    407 char *
    408 menu_pattern(MENU *menu)
    409 {
    410 	if (menu == NULL)
    411 		return _menui_default_menu.pattern;
    412 	else
    413 		return menu->pattern;
    414 }
    415 
    416 /*
    417  * Set the menu pattern buffer to pat and attempt to match the pattern in
    418  * the item list.
    419  */
    420 int
    421 set_menu_pattern(MENU *param_menu, char *pat)
    422 {
    423 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    424 	char *p = pat;
    425 
    426 	  /* check pattern is all printable characters */
    427 	while (*p)
    428 		if (!isprint((unsigned char) *p++)) return E_BAD_ARGUMENT;
    429 
    430         if ((menu->pattern = (char *) realloc(menu->pattern,
    431                                      sizeof(char) * strlen(pat) + 1)) == NULL)
    432                 return E_SYSTEM_ERROR;
    433 
    434         strcpy(menu->pattern, pat);
    435 	menu->plen = strlen(pat);
    436 
    437           /* search item list for pat here */
    438 	return _menui_match_items(menu, MATCH_FORWARD, &menu->cur_item);
    439 }
    440 
    441 /*
    442  * Allocate a new menu structure and fill it in.
    443  */
    444 MENU *
    445 new_menu(ITEM **items)
    446 {
    447         MENU *the_menu;
    448         char mark[2];
    449 
    450         if ((the_menu = (MENU *)malloc(sizeof(MENU))) == NULL)
    451                 return NULL;
    452 
    453           /* copy the defaults */
    454 	(void)memcpy(the_menu, &_menui_default_menu, sizeof(MENU));
    455 
    456 	  /* set a default window if none already set. */
    457 	if (the_menu->menu_win == NULL)
    458 		the_menu->scrwin = stdscr;
    459 
    460 	  /* make a private copy of the mark string */
    461 	if (_menui_default_menu.mark.string != NULL) {
    462 		if ((the_menu->mark.string =
    463 		     (char *) malloc((unsigned) _menui_default_menu.mark.length + 1))
    464 		    == NULL) {
    465 			free(the_menu);
    466 			return NULL;
    467 		}
    468 
    469 		strlcpy(the_menu->mark.string, _menui_default_menu.mark.string,
    470 			(unsigned) _menui_default_menu.mark.length + 1);
    471 	}
    472 
    473 	  /* make a private copy of the unmark string too */
    474 	if (_menui_default_menu.unmark.string != NULL) {
    475 		if ((the_menu->unmark.string =
    476 		     (char *) malloc((unsigned) _menui_default_menu.unmark.length + 1))
    477 		    == NULL) {
    478 			free(the_menu);
    479 			return NULL;
    480 		}
    481 
    482 		strlcpy(the_menu->unmark.string,
    483 			_menui_default_menu.unmark.string,
    484 			(unsigned) _menui_default_menu.unmark.length+ 1 );
    485 	}
    486 
    487 	/* default mark needs to be set */
    488 	mark[0] = '-';
    489 	mark[1] = '\0';
    490 
    491 	set_menu_mark(the_menu, mark);
    492 
    493           /* now attach the items, if any */
    494         if (items != NULL) {
    495 		if(set_menu_items(the_menu, items) < 0) {
    496 			if (the_menu->mark.string != NULL)
    497 				free(the_menu->mark.string);
    498 			if (the_menu->unmark.string != NULL)
    499 				free(the_menu->unmark.string);
    500 			free(the_menu);
    501 			return NULL;
    502 		}
    503 	}
    504 
    505 	return the_menu;
    506 }
    507 
    508 /*
    509  * Free up storage allocated to the menu object and destroy it.
    510  */
    511 int
    512 free_menu(MENU *menu)
    513 {
    514 	int i;
    515 
    516 	if (menu == NULL)
    517 		return E_BAD_ARGUMENT;
    518 
    519 	if (menu->posted != 0)
    520 		return E_POSTED;
    521 
    522 	if (menu->pattern != NULL)
    523 		free(menu->pattern);
    524 
    525 	if (menu->mark.string != NULL)
    526 		free(menu->mark.string);
    527 
    528 	if (menu->items != NULL) {
    529 		  /* disconnect the items from this menu */
    530 		for (i = 0; i < menu->item_count; i++) {
    531 			menu->items[i]->parent = NULL;
    532 		}
    533 	}
    534 
    535 	free(menu);
    536 	return E_OK;
    537 }
    538 
    539 /*
    540  * Calculate the minimum window size for the menu.
    541  */
    542 int
    543 scale_menu(MENU *param_menu, int *rows, int *cols)
    544 {
    545 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    546 
    547 	if (menu->items == NULL)
    548 		return E_BAD_ARGUMENT;
    549 
    550 	  /* calculate the max item size */
    551 	_menui_max_item_size(menu);
    552 
    553 	*rows = menu->rows;
    554 	*cols = menu->cols * menu->max_item_width;
    555 
    556 	  /*
    557 	   * allow for spacing between columns...
    558 	   */
    559 	*cols += (menu->cols - 1);
    560 
    561 	return E_OK;
    562 }
    563 
    564 /*
    565  * Set the menu item list to the one given.
    566  */
    567 int
    568 set_menu_items(MENU *param_menu, ITEM **items)
    569 {
    570 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    571 	int i, new_count = 0, sel_count = 0;
    572 
    573 	  /* don't change if menu is posted */
    574 	if (menu->posted == 1)
    575 		return E_POSTED;
    576 
    577 	  /* count the new items and validate none are connected already */
    578 	while (items[new_count] != NULL) {
    579 		if ((items[new_count]->parent != NULL) &&
    580 		    (items[new_count]->parent != menu))
    581 			return E_CONNECTED;
    582 		if (items[new_count]->selected == 1)
    583 			sel_count++;
    584 		new_count++;
    585 	}
    586 
    587 	  /*
    588 	   * don't allow multiple selected items if menu is radio
    589 	   * button style.
    590 	   */
    591 	if (((menu->opts & O_RADIO) == O_RADIO) &&
    592 	    (sel_count > 1))
    593 		return E_BAD_ARGUMENT;
    594 
    595 	  /* if there were items connected then disconnect them. */
    596 	if (menu->items != NULL) {
    597 		for (i = 0; i < menu->item_count; i++) {
    598 			menu->items[i]->parent = NULL;
    599 			menu->items[i]->index = -1;
    600 		}
    601 	}
    602 
    603 	menu->item_count = new_count;
    604 
    605 	  /* connect the new items to the menu */
    606 	for (i = 0; i < new_count; i++) {
    607 		items[i]->parent = menu;
    608 		items[i]->index = i;
    609 	}
    610 
    611 	menu->items = items;
    612 	menu->cur_item = 0; /* reset current item just in case */
    613 	menu->top_row = 0; /* and the top row too */
    614 	if (menu->pattern != NULL) { /* and the pattern buffer....sigh */
    615 		free(menu->pattern);
    616 		menu->plen = 0;
    617 		menu->match_len = 0;
    618 	}
    619 
    620 	  /*
    621 	   * make sure at least one item is selected on a radio
    622 	   * button style menu.
    623 	   */
    624 	if (((menu->opts & O_RADIO) == O_RADIO) && (sel_count == 0))
    625 		menu->items[0]->selected = 1;
    626 
    627 
    628 	_menui_stitch_items(menu); /* recalculate the item neighbours */
    629 
    630 	return E_OK;
    631 }
    632 
    633 /*
    634  * Return the pointer to the menu items array.
    635  */
    636 ITEM **
    637 menu_items(MENU *menu)
    638 {
    639 	if (menu == NULL)
    640 		return _menui_default_menu.items;
    641 	else
    642 		return menu->items;
    643 }
    644 
    645 /*
    646  * Return the count of items connected to the menu
    647  */
    648 int
    649 item_count(MENU *menu)
    650 {
    651 	if (menu == NULL)
    652 		return _menui_default_menu.item_count;
    653 	else
    654 		return menu->item_count;
    655 }
    656 
    657 /*
    658  * Set the menu top row to be the given row.  The current item becomes the
    659  * leftmost item on that row in the menu.
    660  */
    661 int
    662 set_top_row(MENU *param_menu, int row)
    663 {
    664 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    665 	int i, cur_item, state = E_SYSTEM_ERROR;
    666 
    667 	if (row > menu->item_rows)
    668 		return E_BAD_ARGUMENT;
    669 
    670 	if (menu->items == NULL)
    671 		return E_NOT_CONNECTED;
    672 
    673 	if (menu->in_init == 1)
    674 		return E_BAD_STATE;
    675 
    676 	cur_item = 0;
    677 
    678 	for (i = 0; i < menu->item_count; i++) {
    679 		  /* search for first item that matches row - this will be
    680 		     the current item. */
    681 		if (row == menu->items[i]->row) {
    682 			cur_item = i;
    683 			state = E_OK;
    684 			break; /* found what we want - no need to go further */
    685 		}
    686 	}
    687 
    688 	menu->in_init = 1; /* just in case we call the init/term routines */
    689 
    690 	if (menu->posted == 1) {
    691 		if (menu->menu_term != NULL)
    692 			menu->menu_term(menu);
    693 		if (menu->item_term != NULL)
    694 			menu->item_term(menu);
    695 	}
    696 
    697 	menu->cur_item = cur_item;
    698 	menu->top_row = row;
    699 
    700 	if (menu->posted == 1) {
    701 		if (menu->menu_init != NULL)
    702 			menu->menu_init(menu);
    703 		if (menu->item_init != NULL)
    704 			menu->item_init(menu);
    705 	}
    706 
    707 	menu->in_init = 0;
    708 
    709 	  /* this should always be E_OK unless we are really screwed up */
    710 	return state;
    711 }
    712 
    713 /*
    714  * Return the current top row number.
    715  */
    716 int
    717 top_row(MENU *param_menu)
    718 {
    719 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    720 
    721 	if (menu->items == NULL)
    722 		return E_NOT_CONNECTED;
    723 
    724 	return menu->top_row;
    725 }
    726 
    727 /*
    728  * Position the cursor at the correct place in the menu.
    729  *
    730  */
    731 int
    732 pos_menu_cursor(MENU *menu)
    733 {
    734 	int movx, maxmark;
    735 
    736 	if (menu == NULL)
    737 		return E_BAD_ARGUMENT;
    738 
    739 	maxmark = max(menu->mark.length, menu->unmark.length);
    740 	movx = maxmark + (menu->items[menu->cur_item]->col
    741 		* (menu->col_width + 1));
    742 
    743 	if (menu->match_len > 0)
    744 		movx += menu->match_len - 1;
    745 
    746 	wmove(menu->scrwin,
    747 	      menu->items[menu->cur_item]->row - menu->top_row, movx);
    748 
    749 	return E_OK;
    750 }
    751