1*84d9c625SLionel Sambuc /* $NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $ */
2b618a1eeSThomas Cort
3b618a1eeSThomas Cort /*-
4b618a1eeSThomas Cort * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@yahoo.com.au)
5b618a1eeSThomas Cort * All rights reserved.
6b618a1eeSThomas Cort *
7b618a1eeSThomas Cort * Redistribution and use in source and binary forms, with or without
8b618a1eeSThomas Cort * modification, are permitted provided that the following conditions
9b618a1eeSThomas Cort * are met:
10b618a1eeSThomas Cort * 1. Redistributions of source code must retain the above copyright
11b618a1eeSThomas Cort * notice, this list of conditions and the following disclaimer.
12b618a1eeSThomas Cort * 2. The name of the author may not be used to endorse or promote products
13b618a1eeSThomas Cort * derived from this software without specific prior written permission
14b618a1eeSThomas Cort *
15b618a1eeSThomas Cort * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16b618a1eeSThomas Cort * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17b618a1eeSThomas Cort * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18b618a1eeSThomas Cort * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19b618a1eeSThomas Cort * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20b618a1eeSThomas Cort * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21b618a1eeSThomas Cort * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22b618a1eeSThomas Cort * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23b618a1eeSThomas Cort * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24b618a1eeSThomas Cort * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25b618a1eeSThomas Cort *
26b618a1eeSThomas Cort *
27b618a1eeSThomas Cort */
28b618a1eeSThomas Cort
29b618a1eeSThomas Cort #include <sys/cdefs.h>
30*84d9c625SLionel Sambuc __RCSID("$NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $");
31b618a1eeSThomas Cort
32b618a1eeSThomas Cort #include <menu.h>
33b618a1eeSThomas Cort #include <ctype.h>
34b618a1eeSThomas Cort #include <stdlib.h>
35b618a1eeSThomas Cort #include <string.h>
36b618a1eeSThomas Cort #include "internals.h"
37b618a1eeSThomas Cort
38b618a1eeSThomas Cort /* internal function prototypes */
39b618a1eeSThomas Cort static void
40b618a1eeSThomas Cort _menui_calc_neighbours(MENU *menu, int item_no);
41b618a1eeSThomas Cort static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item);
42b618a1eeSThomas Cort
43b618a1eeSThomas Cort /*
44b618a1eeSThomas Cort * Link all the menu items together to speed up navigation. We need
45b618a1eeSThomas Cort * to calculate the widest item entry, then work out how many columns
46b618a1eeSThomas Cort * of items the window will accommodate and then how many rows there will
47b618a1eeSThomas Cort * be. Once the layout is determined the neighbours of each item is
48b618a1eeSThomas Cort * calculated and the item structures updated.
49b618a1eeSThomas Cort */
50b618a1eeSThomas Cort int
_menui_stitch_items(MENU * menu)51b618a1eeSThomas Cort _menui_stitch_items(MENU *menu)
52b618a1eeSThomas Cort {
53b618a1eeSThomas Cort int i, row_major;
54b618a1eeSThomas Cort
55b618a1eeSThomas Cort row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
56b618a1eeSThomas Cort
57b618a1eeSThomas Cort if (menu->posted == 1)
58b618a1eeSThomas Cort return E_POSTED;
59b618a1eeSThomas Cort if (menu->items == NULL)
60b618a1eeSThomas Cort return E_BAD_ARGUMENT;
61b618a1eeSThomas Cort
62b618a1eeSThomas Cort menu->item_rows = menu->item_count / menu->cols;
63b618a1eeSThomas Cort menu->item_cols = menu->cols;
64b618a1eeSThomas Cort if (menu->item_count > (menu->item_rows * menu->item_cols))
65b618a1eeSThomas Cort menu->item_rows += 1;
66b618a1eeSThomas Cort
67b618a1eeSThomas Cort _menui_max_item_size(menu);
68b618a1eeSThomas Cort
69b618a1eeSThomas Cort for (i = 0; i < menu->item_count; i++) {
70b618a1eeSThomas Cort /* fill in the row and column value of the item */
71b618a1eeSThomas Cort if (row_major) {
72b618a1eeSThomas Cort menu->items[i]->row = i / menu->item_cols;
73b618a1eeSThomas Cort menu->items[i]->col = i % menu->item_cols;
74b618a1eeSThomas Cort } else {
75b618a1eeSThomas Cort menu->items[i]->row = i % menu->item_rows;
76b618a1eeSThomas Cort menu->items[i]->col = i / menu->item_rows;
77b618a1eeSThomas Cort }
78b618a1eeSThomas Cort
79b618a1eeSThomas Cort _menui_calc_neighbours(menu, i);
80b618a1eeSThomas Cort }
81b618a1eeSThomas Cort
82b618a1eeSThomas Cort return E_OK;
83b618a1eeSThomas Cort }
84b618a1eeSThomas Cort
85b618a1eeSThomas Cort /*
86b618a1eeSThomas Cort * Calculate the neighbours for an item in menu.
87b618a1eeSThomas Cort */
88b618a1eeSThomas Cort static void
_menui_calc_neighbours(MENU * menu,int item_no)89b618a1eeSThomas Cort _menui_calc_neighbours(MENU *menu, int item_no)
90b618a1eeSThomas Cort {
91b618a1eeSThomas Cort int neighbour, cycle, row_major, edge;
92b618a1eeSThomas Cort ITEM *item;
93b618a1eeSThomas Cort
94b618a1eeSThomas Cort row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
95b618a1eeSThomas Cort cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC);
96b618a1eeSThomas Cort item = menu->items[item_no];
97b618a1eeSThomas Cort
98b618a1eeSThomas Cort if (menu->item_rows < 2) {
99b618a1eeSThomas Cort if (cycle) {
100b618a1eeSThomas Cort item->up = item;
101b618a1eeSThomas Cort item->down = item;
102b618a1eeSThomas Cort } else {
103b618a1eeSThomas Cort item->up = NULL;
104b618a1eeSThomas Cort item->down = NULL;
105b618a1eeSThomas Cort }
106b618a1eeSThomas Cort } else {
107b618a1eeSThomas Cort
108b618a1eeSThomas Cort /* up */
109b618a1eeSThomas Cort if (menu->item_cols < 2) {
110b618a1eeSThomas Cort if (item_no == 0) {
111b618a1eeSThomas Cort if (cycle)
112b618a1eeSThomas Cort item->up =
113b618a1eeSThomas Cort menu->items[menu->item_count - 1];
114b618a1eeSThomas Cort else
115b618a1eeSThomas Cort item->up = NULL;
116b618a1eeSThomas Cort } else
117b618a1eeSThomas Cort item->up = menu->items[item_no - 1];
118b618a1eeSThomas Cort } else {
119b618a1eeSThomas Cort edge = 0;
120b618a1eeSThomas Cort if (row_major) {
121b618a1eeSThomas Cort if (item->row == 0) {
122b618a1eeSThomas Cort neighbour =
123b618a1eeSThomas Cort (menu->item_rows - 1) * menu->item_cols
124b618a1eeSThomas Cort + item->col;
125b618a1eeSThomas Cort if (neighbour >= menu->item_count)
126b618a1eeSThomas Cort neighbour -= menu->item_cols;
127b618a1eeSThomas Cort edge = 1;
128b618a1eeSThomas Cort } else
129b618a1eeSThomas Cort neighbour = item_no - menu->item_cols;
130b618a1eeSThomas Cort } else {
131b618a1eeSThomas Cort if (item->row == 0) {
132b618a1eeSThomas Cort neighbour = menu->item_rows * item->col
133b618a1eeSThomas Cort + menu->item_rows - 1;
134b618a1eeSThomas Cort if (neighbour >= menu->item_count)
135b618a1eeSThomas Cort neighbour = menu->item_count - 1;
136b618a1eeSThomas Cort edge = 1;
137b618a1eeSThomas Cort } else
138b618a1eeSThomas Cort neighbour = item_no - 1;
139b618a1eeSThomas Cort }
140b618a1eeSThomas Cort
141b618a1eeSThomas Cort
142b618a1eeSThomas Cort item->up = menu->items[neighbour];
143b618a1eeSThomas Cort if ((!cycle) && (edge == 1))
144b618a1eeSThomas Cort item->up = NULL;
145b618a1eeSThomas Cort }
146b618a1eeSThomas Cort
147b618a1eeSThomas Cort /* Down */
148b618a1eeSThomas Cort if (menu->item_cols < 2) {
149b618a1eeSThomas Cort if (item_no == (menu->item_count - 1)) {
150b618a1eeSThomas Cort if (cycle)
151b618a1eeSThomas Cort item->down = menu->items[0];
152b618a1eeSThomas Cort else
153b618a1eeSThomas Cort item->down = NULL;
154b618a1eeSThomas Cort } else
155b618a1eeSThomas Cort item->down = menu->items[item_no + 1];
156b618a1eeSThomas Cort } else {
157b618a1eeSThomas Cort edge = 0;
158b618a1eeSThomas Cort if (row_major) {
159b618a1eeSThomas Cort if (item->row == menu->item_rows - 1) {
160b618a1eeSThomas Cort neighbour = item->col;
161b618a1eeSThomas Cort edge = 1;
162b618a1eeSThomas Cort } else {
163b618a1eeSThomas Cort neighbour = item_no + menu->item_cols;
164b618a1eeSThomas Cort if (neighbour >= menu->item_count) {
165b618a1eeSThomas Cort neighbour = item->col;
166b618a1eeSThomas Cort edge = 1;
167b618a1eeSThomas Cort }
168b618a1eeSThomas Cort }
169b618a1eeSThomas Cort } else {
170b618a1eeSThomas Cort if (item->row == menu->item_rows - 1) {
171b618a1eeSThomas Cort neighbour = item->col * menu->item_rows;
172b618a1eeSThomas Cort edge = 1;
173b618a1eeSThomas Cort } else {
174b618a1eeSThomas Cort neighbour = item_no + 1;
175b618a1eeSThomas Cort if (neighbour >= menu->item_count) {
176b618a1eeSThomas Cort neighbour = item->col
177b618a1eeSThomas Cort * menu->item_rows;
178b618a1eeSThomas Cort edge = 1;
179b618a1eeSThomas Cort }
180b618a1eeSThomas Cort }
181b618a1eeSThomas Cort }
182b618a1eeSThomas Cort
183b618a1eeSThomas Cort item->down = menu->items[neighbour];
184b618a1eeSThomas Cort if ((!cycle) && (edge == 1))
185b618a1eeSThomas Cort item->down = NULL;
186b618a1eeSThomas Cort }
187b618a1eeSThomas Cort }
188b618a1eeSThomas Cort
189b618a1eeSThomas Cort if (menu->item_cols < 2) {
190b618a1eeSThomas Cort if (cycle) {
191b618a1eeSThomas Cort item->left = item;
192b618a1eeSThomas Cort item->right = item;
193b618a1eeSThomas Cort } else {
194b618a1eeSThomas Cort item->left = NULL;
195b618a1eeSThomas Cort item->right = NULL;
196b618a1eeSThomas Cort }
197b618a1eeSThomas Cort } else {
198b618a1eeSThomas Cort /* left */
199b618a1eeSThomas Cort if (menu->item_rows < 2) {
200b618a1eeSThomas Cort if (item_no == 0) {
201b618a1eeSThomas Cort if (cycle)
202b618a1eeSThomas Cort item->left =
203b618a1eeSThomas Cort menu->items[menu->item_count - 1];
204b618a1eeSThomas Cort else
205b618a1eeSThomas Cort item->left = NULL;
206b618a1eeSThomas Cort } else
207b618a1eeSThomas Cort item->left = menu->items[item_no - 1];
208b618a1eeSThomas Cort } else {
209b618a1eeSThomas Cort edge = 0;
210b618a1eeSThomas Cort if (row_major) {
211b618a1eeSThomas Cort if (item->col == 0) {
212b618a1eeSThomas Cort neighbour = item_no + menu->cols - 1;
213b618a1eeSThomas Cort if (neighbour >= menu->item_count)
214b618a1eeSThomas Cort neighbour = menu->item_count - 1;
215b618a1eeSThomas Cort edge = 1;
216b618a1eeSThomas Cort } else
217b618a1eeSThomas Cort neighbour = item_no - 1;
218b618a1eeSThomas Cort } else {
219b618a1eeSThomas Cort if (item->col == 0) {
220b618a1eeSThomas Cort neighbour = menu->item_rows
221b618a1eeSThomas Cort * (menu->item_cols - 1) + item->row;
222b618a1eeSThomas Cort if (neighbour >= menu->item_count)
223b618a1eeSThomas Cort neighbour -= menu->item_rows;
224b618a1eeSThomas Cort edge = 1;
225b618a1eeSThomas Cort } else
226b618a1eeSThomas Cort neighbour = item_no - menu->item_rows;
227b618a1eeSThomas Cort }
228b618a1eeSThomas Cort
229b618a1eeSThomas Cort item->left = menu->items[neighbour];
230b618a1eeSThomas Cort if ((!cycle) && (edge == 1))
231b618a1eeSThomas Cort item->left = NULL;
232b618a1eeSThomas Cort }
233b618a1eeSThomas Cort
234b618a1eeSThomas Cort /* right */
235b618a1eeSThomas Cort if (menu->item_rows < 2) {
236b618a1eeSThomas Cort if (item_no == menu->item_count - 1) {
237b618a1eeSThomas Cort if (cycle)
238b618a1eeSThomas Cort item->right = menu->items[0];
239b618a1eeSThomas Cort else
240b618a1eeSThomas Cort item->right = NULL;
241b618a1eeSThomas Cort } else
242b618a1eeSThomas Cort item->right = menu->items[item_no + 1];
243b618a1eeSThomas Cort } else {
244b618a1eeSThomas Cort edge = 0;
245b618a1eeSThomas Cort if (row_major) {
246b618a1eeSThomas Cort if (item->col == menu->item_cols - 1) {
247b618a1eeSThomas Cort neighbour = item_no - menu->item_cols
248b618a1eeSThomas Cort + 1;
249b618a1eeSThomas Cort edge = 1;
250b618a1eeSThomas Cort } else if (item_no == menu->item_count - 1) {
251b618a1eeSThomas Cort neighbour = item->row * menu->item_cols;
252b618a1eeSThomas Cort edge = 1;
253b618a1eeSThomas Cort } else
254b618a1eeSThomas Cort neighbour = item_no + 1;
255b618a1eeSThomas Cort } else {
256b618a1eeSThomas Cort if (item->col == menu->item_cols - 1) {
257b618a1eeSThomas Cort neighbour = item->row;
258b618a1eeSThomas Cort edge = 1;
259b618a1eeSThomas Cort } else {
260b618a1eeSThomas Cort neighbour = item_no + menu->item_rows;
261b618a1eeSThomas Cort if (neighbour >= menu->item_count) {
262b618a1eeSThomas Cort neighbour = item->row;
263b618a1eeSThomas Cort edge = 1;
264b618a1eeSThomas Cort }
265b618a1eeSThomas Cort }
266b618a1eeSThomas Cort }
267b618a1eeSThomas Cort
268b618a1eeSThomas Cort item->right = menu->items[neighbour];
269b618a1eeSThomas Cort if ((!cycle) && (edge == 1))
270b618a1eeSThomas Cort item->right = NULL;
271b618a1eeSThomas Cort }
272b618a1eeSThomas Cort }
273b618a1eeSThomas Cort }
274b618a1eeSThomas Cort
275b618a1eeSThomas Cort /*
276b618a1eeSThomas Cort * Goto the item pointed to by item and adjust the menu structure
277b618a1eeSThomas Cort * accordingly. Call the term and init functions if required.
278b618a1eeSThomas Cort */
279b618a1eeSThomas Cort int
_menui_goto_item(MENU * menu,ITEM * item,int new_top_row)280b618a1eeSThomas Cort _menui_goto_item(MENU *menu, ITEM *item, int new_top_row)
281b618a1eeSThomas Cort {
282b618a1eeSThomas Cort int old_top_row = menu->top_row, old_cur_item = menu->cur_item;
283b618a1eeSThomas Cort
284b618a1eeSThomas Cort /* If we get a null then the menu is not cyclic so deny request */
285b618a1eeSThomas Cort if (item == NULL)
286b618a1eeSThomas Cort return E_REQUEST_DENIED;
287b618a1eeSThomas Cort
288b618a1eeSThomas Cort menu->in_init = 1;
289b618a1eeSThomas Cort if (menu->top_row != new_top_row) {
290b618a1eeSThomas Cort if ((menu->posted == 1) && (menu->menu_term != NULL))
291b618a1eeSThomas Cort menu->menu_term(menu);
292b618a1eeSThomas Cort menu->top_row = new_top_row;
293b618a1eeSThomas Cort
294b618a1eeSThomas Cort if ((menu->posted == 1) && (menu->menu_init != NULL))
295b618a1eeSThomas Cort menu->menu_init(menu);
296b618a1eeSThomas Cort }
297b618a1eeSThomas Cort
298b618a1eeSThomas Cort /* this looks like wasted effort but it can happen.... */
299b618a1eeSThomas Cort if (menu->cur_item != item->index) {
300b618a1eeSThomas Cort
301b618a1eeSThomas Cort if ((menu->posted == 1) && (menu->item_term != NULL))
302b618a1eeSThomas Cort menu->item_term(menu);
303b618a1eeSThomas Cort
304b618a1eeSThomas Cort menu->cur_item = item->index;
305b618a1eeSThomas Cort menu->cur_row = item->row;
306b618a1eeSThomas Cort menu->cur_col = item->col;
307b618a1eeSThomas Cort
308b618a1eeSThomas Cort if (menu->posted == 1)
309b618a1eeSThomas Cort _menui_redraw_menu(menu, old_top_row, old_cur_item);
310b618a1eeSThomas Cort
311b618a1eeSThomas Cort if ((menu->posted == 1) && (menu->item_init != NULL))
312b618a1eeSThomas Cort menu->item_init(menu);
313b618a1eeSThomas Cort
314b618a1eeSThomas Cort }
315b618a1eeSThomas Cort
316b618a1eeSThomas Cort menu->in_init = 0;
317b618a1eeSThomas Cort return E_OK;
318b618a1eeSThomas Cort }
319b618a1eeSThomas Cort
320b618a1eeSThomas Cort /*
321b618a1eeSThomas Cort * Attempt to match items with the pattern buffer in the direction given
322b618a1eeSThomas Cort * by iterating over the menu items. If a match is found return E_OK
323b618a1eeSThomas Cort * otherwise return E_NO_MATCH
324b618a1eeSThomas Cort */
325b618a1eeSThomas Cort int
_menui_match_items(MENU * menu,int direction,int * item_matched)326b618a1eeSThomas Cort _menui_match_items(MENU *menu, int direction, int *item_matched)
327b618a1eeSThomas Cort {
328b618a1eeSThomas Cort int i, caseless;
329b618a1eeSThomas Cort
330b618a1eeSThomas Cort caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE);
331b618a1eeSThomas Cort
332b618a1eeSThomas Cort i = menu->cur_item;
333b618a1eeSThomas Cort if (direction == MATCH_NEXT_FORWARD) {
334b618a1eeSThomas Cort if (++i >= menu->item_count) i = 0;
335b618a1eeSThomas Cort } else if (direction == MATCH_NEXT_REVERSE) {
336b618a1eeSThomas Cort if (--i < 0) i = menu->item_count - 1;
337b618a1eeSThomas Cort }
338b618a1eeSThomas Cort
339b618a1eeSThomas Cort
340b618a1eeSThomas Cort do {
341b618a1eeSThomas Cort if (menu->items[i]->name.length >= menu->plen) {
342b618a1eeSThomas Cort /* no chance if pattern is longer */
343b618a1eeSThomas Cort if (caseless) {
344b618a1eeSThomas Cort if (strncasecmp(menu->items[i]->name.string,
345b618a1eeSThomas Cort menu->pattern,
346b618a1eeSThomas Cort (size_t) menu->plen) == 0) {
347b618a1eeSThomas Cort *item_matched = i;
348b618a1eeSThomas Cort menu->match_len = menu->plen;
349b618a1eeSThomas Cort return E_OK;
350b618a1eeSThomas Cort }
351b618a1eeSThomas Cort } else {
352b618a1eeSThomas Cort if (strncmp(menu->items[i]->name.string,
353b618a1eeSThomas Cort menu->pattern,
354b618a1eeSThomas Cort (size_t) menu->plen) == 0) {
355b618a1eeSThomas Cort *item_matched = i;
356b618a1eeSThomas Cort menu->match_len = menu->plen;
357b618a1eeSThomas Cort return E_OK;
358b618a1eeSThomas Cort }
359b618a1eeSThomas Cort }
360b618a1eeSThomas Cort }
361b618a1eeSThomas Cort
362b618a1eeSThomas Cort if ((direction == MATCH_FORWARD) ||
363b618a1eeSThomas Cort (direction == MATCH_NEXT_FORWARD)) {
364b618a1eeSThomas Cort if (++i >= menu->item_count) i = 0;
365b618a1eeSThomas Cort } else {
366b618a1eeSThomas Cort if (--i <= 0) i = menu->item_count - 1;
367b618a1eeSThomas Cort }
368b618a1eeSThomas Cort } while (i != menu->cur_item);
369b618a1eeSThomas Cort
370b618a1eeSThomas Cort menu->match_len = 0; /* match did not succeed - kill the match len. */
371b618a1eeSThomas Cort return E_NO_MATCH;
372b618a1eeSThomas Cort }
373b618a1eeSThomas Cort
374b618a1eeSThomas Cort /*
375b618a1eeSThomas Cort * Attempt to match the pattern buffer against the items. If c is a
376b618a1eeSThomas Cort * printable character then add it to the pattern buffer prior to
377b618a1eeSThomas Cort * performing the match. Direction determines the direction of matching.
378b618a1eeSThomas Cort * If the match is successful update the item_matched variable with the
379b618a1eeSThomas Cort * index of the item that matched the pattern.
380b618a1eeSThomas Cort */
381b618a1eeSThomas Cort int
_menui_match_pattern(MENU * menu,int c,int direction,int * item_matched)382b618a1eeSThomas Cort _menui_match_pattern(MENU *menu, int c, int direction, int *item_matched)
383b618a1eeSThomas Cort {
384b618a1eeSThomas Cort if (menu == NULL)
385b618a1eeSThomas Cort return E_BAD_ARGUMENT;
386b618a1eeSThomas Cort if (menu->items == NULL)
387b618a1eeSThomas Cort return E_BAD_ARGUMENT;
388b618a1eeSThomas Cort if (*menu->items == NULL)
389b618a1eeSThomas Cort return E_BAD_ARGUMENT;
390b618a1eeSThomas Cort
391b618a1eeSThomas Cort if (isprint(c)) {
392b618a1eeSThomas Cort /* add char to buffer - first allocate room for it */
393b618a1eeSThomas Cort if ((menu->pattern = (char *)
394b618a1eeSThomas Cort realloc(menu->pattern,
395b618a1eeSThomas Cort menu->plen + sizeof(char) +
396b618a1eeSThomas Cort ((menu->plen > 0)? 0 : 1)))
397b618a1eeSThomas Cort == NULL)
398b618a1eeSThomas Cort return E_SYSTEM_ERROR;
399b618a1eeSThomas Cort menu->pattern[menu->plen] = c;
400b618a1eeSThomas Cort menu->pattern[++menu->plen] = '\0';
401b618a1eeSThomas Cort
402b618a1eeSThomas Cort /* there is no chance of a match if pattern is longer
403b618a1eeSThomas Cort than all the items */
404b618a1eeSThomas Cort if (menu->plen >= menu->max_item_width) {
405b618a1eeSThomas Cort menu->pattern[--menu->plen] = '\0';
406b618a1eeSThomas Cort return E_NO_MATCH;
407b618a1eeSThomas Cort }
408b618a1eeSThomas Cort
409b618a1eeSThomas Cort if (_menui_match_items(menu, direction,
410b618a1eeSThomas Cort item_matched) == E_NO_MATCH) {
411b618a1eeSThomas Cort menu->pattern[--menu->plen] = '\0';
412b618a1eeSThomas Cort return E_NO_MATCH;
413b618a1eeSThomas Cort } else
414b618a1eeSThomas Cort return E_OK;
415b618a1eeSThomas Cort } else {
416b618a1eeSThomas Cort if (_menui_match_items(menu, direction,
417b618a1eeSThomas Cort item_matched) == E_OK) {
418b618a1eeSThomas Cort return E_OK;
419b618a1eeSThomas Cort } else {
420b618a1eeSThomas Cort return E_NO_MATCH;
421b618a1eeSThomas Cort }
422b618a1eeSThomas Cort }
423b618a1eeSThomas Cort }
424b618a1eeSThomas Cort
425b618a1eeSThomas Cort /*
426b618a1eeSThomas Cort * Draw an item in the subwindow complete with appropriate highlighting.
427b618a1eeSThomas Cort */
428b618a1eeSThomas Cort void
_menui_draw_item(MENU * menu,int item)429b618a1eeSThomas Cort _menui_draw_item(MENU *menu, int item)
430b618a1eeSThomas Cort {
431b618a1eeSThomas Cort int j, pad_len, mark_len;
432b618a1eeSThomas Cort
433b618a1eeSThomas Cort mark_len = max(menu->mark.length, menu->unmark.length);
434b618a1eeSThomas Cort
435b618a1eeSThomas Cort wmove(menu->scrwin,
436b618a1eeSThomas Cort menu->items[item]->row - menu->top_row,
437b618a1eeSThomas Cort menu->items[item]->col * (menu->col_width + 1));
438b618a1eeSThomas Cort
439b618a1eeSThomas Cort if (menu->cur_item == item)
440b618a1eeSThomas Cort wattrset(menu->scrwin, menu->fore);
441b618a1eeSThomas Cort if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE)
442b618a1eeSThomas Cort wattron(menu->scrwin, menu->grey);
443b618a1eeSThomas Cort
444b618a1eeSThomas Cort /* deal with the menu mark, if one is set.
445b618a1eeSThomas Cort * We mark the selected items and write blanks for
446b618a1eeSThomas Cort * all others unless the menu unmark string is set in which
447b618a1eeSThomas Cort * case the unmark string is written.
448b618a1eeSThomas Cort */
449*84d9c625SLionel Sambuc if ((menu->items[item]->selected == 1) ||
450*84d9c625SLionel Sambuc (((menu->opts & O_ONEVALUE) == O_ONEVALUE) &&
451*84d9c625SLionel Sambuc (menu->cur_item == item))) {
452b618a1eeSThomas Cort if (menu->mark.string != NULL) {
453b618a1eeSThomas Cort for (j = 0; j < menu->mark.length; j++) {
454b618a1eeSThomas Cort waddch(menu->scrwin,
455b618a1eeSThomas Cort menu->mark.string[j]);
456b618a1eeSThomas Cort }
457b618a1eeSThomas Cort }
458b618a1eeSThomas Cort /* blank any length difference between mark & unmark */
459b618a1eeSThomas Cort for (j = menu->mark.length; j < mark_len; j++)
460b618a1eeSThomas Cort waddch(menu->scrwin, ' ');
461b618a1eeSThomas Cort } else {
462b618a1eeSThomas Cort if (menu->unmark.string != NULL) {
463b618a1eeSThomas Cort for (j = 0; j < menu->unmark.length; j++) {
464b618a1eeSThomas Cort waddch(menu->scrwin,
465b618a1eeSThomas Cort menu->unmark.string[j]);
466b618a1eeSThomas Cort }
467b618a1eeSThomas Cort }
468b618a1eeSThomas Cort /* blank any length difference between mark & unmark */
469b618a1eeSThomas Cort for (j = menu->unmark.length; j < mark_len; j++)
470b618a1eeSThomas Cort waddch(menu->scrwin, ' ');
471b618a1eeSThomas Cort }
472b618a1eeSThomas Cort
473b618a1eeSThomas Cort /* add the menu name */
474b618a1eeSThomas Cort for (j=0; j < menu->items[item]->name.length; j++)
475b618a1eeSThomas Cort waddch(menu->scrwin,
476b618a1eeSThomas Cort menu->items[item]->name.string[j]);
477b618a1eeSThomas Cort
478b618a1eeSThomas Cort pad_len = menu->col_width - menu->items[item]->name.length
479b618a1eeSThomas Cort - mark_len - 1;
480b618a1eeSThomas Cort if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) {
481b618a1eeSThomas Cort pad_len -= menu->items[item]->description.length - 1;
482b618a1eeSThomas Cort for (j = 0; j < pad_len; j++)
483b618a1eeSThomas Cort waddch(menu->scrwin, menu->pad);
484b618a1eeSThomas Cort for (j = 0; j < menu->items[item]->description.length; j++) {
485b618a1eeSThomas Cort waddch(menu->scrwin,
486b618a1eeSThomas Cort menu->items[item]->description.string[j]);
487b618a1eeSThomas Cort }
488b618a1eeSThomas Cort } else {
489b618a1eeSThomas Cort for (j = 0; j < pad_len; j++)
490b618a1eeSThomas Cort waddch(menu->scrwin, ' ');
491b618a1eeSThomas Cort }
492b618a1eeSThomas Cort menu->items[item]->visible = 1;
493b618a1eeSThomas Cort
494b618a1eeSThomas Cort /* kill any special attributes... */
495b618a1eeSThomas Cort wattrset(menu->scrwin, menu->back);
496b618a1eeSThomas Cort
497b618a1eeSThomas Cort /*
498b618a1eeSThomas Cort * Fill in the spacing between items, annoying but it looks
499b618a1eeSThomas Cort * odd if the menu items are inverse because the spacings do not
500b618a1eeSThomas Cort * have the same attributes as the items.
501b618a1eeSThomas Cort */
502b618a1eeSThomas Cort if ((menu->items[item]->col > 0) &&
503b618a1eeSThomas Cort (menu->items[item]->col < (menu->item_cols - 1))) {
504b618a1eeSThomas Cort wmove(menu->scrwin,
505b618a1eeSThomas Cort menu->items[item]->row - menu->top_row,
506b618a1eeSThomas Cort menu->items[item]->col * (menu->col_width + 1) - 1);
507b618a1eeSThomas Cort waddch(menu->scrwin, ' ');
508b618a1eeSThomas Cort }
509b618a1eeSThomas Cort
510b618a1eeSThomas Cort /* and position the cursor nicely */
511b618a1eeSThomas Cort pos_menu_cursor(menu);
512b618a1eeSThomas Cort }
513b618a1eeSThomas Cort
514b618a1eeSThomas Cort /*
515b618a1eeSThomas Cort * Draw the menu in the subwindow provided.
516b618a1eeSThomas Cort */
517b618a1eeSThomas Cort int
_menui_draw_menu(MENU * menu)518b618a1eeSThomas Cort _menui_draw_menu(MENU *menu)
519b618a1eeSThomas Cort {
520*84d9c625SLionel Sambuc int rowmajor, i, j, k, row = -1, stride;
521b618a1eeSThomas Cort int incr, cur_row, offset, row_count;
522b618a1eeSThomas Cort
523b618a1eeSThomas Cort rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
524b618a1eeSThomas Cort
525b618a1eeSThomas Cort if (rowmajor) {
526b618a1eeSThomas Cort stride = 1;
527b618a1eeSThomas Cort incr = menu->item_cols;
528b618a1eeSThomas Cort } else {
529b618a1eeSThomas Cort stride = menu->item_rows;
530b618a1eeSThomas Cort incr = 1;
531b618a1eeSThomas Cort }
532b618a1eeSThomas Cort row_count = 0;
533b618a1eeSThomas Cort
534b618a1eeSThomas Cort for (i = 0; i < menu->item_count; i += incr) {
535b618a1eeSThomas Cort if (menu->items[i]->row == menu->top_row)
536b618a1eeSThomas Cort break;
537b618a1eeSThomas Cort row_count++;
538b618a1eeSThomas Cort for (j = 0; j < menu->item_cols; j++) {
539b618a1eeSThomas Cort offset = j * stride + i;
540b618a1eeSThomas Cort if (offset >= menu->item_count)
541b618a1eeSThomas Cort break; /* done */
542b618a1eeSThomas Cort menu->items[offset]->visible = 0;
543b618a1eeSThomas Cort }
544b618a1eeSThomas Cort }
545b618a1eeSThomas Cort
546b618a1eeSThomas Cort wmove(menu->scrwin, 0, 0);
547b618a1eeSThomas Cort
548b618a1eeSThomas Cort menu->col_width = getmaxx(menu->scrwin) / menu->cols;
549b618a1eeSThomas Cort
550b618a1eeSThomas Cort for (cur_row = 0; cur_row < menu->rows; cur_row++) {
551b618a1eeSThomas Cort for (j = 0; j < menu->cols; j++) {
552b618a1eeSThomas Cort offset = j * stride + i;
553b618a1eeSThomas Cort if (offset >= menu->item_count) {
554b618a1eeSThomas Cort /* no more items to draw, write background blanks */
555b618a1eeSThomas Cort wattrset(menu->scrwin, menu->back);
556b618a1eeSThomas Cort if (row < 0) {
557b618a1eeSThomas Cort row = menu->items[menu->item_count - 1]->row;
558b618a1eeSThomas Cort }
559b618a1eeSThomas Cort
560b618a1eeSThomas Cort wmove(menu->scrwin, cur_row,
561b618a1eeSThomas Cort j * (menu->col_width + 1));
562b618a1eeSThomas Cort for (k = 0; k < menu->col_width; k++)
563b618a1eeSThomas Cort waddch(menu->scrwin, ' ');
564b618a1eeSThomas Cort } else {
565b618a1eeSThomas Cort _menui_draw_item(menu, offset);
566b618a1eeSThomas Cort }
567b618a1eeSThomas Cort }
568b618a1eeSThomas Cort
569b618a1eeSThomas Cort i += incr;
570b618a1eeSThomas Cort row_count++;
571b618a1eeSThomas Cort }
572b618a1eeSThomas Cort
573b618a1eeSThomas Cort if (row_count < menu->item_rows) {
574b618a1eeSThomas Cort for (cur_row = row_count; cur_row < menu->item_rows; cur_row++) {
575b618a1eeSThomas Cort for (j = 0; j < menu->item_cols; j++) {
576b618a1eeSThomas Cort offset = j * stride + i;
577b618a1eeSThomas Cort if (offset >= menu->item_count)
578b618a1eeSThomas Cort break; /* done */
579b618a1eeSThomas Cort menu->items[offset]->visible = 0;
580b618a1eeSThomas Cort }
581b618a1eeSThomas Cort i += incr;
582b618a1eeSThomas Cort }
583b618a1eeSThomas Cort }
584b618a1eeSThomas Cort
585b618a1eeSThomas Cort return E_OK;
586b618a1eeSThomas Cort }
587b618a1eeSThomas Cort
588b618a1eeSThomas Cort
589b618a1eeSThomas Cort /*
590b618a1eeSThomas Cort * Calculate the widest menu item and stash it in the menu struct.
591b618a1eeSThomas Cort *
592b618a1eeSThomas Cort */
593b618a1eeSThomas Cort void
_menui_max_item_size(MENU * menu)594b618a1eeSThomas Cort _menui_max_item_size(MENU *menu)
595b618a1eeSThomas Cort {
596b618a1eeSThomas Cort int i, with_desc, width;
597b618a1eeSThomas Cort
598b618a1eeSThomas Cort with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC);
599b618a1eeSThomas Cort
600b618a1eeSThomas Cort for (i = 0; i < menu->item_count; i++) {
601b618a1eeSThomas Cort width = menu->items[i]->name.length
602b618a1eeSThomas Cort + max(menu->mark.length, menu->unmark.length);
603b618a1eeSThomas Cort if (with_desc)
604b618a1eeSThomas Cort width += menu->items[i]->description.length + 1;
605b618a1eeSThomas Cort
606b618a1eeSThomas Cort menu->max_item_width = max(menu->max_item_width, width);
607b618a1eeSThomas Cort }
608b618a1eeSThomas Cort }
609b618a1eeSThomas Cort
610b618a1eeSThomas Cort
611b618a1eeSThomas Cort /*
612b618a1eeSThomas Cort * Redraw the menu on the screen. If the current item has changed then
613b618a1eeSThomas Cort * unhighlight the old item and highlight the new one.
614b618a1eeSThomas Cort */
615b618a1eeSThomas Cort static void
_menui_redraw_menu(MENU * menu,int old_top_row,int old_cur_item)616b618a1eeSThomas Cort _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item)
617b618a1eeSThomas Cort {
618b618a1eeSThomas Cort
619b618a1eeSThomas Cort if (menu->top_row != old_top_row) {
620b618a1eeSThomas Cort /* top row changed - redo the whole menu
621b618a1eeSThomas Cort * XXXX this could be improved if we had wscrl implemented.
622b618a1eeSThomas Cort
623b618a1eeSThomas Cort * XXXX we could scroll the window and just fill in the
624b618a1eeSThomas Cort * XXXX changed lines.
625b618a1eeSThomas Cort */
626b618a1eeSThomas Cort wclear(menu->scrwin);
627b618a1eeSThomas Cort _menui_draw_menu(menu);
628b618a1eeSThomas Cort } else {
629b618a1eeSThomas Cort if (menu->cur_item != old_cur_item) {
630b618a1eeSThomas Cort /* redo the old item as a normal one. */
631b618a1eeSThomas Cort _menui_draw_item(menu, old_cur_item);
632b618a1eeSThomas Cort }
633b618a1eeSThomas Cort /* and then redraw the current item */
634b618a1eeSThomas Cort _menui_draw_item(menu, menu->cur_item);
635b618a1eeSThomas Cort }
636b618a1eeSThomas Cort }
637