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