1 /* NetHack may be freely redistributed.  See license for details. */
2 
3 #include "vulture_sdl.h" /* XXX this must be the first include,
4                              no idea why but it won't compile otherwise */
5 
6 extern "C" {
7 #include "hack.h"
8 
9 extern int take_off();
10 extern int select_off(struct obj *);
11 extern long takeoff_mask;
12 extern const char *disrobing;
13 }
14 
15 #include "vulture_gra.h"
16 #include "vulture_txt.h"
17 #include "vulture_win.h"
18 #include "vulture_mou.h"
19 #include "vulture_tile.h"
20 
21 #include "inventory.h"
22 #include "objitemwin.h"
23 #include "objheaderwin.h"
24 #include "contextmenu.h"
25 #include "button.h"
26 
27 
28 #define META(c) (0x80 | (c))
29 #define CTRL(c) (0x1f & (c))
30 
31 
inventory(window * p,std::list<menuitem> & menuitems,int how,int id)32 inventory::inventory(window *p, std::list<menuitem> &menuitems, int how, int id) :
33                      menuwin(p, menuitems, how), nhid(id)
34 {
35 	v_type = V_WINTYPE_OBJWIN;
36 
37 	ow_ncols = ow_vcols = ow_firstcol = ow_vrows = 0;
38 	ow_lasttoggled = NULL;
39 }
40 
41 
draw()42 bool inventory::draw()
43 {
44   std::string stored_caption;
45 	char label[32];
46 	int ix ,iy, iw, ih, labelwidth;
47 
48 	/* draw the window, but prevent draw_mainwin from drawing the caption */
49 	stored_caption = caption;
50 	caption.clear();
51 
52 	mainwin::draw();
53 
54 	caption = stored_caption;
55 
56 	ix = abs_x + border_left;
57 	iy = abs_y + border_top;
58 	iw = w - border_left - border_right;
59 	ih = h - border_top - border_bottom;
60 
61 	int headline_height = vulture_get_lineheight(V_FONT_HEADLINE);
62 	int headline_width = vulture_text_length(V_FONT_HEADLINE, caption);
63 
64 	vulture_fill_rect(ix, iy, ix + iw - 1, iy + headline_height * 2, CLR32_BLACK_A50);
65 
66 	vulture_line(ix, iy, ix + iw - 1, iy, CLR32_GRAY20);
67 	vulture_line(ix, iy + headline_height * 2, ix + iw - 1,
68 				iy + headline_height * 2, CLR32_GRAY20);
69 
70 	vulture_put_text_shadow(V_FONT_HEADLINE, caption, vulture_screen, ix+(iw-headline_width)/2,
71 							iy+headline_height/2+2, CLR32_WHITE, CLR32_GRAY20);
72 
73 	if (ow_ncols > ow_vcols) {
74 
75     if ( ow_firstcol + 1 == ow_firstcol + ow_vcols )
76       snprintf(label, 32, "Page %d of %d", ow_firstcol + 1, ow_ncols);
77     else
78       snprintf(label, 32, "Pages %d to %d of %d", ow_firstcol + 1, ow_firstcol + ow_vcols, ow_ncols);
79 
80 		labelwidth = vulture_text_length(V_FONT_MENU, label);
81 
82 		vulture_put_text_shadow(V_FONT_MENU, label, vulture_screen, x+(w-labelwidth)/2,
83 								y+h-21, CLR32_BLACK, CLR32_GRAY20);
84 	}
85 
86 	return 1;
87 }
88 
89 
update_invscroll(int newpos)90 void inventory::update_invscroll(int newpos)
91 {
92 	window * winelem;
93 	int itemcount = 0;
94 	int itemcol;
95 	int leftoffset = border_left;
96 	int topoffset = border_top;
97 
98 	topoffset += vulture_get_lineheight(V_FONT_HEADLINE) * 2 + 2;
99 
100 	if (newpos + ow_vcols > ow_ncols)
101 		newpos = ow_ncols - ow_vcols;
102 	else if (newpos < 0)
103 		newpos = 0;
104 
105 	ow_firstcol = newpos;
106 
107 
108 	for (winelem = first_child; winelem; winelem = winelem->sib_next) {
109 		if (winelem->v_type == V_WINTYPE_OBJITEM ||
110 			winelem->v_type == V_WINTYPE_OBJITEMHEADER) {
111 			itemcol = (itemcount / ow_vrows);
112 
113 			winelem->x = (itemcol - newpos) * (V_LISTITEM_WIDTH + 4) + leftoffset;
114 			winelem->y = (itemcount % ow_vrows) * V_LISTITEM_HEIGHT + topoffset;
115 
116 			winelem->visible = (itemcol >= newpos && itemcol < newpos + ow_vcols);
117 
118 			itemcount++;
119 		}
120 
121 		else if (winelem->v_type == V_WINTYPE_BUTTON) {
122 			if (winelem->menu_id == V_INV_PREVPAGE)
123 				winelem->visible = (newpos != 0);
124 			else if (winelem->menu_id == V_INV_NEXTPAGE)
125 				winelem->visible = (newpos + ow_vcols < ow_ncols);
126 		}
127 	}
128 }
129 
130 
context_menu(objitemwin * target)131 eventresult inventory::context_menu(objitemwin *target)
132 {
133 	int action = 0, key = 0;
134 	contextmenu *menu;
135 
136 	menu = new contextmenu(ROOTWIN);
137 	menu->add_item("Apply", V_INVACTION_APPLY);
138 
139 	if (!target->obj->owornmask)
140 	{
141 		/* if you can wear it there's no way you can eat or drink it */
142 		if (target->obj->oclass == POTION_CLASS)
143 			menu->add_item("Drink", V_INVACTION_DRINK);
144 		menu->add_item("Eat", V_INVACTION_EAT);
145 	}
146 
147 	menu->add_item("Read", V_INVACTION_READ);
148 
149 	if (target->obj->oclass == WAND_CLASS)
150 		menu->add_item("Zap", V_INVACTION_ZAP);
151 
152 	/* you could already be wearing it, then you can't wear it again */
153 	if (!target->obj->owornmask && target->obj->oclass != WAND_CLASS) {
154 		if (target->obj->oclass != RING_CLASS && target->obj->oclass != AMULET_CLASS)
155 			menu->add_item("Wear", V_INVACTION_WEAR);
156 
157 		if (target->obj->oclass != ARMOR_CLASS)
158 			menu->add_item("Put on", V_INVACTION_PUT_ON);
159 	}
160 
161 	menu->add_item("Wield", V_INVACTION_WIELD);
162 	menu->add_item("Quiver", V_INVACTION_QUIVER);
163 
164 	if (target->obj->owornmask)
165 		menu->add_item("Remove", V_INVACTION_REMOVE);
166 
167 	if (!target->obj->owornmask)
168 		menu->add_item("Drop", V_INVACTION_DROP);
169 
170 	if (!objects[target->obj->otyp].oc_name_known)
171 		menu->add_item("Name", V_INVACTION_NAME);
172 
173 	menu->layout();
174 	vulture_event_dispatcher(&action, V_RESPOND_INT, menu);
175 
176 	delete menu;
177 
178 
179 	if (action) {
180 		vulture_eventstack_add('i', -1, -1, V_RESPOND_POSKEY);
181 
182 		switch (action)
183 		{
184 			case V_INVACTION_APPLY: key = 'a'; break;
185 			case V_INVACTION_DRINK: key = 'q'; break;
186 			case V_INVACTION_EAT:   key = 'e'; break;
187 			case V_INVACTION_READ:  key = 'r'; break;
188 			case V_INVACTION_ZAP:   key = 'z'; break;
189 			case V_INVACTION_WEAR:  key = 'W'; break;
190 			case V_INVACTION_PUT_ON:key = 'P'; break;
191 			case V_INVACTION_WIELD: key = 'w'; break;
192 			case V_INVACTION_QUIVER: key = 'Q'; break;
193 			case V_INVACTION_REMOVE:
194 				/* we call a bunch of functions in do_wear.c directly here;
195 					* we can do so safely because take_off() directly accounts for
196 					* elapsed turns */
197 				select_off(target->obj); /* sets takoff_mask */
198 				if (takeoff_mask)
199 				{
200 					/* default activity for armor and/or accessories,
201 						* possibly combined with weapons */
202 					disrobing = "disrobing";
203 
204 					/* specific activity when handling weapons only */
205 					if (!(takeoff_mask & ~(W_WEP|W_SWAPWEP|W_QUIVER)))
206 						disrobing = "disarming";
207 
208 					(void) take_off();
209 				}
210 				/* having performed an action we need to return to the main game loop
211 					* so that thing like AC and vision (because of helmets & amulets of ESP)
212 					* get recalculated.
213 					* However we do not want to perform any more actions or cause messages
214 					* to be printed. CTRL+r (redraw) is a suitable NOP */
215 				key = CTRL('r');
216 				return V_EVENT_HANDLED_FINAL;
217 
218 			case V_INVACTION_NAME:
219 				vulture_eventstack_add(target->menu_id, -1, -1, V_RESPOND_ANY);
220 				vulture_eventstack_add('n', -1,-1, V_RESPOND_ANY);
221 				vulture_eventstack_add(META('n'), -1, -1, V_RESPOND_POSKEY);
222 				return V_EVENT_HANDLED_FINAL;
223 
224 			case V_INVACTION_DROP:  key = 'd'; break;
225 		}
226 
227 		vulture_eventstack_add(target->menu_id, -1, -1, V_RESPOND_CHARACTER);
228 		vulture_eventstack_add(key, -1, -1, V_RESPOND_POSKEY);
229 
230 		return V_EVENT_HANDLED_FINAL;
231 	}
232 
233 	return V_EVENT_HANDLED_NOREDRAW;
234 }
235 
236 
237 
handle_mousemotion_event(window * target,void * result,int xrel,int yrel,int state)238 eventresult inventory::handle_mousemotion_event(window* target, void* result, int xrel,
239                                              int yrel, int state)
240 {
241 	vulture_set_mcursor(V_CURSOR_NORMAL);
242 	return V_EVENT_HANDLED_NOREDRAW;
243 }
244 
245 
handle_mousebuttonup_event(window * target,void * result,int mouse_x,int mouse_y,int button,int state)246 eventresult inventory::handle_mousebuttonup_event(window* target, void* result,
247                                             int mouse_x, int mouse_y, int button, int state)
248 {
249 	point mouse = vulture_get_mouse_pos();
250 	window *winelem;
251 	objitemwin *oitem;
252 
253 	if (select_how == PICK_NONE) {
254 		/* close the window if the user clicks outside it */
255 		if (this == target && (mouse.x < abs_x || mouse.y < abs_y ||
256 							mouse.x > abs_x + w || mouse.y > abs_y + h))
257 			return V_EVENT_HANDLED_FINAL;
258 
259 		/* left clicks on object items do nothing */
260 		if (button == SDL_BUTTON_LEFT &&
261 				target != this && target->v_type == V_WINTYPE_OBJITEM)
262 			return V_EVENT_HANDLED_NOREDRAW;
263 
264 		/* right clicks on object items open a context menu */
265 		else if (button == SDL_BUTTON_RIGHT && target->v_type == V_WINTYPE_OBJITEM)
266 			return context_menu(static_cast<objitemwin*>(target));
267 	}
268 
269 	if (button == SDL_BUTTON_WHEELUP) {
270 		if (ow_firstcol > 0) {
271 			/* scroll inventory backwards */
272 			update_invscroll(ow_firstcol - 1);
273 			need_redraw = 1;
274 			return V_EVENT_HANDLED_REDRAW;
275 		}
276 		return V_EVENT_HANDLED_NOREDRAW;
277 	}
278 
279 	else if (button == SDL_BUTTON_WHEELDOWN) {
280 		if (ow_firstcol + ow_vcols < ow_ncols) {
281 			/* scroll inventory forwards */
282 			update_invscroll(ow_firstcol + 1);
283 			need_redraw = 1;
284 			return V_EVENT_HANDLED_REDRAW;
285 		}
286 		return V_EVENT_HANDLED_NOREDRAW;
287 	}
288 
289 	else if (button == SDL_BUTTON_LEFT) {
290 		if (this == target)
291 			return V_EVENT_HANDLED_NOREDRAW;
292 
293 		if (target->v_type == V_WINTYPE_BUTTON) {
294 			switch (target->menu_id)
295 			{
296 				case V_MENU_ACCEPT:
297 				case V_MENU_CANCEL:
298 				case V_INV_CLOSE:
299 					*(int*)result = target->menu_id;
300 					return V_EVENT_HANDLED_FINAL;
301 
302 				case V_INV_PREVPAGE:
303 					update_invscroll(ow_firstcol - 1);
304 					need_redraw = 1;
305 					return V_EVENT_HANDLED_REDRAW;
306 
307 				case V_INV_NEXTPAGE:
308 					update_invscroll(ow_firstcol + 1);
309 					need_redraw = 1;
310 					return V_EVENT_HANDLED_REDRAW;
311 			}
312 		}
313 
314 		/* select a range of items from target (clicked item) to handler->pd.ow_lasttoggled (previously clicked item) */
315 		if (target->v_type == V_WINTYPE_OBJITEM && (SDL_GetModState() & KMOD_LSHIFT) &&
316 			select_how != PICK_ONE && ow_lasttoggled) {
317 			int selectme = 0;
318 			for (winelem = first_child; winelem; winelem = winelem->sib_next) {
319 				if (winelem == target || winelem == ow_lasttoggled) {
320 					selectme = !selectme;
321 					oitem = static_cast<objitemwin*>(winelem);
322 					oitem->item->selected = 1;
323 					oitem->item->count = -1;
324 				}
325 
326 				if (selectme && winelem->v_type == V_WINTYPE_OBJITEM) {
327 					oitem = static_cast<objitemwin*>(winelem);
328 					oitem->item->selected = 1;
329 					oitem->item->count = -1;
330 				}
331 			}
332 
333 			ow_lasttoggled->last_toggled = 0;
334 			ow_lasttoggled = static_cast<objitemwin*>(target);
335 			static_cast<objitemwin*>(target)->last_toggled = 1;
336 
337 			need_redraw = 1;
338 			return V_EVENT_HANDLED_REDRAW;
339 		}
340 		else if (target->v_type == V_WINTYPE_OBJITEM) {
341 			select_option(static_cast<objitemwin*>(target), -1);
342 
343 			if (ow_lasttoggled)
344 				ow_lasttoggled->last_toggled = false;
345 			ow_lasttoggled = static_cast<objitemwin*>(target);
346 			ow_lasttoggled->last_toggled = true;
347 
348 			if (select_how == PICK_ONE) {
349 				*(int*)result = V_MENU_ACCEPT;
350 				return V_EVENT_HANDLED_FINAL;
351 			}
352 
353 			need_redraw = 1;
354 			return V_EVENT_HANDLED_REDRAW;
355 		}
356 	}
357 
358 	return V_EVENT_HANDLED_NOREDRAW;
359 }
360 
361 
handle_keydown_event(window * target,void * result,int sym,int mod,int unicode)362 eventresult inventory::handle_keydown_event(window* target, void* result, int sym, int mod, int unicode)
363 {
364 	window *winelem;
365 	int itemcount, colno, key;
366 
367 	need_redraw = 1;
368 	key = unicode;
369 	switch (sym) {
370 		case SDLK_RETURN:
371 		case SDLK_KP_ENTER:
372 			*(int*)result = V_MENU_ACCEPT;
373 			return V_EVENT_HANDLED_FINAL;
374 
375 		case SDLK_SPACE:
376 		case SDLK_ESCAPE:
377 			*(int*)result = (select_how == PICK_NONE) ? V_MENU_ACCEPT : V_MENU_CANCEL;
378 			return V_EVENT_HANDLED_FINAL;
379 
380 		/* handle menu control keys */
381 		case SDLK_HOME:     key = MENU_FIRST_PAGE;    /* '^' */ break;
382 		case SDLK_END:      key = MENU_LAST_PAGE;     /* '|' */ break;
383 
384 		/* scroll via arrow keys */
385 		case SDLK_PAGEDOWN:
386 		case SDLK_KP2:
387 		case SDLK_DOWN:
388 		case SDLK_RIGHT:
389 			update_invscroll(ow_firstcol + 1);
390 			return V_EVENT_HANDLED_REDRAW;
391 
392 		case SDLK_PAGEUP:
393 		case SDLK_KP8:
394 		case SDLK_UP:
395 		case SDLK_LEFT:
396 			update_invscroll(ow_firstcol - 1);
397 			return V_EVENT_HANDLED_REDRAW;
398 
399 		case SDLK_BACKSPACE:
400 			if (ow_lasttoggled)
401 				ow_lasttoggled->item->count = ow_lasttoggled->item->count / 10;
402 			return V_EVENT_HANDLED_REDRAW;
403 
404 		default: break;
405 	}
406 
407 	if (!key)
408 		/* a function or modifier key, but not one we recognize, was pressed */
409 		return V_EVENT_HANDLED_NOREDRAW;
410 
411 	switch (key) {
412 		case MENU_PREVIOUS_PAGE:
413 			update_invscroll(ow_firstcol - 1);
414 			return V_EVENT_HANDLED_REDRAW;
415 
416 		case MENU_NEXT_PAGE:
417 			update_invscroll(ow_firstcol + 1);
418 			return V_EVENT_HANDLED_REDRAW;
419 
420 		case MENU_FIRST_PAGE:
421 			update_invscroll(0);
422 			return V_EVENT_HANDLED_REDRAW;
423 
424 		case MENU_LAST_PAGE:
425 			update_invscroll(999999);
426 			return V_EVENT_HANDLED_REDRAW;
427 
428 
429 		case MENU_SELECT_ALL:
430 		case MENU_UNSELECT_ALL:
431 			/* invalid for single selection menus */
432 			if (select_how != PICK_ANY)
433 				return V_EVENT_HANDLED_NOREDRAW;
434 
435 			for (winelem = first_child; winelem; winelem = winelem->sib_next) {
436 				if (winelem->v_type == V_WINTYPE_OBJITEM) {
437 					static_cast<objitemwin*>(winelem)->item->selected =
438 											(key == MENU_SELECT_ALL);
439 					static_cast<objitemwin*>(winelem)->item->count = -1;
440 				}
441 			}
442 			return V_EVENT_HANDLED_REDRAW;
443 
444 
445 		case MENU_INVERT_ALL:
446 			/* invalid for single selection menus */
447 			if (select_how != PICK_ANY)
448 				return V_EVENT_HANDLED_NOREDRAW;
449 
450 			for (winelem = first_child; winelem; winelem = winelem->sib_next) {
451 				if (winelem->v_type == V_WINTYPE_OBJITEM) {
452 					static_cast<objitemwin*>(winelem)->item->selected =
453 					!static_cast<objitemwin*>(winelem)->item->selected;
454 					static_cast<objitemwin*>(winelem)->item->count = -1;
455 				}
456 			}
457 			return V_EVENT_HANDLED_REDRAW;
458 
459 
460 		case MENU_SELECT_PAGE:
461 		case MENU_UNSELECT_PAGE:
462 			/* invalid for single selection menus */
463 			if (select_how != PICK_ANY)
464 				return V_EVENT_HANDLED_NOREDRAW;
465 
466 			for (winelem = first_child; winelem; winelem = winelem->sib_next) {
467 				if (winelem->v_type == V_WINTYPE_OBJITEM && winelem->visible) {
468 					static_cast<objitemwin*>(winelem)->item->selected = (key == MENU_SELECT_PAGE);
469 					static_cast<objitemwin*>(winelem)->item->count = -1;
470 				}
471 			}
472 			return V_EVENT_HANDLED_REDRAW;
473 
474 
475 		case MENU_INVERT_PAGE:
476 			/* invalid for single selection menus */
477 			if (select_how != PICK_ANY)
478 				return V_EVENT_HANDLED_NOREDRAW;
479 
480 			for (winelem = first_child; winelem; winelem = winelem->sib_next) {
481 				if (winelem->v_type == V_WINTYPE_OBJITEM && winelem->visible) {
482 					static_cast<objitemwin*>(winelem)->item->selected =
483 						!static_cast<objitemwin*>(winelem)->item->selected;
484 					static_cast<objitemwin*>(winelem)->item->count = -1;
485 				}
486 			}
487 			return V_EVENT_HANDLED_REDRAW;
488 
489 
490 		case MENU_SEARCH:
491 			char str_to_find[512];
492 			str_to_find[0] = '\0';
493 			if (vulture_get_input(-1, -1, "What are you looking for?", str_to_find) != -1)
494 			{
495 				itemcount = 0;
496 				for (winelem = first_child; winelem; winelem = winelem->sib_next) {
497 					itemcount++;
498 					if (winelem->caption.find(str_to_find)) {
499 						colno = itemcount / ow_vrows;
500 						update_invscroll(colno);
501 						break;
502 					}
503 				}
504 
505 				if (ow_lasttoggled)
506 					ow_lasttoggled->last_toggled = false;
507 				ow_lasttoggled = static_cast<objitemwin*>(winelem);
508 				if (ow_lasttoggled)
509 					ow_lasttoggled->last_toggled = true;
510 			}
511 			return V_EVENT_HANDLED_REDRAW;
512 
513 		default:
514 			if (select_how == PICK_NONE)
515 				return V_EVENT_HANDLED_FINAL;
516 
517 			/* numbers are part of a count */
518 			if (key >= '0' && key <= '9' && ow_lasttoggled &&
519 				ow_lasttoggled->item->count < 1000000) {
520 				if (ow_lasttoggled->item->count == -1)
521 					ow_lasttoggled->item->count = 0;
522 				ow_lasttoggled->item->count = ow_lasttoggled->item->count * 10 + (key - '0');
523 
524 				return V_EVENT_HANDLED_REDRAW;
525 			}
526 
527 			/* try to match the key to an accelerator */
528       std::vector<window *> targets_found( find_accel( key ) );
529       for ( std::vector<window *>::iterator target = targets_found.begin();
530             target != targets_found.end();
531             ++target )
532       {
533 				select_option(static_cast<objitemwin*>(*target), -1);
534 				if (select_how == PICK_ONE) {
535 					*(int*)result = V_MENU_ACCEPT;
536 					return V_EVENT_HANDLED_FINAL;
537 				}
538 
539 				if (ow_lasttoggled)
540 					ow_lasttoggled->last_toggled = 0;
541 				ow_lasttoggled = static_cast<objitemwin*>(*target);
542 				ow_lasttoggled->last_toggled = 1;
543 
544 
545 				/* if the selected element isn't visible bring it into view */
546 				if (!(*target)->visible) {
547 					itemcount = 0;
548 					for (winelem = first_child; winelem && winelem != *target;
549 						winelem = winelem->sib_next)
550 						itemcount++;
551 
552 					colno = itemcount / ow_vrows;
553 					update_invscroll(colno);
554 				}
555 			}
556 
557       if ( targets_found.size() > 0 ) {
558 				return V_EVENT_HANDLED_REDRAW;
559       }
560 
561 			break;
562 	}
563 
564 	return V_EVENT_HANDLED_NOREDRAW;
565 }
566 
567 
handle_resize_event(window * target,void * result,int res_w,int res_h)568 eventresult inventory::handle_resize_event(window* target, void* result, int res_w, int res_h)
569 {
570 	if (visible) {
571 		/* hide_window takes care of the background */
572 		hide();
573 
574 		/* resize */
575 		layout();
576 
577 		/* redraw */
578 		visible = 1;
579 		need_redraw = 1;
580 		return V_EVENT_HANDLED_REDRAW;
581 	}
582 
583 	return V_EVENT_HANDLED_NOREDRAW;
584 }
585 
586 
layout()587 void inventory::layout()
588 {
589 	window *winelem;
590 	button *btn;
591 	struct obj * invitem;
592 
593 	int itemcount = 0;
594 	int ncols, nrows;
595 	int maxitems_per_col, maxitems_per_row;
596 	int textheight;
597 
598 	/* remove all child windows - they get (re-)created here */
599 	while (first_child)
600 		delete first_child;
601 
602 	if (caption.empty())
603 		caption = "Inventory";
604 
605 	int leftoffset = border_left;
606 	int topoffset = border_top;
607 	int rightoffset = border_right;
608 	int bottomoffset = 60; /* guesstimate for bottom border + page arrows + minimal spacing */
609 
610 	textheight = vulture_get_lineheight(V_FONT_LARGE);
611 	topoffset += textheight*2 + 2;
612 
613 	if (select_how != PICK_NONE)
614 		bottomoffset += (textheight + 14);
615 
616 	for (item_iterator i = items.begin(); i != items.end(); ++i) {
617 		if (!i->identifier)
618 			new objheaderwin(this, i->str);
619 		else
620 			new objitemwin(this, &(*i), i->str, i->accelerator, i->group_accelerator,
621 						i->glyph, i->preselected, select_how == PICK_ANY);
622 		itemcount++;
623 
624 	}
625 
626 	/* how many items will fit on the screen vertically */
627 	maxitems_per_col = (parent->h - topoffset - bottomoffset) / V_LISTITEM_HEIGHT;
628 
629 	maxitems_per_row = (parent->w - 2*leftoffset - 10) / (V_LISTITEM_WIDTH + 4);
630 
631 	/* calc number of rows to contain itemcount items */
632 	ncols = itemcount / maxitems_per_col + (itemcount % maxitems_per_col != 0 ? 1 : 0);
633 
634 	/* distribute items evenly among the rows */
635 	nrows = itemcount / ncols + (itemcount % ncols != 0 ? 1 : 0);
636 
637 	/* if there are more columns than can be displayed, prefer to maximize
638 	* space usage over even distribution of the items */
639 	if (ncols > maxitems_per_row)
640 		nrows = maxitems_per_col;
641 
642 	ow_vcols = ncols > maxitems_per_row ? maxitems_per_row : ncols;
643 	ow_vrows = nrows > maxitems_per_col ? maxitems_per_col : nrows;
644 	ow_ncols = ncols;
645 
646 	ow_firstcol = 0;
647 
648 	itemcount = 0;
649 	for (winelem = first_child; winelem; winelem = winelem->sib_next) {
650 		if (winelem->v_type == V_WINTYPE_OBJITEM ||
651 		    winelem->v_type == V_WINTYPE_OBJITEMHEADER) {
652 			winelem->x = (itemcount / ow_vrows) * (V_LISTITEM_WIDTH + 4) + leftoffset;
653 			winelem->y = (itemcount % ow_vrows) * V_LISTITEM_HEIGHT + topoffset;
654 			winelem->visible = ((itemcount / ow_vrows) < ow_vcols);
655 
656 			/* find the objects associated with the items */
657 			if (winelem->v_type == V_WINTYPE_OBJITEM) {
658 				/* nethack may have been nice and passed an object pointer in menu_id_v
659 				 * Unforunately, we need this ugly hack to try to discern between
660 				 * chars, small ints and pointers */
661 				if (winelem->menu_id_v > (void*)0x10000)
662 					static_cast<objitemwin*>(winelem)->obj = (struct obj *)winelem->menu_id_v;
663 				else if (nhid == WIN_INVEN) {
664 					invitem = invent;
665 					while (invitem && invitem->invlet != winelem->accelerator)
666 						invitem = invitem->nobj;
667 					static_cast<objitemwin*>(winelem)->obj = invitem;
668 				}
669 			}
670 
671 			itemcount++;
672 		}
673 	}
674 
675 	w = ow_vcols * (V_LISTITEM_WIDTH + 4) - 4 + leftoffset + rightoffset;
676 	h = ow_vrows * V_LISTITEM_HEIGHT + topoffset + border_bottom;
677 
678 	if (ncols > ow_vcols) {
679 		h += 25;
680 
681 		btn = new button(this, "", V_INV_PREVPAGE, '\0');
682 		btn->x = leftoffset;
683 		btn->y = h - border_bottom - 23;
684 		btn->w = 100;
685 		btn->h = 24;
686 		btn->image = vulture_get_img_src(0, 0, vulture_winelem.invarrow_left->w,
687 								vulture_winelem.invarrow_left->h, vulture_winelem.invarrow_left);
688 		btn->visible = 0;
689 
690 		btn = new button(this, "", V_INV_NEXTPAGE, '\0');
691 		btn->x = w - rightoffset - 101;
692 		btn->y = h - border_bottom - 23;
693 		btn->w = 100;
694 		btn->h = 24;
695 		btn->image = vulture_get_img_src(0, 0, vulture_winelem.invarrow_right->w,
696 								vulture_winelem.invarrow_right->h, vulture_winelem.invarrow_right);
697 		btn->visible = 1;
698 	}
699 
700 	if (select_how != PICK_NONE) {
701     int btn1_width = 0;
702     if ( select_how == PICK_ANY )
703 		  btn1_width = vulture_text_length(V_FONT_MENU, "Accept") + 14;
704 		int btn2_width = vulture_text_length(V_FONT_MENU, "Cancel") + 14;
705 		int max_width = (btn1_width > btn2_width) ? btn1_width : btn2_width;
706 		int total_width = max_width;
707 
708 		h += textheight + 14;
709 
710     if ( select_how == PICK_ANY )
711     {
712       total_width += max_width + 10;
713       btn = new button(this, "Accept", V_MENU_ACCEPT, '\0');
714       btn->h = textheight + 10;
715       btn->y = h - border_bottom - (textheight + 12);
716       btn->w = max_width;
717       btn->x = (w - rightoffset - leftoffset - total_width) / 2 + leftoffset;
718     }
719 
720 		btn = new button(this, "Cancel", V_MENU_CANCEL, '\0');
721 		btn->h = textheight + 10;
722 		btn->y = h - border_bottom - (textheight + 12);
723 		btn->w = max_width;
724 		btn->x = (w - rightoffset - leftoffset - total_width) / 2 + leftoffset;
725     if ( select_how == PICK_ANY )
726       btn->x += max_width + 10;
727 	} else {
728 		btn = new button(this, "", V_INV_CLOSE, '\0');
729 		btn->visible = 1;
730 		btn->x = w - border_right - 28;
731 		btn->y = border_top + 2;
732 		btn->w = 26;
733 		btn->h = 26;
734 		btn->image = vulture_get_img_src(0, 0, vulture_winelem.closebutton->w,
735 								vulture_winelem.closebutton->h, vulture_winelem.closebutton);
736 	}
737 
738 	x = (parent->w - w) / 2;
739 	y = (parent->h - h) / 2;
740 
741 	abs_x = parent->x + x;
742 	abs_y = parent->y + y;
743 
744 }
745