1 /****************************************************************************
2 * Copyright (c) 1998,2000 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Juergen Pfeifer, 1995,1997 *
31 ****************************************************************************/
33 /***************************************************************************
35 * Central dispatching routine *
36 ***************************************************************************/
38 #include "menu.priv.h"
40 MODULE_ID("$Id: m_driver.c,v 1.20 2003/10/25 14:54:48 tom Exp $")
44 /* Remove the last character from the match pattern buffer */
45 #define Remove_Character_From_Pattern(menu) \
46 (menu)->pattern[--((menu)->pindex)] = '\0'
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'; }
53 /*---------------------------------------------------------------------------
55 | Function : static bool Is_Sub_String(
56 | bool IgnoreCaseFlag,
60 | Description : Checks whether or not part is a substring of string.
62 | Return Values : TRUE - if it is a substring
63 | FALSE - if it is not a substring
64 +--------------------------------------------------------------------------*/
65 static bool Is_Sub_String(
71 assert( part && string );
74 while(*string && *part)
76 if (toupper(*string++)!=toupper(*part)) break;
82 while( *string && *part )
83 if (*part != *string++) break;
86 return ( (*part) ? FALSE : TRUE );
89 /*---------------------------------------------------------------------------
91 | Function : int _nc_Match_Next_Character_In_Item_Name(
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
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
111 | Return Values : E_OK - an item matching the pattern was found
112 | E_NO_MATCH - nothing found
113 +--------------------------------------------------------------------------*/
115 _nc_Match_Next_Character_In_Item_Name
116 (MENU *menu, int ch, ITEM **item)
118 bool found = FALSE, passed = FALSE;
121 assert( menu && item && *item);
122 idx = (*item)->index;
126 /* if we become to long, we need no further checking : there can't be
128 if ((menu->pindex+1) > menu->namelen)
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. */
138 idx = menu->nitems-1;
141 last = idx; /* this closes the cycle */
145 { /* we have to go backward */
147 idx = menu->nitems-1;
150 { /* otherwise we always go forward */
151 if (++idx >= menu->nitems)
154 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0,
156 menu->items[idx]->name.str)
161 } while (!found && (idx != last));
165 if (!((idx==(*item)->index) && passed))
167 *item = menu->items[idx];
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.
178 assert( ch==0 || ch==BS );
182 if (ch && ch!=BS && menu->pindex>0)
184 /* if we had no match with a new pattern, we have to restore it */
185 Remove_Character_From_Pattern(menu);
191 /*---------------------------------------------------------------------------
192 | Facility : libnmenu
193 | Function : int menu_driver(MENU *menu, int c)
195 | Description : Central dispatcher for the menu. Translates the logical
196 | request 'c' into a menu action.
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 +--------------------------------------------------------------------------*/
204 menu_driver (MENU * menu, int c)
206 #define NAVIGATE(dir) \
208 result = E_REQUEST_DENIED;\
214 int my_top_row, rdiff;
217 RETURN(E_BAD_ARGUMENT);
219 if ( menu->status & _IN_DRIVER )
221 if ( !( menu->status & _POSTED ) )
222 RETURN(E_NOT_POSTED);
224 item = menu->curitem;
226 my_top_row = menu->toprow;
229 if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND))
231 if (!((c==REQ_BACK_PATTERN)
232 || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH)))
234 assert( menu->pattern );
241 /*=================*/
246 /*==================*/
256 /*=================*/
261 /*=================*/
262 if (my_top_row == 0 || !(item->up))
263 result = E_REQUEST_DENIED;
272 /*=================*/
273 if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
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;
287 /*=================*/
288 rdiff = menu->rows - (menu->arows + my_top_row);
289 if (rdiff > menu->arows)
292 result = E_REQUEST_DENIED;
296 while(rdiff-- > 0 && item!=(ITEM*)0)
302 /*=================*/
303 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
305 result = E_REQUEST_DENIED;
309 while(rdiff-- && item!=(ITEM*)0)
315 /*==================*/
316 item = menu->items[0];
320 /*=================*/
321 item = menu->items[menu->nitems-1];
325 /*=================*/
326 if ((item->index+1)>=menu->nitems)
328 if (menu->opt & O_NONCYCLIC)
329 result = E_REQUEST_DENIED;
331 item = menu->items[0];
334 item = menu->items[item->index + 1];
338 /*=================*/
341 if (menu->opt & O_NONCYCLIC)
342 result = E_REQUEST_DENIED;
344 item = menu->items[menu->nitems-1];
347 item = menu->items[item->index - 1];
350 case REQ_TOGGLE_ITEM:
351 /*===================*/
352 if (menu->opt & O_ONEVALUE)
354 result = E_REQUEST_DENIED;
358 if (menu->curitem->opt & O_SELECTABLE)
360 menu->curitem->value = !menu->curitem->value;
361 Move_And_Post_Item(menu,menu->curitem);
365 result = E_NOT_SELECTABLE;
369 case REQ_CLEAR_PATTERN:
370 /*=====================*/
371 /* already cleared in prologue */
374 case REQ_BACK_PATTERN:
375 /*====================*/
378 assert(menu->pattern);
379 Remove_Character_From_Pattern(menu);
380 pos_menu_cursor( menu );
383 result = E_REQUEST_DENIED;
387 /*==================*/
388 assert(menu->pattern);
389 if (menu->pattern[0])
390 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item);
393 if ((item->index+1)<menu->nitems)
394 item=menu->items[item->index+1];
397 if (menu->opt & O_NONCYCLIC)
398 result = E_REQUEST_DENIED;
400 item = menu->items[0];
406 /*==================*/
407 assert(menu->pattern);
408 if (menu->pattern[0])
409 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item);
413 item = menu->items[item->index-1];
416 if (menu->opt & O_NONCYCLIC)
417 result = E_REQUEST_DENIED;
419 item = menu->items[menu->nitems-1];
426 result = E_UNKNOWN_COMMAND;
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)
438 WINDOW* uwin = Get_Menu_UserWin(menu);
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.
448 WINDOW* sub = Get_Menu_Window(menu);
449 int ry = event.y, rx = event.x; /* screen coordinates */
451 result = E_REQUEST_DENIED;
452 if (mouse_trafo(&ry,&rx,FALSE))
453 { /* rx, ry are now "curses" coordinates */
455 { /* we clicked above the display region; this is
456 * interpreted as "scroll up" request
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);
466 else if (ry >= sub->_begy + sub->_maxy)
467 { /* we clicked below the display region; this is
468 * interpreted as "scroll down" request
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);
478 else if (wenclose(sub,event.y,event.x))
479 { /* Inside the area we try to find the hit item */
481 ry = event.y; rx = event.x;
482 if (wmouse_trafo(sub,&ry,&rx,FALSE))
484 for(i=0;i<menu->nitems;i++)
486 err = _nc_menu_cursor_pos(menu,menu->items[i],
492 (rx < x + menu->itemlen))
494 item = menu->items[i];
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
506 if (event.bstate & BUTTON1_DOUBLE_CLICKED)
508 _nc_New_TopRow_and_CurrentItem(menu,
511 menu_driver(menu,REQ_TOGGLE_ITEM);
512 result = E_UNKNOWN_COMMAND;
520 result = E_REQUEST_DENIED;
522 #endif /* NCURSES_MOUSE_VERSION */
524 result = E_UNKNOWN_COMMAND;
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;
536 _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item );
543 /* m_driver.c ends here */