1 /**
2  * \file ui-options.c
3  * \brief Text UI options handling code (everything accessible from '=')
4  *
5  * Copyright (c) 1997-2000 Robert A. Koeneke, James E. Wilson, Ben Harrison
6  * Copyright (c) 2007 Pete Mack
7  * Copyright (c) 2010 Andi Sidwell
8  *
9  * This work is free software; you can redistribute it and/or modify it
10  * under the terms of either:
11  *
12  * a) the GNU General Public License as published by the Free Software
13  *    Foundation, version 2, or
14  *
15  * b) the "Angband licence":
16  *    This software may be copied and distributed for educational, research,
17  *    and not for profit purposes provided that this copyright and statement
18  *    are included in all such copies.  Other copyrights may also apply.
19  */
20 #include "angband.h"
21 #include "cmds.h"
22 #include "game-input.h"
23 #include "init.h"
24 #include "obj-desc.h"
25 #include "obj-ignore.h"
26 #include "obj-tval.h"
27 #include "obj-util.h"
28 #include "object.h"
29 #include "player-calcs.h"
30 #include "ui-display.h"
31 #include "ui-help.h"
32 #include "ui-input.h"
33 #include "ui-keymap.h"
34 #include "ui-knowledge.h"
35 #include "ui-menu.h"
36 #include "ui-options.h"
37 #include "ui-prefs.h"
38 #include "ui-target.h"
39 
40 
41 /**
42  * Prompt the user for a filename to save the pref file to.
43  */
get_pref_path(const char * what,int row,char * buf,size_t max)44 static bool get_pref_path(const char *what, int row, char *buf, size_t max)
45 {
46 	char ftmp[80];
47 	bool ok;
48 
49 	screen_save();
50 
51 	/* Prompt */
52 	prt(format("%s to a pref file", what), row, 0);
53 	prt("File: ", row + 2, 0);
54 
55 	/* Get the filesystem-safe name and append .prf */
56 	player_safe_name(ftmp, sizeof(ftmp), player->full_name, true);
57 	my_strcat(ftmp, ".prf", sizeof(ftmp));
58 
59 	/* Get a filename */
60 
61 	if(!arg_force_name)
62 		ok = askfor_aux(ftmp, sizeof ftmp, NULL);
63 
64 	else
65 		ok = get_check(format("Confirm writing to %s? ", ftmp));
66 
67 	screen_load();
68 
69 	/* Build the filename */
70 	if (ok)
71 		path_build(buf, max, ANGBAND_DIR_USER, ftmp);
72 
73 	return ok;
74 }
75 
76 
dump_pref_file(void (* dump)(ang_file *),const char * title,int row)77 static void dump_pref_file(void (*dump)(ang_file *), const char *title, int row)
78 {
79 	char buf[1024];
80 
81 	/* Get filename from user */
82 	if (!get_pref_path(title, row, buf, sizeof(buf)))
83 		return;
84 
85 	/* Try to save */
86 	if (prefs_save(buf, dump, title))
87 		msg("Saved %s.", strstr(title, " ") + 1);
88 	else
89 		msg("Failed to save %s.", strstr(title, " ") + 1);
90 
91 	event_signal(EVENT_MESSAGE_FLUSH);
92 
93 	return;
94 }
95 
96 static void do_cmd_pref_file_hack(long row);
97 
98 
99 
100 
101 
102 
103 /**
104  * ------------------------------------------------------------------------
105  * Options display and setting
106  * ------------------------------------------------------------------------ */
107 
108 
109 /**
110  * Displays an option entry.
111  */
option_toggle_display(struct menu * m,int oid,bool cursor,int row,int col,int width)112 static void option_toggle_display(struct menu *m, int oid, bool cursor,
113 		int row, int col, int width)
114 {
115 	byte attr = curs_attrs[CURS_KNOWN][cursor != 0];
116 	bool *options = menu_priv(m);
117 
118 	c_prt(attr, format("%-45s: %s  (%s)", option_desc(oid),
119 			options[oid] ? "yes" : "no ", option_name(oid)), row, col);
120 }
121 
122 /**
123  * Handle keypresses for an option entry.
124  */
option_toggle_handle(struct menu * m,const ui_event * event,int oid)125 static bool option_toggle_handle(struct menu *m, const ui_event *event,
126 		int oid)
127 {
128 	bool next = false;
129 
130 	if (event->type == EVT_SELECT) {
131 		/* Hack -- birth options can not be toggled after birth */
132 		/* At birth, m->flags == MN_DBL_TAP. */
133 		/* After birth, m->flags == MN_NO_TAGS */
134 		if (!((option_type(oid) == OP_BIRTH) && (m->flags == MN_NO_TAGS))) {
135 			option_set(option_name(oid), !player->opts.opt[oid]);
136 		}
137 	} else if (event->type == EVT_KBRD) {
138 		if (event->key.code == 'y' || event->key.code == 'Y') {
139 			option_set(option_name(oid), true);
140 			next = true;
141 		} else if (event->key.code == 'n' || event->key.code == 'N') {
142 			option_set(option_name(oid), false);
143 			next = true;
144 		} else if (event->key.code == 't' || event->key.code == 'T') {
145 			option_set(option_name(oid), !player->opts.opt[oid]);
146 		} else if (event->key.code == 's' || event->key.code == 'S') {
147 			char dummy;
148 
149 			screen_save();
150 			if (options_save_custom_birth(&player->opts)) {
151 				get_com("Successfully saved.  Press any key to continue.", &dummy);
152 			} else {
153 				get_com("Save failed.  Press any key to continue.", &dummy);
154 			}
155 			screen_load();
156 		/* Only allow restore from custom defaults at birth. */
157 		} else if ((event->key.code == 'r' || event->key.code == 'R') &&
158 				m->flags == MN_DBL_TAP) {
159 			screen_save();
160 			if (options_restore_custom_birth(&player->opts)) {
161 				screen_load();
162 				menu_refresh(m, false);
163 			} else {
164 				char dummy;
165 
166 				get_com("Restore failed.  Press any key to continue.", &dummy);
167 				screen_load();
168 			}
169 		/* Only allow reset to maintainer's defaults at birth. */
170 		} else if ((event->key.code == 'm' || event->key.code == 'M') &&
171 				m->flags == MN_DBL_TAP) {
172 			options_reset_birth(&player->opts);
173 			menu_refresh(m, false);
174 		} else if (event->key.code == '?') {
175 			screen_save();
176 			show_file(format("option.txt#%s", option_name(oid)), NULL, 0, 0);
177 			screen_load();
178 		} else {
179 			return false;
180 		}
181 	} else {
182 		return false;
183 	}
184 
185 	if (next) {
186 		m->cursor++;
187 		m->cursor = (m->cursor + m->filter_count) % m->filter_count;
188 	}
189 
190 	return true;
191 }
192 
193 /**
194  * Toggle option menu display and handling functions
195  */
196 static const menu_iter option_toggle_iter = {
197 	NULL,
198 	NULL,
199 	option_toggle_display,
200 	option_toggle_handle,
201 	NULL
202 };
203 
204 
205 /**
206  * Interact with some options
207  */
option_toggle_menu(const char * name,int page)208 static void option_toggle_menu(const char *name, int page)
209 {
210 	int i;
211 
212 	struct menu *m = menu_new(MN_SKIN_SCROLL, &option_toggle_iter);
213 
214 	/* for all menus */
215 	m->prompt = "Set option (y/n/t), '?' for information";
216 	m->cmd_keys = "?YyNnTt";
217 	m->selections = "abcdefghijklmopqrsuvwxz";
218 	m->flags = MN_DBL_TAP;
219 
220 	/* We add 10 onto the page amount to indicate we're at birth */
221 	if (page == OPT_PAGE_BIRTH) {
222 		m->prompt = "You can only modify these options at character birth. '?' for information";
223 		m->cmd_keys = "?";
224 		m->flags = MN_NO_TAGS;
225 	} else if (page == OPT_PAGE_BIRTH + 10) {
226 		m->prompt = "Set option (y/n/t), 's' to save, 'r' to restore, 'm' to reset, '?' for help";
227 		m->cmd_keys = "?YyNnTtSsRrMm";
228 		page -= 10;
229 	}
230 
231 	/* for this particular menu */
232 	m->title = name;
233 
234 	/* Find the number of valid entries */
235 	for (i = 0; i < OPT_PAGE_PER; i++) {
236 		if (option_page[page][i] == OPT_none)
237 			break;
238 	}
239 
240 	/* Set the data to the player's options */
241 	menu_setpriv(m, OPT_MAX, &player->opts.opt);
242 	menu_set_filter(m, option_page[page], i);
243 	menu_layout(m, &SCREEN_REGION);
244 
245 	/* Run the menu */
246 	screen_save();
247 
248 	clear_from(0);
249 	menu_select(m, 0, false);
250 
251 	screen_load();
252 
253 	mem_free(m);
254 }
255 
256 /**
257  * Edit birth options.
258  */
do_cmd_options_birth(void)259 void do_cmd_options_birth(void)
260 {
261 	option_toggle_menu("Birth options", OPT_PAGE_BIRTH + 10);
262 }
263 
264 
265 /**
266  * Modify the "window" options
267  */
do_cmd_options_win(const char * name,int row)268 static void do_cmd_options_win(const char *name, int row)
269 {
270 	int i, j, d;
271 	int y = 0;
272 	int x = 0;
273 	ui_event ke;
274 	u32b new_flags[ANGBAND_TERM_MAX];
275 
276 	/* Set new flags to the old values */
277 	for (j = 0; j < ANGBAND_TERM_MAX; j++)
278 		new_flags[j] = window_flag[j];
279 
280 	/* Clear screen */
281 	screen_save();
282 	clear_from(0);
283 
284 	/* Interact */
285 	while (1) {
286 		/* Prompt */
287 		prt("Window flags (<dir> to move, 't'/Enter to toggle, or ESC)", 0, 0);
288 
289 		/* Display the windows */
290 		for (j = 0; j < ANGBAND_TERM_MAX; j++) {
291 			byte a = COLOUR_WHITE;
292 
293 			const char *s = angband_term_name[j];
294 
295 			/* Use color */
296 			if (j == x) a = COLOUR_L_BLUE;
297 
298 			/* Window name, staggered, centered */
299 			Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
300 		}
301 
302 		/* Display the options */
303 		for (i = 0; i < PW_MAX_FLAGS; i++) {
304 			byte a = COLOUR_WHITE;
305 
306 			const char *str = window_flag_desc[i];
307 
308 			/* Use color */
309 			if (i == y) a = COLOUR_L_BLUE;
310 
311 			/* Unused option */
312 			if (!str) str = "(Unused option)";
313 
314 			/* Flag name */
315 			Term_putstr(0, i + 5, -1, a, str);
316 
317 			/* Display the windows */
318 			for (j = 0; j < ANGBAND_TERM_MAX; j++) {
319 				wchar_t c = L'.';
320 
321 				a = COLOUR_WHITE;
322 
323 				/* Use color */
324 				if ((i == y) && (j == x)) a = COLOUR_L_BLUE;
325 
326 				/* Active flag */
327 				if (new_flags[j] & (1L << i)) c = L'X';
328 
329 				/* Flag value */
330 				Term_putch(35 + j * 5, i + 5, a, c);
331 			}
332 		}
333 
334 		/* Place Cursor */
335 		Term_gotoxy(35 + x * 5, y + 5);
336 
337 		/* Get key */
338 		ke = inkey_ex();
339 
340 		/* Mouse or keyboard interaction */
341 		if (ke.type == EVT_MOUSE) {
342 			int choicey = ke.mouse.y - 5;
343 			int choicex = (ke.mouse.x - 35)/5;
344 
345 			if (ke.mouse.button == 2)
346 				break;
347 
348 			if ((choicey >= 0) && (choicey < PW_MAX_FLAGS)
349 				&& (choicex > 0) && (choicex < ANGBAND_TERM_MAX)
350 				&& !(ke.mouse.x % 5)) {
351 				if ((choicey == y) && (choicex == x)) {
352 					/* Toggle flag (off) */
353 					if (new_flags[x] & (1L << y))
354 						new_flags[x] &= ~(1L << y);
355 					/* Toggle flag (on) */
356 					else
357 						new_flags[x] |= (1L << y);
358 				} else {
359 					y = choicey;
360 					x = (ke.mouse.x - 35)/5;
361 				}
362 			}
363 		} else if (ke.type == EVT_KBRD) {
364 			if (ke.key.code == ESCAPE || ke.key.code == 'q')
365 				break;
366 
367 			/* Toggle */
368 			else if (ke.key.code == '5' || ke.key.code == 't' ||
369 					ke.key.code == KC_ENTER) {
370 				/* Hack -- ignore the main window */
371 				if (x == 0)
372 					bell("Cannot set main window flags!");
373 
374 				/* Toggle flag (off) */
375 				else if (new_flags[x] & (1L << y))
376 					new_flags[x] &= ~(1L << y);
377 
378 				/* Toggle flag (on) */
379 				else
380 					new_flags[x] |= (1L << y);
381 
382 				/* Continue */
383 				continue;
384 			}
385 
386 			/* Extract direction */
387 			d = target_dir(ke.key);
388 
389 			/* Move */
390 			if (d != 0) {
391 				x = (x + ddx[d] + 8) % ANGBAND_TERM_MAX;
392 				y = (y + ddy[d] + 16) % PW_MAX_FLAGS;
393 			}
394 		}
395 	}
396 
397 	/* Notice changes */
398 	subwindows_set_flags(new_flags, ANGBAND_TERM_MAX);
399 
400 	screen_load();
401 }
402 
403 
404 
405 /**
406  * ------------------------------------------------------------------------
407  * Interact with keymaps
408  * ------------------------------------------------------------------------ */
409 
410 /**
411  * Current (or recent) keymap action
412  */
413 static struct keypress keymap_buffer[KEYMAP_ACTION_MAX + 1];
414 
415 
416 /**
417  * Ask for, and display, a keymap trigger.
418  *
419  * Returns the trigger input.
420  *
421  * Note that both "event_signal(EVENT_INPUT_FLUSH)" calls are extremely
422  * important.  This may
423  * no longer be true, since "util.c" is much simpler now.  XXX XXX XXX
424  */
keymap_get_trigger(void)425 static struct keypress keymap_get_trigger(void)
426 {
427 	char tmp[80];
428 	struct keypress buf[2] = { KEYPRESS_NULL, KEYPRESS_NULL };
429 
430 	/* Flush */
431 	event_signal(EVENT_INPUT_FLUSH);
432 
433 	/* Get a key */
434 	buf[0] = inkey();
435 
436 	/* Convert to ascii */
437 	keypress_to_text(tmp, sizeof(tmp), buf, false);
438 
439 	/* Hack -- display the trigger */
440 	Term_addstr(-1, COLOUR_WHITE, tmp);
441 
442 	/* Flush */
443 	event_signal(EVENT_INPUT_FLUSH);
444 
445 	/* Return trigger */
446 	return buf[0];
447 }
448 
449 
450 /**
451  * Keymap menu action functions
452  */
453 
ui_keymap_pref_load(const char * title,int row)454 static void ui_keymap_pref_load(const char *title, int row)
455 {
456 	do_cmd_pref_file_hack(16);
457 }
458 
ui_keymap_pref_append(const char * title,int row)459 static void ui_keymap_pref_append(const char *title, int row)
460 {
461 	dump_pref_file(keymap_dump, "Dump keymaps", 13);
462 }
463 
ui_keymap_query(const char * title,int row)464 static void ui_keymap_query(const char *title, int row)
465 {
466 	char tmp[1024];
467 	int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
468 	struct keypress c;
469 	const struct keypress *act;
470 
471 	prt(title, 13, 0);
472 	prt("Key: ", 14, 0);
473 
474 	/* Get a keymap trigger & mapping */
475 	c = keymap_get_trigger();
476 	act = keymap_find(mode, c);
477 
478 	/* Keymap found? */
479 	if (!act) {
480 		/* Prompt */
481 		prt("No keymap with that trigger.  Press any key to continue.", 16, 0);
482 		inkey();
483 	} else {
484 		/* Analyze the current action */
485 		keypress_to_text(tmp, sizeof(tmp), act, false);
486 
487 		/* Display the current action */
488 		prt("Found: ", 15, 0);
489 		Term_addstr(-1, COLOUR_WHITE, tmp);
490 
491 		prt("Press any key to continue.", 17, 0);
492 		inkey();
493 	}
494 }
495 
ui_keymap_create(const char * title,int row)496 static void ui_keymap_create(const char *title, int row)
497 {
498 	bool done = false;
499 	size_t n = 0;
500 
501 	struct keypress c;
502 	char tmp[1024];
503 	int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
504 
505 	prt(title, 13, 0);
506 	prt("Key: ", 14, 0);
507 
508 	c = keymap_get_trigger();
509 	if (c.code == '$') {
510 		c_prt(COLOUR_L_RED, "The '$' key is reserved.", 16, 2);
511 		prt("Press any key to continue.", 18, 0);
512 		inkey();
513 		return;
514 	}
515 
516 	/* Get an encoded action, with a default response */
517 	while (!done) {
518 		struct keypress kp = {EVT_NONE, 0, 0};
519 
520 		int color = COLOUR_WHITE;
521 		if (n == 0) color = COLOUR_YELLOW;
522 		if (n == KEYMAP_ACTION_MAX) color = COLOUR_L_RED;
523 
524 		keypress_to_text(tmp, sizeof(tmp), keymap_buffer, false);
525 		c_prt(color, format("Action: %s", tmp), 15, 0);
526 
527 		c_prt(COLOUR_L_BLUE, "  Press '$' when finished.", 17, 0);
528 		c_prt(COLOUR_L_BLUE, "  Use 'CTRL-U' to reset.", 18, 0);
529 		c_prt(COLOUR_L_BLUE, format("(Maximum keymap length is %d keys.)",
530 									KEYMAP_ACTION_MAX), 19, 0);
531 
532 		kp = inkey();
533 
534 		if (kp.code == '$') {
535 			done = true;
536 			continue;
537 		}
538 
539 		switch (kp.code) {
540 			case KC_DELETE:
541 			case KC_BACKSPACE: {
542 				if (n > 0) {
543 					n -= 1;
544 				    keymap_buffer[n].type = 0;
545 					keymap_buffer[n].code = 0;
546 					keymap_buffer[n].mods = 0;
547 				}
548 				break;
549 			}
550 
551 			case KTRL('U'): {
552 				memset(keymap_buffer, 0, sizeof keymap_buffer);
553 				n = 0;
554 				break;
555 			}
556 
557 			default: {
558 				if (n == KEYMAP_ACTION_MAX) continue;
559 
560 				if (n == 0) {
561 					memset(keymap_buffer, 0, sizeof keymap_buffer);
562 				}
563 				keymap_buffer[n++] = kp;
564 				break;
565 			}
566 		}
567 	}
568 
569 	if (c.code && get_check("Save this keymap? ")) {
570 		keymap_add(mode, c, keymap_buffer, true);
571 		prt("Keymap added.  Press any key to continue.", 17, 0);
572 		inkey();
573 	}
574 }
575 
ui_keymap_remove(const char * title,int row)576 static void ui_keymap_remove(const char *title, int row)
577 {
578 	struct keypress c;
579 	int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
580 
581 	prt(title, 13, 0);
582 	prt("Key: ", 14, 0);
583 
584 	c = keymap_get_trigger();
585 
586 	if (keymap_remove(mode, c))
587 		prt("Removed.", 16, 0);
588 	else
589 		prt("No keymap to remove!", 16, 0);
590 
591 	/* Prompt */
592 	prt("Press any key to continue.", 17, 0);
593 	inkey();
594 }
595 
keymap_browse_hook(int oid,void * db,const region * loc)596 static void keymap_browse_hook(int oid, void *db, const region *loc)
597 {
598 	char tmp[1024];
599 
600 	event_signal(EVENT_MESSAGE_FLUSH);
601 
602 	clear_from(13);
603 
604 	/* Show current action */
605 	prt("Current action (if any) shown below:", 13, 0);
606 	keypress_to_text(tmp, sizeof(tmp), keymap_buffer, false);
607 	prt(tmp, 14, 0);
608 }
609 
610 static struct menu *keymap_menu;
611 static menu_action keymap_actions[] =
612 {
613 	{ 0, 0, "Load a user pref file",    ui_keymap_pref_load },
614 	{ 0, 0, "Save keymaps to file",     ui_keymap_pref_append },
615 	{ 0, 0, "Query a keymap",           ui_keymap_query },
616 	{ 0, 0, "Create a keymap",          ui_keymap_create },
617 	{ 0, 0, "Remove a keymap",          ui_keymap_remove },
618 };
619 
do_cmd_keymaps(const char * title,int row)620 static void do_cmd_keymaps(const char *title, int row)
621 {
622 	region loc = {0, 0, 0, 12};
623 
624 	screen_save();
625 	clear_from(0);
626 
627 	if (!keymap_menu) {
628 		keymap_menu = menu_new_action(keymap_actions,
629 				N_ELEMENTS(keymap_actions));
630 
631 		keymap_menu->title = title;
632 		keymap_menu->selections = lower_case;
633 		keymap_menu->browse_hook = keymap_browse_hook;
634 	}
635 
636 	menu_layout(keymap_menu, &loc);
637 	menu_select(keymap_menu, 0, false);
638 
639 	screen_load();
640 }
641 
642 
643 
644 /**
645  * ------------------------------------------------------------------------
646  * Interact with visuals
647  * ------------------------------------------------------------------------ */
648 
visuals_pref_load(const char * title,int row)649 static void visuals_pref_load(const char *title, int row)
650 {
651 	do_cmd_pref_file_hack(15);
652 }
653 
visuals_dump_monsters(const char * title,int row)654 static void visuals_dump_monsters(const char *title, int row)
655 {
656 	dump_pref_file(dump_monsters, title, 15);
657 }
658 
visuals_dump_objects(const char * title,int row)659 static void visuals_dump_objects(const char *title, int row)
660 {
661 	dump_pref_file(dump_objects, title, 15);
662 }
663 
visuals_dump_features(const char * title,int row)664 static void visuals_dump_features(const char *title, int row)
665 {
666 	dump_pref_file(dump_features, title, 15);
667 }
668 
visuals_dump_flavors(const char * title,int row)669 static void visuals_dump_flavors(const char *title, int row)
670 {
671 	dump_pref_file(dump_flavors, title, 15);
672 }
673 
visuals_reset(const char * title,int row)674 static void visuals_reset(const char *title, int row)
675 {
676 	/* Reset */
677 	reset_visuals(true);
678 
679 	/* Message */
680 	prt("", 0, 0);
681 	msg("Visual attr/char tables reset.");
682 	event_signal(EVENT_MESSAGE_FLUSH);
683 }
684 
685 
686 static struct menu *visual_menu;
687 static menu_action visual_menu_items [] =
688 {
689 	{ 0, 0, "Load a user pref file",   visuals_pref_load },
690 	{ 0, 0, "Save monster attr/chars", visuals_dump_monsters },
691 	{ 0, 0, "Save object attr/chars",  visuals_dump_objects },
692 	{ 0, 0, "Save feature attr/chars", visuals_dump_features },
693 	{ 0, 0, "Save flavor attr/chars",  visuals_dump_flavors },
694 	{ 0, 0, "Reset visuals",           visuals_reset },
695 };
696 
697 
visuals_browse_hook(int oid,void * db,const region * loc)698 static void visuals_browse_hook(int oid, void *db, const region *loc)
699 {
700 	event_signal(EVENT_MESSAGE_FLUSH);
701 	clear_from(1);
702 }
703 
704 
705 /**
706  * Interact with "visuals"
707  */
do_cmd_visuals(const char * title,int row)708 static void do_cmd_visuals(const char *title, int row)
709 {
710 	screen_save();
711 	clear_from(0);
712 
713 	if (!visual_menu)
714 	{
715 		visual_menu = menu_new_action(visual_menu_items,
716 				N_ELEMENTS(visual_menu_items));
717 
718 		visual_menu->title = title;
719 		visual_menu->selections = lower_case;
720 		visual_menu->browse_hook = visuals_browse_hook;
721 		visual_menu->header = "To edit visuals, use the knowledge menu";
722 	}
723 
724 	menu_layout(visual_menu, &SCREEN_REGION);
725 	menu_select(visual_menu, 0, false);
726 
727 	screen_load();
728 }
729 
730 
731 /**
732  * ------------------------------------------------------------------------
733  * Interact with colours
734  * ------------------------------------------------------------------------ */
735 
colors_pref_load(const char * title,int row)736 static void colors_pref_load(const char *title, int row)
737 {
738 	/* Ask for and load a user pref file */
739 	do_cmd_pref_file_hack(8);
740 
741 	/* XXX should probably be a cleaner way to tell UI about
742 	 * colour changes - how about doing this in the pref file
743 	 * loading code too? */
744 	Term_xtra(TERM_XTRA_REACT, 0);
745 	Term_redraw();
746 }
747 
colors_pref_dump(const char * title,int row)748 static void colors_pref_dump(const char *title, int row)
749 {
750 	dump_pref_file(dump_colors, title, 15);
751 }
752 
colors_modify(const char * title,int row)753 static void colors_modify(const char *title, int row)
754 {
755 	int i;
756 
757 	static byte a = 0;
758 
759 	/* Prompt */
760 	prt("Command: Modify colors", 8, 0);
761 
762 	/* Hack -- query until done */
763 	while (1) {
764 		const char *name;
765 		char index;
766 
767 		struct keypress cx;
768 
769 		/* Clear */
770 		clear_from(10);
771 
772 		/* Exhibit the normal colors */
773 		for (i = 0; i < BASIC_COLORS; i++) {
774 			/* Exhibit this color */
775 			Term_putstr(i*3, 20, -1, a, "##");
776 
777 			/* Exhibit character letter */
778 			Term_putstr(i*3, 21, -1, (byte)i,
779 						format(" %c", color_table[i].index_char));
780 
781 			/* Exhibit all colors */
782 			Term_putstr(i*3, 22, -1, (byte)i, format("%2d", i));
783 		}
784 
785 		/* Describe the color */
786 		name = ((a < BASIC_COLORS) ? color_table[a].name : "undefined");
787 		index = ((a < BASIC_COLORS) ? color_table[a].index_char : '?');
788 
789 		/* Describe the color */
790 		Term_putstr(5, 10, -1, COLOUR_WHITE,
791 					format("Color = %d, Name = %s, Index = %c",
792 						   a, name, index));
793 
794 		/* Label the Current values */
795 		Term_putstr(5, 12, -1, COLOUR_WHITE,
796 				format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
797 				   angband_color_table[a][0],
798 				   angband_color_table[a][1],
799 				   angband_color_table[a][2],
800 				   angband_color_table[a][3]));
801 
802 		/* Prompt */
803 		Term_putstr(0, 14, -1, COLOUR_WHITE,
804 				"Command (n/N/k/K/r/R/g/G/b/B): ");
805 
806 		/* Get a command */
807 		cx = inkey();
808 
809 		/* All done */
810 		if (cx.code == ESCAPE) break;
811 
812 		/* Analyze */
813 		if (cx.code == 'n')
814 			a = (byte)(a + 1);
815 		if (cx.code == 'N')
816 			a = (byte)(a - 1);
817 		if (cx.code == 'k')
818 			angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1);
819 		if (cx.code == 'K')
820 			angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1);
821 		if (cx.code == 'r')
822 			angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1);
823 		if (cx.code == 'R')
824 			angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1);
825 		if (cx.code == 'g')
826 			angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1);
827 		if (cx.code == 'G')
828 			angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1);
829 		if (cx.code == 'b')
830 			angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1);
831 		if (cx.code == 'B')
832 			angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1);
833 
834 		/* Hack -- react to changes */
835 		Term_xtra(TERM_XTRA_REACT, 0);
836 
837 		/* Hack -- redraw */
838 		Term_redraw();
839 	}
840 }
841 
colors_browse_hook(int oid,void * db,const region * loc)842 static void colors_browse_hook(int oid, void *db, const region *loc)
843 {
844 	event_signal(EVENT_MESSAGE_FLUSH);
845 	clear_from(1);
846 }
847 
848 
849 static struct menu *color_menu;
850 static menu_action color_events [] =
851 {
852 	{ 0, 0, "Load a user pref file", colors_pref_load },
853 	{ 0, 0, "Dump colors",           colors_pref_dump },
854 	{ 0, 0, "Modify colors",         colors_modify }
855 };
856 
857 /**
858  * Interact with "colors"
859  */
do_cmd_colors(const char * title,int row)860 static void do_cmd_colors(const char *title, int row)
861 {
862 	screen_save();
863 	clear_from(0);
864 
865 	if (!color_menu)
866 	{
867 		color_menu = menu_new_action(color_events,
868 			N_ELEMENTS(color_events));
869 
870 		color_menu->title = title;
871 		color_menu->selections = lower_case;
872 		color_menu->browse_hook = colors_browse_hook;
873 	}
874 
875 	menu_layout(color_menu, &SCREEN_REGION);
876 	menu_select(color_menu, 0, false);
877 
878 	screen_load();
879 }
880 
881 
882 /**
883  * ------------------------------------------------------------------------
884  * Non-complex menu actions
885  * ------------------------------------------------------------------------ */
886 
askfor_aux_numbers(char * buf,size_t buflen,size_t * curs,size_t * len,struct keypress keypress,bool firsttime)887 static bool askfor_aux_numbers(char *buf, size_t buflen, size_t *curs, size_t *len, struct keypress keypress, bool firsttime)
888 {
889 	switch (keypress.code)
890 	{
891 		case ESCAPE:
892 		case KC_ENTER:
893 		case ARROW_LEFT:
894 		case ARROW_RIGHT:
895 		case KC_DELETE:
896 		case KC_BACKSPACE:
897 		case '0':
898 		case '1':
899 		case '2':
900 		case '3':
901 		case '4':
902 		case '5':
903 		case '6':
904 		case '7':
905 		case '8':
906 		case '9':
907 			return askfor_aux_keypress(buf, buflen, curs, len, keypress,
908 									   firsttime);
909 	}
910 
911 	return false;
912 }
913 
914 
915 /**
916  * Set base delay factor
917  */
do_cmd_delay(const char * name,int row)918 static void do_cmd_delay(const char *name, int row)
919 {
920 	char tmp[4] = "";
921 	int msec = player->opts.delay_factor;
922 
923 	strnfmt(tmp, sizeof(tmp), "%i", player->opts.delay_factor);
924 
925 	screen_save();
926 
927 	/* Prompt */
928 	prt("Command: Base Delay Factor", 20, 0);
929 
930 	prt(format("Current base delay factor: %d msec",
931 			   player->opts.delay_factor, msec), 22, 0);
932 	prt("New base delay factor (0-255): ", 21, 0);
933 
934 	/* Ask for a numeric value */
935 	if (askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers)) {
936 		u16b val = (u16b) strtoul(tmp, NULL, 0);
937 		player->opts.delay_factor = MIN(val, 255);
938 	}
939 
940 	screen_load();
941 }
942 
943 /**
944  * Set sidebar mode
945  */
do_cmd_sidebar_mode(const char * name,int row)946 static void do_cmd_sidebar_mode(const char *name, int row)
947 {
948 	char tmp[20] = "";
949 	const char *names[SIDEBAR_MAX] = {"Left", "Top", "None"};
950 	struct keypress cx = KEYPRESS_NULL;
951 
952 	screen_save();
953 
954 	while (true) {
955 
956 		// Get the name
957 		my_strcpy(tmp, names[SIDEBAR_MODE % SIDEBAR_MAX], sizeof(tmp));
958 
959 		/* Prompt */
960 		prt("Command: Sidebar Mode", 20, 0);
961 
962 		prt("ESC: go back, other: cycle", 22, 0);
963 
964 		prt(format("Current mode: %s", tmp), 21, 0);
965 
966 		/* Get a command */
967 		cx = inkey();
968 
969 		/* All done */
970 		if (cx.code == ESCAPE) break;
971 
972 		// Cycle
973 		SIDEBAR_MODE = (SIDEBAR_MODE + 1) % SIDEBAR_MAX;
974 	}
975 
976 	screen_load();
977 }
978 
979 
980 /**
981  * Set hitpoint warning level
982  */
do_cmd_hp_warn(const char * name,int row)983 static void do_cmd_hp_warn(const char *name, int row)
984 {
985 	bool res;
986 	char tmp[4] = "";
987 	byte warn;
988 
989 	strnfmt(tmp, sizeof(tmp), "%i", player->opts.hitpoint_warn);
990 
991 	screen_save();
992 
993 	/* Prompt */
994 	prt("Command: Hitpoint Warning", 20, 0);
995 
996 	prt(format("Current hitpoint warning: %d (%d%%)",
997 			   player->opts.hitpoint_warn, player->opts.hitpoint_warn * 10), 22, 0);
998 	prt("New hitpoint warning (0-9): ", 21, 0);
999 
1000 	/* Ask the user for a string */
1001 	res = askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers);
1002 
1003 	/* Process input */
1004 	if (res) {
1005 		warn = (byte) strtoul(tmp, NULL, 0);
1006 
1007 		/* Reset nonsensical warnings */
1008 		if (warn > 9)
1009 			warn = 0;
1010 
1011 		player->opts.hitpoint_warn = warn;
1012 	}
1013 
1014 	screen_load();
1015 }
1016 
1017 
1018 /**
1019  * Set "lazy-movement" delay
1020  */
do_cmd_lazymove_delay(const char * name,int row)1021 static void do_cmd_lazymove_delay(const char *name, int row)
1022 {
1023 	bool res;
1024 	char tmp[4] = "";
1025 
1026 	strnfmt(tmp, sizeof(tmp), "%i", player->opts.lazymove_delay);
1027 
1028 	screen_save();
1029 
1030 	/* Prompt */
1031 	prt("Command: Movement Delay Factor", 20, 0);
1032 
1033 	prt(format("Current movement delay: %d (%d msec)",
1034 			   player->opts.lazymove_delay, player->opts.lazymove_delay * 10), 22, 0);
1035 	prt("New movement delay: ", 21, 0);
1036 
1037 	/* Ask the user for a string */
1038 	res = askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers);
1039 
1040 	/* Process input */
1041 	if (res) {
1042 		u16b delay = (u16b)strtoul(tmp, NULL, 0);
1043 		player->opts.lazymove_delay = MIN(delay, 255);
1044 	}
1045 
1046 	screen_load();
1047 }
1048 
1049 
1050 
1051 /**
1052  * Ask for a "user pref file" and process it.
1053  *
1054  * This function should only be used by standard interaction commands,
1055  * in which a standard "Command:" prompt is present on the given row.
1056  *
1057  * Allow absolute file names?  XXX XXX XXX
1058  */
do_cmd_pref_file_hack(long row)1059 static void do_cmd_pref_file_hack(long row)
1060 {
1061 	char ftmp[80];
1062 	bool ok;
1063 
1064 	screen_save();
1065 
1066 	/* Prompt */
1067 	prt("Command: Load a user pref file", row, 0);
1068 
1069 	/* Prompt */
1070 	prt("File: ", row + 2, 0);
1071 
1072 	/* Get the filesystem-safe name and append .prf */
1073 	player_safe_name(ftmp, sizeof(ftmp), player->full_name, true);
1074 	my_strcat(ftmp, ".prf", sizeof(ftmp));
1075 
1076 	if(!arg_force_name)
1077 		ok = askfor_aux(ftmp, sizeof ftmp, NULL);
1078 	else
1079 		ok = get_check(format("Confirm loading %s? ", ftmp));
1080 
1081 	/* Ask for a file (or cancel) */
1082 	if(ok) {
1083 		/* Process the given filename */
1084 		if (process_pref_file(ftmp, false, true) == false) {
1085 			/* Mention failure */
1086 			prt("", 0, 0);
1087 			msg("Failed to load '%s'!", ftmp);
1088 		} else {
1089 			/* Mention success */
1090 			prt("", 0, 0);
1091 			msg("Loaded '%s'.", ftmp);
1092 		}
1093 	}
1094 
1095 	screen_load();
1096 }
1097 
1098 
1099 
1100 /**
1101  * Write options to a file.
1102  */
do_dump_options(const char * title,int row)1103 static void do_dump_options(const char *title, int row) {
1104 	dump_pref_file(option_dump, "Dump window settings", 20);
1105 }
1106 
1107 /**
1108  * Write autoinscriptions to a file.
1109  */
do_dump_autoinsc(const char * title,int row)1110 static void do_dump_autoinsc(const char *title, int row) {
1111 	dump_pref_file(dump_autoinscriptions, "Dump autoinscriptions", 20);
1112 }
1113 
1114 /**
1115  * Write character screen customizations to a file.
1116  */
do_dump_charscreen_opt(const char * title,int row)1117 static void do_dump_charscreen_opt(const char *title, int row) {
1118 	dump_pref_file(dump_ui_entry_renderers, "Dump char screen options", 20);
1119 }
1120 
1121 /**
1122  * Load a pref file.
1123  */
options_load_pref_file(const char * n,int row)1124 static void options_load_pref_file(const char *n, int row)
1125 {
1126 	do_cmd_pref_file_hack(20);
1127 }
1128 
1129 
1130 
1131 /**
1132  * ------------------------------------------------------------------------
1133  * Ego item ignore menu
1134  * ------------------------------------------------------------------------ */
1135 
1136 #define EGO_MENU_HELPTEXT \
1137 "{light green}Movement keys{/} scroll the list\n{light red}ESC{/} returns to the previous menu\n{light blue}Enter{/} toggles the current setting."
1138 
1139 /**
1140  * Skip common prefixes in ego-item names.
1141  */
strip_ego_name(const char * name)1142 static const char *strip_ego_name(const char *name)
1143 {
1144 	if (prefix(name, "of the "))
1145 		return name + 7;
1146 	if (prefix(name, "of "))
1147 		return name + 3;
1148 	return name;
1149 }
1150 
1151 
1152 /**
1153  * Display an ego-item type on the screen.
1154  */
ego_item_name(char * buf,size_t buf_size,struct ego_desc * desc)1155 int ego_item_name(char *buf, size_t buf_size, struct ego_desc *desc)
1156 {
1157 	size_t i;
1158 	int end;
1159 	size_t prefix_size;
1160 	const char *long_name;
1161 
1162 	struct ego_item *ego = &e_info[desc->e_idx];
1163 
1164 	/* Find the ignore type */
1165 	for (i = 0; i < N_ELEMENTS(quality_choices); i++)
1166 		if (desc->itype == i) break;
1167 
1168 	if (i == N_ELEMENTS(quality_choices)) return 0;
1169 
1170 	/* Initialize the buffer */
1171 	end = my_strcat(buf, "[ ] ", buf_size);
1172 
1173 	/* Append the name */
1174 	end += my_strcat(buf, quality_choices[i].name, buf_size);
1175 
1176 	/* Append an extra space */
1177 	end += my_strcat(buf, " ", buf_size);
1178 
1179 	/* Get the full ego-item name */
1180 	long_name = ego->name;
1181 
1182 	/* Get the length of the common prefix, if any */
1183 	prefix_size = (desc->short_name - long_name);
1184 
1185 	/* Found a prefix? */
1186 	if (prefix_size > 0) {
1187 		char prefix[100];
1188 
1189 		/* Get a copy of the prefix */
1190 		my_strcpy(prefix, long_name, prefix_size + 1);
1191 
1192 		/* Append the prefix */
1193 		end += my_strcat(buf, prefix, buf_size);
1194 	}
1195 	/* Set the name to the right length */
1196 	return end;
1197 }
1198 
1199 /**
1200  * Utility function used for sorting an array of ego-item indices by
1201  * ego-item name.
1202  */
ego_comp_func(const void * a_ptr,const void * b_ptr)1203 static int ego_comp_func(const void *a_ptr, const void *b_ptr)
1204 {
1205 	const struct ego_desc *a = a_ptr;
1206 	const struct ego_desc *b = b_ptr;
1207 
1208 	/* Note the removal of common prefixes */
1209 	return (strcmp(a->short_name, b->short_name));
1210 }
1211 
1212 /**
1213  * Display an entry on the sval menu
1214  */
ego_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1215 static void ego_display(struct menu * menu, int oid, bool cursor, int row,
1216 						int col, int width)
1217 {
1218 	char buf[80] = "";
1219 	struct ego_desc *choice = (struct ego_desc *) menu->menu_data;
1220 	bool ignored = ego_is_ignored(choice[oid].e_idx, choice[oid].itype);
1221 
1222 	byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1223 	byte sq_attr = (ignored ? COLOUR_L_RED : COLOUR_L_GREEN);
1224 
1225 	/* Acquire the "name" of object "i" */
1226 	(void) ego_item_name(buf, sizeof(buf), &choice[oid]);
1227 
1228 	/* Print it */
1229 	c_put_str(attr, format("%s", buf), row, col);
1230 
1231 	/* Show ignore mark, if any */
1232 	if (ignored)
1233 		c_put_str(COLOUR_L_RED, "*", row, col + 1);
1234 
1235 	/* Show the stripped ego-item name using another colour */
1236 	c_put_str(sq_attr, choice[oid].short_name, row, col + strlen(buf));
1237 }
1238 
1239 /**
1240  * Deal with events on the sval menu
1241  */
ego_action(struct menu * menu,const ui_event * event,int oid)1242 static bool ego_action(struct menu * menu, const ui_event * event, int oid)
1243 {
1244 	struct ego_desc *choice = menu->menu_data;
1245 
1246 	/* Toggle */
1247 	if (event->type == EVT_SELECT) {
1248 		ego_ignore_toggle(choice[oid].e_idx, choice[oid].itype);
1249 
1250 		return true;
1251 	}
1252 
1253 	return false;
1254 }
1255 
1256 /**
1257  * Display list of ego items to be ignored.
1258  */
ego_menu(const char * unused,int also_unused)1259 static void ego_menu(const char *unused, int also_unused)
1260 {
1261 	int max_num = 0;
1262 	struct ego_item *ego;
1263 	struct ego_desc *choice;
1264 
1265 	struct menu menu;
1266 	menu_iter menu_f = { 0, 0, ego_display, ego_action, 0 };
1267 	region area = { 1, 5, -1, -1 };
1268 	int cursor = 0;
1269 
1270 	int i;
1271 
1272 	/* Create the array */
1273 	choice = mem_zalloc(z_info->e_max * ITYPE_MAX * sizeof(struct ego_desc));
1274 
1275 	/* Get the valid ego-items */
1276 	for (i = 0; i < z_info->e_max; i++) {
1277 		int itype;
1278 		ego = &e_info[i];
1279 
1280 		/* Only valid known ego-items allowed */
1281 		if (!ego->name || !ego->everseen)
1282 			continue;
1283 
1284 		/* Find appropriate ignore types */
1285 		for (itype = ITYPE_NONE + 1; itype < ITYPE_MAX; itype++)
1286 			if (ego_has_ignore_type(ego, itype)) {
1287 
1288 				/* Fill in the details */
1289 				choice[max_num].e_idx = i;
1290 				choice[max_num].itype = itype;
1291 				choice[max_num].short_name = strip_ego_name(ego->name);
1292 
1293 				++max_num;
1294 			}
1295 	}
1296 
1297 	/* Quickly sort the array by ego-item name */
1298 	qsort(choice, max_num, sizeof(choice[0]), ego_comp_func);
1299 
1300 	/* Return here if there are no objects */
1301 	if (!max_num) {
1302 		mem_free(choice);
1303 		return;
1304 	}
1305 
1306 
1307 	/* Save the screen and clear it */
1308 	screen_save();
1309 	clear_from(0);
1310 
1311 	/* Help text */
1312 	prt("Ego item ignore menu", 0, 0);
1313 
1314 	/* Output to the screen */
1315 	text_out_hook = text_out_to_screen;
1316 
1317 	/* Indent output */
1318 	text_out_indent = 1;
1319 	text_out_wrap = 79;
1320 	Term_gotoxy(1, 1);
1321 
1322 	/* Display some helpful information */
1323 	text_out_e(EGO_MENU_HELPTEXT);
1324 
1325 	text_out_indent = 0;
1326 
1327 	/* Set up the menu */
1328 	memset(&menu, 0, sizeof(menu));
1329 	menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1330 	menu_setpriv(&menu, max_num, choice);
1331 	menu_layout(&menu, &area);
1332 
1333 	/* Select an entry */
1334 	(void) menu_select(&menu, cursor, false);
1335 
1336 	/* Free memory */
1337 	mem_free(choice);
1338 
1339 	/* Load screen */
1340 	screen_load();
1341 
1342 	return;
1343 }
1344 
1345 
1346 /**
1347  * ------------------------------------------------------------------------
1348  * Quality ignore menu
1349  * ------------------------------------------------------------------------ */
1350 
1351 /**
1352  * Menu struct for differentiating aware from unaware ignore
1353  */
1354 typedef struct
1355 {
1356 	struct object_kind *kind;
1357 	bool aware;
1358 } ignore_choice;
1359 
1360 /**
1361  * Ordering function for ignore choices.
1362  * Aware comes before unaware, and then sort alphabetically.
1363  */
cmp_ignore(const void * a,const void * b)1364 static int cmp_ignore(const void *a, const void *b)
1365 {
1366 	char bufa[80];
1367 	char bufb[80];
1368 	const ignore_choice *x = a;
1369 	const ignore_choice *y = b;
1370 
1371 	if (!x->aware && y->aware)
1372 		return 1;
1373 	if (x->aware && !y->aware)
1374 		return -1;
1375 
1376 	object_kind_name(bufa, sizeof(bufa), x->kind, x->aware);
1377 	object_kind_name(bufb, sizeof(bufb), y->kind, y->aware);
1378 
1379 	return strcmp(bufa, bufb);
1380 }
1381 
1382 /**
1383  * Determine if an item is a valid choice
1384  */
quality_validity(struct menu * menu,int oid)1385 int quality_validity(struct menu *menu, int oid)
1386 {
1387 	return oid ? 1 : 0;
1388 }
1389 
1390 /**
1391  * Display an entry in the menu.
1392  */
quality_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1393 static void quality_display(struct menu *menu, int oid, bool cursor, int row,
1394 							int col, int width)
1395 {
1396 	/* Note: the order of the values in quality_choices do not align with the
1397 	 * ignore_type_t enum order. - fix? NRM*/
1398 	const char *name = quality_choices[oid].name;
1399 
1400 	byte level = ignore_level[oid];
1401 	const char *level_name = quality_values[level].name;
1402 
1403 	byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1404 
1405 	if (oid)
1406 		c_put_str(attr, format("%-30s : %s", name, level_name), row, col);
1407 }
1408 
1409 
1410 /**
1411  * Display the quality ignore subtypes.
1412  */
quality_subdisplay(struct menu * menu,int oid,bool cursor,int row,int col,int width)1413 static void quality_subdisplay(struct menu *menu, int oid, bool cursor, int row,
1414 							   int col, int width)
1415 {
1416 	const char *name = quality_values[oid].name;
1417 	byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1418 
1419 	c_put_str(attr, name, row, col);
1420 }
1421 
1422 
1423 /**
1424  * Handle keypresses.
1425  */
quality_action(struct menu * m,const ui_event * event,int oid)1426 static bool quality_action(struct menu *m, const ui_event *event, int oid)
1427 {
1428 	struct menu menu;
1429 	menu_iter menu_f = { NULL, NULL, quality_subdisplay, NULL, NULL };
1430 	region area = { 37, 2, 29, IGNORE_MAX };
1431 	ui_event evt;
1432 	int count;
1433 
1434 	/* Display at the right point */
1435 	area.row += oid;
1436 
1437 	/* Save */
1438 	screen_save();
1439 
1440 	/* Work out how many options we have */
1441 	count = IGNORE_MAX;
1442 	if ((oid == ITYPE_RING) || (oid == ITYPE_AMULET))
1443 		count = area.page_rows = IGNORE_BAD + 1;
1444 
1445 	/* Run menu */
1446 	menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1447 	menu_setpriv(&menu, count, quality_values);
1448 
1449 	/* Stop menus from going off the bottom of the screen */
1450 	if (area.row + menu.count > Term->hgt - 1)
1451 		area.row += Term->hgt - 1 - area.row - menu.count;
1452 
1453 	menu_layout(&menu, &area);
1454 
1455 	window_make(area.col - 2, area.row - 1, area.col + area.width + 2,
1456 				area.row + area.page_rows);
1457 
1458 	evt = menu_select(&menu, 0, true);
1459 
1460 	/* Set the new value appropriately */
1461 	if (evt.type == EVT_SELECT)
1462 		ignore_level[oid] = menu.cursor;
1463 
1464 	/* Load and finish */
1465 	screen_load();
1466 	return true;
1467 }
1468 
1469 /**
1470  * Display quality ignore menu.
1471  */
quality_menu(void * unused,const char * also_unused)1472 static void quality_menu(void *unused, const char *also_unused)
1473 {
1474 	struct menu menu;
1475 	menu_iter menu_f = { NULL, quality_validity, quality_display,
1476 						 quality_action, NULL };
1477 	region area = { 0, 0, 0, 0 };
1478 
1479 	/* Save screen */
1480 	screen_save();
1481 	clear_from(0);
1482 
1483 	/* Set up the menu */
1484 	menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1485 	menu.title = "Quality ignore menu";
1486 	menu_setpriv(&menu, ITYPE_MAX, quality_values);
1487 	menu_layout(&menu, &area);
1488 
1489 	/* Select an entry */
1490 	menu_select(&menu, 0, false);
1491 
1492 	/* Load screen */
1493 	screen_load();
1494 	return;
1495 }
1496 
1497 
1498 
1499 /**
1500  * ------------------------------------------------------------------------
1501  * Sval ignore menu
1502  * ------------------------------------------------------------------------ */
1503 
1504 /**
1505  * Structure to describe tval/description pairings.
1506  */
1507 typedef struct
1508 {
1509 	int tval;
1510 	const char *desc;
1511 } tval_desc;
1512 
1513 /**
1514  * Categories for sval-dependent ignore.
1515  */
1516 static tval_desc sval_dependent[] =
1517 {
1518 	{ TV_STAFF,			"Staffs" },
1519 	{ TV_WAND,			"Wands" },
1520 	{ TV_ROD,			"Rods" },
1521 	{ TV_SCROLL,		"Scrolls" },
1522 	{ TV_POTION,		"Potions" },
1523 	{ TV_RING,			"Rings" },
1524 	{ TV_AMULET,		"Amulets" },
1525 	{ TV_FOOD,			"Food" },
1526 	{ TV_MUSHROOM,		"Mushrooms" },
1527 	{ TV_MAGIC_BOOK,	"Magic books" },
1528 	{ TV_PRAYER_BOOK,	"Prayer books" },
1529 	{ TV_NATURE_BOOK,	"Nature books" },
1530 	{ TV_SHADOW_BOOK,	"Shadow books" },
1531 	{ TV_OTHER_BOOK,	"Mystery books" },
1532 	{ TV_LIGHT,			"Lights" },
1533 	{ TV_FLASK,			"Flasks of oil" },
1534 	{ TV_GOLD,			"Money" },
1535 };
1536 
1537 
1538 /**
1539  * Determines whether a tval is eligible for sval-ignore.
1540  */
ignore_tval(int tval)1541 bool ignore_tval(int tval)
1542 {
1543 	size_t i;
1544 
1545 	/* Only ignore if the tval's allowed */
1546 	for (i = 0; i < N_ELEMENTS(sval_dependent); i++) {
1547 		if (kb_info[tval].num_svals == 0) continue;
1548 		if (tval == sval_dependent[i].tval)
1549 			return true;
1550 	}
1551 
1552 	return false;
1553 }
1554 
1555 
1556 /**
1557  * Display an entry on the sval menu
1558  */
ignore_sval_menu_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1559 static void ignore_sval_menu_display(struct menu *menu, int oid, bool cursor,
1560 									 int row, int col, int width)
1561 {
1562 	char buf[80];
1563 	const ignore_choice *choice = menu_priv(menu);
1564 
1565 	struct object_kind *kind = choice[oid].kind;
1566 	bool aware = choice[oid].aware;
1567 
1568 	byte attr = curs_attrs[(int)aware][0 != cursor];
1569 
1570 	/* Acquire the "name" of object "i" */
1571 	object_kind_name(buf, sizeof(buf), kind, aware);
1572 
1573 	/* Print it */
1574 	c_put_str(attr, format("[ ] %s", buf), row, col);
1575 	if ((aware && (kind->ignore & IGNORE_IF_AWARE)) ||
1576 			(!aware && (kind->ignore & IGNORE_IF_UNAWARE)))
1577 		c_put_str(COLOUR_L_RED, "*", row, col + 1);
1578 }
1579 
1580 
1581 /**
1582  * Deal with events on the sval menu
1583  */
ignore_sval_menu_action(struct menu * m,const ui_event * event,int oid)1584 static bool ignore_sval_menu_action(struct menu *m, const ui_event *event,
1585 									int oid)
1586 {
1587 	const ignore_choice *choice = menu_priv(m);
1588 
1589 	if (event->type == EVT_SELECT ||
1590 			(event->type == EVT_KBRD && tolower(event->key.code) == 't')) {
1591 		struct object_kind *kind = choice[oid].kind;
1592 
1593 		/* Toggle the appropriate flag */
1594 		if (choice[oid].aware)
1595 			kind->ignore ^= IGNORE_IF_AWARE;
1596 		else
1597 			kind->ignore ^= IGNORE_IF_UNAWARE;
1598 
1599 		player->upkeep->notice |= PN_IGNORE;
1600 		return true;
1601 	}
1602 
1603 	return false;
1604 }
1605 
1606 static const menu_iter ignore_sval_menu =
1607 {
1608 	NULL,
1609 	NULL,
1610 	ignore_sval_menu_display,
1611 	ignore_sval_menu_action,
1612 	NULL,
1613 };
1614 
1615 
1616 /**
1617  * Collect all tvals in the big ignore_choice array
1618  */
ignore_collect_kind(int tval,ignore_choice ** ch)1619 static int ignore_collect_kind(int tval, ignore_choice **ch)
1620 {
1621 	ignore_choice *choice;
1622 	int num = 0;
1623 
1624 	int i;
1625 
1626 	/* Create the array, with entries both for aware and unaware ignore */
1627 	choice = mem_alloc(2 * z_info->k_max * sizeof *choice);
1628 
1629 	for (i = 1; i < z_info->k_max; i++) {
1630 		struct object_kind *kind = &k_info[i];
1631 
1632 		/* Skip empty objects, unseen objects, and incorrect tvals */
1633 		if (!kind->name || kind->tval != tval)
1634 			continue;
1635 
1636 		if (!kind->aware) {
1637 			/* can unaware ignore anything */
1638 			choice[num].kind = kind;
1639 			choice[num++].aware = false;
1640 		}
1641 
1642 		if ((kind->everseen && !kf_has(kind->kind_flags, KF_INSTA_ART)) ||
1643 			tval_is_money_k(kind)) {
1644 			/* Do not display the artifact base kinds in this list
1645 			 * aware ignore requires everseen
1646 			 * do not require awareness for aware ignore, so people can set
1647 			 * at game start */
1648 			choice[num].kind = kind;
1649 			choice[num++].aware = true;
1650 		}
1651 	}
1652 
1653 	if (num == 0)
1654 		mem_free(choice);
1655 	else
1656 		*ch = choice;
1657 
1658 	return num;
1659 }
1660 
1661 /**
1662  * Display list of svals to be ignored.
1663  */
sval_menu(int tval,const char * desc)1664 static bool sval_menu(int tval, const char *desc)
1665 {
1666 	struct menu *menu;
1667 	region area = { 1, 2, -1, -1 };
1668 
1669 	ignore_choice *choices;
1670 
1671 	int n_choices = ignore_collect_kind(tval, &choices);
1672 	if (!n_choices)
1673 		return false;
1674 
1675 	/* Sort by name in ignore menus except for categories of items that are
1676 	 * aware from the start */
1677 	switch (tval)
1678 	{
1679 		case TV_LIGHT:
1680 		case TV_MAGIC_BOOK:
1681 		case TV_PRAYER_BOOK:
1682 		case TV_NATURE_BOOK:
1683 		case TV_SHADOW_BOOK:
1684 		case TV_OTHER_BOOK:
1685 		case TV_DRAG_ARMOR:
1686 		case TV_GOLD:
1687 			/* leave sorted by sval */
1688 			break;
1689 
1690 		default:
1691 			/* sort by name */
1692 			sort(choices, n_choices, sizeof(*choices), cmp_ignore);
1693 	}
1694 
1695 
1696 	/* Save the screen and clear it */
1697 	screen_save();
1698 	clear_from(0);
1699 
1700 	/* Help text */
1701 	prt(format("Ignore the following %s:", desc), 0, 0);
1702 
1703 	/* Run menu */
1704 	menu = menu_new(MN_SKIN_COLUMNS, &ignore_sval_menu);
1705 	menu_setpriv(menu, n_choices, choices);
1706 	menu->cmd_keys = "Tt";
1707 	menu_layout(menu, &area);
1708 	menu_set_cursor_x_offset(menu, 1); /* Place cursor in brackets. */
1709 	menu_select(menu, 0, false);
1710 
1711 	/* Free memory */
1712 	mem_free(menu);
1713 	mem_free(choices);
1714 
1715 	/* Load screen */
1716 	screen_load();
1717 	return true;
1718 }
1719 
1720 
1721 /**
1722  * Returns true if there's anything to display a menu of
1723  */
seen_tval(int tval)1724 static bool seen_tval(int tval)
1725 {
1726 	int i;
1727 
1728 	for (i = 1; i < z_info->k_max; i++) {
1729 		struct object_kind *kind = &k_info[i];
1730 
1731 		/* Skip empty objects, unseen objects, and incorrect tvals */
1732 		if (!kind->name) continue;
1733 		if (!kind->everseen) continue;
1734 		if (kind->tval != tval) continue;
1735 
1736 		 return true;
1737 	}
1738 
1739 
1740 	return false;
1741 }
1742 
1743 
1744 /**
1745  * Extra options on the "item options" menu
1746  */
1747 static struct
1748 {
1749 	char tag;
1750 	const char *name;
1751 	void (*action)(); /* this is a nasty hack */
1752 } extra_item_options[] = {
1753 	{ 'Q', "Quality ignoring options", quality_menu },
1754 	{ 'E', "Ego ignoring options", ego_menu},
1755 	{ '{', "Autoinscription setup", textui_browse_object_knowledge },
1756 };
1757 
tag_options_item(struct menu * menu,int oid)1758 static char tag_options_item(struct menu *menu, int oid)
1759 {
1760 	size_t line = (size_t) oid;
1761 
1762 	if (line < N_ELEMENTS(sval_dependent))
1763 		return I2A(oid);
1764 
1765 	/* Separator - blank line. */
1766 	if (line == N_ELEMENTS(sval_dependent))
1767 		return 0;
1768 
1769 	line = line - N_ELEMENTS(sval_dependent) - 1;
1770 
1771 	if (line < N_ELEMENTS(extra_item_options))
1772 		return extra_item_options[line].tag;
1773 
1774 	return 0;
1775 }
1776 
valid_options_item(struct menu * menu,int oid)1777 static int valid_options_item(struct menu *menu, int oid)
1778 {
1779 	size_t line = (size_t) oid;
1780 
1781 	if (line < N_ELEMENTS(sval_dependent))
1782 		return 1;
1783 
1784 	/* Separator - blank line. */
1785 	if (line == N_ELEMENTS(sval_dependent))
1786 		return 0;
1787 
1788 	line = line - N_ELEMENTS(sval_dependent) - 1;
1789 
1790 	if (line < N_ELEMENTS(extra_item_options))
1791 		return 1;
1792 
1793 	return 0;
1794 }
1795 
display_options_item(struct menu * menu,int oid,bool cursor,int row,int col,int width)1796 static void display_options_item(struct menu *menu, int oid, bool cursor,
1797 								 int row, int col, int width)
1798 {
1799 	size_t line = (size_t) oid;
1800 
1801 	/* Most of the menu is svals, with a small "extra options" section below */
1802 	if (line < N_ELEMENTS(sval_dependent)) {
1803 		bool known = seen_tval(sval_dependent[line].tval);
1804 		byte attr = curs_attrs[known ? CURS_KNOWN: CURS_UNKNOWN][(int)cursor];
1805 
1806 		c_prt(attr, sval_dependent[line].desc, row, col);
1807 	} else {
1808 		byte attr = curs_attrs[CURS_KNOWN][(int)cursor];
1809 
1810 		line = line - N_ELEMENTS(sval_dependent) - 1;
1811 
1812 		if (line < N_ELEMENTS(extra_item_options))
1813 			c_prt(attr, extra_item_options[line].name, row, col);
1814 	}
1815 }
1816 
handle_options_item(struct menu * menu,const ui_event * event,int oid)1817 static bool handle_options_item(struct menu *menu, const ui_event *event,
1818 								int oid)
1819 {
1820 	if (event->type == EVT_SELECT) {
1821 		if ((size_t) oid < N_ELEMENTS(sval_dependent))
1822 		{
1823 			sval_menu(sval_dependent[oid].tval, sval_dependent[oid].desc);
1824 		} else {
1825 			oid = oid - (int)N_ELEMENTS(sval_dependent) - 1;
1826 			assert((size_t) oid < N_ELEMENTS(extra_item_options));
1827 			extra_item_options[oid].action();
1828 		}
1829 
1830 		return true;
1831 	}
1832 
1833 	return false;
1834 }
1835 
1836 
1837 static const menu_iter options_item_iter =
1838 {
1839 	tag_options_item,
1840 	valid_options_item,
1841 	display_options_item,
1842 	handle_options_item,
1843 	NULL
1844 };
1845 
1846 
1847 /**
1848  * Display and handle the main ignoring menu.
1849  */
do_cmd_options_item(const char * title,int row)1850 void do_cmd_options_item(const char *title, int row)
1851 {
1852 	struct menu menu;
1853 
1854 	menu_init(&menu, MN_SKIN_SCROLL, &options_item_iter);
1855 	menu_setpriv(&menu, N_ELEMENTS(sval_dependent) +
1856 				 N_ELEMENTS(extra_item_options) + 1, NULL);
1857 
1858 	menu.title = title;
1859 	menu_layout(&menu, &SCREEN_REGION);
1860 
1861 	screen_save();
1862 	clear_from(0);
1863 	menu_select(&menu, 0, false);
1864 	screen_load();
1865 
1866 	player->upkeep->notice |= PN_IGNORE;
1867 
1868 	return;
1869 }
1870 
1871 
1872 
1873 /**
1874  * ------------------------------------------------------------------------
1875  * Main menu definitions and display
1876  * ------------------------------------------------------------------------ */
1877 
1878 static struct menu *option_menu;
1879 static menu_action option_actions[] =
1880 {
1881 	{ 0, 'a', "User interface options", option_toggle_menu },
1882 	{ 0, 'b', "Birth (difficulty) options", option_toggle_menu },
1883 	{ 0, 'x', "Cheat options", option_toggle_menu },
1884 	{ 0, 'w', "Subwindow setup", do_cmd_options_win },
1885 	{ 0, 'i', "Item ignoring setup", do_cmd_options_item },
1886 	{ 0, '{', "Auto-inscription setup", textui_browse_object_knowledge },
1887 	{ 0, 0, NULL, NULL },
1888 	{ 0, 'd', "Set base delay factor", do_cmd_delay },
1889 	{ 0, 'h', "Set hitpoint warning", do_cmd_hp_warn },
1890 	{ 0, 'm', "Set movement delay", do_cmd_lazymove_delay },
1891 	{ 0, 'o', "Set sidebar mode", do_cmd_sidebar_mode },
1892 	{ 0, 0, NULL, NULL },
1893 	{ 0, 's', "Save subwindow setup to pref file", do_dump_options },
1894 	{ 0, 't', "Save autoinscriptions to pref file", do_dump_autoinsc },
1895 	{ 0, 'u', "Save char screen options to pref file", do_dump_charscreen_opt },
1896 	{ 0, 0, NULL, NULL },
1897 	{ 0, 'l', "Load a user pref file", options_load_pref_file },
1898 	{ 0, 'k', "Edit keymaps (advanced)", do_cmd_keymaps },
1899 	{ 0, 'c', "Edit colours (advanced)", do_cmd_colors },
1900 	{ 0, 'v', "Save visuals (advanced)", do_cmd_visuals },
1901 };
1902 
1903 
1904 /**
1905  * Display the options main menu.
1906  */
do_cmd_options(void)1907 void do_cmd_options(void)
1908 {
1909 	if (!option_menu) {
1910 		/* Main option menu */
1911 		option_menu = menu_new_action(option_actions,
1912 				N_ELEMENTS(option_actions));
1913 
1914 		option_menu->title = "Options Menu";
1915 		option_menu->flags = MN_CASELESS_TAGS;
1916 	}
1917 
1918 	screen_save();
1919 	clear_from(0);
1920 
1921 	menu_layout(option_menu, &SCREEN_REGION);
1922 	menu_select(option_menu, 0, false);
1923 
1924 	screen_load();
1925 }
1926 
cleanup_options(void)1927 void cleanup_options(void)
1928 {
1929 	if (keymap_menu) menu_free(keymap_menu);
1930 	if (visual_menu) menu_free(visual_menu);
1931 	if (color_menu) menu_free(color_menu);
1932 	if (option_menu) menu_free(option_menu);
1933 }
1934