if not luad then
	local luad = dofile "patcher/skin/luad.lua"
	luad.setRootPath "patcher/skin"
	local malut = luad.require( "malut", "malut.lua" )
end

local unused = "unused\024\0\0\0\0\0"

local luad = assert(luad)
local malut = luad.require "malut"
local DT = malut.DT
local baseof = malut.baseof
local LuaSkinApi = LuaSkinApi

local printTable = malut.printTable
local type = type
local print = print
local errorPrint = print
local pairs = pairs
local tostring = tostring
local tonumber = tonumber
local error = error
local mathfloor = math.floor
local mathceil = math.ceil
local mathlog = math.log
local mathexp = math.exp
local debug = debug
local assert = assert
local stringchar = string.char
local tableremove = table.remove
local tableinsert = table.insert

module "UI"
local here = _M
if luad then
	luad.register ("UI", here)
end

--
-- Utility
--
local function isHit (item, x, y)
	local l = item.m_curX
	local t = item.m_curY
	local r = l + item.m_curWidth
	local b = t + item.m_curHeight
	return x>=l and x<r and y>=t and y<b
end

local function mergeRect (x1,y1,w1,h1, x2,y2,w2,h2)
	local l1, t1, r1, b1 = x1, y1, x1+w1, y1+h1
	local l2, t2, r2, b2 = x2, y2, x2+w2, y2+h2
	local l = l1 < l2 and l1 or l2
	local t = t1 < t2 and t1 or t2
	local r = r1 > r2 and r1 or r2
	local b = b1 > b2 and b1 or b2
	return l, t, r-l, b-t
end

local function clamp (v, vMin, vMax)
	return v < vMin and vMin or v > vMax and vMax or v
end

-- root -> children
-- bAll: perform on invisible objects
local function onTree(tree, funcName, bAll)
	if not bAll and not tree.m_visible then
		return
	end
	
	local flag
	local func = tree[funcName]
	if func then
		flag = func(tree)
	elseif type(funcName)=="function" then
		flag = funcName(tree)
	end
	
	if not flag then
		for i=1, #tree do
			onTree(tree[i], funcName, bAll)
		end
	end
end

-- children -> sub children
local function onTreeSub(tree, funcName, bAll)
	for i=1, #tree do
		onTree(tree[i], funcName, bAll)
	end
end

-- for draw()
local function drawTree (itemTree, bAll)
	if not bAll and not itemTree.m_visible then
		return
	end

	local cwindow = itemTree.m_window.m_cwindow
	cwindow:pushTransform(itemTree.m_absOffsetX, itemTree.m_absOffsetY, 
						itemTree.m_absScaleX, itemTree.m_absScaleY )
	cwindow:pushAlpha(itemTree.m_absAlpha)
	local flag = itemTree:draw()
	if not flag then
		for i=1, #itemTree do
			drawTree(itemTree[i], bAll)
		end
	end	
	cwindow:popAlpha()
	cwindow:popTransform()
end


-- scale -> offset
local function combineTrans (offsetX1, offsetY1, scaleX1, scaleY1,  offsetX2, offsetY2, scaleX2, scaleY2)
	return scaleX1 * offsetX2 + offsetX1, scaleY1 * offsetY2 + offsetY1, scaleX1 * scaleX2, scaleY1 * scaleY2
end

local function transformPos (item, x, y)
	return x * item.m_absScaleX + item.m_absOffsetX, y * item.m_absScaleY + item.m_absOffsetY
end

local function distransformPos (item, x, y)
	return (x - item.m_absOffsetX) / item.m_absScaleX, (y - item.m_absOffsetY) / item.m_absScaleY
end

local function findItemByName_ (item, name)
	if item.m_name == name then
		return item
	else
		for i=1, #item do
			local ret = findItemByName_(item[i], name)
			if ret then return ret end
		end
	end
	return nil
end

--
-- Position
--
-- input: see output of photoshop script
-- output: 
--[[
{
	item1Name=
	{
		name="winName", x=0, y=0, width=1, height=1, 
		imageName1 = {x, y, width, height, file},
		sub1Name={...}, 
		sub2Name={...}, 
	}, 
	item1Name={...}
}
--]]

local function convertItemPos (itemPosOri, imageRootPath)
	local item = {}
	if not itemPosOri.bGroup then
		errorPrint( "Item is not a group: "..itemPosOri.name )
		return nil
	end
	
	item.bItem = true
	item.name = itemPosOri.name
	
	local firstImage
	for i=1, #itemPosOri do
		image = itemPosOri[i]
		if not image.bGroup then
			firstImage = image
			break
		end
	end
	if firstImage then
		item.x = firstImage.x
		item.y = firstImage.y
		item.width = firstImage.width
		item.height = firstImage.height
	else
		item.x = 0
		item.y = 0
		item.width = 1
		item.height = 1
	end
	local itemX, itemY = item.x, item.y
	
	for i=1, #itemPosOri do
		local image = itemPosOri[i]
		local imageName = image.name
		if imageName then
			if not image.bGroup then
				item[imageName] = {file=imageRootPath..image.file, 
						x=image.x-itemX, y=image.y-itemY, width=image.width, height=image.height}
			else
				item[imageName] = convertItemPos(image, imageRootPath)
			end
		end
	end
	
	return item
end

function convertPosition (posOrigin, imageRootPath)
	local position = {}
	for i=1, #posOrigin do		-- find all window
		local winPosOri = posOrigin[i]
		
		if type(winPosOri) == "table" then
			local windowName = winPosOri.name
			if type(winPosOri.name)~="string" then
				errorPrint( "Invalid name for window at index: "..i )
			elseif position[windowName] then
				errorPrint( "Window name conflict: "..windowName )
			elseif winPosOri.bGroup then
				position[windowName] = convertItemPos(winPosOri, imageRootPath)
			end
		end
	end
	return position
end

local function applyPositionData (itemTree, itemPosData, biasX, biasY)
	if not itemTree or not itemPosData then
		return
	end
	
	-- apply position
	itemTree.m_x = itemPosData.x - biasX
	itemTree.m_y = itemPosData.y - biasY
	itemTree.m_width = itemPosData.width
	itemTree.m_height = itemPosData.height
	
	-- set image
	local images = itemTree.m_images
	if images == unused then
		images = {}
		itemTree.m_images = images
	end
	
	images.x = itemPosData.x
	images.y = itemPosData.y
	images.width = itemPosData.width
	images.height = itemPosData.height
	
	for name, image in pairs(itemPosData) do
		if type(image)=="table" and not image.bItem then
			images[name] = image
		end
	end
	
	if images.bg then
		itemTree.m_curImage = images.bg
	end
	
	-- apply to sub items
	for i = 1, #itemTree do
		local subItem = itemTree[i]
		local subPosData = itemPosData[subItem.m_name]
		if type(subPosData)=="table" and subPosData.bItem then
			applyPositionData(subItem, subPosData, biasX, biasY)
		end
	end
end

here.applyPositionData = applyPositionData

function applyPositionDataToWindowList (windowList, positionData)
	for winName, win in pairs(windowList) do
		local winPosData = positionData[winName]
		if type(winPosData)=="table" then
			applyPositionData(win:getItemGroup(), winPosData, winPosData.x, winPosData.y)
		end
		win:updateTransform()
	end
end

-- resource loaders
local function pictureLoader (file)
	local pic = LuaSkinApi.SkinPicture()
	pic:load(file)
	if pic:isOK() then
		return pic
	else
		errorPrint( "Fail to load picture "..file )
		return nil
	end
end

local function animationPictureLoader (file)
	local pic = LuaSkinApi.SkinAnimation()
	pic:load(file)
	if pic:isOK() then
		return pic
	else
		errorPrint( "Fail to load animation picture "..file )
		return nil
	end
end


--
-- Items
--

--local class Item
local Item
do
	local super = malut.Object
	local proto = super:clone() local Class = malut.baseof(proto)
	Item = proto
	
	proto.m_parent = unused
	proto.m_name = unused	-- required
	
	proto.m_visible = true
	proto.m_bTop = false
	
	-- for draw
	proto.m_x = 0
	proto.m_y = 0
	proto.m_width = 1
	proto.m_height = 1
	
	-- for transfrom
	proto.m_offsetX = 0
	proto.m_offsetY = 0
	proto.m_scaleX = 1
	proto.m_scaleY = 1
	
	-- for drawPicture, generated by updateTransform
	proto.m_absOffsetX = unused
	proto.m_absOffsetY = unused
	proto.m_absScaleX = unused
	proto.m_absScaleY = unused
	
	-- for hit test, generated by updateTransform
	proto.m_curX = unused
	proto.m_curY = unused
	proto.m_curWidth = unused
	proto.m_curHeight = unused
	
	proto.m_alpha = 1
	proto.m_absAlpha = 1
	
	proto.m_cursorStyle = LuaSkinApi.SkinWindow_CURSOR_NORMAL
	
	proto.m_imageLoader = pictureLoader
	proto:addTable("m_images", unused)		-- required
	proto:addTable("m_actions", unused)
	
	proto.m_window = unused		-- required, set by parent
	proto.m_cwindow = unused		-- required
	
	function Class:init (layout, window)
		self.m_window = window
		self.m_name = layout.name or unused
		self.m_x = layout.x or 0
		self.m_y = layout.y or 0
		self.m_width = layout.width or 1
		self.m_height = layout.height or 1
		
		return self
	end

	function Class.__call (proto, window)
		return function (layout)
			return proto:new():init(layout, window)
		end
	end
	
	-- if return true, window will not send onInit to children; otherwise, send
	function Class:onInit ()
		self.m_cwindow = self.m_window.m_cwindow
		
		-- load pictures
		local imageLoader = self.m_imageLoader
		local images = self.m_images
		if images ~= unused then
			for k, v in pairs(images) do
				if type(v)=="table" and v.file then
					v.pic = imageLoader(v.file)
					if not v.pic then
						images[k] = nil
					else		-- default size
						if image.width == nil then
							image.width = v.pic:getWidth()
						end
						if image.height == nil then
							image.height = v.pic:getHeight()
						end
					end
				end
			end
		end
	end
	
	-- move self and children
	function Class:moveTo (x, y)
		local dx = x - self.m_x
		local dy = y - self.m_y
		
		local function moveD (obj)
			obj.m_x = obj.m_x + dx
			obj.m_y = obj.m_y + dy
		end
		
		onTree(self, moveD, true)
		self:updateTransform()
	end
	
	function Class:setTransform (offsetX, offsetY, scaleX, scaleY)
		self.m_offsetX, self.m_offsetY, self.m_scaleX, self.m_scaleY = offsetX, offsetY, scaleX, scaleY
		self:updateTransform()
	end
	
	-- update self's position and update transform of children
	function Class:updateTransform ()
		local absOffsetX, absOffsetY, absScaleX, absScaleY
		local parent = self.m_parent
		if parent == unused then
			absOffsetX, absOffsetY, absScaleX, absScaleY = self.m_offsetX, self.m_offsetY, self.m_scaleX, self.m_scaleY
		else
			absOffsetX, absOffsetY, absScaleX, absScaleY = combineTrans(
				parent.m_absOffsetX, parent.m_absOffsetY, parent.m_absScaleX, parent.m_absScaleY,
				self.m_offsetX, self.m_offsetY, self.m_scaleX, self.m_scaleY)
		end
		
		self.m_curX = self.m_x * absScaleX + absOffsetX
		self.m_curY = self.m_y * absScaleY + absOffsetY
		self.m_curWidth = self.m_width * absScaleX
		self.m_curHeight = self.m_height * absScaleY
		self.m_absOffsetX, self.m_absOffsetY, self.m_absScaleX, self.m_absScaleY = absOffsetX, absOffsetY, absScaleX, absScaleY
		
		for i = 1, #self do
			self[i]:updateTransform()
		end
	end
	
	function Class:setAlpha (alpha)
		self.m_alpha = alpha
		self:updateAlpha()
	end
	
	function Class:updateAlpha (alpha)
		local absAlpha
		local parent = self.m_parent
		if parent == unused then
			absAlpha = self.m_alpha
		else
			absAlpha = parent.m_absAlpha * self.m_alpha
		end
		self.m_absAlpha = absAlpha
		
		for i = 1, #self do
			self[i]:updateAlpha()
		end
	end
	
	function Class:draw ()		-- does not know what to draw
	end
	
	function Class:show (bShow)
		local bShow = not not bShow
		if bShow ~= self.m_visible then
			self.m_visible = bShow
			self.m_window:setDirty()
		end
	end
	
	function Class:isShow ()
		return self.m_visible
	end
	
	function Class:addChild (child)
		assert(child.m_parent == unused)
		child.m_parent = self
		self[#self+1] = child
	end
	
	function Class:removeChild (child)
		for i=1, #self do
			if self[i] == child then
				tableremove(self, i)
				child.m_parent = unused
			end
		end
	end
	
	function Class:findChildByName (name)
		return findItemByName_(self, name)
	end
	
	function Class:addToGroup (group)
		group = group or self.m_window:getItemGroup()
		group:addChild(self)
		return self
	end
	
	-- default event handler, call handler of parent
	function Class:onLButtonDown (x, y)
		local parent = self.m_parent
		if parent ~= unused then
			parent:onLButtonDown(x, y)
		end
	end
	
	function Class:onLButtonUp (x, y)
		local parent = self.m_parent
		if parent ~= unused then
			parent:onLButtonUp(x, y)
		end
	end

	function Class:onMouseMove (x, y)
		local parent = self.m_parent
		if parent ~= unused then
			parent:onMouseMove(x, y)
		end
	end
	
	function Class:onMouseWheelUp (x, y)
		local parent = self.m_parent
		if parent ~= unused then
			parent:onMouseWheelUp(x, y)
		end
	end
	
	function Class:onMouseWheelDown (x, y)
		local parent = self.m_parent
		if parent ~= unused then
			parent:onMouseWheelDown(x, y)
		end
	end
		
	-- should not overload this function, use setAction instead
	function Class:onTimer (dtime)
		local actions = self.m_actions
		if actions == unused then
			return
		end
		
		for key, act in pairs(actions) do
			local ret = act(dtime)
			if ret == "end" then
				actions[key] = nil
			end
		end
	end
	
	function Class:setAction (key, act)
		assert(type(act)=="function" or act == nil)
		
		local actions = self.m_actions
		if actions == unused then
			actions = {}
			self.m_actions = actions
		end
		actions[key] = act
	end
	
	function Class:getAction (key)
		return self.m_actions[key]
	end
	
	-- transformTime: unit is second
	function Class:transformTo (localOffsetX, localOffsetY, scaleX, scaleY, transformTime, centerX, centerY, onFinish)
		centerX = centerX or self.m_width/2
		centerY = centerY or self.m_height/2
		
		local offsetX = localOffsetX - (scaleX-1)*(self.m_x + centerX)
		local offsetY = localOffsetY - (scaleY-1)*(self.m_y + centerY)
		
		if transformTime <= 0 then		-- set at once
			self:setTransform(offsetX, offsetY, scaleX, scaleY)
			if onFinish then
				onFinish()
			end
		end
		
		local offsetXSpeed = (offsetX - self.m_offsetX)/transformTime
		local offsetYSpeed = (offsetY - self.m_offsetY)/transformTime
		local scaleXSpeed = (scaleX - self.m_scaleX)/transformTime
		local scaleYSpeed = (scaleY - self.m_scaleY)/transformTime
		
		local function action_transformTo (dtime)
			local curOffsetX, curOffsetY, curScaleX, curScaleY = 
				self.m_offsetX, self.m_offsetY, self.m_scaleX, self.m_scaleY

			transformTime = transformTime - dtime/1000
			if transformTime > 0 then
				self:setTransform( curOffsetX + offsetXSpeed*dtime/1000, curOffsetY + offsetYSpeed*dtime/1000,
									curScaleX + scaleXSpeed*dtime/1000, curScaleY + scaleYSpeed*dtime/1000 )
				self.m_window:setDirty()
			else
				self:setTransform( offsetX, offsetY, scaleX, scaleY )
				self.m_window:setDirty()
				self:setAction("transformTo", nil)
				if onFinish then
					onFinish()
				end
			end
		end
		
		self:setAction("transformTo", action_transformTo)
	end
end

--local class Group
local Group
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	Group = proto
end

--class Figure
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	Figure = proto
	proto.m_curImage = unused		-- unused if none
	proto.m_imageFile = unused			-- unused if none

	function Class:init (layout, window)
		super.init(self, layout, window)
		if layout.bAnimation then
			self.m_imageLoader = animationPictureLoader
		end
		self.m_imageFile = layout.imageFile or unused
		return self
	end
	
	function Class:onInit ()
		super.onInit(self)
		
		local imageFile = self.m_imageFile
		if imageFile ~= unused then
			local image = {file = imageFile, x = 0, y = 0}
			image.pic = self.m_imageLoader(imageFile)
			if image.pic then	-- use picture original size as item size
				image.width = image.pic:getWidth()
				image.height = image.pic:getHeight()
				self.m_curImage = image
			end
		end
	end
	
	function Class:draw ()
		local curImage = self.m_curImage
		if curImage~=unused then
			local images = self.m_images
			local x = self.m_x
			local y = self.m_y
			self.m_cwindow:drawPicture( curImage.pic, 0, 0, curImage.width, curImage.height, 
				curImage.x + x, curImage.y + y )
		end
	end
end

--class Button
--images: bg, hover, press, disable
do
	local super = Figure
	local proto = super:clone() local Class = malut.baseof(proto)
	Button = proto
	
	proto.m_buttonState = "normal"		-- normal, hover, press, disable
	proto.m_disabled = false
	proto.m_hovered = false
	proto.m_clicked = false
		
	function Class:updateImage ()
		local images = self.m_images
		local curImage
		
		local buttonState = self.m_buttonState
		if buttonState == "normal" then
			curImage = images.bg
		elseif buttonState == "hover" then
			curImage = images.hover
		elseif buttonState == "press" then
			curImage = images.press or images.hover
		elseif buttonState == "disable" then
			curImage = images.disable
		end

		self.m_curImage = curImage or images.bg or unused
	end
	
	function Class:enable (flag)
		self.m_disabled = not flag
		self.m_buttonState = self.m_disabled and "disable" or "normal"
		self.m_window:setDirty()
	end
	
	function Class:isActive ()
		return self.m_buttonState ~= "disable"
	end
	
	function Class:onMouseOver ()
		local buttonState = self.m_buttonState
		if buttonState=="disable" or buttonState=="hover" then
			return
		end
		self.m_buttonState = "hover"
		self.m_window:setDirty()
		
		if self.onHover then
			self:onHover()
		end
	end
	function Class:onMouseOut ()
		local buttonState = self.m_buttonState
		if not (buttonState=="press" or buttonState=="hover") then
			return
		end
		self.m_buttonState = "normal"
		self.m_window:setDirty()
				
		if self.onDisHover then
			self:onDisHover()
		end
	end
	function Class:onLButtonDown ()
		local buttonState = self.m_buttonState
		if buttonState=="disable" then
			return
		end
		self.m_buttonState = "press"
		self.m_window:setDirty()
		
		if self.onPress then
			self:onPress()
		end
	end
	function Class:onLButtonUp ()
		local buttonState = self.m_buttonState
		if buttonState=="disable" then
			return
		end
		self.m_buttonState = "hover"
		self.m_window:setDirty()
		
		if self.onClick then
			self:onClick()
		end
	end
	
	function Class:draw ()
		self:updateImage()
		super.draw(self)
	end
end

--class AnimationButton
--images: bg, hover(a), press(a), click(a), dishover(a), disable. (*a means that it is an animation)
do
	local super = Figure
	local proto = super:clone() local Class = malut.baseof(proto)
	AnimationButton = proto
	
	proto.m_imageLoader = animationPictureLoader
	proto.m_buttonState = "normal"		-- normal, hover, press, click, dishover, disable
	
	proto.m_disabled = false
	proto.m_hovered = false
	proto.m_clicked = false
		
	function Class:init (...)
		super.init(self, ...)
		self:setAction("AnimationButton", function (dtime)
			local curImage = self.m_curImage
			if curImage ~= unused then
				local bMore, bNewFrame = curImage.pic:increaseTime(dtime)
				if bNewFrame then
					self.m_window:setDirty()
				end
			end
		end)
		return self
	end
	
	function Class:updateImage ()
		local lastImage = self.m_curImage
		
		local images = self.m_images
		local curImage
		
		local buttonState = self.m_buttonState
		local bFullMatch = true
		if buttonState == "normal" then
			curImage = images.bg
		elseif buttonState == "hover" then
			curImage = images.hover
		elseif buttonState == "press" then
			curImage = images.press
			if not curImage then
				bFullMatch = false
				curImage = images.hover
			end
		elseif buttonState == "click" then
			curImage = images.click
			if not curImage then
				bFullMatch = false
				curImage = images.hover
			end
		elseif buttonState == "dishover" then
			curImage = images.dishover
		elseif buttonState == "disable" then
			curImage = images.disable
		end

		if not curImage then
			bFullMatch = false
			curImage = images.bg or unused
		end
		self.m_curImage = curImage
		
		if curImage ~= lastImage and curImage ~= unused then
			curImage.pic:resetTime(not bFullMatch)
		end
	end
	
	function Class:enable (flag)
		self.m_disabled = not flag
		self.m_buttonState = "normal"
		self.m_window:setDirty()
	end
	
	function Class:isActive ()
		return self.m_buttonState ~= "disable"
	end
	
	function Class:onMouseOver ()
		local buttonState = self.m_buttonState
		local bMouseIn = buttonState=="hover" or buttonState=="press" or buttonState=="click"
		if buttonState=="disable" or bMouseIn then
			return
		end
		self.m_buttonState = "hover"
		self.m_window:setDirty()
		
		if self.onHover then
			self:onHover()
		end
	end
	function Class:onMouseOut ()
		local buttonState = self.m_buttonState
		local bMouseIn = buttonState=="hover" or buttonState=="press" or buttonState=="click"
		if not bMouseIn then
			return
		end
		self.m_buttonState = "dishover"
		self.m_window:setDirty()
				
		if self.onDisHover then
			self:onDisHover()
		end
	end
	function Class:onLButtonDown ()
		local buttonState = self.m_buttonState
		if buttonState=="disable" then
			return
		end
		self.m_buttonState = "press"
		self.m_window:setDirty()
		
		if self.onPress then
			self:onPress()
		end
	end
	function Class:onLButtonUp ()
		local buttonState = self.m_buttonState
		if buttonState=="disable" then
			return
		end
		self.m_buttonState = "click"
		self.m_window:setDirty()
		
		if self.onClick then
			self:onClick()
		end
	end
	
	function Class:draw ()
		self:updateImage()
		super.draw(self)
	end
end

--local class BasicProgressBar
local BasicProgressBar
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	BasicProgressBar = proto
	
	proto.m_progress = 0
	
	function Class:setProgress (progress, bForce)		-- 0~100
		realProgress = progress
		
		if realProgress < 0 then realProgress = 0 end
		if realProgress > 100 then realProgress = 100 end
		
		if bForce or self.m_progress ~= realProgress then
			self.m_progress = realProgress
			self.m_window:setDirty()
		end
	end
	
	function Class:getProgress ()
		return self.m_progress
	end	
end

--local class ProgressBar
local ProgressBar
do
	local super = BasicProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	ProgressBar = proto
	
	proto.m_progress = 0
	proto.m_bVertical = unused		--required, set by init
	
	-- return x, y, w, h (of src), newSrcX, newSrcY
	local function clip (picX, picY, picW, picH, clipX, clipY, clipW, clipH)
		local l, t, r, b = picX, picY, picX+picW, picY+picH
		if l < clipX then l = clipX end
		if t < clipY then t = clipY end
		if r > clipX+clipW then r = clipX+clipW end
		if b > clipY+clipH then b = clipY+clipH end
		return l, t, r-l, b-t, l-picX, t-picY
	end
	
	function Class:draw ()
		local progress = self.m_progress
		local bVertical = self.m_bVertical
		local unfillPic = self.m_images.unfill
		local fillPic = self.m_images.fill
		
		local images = self.m_images
		if images == unused then
			return
		end
		local itemX = self.m_x
		local itemY = self.m_y
		
		-- draw unfilled part
		if unfillPic ~= nil then
			local clipX, clipY, clipW, clipH
			if not bVertical then
				local u = mathfloor( self.m_width*progress/100 + 0.5 )	-- rightward
				clipX = self.m_x + u
				clipW = self.m_width - u
				clipY, clipH = self.m_y, self.m_height
			else
				local u = mathfloor( self.m_height*progress/100	+ 0.5 )	-- upward
				clipY = self.m_y
				clipH = self.oriHeight - u
				clipX, clipW = self.m_y, self.m_height
			end
			
			local x, y, w, h, newSrcX, newSrcY = 
				clip( unfillPic.x+itemX, unfillPic.y+itemY, unfillPic.width, unfillPic.height,  clipX, clipY, clipW, clipH )
			self.m_cwindow:drawPicture( unfillPic.pic, newSrcX, newSrcY, w, h, x, y )
		end
		
		-- draw filled part
		if fillPic ~= nil then
			local clipX, clipY, clipW, clipH
			if not bVertical then
				local u = mathfloor( self.m_width*progress/100 + 0.5 )	-- rightward
				clipX = self.m_x
				clipW = u
				clipY, clipH = self.m_y, self.m_height
			else
				local u = mathfloor( self.m_height*progress/100	+ 0.5 )	-- upward
				clipY = self.m_y + self.m_height - u
				clipH = u
				clipX, clipW = self.m_y, self.m_height
			end
			local x, y, w, h, newSrcX, newSrcY = 
				clip( fillPic.x+itemX, fillPic.y+itemY, fillPic.width, fillPic.height,  clipX, clipY, clipW, clipH )
			self.m_cwindow:drawPicture( fillPic.pic, newSrcX, newSrcY, w, h, x, y )
		end
	end
end

--class HProgressBar
do
	local super = ProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	HProgressBar = proto
	
	function Class:init (...)
		self.m_bVertical = false
		return super.init(self, ...)
	end
end

--class VProgressBar
do
	local super = ProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	VProgressBar = proto
	
	function Class:init (...)
		self.m_bVertical = true
		return super.init(self, ...)
	end
end

--local class RoundProgressBar
-- picture must fit bounding box
local RoundProgressBar
do
	local super = BasicProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	RoundProgressBar = proto
	
	proto.m_progress = 0
	proto.m_bVertical = unused		--required, set by init
		
	function Class:draw ()
		local progress = self.m_progress
		local bVertical = self.m_bVertical
		
		local headfillPic = self.m_images.headfill
		local midfillPic = self.m_images.midfill
		local tailfillPic = self.m_images.tailfill
		
		local headWidth = headfillPic.width
		local tailWidth = tailfillPic.width
		
		if not headfillPic or not midfillPic or not tailfillPic then
			return
		end
		
		local images = self.m_images
		if images == unused then
			return
		end
		local itemX = self.m_x
		local itemY = self.m_y
		
		--... draw unfilled part
		
		-- draw filled part
		local clipX, clipY, clipW, clipH
		if not bVertical then
			local u = mathfloor( self.m_width*progress/100 + 0.5 )	-- rightward
			
			if u <= headWidth + tailWidth  then		-- head & tail
				local headPart = mathfloor(u * headWidth / (headWidth + tailWidth) + 0.5)
				if headPart > headWidth then
					headPart = headWidth
				end
				if u - headPart > tailWidth then
					headPart = u - tailWidth
				end
				local tailPart = u - headPart
				
				self.m_cwindow:drawPicture( headfillPic.pic, 0, 0, headPart, headfillPic.height, self.m_x, self.m_y )
				self.m_cwindow:drawPicture( tailfillPic.pic, tailWidth - tailPart, 0, 
					tailPart, tailfillPic.height,
					self.m_x + headPart, self.m_y )
			else
				self.m_cwindow:drawPicture( headfillPic.pic, 0, 0, headWidth, headfillPic.height, self.m_x, self.m_y )
				self.m_cwindow:drawPicture( midfillPic.pic, 0, 0, 1, midfillPic.height, 
					self.m_x + headWidth, self.m_y, 
					u - headWidth - tailWidth, midfillPic.height )
				self.m_cwindow:drawPicture( tailfillPic.pic, 0, 0, tailWidth, tailfillPic.height, 
					self.m_x + u - tailWidth, self.m_y )
			end
		else
			--...
			assert(false and "not supported")
		end
	end
end

--class HRoundProgressBar
do
	local super = RoundProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	HRoundProgressBar = proto
	
	function Class:init (...)
		self.m_bVertical = false
		return super.init(self, ...)
	end
end

--class VRoundProgressBar
do
	local super = RoundProgressBar
	local proto = super:clone() local Class = malut.baseof(proto)
	VRoundProgressBar = proto
	
	function Class:init (...)
		self.m_bVertical = true
		return super.init(self, ...)
	end
end


--class Text
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	Text = proto
	
	proto.m_font = unused		-- required
	proto.m_size = unused		-- required
	proto.m_color = unused		-- required
	proto.m_align = unused		-- required
	proto.m_valign = unused		-- required
	proto.m_bMultiLine = unused		-- required
	
	proto.m_text = unused		-- required
	
	proto.m_fontRes = unused		-- required
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		
		self.m_font = layout.font or ""
		self.m_size = layout.size or 10
		self.m_color = layout.color or 0xffffffff
		self.m_align = layout.align or "center"
		self.m_valign = layout.valign or "center"
		self.m_bMultiLine = not not layout.bMultiLine
		
		self.m_text = layout.text or ""
		
		return self
	end
	
	function Class:onInit ()
		self.m_cwindow = self.m_window.m_cwindow
		
		self.m_fontRes = LuaSkinApi.SkinFont()
		self.m_fontRes:load(self.m_font, self.m_size)
	end
	
	function Class:draw ()
		local align = self.m_align
		local valign = self.m_valign
		
		if self.m_width > 0 or self.m_height > 0 then
			self.m_cwindow:drawText( self.m_text, self.m_fontRes, 
						self.m_x, self.m_y, self.m_width, self.m_height, self.m_color,
						align=="left" and -1 or align=="right" and 1 or 0,
						valign=="top" and -1 or valign=="bottom" and 1 or 0,
						self.m_bMultiLine )
		end
	end
	
	function Class:setText (text, color)
		if text then self.m_text = text end
		if color then self.m_color = color end

		self.m_window:setDirty()
	end
	
	--NOTE 目前仅能计算单行、左对齐时的尺寸。iFrom, iTo 都是左沿 return x, y, w, h
	function Class:getTextSize(iFrom, iTo)
		local text = self.m_text
		iTo = iTo or #text
		
		local _, w, h = self.m_cwindow:getTextSize(text:sub(iFrom,iTo), self.m_fontRes)
		local _, headW, headH = self.m_cwindow:getTextSize(text:sub(1,iFrom-1), self.m_fontRes)
		local _, allW, allH = self.m_cwindow:getTextSize(text:sub(1,iTo), self.m_fontRes)
		--取中间一截
		return self.m_x + (allW + headW - w)/2, self.m_y, w, self.m_height
	end
end

-- text color is selected base on state of buttonObj
local function updateTextColor (buttonObj, textObj, colors)
	local colorDefault = colors.default
	local buttonState = buttonObj.m_buttonState
	if buttonState == "normal" then
		textObj.m_color = colorDefault
	elseif buttonState == "hover" then
		textObj.m_color = colors.hover or colorDefault
	elseif buttonState == "press" then
		textObj.m_color = colors.press or colors.hover or colorDefault
	elseif buttonState == "disable" then
		textObj.m_color = colors.disable or colorDefault
	end
end

--class TextButton, Button with a inner Text object
do
	local super = Button
	local proto = super:clone() local Class = malut.baseof(proto)
	TextButton = proto
	
	proto.sub_text = unused		-- required, text object
	
	function Class:init (layout, window)
		-- init Button part
		super.init(self, layout, window)
		
		-- init Text part
		local textObj = Text(window)(layout.textStyle)
		textObj.m_name = "text"
		self.sub_text = textObj		
		self:addChild(textObj)

		self.m_colors = layout.colors or {default=self.sub_text.m_color or 0xffffffff}
		
		return self
	end
	
	function Class:enable (flag)
		super.enable(self, flag)
		updateTextColor(self, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onMouseOver ()
		super.onMouseOver(self)
		updateTextColor(self, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onMouseOut ()
		super.onMouseOut(self)
		updateTextColor(self, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onLButtonDown ()
		super.onLButtonDown(self)
		updateTextColor(self, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onLButtonUp ()
		super.onLButtonUp(self)
		updateTextColor(self, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	
	function Class:setText (text, color)
		self.sub_text:setText(text, color)
		self.m_window:setDirty()
	end
end

--local class CheckButton
local CheckButton
do
	local super = Button
	local proto = super:clone() local Class = malut.baseof(proto)
	CheckButton = proto
	
	proto.m_bCheck = false
	
	function Class:updateImage ()
		local images = self.m_images
		local curImage
		
		local buttonState = self.m_buttonState
		if not self.m_bCheck then
			if buttonState == "normal" then
				curImage = images.bg
			elseif buttonState == "hover" then
				curImage = images.hover
			elseif buttonState == "press" then
				curImage = images.press or images.hover
			elseif buttonState == "disable" then
				curImage = images.disable
			end
			curImage = curImage or images.bg
		else
			if buttonState == "normal" then
				curImage = images.bgv
			elseif buttonState == "hover" then
				curImage = images.hoverv
			elseif buttonState == "press" then
				curImage = images.pressv or images.hoverv
			elseif buttonState == "disable" then
				curImage = images.disablev
			end
			curImage = curImage or images.bgv
		end
		
		self.m_curImage = curImage or unused
	end
end

--class CheckBox, has inner CheckButton and Text object
--[[
	layout 支持
		textStyle，但 textStyle 中 bMultiLine, align, valign 无效
]]

do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	CheckBox = proto
	
	proto.m_bCheck = false
	proto.sub_button = unused		-- required, CheckButton object
	proto.sub_text = unused		-- required, Text object

	function Class:init (layout, window)
		super.init(self, layout, window)	--load position
		
		self.m_colors = layout.colors or {default = 0xffffffff}
		
		-- init Button part
		local buttonObj = CheckButton(window) {name="button"}
		self.sub_button = buttonObj
		buttonObj.isActive = false
		buttonObj.m_bCheck = not not layout.bCheck
				
		-- init Text part, name is not need
		layout.textStyle.bMultiLine = false
		layout.textStyle.align = "left"
		layout.textStyle.valign = "bottom"
		local textObj = Text(window)(layout.textStyle)
		textObj.m_name = "text"
		self.sub_text = textObj
		
		self:addChild(buttonObj)
		self:addChild(textObj)
		
		return self
	end
	
	function Class:onInit ()
		super.onInit(self)
		
		onTreeSub(self, "onInit", true)
		
		local buttonObj = self.sub_button
		local textObj = self.sub_text
		
		-- get text size
		local _, textWidth, textHeight = self.m_cwindow:getTextSize(textObj.m_text, textObj.m_fontRes)
		
		textObj.m_width = textWidth
		textObj.m_height = textHeight
		textObj.m_x = buttonObj.m_x + buttonObj.m_width
		textObj.m_y = buttonObj.m_y + buttonObj.m_height - textHeight
		updateTextColor(buttonObj, self.sub_text, self.m_colors)
		
		-- calc boundary box of check box
		self.m_x = buttonObj.m_x
		self.m_width = buttonObj.m_width + textObj.m_width
		
		if buttonObj.m_width > textObj.m_width then
			self.m_y = buttonObj.m_y
			self.m_height = buttonObj.m_height
		else
			self.m_y = textObj.m_y
			self.m_height = textObj.m_height
		end

		return true		-- window will not onInit children
	end
	
	function Class:isActive ()
		return not self.sub_button.m_disabled
	end
	
	function Class:enable (flag)
		self.sub_button:enable(flag)
		updateTextColor(self.sub_button, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onMouseOver (x, y)
		self.sub_button:onMouseOver(x, y)
		updateTextColor(self.sub_button, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onMouseOut ()
		self.sub_button:onMouseOut(self)
		updateTextColor(self.sub_button, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onLButtonDown (x, y)
		self.sub_button:onLButtonDown(x, y)
		updateTextColor(self.sub_button, self.sub_text, self.m_colors)
		self.m_window:setDirty()
	end
	function Class:onLButtonUp (x, y)
		local buttonObj = self.sub_button
		buttonObj.m_bCheck = not buttonObj.m_bCheck
		buttonObj:onLButtonUp(x, y)
		updateTextColor(buttonObj, self.sub_text, self.m_colors)
		self.m_window:setDirty()
		if self.onClick then
			self:onClick()
		end
	end
	
	function Class:setText (text, color)
		self.sub_text:setText(text, color)
		self.m_window:setDirty()
	end
	
	function Class:onHitText (x, y)
		return isHit(self.sub_button, x, y) or isHit(self.sub_text, x, y)
	end
	
	function Class:check (flag)
		self.sub_button.m_bCheck = not not flag
		self.m_window:setDirty()
	end
	function Class:isChecked ()
		return self.sub_button.m_bCheck
	end
end

--local class HoldButton
local HoldButton
do
	local super = Button
	local proto = super:clone() local Class = malut.baseof(proto)
	HoldButton = proto
	
	proto.m_bCapture = false
	proto.m_capOffsetX = 0		-- mouseX - self.centerX when capture
	proto.m_capOffsetY = 0
	
	function Class:updateImage ()
		local images = self.m_images
		if self.m_bCapture then
			self.m_curImage = images.press or images.hover or images.bg or unused
		else
			super.updateImage(self)
		end
	end
	
	function Class:onLButtonDown (x, y)
		if self.m_bCapture then
			return		-- abnormal
		end
		local oriX, oriY = distransformPos(self, x, y)
		self.m_window:setCapture(self)
		self.m_bCapture = true
		self.m_capOffsetX = oriX - (self.m_x + self.m_width/2)
		self.m_capOffsetY = oriY - (self.m_y + self.m_height/2)
		super.onLButtonDown(self, x, y)
	end
	
	function Class:onLButtonUp (x, y)
		if self.m_bCapture then
			self.m_window:releaseCapture(self)
			self.m_bCapture = false
		end
		super.onLButtonUp(self, x, y)
	end
	
	function Class:onMouseMove (x, y)
		if self.m_bCapture then
			if self.onMove then
				local oriX, oriY = distransformPos(self, x, y)
				self:onMove( oriX - self.m_capOffsetX, oriY - self.m_capOffsetY)
			end
		end
	end
end

--class Slider
do
	local super = Figure
	local proto = super:clone() local Class = malut.baseof(proto)
	Slider = proto

	proto.m_max = unused		-- required
	proto.m_grads = unused		-- required, table, x - self.m_x of graduation
	proto.m_bCapture = false
	proto.m_value = 1			-- 1~max
	proto.m_disabled = false
	proto.sub_button = unused		-- required, button object
	
	local kGWidth = 3		-- width of graduation marker
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		local buttonObj = HoldButton(window) {name="button"}
		self.sub_button = buttonObj
		self:addChild(buttonObj)
		
		function buttonObj.onMove (which, x, y)
			self:onHoldButtonMove(which, x, y)
		end
		
		self.m_max = layout.max or 1
		return self
	end
	
	function Class:onInit ()
		super.onInit(self)
		
		local buttonObj = self.sub_button
		local xMargin = buttonObj.m_width/2
		self.m_grads = {}
		self.m_grads[1] = xMargin
		for i=2, self.m_max do
			self.m_grads[i] = xMargin + (self.m_width-xMargin*2)/(self.m_max-1) * (i-1)
		end
		
		self:setValue(self.m_value, true)
	end
	
	function Class:setValue (value, bForce)
		assert( type(value)=="number" )
		
		local newValue = value<1 and 1 or value>self.m_max and self.m_max or value
		if not bForce and self.m_value == newValue then return end
		
		self.m_value = newValue
		
		local buttonObj = self.sub_button
		local buttonX = self.m_grads[self.m_value] + self.m_x - buttonObj.m_width/2
		local buttonY = buttonObj.m_y
		buttonObj:moveTo(mathfloor(buttonX+0.5), buttonY)
		buttonObj:updateTransform()
		
		self.m_window:setDirty()
	end
	
	function Class:getValue ()
		return self.m_value
	end
	
	function Class:onLButtonDown (x, y)
		if self.m_disabled then
			return
		end
		self:onHoldButtonMove (nil, x, y)
	end
	
	function Class:onHoldButtonMove (which, x, y)		-- called by inner button object
		local realX = x - self.m_x
		
		local grads = self.m_grads
		local max = #grads
		local newValue
		if realX < grads[1] then
			newValue = 1
		elseif realX > grads[max] then
			newValue = max
		else
			newValue = mathfloor( (realX-grads[1])/(grads[max]-grads[1])*(max-1) + 0.5 ) + 1
		end
		self:setValue(newValue)
	end
	
	function Class:isActive ()
		return not self.m_disabled
	end

	function Class:enable (flag)
		self.m_disabled = not flag
		local images = self.m_images
		if self.m_disabled then
			self.m_curImage = images.disable or images.bg or unused
		else
			self.m_curImage = images.bg or unused
		end
		self.sub_button:enable(flag)
		self.m_window:setDirty()
	end
	
	function Class:draw ()		
		-- draw background
		super.draw(self)

		-- draw graduation
		local images = self.m_images
		local gradPic = images.grad
		local itemX = self.m_x
		local itemY = self.m_y
		if gradPic then
			local grads = self.m_grads
			for i=1, #grads do
				self.m_cwindow:drawPicture( gradPic.pic, 
						grads[i]-gradPic.x-kGWidth/2, 0, kGWidth, gradPic.height, 
						itemX+grads[i]-kGWidth/2, gradPic.y+itemY )
			end
		end
	end
end

--local class ScrollBar
local ScrollBar
do
	local super = Figure
	local proto = super:clone() local Class = malut.baseof(proto)
	ScrollBar = proto
	
	proto.m_position = unused		-- 0 ~ 1.0
	proto.m_minY = unused		-- required
	proto.m_maxY = unused		-- required
	proto.sub_button = unused	-- required
	
	function Class:init (layout, window)
		super.init(self, layout, window)

		local buttonObj = HoldButton(window) {name="button"}
		self.sub_button = buttonObj
		self:addChild(buttonObj)
				
		function buttonObj.onMove (which, x, y)
			self:onHoldButtonMove(which, x, y)
		end
		
		return self
	end
	
	function Class:onInit ()
		super.onInit(self)
		
		local buttonObj = self.sub_button
		self.m_minY = self.m_y + buttonObj.m_height/2
		self.m_maxY = self.m_y + self.m_height - buttonObj.m_height/2
		if self.m_maxY <= self.m_minY then
			self.m_maxY = self.m_minY + 0.01
		end
		self:setPosition(0)
	end
	
	function Class:setPosition (position)
		assert( type(position)=="number" )
		
		local newPosition = clamp(position, 0, 1)
		self.m_position = newPosition
		
		local buttonObj = self.sub_button
		local y = self.m_minY + (self.m_maxY - self.m_minY)*newPosition - buttonObj.m_height/2
		buttonObj:moveTo(buttonObj.m_x, mathfloor(y+0.5))
		
		self.m_window:setDirty()
		
		if self.onPositionChange then
			self:onPositionChange(newPosition)
		end
	end
	
	function Class:getPosition ()
		return self.m_position
	end
	
	function Class:onLButtonDown (x, y)
		self:onHoldButtonMove (nil, x, y)
	end
	
	function Class:onHoldButtonMove (which, x, y)		-- called by inner button object
		local newPosition
		local minY, maxY = self.m_minY, self.m_maxY
		if y < minY then
			newPosition = 0
		elseif y > maxY then
			newPosition = 1
		else
			newPosition = (y-minY)/(maxY-minY)
		end
		self:setPosition(newPosition)
	end
	
	function Class:isActive ()
		return true
	end
end

--local class TextList
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	TextList = proto
	
	proto.m_fontRes = unused	-- required
	proto:addTable("m_textList", {})
	proto.m_iStartLine = 1
	proto.m_iSelectedLine = unused
	proto.m_iHoveredLine = unused
	
	proto.sub_text = unused
	
	proto.m_lineHeight = unused	-- required
	proto.m_colors = unused		-- required

	function Class:init (layout, window)
		super.init(self, layout, window)
		
		self.m_lineHeight = layout.lineHeight
		local colors = layout.colors or {}
		colors.default = colors.default or 0xffffff
		colors.hover = colors.hover or 0xffffff
		colors.select = colors.select or 0xffffff
		colors.hoverBg = colors.hoverBg or 0x00000000
		colors.selectBg = colors.selectBg or 0x00000000
		self.m_colors = colors
		
		-- inner textObj, only for drawing
		local textObj = Text(window)(layout.textStyle)
		textObj.m_name = "text"
		textObj:show(false)
		self.sub_text = textObj
		self:addChild(textObj)
		
		return self
	end
		
	function Class:draw ()
		local textList = self.m_textList
		local x, y, w, h = self.m_x, self.m_y, self.m_width, self.m_height
		local cwindow = self.m_cwindow
		
		local textObj = self.sub_text
		local iStartLine = self.m_iStartLine
		local colors = self.m_colors
		
		cwindow:pushClip(x, y, w, h)
		for i = 1, #textList do
			local lineX = x
			local lineH = self.m_lineHeight
			local lineW = w
			local lineY = y + lineH * (i-iStartLine)
			
			if lineY < y + w and lineY + lineW > y then
				textObj.m_x, textObj.m_y, textObj.m_width, textObj.m_height = lineX, lineY, lineW, lineH
				textObj.m_text = textList[i]
				
				if i == self.m_iSelectedLine then
					textObj.m_color = colors.select
					cwindow:drawRect(lineX, lineY, lineW, lineH, colors.selectBg)
				elseif i == self.m_iHoveredLine then
					textObj.m_color = colors.hover
					cwindow:drawRect(lineX, lineY, lineW, lineH, colors.hoverBg)
				else
					textObj.m_color = colors.default
				end
				drawTree(textObj, true)
			end
		end
		cwindow:popClip()
	end
	
	function Class:isActive ()
		return true
	end
	
	function Class:clearAllLines ()
		local textList = self.m_textList
		for i = #textList, 1 do
			textList[i] = nil
		end
	end
	
	function Class:addLine (text, iLine)		-- if iLine == nil, add at the end
		local textList = self.m_textList
		if iLine == nil then
			textList[#textList+1] = text
		elseif iLine <= #textList then
			tableinsert(textList, iLine, text)
		end
	end
	
	function Class:findLine (text)
		local textList = self.m_textList
		for i = 1, #textList do
			if textList[i] == text then
				return i
			end
		end
		return nil
	end
	
	function Class:getLine (iLine)
		if iLine ~= nil then
			return self.m_textList[iLine]
		else
			return nil
		end
	end
	
	function Class:setStartLine (iLine)
		self.m_iStartLine = iLine
		self.m_window:setDirty()
	end
	
	function Class:getStartLine ()
		return self.m_iStartLine
	end
	
	function Class:setSelectedLine (iLine)
		self.m_iSelectedLine = iLine
		self.m_window:setDirty()
	end
	
	function Class:getSelectedLine ()
		local iSelectedLine = self.m_iSelectedLine
		if iSelectedLine ~= unused then
			return iSelectedLine, self.m_textList[iSelectedLine]
		end
	end
	
	function Class:setScrollPos (pos)
		local lineHeight = self.m_lineHeight
		local lineCount = #self.m_textList
		local height = self.m_height
		local iStartLine
		
		local nRealLines = mathfloor(height/lineHeight)
		if lineCount <= nRealLines then
			iStartLine = 1
		else
			iStartLine = mathfloor( pos * (lineCount-nRealLines) + 0.5 ) + 1
		end
		self.m_iStartLine = iStartLine
		self.m_window:setDirty()
	end
	
	function Class:getScrollPos ()
		local lineHeight = self.m_lineHeight
		local lineCount = #self.m_textList
		local height = self.m_height
		local iStartLine = self.m_iStartLine
		
		local nRealLines = mathfloor(height/lineHeight)
		if lineCount <= nRealLines then
			return 0
		else
			return (iStartLine-1)/(lineCount-nRealLines)
		end
	end
	
	local function getLine_ (self, mouseX, mouseY)
		local x, y, w, h = self.m_x, self.m_y, self.m_width, self.m_height
		local lineHeight = self.m_lineHeight
		
		if mouseX < x or mouseX >= x+w or mouseY < y or mouseY >= y+h then
			return nil
		end
		local iLine = mathfloor( (mouseY - y)/lineHeight )
		return iLine + self.m_iStartLine
	end
	
	function Class:onLButtonUp (x, y)
		local iLine = getLine_(self, distransformPos(self, x, y))
		
		if iLine and self.onClickOnLine then
			self.onClickOnLine(iLine, self.m_textList[iLine])
		end
	end
	
	function Class:onMouseMove (x, y)
		local iLine = getLine_(self, distransformPos(self, x, y))
		if iLine and iLine ~= self.m_iHoveredLine then
			self.m_iHoveredLine = iLine
			self.m_window:setDirty()
		elseif not iLine and self.m_iHoveredLine ~= unused then
			self.m_iHoveredLine = unused
			self.m_window:setDirty()
		end
	end
	
	function Class:onMouseOut ()
		self.m_iHoveredLine = unused
	end
end

--class ComboBox
do
	local super = TextButton
	local proto = super:clone() local Class = malut.baseof(proto)
	ComboBox = proto
	
	proto.m_bExpand = false
	proto.sub_expandbg = unused		-- required
	proto.sub_scrollbar = unused	-- required
	proto.sub_upbutton = unused		-- required
	proto.sub_downbutton = unused	-- required
	proto.sub_textList = unused			-- required
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		
		local expandbgObj = Figure(window) {name="expandbg"}
		expandbgObj.m_bTop = true
		function expandbgObj:isActive ()
			return true
		end
		self.sub_expandbg = expandbgObj
		
		local scrollbarObj = ScrollBar(window) {name="scrollbar"}
		scrollbarObj.m_bTop = true
		self.sub_scrollbar = scrollbarObj
		
		function scrollbarObj.onPositionChange (which, position)
			self:onScroll(which, position)
		end
		
		local upbuttonObj = Button(window) {name="upbutton"}
		upbuttonObj.m_bTop = true
		function upbuttonObj.onClick ()
			self:onUpButtonClick()
		end
		self.sub_upbutton = upbuttonObj
		
		local downbuttonObj = Button(window) {name="downbutton"}
		downbuttonObj.m_bTop = true
		function downbuttonObj.onClick ()
			self:onDownButtonClick()
		end
		self.sub_downbutton = downbuttonObj
		
		local textListObj = TextList(window)(layout.listStyle)
		textListObj.m_bTop = true
		textListObj.m_name = "list"
		function textListObj.onClickOnLine (iLine, text)
			self:onClickOnLine(iLine, text)
		end
		self.sub_textList = textListObj
		
		expandbgObj:show(false)
		expandbgObj:show(false)
		scrollbarObj:show(false)
		upbuttonObj:show(false)
		downbuttonObj:show(false)
		textListObj:show(false)
		
		self:addChild(expandbgObj)
		self:addChild(scrollbarObj)
		self:addChild(upbuttonObj)
		self:addChild(downbuttonObj)
		self:addChild(textListObj)
		
		return self
	end
	
	function Class:onScroll(which, position)
		self.sub_textList:setScrollPos(position)
	end
	
	local function scrollUpOneLine (self)
		local textListObj = self.sub_textList
		local iCurLine = textListObj:getStartLine()
		if textListObj:getScrollPos() > 0 then
			textListObj:setStartLine(iCurLine - 1)
			self.sub_scrollbar:setPosition( textListObj:getScrollPos() )
		end
	end
	
	local function scrollDownOneLine (self)
		local textListObj = self.sub_textList
		local iCurLine = textListObj:getStartLine()
		if textListObj:getScrollPos() < 1 then
			textListObj:setStartLine(iCurLine + 1)
			self.sub_scrollbar:setPosition( textListObj:getScrollPos() )
		end
	end
	
	function Class:onUpButtonClick()
		scrollUpOneLine(self)
	end
	function Class:onDownButtonClick()
		scrollDownOneLine(self)
	end
	function Class:onClickOnLine(iLine, text)
		self.sub_textList:setSelectedLine(iLine)
		self:setText(text)
		self:expand(false)
	end
		
	function Class:expand (flag)
		flag = not not flag
		if self.m_bExpand == flag then
			return
		else
			self.m_bExpand = flag
		end
		
		self:updateTransform()
		if flag then
			self.sub_expandbg:show(true)
			self.sub_scrollbar:show(true)
			self.sub_upbutton:show(true)
			self.sub_downbutton:show(true)
			self.sub_textList:show(true)
			self.m_window:setCapture(self)
		else
			self.sub_expandbg:show(false)
			self.sub_scrollbar:show(false)
			self.sub_upbutton:show(false)
			self.sub_downbutton:show(false)
			self.sub_textList:show(false)
			self.m_window:releaseCapture(self)
		end
		self.m_window:setDirty()
		
		if self.onExpand then
			self.onExpand(self, flag)
		end
	end
	
	function Class:onLButtonDown (x, y)
		if self.m_bExpand then
			if not isHit(self, x, y) and not isHit(self.sub_expandbg, x, y) then
				self:expand(false)
				super.onMouseOut(self, x, y)
			end
		else
			super.onLButtonDown(self, x, y)
		end
	end
	
	function Class:onLButtonUp (x, y)
		if self.m_bExpand then		-- capture == true
			if isHit(self, x, y) then
				self:expand(false)
				super.onLButtonUp(self, x, y)
			else
				if not isHit(self.sub_expandbg, x, y) then
					self:expand(false)
					super.onLButtonUp(self, x, y)
				end
			end
		else
			self:expand(true)
		end
	end
	
	function Class:onMouseOver (x,y)
		if self.m_bExpand then
		else
			super.onMouseOver(self, x, y)
		end
	end
	
	function Class:onMouseOut ()
		if self.m_bExpand then
		else
			super.onMouseOut(self)
		end
	end
	
	function Class:onMouseWheelUp (x, y)
		scrollUpOneLine(self)
		self.sub_textList:onMouseMove(x, y)
	end	
	function Class:onMouseWheelDown (x, y)
		scrollDownOneLine(self)
		self.sub_textList:onMouseMove(x, y)
	end

	function Class:clearAllLines ()
		return self.m_textList:clearAllLines ()
	end
	
	function Class:addLine (...)
		return self.sub_textList:addLine (...)
	end
	
	function Class:setSelectedLine (line)
		local textListObj = self.sub_textList
		local iLine, text
		if type(line) == "number" then
			iLine = line
			text = textListObj:getLine(iLine)
		else
			iLine = textListObj:findLine(line)
			text = line
		end
		if iLine and text then
			textListObj:setSelectedLine(iLine)
			self:setText(text)
		end
	end
	
	function Class:getSelectedLine ()
		return self.sub_textList:getSelectedLine()
	end
end

--class InputBox
--[[
	layout 支持
		textStyle，但 textStyle 中 bMultiLine, align 无效
		maxLength
		filter = 正则表达式, 输入字符后完整文字符合正则表达式时，输入有效
]]
do
	local super = Figure
	local proto = super:clone() local Class = malut.baseof(proto)
	InputBox = proto
	
	-- from layout
	proto.m_bReadOnly = false
	proto.m_maxLength = unused
	proto.m_filter = unused
	proto.m_textColor = 0xffffffff
	
	-- subs
	proto.sub_text = unused
	
	-- temporary state
	proto.m_disabled = false
	proto.m_hasFocus = false
	proto.m_offset = 0
	proto.m_cursorIndex = 1	--在此字左边
--~ 	proto.m_text = ""
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		self.m_cursorStyle = LuaSkinApi.SkinWindow_CURSOR_EDIT
		
		self.m_bReadOnly = not not layout.bReadOnly
		self.m_maxLength = layout.maxLength or unused
		self.m_filter = layout.filter or unused
		
		-- init Text part
		local textObj = Text(window)(layout.textStyle)
		textObj.m_bMultiLine = false
		textObj.m_align = "left"
		textObj.m_name = "text"
		self.sub_text = textObj		
		self:addChild(textObj)
		
		proto.m_textColor = textObj.m_color
						
		return self
	end
	
	function Class:enable (flag)
		self.m_disabled = not flag
		if self.m_disabled then
			self.sub_text.m_color = 0xff666666
		else
			self.sub_text.m_color = self.m_textColor
		end
		self.m_window:setDirty()
	end
		
	function Class:isActive ()
		return not self.m_disabled	--and not self.m_bReadOnly
	end
	
	function Class:isStrValid (str)
		local maxLength = self.m_maxLength
		local filter = self.m_filter
		
		if maxLength ~= unused and #str > maxLength then
			return false
		end
		
		if filter ~= unused and not str:find(filter) then
			return false
		end
		
		return true
	end
	
	--计算点击后光标在哪个字左边
	local function calcCursorIndex (self, x, y)
		local text = self.sub_text.m_text
		if #text == 0 then
			return 1
		end
		
		local lastX = self.sub_text:getTextSize(1, 1)
		for i=2, #text+1 do	--至此字左沿
			local textX, textY, textW, textH = self.sub_text:getTextSize(i, i)
			if (textX+lastX)/2 >= x then
				return i-1
			end
			lastX = textX
		end
		return #text + 1
	end
	
	--计算光标的位置
	local function calcCursorPos (self, index)
		local textX, textY, textW, textH = self.sub_text:getTextSize(index, index)
		return textX, textY+1, 1, textH-1
	end
	
	function Class:onLButtonDown (x, y)	
		--处理输入框激活
		if not self.m_hasFocus then
			self.m_hasFocus = true
			self.m_window:setCapture(self)
			
			self.m_window:setDirty()
		else
			if not isHit(self, x, y) then
				self.m_hasFocus = false
				self.m_window:releaseCapture(self)
				
				self.m_window:onLButtonDown (x, y)	--feedback mesaage
				
				self.m_window:setDirty()
			end
		end
		
		if isHit(self, x, y) then
			self.m_cursorIndex = calcCursorIndex(self, x, y)
			self.m_window:setDirty()
		end
	end
	
	local function doBackspace (self)
		local text = self.sub_text.m_text
		local curIndex = self.m_cursorIndex
		if curIndex > 1 then
			local newText = text:sub(1, curIndex-2)..text:sub(curIndex)
			self.sub_text:setText(newText)
			self.m_cursorIndex = curIndex - 1
			self.m_window:setDirty()
		end
	end
	
	local function doInsert (self, str)
		local text = self.sub_text.m_text
		local curIndex = self.m_cursorIndex
		local newText = ("%s%s%s"):format(text:sub(1,curIndex-1), str, text:sub(curIndex))
		
		if self:isStrValid(newText) then
			self.sub_text:setText(newText)
			self.m_cursorIndex = curIndex + #str
			self.m_window:setDirty()
		end
	end
	
	function Class:onChar (ch)
		if not self.m_hasFocus then
			return
		end
		
		local chStr = stringchar(ch)
		if chStr == '\b' then	--backspace
			doBackspace(self)
		elseif ch >= 32 and ch < 127 then	--可见字符
			doInsert(self, chStr)
		end
	end
	
	local function doDelete (self)
		local text = self.sub_text.m_text
		local curIndex = self.m_cursorIndex
		if curIndex <= #text then
			local newText = text:sub(1, curIndex-1)..text:sub(curIndex+1)
			self.sub_text:setText(newText)
			self.m_window:setDirty()
		end
	end
	
	local function doMoveCursor (self, offset)
		local text = self.sub_text.m_text
		local curIndex = self.m_cursorIndex
		local newIndex = curIndex + offset
		if newIndex < 1 then
			newIndex = 1
		end
		if newIndex > #text then
			newIndex = #text + 1
		end
		if newIndex ~= curIndex then
			self.m_cursorIndex = newIndex
			self.m_window:setDirty()
		end
	end
	
	function Class:onKeyDown (key)
		if key == LuaSkinApi.SkinWindow_KEYCODE_DELETE then
			doDelete(self)
		elseif key == LuaSkinApi.SkinWindow_KEYCODE_LEFT then
			doMoveCursor(self, -1)
		elseif key == LuaSkinApi.SkinWindow_KEYCODE_RIGHT then
			doMoveCursor(self, 1)
		end
	end
	
	function Class:replaceText (text)
		self.sub_text:setText(text)
		self.m_cursorIndex = #self.sub_text.m_text + 1
		self.m_window:setDirty()
	end
	
	function Class:draw ()
		super.draw(self)
		--画文本光标
		if self.m_hasFocus then
			local x, y, w, h = calcCursorPos(self, self.m_cursorIndex)
			self.m_cwindow:drawRect(x, y, w, h, self.sub_text.m_color)
		end
	end
end

--class SpinBox
--[[
	InputBox 的派生类
	layout不支持 filter
	layout支持
		min
		max
		step
]]
do
	local super = InputBox
	local proto = super:clone() local Class = malut.baseof(proto)
	SpinBox = proto
	
	-- from layout
	proto.m_min = unused
	proto.m_max = unused
	proto.m_step = 1		--required
	
	-- subs
	proto.sub_upbutton = unused		--required
	proto.sub_downbutton = unused		--required
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		self.m_cursorStyle = LuaSkinApi.SkinWindow_CURSOR_EDIT
		
		--override parent setting
		self.m_filter = "^%-?%d*$"
		
		--from layout
		self.m_min = layout.min or unused
		self.m_max = layout.max or unused
		self.m_step = layout.step or 1
		
		-- init up and down button part
		local upbuttonObj = Button(window) {name="upbutton"}
		function upbuttonObj.onClick ()
			self:onUpButtonClick()
		end
		self.sub_upbutton = upbuttonObj
		self:addChild(upbuttonObj)

		local downbuttonObj = Button(window) {name="downbutton"}
		function downbuttonObj.onClick ()
			self:onDownButtonClick()
		end
		self.sub_downbutton = downbuttonObj
		self:addChild(downbuttonObj)
		
		self.m_colors = layout.colors or {default=self.sub_text.m_color or 0xffffffff}
				
		return self
	end
	
	function Class:enable (flag)
		InputBox.enable(self, flag)
		self.sub_upbutton:enable(flag)
		self.sub_downbutton:enable(flag)
	end
	
	local function refineValue (self, value)
		local min = self.m_min
		local max = self.m_max
		
		value = mathfloor(value)
		if min ~= unused and value < min then
			value = min
		end
		if max ~= unused and value > max then
			value = max
		end
		return value
	end
	
	function Class:setValue (value)
		if type(value) == "number" then
			self:replaceText(tostring(refineValue(self, value)))
		end
	end
	
	function Class:getValue ()
		return refineValue(self, tonumber(self.sub_text.m_text) or 0)
	end
	
	function Class:onUpButtonClick ()
		self:setValue(self:getValue() + self.m_step)
	end
	
	function Class:onDownButtonClick ()
		self:setValue(self:getValue() - self.m_step)
	end
end

--class Browser
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	Browser = proto
	
	proto.m_skinBrowser = unused
	proto.isBrowser = true
	
	function Class:onInit ()
		super.onInit(self)
		self.m_skinBrowser = self.m_window.m_cwindow:createBrowser(
								self.m_x, self.m_y, self.m_width, self.m_height)
	end
	
	function Class:setUrl (url)
		assert(type(self.m_skinBrowser)=="userdata")
		self.m_skinBrowser:setUrl(url)
	end
	
	function Class:draw ()
		self.m_skinBrowser:draw()
	end
	
end

--local class PictureObj
local PictureObj
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	PictureObj = proto
	proto.m_leftTime = -1
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		
		local res = layout.res
		self.m_curImage = {x = self.m_x, y = self.m_y, width = res:getWidth(), height = res:getHeight(), pic = res}
		self.m_leftTime = layout.lifeTime or -1
		
		self:onInit()
		
		self:setAction( "PictureObj", function (dtime)
			if self.m_leftTime >= 0 then
				local newLeftTime = self.m_leftTime - dtime
				if newLeftTime <= 0 then
					self.m_window:removeItem(self)
					self.m_window:setDirty()
				else
					self.m_leftTime = newLeftTime
				end
			end
		end )
		
		return self
	end
	
	function Class:draw ()
		local curImage = self.m_curImage
		self.m_cwindow:drawPicture( curImage.pic, 0, 0, curImage.width, curImage.height, self.m_x, self.m_y )
	end
end

--local class AnimationPictureObj
local AnimationPictureObj
do
	local super = Item
	local proto = super:clone() local Class = malut.baseof(proto)
	AnimationPictureObj = proto
	
	function Class:init (layout, window)
		super.init(self, layout, window)
		
		local res = layout.res:duplicate()
		self.m_curImage = {x = self.m_x, y = self.m_y, width = res:getWidth(), height = res:getHeight(), pic = res}
		
		super.onInit(self)
		
		self:setAction( "AnimationPictureObj", function (dtime)
			local curImage = self.m_curImage
			if curImage ~= unused then
				local bMore, bNewFrame = curImage.pic:increaseTime(dtime)
				if not bMore then
					if self.onEnd then
						self:onEnd()
						self.m_curImage.pic:destroy()
					end
					self.m_window:removeItem(self)
					self.m_window:setDirty()
				end
				if bNewFrame then
					self.m_window:setDirty()
				end
			end
		end )
		return self
	end
	
	function Class:onInit() end
	
	function Class:draw ()
		local curImage = self.m_curImage
		self.m_cwindow:drawPicture( curImage.pic, 0, 0, curImage.width, curImage.height, self.m_x, self.m_y )
	end
end

--class Window
do
	local super = malut.Object
	local proto = super:clone() local Class = malut.baseof(proto)
	Window = proto
	
	proto.m_name = unused
	proto.m_width = 0
	proto.m_height = 0
	proto.m_title = ""
	proto.m_safeRect = unused
	
	proto.m_rootGroup = unused	-- required, a Group
	proto.m_dynamicGroup = unused	-- required, a Group
	proto:addTable( "m_removeList", {} )
	
	proto:addTable("m_images", unused)		-- required
	proto.m_bMain = false
	proto.m_captureItem = unused		-- unused if none
	proto:addTable( "m_captureStack", {} )
	
	proto.m_cwindow = unused		-- required
	proto.m_hoveredItem = unused		-- unused if none
	proto.m_bHoverCapture = false		-- whether the hovered item is capture or its child
	proto.m_dirty = true
	proto:addTable( "m_transformStack", {{offsetX=0, offsetY=0, scaleX=1, scaleY=1},} )
	
	proto.m_needDrawBrowserLeftTime = 0
		
	function Class:init (layout)
		self.m_name = layout.name or unused
		self.m_width = layout.width or 0
		self.m_height= layout.height or 0
		self.m_title = layout.title or ""
		self.m_safeRect = layout.safeRect
		
		local rootGroupObj = Group(self) {name="root"}
		rootGroupObj.m_window = self
		self.m_rootGroup = rootGroupObj
		
		local itemGroupObj = Figure(self) {name="items"}
		self.m_itemGroup = itemGroupObj
		
		local dynamicGroupObj = Group(self) {name="dynamicGroup"}
		self.m_dynamicGroup = dynamicGroupObj
		
		rootGroupObj:addChild(itemGroupObj)
		rootGroupObj:addChild(dynamicGroupObj)
		
		return self
	end
	
	function Class.__call (proto, layout)
		return proto:new():init(layout) 
	end
		
	function Class:getItemGroup ()
		return self.m_itemGroup
	end
	
	-- return a pre-order traversal iterator function
	function Class:treeEach (itemTree)
		local parentTree = itemTree
		local childIndex = nil		-- last pos, if nil, visit parentTree (at the beginning)
		local treeStack = {}
		local indexStack = {}
		local removeList = self.m_removeList
		
		-- return: item, level, bValid
		--  if bValid == false, that is, the item is removed, need to call nextItem again
		local function nextItem (option)
			if option == "skip" then
				if childIndex == nil or childIndex == 0 then
					return nil, "distant", true	-- some error ?
				end
			
				local item = parentTree[childIndex]
				if #item ~= 0 then
					indexStack[#indexStack+1] = childIndex
					treeStack[#treeStack+1] = parentTree

					parentTree = item
					childIndex = #item
				end
				return item, true
			end
			
			if childIndex == nil then		-- begin
				childIndex = 0
				return parentTree, true
			end
			
			-- find next
			local item = parentTree[childIndex]
			if childIndex > 0 and item and #item > 0 then		-- visit first child
				indexStack[#indexStack+1] = childIndex
				treeStack[#treeStack+1] = parentTree
				
				parentTree = item
				childIndex = 1
			else
				while childIndex >= #parentTree do	-- if need, back to parent
					local stackLen = #indexStack
					if stackLen == 0 then
						return nil, "distant", true	-- some error ?
					end
					
					parentTree = treeStack[stackLen]
					treeStack[stackLen] = nil
					childIndex = indexStack[stackLen]
					indexStack[stackLen] = nil
				end
				
				childIndex = childIndex + 1
			end
			
			local ret = parentTree[childIndex]
			if removeList[ret] ~= nil then
				parentTree:removeChild(ret)
				self:setDirty()
				removeList[ret] = nil
				childIndex = childIndex - 1
				return ret, false
			else
				return ret, true
			end
		end
		
		return function (option)
			local item, bValid = nextItem(option)
			while not bValid do
				item, bValid = nextItem(option)
			end
			return item
		end
	end
	
	local function changeHoveredItem (self, newHover)
		--test begin
		if false then
			if newHover == unused then
				print("hover none")
			else
				print("hover", newHover.m_name)
			end
		end
		--test end
		
		local hover = self.m_hoveredItem
		if hover == newHover then
			return
		end
		
		-- leave the former
		if hover ~= unused then
			if hover.onMouseOut then
				hover:onMouseOut()
				self:redraw()
			end
		end
		self.m_hoveredItem = unused
		
		--hover the new one
		if newHover ~= unused and newHover.isActive and newHover:isActive() then
			if newHover.onMouseOver then
				newHover:onMouseOver(x, y)
				self:redraw()
			end
			
			self.m_hoveredItem = newHover
		end
		
		local curHover = self.m_hoveredItem
		if curHover ~= unused then
			self.m_cwindow:SetCursorStyle(curHover.m_cursorStyle)
		else
			self.m_cwindow:SetCursorStyle(LuaSkinApi.SkinWindow_CURSOR_NORMAL)
		end
	end
	
	function Class:removeItem (item)
		self.m_removeList[item] = true
		if item == self.m_hoveredItem then
			changeHoveredItem(self, unused)
		end
		self:releaseCapture(item)
		self:redraw()
	end
	
	function Class:setSkinWindow (cwindow)
		self.m_cwindow = cwindow
	end
	
	function Class:onInit ()
		self.m_cwindow:setTitle(self.m_title)
		
		onTree(self.m_rootGroup, "onInit", true)
		self:updateTransform()
		self:redraw(true)
	end
	
	function Class:setDirty ()
		self.m_dirty = true
	end
	
	function Class:redraw (bForce)
		return self:redrawEx(bForce, true, false, true)
	end
	
	function Class:redrawEx (bForce, bDrawNormal, bDrawBrowser, doPresent)
		if not bForce and not self.m_dirty then
			return		-- not need to redraw
		end
		local cwindow = self.m_cwindow
		if cwindow == unused then return end
		if not bForce then
			if not cwindow:isShow() --[[or cwindow:isMinimized()]] then
				return
			end
		end
		
		-- do remove
		for _ in self:treeEach(self.m_rootGroup) do
			-- remove done by self:treeEach
		end
		
		self.m_dirty = false
						
		if bDrawNormal then
			cwindow:selectDrawTarget(LuaSkinApi.SkinWindow_DRAWTARGET_NORMAL)
			cwindow:clear()
			self:redrawEx_internal(function (item) return not item.isBrowser end)
		end
		
		if bDrawBrowser then
			cwindow:selectDrawTarget(LuaSkinApi.SkinWindow_DRAWTARGET_BROWSER)
			cwindow:clear()
			self:redrawEx_internal(function (item) return item.isBrowser end)
		end
		
		if doPresent then
			cwindow:present(bDrawBrowser)
		end
	end
	
	function Class:redrawEx_internal (filter)
		local cwindow = self.m_cwindow
		local rootChild = self.m_rootGroup
		
		local function drawTree_ (itemTree, bDrawTop, bTreeTop)
			if not itemTree.m_visible then
				return
			end
			
			local flag
			local bTreeTop = bTreeTop or itemTree.m_bTop
			if not not bTreeTop == bDrawTop and itemTree.draw then
				if filter(itemTree) then
					cwindow:pushTransform(itemTree.m_absOffsetX, itemTree.m_absOffsetY, 
											itemTree.m_absScaleX, itemTree.m_absScaleY )
					cwindow:pushAlpha(itemTree.m_absAlpha)
					flag = itemTree:draw()
					cwindow:popAlpha()
					cwindow:popTransform()
				end
			end			
			if not flag then
				for i=1, #itemTree do
					drawTree_(itemTree[i], bDrawTop, bTreeTop)
				end
			end	
		end
		
		drawTree_(rootChild, false)
		drawTree_(rootChild, true)
	end
	
	function Class:setCapture (obj)
		self.m_captureStack[#self.m_captureStack+1] = obj
		self.m_captureItem = obj
	end
	
	function Class:releaseCapture (obj)
		self.m_captureItem = unused
		
		local captureStack = self.m_captureStack
		local iObj		-- find it in m_captureStack
		for i = #captureStack, 1, -1 do
			if captureStack[i] == obj then
				iObj = i
			end
		end
		if iObj then
			local nStack = #captureStack
			for i=nStack, iObj, -1 do		-- pop
				captureStack[i] = nil
			end
			if iObj > 1 then
				self.m_captureItem = captureStack[iObj-1]
			end
		end
		
		if self.m_hoveredItem ~= obj and obj.onMouseOut then
			obj:onMouseOut()
		end
	end
	
	function Class:findItemByName (name)
		return findItemByName_(self.m_rootGroup, name)
	end

	function Class:onDestroy ()
		self.m_cwindow = unused		--回调函数只生效一次
	end
	
	function Class:onShowWindow ()
		self:redraw(true)
		self:onMouseLeave()
	end
	
	function Class:onClose ()
		self.m_cwindow:show(false)
	end
	
	-- root last traversal
	local function getItem_ (itemTree, x, y, capture, bGetTop, bTreeTop)
		if not itemTree.m_visible then
			return nil, false
		end
		
		bTreeTop = bTreeTop or itemTree.m_bTop
		
		for i=#itemTree, 1, -1 do
			local item = itemTree[i]
			local subItem, bCap = getItem_ (item, x, y, capture, bGetTop, bTreeTop)
			if subItem then
				return subItem, bCap or itemTree==capture
			end
		end
		if not not bTreeTop == bGetTop and itemTree.isActive and itemTree:isActive() and 
				isHit(itemTree, x, y) and (itemTree.onHitTest==nil or itemTree:onHitTest(x, y)) then
			return itemTree, itemTree==capture
		end
		return nil, false
	end
	
	-- return item at (x,y), and whether it is the capture or its child
	-- if item has children, children are checked first
	-- if capture is unused, the capture flag will be false
	local function getItem (itemTree, x, y, capture)
		local item, bCap = getItem_ (itemTree, x, y, capture, true)
		if item == nil then
			item, bCap = getItem_ (itemTree, x, y, capture, false)
		end
--~ 		print( "getItem at", x, y, item and item.m_name or "nil", bCap )	--D
		return item, bCap
	end
	
	local function isChildOf (item, parent)
		local realParent = item.m_parent
		while realParent ~= unused do
			if realParent == parent then
				return true
			end
			parent = parent.m_parent
		end
		return false
	end
	
	-- return client: 1, caption: -1, none: 0
	function Class:onNcHitTest (x, y)
		-- before hit test, do remove
		for _ in self:treeEach(self.m_rootGroup) do
		end
	
		local capture = self.m_captureItem
		local item, bCap = getItem(self.m_rootGroup, x, y, capture)
		item = item or unused		-- to compare with hover, which could be unused
		local hover = self.m_hoveredItem
		if item ~= hover then
			changeHoveredItem(self, unused)	-- leave the former
			
			if item ~= unused and item.isActive and item:isActive()
					and (capture == unused or bCap) then
				changeHoveredItem(self, item)
				self.m_bHoverCapture = bCap
			else
				self.m_bHoverCapture = false
			end
		end
		if capture ~= unused then
			return 1
		else
			if item.isBrowser then
				return 0
			else
				if item ~= nil and item.isActive and item:isActive() then
					return 1
				else
					return -1
				end
			end
		end
	end
	
	local function onHoverEvent (self, event, ...)
		local hover = self.m_hoveredItem
		local capture = self.m_captureItem
		
		if capture==unused or self.m_bHoverCapture then
			local func = hover[event]
			if func then
				func(hover, ...)
			end
		else
			local func = capture[event]
			if func then
				func(capture, ...)
			end
		end
	end
	
	function Class:onLButtonDown (x, y)
		onHoverEvent(self, "onLButtonDown", x, y)
		self:redraw()
	end
	
	function Class:onLButtonUp (x, y)
		onHoverEvent(self, "onLButtonUp", x, y)
		self:redraw()
	end
	
	function Class:onMouseMove (x, y)
		onHoverEvent(self, "onMouseMove", x, y)
		self:redraw()
	end
	
	function Class:onMouseLeave ()
		changeHoveredItem(self, unused)
		self.m_bHoverCapture = false
		self:redraw()
	end
	
	function Class:onMouseWheelUp (x, y)
		onHoverEvent(self, "onMouseWheelUp", x, y)
		self:redraw()
	end
	
	function Class:onMouseWheelDown (x, y)
		onHoverEvent(self, "onMouseWheelDown", x, y)
		self:redraw()
	end
	
	function Class:onKeyDown (key)
		onHoverEvent(self, "onKeyDown", key)
		if key == LuaSkinApi.SkinWindow_KEYCODE_RETURN then
			if self.onOK then
				self:onOK()
			end
		elseif key == LuaSkinApi.SkinWindow_KEYCODE_ESCAPE then
			if self.onCancel then
				self:onCancel()
			end
		end
		self:redraw()
	end

	function Class:onKeyUp (key)
		onHoverEvent(self, "onKeyUp", key)
		self:redraw()
	end

	function Class:onChar (ch)
		onHoverEvent(self, "onChar", ch)
		self:redraw()
	end

	function Class:onPaint ()
		self:redraw(true)
	end
	
	function Class:onEraseBkgnd ()
		self:redraw(true)
	end
	
	function Class:onMove (x, y)
--~ 		local oldLeftTime = self.m_needDrawBrowserLeftTime
--~ 		self.m_needDrawBrowserLeftTime = 700
--~ 		self.m_cwindow:setBrowserStandAlone(false)
--~ 		if oldLeftTime == 0 then
--~ 			self:redrawEx(true, true, true, true)
--~ 		end
	end
	
	function Class:onDrawPreview ()
		self:redrawEx(true, true, true, false)
	end
	
	function Class:onTimer (dtime)
		local iter = self:treeEach(self.m_rootGroup)
		local item = iter()
		while item do
			if item:onTimer(dtime) then
				iter "skip"
			end
			item = iter()
		end
		
--~ 		if self.m_needDrawBrowserLeftTime > 0 then
--~ 			local newleftTime = self.m_needDrawBrowserLeftTime - dtime
--~ 			if newleftTime < 0 then newleftTime = 0 end
--~ 			self.m_needDrawBrowserLeftTime = newleftTime
--~ 			
--~ 			if newleftTime > 0 then
--~ 				self:redrawEx(true, false, true, true)
--~ 			else	--移动结束
--~ 				self.m_cwindow:setBrowserStandAlone(true)
--~ 				self:redrawEx(true, true, false, true)
--~ 			end
--~ 		else
--~ 			self:redraw()
--~ 		end
		
		self:redraw()
	end
	
	function Class:updateTransform ()
		self.m_rootGroup:updateTransform()
	end
	
	function Class:showPicture (pictureRes, x, y, lifeTime)
		local res
		if type(pictureRes) == "string" then
			res = LuaSkinApi.SkinPicture()
			res:load(pictureRes)
			if not res:isOK() then
				errorPrint( "Fail to load picture "..pictureRes )
				return nil
			end
		else
			res = pictureRes
		end
		
		local pictureObj = PictureObj:new():init({x=x, y=y, width=1, height=1, res=res, lifeTime=lifeTime}, self)
		pictureObj:onInit()
		pictureObj:updateTransform()
		self.m_dynamicGroup:addChild(pictureObj)
		
		return pictureObj
	end
	
	function Class:showAnimation (animationRes, x, y)
		local res
		if type(animationRes) == "string" then
			res = LuaSkinApi.SkinAnimation()
			res:load(animationRes)
			if not res:isOK() then
				errorPrint( "Fail to load animation picture "..animationRes )
				return nil
			end
		else
			res = animationRes
		end
		
		local animationObj = AnimationPictureObj:new():init({x=x, y=y, width=1, height=1, res=res}, self)
		animationObj:onInit()
		self.m_dynamicGroup:addChild(animationObj)
		animationObj:updateTransform()
		
		return animationObj
	end
	
	function Class:getSize ()
		return self.m_width, self.m_height
	end
	
	function Class:getSafeRect ()
		local posSafeRect = self.m_safeRect
		if posSafeRect then
			return posSafeRect.x, posSafeRect.y, posSafeRect.width, posSafeRect.height
		else
			return 0, 0, self.m_width, self.m_height
		end
	end
end



