1 /* $OpenBSD: m_global.c,v 1.8 2010/01/12 23:22:07 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Juergen Pfeifer, 1995,1997 * 33 ****************************************************************************/ 34 35 /*************************************************************************** 36 * Module m_global * 37 * Globally used internal routines and the default menu and item structures * 38 ***************************************************************************/ 39 40 #include "menu.priv.h" 41 42 MODULE_ID("$Id: m_global.c,v 1.8 2010/01/12 23:22:07 nicm Exp $") 43 44 static char mark[] = "-"; 45 /* *INDENT-OFF* */ 46 NCURSES_EXPORT_VAR(MENU) _nc_Default_Menu = { 47 16, /* Nr. of chars high */ 48 1, /* Nr. of chars wide */ 49 16, /* Nr. of items high */ 50 1, /* Nr. of items wide */ 51 16, /* Nr. of formatted items high */ 52 1, /* Nr. of formatted items wide */ 53 16, /* Nr. of items high (actual) */ 54 0, /* length of widest name */ 55 0, /* length of widest description */ 56 1, /* length of mark */ 57 1, /* length of one item */ 58 1, /* Spacing for descriptor */ 59 1, /* Spacing for columns */ 60 1, /* Spacing for rows */ 61 (char *)0, /* buffer used to store match chars */ 62 0, /* Index into pattern buffer */ 63 (WINDOW *)0, /* Window containing entire menu */ 64 (WINDOW *)0, /* Portion of menu displayed */ 65 (WINDOW *)0, /* User's window */ 66 (WINDOW *)0, /* User's subwindow */ 67 (ITEM **)0, /* List of items */ 68 0, /* Total Nr. of items in menu */ 69 (ITEM *)0, /* Current item */ 70 0, /* Top row of menu */ 71 (chtype)A_REVERSE, /* Attribute for selection */ 72 (chtype)A_NORMAL, /* Attribute for nonselection */ 73 (chtype)A_UNDERLINE, /* Attribute for inactive */ 74 ' ', /* Pad character */ 75 (Menu_Hook)0, /* Menu init */ 76 (Menu_Hook)0, /* Menu term */ 77 (Menu_Hook)0, /* Item init */ 78 (Menu_Hook)0, /* Item term */ 79 (void *)0, /* userptr */ 80 mark, /* mark */ 81 ALL_MENU_OPTS, /* options */ 82 0 /* status */ 83 }; 84 85 NCURSES_EXPORT_VAR(ITEM) _nc_Default_Item = { 86 { (char *)0, 0 }, /* name */ 87 { (char *)0, 0 }, /* description */ 88 (MENU *)0, /* Pointer to parent menu */ 89 (char *)0, /* Userpointer */ 90 ALL_ITEM_OPTS, /* options */ 91 0, /* Item Nr. */ 92 0, /* y */ 93 0, /* x */ 94 FALSE, /* value */ 95 (ITEM *)0, /* left */ 96 (ITEM *)0, /* right */ 97 (ITEM *)0, /* up */ 98 (ITEM *)0 /* down */ 99 }; 100 /* *INDENT-ON* */ 101 102 /*--------------------------------------------------------------------------- 103 | Facility : libnmenu 104 | Function : static void ComputeMaximum_NameDesc_Lenths(MENU *menu) 105 | 106 | Description : Calculates the maximum name and description lengths 107 | of the items connected to the menu 108 | 109 | Return Values : - 110 +--------------------------------------------------------------------------*/ 111 NCURSES_INLINE static void 112 ComputeMaximum_NameDesc_Lengths(MENU * menu) 113 { 114 unsigned MaximumNameLength = 0; 115 unsigned MaximumDescriptionLength = 0; 116 ITEM **items; 117 unsigned check; 118 119 assert(menu && menu->items); 120 for (items = menu->items; *items; items++) 121 { 122 check = _nc_Calculate_Text_Width(&((*items)->name)); 123 if (check > MaximumNameLength) 124 MaximumNameLength = check; 125 126 check = _nc_Calculate_Text_Width(&((*items)->description)); 127 if (check > MaximumDescriptionLength) 128 MaximumDescriptionLength = check; 129 } 130 131 menu->namelen = MaximumNameLength; 132 menu->desclen = MaximumDescriptionLength; 133 T(("ComputeMaximum_NameDesc_Lengths %d,%d", menu->namelen, menu->desclen)); 134 } 135 136 /*--------------------------------------------------------------------------- 137 | Facility : libnmenu 138 | Function : static void ResetConnectionInfo(MENU *, ITEM **) 139 | 140 | Description : Reset all informations in the menu and the items in 141 | the item array that indicates a connection 142 | 143 | Return Values : - 144 +--------------------------------------------------------------------------*/ 145 NCURSES_INLINE static void 146 ResetConnectionInfo(MENU * menu, ITEM ** items) 147 { 148 ITEM **item; 149 150 assert(menu && items); 151 for (item = items; *item; item++) 152 { 153 (*item)->index = 0; 154 (*item)->imenu = (MENU *) 0; 155 } 156 if (menu->pattern) 157 free(menu->pattern); 158 menu->pattern = (char *)0; 159 menu->pindex = 0; 160 menu->items = (ITEM **) 0; 161 menu->nitems = 0; 162 } 163 164 /*--------------------------------------------------------------------------- 165 | Facility : libnmenu 166 | Function : bool _nc_Connect_Items(MENU *menu, ITEM **items) 167 | 168 | Description : Connect the items in the item array to the menu. 169 | Decorate all the items with a number and a backward 170 | pointer to the menu. 171 | 172 | Return Values : TRUE - successful connection 173 | FALSE - connection failed 174 +--------------------------------------------------------------------------*/ 175 NCURSES_EXPORT(bool) 176 _nc_Connect_Items(MENU * menu, ITEM ** items) 177 { 178 ITEM **item; 179 unsigned int ItemCount = 0; 180 181 if (menu && items) 182 { 183 for (item = items; *item; item++) 184 { 185 if ((*item)->imenu) 186 { 187 /* if a item is already connected, reject connection */ 188 break; 189 } 190 } 191 if (!(*item)) 192 /* we reached the end, so there was no connected item */ 193 { 194 for (item = items; *item; item++) 195 { 196 if (menu->opt & O_ONEVALUE) 197 { 198 (*item)->value = FALSE; 199 } 200 (*item)->index = ItemCount++; 201 (*item)->imenu = menu; 202 } 203 } 204 } 205 else 206 return (FALSE); 207 208 if (ItemCount != 0) 209 { 210 menu->items = items; 211 menu->nitems = ItemCount; 212 ComputeMaximum_NameDesc_Lengths(menu); 213 if ((menu->pattern = typeMalloc(char, (unsigned)(1 + menu->namelen)))) 214 { 215 Reset_Pattern(menu); 216 set_menu_format(menu, menu->frows, menu->fcols); 217 menu->curitem = *items; 218 menu->toprow = 0; 219 return (TRUE); 220 } 221 } 222 223 /* If we fall through to this point, we have to reset all items connection 224 and inform about a reject connection */ 225 ResetConnectionInfo(menu, items); 226 return (FALSE); 227 } 228 229 /*--------------------------------------------------------------------------- 230 | Facility : libnmenu 231 | Function : void _nc_Disconnect_Items(MENU *menu) 232 | 233 | Description : Disconnect the menus item array from the menu 234 | 235 | Return Values : - 236 +--------------------------------------------------------------------------*/ 237 NCURSES_EXPORT(void) 238 _nc_Disconnect_Items(MENU * menu) 239 { 240 if (menu && menu->items) 241 ResetConnectionInfo(menu, menu->items); 242 } 243 244 /*--------------------------------------------------------------------------- 245 | Facility : libnmenu 246 | Function : int _nc_Calculate_Text_Width(const TEXT * item) 247 | 248 | Description : Calculate the number of columns for a TEXT. 249 | 250 | Return Values : the width 251 +--------------------------------------------------------------------------*/ 252 NCURSES_EXPORT(int) 253 _nc_Calculate_Text_Width(const TEXT * item /*FIXME: limit length */ ) 254 { 255 #if USE_WIDEC_SUPPORT 256 int result = item->length; 257 258 T((T_CALLED("_nc_menu_text_width(%p)"), item)); 259 if (result != 0 && item->str != 0) 260 { 261 int count = mbstowcs(0, item->str, 0); 262 wchar_t *temp = 0; 263 264 if (count > 0 265 && (temp = typeMalloc(wchar_t, 2 + count)) != 0) 266 { 267 int n; 268 269 result = 0; 270 mbstowcs(temp, item->str, (unsigned)count); 271 for (n = 0; n < count; ++n) 272 { 273 int test = wcwidth(temp[n]); 274 275 if (test <= 0) 276 test = 1; 277 result += test; 278 } 279 free(temp); 280 } 281 } 282 returnCode(result); 283 #else 284 return item->length; 285 #endif 286 } 287 288 /* 289 * Calculate the actual width of a menu entry for wide-characters. 290 */ 291 #if USE_WIDEC_SUPPORT 292 static int 293 calculate_actual_width(MENU * menu, bool name) 294 { 295 int width = 0; 296 int check = 0; 297 ITEM **items; 298 299 assert(menu && menu->items); 300 301 if (menu->items != 0) 302 { 303 for (items = menu->items; *items; items++) 304 { 305 if (name) 306 { 307 check = _nc_Calculate_Text_Width(&((*items)->name)); 308 } 309 else 310 { 311 check = _nc_Calculate_Text_Width(&((*items)->description)); 312 } 313 if (check > width) 314 width = check; 315 } 316 } 317 else 318 { 319 width = (name ? menu->namelen : menu->desclen); 320 } 321 322 T(("calculate_actual_width %s = %d/%d", 323 name ? "name" : "desc", 324 width, 325 name ? menu->namelen : menu->desclen)); 326 return width; 327 } 328 #else 329 #define calculate_actual_width(menu, name) (name ? menu->namelen : menu->desclen) 330 #endif 331 332 /*--------------------------------------------------------------------------- 333 | Facility : libnmenu 334 | Function : void _nc_Calculate_Item_Length_and_Width(MENU *menu) 335 | 336 | Description : Calculate the length of an item and the width of the 337 | whole menu. 338 | 339 | Return Values : - 340 +--------------------------------------------------------------------------*/ 341 NCURSES_EXPORT(void) 342 _nc_Calculate_Item_Length_and_Width(MENU * menu) 343 { 344 int l; 345 346 assert(menu); 347 348 menu->height = 1 + menu->spc_rows * (menu->arows - 1); 349 350 l = calculate_actual_width(menu, TRUE); 351 l += menu->marklen; 352 353 if ((menu->opt & O_SHOWDESC) && (menu->desclen > 0)) 354 { 355 l += calculate_actual_width(menu, FALSE); 356 l += menu->spc_desc; 357 } 358 359 menu->itemlen = l; 360 l *= menu->cols; 361 l += (menu->cols - 1) * menu->spc_cols; /* for the padding between the columns */ 362 menu->width = l; 363 364 T(("_nc_CalculateItem_Length_and_Width columns %d, item %d, width %d", 365 menu->cols, 366 menu->itemlen, 367 menu->width)); 368 } 369 370 /*--------------------------------------------------------------------------- 371 | Facility : libnmenu 372 | Function : void _nc_Link_Item(MENU *menu) 373 | 374 | Description : Statically calculate for every item its four neighbors. 375 | This depends on the orientation of the menu. This 376 | static approach simplifies navigation in the menu a lot. 377 | 378 | Return Values : - 379 +--------------------------------------------------------------------------*/ 380 NCURSES_EXPORT(void) 381 _nc_Link_Items(MENU * menu) 382 { 383 if (menu && menu->items && *(menu->items)) 384 { 385 int i, j; 386 ITEM *item; 387 int Number_Of_Items = menu->nitems; 388 int col = 0, row = 0; 389 int Last_in_Row; 390 int Last_in_Column; 391 bool cycle = (menu->opt & O_NONCYCLIC) ? FALSE : TRUE; 392 393 menu->status &= ~_LINK_NEEDED; 394 395 if (menu->opt & O_ROWMAJOR) 396 { 397 int Number_Of_Columns = menu->cols; 398 399 for (i = 0; i < Number_Of_Items; i++) 400 { 401 item = menu->items[i]; 402 403 Last_in_Row = row * Number_Of_Columns + (Number_Of_Columns - 1); 404 405 item->left = (col) ? 406 /* if we are not in the leftmost column, we can use the 407 predecessor in the items array */ 408 menu->items[i - 1] : 409 (cycle ? menu->items[(Last_in_Row >= Number_Of_Items) ? 410 Number_Of_Items - 1 : 411 Last_in_Row] : 412 (ITEM *) 0); 413 414 item->right = ((col < (Number_Of_Columns - 1)) && 415 ((i + 1) < Number_Of_Items) 416 )? 417 menu->items[i + 1] : 418 (cycle ? menu->items[row * Number_Of_Columns] : 419 (ITEM *) 0 420 ); 421 422 Last_in_Column = (menu->rows - 1) * Number_Of_Columns + col; 423 424 item->up = (row) ? menu->items[i - Number_Of_Columns] : 425 (cycle ? menu->items[(Last_in_Column >= Number_Of_Items) ? 426 Number_Of_Items - 1 : 427 Last_in_Column] : 428 (ITEM *) 0); 429 430 item->down = ((i + Number_Of_Columns) < Number_Of_Items) 431 ? 432 menu->items[i + Number_Of_Columns] : 433 (cycle ? menu->items[(row + 1) < menu->rows ? 434 Number_Of_Items - 1 : col] : 435 (ITEM *) 0); 436 item->x = col; 437 item->y = row; 438 if (++col == Number_Of_Columns) 439 { 440 row++; 441 col = 0; 442 } 443 } 444 } 445 else 446 { 447 int Number_Of_Rows = menu->rows; 448 449 for (j = 0; j < Number_Of_Items; j++) 450 { 451 item = menu->items[i = (col * Number_Of_Rows + row)]; 452 453 Last_in_Column = (menu->cols - 1) * Number_Of_Rows + row; 454 455 item->left = (col) ? 456 menu->items[i - Number_Of_Rows] : 457 (cycle ? (Last_in_Column >= Number_Of_Items) ? 458 menu->items[Last_in_Column - Number_Of_Rows] : 459 menu->items[Last_in_Column] : 460 (ITEM *) 0); 461 462 item->right = ((i + Number_Of_Rows) < Number_Of_Items) 463 ? 464 menu->items[i + Number_Of_Rows] : 465 (cycle ? menu->items[row] : (ITEM *) 0); 466 467 Last_in_Row = col * Number_Of_Rows + (Number_Of_Rows - 1); 468 469 item->up = (row) ? 470 menu->items[i - 1] : 471 (cycle ? 472 menu->items[(Last_in_Row >= Number_Of_Items) ? 473 Number_Of_Items - 1 : 474 Last_in_Row] : 475 (ITEM *) 0); 476 477 item->down = (row < (Number_Of_Rows - 1)) 478 ? 479 (menu->items[((i + 1) < Number_Of_Items) ? 480 i + 1 : 481 (col - 1) * Number_Of_Rows + row + 1]) : 482 (cycle ? 483 menu->items[col * Number_Of_Rows] : 484 (ITEM *) 0 485 ); 486 487 item->x = col; 488 item->y = row; 489 if ((++row) == Number_Of_Rows) 490 { 491 col++; 492 row = 0; 493 } 494 } 495 } 496 } 497 } 498 499 /*--------------------------------------------------------------------------- 500 | Facility : libnmenu 501 | Function : void _nc_Show_Menu(const MENU *menu) 502 | 503 | Description : Update the window that is associated with the menu 504 | 505 | Return Values : - 506 +--------------------------------------------------------------------------*/ 507 NCURSES_EXPORT(void) 508 _nc_Show_Menu(const MENU * menu) 509 { 510 WINDOW *win; 511 int maxy, maxx; 512 513 assert(menu); 514 if ((menu->status & _POSTED) && !(menu->status & _IN_DRIVER)) 515 { 516 /* adjust the internal subwindow to start on the current top */ 517 assert(menu->sub); 518 mvderwin(menu->sub, menu->spc_rows * menu->toprow, 0); 519 win = Get_Menu_Window(menu); 520 521 maxy = getmaxy(win); 522 maxx = getmaxx(win); 523 524 if (menu->height < maxy) 525 maxy = menu->height; 526 if (menu->width < maxx) 527 maxx = menu->width; 528 529 copywin(menu->sub, win, 0, 0, 0, 0, maxy - 1, maxx - 1, 0); 530 pos_menu_cursor(menu); 531 } 532 } 533 534 /*--------------------------------------------------------------------------- 535 | Facility : libnmenu 536 | Function : void _nc_New_TopRow_and_CurrentItem( 537 | MENU *menu, 538 | int new_toprow, 539 | ITEM *new_current_item) 540 | 541 | Description : Redisplay the menu so that the given row becomes the 542 | top row and the given item becomes the new current 543 | item. 544 | 545 | Return Values : - 546 +--------------------------------------------------------------------------*/ 547 NCURSES_EXPORT(void) 548 _nc_New_TopRow_and_CurrentItem 549 (MENU * menu, int new_toprow, ITEM * new_current_item) 550 { 551 ITEM *cur_item; 552 bool mterm_called = FALSE; 553 bool iterm_called = FALSE; 554 555 assert(menu); 556 if (menu->status & _POSTED) 557 { 558 if (new_current_item != menu->curitem) 559 { 560 Call_Hook(menu, itemterm); 561 iterm_called = TRUE; 562 } 563 if (new_toprow != menu->toprow) 564 { 565 Call_Hook(menu, menuterm); 566 mterm_called = TRUE; 567 } 568 569 cur_item = menu->curitem; 570 assert(cur_item); 571 menu->toprow = new_toprow; 572 menu->curitem = new_current_item; 573 574 if (mterm_called) 575 { 576 Call_Hook(menu, menuinit); 577 } 578 if (iterm_called) 579 { 580 /* this means, move from the old current_item to the new one... */ 581 Move_To_Current_Item(menu, cur_item); 582 Call_Hook(menu, iteminit); 583 } 584 if (mterm_called || iterm_called) 585 { 586 _nc_Show_Menu(menu); 587 } 588 else 589 pos_menu_cursor(menu); 590 } 591 else 592 { /* if we are not posted, this is quite simple */ 593 menu->toprow = new_toprow; 594 menu->curitem = new_current_item; 595 } 596 } 597 598 /* m_global.c ends here */ 599