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