1 /**************************************************************************** 2 * Copyright (c) 1998,2000 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Juergen Pfeifer <juergen.pfeifer@gmx.net> 1995,1997 * 31 ****************************************************************************/ 32 33 /*************************************************************************** 34 * Module m_driver * 35 * Central dispatching routine * 36 ***************************************************************************/ 37 38 #include "menu.priv.h" 39 40 MODULE_ID("$Id: m_driver.c,v 1.18 2000/12/10 02:16:48 tom Exp $") 41 42 /* Macros */ 43 44 /* Remove the last character from the match pattern buffer */ 45 #define Remove_Character_From_Pattern(menu) \ 46 (menu)->pattern[--((menu)->pindex)] = '\0' 47 48 /* Add a new character to the match pattern buffer */ 49 #define Add_Character_To_Pattern(menu,ch) \ 50 { (menu)->pattern[((menu)->pindex)++] = (ch);\ 51 (menu)->pattern[(menu)->pindex] = '\0'; } 52 53 /*--------------------------------------------------------------------------- 54 | Facility : libnmenu 55 | Function : static bool Is_Sub_String( 56 | bool IgnoreCaseFlag, 57 | const char *part, 58 | const char *string) 59 | 60 | Description : Checks whether or not part is a substring of string. 61 | 62 | Return Values : TRUE - if it is a substring 63 | FALSE - if it is not a substring 64 +--------------------------------------------------------------------------*/ 65 static bool Is_Sub_String( 66 bool IgnoreCaseFlag, 67 const char *part, 68 const char *string 69 ) 70 { 71 assert( part && string ); 72 if ( IgnoreCaseFlag ) 73 { 74 while(*string && *part) 75 { 76 if (toupper(*string++)!=toupper(*part)) break; 77 part++; 78 } 79 } 80 else 81 { 82 while( *string && *part ) 83 if (*part != *string++) break; 84 part++; 85 } 86 return ( (*part) ? FALSE : TRUE ); 87 } 88 89 /*--------------------------------------------------------------------------- 90 | Facility : libnmenu 91 | Function : int _nc_Match_Next_Character_In_Item_Name( 92 | MENU *menu, 93 | int ch, 94 | ITEM **item) 95 | 96 | Description : This internal routine is called for a menu positioned 97 | at an item with three different classes of characters: 98 | - a printable character; the character is added to 99 | the current pattern and the next item matching 100 | this pattern is searched. 101 | - NUL; the pattern stays as it is and the next item 102 | matching the pattern is searched 103 | - BS; the pattern stays as it is and the previous 104 | item matching the pattern is searched 105 | 106 | The item parameter contains on call a pointer to 107 | the item where the search starts. On return - if 108 | a match was found - it contains a pointer to the 109 | matching item. 110 | 111 | Return Values : E_OK - an item matching the pattern was found 112 | E_NO_MATCH - nothing found 113 +--------------------------------------------------------------------------*/ 114 NCURSES_EXPORT(int) 115 _nc_Match_Next_Character_In_Item_Name 116 (MENU *menu, int ch, ITEM **item) 117 { 118 bool found = FALSE, passed = FALSE; 119 int idx, last; 120 121 assert( menu && item && *item); 122 idx = (*item)->index; 123 124 if (ch && ch!=BS) 125 { 126 /* if we become to long, we need no further checking : there can't be 127 a match ! */ 128 if ((menu->pindex+1) > menu->namelen) 129 RETURN(E_NO_MATCH); 130 131 Add_Character_To_Pattern(menu,ch); 132 /* we artificially position one item back, because in the do...while 133 loop we start with the next item. This means, that with a new 134 pattern search we always start the scan with the actual item. If 135 we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the 136 one after or before the actual item. */ 137 if (--idx < 0) 138 idx = menu->nitems-1; 139 } 140 141 last = idx; /* this closes the cycle */ 142 143 do{ 144 if (ch==BS) 145 { /* we have to go backward */ 146 if (--idx < 0) 147 idx = menu->nitems-1; 148 } 149 else 150 { /* otherwise we always go forward */ 151 if (++idx >= menu->nitems) 152 idx = 0; 153 } 154 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0, 155 menu->pattern, 156 menu->items[idx]->name.str) 157 ) 158 found = TRUE; 159 else 160 passed = TRUE; 161 } while (!found && (idx != last)); 162 163 if (found) 164 { 165 if (!((idx==(*item)->index) && passed)) 166 { 167 *item = menu->items[idx]; 168 RETURN(E_OK); 169 } 170 /* This point is reached, if we fully cycled through the item list 171 and the only match we found is the starting item. With a NEXT_PATTERN 172 or PREV_PATTERN scan this means, that there was no additional match. 173 If we searched with an expanded new pattern, we should never reach 174 this point, because if the expanded pattern matches also the actual 175 item we will find it in the first attempt (passed==FALSE) and we 176 will never cycle through the whole item array. 177 */ 178 assert( ch==0 || ch==BS ); 179 } 180 else 181 { 182 if (ch && ch!=BS && menu->pindex>0) 183 { 184 /* if we had no match with a new pattern, we have to restore it */ 185 Remove_Character_From_Pattern(menu); 186 } 187 } 188 RETURN(E_NO_MATCH); 189 } 190 191 /*--------------------------------------------------------------------------- 192 | Facility : libnmenu 193 | Function : int menu_driver(MENU *menu, int c) 194 | 195 | Description : Central dispatcher for the menu. Translates the logical 196 | request 'c' into a menu action. 197 | 198 | Return Values : E_OK - success 199 | E_BAD_ARGUMENT - invalid menu pointer 200 | E_BAD_STATE - menu is in user hook routine 201 | E_NOT_POSTED - menu is not posted 202 +--------------------------------------------------------------------------*/ 203 NCURSES_EXPORT(int) 204 menu_driver (MENU * menu, int c) 205 { 206 #define NAVIGATE(dir) \ 207 if (!item->dir)\ 208 result = E_REQUEST_DENIED;\ 209 else\ 210 item = item->dir 211 212 int result = E_OK; 213 ITEM *item; 214 int my_top_row, rdiff; 215 216 if (!menu) 217 RETURN(E_BAD_ARGUMENT); 218 219 if ( menu->status & _IN_DRIVER ) 220 RETURN(E_BAD_STATE); 221 if ( !( menu->status & _POSTED ) ) 222 RETURN(E_NOT_POSTED); 223 224 item = menu->curitem; 225 226 my_top_row = menu->toprow; 227 assert(item); 228 229 if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND)) 230 { 231 if (!((c==REQ_BACK_PATTERN) 232 || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH))) 233 { 234 assert( menu->pattern ); 235 Reset_Pattern(menu); 236 } 237 238 switch(c) 239 { 240 case REQ_LEFT_ITEM: 241 /*=================*/ 242 NAVIGATE(left); 243 break; 244 245 case REQ_RIGHT_ITEM: 246 /*==================*/ 247 NAVIGATE(right); 248 break; 249 250 case REQ_UP_ITEM: 251 /*===============*/ 252 NAVIGATE(up); 253 break; 254 255 case REQ_DOWN_ITEM: 256 /*=================*/ 257 NAVIGATE(down); 258 break; 259 260 case REQ_SCR_ULINE: 261 /*=================*/ 262 if (my_top_row == 0 || !(item->up)) 263 result = E_REQUEST_DENIED; 264 else 265 { 266 --my_top_row; 267 item = item->up; 268 } 269 break; 270 271 case REQ_SCR_DLINE: 272 /*=================*/ 273 if ((my_top_row + menu->arows >= menu->rows) || !(item->down)) 274 { 275 /* only if the menu has less items than rows, we can deny the 276 request. Otherwise the epilogue of this routine adjusts the 277 top row if necessary */ 278 result = E_REQUEST_DENIED; 279 } 280 else { 281 my_top_row++; 282 item = item->down; 283 } 284 break; 285 286 case REQ_SCR_DPAGE: 287 /*=================*/ 288 rdiff = menu->rows - (menu->arows + my_top_row); 289 if (rdiff > menu->arows) 290 rdiff = menu->arows; 291 if (rdiff<=0) 292 result = E_REQUEST_DENIED; 293 else 294 { 295 my_top_row += rdiff; 296 while(rdiff-- > 0 && item!=(ITEM*)0) 297 item = item->down; 298 } 299 break; 300 301 case REQ_SCR_UPAGE: 302 /*=================*/ 303 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row; 304 if (rdiff<=0) 305 result = E_REQUEST_DENIED; 306 else 307 { 308 my_top_row -= rdiff; 309 while(rdiff-- && item!=(ITEM*)0) 310 item = item->up; 311 } 312 break; 313 314 case REQ_FIRST_ITEM: 315 /*==================*/ 316 item = menu->items[0]; 317 break; 318 319 case REQ_LAST_ITEM: 320 /*=================*/ 321 item = menu->items[menu->nitems-1]; 322 break; 323 324 case REQ_NEXT_ITEM: 325 /*=================*/ 326 if ((item->index+1)>=menu->nitems) 327 { 328 if (menu->opt & O_NONCYCLIC) 329 result = E_REQUEST_DENIED; 330 else 331 item = menu->items[0]; 332 } 333 else 334 item = menu->items[item->index + 1]; 335 break; 336 337 case REQ_PREV_ITEM: 338 /*=================*/ 339 if (item->index<=0) 340 { 341 if (menu->opt & O_NONCYCLIC) 342 result = E_REQUEST_DENIED; 343 else 344 item = menu->items[menu->nitems-1]; 345 } 346 else 347 item = menu->items[item->index - 1]; 348 break; 349 350 case REQ_TOGGLE_ITEM: 351 /*===================*/ 352 if (menu->opt & O_ONEVALUE) 353 { 354 result = E_REQUEST_DENIED; 355 } 356 else 357 { 358 if (menu->curitem->opt & O_SELECTABLE) 359 { 360 menu->curitem->value = !menu->curitem->value; 361 Move_And_Post_Item(menu,menu->curitem); 362 _nc_Show_Menu(menu); 363 } 364 else 365 result = E_NOT_SELECTABLE; 366 } 367 break; 368 369 case REQ_CLEAR_PATTERN: 370 /*=====================*/ 371 /* already cleared in prologue */ 372 break; 373 374 case REQ_BACK_PATTERN: 375 /*====================*/ 376 if (menu->pindex>0) 377 { 378 assert(menu->pattern); 379 Remove_Character_From_Pattern(menu); 380 pos_menu_cursor( menu ); 381 } 382 else 383 result = E_REQUEST_DENIED; 384 break; 385 386 case REQ_NEXT_MATCH: 387 /*==================*/ 388 assert(menu->pattern); 389 if (menu->pattern[0]) 390 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item); 391 else 392 { 393 if ((item->index+1)<menu->nitems) 394 item=menu->items[item->index+1]; 395 else 396 { 397 if (menu->opt & O_NONCYCLIC) 398 result = E_REQUEST_DENIED; 399 else 400 item = menu->items[0]; 401 } 402 } 403 break; 404 405 case REQ_PREV_MATCH: 406 /*==================*/ 407 assert(menu->pattern); 408 if (menu->pattern[0]) 409 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item); 410 else 411 { 412 if (item->index) 413 item = menu->items[item->index-1]; 414 else 415 { 416 if (menu->opt & O_NONCYCLIC) 417 result = E_REQUEST_DENIED; 418 else 419 item = menu->items[menu->nitems-1]; 420 } 421 } 422 break; 423 424 default: 425 /*======*/ 426 result = E_UNKNOWN_COMMAND; 427 break; 428 } 429 } 430 else 431 { /* not a command */ 432 if ( !(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(c) ) 433 result = _nc_Match_Next_Character_In_Item_Name( menu, c, &item ); 434 #ifdef NCURSES_MOUSE_VERSION 435 else if (KEY_MOUSE == c) 436 { 437 MEVENT event; 438 WINDOW* uwin = Get_Menu_UserWin(menu); 439 440 getmouse(&event); 441 if ((event.bstate & (BUTTON1_CLICKED | 442 BUTTON1_DOUBLE_CLICKED | 443 BUTTON1_TRIPLE_CLICKED )) 444 && wenclose(uwin,event.y, event.x)) 445 { /* we react only if the click was in the userwin, that means 446 * inside the menu display area or at the decoration window. 447 */ 448 WINDOW* sub = Get_Menu_Window(menu); 449 int ry = event.y, rx = event.x; /* screen coordinates */ 450 451 result = E_REQUEST_DENIED; 452 if (mouse_trafo(&ry,&rx,FALSE)) 453 { /* rx, ry are now "curses" coordinates */ 454 if (ry < sub->_begy) 455 { /* we clicked above the display region; this is 456 * interpreted as "scroll up" request 457 */ 458 if (event.bstate & BUTTON1_CLICKED) 459 result = menu_driver(menu,REQ_SCR_ULINE); 460 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 461 result = menu_driver(menu,REQ_SCR_UPAGE); 462 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 463 result = menu_driver(menu,REQ_FIRST_ITEM); 464 RETURN(result); 465 } 466 else if (ry >= sub->_begy + sub->_maxy) 467 { /* we clicked below the display region; this is 468 * interpreted as "scroll down" request 469 */ 470 if (event.bstate & BUTTON1_CLICKED) 471 result = menu_driver(menu,REQ_SCR_DLINE); 472 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 473 result = menu_driver(menu,REQ_SCR_DPAGE); 474 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 475 result = menu_driver(menu,REQ_LAST_ITEM); 476 RETURN(result); 477 } 478 else if (wenclose(sub,event.y,event.x)) 479 { /* Inside the area we try to find the hit item */ 480 int i,x,y,err; 481 ry = event.y; rx = event.x; 482 if (wmouse_trafo(sub,&ry,&rx,FALSE)) 483 { 484 for(i=0;i<menu->nitems;i++) 485 { 486 err = _nc_menu_cursor_pos(menu,menu->items[i], 487 &y, &x); 488 if (E_OK==err) 489 { 490 if ((ry==y) && 491 (rx>=x) && 492 (rx < x + menu->itemlen)) 493 { 494 item = menu->items[i]; 495 result = E_OK; 496 break; 497 } 498 } 499 } 500 if (E_OK==result) 501 { /* We found an item, now we can handle the click. 502 * A single click just positions the menu cursor 503 * to the clicked item. A double click toggles 504 * the item. 505 */ 506 if (event.bstate & BUTTON1_DOUBLE_CLICKED) 507 { 508 _nc_New_TopRow_and_CurrentItem(menu, 509 my_top_row, 510 item); 511 menu_driver(menu,REQ_TOGGLE_ITEM); 512 result = E_UNKNOWN_COMMAND; 513 } 514 } 515 } 516 } 517 } 518 } 519 else 520 result = E_REQUEST_DENIED; 521 } 522 #endif /* NCURSES_MOUSE_VERSION */ 523 else 524 result = E_UNKNOWN_COMMAND; 525 } 526 527 if (E_OK==result) 528 { 529 /* Adjust the top row if it turns out that the current item unfortunately 530 doesn't appear in the menu window */ 531 if ( item->y < my_top_row ) 532 my_top_row = item->y; 533 else if ( item->y >= (my_top_row + menu->arows) ) 534 my_top_row = item->y - menu->arows + 1; 535 536 _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item ); 537 538 } 539 540 RETURN(result); 541 } 542 543 /* m_driver.c ends here */ 544