local h,m="\084\114\101\101\071\114\111\117\112",47 local t=LibStub and LibStub("\065\099\101\071\085\073-\051\046\048",true)if not t or(t:GetWidgetVersion(h)or 0)>=m then return end local a,N,z,s,S=next,pairs,ipairs,assert,type local g,i,j=math.min,math.max,math.floor local D,U,E,Q=select,table.remove,unpack,table.concat local L,H=CreateFrame,UIParent local y,J do local h=setmetatable({},{__mode="\107"})function y()local m=a(h)if m then h[m]=nil return m else return{}end end function J(m)for h in N(m)do m[h]=nil end h[m]=true end end local f=175 local W=true local function P(h)local m=h.parent if m and m.value then return P(m)..("\001"..h.value)else return h.value end end local function R(h,m,t,a,N)local z=h.obj local s=h.toggle local S=m.text or""local g=m.icon local i=m.iconCoords local j=m.level local D=m.value local U=m.uniquevalue local Q=m.disabled h.treeline=m h.value=D h.uniquevalue=U if t then h:LockHighlight()h.selected=true else h:UnlockHighlight()h.selected=false end h.level=j if j==1 then h:SetNormalFontObject("\071\097\109\101\070\111\110\116\078\111\114\109\097\108")h:SetHighlightFontObject("\071\097\109\101\070\111\110\116\072\105\103\104\108\105\103\104\116")h.text:SetPoint("\076\069\070\084",(g and 16 or 0)+8,2)else h:SetNormalFontObject("\071\097\109\101\070\111\110\116\072\105\103\104\108\105\103\104\116\083\109\097\108\108")h:SetHighlightFontObject("\071\097\109\101\070\111\110\116\072\105\103\104\108\105\103\104\116\083\109\097\108\108")h.text:SetPoint("\076\069\070\084",(g and 16 or 0)+8*j,2)end if Q then h:EnableMouse(false)h.text:SetText("\124\099\102\102\056\048\056\048\056\048"..(S..FONT_COLOR_CODE_CLOSE))else h.text:SetText(S)h:EnableMouse(true)end if g then h.icon:SetTexture(g)h.icon:SetPoint("\076\069\070\084",8*j,j==1 and 0 or 1)else h.icon:SetTexture(nil)end if i then h.icon:SetTexCoord(E(i))else h.icon:SetTexCoord(0,1,0,1)end if a then if not N then s:SetNormalTexture(130838)s:SetPushedTexture(130836)else s:SetNormalTexture(130821)s:SetPushedTexture(130820)end s:Show()else s:Hide()end end local function v(h)local m=false for h,t in z(h)do if t.children==nil and t.visible~=false then m=true elseif t.children then m=m or v(t.children)end if m then return m end end return false end local function q(h,m,t,a,N)local z=y()z.value=m.value z.text=m.text z.icon=m.icon z.iconCoords=m.iconCoords z.disabled=m.disabled z.tree=t z.level=a z.parent=N z.visible=m.visible z.uniquevalue=P(z)if m.children then z.hasChildren=true else z.hasChildren=nil end h.lines[#h.lines+1]=z return z end local function T(h)local m=h.obj h:SetScript("\079\110\085\112\100\097\116\101",nil)m:RefreshTree(nil,true)end local function x(...)local h=D("\035",...)if h==1 then return...else return.....("\001"..x(D(2,...)))end end local function b(h)local m=h.button local t=m.obj local a=(t.status or t.localstatus).groups a[m.uniquevalue]=not a[m.uniquevalue]t:RefreshTree()end local function u(h)local m=h.obj m:Fire("\079\110\067\108\105\099\107",h.uniquevalue,h.selected)if not h.selected then m:SetSelected(h.uniquevalue)h.selected=true h:LockHighlight()m:RefreshTree()end t:ClearFocus()end local function d(h)local m=h.obj local t=(m.status or m.localstatus).groups t[h.uniquevalue]=not t[h.uniquevalue]m:RefreshTree()end local function G(h)local m=h.obj m:Fire("\079\110\066\117\116\116\111\110\069\110\116\101\114",h.uniquevalue,h)if m.enabletooltips then local m=t.tooltip m:SetOwner(h,"\065\078\067\072\079\082\095\078\079\078\069")m:ClearAllPoints()m:SetPoint("\076\069\070\084",h,"\082\073\071\072\084")m:SetText(h.text:GetText()or"",1,.82,0,true)m:Show()end end local function A(h)local m=h.obj m:Fire("\079\110\066\117\116\116\111\110\076\101\097\118\101",h.uniquevalue,h)if m.enabletooltips then t.tooltip:Hide()end end local function k(h,m)if h.obj.noupdate then return end local a=h.obj local N=a.status or a.localstatus N.scrollvalue=j(m+.5)a:RefreshTree()t:ClearFocus()end local function o(h)h.obj:RefreshTree()end local function B(h,m)local t=h.obj if t.showscroll then local h=t.scrollbar local a,N=h:GetMinMaxValues()local z=h:GetValue()local s=g(N,i(a,z-m))if z~=s then h:SetValue(s)end end end local function F(h)h:SetBackdropColor(1,1,1,0)end local function e(h)h:SetBackdropColor(1,1,1,.8)end local function r(h)local m=h:GetParent()m:StartSizing("\082\073\071\072\084")end local function K(h)local m=h:GetParent()local t=m.obj local a=m:GetParent()m:StopMovingOrSizing()m:SetUserPlaced(false)m:SetHeight(0)m:ClearAllPoints()m:SetPoint("\084\079\080\076\069\070\084",a,"\084\079\080\076\069\070\084",0,0)m:SetPoint("\066\079\084\084\079\077\076\069\070\084",a,"\066\079\084\084\079\077\076\069\070\084",0,0)local N=t.status or t.localstatus N.treewidth=m:GetWidth()m.obj:Fire("\079\110\084\114\101\101\082\101\115\105\122\101",m:GetWidth())m.obj:OnWidthSet(N.fullwidth)m.obj:DoLayout()end local p={OnAcquire=function(h)h:SetTreeWidth(f,W)h:EnableButtonTooltips(true)h.frame:SetScript("\079\110\085\112\100\097\116\101",T)end,OnRelease=function(h)h.status=nil h.tree=nil h.frame:SetScript("\079\110\085\112\100\097\116\101",nil)for m,t in N(h.localstatus)do if m=="\103\114\111\117\112\115"then for h in N(t)do t[h]=nil end else h.localstatus[m]=nil end end h.localstatus.scrollvalue=0 h.localstatus.treewidth=f h.localstatus.treesizable=W end;EnableButtonTooltips=function(h,m)h.enabletooltips=m end;CreateButton=function(h)local m=t:GetNextWidgetNum("\084\114\101\101\071\114\111\117\112\066\117\116\116\111\110")local a=L("\066\117\116\116\111\110",("\065\099\101\071\085\073\051\048\084\114\101\101\066\117\116\116\111\110\037\100"):format(m),h.treeframe,"\079\112\116\105\111\110\115\076\105\115\116\066\117\116\116\111\110\084\101\109\112\108\097\116\101")a.obj=h local N=a:CreateTexture(nil,"\079\086\069\082\076\065\089")N:SetWidth(14)N:SetHeight(14)a.icon=N a:SetScript("\079\110\067\108\105\099\107",u)a:SetScript("\079\110\068\111\117\098\108\101\067\108\105\099\107",d)a:SetScript("\079\110\069\110\116\101\114",G)a:SetScript("\079\110\076\101\097\118\101",A)a.toggle.button=a a.toggle:SetScript("\079\110\067\108\105\099\107",b)a.text:SetHeight(14)return a end;SetStatusTable=function(h,m)s(S(m)=="\116\097\098\108\101")h.status=m if not m.groups then m.groups={}end if not m.scrollvalue then m.scrollvalue=0 end if not m.treewidth then m.treewidth=f end if m.treesizable==nil then m.treesizable=W end h:SetTreeWidth(m.treewidth,m.treesizable)h:RefreshTree()end;SetTree=function(h,m,t)h.filter=t if m then s(S(m)=="\116\097\098\108\101")end h.tree=m h:RefreshTree()end,BuildLevel=function(h,m,t,a)local N=(h.status or h.localstatus).groups for z,s in z(m)do if s.children then if not h.filter or v(s.children)then local z=q(h,s,m,t,a)if N[z.uniquevalue]then h:BuildLevel(s.children,t+1,z)end end elseif s.visible~=false or not h.filter then q(h,s,m,t,a)end end end;RefreshTree=function(h,m,t)local a=h.buttons local s=h.lines for h,m in z(a)do m:Hide()end while s[1]do local h=U(s)for m in N(h)do h[m]=nil end J(h)end if not h.tree then return end local S=h.status or h.localstatus local g=S.groups local i=h.tree local D=h.treeframe S.scrollToSelection=S.scrollToSelection or m h:BuildLevel(i,1)local E=#s local Q=j(((h.treeframe:GetHeight()or 0)-20)/18)if Q<=0 then return end if h.frame:GetParent()==H and not t then h.frame:SetScript("\079\110\085\112\100\097\116\101",T)return end local L,y m=S.scrollToSelection S.scrollToSelection=nil if E<=Q then S.scrollvalue=0 h:ShowScroll(false)L,y=1,E else h:ShowScroll(true)h.noupdate=true h.scrollbar:SetMinMaxValues(0,E-Q)if E-S.scrollvalue<Q then S.scrollvalue=E-Q end h.noupdate=nil L,y=S.scrollvalue+1,S.scrollvalue+Q if m and S.selected then local h for m,t in z(s)do if t.uniquevalue==S.selected then h=m end end if not h then elseif h>=L and h<=y then else if h<L then S.scrollvalue=h-1 else S.scrollvalue=h-Q end L,y=S.scrollvalue+1,S.scrollvalue+Q end end if h.scrollbar:GetValue()~=S.scrollvalue then h.scrollbar:SetValue(S.scrollvalue)end end local f=1 for m=L,y,1 do local t=s[m]local N=a[f]if not N then N=h:CreateButton()a[f]=N N:SetParent(D)N:SetFrameLevel(D:GetFrameLevel()+1)N:ClearAllPoints()if f==1 then if h.showscroll then N:SetPoint("\084\079\080\082\073\071\072\084",-22,-10)N:SetPoint("\084\079\080\076\069\070\084",0,-10)else N:SetPoint("\084\079\080\082\073\071\072\084",0,-10)N:SetPoint("\084\079\080\076\069\070\084",0,-10)end else N:SetPoint("\084\079\080\082\073\071\072\084",a[f-1],"\066\079\084\084\079\077\082\073\071\072\084",0,0)N:SetPoint("\084\079\080\076\069\070\084",a[f-1],"\066\079\084\084\079\077\076\069\070\084",0,0)end end R(N,t,S.selected==t.uniquevalue,t.hasChildren,g[t.uniquevalue])N:Show()f=f+1 end end,SetSelected=function(h,m)local t=h.status or h.localstatus if t.selected~=m then t.selected=m h:Fire("\079\110\071\114\111\117\112\083\101\108\101\099\116\101\100",m)end end,Select=function(h,m,...)h.filter=false local t=h.status or h.localstatus local a=t.groups local N={...}for h=1,#N,1 do a[Q(N,"\001",1,h)]=true end t.selected=m h:RefreshTree(true)h:Fire("\079\110\071\114\111\117\112\083\101\108\101\099\116\101\100",m)end;SelectByPath=function(h,...)h:Select(x(...),...)end;SelectByValue=function(h,m)h:Select(m,("\001"):split(m))end,ShowScroll=function(h,m)h.showscroll=m if m then h.scrollbar:Show()if h.buttons[1]then h.buttons[1]:SetPoint("\084\079\080\082\073\071\072\084",h.treeframe,"\084\079\080\082\073\071\072\084",-22,-10)end else h.scrollbar:Hide()if h.buttons[1]then h.buttons[1]:SetPoint("\084\079\080\082\073\071\072\084",h.treeframe,"\084\079\080\082\073\071\072\084",0,-10)end end end,OnWidthSet=function(h,m)local t=h.content local a=h.treeframe local N=h.status or h.localstatus N.fullwidth=m local z=(m-N.treewidth)-20 if z<0 then z=0 end t:SetWidth(z)t.width=z local s=g(400,m-50)if s>100 and N.treewidth>s then h:SetTreeWidth(s,N.treesizable)end if a.SetResizeBounds then a:SetResizeBounds(100,1,s,1600)else a:SetMaxResize(s,1600)end end,OnHeightSet=function(h,m)local t=h.content local a=m-20 if a<0 then a=0 end t:SetHeight(a)t.height=a end,SetTreeWidth=function(h,m,t)if not t then if S(m)=="\110\117\109\098\101\114"then t=false elseif S(m)=="\098\111\111\108\101\097\110"then t=m m=f else t=false m=f end end h.treeframe:SetWidth(m)h.dragger:EnableMouse(t)local a=h.status or h.localstatus a.treewidth=m a.treesizable=t if a.fullwidth then h:OnWidthSet(a.fullwidth)end end,GetTreeWidth=function(h)local m=h.status or h.localstatus return m.treewidth or f end;LayoutFinished=function(h,m,t)if h.noAutoHeight then return end h:SetHeight((t or 0)+20)end}local V={bgFile="\073\110\116\101\114\102\097\099\101\092\067\104\097\116\070\114\097\109\101\092\067\104\097\116\070\114\097\109\101\066\097\099\107\103\114\111\117\110\100",edgeFile="\073\110\116\101\114\102\097\099\101\092\084\111\111\108\116\105\112\115\092\085\073-\084\111\111\108\116\105\112-\066\111\114\100\101\114",tile=true,tileSize=16;edgeSize=16;insets={left=3;right=3;top=5,bottom=3}}local X={bgFile="\073\110\116\101\114\102\097\099\101\092\084\111\111\108\116\105\112\115\092\085\073-\084\111\111\108\116\105\112-\066\097\099\107\103\114\111\117\110\100";edgeFile=nil;tile=true;tileSize=16,edgeSize=1,insets={left=3,right=3;top=7;bottom=7}}local function w()local m=t:GetNextWidgetNum(h)local a=L("\070\114\097\109\101",nil,H)local z=L("\070\114\097\109\101",nil,a,"\066\097\099\107\100\114\111\112\084\101\109\112\108\097\116\101")z:SetPoint("\084\079\080\076\069\070\084")z:SetPoint("\066\079\084\084\079\077\076\069\070\084")z:SetWidth(f)z:EnableMouseWheel(true)z:SetBackdrop(V)z:SetBackdropColor(.1,.1,.1,.5)z:SetBackdropBorderColor(.4,.4,.4)z:SetResizable(true)if z.SetResizeBounds then z:SetResizeBounds(100,1,400,1600)else z:SetMinResize(100,1)z:SetMaxResize(400,1600)end z:SetScript("\079\110\085\112\100\097\116\101",T)z:SetScript("\079\110\083\105\122\101\067\104\097\110\103\101\100",o)z:SetScript("\079\110\077\111\117\115\101\087\104\101\101\108",B)local s=L("\070\114\097\109\101",nil,z,"\066\097\099\107\100\114\111\112\084\101\109\112\108\097\116\101")s:SetWidth(8)s:SetPoint("\084\079\080",z,"\084\079\080\082\073\071\072\084")s:SetPoint("\066\079\084\084\079\077",z,"\066\079\084\084\079\077\082\073\071\072\084")s:SetBackdrop(X)s:SetBackdropColor(1,1,1,0)s:SetScript("\079\110\069\110\116\101\114",e)s:SetScript("\079\110\076\101\097\118\101",F)s:SetScript("\079\110\077\111\117\115\101\068\111\119\110",r)s:SetScript("\079\110\077\111\117\115\101\085\112",K)local S=L("\083\108\105\100\101\114",("\065\099\101\067\111\110\102\105\103\068\105\097\108\111\103\084\114\101\101\071\114\111\117\112\037\100\083\099\114\111\108\108\066\097\114"):format(m),z,"\085\073\080\097\110\101\108\083\099\114\111\108\108\066\097\114\084\101\109\112\108\097\116\101")S:SetScript("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",nil)S:SetPoint("\084\079\080\082\073\071\072\084",-10,-26)S:SetPoint("\066\079\084\084\079\077\082\073\071\072\084",-10,26)S:SetMinMaxValues(0,0)S:SetValueStep(1)S:SetValue(0)S:SetWidth(16)S:SetScript("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",k)local g=S:CreateTexture(nil,"\066\065\067\075\071\082\079\085\078\068")g:SetAllPoints(S)g:SetColorTexture(0,0,0,.4)local i=L("\070\114\097\109\101",nil,a,"\066\097\099\107\100\114\111\112\084\101\109\112\108\097\116\101")i:SetPoint("\084\079\080\076\069\070\084",z,"\084\079\080\082\073\071\072\084")i:SetPoint("\066\079\084\084\079\077\082\073\071\072\084")i:SetBackdrop(V)i:SetBackdropColor(.1,.1,.1,.5)i:SetBackdropBorderColor(.4,.4,.4)local j=L("\070\114\097\109\101",nil,i)j:SetPoint("\084\079\080\076\069\070\084",10,-10)j:SetPoint("\066\079\084\084\079\077\082\073\071\072\084",-10,10)local D={frame=a,lines={},levels={},buttons={};hasChildren={};localstatus={groups={},scrollvalue=0};filter=false;treeframe=z;dragger=s,scrollbar=S;border=i,content=j;type=h}for h,m in N(p)do D[h]=m end z.obj,s.obj,S.obj=D,D,D return t:RegisterAsContainer(D)end t:RegisterWidgetType(h,w,m)
--[[-----------------------------------------------------------------------------
TreeGroup Container
Container that uses a tree control to switch between groups.
-------------------------------------------------------------------------------]]
local Type, Version = "TreeGroup", 47
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end

-- Lua APIs
local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
local math_min, math_max, floor = math.min, math.max, math.floor
local select, tremove, unpack, tconcat = select, table.remove, unpack, table.concat

-- WoW APIs
local CreateFrame, UIParent = CreateFrame, UIParent

-- Recycling functions
local new, del
do
	local pool = setmetatable({},{__mode='k'})
	function new()
		local t = next(pool)
		if t then
			pool[t] = nil
			return t
		else
			return {}
		end
	end
	function del(t)
		for k in pairs(t) do
			t[k] = nil
		end
		pool[t] = true
	end
end

local DEFAULT_TREE_WIDTH = 175
local DEFAULT_TREE_SIZABLE = true

--[[-----------------------------------------------------------------------------
Support functions
-------------------------------------------------------------------------------]]
local function GetButtonUniqueValue(line)
	local parent = line.parent
	if parent and parent.value then
		return GetButtonUniqueValue(parent).."\001"..line.value
	else
		return line.value
	end
end

local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
	local self = button.obj
	local toggle = button.toggle
	local text = treeline.text or ""
	local icon = treeline.icon
	local iconCoords = treeline.iconCoords
	local level = treeline.level
	local value = treeline.value
	local uniquevalue = treeline.uniquevalue
	local disabled = treeline.disabled

	button.treeline = treeline
	button.value = value
	button.uniquevalue = uniquevalue
	if selected then
		button:LockHighlight()
		button.selected = true
	else
		button:UnlockHighlight()
		button.selected = false
	end
	button.level = level
	if ( level == 1 ) then
		button:SetNormalFontObject("GameFontNormal")
		button:SetHighlightFontObject("GameFontHighlight")
		button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
	else
		button:SetNormalFontObject("GameFontHighlightSmall")
		button:SetHighlightFontObject("GameFontHighlightSmall")
		button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
	end

	if disabled then
		button:EnableMouse(false)
		button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
	else
		button.text:SetText(text)
		button:EnableMouse(true)
	end

	if icon then
		button.icon:SetTexture(icon)
		button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1)
	else
		button.icon:SetTexture(nil)
	end

	if iconCoords then
		button.icon:SetTexCoord(unpack(iconCoords))
	else
		button.icon:SetTexCoord(0, 1, 0, 1)
	end

	if canExpand then
		if not isExpanded then
			toggle:SetNormalTexture(130838) -- Interface\\Buttons\\UI-PlusButton-UP
			toggle:SetPushedTexture(130836) -- Interface\\Buttons\\UI-PlusButton-DOWN
		else
			toggle:SetNormalTexture(130821) -- Interface\\Buttons\\UI-MinusButton-UP
			toggle:SetPushedTexture(130820) -- Interface\\Buttons\\UI-MinusButton-DOWN
		end
		toggle:Show()
	else
		toggle:Hide()
	end
end

local function ShouldDisplayLevel(tree)
	local result = false
	for k, v in ipairs(tree) do
		if v.children == nil and v.visible ~= false then
			result = true
		elseif v.children then
			result = result or ShouldDisplayLevel(v.children)
		end
		if result then return result end
	end
	return false
end

local function addLine(self, v, tree, level, parent)
	local line = new()
	line.value = v.value
	line.text = v.text
	line.icon = v.icon
	line.iconCoords = v.iconCoords
	line.disabled = v.disabled
	line.tree = tree
	line.level = level
	line.parent = parent
	line.visible = v.visible
	line.uniquevalue = GetButtonUniqueValue(line)
	if v.children then
		line.hasChildren = true
	else
		line.hasChildren = nil
	end
	self.lines[#self.lines+1] = line
	return line
end

--fire an update after one frame to catch the treeframes height
local function FirstFrameUpdate(frame)
	local self = frame.obj
	frame:SetScript("OnUpdate", nil)
	self:RefreshTree(nil, true)
end

local function BuildUniqueValue(...)
	local n = select('#', ...)
	if n == 1 then
		return ...
	else
		return (...).."\001"..BuildUniqueValue(select(2,...))
	end
end

--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]
local function Expand_OnClick(frame)
	local button = frame.button
	local self = button.obj
	local status = (self.status or self.localstatus).groups
	status[button.uniquevalue] = not status[button.uniquevalue]
	self:RefreshTree()
end

local function Button_OnClick(frame)
	local self = frame.obj
	self:Fire("OnClick", frame.uniquevalue, frame.selected)
	if not frame.selected then
		self:SetSelected(frame.uniquevalue)
		frame.selected = true
		frame:LockHighlight()
		self:RefreshTree()
	end
	AceGUI:ClearFocus()
end

local function Button_OnDoubleClick(button)
	local self = button.obj
	local status = (self.status or self.localstatus).groups
	status[button.uniquevalue] = not status[button.uniquevalue]
	self:RefreshTree()
end

local function Button_OnEnter(frame)
	local self = frame.obj
	self:Fire("OnButtonEnter", frame.uniquevalue, frame)

	if self.enabletooltips then
		local tooltip = AceGUI.tooltip
		tooltip:SetOwner(frame, "ANCHOR_NONE")
		tooltip:ClearAllPoints()
		tooltip:SetPoint("LEFT",frame,"RIGHT")
		tooltip:SetText(frame.text:GetText() or "", 1, .82, 0, true)

		tooltip:Show()
	end
end

local function Button_OnLeave(frame)
	local self = frame.obj
	self:Fire("OnButtonLeave", frame.uniquevalue, frame)

	if self.enabletooltips then
		AceGUI.tooltip:Hide()
	end
end

local function OnScrollValueChanged(frame, value)
	if frame.obj.noupdate then return end
	local self = frame.obj
	local status = self.status or self.localstatus
	status.scrollvalue = floor(value + 0.5)
	self:RefreshTree()
	AceGUI:ClearFocus()
end

local function Tree_OnSizeChanged(frame)
	frame.obj:RefreshTree()
end

local function Tree_OnMouseWheel(frame, delta)
	local self = frame.obj
	if self.showscroll then
		local scrollbar = self.scrollbar
		local min, max = scrollbar:GetMinMaxValues()
		local value = scrollbar:GetValue()
		local newvalue = math_min(max,math_max(min,value - delta))
		if value ~= newvalue then
			scrollbar:SetValue(newvalue)
		end
	end
end

local function Dragger_OnLeave(frame)
	frame:SetBackdropColor(1, 1, 1, 0)
end

local function Dragger_OnEnter(frame)
	frame:SetBackdropColor(1, 1, 1, 0.8)
end

local function Dragger_OnMouseDown(frame)
	local treeframe = frame:GetParent()
	treeframe:StartSizing("RIGHT")
end

local function Dragger_OnMouseUp(frame)
	local treeframe = frame:GetParent()
	local self = treeframe.obj
	local treeframeParent = treeframe:GetParent()
	treeframe:StopMovingOrSizing()
	--treeframe:SetScript("OnUpdate", nil)
	treeframe:SetUserPlaced(false)
	--Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
	treeframe:SetHeight(0)
	treeframe:ClearAllPoints()
	treeframe:SetPoint("TOPLEFT", treeframeParent, "TOPLEFT",0,0)
	treeframe:SetPoint("BOTTOMLEFT", treeframeParent, "BOTTOMLEFT",0,0)

	local status = self.status or self.localstatus
	status.treewidth = treeframe:GetWidth()

	treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
	-- recalculate the content width
	treeframe.obj:OnWidthSet(status.fullwidth)
	-- update the layout of the content
	treeframe.obj:DoLayout()
end

--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
	["OnAcquire"] = function(self)
		self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE)
		self:EnableButtonTooltips(true)
		self.frame:SetScript("OnUpdate", FirstFrameUpdate)
	end,

	["OnRelease"] = function(self)
		self.status = nil
		self.tree = nil
		self.frame:SetScript("OnUpdate", nil)
		for k, v in pairs(self.localstatus) do
			if k == "groups" then
				for k2 in pairs(v) do
					v[k2] = nil
				end
			else
				self.localstatus[k] = nil
			end
		end
		self.localstatus.scrollvalue = 0
		self.localstatus.treewidth = DEFAULT_TREE_WIDTH
		self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
	end,

	["EnableButtonTooltips"] = function(self, enable)
		self.enabletooltips = enable
	end,

	["CreateButton"] = function(self)
		local num = AceGUI:GetNextWidgetNum("TreeGroupButton")
		local button = CreateFrame("Button", ("AceGUI30TreeButton%d"):format(num), self.treeframe, "OptionsListButtonTemplate")
		button.obj = self

		local icon = button:CreateTexture(nil, "OVERLAY")
		icon:SetWidth(14)
		icon:SetHeight(14)
		button.icon = icon

		button:SetScript("OnClick",Button_OnClick)
		button:SetScript("OnDoubleClick", Button_OnDoubleClick)
		button:SetScript("OnEnter",Button_OnEnter)
		button:SetScript("OnLeave",Button_OnLeave)

		button.toggle.button = button
		button.toggle:SetScript("OnClick",Expand_OnClick)

		button.text:SetHeight(14) -- Prevents text wrapping

		return button
	end,

	["SetStatusTable"] = function(self, status)
		assert(type(status) == "table")
		self.status = status
		if not status.groups then
			status.groups = {}
		end
		if not status.scrollvalue then
			status.scrollvalue = 0
		end
		if not status.treewidth then
			status.treewidth = DEFAULT_TREE_WIDTH
		end
		if status.treesizable == nil then
			status.treesizable = DEFAULT_TREE_SIZABLE
		end
		self:SetTreeWidth(status.treewidth,status.treesizable)
		self:RefreshTree()
	end,

	--sets the tree to be displayed
	["SetTree"] = function(self, tree, filter)
		self.filter = filter
		if tree then
			assert(type(tree) == "table")
		end
		self.tree = tree
		self:RefreshTree()
	end,

	["BuildLevel"] = function(self, tree, level, parent)
		local groups = (self.status or self.localstatus).groups

		for i, v in ipairs(tree) do
			if v.children then
				if not self.filter or ShouldDisplayLevel(v.children) then
					local line = addLine(self, v, tree, level, parent)
					if groups[line.uniquevalue] then
						self:BuildLevel(v.children, level+1, line)
					end
				end
			elseif v.visible ~= false or not self.filter then
				addLine(self, v, tree, level, parent)
			end
		end
	end,

	["RefreshTree"] = function(self,scrollToSelection,fromOnUpdate)
		local buttons = self.buttons
		local lines = self.lines

		for i, v in ipairs(buttons) do
			v:Hide()
		end
		while lines[1] do
			local t = tremove(lines)
			for k in pairs(t) do
				t[k] = nil
			end
			del(t)
		end

		if not self.tree then return end
		--Build the list of visible entries from the tree and status tables
		local status = self.status or self.localstatus
		local groupstatus = status.groups
		local tree = self.tree

		local treeframe = self.treeframe

		status.scrollToSelection = status.scrollToSelection or scrollToSelection	-- needs to be cached in case the control hasn't been drawn yet (code bails out below)

		self:BuildLevel(tree, 1)

		local numlines = #lines

		local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
		if maxlines <= 0 then return end

		if self.frame:GetParent() == UIParent and not fromOnUpdate then
			self.frame:SetScript("OnUpdate", FirstFrameUpdate)
			return
		end

		local first, last

		scrollToSelection = status.scrollToSelection
		status.scrollToSelection = nil

		if numlines <= maxlines then
			--the whole tree fits in the frame
			status.scrollvalue = 0
			self:ShowScroll(false)
			first, last = 1, numlines
		else
			self:ShowScroll(true)
			--scrolling will be needed
			self.noupdate = true
			self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
			--check if we are scrolled down too far
			if numlines - status.scrollvalue < maxlines then
				status.scrollvalue = numlines - maxlines
			end
			self.noupdate = nil
			first, last = status.scrollvalue+1, status.scrollvalue + maxlines
			--show selection?
			if scrollToSelection and status.selected then
				local show
				for i,line in ipairs(lines) do	-- find the line number
					if line.uniquevalue==status.selected then
						show=i
					end
				end
				if not show then
					-- selection was deleted or something?
				elseif show>=first and show<=last then
					-- all good
				else
					-- scrolling needed!
					if show<first then
						status.scrollvalue = show-1
					else
						status.scrollvalue = show-maxlines
					end
					first, last = status.scrollvalue+1, status.scrollvalue + maxlines
				end
			end
			if self.scrollbar:GetValue() ~= status.scrollvalue then
				self.scrollbar:SetValue(status.scrollvalue)
			end
		end

		local buttonnum = 1
		for i = first, last do
			local line = lines[i]
			local button = buttons[buttonnum]
			if not button then
				button = self:CreateButton()

				buttons[buttonnum] = button
				button:SetParent(treeframe)
				button:SetFrameLevel(treeframe:GetFrameLevel()+1)
				button:ClearAllPoints()
				if buttonnum == 1 then
					if self.showscroll then
						button:SetPoint("TOPRIGHT", -22, -10)
						button:SetPoint("TOPLEFT", 0, -10)
					else
						button:SetPoint("TOPRIGHT", 0, -10)
						button:SetPoint("TOPLEFT", 0, -10)
					end
				else
					button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
					button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
				end
			end

			UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
			button:Show()
			buttonnum = buttonnum + 1
		end

	end,

	["SetSelected"] = function(self, value)
		local status = self.status or self.localstatus
		if status.selected ~= value then
			status.selected = value
			self:Fire("OnGroupSelected", value)
		end
	end,

	["Select"] = function(self, uniquevalue, ...)
		self.filter = false
		local status = self.status or self.localstatus
		local groups = status.groups
		local path = {...}
		for i = 1, #path do
			groups[tconcat(path, "\001", 1, i)] = true
		end
		status.selected = uniquevalue
		self:RefreshTree(true)
		self:Fire("OnGroupSelected", uniquevalue)
	end,

	["SelectByPath"] = function(self, ...)
		self:Select(BuildUniqueValue(...), ...)
	end,

	["SelectByValue"] = function(self, uniquevalue)
		self:Select(uniquevalue, ("\001"):split(uniquevalue))
	end,

	["ShowScroll"] = function(self, show)
		self.showscroll = show
		if show then
			self.scrollbar:Show()
			if self.buttons[1] then
				self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
			end
		else
			self.scrollbar:Hide()
			if self.buttons[1] then
				self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
			end
		end
	end,

	["OnWidthSet"] = function(self, width)
		local content = self.content
		local treeframe = self.treeframe
		local status = self.status or self.localstatus
		status.fullwidth = width

		local contentwidth = width - status.treewidth - 20
		if contentwidth < 0 then
			contentwidth = 0
		end
		content:SetWidth(contentwidth)
		content.width = contentwidth

		local maxtreewidth = math_min(400, width - 50)

		if maxtreewidth > 100 and status.treewidth > maxtreewidth then
			self:SetTreeWidth(maxtreewidth, status.treesizable)
		end
		if treeframe.SetResizeBounds then
			treeframe:SetResizeBounds(100, 1, maxtreewidth, 1600)
		else
			treeframe:SetMaxResize(maxtreewidth, 1600)
		end
	end,

	["OnHeightSet"] = function(self, height)
		local content = self.content
		local contentheight = height - 20
		if contentheight < 0 then
			contentheight = 0
		end
		content:SetHeight(contentheight)
		content.height = contentheight
	end,

	["SetTreeWidth"] = function(self, treewidth, resizable)
		if not resizable then
			if type(treewidth) == 'number' then
				resizable = false
			elseif type(treewidth) == 'boolean' then
				resizable = treewidth
				treewidth = DEFAULT_TREE_WIDTH
			else
				resizable = false
				treewidth = DEFAULT_TREE_WIDTH
			end
		end
		self.treeframe:SetWidth(treewidth)
		self.dragger:EnableMouse(resizable)

		local status = self.status or self.localstatus
		status.treewidth = treewidth
		status.treesizable = resizable

		-- recalculate the content width
		if status.fullwidth then
			self:OnWidthSet(status.fullwidth)
		end
	end,

	["GetTreeWidth"] = function(self)
		local status = self.status or self.localstatus
		return status.treewidth or DEFAULT_TREE_WIDTH
	end,

	["LayoutFinished"] = function(self, width, height)
		if self.noAutoHeight then return end
		self:SetHeight((height or 0) + 20)
	end
}

--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]
local PaneBackdrop  = {
	bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
	edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
	tile = true, tileSize = 16, edgeSize = 16,
	insets = { left = 3, right = 3, top = 5, bottom = 3 }
}

local DraggerBackdrop  = {
	bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
	edgeFile = nil,
	tile = true, tileSize = 16, edgeSize = 1,
	insets = { left = 3, right = 3, top = 7, bottom = 7 }
}

local function Constructor()
	local num = AceGUI:GetNextWidgetNum(Type)
	local frame = CreateFrame("Frame", nil, UIParent)

	local treeframe = CreateFrame("Frame", nil, frame, "BackdropTemplate")
	treeframe:SetPoint("TOPLEFT")
	treeframe:SetPoint("BOTTOMLEFT")
	treeframe:SetWidth(DEFAULT_TREE_WIDTH)
	treeframe:EnableMouseWheel(true)
	treeframe:SetBackdrop(PaneBackdrop)
	treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
	treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
	treeframe:SetResizable(true)
	if treeframe.SetResizeBounds then -- WoW 10.0
		treeframe:SetResizeBounds(100, 1, 400, 1600)
	else
		treeframe:SetMinResize(100, 1)
		treeframe:SetMaxResize(400, 1600)
	end
	treeframe:SetScript("OnUpdate", FirstFrameUpdate)
	treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
	treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)

	local dragger = CreateFrame("Frame", nil, treeframe, "BackdropTemplate")
	dragger:SetWidth(8)
	dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
	dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
	dragger:SetBackdrop(DraggerBackdrop)
	dragger:SetBackdropColor(1, 1, 1, 0)
	dragger:SetScript("OnEnter", Dragger_OnEnter)
	dragger:SetScript("OnLeave", Dragger_OnLeave)
	dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
	dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)

	local scrollbar = CreateFrame("Slider", ("AceConfigDialogTreeGroup%dScrollBar"):format(num), treeframe, "UIPanelScrollBarTemplate")
	scrollbar:SetScript("OnValueChanged", nil)
	scrollbar:SetPoint("TOPRIGHT", -10, -26)
	scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
	scrollbar:SetMinMaxValues(0,0)
	scrollbar:SetValueStep(1)
	scrollbar:SetValue(0)
	scrollbar:SetWidth(16)
	scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)

	local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
	scrollbg:SetAllPoints(scrollbar)
	scrollbg:SetColorTexture(0,0,0,0.4)

	local border = CreateFrame("Frame", nil, frame, "BackdropTemplate")
	border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
	border:SetPoint("BOTTOMRIGHT")
	border:SetBackdrop(PaneBackdrop)
	border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
	border:SetBackdropBorderColor(0.4, 0.4, 0.4)

	--Container Support
	local content = CreateFrame("Frame", nil, border)
	content:SetPoint("TOPLEFT", 10, -10)
	content:SetPoint("BOTTOMRIGHT", -10, 10)

	local widget = {
		frame        = frame,
		lines        = {},
		levels       = {},
		buttons      = {},
		hasChildren  = {},
		localstatus  = { groups = {}, scrollvalue = 0 },
		filter       = false,
		treeframe    = treeframe,
		dragger      = dragger,
		scrollbar    = scrollbar,
		border       = border,
		content      = content,
		type         = Type
	}
	for method, func in pairs(methods) do
		widget[method] = func
	end
	treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget

	return AceGUI:RegisterAsContainer(widget)
end

AceGUI:RegisterWidgetType(Type, Constructor, Version)
