#include "StdAfx.h"
#include "skinnablelistbox.h"


SkinnableListBox::SkinnableListBox(void)
{
	type = ListBoxType;
	ShowingDataEntry = 0;
	ShowingEmptyItem = 0;
	selectedItem = NULL;
	InitializeCriticalSection(&cs);
}

SkinnableListBox::~SkinnableListBox(void)
{
	DeleteCriticalSection(&cs);
}

int SkinnableListBox::Create(HWND parent)
{
	SkinnableWindow::Create(parent);
	scrollbar.Hide();
	scrollbar.SetLocation(size.Width-scrollbar.size.Width,0);
	scrollbar.SetSize(scrollbar.size.Width,size.Height);
	scrollbar.Create(hWnd);
	
	itemwindow.Set(*this);
	itemwindow.SetLocation(0,0);
	itemwindow.SetSize(size.Width,0);
	itemwindow.Create(hWnd);

	// this defines the list box's maximum height
	maxHeight = size.Height;

	// list boxes have no border
	selected.borderwidth = 0;
	normal.borderwidth = 0;
	mouseover.borderwidth = 0;
	pressed.borderwidth = 0;

	return 1;
}

void SkinnableListBox::FitToContents()
{
	// conditionally shrink the list's height to fit the items
	if(itemwindow.size.Height<maxHeight)
		SetSize(size.Width,itemwindow.size.Height);
	else
		SetSize(size.Width,maxHeight);

	// hide the scroll bar if needed
	if(itemwindow.size.Height<maxHeight)
		HideScrollBar();
	else
		ShowScrollBar();
	
	// set the scroll bar parameters
	scrollbar.SetPage(size.Height/(float)itemwindow.size.Height);	
	int n = items.size()+ShowingDataEntry;
	if(n>0)
		scrollbar.SetLine((itemwindow.size.Height/n)/(float)itemwindow.size.Height);
}

ListItem * SkinnableListBox::CreateItem(const TCHAR * text, int key)
{
	ListItem * item = new ListItem;
	item->title = text;
	item->key = key;
	return item;
}

void SkinnableListBox::AddItem(ListItem * item)
{
	if(ShowingEmptyItem && items.size()==0){
		EmptyItem.Hide();
		itemwindow.SetSize(itemwindow.size.Width,0);
	}

	int y;
	if(items.size()==0){
		y = 0;
	} else {
		ListItem * last = items.back();
		y = last->location.Y+last->size.Height;
		last->next = item;
		item->prev = last;
	}
	items.push_back(item);

	item->SetLocation(0,y);

	if(ShowingDataEntry){
		DataEntry.SetLocation(0,item->location.Y+item->size.Height);
	}
	item->SetSize(size.Width,item->size.Height);
	itemwindow.SetSize(itemwindow.size.Width,itemwindow.size.Height+item->size.Height);
	item->Create(itemwindow.hWnd);
	item->SetMessageSink(hWnd);

	FitToContents();

	SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
}

// return 0 if item not found, 1 otherwise
int SkinnableListBox::DeleteItem(ListItem * item)
{
	if(selectedItem==item) selectedItem = NULL;
	lipos i;
	int result = 0;
	EnterCriticalSection(&cs);
	for(i=items.begin();i!=items.end();i++){
		if(item==*i){
			if(item->prev){
				item->prev->next = item->next;
			}
			if(item->next){
				item->next->prev = item->prev;
			}
			delete *i;
			items.erase(i);
			result = 1;
			break;
		}
	}
	// repack the items
	int y = 0;
	for(i=items.begin();i!=items.end();i++){
		item = *i;
		item->SetLocation(item->location.X,y);
		y += item->size.Height;
	}
	LeaveCriticalSection(&cs);
	if(ShowingDataEntry){
		DataEntry.SetLocation(DataEntry.location.X,y);
		y += DataEntry.size.Height;
	} else if(items.size()==0 && ShowingEmptyItem) {
		EmptyItem.Show();
		y = EmptyItem.size.Height;
	} 

	itemwindow.SetSize(itemwindow.size.Width,y);

	FitToContents();

	SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);

	return result;
}

void SkinnableListBox::DeleteAllItems()
{
	EnterCriticalSection(&cs);
	lipos i;
	for(i=items.begin();i!=items.end();i++){
		delete *i;
	}
	items.clear();
	LeaveCriticalSection(&cs);
	selectedItem = NULL;
	if(ShowingDataEntry)
		itemwindow.SetSize(itemwindow.size.Width,DataEntry.size.Height);
	else if(ShowingEmptyItem) {
		itemwindow.SetSize(itemwindow.size.Width,EmptyItem.size.Height);
		EmptyItem.Show();
	} else
		itemwindow.SetSize(itemwindow.size.Width,0);

	FitToContents();

	SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
}

// gets the n_th item
ListItem * SkinnableListBox::GetItemByIndex(int n)
{
	EnterCriticalSection(&cs);
	lipos i;
	int j;
	for(i=items.begin(),j=0;i!=items.end();i++,j++){
		if(j==n){
			LeaveCriticalSection(&cs);
			return *i;
		}
	}
	LeaveCriticalSection(&cs);
	return NULL;
}

ListItem * SkinnableListBox::GetItemByName(const TCHAR * name)
{
	EnterCriticalSection(&cs);
	lipos i;
	for(i=items.begin();i!=items.end();i++){
		if((*i)->title==name){
			LeaveCriticalSection(&cs);
			return *i;
		}
	}
	LeaveCriticalSection(&cs);
	return NULL;
}

ListItem * SkinnableListBox::GetItemByKey(int key)
{
	EnterCriticalSection(&cs);
	lipos i;
	for(i=items.begin();i!=items.end();i++){
		if((*i)->key==key){
			LeaveCriticalSection(&cs);
			return *i;
		}
	}
	LeaveCriticalSection(&cs);
	return NULL;
}

void SkinnableListBox::ShowDataEntry(const TCHAR * text, Appearance& x)
{
	DataEntry.Set(x);
	DataEntry.title = text;
	ShowingDataEntry = 1;

	int y;
	if(items.size()==0){
		y = 0;
	} else {
		ListItem * last = items.back();
		y = last->location.Y+last->size.Height;
	}
	DataEntry.SetLocation(0,y);
	DataEntry.SetSize(size.Width,DataEntry.size.Height);
	itemwindow.SetSize(itemwindow.size.Width,itemwindow.size.Height+DataEntry.size.Height);
	DataEntry.Create(itemwindow.hWnd);
	DataEntry.SetMessageSink(hWnd);
	inplace.Hide();
	inplace.Create(DataEntry.hWnd);

	FitToContents();

	SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
}

void SkinnableListBox::HideDataEntry()
{
	DataEntry.Hide();
	ShowingDataEntry = 0;
	itemwindow.SetSize(itemwindow.size.Width,itemwindow.size.Height-DataEntry.size.Height);

	FitToContents();

	SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
}


void SkinnableListBox::ShowEmptyItem(const TCHAR * text, Appearance& x)
{
	EmptyItem.Set(x);
	EmptyItem.title = text;
	ShowingEmptyItem = 1;

	DataEntry.SetLocation(0,0);
	DataEntry.SetSize(size.Width,DataEntry.size.Height);
	EmptyItem.AdjustHeightToText();
	EmptyItem.Create(itemwindow.hWnd);

	if(items.size()==0){
		EmptyItem.Show();
		itemwindow.SetSize(itemwindow.size.Width,EmptyItem.size.Height);
		FitToContents();
		SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
	} 

}

void SkinnableListBox::HideEmptyItem()
{
	EmptyItem.Hide();
	ShowingEmptyItem = 0;

	if(items.size()==0){
		itemwindow.SetSize(itemwindow.size.Width,0);
		FitToContents();
		SafeSendMessage(hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
	} 
}

void SkinnableListBox::ShowScrollBar()
{
	// go through all the list items and shorten them
	// so the scroll bar can fit
	//lipos i;
	//for(i=items.begin();i!=items.end();i++){
	//	ListItem * item = *i;
	//	item->SetSize(size.Width-scrollbar.size.Width,item->size.Height);
	//}
	itemwindow.SetSize(size.Width-scrollbar.size.Width,itemwindow.size.Height);
	scrollbar.Show();
}

void SkinnableListBox::HideScrollBar()
{
	// go through all the list items and lengthen them
	// to fill the space the scrollbar will leave
	scrollbar.Hide();
	//lipos i;
	//for(i=items.begin();i!=items.end();i++){
	//	ListItem * item = *i;
	//	item->SetSize(size.Width+scrollbar.size.Width,item->size.Height);
	//}
	itemwindow.SetSize(size.Width+scrollbar.size.Width,itemwindow.size.Height);
}

void SkinnableListBox::OnScroll(HWND h)
{
	if(scrollbar.hWnd==h){
		if(itemwindow.size.Height>size.Height) {
			itemwindow.SetLocation(0,-scrollbar.GetPos()*(itemwindow.size.Height-size.Height));
		}
	}
}

void SkinnableListBox::OnButton(HWND h)
{
	if(DataEntry.hWnd==h){ // data entry start requested
		inplace.SetText(L"");
		inplace.Start();
	} else if(inplace.hWnd==h) { // inplace edit is finished
		inplace.Stop();
	} else if(GetType(h)==ListItemType) { 
		// first set it to be selected
		ListItem * item = (ListItem*)GetWindowLongPtr(h,GWLP_USERDATA);
		if(item){
			SetSelection(item);
			// send these on to the parent window and the sink
			SafeSendMessage(hWndParent,ID_LBSELECT,(WPARAM)hWnd,(LPARAM)item->hWnd);
			if(hWndSink)
				SafeSendMessage(hWndSink,ID_LBSELECT,(WPARAM)hWnd,(LPARAM)item->hWnd);
		}
	}
}

void SkinnableListBox::OnRightButton(HWND h)
{
	if(DataEntry.hWnd==h){ // data entry start requested
		inplace.SetText(L"");
		inplace.Start();
	} else if(inplace.hWnd==h) { // inplace edit is finished
		inplace.Stop();
	} else if(GetType(h)==ListItemType) { 
		// first set it to be selected
		ListItem * item = (ListItem*)GetWindowLongPtr(h,GWLP_USERDATA);
		if(item){
			SetSelection(item);
			// send these on to the parent window and the sink
			SafeSendMessage(hWndParent,ID_LBSELECT_RIGHT,(WPARAM)hWnd,(LPARAM)item->hWnd);
			if(hWndSink)
				SafeSendMessage(hWndSink,ID_LBSELECT_RIGHT,(WPARAM)hWnd,(LPARAM)item->hWnd);
		}
	}
}

void SkinnableListBox::OnDblButton(HWND h)
{
	if(GetType(h)==ListItemType){ // maybe it was a list item
		// send these on to the parent window and the sink
		SafeSendMessage(hWndParent,ID_LBSELECT_DBLCLK,(WPARAM)hWnd,(LPARAM)h);
		if(hWndSink)
			SafeSendMessage(hWndSink,ID_LBSELECT_DBLCLK,(WPARAM)hWnd,(LPARAM)h);
	}
}

void SkinnableListBox::OnPopupStart(WPARAM wParam, LPARAM lParam)
{
	SafeSendMessage(hWndParent,ID_POPUPSTART,wParam,(LPARAM)hWnd);
	if(hWndSink)
		SafeSendMessage(hWndSink,ID_POPUPSTART,wParam,(LPARAM)hWnd);	
}

void SkinnableListBox::OnPopupEnd(WPARAM wParam, LPARAM lParam)
{
	SafeSendMessage(hWndParent,ID_POPUPEND,wParam,(LPARAM)hWnd);
	if(hWndSink)
		SafeSendMessage(hWndSink,ID_POPUPEND,wParam,(LPARAM)hWnd);	
}

void SkinnableListBox::OnListDeleteItem(WPARAM wParam, LPARAM lParam)
{
	SafeSendMessage(hWndParent,ID_LIST_DELETE,(WPARAM)hWnd,lParam);
	if(hWndSink)
		SafeSendMessage(hWndSink,ID_LIST_DELETE,(WPARAM)hWnd,lParam);
}

void SkinnableListBox::SetSelection(int key)
{
	ListItem * item = GetItemByKey(key);
	if(item) SetSelection(item);
}

void SkinnableListBox::SetSelection(const TCHAR * name)
{
	ListItem * item = GetItemByName(name);
	if(item) SetSelection(item);
}

void SkinnableListBox::SetSelection(ListItem * item)
{
	if(selectedItem){
		selectedItem->SetState(AppearanceState::normal);
	}
	selectedItem = item;
	item->SetState(AppearanceState::selected);
}

void SkinnableListBox::ClearSelection()
{
	if(selectedItem){
		selectedItem->SetState(AppearanceState::normal);
	}
	selectedItem = NULL;
}

void SkinnableListBox::GetKeys(Vector<int>& keys)
{
	EnterCriticalSection(&cs);
	if(items.size()>0){
		keys.allocate(items.size());
		std::list<ListItem *>::iterator i;
		int j;
		for(i=items.begin(),j=0;i!=items.end();i++,j++){
			keys[j] = (*i)->key;
		}
	} else
		keys.deallocate();
	LeaveCriticalSection(&cs);
}