1 /**************************************************************************** 2 * Copyright (c) 1998 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.17 1999/05/16 17:24:55 juergen 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 int _nc_Match_Next_Character_In_Item_Name(MENU *menu, int ch, ITEM **item) 115 { 116 bool found = FALSE, passed = FALSE; 117 int idx, last; 118 119 assert( menu && item && *item); 120 idx = (*item)->index; 121 122 if (ch && ch!=BS) 123 { 124 /* if we become to long, we need no further checking : there can't be 125 a match ! */ 126 if ((menu->pindex+1) > menu->namelen) 127 RETURN(E_NO_MATCH); 128 129 Add_Character_To_Pattern(menu,ch); 130 /* we artificially position one item back, because in the do...while 131 loop we start with the next item. This means, that with a new 132 pattern search we always start the scan with the actual item. If 133 we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the 134 one after or before the actual item. */ 135 if (--idx < 0) 136 idx = menu->nitems-1; 137 } 138 139 last = idx; /* this closes the cycle */ 140 141 do{ 142 if (ch==BS) 143 { /* we have to go backward */ 144 if (--idx < 0) 145 idx = menu->nitems-1; 146 } 147 else 148 { /* otherwise we always go forward */ 149 if (++idx >= menu->nitems) 150 idx = 0; 151 } 152 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0, 153 menu->pattern, 154 menu->items[idx]->name.str) 155 ) 156 found = TRUE; 157 else 158 passed = TRUE; 159 } while (!found && (idx != last)); 160 161 if (found) 162 { 163 if (!((idx==(*item)->index) && passed)) 164 { 165 *item = menu->items[idx]; 166 RETURN(E_OK); 167 } 168 /* This point is reached, if we fully cycled through the item list 169 and the only match we found is the starting item. With a NEXT_PATTERN 170 or PREV_PATTERN scan this means, that there was no additional match. 171 If we searched with an expanded new pattern, we should never reach 172 this point, because if the expanded pattern matches also the actual 173 item we will find it in the first attempt (passed==FALSE) and we 174 will never cycle through the whole item array. 175 */ 176 assert( ch==0 || ch==BS ); 177 } 178 else 179 { 180 if (ch && ch!=BS && menu->pindex>0) 181 { 182 /* if we had no match with a new pattern, we have to restore it */ 183 Remove_Character_From_Pattern(menu); 184 } 185 } 186 RETURN(E_NO_MATCH); 187 } 188 189 /*--------------------------------------------------------------------------- 190 | Facility : libnmenu 191 | Function : int menu_driver(MENU *menu, int c) 192 | 193 | Description : Central dispatcher for the menu. Translates the logical 194 | request 'c' into a menu action. 195 | 196 | Return Values : E_OK - success 197 | E_BAD_ARGUMENT - invalid menu pointer 198 | E_BAD_STATE - menu is in user hook routine 199 | E_NOT_POSTED - menu is not posted 200 +--------------------------------------------------------------------------*/ 201 int menu_driver(MENU * menu, int c) 202 { 203 #define NAVIGATE(dir) \ 204 if (!item->dir)\ 205 result = E_REQUEST_DENIED;\ 206 else\ 207 item = item->dir 208 209 int result = E_OK; 210 ITEM *item; 211 int my_top_row, rdiff; 212 213 if (!menu) 214 RETURN(E_BAD_ARGUMENT); 215 216 if ( menu->status & _IN_DRIVER ) 217 RETURN(E_BAD_STATE); 218 if ( !( menu->status & _POSTED ) ) 219 RETURN(E_NOT_POSTED); 220 221 item = menu->curitem; 222 223 my_top_row = menu->toprow; 224 assert(item); 225 226 if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND)) 227 { 228 if (!((c==REQ_BACK_PATTERN) 229 || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH))) 230 { 231 assert( menu->pattern ); 232 Reset_Pattern(menu); 233 } 234 235 switch(c) 236 { 237 case REQ_LEFT_ITEM: 238 /*=================*/ 239 NAVIGATE(left); 240 break; 241 242 case REQ_RIGHT_ITEM: 243 /*==================*/ 244 NAVIGATE(right); 245 break; 246 247 case REQ_UP_ITEM: 248 /*===============*/ 249 NAVIGATE(up); 250 break; 251 252 case REQ_DOWN_ITEM: 253 /*=================*/ 254 NAVIGATE(down); 255 break; 256 257 case REQ_SCR_ULINE: 258 /*=================*/ 259 if (my_top_row == 0 || !(item->up)) 260 result = E_REQUEST_DENIED; 261 else 262 { 263 --my_top_row; 264 item = item->up; 265 } 266 break; 267 268 case REQ_SCR_DLINE: 269 /*=================*/ 270 if ((my_top_row + menu->arows >= menu->rows) || !(item->down)) 271 { 272 /* only if the menu has less items than rows, we can deny the 273 request. Otherwise the epilogue of this routine adjusts the 274 top row if necessary */ 275 result = E_REQUEST_DENIED; 276 } 277 else { 278 my_top_row++; 279 item = item->down; 280 } 281 break; 282 283 case REQ_SCR_DPAGE: 284 /*=================*/ 285 rdiff = menu->rows - (menu->arows + my_top_row); 286 if (rdiff > menu->arows) 287 rdiff = menu->arows; 288 if (rdiff<=0) 289 result = E_REQUEST_DENIED; 290 else 291 { 292 my_top_row += rdiff; 293 while(rdiff-- > 0 && item!=(ITEM*)0) 294 item = item->down; 295 } 296 break; 297 298 case REQ_SCR_UPAGE: 299 /*=================*/ 300 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row; 301 if (rdiff<=0) 302 result = E_REQUEST_DENIED; 303 else 304 { 305 my_top_row -= rdiff; 306 while(rdiff-- && item!=(ITEM*)0) 307 item = item->up; 308 } 309 break; 310 311 case REQ_FIRST_ITEM: 312 /*==================*/ 313 item = menu->items[0]; 314 break; 315 316 case REQ_LAST_ITEM: 317 /*=================*/ 318 item = menu->items[menu->nitems-1]; 319 break; 320 321 case REQ_NEXT_ITEM: 322 /*=================*/ 323 if ((item->index+1)>=menu->nitems) 324 { 325 if (menu->opt & O_NONCYCLIC) 326 result = E_REQUEST_DENIED; 327 else 328 item = menu->items[0]; 329 } 330 else 331 item = menu->items[item->index + 1]; 332 break; 333 334 case REQ_PREV_ITEM: 335 /*=================*/ 336 if (item->index<=0) 337 { 338 if (menu->opt & O_NONCYCLIC) 339 result = E_REQUEST_DENIED; 340 else 341 item = menu->items[menu->nitems-1]; 342 } 343 else 344 item = menu->items[item->index - 1]; 345 break; 346 347 case REQ_TOGGLE_ITEM: 348 /*===================*/ 349 if (menu->opt & O_ONEVALUE) 350 { 351 result = E_REQUEST_DENIED; 352 } 353 else 354 { 355 if (menu->curitem->opt & O_SELECTABLE) 356 { 357 menu->curitem->value = !menu->curitem->value; 358 Move_And_Post_Item(menu,menu->curitem); 359 _nc_Show_Menu(menu); 360 } 361 else 362 result = E_NOT_SELECTABLE; 363 } 364 break; 365 366 case REQ_CLEAR_PATTERN: 367 /*=====================*/ 368 /* already cleared in prologue */ 369 break; 370 371 case REQ_BACK_PATTERN: 372 /*====================*/ 373 if (menu->pindex>0) 374 { 375 assert(menu->pattern); 376 Remove_Character_From_Pattern(menu); 377 pos_menu_cursor( menu ); 378 } 379 else 380 result = E_REQUEST_DENIED; 381 break; 382 383 case REQ_NEXT_MATCH: 384 /*==================*/ 385 assert(menu->pattern); 386 if (menu->pattern[0]) 387 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item); 388 else 389 { 390 if ((item->index+1)<menu->nitems) 391 item=menu->items[item->index+1]; 392 else 393 { 394 if (menu->opt & O_NONCYCLIC) 395 result = E_REQUEST_DENIED; 396 else 397 item = menu->items[0]; 398 } 399 } 400 break; 401 402 case REQ_PREV_MATCH: 403 /*==================*/ 404 assert(menu->pattern); 405 if (menu->pattern[0]) 406 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item); 407 else 408 { 409 if (item->index) 410 item = menu->items[item->index-1]; 411 else 412 { 413 if (menu->opt & O_NONCYCLIC) 414 result = E_REQUEST_DENIED; 415 else 416 item = menu->items[menu->nitems-1]; 417 } 418 } 419 break; 420 421 default: 422 /*======*/ 423 result = E_UNKNOWN_COMMAND; 424 break; 425 } 426 } 427 else 428 { /* not a command */ 429 if ( !(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(c) ) 430 result = _nc_Match_Next_Character_In_Item_Name( menu, c, &item ); 431 #ifdef NCURSES_MOUSE_VERSION 432 else if (KEY_MOUSE == c) 433 { 434 MEVENT event; 435 WINDOW* uwin = Get_Menu_UserWin(menu); 436 437 getmouse(&event); 438 if ((event.bstate & (BUTTON1_CLICKED | 439 BUTTON1_DOUBLE_CLICKED | 440 BUTTON1_TRIPLE_CLICKED )) 441 && wenclose(uwin,event.y, event.x)) 442 { /* we react only if the click was in the userwin, that means 443 * inside the menu display area or at the decoration window. 444 */ 445 WINDOW* sub = Get_Menu_Window(menu); 446 int ry = event.y, rx = event.x; /* screen coordinates */ 447 448 result = E_REQUEST_DENIED; 449 if (mouse_trafo(&ry,&rx,FALSE)) 450 { /* rx, ry are now "curses" coordinates */ 451 if (ry < sub->_begy) 452 { /* we clicked above the display region; this is 453 * interpreted as "scroll up" request 454 */ 455 if (event.bstate & BUTTON1_CLICKED) 456 result = menu_driver(menu,REQ_SCR_ULINE); 457 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 458 result = menu_driver(menu,REQ_SCR_UPAGE); 459 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 460 result = menu_driver(menu,REQ_FIRST_ITEM); 461 RETURN(result); 462 } 463 else if (ry >= sub->_begy + sub->_maxy) 464 { /* we clicked below the display region; this is 465 * interpreted as "scroll down" request 466 */ 467 if (event.bstate & BUTTON1_CLICKED) 468 result = menu_driver(menu,REQ_SCR_DLINE); 469 else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 470 result = menu_driver(menu,REQ_SCR_DPAGE); 471 else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 472 result = menu_driver(menu,REQ_LAST_ITEM); 473 RETURN(result); 474 } 475 else if (wenclose(sub,event.y,event.x)) 476 { /* Inside the area we try to find the hit item */ 477 int i,x,y,err; 478 ry = event.y; rx = event.x; 479 if (wmouse_trafo(sub,&ry,&rx,FALSE)) 480 { 481 for(i=0;i<menu->nitems;i++) 482 { 483 err = _nc_menu_cursor_pos(menu,menu->items[i], 484 &y, &x); 485 if (E_OK==err) 486 { 487 if ((ry==y) && 488 (rx>=x) && 489 (rx < x + menu->itemlen)) 490 { 491 item = menu->items[i]; 492 result = E_OK; 493 break; 494 } 495 } 496 } 497 if (E_OK==result) 498 { /* We found an item, now we can handle the click. 499 * A single click just positions the menu cursor 500 * to the clicked item. A double click toggles 501 * the item. 502 */ 503 if (event.bstate & BUTTON1_DOUBLE_CLICKED) 504 { 505 _nc_New_TopRow_and_CurrentItem(menu, 506 my_top_row, 507 item); 508 menu_driver(menu,REQ_TOGGLE_ITEM); 509 result = E_UNKNOWN_COMMAND; 510 } 511 } 512 } 513 } 514 } 515 } 516 else 517 result = E_REQUEST_DENIED; 518 } 519 #endif /* NCURSES_MOUSE_VERSION */ 520 else 521 result = E_UNKNOWN_COMMAND; 522 } 523 524 if (E_OK==result) 525 { 526 /* Adjust the top row if it turns out that the current item unfortunately 527 doesn't appear in the menu window */ 528 if ( item->y < my_top_row ) 529 my_top_row = item->y; 530 else if ( item->y >= (my_top_row + menu->arows) ) 531 my_top_row = item->y - menu->arows + 1; 532 533 _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item ); 534 535 } 536 537 RETURN(result); 538 } 539 540 /* m_driver.c ends here */ 541