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