1 /****************************************************************************
2 * Copyright (c) 1998-2009,2010 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.29 2010/01/23 21:20:10 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 +--------------------------------------------------------------------------*/
72 assert(part
&& string
);
75 while (*string
&& *part
)
77 if (toupper(UChar(*string
++)) != toupper(UChar(*part
)))
84 while (*string
&& *part
)
85 if (*part
!= *string
++)
89 return ((*part
) ? FALSE
: TRUE
);
92 /*---------------------------------------------------------------------------
94 | Function : int _nc_Match_Next_Character_In_Item_Name(
99 | Description : This internal routine is called for a menu positioned
100 | at an item with three different classes of characters:
101 | - a printable character; the character is added to
102 | the current pattern and the next item matching
103 | this pattern is searched.
104 | - NUL; the pattern stays as it is and the next item
105 | matching the pattern is searched
106 | - BS; the pattern stays as it is and the previous
107 | item matching the pattern is searched
109 | The item parameter contains on call a pointer to
110 | the item where the search starts. On return - if
111 | a match was found - it contains a pointer to the
114 | Return Values : E_OK - an item matching the pattern was found
115 | E_NO_MATCH - nothing found
116 +--------------------------------------------------------------------------*/
118 _nc_Match_Next_Character_In_Item_Name
119 (MENU
* menu
, int ch
, ITEM
** item
)
121 bool found
= FALSE
, passed
= FALSE
;
124 T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"),
125 (void *)menu
, ch
, (void *)item
));
127 assert(menu
&& item
&& *item
);
128 idx
= (*item
)->index
;
132 /* if we become to long, we need no further checking : there can't be
134 if ((menu
->pindex
+ 1) > menu
->namelen
)
137 Add_Character_To_Pattern(menu
, ch
);
138 /* we artificially position one item back, because in the do...while
139 loop we start with the next item. This means, that with a new
140 pattern search we always start the scan with the actual item. If
141 we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the
142 one after or before the actual item. */
144 idx
= menu
->nitems
- 1;
147 last
= idx
; /* this closes the cycle */
152 { /* we have to go backward */
154 idx
= menu
->nitems
- 1;
157 { /* otherwise we always go forward */
158 if (++idx
>= menu
->nitems
)
161 if (Is_Sub_String((bool)((menu
->opt
& O_IGNORECASE
) != 0),
163 menu
->items
[idx
]->name
.str
)
169 while (!found
&& (idx
!= last
));
173 if (!((idx
== (*item
)->index
) && passed
))
175 *item
= menu
->items
[idx
];
178 /* This point is reached, if we fully cycled through the item list
179 and the only match we found is the starting item. With a NEXT_PATTERN
180 or PREV_PATTERN scan this means, that there was no additional match.
181 If we searched with an expanded new pattern, we should never reach
182 this point, because if the expanded pattern matches also the actual
183 item we will find it in the first attempt (passed == FALSE) and we
184 will never cycle through the whole item array.
186 assert(ch
== 0 || ch
== BS
);
190 if (ch
&& ch
!= BS
&& menu
->pindex
> 0)
192 /* if we had no match with a new pattern, we have to restore it */
193 Remove_Character_From_Pattern(menu
);
199 /*---------------------------------------------------------------------------
200 | Facility : libnmenu
201 | Function : int menu_driver(MENU* menu, int c)
203 | Description : Central dispatcher for the menu. Translates the logical
204 | request 'c' into a menu action.
206 | Return Values : E_OK - success
207 | E_BAD_ARGUMENT - invalid menu pointer
208 | E_BAD_STATE - menu is in user hook routine
209 | E_NOT_POSTED - menu is not posted
210 +--------------------------------------------------------------------------*/
212 menu_driver(MENU
* menu
, int c
)
214 #define NAVIGATE(dir) \
216 result = E_REQUEST_DENIED;\
222 int my_top_row
, rdiff
;
224 T((T_CALLED("menu_driver(%p,%d)"), (void *)menu
, c
));
227 RETURN(E_BAD_ARGUMENT
);
229 if (menu
->status
& _IN_DRIVER
)
231 if (!(menu
->status
& _POSTED
))
232 RETURN(E_NOT_POSTED
);
234 item
= menu
->curitem
;
236 my_top_row
= menu
->toprow
;
239 if ((c
> KEY_MAX
) && (c
<= MAX_MENU_COMMAND
))
241 if (!((c
== REQ_BACK_PATTERN
)
242 || (c
== REQ_NEXT_MATCH
) || (c
== REQ_PREV_MATCH
)))
244 assert(menu
->pattern
);
251 /*=================*/
256 /*==================*/
266 /*=================*/
271 /*=================*/
272 if (my_top_row
== 0 || !(item
->up
))
273 result
= E_REQUEST_DENIED
;
282 /*=================*/
283 if ((my_top_row
+ menu
->arows
>= menu
->rows
) || !(item
->down
))
285 /* only if the menu has less items than rows, we can deny the
286 request. Otherwise the epilogue of this routine adjusts the
287 top row if necessary */
288 result
= E_REQUEST_DENIED
;
298 /*=================*/
299 rdiff
= menu
->rows
- (menu
->arows
+ my_top_row
);
300 if (rdiff
> menu
->arows
)
303 result
= E_REQUEST_DENIED
;
307 while (rdiff
-- > 0 && item
!= 0 && item
->down
!= 0)
313 /*=================*/
314 rdiff
= (menu
->arows
< my_top_row
) ? menu
->arows
: my_top_row
;
316 result
= E_REQUEST_DENIED
;
320 while (rdiff
-- > 0 && item
!= 0 && item
->up
!= 0)
326 /*==================*/
327 item
= menu
->items
[0];
331 /*=================*/
332 item
= menu
->items
[menu
->nitems
- 1];
336 /*=================*/
337 if ((item
->index
+ 1) >= menu
->nitems
)
339 if (menu
->opt
& O_NONCYCLIC
)
340 result
= E_REQUEST_DENIED
;
342 item
= menu
->items
[0];
345 item
= menu
->items
[item
->index
+ 1];
349 /*=================*/
350 if (item
->index
<= 0)
352 if (menu
->opt
& O_NONCYCLIC
)
353 result
= E_REQUEST_DENIED
;
355 item
= menu
->items
[menu
->nitems
- 1];
358 item
= menu
->items
[item
->index
- 1];
361 case REQ_TOGGLE_ITEM
:
362 /*===================*/
363 if (menu
->opt
& O_ONEVALUE
)
365 result
= E_REQUEST_DENIED
;
369 if (menu
->curitem
->opt
& O_SELECTABLE
)
371 menu
->curitem
->value
= !menu
->curitem
->value
;
372 Move_And_Post_Item(menu
, menu
->curitem
);
376 result
= E_NOT_SELECTABLE
;
380 case REQ_CLEAR_PATTERN
:
381 /*=====================*/
382 /* already cleared in prologue */
385 case REQ_BACK_PATTERN
:
386 /*====================*/
387 if (menu
->pindex
> 0)
389 assert(menu
->pattern
);
390 Remove_Character_From_Pattern(menu
);
391 pos_menu_cursor(menu
);
394 result
= E_REQUEST_DENIED
;
398 /*==================*/
399 assert(menu
->pattern
);
400 if (menu
->pattern
[0])
401 result
= _nc_Match_Next_Character_In_Item_Name(menu
, 0, &item
);
404 if ((item
->index
+ 1) < menu
->nitems
)
405 item
= menu
->items
[item
->index
+ 1];
408 if (menu
->opt
& O_NONCYCLIC
)
409 result
= E_REQUEST_DENIED
;
411 item
= menu
->items
[0];
417 /*==================*/
418 assert(menu
->pattern
);
419 if (menu
->pattern
[0])
420 result
= _nc_Match_Next_Character_In_Item_Name(menu
, BS
, &item
);
424 item
= menu
->items
[item
->index
- 1];
427 if (menu
->opt
& O_NONCYCLIC
)
428 result
= E_REQUEST_DENIED
;
430 item
= menu
->items
[menu
->nitems
- 1];
437 result
= E_UNKNOWN_COMMAND
;
442 { /* not a command */
443 if (!(c
& ~((int)MAX_REGULAR_CHARACTER
)) && isprint(UChar(c
)))
444 result
= _nc_Match_Next_Character_In_Item_Name(menu
, c
, &item
);
445 #ifdef NCURSES_MOUSE_VERSION
446 else if (KEY_MOUSE
== c
)
449 WINDOW
*uwin
= Get_Menu_UserWin(menu
);
452 if ((event
.bstate
& (BUTTON1_CLICKED
|
453 BUTTON1_DOUBLE_CLICKED
|
454 BUTTON1_TRIPLE_CLICKED
))
455 && wenclose(uwin
, event
.y
, event
.x
))
456 { /* we react only if the click was in the userwin, that means
457 * inside the menu display area or at the decoration window.
459 WINDOW
*sub
= Get_Menu_Window(menu
);
460 int ry
= event
.y
, rx
= event
.x
; /* screen coordinates */
462 result
= E_REQUEST_DENIED
;
463 if (mouse_trafo(&ry
, &rx
, FALSE
))
464 { /* rx, ry are now "curses" coordinates */
466 { /* we clicked above the display region; this is
467 * interpreted as "scroll up" request
469 if (event
.bstate
& BUTTON1_CLICKED
)
470 result
= menu_driver(menu
, REQ_SCR_ULINE
);
471 else if (event
.bstate
& BUTTON1_DOUBLE_CLICKED
)
472 result
= menu_driver(menu
, REQ_SCR_UPAGE
);
473 else if (event
.bstate
& BUTTON1_TRIPLE_CLICKED
)
474 result
= menu_driver(menu
, REQ_FIRST_ITEM
);
477 else if (ry
> sub
->_begy
+ sub
->_maxy
)
478 { /* we clicked below the display region; this is
479 * interpreted as "scroll down" request
481 if (event
.bstate
& BUTTON1_CLICKED
)
482 result
= menu_driver(menu
, REQ_SCR_DLINE
);
483 else if (event
.bstate
& BUTTON1_DOUBLE_CLICKED
)
484 result
= menu_driver(menu
, REQ_SCR_DPAGE
);
485 else if (event
.bstate
& BUTTON1_TRIPLE_CLICKED
)
486 result
= menu_driver(menu
, REQ_LAST_ITEM
);
489 else if (wenclose(sub
, event
.y
, event
.x
))
490 { /* Inside the area we try to find the hit item */
495 if (wmouse_trafo(sub
, &ry
, &rx
, FALSE
))
497 for (i
= 0; i
< menu
->nitems
; i
++)
499 err
= _nc_menu_cursor_pos(menu
, menu
->items
[i
],
505 (rx
< x
+ menu
->itemlen
))
507 item
= menu
->items
[i
];
514 { /* We found an item, now we can handle the click.
515 * A single click just positions the menu cursor
516 * to the clicked item. A double click toggles
519 if (event
.bstate
& BUTTON1_DOUBLE_CLICKED
)
521 _nc_New_TopRow_and_CurrentItem(menu
,
524 menu_driver(menu
, REQ_TOGGLE_ITEM
);
525 result
= E_UNKNOWN_COMMAND
;
533 result
= E_REQUEST_DENIED
;
535 #endif /* NCURSES_MOUSE_VERSION */
537 result
= E_UNKNOWN_COMMAND
;
542 /* Adjust the top row if it turns out that the current item unfortunately
543 doesn't appear in the menu window */
544 if (item
->y
< my_top_row
)
545 my_top_row
= item
->y
;
546 else if (item
->y
>= (my_top_row
+ menu
->arows
))
547 my_top_row
= item
->y
- menu
->arows
+ 1;
549 _nc_New_TopRow_and_CurrentItem(menu
, my_top_row
, item
);
556 /* m_driver.c ends here */