1 /*
2  * menu.c: stuff for the menu's..
3  *
4  * Written By Troy Rollo <troy@cbme.unsw.oz.au>
5  *
6  * Copyright (c) 1991, 1992 Troy Rollo.
7  * Copyright (c) 1992-2017 Matthew R. Green.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "irc.h"
35 IRCII_RCSID("@(#)$eterna: menu.c,v 1.56 2021/03/02 21:47:06 mrg Exp $");
36 
37 #include "menu.h"
38 #include "list.h"
39 #include "ircaux.h"
40 #include "ircterm.h"
41 #include "window.h"
42 #include "screen.h"
43 #include "input.h"
44 #include "vars.h"
45 #include "output.h"
46 
47 #ifdef lines
48 #undef lines
49 #endif /* lines */
50 
51 typedef	struct
52 {
53 	u_char	*Name;
54 	u_char	*Arguments;
55 	void	(*Func)(u_char *);
56 } MenuOption;
57 
58 typedef struct	menu_stru Menu;
59 struct	menu_stru
60 {
61 	Menu	*next;
62 	u_char	*Name;
63 	u_int	TotalOptions;
64 	MenuOption	**Options;
65 };
66 
67 struct	window_menu_stru
68 {
69 	Menu	*menu;
70 	u_int	lines;
71 	u_int	items_per_line;
72 	u_int	cursor;
73 };
74 
75 static	Menu	*MenuList = NULL;
76 
77 struct	OptionTableTag
78 {
79 	char	*name;
80 	void	(*func)(u_char *);
81 };
82 
83 typedef	struct	OptionTableTag OptionTable;
84 
85 static	OptionTable	OptionsList[] =
86 {
87 	{ "PREVIOUS",	menu_previous },
88 	{ "MENU",	menu_submenu },
89 	{ "EXIT",	menu_exit },
90 	{ "CHANNELS",	menu_channels },
91 	{ "COMMAND",	menu_command },
92 	{ NULL,	NULL }
93 };
94 
95 static	MenuOption *create_new_option(void);
96 static	Menu	* create_new_menu(void);
97 static	void	install_menu(Menu *, int);
98 
99 static	MenuOption *
create_new_option(void)100 create_new_option(void)
101 {
102 	MenuOption *new;
103 
104 	new = new_malloc(sizeof *new);
105 	new->Name = NULL;
106 	new->Arguments = NULL;
107 	new->Func = NULL;
108 	return new;
109 }
110 
111 static	Menu	*
create_new_menu(void)112 create_new_menu(void)
113 {
114 	Menu	*new;
115 
116 	new = new_malloc(sizeof *new);
117 	new->Name = NULL;
118 	new->TotalOptions = 0;
119 	new->Options = NULL;
120 	return new;
121 }
122 
123 
124 static	void
install_menu(Menu * NewMenu,int TotalMade)125 install_menu(Menu *NewMenu, int TotalMade)
126 {
127 	MenuOption **NewOptions;
128 	int	i;
129 
130 	if (!NewMenu)
131 		return;
132 	if (TotalMade != NewMenu->TotalOptions)
133 	{
134 		NewOptions = new_malloc(TotalMade * sizeof(MenuOption *));
135 		for (i = 0; i < TotalMade; i++)
136 			NewOptions[i] = NewMenu->Options[i];
137 		new_free(&NewMenu->Options);
138 		NewMenu->Options = NewOptions;
139 		NewMenu->TotalOptions = TotalMade;
140 	}
141 	add_to_list((List **)(void *)&MenuList, (List *) NewMenu);
142 	say("Menu \"%s\" added", NewMenu->Name);
143 }
144 
145 void
load_menu(u_char * FileName)146 load_menu(u_char *FileName)
147 {
148 	FILE	*fp;
149 	Menu	*NewMenu = NULL;
150 	MenuOption **NewOptions;
151 	u_char	*line, *command;
152 	u_char	*name, *func;
153 	u_char	buffer[FS_BUFFER_SIZE];
154 	int	linenum = 0;
155 	int	CurTotal = 0;
156 	int	FuncNum;
157 	int	i;
158 
159 	if ((fp = fopen(CP(FileName), "r")) == NULL)
160 	{
161 		say("Unable to open %s", FileName);
162 		return;
163 	}
164 	while (fgets(CP(buffer), sizeof buffer, fp))
165 	{
166 		u_char *s = my_index(buffer, '\n');
167 
168 		if (s)
169 			*s = '\0';
170 		linenum++;
171 		line = buffer;
172 		while (isspace(*line))
173 			line++;
174 		if (*line == '#' || !(command = next_arg(line, &line)))
175 			continue;
176 		if (!my_stricmp(command, UP("MENU")))
177 		{
178 			if (!line || !*line)
179 			{
180 				put_it("Missing argument in line %d of %s",
181 						linenum, FileName);
182 				continue;
183 			}
184 			install_menu(NewMenu, CurTotal);
185 			NewMenu = create_new_menu();
186 			malloc_strcpy(&NewMenu->Name, line);
187 		}
188 		else if (!my_stricmp(command, UP("OPTION")))
189 		{
190 			if (!(name = new_next_arg(line, &line)) ||
191 			    !(func = next_arg(line, &line)))
192 			{
193 				say("Missing argument in line %d of %s",
194 						linenum, FileName);
195 				continue;
196 			}
197 			for (i=0; OptionsList[i].name; i++)
198 				if (!my_stricmp(func, UP(OptionsList[i].name)))
199 					break;
200 			if (!OptionsList[i].name)
201 			{
202 				say("Unknown menu function \"%s\" in line %d of %s",
203 					func, linenum, FileName);
204 				continue;
205 			}
206 			FuncNum = i;
207 			if (++CurTotal > NewMenu->TotalOptions)
208 			{
209 				NewOptions = new_malloc(sizeof(*NewOptions) *
210 						(CurTotal + 4));
211 				for (i = 0; i < NewMenu->TotalOptions; i++)
212 					NewOptions[i] = NewMenu->Options[i];
213 				new_free(&NewMenu->Options);
214 				NewMenu->Options = NewOptions;
215 				NewMenu->TotalOptions = CurTotal + 4;
216 			}
217 			NewMenu->Options[CurTotal-1] = create_new_option();
218 			malloc_strcpy(&NewMenu->Options[CurTotal-1]->Name,
219 				name);
220 			malloc_strcpy(&NewMenu->Options[CurTotal-1]->Arguments,
221 				line);
222 			NewMenu->Options[CurTotal-1]->Func =
223 					OptionsList[FuncNum].func;
224 		}
225 		else
226 			say("Unkown menu command in line %d of %s",
227 				linenum, FileName);
228 	}
229 	install_menu(NewMenu, CurTotal);
230 	fclose(fp);
231 }
232 
233 int
show_menu_by_window(Window * window,int flags)234 show_menu_by_window(Window *window, int flags)
235 {
236 	int	i;
237 	int	largest;
238 	int	NumPerLine;
239 	int	len;
240 	WindowMenu *menu_info;
241 	Menu	*ThisMenu;
242 	int	CursorLoc;
243 	int	co = get_co();
244 
245 	menu_info = window_get_menu(window);
246 	ThisMenu = menu_info->menu;
247 	CursorLoc = menu_info->cursor;
248 	largest = 0;
249 	for (i = 0; i < ThisMenu->TotalOptions; i++)
250 		if ((len = my_strlen(ThisMenu->Options[i]->Name))>largest)
251 			largest = len;
252 	NumPerLine = (co - co % (largest + 3)) / (largest + 3);
253 	menu_info->items_per_line = NumPerLine;
254 	menu_info->lines = 0;
255 	for (i = 0; i < ThisMenu->TotalOptions; i++)
256 	{
257 		if ((flags & SMF_ERASE) && !(i % NumPerLine) &&
258 		    !(flags & SMF_CALCONLY))
259 		{
260 			term_move_cursor(0, window_get_top(window) +
261 					    menu_info->lines);
262 			term_clear_to_eol();
263 		}
264 		if ((i == CursorLoc || !(flags&SMF_CURSONLY)) &&
265 		    !(flags & SMF_CALCONLY))
266 		{
267 			if (i == CursorLoc && !(flags & SMF_NOCURSOR) &&
268 			    get_inside_menu() == 1)
269 				term_standout_on();
270 			else
271 				term_bold_on();
272 			term_move_cursor((i % NumPerLine) * (largest + 3),
273 				window_get_top(window) + menu_info->lines);
274 			fwrite(ThisMenu->Options[i]->Name,
275 				my_strlen(ThisMenu->Options[i]->Name), 1, stdout);
276 			if (i == CursorLoc && !(flags & SMF_NOCURSOR))
277 				term_standout_off();
278 			else
279 				term_bold_off();
280 		}
281 		if (!((i + 1) % NumPerLine))
282 			menu_info->lines++;
283 	}
284 	if (i % NumPerLine)
285 		menu_info->lines++;
286 	window_set_display_size_normal(window);
287 	if (window_get_cursor(window) < 0)
288 		window_set_cursor(window, 0);
289 	fflush(stdout);
290 	update_input(UPDATE_JUST_CURSOR);
291 	return ThisMenu->TotalOptions;
292 }
293 
294 int
show_menu(u_char * Name)295 show_menu(u_char *Name)
296 {
297 	Menu	*ThisMenu;
298 	Window	*window;
299 	WindowMenu *menu_info;
300 
301 	window = curr_scr_win;
302 	menu_info = window_get_menu(window);
303 	ThisMenu = (Menu *) find_in_list((List **)(void *)&MenuList, Name, 0);
304 	if (!ThisMenu)
305 	{
306 		say("No such menu \"%s\"", Name);
307 		return -1;
308 	}
309 	menu_info->cursor = 0;
310 	menu_info->menu = ThisMenu;
311 	return show_menu_by_window(window, SMF_CALCONLY);
312 }
313 
314 void
set_menu(u_char * Value)315 set_menu(u_char *Value)
316 {
317 	Window	*window;
318 	WindowMenu *menu_info;
319 	int	bottom;
320 
321 	window = curr_scr_win;
322 	menu_info = window_get_menu(window);
323 	if (!Value)
324 	{
325 		menu_info->menu = NULL;
326 		menu_info->lines = 0;
327 		window_set_display_size_normal(window);
328 		bottom = resize_display(window);
329 		redraw_resized(window, bottom);
330 		update_input(UPDATE_JUST_CURSOR);
331 		set_inside_menu(-1);
332 	}
333 	else
334 	{
335 		if (show_menu(Value) == -1)
336 		{
337 			set_string_var(MENU_VAR, NULL);
338 			return;
339 		}
340 		bottom = resize_display(window);
341 		redraw_resized(window, bottom);
342 		show_menu_by_window(window, SMF_ERASE);
343 	}
344 }
345 
346 void
enter_menu(u_int key,u_char * ptr)347 enter_menu(u_int key, u_char *ptr)
348 {
349 	WindowMenu *menu_info;
350 
351 	menu_info = window_get_menu(curr_scr_win);
352 	if (!menu_info->menu)
353 		return;
354 	set_inside_menu(1);
355 	show_menu_by_window(curr_scr_win, SMF_CURSONLY);
356 }
357 
358 
359 void
menu_previous(u_char * args)360 menu_previous(u_char *args)
361 {
362 }
363 
364 void
menu_submenu(u_char * args)365 menu_submenu(u_char *args)
366 {
367 }
368 
369 void
menu_exit(u_char * args)370 menu_exit(u_char *args)
371 {
372 	set_inside_menu(-1);
373 }
374 
375 void
menu_channels(u_char * args)376 menu_channels(u_char *args)
377 {
378 }
379 
380 void
menu_key(u_int ikey)381 menu_key(u_int ikey)
382 {
383 	Window *window;
384 	WindowMenu *menu_info;
385 	Menu	*ThisMenu;
386 	u_char	key = (u_char)ikey;
387 
388 	window = curr_scr_win;
389 	menu_info = window_get_menu(window);
390 	ThisMenu = menu_info->menu;
391 	show_menu_by_window(window, SMF_CURSONLY | SMF_NOCURSOR);
392 	switch(key)
393 	{
394 	case 'U':
395 	case 'u':
396 	case 'P':
397 	case 'p':
398 	case 'k':
399 	case 'K':
400 	case 'U' - '@':
401 	case 'P' - '@':
402 		menu_info->cursor -= menu_info->items_per_line;
403 		break;
404 	case 'n':
405 	case 'd':
406 	case 'j':
407 	case 'N':
408 	case 'D':
409 	case 'J':
410 	case 'D' - '@':
411 	case 'N' - '@':
412 		menu_info->cursor += menu_info->items_per_line;
413 		break;
414 	case 'b':
415 	case 'h':
416 	case 'B':
417 	case 'H':
418 	case 'B' - '@':
419 		menu_info->cursor--;
420 		break;
421 	case 'f':
422 	case 'l':
423 	case 'F':
424 	case 'L':
425 	case 'F' - '@':
426 		menu_info->cursor++;
427 		break;
428 	case '\033':
429 		break;
430 	case '\r':
431 	case '\n':
432 		window_hold_mode(NULL, OFF, 1);
433 		break;
434 	case ' ':
435 	case '.':
436 		set_inside_menu(0);
437 		ThisMenu->Options[menu_info->cursor]->Func(
438 			ThisMenu->Options[menu_info->cursor]->Arguments);
439 		if (get_inside_menu() != -1)
440 			set_inside_menu(1);
441 		else
442 			set_inside_menu(0);
443 		return; /* The menu may not be here any more */
444 	}
445 	if (menu_info->cursor >= menu_info->menu->TotalOptions)
446 		menu_info->cursor = menu_info->menu->TotalOptions - 1;
447 	if (get_inside_menu())
448 		show_menu_by_window(window, SMF_CURSONLY);
449 }
450 
451 void
menu_command(u_char * args)452 menu_command(u_char *args)
453 {
454 	parse_line(NULL, args, empty_string(), 0, 0, 0);
455 }
456 
457 WindowMenu *
menu_init(void)458 menu_init(void)
459 {
460 	WindowMenu *new = new_malloc(sizeof *new);
461 
462 	new->lines = 0;
463 	new->menu = 0;
464 
465 	return new;
466 }
467 
468 u_int
menu_lines(WindowMenu * menu)469 menu_lines(WindowMenu *menu)
470 {
471 	return menu->lines;
472 }
473 
474 int
menu_active(WindowMenu * menu)475 menu_active(WindowMenu *menu)
476 {
477 	return menu->menu != NULL;
478 }
479