150276Speter/**************************************************************************** 2184989Srafan * Copyright (c) 1998-2005,2008 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 30166124Srafan * Author: Juergen Pfeifer, 1995,1997 * 3150276Speter ****************************************************************************/ 3250276Speter 3350276Speter/*************************************************************************** 3450276Speter* Module m_driver * 3550276Speter* Central dispatching routine * 3650276Speter***************************************************************************/ 3750276Speter 3850276Speter#include "menu.priv.h" 3950276Speter 40184989SrafanMODULE_ID("$Id: m_driver.c,v 1.27 2008/08/03 22:08:22 tom Exp $") 4150276Speter 4250276Speter/* Macros */ 4350276Speter 4450276Speter/* Remove the last character from the match pattern buffer */ 4550276Speter#define Remove_Character_From_Pattern(menu) \ 4650276Speter (menu)->pattern[--((menu)->pindex)] = '\0' 4750276Speter 4850276Speter/* Add a new character to the match pattern buffer */ 4950276Speter#define Add_Character_To_Pattern(menu,ch) \ 5050276Speter { (menu)->pattern[((menu)->pindex)++] = (ch);\ 5150276Speter (menu)->pattern[(menu)->pindex] = '\0'; } 5250276Speter 5350276Speter/*--------------------------------------------------------------------------- 5450276Speter| Facility : libnmenu 5550276Speter| Function : static bool Is_Sub_String( 5650276Speter| bool IgnoreCaseFlag, 5750276Speter| const char *part, 5850276Speter| const char *string) 5950276Speter| 6050276Speter| Description : Checks whether or not part is a substring of string. 6150276Speter| 6250276Speter| Return Values : TRUE - if it is a substring 6350276Speter| FALSE - if it is not a substring 6450276Speter+--------------------------------------------------------------------------*/ 65166124Srafanstatic bool 66166124SrafanIs_Sub_String( 67166124Srafan bool IgnoreCaseFlag, 68166124Srafan const char *part, 69166124Srafan const char *string 70166124Srafan) 7150276Speter{ 72166124Srafan assert(part && string); 73166124Srafan if (IgnoreCaseFlag) 7450276Speter { 75166124Srafan while (*string && *part) 7650276Speter { 77184989Srafan if (toupper(UChar(*string++)) != toupper(UChar(*part))) 78166124Srafan break; 7950276Speter part++; 8050276Speter } 8150276Speter } 8250276Speter else 8350276Speter { 84166124Srafan while (*string && *part) 85166124Srafan if (*part != *string++) 86166124Srafan break; 8750276Speter part++; 8850276Speter } 89166124Srafan return ((*part) ? FALSE : TRUE); 9050276Speter} 9150276Speter 9250276Speter/*--------------------------------------------------------------------------- 9350276Speter| Facility : libnmenu 9450276Speter| Function : int _nc_Match_Next_Character_In_Item_Name( 9550276Speter| MENU *menu, 9650276Speter| int ch, 9750276Speter| ITEM **item) 9850276Speter| 9950276Speter| Description : This internal routine is called for a menu positioned 10050276Speter| at an item with three different classes of characters: 10150276Speter| - a printable character; the character is added to 10250276Speter| the current pattern and the next item matching 10350276Speter| this pattern is searched. 10450276Speter| - NUL; the pattern stays as it is and the next item 10550276Speter| matching the pattern is searched 10650276Speter| - BS; the pattern stays as it is and the previous 10750276Speter| item matching the pattern is searched 10850276Speter| 10950276Speter| The item parameter contains on call a pointer to 11050276Speter| the item where the search starts. On return - if 11150276Speter| a match was found - it contains a pointer to the 11250276Speter| matching item. 11350276Speter| 11450276Speter| Return Values : E_OK - an item matching the pattern was found 11550276Speter| E_NO_MATCH - nothing found 11650276Speter+--------------------------------------------------------------------------*/ 11776726SpeterNCURSES_EXPORT(int) 11876726Speter_nc_Match_Next_Character_In_Item_Name 119166124Srafan(MENU * menu, int ch, ITEM ** item) 12050276Speter{ 12150276Speter bool found = FALSE, passed = FALSE; 122166124Srafan int idx, last; 12350276Speter 124166124Srafan T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"), menu, ch, item)); 125166124Srafan 126166124Srafan assert(menu && item && *item); 12750276Speter idx = (*item)->index; 12850276Speter 129166124Srafan if (ch && ch != BS) 13050276Speter { 13150276Speter /* if we become to long, we need no further checking : there can't be 132166124Srafan a match ! */ 133166124Srafan if ((menu->pindex + 1) > menu->namelen) 13450276Speter RETURN(E_NO_MATCH); 13550276Speter 136166124Srafan Add_Character_To_Pattern(menu, ch); 13750276Speter /* we artificially position one item back, because in the do...while 138166124Srafan loop we start with the next item. This means, that with a new 139166124Srafan pattern search we always start the scan with the actual item. If 140166124Srafan we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the 141166124Srafan one after or before the actual item. */ 14250276Speter if (--idx < 0) 143166124Srafan idx = menu->nitems - 1; 14450276Speter } 14550276Speter 14650276Speter last = idx; /* this closes the cycle */ 14750276Speter 148166124Srafan do 149166124Srafan { 150166124Srafan if (ch == BS) 151166124Srafan { /* we have to go backward */ 152166124Srafan if (--idx < 0) 153166124Srafan idx = menu->nitems - 1; 154166124Srafan } 155166124Srafan else 156166124Srafan { /* otherwise we always go forward */ 157166124Srafan if (++idx >= menu->nitems) 158166124Srafan idx = 0; 159166124Srafan } 160166124Srafan if (Is_Sub_String((bool)((menu->opt & O_IGNORECASE) != 0), 161166124Srafan menu->pattern, 162166124Srafan menu->items[idx]->name.str) 16350276Speter ) 164166124Srafan found = TRUE; 165166124Srafan else 166166124Srafan passed = TRUE; 167166124Srafan } 168166124Srafan while (!found && (idx != last)); 16950276Speter 17050276Speter if (found) 17150276Speter { 172166124Srafan if (!((idx == (*item)->index) && passed)) 17350276Speter { 17450276Speter *item = menu->items[idx]; 17550276Speter RETURN(E_OK); 17650276Speter } 17750276Speter /* This point is reached, if we fully cycled through the item list 178166124Srafan and the only match we found is the starting item. With a NEXT_PATTERN 179166124Srafan or PREV_PATTERN scan this means, that there was no additional match. 180166124Srafan If we searched with an expanded new pattern, we should never reach 181166124Srafan this point, because if the expanded pattern matches also the actual 182166124Srafan item we will find it in the first attempt (passed==FALSE) and we 183166124Srafan will never cycle through the whole item array. 184166124Srafan */ 185166124Srafan assert(ch == 0 || ch == BS); 18650276Speter } 18750276Speter else 18850276Speter { 189166124Srafan if (ch && ch != BS && menu->pindex > 0) 19050276Speter { 19150276Speter /* if we had no match with a new pattern, we have to restore it */ 19250276Speter Remove_Character_From_Pattern(menu); 19350276Speter } 19450276Speter } 19550276Speter RETURN(E_NO_MATCH); 19650276Speter} 19750276Speter 19850276Speter/*--------------------------------------------------------------------------- 19950276Speter| Facility : libnmenu 20050276Speter| Function : int menu_driver(MENU *menu, int c) 20150276Speter| 20250276Speter| Description : Central dispatcher for the menu. Translates the logical 20350276Speter| request 'c' into a menu action. 20450276Speter| 20550276Speter| Return Values : E_OK - success 20650276Speter| E_BAD_ARGUMENT - invalid menu pointer 20750276Speter| E_BAD_STATE - menu is in user hook routine 20850276Speter| E_NOT_POSTED - menu is not posted 20950276Speter+--------------------------------------------------------------------------*/ 21076726SpeterNCURSES_EXPORT(int) 211166124Srafanmenu_driver(MENU * menu, int c) 21250276Speter{ 21350276Speter#define NAVIGATE(dir) \ 21450276Speter if (!item->dir)\ 21550276Speter result = E_REQUEST_DENIED;\ 21650276Speter else\ 21750276Speter item = item->dir 21850276Speter 21950276Speter int result = E_OK; 22050276Speter ITEM *item; 22150276Speter int my_top_row, rdiff; 22250276Speter 223166124Srafan T((T_CALLED("menu_driver(%p,%d)"), menu, c)); 224166124Srafan 22550276Speter if (!menu) 22650276Speter RETURN(E_BAD_ARGUMENT); 22750276Speter 228166124Srafan if (menu->status & _IN_DRIVER) 22950276Speter RETURN(E_BAD_STATE); 230166124Srafan if (!(menu->status & _POSTED)) 23150276Speter RETURN(E_NOT_POSTED); 23250276Speter 23350276Speter item = menu->curitem; 23450276Speter 235166124Srafan my_top_row = menu->toprow; 236166124Srafan assert(item); 23750276Speter 238166124Srafan if ((c > KEY_MAX) && (c <= MAX_MENU_COMMAND)) 239166124Srafan { 240166124Srafan if (!((c == REQ_BACK_PATTERN) 241166124Srafan || (c == REQ_NEXT_MATCH) || (c == REQ_PREV_MATCH))) 242166124Srafan { 243166124Srafan assert(menu->pattern); 244166124Srafan Reset_Pattern(menu); 245166124Srafan } 24650276Speter 247166124Srafan switch (c) 248166124Srafan { 249166124Srafan case REQ_LEFT_ITEM: 25050276Speter /*=================*/ 251166124Srafan NAVIGATE(left); 252166124Srafan break; 25350276Speter 254166124Srafan case REQ_RIGHT_ITEM: 25550276Speter /*==================*/ 256166124Srafan NAVIGATE(right); 257166124Srafan break; 25850276Speter 259166124Srafan case REQ_UP_ITEM: 26050276Speter /*===============*/ 261166124Srafan NAVIGATE(up); 262166124Srafan break; 26350276Speter 264166124Srafan case REQ_DOWN_ITEM: 26550276Speter /*=================*/ 266166124Srafan NAVIGATE(down); 267166124Srafan break; 26850276Speter 269166124Srafan case REQ_SCR_ULINE: 27050276Speter /*=================*/ 27150276Speter if (my_top_row == 0 || !(item->up)) 272166124Srafan result = E_REQUEST_DENIED; 273166124Srafan else 274166124Srafan { 275166124Srafan --my_top_row; 276166124Srafan item = item->up; 277166124Srafan } 278166124Srafan break; 27950276Speter 280166124Srafan case REQ_SCR_DLINE: 28150276Speter /*=================*/ 28250276Speter if ((my_top_row + menu->arows >= menu->rows) || !(item->down)) 283166124Srafan { 284166124Srafan /* only if the menu has less items than rows, we can deny the 285166124Srafan request. Otherwise the epilogue of this routine adjusts the 286166124Srafan top row if necessary */ 287166124Srafan result = E_REQUEST_DENIED; 288166124Srafan } 289166124Srafan else 290166124Srafan { 291166124Srafan my_top_row++; 29250276Speter item = item->down; 293166124Srafan } 294166124Srafan break; 29550276Speter 296166124Srafan case REQ_SCR_DPAGE: 29750276Speter /*=================*/ 29850276Speter rdiff = menu->rows - (menu->arows + my_top_row); 299166124Srafan if (rdiff > menu->arows) 300166124Srafan rdiff = menu->arows; 301166124Srafan if (rdiff <= 0) 302166124Srafan result = E_REQUEST_DENIED; 303166124Srafan else 304166124Srafan { 305166124Srafan my_top_row += rdiff; 306184989Srafan while (rdiff-- > 0 && item != 0 && item->down != 0) 307166124Srafan item = item->down; 308166124Srafan } 309166124Srafan break; 31050276Speter 311166124Srafan case REQ_SCR_UPAGE: 31250276Speter /*=================*/ 31350276Speter rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row; 314166124Srafan if (rdiff <= 0) 315166124Srafan result = E_REQUEST_DENIED; 316166124Srafan else 317166124Srafan { 318166124Srafan my_top_row -= rdiff; 319184989Srafan while (rdiff-- > 0 && item != 0 && item->up != 0) 320166124Srafan item = item->up; 321166124Srafan } 322166124Srafan break; 32350276Speter 324166124Srafan case REQ_FIRST_ITEM: 32550276Speter /*==================*/ 326166124Srafan item = menu->items[0]; 327166124Srafan break; 32850276Speter 329166124Srafan case REQ_LAST_ITEM: 33050276Speter /*=================*/ 331166124Srafan item = menu->items[menu->nitems - 1]; 332166124Srafan break; 33350276Speter 334166124Srafan case REQ_NEXT_ITEM: 33550276Speter /*=================*/ 336166124Srafan if ((item->index + 1) >= menu->nitems) 337166124Srafan { 338166124Srafan if (menu->opt & O_NONCYCLIC) 339166124Srafan result = E_REQUEST_DENIED; 340166124Srafan else 341166124Srafan item = menu->items[0]; 342166124Srafan } 343166124Srafan else 344166124Srafan item = menu->items[item->index + 1]; 345166124Srafan break; 34650276Speter 347166124Srafan case REQ_PREV_ITEM: 34850276Speter /*=================*/ 349166124Srafan if (item->index <= 0) 350166124Srafan { 351166124Srafan if (menu->opt & O_NONCYCLIC) 352166124Srafan result = E_REQUEST_DENIED; 353166124Srafan else 354166124Srafan item = menu->items[menu->nitems - 1]; 355166124Srafan } 356166124Srafan else 357166124Srafan item = menu->items[item->index - 1]; 358166124Srafan break; 35950276Speter 360166124Srafan case REQ_TOGGLE_ITEM: 36150276Speter /*===================*/ 362166124Srafan if (menu->opt & O_ONEVALUE) 363166124Srafan { 364166124Srafan result = E_REQUEST_DENIED; 365166124Srafan } 366166124Srafan else 367166124Srafan { 368166124Srafan if (menu->curitem->opt & O_SELECTABLE) 369166124Srafan { 370166124Srafan menu->curitem->value = !menu->curitem->value; 371166124Srafan Move_And_Post_Item(menu, menu->curitem); 372166124Srafan _nc_Show_Menu(menu); 373166124Srafan } 374166124Srafan else 375166124Srafan result = E_NOT_SELECTABLE; 376166124Srafan } 377166124Srafan break; 37850276Speter 379166124Srafan case REQ_CLEAR_PATTERN: 38050276Speter /*=====================*/ 381166124Srafan /* already cleared in prologue */ 382166124Srafan break; 38350276Speter 384166124Srafan case REQ_BACK_PATTERN: 38550276Speter /*====================*/ 386166124Srafan if (menu->pindex > 0) 387166124Srafan { 388166124Srafan assert(menu->pattern); 389166124Srafan Remove_Character_From_Pattern(menu); 390166124Srafan pos_menu_cursor(menu); 391166124Srafan } 392166124Srafan else 393166124Srafan result = E_REQUEST_DENIED; 394166124Srafan break; 39550276Speter 396166124Srafan case REQ_NEXT_MATCH: 39750276Speter /*==================*/ 398166124Srafan assert(menu->pattern); 399166124Srafan if (menu->pattern[0]) 400166124Srafan result = _nc_Match_Next_Character_In_Item_Name(menu, 0, &item); 401166124Srafan else 402166124Srafan { 403166124Srafan if ((item->index + 1) < menu->nitems) 404166124Srafan item = menu->items[item->index + 1]; 405166124Srafan else 406166124Srafan { 407166124Srafan if (menu->opt & O_NONCYCLIC) 408166124Srafan result = E_REQUEST_DENIED; 409166124Srafan else 410166124Srafan item = menu->items[0]; 411166124Srafan } 412166124Srafan } 413166124Srafan break; 41450276Speter 415166124Srafan case REQ_PREV_MATCH: 41650276Speter /*==================*/ 417166124Srafan assert(menu->pattern); 418166124Srafan if (menu->pattern[0]) 419166124Srafan result = _nc_Match_Next_Character_In_Item_Name(menu, BS, &item); 420166124Srafan else 421166124Srafan { 422166124Srafan if (item->index) 423166124Srafan item = menu->items[item->index - 1]; 424166124Srafan else 425166124Srafan { 426166124Srafan if (menu->opt & O_NONCYCLIC) 427166124Srafan result = E_REQUEST_DENIED; 428166124Srafan else 429166124Srafan item = menu->items[menu->nitems - 1]; 430166124Srafan } 431166124Srafan } 432166124Srafan break; 43350276Speter 434166124Srafan default: 43550276Speter /*======*/ 436166124Srafan result = E_UNKNOWN_COMMAND; 437166124Srafan break; 438166124Srafan } 439166124Srafan } 440166124Srafan else 441166124Srafan { /* not a command */ 442166124Srafan if (!(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(UChar(c))) 443166124Srafan result = _nc_Match_Next_Character_In_Item_Name(menu, c, &item); 44450276Speter#ifdef NCURSES_MOUSE_VERSION 445166124Srafan else if (KEY_MOUSE == c) 446166124Srafan { 447166124Srafan MEVENT event; 448166124Srafan WINDOW *uwin = Get_Menu_UserWin(menu); 44950276Speter 450166124Srafan getmouse(&event); 451166124Srafan if ((event.bstate & (BUTTON1_CLICKED | 452166124Srafan BUTTON1_DOUBLE_CLICKED | 453166124Srafan BUTTON1_TRIPLE_CLICKED)) 454166124Srafan && wenclose(uwin, event.y, event.x)) 455166124Srafan { /* we react only if the click was in the userwin, that means 456166124Srafan * inside the menu display area or at the decoration window. 457166124Srafan */ 458166124Srafan WINDOW *sub = Get_Menu_Window(menu); 459166124Srafan int ry = event.y, rx = event.x; /* screen coordinates */ 46050276Speter 461166124Srafan result = E_REQUEST_DENIED; 462166124Srafan if (mouse_trafo(&ry, &rx, FALSE)) 463166124Srafan { /* rx, ry are now "curses" coordinates */ 464166124Srafan if (ry < sub->_begy) 465166124Srafan { /* we clicked above the display region; this is 466166124Srafan * interpreted as "scroll up" request 467166124Srafan */ 468166124Srafan if (event.bstate & BUTTON1_CLICKED) 469166124Srafan result = menu_driver(menu, REQ_SCR_ULINE); 470166124Srafan else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 471166124Srafan result = menu_driver(menu, REQ_SCR_UPAGE); 472166124Srafan else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 473166124Srafan result = menu_driver(menu, REQ_FIRST_ITEM); 474166124Srafan RETURN(result); 475166124Srafan } 476166124Srafan else if (ry > sub->_begy + sub->_maxy) 477166124Srafan { /* we clicked below the display region; this is 478166124Srafan * interpreted as "scroll down" request 479166124Srafan */ 480166124Srafan if (event.bstate & BUTTON1_CLICKED) 481166124Srafan result = menu_driver(menu, REQ_SCR_DLINE); 482166124Srafan else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 483166124Srafan result = menu_driver(menu, REQ_SCR_DPAGE); 484166124Srafan else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 485166124Srafan result = menu_driver(menu, REQ_LAST_ITEM); 486166124Srafan RETURN(result); 487166124Srafan } 488166124Srafan else if (wenclose(sub, event.y, event.x)) 489166124Srafan { /* Inside the area we try to find the hit item */ 490166124Srafan int i, x, y, err; 491166124Srafan 492166124Srafan ry = event.y; 493166124Srafan rx = event.x; 494166124Srafan if (wmouse_trafo(sub, &ry, &rx, FALSE)) 495166124Srafan { 496166124Srafan for (i = 0; i < menu->nitems; i++) 497166124Srafan { 498166124Srafan err = _nc_menu_cursor_pos(menu, menu->items[i], 499166124Srafan &y, &x); 500166124Srafan if (E_OK == err) 501166124Srafan { 502166124Srafan if ((ry == y) && 503166124Srafan (rx >= x) && 504166124Srafan (rx < x + menu->itemlen)) 505166124Srafan { 506166124Srafan item = menu->items[i]; 507166124Srafan result = E_OK; 508166124Srafan break; 509166124Srafan } 510166124Srafan } 511166124Srafan } 512166124Srafan if (E_OK == result) 513166124Srafan { /* We found an item, now we can handle the click. 51450276Speter * A single click just positions the menu cursor 51550276Speter * to the clicked item. A double click toggles 51650276Speter * the item. 51750276Speter */ 518166124Srafan if (event.bstate & BUTTON1_DOUBLE_CLICKED) 519166124Srafan { 520166124Srafan _nc_New_TopRow_and_CurrentItem(menu, 521166124Srafan my_top_row, 522166124Srafan item); 523166124Srafan menu_driver(menu, REQ_TOGGLE_ITEM); 524166124Srafan result = E_UNKNOWN_COMMAND; 525166124Srafan } 526166124Srafan } 527166124Srafan } 528166124Srafan } 529166124Srafan } 530166124Srafan } 531166124Srafan else 532166124Srafan result = E_REQUEST_DENIED; 533166124Srafan } 53450276Speter#endif /* NCURSES_MOUSE_VERSION */ 535166124Srafan else 536166124Srafan result = E_UNKNOWN_COMMAND; 537166124Srafan } 53850276Speter 539166124Srafan if (E_OK == result) 54050276Speter { 541166124Srafan /* Adjust the top row if it turns out that the current item unfortunately 542166124Srafan doesn't appear in the menu window */ 543166124Srafan if (item->y < my_top_row) 544166124Srafan my_top_row = item->y; 545166124Srafan else if (item->y >= (my_top_row + menu->arows)) 546166124Srafan my_top_row = item->y - menu->arows + 1; 54750276Speter 548166124Srafan _nc_New_TopRow_and_CurrentItem(menu, my_top_row, item); 54950276Speter 55050276Speter } 55150276Speter 55250276Speter RETURN(result); 55350276Speter} 55450276Speter 55550276Speter/* m_driver.c ends here */ 556