1 /* $NetBSD: driver.c,v 1.8 2002/07/29 13:03:51 blymn Exp $ */ 2 3 /*- 4 * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@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 <menu.h> 30 #include <ctype.h> 31 #include <stdlib.h> 32 #include "internals.h" 33 34 /* 35 * The guts of the menu library. This function processes the character 36 * in c and performs actions based on the value of the character. If the 37 * character is a normal one then the driver attempts to match the character 38 * against the items. If the character is a recognised request then the 39 * request is processed by the driver, if the character is not a recognised 40 * request and is not printable then it assumed to be a user defined command. 41 */ 42 int 43 menu_driver(MENU *menu, int c) 44 { 45 int drv_top_row, drv_scroll, i, it, status = E_OK; 46 ITEM *drv_new_item; 47 48 i = 0; 49 50 if (menu == NULL) 51 return E_BAD_ARGUMENT; 52 if (menu->posted == 0) 53 return E_NOT_POSTED; 54 if (menu->items == NULL) 55 return E_NOT_CONNECTED; 56 if (*menu->items == NULL) 57 return E_NOT_CONNECTED; 58 if (menu->in_init == 1) 59 return E_BAD_STATE; 60 61 /* this one should never happen but just in case.... */ 62 if (menu->items[menu->cur_item] == NULL) 63 return E_SYSTEM_ERROR; 64 65 drv_new_item = menu->items[menu->cur_item]; 66 it = menu->cur_item; 67 drv_top_row = menu->top_row; 68 69 if ((c > REQ_BASE_NUM) && (c <= MAX_COMMAND)) { 70 /* is a known driver request - first check if the pattern 71 * buffer needs to be cleared, we do this on non-search 72 * type requests. 73 */ 74 if (! ((c == REQ_BACK_PATTERN) || (c == REQ_NEXT_MATCH) || 75 (c == REQ_PREV_MATCH))) { 76 if ((c == REQ_CLEAR_PATTERN) 77 && (menu->pattern == NULL)) 78 return E_REQUEST_DENIED; 79 free(menu->pattern); 80 menu->pattern = NULL; 81 menu->plen = 0; 82 menu->match_len = 0; 83 } 84 85 switch (c) { 86 case REQ_LEFT_ITEM: 87 drv_new_item = drv_new_item->left; 88 break; 89 case REQ_RIGHT_ITEM: 90 drv_new_item = drv_new_item->right; 91 break; 92 case REQ_UP_ITEM: 93 drv_new_item = drv_new_item->up; 94 break; 95 case REQ_DOWN_ITEM: 96 drv_new_item = drv_new_item->down; 97 break; 98 case REQ_SCR_ULINE: 99 if (drv_top_row == 0) 100 return E_REQUEST_DENIED; 101 drv_top_row--; 102 drv_new_item = drv_new_item->up; 103 break; 104 case REQ_SCR_DLINE: 105 drv_top_row++; 106 if ((drv_top_row + menu->rows - 1)> menu->item_rows) 107 return E_REQUEST_DENIED; 108 drv_new_item = drv_new_item->down; 109 break; 110 case REQ_SCR_DPAGE: 111 drv_scroll = menu->item_rows - menu->rows 112 - menu->top_row; 113 if (drv_scroll > menu->rows) { 114 drv_scroll = menu->rows; 115 } 116 117 if (drv_scroll <= 0) { 118 return E_REQUEST_DENIED; 119 } else { 120 drv_top_row += drv_scroll; 121 while (drv_scroll-- > 0) 122 drv_new_item = drv_new_item->down; 123 } 124 break; 125 case REQ_SCR_UPAGE: 126 if (menu->rows < menu->top_row) { 127 drv_scroll = menu->rows; 128 } else { 129 drv_scroll = menu->top_row; 130 } 131 if (drv_scroll == 0) 132 return E_REQUEST_DENIED; 133 134 drv_top_row -= drv_scroll; 135 while (drv_scroll-- > 0) 136 drv_new_item = drv_new_item->up; 137 break; 138 case REQ_FIRST_ITEM: 139 drv_new_item = menu->items[0]; 140 break; 141 case REQ_LAST_ITEM: 142 drv_new_item = menu->items[menu->item_count - 1]; 143 break; 144 case REQ_NEXT_ITEM: 145 if ((menu->cur_item + 1) >= menu->item_count) { 146 if ((menu->opts & O_NONCYCLIC) 147 == O_NONCYCLIC) { 148 return E_REQUEST_DENIED; 149 } else { 150 drv_new_item = menu->items[0]; 151 } 152 } else { 153 drv_new_item = 154 menu->items[menu->cur_item + 1]; 155 } 156 break; 157 case REQ_PREV_ITEM: 158 if (menu->cur_item == 0) { 159 if ((menu->opts & O_NONCYCLIC) 160 == O_NONCYCLIC) { 161 return E_REQUEST_DENIED; 162 } else { 163 drv_new_item = menu->items[ 164 menu->item_count - 1]; 165 } 166 } else { 167 drv_new_item = 168 menu->items[menu->cur_item - 1]; 169 } 170 break; 171 case REQ_TOGGLE_ITEM: 172 if ((menu->opts & (O_RADIO | O_ONEVALUE)) != 0) { 173 if ((menu->opts & O_RADIO) == O_RADIO) { 174 if ((drv_new_item->opts & O_SELECTABLE) 175 != O_SELECTABLE) 176 return E_NOT_SELECTABLE; 177 178 /* don't deselect selected item */ 179 if (drv_new_item->selected == 1) 180 return E_REQUEST_DENIED; 181 182 /* deselect all items */ 183 for (i = 0; i < menu->item_count; i++) { 184 if ((menu->items[i]->selected) && 185 (drv_new_item->index != i)) { 186 menu->items[i]->selected ^= 1; 187 _menui_draw_item(menu, 188 menu->items[i]->index); 189 } 190 } 191 192 /* turn on selected item */ 193 drv_new_item->selected ^= 1; 194 _menui_draw_item(menu, drv_new_item->index); 195 } else { 196 return E_REQUEST_DENIED; 197 } 198 } else { 199 if ((drv_new_item->opts 200 & O_SELECTABLE) == O_SELECTABLE) { 201 /* toggle select flag */ 202 drv_new_item->selected ^= 1; 203 /* update item in menu */ 204 _menui_draw_item(menu, 205 drv_new_item->index); 206 } else { 207 return E_NOT_SELECTABLE; 208 } 209 } 210 break; 211 case REQ_CLEAR_PATTERN: 212 /* this action is taken before the 213 case statement */ 214 break; 215 case REQ_BACK_PATTERN: 216 if (menu->pattern == NULL) 217 return E_REQUEST_DENIED; 218 219 if (menu->plen == 0) 220 return E_REQUEST_DENIED; 221 menu->pattern[menu->plen--] = '\0'; 222 break; 223 case REQ_NEXT_MATCH: 224 if (menu->pattern == NULL) 225 return E_REQUEST_DENIED; 226 227 status = _menui_match_pattern(menu, 0, 228 MATCH_NEXT_FORWARD, 229 &it); 230 drv_new_item = menu->items[it]; 231 break; 232 case REQ_PREV_MATCH: 233 if (menu->pattern == NULL) 234 return E_REQUEST_DENIED; 235 236 status = _menui_match_pattern(menu, 0, 237 MATCH_NEXT_REVERSE, 238 &it); 239 drv_new_item = menu->items[it]; 240 break; 241 } 242 } else if (c > MAX_COMMAND) { 243 /* must be a user command */ 244 return E_UNKNOWN_COMMAND; 245 } else if (isprint((unsigned char) c)) { 246 /* otherwise search items for the character. */ 247 status = _menui_match_pattern(menu, (unsigned char) c, 248 MATCH_FORWARD, &it); 249 drv_new_item = menu->items[it]; 250 251 /* update the position of the cursor if we are doing 252 * show match and the current item has not changed. If 253 * we don't do this here it won't get done since the 254 * display will not be updated due to the current item 255 * not changing. 256 */ 257 if ((drv_new_item->index == menu->cur_item) 258 && ((menu->opts & O_SHOWMATCH) == O_SHOWMATCH)) { 259 pos_menu_cursor(menu); 260 } 261 262 263 } else { 264 /* bad character */ 265 return E_BAD_ARGUMENT; 266 } 267 268 if (drv_new_item == NULL) 269 return E_REQUEST_DENIED; 270 271 if (drv_new_item->row < drv_top_row) drv_top_row = drv_new_item->row; 272 if (drv_new_item->row >= (drv_top_row + menu->rows)) 273 drv_top_row = drv_new_item->row - menu->rows + 1; 274 275 if ((drv_new_item->index != menu->cur_item) 276 || (drv_top_row != menu->top_row)) 277 _menui_goto_item(menu, drv_new_item, drv_top_row); 278 279 return status; 280 } 281 282 283 284 285 286