3 * Purpose: Generic menu interaction functions
5 * Copyright (c) 2007 Pete Mack and others.
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
21 * Implementation of Extremely Basic Event Model.
23 * all events are of the concrete type event_type (see z-util.h),
24 * which are supposed to model simple UI actions:
28 * - select menu element
30 * - back to parent (hierarchical menu escape)
32 * There are 3 basic event-related classes:
34 * Concrete event, with at most 32 distinct types.
36 * The event_listener observer for key events
38 * The event_target The registrar for event_listeners.
39 * For convenience, the event target is also an event_listener.
42 /* Some useful constants */
43 const char default_choice
[] =
44 "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
46 const char lower_case
[] = "abcdefghijklmnopqrstuvwxyz";
47 const char upper_case
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
52 /* forward declarations */
53 static void display_menu_row(menu_type
*menu
, int pos
, int top
,
54 bool cursor
, int row
, int col
, int width
);
56 /* =================== GEOMETRY ================= */
58 void region_erase(const region
*loc
)
62 int h
= loc
->page_rows
;
64 if (loc
->width
<= 0 || loc
->page_rows
<= 0)
66 Term_get_size(&w
, &h
);
67 if (loc
->width
<= 0) w
-= loc
->width
;
68 if (loc
->page_rows
<= 0) h
-= loc
->page_rows
;
71 for (i
= 0; i
< h
; i
++)
72 Term_erase(loc
->col
, loc
->row
+ i
, w
);
75 bool region_inside(const region
*loc
, const event_type
*key
)
77 if ((loc
->col
> key
->mousex
) || (loc
->col
+ loc
->width
<= key
->mousex
))
80 if ((loc
->row
> key
->mousey
) || (loc
->row
+ loc
->page_rows
<= key
->mousey
))
86 /* ======================= EVENTS ======================== */
88 /* List of event listeners--Helper class for event_target and the event loop */
91 event_listener
*listener
;
92 struct listener_list
*next
;
96 void stop_event_loop()
98 event_type stop
= { EVT_STOP
, 0, 0, 0, 0 };
100 /* Stop right away! */
101 Term_event_push(&stop
);
105 * Primitive event loop.
106 * - target = the event target
107 * - forever - if false, stop at first unhandled event. Otherwise, stop only
109 * - start - optional initial event that allows you to prime the loop without
110 * pushing the event queue.
112 * EVT_STOP - the loop was halted.
113 * EVT_AGAIN - start was not handled, and forever is false
114 * The first unhandled event - forever is false.
116 event_type
run_event_loop(event_target
* target
, bool forever
, const event_type
*start
)
121 while (forever
|| handled
)
123 listener_list
*list
= target
->observers
;
126 if (start
) ke
= *start
;
127 else ke
= inkey_ex();
129 if (ke
.type
== EVT_STOP
)
132 if (ke
.type
& target
->self
.events
.evt_flags
)
133 handled
= target
->self
.handler(target
->self
.object
, &ke
);
135 if (!target
->is_modal
)
137 while (list
&& !handled
)
139 if (ke
.type
& list
->listener
->events
.evt_flags
)
140 handled
= list
->listener
->handler(list
->listener
->object
, &ke
);
146 if (handled
) start
= NULL
;
158 void add_listener(event_target
* target
, event_listener
* observer
)
162 MAKE(link
, listener_list
);
163 link
->listener
= observer
;
164 link
->next
= target
->observers
;
165 target
->observers
= link
;
168 void remove_listener(event_target
* target
, event_listener
* observer
)
170 listener_list
*cur
= target
->observers
;
171 listener_list
**prev
= &target
->observers
;
175 if (cur
->listener
== observer
)
183 bell("remove_listener: no such observer");
188 /* ======================= MN_EVT HELPER FUNCTIONS ====================== */
190 /* Display an event, with possible preference overrides */
191 static void display_event_aux(event_action
*event
, int menu_id
, byte color
, int row
, int col
, int wid
)
193 /* TODO: add preference support */
194 /* TODO: wizard mode should show more data */
195 Term_erase(col
, row
, wid
);
198 Term_putstr(col
, row
, wid
, color
, event
->name
);
201 static void display_event(menu_type
*menu
, int oid
, bool cursor
, int row
, int col
, int width
)
203 event_action
*evts
= (event_action
*)menu
->menu_data
;
204 byte color
= curs_attrs
[CURS_KNOWN
][0 != cursor
];
206 display_event_aux(&evts
[oid
], menu
->target
.self
.object_id
, color
, row
, col
,
210 /* act on selection only */
211 /* Return: true if handled. */
212 static bool handle_menu_item_event(char cmd
, void *db
, int oid
)
214 event_action
*evt
= &((event_action
*)db
)[oid
];
216 if (cmd
== '\xff' && evt
->action
)
218 evt
->action(evt
->data
, evt
->name
);
221 else if (cmd
== '\xff')
229 static int valid_menu_event(menu_type
*menu
, int oid
)
231 event_action
*evts
= (event_action
*)menu
->menu_data
;
232 return (NULL
!= evts
[oid
].name
);
235 /* Virtual function table for action_events */
236 static const menu_iter menu_iter_event
=
242 handle_menu_item_event
246 /*======================= MN_ACT HELPER FUNCTIONS ====================== */
248 static char tag_menu_item(menu_type
*menu
, int oid
)
250 menu_item
*items
= (menu_item
*)menu
->menu_data
;
251 return items
[oid
].sel
;
254 static void display_menu_item(menu_type
*menu
, int oid
, bool cursor
, int row
, int col
, int width
)
256 menu_item
*items
= (menu_item
*)menu
->menu_data
;
257 byte color
= curs_attrs
[!(items
[oid
].flags
& (MN_GRAYED
))][0 != cursor
];
259 display_event_aux(&items
[oid
].evt
, menu
->target
.self
.object_id
, color
, row
,
263 /* act on selection only */
264 static bool handle_menu_item(char cmd
, void *db
, int oid
)
268 menu_item
*item
= &((menu_item
*)db
)[oid
];
270 if (item
->flags
& MN_DISABLED
)
272 if (item
->evt
.action
)
273 item
->evt
.action(item
->evt
.data
, item
->evt
.name
);
274 if (item
->flags
& MN_SELECTABLE
)
275 item
->flags
^= MN_SELECTED
;
283 static int valid_menu_item(menu_type
*menu
, int oid
)
285 menu_item
*items
= (menu_item
*)menu
->menu_data
;
287 if (items
[oid
].flags
& MN_HIDDEN
)
290 return (NULL
!= items
[oid
].evt
.name
);
293 /* Virtual function table for menu items */
294 static const menu_iter menu_iter_item
=
303 /* Simple strings - display and selection only */
304 static void display_string(menu_type
*menu
, int oid
, bool cursor
,
305 int row
, int col
, int width
)
307 const char **items
= (const char **)menu
->menu_data
;
308 byte color
= curs_attrs
[CURS_KNOWN
][0 != cursor
];
309 Term_putstr(col
, row
, width
, color
, items
[oid
]);
312 /* Virtual function table for displaying arrays of strings */
313 static const menu_iter menu_iter_string
=
314 { MN_STRING
, 0, 0, display_string
, 0 };
319 /* ================== SKINS ============== */
323 /* Find the position of a cursor given a screen address */
324 static int scrolling_get_cursor(int row
, int col
, int n
, int top
, region
*loc
)
326 int cursor
= row
- loc
->row
+ top
;
327 if (cursor
>= n
) cursor
= n
- 1;
333 /* Display current view of a skin */
334 static void display_scrolling(menu_type
*menu
, int cursor
, int *top
, region
*loc
)
338 int rows_per_page
= loc
->page_rows
;
339 int n
= menu
->filter_count
;
342 if ((cursor
<= *top
) && (*top
> 0))
344 if(menu
->flags
& MN_PAGE
) *top
= cursor
;
345 else *top
= cursor
- jumpscroll
- 1;
348 if (cursor
>= *top
+ (rows_per_page
- 1))
350 if (menu
->flags
& MN_PAGE
&& cursor
+ rows_per_page
< n
)
352 else if (menu
->flags
& MN_PAGE
)
353 *top
= n
- rows_per_page
;
355 *top
= cursor
- (rows_per_page
- 1) + 1 + jumpscroll
;
358 if (*top
> n
- rows_per_page
)
359 *top
= n
- rows_per_page
;
363 for (i
= 0; i
< rows_per_page
&& i
< n
; i
++)
365 bool is_curs
= (i
== cursor
- *top
);
366 display_menu_row(menu
, i
+ *top
, *top
, is_curs
, row
+ i
, col
,
370 if (menu
->cursor
>= 0)
371 Term_gotoxy(col
, row
+ cursor
- *top
);
374 static char scroll_get_tag(menu_type
*menu
, int pos
)
376 if (menu
->selections
)
377 return menu
->selections
[pos
- menu
->top
];
382 /* Virtual function table for scrollable menu skin */
383 static const menu_skin scroll_skin
=
386 scrolling_get_cursor
,
392 /* Multi-column menu */
393 /* Find the position of a cursor given a screen address */
394 static int columns_get_cursor(int row
, int col
, int n
, int top
, region
*loc
)
396 int rows_per_page
= loc
->page_rows
;
397 int colw
= loc
->width
/ (n
+ rows_per_page
- 1) / rows_per_page
;
398 int cursor
= row
+ rows_per_page
* (col
- loc
->col
) / colw
;
400 if (cursor
< 0) cursor
= 0; /* assert: This should never happen */
401 if (cursor
>= n
) cursor
= n
- 1;
406 void display_columns(menu_type
*menu
, int cursor
, int *top
, region
*loc
)
410 int n
= menu
->filter_count
;
413 int rows_per_page
= loc
->page_rows
;
414 int cols
= (n
+ rows_per_page
- 1) / rows_per_page
;
415 int colw
= menu_width
;
417 Term_get_size(&w
, &h
);
419 if ((colw
* cols
) > (w
- col
))
420 colw
= (w
- col
) / cols
;
422 for (c
= 0; c
< cols
; c
++)
424 for (r
= 0; r
< rows_per_page
; r
++)
426 int pos
= c
* rows_per_page
+ r
;
427 bool is_cursor
= (pos
== cursor
);
428 display_menu_row(menu
, pos
, 0, is_cursor
, row
+ r
, col
+ c
* colw
,
434 static char column_get_tag(menu_type
*menu
, int pos
)
436 if (menu
->selections
)
437 return menu
->selections
[pos
];
442 /* Virtual function table for multi-column menu skin */
443 static const menu_skin column_skin
=
451 /* ================== IMPLICIT MENU FOR KEY SELECTION ================== */
453 static void display_nothing(menu_type
*menu
, int cursor
, int *top
, region
*loc
)
457 static int no_cursor(int row
, int col
, int n
, int top
, region
*loc
)
462 static const menu_skin key_select_skin
=
470 /* ================== GENERIC HELPER FUNCTIONS ============== */
472 static bool is_valid_row(menu_type
*menu
, int cursor
)
476 if (cursor
< 0 || cursor
>= menu
->filter_count
)
479 if (menu
->object_list
)
480 oid
= menu
->object_list
[cursor
];
482 if (!menu
->row_funcs
->valid_row
)
485 return menu
->row_funcs
->valid_row(menu
, oid
);
489 * Return a new position in the menu based on the key
490 * pressed and the flags and various handler functions.
492 static int get_cursor_key(menu_type
*menu
, int top
, char key
)
495 int n
= menu
->filter_count
;
497 if (menu
->flags
& MN_CASELESS_TAGS
)
498 key
= toupper((unsigned char) key
);
500 if (menu
->flags
& MN_NO_TAGS
)
504 else if (menu
->flags
& MN_REL_TAGS
)
506 for (i
= 0; i
< n
; i
++)
508 char c
= menu
->skin
->get_tag(menu
, i
);
510 if ((menu
->flags
& MN_CASELESS_TAGS
) && c
)
511 c
= toupper((unsigned char) c
);
514 return i
+ menu
->top
;
517 else if (!(menu
->flags
& MN_PVT_TAGS
) && menu
->selections
)
519 for (i
= 0; menu
->selections
[i
]; i
++)
521 char c
= menu
->selections
[i
];
523 if (menu
->flags
& MN_CASELESS_TAGS
)
524 c
= toupper((unsigned char) c
);
530 else if (menu
->row_funcs
->get_tag
)
532 for (i
= 0; i
< n
; i
++)
534 int oid
= menu
->object_list
? menu
->object_list
[i
] : i
;
535 char c
= menu
->row_funcs
->get_tag(menu
, oid
);
537 if ((menu
->flags
& MN_CASELESS_TAGS
) && c
)
538 c
= toupper((unsigned char) c
);
549 * Event handler wrapper function
550 * Filters unhandled keys & conditions
552 static bool handle_menu_key(char cmd
, menu_type
*menu
, int cursor
)
555 int flags
= menu
->flags
;
557 if (menu
->object_list
) oid
= menu
->object_list
[cursor
];
558 if (flags
& MN_NO_ACT
) return FALSE
;
560 if (cmd
== ESCAPE
) return FALSE
;
561 if (!cmd
== '\xff' && (!menu
->cmd_keys
|| !strchr(menu
->cmd_keys
, cmd
)))
564 if (menu
->row_funcs
->row_handler
&&
565 menu
->row_funcs
->row_handler(cmd
, (void *)menu
->menu_data
, oid
))
568 ke
.type
= EVT_SELECT
;
571 Term_event_push(&ke
);
578 /* Modal display of menu */
579 static void display_menu_row(menu_type
*menu
, int pos
, int top
,
580 bool cursor
, int row
, int col
, int width
)
582 int flags
= menu
->flags
;
586 if (menu
->object_list
)
587 oid
= menu
->object_list
[oid
];
589 if (menu
->row_funcs
->valid_row
&& menu
->row_funcs
->valid_row(menu
, oid
) == 2)
592 if (!(flags
& MN_NO_TAGS
))
594 if (flags
& MN_REL_TAGS
)
595 sel
= menu
->skin
->get_tag(menu
, pos
);
596 else if (menu
->selections
&& !(flags
& MN_PVT_TAGS
))
597 sel
= menu
->selections
[pos
];
598 else if (menu
->row_funcs
->get_tag
)
599 sel
= menu
->row_funcs
->get_tag(menu
, oid
);
604 /* TODO: CHECK FOR VALID */
605 byte color
= curs_attrs
[CURS_KNOWN
][0 != (cursor
)];
606 Term_putstr(col
, row
, 3, color
, format("%c) ", sel
));
611 menu
->row_funcs
->display_row(menu
, oid
, cursor
, row
, col
, width
);
614 void menu_refresh(menu_type
*menu
)
616 region
*loc
= &menu
->boundary
;
617 int oid
= menu
->cursor
;
619 if (menu
->object_list
&& menu
->cursor
>= 0)
620 oid
= menu
->object_list
[oid
];
622 region_erase(&menu
->boundary
);
625 Term_putstr(loc
->col
, loc
->row
, loc
->width
, TERM_WHITE
, menu
->title
);
628 Term_putstr(loc
->col
, loc
->row
+ loc
->page_rows
- 1, loc
->width
,
629 TERM_WHITE
, menu
->prompt
);
631 if (menu
->browse_hook
&& oid
>= 0)
632 menu
->browse_hook(oid
, (void*) menu
->menu_data
, loc
);
634 menu
->skin
->display_list(menu
, menu
->cursor
, &menu
->top
, &menu
->active
);
637 /* The menu event loop */
638 static bool menu_handle_event(menu_type
*menu
, const event_type
*in
)
640 int n
= menu
->filter_count
;
641 int *cursor
= &menu
->cursor
;
646 if (menu
->target
.observers
)
648 /* TODO: need a panel dispatcher here, not a generic target */
649 event_target t
= { { 0, 0, 0, 0, { 0 } }, FALSE
, 0 /* menu->target.observers */};
650 t
.observers
= menu
->target
.observers
;
651 out
= run_event_loop(&t
, FALSE
, in
);
653 if (out
.type
!= EVT_AGAIN
)
655 if (out
.type
== EVT_SELECT
)
657 /* HACK: can't return selection event from submenu (no ID) */
658 out
.type
= EVT_REFRESH
;
659 Term_event_push(&out
);
672 if (!region_inside(&menu
->active
, in
))
674 if (!region_inside(&menu
->boundary
, in
))
679 /* used for heirarchical menus */
680 if (in
->mousex
< menu
->active
.col
)
689 m_curs
= menu
->skin
->get_cursor(in
->mousey
, in
->mousex
,
690 menu
->filter_count
, menu
->top
,
693 /* Ignore this event - retry */
694 if (!is_valid_row(menu
, m_curs
))
699 if (*cursor
== m_curs
|| !(menu
->flags
& MN_DBL_TAP
))
701 if (*cursor
!= m_curs
)
706 out
.type
= EVT_SELECT
;
722 /* could install handle_menu_key as a handler */
723 if ((menu
->cmd_keys
&& strchr(menu
->cmd_keys
, in
->key
))
724 || in
->key
== ESCAPE
)
726 if (menu
->flags
& MN_NO_ACT
)
729 return handle_menu_key(in
->key
, menu
, *cursor
);
732 c
= get_cursor_key(menu
, menu
->top
, in
->key
);
734 /* keypress shortcuts are allowed, but the choice */
735 /* was invalid - try again! */
736 if (c
> 0 && !is_valid_row(menu
, c
))
740 /* Valid selection */
745 if (*cursor
== c
|| !(menu
->flags
& MN_DBL_TAP
))
752 out
.type
= EVT_SELECT
;
765 if (menu
->flags
& MN_NO_CURSOR
)
768 if (in
->key
== ' ' && (menu
->flags
& MN_PAGE
))
770 /* Go to start of next page */
771 *cursor
+= menu
->active
.page_rows
- (*cursor
% menu
->active
.page_rows
);
773 if (*cursor
>= menu
->filter_count
)
782 /* Cursor movement */
783 dir
= target_dir(in
->key
);
786 if (in
->key
== '\n' || in
->key
== '\r')
788 out
.type
= EVT_SELECT
;
791 /* Reject diagonals */
792 else if (ddx
[dir
] && ddy
[dir
])
799 out
.type
= ddx
[dir
] < 0 ? EVT_BACK
: EVT_SELECT
;
802 /* Move up or down to the next valid & visible row */
809 for (ind
= *cursor
+ dy
; ind
< n
&& ind
>= 0
810 && (TRUE
!= is_valid_row(menu
, ind
)); ind
+= dy
) ;
812 if (ind
< n
&& ind
>= 0) *cursor
= ind
;
834 if (out
.type
== EVT_SELECT
&& handle_menu_key('\xff', menu
, *cursor
))
837 if (out
.type
== EVT_MOVE
)
840 Term_event_push(&out
);
847 static const panel_type menu_target
=
850 {0, /* listener.object_id */
851 (handler_f
) menu_handle_event
, /* listener.handler */
852 (release_f
) menu_destroy
, /* listener.release */
853 0, /* listener.object */
854 {EVT_KBRD
| EVT_MOUSE
| EVT_REFRESH
} /* listener.events */
856 TRUE
, /* target.is_modal */
858 menu_refresh
, /* refresh() */
859 {0, 0, 0, 0} /* boundary */
863 * Modal selection from a menu.
866 * - cursor - the row in which the cursor should start.
867 * - no_handle - Don't handle these events. ( bitwise or of event_class)
868 * 0 - return values below are limited to the set below.
869 * Additional examples:
870 * EVT_MOVE - return values also include menu movement.
871 * EVT_CMD - return values also include command IDs to process.
872 * Returns: an event, possibly requiring further handling.
874 * EVT_SELECT - success. event_type::index is set to the cursor position.
875 * *cursor is also set to the cursor position.
876 * EVT_OK - success. A command event was handled.
877 * *cursor is also set to the associated row.
878 * EVT_BACK - no selection; go to previous menu in hierarchy
879 * EVT_ESCAPE - Abandon modal interaction
880 * EVT_KBRD - An unhandled keyboard event
882 event_type
menu_select(menu_type
*menu
, int *cursor
, int no_handle
)
886 menu
->cursor
= *cursor
;
888 /* Menu shall not handle these */
889 no_handle
|= (EVT_SELECT
| EVT_BACK
| EVT_ESCAPE
| EVT_STOP
);
890 no_handle
&= ~(EVT_REFRESH
);
892 if (!menu
->object_list
)
893 menu
->filter_count
= menu
->count
;
895 ke
.type
= EVT_REFRESH
;
896 (void)run_event_loop(&menu
->target
, FALSE
, &ke
);
898 /* Check for command flag */
899 if (p_ptr
->command_new
)
901 Term_key_push(p_ptr
->command_new
);
902 p_ptr
->command_new
= 0;
905 /* Stop on first unhandled event. */
906 while (!(ke
.type
& no_handle
))
908 ke
= run_event_loop(&menu
->target
, FALSE
, 0);
912 /* menu always returns these */
915 if (*cursor
!= ke
.index
)
923 /* return sometimes-interesting things here */
928 /* EVT_MOVE uses -1, n to allow modular cursor */
929 if (ke
.index
< menu
->filter_count
&& ke
.index
>= 0)
930 *cursor
= menu
->cursor
;
936 if (ke
.key
== ESCAPE
)
937 ke
.type
= EVT_ESCAPE
;
952 /* ================== MENU ACCESSORS ================ */
955 * The menu skin registry. In the unlikely event you need to register
956 * more skins, make the array bigger.
958 static menu_skin
const *menu_skin_reg
[20] =
967 * The menu row-iterator registry.
968 * Note that there's no need to register "anonymous" row iterators, that is,
969 * iterators always accessed by address, and not available in pref files.
971 static menu_iter
const *menu_iter_reg
[20] =
980 const menu_iter
*find_menu_iter(menu_iter_id id
)
983 for (i
= 0; i
< N_ELEMENTS(menu_iter_reg
) && menu_iter_reg
[i
]; i
++)
985 if (menu_iter_reg
[i
]->id
== id
)
986 return menu_iter_reg
[i
];
991 const menu_skin
*find_menu_skin(skin_id id
)
994 for (i
= 0; i
< N_ELEMENTS(menu_skin_reg
) && menu_skin_reg
[i
]; i
++)
996 if (menu_skin_reg
[i
]->id
== id
)
997 return menu_skin_reg
[i
];
1002 void add_menu_skin(const menu_skin
*skin
, skin_id id
)
1006 assert(skin
->id
== id
);
1007 for (i
= 0; i
< N_ELEMENTS(menu_skin_reg
) && menu_skin_reg
[i
]; i
++)
1008 assert(skin
->id
!= menu_skin_reg
[id
]->id
);
1010 if (i
== N_ELEMENTS(menu_skin_reg
))
1011 quit("too many registered skins!");
1013 menu_skin_reg
[i
] = skin
;
1016 void add_menu_iter(const menu_iter
* iter
, menu_iter_id id
)
1020 assert(iter
->id
== id
);
1021 for (i
= 0; i
< N_ELEMENTS(menu_iter_reg
) && menu_iter_reg
[i
]; i
++)
1022 assert(iter
->id
!= menu_iter_reg
[id
]->id
);
1024 if (i
== N_ELEMENTS(menu_iter_reg
))
1025 quit("too many registered iters!");
1027 menu_iter_reg
[i
] = iter
;
1031 * Set the filter to a new value.
1032 * IMPORTANT: The filter is assumed to be owned by the menu.
1033 * To remove a filter that is not owned by the menu, use:
1034 * menu_set_filter(m, NULL, m->count);
1036 void menu_set_filter(menu_type
*menu
, const int object_list
[], int n
)
1038 menu
->object_list
= object_list
;
1039 menu
->filter_count
= n
;
1042 /* Delete the filter */
1043 /* HACK: returns old filter for possible destruction */
1044 /* as: FREE(menu_remove_filter(m)); */
1045 void menu_release_filter(menu_type
*menu
)
1047 if (menu
->object_list
)
1048 FREE((void *)menu
->object_list
);
1049 menu
->object_list
= 0;
1050 menu
->filter_count
= menu
->count
;
1053 void menu_set_id(menu_type
*menu
, int id
)
1055 menu
->target
.self
.object_id
= id
;
1058 /* ======================== MENU INITIALIZATION ==================== */
1060 /* This is extremely primitive, barely sufficient to the job done */
1061 bool menu_layout(menu_type
*menu
, const region
*loc
)
1065 if (!loc
) return TRUE
;
1068 if (active
.width
<= 0 || active
.page_rows
<= 0)
1071 Term_get_size(&w
, &h
);
1072 if (active
.width
<= 0)
1073 active
.width
= w
+ active
.width
- active
.col
;
1074 if (active
.page_rows
<= 0)
1075 active
.page_rows
= h
+ loc
->page_rows
- active
.row
;
1078 menu
->boundary
= active
;
1083 active
.page_rows
-= 2;
1084 /* TODO: handle small screens */
1089 if (active
.page_rows
> 1)
1093 int offset
= strlen(menu
->prompt
) + 2;
1094 active
.col
+= offset
;
1095 active
.width
-= offset
;
1099 /* if(menu->cmd_keys) active.page_rows--; */
1101 menu
->active
= active
;
1102 return (active
.width
> 0 && active
.page_rows
> 0);
1106 bool menu_init2(menu_type
*menu
, const menu_skin
*skin
,
1107 const menu_iter
* iter
, const region
*loc
)
1110 *((panel_type
*) menu
) = menu_target
;
1111 menu
->target
.self
.object
= menu
;
1112 menu
->target
.is_modal
= TRUE
;
1113 menu
->row_funcs
= iter
;
1115 menu
->target
.self
.events
.evt_flags
= (EVT_MOUSE
| EVT_REFRESH
| EVT_KBRD
);
1117 if (menu
->count
&& !menu
->object_list
) menu
->filter_count
= menu
->count
;
1119 if (!loc
) loc
= &SCREEN_REGION
;
1122 menu_layout(menu
, loc
);
1124 /* TODO: Check for collisions in selections & command keys here */
1128 bool menu_init(menu_type
*menu
, skin_id skin_id
,
1129 menu_iter_id iter_id
, const region
*loc
)
1131 const menu_skin
*skin
= find_menu_skin(skin_id
);
1132 const menu_iter
*iter
= find_menu_iter(iter_id
);
1136 msg_format("could not find menu VTAB (%d, %d)!", skin_id
, iter_id
);
1140 return menu_init2(menu
, skin
, iter
, loc
);
1143 void menu_destroy(menu_type
*menu
)
1145 if (menu
->object_list
)
1146 FREE((void *)menu
->object_list
);
1151 /*** Miscellaneous things ***/
1154 * A Hengband-like 'window' function, that draws a surround box in ASCII art.
1156 void window_make(int origin_x
, int origin_y
, int end_x
, int end_y
)
1161 to_clear
.col
= origin_x
;
1162 to_clear
.row
= origin_y
;
1163 to_clear
.width
= end_x
- origin_x
;
1164 to_clear
.page_rows
= end_y
- origin_y
;
1166 region_erase(&to_clear
);
1168 Term_putch(origin_x
, origin_y
, TERM_WHITE
, '+');
1169 Term_putch(end_x
, origin_y
, TERM_WHITE
, '+');
1170 Term_putch(origin_x
, end_y
, TERM_WHITE
, '+');
1171 Term_putch(end_x
, end_y
, TERM_WHITE
, '+');
1173 for (n
= 1; n
< (end_x
- origin_x
); n
++)
1175 Term_putch(origin_x
+ n
, origin_y
, TERM_WHITE
, '-');
1176 Term_putch(origin_x
+ n
, end_y
, TERM_WHITE
, '-');
1179 for (n
= 1; n
< (end_y
- origin_y
); n
++)
1181 Term_putch(origin_x
, origin_y
+ n
, TERM_WHITE
, '|');
1182 Term_putch(end_x
, origin_y
+ n
, TERM_WHITE
, '|');