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 * Contact: http://www.familiepfeifer.de/Contact.aspx?Lang=en *
32 ****************************************************************************/
34 /***************************************************************************
36 * Central dispatching routine *
37 ***************************************************************************/
39 #include "menu.priv.h"
41 MODULE_ID("$Id: m_driver.c,v 1.19 2002/07/06 15:22:16 juergen Exp $")
45 /* Remove the last character from the match pattern buffer */
46 #define Remove_Character_From_Pattern(menu) \
47 (menu)->pattern[--((menu)->pindex)] = '\0'
49 /* Add a new character to the match pattern buffer */
50 #define Add_Character_To_Pattern(menu,ch) \
51 { (menu)->pattern[((menu)->pindex)++] = (ch);\
52 (menu)->pattern[(menu)->pindex] = '\0'; }
54 /*---------------------------------------------------------------------------
56 | Function : static bool Is_Sub_String(
57 | bool IgnoreCaseFlag,
61 | Description : Checks whether or not part is a substring of string.
63 | Return Values : TRUE - if it is a substring
64 | FALSE - if it is not a substring
65 +--------------------------------------------------------------------------*/
66 static bool Is_Sub_String(
72 assert( part && string );
75 while(*string && *part)
77 if (toupper(*string++)!=toupper(*part)) break;
83 while( *string && *part )
84 if (*part != *string++) break;
87 return ( (*part) ? FALSE : TRUE );
90 /*---------------------------------------------------------------------------
92 | Function : int _nc_Match_Next_Character_In_Item_Name(
97 | Description : This internal routine is called for a menu positioned
98 | at an item with three different classes of characters:
99 | - a printable character; the character is added to
100 | the current pattern and the next item matching
101 | this pattern is searched.
102 | - NUL; the pattern stays as it is and the next item
103 | matching the pattern is searched
104 | - BS; the pattern stays as it is and the previous
105 | item matching the pattern is searched
107 | The item parameter contains on call a pointer to
108 | the item where the search starts. On return - if
109 | a match was found - it contains a pointer to the
112 | Return Values : E_OK - an item matching the pattern was found
113 | E_NO_MATCH - nothing found
114 +--------------------------------------------------------------------------*/
116 _nc_Match_Next_Character_In_Item_Name
117 (MENU *menu, int ch, ITEM **item)
119 bool found = FALSE, passed = FALSE;
122 assert( menu && item && *item);
123 idx = (*item)->index;
127 /* if we become to long, we need no further checking : there can't be
129 if ((menu->pindex+1) > menu->namelen)
132 Add_Character_To_Pattern(menu,ch);
133 /* we artificially position one item back, because in the do...while
134 loop we start with the next item. This means, that with a new
135 pattern search we always start the scan with the actual item. If
136 we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the
137 one after or before the actual item. */
139 idx = menu->nitems-1;
142 last = idx; /* this closes the cycle */
146 { /* we have to go backward */
148 idx = menu->nitems-1;
151 { /* otherwise we always go forward */
152 if (++idx >= menu->nitems)
155 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0,
157 menu->items[idx]->name.str)
162 } while (!found && (idx != last));
166 if (!((idx==(*item)->index) && passed))
168 *item = menu->items[idx];
171 /* This point is reached, if we fully cycled through the item list
172 and the only match we found is the starting item. With a NEXT_PATTERN
173 or PREV_PATTERN scan this means, that there was no additional match.
174 If we searched with an expanded new pattern, we should never reach
175 this point, because if the expanded pattern matches also the actual
176 item we will find it in the first attempt (passed==FALSE) and we
177 will never cycle through the whole item array.
179 assert( ch==0 || ch==BS );
183 if (ch && ch!=BS && menu->pindex>0)
185 /* if we had no match with a new pattern, we have to restore it */
186 Remove_Character_From_Pattern(menu);
192 /*---------------------------------------------------------------------------
193 | Facility : libnmenu
194 | Function : int menu_driver(MENU *menu, int c)
196 | Description : Central dispatcher for the menu. Translates the logical
197 | request 'c' into a menu action.
199 | Return Values : E_OK - success
200 | E_BAD_ARGUMENT - invalid menu pointer
201 | E_BAD_STATE - menu is in user hook routine
202 | E_NOT_POSTED - menu is not posted
203 +--------------------------------------------------------------------------*/
205 menu_driver (MENU * menu, int c)
207 #define NAVIGATE(dir) \
209 result = E_REQUEST_DENIED;\
215 int my_top_row, rdiff;
218 RETURN(E_BAD_ARGUMENT);
220 if ( menu->status & _IN_DRIVER )
222 if ( !( menu->status & _POSTED ) )
223 RETURN(E_NOT_POSTED);
225 item = menu->curitem;
227 my_top_row = menu->toprow;
230 if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND))
232 if (!((c==REQ_BACK_PATTERN)
233 || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH)))
235 assert( menu->pattern );
242 /*=================*/
247 /*==================*/
257 /*=================*/
262 /*=================*/
263 if (my_top_row == 0 || !(item->up))
264 result = E_REQUEST_DENIED;
273 /*=================*/
274 if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
276 /* only if the menu has less items than rows, we can deny the
277 request. Otherwise the epilogue of this routine adjusts the
278 top row if necessary */
279 result = E_REQUEST_DENIED;
288 /*=================*/
289 rdiff = menu->rows - (menu->arows + my_top_row);
290 if (rdiff > menu->arows)
293 result = E_REQUEST_DENIED;
297 while(rdiff-- > 0 && item!=(ITEM*)0)
303 /*=================*/
304 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
306 result = E_REQUEST_DENIED;
310 while(rdiff-- && item!=(ITEM*)0)
316 /*==================*/
317 item = menu->items[0];
321 /*=================*/
322 item = menu->items[menu->nitems-1];
326 /*=================*/
327 if ((item->index+1)>=menu->nitems)
329 if (menu->opt & O_NONCYCLIC)
330 result = E_REQUEST_DENIED;
332 item = menu->items[0];
335 item = menu->items[item->index + 1];
339 /*=================*/
342 if (menu->opt & O_NONCYCLIC)
343 result = E_REQUEST_DENIED;
345 item = menu->items[menu->nitems-1];
348 item = menu->items[item->index - 1];
351 case REQ_TOGGLE_ITEM:
352 /*===================*/
353 if (menu->opt & O_ONEVALUE)
355 result = E_REQUEST_DENIED;
359 if (menu->curitem->opt & O_SELECTABLE)
361 menu->curitem->value = !menu->curitem->value;
362 Move_And_Post_Item(menu,menu->curitem);
366 result = E_NOT_SELECTABLE;
370 case REQ_CLEAR_PATTERN:
371 /*=====================*/
372 /* already cleared in prologue */
375 case REQ_BACK_PATTERN:
376 /*====================*/
379 assert(menu->pattern);
380 Remove_Character_From_Pattern(menu);
381 pos_menu_cursor( menu );
384 result = E_REQUEST_DENIED;
388 /*==================*/
389 assert(menu->pattern);
390 if (menu->pattern[0])
391 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item);
394 if ((item->index+1)<menu->nitems)
395 item=menu->items[item->index+1];
398 if (menu->opt & O_NONCYCLIC)
399 result = E_REQUEST_DENIED;
401 item = menu->items[0];
407 /*==================*/
408 assert(menu->pattern);
409 if (menu->pattern[0])
410 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item);
414 item = menu->items[item->index-1];
417 if (menu->opt & O_NONCYCLIC)
418 result = E_REQUEST_DENIED;
420 item = menu->items[menu->nitems-1];
427 result = E_UNKNOWN_COMMAND;
432 { /* not a command */
433 if ( !(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(c) )
434 result = _nc_Match_Next_Character_In_Item_Name( menu, c, &item );
435 #ifdef NCURSES_MOUSE_VERSION
436 else if (KEY_MOUSE == c)
439 WINDOW* uwin = Get_Menu_UserWin(menu);
442 if ((event.bstate & (BUTTON1_CLICKED |
443 BUTTON1_DOUBLE_CLICKED |
444 BUTTON1_TRIPLE_CLICKED ))
445 && wenclose(uwin,event.y, event.x))
446 { /* we react only if the click was in the userwin, that means
447 * inside the menu display area or at the decoration window.
449 WINDOW* sub = Get_Menu_Window(menu);
450 int ry = event.y, rx = event.x; /* screen coordinates */
452 result = E_REQUEST_DENIED;
453 if (mouse_trafo(&ry,&rx,FALSE))
454 { /* rx, ry are now "curses" coordinates */
456 { /* we clicked above the display region; this is
457 * interpreted as "scroll up" request
459 if (event.bstate & BUTTON1_CLICKED)
460 result = menu_driver(menu,REQ_SCR_ULINE);
461 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
462 result = menu_driver(menu,REQ_SCR_UPAGE);
463 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
464 result = menu_driver(menu,REQ_FIRST_ITEM);
467 else if (ry >= sub->_begy + sub->_maxy)
468 { /* we clicked below the display region; this is
469 * interpreted as "scroll down" request
471 if (event.bstate & BUTTON1_CLICKED)
472 result = menu_driver(menu,REQ_SCR_DLINE);
473 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
474 result = menu_driver(menu,REQ_SCR_DPAGE);
475 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
476 result = menu_driver(menu,REQ_LAST_ITEM);
479 else if (wenclose(sub,event.y,event.x))
480 { /* Inside the area we try to find the hit item */
482 ry = event.y; rx = event.x;
483 if (wmouse_trafo(sub,&ry,&rx,FALSE))
485 for(i=0;i<menu->nitems;i++)
487 err = _nc_menu_cursor_pos(menu,menu->items[i],
493 (rx < x + menu->itemlen))
495 item = menu->items[i];
502 { /* We found an item, now we can handle the click.
503 * A single click just positions the menu cursor
504 * to the clicked item. A double click toggles
507 if (event.bstate & BUTTON1_DOUBLE_CLICKED)
509 _nc_New_TopRow_and_CurrentItem(menu,
512 menu_driver(menu,REQ_TOGGLE_ITEM);
513 result = E_UNKNOWN_COMMAND;
521 result = E_REQUEST_DENIED;
523 #endif /* NCURSES_MOUSE_VERSION */
525 result = E_UNKNOWN_COMMAND;
530 /* Adjust the top row if it turns out that the current item unfortunately
531 doesn't appear in the menu window */
532 if ( item->y < my_top_row )
533 my_top_row = item->y;
534 else if ( item->y >= (my_top_row + menu->arows) )
535 my_top_row = item->y - menu->arows + 1;
537 _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item );
544 /* m_driver.c ends here */