xref: /netbsd/lib/libmenu/driver.c (revision bf9ec67e)
1 /*	$NetBSD: driver.c,v 1.7 2001/06/13 10:45:59 wiz Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@yahoo.com.au)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  *
27  */
28 
29 #include <menu.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include "internals.h"
33 
34 /*
35  * The guts of the menu library.  This function processes the character
36  * in c and performs actions based on the value of the character.  If the
37  * character is a normal one then the driver attempts to match the character
38  * against the items.  If the character is a recognised request then the
39  * request is processed by the driver, if the character is not a recognised
40  * request and is not printable then it assumed to be a user defined command.
41  */
42 int
43 menu_driver(MENU *menu, int c)
44 {
45 	int drv_top_row, drv_scroll, it, status = E_OK;
46 	ITEM *drv_new_item;
47 
48 	if (menu == NULL)
49 		return E_BAD_ARGUMENT;
50 	if (menu->posted == 0)
51 		return E_NOT_POSTED;
52 	if (menu->items == NULL)
53 		return E_NOT_CONNECTED;
54 	if (*menu->items == NULL)
55 		return E_NOT_CONNECTED;
56 	if (menu->in_init == 1)
57 		return E_BAD_STATE;
58 
59 	  /* this one should never happen but just in case.... */
60 	if (menu->items[menu->cur_item] == NULL)
61 		return E_SYSTEM_ERROR;
62 
63 	drv_new_item = menu->items[menu->cur_item];
64 	it = menu->cur_item;
65 	drv_top_row = menu->top_row;
66 
67 	if ((c > REQ_BASE_NUM) && (c <= MAX_COMMAND)) {
68 		  /* is a known driver request  - first check if the pattern
69 		   * buffer needs to be cleared, we do this on non-search
70 		   * type requests.
71 		   */
72 		if (! ((c == REQ_BACK_PATTERN) || (c == REQ_NEXT_MATCH) ||
73 		       (c == REQ_PREV_MATCH))) {
74 			if ((c == REQ_CLEAR_PATTERN)
75 			    && (menu->pattern == NULL))
76 				return E_REQUEST_DENIED;
77 			free(menu->pattern);
78 			menu->pattern = NULL;
79 			menu->plen = 0;
80 			menu->match_len = 0;
81 		}
82 
83 		switch (c) {
84 		  case REQ_LEFT_ITEM:
85 			  drv_new_item = drv_new_item->left;
86 			  break;
87 		  case REQ_RIGHT_ITEM:
88 			  drv_new_item = drv_new_item->right;
89 			  break;
90 		  case REQ_UP_ITEM:
91 			  drv_new_item = drv_new_item->up;
92 			  break;
93 		  case REQ_DOWN_ITEM:
94 			  drv_new_item = drv_new_item->down;
95 			  break;
96 		  case REQ_SCR_ULINE:
97 			  if (drv_top_row == 0)
98 				  return E_REQUEST_DENIED;
99 			  drv_top_row--;
100 			  drv_new_item = drv_new_item->up;
101 			  break;
102 		  case REQ_SCR_DLINE:
103 			  drv_top_row++;
104 			  if ((drv_top_row + menu->rows - 1)> menu->item_rows)
105 				  return E_REQUEST_DENIED;
106 			  drv_new_item = drv_new_item->down;
107 			  break;
108 		  case REQ_SCR_DPAGE:
109 			  drv_scroll = menu->item_rows - menu->rows
110 				  - menu->top_row;
111 			  if (drv_scroll > menu->rows) {
112 				  drv_scroll = menu->rows;
113 			  }
114 
115 			  if (drv_scroll <= 0) {
116 				  return E_REQUEST_DENIED;
117 			  } else {
118 				  drv_top_row += drv_scroll;
119 				  while (drv_scroll-- > 0)
120 					  drv_new_item = drv_new_item->down;
121 			  }
122 			  break;
123 		  case REQ_SCR_UPAGE:
124 			  if (menu->rows < menu->top_row) {
125 				  drv_scroll = menu->rows;
126 			  } else {
127 				  drv_scroll = menu->top_row;
128 			  }
129 			  if (drv_scroll == 0)
130 				  return E_REQUEST_DENIED;
131 
132 			  drv_top_row -= drv_scroll;
133 			  while (drv_scroll-- > 0)
134 				  drv_new_item = drv_new_item->up;
135 			  break;
136 		  case REQ_FIRST_ITEM:
137 			  drv_new_item = menu->items[0];
138 			  break;
139 		  case REQ_LAST_ITEM:
140 			  drv_new_item = menu->items[menu->item_count - 1];
141 			  break;
142 		  case REQ_NEXT_ITEM:
143 			  if ((menu->cur_item + 1) >= menu->item_count) {
144 				  if ((menu->opts & O_NONCYCLIC)
145 				      == O_NONCYCLIC) {
146 					  return E_REQUEST_DENIED;
147 				  } else {
148 					  drv_new_item = menu->items[0];
149 				  }
150 			  } else {
151 				  drv_new_item =
152 					  menu->items[menu->cur_item + 1];
153 			  }
154 			  break;
155 		  case REQ_PREV_ITEM:
156 			  if (menu->cur_item == 0) {
157 				  if ((menu->opts & O_NONCYCLIC)
158 				      == O_NONCYCLIC) {
159 					  return E_REQUEST_DENIED;
160 				  } else {
161 					  drv_new_item = menu->items[
162 						  menu->item_count - 1];
163 				  }
164 			  } else {
165 				  drv_new_item =
166 					  menu->items[menu->cur_item - 1];
167 			  }
168 			  break;
169 		  case REQ_TOGGLE_ITEM:
170 			  if ((menu->opts & O_ONEVALUE) == O_ONEVALUE) {
171 				  return E_REQUEST_DENIED;
172 			  } else {
173 				  if ((drv_new_item->opts
174 				       & O_SELECTABLE) == O_SELECTABLE) {
175 					    /* toggle select flag */
176 					  drv_new_item->selected ^= 1;
177 					    /* update item in menu */
178 					  _menui_draw_item(menu,
179 							    drv_new_item->index);
180 				  } else {
181 					  return E_NOT_SELECTABLE;
182 				  }
183 			  }
184 			  break;
185 		  case REQ_CLEAR_PATTERN:
186 			    /* this action is taken before the
187 			       case statement */
188 			  break;
189 		  case REQ_BACK_PATTERN:
190 			  if (menu->pattern == NULL)
191 				  return E_REQUEST_DENIED;
192 
193 			  if (menu->plen == 0)
194 				  return E_REQUEST_DENIED;
195 			  menu->pattern[menu->plen--] = '\0';
196 			  break;
197 		  case REQ_NEXT_MATCH:
198 			  if (menu->pattern == NULL)
199 				  return E_REQUEST_DENIED;
200 
201 			  status = _menui_match_pattern(menu, 0,
202 							 MATCH_NEXT_FORWARD,
203 							 &it);
204 			  drv_new_item = menu->items[it];
205 			  break;
206 		  case REQ_PREV_MATCH:
207 			  if (menu->pattern == NULL)
208 				  return E_REQUEST_DENIED;
209 
210 			  status = _menui_match_pattern(menu, 0,
211 							 MATCH_NEXT_REVERSE,
212 							 &it);
213 			  drv_new_item = menu->items[it];
214 			  break;
215 		}
216 	} else if (c > MAX_COMMAND) {
217 		  /* must be a user command */
218 		return E_UNKNOWN_COMMAND;
219 	} else if (isprint((unsigned char) c)) {
220 		  /* otherwise search items for the character. */
221 		status = _menui_match_pattern(menu, (unsigned char) c,
222 					       MATCH_FORWARD, &it);
223 		drv_new_item = menu->items[it];
224 
225 		  /* update the position of the cursor if we are doing
226 		   * show match and the current item has not changed.  If
227 		   * we don't do this here it won't get done since the
228 		   * display will not be updated due to the current item
229 		   * not changing.
230 		   */
231 		if ((drv_new_item->index == menu->cur_item)
232 		    && ((menu->opts & O_SHOWMATCH) == O_SHOWMATCH)) {
233 			pos_menu_cursor(menu);
234 		}
235 
236 
237 	} else {
238 		  /* bad character */
239 		return E_BAD_ARGUMENT;
240 	}
241 
242 	if (drv_new_item == NULL)
243 		return E_REQUEST_DENIED;
244 
245 	if (drv_new_item->row < drv_top_row) drv_top_row = drv_new_item->row;
246 	if (drv_new_item->row >= (drv_top_row + menu->rows))
247 		drv_top_row = drv_new_item->row - menu->rows + 1;
248 
249 	if ((drv_new_item->index != menu->cur_item)
250 	    || (drv_top_row != menu->top_row))
251 		_menui_goto_item(menu, drv_new_item, drv_top_row);
252 
253 	return status;
254 }
255 
256 
257 
258 
259 
260