1 /****************************************************************************
2 * Copyright 2020 Thomas E. Dickey *
3 * Copyright 1998-2012,2016 Free Software Foundation, Inc. *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
28 ****************************************************************************/
30 /****************************************************************************
31 * Author: Juergen Pfeifer, 1995,1997 *
32 ****************************************************************************/
34 /***************************************************************************
36 * Central dispatching routine *
37 ***************************************************************************/
39 #include "menu.priv.h"
41 MODULE_ID("$Id: m_driver.c,v 1.35 2020/05/24 01:40:20 anonymous.maarten 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)++] = (char) (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 +--------------------------------------------------------------------------*/
73 assert(part && string);
76 while (*string && *part)
78 if (toupper(UChar(*string++)) != toupper(UChar(*part)))
85 while (*string && *part)
86 if (*part != *string++)
90 return ((*part) ? FALSE : TRUE);
93 /*---------------------------------------------------------------------------
95 | Function : int _nc_Match_Next_Character_In_Item_Name(
100 | Description : This internal routine is called for a menu positioned
101 | at an item with three different classes of characters:
102 | - a printable character; the character is added to
103 | the current pattern and the next item matching
104 | this pattern is searched.
105 | - NUL; the pattern stays as it is and the next item
106 | matching the pattern is searched
107 | - BS; the pattern stays as it is and the previous
108 | item matching the pattern is searched
110 | The item parameter contains on call a pointer to
111 | the item where the search starts. On return - if
112 | a match was found - it contains a pointer to the
115 | Return Values : E_OK - an item matching the pattern was found
116 | E_NO_MATCH - nothing found
117 +--------------------------------------------------------------------------*/
119 _nc_Match_Next_Character_In_Item_Name
120 (MENU * menu, int ch, ITEM ** item)
122 bool found = FALSE, passed = FALSE;
125 T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"),
126 (void *)menu, ch, (void *)item));
128 assert(menu && item && *item);
129 idx = (*item)->index;
133 /* if we become to long, we need no further checking : there can't be
135 if ((menu->pindex + 1) > menu->namelen)
138 Add_Character_To_Pattern(menu, ch);
139 /* we artificially position one item back, because in the do...while
140 loop we start with the next item. This means, that with a new
141 pattern search we always start the scan with the actual item. If
142 we do a NEXT_PATTERN or PREV_PATTERN search, we start with the
143 one after or before the actual item. */
145 idx = menu->nitems - 1;
148 last = idx; /* this closes the cycle */
153 { /* we have to go backward */
155 idx = menu->nitems - 1;
158 { /* otherwise we always go forward */
159 if (++idx >= menu->nitems)
162 if (Is_Sub_String((bool)((menu->opt & O_IGNORECASE) != 0),
164 menu->items[idx]->name.str)
170 while (!found && (idx != last));
174 if (!((idx == (*item)->index) && passed))
176 *item = menu->items[idx];
179 /* This point is reached, if we fully cycled through the item list
180 and the only match we found is the starting item. With a NEXT_PATTERN
181 or PREV_PATTERN scan this means, that there was no additional match.
182 If we searched with an expanded new pattern, we should never reach
183 this point, because if the expanded pattern matches also the actual
184 item we will find it in the first attempt (passed==FALSE) and we
185 will never cycle through the whole item array.
187 assert(ch == 0 || ch == BS);
191 if (ch && ch != BS && menu->pindex > 0)
193 /* if we had no match with a new pattern, we have to restore it */
194 Remove_Character_From_Pattern(menu);
200 /*---------------------------------------------------------------------------
201 | Facility : libnmenu
202 | Function : int menu_driver(MENU* menu, int c)
204 | Description : Central dispatcher for the menu. Translates the logical
205 | request 'c' into a menu action.
207 | Return Values : E_OK - success
208 | E_BAD_ARGUMENT - invalid menu pointer
209 | E_BAD_STATE - menu is in user hook routine
210 | E_NOT_POSTED - menu is not posted
211 +--------------------------------------------------------------------------*/
213 menu_driver(MENU * menu, int c)
215 #define NAVIGATE(dir) \
217 result = E_REQUEST_DENIED;\
223 int my_top_row, rdiff;
225 T((T_CALLED("menu_driver(%p,%d)"), (void *)menu, c));
228 RETURN(E_BAD_ARGUMENT);
230 if (menu->status & _IN_DRIVER)
232 if (!(menu->status & _POSTED))
233 RETURN(E_NOT_POSTED);
235 item = menu->curitem;
237 my_top_row = menu->toprow;
240 if ((c > KEY_MAX) && (c <= MAX_MENU_COMMAND))
242 if (!((c == REQ_BACK_PATTERN)
243 || (c == REQ_NEXT_MATCH) || (c == REQ_PREV_MATCH)))
245 assert(menu->pattern);
252 /*=================*/
257 /*==================*/
267 /*=================*/
272 /*=================*/
273 if (my_top_row == 0 || !(item->up))
274 result = E_REQUEST_DENIED;
283 /*=================*/
284 if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
286 /* only if the menu has less items than rows, we can deny the
287 request. Otherwise the epilogue of this routine adjusts the
288 top row if necessary */
289 result = E_REQUEST_DENIED;
299 /*=================*/
300 rdiff = menu->rows - (menu->arows + my_top_row);
301 if (rdiff > menu->arows)
304 result = E_REQUEST_DENIED;
308 while (rdiff-- > 0 && item != 0 && item->down != 0)
314 /*=================*/
315 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
317 result = E_REQUEST_DENIED;
321 while (rdiff-- > 0 && item != 0 && item->up != 0)
327 /*==================*/
328 item = menu->items[0];
332 /*=================*/
333 item = menu->items[menu->nitems - 1];
337 /*=================*/
338 if ((item->index + 1) >= menu->nitems)
340 if (menu->opt & O_NONCYCLIC)
341 result = E_REQUEST_DENIED;
343 item = menu->items[0];
346 item = menu->items[item->index + 1];
350 /*=================*/
351 if (item->index <= 0)
353 if (menu->opt & O_NONCYCLIC)
354 result = E_REQUEST_DENIED;
356 item = menu->items[menu->nitems - 1];
359 item = menu->items[item->index - 1];
362 case REQ_TOGGLE_ITEM:
363 /*===================*/
364 if (menu->opt & O_ONEVALUE)
366 result = E_REQUEST_DENIED;
370 if (menu->curitem->opt & O_SELECTABLE)
372 menu->curitem->value = !menu->curitem->value;
373 Move_And_Post_Item(menu, menu->curitem);
377 result = E_NOT_SELECTABLE;
381 case REQ_CLEAR_PATTERN:
382 /*=====================*/
383 /* already cleared in prologue */
386 case REQ_BACK_PATTERN:
387 /*====================*/
388 if (menu->pindex > 0)
390 assert(menu->pattern);
391 Remove_Character_From_Pattern(menu);
392 pos_menu_cursor(menu);
395 result = E_REQUEST_DENIED;
399 /*==================*/
400 assert(menu->pattern);
401 if (menu->pattern[0])
402 result = _nc_Match_Next_Character_In_Item_Name(menu, 0, &item);
405 if ((item->index + 1) < menu->nitems)
406 item = menu->items[item->index + 1];
409 if (menu->opt & O_NONCYCLIC)
410 result = E_REQUEST_DENIED;
412 item = menu->items[0];
418 /*==================*/
419 assert(menu->pattern);
420 if (menu->pattern[0])
421 result = _nc_Match_Next_Character_In_Item_Name(menu, BS, &item);
425 item = menu->items[item->index - 1];
428 if (menu->opt & O_NONCYCLIC)
429 result = E_REQUEST_DENIED;
431 item = menu->items[menu->nitems - 1];
438 result = E_UNKNOWN_COMMAND;
443 { /* not a command */
444 if (!(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(UChar(c)))
445 result = _nc_Match_Next_Character_In_Item_Name(menu, c, &item);
446 #ifdef NCURSES_MOUSE_VERSION
447 else if (KEY_MOUSE == c)
450 WINDOW *uwin = Get_Menu_UserWin(menu);
453 if ((event.bstate & (BUTTON1_CLICKED |
454 BUTTON1_DOUBLE_CLICKED |
455 BUTTON1_TRIPLE_CLICKED))
456 && wenclose(uwin, event.y, event.x))
457 { /* we react only if the click was in the userwin, that means
458 * inside the menu display area or at the decoration window.
460 WINDOW *sub = Get_Menu_Window(menu);
461 int ry = event.y, rx = event.x; /* screen coordinates */
463 result = E_REQUEST_DENIED;
464 if (mouse_trafo(&ry, &rx, FALSE))
465 { /* rx, ry are now "curses" coordinates */
467 { /* we clicked above the display region; this is
468 * interpreted as "scroll up" request
470 if (event.bstate & BUTTON1_CLICKED)
471 result = menu_driver(menu, REQ_SCR_ULINE);
472 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
473 result = menu_driver(menu, REQ_SCR_UPAGE);
474 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
475 result = menu_driver(menu, REQ_FIRST_ITEM);
478 else if (ry > sub->_begy + sub->_maxy)
479 { /* we clicked below the display region; this is
480 * interpreted as "scroll down" request
482 if (event.bstate & BUTTON1_CLICKED)
483 result = menu_driver(menu, REQ_SCR_DLINE);
484 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
485 result = menu_driver(menu, REQ_SCR_DPAGE);
486 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
487 result = menu_driver(menu, REQ_LAST_ITEM);
490 else if (wenclose(sub, event.y, event.x))
491 { /* Inside the area we try to find the hit item */
496 if (wmouse_trafo(sub, &ry, &rx, FALSE))
498 for (i = 0; i < menu->nitems; i++)
500 err = _nc_menu_cursor_pos(menu, menu->items[i],
506 (rx < x + menu->itemlen))
508 item = menu->items[i];
515 { /* We found an item, now we can handle the click.
516 * A single click just positions the menu cursor
517 * to the clicked item. A double click toggles
520 if (event.bstate & BUTTON1_DOUBLE_CLICKED)
522 _nc_New_TopRow_and_CurrentItem(menu,
525 menu_driver(menu, REQ_TOGGLE_ITEM);
526 result = E_UNKNOWN_COMMAND;
535 if (menu->opt & O_MOUSE_MENU)
536 ungetmouse(&event); /* let someone else handle this */
537 result = E_REQUEST_DENIED;
540 #endif /* NCURSES_MOUSE_VERSION */
542 result = E_UNKNOWN_COMMAND;
547 result = E_BAD_STATE;
549 else if (E_OK == result)
551 /* Adjust the top row if it turns out that the current item unfortunately
552 doesn't appear in the menu window */
553 if (item->y < my_top_row)
554 my_top_row = item->y;
555 else if (item->y >= (my_top_row + menu->arows))
556 my_top_row = item->y - menu->arows + 1;
558 _nc_New_TopRow_and_CurrentItem(menu, my_top_row, item);
565 /* m_driver.c ends here */