
--proc/controls/listbox: standard listbox control
--Written by Cosmin Apreutesei. Public Domain.

setfenv(1, require'winapi')
require'winapi.window'
require'winapi.comctl'

--creation

WC_LISTBOX             = 'LISTBOX'

LBS_NOTIFY             = 0x0001
LBS_SORT               = 0x0002
LBS_NOREDRAW           = 0x0004
LBS_MULTIPLESEL        = 0x0008
LBS_OWNERDRAWFIXED     = 0x0010
LBS_OWNERDRAWVARIABLE  = 0x0020
LBS_HASSTRINGS         = 0x0040
LBS_USETABSTOPS        = 0x0080
LBS_NOINTEGRALHEIGHT   = 0x0100
LBS_MULTICOLUMN        = 0x0200
LBS_WANTKEYBOARDINPUT  = 0x0400
LBS_EXTENDEDSEL        = 0x0800
LBS_DISABLENOSCROLL    = 0x1000
LBS_NODATA             = 0x2000
LBS_NOSEL              = 0x4000
LBS_COMBOBOX           = 0x8000
LBS_STANDARD           = bit.bor(LBS_NOTIFY, LBS_SORT, WS_VSCROLL, WS_BORDER)

--commands

update(WM_NAMES, constants{
	LB_ADDSTRING             = 0x0180,
	LB_INSERTSTRING          = 0x0181,
	LB_DELETESTRING          = 0x0182,
	LB_SELITEMRANGEEX        = 0x0183,
	LB_RESETCONTENT          = 0x0184,
	LB_SETSEL                = 0x0185,
	LB_SETCURSEL             = 0x0186,
	LB_GETSEL                = 0x0187,
	LB_GETCURSEL             = 0x0188,
	LB_GETTEXT               = 0x0189,
	LB_GETTEXTLEN            = 0x018A,
	LB_GETCOUNT              = 0x018B,
	LB_DIR                   = 0x018D,
	LB_GETTOPINDEX           = 0x018E,
	LB_FINDSTRING            = 0x018F,
	LB_GETSELCOUNT           = 0x0190,
	LB_GETSELITEMS           = 0x0191,
	LB_SETTABSTOPS           = 0x0192,
	LB_GETHORIZONTALEXTENT   = 0x0193,
	LB_SETHORIZONTALEXTENT   = 0x0194,
	LB_SETCOLUMNWIDTH        = 0x0195,
	LB_ADDFILE               = 0x0196,
	LB_SETTOPINDEX           = 0x0197,
	LB_GETITEMRECT           = 0x0198,
	LB_GETITEMDATA           = 0x0199,
	LB_SETITEMDATA           = 0x019A,
	LB_SELITEMRANGE          = 0x019B,
	LB_SETANCHORINDEX        = 0x019C,
	LB_GETANCHORINDEX        = 0x019D,
	LB_SETCARETINDEX         = 0x019E,
	LB_GETCARETINDEX         = 0x019F,
	LB_SETITEMHEIGHT         = 0x01A0,
	LB_GETITEMHEIGHT         = 0x01A1,
	LB_FINDSTRINGEXACT       = 0x01A2,
	LB_SETLOCALE             = 0x01A5,
	LB_GETLOCALE             = 0x01A6,
	LB_SETCOUNT              = 0x01A7,
	LB_INITSTORAGE           = 0x01A8,
	LB_ITEMFROMPOINT         = 0x01A9,
	LB_MULTIPLEADDSTRING     = 0x01B1,
	LB_GETLISTBOXINFO        = 0x01B2,
})

function ListBox_AddString(hwnd, s) --returns index
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_ADDSTRING, 0, wcs(s))))
end

function ListBox_InsertString(hwnd, i, s) --returns index
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_INSERTSTRING, countfrom0(i), wcs(s))))
end

function ListBox_DeleteString(hwnd, i) --returns count
	return checkpoz(SNDMSG(hwnd, LB_DELETESTRING, countfrom0(i)))
end

function ListBox_GetCount(hwnd)
	return SNDMSG(hwnd, LB_GETCOUNT)
end

function ListBox_ResetContent(hwnd)
	checknz(SNDMSG(hwnd, LB_RESETCONTENT))
end

function ListBox_GetString(hwnd, i, buf)
	local ws = WCS(buf or checkpoz(SNDMSG(hwnd, LB_GETTEXTLEN, countfrom0(i))))
	checkpoz(SNDMSG(hwnd, LB_GETTEXT, countfrom0(i), ws))
	return buf or mbs(ws)
end

function ListBox_GetItemData(hwnd, i)
	return SNDMSG_PTR(hwnd, LB_GETITEMDATA, countfrom0(i))
end

function ListBox_SetItemData(hwnd, i, data)
	checkpoz(SNDMSG(hwnd, LB_SETITEMDATA, countfrom0(i), data))
end

function ListBox_FindString(hwnd, s, indexStart) --returns index
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_FINDSTRING, countfrom0(indexStart or -1), wcs(s))))
end

function ListBox_FindItemData(hwnd, data, indexStart) --returns index
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_FINDSTRING, countfrom0(indexStart or -1), data)))
end

function ListBox_FindStringExact(hwnd, s, istart) --istart is optional
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_FINDSTRINGEXACT, countfrom0(istart), wcs(s))))
end

function ListBox_SetSel(hwnd, yes, i)
	checkpoz(SNDMSG(hwnd, LB_SETSEL, yes, countfrom0(i)))
end

function ListBox_GetSel(hwnd, i)
	return checkpoz(SNDMSG(hwnd, LB_GETSEL, countfrom0(i))) ~= 0
end

function ListBox_SelItemRange(hwnd, fSelect, first, last)
   checkpoz(SNDMSG(hwnd, LB_SELITEMRANGE, fSelect, MAKELPARAM(countfrom0(first), countfrom0(last))))
end

function ListBox_GetCurSel(hwnd)
	return countfrom1(SNDMSG(hwnd, LB_GETCURSEL))
end

function ListBox_SetCurSel(hwnd, i) --a nil i means no selection
	local check = i == nil and pass or checkpoz
	check(SNDMSG(hwnd, LB_SETCURSEL, countfrom0(i)))
end

function ListBox_GetSelCount(hwnd)
	return checkpoz(SNDMSG(hwnd, LB_GETSELCOUNT))
end

function ListBox_GetSelItems(hwnd)
	local n = GetSelCount(hwnd)
	local items = ffi.new('int[?]', n)
	n = checkpoz(SNDMSG(hwnd, LB_GETSELITEMS, n, items))
	--TODO: should we return the zero-based cdata array and its length instead?
	local t={}
	for i=1,n do
		t[i] = items[i-1]+1
	end
	return t
end

function ListBox_GetTopIndex(hwnd)
	return countfrom1(checkpoz(SNDMSG(hwnd, LB_GETTOPINDEX)))
end

function ListBox_SetTopIndex(hwnd, indexTop)
	checkpoz(SNDMSG(hwnd, LB_SETTOPINDEX, countfrom0(indexTop)))
end

function ListBox_SetColumnWidth(hwnd, cxColumn)
	SNDMSG(hwnd, LB_SETCOLUMNWIDTH, cxColumn)
end

function ListBox_SetTabStops(hwnd, tabs)
	local tabs, n = array('int', tabs)
	checknz(SNDMSG(hwnd, LB_SETTABSTOPS, n, tabs))
end

function ListBox_GetHorizontalExtent(hwnd)
	return SNDMSG(hwnd, LB_GETHORIZONTALEXTENT)
end

function ListBox_SetHorizontalExtent(hwnd, cxExtent)
	SNDMSG(hwnd, LB_SETHORIZONTALEXTENT, cxExtent)
end

function ListBox_GetItemRect(hwnd, i, rect)
	rect = RECT(rect)
	checkpoz(SNDMSG(hwnd, LB_GETITEMRECT, countfrom0(i), ffi.cast('RECT*', rect)))
	return rect
end

function ListBox_SetCaretIndex(hwnd, i)
	checkz(SNDMSG(hwnd, LB_SETCARETINDEX, countfrom0(i)))
end

function ListBox_GetCaretIndex(hwnd)
	return checkpoz(SNDMSG(hwnd, LB_GETCARETINDEX))+1
end

function ListBox_SetItemHeight(hwnd, i, h)
   SNDMSG(hwnd, LB_SETITEMHEIGHT, countfrom0(i), MAKELPARAM(h, 0))
end

function ListBox_GetItemHeight(hwnd, i)
	return checkpoz(SNDMSG(hwnd, LB_GETITEMHEIGHT, countfrom0(i)))
end

function ListBox_Dir(hwnd, attrs, lpszFileSpec)
	return checkpoz(SNDMSG(hwnd, LB_DIR, attrs, ffi.cast('LPCTSTR', wcs(lpszFileSpec))))
end

--notifications

LBN_ERRSPACE         = -2
LBN_SELCHANGE        = 1
LBN_DBLCLK           = 2
LBN_SELCANCEL        = 3
LBN_SETFOCUS         = 4
LBN_KILLFOCUS        = 5

