Merge branch 'vendor/GCC44'
[dragonfly.git] / contrib / ncurses-5.4 / menu / m_driver.c
blob215cb5fa5f8308af25d9b9ad35225469da19aa0d
1 /****************************************************************************
2 * Copyright (c) 1998,2000 Free Software Foundation, Inc. *
3 * *
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: *
11 * *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
14 * *
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. *
22 * *
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 *
26 * authorization. *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Juergen Pfeifer, 1995,1997 *
31 ****************************************************************************/
33 /***************************************************************************
34 * Module m_driver *
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 $")
42 /* Macros */
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 /*---------------------------------------------------------------------------
54 | Facility : libnmenu
55 | Function : static bool Is_Sub_String(
56 | bool IgnoreCaseFlag,
57 | const char *part,
58 | const char *string)
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(
66 bool IgnoreCaseFlag,
67 const char *part,
68 const char *string
71 assert( part && string );
72 if ( IgnoreCaseFlag )
74 while(*string && *part)
76 if (toupper(*string++)!=toupper(*part)) break;
77 part++;
80 else
82 while( *string && *part )
83 if (*part != *string++) break;
84 part++;
86 return ( (*part) ? FALSE : TRUE );
89 /*---------------------------------------------------------------------------
90 | Facility : libnmenu
91 | Function : int _nc_Match_Next_Character_In_Item_Name(
92 | MENU *menu,
93 | int ch,
94 | ITEM **item)
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
109 | matching item.
111 | Return Values : E_OK - an item matching the pattern was found
112 | E_NO_MATCH - nothing found
113 +--------------------------------------------------------------------------*/
114 NCURSES_EXPORT(int)
115 _nc_Match_Next_Character_In_Item_Name
116 (MENU *menu, int ch, ITEM **item)
118 bool found = FALSE, passed = FALSE;
119 int idx, last;
121 assert( menu && item && *item);
122 idx = (*item)->index;
124 if (ch && ch!=BS)
126 /* if we become to long, we need no further checking : there can't be
127 a match ! */
128 if ((menu->pindex+1) > menu->namelen)
129 RETURN(E_NO_MATCH);
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. */
137 if (--idx < 0)
138 idx = menu->nitems-1;
141 last = idx; /* this closes the cycle */
144 if (ch==BS)
145 { /* we have to go backward */
146 if (--idx < 0)
147 idx = menu->nitems-1;
149 else
150 { /* otherwise we always go forward */
151 if (++idx >= menu->nitems)
152 idx = 0;
154 if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0,
155 menu->pattern,
156 menu->items[idx]->name.str)
158 found = TRUE;
159 else
160 passed = TRUE;
161 } while (!found && (idx != last));
163 if (found)
165 if (!((idx==(*item)->index) && passed))
167 *item = menu->items[idx];
168 RETURN(E_OK);
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 );
180 else
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);
188 RETURN(E_NO_MATCH);
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 +--------------------------------------------------------------------------*/
203 NCURSES_EXPORT(int)
204 menu_driver (MENU * menu, int c)
206 #define NAVIGATE(dir) \
207 if (!item->dir)\
208 result = E_REQUEST_DENIED;\
209 else\
210 item = item->dir
212 int result = E_OK;
213 ITEM *item;
214 int my_top_row, rdiff;
216 if (!menu)
217 RETURN(E_BAD_ARGUMENT);
219 if ( menu->status & _IN_DRIVER )
220 RETURN(E_BAD_STATE);
221 if ( !( menu->status & _POSTED ) )
222 RETURN(E_NOT_POSTED);
224 item = menu->curitem;
226 my_top_row = menu->toprow;
227 assert(item);
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 );
235 Reset_Pattern(menu);
238 switch(c)
240 case REQ_LEFT_ITEM:
241 /*=================*/
242 NAVIGATE(left);
243 break;
245 case REQ_RIGHT_ITEM:
246 /*==================*/
247 NAVIGATE(right);
248 break;
250 case REQ_UP_ITEM:
251 /*===============*/
252 NAVIGATE(up);
253 break;
255 case REQ_DOWN_ITEM:
256 /*=================*/
257 NAVIGATE(down);
258 break;
260 case REQ_SCR_ULINE:
261 /*=================*/
262 if (my_top_row == 0 || !(item->up))
263 result = E_REQUEST_DENIED;
264 else
266 --my_top_row;
267 item = item->up;
269 break;
271 case REQ_SCR_DLINE:
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;
280 else {
281 my_top_row++;
282 item = item->down;
284 break;
286 case REQ_SCR_DPAGE:
287 /*=================*/
288 rdiff = menu->rows - (menu->arows + my_top_row);
289 if (rdiff > menu->arows)
290 rdiff = menu->arows;
291 if (rdiff<=0)
292 result = E_REQUEST_DENIED;
293 else
295 my_top_row += rdiff;
296 while(rdiff-- > 0 && item!=(ITEM*)0)
297 item = item->down;
299 break;
301 case REQ_SCR_UPAGE:
302 /*=================*/
303 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
304 if (rdiff<=0)
305 result = E_REQUEST_DENIED;
306 else
308 my_top_row -= rdiff;
309 while(rdiff-- && item!=(ITEM*)0)
310 item = item->up;
312 break;
314 case REQ_FIRST_ITEM:
315 /*==================*/
316 item = menu->items[0];
317 break;
319 case REQ_LAST_ITEM:
320 /*=================*/
321 item = menu->items[menu->nitems-1];
322 break;
324 case REQ_NEXT_ITEM:
325 /*=================*/
326 if ((item->index+1)>=menu->nitems)
328 if (menu->opt & O_NONCYCLIC)
329 result = E_REQUEST_DENIED;
330 else
331 item = menu->items[0];
333 else
334 item = menu->items[item->index + 1];
335 break;
337 case REQ_PREV_ITEM:
338 /*=================*/
339 if (item->index<=0)
341 if (menu->opt & O_NONCYCLIC)
342 result = E_REQUEST_DENIED;
343 else
344 item = menu->items[menu->nitems-1];
346 else
347 item = menu->items[item->index - 1];
348 break;
350 case REQ_TOGGLE_ITEM:
351 /*===================*/
352 if (menu->opt & O_ONEVALUE)
354 result = E_REQUEST_DENIED;
356 else
358 if (menu->curitem->opt & O_SELECTABLE)
360 menu->curitem->value = !menu->curitem->value;
361 Move_And_Post_Item(menu,menu->curitem);
362 _nc_Show_Menu(menu);
364 else
365 result = E_NOT_SELECTABLE;
367 break;
369 case REQ_CLEAR_PATTERN:
370 /*=====================*/
371 /* already cleared in prologue */
372 break;
374 case REQ_BACK_PATTERN:
375 /*====================*/
376 if (menu->pindex>0)
378 assert(menu->pattern);
379 Remove_Character_From_Pattern(menu);
380 pos_menu_cursor( menu );
382 else
383 result = E_REQUEST_DENIED;
384 break;
386 case REQ_NEXT_MATCH:
387 /*==================*/
388 assert(menu->pattern);
389 if (menu->pattern[0])
390 result = _nc_Match_Next_Character_In_Item_Name(menu,0,&item);
391 else
393 if ((item->index+1)<menu->nitems)
394 item=menu->items[item->index+1];
395 else
397 if (menu->opt & O_NONCYCLIC)
398 result = E_REQUEST_DENIED;
399 else
400 item = menu->items[0];
403 break;
405 case REQ_PREV_MATCH:
406 /*==================*/
407 assert(menu->pattern);
408 if (menu->pattern[0])
409 result = _nc_Match_Next_Character_In_Item_Name(menu,BS,&item);
410 else
412 if (item->index)
413 item = menu->items[item->index-1];
414 else
416 if (menu->opt & O_NONCYCLIC)
417 result = E_REQUEST_DENIED;
418 else
419 item = menu->items[menu->nitems-1];
422 break;
424 default:
425 /*======*/
426 result = E_UNKNOWN_COMMAND;
427 break;
430 else
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)
437 MEVENT event;
438 WINDOW* uwin = Get_Menu_UserWin(menu);
440 getmouse(&event);
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 */
454 if (ry < sub->_begy)
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);
464 RETURN(result);
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);
476 RETURN(result);
478 else if (wenclose(sub,event.y,event.x))
479 { /* Inside the area we try to find the hit item */
480 int i,x,y,err;
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],
487 &y, &x);
488 if (E_OK==err)
490 if ((ry==y) &&
491 (rx>=x) &&
492 (rx < x + menu->itemlen))
494 item = menu->items[i];
495 result = E_OK;
496 break;
500 if (E_OK==result)
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
504 * the item.
506 if (event.bstate & BUTTON1_DOUBLE_CLICKED)
508 _nc_New_TopRow_and_CurrentItem(menu,
509 my_top_row,
510 item);
511 menu_driver(menu,REQ_TOGGLE_ITEM);
512 result = E_UNKNOWN_COMMAND;
519 else
520 result = E_REQUEST_DENIED;
522 #endif /* NCURSES_MOUSE_VERSION */
523 else
524 result = E_UNKNOWN_COMMAND;
527 if (E_OK==result)
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 );
540 RETURN(result);
543 /* m_driver.c ends here */