1 /****************************************************************************
2 * Copyright (c) 1998 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 <juergen.pfeifer@gmx.net> 1995,1997 *
31 ****************************************************************************/
33 /***************************************************************************
35 * Central dispatching routine *
36 ***************************************************************************/
38 #include "menu.priv.h"
40 MODULE_ID("$Id: m_driver.c,v 1.17 1999/05/16 17:24:55 juergen 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 +--------------------------------------------------------------------------*/
114 int _nc_Match_Next_Character_In_Item_Name(MENU *menu, int ch, ITEM **item)
116 bool found = FALSE, passed = FALSE;
119 assert( menu && item && *item);
120 idx = (*item)->index;
124 /* if we become to long, we need no further checking : there can't be
126 if ((menu->pindex+1) > menu->namelen)
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. */
136 idx = menu->nitems-1;
139 last = idx; /* this closes the cycle */
143 { /* we have to go backward */
145 idx = menu->nitems-1;
148 { /* otherwise we always go forward */
149 if (++idx >= menu->nitems)
152 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0,
154 menu->items[idx]->name.str)
159 } while (!found && (idx != last));
163 if (!((idx==(*item)->index) && passed))
165 *item = menu->items[idx];
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.
176 assert( ch==0 || ch==BS );
180 if (ch && ch!=BS && menu->pindex>0)
182 /* if we had no match with a new pattern, we have to restore it */
183 Remove_Character_From_Pattern(menu);
189 /*---------------------------------------------------------------------------
190 | Facility : libnmenu
191 | Function : int menu_driver(MENU *menu, int c)
193 | Description : Central dispatcher for the menu. Translates the logical
194 | request 'c' into a menu action.
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)
203 #define NAVIGATE(dir) \
205 result = E_REQUEST_DENIED;\
211 int my_top_row, rdiff;
214 RETURN(E_BAD_ARGUMENT);
216 if ( menu->status & _IN_DRIVER )
218 if ( !( menu->status & _POSTED ) )
219 RETURN(E_NOT_POSTED);
221 item = menu->curitem;
223 my_top_row = menu->toprow;
226 if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND))
228 if (!((c==REQ_BACK_PATTERN)
229 || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH)))
231 assert( menu->pattern );
238 /*=================*/
243 /*==================*/
253 /*=================*/
258 /*=================*/
259 if (my_top_row == 0 || !(item->up))
260 result = E_REQUEST_DENIED;
269 /*=================*/
270 if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
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;
284 /*=================*/
285 rdiff = menu->rows - (menu->arows + my_top_row);
286 if (rdiff > menu->arows)
289 result = E_REQUEST_DENIED;
293 while(rdiff-- > 0 && item!=(ITEM*)0)
299 /*=================*/
300 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
302 result = E_REQUEST_DENIED;
306 while(rdiff-- && item!=(ITEM*)0)
312 /*==================*/
313 item = menu->items[0];
317 /*=================*/
318 item = menu->items[menu->nitems-1];
322 /*=================*/
323 if ((item->index+1)>=menu->nitems)
325 if (menu->opt & O_NONCYCLIC)
326 result = E_REQUEST_DENIED;
328 item = menu->items[0];
331 item = menu->items[item->index + 1];
335 /*=================*/
338 if (menu->opt & O_NONCYCLIC)
339 result = E_REQUEST_DENIED;
341 item = menu->items[menu->nitems-1];
344 item = menu->items[item->index - 1];
347 case REQ_TOGGLE_ITEM:
348 /*===================*/
349 if (menu->opt & O_ONEVALUE)
351 result = E_REQUEST_DENIED;
355 if (menu->curitem->opt & O_SELECTABLE)
357 menu->curitem->value = !menu->curitem->value;
358 Move_And_Post_Item(menu,menu->curitem);
362 result = E_NOT_SELECTABLE;
366 case REQ_CLEAR_PATTERN:
367 /*=====================*/
368 /* already cleared in prologue */
371 case REQ_BACK_PATTERN:
372 /*====================*/
375 assert(menu->pattern);
376 Remove_Character_From_Pattern(menu);
377 pos_menu_cursor( menu );
380 result = E_REQUEST_DENIED;
384 /*==================*/
385 assert(menu->pattern);
386 if (menu->pattern[0])
387 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item);
390 if ((item->index+1)<menu->nitems)
391 item=menu->items[item->index+1];
394 if (menu->opt & O_NONCYCLIC)
395 result = E_REQUEST_DENIED;
397 item = menu->items[0];
403 /*==================*/
404 assert(menu->pattern);
405 if (menu->pattern[0])
406 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item);
410 item = menu->items[item->index-1];
413 if (menu->opt & O_NONCYCLIC)
414 result = E_REQUEST_DENIED;
416 item = menu->items[menu->nitems-1];
423 result = E_UNKNOWN_COMMAND;
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)
435 WINDOW* uwin = Get_Menu_UserWin(menu);
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.
445 WINDOW* sub = Get_Menu_Window(menu);
446 int ry = event.y, rx = event.x; /* screen coordinates */
448 result = E_REQUEST_DENIED;
449 if (mouse_trafo(&ry,&rx,FALSE))
450 { /* rx, ry are now "curses" coordinates */
452 { /* we clicked above the display region; this is
453 * interpreted as "scroll up" request
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);
463 else if (ry >= sub->_begy + sub->_maxy)
464 { /* we clicked below the display region; this is
465 * interpreted as "scroll down" request
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);
475 else if (wenclose(sub,event.y,event.x))
476 { /* Inside the area we try to find the hit item */
478 ry = event.y; rx = event.x;
479 if (wmouse_trafo(sub,&ry,&rx,FALSE))
481 for(i=0;i<menu->nitems;i++)
483 err = _nc_menu_cursor_pos(menu,menu->items[i],
489 (rx < x + menu->itemlen))
491 item = menu->items[i];
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
503 if (event.bstate & BUTTON1_DOUBLE_CLICKED)
505 _nc_New_TopRow_and_CurrentItem(menu,
508 menu_driver(menu,REQ_TOGGLE_ITEM);
509 result = E_UNKNOWN_COMMAND;
517 result = E_REQUEST_DENIED;
519 #endif /* NCURSES_MOUSE_VERSION */
521 result = E_UNKNOWN_COMMAND;
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;
533 _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item );
540 /* m_driver.c ends here */