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