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