/*
 * CINELERRA
 * Copyright (C) 2010-2014 Adam Williams <broadcast at earthling dot net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "bcdisplayinfo.h"
#include "bcdragwindow.h"
#include "bclistbox.h"
#include "bclistboxitem.h"
#include "bcpixmap.h"
#include "bcresources.h"
#include "bcsignals.h"
#include "clip.h"
#include "cursors.h"
#include "fonts.h"
#include "keys.h"
#include "language.h"
#include "bctimer.h"
#include "vframe.h"

#include <string.h>
#include <unistd.h>

// ====================================================== scrollbars

BC_ListBoxYScroll::BC_ListBoxYScroll(BC_ListBox *listbox)
 : BC_ScrollBar(listbox->get_yscroll_x(), listbox->get_yscroll_y(),
	listbox->yscroll_orientation, listbox->get_yscroll_height(),
	listbox->items_h, listbox->yposition, listbox->view_h)
{
	this->listbox = listbox;
}

BC_ListBoxYScroll::~BC_ListBoxYScroll()
{
}

int BC_ListBoxYScroll::handle_event()
{
	listbox->set_yposition(get_value());
	return 1;
}

BC_ListBoxXScroll::BC_ListBoxXScroll(BC_ListBox *listbox)
 : BC_ScrollBar(listbox->get_xscroll_x(), listbox->get_xscroll_y(),
	listbox->xscroll_orientation, listbox->get_xscroll_width(),
	listbox->items_w, listbox->xposition, listbox->view_w)
{
	this->listbox = listbox;
}

BC_ListBoxXScroll::~BC_ListBoxXScroll()
{
}

int BC_ListBoxXScroll::handle_event()
{
	listbox->set_xposition(get_value());
	return 1;
}


BC_ListBoxToggle::BC_ListBoxToggle(BC_ListBox *listbox,
	BC_ListBoxItem *item,
	int x,
	int y)
{
	this->listbox = listbox;
	this->item = item;
	this->x = x;
	this->y = y;
	this->value = item->get_expand();
	if( this->value )
		state = BC_Toggle::TOGGLE_CHECKED;
	else
		state = BC_Toggle::TOGGLE_UP;
}

void BC_ListBoxToggle::update(BC_ListBoxItem *item,
	int x,
	int y,
	int flash)
{
	this->value = item->get_expand();
	this->item = item;
	this->x = x;
	this->y = y;

// update state
	switch( state ) {
	case TOGGLE_UP:
		if( value )
			state = TOGGLE_CHECKED;
		break;

	case TOGGLE_UPHI:
		if( value )
			state = TOGGLE_CHECKEDHI;
		break;

	case TOGGLE_CHECKED:
		if( !value )
			state = TOGGLE_UP;
		break;

	case TOGGLE_DOWN:
		break;

	case TOGGLE_CHECKEDHI:
		if( !value )
			state = TOGGLE_UPHI;
		break;

	case TOGGLE_DOWN_EXIT:
		break;
	}


	draw(flash);
}

int BC_ListBoxToggle::cursor_motion_event(int *redraw_toggles)
{
	int w = listbox->toggle_images[0]->get_w();
	int h = listbox->toggle_images[0]->get_h();
	int cursor_inside = listbox->get_cursor_x() >= x &&
		listbox->get_cursor_x() < x + w &&
		listbox->get_cursor_y() >= y &&
		listbox->get_cursor_y() < y + h;
	int result = 0;

	switch( state ) {
	case BC_ListBoxToggle::TOGGLE_UPHI:
		if( !cursor_inside ) {
			state = BC_ListBoxToggle::TOGGLE_UP;
			*redraw_toggles = 1;
		}
		break;

	case BC_ListBoxToggle::TOGGLE_CHECKEDHI:
		if( !cursor_inside ) {
			state = BC_ListBoxToggle::TOGGLE_CHECKED;
			*redraw_toggles = 1;
		}
		break;

	case BC_ListBoxToggle::TOGGLE_DOWN:
		if( !cursor_inside ) {
			state = BC_ListBoxToggle::TOGGLE_DOWN_EXIT;
			*redraw_toggles = 1;
		}
		result = 1;
		break;

	case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
		if( cursor_inside ) {
			state = BC_ListBoxToggle::TOGGLE_DOWN;
			*redraw_toggles = 1;
		}
		result = 1;
		break;

	default:
		if( cursor_inside ) {
			if( value )
				state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
			else
				state = BC_ListBoxToggle::TOGGLE_UPHI;
			*redraw_toggles = 1;
		}
		break;
	}
	return result;
}

int BC_ListBoxToggle::cursor_leave_event(int *redraw_toggles)
{
	if( value )
		state = BC_ListBoxToggle::TOGGLE_CHECKED;
	else
		state = BC_ListBoxToggle::TOGGLE_UP;
	return 0;
}

int BC_ListBoxToggle::button_press_event()
{
	int w = listbox->toggle_images[0]->get_w();
	int h = listbox->toggle_images[0]->get_h();

	if( listbox->gui->get_cursor_x() >= x &&
	    listbox->gui->get_cursor_x() < x + w &&
	    listbox->gui->get_cursor_y() >= y &&
	    listbox->gui->get_cursor_y() < y + h ) {
		state = BC_ListBoxToggle::TOGGLE_DOWN;
		return 1;
	}
	return 0;
}

int BC_ListBoxToggle::button_release_event(int *redraw_toggles)
{
	int result = 0;

	switch( state ) {
	case BC_ListBoxToggle::TOGGLE_DOWN:
		value = !value;
		if( value )
			state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
		else
			state = BC_ListBoxToggle::TOGGLE_UPHI;
		listbox->expand_item(item, value);
		result = 1;
		break;

	case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
		if( value )
			state = BC_ListBoxToggle::TOGGLE_CHECKED;
		else
			state = BC_ListBoxToggle::TOGGLE_UP;
		*redraw_toggles = 1;
		result = 1;
		break;
	}
	return result;
}

void BC_ListBoxToggle::draw(int flash)
{
	if( listbox->gui ) {
		int image_number = 0;
		int w = listbox->toggle_images[0]->get_w();
		int h = listbox->toggle_images[0]->get_h();

		switch( state ) {
		case BC_ListBoxToggle::TOGGLE_UP:        image_number = 0; break;
		case BC_ListBoxToggle::TOGGLE_UPHI:      image_number = 1; break;
		case BC_ListBoxToggle::TOGGLE_CHECKED:   image_number = 2; break;
		case BC_ListBoxToggle::TOGGLE_DOWN:      image_number = 3; break;
		case BC_ListBoxToggle::TOGGLE_CHECKEDHI: image_number = 4; break;
		case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
			image_number = value ? 2 : 0;
			break;
		}

//printf("BC_ListBoxToggle::draw 1 %d\n", state);
		listbox->gui->draw_pixmap(listbox->toggle_images[image_number], x, y);

		if( flash ) {
			listbox->gui->flash(x, y, w, h);
			listbox->gui->flush();
		}
	}
}


// ====================================================== box

BC_ListBox::BC_ListBox(int x, int y, int w, int h,
	int display_format, ArrayList<BC_ListBoxItem*> *data,
	const char **column_titles, int *column_width, int columns,
	int yposition, int is_popup, int selection_mode,
	int icon_position, int allow_drag)
 : BC_SubWindow(x, y, w, h, -1)
{
	justify = LISTBOX_RIGHT;
	xposition = 0;
	highlighted_item = -1;
	highlighted_title = -1;
	highlighted_division = -1;
	highlighted_ptr = 0;
	xscrollbar = 0;
	yscrollbar = 0;
	current_cursor = ARROW_CURSOR;
	gui = 0;
	view_w = items_w = 0;
	view_h = items_h = 0;
	title_h = 0;
	active = 0;
	is_suggestions = 0;
	new_value = 0;
	need_xscroll = 0;
	need_yscroll = 0;
	xscroll_orientation = SCROLL_HORIZ;
	yscroll_orientation = SCROLL_VERT;
 	bg_tile = 0;
	bg_draw = 1;
 	drag_popup = 0;
	dragged_title = 0;
 	selection_number1 = -1;
	selection_number2 = -1;
	bg_surface = 0;
 	bg_pixmap = 0;
	row_height = row_ascent = row_descent = 0;
	selection_start = 0;
	selection_center = 0;
	selection_end = -1;
	selection_number = -1;
 	current_operation = NO_OPERATION;
	button_highlighted = 0;
	button_releases = 0;
	list_highlighted = 0;
	disabled = 0;

	scroll_repeat = 0;
 	allow_drag_scroll = 1;
	process_drag = 1;
	for( int i=0; i<32; ++i ) default_column_width[i] = 0;

	sort_column = -1;
	sort_order = 0;

	allow_drag_column = 0;
	master_column = 0;
	search_column = 0;

	popup_w = w;
	popup_h = h;

	for( int i=0; i<3; ++i ) column_bg[i] = 0;
	for( int i=0; i<4; ++i ) button_images[i] = 0;
	for( int i=0; i<5; ++i ) toggle_images[i] = 0;

	column_sort_up = 0;
	column_sort_dn = 0;

//printf("BC_ListBox::BC_ListBox 1\n");
	this->data = data;
	this->columns = columns;
	this->yposition = yposition;
	this->is_popup = is_popup;
	this->use_button = 1;
	this->display_format = display_format;
	this->selection_mode = selection_mode;
	this->icon_position = icon_position;
	this->allow_drag = allow_drag;
	this->column_titles = 0;
	this->column_width = 0;
	this->first_in_view = -1;
	this->last_in_view = 0;
//printf("BC_ListBox::BC_ListBox 1\n");

	if( (!column_titles && column_width) ||
	    (column_titles && !column_width) ) {
		printf("BC_ListBox::BC_ListBox either column_titles or column_widths == NULL but not both.\n");
	}
//printf("BC_ListBox::BC_ListBox 2 %p %p\n", column_titles, column_width);

	set_columns(column_titles, column_width, columns);

//printf("BC_ListBox::BC_ListBox 3\n");

	drag_icon_vframe = 0;
	drag_column_icon_vframe = 0;
	drag_cursor_x = 0;
	drag_column_w = 0;
	temp_display_format = display_format;
	packed_icons = display_format == LISTBOX_ICONS_PACKED ? 1 : 0;
	rect_x1 = rect_x2 = 0;
	rect_y1 = rect_y2 = 0;

// reset the search engine
//printf("BC_ListBox::BC_ListBox 4\n");
	show_query = 0;
	reset_query();
//printf("BC_ListBox::BC_ListBox 5\n");
}

BC_ListBox::~BC_ListBox()
{
	expanders.remove_all_objects();
	if( bg_surface ) delete bg_surface;
	if( bg_pixmap ) delete bg_pixmap;
	if( xscrollbar ) delete xscrollbar;
	if( yscrollbar ) delete yscrollbar;
	for( int i=0; i<3; ++i ) delete column_bg[i];
	for( int i=0; i<4; ++i ) delete button_images[i];
	for( int i=0; i<5; ++i ) delete toggle_images[i];
	if( column_sort_up ) delete column_sort_up;
	if( column_sort_dn ) delete column_sort_dn;

	delete_columns();
	if( drag_popup ) delete drag_popup;
}

int BC_ListBox::enable()
{
	disabled = 0;
	draw_button(1);
	return 1;
}

int BC_ListBox::disable()
{
	disabled = 1;
	draw_button(1);
	return 1;
}

void BC_ListBox::reset_query()
{
	query[0] = 0;  // reset query
}

int BC_ListBox::evaluate_query(char *string)
{
	for( int i=0; i<data[search_column].size(); ++i ) {
		if( strcmp(string, data[search_column].get(i)->text) <= 0 &&
		    data[search_column].get(i)->searchable ) {
			return i;
		}
	}

	return -1;
}

int BC_ListBox::query_list()
{
	if( query[0] == 0 ) return 0;

	int done = 0;
	int result;
	int selection_changed = 0;
	int prev_selection = -1;
	result = evaluate_query(query);
	if( result >= 0 ) done = 1;

	if( done ) {
// Deselect all
		for( int i=0; i<data[0].total; ++i ) {
			for( int j=0; j<columns; ++j ) {
				if( data[j].values[i]->selected ) prev_selection = i;
				data[j].values[i]->selected = 0;
			}
		}

// Select one
		if( prev_selection != result )
			selection_changed = 1;
		for( int j=0; j<columns; ++j ) {
			data[j].values[result]->selected = 1;
		}
		center_selection(result);
	}

	return selection_changed;
}

void BC_ListBox::init_column_width()
{
	if( !column_width && data ) {
		int widest = 5, wd;
		for( int i=0; i<data[0].total; ++i ) {
			wd = get_text_w(data[0].values[i]);
			if( wd > widest ) widest = wd;
		}
		default_column_width[0] = widest + 2 * LISTBOX_MARGIN;
	}
}

int BC_ListBox::initialize()
{
	if( is_popup ) {
		if( use_button ) {
			for( int i=0; i<4; ++i ) {
				button_images[i] = new BC_Pixmap(parent_window,
					BC_WindowBase::get_resources()->listbox_button[i],
					PIXMAP_ALPHA);
			}
			w = button_images[0]->get_w();
			h = button_images[0]->get_h();
		}

		gui = 0;
		current_operation = NO_OPERATION;

	}
	else {
		gui = this;
		current_operation = NO_OPERATION;
	}

	for( int i=0; i<3; ++i ) {
		column_bg[i] = new BC_Pixmap(parent_window,
			get_resources()->listbox_column[i],
			PIXMAP_ALPHA);
	}
	for( int i=0; i<5; ++i ) {
		toggle_images[i] = new BC_Pixmap(parent_window,
			get_resources()->listbox_expand[i],
			PIXMAP_ALPHA);
	}

	column_sort_up = new BC_Pixmap(parent_window,
		BC_WindowBase::get_resources()->listbox_up,
		PIXMAP_ALPHA);
	column_sort_dn = new BC_Pixmap(parent_window,
		BC_WindowBase::get_resources()->listbox_dn,
		PIXMAP_ALPHA);

//printf("BC_ListBox::initialize 10\n");
	drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN];
	drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN];
// = new BC_Pixmap(parent_window,
//		get_resources()->type_to_icon[ICON_UNKNOWN],
//		PIXMAP_ALPHA);
//	drag_column_icon = new BC_Pixmap(parent_window,
//		get_resources()->type_to_icon[ICON_COLUMN],
//		PIXMAP_ALPHA);
	BC_SubWindow::initialize();

	init_column_width();

	if( top_level->get_resources()->listbox_bg )
		bg_pixmap = new BC_Pixmap(this,
			get_resources()->listbox_bg,
			PIXMAP_OPAQUE);

	draw_button(0);
	draw_items(0);

	if( !use_button && is_popup ) {
		hide_window(1);
	}


	return 0;
}

void BC_ListBox::deactivate_selection()
{
	current_operation = NO_OPERATION;
}

int BC_ListBox::draw_button(int flush)
{
// Draw the button for a popup listbox
	if( use_button && is_popup ) {
		int image_number = 0;
		if( button_highlighted )
			image_number = 1;
		if( current_operation == BUTTON_DN )
			image_number = 2;
		if( disabled )
			image_number = 3;

		int iw = button_images[image_number]->get_w();
		int ih = button_images[image_number]->get_h();
		pixmap->draw_pixmap(button_images[image_number],0,0,iw,ih);
		flash(flush);
	}
	return 0;
}

int BC_ListBox::calculate_item_coords()
{
	if( !data ) return 0;

	int icon_x = 0;
	int next_icon_x = 0;
	int next_icon_y = 0;
	int next_text_y = 0;
// Change the display_format to get the right item dimensions for both
// text and icons.
	temp_display_format = display_format;


// Scan the first column for lowest y coord of all text
// and lowest right x and y coord for all icons which aren't auto placable
	calculate_last_coords_recursive(data,
		&icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);

// Reset last column width.  It's recalculated based on text width.
	calculate_item_coords_recursive(data,
		&icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);

	display_format = temp_display_format;

	return 0;
}

void BC_ListBox::calculate_last_coords_recursive(
	ArrayList<BC_ListBoxItem*> *data,
	int *icon_x,
	int *next_icon_x,
	int *next_icon_y,
	int *next_text_y,
	int top_level)
{
	for( int i=0; i<data[0].size(); ++i ) {
		int current_text_y = 0;
		int current_icon_x = 0;
		int current_icon_y = 0;
		BC_ListBoxItem *item = data[0].get(i);

// Get next_text_y
		if( !item->autoplace_text ) {
// Lowest text coordinate
			display_format = LISTBOX_TEXT;
			current_text_y = item->text_y + get_text_h(item);
			if( current_text_y > *next_text_y )
				*next_text_y = current_text_y;

// Add sublist depth if it is expanded
			if( item->sublist_active() && item->get_columns() ) {
				calculate_last_coords_recursive(item->get_sublist(),
					icon_x, next_icon_x, next_icon_y, next_text_y, 0);
			}
		}

// Get next_icon coordinate
		if( top_level ) {
			BC_ListBoxItem *item = data[master_column].get(i);
			if( !item->autoplace_icon ) {
				display_format = LISTBOX_ICONS;
// Lowest right icon coordinate.
				current_icon_x = item->icon_x;
				if( current_icon_x > *icon_x ) *icon_x = current_icon_x;
				if( current_icon_x + get_item_w(item) > *next_icon_x ) {
					*next_icon_x = current_icon_x + get_item_w(item);
					*next_icon_y = 0;
				}
				current_icon_y = item->icon_y + get_item_h(item);
				if( current_icon_y > *next_icon_y )
					*next_icon_y = current_icon_y;
			}
		}
	}
}


void BC_ListBox::calculate_item_coords_recursive(
	ArrayList<BC_ListBoxItem*> *data,
	int *icon_x, int *next_icon_x, int *next_icon_y, int *next_text_y,
	int top_level)
{
// get maximum height of an icon
	row_height = get_text_height(MEDIUMFONT);
	if( temp_display_format == LISTBOX_ICON_LIST ) {
		for( int i=0; i<data[0].size(); ++i ) {
			if( data[0].get(i)->icon ) {
				int icon_h = data[0].get(i)->icon->get_h() + 2*ICON_MARGIN;
				if( row_height < icon_h ) row_height = icon_h;
			}
		}
	}


// Set up items which need autoplacement.
// Should fill icons down and then across
	for( int i=0; i<data[0].size(); ++i ) {
// Don't increase y unless the row requires autoplacing.
		int total_autoplaced_columns = 0;

// Set up icons in first column
		if( top_level ) {
			BC_ListBoxItem *item = data[master_column].get(i);
			if( item->autoplace_icon ) {
// 1 column only if icons are used
				display_format = LISTBOX_ICONS;
// Test row height
// Start new column.
				if( *next_icon_y + get_item_h(item) >= get_h() &&
				    *next_icon_y > 0 ) {
					*icon_x = *next_icon_x;
					*next_icon_y = 0;
				}

				if( *icon_x + get_item_w(item) > *next_icon_x )
					*next_icon_x = *icon_x + get_item_w(item);


				item->set_icon_x(*icon_x);
				item->set_icon_y(*next_icon_y);

				*next_icon_y += get_item_h(item);
			}
		}

// Set up a text row
		int next_text_x = 0;
		row_ascent = row_descent = 0;
// row_height still holds icon max height
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *item = data[j].get(i);
			if( item->autoplace_text ) {
				display_format = LISTBOX_TEXT;
				item->set_text_x(next_text_x);
				item->set_text_y(*next_text_y);
				int ht = get_text_h(item);
				if( ht > row_height ) row_height = ht;
				int bl = get_baseline(item);
				if( bl > row_ascent ) row_ascent = bl;
				int dt = ht - bl;
				if( dt > row_descent ) row_descent = dt;

// printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n",
// item->sublist, item->get_columns(), item->get_expand(),
// next_text_x, *next_text_y, item->get_text());
// Increment position of next column
				if( j < columns - 1 ) {
					next_text_x += (column_width ?
						column_width[j] :
						default_column_width[j]);
				}
// Set last column width based on text width
				else {
					int new_w = get_item_w(item);

					int *previous_w = (column_width ?
						&column_width[j] :
						&default_column_width[j]);
					if( new_w > *previous_w )
						*previous_w = new_w;
//printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w);
				}
				total_autoplaced_columns++;
			}
		}

// Increase the text vertical position
		if( total_autoplaced_columns ) {
			display_format = LISTBOX_TEXT;
			*next_text_y += row_height;
		}

// Set up a sublist
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() && item->get_columns() ) {
			calculate_item_coords_recursive( item->get_sublist(),
				icon_x, next_icon_x, next_icon_y, next_text_y, 0);
		}
	}
}

void BC_ListBox::set_is_suggestions(int value)
{
	this->is_suggestions = value;
}
void BC_ListBox::set_scroll_repeat()
{
	if( scroll_repeat ) return;
	scroll_repeat = 1;
	set_repeat(get_resources()->scroll_repeat);
}

void BC_ListBox::unset_scroll_repeat()
{
	if( !scroll_repeat ) return;
	scroll_repeat = 0;
	unset_repeat(get_resources()->scroll_repeat);
}

void BC_ListBox::set_use_button(int value)
{
	this->use_button = value;
}

void BC_ListBox::set_justify(int value)
{
	this->justify = value;
}

void BC_ListBox::set_allow_drag_column(int value)
{
	this->allow_drag_column = value;
}

void BC_ListBox::set_process_drag(int value)
{
	this->process_drag = value;
}

void BC_ListBox::set_master_column(int value, int redraw)
{
	this->master_column = value;
	if( redraw ) {
		draw_items(1);
	}
}

void BC_ListBox::set_search_column(int value)
{
	this->search_column = value;
}

int BC_ListBox::get_sort_column()
{
	return sort_column;
}

void BC_ListBox::set_sort_column(int value, int redraw)
{
	sort_column = value;
	if( redraw ) {
		draw_titles(1);
	}
}

int BC_ListBox::get_sort_order()
{
	return sort_order;
}

void BC_ListBox::set_sort_order(int value, int redraw)
{
	sort_order = value;
	if( redraw ) {
		draw_titles(1);
	}
}


int BC_ListBox::get_display_mode()
{
	return display_format;
}

int BC_ListBox::get_yposition()
{
	return yposition;
}

int BC_ListBox::get_xposition()
{
	return xposition;
}

int BC_ListBox::get_highlighted_item()
{
	return highlighted_item;
}


int BC_ListBox::get_item_x(BC_ListBoxItem *item)
{
	switch( display_format ) {
	case LISTBOX_TEXT:
	case LISTBOX_ICON_LIST:
		return item->text_x - xposition + 2;
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED:
		return item->icon_x - xposition + 2;
	}
	return 0;
}

int BC_ListBox::get_item_y(BC_ListBoxItem *item)
{
	switch( display_format ) {
	case LISTBOX_TEXT:
	case LISTBOX_ICON_LIST:
		return item->text_y - yposition + title_h + 2;
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED:
		return item->icon_y - yposition + 2;
	}
	return 0;
}

int BC_ListBox::get_item_w(BC_ListBoxItem *item)
{
	switch( display_format ) {
	case LISTBOX_TEXT:
	case LISTBOX_ICON_LIST: {
		return get_text_w(item) + 2 * LISTBOX_MARGIN; }
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED: {
		int x, y, w, h;
		get_icon_mask(item, x, y, w, h);
		int icon_w = w;
		get_text_mask(item, x, y, w, h);
		int text_w = w;

		return icon_position == ICON_LEFT ? icon_w + text_w :
			icon_w > text_w ? icon_w : text_w;
		}
	}
	return 0;
}

int BC_ListBox::get_item_h(BC_ListBoxItem *item)
{
	switch( display_format ) {
	case LISTBOX_TEXT:
	case LISTBOX_ICON_LIST:
		return get_text_h(item);
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED: {
		int x, y, w, h;
		get_icon_mask(item, x, y, w, h);
		int icon_h = h;
		get_text_mask(item, x, y, w, h);
		int text_h = h;

		return icon_position != ICON_LEFT ? icon_h + text_h :
			icon_h > text_h ? icon_h : text_h; }
	}
	return 0;
}


int BC_ListBox::get_icon_x(BC_ListBoxItem *item)
{
	return get_item_x(item) + ICON_MARGIN;
}

int BC_ListBox::get_icon_y(BC_ListBoxItem *item)
{
	return get_item_y(item) + ICON_MARGIN;
}

int BC_ListBox::get_icon_w(BC_ListBoxItem *item)
{
	return item->get_icon_w() + 2*ICON_MARGIN;
}

int BC_ListBox::get_icon_h(BC_ListBoxItem *item)
{
	return item->get_icon_h() + 2*ICON_MARGIN;
}

int BC_ListBox::get_text_w(BC_ListBoxItem *item)
{
	int w = item->get_text_w();
	if( w < 0 ) item->set_text_w(w = get_text_width(MEDIUMFONT, item->get_text()));
	return w;
}

int BC_ListBox::get_text_h(BC_ListBoxItem *item)
{
	int h = item->get_text_h();
	if( h < 0 ) item->set_text_h(h = get_text_height(MEDIUMFONT, item->get_text()));
	return h;
}

int BC_ListBox::get_baseline(BC_ListBoxItem *item)
{
	int b = item->get_baseline();
	if( b < 0 ) item->set_baseline(b = get_text_ascent(MEDIUMFONT));
	return b;
}

int BC_ListBox::get_items_width()
{
	switch( display_format ) {
	case LISTBOX_TEXT:
		return get_column_offset(columns);
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED: {
		int widest = 0;
		for( int i=0; i<columns; ++i ) {
			for( int j=0; j<data[i].total; ++j ) {
				int x1, x, y, w, h;
				BC_ListBoxItem *item = data[i].values[j];
				x1 = item->icon_x;

				get_icon_mask(item, x, y, w, h);
				if( x1 + w > widest ) widest = x1 + w;

				if( icon_position == ICON_LEFT )
					x1 += w;

				get_text_mask(item, x, y, w, h);
				if( x1 + w > widest ) widest = x1 + w;
			}
		}
		return widest; }
	case LISTBOX_ICON_LIST:
		return get_column_offset(columns);
	}
	return 0;
}

int BC_ListBox::get_items_height(ArrayList<BC_ListBoxItem*> *data, int columns,
		int *result)
{
	int temp = 0;
	int top_level = 0;
	int highest = 0;
	if( !result ) {
		result = &temp;
		top_level = 1;
	}

	for( int j=0; j<(data?data[master_column].total:0); ++j ) {
		int x, y, w, h;
		BC_ListBoxItem *item = data[master_column].values[j];

		switch( display_format ) {
		case LISTBOX_TEXT:
			get_text_mask(item, x, y, w, h);
			*result += h;
// Descend into sublist
			if( item->sublist_active() )
				get_items_height(item->get_sublist(), item->get_columns(), result);
			break;
		case LISTBOX_ICONS:
		case LISTBOX_ICONS_PACKED:
		case LISTBOX_ICON_LIST:
			get_icon_mask(item, x, y, w, h);
			if( y + h + yposition > highest ) highest = y + h + yposition;

			get_text_mask(item, x, y, w, h);
			if( y + h + yposition > highest ) highest = y + h + yposition;
			break;
		}
	}

	if( display_format == LISTBOX_TEXT && top_level ) {
		highest = LISTBOX_MARGIN + *result;
	}

	return highest;
}

int BC_ListBox::set_yposition(int position, int draw_items)
{
	this->yposition = position;
	if( draw_items ) {
		this->draw_items(1);
	}
	return 0;
}

int BC_ListBox::set_xposition(int position)
{
	this->xposition = position;
	draw_items(1);
	return 0;
}

int BC_ListBox::is_highlighted()
{
	return list_highlighted;
}

void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand)
{
	if( item ) {
		item->expand = expand;
// Collapse sublists if this is collapsed to make it easier to calculate
// coordinates
		if( item->get_sublist() )
			collapse_recursive(item->get_sublist(), master_column);

// Set everything for autoplacement
		set_autoplacement(data, 0, 1);
		draw_items(1);
	}
}

void BC_ListBox::collapse_recursive(ArrayList<BC_ListBoxItem*> *data,
		int master_column)
{
	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			item->expand = 0;
			collapse_recursive(item->get_sublist(), master_column);
		}
	}
}

void BC_ListBox::set_autoplacement(ArrayList<BC_ListBoxItem*> *data,
	int do_icons,
	int do_text)
{
	for( int i=0; i<data[0].total; ++i ) {
		for( int j=0; j<columns; ++j ) {
			if( do_icons ) data[j].values[i]->autoplace_icon = 1;
			if( do_text ) data[j].values[i]->autoplace_text = 1;
		}

		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			set_autoplacement(item->get_sublist(), do_icons, do_text);
		}
	}
}


void BC_ListBox::set_scroll_stretch(int xv, int yv)
{
	if( xv >= 0 ) xscroll_orientation =
		!xv ? SCROLL_HORIZ : SCROLL_HORIZ + SCROLL_STRETCH;
	if( yv >= 0 ) yscroll_orientation =
		!yv ? SCROLL_VERT  : SCROLL_VERT  + SCROLL_STRETCH;
}

int BC_ListBox::get_yscroll_x()
{
	if( is_popup )
		return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
	else
		return get_x() + popup_w -
			get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
}

int BC_ListBox::get_yscroll_y()
{
	return is_popup ? 0 : get_y();
}

int BC_ListBox::get_yscroll_height()
{
	return popup_h - (need_xscroll ?
		get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() :
		0);
}

int BC_ListBox::get_xscroll_x()
{
	return is_popup ?  0 : get_x();
}

int BC_ListBox::get_xscroll_y()
{
	return (is_popup ? popup_h : get_y() + popup_h) -
		get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
}

int BC_ListBox::get_xscroll_width()
{
	return popup_w - (need_yscroll ?
		get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 0);
}

int BC_ListBox::get_column_offset(int column)
{
	int x = 0;
	while( column > 0 ) {
		x += column_width ?
			column_width[--column] :
			default_column_width[--column];
	}
	return x;
}

void BC_ListBox::column_width_boundaries()
{
	if( column_width ) {
		for( int i=0; i<columns; ++i ) {
			if( column_width[i] < MIN_COLUMN_WIDTH )
				 column_width[i] = MIN_COLUMN_WIDTH;
		}
	}
	else {
		for( int i=0; i<columns; ++i ) {
			if( default_column_width[i] < MIN_COLUMN_WIDTH )
				 default_column_width[i] = MIN_COLUMN_WIDTH;
		}
	}
}

int BC_ListBox::get_column_width(int column, int clamp_right)
{
	if( column < columns - 1 || !clamp_right )
		return column_width ?  column_width[column] : default_column_width[column];
	return popup_w + xposition - get_column_offset(column);
}

int BC_ListBox::get_icon_mask(BC_ListBoxItem *item,
	int &x, int &y, int &w, int &h)
{
	switch( display_format ) {
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED:
	case LISTBOX_ICON_LIST: {
		x = get_icon_x(item);
		y = get_icon_y(item);
		w = get_icon_w(item);
		h = get_icon_h(item);
		break; }
	case LISTBOX_TEXT:
	default: {
		x = y = w = h = 0;
		break; }
	}
	return 0;
}

int BC_ListBox::get_text_mask(BC_ListBoxItem *item,
	int &x, int &y, int &w, int &h)
{
	x = get_item_x(item);
	y = get_item_y(item);

	switch( display_format ) {
	case LISTBOX_TEXT: {
		w = get_text_w(item) + LISTBOX_MARGIN * 2;
		h = get_text_h(item);
		break; }
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED: {
		if( icon_position == ICON_LEFT ) {
			x += get_icon_w(item);
			y += get_icon_h(item) - get_text_h(item);
		}
		else {
			y += get_icon_h(item);
		}

		w = packed_icons ?
			get_icon_w(item) + ICON_MARGIN * 2 :
			get_text_w(item) + ICON_MARGIN * 2 ;
		h = get_text_h(item) + ICON_MARGIN * 2;
		break; }
	case LISTBOX_ICON_LIST: {
		w = column_width ? column_width[0] : default_column_width[0];
		h = row_height;
		break; }
	default:
		w = h = 0;
	}
	return 0;
}

int BC_ListBox::get_item_highlight(ArrayList<BC_ListBoxItem*> *data,
		int column, int item)
{
	BC_Resources *resources = get_resources();
	if( data[column].values[item]->selected )
		return resources->listbox_selected;
	if( highlighted_item >= 0 &&
	    highlighted_ptr == data[master_column].values[item] )
		return resources->listbox_highlighted;
	return resources->listbox_inactive;
}

int BC_ListBox::get_item_color(ArrayList<BC_ListBoxItem*> *data,
		int column, int item)
{
	int color = data[column].values[item]->color;
	if( color == -1 ) color = get_resources()->listbox_text;
	if( get_item_highlight(data, column, item) == color )
		return BLACK;
	return color;
}

int BC_ListBox::get_from_column()
{
	return dragged_title;
}

int BC_ListBox::get_to_column()
{
	return highlighted_title;
}


BC_ListBoxItem* BC_ListBox::get_selection(int column, int selection_number)
{
	return get_selection_recursive(data,
		column,
		selection_number);
}

BC_ListBoxItem* BC_ListBox::get_selection_recursive(ArrayList<BC_ListBoxItem*> *data,
		int column, int selection_number)
{
	if( !data ) return 0;

	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->selected ) {
//printf("BC_ListBox::get_selection_recursive %d\n", __LINE__);
			selection_number--;
			if( selection_number < 0 ) {

				return data[column].values[i];
			}
		}

		if( item->sublist_active() ) {
			BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(),
				column,
				selection_number);
			if( result ) return result;
		}
	}

	return 0;
}


int BC_ListBox::get_selection_number(int column, int selection_number)
{
	return get_selection_number_recursive(data, column, selection_number);
}

int BC_ListBox::get_selection_number_recursive(ArrayList<BC_ListBoxItem*> *data,
		int column, int selection_number, int *counter)
{
	int temp = -1;
	if( !data ) return 0;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		(*counter)++;
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->selected ) {
			selection_number--;
			if( selection_number < 0 ) {
				return (*counter);
			}
		}
		if( item->sublist_active() ) {
			int result = get_selection_number_recursive(item->get_sublist(),
				column, selection_number, counter);
			if( result >= 0 ) return result;
		}
	}
	return -1;
}


int BC_ListBox::set_selection_mode(int mode)
{
	this->selection_mode = mode;
	return 0;
}

void BC_ListBox::delete_columns()
{
	if( column_titles ) {
		for( int i=0; i<columns; ++i ) {
			delete [] column_titles[i];
		}
		delete [] column_titles;
	}

	if( column_width ) delete [] column_width;

	column_titles = 0;
	column_width = 0;
}

// Need to copy titles so EDL can change
void BC_ListBox::set_columns(const char **column_titles, int *column_width, int columns)
{
	if( (!column_titles && column_width) ||
	    (column_titles && !column_width) ) {
		printf("BC_ListBox::set_columns either column_titles or column_width == NULL but not both.\n");
		return;
	}

	delete_columns();
	if( column_titles ) {
		this->column_titles = new char*[columns];
		for( int i=0; i<columns; ++i ) {
			this->column_titles[i] = new char[strlen(column_titles[i]) + 1];
			strcpy(this->column_titles[i], column_titles[i]);
		}
	}
	if( column_width ) {
		this->column_width = new int[columns];
		for( int i=0; i<columns; ++i ) {
			this->column_width[i] = column_width[i];
		}
	}

	this->columns = columns;

}


int BC_ListBox::update(ArrayList<BC_ListBoxItem*> *data,
	const char **column_titles, int *column_widths, int columns,
	int xposition, int yposition, int highlighted_number,
	int recalc_positions, int draw)
{
	set_columns(column_titles, column_widths, columns);
	this->data = data;
	this->yposition = yposition;
	this->xposition = xposition;
	this->highlighted_item = highlighted_number;
	this->highlighted_ptr = index_to_item(data, highlighted_number, 0);

	if( recalc_positions )
		set_autoplacement(data, 1, 1);

	init_column_width();

	if( gui && draw ) {
		draw_items(0, 1);
		update_scrollbars(1);
	}

	return 0;
}

void BC_ListBox::center_selection()
{
	int selection = get_selection_number(0, 0);
	calculate_item_coords();
	center_selection(selection);

	if( gui ) {
		draw_items(1, 1);
		update_scrollbars(0);
		gui->show_window(1);
	}
}

void BC_ListBox::move_vertical(int pixels)
{
}

void BC_ListBox::move_horizontal(int pixels)
{
}

int BC_ListBox::select_previous(int skip, BC_ListBoxItem *selected_item, int *counter,
		ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
{
	int top_level = 0;
	if( !selected_item )
		selected_item = get_selection(0, 0);
	int temp = -1;
	if( !counter )
		counter = &temp;
	int temp2 = 0;
	if( !got_first ) {
		got_first = &temp2;
		top_level = 1;
	}
	int temp3 = 0;
	if( !got_second )
		got_second = &temp3;
	if( !data )
		data = this->data;

// Scan backwards to item pointer.  Then count visible items to get
// destination.  No wraparound.
	do {
		for( int i=data[master_column].total-1; i>=0; --i ) {
			BC_ListBoxItem *current_item = data[master_column].values[i];
			if( current_item->sublist_active() ) {
				int result = select_previous(skip, selected_item, counter,
					current_item->get_sublist(), got_first, got_second);
				if( *got_second )
					return result;
			}

			if( *got_first ) {
				(*counter)++;
				if( (*counter) >= skip ) {
					for( int j=0; j<columns; ++j )
						data[j].values[i]->selected = 1;
					(*got_second) = 1;
					return item_to_index(this->data, current_item);
				}
			}
			else {
				if( current_item->selected ) {
					for( int j=0; j<columns; ++j )
						data[j].values[i]->selected = 0;
					(*got_first) = 1;
					(*counter)++;
				}
			}
		}

// Hit top of top level without finding a selected item.
		if( top_level ) {
// Select first item in top level and quit
			BC_ListBoxItem *current_item;
			(*got_first) = 1;
			current_item = data[master_column].values[0];

			for( int j=0; j<columns; ++j )
				data[j].values[0]->selected = 1;
			(*got_second) = 1;
			return item_to_index(this->data, current_item);
		}
	} while( top_level && data[master_column].total );

	return -1;
}

int BC_ListBox::select_next(int skip, BC_ListBoxItem *selected_item, int *counter,
		ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
{
	int top_level = 0;
	if( !selected_item )
		selected_item = get_selection(0, 0);
	int temp = -1;
	if( !counter )
		counter = &temp;
	int temp2 = 0;
	if( !got_first ) {
		got_first = &temp2;
		top_level = 1;
	}
	int temp3 = 0;
	if( !got_second )
		got_second = &temp3;
	if( !data )
		data = this->data;

// Scan forwards to currently selected item pointer.
// Then count visible items to get destination.  No wraparound.
	do {
		for( int i=0; i<data[master_column].total; ++i ) {
			BC_ListBoxItem *current_item = data[master_column].values[i];

// Select next item once the number items after the currently selected item
// have been passed.
			if( *got_first ) {
				(*counter)++;
				if( (*counter) >= skip ) {
					for( int j=0; j<columns; ++j )
						data[j].values[i]->selected = 1;
					(*got_second) = 1;
					return item_to_index(this->data, current_item);
				}
			}
			else {
// Got currently selected item.  Deselect it.
				if( current_item->selected ) {
					for( int j=0; j<columns; ++j )
						data[j].values[i]->selected = 0;
					(*got_first) = 1;
					(*counter)++;
				}
			}

// Descend into expanded level
			if( current_item->sublist_active() ) {
				int result = select_next(skip, selected_item, counter,
					current_item->get_sublist(), got_first, got_second);
				if( *got_second ) {
					return result;
				}
			}
		}

// Hit bottom of top level without finding the next item.
		if( top_level ) {
			BC_ListBoxItem *current_item;
// Select first item in top level and quit
			if( !(*got_first) ) {
				(*got_first) = 1;
				current_item = data[master_column].values[0];

				for( int j=0; j<columns; ++j )
					data[j].values[0]->selected = 1;
				(*got_second) = 1;
			}
			else {
// Select last item in top level and quit
				(*got_first) = 1;
				int current_row = data[master_column].total - 1;
				current_item = data[master_column].values[current_row];

				for( int j=0; j<columns; ++j )
					data[j].values[current_row]->selected = 1;
				(*got_second) = 1;
			}

			return item_to_index(this->data, current_item);
		}
	} while( top_level && data[master_column].total );

	return -1;
}


void BC_ListBox::clamp_positions()
{
	items_w = get_items_width();
	if( xscroll_orientation & SCROLL_STRETCH )
		items_w += view_w / 4;
	items_h = get_items_height(data, columns);
	if( yscroll_orientation & SCROLL_STRETCH )
		items_h += view_h / 4;

	if( yposition < 0 ) yposition = 0;
	else
	if( yposition > items_h - view_h )
		yposition = items_h - view_h;

	if( yposition < 0 ) yposition = 0;

	if( xposition < 0 ) xposition = 0;
	else
	if( xposition >= items_w - view_w )
		xposition = items_w - view_w;

	if( xposition < 0 ) xposition = 0;
}

int BC_ListBox::center_selection(int selection,
		ArrayList<BC_ListBoxItem*> *data, int *counter)
{
	int temp = -1;
	if( !data ) data = this->data;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		(*counter)++;

// Got it
		BC_ListBoxItem *item = data[master_column].values[i];
		if( (*counter) == selection ) {
			BC_ListBoxItem *top_item = this->data[master_column].values[0];
			switch( display_format ) {
			case LISTBOX_ICONS:
			case LISTBOX_ICONS_PACKED: {
// Icon is out of window
				if( item->icon_y-yposition  > view_h-get_text_h(item) ||
				    item->icon_y-yposition < 0 ) {
					yposition = item->icon_y - view_h / 2;
				}

				if( data[master_column].values[selection]->icon_x - xposition > view_w ||
				    data[master_column].values[selection]->icon_x - xposition < 0 ) {
					xposition = item->icon_x - view_w / 2;
				}
				break; }
			case LISTBOX_TEXT:
			case LISTBOX_ICON_LIST:
// Text coordinate is out of window
				if( item->text_y-yposition  > view_h-get_text_h(item) ||
				    item->text_y-yposition < 0 ) {
					yposition = item->text_y - top_item->text_y - view_h / 2;
				}
			}
			if( yposition < 0 ) yposition = 0;
			return 1;
		}

// Descend
		if( item->sublist_active() ) {
			int result = center_selection(selection, item->get_sublist(), counter);
			if( result ) return result;
		}
	}
	return 0;
}

void BC_ListBox::update_scrollbars(int flush)
{
	int h_needed = items_h = get_items_height(data, columns);
	int w_needed = items_w = get_items_width();

// if( columns > 0 && column_width )
// printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);

	if( xscrollbar ) {
		if( xposition != xscrollbar->get_value() )
			xscrollbar->update_value(xposition);

		if( w_needed != xscrollbar->get_length() ||
		    view_w != xscrollbar->get_handlelength() )
			xscrollbar->update_length(w_needed, xposition, view_w, 0);
	}

	if( yscrollbar ) {
		if( yposition != yscrollbar->get_value() )
			yscrollbar->update_value(yposition);

		if( h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength() )
			yscrollbar->update_length(h_needed, yposition, view_h, 0);
	}

	if( flush ) this->flush();
}

int BC_ListBox::get_scrollbars()
{
	int h_needed = items_h = get_items_height(data, columns);
	int w_needed = items_w = get_items_width();
	int flush = 0;

	title_h = get_title_h();
	view_h = popup_h - title_h - yS(4);
	view_w = popup_w - xS(4);

// Create scrollbars as needed
	for( int i=0; i<2; ++i ) {
		if( w_needed > view_w ) {
			need_xscroll = 1;
			view_h = popup_h - title_h -
				get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() -
				yS(4);
		}
		else {
			need_xscroll = 0;
		}

		if( h_needed > view_h ) {
			need_yscroll = 1;
			view_w = popup_w -
				get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() -
				xS(4);
		}
		else {
			need_yscroll = 0;
		}
	}

// Update subwindow size
	int new_w = popup_w;
	int new_h = popup_h;
	if( need_xscroll ) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
	if( need_yscroll ) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();

	if( !is_popup )
		if( new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h() )
			gui->resize_window(new_w, new_h);

	BC_WindowBase *destination = (is_popup ? gui : parent_window);
	if( need_xscroll ) {
		if( !xscrollbar ) {
			xscrollbar = new BC_ListBoxXScroll(this);
			destination->add_subwindow(xscrollbar);
			xscrollbar->show_window(0);
			xscrollbar->bound_to = this;
		}
		else {
		    xscrollbar->update_length(w_needed, xposition, view_w, flush);
			xscrollbar->reposition_window(get_xscroll_x(),
				get_xscroll_y(),
				get_xscroll_width());
		}
	}
	else {
		if( xscrollbar ) delete xscrollbar;
		xscrollbar = 0;
		xposition = 0;
	}

	if( need_yscroll ) {
		if( !yscrollbar ) {
			yscrollbar = new BC_ListBoxYScroll(this);
			destination->add_subwindow(yscrollbar);
			yscrollbar->show_window(0);
			yscrollbar->bound_to = this;
		}
		else {
			yscrollbar->update_length(h_needed, yposition, view_h, flush);
			yscrollbar->reposition_window(get_yscroll_x(),
				get_yscroll_y(),
				get_yscroll_height());
		}
	}
	else {
		if( yscrollbar ) delete yscrollbar;
		yscrollbar = 0;
		yposition = 0;
	}

	if( !bg_surface ||
	    view_w + xS(4) != bg_surface->get_w() ||
	    view_h + yS(4) != bg_surface->get_h() ) {
		if( bg_surface ) delete bg_surface;
		bg_surface = new BC_Pixmap(gui, view_w + xS(4), view_h + yS(4));
		bg_draw = 1;
	}


	return 0;
}

int BC_ListBox::get_w()
{
	return is_popup ? BCPOPUPLISTBOX_W : popup_w;
}
int BC_ListBox::get_h()
{
	return is_popup ? BCPOPUPLISTBOX_H : popup_h;
}

int BC_ListBox::gui_tooltip(const char *text)
{
	return is_popup && gui ? gui->show_tooltip(text, gui->get_w(),0, -1,-1) : -1;
}

void BC_ListBox::set_drag_scroll(int value)
{
	allow_drag_scroll = value;
}

// Test for scrolling by dragging

int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
{
	int result = 0;
	if( allow_drag_scroll ||
	    current_operation == SELECT_RECT ) {

		int top_boundary = get_title_h();

		if( cursor_y < top_boundary ||
		    cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
		    cursor_x < LISTBOX_BORDER ||
		    cursor_x >= view_w + LISTBOX_BORDER ) {
			result = 1;
		}
	}
	return result;
}

int BC_ListBox::drag_scroll_event()
{
	int top_boundary = get_title_h();
	int result = 0;

	if( get_cursor_y() < top_boundary ) {
		yposition -= top_boundary - get_cursor_y();
		result = 1;
	}
	else
	if( get_cursor_y() >= view_h + title_h + yS(4) ) {
		yposition += get_cursor_y() - (view_h + title_h + yS(4));
		result = 1;
	}

	if( get_cursor_x() < xS(2) ) {
		xposition -= xS(2) - get_cursor_x();
		result = 1;
	}
	else
	if( get_cursor_x() >= view_w + xS(2) ) {
		xposition += get_cursor_x() - (view_w + xS(2));
		result = 1;
	}

	if( result )
		clamp_positions();

	return result;
}

int BC_ListBox::rectangle_scroll_event()
{
	int old_xposition = xposition;
	int old_yposition = yposition;
	int result = drag_scroll_event();

	if( result ) {
		rect_x1 += old_xposition - xposition;
		rect_y1 += old_yposition - yposition;
		rect_x2 = get_cursor_x();
		rect_y2 = get_cursor_y();

		int x1 = MIN(rect_x1, rect_x2);
		int x2 = MAX(rect_x1, rect_x2);
		int y1 = MIN(rect_y1, rect_y2);
		int y2 = MAX(rect_y1, rect_y2);

		if( select_rectangle(data, x1, y1, x2, y2) ) {
			selection_changed();
		}

		clamp_positions();
		draw_items(0);
		update_scrollbars(1);
	}
	return result;
}

int BC_ListBox::select_scroll_event()
{
	int result = drag_scroll_event();
	if( result ) {
		highlighted_item = selection_number = get_cursor_item(data,
			get_cursor_x(), get_cursor_y(), &highlighted_ptr);
		clamp_positions();
		draw_items(0);
		update_scrollbars(1);
		selection_changed();
	}
	return result;
}

int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
		int x1, int y1, int x2, int y2)
{
	int result = 0;
	for( int i=0; i<data[master_column].total; ++i ) {
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *item = data[j].values[i];
			switch( display_format ) {
			case LISTBOX_ICONS:
			case LISTBOX_ICONS_PACKED: {
				int icon_x, icon_y, icon_w, icon_h;
				int text_x, text_y, text_w, text_h;
				get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
				get_text_mask(item, text_x, text_y, text_w, text_h);

				if( (x2 >= icon_x && x1 < icon_x + icon_w &&
				    y2 >= icon_y && y1 < icon_y + icon_h) ||
				    (x2 >= text_x && x1 < text_x + text_w &&
				     y2 >= text_y && y1 < text_y + text_h) ) {
					if( !item->selected ) {
						item->selected = 1;
						result = 1;
					}
				}
				else {
					if( item->selected ) {
						item->selected = 0;
						result = 1;
					}
				}
				break; }
			case LISTBOX_TEXT:
			case LISTBOX_ICON_LIST: {
				if( x2 >= 0 &&
				    x1 < (yscrollbar ?
					gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
					gui->get_w()) && y2 > 0 &&
				    y1 < gui->get_h() && y2 >= get_item_y(item) &&
				    y1 < get_item_y(item) + get_item_h(item) ) {
					if( !item->selected ) {
						item->selected = 1;
						result = 1;
					}
				}
				else {
					if( item->selected ) {
						item->selected = 0;
						result = 1;
					}
				}
				break; }
			}
		}

		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			result |= select_rectangle(item->get_sublist(),
				x1, y1, x2, y2);
		}
	}
	return result;
}

int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
		int selection_number, int x, int y, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		(*counter)++;
		if( (*counter) == selection_number ) {
			item->icon_x = x;
			item->icon_y = y;
			return 1;
		}
// Not recursive because it's only used for icons
	}
	return 0;
}

void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
	ArrayList<BC_ListBoxItem*> *src)
{
	for( int i=0; i<src[master_column].total;  ) {
		BC_ListBoxItem *item = src[master_column].values[i];

// Move item to dst
		if( item->selected ) {
			for( int j=0; j<columns; ++j ) {
				dst[j].append(src[j].values[i]);
				src[j].remove_number(i);
			}
			continue;
		}
// Descend into sublist
		if( item->sublist_active() ) {
			move_selection(dst, item->get_sublist());
		}
		++i;
	}
}

int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
		ArrayList<BC_ListBoxItem*> *src, int destination, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;

	if( destination < 0 || destination >= data[master_column].total ) {
		for( int j=0; j<columns; ++j ) {
			for( int i=0; i<src[j].total; ++i ) {
				data[j].append(src[j].values[i]);
			}
		}
		return 1;
	}
	else
	for( int i=0; i<data[master_column].total; ++i ) {
		(*counter)++;
		if( (*counter) == destination ) {
			for( int j=0; j<columns; ++j ) {
				for( int k=0; k<src[j].total; ++k ) {
					data[j].insert(src[j].values[k], destination + k);
				}
			}
			return 1;
		}

		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			if( put_selection(item->get_sublist(), src, destination, counter) )
				return 1;
		}
	}
	return 0;
}



int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
		BC_ListBoxItem *item, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		(*counter)++;
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *new_item = data[j].values[i];
//printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
			if( new_item == item ) {
				return (*counter);
			}
		}

		BC_ListBoxItem *new_item = data[master_column].values[i];
		if( new_item->sublist_active() ) {
			if( item_to_index(new_item->get_sublist(), item, counter) >= 0 )
				return (*counter);
		}
	}

	return -1;
}

BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
		int number, int column, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;
	for( int i=0; i<data[master_column].total; ++i ) {
		(*counter)++;
		if( (*counter) == number ) {
			return data[column].values[i];
		}
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			BC_ListBoxItem *result = index_to_item(item->get_sublist(),
				number, column, counter);
			if( result ) return result;
		}
	}
	return 0;
}

int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data, int cursor_x, int cursor_y,
		BC_ListBoxItem **item_return, int *counter, int expanded)
{
	int temp = -1;
	if( !data ) return -1;
	if( !counter ) counter = &temp;

// Icons are not treed
	switch( display_format ) {
	case LISTBOX_ICONS:
	case LISTBOX_ICONS_PACKED:
	case LISTBOX_ICON_LIST: {
		for( int j=data[master_column].total; --j>=0; ) {
			int icon_x, icon_y, icon_w, icon_h;
			int text_x, text_y, text_w, text_h;
			BC_ListBoxItem *item = data[master_column].values[j];
			get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
			get_text_mask(item, text_x, text_y, text_w, text_h);

			if( (cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
			     cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
			    (cursor_x >= text_x && cursor_x < text_x + text_w &&
			     cursor_y >= text_y && cursor_y < text_y + text_h) ) {
				if( item_return ) (*item_return) = item;
				return j;
			}
		}
		return -1; }
	case LISTBOX_TEXT:
		if( !gui ) break;
// Text is treed
// Cursor is inside items rectangle
		if( cursor_x >= 0 &&
		    cursor_x < (yscrollbar ?
				gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
				gui->get_w()) &&
// Only clamp y if we're not in a SELECT operation.
		    (current_operation == BC_ListBox::SELECT ||
		    	(cursor_y > get_title_h() + LISTBOX_BORDER &&
			 cursor_y < gui->get_h())) ) {
// Search table for cursor obstruction
			for( int i=0; i<data[master_column].total; ++i ) {
				BC_ListBoxItem *item = data[master_column].values[i];
				(*counter)++;

// Cursor is inside item on current level
				if( expanded && item->selectable &&
				    cursor_y >= get_item_y(item) &&
				    cursor_y < get_item_y(item) + get_item_h(item) ) {
//printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
					if( item_return ) (*item_return) = item;
					if( expanded < 0 ) (*counter) = i;
					return (*counter);
				}

// Descend into sublist
				if( item->sublist_active() ) {
					if( get_cursor_item(item->get_sublist(),
					      cursor_x, cursor_y, item_return, counter,
					      item->get_expand()) >= 0 ) {
						if( expanded < 0 ) (*counter) = i;
						return (*counter);
					}
				}
			}
		}
		break;
	}

	return -1;
}

// short version
int BC_ListBox::get_cursor_item_no()
{
	int rx, ry;
	gui->get_relative_cursor(rx, ry);
	return get_cursor_item(data, rx, ry, 0, 0);
}

int BC_ListBox::get_cursor_data_item_no(BC_ListBoxItem **item_return)
{
	int rx, ry;
	gui->get_relative_cursor(rx, ry);
	return get_cursor_item(data, rx, ry, item_return, 0, -1);
}

int BC_ListBox::repeat_event(int64_t duration)
{
	switch( current_operation ) {
// Repeat out of bounds selection
	case SELECT_RECT:
		if( duration != get_resources()->scroll_repeat ) break;
		return rectangle_scroll_event();

	case DRAG_ITEM:
		if( duration != get_resources()->scroll_repeat ) break;
		if( !drag_scroll_event() ) break;
		clamp_positions();
		draw_items(0);
		update_scrollbars(1);
		return 1;

	case SELECT:
		if( duration != get_resources()->scroll_repeat ) break;
		return select_scroll_event();

	case NO_OPERATION:
// Show tooltip
		if( duration != get_resources()->tooltip_delay ) break;
		if( !button_highlighted || !is_popup ) break;
		if( !tooltip_text || !tooltip_text[0] ) break;
		show_tooltip();
		return 1;
	}
	return 0;
}


int BC_ListBox::cursor_enter_event()
{
	int result = 0;

	switch( current_operation ) {
// Cursor moved over button, pressed, and exited.
	case BUTTON_DOWN_SELECT:
		if( top_level->event_win == win ) {
			current_operation = BUTTON_DN;
			result = 1;
			button_highlighted = 1;
			draw_button(1);
		}
		break;

	case NO_OPERATION:
// Cursor entered button
		if( is_popup && top_level->event_win == win ) {
			button_highlighted = 1;
			result = 1;
			draw_button(1);
		}
		else
// TODO: Need to get the highlighted column title or item
		if( gui && top_level->event_win == gui->win ) {
			list_highlighted = 1;
			draw_border(1);
			result = 1;
		}
		break;
	}

	return result;
}

int BC_ListBox::cursor_leave_event()
{
	if( current_operation == COLUMN_DRAG ) return 0;

// Left button area
	if( button_highlighted ) {
		button_highlighted = 0;
		hide_tooltip();
		draw_button(1);
	}

	if( list_highlighted ) {
		list_highlighted = 0;
		highlighted_item = -1;
		highlighted_ptr = 0;
		highlighted_title = -1;
		int redraw_toggles = 0;
		for( int i=0; i<expanders.total; ++i )
			expanders.values[i]->cursor_leave_event(&redraw_toggles);

		draw_items(1);
	}

	return 0;
}

int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
{
	int temp = -1;
	if( !result ) result = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		(*result)++;
		if( item->selected ) return (*result);
		if( item->sublist_active() ) {
			if( get_first_selection(item->get_sublist(), result) >= 0 )
				return (*result);
		}
	}
	return -1;
}

int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
	int *result,
	int master_column)
{
	int temp = 0;
	if( !result ) result = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		(*result)++;
		if( data[master_column].values[i]->sublist_active() )
			get_total_items(data[master_column].values[i]->get_sublist(),
				result,
				master_column);
	}

	return (*result);
}


int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
	int *result)
{
	int temp = -1;
	int top_level = 0;
	if( !result ) {
		result = &temp;
		top_level = 1;
	}

	for( int i=data[master_column].total-1; i>=0; --i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		(*result)++;
		if( item->selected ) {
			if( top_level )
				return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
			else
				return (*result);
		}

		if( item->sublist_active() ) {
			if( get_last_selection(item->get_sublist(), result) >= 0 ) {
				if( top_level )
					return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
				else
					return (*result);
			}
		}
	}
	return -1;
}

void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
		int start, int end, int *current)
{
	int temp = -1;
	if( !current ) current = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		(*current)++;
		if( (*current) >= start && (*current) < end ) {
			for( int j=0; j<columns; ++j )
				data[j].values[i]->selected = 1;
		}
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() )
			select_range(item->get_sublist(), start, end, current);
	}
}


// Fill items between current selection and new selection
int BC_ListBox::expand_selection(int button_press, int selection_number)
{
	int old_selection_start = selection_start;
	int old_selection_end = selection_end;

// printf("BC_ListBox::expand_selection %d %d\n",
// selection_center,
// selection_number);

// Calculate the range to select based on selection_center and selection_number
	if( selection_number < selection_center ) {
		selection_start = selection_number;
	}
	else {
		selection_end = selection_number + 1;
	}

//printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
// Recurse through all the items and select the desired range
	select_range(data, selection_start, selection_end);

// Trigger redraw
	return (old_selection_start != selection_start ||
		old_selection_end != selection_end);
}

int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
		int selection_number, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		(*counter)++;
		if( (*counter) == selection_number ) {
// Get new value for selection
			int selected = !item->selected;
// Set row
			for( int j=0; j<columns; ++j )
				data[j].values[i]->selected = selected;
			return 1;
		}

// Descend into sublist
		if( item->sublist_active() ) {
			if( toggle_item_selection(item->get_sublist(),
			      selection_number, counter) )
				return 1;
		}
	}

	return 0;
}


void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
{
	for( int i=0; i<data[master_column].total; ++i ) {
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *item = data[j].values[i];
			item->selected = value;
		}
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->get_sublist() ) {
			set_all_selected(item->get_sublist(), value);
		}
	}
}

void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
		int item_number, int value, int *counter)
{
	int temp = -1;
	if( !counter ) counter = &temp;
	for( int i=0; i<data[master_column].total&&(*counter)!=item_number; ++i ) {
		(*counter)++;
		if( (*counter) == item_number ) {
			for( int j=0; j<columns; ++j ) {
				BC_ListBoxItem *item = data[j].values[i];
				item->selected = value;
			}
			return;
		}

		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() ) {
			set_selected(item->get_sublist(), item_number, value, counter);
		}
	}
}

int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
		int selection_number, int *counter)
{
	int temp = -1;
	int result = 0;
	if( !counter ) counter = &temp;

	for( int i=0; i<data[master_column].total; ++i ) {
		BC_ListBoxItem *item = data[master_column].values[i];
		(*counter)++;
		if( (*counter) == selection_number && !item->selected ) {
			result = 1;
			for( int j=0; j<columns; ++j )
				data[j].values[i]->selected = 1;
		}
		else
		if( (*counter) != selection_number && item->selected ) {
			result = 1;
			for( int j=0; j<columns; ++j )
				data[j].values[i]->selected = 0;
		}
		if( item->sublist_active() )
			result |= update_selection(item->get_sublist(),
				selection_number,
				counter);
	}
	return result;
}

void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
		int old_value, int new_value)
{
	for( int i=0; i<data[master_column].total; ++i ) {
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *item = data[j].values[i];
			if( item->selected == old_value ) item->selected = new_value;
		}
		BC_ListBoxItem *item = data[master_column].values[i];
		if( item->sublist_active() )
			promote_selections(item->get_sublist(), old_value, new_value);
	}
}

int BC_ListBox::focus_out_event()
{
	deactivate();
	return 0;
}

int BC_ListBox::button_press_event()
{
	int result = 0;
	BC_ListBoxItem *current_item = 0;
	int new_cursor;
	int do_selection_change = 0;
	const int debug = 0;

	hide_tooltip();
	if( debug ) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
		__LINE__, this, (void*)top_level->event_win,
		(void*)(gui ? gui->win : 0), (void*)win, (void*)parent_window->win);

// Pressed in button
	if( is_popup && top_level->event_win == win ) {
		if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
		current_operation = BUTTON_DN;
		draw_button(1);

// Deploy listbox
		if( !active && !disabled ) {
			top_level->deactivate();
			activate();
		}

		result = 1;
	}
	else
// Pressed in scrollbar
	if( (xscrollbar && top_level->event_win == xscrollbar->win) ||
	    (yscrollbar && top_level->event_win == yscrollbar->win) ) {
		if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
		result = 0;
	}
	else
// Pressed in items
	if( gui && top_level->event_win == gui->win ) {
		if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);

// Activate list items
// If it is a suggestion popup, it is visible without being active
		if( !active ) {
			if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
			if( !is_suggestions ) top_level->deactivate();
			if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
			activate();
			if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
		}

		if( current_operation != NO_OPERATION ) {
			switch( current_operation ) {
			case DRAG_ITEM:
			case COLUMN_DRAG:
				return drag_stop_event();
			}
		}

// Wheel mouse pressed
		if( get_buttonpress() == 4 ) {
			if( current_operation == NO_OPERATION ) {
				current_operation = WHEEL;
				if( yscrollbar ) {
					set_yposition(yposition - gui->get_h() / 10, 0);
					clamp_positions();
					update_scrollbars(0);
					highlighted_ptr = 0;
					highlighted_item = get_cursor_item(data,
						top_level->cursor_x, top_level->cursor_y,
						&highlighted_ptr);
					draw_items(1);
					result = 1;
				}
			}
		}
		else
		if( get_buttonpress() == 5 ) {
			if( current_operation == NO_OPERATION ) {
				current_operation = WHEEL;
				if( yscrollbar ) {
					set_yposition(yposition + gui->get_h() / 10, 0);
					clamp_positions();
					update_scrollbars(0);
					highlighted_ptr = 0;
					highlighted_item = get_cursor_item(data,
						top_level->cursor_x, top_level->cursor_y,
						&highlighted_ptr);
					draw_items(1);
					result = 1;
				}
			}
		}
		else
// Pressed over column title division
		if( test_column_divisions(gui->get_cursor_x(),
			gui->get_cursor_y(),
			new_cursor) ) {
			drag_cursor_x = gui->get_cursor_x() + xposition;
			if( column_width )
				drag_column_w = column_width[highlighted_division - 1];
			else
				drag_column_w = default_column_width[highlighted_division - 1];

			current_operation = DRAG_DIVISION;
			reset_query();
		}
		else
// Pressed in column title
		if( test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()) ) {
			current_operation = COLUMN_DN;
			button_highlighted = 0;
			list_highlighted = 1;
			draw_items(1);
			result = 1;
		}
		else
// Pressed in expander
		if( test_expanders() ) {
			current_operation = EXPAND_DN;
// Need to redraw items because of alpha
			draw_items(1);
			result = 1;
		}
		else
// Pressed over item
		if( (selection_number = get_cursor_item(data,
					gui->get_cursor_x(), gui->get_cursor_y(),
					&current_item)) >= 0 ) {
			if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);

// Get item button was pressed over
			selection_number2 = selection_number1;
			selection_number1 = selection_number;

			selection_start = -1;
			selection_end = -1;


// Multiple item selection is possible
			if( selection_mode == LISTBOX_MULTIPLE &&
			    (ctrl_down() || shift_down() || current_item->selected) ) {
// Expand text selection.
// Fill items between selected region and current item.
				if( shift_down() &&
				    (display_format == LISTBOX_TEXT ||
				     display_format == LISTBOX_ICON_LIST) ) {
// Get first item selected
					selection_start = get_first_selection(data);
// Get last item selected
					selection_end = get_last_selection(data);
// Get center of selected region
					if( selection_end > selection_start ) {
						selection_center = (selection_end + selection_start) >> 1;
					}
					else {
						selection_center = selection_number;
					}
// Deselect everything.
					set_all_selected(data, 0);
// Select just the items
					expand_selection(1, selection_number);
					new_value = 1;
				}
// Toggle a single item on or off
				else if( ctrl_down() ) {
					toggle_item_selection(data, selection_number);
					new_value = current_item->selected;
				}
			}
// Select single item
			else {
				if( !current_item->selected || (get_buttonpress() == 1 && !new_value) ) {
					set_all_selected(data, 0);
					set_selected(data, selection_number, 1);
				}
				new_value = 1;
			}


			current_operation = SELECT;
			highlighted_item = selection_number;
			highlighted_ptr = current_item;
			button_highlighted = 0;
			list_highlighted = 1;
			reset_query();
			draw_items(1);
			do_selection_change = 1;
			result = 1;
		}
		else
// Pressed over nothing.  Start rectangle selection.
		if( data ) {
			if( get_buttonpress() == 1 &&
			    selection_mode == LISTBOX_MULTIPLE ) {
				if( !shift_down() ) {
// Deselect all and redraw if anything was selected
					if( get_selection_number(0, 0) >= 0 ) {
						set_all_selected(data, 0);
						draw_items(1);
						do_selection_change = 1;
						result = 1;
					}
				}
				else {
// Promote selections to protect from a rectangle selection
					promote_selections(data, 1, 2);
				}

// Start rectangle selection
				current_operation = SELECT_RECT;
				rect_x1 = rect_x2 = get_cursor_x();
				rect_y1 = rect_y2 = get_cursor_y();
			}
		}


		reset_query();
	}
	else
// Suggestion box is not active but visible, so lie to get it to deactivate
	if( is_popup && (active || (is_suggestions && gui)) ) {
		active = 1;
		if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
		deactivate();
		result = 1;
	}


	if( do_selection_change ) selection_changed();
	if( debug ) printf("BC_ListBox::button_press_event %d %d\n", __LINE__, result);

	return result;
}

int BC_ListBox::button_release_event()
{
	int result = 0;
	int do_event = 0;
	new_value = 0;
	unset_scroll_repeat();

//printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
	switch( current_operation ) {
	case DRAG_DIVISION:
		current_operation = NO_OPERATION;
		result = 1;
		break;

	case WHEEL:
		current_operation = NO_OPERATION;
		result = 1;
		break;

// Release item selection
	case BUTTON_DOWN_SELECT:
	case SELECT:
//printf("BC_ListBox::button_release_event 10\n");
		current_operation = NO_OPERATION;
		if( gui ) {
			selection_number1 = selection_number = get_cursor_item_no();
//printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
		}

		if( is_popup ) {
			button_releases++;
			if( selection_number >= 0 ) {
				deactivate();
				do_event = 1;
			}
			else
// Second button release outside button
			if( button_releases > 1 ) {
				deactivate();
			}
		}
		else {
			if( top_level->get_double_click() &&
			    selection_number2 == selection_number1 &&
			    selection_number2 >= 0 && selection_number1 >= 0 ) {
				do_event = 1;
			}
			result = 1;
		}
		break;


	case SELECT_RECT:
		if( data ) {
// Demote selections from rectangle selection
			promote_selections(data, 2, 1);
		}

// Hide rectangle overlay
		draw_rectangle(1);
		current_operation = NO_OPERATION;
		result = 1;
		break;

// Release popup button
	case BUTTON_DN:
		hide_tooltip();
		current_operation = NO_OPERATION;
		button_releases++;
		draw_button(1);

// Second button release inside button
		if( button_releases > 1 ) {
			deactivate();
		}
		result = 1;
		break;

	case COLUMN_DN:
		current_operation = NO_OPERATION;
// Update the sort column and the sort order for the user only if the existing
// sort column is valid.
		if( sort_column >= 0 ) {
// Invert order only if column is the same
			if( highlighted_title == sort_column )
				sort_order = sort_order == SORT_ASCENDING ?
					SORT_DESCENDING : SORT_ASCENDING;
// Set the new sort column
			sort_column = highlighted_title;
			if( !sort_order_event() ) {
				draw_titles(1);
			}
		}
		else
// Sorting not enabled.  Redraw the title state.
		{
			draw_titles(1);
		}
		result = 1;
		break;

	case EXPAND_DN: {
		int redraw_toggles = 0;
		for( int i=0; i<expanders.total&&!result; ++i ) {
			if( expanders.values[i]->button_release_event(&redraw_toggles) ) {
				result = 1;
			}
		}
// Need to redraw items because of alpha
		if( redraw_toggles ) draw_items(1);
		current_operation = NO_OPERATION;
		break; }

	default:
// Can't default to NO_OPERATION because it may be used in a drag event.
		break;
	}


	if( do_event ) handle_event();

//printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
	return result;
}

int BC_ListBox::get_title_h()
{
	if( display_format == LISTBOX_TEXT ||
	    display_format == LISTBOX_ICON_LIST )
		return column_titles ? column_bg[0]->get_h() : 0;
	return 0;
}

void BC_ListBox::reset_cursor(int new_cursor)
{
	if( is_popup ) {
		if( gui->get_cursor() != new_cursor ) {
			gui->set_cursor(new_cursor, 0, 0);
		}
	}
	else
	if( get_cursor() != new_cursor ) {
		set_cursor(new_cursor, 0, 0);
	}
}

int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
{
	if( gui && column_titles &&
	    cursor_y >= 0 && cursor_y < get_title_h() &&
	    cursor_x >= 0 && cursor_x < gui->get_w() ) {
		for( int i=1; i<columns; ++i ) {
			if( cursor_x >= -xposition + get_column_offset(i) - 5 &&
			    cursor_x <  -xposition + get_column_offset(i) +
					get_resources()->listbox_title_hotspot ) {
				highlighted_item = -1;
				highlighted_ptr = 0;
				highlighted_division = i;
				highlighted_title = -1;
				list_highlighted = 1;
				new_cursor = HSEPARATE_CURSOR;
				return 1;
			}
		}
	}
	highlighted_division = -1;
	return 0;
}

int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
{
	if( gui && column_titles &&
	    cursor_y >= 0 && cursor_y < get_title_h() &&
	    cursor_x >= 0 && cursor_x < gui->get_w() ) {
		for( int i=0; i<columns; ++i ) {
			if( cursor_x >= -xposition + get_column_offset(i) &&
				(cursor_x < -xposition + get_column_offset(i + 1) ||
					i == columns - 1) ) {
				highlighted_item = -1;
				highlighted_ptr = 0;
				highlighted_division = -1;
				highlighted_title = i;
				list_highlighted = 1;
				return 1;
			}
		}
	}
	highlighted_title = -1;
	return 0;
}

int BC_ListBox::test_expanders()
{
	for( int i=0; i<expanders.total; ++i ) {
		if( expanders.values[i]->button_press_event() ) {
			current_operation = EXPAND_DN;
			draw_toggles(1);
			return 1;
		}
	}
	return 0 ;
}

int BC_ListBox::cursor_motion_event()
{
	int redraw = 0, result = 0;
	int new_cursor = ARROW_CURSOR;

	selection_number = -1;


	switch( current_operation ) {
	case BUTTON_DN:
// Button pressed and slid off button
		if( !cursor_inside() ) {
			current_operation = BUTTON_DOWN_SELECT;
			draw_button(1);
			result = 1;
		}
		break;

	case DRAG_DIVISION: {
//		int new_w = get_cursor_x() +
//			xposition -
//			get_column_offset(highlighted_division - 1);
		int difference = get_cursor_x() + xposition - drag_cursor_x;
		int new_w = drag_column_w + difference;

		new_cursor = HSEPARATE_CURSOR;

		if( column_width ) {
			column_width[highlighted_division - 1] = new_w;
		}
		else {
			default_column_width[highlighted_division - 1] = new_w;
		}

		column_width_boundaries();

// Force update of coords
		set_autoplacement(data, 0, 1);
		column_resize_event();

		clamp_positions();
		draw_items(0);
		update_scrollbars(1);
		result = 1;
		break; }

	case SELECT_RECT: {
		if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
			set_scroll_repeat();

		int old_x1 = MIN(rect_x1, rect_x2);
		int old_x2 = MAX(rect_x1, rect_x2);
		int old_y1 = MIN(rect_y1, rect_y2);
		int old_y2 = MAX(rect_y1, rect_y2);

		int new_rect_x2 = get_cursor_x();
		int new_rect_y2 = get_cursor_y();

		int x1 = MIN(rect_x1, new_rect_x2);
		int x2 = MAX(rect_x1, new_rect_x2);
		int y1 = MIN(rect_y1, new_rect_y2);
		int y2 = MAX(rect_y1, new_rect_y2);

// Adjust rectangle coverage
		if( old_x1 != x1 || old_x2 != x2 ||
		    old_y1 != y1 || old_y2 != y2 ) {
			if( data ) {
				redraw = select_rectangle(data, x1, y1, x2, y2);
			}

// hide rectangle
			if( !redraw ) {
//printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
				draw_rectangle(0);
			}
		}

		rect_x2 = get_cursor_x();
		rect_y2 = get_cursor_y();
		if( redraw ) {
			clamp_positions();
			draw_items(0);
			update_scrollbars(1);
			selection_changed();
//printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
		}
		else
		if( old_x1 != x1 || old_x2 != x2 ||
		    old_y1 != y1 || old_y2 != y2 ) {
//printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
			draw_rectangle(1);
		}

		result = 1;
		break; }

	case SELECT: {
		int old_highlighted_item = highlighted_item;

		if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
			set_scroll_repeat();

		highlighted_item = selection_number = get_cursor_item(data,
			get_cursor_x(), get_cursor_y(), &highlighted_ptr);
		result = 1;

// Deselect all items and select just the one we're over
		if( selection_number >= 0 && !allow_drag &&
		    ((!shift_down() && !ctrl_down()) ||
		     selection_mode == LISTBOX_SINGLE) ) {
			redraw = update_selection(data, selection_number);
		}
		else
// Expand multiple selection
		if( selection_mode == LISTBOX_MULTIPLE &&
		    (shift_down() || ctrl_down()) ) {
// Expand selected region in text mode centered around initial range
			if( (display_format == LISTBOX_TEXT ||
			     display_format == LISTBOX_ICON_LIST) &&
				shift_down() ) {
// Select just the items
				redraw = expand_selection(0, selection_number);
			}
// Set the one item we're over to the selection value determined in
// button_press_event.
			else {
				set_selected(data, selection_number, new_value);
			}
		}

		if( highlighted_item != old_highlighted_item ) {
			clamp_positions();
			draw_items(0);
			update_scrollbars(1);
//printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
			selection_changed();
		}
		break; }

	case BUTTON_DOWN_SELECT:
// Went back into button area
		if( cursor_inside() ) {
			current_operation = BUTTON_DN;
			draw_button(1);
			result = 1;
		}
		else
// Went into item area
		if( gui ) {
			int cursor_x = 0, cursor_y = 0;
			translate_coordinates(top_level->event_win, gui->win,
				top_level->cursor_x, top_level->cursor_y,
				&cursor_x, &cursor_y);
			int old_highlighted_item = highlighted_item;
			highlighted_item = selection_number = get_cursor_item(data,
					cursor_x,
					cursor_y,
					&highlighted_ptr);

			if( highlighted_item != old_highlighted_item ) {
				update_selection(data, selection_number);
				draw_items(1);
				selection_changed();
			}
		}
		break;

	case EXPAND_DN: {
		int redraw_toggles = 0;
		for( int i=0; i<expanders.total&&!result; ++i ) {
			result = expanders.values[i]->cursor_motion_event(
				&redraw_toggles);
		}
		if( redraw_toggles ) {
// Need to redraw items because of the alpha
			draw_items(1);
		}
		break; }

	case NO_OPERATION: {
		int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
		if( gui && top_level->event_win == gui->win ) {
			int old_highlighted_title = highlighted_title;
			int old_list_highlighted = list_highlighted;
			int old_highlighted_item = highlighted_item;
			int redraw_titles = 0;
			int redraw_border = 0;
			int redraw_items = 0;
			int redraw_toggles = 0;
			result = 1;

// Test if cursor moved over a title division
			test_column_divisions(cursor_x, cursor_y, new_cursor);

// Test if cursor moved over a title
			if( highlighted_division < 0 ) {
				test_column_titles(cursor_x, cursor_y);
			}

// Test if cursor moved over expander
			if( highlighted_division < 0 && highlighted_title < 0 &&
			    (display_format == LISTBOX_TEXT ||
			     display_format == LISTBOX_ICON_LIST) ) {
				for( int i=0; i<expanders.total; ++i ) {
					expanders.values[i]->cursor_motion_event(
						&redraw_toggles);
				}
//printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
			}

// Test if cursor moved over an item
			if( highlighted_division < 0 && highlighted_title < 0 ) {
				highlighted_item = get_cursor_item(data,
					cursor_x,
					cursor_y,
					&highlighted_ptr);
			}


// Clear title highlighting if moved over division
			if( old_highlighted_title != highlighted_title ) {
				redraw_titles = 1;
			}

// Highlight list border
			if( old_list_highlighted != list_highlighted ) {
				redraw_border = 1;
			}

// Moved out of item area
			if( old_highlighted_item != highlighted_item ) {
				if( !mouse_over_event(highlighted_item) )
					redraw_items = 1;
			}

//printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);

// Change cursor to title division adjustment
			reset_cursor(new_cursor);

			if( redraw_items ) {
				draw_items(0);
			}
			else {
				if( redraw_titles )
					draw_titles(0);
				if( redraw_border )
					draw_border(0);
				if( redraw_toggles )
					draw_toggles(0);
			}

			if( redraw_items || redraw_titles ||
			    redraw_border || redraw_toggles ) {
				gui->flash();
				gui->flush();
			}
		}


		if( !result && list_highlighted ) {
			list_highlighted = 0;
			highlighted_item = -1;
			highlighted_ptr = 0;
			highlighted_title = -1;
			highlighted_division = -1;
			draw_items(1);
			result = 0;
		}
		break; }
	}

	return result;
}

int BC_ListBox::drag_start_event()
{
	switch( current_operation ) {
	case SELECT:
		if( expander_active() ) break;
		if( gui && gui->is_event_win() && allow_drag ) {
			BC_ListBoxItem *item_return = 0;
			selection_number = get_cursor_item(data,
				top_level->cursor_x,
				top_level->cursor_y,
				&item_return);

			if( selection_number >= 0 ) {
				int cx, cy;
				get_abs_cursor(cx, cy);
				if( item_return->icon_vframe ) {
					drag_popup = new BC_DragWindow(this,
						item_return->icon_vframe, cx, cy);
				}
				else
				if( item_return->icon ) {
					drag_popup = new BC_DragWindow(this,
						item_return->icon, cx, cy);
				}
				else {
					drag_popup = new BC_DragWindow(this,
						drag_icon_vframe, cx, cy);
				}
				current_operation = DRAG_ITEM;
// require shift down for scrolling
				if( allow_drag < 0 && shift_down() )
					set_scroll_repeat();
				return 1;
			}
		}
		break;

	case COLUMN_DN:
		if( gui && gui->is_event_win() && allow_drag_column ) {
			int cx, cy;
			get_abs_cursor(cx, cy);
			cx -= drag_column_icon_vframe->get_w() / 2,
			cy -= drag_column_icon_vframe->get_h() / 2;
			drag_popup = new BC_DragWindow(this,
				drag_column_icon_vframe, cx, cy);
			dragged_title = highlighted_title;
			current_operation = COLUMN_DRAG;
			draw_titles(1);
			return 1;
		}
		break;
	}

	return 0;
}

int BC_ListBox::drag_motion_event()
{
//printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
	switch( current_operation ) {
	case DRAG_ITEM: {
		int redraw = 0;
		int new_highlighted_item = -1;
		BC_ListBoxItem *new_highlighted_ptr = 0;
		new_highlighted_item = get_cursor_item(data,
			top_level->cursor_x, top_level->cursor_y,
			&new_highlighted_ptr);

		if( new_highlighted_item != highlighted_item ) {
			redraw = 1;
		}

// Always update highlighted value for drag_stop
		highlighted_item = new_highlighted_item;
		highlighted_ptr = new_highlighted_ptr;
//printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
		if( redraw ) {
			clamp_positions();
			draw_items(0);
			update_scrollbars(1);
		}

		return drag_popup->cursor_motion_event(); }

	case COLUMN_DRAG: {
		int old_highlighted_title = highlighted_title;
		test_column_titles(get_cursor_x(), get_cursor_y());
		if( old_highlighted_title != highlighted_title ) {
			draw_titles(1);
		}
		return drag_popup->cursor_motion_event(); }
	}

	return 0;
}

int BC_ListBox::drag_stop_event()
{
	int result = 0;
	unset_scroll_repeat();
	switch( current_operation ) {
	case DRAG_ITEM:
// Inside window boundary
		if( top_level->cursor_x > 0 &&
		    top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
		    top_level->cursor_y > 0 &&
		    top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2 ) {
// Move icon


			if( display_format == LISTBOX_ICONS ||
			    display_format == LISTBOX_ICONS_PACKED ) {
				reposition_item(data,
					selection_number,
					top_level->cursor_x - drag_popup->get_w() / 2 -
						LISTBOX_MARGIN - xS(2) + xposition,
					top_level->cursor_y - drag_popup->get_h() / 2 -
						LISTBOX_MARGIN - yS(2) + yposition);
			}
			else
// Move rows
			if( process_drag ) {
// Move selected items from data to temporary
				ArrayList<BC_ListBoxItem*> *src_items =
					new ArrayList<BC_ListBoxItem*>[columns];
				move_selection(src_items, data);
// Get destination
				int destination = highlighted_item = item_to_index(data,
					highlighted_ptr);
// Insert items from temporary to data
				put_selection(data, src_items, destination);

				delete [] src_items;
				set_autoplacement(data, 0, 1);
			}

			draw_items(1);
		}
		else
			drag_popup->drag_failure_event();
		result = 1;
		break;

	case COLUMN_DRAG:
		if( dragged_title != highlighted_title ) {
			if( highlighted_title >= 0 ) {
				if( !move_column_event() ) draw_titles(1);
			}
			else
				drag_popup->drag_failure_event();
		}
		result = 1;
	}

	if( result ) {
		current_operation = NO_OPERATION;
		delete drag_popup;
		flush();
		drag_popup = 0;
		new_value = 0;
	}

	return result;
}

BC_DragWindow* BC_ListBox::get_drag_popup()
{
	return drag_popup;
}

int BC_ListBox::translation_event()
{
	if( is_popup && gui ) {
		int new_x = gui->get_x() +
			(top_level->last_translate_x - top_level->prev_x -
				BC_DisplayInfo::get_left_border());
		int new_y = gui->get_y() +
			(top_level->last_translate_y - top_level->prev_y -
				BC_DisplayInfo::get_top_border());

		gui->reposition_window(new_x, new_y);

	}
	return 0;
}

int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
{
	if( w != -1 ) {
		popup_w = w;
		if( h != -1 ) popup_h = h;
//printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);

		if( !is_popup ) {
			if( xscrollbar )
				xscrollbar->reposition_window(get_xscroll_x(),
					get_xscroll_y(),
					get_xscroll_width());
			if( yscrollbar )
				yscrollbar->reposition_window(get_yscroll_x(),
					get_yscroll_y(),
					get_yscroll_height());
		}
	}

	BC_WindowBase::reposition_window(x, y, w, h);
	draw_button(0);
	draw_items(flush);
	return 0;
}

int BC_ListBox::deactivate()
{
	hide_tooltip();
// printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
// __LINE__, this, gui, active);
	if( active ) {
		if( is_popup ) {
//printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
			if( gui ) {
				delete gui;  gui = 0;
				flush();
			}
			xscrollbar = 0;
			yscrollbar = 0;
			highlighted_item = -1;
			highlighted_ptr = 0;
//sleep(1);
		}
//printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
		active = 0;
		top_level->active_subwindow = 0;
	}

	return 0;
}

int BC_ListBox::activate(int take_focus)
{
	if( active ) return 0;
	active = 1;
	if( take_focus )
		set_active_subwindow(this);
	button_releases = 0;
	if( !is_popup || gui ) return 0;
	int wx = get_x(), wy = get_y() + get_h();
	if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w;
	Window xwin;
	int abs_x, abs_y;
	XTranslateCoordinates(top_level->display,
		parent_window->win, top_level->rootwin,
		wx, wy, &abs_x, &abs_y, &xwin);
	if( x <= 0 ) x = xS(2);
	if( y <= 0 ) y = yS(2);
	return activate(abs_x, abs_y);
}

int BC_ListBox::activate(int x, int y, int w, int h)
{
	if( !is_popup || gui ) return 0;
	active = 1;
	if( w != -1 ) popup_w = w;
	if( h != -1 ) popup_h = h;
	reset_query();
	if( y + popup_h > top_level->get_root_h(0) )
		y -= get_h() + popup_h;
	add_subwindow(gui = new BC_Popup(this,
		x, y, popup_w, popup_h, -1, 0, 0));
	draw_items(1);
	gui->show_window(1);
	return 0;
}

int BC_ListBox::is_active()
{
	return active;
}

int BC_ListBox::expander_active()
{
	for( int i=0; i<expanders.total; ++i ) {
		if( expanders.values[i]->value ) return 1;
	}
	return 0 ;
}

int BC_ListBox::keypress_event()
{
	if( !active ) return context_help_check_and_show();

//printf("BC_ListBox::keypress_event %d\n", __LINE__);

	int result = 0, redraw = 0;
	int view_items = last_in_view - first_in_view + 1;
	if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
	int new_item = -1, new_selection = -1;

	switch( top_level->get_keypress() ) {
	case ESC:
	case RETURN:
		top_level->deactivate();

// If user is manipulating popup with keyboard, don't pass on event.
		if( is_popup ) {
//printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
			if( top_level->get_keypress() == RETURN )
				handle_event();
//printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
			result = 1;
		}
		else
			result = 0;
		break;

	case UP:
		new_selection = new_item = select_previous(0);

//printf("BC_ListBox::keypress_event 1 %d\n", new_item);
		if( new_item >= 0 ) {
			center_selection(new_item);
			redraw = 1;
		}
		reset_query();
		result = 1;
		break;

	case DOWN:
		new_selection = new_item = select_next(0);

		if( new_item >= 0 ) {
			center_selection(new_item);
			redraw = 1;
		}
		reset_query();
		result = 1;
		break;

	case PGUP:
		new_selection = new_item = select_previous(view_items - 1);

		if( new_item >= 0 ) {
			center_selection(new_item);
			redraw = 1;
		}
		reset_query();
		result = 1;
		break;

	case PGDN:
		new_selection = new_item = select_next(view_items - 1);

		if( new_item >= 0 ) {
			center_selection(new_item);
			redraw = 1;
		}
		reset_query();
		result = 1;
		break;

	case LEFT:
		xposition -= xS(10);
		redraw = 1;
		result = 1;
		break;

	case RIGHT:
		xposition += xS(10);
		redraw = 1;
		result = 1;
		break;

	case DELETE:
	case HOME:
	case END:
		result = 0;
		break;

	default:
		if( show_query && !ctrl_down() ) {
			int query_len = strlen(query);
			if( query_len < (int)sizeof(query)-1 &&
			    top_level->get_keypress() > 30 &&
			    top_level->get_keypress() < 127 ) {
				query[query_len++] = top_level->get_keypress();
				query[query_len] = 0;
				new_selection = query_list();
			}
			else
			if( top_level->get_keypress() == BACKSPACE ) {
				if( query_len > 0 ) query[--query_len] = 0;
				new_selection = query_list();
			}
			if( query_len > 0 )
				show_tooltip(query);
			else
				hide_tooltip();
			redraw = 1;
			result = 1;
		}
		break;
	}

	if( redraw ) {
		clamp_positions();
		draw_items(0);
		update_scrollbars(1);
	}

//printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
	if( new_selection >= 0 && !is_suggestions ) {
//printf("BC_ListBox::keypress_event %d\n", __LINE__);
		selection_changed();
//printf("BC_ListBox::keypress_event %d\n", __LINE__);
	}

	if( !result )
		result = context_help_check_and_show();

	return result;
}


BC_Pixmap* BC_ListBox::get_bg_surface()
{
	return bg_surface;
}


void BC_ListBox::draw_background()
{
	if( !bg_draw ) return;
	bg_draw = 0;
// White background pixmap
	set_color(top_level->get_resources()->listbox_inactive);
	draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);

// Optional heroine pixmap
	if( bg_pixmap )
		bg_surface->draw_pixmap(bg_pixmap,
			bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
			0);
}

void BC_ListBox::clear_listbox(int x, int y, int w, int h)
{
	gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
}

void BC_ListBox::update_format(int display_format, int redraw)
{
	this->display_format = display_format;
	packed_icons = display_format == LISTBOX_ICONS_PACKED ? 1 : 0;
	xposition = 0;  yposition = 0;
	if( redraw && gui ) draw_items(1, 1);
}

int BC_ListBox::get_format()
{
	return display_format;
}



int BC_ListBox::draw_items(int flush, int draw_bg)
{
	if( gui ) {
		BC_Resources *resources = get_resources();

//dump(data, columns);

// Calculate items width
		calculate_item_coords();
// Create and destroy scrollbars as needed
		get_scrollbars();

		if( bg_draw ) this->bg_draw = 1;
		draw_background();

		first_in_view = -1;
		last_in_view = 0;
// Icon display
		if( display_format == LISTBOX_ICONS ||
		    display_format == LISTBOX_ICONS_PACKED ) {
			clear_listbox(2, 2 + title_h, view_w, view_h);

			set_font(MEDIUMFONT);
			for( int i=0; i<data[master_column].size(); ++i ) {
				BC_ListBoxItem *item = data[master_column].get(i);
				if( get_item_x(item) >= -get_item_w(item) &&
				    get_item_x(item) < view_w &&
				    get_item_y(item) >= -get_item_h(item) + title_h &&
				    get_item_y(item) < view_h + title_h ) {
					item->set_in_view(1);
					if( first_in_view < 0 ) first_in_view = i;
					last_in_view = i;
					int item_color = get_item_highlight(data, 0, i);
					int icon_x, icon_y, icon_w, icon_h;
					int text_x, text_y, text_w, text_h;

// Draw highlights
					get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
					get_text_mask(item, text_x, text_y, text_w, text_h);

					if( item_color != resources->listbox_inactive ) {
						gui->set_color(BLACK);
						gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
						gui->set_color(item_color);
						gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
						gui->set_color(BLACK);
						gui->draw_rectangle(text_x, text_y, text_w, text_h);
						gui->set_color(item_color);
						gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);

						if( icon_position == ICON_LEFT )
							gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
						else
						if( icon_position == ICON_TOP )
							gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
						if( text_x + text_w < icon_x + icon_w ) {
							gui->set_color(BLACK);
							gui->draw_line(text_x + text_w, icon_y + icon_h,
								icon_x + icon_w, icon_y + icon_h);
						}
					}
// Draw icons
					gui->set_color(get_item_color(data, 0, i));
					VFrame *vicon = item->get_vicon_frame();
					if( vicon )
						gui->pixmap->draw_vframe(vicon, icon_x, icon_y);
					else if( item->icon )
						gui->pixmap->draw_pixmap(item->icon, icon_x, icon_y);
					char *item_text = display_format == LISTBOX_ICONS_PACKED ?
						get_truncated_text(MEDIUMFONT, item->text, text_w) :
						cstrdup(item->text);
					gui->draw_text(text_x, text_y + get_baseline(item), item_text);
					delete [] item_text;
				}
				else
					item->set_in_view(0);
			}
		}
// Text display
		else {
// Draw one column at a time so text overruns don't go into the next column
// clear column backgrounds
			int current_toggle = 0;
			for( int j=0; j<columns; ++j ) {
				clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
					LISTBOX_BORDER + title_h,
					get_column_width(j, 1),
					view_h);
// Draw rows in the column recursively
				draw_text_recursive(data, j, 0, &current_toggle);
			}

// Delete excess expanders
			while( expanders.total > current_toggle ) {
				expanders.remove_object();
			}
		}

// draw user images if available
		draw_images();
// Draw titles on top of rows for superposition effect
		draw_titles(0);

// Clear garbage from bottom right corner
		if( xscrollbar && yscrollbar && is_popup ) {
			gui->draw_top_background(parent_window,
				popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
				popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
				get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
				get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
		}

// Draw borders
		draw_border(0);


		if( current_operation == SELECT_RECT )
			draw_rectangle(0);

		gui->flash(flush);
	}

	return 0;
}


void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
		int column, int indent, int *current_toggle)
{
	if( !data ) return;
	BC_Resources *resources = get_resources();

	set_font(MEDIUMFONT);
	int subindent = 0;

// Search for a branch and make room for toggle if there is one
	if( column == 0 ) {
		for( int i=0; i<data[column].size(); ++i ) {
			if( data[column].get(i)->get_sublist() ) {
				subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
				break;
			}
		}
	}

	row_height = row_ascent = row_descent = 0;
	for( int i=0; i<data[column].total; ++i ) {
		BC_ListBoxItem *item = data[column].values[i];
		int ht = get_text_h(item);
		if( ht > row_height ) row_height = ht;
		int bl = get_baseline(item);
		if( bl > row_ascent ) row_ascent = bl;
		int dt = ht - bl;
		if( dt > row_descent ) row_descent = dt;
	}

	for( int i=0; i<data[column].size(); ++i ) {
// Draw a row
		BC_ListBoxItem *item = data[column].values[i];
		BC_ListBoxItem *first_item = data[master_column].values[i];

		if( get_item_y(item) >= -get_item_h(item) + title_h &&
		    get_item_y(item) < view_h + title_h ) {
			int row_color = get_item_highlight(data, 0, i);
			int x, y, w, h, column_width;

			get_text_mask(item, x, y, w, h);
			column_width = get_column_width(column, 1);
			if( x + column_width > view_w + LISTBOX_BORDER * 2 )
				column_width = view_w + LISTBOX_BORDER * 2 - x;

			if( row_color != resources->listbox_inactive ) {
				gui->set_color(row_color);
				gui->draw_box(x, y, column_width, h);
				gui->set_color(BLACK);
				int xx = x + column_width-1;
				gui->draw_line(x, y, xx, y);
				int hh = h;
				if( display_format == LISTBOX_ICON_LIST ) {
					int ih = get_icon_h(item);
					if( ih > hh ) hh = ih;
				}
				int yy = y + hh-1;
				gui->draw_line(x, yy, xx, yy);
			}

			gui->set_color(get_item_color(data, column, i));


			if( column == 0 && display_format == LISTBOX_ICON_LIST ) {
				int ix = get_icon_x(item), iy = get_icon_y(item);
				VFrame *vicon = item->get_vicon_frame();
				if( vicon ) {
					gui->pixmap->draw_vframe(vicon, ix, iy);
					x += vicon->get_w() + ICON_MARGIN;
				}
				else if( item->icon ) {
					gui->pixmap->draw_pixmap(item->icon, ix, iy);
					x += item->icon->get_w() + ICON_MARGIN;
				}
			}


// Indent only applies to first column
			gui->draw_text(
				x + LISTBOX_BORDER + LISTBOX_MARGIN +
					(column == 0 ? indent + subindent : 0),
				y + get_baseline(item), item->text);
			item->set_in_view(1);
			if( !indent ) {
				if( first_in_view < 0 ) first_in_view = i;
				last_in_view = i;
			}

// Update expander
			if( column == 0 && item->get_sublist() && item->get_columns() ) {
// Create new expander
				if( *current_toggle >= expanders.total ) {
					BC_ListBoxToggle *toggle =
						new BC_ListBoxToggle(this, item,
							x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y);
					toggle->draw(0);
					expanders.append(toggle);
				}
// Reposition existing expander
				else {
					BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
//printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
					toggle->update(item,
						x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y, 0);
				}
				(*current_toggle)++;
			}



		}
		else
			item->set_in_view(0);

// Descend into sublist
		if( first_item->sublist_active() ) {
			draw_text_recursive(first_item->get_sublist(),
				column,
				indent + LISTBOX_INDENT,
				current_toggle);
		}
	}
}

int BC_ListBox::draw_border(int flash)
{
	BC_Resources *resources = top_level->get_resources();
	gui->draw_3d_border(0, 0,
		view_w + LISTBOX_BORDER * 2, view_h + title_h + LISTBOX_BORDER * 2,
		resources->listbox_border1,
		list_highlighted ?
			resources->listbox_border2_hi : resources->listbox_border2,
		list_highlighted ?
			resources->listbox_border3_hi : resources->listbox_border3,
		resources->listbox_border4);

	if( flash ) {
		gui->flash();
		gui->flush();
	}
	return 0;
}

void BC_ListBox::draw_title(int number)
{
// Column title background
	int image_number = 0;
	if( number == highlighted_title ) {
		image_number = 1;
		if( current_operation == COLUMN_DN )
			image_number = 2;
	}

	gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
		LISTBOX_BORDER,
		get_column_width(number, 1) + get_resources()->listbox_title_overlap,
		column_bg[image_number]);

	int title_x = -xposition + get_column_offset(number) + 
		LISTBOX_MARGIN + LISTBOX_BORDER;
	title_x += get_resources()->listbox_title_margin;

	gui->set_color(get_resources()->listbox_title_color);
	gui->draw_text(title_x, 
		LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT), 
		column_titles[number]);

// Column sort order
	if( number == sort_column ) {
		BC_Pixmap *src = sort_order == SORT_ASCENDING ?
			column_sort_dn : column_sort_up;

//		int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
//		int column_width = get_column_width(number, 1);
// 		int toggle_x = column_offset + column_width - LISTBOX_BORDER;
// 		if( toggle_x > items_w ) toggle_x = items_w;
// 		toggle_x -= 5 + src->get_w();

		int x = title_x + 
			get_text_width(MEDIUMFONT, column_titles[number]) +
			LISTBOX_MARGIN;
		
		gui->draw_pixmap(src,
			x,
			title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
	}
}

int BC_ListBox::draw_titles(int flash)
{
	if( column_titles &&
	    (display_format == LISTBOX_TEXT ||
	     display_format == LISTBOX_ICON_LIST) ) {
//printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
		for( int i=0; i<columns; ++i ) {
			if( i != highlighted_title )
				draw_title(i);
		}

		if( highlighted_title >= 0 ) draw_title(highlighted_title);
		draw_border(0);
	}

	if( flash ) {
		gui->flash();
	}
	return 0;
}

void BC_ListBox::draw_toggles(int flash)
{
	for( int i=0; i<expanders.total; ++i )
		expanders.values[i]->draw(0);

//printf("BC_ListBox::draw_toggles 1 %d\n", flash);
	if( flash && expanders.total ) {
		gui->flash();
	}
}

int BC_ListBox::draw_rectangle(int flash)
{
	int x1 = MIN(rect_x1, rect_x2);
	int x2 = MAX(rect_x1, rect_x2);
	int y1 = MIN(rect_y1, rect_y2);
	int y2 = MAX(rect_y1, rect_y2);

	if( x1 == x2 || y1 == y2 ) return 0;

	gui->set_inverse();
	gui->set_color(WHITE);
	gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
	gui->set_opaque();


	if( flash ) {
		gui->flash();
	}
	return 0;
}

void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data, int columns,
		int indent, int master_column)
{
	if( !indent ) {
		printf("BC_ListBox::dump 1\n");
	}

	for( int i=0; i<data[master_column].total; ++i ) {
		for( int k=0; k<indent; ++k )
			printf(" ");
		for( int j=0; j<columns; ++j ) {
			BC_ListBoxItem *item = data[j].values[i];
			printf("%d,%d,%d=%s ",
				item->get_text_x(), item->get_text_y(),
				item->autoplace_text, item->get_text());
		}
		printf("\n");

		if( data[master_column].values[i]->get_sublist() ) {
			dump(data[master_column].values[i]->get_sublist(),
				data[master_column].values[i]->get_columns(),
				indent + 4,
				master_column);
		}
	}
}

