Remove building with NOCRYPTO option
[minix.git] / lib / libmenu / internals.c
blob8dcca55b8a53e3e7227d14dff662f67dc270f075
1 /* $NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $ */
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@yahoo.com.au)
5 * All rights reserved.
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
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.
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: internals.c,v 1.17 2013/10/18 19:53:59 christos Exp $");
32 #include <menu.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "internals.h"
38 /* internal function prototypes */
39 static void
40 _menui_calc_neighbours(MENU *menu, int item_no);
41 static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item);
44 * Link all the menu items together to speed up navigation. We need
45 * to calculate the widest item entry, then work out how many columns
46 * of items the window will accommodate and then how many rows there will
47 * be. Once the layout is determined the neighbours of each item is
48 * calculated and the item structures updated.
50 int
51 _menui_stitch_items(MENU *menu)
53 int i, row_major;
55 row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
57 if (menu->posted == 1)
58 return E_POSTED;
59 if (menu->items == NULL)
60 return E_BAD_ARGUMENT;
62 menu->item_rows = menu->item_count / menu->cols;
63 menu->item_cols = menu->cols;
64 if (menu->item_count > (menu->item_rows * menu->item_cols))
65 menu->item_rows += 1;
67 _menui_max_item_size(menu);
69 for (i = 0; i < menu->item_count; i++) {
70 /* fill in the row and column value of the item */
71 if (row_major) {
72 menu->items[i]->row = i / menu->item_cols;
73 menu->items[i]->col = i % menu->item_cols;
74 } else {
75 menu->items[i]->row = i % menu->item_rows;
76 menu->items[i]->col = i / menu->item_rows;
79 _menui_calc_neighbours(menu, i);
82 return E_OK;
86 * Calculate the neighbours for an item in menu.
88 static void
89 _menui_calc_neighbours(MENU *menu, int item_no)
91 int neighbour, cycle, row_major, edge;
92 ITEM *item;
94 row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
95 cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC);
96 item = menu->items[item_no];
98 if (menu->item_rows < 2) {
99 if (cycle) {
100 item->up = item;
101 item->down = item;
102 } else {
103 item->up = NULL;
104 item->down = NULL;
106 } else {
108 /* up */
109 if (menu->item_cols < 2) {
110 if (item_no == 0) {
111 if (cycle)
112 item->up =
113 menu->items[menu->item_count - 1];
114 else
115 item->up = NULL;
116 } else
117 item->up = menu->items[item_no - 1];
118 } else {
119 edge = 0;
120 if (row_major) {
121 if (item->row == 0) {
122 neighbour =
123 (menu->item_rows - 1) * menu->item_cols
124 + item->col;
125 if (neighbour >= menu->item_count)
126 neighbour -= menu->item_cols;
127 edge = 1;
128 } else
129 neighbour = item_no - menu->item_cols;
130 } else {
131 if (item->row == 0) {
132 neighbour = menu->item_rows * item->col
133 + menu->item_rows - 1;
134 if (neighbour >= menu->item_count)
135 neighbour = menu->item_count - 1;
136 edge = 1;
137 } else
138 neighbour = item_no - 1;
142 item->up = menu->items[neighbour];
143 if ((!cycle) && (edge == 1))
144 item->up = NULL;
147 /* Down */
148 if (menu->item_cols < 2) {
149 if (item_no == (menu->item_count - 1)) {
150 if (cycle)
151 item->down = menu->items[0];
152 else
153 item->down = NULL;
154 } else
155 item->down = menu->items[item_no + 1];
156 } else {
157 edge = 0;
158 if (row_major) {
159 if (item->row == menu->item_rows - 1) {
160 neighbour = item->col;
161 edge = 1;
162 } else {
163 neighbour = item_no + menu->item_cols;
164 if (neighbour >= menu->item_count) {
165 neighbour = item->col;
166 edge = 1;
169 } else {
170 if (item->row == menu->item_rows - 1) {
171 neighbour = item->col * menu->item_rows;
172 edge = 1;
173 } else {
174 neighbour = item_no + 1;
175 if (neighbour >= menu->item_count) {
176 neighbour = item->col
177 * menu->item_rows;
178 edge = 1;
183 item->down = menu->items[neighbour];
184 if ((!cycle) && (edge == 1))
185 item->down = NULL;
189 if (menu->item_cols < 2) {
190 if (cycle) {
191 item->left = item;
192 item->right = item;
193 } else {
194 item->left = NULL;
195 item->right = NULL;
197 } else {
198 /* left */
199 if (menu->item_rows < 2) {
200 if (item_no == 0) {
201 if (cycle)
202 item->left =
203 menu->items[menu->item_count - 1];
204 else
205 item->left = NULL;
206 } else
207 item->left = menu->items[item_no - 1];
208 } else {
209 edge = 0;
210 if (row_major) {
211 if (item->col == 0) {
212 neighbour = item_no + menu->cols - 1;
213 if (neighbour >= menu->item_count)
214 neighbour = menu->item_count - 1;
215 edge = 1;
216 } else
217 neighbour = item_no - 1;
218 } else {
219 if (item->col == 0) {
220 neighbour = menu->item_rows
221 * (menu->item_cols - 1) + item->row;
222 if (neighbour >= menu->item_count)
223 neighbour -= menu->item_rows;
224 edge = 1;
225 } else
226 neighbour = item_no - menu->item_rows;
229 item->left = menu->items[neighbour];
230 if ((!cycle) && (edge == 1))
231 item->left = NULL;
234 /* right */
235 if (menu->item_rows < 2) {
236 if (item_no == menu->item_count - 1) {
237 if (cycle)
238 item->right = menu->items[0];
239 else
240 item->right = NULL;
241 } else
242 item->right = menu->items[item_no + 1];
243 } else {
244 edge = 0;
245 if (row_major) {
246 if (item->col == menu->item_cols - 1) {
247 neighbour = item_no - menu->item_cols
248 + 1;
249 edge = 1;
250 } else if (item_no == menu->item_count - 1) {
251 neighbour = item->row * menu->item_cols;
252 edge = 1;
253 } else
254 neighbour = item_no + 1;
255 } else {
256 if (item->col == menu->item_cols - 1) {
257 neighbour = item->row;
258 edge = 1;
259 } else {
260 neighbour = item_no + menu->item_rows;
261 if (neighbour >= menu->item_count) {
262 neighbour = item->row;
263 edge = 1;
268 item->right = menu->items[neighbour];
269 if ((!cycle) && (edge == 1))
270 item->right = NULL;
276 * Goto the item pointed to by item and adjust the menu structure
277 * accordingly. Call the term and init functions if required.
280 _menui_goto_item(MENU *menu, ITEM *item, int new_top_row)
282 int old_top_row = menu->top_row, old_cur_item = menu->cur_item;
284 /* If we get a null then the menu is not cyclic so deny request */
285 if (item == NULL)
286 return E_REQUEST_DENIED;
288 menu->in_init = 1;
289 if (menu->top_row != new_top_row) {
290 if ((menu->posted == 1) && (menu->menu_term != NULL))
291 menu->menu_term(menu);
292 menu->top_row = new_top_row;
294 if ((menu->posted == 1) && (menu->menu_init != NULL))
295 menu->menu_init(menu);
298 /* this looks like wasted effort but it can happen.... */
299 if (menu->cur_item != item->index) {
301 if ((menu->posted == 1) && (menu->item_term != NULL))
302 menu->item_term(menu);
304 menu->cur_item = item->index;
305 menu->cur_row = item->row;
306 menu->cur_col = item->col;
308 if (menu->posted == 1)
309 _menui_redraw_menu(menu, old_top_row, old_cur_item);
311 if ((menu->posted == 1) && (menu->item_init != NULL))
312 menu->item_init(menu);
316 menu->in_init = 0;
317 return E_OK;
321 * Attempt to match items with the pattern buffer in the direction given
322 * by iterating over the menu items. If a match is found return E_OK
323 * otherwise return E_NO_MATCH
326 _menui_match_items(MENU *menu, int direction, int *item_matched)
328 int i, caseless;
330 caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE);
332 i = menu->cur_item;
333 if (direction == MATCH_NEXT_FORWARD) {
334 if (++i >= menu->item_count) i = 0;
335 } else if (direction == MATCH_NEXT_REVERSE) {
336 if (--i < 0) i = menu->item_count - 1;
340 do {
341 if (menu->items[i]->name.length >= menu->plen) {
342 /* no chance if pattern is longer */
343 if (caseless) {
344 if (strncasecmp(menu->items[i]->name.string,
345 menu->pattern,
346 (size_t) menu->plen) == 0) {
347 *item_matched = i;
348 menu->match_len = menu->plen;
349 return E_OK;
351 } else {
352 if (strncmp(menu->items[i]->name.string,
353 menu->pattern,
354 (size_t) menu->plen) == 0) {
355 *item_matched = i;
356 menu->match_len = menu->plen;
357 return E_OK;
362 if ((direction == MATCH_FORWARD) ||
363 (direction == MATCH_NEXT_FORWARD)) {
364 if (++i >= menu->item_count) i = 0;
365 } else {
366 if (--i <= 0) i = menu->item_count - 1;
368 } while (i != menu->cur_item);
370 menu->match_len = 0; /* match did not succeed - kill the match len. */
371 return E_NO_MATCH;
375 * Attempt to match the pattern buffer against the items. If c is a
376 * printable character then add it to the pattern buffer prior to
377 * performing the match. Direction determines the direction of matching.
378 * If the match is successful update the item_matched variable with the
379 * index of the item that matched the pattern.
382 _menui_match_pattern(MENU *menu, int c, int direction, int *item_matched)
384 if (menu == NULL)
385 return E_BAD_ARGUMENT;
386 if (menu->items == NULL)
387 return E_BAD_ARGUMENT;
388 if (*menu->items == NULL)
389 return E_BAD_ARGUMENT;
391 if (isprint(c)) {
392 /* add char to buffer - first allocate room for it */
393 if ((menu->pattern = (char *)
394 realloc(menu->pattern,
395 menu->plen + sizeof(char) +
396 ((menu->plen > 0)? 0 : 1)))
397 == NULL)
398 return E_SYSTEM_ERROR;
399 menu->pattern[menu->plen] = c;
400 menu->pattern[++menu->plen] = '\0';
402 /* there is no chance of a match if pattern is longer
403 than all the items */
404 if (menu->plen >= menu->max_item_width) {
405 menu->pattern[--menu->plen] = '\0';
406 return E_NO_MATCH;
409 if (_menui_match_items(menu, direction,
410 item_matched) == E_NO_MATCH) {
411 menu->pattern[--menu->plen] = '\0';
412 return E_NO_MATCH;
413 } else
414 return E_OK;
415 } else {
416 if (_menui_match_items(menu, direction,
417 item_matched) == E_OK) {
418 return E_OK;
419 } else {
420 return E_NO_MATCH;
426 * Draw an item in the subwindow complete with appropriate highlighting.
428 void
429 _menui_draw_item(MENU *menu, int item)
431 int j, pad_len, mark_len;
433 mark_len = max(menu->mark.length, menu->unmark.length);
435 wmove(menu->scrwin,
436 menu->items[item]->row - menu->top_row,
437 menu->items[item]->col * (menu->col_width + 1));
439 if (menu->cur_item == item)
440 wattrset(menu->scrwin, menu->fore);
441 if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE)
442 wattron(menu->scrwin, menu->grey);
444 /* deal with the menu mark, if one is set.
445 * We mark the selected items and write blanks for
446 * all others unless the menu unmark string is set in which
447 * case the unmark string is written.
449 if ((menu->items[item]->selected == 1) ||
450 (((menu->opts & O_ONEVALUE) == O_ONEVALUE) &&
451 (menu->cur_item == item))) {
452 if (menu->mark.string != NULL) {
453 for (j = 0; j < menu->mark.length; j++) {
454 waddch(menu->scrwin,
455 menu->mark.string[j]);
458 /* blank any length difference between mark & unmark */
459 for (j = menu->mark.length; j < mark_len; j++)
460 waddch(menu->scrwin, ' ');
461 } else {
462 if (menu->unmark.string != NULL) {
463 for (j = 0; j < menu->unmark.length; j++) {
464 waddch(menu->scrwin,
465 menu->unmark.string[j]);
468 /* blank any length difference between mark & unmark */
469 for (j = menu->unmark.length; j < mark_len; j++)
470 waddch(menu->scrwin, ' ');
473 /* add the menu name */
474 for (j=0; j < menu->items[item]->name.length; j++)
475 waddch(menu->scrwin,
476 menu->items[item]->name.string[j]);
478 pad_len = menu->col_width - menu->items[item]->name.length
479 - mark_len - 1;
480 if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) {
481 pad_len -= menu->items[item]->description.length - 1;
482 for (j = 0; j < pad_len; j++)
483 waddch(menu->scrwin, menu->pad);
484 for (j = 0; j < menu->items[item]->description.length; j++) {
485 waddch(menu->scrwin,
486 menu->items[item]->description.string[j]);
488 } else {
489 for (j = 0; j < pad_len; j++)
490 waddch(menu->scrwin, ' ');
492 menu->items[item]->visible = 1;
494 /* kill any special attributes... */
495 wattrset(menu->scrwin, menu->back);
498 * Fill in the spacing between items, annoying but it looks
499 * odd if the menu items are inverse because the spacings do not
500 * have the same attributes as the items.
502 if ((menu->items[item]->col > 0) &&
503 (menu->items[item]->col < (menu->item_cols - 1))) {
504 wmove(menu->scrwin,
505 menu->items[item]->row - menu->top_row,
506 menu->items[item]->col * (menu->col_width + 1) - 1);
507 waddch(menu->scrwin, ' ');
510 /* and position the cursor nicely */
511 pos_menu_cursor(menu);
515 * Draw the menu in the subwindow provided.
518 _menui_draw_menu(MENU *menu)
520 int rowmajor, i, j, k, row = -1, stride;
521 int incr, cur_row, offset, row_count;
523 rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
525 if (rowmajor) {
526 stride = 1;
527 incr = menu->item_cols;
528 } else {
529 stride = menu->item_rows;
530 incr = 1;
532 row_count = 0;
534 for (i = 0; i < menu->item_count; i += incr) {
535 if (menu->items[i]->row == menu->top_row)
536 break;
537 row_count++;
538 for (j = 0; j < menu->item_cols; j++) {
539 offset = j * stride + i;
540 if (offset >= menu->item_count)
541 break; /* done */
542 menu->items[offset]->visible = 0;
546 wmove(menu->scrwin, 0, 0);
548 menu->col_width = getmaxx(menu->scrwin) / menu->cols;
550 for (cur_row = 0; cur_row < menu->rows; cur_row++) {
551 for (j = 0; j < menu->cols; j++) {
552 offset = j * stride + i;
553 if (offset >= menu->item_count) {
554 /* no more items to draw, write background blanks */
555 wattrset(menu->scrwin, menu->back);
556 if (row < 0) {
557 row = menu->items[menu->item_count - 1]->row;
560 wmove(menu->scrwin, cur_row,
561 j * (menu->col_width + 1));
562 for (k = 0; k < menu->col_width; k++)
563 waddch(menu->scrwin, ' ');
564 } else {
565 _menui_draw_item(menu, offset);
569 i += incr;
570 row_count++;
573 if (row_count < menu->item_rows) {
574 for (cur_row = row_count; cur_row < menu->item_rows; cur_row++) {
575 for (j = 0; j < menu->item_cols; j++) {
576 offset = j * stride + i;
577 if (offset >= menu->item_count)
578 break; /* done */
579 menu->items[offset]->visible = 0;
581 i += incr;
585 return E_OK;
590 * Calculate the widest menu item and stash it in the menu struct.
593 void
594 _menui_max_item_size(MENU *menu)
596 int i, with_desc, width;
598 with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC);
600 for (i = 0; i < menu->item_count; i++) {
601 width = menu->items[i]->name.length
602 + max(menu->mark.length, menu->unmark.length);
603 if (with_desc)
604 width += menu->items[i]->description.length + 1;
606 menu->max_item_width = max(menu->max_item_width, width);
612 * Redraw the menu on the screen. If the current item has changed then
613 * unhighlight the old item and highlight the new one.
615 static void
616 _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item)
619 if (menu->top_row != old_top_row) {
620 /* top row changed - redo the whole menu
621 * XXXX this could be improved if we had wscrl implemented.
623 * XXXX we could scroll the window and just fill in the
624 * XXXX changed lines.
626 wclear(menu->scrwin);
627 _menui_draw_menu(menu);
628 } else {
629 if (menu->cur_item != old_cur_item) {
630 /* redo the old item as a normal one. */
631 _menui_draw_item(menu, old_cur_item);
633 /* and then redraw the current item */
634 _menui_draw_item(menu, menu->cur_item);