10SN/A/**************************************************************************** 29330SN/A * Copyright (c) 1998-2005,2008 Free Software Foundation, Inc. * 30SN/A * * 40SN/A * Permission is hereby granted, free of charge, to any person obtaining a * 50SN/A * copy of this software and associated documentation files (the * 60SN/A * "Software"), to deal in the Software without restriction, including * 72362SN/A * without limitation the rights to use, copy, modify, merge, publish, * 80SN/A * distribute, distribute with modifications, sublicense, and/or sell * 92362SN/A * copies of the Software, and to permit persons to whom the Software is * 100SN/A * furnished to do so, subject to the following conditions: * 110SN/A * * 120SN/A * The above copyright notice and this permission notice shall be included * 130SN/A * in all copies or substantial portions of the Software. * 140SN/A * * 150SN/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 160SN/A * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 170SN/A * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 180SN/A * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 190SN/A * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 200SN/A * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 212362SN/A * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 222362SN/A * * 232362SN/A * Except as contained in this notice, the name(s) of the above copyright * 240SN/A * holders shall not be used in advertising or otherwise to promote the * 250SN/A * sale, use or other dealings in this Software without prior written * 260SN/A * authorization. * 270SN/A ****************************************************************************/ 280SN/A 290SN/A/**************************************************************************** 300SN/A * Author: Juergen Pfeifer, 1995,1997 * 310SN/A ****************************************************************************/ 320SN/A 330SN/A/*************************************************************************** 340SN/A* Module m_driver * 350SN/A* Central dispatching routine * 360SN/A***************************************************************************/ 370SN/A 380SN/A#include "menu.priv.h" 390SN/A 400SN/AMODULE_ID("$Id: m_driver.c,v 1.27 2008/08/03 22:08:22 tom Exp $") 410SN/A 420SN/A/* Macros */ 430SN/A 440SN/A/* Remove the last character from the match pattern buffer */ 450SN/A#define Remove_Character_From_Pattern(menu) \ 460SN/A (menu)->pattern[--((menu)->pindex)] = '\0' 470SN/A 480SN/A/* Add a new character to the match pattern buffer */ 490SN/A#define Add_Character_To_Pattern(menu,ch) \ 500SN/A { (menu)->pattern[((menu)->pindex)++] = (ch);\ 510SN/A (menu)->pattern[(menu)->pindex] = '\0'; } 520SN/A 530SN/A/*--------------------------------------------------------------------------- 540SN/A| Facility : libnmenu 550SN/A| Function : static bool Is_Sub_String( 560SN/A| bool IgnoreCaseFlag, 570SN/A| const char *part, 580SN/A| const char *string) 590SN/A| 600SN/A| Description : Checks whether or not part is a substring of string. 610SN/A| 620SN/A| Return Values : TRUE - if it is a substring 630SN/A| FALSE - if it is not a substring 640SN/A+--------------------------------------------------------------------------*/ 650SN/Astatic bool 660SN/AIs_Sub_String( 670SN/A bool IgnoreCaseFlag, 680SN/A const char *part, 690SN/A const char *string 700SN/A) 710SN/A{ 720SN/A assert(part && string); 730SN/A if (IgnoreCaseFlag) 740SN/A { 750SN/A while (*string && *part) 760SN/A { 770SN/A if (toupper(UChar(*string++)) != toupper(UChar(*part))) 780SN/A break; 790SN/A part++; 800SN/A } 810SN/A } 820SN/A else 830SN/A { 840SN/A while (*string && *part) 850SN/A if (*part != *string++) 860SN/A break; 870SN/A part++; 880SN/A } 890SN/A return ((*part) ? FALSE : TRUE); 900SN/A} 910SN/A 920SN/A/*--------------------------------------------------------------------------- 930SN/A| Facility : libnmenu 940SN/A| Function : int _nc_Match_Next_Character_In_Item_Name( 950SN/A| MENU *menu, 960SN/A| int ch, 970SN/A| ITEM **item) 980SN/A| 990SN/A| Description : This internal routine is called for a menu positioned 1000SN/A| at an item with three different classes of characters: 1010SN/A| - a printable character; the character is added to 1020SN/A| the current pattern and the next item matching 1030SN/A| this pattern is searched. 1040SN/A| - NUL; the pattern stays as it is and the next item 1050SN/A| matching the pattern is searched 1060SN/A| - BS; the pattern stays as it is and the previous 1070SN/A| item matching the pattern is searched 1080SN/A| 1090SN/A| The item parameter contains on call a pointer to 1100SN/A| the item where the search starts. On return - if 1110SN/A| a match was found - it contains a pointer to the 1120SN/A| matching item. 1130SN/A| 1140SN/A| Return Values : E_OK - an item matching the pattern was found 1150SN/A| E_NO_MATCH - nothing found 1160SN/A+--------------------------------------------------------------------------*/ 1170SN/ANCURSES_EXPORT(int) 1180SN/A_nc_Match_Next_Character_In_Item_Name 1190SN/A(MENU * menu, int ch, ITEM ** item) 1200SN/A{ 1210SN/A bool found = FALSE, passed = FALSE; 1220SN/A int idx, last; 1230SN/A 1240SN/A T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"), menu, ch, item)); 1250SN/A 1260SN/A assert(menu && item && *item); 1270SN/A idx = (*item)->index; 1280SN/A 1290SN/A if (ch && ch != BS) 1300SN/A { 1310SN/A /* if we become to long, we need no further checking : there can't be 1320SN/A a match ! */ 1330SN/A if ((menu->pindex + 1) > menu->namelen) 1340SN/A RETURN(E_NO_MATCH); 1350SN/A 1360SN/A Add_Character_To_Pattern(menu, ch); 1370SN/A /* we artificially position one item back, because in the do...while 1380SN/A loop we start with the next item. This means, that with a new 1390SN/A pattern search we always start the scan with the actual item. If 1400SN/A we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the 1410SN/A one after or before the actual item. */ 1420SN/A if (--idx < 0) 1430SN/A idx = menu->nitems - 1; 1440SN/A } 1450SN/A 1460SN/A last = idx; /* this closes the cycle */ 1470SN/A 1480SN/A do 1490SN/A { 1500SN/A if (ch == BS) 1510SN/A { /* we have to go backward */ 1520SN/A if (--idx < 0) 1530SN/A idx = menu->nitems - 1; 1540SN/A } 1550SN/A else 1560SN/A { /* otherwise we always go forward */ 1570SN/A if (++idx >= menu->nitems) 1580SN/A idx = 0; 1590SN/A } 1600SN/A if (Is_Sub_String((bool)((menu->opt & O_IGNORECASE) != 0), 1610SN/A menu->pattern, 1620SN/A menu->items[idx]->name.str) 1630SN/A ) 16412745Smartin found = TRUE; 1650SN/A else 1660SN/A passed = TRUE; 1670SN/A } 1680SN/A while (!found && (idx != last)); 1690SN/A 1700SN/A if (found) 1710SN/A { 1720SN/A if (!((idx == (*item)->index) && passed)) 1730SN/A { 1740SN/A *item = menu->items[idx]; 1750SN/A RETURN(E_OK); 1760SN/A } 1770SN/A /* This point is reached, if we fully cycled through the item list 1780SN/A and the only match we found is the starting item. With a NEXT_PATTERN 1790SN/A or PREV_PATTERN scan this means, that there was no additional match. 1800SN/A If we searched with an expanded new pattern, we should never reach 1810SN/A this point, because if the expanded pattern matches also the actual 1820SN/A item we will find it in the first attempt (passed==FALSE) and we 1830SN/A will never cycle through the whole item array. 1840SN/A */ 1850SN/A assert(ch == 0 || ch == BS); 1860SN/A } 1870SN/A else 1880SN/A { 1890SN/A if (ch && ch != BS && menu->pindex > 0) 1900SN/A { 1910SN/A /* if we had no match with a new pattern, we have to restore it */ 1920SN/A Remove_Character_From_Pattern(menu); 1930SN/A } 1940SN/A } 1950SN/A RETURN(E_NO_MATCH); 1960SN/A} 1970SN/A 1980SN/A/*--------------------------------------------------------------------------- 1990SN/A| Facility : libnmenu 2000SN/A| Function : int menu_driver(MENU *menu, int c) 2010SN/A| 2020SN/A| Description : Central dispatcher for the menu. Translates the logical 2030SN/A| request 'c' into a menu action. 2040SN/A| 2050SN/A| Return Values : E_OK - success 2060SN/A| E_BAD_ARGUMENT - invalid menu pointer 2070SN/A| E_BAD_STATE - menu is in user hook routine 2080SN/A| E_NOT_POSTED - menu is not posted 2090SN/A+--------------------------------------------------------------------------*/ 2100SN/ANCURSES_EXPORT(int) 2110SN/Amenu_driver(MENU * menu, int c) 2120SN/A{ 2130SN/A#define NAVIGATE(dir) \ 2140SN/A if (!item->dir)\ 2150SN/A result = E_REQUEST_DENIED;\ 2160SN/A else\ 2170SN/A item = item->dir 2180SN/A 2190SN/A int result = E_OK; 2200SN/A ITEM *item; 2210SN/A int my_top_row, rdiff; 2220SN/A 2230SN/A T((T_CALLED("menu_driver(%p,%d)"), menu, c)); 2240SN/A 2250SN/A if (!menu) 2260SN/A RETURN(E_BAD_ARGUMENT); 2270SN/A 2280SN/A if (menu->status & _IN_DRIVER) 2290SN/A RETURN(E_BAD_STATE); 2300SN/A if (!(menu->status & _POSTED)) 2310SN/A RETURN(E_NOT_POSTED); 2320SN/A 2330SN/A item = menu->curitem; 2340SN/A 2350SN/A my_top_row = menu->toprow; 2360SN/A assert(item); 2370SN/A 2380SN/A if ((c > KEY_MAX) && (c <= MAX_MENU_COMMAND)) 2390SN/A { 2400SN/A if (!((c == REQ_BACK_PATTERN) 2410SN/A || (c == REQ_NEXT_MATCH) || (c == REQ_PREV_MATCH))) 2420SN/A { 2430SN/A assert(menu->pattern); 2440SN/A Reset_Pattern(menu); 2450SN/A } 2460SN/A 2470SN/A switch (c) 2480SN/A { 2490SN/A case REQ_LEFT_ITEM: 2500SN/A /*=================*/ 2510SN/A NAVIGATE(left); 2520SN/A break; 2533625SN/A 2540SN/A case REQ_RIGHT_ITEM: 2550SN/A /*==================*/ 2560SN/A NAVIGATE(right); 2570SN/A break; 2580SN/A 2590SN/A case REQ_UP_ITEM: 2600SN/A /*===============*/ 2610SN/A NAVIGATE(up); 2620SN/A break; 2630SN/A 2640SN/A case REQ_DOWN_ITEM: 2650SN/A /*=================*/ 2660SN/A NAVIGATE(down); 2670SN/A break; 2680SN/A 2690SN/A case REQ_SCR_ULINE: 2700SN/A /*=================*/ 2710SN/A if (my_top_row == 0 || !(item->up)) 2720SN/A result = E_REQUEST_DENIED; 2730SN/A else 2740SN/A { 2750SN/A --my_top_row; 2760SN/A item = item->up; 2770SN/A } 2780SN/A break; 2790SN/A 2800SN/A case REQ_SCR_DLINE: 2810SN/A /*=================*/ 2820SN/A if ((my_top_row + menu->arows >= menu->rows) || !(item->down)) 2830SN/A { 2843626SN/A /* only if the menu has less items than rows, we can deny the 2853626SN/A request. Otherwise the epilogue of this routine adjusts the 2863626SN/A top row if necessary */ 2873626SN/A result = E_REQUEST_DENIED; 2883626SN/A } 2893626SN/A else 2903626SN/A { 2910SN/A my_top_row++; 2920SN/A item = item->down; 2930SN/A } 2940SN/A break; 2950SN/A 2960SN/A case REQ_SCR_DPAGE: 2970SN/A /*=================*/ 2980SN/A rdiff = menu->rows - (menu->arows + my_top_row); 2990SN/A if (rdiff > menu->arows) 3000SN/A rdiff = menu->arows; 3010SN/A if (rdiff <= 0) 3020SN/A result = E_REQUEST_DENIED; 3030SN/A else 3040SN/A { 3054818SN/A my_top_row += rdiff; 3060SN/A while (rdiff-- > 0 && item != 0 && item->down != 0) 3070SN/A item = item->down; 3080SN/A } 3090SN/A break; 3100SN/A 3110SN/A case REQ_SCR_UPAGE: 3120SN/A /*=================*/ 3130SN/A rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row; 3140SN/A if (rdiff <= 0) 3150SN/A result = E_REQUEST_DENIED; 3160SN/A else 3170SN/A { 3180SN/A my_top_row -= rdiff; 3190SN/A while (rdiff-- > 0 && item != 0 && item->up != 0) 3200SN/A item = item->up; 3210SN/A } 3220SN/A break; 3230SN/A 3240SN/A case REQ_FIRST_ITEM: 3250SN/A /*==================*/ 3260SN/A item = menu->items[0]; 3270SN/A break; 3280SN/A 3290SN/A case REQ_LAST_ITEM: 3300SN/A /*=================*/ 3310SN/A item = menu->items[menu->nitems - 1]; 3320SN/A break; 3330SN/A 3340SN/A case REQ_NEXT_ITEM: 3350SN/A /*=================*/ 3360SN/A if ((item->index + 1) >= menu->nitems) 3370SN/A { 3380SN/A if (menu->opt & O_NONCYCLIC) 3390SN/A result = E_REQUEST_DENIED; 3400SN/A else 3410SN/A item = menu->items[0]; 3420SN/A } 3430SN/A else 3440SN/A item = menu->items[item->index + 1]; 3450SN/A break; 3460SN/A 3470SN/A case REQ_PREV_ITEM: 3480SN/A /*=================*/ 3490SN/A if (item->index <= 0) 3500SN/A { 3510SN/A if (menu->opt & O_NONCYCLIC) 3520SN/A result = E_REQUEST_DENIED; 3530SN/A else 3540SN/A item = menu->items[menu->nitems - 1]; 3550SN/A } 3560SN/A else 3570SN/A item = menu->items[item->index - 1]; 35812143Savstepan break; 3590SN/A 3600SN/A case REQ_TOGGLE_ITEM: 3610SN/A /*===================*/ 3620SN/A if (menu->opt & O_ONEVALUE) 3630SN/A { 3640SN/A result = E_REQUEST_DENIED; 3650SN/A } 3660SN/A else 3670SN/A { 3680SN/A if (menu->curitem->opt & O_SELECTABLE) 3690SN/A { 3700SN/A menu->curitem->value = !menu->curitem->value; 3710SN/A Move_And_Post_Item(menu, menu->curitem); 3720SN/A _nc_Show_Menu(menu); 37315451Sredestad } 3740SN/A else 3750SN/A result = E_NOT_SELECTABLE; 3760SN/A } 3770SN/A break; 3780SN/A 3790SN/A case REQ_CLEAR_PATTERN: 3800SN/A /*=====================*/ 3810SN/A /* already cleared in prologue */ 3820SN/A break; 3830SN/A 3840SN/A case REQ_BACK_PATTERN: 3850SN/A /*====================*/ 3860SN/A if (menu->pindex > 0) 3870SN/A { 3880SN/A assert(menu->pattern); 3890SN/A Remove_Character_From_Pattern(menu); 3900SN/A pos_menu_cursor(menu); 3910SN/A } 3920SN/A else 3930SN/A result = E_REQUEST_DENIED; 3940SN/A break; 3950SN/A 3960SN/A case REQ_NEXT_MATCH: 3970SN/A /*==================*/ 3980SN/A assert(menu->pattern); 3990SN/A if (menu->pattern[0]) 4000SN/A result = _nc_Match_Next_Character_In_Item_Name(menu, 0, &item); 4010SN/A else 4020SN/A { 4030SN/A if ((item->index + 1) < menu->nitems) 4040SN/A item = menu->items[item->index + 1]; 4050SN/A else 4060SN/A { 4070SN/A if (menu->opt & O_NONCYCLIC) 4080SN/A result = E_REQUEST_DENIED; 4090SN/A else 4100SN/A item = menu->items[0]; 4110SN/A } 4120SN/A } 4130SN/A break; 4140SN/A 4150SN/A case REQ_PREV_MATCH: 4160SN/A /*==================*/ 4170SN/A assert(menu->pattern); 4180SN/A if (menu->pattern[0]) 4190SN/A result = _nc_Match_Next_Character_In_Item_Name(menu, BS, &item); 4200SN/A else 4210SN/A { 4220SN/A if (item->index) 4230SN/A item = menu->items[item->index - 1]; 4240SN/A else 4250SN/A { 4260SN/A if (menu->opt & O_NONCYCLIC) 4270SN/A result = E_REQUEST_DENIED; 4280SN/A else 4290SN/A item = menu->items[menu->nitems - 1]; 4300SN/A } 4310SN/A } 4320SN/A break; 4330SN/A 4340SN/A default: 4350SN/A /*======*/ 4360SN/A result = E_UNKNOWN_COMMAND; 4370SN/A break; 4380SN/A } 4390SN/A } 4400SN/A else 4410SN/A { /* not a command */ 4420SN/A if (!(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(UChar(c))) 4430SN/A result = _nc_Match_Next_Character_In_Item_Name(menu, c, &item); 4440SN/A#ifdef NCURSES_MOUSE_VERSION 4450SN/A else if (KEY_MOUSE == c) 4463625SN/A { 4473625SN/A MEVENT event; 4483625SN/A WINDOW *uwin = Get_Menu_UserWin(menu); 4493625SN/A 4503625SN/A getmouse(&event); 4513625SN/A if ((event.bstate & (BUTTON1_CLICKED | 4523625SN/A BUTTON1_DOUBLE_CLICKED | 4533625SN/A BUTTON1_TRIPLE_CLICKED)) 4543625SN/A && wenclose(uwin, event.y, event.x)) 4553625SN/A { /* we react only if the click was in the userwin, that means 4563625SN/A * inside the menu display area or at the decoration window. 4573625SN/A */ 4583625SN/A WINDOW *sub = Get_Menu_Window(menu); 4593625SN/A int ry = event.y, rx = event.x; /* screen coordinates */ 4603625SN/A 4613625SN/A result = E_REQUEST_DENIED; 4623625SN/A if (mouse_trafo(&ry, &rx, FALSE)) 4633625SN/A { /* rx, ry are now "curses" coordinates */ 4643625SN/A if (ry < sub->_begy) 4653625SN/A { /* we clicked above the display region; this is 4663625SN/A * interpreted as "scroll up" request 4673625SN/A */ 4683625SN/A if (event.bstate & BUTTON1_CLICKED) 4693625SN/A result = menu_driver(menu, REQ_SCR_ULINE); 4703625SN/A else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 4713625SN/A result = menu_driver(menu, REQ_SCR_UPAGE); 4723625SN/A else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 4733625SN/A result = menu_driver(menu, REQ_FIRST_ITEM); 4743625SN/A RETURN(result); 4753625SN/A } 4760SN/A else if (ry > sub->_begy + sub->_maxy) 4770SN/A { /* we clicked below the display region; this is 4780SN/A * interpreted as "scroll down" request 4790SN/A */ 4800SN/A if (event.bstate & BUTTON1_CLICKED) 4810SN/A result = menu_driver(menu, REQ_SCR_DLINE); 4820SN/A else if (event.bstate & BUTTON1_DOUBLE_CLICKED) 4830SN/A result = menu_driver(menu, REQ_SCR_DPAGE); 4840SN/A else if (event.bstate & BUTTON1_TRIPLE_CLICKED) 4850SN/A result = menu_driver(menu, REQ_LAST_ITEM); 4860SN/A RETURN(result); 4870SN/A } 4880SN/A else if (wenclose(sub, event.y, event.x)) 4890SN/A { /* Inside the area we try to find the hit item */ 4900SN/A int i, x, y, err; 4910SN/A 4920SN/A ry = event.y; 4930SN/A rx = event.x; 4940SN/A if (wmouse_trafo(sub, &ry, &rx, FALSE)) 4950SN/A { 4960SN/A for (i = 0; i < menu->nitems; i++) 4970SN/A { 4980SN/A err = _nc_menu_cursor_pos(menu, menu->items[i], 4990SN/A &y, &x); 5000SN/A if (E_OK == err) 5010SN/A { 5020SN/A if ((ry == y) && 5030SN/A (rx >= x) && 5040SN/A (rx < x + menu->itemlen)) 5056548SN/A { 5060SN/A item = menu->items[i]; 5070SN/A result = E_OK; 5080SN/A break; 5090SN/A } 5100SN/A } 5110SN/A } 5120SN/A if (E_OK == result) 5130SN/A { /* We found an item, now we can handle the click. 5140SN/A * A single click just positions the menu cursor 5150SN/A * to the clicked item. A double click toggles 5160SN/A * the item. 5170SN/A */ 5180SN/A if (event.bstate & BUTTON1_DOUBLE_CLICKED) 5190SN/A { 5200SN/A _nc_New_TopRow_and_CurrentItem(menu, 5210SN/A my_top_row, 5220SN/A item); 5230SN/A menu_driver(menu, REQ_TOGGLE_ITEM); 5240SN/A result = E_UNKNOWN_COMMAND; 5250SN/A } 5260SN/A } 5270SN/A } 5280SN/A } 5290SN/A } 5300SN/A } 5310SN/A else 5320SN/A result = E_REQUEST_DENIED; 5330SN/A } 5340SN/A#endif /* NCURSES_MOUSE_VERSION */ 5350SN/A else 5360SN/A result = E_UNKNOWN_COMMAND; 5370SN/A } 5380SN/A 5390SN/A if (E_OK == result) 5400SN/A { 5410SN/A /* Adjust the top row if it turns out that the current item unfortunately 5420SN/A doesn't appear in the menu window */ 5430SN/A if (item->y < my_top_row) 5440SN/A my_top_row = item->y; 5450SN/A else if (item->y >= (my_top_row + menu->arows)) 5460SN/A my_top_row = item->y - menu->arows + 1; 5470SN/A 5480SN/A _nc_New_TopRow_and_CurrentItem(menu, my_top_row, item); 5490SN/A 5500SN/A } 5510SN/A 5520SN/A RETURN(result); 5530SN/A} 5540SN/A 5550SN/A/* m_driver.c ends here */ 5566897SN/A