1/*	$NetBSD: item.c,v 1.11 2007/07/23 12:12:19 blymn Exp $	*/
2
3/*-
4 * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@yahoo.com.au)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 *
27 */
28
29#include <sys/cdefs.h>
30__RCSID("$NetBSD: item.c,v 1.11 2007/07/23 12:12:19 blymn Exp $");
31
32#include <menu.h>
33#include <stdlib.h>
34#include <string.h>
35#include "internals.h"
36
37/* the following is defined in menu.c - it is the default menu struct */
38extern MENU _menui_default_menu;
39
40/* keep default item options for setting in new_item */
41ITEM _menui_default_item = {
42	{NULL, 0}, /* item name struct */
43	{NULL, 0}, /* item description struct */
44	NULL, /* user pointer */
45	0, /* is item visible? */
46	0, /* is item selected? */
47	0, /* row item is on */
48	0, /* column item is on */
49	O_SELECTABLE, /* item options */
50	NULL, /* parent menu item is bound to */
51	-1, /* index number if item attached to a menu */
52	NULL, /* left neighbour */
53	NULL, /* right neighbour */
54	NULL, /* up neighbour */
55	NULL /* down neighbour */
56};
57
58/*
59 * Return the item visibility flag
60 */
61int
62item_visible(ITEM *item)
63{
64	if (item == NULL)
65		return E_BAD_ARGUMENT;
66	if (item->parent == NULL)
67		return E_NOT_CONNECTED;
68
69        return item->visible;
70}
71
72/*
73 * Return the pointer to the item name
74 */
75char *
76item_name(ITEM *item)
77{
78	if (item == NULL)
79		return NULL;
80
81        return item->name.string;
82}
83
84/*
85 * Return the pointer to the item description
86 */
87char *
88item_description(ITEM *item)
89{
90	if (item == NULL)
91		return NULL;
92
93        return item->description.string;
94}
95
96/*
97 * Set the application defined function called when the menu is posted or
98 * just after the current item changes.
99 */
100int
101set_item_init(MENU *menu, Menu_Hook func)
102{
103	if (menu == NULL)
104		_menui_default_menu.item_init = func;
105	else
106		menu->item_init = func;
107        return E_OK;
108}
109
110
111/*
112 * Return a pointer to the item initialisation routine.
113 */
114Menu_Hook
115item_init(MENU *menu)
116{
117	if (menu == NULL)
118		return _menui_default_menu.item_init;
119	else
120		return menu->item_init;
121}
122
123/*
124 * Set the user defined function to be called when menu is unposted or just
125 * before the current item changes.
126 */
127int
128set_item_term(MENU *menu, Menu_Hook func)
129{
130	if (menu == NULL)
131		_menui_default_menu.item_term = func;
132	else
133		menu->item_term = func;
134        return E_OK;
135}
136
137/*
138 * Return a pointer to the termination function
139 */
140Menu_Hook
141item_term(MENU *menu)
142{
143	if (menu == NULL)
144		return _menui_default_menu.item_term;
145	else
146		return menu->item_term;
147}
148
149/*
150 * Returns the number of items that are selected.
151 * The index numbers of the items are placed in the dynamically allocated
152 * int array *sel.
153 */
154int
155item_selected(MENU *menu, int **sel)
156{
157	int i, j;
158
159	if (menu == NULL)
160		return E_BAD_ARGUMENT;
161
162	/* count selected */
163	for (i = 0, j = 0; i < menu->item_count; i++)
164		if (menu->items[i]->selected)
165			j++;
166
167	if (j == 0) {
168		*sel = NULL;
169		return 0;
170	}
171
172	if ( (*sel = malloc(sizeof(int) * j)) == NULL)
173		return E_SYSTEM_ERROR;
174
175	for (i = 0, j = 0; i < menu->item_count; i++)
176		if (menu->items[i]->selected)
177			(*sel)[j++] = i;
178
179	return j;
180}
181
182/*
183 * Set the item options.  We keep a global copy of the current item options
184 * as subsequent new_item calls will use the updated options as their
185 * defaults.
186 */
187int
188set_item_opts(ITEM *item, OPTIONS opts)
189{
190          /* selectable seems to be the only allowable item opt! */
191        if (opts != O_SELECTABLE)
192                return E_SYSTEM_ERROR;
193
194	if (item == NULL)
195		_menui_default_item.opts = opts;
196	else
197		item->opts = opts;
198        return E_OK;
199}
200
201/*
202 * Set item options on.
203 */
204int
205item_opts_on(ITEM *item, OPTIONS opts)
206{
207        if (opts != O_SELECTABLE)
208                return E_SYSTEM_ERROR;
209
210        if (item == NULL)
211		_menui_default_item.opts |= opts;
212	else
213		item->opts |= opts;
214        return E_OK;
215}
216
217/*
218 * Turn off the named options.
219 */
220int
221item_opts_off(ITEM *item, OPTIONS opts)
222{
223        if (opts != O_SELECTABLE)
224                return E_SYSTEM_ERROR;
225
226	if (item == NULL)
227		_menui_default_item.opts &= ~(opts);
228	else
229		item->opts &= ~(opts);
230        return E_OK;
231}
232
233/*
234 * Return the current options set in item.
235 */
236OPTIONS
237item_opts(ITEM *item)
238{
239	if (item == NULL)
240		return _menui_default_item.opts;
241	else
242		return item->opts;
243}
244
245/*
246 * Set the selected flag of the item iff the menu options allow it.
247 */
248int
249set_item_value(ITEM *param_item, int flag)
250{
251	ITEM *item = (param_item != NULL) ? param_item : &_menui_default_item;
252
253          /* not bound to a menu */
254        if (item->parent == NULL)
255                return E_NOT_CONNECTED;
256
257          /* menu options do not allow multi-selection */
258        if ((item->parent->opts & O_ONEVALUE) == O_ONEVALUE)
259                return E_REQUEST_DENIED;
260
261        item->selected = flag;
262	_menui_draw_item(item->parent, item->index);
263        return E_OK;
264}
265
266/*
267 * Return the item value of the item.
268 */
269int
270item_value(ITEM *item)
271{
272	if (item == NULL)
273		return _menui_default_item.selected;
274	else
275		return item->selected;
276}
277
278/*
279 * Allocate a new item and return the pointer to the newly allocated
280 * structure.
281 */
282ITEM *
283new_item(char *name, char *description)
284{
285        ITEM *new_one;
286
287	if (name == NULL)
288		return NULL;
289
290	  /* allocate a new item structure for ourselves */
291        if ((new_one = (ITEM *)malloc(sizeof(ITEM))) == NULL)
292                return NULL;
293
294	  /* copy in the defaults for the item */
295	(void)memcpy(new_one, &_menui_default_item, sizeof(ITEM));
296
297	  /* fill in the name structure - first the length and then
298	     allocate room for the string & copy that. */
299	new_one->name.length = strlen(name);
300        if ((new_one->name.string = (char *)
301             malloc(sizeof(char) * new_one->name.length + 1)) == NULL) {
302		  /* uh oh malloc failed - clean up & exit */
303		free(new_one);
304                return NULL;
305        }
306
307        strcpy(new_one->name.string, name);
308
309	if (description == NULL)
310		new_one->description.length = 0;
311	else {
312	  /* fill in the description structure, stash the length then
313	     allocate room for description string and copy it in */
314        	new_one->description.length = strlen(description);
315        	if ((new_one->description.string =
316		    (char *) malloc(sizeof(char) *
317		    new_one->description.length + 1)) == NULL) {
318		  	/*
319			 * malloc has failed
320			 * - free up allocated memory and return
321			 */
322			free(new_one->name.string);
323			free(new_one);
324			return NULL;
325		}
326
327		strcpy(new_one->description.string, description);
328	}
329
330	return new_one;
331}
332
333/*
334 * Free the allocated storage associated with item.
335 */
336int
337free_item(ITEM *item)
338{
339	if (item == NULL)
340		return E_BAD_ARGUMENT;
341
342	  /* check for connection to menu */
343	if (item->parent != NULL)
344		return E_CONNECTED;
345
346	  /* no connections, so free storage starting with the strings */
347	free(item->name.string);
348	if (item->description.length)
349		free(item->description.string);
350	free(item);
351	return E_OK;
352}
353
354/*
355 * Set the menu's current item to the one given.
356 */
357int
358set_current_item(MENU *param_menu, ITEM *item)
359{
360	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
361	int i = 0;
362
363	  /* check if we have been called from an init type function */
364	if (menu->in_init == 1)
365		return E_BAD_STATE;
366
367	  /* check we have items in the menu */
368	if (menu->items == NULL)
369		return E_NOT_CONNECTED;
370
371	if ((i = item_index(item)) < 0)
372		  /* item must not be a part of this menu */
373		return E_BAD_ARGUMENT;
374
375	menu->cur_item = i;
376	return E_OK;
377}
378
379/*
380 * Return a pointer to the current item for the menu
381 */
382ITEM *
383current_item(MENU *menu)
384{
385	if (menu == NULL)
386		return NULL;
387
388	if (menu->items == NULL)
389		return NULL;
390
391	return menu->items[menu->cur_item];
392}
393
394/*
395 * Return the index into the item array that matches item.
396 */
397int
398item_index(ITEM *item)
399{
400	if (item == NULL)
401		return _menui_default_item.index;
402	else
403		return item->index;
404}
405