1 1.17 christos /* $NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $ */ 2 1.1 blymn 3 1.1 blymn /*- 4 1.3 blymn * Copyright (c) 1998-1999 Brett Lymn (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au) 5 1.1 blymn * All rights reserved. 6 1.1 blymn * 7 1.1 blymn * Redistribution and use in source and binary forms, with or without 8 1.1 blymn * modification, are permitted provided that the following conditions 9 1.1 blymn * are met: 10 1.1 blymn * 1. Redistributions of source code must retain the above copyright 11 1.1 blymn * notice, this list of conditions and the following disclaimer. 12 1.1 blymn * 2. The name of the author may not be used to endorse or promote products 13 1.8 wiz * derived from this software without specific prior written permission 14 1.1 blymn * 15 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 1.1 blymn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 1.1 blymn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 1.1 blymn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 1.1 blymn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 1.1 blymn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 1.1 blymn * 26 1.1 blymn * 27 1.1 blymn */ 28 1.12 lukem 29 1.12 lukem #include <sys/cdefs.h> 30 1.17 christos __RCSID("$NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $"); 31 1.1 blymn 32 1.1 blymn #include <menu.h> 33 1.1 blymn #include <ctype.h> 34 1.1 blymn #include <stdlib.h> 35 1.1 blymn #include <string.h> 36 1.1 blymn #include "internals.h" 37 1.1 blymn 38 1.1 blymn /* internal function prototypes */ 39 1.1 blymn static void 40 1.15 blymn _menui_calc_neighbours(MENU *menu, int item_no); 41 1.6 blymn static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item); 42 1.1 blymn 43 1.1 blymn /* 44 1.1 blymn * Link all the menu items together to speed up navigation. We need 45 1.1 blymn * to calculate the widest item entry, then work out how many columns 46 1.13 christos * of items the window will accommodate and then how many rows there will 47 1.1 blymn * be. Once the layout is determined the neighbours of each item is 48 1.1 blymn * calculated and the item structures updated. 49 1.1 blymn */ 50 1.1 blymn int 51 1.6 blymn _menui_stitch_items(MENU *menu) 52 1.1 blymn { 53 1.15 blymn int i, row_major; 54 1.1 blymn 55 1.1 blymn row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR); 56 1.1 blymn 57 1.1 blymn if (menu->posted == 1) 58 1.1 blymn return E_POSTED; 59 1.1 blymn if (menu->items == NULL) 60 1.1 blymn return E_BAD_ARGUMENT; 61 1.1 blymn 62 1.15 blymn menu->item_rows = menu->item_count / menu->cols; 63 1.15 blymn menu->item_cols = menu->cols; 64 1.15 blymn if (menu->item_count > (menu->item_rows * menu->item_cols)) 65 1.15 blymn menu->item_rows += 1; 66 1.1 blymn 67 1.3 blymn _menui_max_item_size(menu); 68 1.1 blymn 69 1.1 blymn for (i = 0; i < menu->item_count; i++) { 70 1.1 blymn /* fill in the row and column value of the item */ 71 1.1 blymn if (row_major) { 72 1.1 blymn menu->items[i]->row = i / menu->item_cols; 73 1.1 blymn menu->items[i]->col = i % menu->item_cols; 74 1.1 blymn } else { 75 1.1 blymn menu->items[i]->row = i % menu->item_rows; 76 1.1 blymn menu->items[i]->col = i / menu->item_rows; 77 1.1 blymn } 78 1.15 blymn 79 1.15 blymn _menui_calc_neighbours(menu, i); 80 1.1 blymn } 81 1.14 blymn 82 1.1 blymn return E_OK; 83 1.1 blymn } 84 1.1 blymn 85 1.1 blymn /* 86 1.15 blymn * Calculate the neighbours for an item in menu. 87 1.1 blymn */ 88 1.1 blymn static void 89 1.15 blymn _menui_calc_neighbours(MENU *menu, int item_no) 90 1.1 blymn { 91 1.15 blymn int neighbour, cycle, row_major, edge; 92 1.15 blymn ITEM *item; 93 1.15 blymn 94 1.15 blymn row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR); 95 1.15 blymn cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC); 96 1.15 blymn item = menu->items[item_no]; 97 1.1 blymn 98 1.15 blymn if (menu->item_rows < 2) { 99 1.1 blymn if (cycle) { 100 1.15 blymn item->up = item; 101 1.15 blymn item->down = item; 102 1.1 blymn } else { 103 1.15 blymn item->up = NULL; 104 1.15 blymn item->down = NULL; 105 1.1 blymn } 106 1.1 blymn } else { 107 1.15 blymn 108 1.15 blymn /* up */ 109 1.15 blymn if (menu->item_cols < 2) { 110 1.15 blymn if (item_no == 0) { 111 1.15 blymn if (cycle) 112 1.15 blymn item->up = 113 1.15 blymn menu->items[menu->item_count - 1]; 114 1.15 blymn else 115 1.15 blymn item->up = NULL; 116 1.1 blymn } else 117 1.15 blymn item->up = menu->items[item_no - 1]; 118 1.15 blymn } else { 119 1.15 blymn edge = 0; 120 1.15 blymn if (row_major) { 121 1.15 blymn if (item->row == 0) { 122 1.15 blymn neighbour = 123 1.15 blymn (menu->item_rows - 1) * menu->item_cols 124 1.15 blymn + item->col; 125 1.15 blymn if (neighbour >= menu->item_count) 126 1.15 blymn neighbour -= menu->item_cols; 127 1.15 blymn edge = 1; 128 1.15 blymn } else 129 1.15 blymn neighbour = item_no - menu->item_cols; 130 1.15 blymn } else { 131 1.15 blymn if (item->row == 0) { 132 1.15 blymn neighbour = menu->item_rows * item->col 133 1.15 blymn + menu->item_rows - 1; 134 1.15 blymn if (neighbour >= menu->item_count) 135 1.15 blymn neighbour = menu->item_count - 1; 136 1.15 blymn edge = 1; 137 1.15 blymn } else 138 1.15 blymn neighbour = item_no - 1; 139 1.15 blymn } 140 1.15 blymn 141 1.14 blymn 142 1.15 blymn item->up = menu->items[neighbour]; 143 1.15 blymn if ((!cycle) && (edge == 1)) 144 1.15 blymn item->up = NULL; 145 1.15 blymn } 146 1.14 blymn 147 1.15 blymn /* Down */ 148 1.15 blymn if (menu->item_cols < 2) { 149 1.15 blymn if (item_no == (menu->item_count - 1)) { 150 1.15 blymn if (cycle) 151 1.15 blymn item->down = menu->items[0]; 152 1.15 blymn else 153 1.15 blymn item->down = NULL; 154 1.15 blymn } else 155 1.15 blymn item->down = menu->items[item_no + 1]; 156 1.15 blymn } else { 157 1.15 blymn edge = 0; 158 1.15 blymn if (row_major) { 159 1.15 blymn if (item->row == menu->item_rows - 1) { 160 1.15 blymn neighbour = item->col; 161 1.15 blymn edge = 1; 162 1.15 blymn } else { 163 1.15 blymn neighbour = item_no + menu->item_cols; 164 1.15 blymn if (neighbour >= menu->item_count) { 165 1.15 blymn neighbour = item->col; 166 1.15 blymn edge = 1; 167 1.15 blymn } 168 1.15 blymn } 169 1.15 blymn } else { 170 1.15 blymn if (item->row == menu->item_rows - 1) { 171 1.15 blymn neighbour = item->col * menu->item_rows; 172 1.15 blymn edge = 1; 173 1.1 blymn } else { 174 1.15 blymn neighbour = item_no + 1; 175 1.15 blymn if (neighbour >= menu->item_count) { 176 1.15 blymn neighbour = item->col 177 1.15 blymn * menu->item_rows; 178 1.15 blymn edge = 1; 179 1.15 blymn } 180 1.15 blymn } 181 1.15 blymn } 182 1.1 blymn 183 1.15 blymn item->down = menu->items[neighbour]; 184 1.15 blymn if ((!cycle) && (edge == 1)) 185 1.15 blymn item->down = NULL; 186 1.15 blymn } 187 1.1 blymn } 188 1.14 blymn 189 1.15 blymn if (menu->item_cols < 2) { 190 1.1 blymn if (cycle) { 191 1.15 blymn item->left = item; 192 1.15 blymn item->right = item; 193 1.15 blymn } else { 194 1.15 blymn item->left = NULL; 195 1.15 blymn item->right = NULL; 196 1.15 blymn } 197 1.15 blymn } else { 198 1.15 blymn /* left */ 199 1.15 blymn if (menu->item_rows < 2) { 200 1.15 blymn if (item_no == 0) { 201 1.15 blymn if (cycle) 202 1.15 blymn item->left = 203 1.15 blymn menu->items[menu->item_count - 1]; 204 1.15 blymn else 205 1.15 blymn item->left = NULL; 206 1.15 blymn } else 207 1.15 blymn item->left = menu->items[item_no - 1]; 208 1.15 blymn } else { 209 1.15 blymn edge = 0; 210 1.15 blymn if (row_major) { 211 1.15 blymn if (item->col == 0) { 212 1.15 blymn neighbour = item_no + menu->cols - 1; 213 1.15 blymn if (neighbour >= menu->item_count) 214 1.15 blymn neighbour = menu->item_count - 1; 215 1.15 blymn edge = 1; 216 1.15 blymn } else 217 1.15 blymn neighbour = item_no - 1; 218 1.1 blymn } else { 219 1.15 blymn if (item->col == 0) { 220 1.15 blymn neighbour = menu->item_rows 221 1.15 blymn * (menu->item_cols - 1) + item->row; 222 1.15 blymn if (neighbour >= menu->item_count) 223 1.15 blymn neighbour -= menu->item_rows; 224 1.15 blymn edge = 1; 225 1.1 blymn } else 226 1.15 blymn neighbour = item_no - menu->item_rows; 227 1.1 blymn } 228 1.14 blymn 229 1.15 blymn item->left = menu->items[neighbour]; 230 1.15 blymn if ((!cycle) && (edge == 1)) 231 1.15 blymn item->left = NULL; 232 1.15 blymn } 233 1.15 blymn 234 1.15 blymn /* right */ 235 1.15 blymn if (menu->item_rows < 2) { 236 1.15 blymn if (item_no == menu->item_count - 1) { 237 1.15 blymn if (cycle) 238 1.15 blymn item->right = menu->items[0]; 239 1.15 blymn else 240 1.15 blymn item->right = NULL; 241 1.15 blymn } else 242 1.15 blymn item->right = menu->items[item_no + 1]; 243 1.15 blymn } else { 244 1.15 blymn edge = 0; 245 1.15 blymn if (row_major) { 246 1.15 blymn if (item->col == menu->item_cols - 1) { 247 1.15 blymn neighbour = item_no - menu->item_cols 248 1.15 blymn + 1; 249 1.15 blymn edge = 1; 250 1.15 blymn } else if (item_no == menu->item_count - 1) { 251 1.15 blymn neighbour = item->row * menu->item_cols; 252 1.15 blymn edge = 1; 253 1.15 blymn } else 254 1.15 blymn neighbour = item_no + 1; 255 1.1 blymn } else { 256 1.15 blymn if (item->col == menu->item_cols - 1) { 257 1.15 blymn neighbour = item->row; 258 1.15 blymn edge = 1; 259 1.15 blymn } else { 260 1.15 blymn neighbour = item_no + menu->item_rows; 261 1.15 blymn if (neighbour >= menu->item_count) { 262 1.15 blymn neighbour = item->row; 263 1.15 blymn edge = 1; 264 1.1 blymn } 265 1.15 blymn } 266 1.1 blymn } 267 1.15 blymn 268 1.15 blymn item->right = menu->items[neighbour]; 269 1.15 blymn if ((!cycle) && (edge == 1)) 270 1.15 blymn item->right = NULL; 271 1.15 blymn } 272 1.1 blymn } 273 1.1 blymn } 274 1.1 blymn 275 1.1 blymn /* 276 1.1 blymn * Goto the item pointed to by item and adjust the menu structure 277 1.1 blymn * accordingly. Call the term and init functions if required. 278 1.1 blymn */ 279 1.1 blymn int 280 1.6 blymn _menui_goto_item(MENU *menu, ITEM *item, int new_top_row) 281 1.1 blymn { 282 1.1 blymn int old_top_row = menu->top_row, old_cur_item = menu->cur_item; 283 1.14 blymn 284 1.1 blymn /* If we get a null then the menu is not cyclic so deny request */ 285 1.1 blymn if (item == NULL) 286 1.1 blymn return E_REQUEST_DENIED; 287 1.1 blymn 288 1.1 blymn menu->in_init = 1; 289 1.3 blymn if (menu->top_row != new_top_row) { 290 1.1 blymn if ((menu->posted == 1) && (menu->menu_term != NULL)) 291 1.1 blymn menu->menu_term(menu); 292 1.3 blymn menu->top_row = new_top_row; 293 1.1 blymn 294 1.1 blymn if ((menu->posted == 1) && (menu->menu_init != NULL)) 295 1.1 blymn menu->menu_init(menu); 296 1.1 blymn } 297 1.14 blymn 298 1.1 blymn /* this looks like wasted effort but it can happen.... */ 299 1.1 blymn if (menu->cur_item != item->index) { 300 1.1 blymn 301 1.1 blymn if ((menu->posted == 1) && (menu->item_term != NULL)) 302 1.1 blymn menu->item_term(menu); 303 1.1 blymn 304 1.1 blymn menu->cur_item = item->index; 305 1.1 blymn menu->cur_row = item->row; 306 1.1 blymn menu->cur_col = item->col; 307 1.1 blymn 308 1.1 blymn if (menu->posted == 1) 309 1.3 blymn _menui_redraw_menu(menu, old_top_row, old_cur_item); 310 1.14 blymn 311 1.1 blymn if ((menu->posted == 1) && (menu->item_init != NULL)) 312 1.1 blymn menu->item_init(menu); 313 1.14 blymn 314 1.1 blymn } 315 1.1 blymn 316 1.1 blymn menu->in_init = 0; 317 1.1 blymn return E_OK; 318 1.1 blymn } 319 1.1 blymn 320 1.1 blymn /* 321 1.1 blymn * Attempt to match items with the pattern buffer in the direction given 322 1.1 blymn * by iterating over the menu items. If a match is found return E_OK 323 1.1 blymn * otherwise return E_NO_MATCH 324 1.1 blymn */ 325 1.1 blymn int 326 1.6 blymn _menui_match_items(MENU *menu, int direction, int *item_matched) 327 1.1 blymn { 328 1.1 blymn int i, caseless; 329 1.1 blymn 330 1.1 blymn caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE); 331 1.14 blymn 332 1.1 blymn i = menu->cur_item; 333 1.1 blymn if (direction == MATCH_NEXT_FORWARD) { 334 1.1 blymn if (++i >= menu->item_count) i = 0; 335 1.1 blymn } else if (direction == MATCH_NEXT_REVERSE) { 336 1.1 blymn if (--i < 0) i = menu->item_count - 1; 337 1.1 blymn } 338 1.1 blymn 339 1.14 blymn 340 1.1 blymn do { 341 1.1 blymn if (menu->items[i]->name.length >= menu->plen) { 342 1.1 blymn /* no chance if pattern is longer */ 343 1.1 blymn if (caseless) { 344 1.1 blymn if (strncasecmp(menu->items[i]->name.string, 345 1.1 blymn menu->pattern, 346 1.3 blymn (size_t) menu->plen) == 0) { 347 1.1 blymn *item_matched = i; 348 1.1 blymn menu->match_len = menu->plen; 349 1.1 blymn return E_OK; 350 1.1 blymn } 351 1.1 blymn } else { 352 1.1 blymn if (strncmp(menu->items[i]->name.string, 353 1.3 blymn menu->pattern, 354 1.3 blymn (size_t) menu->plen) == 0) { 355 1.1 blymn *item_matched = i; 356 1.1 blymn menu->match_len = menu->plen; 357 1.1 blymn return E_OK; 358 1.1 blymn } 359 1.1 blymn } 360 1.1 blymn } 361 1.14 blymn 362 1.1 blymn if ((direction == MATCH_FORWARD) || 363 1.1 blymn (direction == MATCH_NEXT_FORWARD)) { 364 1.1 blymn if (++i >= menu->item_count) i = 0; 365 1.1 blymn } else { 366 1.1 blymn if (--i <= 0) i = menu->item_count - 1; 367 1.1 blymn } 368 1.1 blymn } while (i != menu->cur_item); 369 1.1 blymn 370 1.1 blymn menu->match_len = 0; /* match did not succeed - kill the match len. */ 371 1.1 blymn return E_NO_MATCH; 372 1.1 blymn } 373 1.1 blymn 374 1.1 blymn /* 375 1.14 blymn * Attempt to match the pattern buffer against the items. If c is a 376 1.1 blymn * printable character then add it to the pattern buffer prior to 377 1.1 blymn * performing the match. Direction determines the direction of matching. 378 1.1 blymn * If the match is successful update the item_matched variable with the 379 1.1 blymn * index of the item that matched the pattern. 380 1.1 blymn */ 381 1.1 blymn int 382 1.7 itohy _menui_match_pattern(MENU *menu, int c, int direction, int *item_matched) 383 1.1 blymn { 384 1.1 blymn if (menu == NULL) 385 1.1 blymn return E_BAD_ARGUMENT; 386 1.1 blymn if (menu->items == NULL) 387 1.1 blymn return E_BAD_ARGUMENT; 388 1.1 blymn if (*menu->items == NULL) 389 1.1 blymn return E_BAD_ARGUMENT; 390 1.1 blymn 391 1.1 blymn if (isprint(c)) { 392 1.1 blymn /* add char to buffer - first allocate room for it */ 393 1.1 blymn if ((menu->pattern = (char *) 394 1.1 blymn realloc(menu->pattern, 395 1.1 blymn menu->plen + sizeof(char) + 396 1.1 blymn ((menu->plen > 0)? 0 : 1))) 397 1.1 blymn == NULL) 398 1.1 blymn return E_SYSTEM_ERROR; 399 1.1 blymn menu->pattern[menu->plen] = c; 400 1.1 blymn menu->pattern[++menu->plen] = '\0'; 401 1.1 blymn 402 1.1 blymn /* there is no chance of a match if pattern is longer 403 1.1 blymn than all the items */ 404 1.1 blymn if (menu->plen >= menu->max_item_width) { 405 1.1 blymn menu->pattern[--menu->plen] = '\0'; 406 1.1 blymn return E_NO_MATCH; 407 1.1 blymn } 408 1.14 blymn 409 1.3 blymn if (_menui_match_items(menu, direction, 410 1.1 blymn item_matched) == E_NO_MATCH) { 411 1.1 blymn menu->pattern[--menu->plen] = '\0'; 412 1.1 blymn return E_NO_MATCH; 413 1.1 blymn } else 414 1.1 blymn return E_OK; 415 1.1 blymn } else { 416 1.3 blymn if (_menui_match_items(menu, direction, 417 1.1 blymn item_matched) == E_OK) { 418 1.1 blymn return E_OK; 419 1.1 blymn } else { 420 1.1 blymn return E_NO_MATCH; 421 1.1 blymn } 422 1.1 blymn } 423 1.1 blymn } 424 1.1 blymn 425 1.1 blymn /* 426 1.1 blymn * Draw an item in the subwindow complete with appropriate highlighting. 427 1.1 blymn */ 428 1.1 blymn void 429 1.6 blymn _menui_draw_item(MENU *menu, int item) 430 1.1 blymn { 431 1.1 blymn int j, pad_len, mark_len; 432 1.14 blymn 433 1.1 blymn mark_len = max(menu->mark.length, menu->unmark.length); 434 1.14 blymn 435 1.9 blymn wmove(menu->scrwin, 436 1.1 blymn menu->items[item]->row - menu->top_row, 437 1.11 blymn menu->items[item]->col * (menu->col_width + 1)); 438 1.6 blymn 439 1.10 blymn if (menu->cur_item == item) 440 1.9 blymn wattrset(menu->scrwin, menu->fore); 441 1.1 blymn if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE) 442 1.9 blymn wattron(menu->scrwin, menu->grey); 443 1.1 blymn 444 1.1 blymn /* deal with the menu mark, if one is set. 445 1.1 blymn * We mark the selected items and write blanks for 446 1.1 blymn * all others unless the menu unmark string is set in which 447 1.1 blymn * case the unmark string is written. 448 1.1 blymn */ 449 1.16 blymn if ((menu->items[item]->selected == 1) || 450 1.16 blymn (((menu->opts & O_ONEVALUE) == O_ONEVALUE) && 451 1.16 blymn (menu->cur_item == item))) { 452 1.1 blymn if (menu->mark.string != NULL) { 453 1.1 blymn for (j = 0; j < menu->mark.length; j++) { 454 1.9 blymn waddch(menu->scrwin, 455 1.1 blymn menu->mark.string[j]); 456 1.1 blymn } 457 1.1 blymn } 458 1.1 blymn /* blank any length difference between mark & unmark */ 459 1.1 blymn for (j = menu->mark.length; j < mark_len; j++) 460 1.9 blymn waddch(menu->scrwin, ' '); 461 1.1 blymn } else { 462 1.1 blymn if (menu->unmark.string != NULL) { 463 1.1 blymn for (j = 0; j < menu->unmark.length; j++) { 464 1.9 blymn waddch(menu->scrwin, 465 1.1 blymn menu->unmark.string[j]); 466 1.1 blymn } 467 1.1 blymn } 468 1.1 blymn /* blank any length difference between mark & unmark */ 469 1.1 blymn for (j = menu->unmark.length; j < mark_len; j++) 470 1.9 blymn waddch(menu->scrwin, ' '); 471 1.1 blymn } 472 1.14 blymn 473 1.1 blymn /* add the menu name */ 474 1.1 blymn for (j=0; j < menu->items[item]->name.length; j++) 475 1.9 blymn waddch(menu->scrwin, 476 1.1 blymn menu->items[item]->name.string[j]); 477 1.14 blymn 478 1.1 blymn pad_len = menu->col_width - menu->items[item]->name.length 479 1.1 blymn - mark_len - 1; 480 1.1 blymn if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) { 481 1.1 blymn pad_len -= menu->items[item]->description.length - 1; 482 1.1 blymn for (j = 0; j < pad_len; j++) 483 1.9 blymn waddch(menu->scrwin, menu->pad); 484 1.1 blymn for (j = 0; j < menu->items[item]->description.length; j++) { 485 1.9 blymn waddch(menu->scrwin, 486 1.1 blymn menu->items[item]->description.string[j]); 487 1.1 blymn } 488 1.1 blymn } else { 489 1.1 blymn for (j = 0; j < pad_len; j++) 490 1.9 blymn waddch(menu->scrwin, ' '); 491 1.1 blymn } 492 1.1 blymn menu->items[item]->visible = 1; 493 1.14 blymn 494 1.11 blymn /* kill any special attributes... */ 495 1.11 blymn wattrset(menu->scrwin, menu->back); 496 1.11 blymn 497 1.6 blymn /* 498 1.6 blymn * Fill in the spacing between items, annoying but it looks 499 1.6 blymn * odd if the menu items are inverse because the spacings do not 500 1.6 blymn * have the same attributes as the items. 501 1.6 blymn */ 502 1.11 blymn if ((menu->items[item]->col > 0) && 503 1.11 blymn (menu->items[item]->col < (menu->item_cols - 1))) { 504 1.9 blymn wmove(menu->scrwin, 505 1.6 blymn menu->items[item]->row - menu->top_row, 506 1.11 blymn menu->items[item]->col * (menu->col_width + 1) - 1); 507 1.9 blymn waddch(menu->scrwin, ' '); 508 1.6 blymn } 509 1.14 blymn 510 1.1 blymn /* and position the cursor nicely */ 511 1.1 blymn pos_menu_cursor(menu); 512 1.1 blymn } 513 1.1 blymn 514 1.1 blymn /* 515 1.1 blymn * Draw the menu in the subwindow provided. 516 1.1 blymn */ 517 1.1 blymn int 518 1.6 blymn _menui_draw_menu(MENU *menu) 519 1.1 blymn { 520 1.17 christos int rowmajor, i, j, k, row = -1, stride; 521 1.15 blymn int incr, cur_row, offset, row_count; 522 1.14 blymn 523 1.1 blymn rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR); 524 1.1 blymn 525 1.15 blymn if (rowmajor) { 526 1.15 blymn stride = 1; 527 1.15 blymn incr = menu->item_cols; 528 1.15 blymn } else { 529 1.15 blymn stride = menu->item_rows; 530 1.15 blymn incr = 1; 531 1.15 blymn } 532 1.15 blymn row_count = 0; 533 1.15 blymn 534 1.15 blymn for (i = 0; i < menu->item_count; i += incr) { 535 1.1 blymn if (menu->items[i]->row == menu->top_row) 536 1.1 blymn break; 537 1.15 blymn row_count++; 538 1.15 blymn for (j = 0; j < menu->item_cols; j++) { 539 1.15 blymn offset = j * stride + i; 540 1.15 blymn if (offset >= menu->item_count) 541 1.15 blymn break; /* done */ 542 1.15 blymn menu->items[offset]->visible = 0; 543 1.15 blymn } 544 1.1 blymn } 545 1.1 blymn 546 1.9 blymn wmove(menu->scrwin, 0, 0); 547 1.1 blymn 548 1.9 blymn menu->col_width = getmaxx(menu->scrwin) / menu->cols; 549 1.1 blymn 550 1.15 blymn for (cur_row = 0; cur_row < menu->rows; cur_row++) { 551 1.15 blymn for (j = 0; j < menu->cols; j++) { 552 1.15 blymn offset = j * stride + i; 553 1.15 blymn if (offset >= menu->item_count) { 554 1.15 blymn /* no more items to draw, write background blanks */ 555 1.15 blymn wattrset(menu->scrwin, menu->back); 556 1.15 blymn if (row < 0) { 557 1.15 blymn row = menu->items[menu->item_count - 1]->row; 558 1.15 blymn } 559 1.1 blymn 560 1.15 blymn wmove(menu->scrwin, cur_row, 561 1.15 blymn j * (menu->col_width + 1)); 562 1.15 blymn for (k = 0; k < menu->col_width; k++) 563 1.15 blymn waddch(menu->scrwin, ' '); 564 1.1 blymn } else { 565 1.15 blymn _menui_draw_item(menu, offset); 566 1.1 blymn } 567 1.1 blymn } 568 1.1 blymn 569 1.15 blymn i += incr; 570 1.15 blymn row_count++; 571 1.1 blymn } 572 1.1 blymn 573 1.15 blymn if (row_count < menu->item_rows) { 574 1.15 blymn for (cur_row = row_count; cur_row < menu->item_rows; cur_row++) { 575 1.15 blymn for (j = 0; j < menu->item_cols; j++) { 576 1.15 blymn offset = j * stride + i; 577 1.15 blymn if (offset >= menu->item_count) 578 1.15 blymn break; /* done */ 579 1.15 blymn menu->items[offset]->visible = 0; 580 1.15 blymn } 581 1.15 blymn i += incr; 582 1.15 blymn } 583 1.1 blymn } 584 1.14 blymn 585 1.1 blymn return E_OK; 586 1.1 blymn } 587 1.1 blymn 588 1.14 blymn 589 1.1 blymn /* 590 1.1 blymn * Calculate the widest menu item and stash it in the menu struct. 591 1.1 blymn * 592 1.1 blymn */ 593 1.1 blymn void 594 1.6 blymn _menui_max_item_size(MENU *menu) 595 1.1 blymn { 596 1.1 blymn int i, with_desc, width; 597 1.1 blymn 598 1.1 blymn with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC); 599 1.14 blymn 600 1.1 blymn for (i = 0; i < menu->item_count; i++) { 601 1.1 blymn width = menu->items[i]->name.length 602 1.1 blymn + max(menu->mark.length, menu->unmark.length); 603 1.1 blymn if (with_desc) 604 1.1 blymn width += menu->items[i]->description.length + 1; 605 1.1 blymn 606 1.1 blymn menu->max_item_width = max(menu->max_item_width, width); 607 1.1 blymn } 608 1.1 blymn } 609 1.1 blymn 610 1.1 blymn 611 1.1 blymn /* 612 1.1 blymn * Redraw the menu on the screen. If the current item has changed then 613 1.1 blymn * unhighlight the old item and highlight the new one. 614 1.1 blymn */ 615 1.1 blymn static void 616 1.6 blymn _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item) 617 1.1 blymn { 618 1.1 blymn 619 1.1 blymn if (menu->top_row != old_top_row) { 620 1.1 blymn /* top row changed - redo the whole menu 621 1.1 blymn * XXXX this could be improved if we had wscrl implemented. 622 1.1 blymn 623 1.1 blymn * XXXX we could scroll the window and just fill in the 624 1.1 blymn * XXXX changed lines. 625 1.1 blymn */ 626 1.9 blymn wclear(menu->scrwin); 627 1.3 blymn _menui_draw_menu(menu); 628 1.1 blymn } else { 629 1.1 blymn if (menu->cur_item != old_cur_item) { 630 1.1 blymn /* redo the old item as a normal one. */ 631 1.3 blymn _menui_draw_item(menu, old_cur_item); 632 1.1 blymn } 633 1.1 blymn /* and then redraw the current item */ 634 1.3 blymn _menui_draw_item(menu, menu->cur_item); 635 1.1 blymn } 636 1.1 blymn } 637