local l=LibStub local r=l("\065\099\101\071\085\073-\051\046\048")local i=l("\065\099\101\067\111\110\102\105\103\082\101\103\105\115\116\114\121-\051\046\048")local y,P="\065\099\101\067\111\110\102\105\103\068\105\097\108\111\103-\051\046\048",86 local g,B=l:NewLibrary(y,P)if not g then return end g.OpenFrames=g.OpenFrames or{}g.Status=g.Status or{}g.frame=g.frame or CreateFrame("\070\114\097\109\101")g.tooltip=g.tooltip or CreateFrame("\071\097\109\101\084\111\111\108\116\105\112","\065\099\101\067\111\110\102\105\103\068\105\097\108\111\103\084\111\111\108\116\105\112",UIParent,"\071\097\109\101\084\111\111\108\116\105\112\084\101\109\112\108\097\116\101")g.frame.apps=g.frame.apps or{}g.frame.closing=g.frame.closing or{}g.frame.closeAllOverride=g.frame.closeAllOverride or{}local C,U,f,c=table.insert,table.sort,table.remove,table.wipe local W,u=string.match,string.format local G=error local o,e,M,O,q,D=pairs,next,select,type,unpack,ipairs local V,Z=tostring,tonumber local T,N,z=math.min,math.max,math.floor local E={}local x=xpcall local function t(l)return(geterrorhandler())(l)end local function b(l,...)if l then return x(l,t,...)end end local d=170 local p,H,m do local l=setmetatable({},{__mode="\107"})function p()local r=e(l)if r then l[r]=nil return r else return{}end end function m(l)local r=p()for l,i in o(l)do r[l]=i end return r end function H(r)c(r)l[r]=true end end local function Y(...)for l=1,M("\035",...),1 do if M(l,...)~=nil then return M(l,...)end end end local function J(l,r)if l.plugins then for l,i in o(l.plugins)do if i[r]then return i[r]end end end return l.args[r]end local j={set=true;get=true;func=true,confirm=true;validate=true,disabled=true,hidden=true}local X={name=true,desc=true;icon=true;usage=true;width=true,image=true;fontSize=true;tooltipHyperlink=true}local I={type=true;descStyle=true;imageWidth=true;imageHeight=true}local function a(l,r,i,P,g,...)local B=j[l]local C if B then local r=i if r[l]~=nil then C=r[l]end for i=1,#P,1 do r=J(r,P[i])if r[l]~=nil then C=r[l]end end else C=r[l]end if not I[l]and(O(C)=="\102\117\110\099\116\105\111\110"or not X[l]and O(C)=="\115\116\114\105\110\103")then local B=p()local U=i local f=U.handler for l=1,#P,1 do U=J(U,P[l])B[l]=P[l]f=U.handler or f end B.options=i B.appName=g B[0]=g B.arg=r.arg B.handler=f B.option=r B.type=r.type B.uiType="\100\105\097\108\111\103"B.uiName=y local c,W,o,e if O(C)=="\102\117\110\099\116\105\111\110"then c,W,o,e=C(B,...)else if f and f[C]then c,W,o,e=f[C](f,B,...)else G(u("\077\101\116\104\111\100 \037\115 \100\111\101\115\110\'\116 \101\120\105\115\116 \105\110 \104\097\110\100\108\101\114 \102\111\114 \116\121\112\101 \037\115",C,l))end end H(B)return c,W,o,e else return C end end local h local F local function n(l,r)if not l then return true end if not r then return false end local i,y=h[l]or 100,h[r]or 100 if i==y then local i=O(F[l])=="\115\116\114\105\110\103"and F[l]or""local y=O(F[r])=="\115\116\114\105\110\103"and F[r]or""return i:upper()<y:upper()end if i<0 then if y>=0 then return false end else if y<0 then return true end end return i<y end local function K(l,r,i,y,P,g)h=p()F=p()if l.plugins then for l,B in o(l.plugins)do for l,B in o(B)do if not i[l]then C(r,l)i[l]=B P[#P+1]=l h[l]=a("\111\114\100\101\114",B,y,P,g)F[l]=a("\110\097\109\101",B,y,P,g)P[#P]=nil end end end end for l,B in o(l.args)do if not i[l]then C(r,l)i[l]=B P[#P+1]=l h[l]=a("\111\114\100\101\114",B,y,P,g)F[l]=a("\110\097\109\101",B,y,P,g)P[#P]=nil end end U(r,n)H(h)H(F)end local function w(l)if l.children then local r=l.children for l=1,#r,1 do w(r[l])H(r[l])end H(r)end end local function L(l,r)local i=l:GetUserDataTable()if i.path then H(i.path)end if l.type=="\084\114\101\101\071\114\111\117\112"then local r=i.tree l:SetTree(nil)if r then for l=1,#r,1 do w(r[l])H(r[l])end H(r)end end if l.type=="\084\097\098\071\114\111\117\112"then l:SetTabs(nil)if i.tablist then H(i.tablist)end end if l.type=="\068\114\111\112\100\111\119\110\071\114\111\117\112"then l:SetGroupList(nil)if i.grouplist then H(i.grouplist)end if i.orderlist then H(i.orderlist)end end end function g.GetStatusTable(i,l,r)local y=i.Status if not y[l]then y[l]={}y[l].status={}y[l].children={}end y=y[l]if r then for l=1,#r,1 do local i=r[l]if not y.children[i]then y.children[i]={}y.children[i].status={}y.children[i].children={}end y=y.children[i]end end return y.status end function g.SelectGroup(r,l,...)local P=p()local g=i:GetOptionsTable(l)if not g then G(("\037\115 \105\115\110\'\116 \114\101\103\105\115\116\101\100 \119\105\116\104 \065\099\101\067\111\110\102\105\103\082\101\103\105\115\116\114\121\044 \117\110\097\098\108\101 \116\111 \111\112\101\110 \099\111\110\102\105\103"):format(l),2)end local B=g("\100\105\097\108\111\103",y)local U=B local f=r:GetStatusTable(l,P)if not f.groups then f.groups={}end f=f.groups local c local W for i=1,M("\035",...),1 do local y=M(i,...)if U.childGroups=="\116\097\098"or U.childGroups=="\115\101\108\101\099\116"then f.selected=y c=nil else if c then c=c..("\001"..y)else c=y if not f.groups then f.groups={}end W=f end W.selected=c W.groups[c]=true end U=J(U,y)if not U then break end C(P,y)f=r:GetStatusTable(l,P)if not f.groups then f.groups={}end f=f.groups end H(P)i:NotifyChange(l)end local function A(l,r)local i=l:GetUserDataTable()local y=i.option local P=i.options local B=i.path local C=i.appName local U=g.tooltip U:SetOwner(l.frame,"\065\078\067\072\079\082\095\084\079\080\082\073\071\072\084")local f=a("\116\111\111\108\116\105\112\072\121\112\101\114\108\105\110\107",y,P,B,C)if f then U:SetHyperlink(f)U:Show()return end local c=a("\110\097\109\101",y,P,B,C)local W=a("\100\101\115\099",y,P,B,C)local u=a("\117\115\097\103\101",y,P,B,C)local G=y.descStyle if G and G~="\116\111\111\108\116\105\112"then return end U:SetText(c,1,.82,0,true)if y.type=="\109\117\108\116\105\115\101\108\101\099\116"then U:AddLine(i.text,.5,.5,.8,true)end if O(W)=="\115\116\114\105\110\103"then U:AddLine(W,1,1,1,true)end if O(u)=="\115\116\114\105\110\103"then U:AddLine("\085\115\097\103\101\058 "..u,NORMAL_FONT_COLOR.r,NORMAL_FONT_COLOR.g,NORMAL_FONT_COLOR.b,true)end U:Show()end local function s(l,r)g.tooltip:Hide()end local function Q(l)if l.type=="\101\120\101\099\117\116\101"then return"\102\117\110\099"else return"\115\101\116"end end do local l=g.popup if not l or B<81 then l=CreateFrame("\070\114\097\109\101",nil,UIParent)g.popup=l l:Hide()l:SetPoint("\067\069\078\084\069\082",UIParent,"\067\069\078\084\069\082")l:SetSize(320,72)l:EnableMouse(true)l:SetFrameStrata("\084\079\079\076\084\073\080")l:SetFrameLevel(100)l:SetScript("\079\110\075\101\121\068\111\119\110",function(l,r)if r=="\069\083\067\065\080\069"then l:SetPropagateKeyboardInput(false)if l.cancel:IsShown()then l.cancel:Click()else l:Hide()end else l:SetPropagateKeyboardInput(true)end end)local r=CreateFrame("\070\114\097\109\101",nil,l,"\068\105\097\108\111\103\066\111\114\100\101\114\079\112\097\113\117\101\084\101\109\112\108\097\116\101")r:SetAllPoints(l)l:SetFixedFrameStrata(true)l:SetFixedFrameLevel(true)local i=l:CreateFontString(nil,"\065\082\084\087\079\082\075","\071\097\109\101\070\111\110\116\072\105\103\104\108\105\103\104\116")i:SetSize(290,0)i:SetPoint("\084\079\080",0,-16)l.text=i local function y(r)local i=CreateFrame("\066\117\116\116\111\110",nil,l)i:SetSize(128,21)i:SetNormalFontObject(GameFontNormal)i:SetHighlightFontObject(GameFontHighlight)i:SetNormalTexture(130763);(i:GetNormalTexture()):SetTexCoord(.0,1.0,.0,.71875)i:SetPushedTexture(130761);(i:GetPushedTexture()):SetTexCoord(.0,1.0,.0,.71875)i:SetHighlightTexture(130762);(i:GetHighlightTexture()):SetTexCoord(.0,1.0,.0,.71875)i:SetText(r)return i end local P=y(ACCEPT)P:SetPoint("\066\079\084\084\079\077\082\073\071\072\084",l,"\066\079\084\084\079\077",-6,16)l.accept=P local B=y(CANCEL)B:SetPoint("\076\069\070\084",P,"\082\073\071\072\084",13,0)l.cancel=B end end local function S(l,r,i,y,P,B,...)local C=g.popup C:Show()C.text:SetText(P)local U=61+C.text:GetHeight()C:SetHeight(U)C.accept:ClearAllPoints()C.accept:SetPoint("\066\079\084\084\079\077\082\073\071\072\084",C,"\066\079\084\084\079\077",-6,16)C.cancel:Show()local f={...}local c=M("\035",...)C.accept:SetScript("\079\110\067\108\105\099\107",function(P)b(B,q(f,1,c))g:Open(l,r,q(i or E))C:Hide()P:SetScript("\079\110\067\108\105\099\107",nil)C.cancel:SetScript("\079\110\067\108\105\099\107",nil)H(y)end)C.cancel:SetScript("\079\110\067\108\105\099\107",function(P)g:Open(l,r,q(i or E))C:Hide()P:SetScript("\079\110\067\108\105\099\107",nil)C.accept:SetScript("\079\110\067\108\105\099\107",nil)H(y)end)end local function v(l)local r=g.popup r:Show()r.text:SetText(l)local i=61+r.text:GetHeight()r:SetHeight(i)r.accept:ClearAllPoints()r.accept:SetPoint("\066\079\084\084\079\077",r,"\066\079\084\084\079\077",0,16)r.cancel:Hide()r.accept:SetScript("\079\110\067\108\105\099\107",function()r:Hide()end)end local function R(l,r,...)local i=l:GetUserDataTable()local P=i.option local B=i.options local C=i.path local U=p()local f local c=B local o=Q(P)local e local M local D if c[o]~=nil then f=c[o]end e=c.handler M=c.confirm D=c.validate for l=1,#C,1 do local r=C[l]c=J(c,r)U[l]=r if c[o]~=nil then f=c[o]end e=c.handler or e if c.confirm~=nil then M=c.confirm end if c.validate~=nil then D=c.validate end end U.options=B U.appName=i.appName U.arg=P.arg U.handler=e U.option=P U.type=P.type U.uiType="\100\105\097\108\111\103"U.uiName=y local V if O(P.name)=="\102\117\110\099\116\105\111\110"then V=P.name(U)elseif O(P.name)=="\115\116\114\105\110\103"then V=P.name else V=""end local Z=P.usage local T=P.pattern local N=true if P.type=="\105\110\112\117\116"then if O(T)=="\115\116\114\105\110\103"then if not W(...,T)then N=false end end end local z if N and P.type~="\101\120\101\099\117\116\101"then if O(D)=="\115\116\114\105\110\103"then if e and e[D]then z,N=b(e[D],e,U,...)if not z then N=false end else G(u("\077\101\116\104\111\100 \037\115 \100\111\101\115\110\'\116 \101\120\105\115\116 \105\110 \104\097\110\100\108\101\114 \102\111\114 \116\121\112\101 \101\120\101\099\117\116\101",D))end elseif O(D)=="\102\117\110\099\116\105\111\110"then z,N=b(D,U,...)if not z then N=false end end end if not N or O(N)=="\115\116\114\105\110\103"then if not N then if Z then N=V..("\058 "..Z)else if T then N=V..("\058 \069\120\112\101\099\116\101\100 "..T)else N=V.."\058 \073\110\118\097\108\105\100 \086\097\108\117\101"end end end if i.rootframe.SetStatusText then i.rootframe:SetStatusText(N)else v(N)end PlaySound(882)H(U)return true else local l=P.confirmText if O(M)=="\115\116\114\105\110\103"then if e and e[M]then z,M=b(e[M],e,U,...)if z and O(M)=="\115\116\114\105\110\103"then l=M M=true elseif not z then M=false end else G(u("\077\101\116\104\111\100 \037\115 \100\111\101\115\110\'\116 \101\120\105\115\116 \105\110 \104\097\110\100\108\101\114 \102\111\114 \116\121\112\101 \099\111\110\102\105\114\109",M))end elseif O(M)=="\102\117\110\099\116\105\111\110"then z,M=b(M,U,...)if z and O(M)=="\115\116\114\105\110\103"then l=M M=true elseif not z then M=false end end if O(M)=="\098\111\111\108\101\097\110"then if M then if not l then local r,i=P.name,P.desc if O(r)=="\102\117\110\099\116\105\111\110"then r=r(U)end if O(i)=="\102\117\110\099\116\105\111\110"then i=i(U)end l=r if i then l=l..(" - "..i)end end local r=i.rootframe:GetUserData("\105\115\099\117\115\116\111\109")local y if r then y=i.rootframe end local g=i.rootframe:GetUserData("\098\097\115\101\112\097\116\104")if O(f)=="\115\116\114\105\110\103"then if e and e[f]then S(i.appName,y,g,U,l,e[f],e,U,...)else G(u("\077\101\116\104\111\100 \037\115 \100\111\101\115\110\'\116 \101\120\105\115\116 \105\110 \104\097\110\100\108\101\114 \102\111\114 \116\121\112\101 \102\117\110\099",f))end elseif O(f)=="\102\117\110\099\116\105\111\110"then S(i.appName,y,g,U,l,f,U,...)end return end end if O(f)=="\115\116\114\105\110\103"then if e and e[f]then b(e[f],e,U,...)else G(u("\077\101\116\104\111\100 \037\115 \100\111\101\115\110\'\116 \101\120\105\115\116 \105\110 \104\097\110\100\108\101\114 \102\111\114 \116\121\112\101 \102\117\110\099",f))end elseif O(f)=="\102\117\110\099\116\105\111\110"then b(f,U,...)end local y=i.rootframe:GetUserData("\105\115\099\117\115\116\111\109")local B=i.rootframe:GetUserData("\098\097\115\101\112\097\116\104")or E if P.type=="\099\111\108\111\114"then if r=="\079\110\086\097\108\117\101\067\111\110\102\105\114\109\101\100"then if y then g:Open(i.appName,i.rootframe,q(B))else g:Open(i.appName,q(B))end end elseif P.type=="\114\097\110\103\101"then if r=="\079\110\077\111\117\115\101\085\112"then if y then g:Open(i.appName,i.rootframe,q(B))else g:Open(i.appName,q(B))end end elseif P.type=="\109\117\108\116\105\115\101\108\101\099\116"then i.valuechanged=true else if y then g:Open(i.appName,i.rootframe,q(B))else g:Open(i.appName,q(B))end end end H(U)end local function k(l,r,i)local y=l:GetUserData("\111\112\116\105\111\110")local P,g,B=y.min or not y.softMin and 0 or nil,y.max or not y.softMax and 100 or nil,y.step if P then if B then i=z((i-P)/B+.5)*B+P end i=N(i,P)end if g then i=T(i,g)end R(l,r,i)end local function lq(l,r,...)R(l,r,l:GetUserData("\118\097\108\117\101"),...)local i=l:GetUserDataTable()local y=i.rootframe:GetUserData("\105\115\099\117\115\116\111\109")local P=i.rootframe:GetUserData("\098\097\115\101\112\097\116\104")or E if y then g:Open(i.appName,i.rootframe,q(P))else g:Open(i.appName,q(P))end end local function rq(l,r,...)local i=l:GetUserDataTable()if i.valuechanged and not l:IsReleasing()then local l=i.rootframe:GetUserData("\105\115\099\117\115\116\111\109")local r=i.rootframe:GetUserData("\098\097\115\101\112\097\116\104")or E if l then g:Open(i.appName,i.rootframe,q(r))else g:Open(i.appName,q(r))end end end local function iq(l,i)local y=l:GetUserData("\097\112\112\078\097\109\101")g.OpenFrames[y]=nil r:Release(l)end local function yq(l,r,i,y)local P=Y(l.dialogHidden,l.guiHidden)if P~=nil then return P end return a("\104\105\100\100\101\110",l,r,i,y)end local function Pq(l,r,i,y)local P=Y(l.dialogDisabled,l.guiDisabled)if P~=nil then return P end return a("\100\105\115\097\098\108\101\100",l,r,i,y)end local function gq(l,r,i,y)local P=p()local g=p()local B=p()local U=p()K(l,B,U,r,i,y)for l=1,#B,1 do local f=B[l]local c=U[f]if c.type=="\103\114\111\117\112"then i[#i+1]=f local l=Y(c.dialogInline,c.guiInline,c.inline,false)local B=yq(c,r,i,y)if not l and not B then P[f]=a("\110\097\109\101",c,r,i,y)C(g,f)end i[#i]=nil end end H(U)H(B)return P,g end local function Bq(l,r,i,y,P)local g=p()local B=p()K(l,g,B,i,y,P)for l=1,#g,1 do local U=g[l]local f=B[U]if f.type=="\103\114\111\117\112"then y[#y+1]=U local l=Y(f.dialogInline,f.guiInline,f.inline,false)local g=yq(f,i,y,P)if not l and not g then local l=p()l.value=U l.text=a("\110\097\109\101",f,i,y,P)l.icon=a("\105\099\111\110",f,i,y,P)l.iconCoords=a("\105\099\111\110\067\111\111\114\100\115",f,i,y,P)l.disabled=Pq(f,i,y,P)if not r.children then r.children=p()end C(r.children,l)if(f.childGroups or"\116\114\101\101")=="\116\114\101\101"then Bq(f,l,i,y,P)end end y[#y]=nil end end H(g)H(B)end local function Cq(l,r,i,y,P)local g=p()local B=p()local U=p()K(l,B,U,r,i,y)for l=1,#B,1 do local f=B[l]local c=U[f]if c.type=="\103\114\111\117\112"then i[#i+1]=f local l=Y(c.dialogInline,c.guiInline,c.inline,false)local B=yq(c,r,i,y)if not l and not B then local l=p()l.value=f l.text=a("\110\097\109\101",c,r,i,y)l.icon=a("\105\099\111\110",c,r,i,y)l.iconCoords=a("\105\099\111\110\067\111\111\114\100\115",c,r,i,y)l.disabled=Pq(c,r,i,y)C(g,l)if P and(c.childGroups or"\116\114\101\101")=="\116\114\101\101"then Bq(c,l,r,i,y)end end i[#i]=nil end end H(B)H(U)return g end local function Uq(l,r,i,y,P,g)local B=l:GetUserDataTable()for l=1,#y,1 do B[l]=y[l]end B.rootframe=P B.option=i B.options=r B.path=m(y)B.appName=g l:SetCallback("\079\110\082\101\108\101\097\115\101",L)l:SetCallback("\079\110\076\101\097\118\101",s)l:SetCallback("\079\110\069\110\116\101\114",A)end local function fq(l,i)local y if l then y=r:Create(l)if not y then(geterrorhandler())(("\073\110\118\097\108\105\100 \067\117\115\116\111\109 \067\111\110\116\114\111\108 \084\121\112\101 - \037\115"):format(V(l)))end end if not y then y=r:Create(i)end return y end local function cq(l,r)return V(l)<V(r)end local function Wq(l,i,y,P,g,B,c)local W=p()local u=p()K(B,W,u,i,g,l)for B=1,#W,1 do local G=W[B]local e=u[G]C(g,G)local M=yq(e,i,g,l)local T=a("\110\097\109\101",e,i,g,l)if not M then if e.type=="\103\114\111\117\112"then if c or Y(e.dialogInline,e.guiInline,e.inline,false)then local B if T and T~=""then B=r:Create("\073\110\108\105\110\101\071\114\111\117\112")B:SetTitle(T or"")else B=r:Create("\083\105\109\112\108\101\071\114\111\117\112")end B.width="\102\105\108\108"B:SetLayout("\102\108\111\119")y:AddChild(B)Wq(l,i,B,P,g,e,true)end else local B if e.type=="\101\120\101\099\117\116\101"then local r=a("\105\109\097\103\101\067\111\111\114\100\115",e,i,g,l)local y,P,C=a("\105\109\097\103\101",e,i,g,l)local U=O(y)=="\115\116\114\105\110\103"or O(y)=="\110\117\109\098\101\114"B=fq(e.dialogControl or e.control,U and"\073\099\111\110"or"\066\117\116\116\111\110")if U then if not P then P=a("\105\109\097\103\101\087\105\100\116\104",e,i,g,l)end if not C then C=a("\105\109\097\103\101\072\101\105\103\104\116",e,i,g,l)end if O(r)=="\116\097\098\108\101"then B:SetImage(y,q(r))else B:SetImage(y)end if O(P)~="\110\117\109\098\101\114"then P=32 end if O(C)~="\110\117\109\098\101\114"then C=32 end B:SetImageSize(P,C)B:SetLabel(T)else B:SetText(T)end B:SetCallback("\079\110\067\108\105\099\107",R)elseif e.type=="\105\110\112\117\116"then B=fq(e.dialogControl or e.control,e.multiline and"\077\117\108\116\105\076\105\110\101\069\100\105\116\066\111\120"or"\069\100\105\116\066\111\120")if e.multiline and B.SetNumLines then B:SetNumLines(Z(e.multiline)or 4)end B:SetLabel(T)B:SetCallback("\079\110\069\110\116\101\114\080\114\101\115\115\101\100",R)local r=a("\103\101\116",e,i,g,l)if O(r)~="\115\116\114\105\110\103"then r=""end B:SetText(r)elseif e.type=="\116\111\103\103\108\101"then B=fq(e.dialogControl or e.control,"\067\104\101\099\107\066\111\120")B:SetLabel(T)B:SetTriState(e.tristate)local r=a("\103\101\116",e,i,g,l)B:SetValue(r)B:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",R)if e.descStyle=="\105\110\108\105\110\101"then local r=a("\100\101\115\099",e,i,g,l)B:SetDescription(r)end local y=a("\105\109\097\103\101",e,i,g,l)local P=a("\105\109\097\103\101\067\111\111\114\100\115",e,i,g,l)if O(y)=="\115\116\114\105\110\103"or O(y)=="\110\117\109\098\101\114"then if O(P)=="\116\097\098\108\101"then B:SetImage(y,q(P))else B:SetImage(y)end end elseif e.type=="\114\097\110\103\101"then B=fq(e.dialogControl or e.control,"\083\108\105\100\101\114")B:SetLabel(T)B:SetSliderValues(e.softMin or e.min or 0,e.softMax or e.max or 100,e.bigStep or e.step or 0)B:SetIsPercent(e.isPercent)local r=a("\103\101\116",e,i,g,l)if O(r)~="\110\117\109\098\101\114"then r=0 end B:SetValue(r)B:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",k)B:SetCallback("\079\110\077\111\117\115\101\085\112",k)elseif e.type=="\115\101\108\101\099\116"then local y=a("\118\097\108\117\101\115",e,i,g,l)local C=a("\115\111\114\116\105\110\103",e,i,g,l)if e.style=="\114\097\100\105\111"then local f=Pq(e,i,g,l)local c=a("\119\105\100\116\104",e,i,g,l)B=r:Create("\073\110\108\105\110\101\071\114\111\117\112")B:SetLayout("\070\108\111\119")B:SetTitle(T)B.width="\102\105\108\108"B:PauseLayout()local W=a("\103\101\116",e,i,g,l)if not C then C={}for l,r in o(y)do C[#C+1]=l end U(C,cq)end for C,U in D(C)do local u=y[U]local G=r:Create("\067\104\101\099\107\066\111\120")G:SetLabel(u)G:SetUserData("\118\097\108\117\101",U)G:SetUserData("\116\101\120\116",u)G:SetDisabled(f)G:SetType("\114\097\100\105\111")G:SetValue(W==U)G:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",lq)Uq(G,i,e,g,P,l)B:AddChild(G)if c=="\100\111\117\098\108\101"then G:SetWidth(d*2)elseif c=="\104\097\108\102"then G:SetWidth(d/2)elseif O(c)=="\110\117\109\098\101\114"then G:SetWidth(d*c)elseif c=="\102\117\108\108"then G.width="\102\105\108\108"else G:SetWidth(d)end end B:ResumeLayout()B:DoLayout()else B=fq(e.dialogControl or e.control,"\068\114\111\112\100\111\119\110")local P=e.itemControl if P and not r:GetWidgetVersion(P)then(geterrorhandler())(("\073\110\118\097\108\105\100 \067\117\115\116\111\109 \073\116\101\109 \084\121\112\101 - \037\115"):format(V(P)))P=nil end B:SetLabel(T)B:SetList(y,C,P)local U=a("\103\101\116",e,i,g,l)if not y[U]then U=nil end B:SetValue(U)B:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",R)end elseif e.type=="\109\117\108\116\105\115\101\108\101\099\116"then local y=a("\118\097\108\117\101\115",e,i,g,l)local f=Pq(e,i,g,l)local c=p()if y then for l,r in o(y)do C(c,l)end end U(c)local W=e.dialogControl or e.control if W then B=r:Create(W)if not B then(geterrorhandler())(("\073\110\118\097\108\105\100 \067\117\115\116\111\109 \067\111\110\116\114\111\108 \084\121\112\101 - \037\115"):format(V(W)))end end if B then B:SetMultiselect(true)B:SetLabel(T)B:SetList(y)B:SetDisabled(f)B:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",R)B:SetCallback("\079\110\067\108\111\115\101\100",rq)local r=a("\119\105\100\116\104",e,i,g,l)if r=="\100\111\117\098\108\101"then B:SetWidth(d*2)elseif r=="\104\097\108\102"then B:SetWidth(d/2)elseif O(r)=="\110\117\109\098\101\114"then B:SetWidth(d*r)elseif r=="\102\117\108\108"then B.width="\102\105\108\108"else B:SetWidth(d)end for r=1,#c,1 do local y=c[r]local P=a("\103\101\116",e,i,g,l,y)B:SetItemValue(y,P)end else B=r:Create("\073\110\108\105\110\101\071\114\111\117\112")B:SetLayout("\070\108\111\119")B:SetTitle(T)B.width="\102\105\108\108"B:PauseLayout()local C=a("\119\105\100\116\104",e,i,g,l)for U=1,#c,1 do local W=c[U]local u=y[W]local G=r:Create("\067\104\101\099\107\066\111\120")G:SetLabel(u)G:SetUserData("\118\097\108\117\101",W)G:SetUserData("\116\101\120\116",u)G:SetDisabled(f)G:SetTriState(e.tristate)G:SetValue(a("\103\101\116",e,i,g,l,W))G:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",lq)Uq(G,i,e,g,P,l)B:AddChild(G)if C=="\100\111\117\098\108\101"then G:SetWidth(d*2)elseif C=="\104\097\108\102"then G:SetWidth(d/2)elseif O(C)=="\110\117\109\098\101\114"then G:SetWidth(d*C)elseif C=="\102\117\108\108"then G.width="\102\105\108\108"else G:SetWidth(d)end end B:ResumeLayout()B:DoLayout()end H(c)elseif e.type=="\099\111\108\111\114"then B=fq(e.dialogControl or e.control,"\067\111\108\111\114\080\105\099\107\101\114")B:SetLabel(T)B:SetHasAlpha(a("\104\097\115\065\108\112\104\097",e,i,g,l))B:SetColor(a("\103\101\116",e,i,g,l))B:SetCallback("\079\110\086\097\108\117\101\067\104\097\110\103\101\100",R)B:SetCallback("\079\110\086\097\108\117\101\067\111\110\102\105\114\109\101\100",R)elseif e.type=="\107\101\121\098\105\110\100\105\110\103"then B=fq(e.dialogControl or e.control,"\075\101\121\098\105\110\100\105\110\103")B:SetLabel(T)B:SetKey(a("\103\101\116",e,i,g,l))B:SetCallback("\079\110\075\101\121\067\104\097\110\103\101\100",R)elseif e.type=="\104\101\097\100\101\114"then B=fq(e.dialogControl or e.control,"\072\101\097\100\105\110\103")B:SetText(T)B.width="\102\105\108\108"elseif e.type=="\100\101\115\099\114\105\112\116\105\111\110"then B=fq(e.dialogControl or e.control,"\076\097\098\101\108")B:SetText(T)local r=a("\102\111\110\116\083\105\122\101",e,i,g,l)if r=="\109\101\100\105\117\109"then B:SetFontObject(GameFontHighlight)elseif r=="\108\097\114\103\101"then B:SetFontObject(GameFontHighlightLarge)else B:SetFontObject(GameFontHighlightSmall)end local y=a("\105\109\097\103\101\067\111\111\114\100\115",e,i,g,l)local P,C,U=a("\105\109\097\103\101",e,i,g,l)if O(P)=="\115\116\114\105\110\103"or O(P)=="\110\117\109\098\101\114"then if not C then C=a("\105\109\097\103\101\087\105\100\116\104",e,i,g,l)end if not U then U=a("\105\109\097\103\101\072\101\105\103\104\116",e,i,g,l)end if O(y)=="\116\097\098\108\101"then B:SetImage(P,q(y))else B:SetImage(P)end if O(C)~="\110\117\109\098\101\114"then C=32 end if O(U)~="\110\117\109\098\101\114"then U=32 end B:SetImageSize(C,U)end local f=a("\119\105\100\116\104",e,i,g,l)B.width=not f and"\102\105\108\108"end if B then if B.width~="\102\105\108\108"then local r=a("\119\105\100\116\104",e,i,g,l)if r=="\100\111\117\098\108\101"then B:SetWidth(d*2)elseif r=="\104\097\108\102"then B:SetWidth(d/2)elseif O(r)=="\110\117\109\098\101\114"then B:SetWidth(d*r)elseif r=="\102\117\108\108"then B.width="\102\105\108\108"else B:SetWidth(d)end end if B.SetDisabled then local r=Pq(e,i,g,l)B:SetDisabled(r)end Uq(B,i,e,g,P,l)y:AddChild(B)end end end f(g)end y:ResumeLayout()y:DoLayout()H(W)H(u)end local function uq(l,...)for r=1,M("\035",...),1 do C(l,M(r,...))end end local function Gq(l,r,i,y)local P=l:GetUserDataTable()if not P then return end local B=P.options local C=P.option local U=P.path local f=P.appName local c=g.tooltip local W=p()for l=1,#U,1 do W[l]=U[l]end uq(W,("\001"):split(i))local u=B for l=1,#W,1 do if not u then return end u=J(u,W[l])end local G=a("\110\097\109\101",u,B,W,f)local o=a("\100\101\115\099",u,B,W,f)c:SetOwner(y,"\065\078\067\072\079\082\095\078\079\078\069")c:ClearAllPoints()if l.type=="\084\097\098\071\114\111\117\112"then c:SetPoint("\066\079\084\084\079\077",y,"\084\079\080")else c:SetPoint("\076\069\070\084",y,"\082\073\071\072\084")end c:SetText(G,1,.82,0,true)if O(o)=="\115\116\114\105\110\103"then c:AddLine(o,1,1,1,true)end c:Show()end local function oq(l,r,i,y)g.tooltip:Hide()end local function eq(l,r,i,y)if not y then return false end local P=p()local g=p()for l=1,#i,1 do P[l]=i[l]end uq(P,("\001"):split(y))local B=r for i=1,#P,1 do local y=P[i]g[i]=y B=J(B,y)if not B or B.type~="\103\114\111\117\112"or yq(B,r,g,l)then H(P)H(g)return false end end H(P)H(g)return true end local function Mq(l,r,i)local y=l:GetUserDataTable()local P=y.options local B=y.option local C=y.path local U=y.rootframe local f=p()for l=1,#C,1 do f[l]=C[l]end uq(f,("\001"):split(i))l:ReleaseChildren()g:FeedGroup(y.appName,P,l,U,f)H(f)end function g.FeedGroup(U,l,i,y,P,B,C)local f=i local c local W,u=i.childGroups,"\110\111\110\101"for l=1,#B,1 do local r=B[l]f=J(f,r)c=c or Y(r.dialogInline,r.guiInline,r.inline,false)u=W W=f.childGroups end if not u then u="\116\114\101\101"end local G for r,y in o(f.args)do if y.type=="\103\114\111\117\112"and(not Y(y.dialogInline,y.guiInline,y.inline,false)and not yq(y,i,B,l))then G=true end end if f.plugins then for r,y in o(f.plugins)do for r,y in o(y)do if y.type=="\103\114\111\117\112"and(not Y(y.dialogInline,y.guiInline,y.inline,false)and not yq(y,i,B,l))then G=true end end end end y:SetLayout("\102\108\111\119")local e if not(G and not c)or W~="\116\097\098"and(W~="\115\101\108\101\099\116"and(u=="\116\114\101\101"and not C))then if y.type~="\073\110\108\105\110\101\071\114\111\117\112"and y.type~="\083\105\109\112\108\101\071\114\111\117\112"then e=r:Create("\083\099\114\111\108\108\070\114\097\109\101")e:SetLayout("\102\108\111\119")e.width="\102\105\108\108"e.height="\102\105\108\108"y:SetLayout("\102\105\108\108")y:AddChild(e)y=e end end Wq(l,i,y,P,B,f,nil)if e then y:PerformLayout()local r=U:GetStatusTable(l,B)if not r.scroll then r.scroll={}end e:SetStatusTable(r.scroll)end if G and not c then local U=a("\110\097\109\101",f,i,B,l)if W=="\116\097\098"then local C=r:Create("\084\097\098\071\114\111\117\112")Uq(C,i,f,B,P,l)C:SetCallback("\079\110\071\114\111\117\112\083\101\108\101\099\116\101\100",Mq)C:SetCallback("\079\110\084\097\098\069\110\116\101\114",Gq)C:SetCallback("\079\110\084\097\098\076\101\097\118\101",oq)local U=g:GetStatusTable(l,B)if not U.groups then U.groups={}end C:SetStatusTable(U.groups)C.width="\102\105\108\108"C.height="\102\105\108\108"local c=Cq(f,i,B,l)C:SetTabs(c)C:SetUserData("\116\097\098\108\105\115\116",c)for r=1,#c,1 do local y=c[r]if not y.disabled then C:SelectTab(eq(l,i,B,U.groups.selected)and U.groups.selected or y.value)break end end y:AddChild(C)elseif W=="\115\101\108\101\099\116"then local C=r:Create("\068\114\111\112\100\111\119\110\071\114\111\117\112")C:SetTitle(U)Uq(C,i,f,B,P,l)C:SetCallback("\079\110\071\114\111\117\112\083\101\108\101\099\116\101\100",Mq)local c=g:GetStatusTable(l,B)if not c.groups then c.groups={}end C:SetStatusTable(c.groups)local W,u=gq(f,i,B,l)C:SetGroupList(W,u)C:SetUserData("\103\114\111\117\112\108\105\115\116",W)C:SetUserData("\111\114\100\101\114\108\105\115\116",u)local G=u[1]if G then C:SetGroup(eq(l,i,B,c.groups.selected)and c.groups.selected or G)end C.width="\102\105\108\108"C.height="\102\105\108\108"y:AddChild(C)elseif u~="\116\114\101\101"or C then local C=r:Create("\084\114\101\101\071\114\111\117\112")Uq(C,i,f,B,P,l)C:EnableButtonTooltips(false)C.width="\102\105\108\108"C.height="\102\105\108\108"C:SetCallback("\079\110\071\114\111\117\112\083\101\108\101\099\116\101\100",Mq)C:SetCallback("\079\110\066\117\116\116\111\110\069\110\116\101\114",Gq)C:SetCallback("\079\110\066\117\116\116\111\110\076\101\097\118\101",oq)local U=g:GetStatusTable(l,B)if not U.groups then U.groups={}end local c=Cq(f,i,B,l,true)C:SetStatusTable(U.groups)C:SetTree(c)C:SetUserData("\116\114\101\101",c)for r=1,#c,1 do local y=c[r]if not y.disabled then C:SelectByValue(eq(l,i,B,U.groups.selected)and U.groups.selected or y.value)break end end y:AddChild(C)end end end local Oq local function qq(l)for r in o(l.closing)do if g.OpenFrames[r]then g.OpenFrames[r]:Hide()end if g.BlizOptions and g.BlizOptions[r]then for l,r in o(g.BlizOptions[r])do if not r:IsVisible()then r:ReleaseChildren()end end end l.closing[r]=nil end if l.closeAll then for r,i in o(g.OpenFrames)do if not l.closeAllOverride[r]then i:Hide()end end l.closeAll=nil c(l.closeAllOverride)end for r in o(l.apps)do if g.OpenFrames[r]then local l=g.OpenFrames[r]:GetUserDataTable()g:Open(r,q(l.basepath or E))end if g.BlizOptions and g.BlizOptions[r]then for l,r in o(g.BlizOptions[r])do local i=r:GetUserDataTable()if r:IsVisible()then g:Open(r:GetUserData("\097\112\112\078\097\109\101"),r,q(i.basepath or E))end end end l.apps[r]=nil end l:SetScript("\079\110\085\112\100\097\116\101",nil)end if g.frame:GetScript("\079\110\085\112\100\097\116\101")then g.frame:SetScript("\079\110\085\112\100\097\116\101",qq)end function g.CloseAll(l)g.frame.closeAll=true g.frame:SetScript("\079\110\085\112\100\097\116\101",qq)if e(l.OpenFrames)then return true end end function g.Close(r,l)if r.OpenFrames[l]then g.frame.closing[l]=true g.frame:SetScript("\079\110\085\112\100\097\116\101",qq)return true end end function g.ConfigTableChanged(i,l,r)g.frame.apps[r]=true g.frame:SetScript("\079\110\085\112\100\097\116\101",qq)end i.RegisterCallback(g,"\067\111\110\102\105\103\084\097\098\108\101\067\104\097\110\103\101","\067\111\110\102\105\103\084\097\098\108\101\067\104\097\110\103\101\100")function g.SetDefaultSize(y,l,r,i)local P=g:GetStatusTable(l)if O(r)=="\110\117\109\098\101\114"and O(i)=="\110\117\109\098\101\114"then P.width=r P.height=i end end function g.Open(B,l,P,...)if not Oq then Oq=CloseSpecialWindows CloseSpecialWindows=function()local l=Oq()return B:CloseAll()or l end end local U=i:GetOptionsTable(l)if not U then G(("\037\115 \105\115\110\'\116 \114\101\103\105\115\116\101\100 \119\105\116\104 \065\099\101\067\111\110\102\105\103\082\101\103\105\115\116\114\121\044 \117\110\097\098\108\101 \116\111 \111\112\101\110 \099\111\110\102\105\103"):format(l),2)end local f=U("\100\105\097\108\111\103",y)local c local W=p()local o=a("\110\097\109\101",f,f,W,l)if O(P)=="\115\116\114\105\110\103"then C(W,P)P=nil end for l=1,M("\035",...),1 do C(W,M(l,...))end local e=f if O(P)=="\116\097\098\108\101"and(P.type=="\066\108\105\122\079\112\116\105\111\110\115\071\114\111\117\112"and#W>0)then for l=1,#W,1 do e=f.args[W[l]]end o=u("\037\115 - \037\115",o,a("\110\097\109\101",e,f,W,l))end if P then c=P c:ReleaseChildren()c:SetUserData("\097\112\112\078\097\109\101",l)c:SetUserData("\105\115\099\117\115\116\111\109",true)if#W>0 then c:SetUserData("\098\097\115\101\112\097\116\104",m(W))end local r=g:GetStatusTable(l)if not r.width then r.width=700 end if not r.height then r.height=500 end if c.SetStatusTable then c:SetStatusTable(r)end if c.SetTitle then c:SetTitle(o or"")end else if not B.OpenFrames[l]then c=r:Create("\070\114\097\109\101")B.OpenFrames[l]=c else c=B.OpenFrames[l]end c:ReleaseChildren()c:SetCallback("\079\110\067\108\111\115\101",iq)c:SetUserData("\097\112\112\078\097\109\101",l)if#W>0 then c:SetUserData("\098\097\115\101\112\097\116\104",m(W))end c:SetTitle(o or"")local i=g:GetStatusTable(l)c:SetStatusTable(i)end B:FeedGroup(l,f,c,c,W,true)if c.Show then c:Show()end H(W)if g.frame.closeAll then g.frame.closeAllOverride[l]=true end end if B and(B<39 and g.BlizOptions)then local l=g.BlizOptions local r={}for l,i in o(l)do local y=i:GetUserData("\097\112\112\078\097\109\101")if not r[y]then r[y]={}end r[y][l]=i end g.BlizOptions=r else g.BlizOptions=g.BlizOptions or{}end local function Dq(l,r)local i=l:GetUserData("\112\097\116\104")g:Open(l:GetUserData("\097\112\112\078\097\109\101"),l,q(i or E))end local function Vq(l,r)local i=l:GetUserData("\097\112\112\078\097\109\101")g.frame.closing[i]=true g.frame:SetScript("\079\110\085\112\100\097\116\101",qq)end function g.AddToBlizOptions(P,l,i,y,...)local B=g.BlizOptions local U=l for l=1,M("\035",...),1 do U=U..("\001"..M(l,...))end if not B[l]then B[l]={}end if not B[l][U]then local P=r:Create("\066\108\105\122\079\112\116\105\111\110\115\071\114\111\117\112")B[l][U]=P P:SetTitle(i or l)P:SetUserData("\097\112\112\078\097\109\101",l)if M("\035",...)>0 then local l={}for r=1,M("\035",...),1 do C(l,M(r,...))end P:SetUserData("\112\097\116\104",l)end P:SetCallback("\079\110\083\104\111\119",Dq)P:SetCallback("\079\110\072\105\100\101",Vq)if Settings and Settings.RegisterCanvasLayoutCategory then local r=i or l if y then local l=Settings.GetCategory(y)if not l then G(("\084\104\101 \112\097\114\101\110\116 \099\097\116\101\103\111\114\121 \'\037\115\' \119\097\115 \110\111\116 \102\111\117\110\100"):format(y),2)end local i=Settings.RegisterCanvasLayoutSubcategory(l,P.frame,r)P:SetName(i.ID,y)else local l=Settings.RegisterCanvasLayoutCategory(P.frame,r)l.ID=r P:SetName(r,y)Settings.RegisterAddOnCategory(l)end else P:SetName(i or l,y)InterfaceOptions_AddCategory(P.frame)end return P.frame,P.frame.name else G(("\037\115 \104\097\115 \097\108\114\101\097\100\121 \098\101\101\110 \097\100\100\101\100 \116\111 \116\104\101 \066\108\105\122\122\097\114\100 \079\112\116\105\111\110\115 \087\105\110\100\111\119 \119\105\116\104 \116\104\101 \103\105\118\101\110 \112\097\116\104"):format(l),2)end end
--- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables.
-- @class file
-- @name AceConfigDialog-3.0
-- @release $Id: AceConfigDialog-3.0.lua 1296 2022-11-04 18:50:10Z nevcairiel $

local LibStub = LibStub
local gui = LibStub("AceGUI-3.0")
local reg = LibStub("AceConfigRegistry-3.0")

local MAJOR, MINOR = "AceConfigDialog-3.0", 86
local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR)

if not AceConfigDialog then return end

AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {}
AceConfigDialog.Status = AceConfigDialog.Status or {}
AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame")
AceConfigDialog.tooltip = AceConfigDialog.tooltip or CreateFrame("GameTooltip", "AceConfigDialogTooltip", UIParent, "GameTooltipTemplate")

AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {}
AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {}
AceConfigDialog.frame.closeAllOverride = AceConfigDialog.frame.closeAllOverride or {}

-- Lua APIs
local tinsert, tsort, tremove, wipe = table.insert, table.sort, table.remove, table.wipe
local strmatch, format = string.match, string.format
local error = error
local pairs, next, select, type, unpack, ipairs = pairs, next, select, type, unpack, ipairs
local tostring, tonumber = tostring, tonumber
local math_min, math_max, math_floor = math.min, math.max, math.floor

local emptyTbl = {}

--[[
	 xpcall safecall implementation
]]
local xpcall = xpcall

local function errorhandler(err)
	return geterrorhandler()(err)
end

local function safecall(func, ...)
	if func then
		return xpcall(func, errorhandler, ...)
	end
end

local width_multiplier = 170

--[[
Group Types
  Tree 	- All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree
        - Descendant Groups with inline=true and thier children will not become nodes

  Tab	- Direct Child Groups will become tabs, direct child options will appear above the tab control
        - Grandchild groups will default to inline unless specified otherwise

  Select- Same as Tab but with entries in a dropdown rather than tabs


  Inline Groups
    - Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border
    - If declared on a direct child of a root node of a select group, they will appear above the group container control
    - When a group is displayed inline, all descendants will also be inline members of the group

]]

-- Recycling functions
local new, del, copy
--newcount, delcount,createdcount,cached = 0,0,0
do
	local pool = setmetatable({},{__mode="k"})
	function new()
		--newcount = newcount + 1
		local t = next(pool)
		if t then
			pool[t] = nil
			return t
		else
			--createdcount = createdcount + 1
			return {}
		end
	end
	function copy(t)
		local c = new()
		for k, v in pairs(t) do
			c[k] = v
		end
		return c
	end
	function del(t)
		--delcount = delcount + 1
		wipe(t)
		pool[t] = true
	end
--	function cached()
--		local n = 0
--		for k in pairs(pool) do
--			n = n + 1
--		end
--		return n
--	end
end

-- picks the first non-nil value and returns it
local function pickfirstset(...)
  for i=1,select("#",...) do
    if select(i,...)~=nil then
      return select(i,...)
    end
  end
end

--gets an option from a given group, checking plugins
local function GetSubOption(group, key)
	if group.plugins then
		for plugin, t in pairs(group.plugins) do
			if t[key] then
				return t[key]
			end
		end
	end

	return group.args[key]
end

--Option member type definitions, used to decide how to access it

--Is the member Inherited from parent options
local isInherited = {
	set = true,
	get = true,
	func = true,
	confirm = true,
	validate = true,
	disabled = true,
	hidden = true
}

--Does a string type mean a literal value, instead of the default of a method of the handler
local stringIsLiteral = {
	name = true,
	desc = true,
	icon = true,
	usage = true,
	width = true,
	image = true,
	fontSize = true,
	tooltipHyperlink = true
}

--Is Never a function or method
local allIsLiteral = {
	type = true,
	descStyle = true,
	imageWidth = true,
	imageHeight = true,
}

--gets the value for a member that could be a function
--function refs are called with an info arg
--every other type is returned
local function GetOptionsMemberValue(membername, option, options, path, appName, ...)
	--get definition for the member
	local inherits = isInherited[membername]


	--get the member of the option, traversing the tree if it can be inherited
	local member

	if inherits then
		local group = options
		if group[membername] ~= nil then
			member = group[membername]
		end
		for i = 1, #path do
			group = GetSubOption(group, path[i])
			if group[membername] ~= nil then
				member = group[membername]
			end
		end
	else
		member = option[membername]
	end

	--check if we need to call a functon, or if we have a literal value
	if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then
		--We have a function to call
		local info = new()
		--traverse the options table, picking up the handler and filling the info with the path
		local group = options
		local handler = group.handler

		for i = 1, #path do
			group = GetSubOption(group, path[i])
			info[i] = path[i]
			handler = group.handler or handler
		end

		info.options = options
		info.appName = appName
		info[0] = appName
		info.arg = option.arg
		info.handler = handler
		info.option = option
		info.type = option.type
		info.uiType = "dialog"
		info.uiName = MAJOR

		local a, b, c ,d
		--using 4 returns for the get of a color type, increase if a type needs more
		if type(member) == "function" then
			--Call the function
			a,b,c,d = member(info, ...)
		else
			--Call the method
			if handler and handler[member] then
				a,b,c,d = handler[member](handler, info, ...)
			else
				error(format("Method %s doesn't exist in handler for type %s", member, membername))
			end
		end
		del(info)
		return a,b,c,d
	else
		--The value isnt a function to call, return it
		return member
	end
end

--[[calls an options function that could be inherited, method name or function ref
local function CallOptionsFunction(funcname ,option, options, path, appName, ...)
	local info = new()

	local func
	local group = options
	local handler

	--build the info table containing the path
	-- pick up functions while traversing the tree
	if group[funcname] ~= nil then
		func = group[funcname]
	end
	handler = group.handler or handler

	for i, v in ipairs(path) do
		group = GetSubOption(group, v)
		info[i] = v
		if group[funcname] ~= nil then
			func =  group[funcname]
		end
		handler = group.handler or handler
	end

	info.options = options
	info[0] = appName
	info.arg = option.arg

	local a, b, c ,d
	if type(func) == "string" then
		if handler and handler[func] then
			a,b,c,d = handler[func](handler, info, ...)
		else
			error(string.format("Method %s doesn't exist in handler for type func", func))
		end
	elseif type(func) == "function" then
		a,b,c,d = func(info, ...)
	end
	del(info)
	return a,b,c,d
end
--]]

--tables to hold orders and names for options being sorted, will be created with new()
--prevents needing to call functions repeatedly while sorting
local tempOrders
local tempNames

local function compareOptions(a,b)
	if not a then
		return true
	end
	if not b then
		return false
	end
	local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100
	if OrderA == OrderB then
		local NameA = (type(tempNames[a]) == "string") and tempNames[a] or ""
		local NameB = (type(tempNames[b]) == "string") and tempNames[b] or ""
		return NameA:upper() < NameB:upper()
	end
	if OrderA < 0 then
		if OrderB >= 0 then
			return false
		end
	else
		if OrderB < 0 then
			return true
		end
	end
	return OrderA < OrderB
end



--builds 2 tables out of an options group
-- keySort, sorted keys
-- opts, combined options from .plugins and args
local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName)
	tempOrders = new()
	tempNames = new()

	if group.plugins then
		for plugin, t in pairs(group.plugins) do
			for k, v in pairs(t) do
				if not opts[k] then
					tinsert(keySort, k)
					opts[k] = v

					path[#path+1] = k
					tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName)
					tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName)
					path[#path] = nil
				end
			end
		end
	end

	for k, v in pairs(group.args) do
		if not opts[k] then
			tinsert(keySort, k)
			opts[k] = v

			path[#path+1] = k
			tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName)
			tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName)
			path[#path] = nil
		end
	end

	tsort(keySort, compareOptions)

	del(tempOrders)
	del(tempNames)
end

local function DelTree(tree)
	if tree.children then
		local childs = tree.children
		for i = 1, #childs do
			DelTree(childs[i])
			del(childs[i])
		end
		del(childs)
	end
end

local function CleanUserData(widget, event)

	local user = widget:GetUserDataTable()

	if user.path then
		del(user.path)
	end

	if widget.type == "TreeGroup" then
		local tree = user.tree
		widget:SetTree(nil)
		if tree then
			for i = 1, #tree do
				DelTree(tree[i])
				del(tree[i])
			end
			del(tree)
		end
	end

	if widget.type == "TabGroup" then
		widget:SetTabs(nil)
		if user.tablist then
			del(user.tablist)
		end
	end

	if widget.type == "DropdownGroup" then
		widget:SetGroupList(nil)
		if user.grouplist then
			del(user.grouplist)
		end
		if user.orderlist then
			del(user.orderlist)
		end
	end
end

-- - Gets a status table for the given appname and options path.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param path The path to the options (a table with all group keys)
-- @return
function AceConfigDialog:GetStatusTable(appName, path)
	local status = self.Status

	if not status[appName] then
		status[appName] = {}
		status[appName].status = {}
		status[appName].children = {}
	end

	status = status[appName]

	if path then
		for i = 1, #path do
			local v = path[i]
			if not status.children[v] then
				status.children[v] = {}
				status.children[v].status = {}
				status.children[v].children = {}
			end
			status = status.children[v]
		end
	end

	return status.status
end

--- Selects the specified path in the options window.
-- The path specified has to match the keys of the groups in the table.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param ... The path to the key that should be selected
function AceConfigDialog:SelectGroup(appName, ...)
	local path = new()


	local app = reg:GetOptionsTable(appName)
	if not app then
		error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2)
	end
	local options = app("dialog", MAJOR)
	local group = options
	local status = self:GetStatusTable(appName, path)
	if not status.groups then
		status.groups = {}
	end
	status = status.groups
	local treevalue
	local treestatus

	for n = 1, select("#",...) do
		local key = select(n, ...)

		if group.childGroups == "tab" or group.childGroups == "select" then
			--if this is a tab or select group, select the group
			status.selected = key
			--children of this group are no longer extra levels of a tree
			treevalue = nil
		else
			--tree group by default
			if treevalue then
				--this is an extra level of a tree group, build a uniquevalue for it
				treevalue = treevalue.."\001"..key
			else
				--this is the top level of a tree group, the uniquevalue is the same as the key
				treevalue = key
				if not status.groups then
					status.groups = {}
				end
				--save this trees status table for any extra levels or groups
				treestatus = status
			end
			--make sure that the tree entry is open, and select it.
			--the selected group will be overwritten if a child is the final target but still needs to be open
			treestatus.selected = treevalue
			treestatus.groups[treevalue] = true

		end

		--move to the next group in the path
		group = GetSubOption(group, key)
		if not group then
			break
		end
		tinsert(path, key)
		status = self:GetStatusTable(appName, path)
		if not status.groups then
			status.groups = {}
		end
		status = status.groups
	end

	del(path)
	reg:NotifyChange(appName)
end

local function OptionOnMouseOver(widget, event)
	--show a tooltip/set the status bar to the desc text
	local user = widget:GetUserDataTable()
	local opt = user.option
	local options = user.options
	local path = user.path
	local appName = user.appName
	local tooltip = AceConfigDialog.tooltip

	tooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT")

	local tooltipHyperlink = GetOptionsMemberValue("tooltipHyperlink", opt, options, path, appName)
	if tooltipHyperlink then
		tooltip:SetHyperlink(tooltipHyperlink)
		tooltip:Show()
		return
	end

	local name = GetOptionsMemberValue("name", opt, options, path, appName)
	local desc = GetOptionsMemberValue("desc", opt, options, path, appName)
	local usage = GetOptionsMemberValue("usage", opt, options, path, appName)
	local descStyle = opt.descStyle

	if descStyle and descStyle ~= "tooltip" then return end

	tooltip:SetText(name, 1, .82, 0, true)

	if opt.type == "multiselect" then
		tooltip:AddLine(user.text, 0.5, 0.5, 0.8, true)
	end
	if type(desc) == "string" then
		tooltip:AddLine(desc, 1, 1, 1, true)
	end
	if type(usage) == "string" then
		tooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, true)
	end

	tooltip:Show()
end

local function OptionOnMouseLeave(widget, event)
	AceConfigDialog.tooltip:Hide()
end

local function GetFuncName(option)
	if option.type == "execute" then
		return "func"
	else
		return "set"
	end
end
do
	local frame = AceConfigDialog.popup
	if not frame or oldminor < 81 then
		frame = CreateFrame("Frame", nil, UIParent)
		AceConfigDialog.popup = frame
		frame:Hide()
		frame:SetPoint("CENTER", UIParent, "CENTER")
		frame:SetSize(320, 72)
		frame:EnableMouse(true) -- Do not allow click-through on the frame
		frame:SetFrameStrata("TOOLTIP")
		frame:SetFrameLevel(100) -- Lots of room to draw under it
		frame:SetScript("OnKeyDown", function(self, key)
			if key == "ESCAPE" then
				self:SetPropagateKeyboardInput(false)
				if self.cancel:IsShown() then
					self.cancel:Click()
				else -- Showing a validation error
					self:Hide()
				end
			else
				self:SetPropagateKeyboardInput(true)
			end
		end)

		local border = CreateFrame("Frame", nil, frame, "DialogBorderOpaqueTemplate")
		border:SetAllPoints(frame)
		frame:SetFixedFrameStrata(true)
		frame:SetFixedFrameLevel(true)

		local text = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
		text:SetSize(290, 0)
		text:SetPoint("TOP", 0, -16)
		frame.text = text

		local function newButton(newText)
			local button = CreateFrame("Button", nil, frame)
			button:SetSize(128, 21)
			button:SetNormalFontObject(GameFontNormal)
			button:SetHighlightFontObject(GameFontHighlight)
			button:SetNormalTexture(130763) -- "Interface\\Buttons\\UI-DialogBox-Button-Up"
			button:GetNormalTexture():SetTexCoord(0.0, 1.0, 0.0, 0.71875)
			button:SetPushedTexture(130761) -- "Interface\\Buttons\\UI-DialogBox-Button-Down"
			button:GetPushedTexture():SetTexCoord(0.0, 1.0, 0.0, 0.71875)
			button:SetHighlightTexture(130762) -- "Interface\\Buttons\\UI-DialogBox-Button-Highlight"
			button:GetHighlightTexture():SetTexCoord(0.0, 1.0, 0.0, 0.71875)
			button:SetText(newText)
			return button
		end

		local accept = newButton(ACCEPT)
		accept:SetPoint("BOTTOMRIGHT", frame, "BOTTOM", -6, 16)
		frame.accept = accept

		local cancel = newButton(CANCEL)
		cancel:SetPoint("LEFT", accept, "RIGHT", 13, 0)
		frame.cancel = cancel
	end
end
local function confirmPopup(appName, rootframe, basepath, info, message, func, ...)
	local frame = AceConfigDialog.popup
	frame:Show()
	frame.text:SetText(message)
	-- From StaticPopup.lua
	-- local height = 32 + text:GetHeight() + 2;
	-- height = height + 6 + accept:GetHeight()
	-- We add 32 + 2 + 6 + 21 (button height) == 61
	local height = 61 + frame.text:GetHeight()
	frame:SetHeight(height)

	frame.accept:ClearAllPoints()
	frame.accept:SetPoint("BOTTOMRIGHT", frame, "BOTTOM", -6, 16)
	frame.cancel:Show()

	local t = {...}
	local tCount = select("#", ...)
	frame.accept:SetScript("OnClick", function(self)
		safecall(func, unpack(t, 1, tCount)) -- Manually set count as unpack() stops on nil (bug with #table)
		AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl))
		frame:Hide()
		self:SetScript("OnClick", nil)
		frame.cancel:SetScript("OnClick", nil)
		del(info)
	end)
	frame.cancel:SetScript("OnClick", function(self)
		AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl))
		frame:Hide()
		self:SetScript("OnClick", nil)
		frame.accept:SetScript("OnClick", nil)
		del(info)
	end)
end

local function validationErrorPopup(message)
	local frame = AceConfigDialog.popup
	frame:Show()
	frame.text:SetText(message)
	-- From StaticPopup.lua
	-- local height = 32 + text:GetHeight() + 2;
	-- height = height + 6 + accept:GetHeight()
	-- We add 32 + 2 + 6 + 21 (button height) == 61
	local height = 61 + frame.text:GetHeight()
	frame:SetHeight(height)

	frame.accept:ClearAllPoints()
	frame.accept:SetPoint("BOTTOM", frame, "BOTTOM", 0, 16)
	frame.cancel:Hide()

	frame.accept:SetScript("OnClick", function()
		frame:Hide()
	end)
end

local function ActivateControl(widget, event, ...)
	--This function will call the set / execute handler for the widget
	--widget:GetUserDataTable() contains the needed info
	local user = widget:GetUserDataTable()
	local option = user.option
	local options = user.options
	local path = user.path
	local info = new()

	local func
	local group = options
	local funcname = GetFuncName(option)
	local handler
	local confirm
	local validate
	--build the info table containing the path
	-- pick up functions while traversing the tree
	if group[funcname] ~= nil then
		func =  group[funcname]
	end
	handler = group.handler
	confirm = group.confirm
	validate = group.validate
	for i = 1, #path do
		local v = path[i]
		group = GetSubOption(group, v)
		info[i] = v
		if group[funcname] ~= nil then
			func =  group[funcname]
		end
		handler = group.handler or handler
		if group.confirm ~= nil then
			confirm = group.confirm
		end
		if group.validate ~= nil then
			validate = group.validate
		end
	end

	info.options = options
	info.appName = user.appName
	info.arg = option.arg
	info.handler = handler
	info.option = option
	info.type = option.type
	info.uiType = "dialog"
	info.uiName = MAJOR

	local name
	if type(option.name) == "function" then
		name = option.name(info)
	elseif type(option.name) == "string" then
		name = option.name
	else
		name = ""
	end
	local usage = option.usage
	local pattern = option.pattern

	local validated = true

	if option.type == "input" then
		if type(pattern)=="string" then
			if not strmatch(..., pattern) then
				validated = false
			end
		end
	end

	local success
	if validated and option.type ~= "execute" then
		if type(validate) == "string" then
			if handler and handler[validate] then
				success, validated = safecall(handler[validate], handler, info, ...)
				if not success then validated = false end
			else
				error(format("Method %s doesn't exist in handler for type execute", validate))
			end
		elseif type(validate) == "function" then
			success, validated = safecall(validate, info, ...)
			if not success then validated = false end
		end
	end

	if not validated or type(validated) == "string" then
		if not validated then
			if usage then
				validated = name..": "..usage
			else
				if pattern then
					validated = name..": Expected "..pattern
				else
					validated = name..": Invalid Value"
				end
			end
		end

		-- show validate message
		if user.rootframe.SetStatusText then
			user.rootframe:SetStatusText(validated)
		else
			validationErrorPopup(validated)
		end
		PlaySound(882) -- SOUNDKIT.IG_PLAYER_INVITE_DECLINE || _DECLINE is actually missing from the table
		del(info)
		return true
	else

		local confirmText = option.confirmText
		--call confirm func/method
		if type(confirm) == "string" then
			if handler and handler[confirm] then
				success, confirm = safecall(handler[confirm], handler, info, ...)
				if success and type(confirm) == "string" then
					confirmText = confirm
					confirm = true
				elseif not success then
					confirm = false
				end
			else
				error(format("Method %s doesn't exist in handler for type confirm", confirm))
			end
		elseif type(confirm) == "function" then
			success, confirm = safecall(confirm, info, ...)
			if success and type(confirm) == "string" then
				confirmText = confirm
				confirm = true
			elseif not success then
				confirm = false
			end
		end

		--confirm if needed
		if type(confirm) == "boolean" then
			if confirm then
				if not confirmText then
					local option_name, desc = option.name, option.desc
					if type(option_name) == "function" then
						option_name = option_name(info)
					end
					if type(desc) == "function" then
						desc = desc(info)
					end
					confirmText = option_name
					if desc then
						confirmText = confirmText.." - "..desc
					end
				end

				local iscustom = user.rootframe:GetUserData("iscustom")
				local rootframe

				if iscustom then
					rootframe = user.rootframe
				end
				local basepath = user.rootframe:GetUserData("basepath")
				if type(func) == "string" then
					if handler and handler[func] then
						confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...)
					else
						error(format("Method %s doesn't exist in handler for type func", func))
					end
				elseif type(func) == "function" then
					confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...)
				end
				--func will be called and info deleted when the confirm dialog is responded to
				return
			end
		end

		--call the function
		if type(func) == "string" then
			if handler and handler[func] then
				safecall(handler[func],handler, info, ...)
			else
				error(format("Method %s doesn't exist in handler for type func", func))
			end
		elseif type(func) == "function" then
			safecall(func,info, ...)
		end



		local iscustom = user.rootframe:GetUserData("iscustom")
		local basepath = user.rootframe:GetUserData("basepath") or emptyTbl
		--full refresh of the frame, some controls dont cause this on all events
		if option.type == "color" then
			if event == "OnValueConfirmed" then

				if iscustom then
					AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
				else
					AceConfigDialog:Open(user.appName, unpack(basepath))
				end
			end
		elseif option.type == "range" then
			if event == "OnMouseUp" then
				if iscustom then
					AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
				else
					AceConfigDialog:Open(user.appName, unpack(basepath))
				end
			end
		--multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed'
		elseif option.type == "multiselect" then
			user.valuechanged = true
		else
			if iscustom then
				AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
			else
				AceConfigDialog:Open(user.appName, unpack(basepath))
			end
		end

	end
	del(info)
end

local function ActivateSlider(widget, event, value)
	local option = widget:GetUserData("option")
	local min, max, step = option.min or (not option.softMin and 0 or nil), option.max or (not option.softMax and 100 or nil), option.step
	if min then
		if step then
			value = math_floor((value - min) / step + 0.5) * step + min
		end
		value = math_max(value, min)
	end
	if max then
		value = math_min(value, max)
	end
	ActivateControl(widget,event,value)
end

--called from a checkbox that is part of an internally created multiselect group
--this type is safe to refresh on activation of one control
local function ActivateMultiControl(widget, event, ...)
	ActivateControl(widget, event, widget:GetUserData("value"), ...)
	local user = widget:GetUserDataTable()
	local iscustom = user.rootframe:GetUserData("iscustom")
	local basepath = user.rootframe:GetUserData("basepath") or emptyTbl
	if iscustom then
		AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
	else
		AceConfigDialog:Open(user.appName, unpack(basepath))
	end
end

local function MultiControlOnClosed(widget, event, ...)
	local user = widget:GetUserDataTable()
	if user.valuechanged and not widget:IsReleasing() then
		local iscustom = user.rootframe:GetUserData("iscustom")
		local basepath = user.rootframe:GetUserData("basepath") or emptyTbl
		if iscustom then
			AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath))
		else
			AceConfigDialog:Open(user.appName, unpack(basepath))
		end
	end
end

local function FrameOnClose(widget, event)
	local appName = widget:GetUserData("appName")
	AceConfigDialog.OpenFrames[appName] = nil
	gui:Release(widget)
end

local function CheckOptionHidden(option, options, path, appName)
	--check for a specific boolean option
	local hidden = pickfirstset(option.dialogHidden,option.guiHidden)
	if hidden ~= nil then
		return hidden
	end

	return GetOptionsMemberValue("hidden", option, options, path, appName)
end

local function CheckOptionDisabled(option, options, path, appName)
	--check for a specific boolean option
	local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled)
	if disabled ~= nil then
		return disabled
	end

	return GetOptionsMemberValue("disabled", option, options, path, appName)
end
--[[
local function BuildTabs(group, options, path, appName)
	local tabs = new()
	local text = new()
	local keySort = new()
	local opts = new()

	BuildSortedOptionsTable(group, keySort, opts, options, path, appName)

	for i = 1, #keySort do
		local k = keySort[i]
		local v = opts[k]
		if v.type == "group" then
			path[#path+1] = k
			local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false)
			local hidden = CheckOptionHidden(v, options, path, appName)
			if not inline and not hidden then
				tinsert(tabs, k)
				text[k] = GetOptionsMemberValue("name", v, options, path, appName)
			end
			path[#path] = nil
		end
	end

	del(keySort)
	del(opts)

	return tabs, text
end
]]
local function BuildSelect(group, options, path, appName)
	local groups = new()
	local order = new()
	local keySort = new()
	local opts = new()

	BuildSortedOptionsTable(group, keySort, opts, options, path, appName)

	for i = 1, #keySort do
		local k = keySort[i]
		local v = opts[k]
		if v.type == "group" then
			path[#path+1] = k
			local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false)
			local hidden = CheckOptionHidden(v, options, path, appName)
			if not inline and not hidden then
				groups[k] = GetOptionsMemberValue("name", v, options, path, appName)
				tinsert(order, k)
			end
			path[#path] = nil
		end
	end

	del(opts)
	del(keySort)

	return groups, order
end

local function BuildSubGroups(group, tree, options, path, appName)
	local keySort = new()
	local opts = new()

	BuildSortedOptionsTable(group, keySort, opts, options, path, appName)

	for i = 1, #keySort do
		local k = keySort[i]
		local v = opts[k]
		if v.type == "group" then
			path[#path+1] = k
			local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false)
			local hidden = CheckOptionHidden(v, options, path, appName)
			if not inline and not hidden then
				local entry = new()
				entry.value = k
				entry.text = GetOptionsMemberValue("name", v, options, path, appName)
				entry.icon = GetOptionsMemberValue("icon", v, options, path, appName)
				entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName)
				entry.disabled = CheckOptionDisabled(v, options, path, appName)
				if not tree.children then tree.children = new() end
				tinsert(tree.children,entry)
				if (v.childGroups or "tree") == "tree" then
					BuildSubGroups(v,entry, options, path, appName)
				end
			end
			path[#path] = nil
		end
	end

	del(keySort)
	del(opts)
end

local function BuildGroups(group, options, path, appName, recurse)
	local tree = new()
	local keySort = new()
	local opts = new()

	BuildSortedOptionsTable(group, keySort, opts, options, path, appName)

	for i = 1, #keySort do
		local k = keySort[i]
		local v = opts[k]
		if v.type == "group" then
			path[#path+1] = k
			local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false)
			local hidden = CheckOptionHidden(v, options, path, appName)
			if not inline and not hidden then
				local entry = new()
				entry.value = k
				entry.text = GetOptionsMemberValue("name", v, options, path, appName)
				entry.icon = GetOptionsMemberValue("icon", v, options, path, appName)
				entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName)
				entry.disabled = CheckOptionDisabled(v, options, path, appName)
				tinsert(tree,entry)
				if recurse and (v.childGroups or "tree") == "tree" then
					BuildSubGroups(v,entry, options, path, appName)
				end
			end
			path[#path] = nil
		end
	end
	del(keySort)
	del(opts)
	return tree
end

local function InjectInfo(control, options, option, path, rootframe, appName)
	local user = control:GetUserDataTable()
	for i = 1, #path do
		user[i] = path[i]
	end
	user.rootframe = rootframe
	user.option = option
	user.options = options
	user.path = copy(path)
	user.appName = appName
	control:SetCallback("OnRelease", CleanUserData)
	control:SetCallback("OnLeave", OptionOnMouseLeave)
	control:SetCallback("OnEnter", OptionOnMouseOver)
end

local function CreateControl(userControlType, fallbackControlType)
	local control
	if userControlType then
		control = gui:Create(userControlType)
		if not control then
			geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(userControlType)))
		end
	end
	if not control then
		control = gui:Create(fallbackControlType)
	end
	return control
end

local function sortTblAsStrings(x,y)
	return tostring(x) < tostring(y) -- Support numbers as keys
end

--[[
	options - root of the options table being fed
	container - widget that controls will be placed in
	rootframe - Frame object the options are in
	path - table with the keys to get to the group being fed
--]]

local function FeedOptions(appName, options,container,rootframe,path,group,inline)
	local keySort = new()
	local opts = new()

	BuildSortedOptionsTable(group, keySort, opts, options, path, appName)

	for i = 1, #keySort do
		local k = keySort[i]
		local v = opts[k]
		tinsert(path, k)
		local hidden = CheckOptionHidden(v, options, path, appName)
		local name = GetOptionsMemberValue("name", v, options, path, appName)
		if not hidden then
			if v.type == "group" then
				if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then
					--Inline group
					local GroupContainer
					if name and name ~= "" then
						GroupContainer = gui:Create("InlineGroup")
						GroupContainer:SetTitle(name or "")
					else
						GroupContainer = gui:Create("SimpleGroup")
					end

					GroupContainer.width = "fill"
					GroupContainer:SetLayout("flow")
					container:AddChild(GroupContainer)
					FeedOptions(appName,options,GroupContainer,rootframe,path,v,true)
				end
			else
				--Control to feed
				local control

				if v.type == "execute" then

					local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName)
					local image, width, height = GetOptionsMemberValue("image",v, options, path, appName)

					local iconControl = type(image) == "string" or type(image) == "number"
					control = CreateControl(v.dialogControl or v.control, iconControl and "Icon" or "Button")
					if iconControl then
						if not width then
							width = GetOptionsMemberValue("imageWidth",v, options, path, appName)
						end
						if not height then
							height = GetOptionsMemberValue("imageHeight",v, options, path, appName)
						end
						if type(imageCoords) == "table" then
							control:SetImage(image, unpack(imageCoords))
						else
							control:SetImage(image)
						end
						if type(width) ~= "number" then
							width = 32
						end
						if type(height) ~= "number" then
							height = 32
						end
						control:SetImageSize(width, height)
						control:SetLabel(name)
					else
						control:SetText(name)
					end
					control:SetCallback("OnClick",ActivateControl)

				elseif v.type == "input" then
					control = CreateControl(v.dialogControl or v.control, v.multiline and "MultiLineEditBox" or "EditBox")

					if v.multiline and control.SetNumLines then
						control:SetNumLines(tonumber(v.multiline) or 4)
					end
					control:SetLabel(name)
					control:SetCallback("OnEnterPressed",ActivateControl)
					local text = GetOptionsMemberValue("get",v, options, path, appName)
					if type(text) ~= "string" then
						text = ""
					end
					control:SetText(text)

				elseif v.type == "toggle" then
					control = CreateControl(v.dialogControl or v.control, "CheckBox")
					control:SetLabel(name)
					control:SetTriState(v.tristate)
					local value = GetOptionsMemberValue("get",v, options, path, appName)
					control:SetValue(value)
					control:SetCallback("OnValueChanged",ActivateControl)

					if v.descStyle == "inline" then
						local desc = GetOptionsMemberValue("desc", v, options, path, appName)
						control:SetDescription(desc)
					end

					local image = GetOptionsMemberValue("image", v, options, path, appName)
					local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName)

					if type(image) == "string" or type(image) == "number" then
						if type(imageCoords) == "table" then
							control:SetImage(image, unpack(imageCoords))
						else
							control:SetImage(image)
						end
					end
				elseif v.type == "range" then
					control = CreateControl(v.dialogControl or v.control, "Slider")
					control:SetLabel(name)
					control:SetSliderValues(v.softMin or v.min or 0, v.softMax or v.max or 100, v.bigStep or v.step or 0)
					control:SetIsPercent(v.isPercent)
					local value = GetOptionsMemberValue("get",v, options, path, appName)
					if type(value) ~= "number" then
						value = 0
					end
					control:SetValue(value)
					control:SetCallback("OnValueChanged",ActivateSlider)
					control:SetCallback("OnMouseUp",ActivateSlider)

				elseif v.type == "select" then
					local values = GetOptionsMemberValue("values", v, options, path, appName)
					local sorting = GetOptionsMemberValue("sorting", v, options, path, appName)
					if v.style == "radio" then
						local disabled = CheckOptionDisabled(v, options, path, appName)
						local width = GetOptionsMemberValue("width",v,options,path,appName)
						control = gui:Create("InlineGroup")
						control:SetLayout("Flow")
						control:SetTitle(name)
						control.width = "fill"

						control:PauseLayout()
						local optionValue = GetOptionsMemberValue("get",v, options, path, appName)
						if not sorting then
							sorting = {}
							for value, text in pairs(values) do
								sorting[#sorting+1]=value
							end
							tsort(sorting, sortTblAsStrings)
						end
						for _, value in ipairs(sorting) do
							local text = values[value]
							local radio = gui:Create("CheckBox")
							radio:SetLabel(text)
							radio:SetUserData("value", value)
							radio:SetUserData("text", text)
							radio:SetDisabled(disabled)
							radio:SetType("radio")
							radio:SetValue(optionValue == value)
							radio:SetCallback("OnValueChanged", ActivateMultiControl)
							InjectInfo(radio, options, v, path, rootframe, appName)
							control:AddChild(radio)
							if width == "double" then
								radio:SetWidth(width_multiplier * 2)
							elseif width == "half" then
								radio:SetWidth(width_multiplier / 2)
							elseif (type(width) == "number") then
								radio:SetWidth(width_multiplier * width)
							elseif width == "full" then
								radio.width = "fill"
							else
								radio:SetWidth(width_multiplier)
							end
						end
						control:ResumeLayout()
						control:DoLayout()
					else
						control = CreateControl(v.dialogControl or v.control, "Dropdown")
						local itemType = v.itemControl
						if itemType and not gui:GetWidgetVersion(itemType) then
							geterrorhandler()(("Invalid Custom Item Type - %s"):format(tostring(itemType)))
							itemType = nil
						end
						control:SetLabel(name)
						control:SetList(values, sorting, itemType)
						local value = GetOptionsMemberValue("get",v, options, path, appName)
						if not values[value] then
							value = nil
						end
						control:SetValue(value)
						control:SetCallback("OnValueChanged", ActivateControl)
					end

				elseif v.type == "multiselect" then
					local values = GetOptionsMemberValue("values", v, options, path, appName)
					local disabled = CheckOptionDisabled(v, options, path, appName)

					local valuesort = new()
					if values then
						for value, text in pairs(values) do
							tinsert(valuesort, value)
						end
					end
					tsort(valuesort)

					local controlType = v.dialogControl or v.control
					if controlType then
						control = gui:Create(controlType)
						if not control then
							geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType)))
						end
					end
					if control then
						control:SetMultiselect(true)
						control:SetLabel(name)
						control:SetList(values)
						control:SetDisabled(disabled)
						control:SetCallback("OnValueChanged",ActivateControl)
						control:SetCallback("OnClosed", MultiControlOnClosed)
						local width = GetOptionsMemberValue("width",v,options,path,appName)
						if width == "double" then
							control:SetWidth(width_multiplier * 2)
						elseif width == "half" then
							control:SetWidth(width_multiplier / 2)
						elseif (type(width) == "number") then
							control:SetWidth(width_multiplier * width)
						elseif width == "full" then
							control.width = "fill"
						else
							control:SetWidth(width_multiplier)
						end
						--check:SetTriState(v.tristate)
						for s = 1, #valuesort do
							local key = valuesort[s]
							local value = GetOptionsMemberValue("get",v, options, path, appName, key)
							control:SetItemValue(key,value)
						end
					else
						control = gui:Create("InlineGroup")
						control:SetLayout("Flow")
						control:SetTitle(name)
						control.width = "fill"

						control:PauseLayout()
						local width = GetOptionsMemberValue("width",v,options,path,appName)
						for s = 1, #valuesort do
							local value = valuesort[s]
							local text = values[value]
							local check = gui:Create("CheckBox")
							check:SetLabel(text)
							check:SetUserData("value", value)
							check:SetUserData("text", text)
							check:SetDisabled(disabled)
							check:SetTriState(v.tristate)
							check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value))
							check:SetCallback("OnValueChanged",ActivateMultiControl)
							InjectInfo(check, options, v, path, rootframe, appName)
							control:AddChild(check)
							if width == "double" then
								check:SetWidth(width_multiplier * 2)
							elseif width == "half" then
								check:SetWidth(width_multiplier / 2)
							elseif (type(width) == "number") then
								check:SetWidth(width_multiplier * width)
							elseif width == "full" then
								check.width = "fill"
							else
								check:SetWidth(width_multiplier)
							end
						end
						control:ResumeLayout()
						control:DoLayout()


					end

					del(valuesort)

				elseif v.type == "color" then
					control = CreateControl(v.dialogControl or v.control, "ColorPicker")
					control:SetLabel(name)
					control:SetHasAlpha(GetOptionsMemberValue("hasAlpha",v, options, path, appName))
					control:SetColor(GetOptionsMemberValue("get",v, options, path, appName))
					control:SetCallback("OnValueChanged",ActivateControl)
					control:SetCallback("OnValueConfirmed",ActivateControl)

				elseif v.type == "keybinding" then
					control = CreateControl(v.dialogControl or v.control, "Keybinding")
					control:SetLabel(name)
					control:SetKey(GetOptionsMemberValue("get",v, options, path, appName))
					control:SetCallback("OnKeyChanged",ActivateControl)

				elseif v.type == "header" then
					control = CreateControl(v.dialogControl or v.control, "Heading")
					control:SetText(name)
					control.width = "fill"

				elseif v.type == "description" then
					control = CreateControl(v.dialogControl or v.control, "Label")
					control:SetText(name)

					local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName)
					if fontSize == "medium" then
						control:SetFontObject(GameFontHighlight)
					elseif fontSize == "large" then
						control:SetFontObject(GameFontHighlightLarge)
					else -- small or invalid
						control:SetFontObject(GameFontHighlightSmall)
					end

					local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName)
					local image, width, height = GetOptionsMemberValue("image",v, options, path, appName)

					if type(image) == "string" or type(image) == "number" then
						if not width then
							width = GetOptionsMemberValue("imageWidth",v, options, path, appName)
						end
						if not height then
							height = GetOptionsMemberValue("imageHeight",v, options, path, appName)
						end
						if type(imageCoords) == "table" then
							control:SetImage(image, unpack(imageCoords))
						else
							control:SetImage(image)
						end
						if type(width) ~= "number" then
							width = 32
						end
						if type(height) ~= "number" then
							height = 32
						end
						control:SetImageSize(width, height)
					end
					local controlWidth = GetOptionsMemberValue("width",v,options,path,appName)
					control.width = not controlWidth and "fill"
				end

				--Common Init
				if control then
					if control.width ~= "fill" then
						local width = GetOptionsMemberValue("width",v,options,path,appName)
						if width == "double" then
							control:SetWidth(width_multiplier * 2)
						elseif width == "half" then
							control:SetWidth(width_multiplier / 2)
						elseif (type(width) == "number") then
							control:SetWidth(width_multiplier * width)
						elseif width == "full" then
							control.width = "fill"
						else
							control:SetWidth(width_multiplier)
						end
					end
					if control.SetDisabled then
						local disabled = CheckOptionDisabled(v, options, path, appName)
						control:SetDisabled(disabled)
					end

					InjectInfo(control, options, v, path, rootframe, appName)
					container:AddChild(control)
				end

			end
		end
		tremove(path)
	end
	container:ResumeLayout()
	container:DoLayout()
	del(keySort)
	del(opts)
end

local function BuildPath(path, ...)
	for i = 1, select("#",...)  do
		tinsert(path, (select(i,...)))
	end
end


local function TreeOnButtonEnter(widget, event, uniquevalue, button)
	local user = widget:GetUserDataTable()
	if not user then return end
	local options = user.options
	local option = user.option
	local path = user.path
	local appName = user.appName
	local tooltip = AceConfigDialog.tooltip

	local feedpath = new()
	for i = 1, #path do
		feedpath[i] = path[i]
	end

	BuildPath(feedpath, ("\001"):split(uniquevalue))
	local group = options
	for i = 1, #feedpath do
		if not group then return end
		group = GetSubOption(group, feedpath[i])
	end

	local name = GetOptionsMemberValue("name", group, options, feedpath, appName)
	local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName)

	tooltip:SetOwner(button, "ANCHOR_NONE")
	tooltip:ClearAllPoints()
	if widget.type == "TabGroup" then
		tooltip:SetPoint("BOTTOM",button,"TOP")
	else
		tooltip:SetPoint("LEFT",button,"RIGHT")
	end

	tooltip:SetText(name, 1, .82, 0, true)

	if type(desc) == "string" then
		tooltip:AddLine(desc, 1, 1, 1, true)
	end

	tooltip:Show()
end

local function TreeOnButtonLeave(widget, event, value, button)
	AceConfigDialog.tooltip:Hide()
end


local function GroupExists(appName, options, path, uniquevalue)
	if not uniquevalue then return false end

	local feedpath = new()
	local temppath = new()
	for i = 1, #path do
		feedpath[i] = path[i]
	end

	BuildPath(feedpath, ("\001"):split(uniquevalue))

	local group = options
	for i = 1, #feedpath do
		local v = feedpath[i]
		temppath[i] = v
		group = GetSubOption(group, v)

		if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then
			del(feedpath)
			del(temppath)
			return false
		end
	end
	del(feedpath)
	del(temppath)
	return true
end

local function GroupSelected(widget, event, uniquevalue)

	local user = widget:GetUserDataTable()

	local options = user.options
	local option = user.option
	local path = user.path
	local rootframe = user.rootframe

	local feedpath = new()
	for i = 1, #path do
		feedpath[i] = path[i]
	end

	BuildPath(feedpath, ("\001"):split(uniquevalue))
	widget:ReleaseChildren()
	AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath)

	del(feedpath)
end



--[[
-- INTERNAL --
This function will feed one group, and any inline child groups into the given container
Select Groups will only have the selection control (tree, tabs, dropdown) fed in
and have a group selected, this event will trigger the feeding of child groups

Rules:
	If the group is Inline, FeedOptions
	If the group has no child groups, FeedOptions

	If the group is a tab or select group, FeedOptions then add the Group Control
	If the group is a tree group FeedOptions then
		its parent isnt a tree group:  then add the tree control containing this and all child tree groups
		if its parent is a tree group, its already a node on a tree
--]]

function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot)
	local group = options
	--follow the path to get to the curent group
	local inline
	local grouptype, parenttype = options.childGroups, "none"


	for i = 1, #path do
		local v = path[i]
		group = GetSubOption(group, v)
		inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false)
		parenttype = grouptype
		grouptype = group.childGroups
	end

	if not parenttype then
		parenttype = "tree"
	end

	--check if the group has child groups
	local hasChildGroups
	for k, v in pairs(group.args) do
		if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then
			hasChildGroups = true
		end
	end
	if group.plugins then
		for plugin, t in pairs(group.plugins) do
			for k, v in pairs(t) do
				if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then
					hasChildGroups = true
				end
			end
		end
	end

	container:SetLayout("flow")
	local scroll

	--Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on
	if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then
		if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then
			scroll = gui:Create("ScrollFrame")
			scroll:SetLayout("flow")
			scroll.width = "fill"
			scroll.height = "fill"
			container:SetLayout("fill")
			container:AddChild(scroll)
			container = scroll
		end
	end

	FeedOptions(appName,options,container,rootframe,path,group,nil)

	if scroll then
		container:PerformLayout()
		local status = self:GetStatusTable(appName, path)
		if not status.scroll then
			status.scroll = {}
		end
		scroll:SetStatusTable(status.scroll)
	end

	if hasChildGroups and not inline then
		local name = GetOptionsMemberValue("name", group, options, path, appName)
		if grouptype == "tab" then

			local tab = gui:Create("TabGroup")
			InjectInfo(tab, options, group, path, rootframe, appName)
			tab:SetCallback("OnGroupSelected", GroupSelected)
			tab:SetCallback("OnTabEnter", TreeOnButtonEnter)
			tab:SetCallback("OnTabLeave", TreeOnButtonLeave)

			local status = AceConfigDialog:GetStatusTable(appName, path)
			if not status.groups then
				status.groups = {}
			end
			tab:SetStatusTable(status.groups)
			tab.width = "fill"
			tab.height = "fill"

			local tabs = BuildGroups(group, options, path, appName)
			tab:SetTabs(tabs)
			tab:SetUserData("tablist", tabs)

			for i = 1, #tabs do
				local entry = tabs[i]
				if not entry.disabled then
					tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value)
					break
				end
			end

			container:AddChild(tab)

		elseif grouptype == "select" then

			local selectGroup = gui:Create("DropdownGroup")
			selectGroup:SetTitle(name)
			InjectInfo(selectGroup, options, group, path, rootframe, appName)
			selectGroup:SetCallback("OnGroupSelected", GroupSelected)
			local status = AceConfigDialog:GetStatusTable(appName, path)
			if not status.groups then
				status.groups = {}
			end
			selectGroup:SetStatusTable(status.groups)
			local grouplist, orderlist = BuildSelect(group, options, path, appName)
			selectGroup:SetGroupList(grouplist, orderlist)
			selectGroup:SetUserData("grouplist", grouplist)
			selectGroup:SetUserData("orderlist", orderlist)

			local firstgroup = orderlist[1]
			if firstgroup then
				selectGroup:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup)
			end

			selectGroup.width = "fill"
			selectGroup.height = "fill"

			container:AddChild(selectGroup)

		--assume tree group by default
		--if parenttype is tree then this group is already a node on that tree
		elseif (parenttype ~= "tree") or isRoot then
			local tree = gui:Create("TreeGroup")
			InjectInfo(tree, options, group, path, rootframe, appName)
			tree:EnableButtonTooltips(false)

			tree.width = "fill"
			tree.height = "fill"

			tree:SetCallback("OnGroupSelected", GroupSelected)
			tree:SetCallback("OnButtonEnter", TreeOnButtonEnter)
			tree:SetCallback("OnButtonLeave", TreeOnButtonLeave)

			local status = AceConfigDialog:GetStatusTable(appName, path)
			if not status.groups then
				status.groups = {}
			end
			local treedefinition = BuildGroups(group, options, path, appName, true)
			tree:SetStatusTable(status.groups)

			tree:SetTree(treedefinition)
			tree:SetUserData("tree",treedefinition)

			for i = 1, #treedefinition do
				local entry = treedefinition[i]
				if not entry.disabled then
					tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value)
					break
				end
			end

			container:AddChild(tree)
		end
	end
end

local old_CloseSpecialWindows


local function RefreshOnUpdate(this)
	for appName in pairs(this.closing) do
		if AceConfigDialog.OpenFrames[appName] then
			AceConfigDialog.OpenFrames[appName]:Hide()
		end
		if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then
			for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do
				if not widget:IsVisible() then
					widget:ReleaseChildren()
				end
			end
		end
		this.closing[appName] = nil
	end

	if this.closeAll then
		for k, v in pairs(AceConfigDialog.OpenFrames) do
			if not this.closeAllOverride[k] then
				v:Hide()
			end
		end
		this.closeAll = nil
		wipe(this.closeAllOverride)
	end

	for appName in pairs(this.apps) do
		if AceConfigDialog.OpenFrames[appName] then
			local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable()
			AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl))
		end
		if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then
			for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do
				local user = widget:GetUserDataTable()
				if widget:IsVisible() then
					AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(user.basepath or emptyTbl))
				end
			end
		end
		this.apps[appName] = nil
	end
	this:SetScript("OnUpdate", nil)
end

-- Upgrade the OnUpdate script as well, if needed.
if AceConfigDialog.frame:GetScript("OnUpdate") then
	AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
end

--- Close all open options windows
function AceConfigDialog:CloseAll()
	AceConfigDialog.frame.closeAll = true
	AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
	if next(self.OpenFrames) then
		return true
	end
end

--- Close a specific options window.
-- @param appName The application name as given to `:RegisterOptionsTable()`
function AceConfigDialog:Close(appName)
	if self.OpenFrames[appName] then
		AceConfigDialog.frame.closing[appName] = true
		AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
		return true
	end
end

-- Internal -- Called by AceConfigRegistry
function AceConfigDialog:ConfigTableChanged(event, appName)
	AceConfigDialog.frame.apps[appName] = true
	AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
end

reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged")

--- Sets the default size of the options window for a specific application.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param width The default width
-- @param height The default height
function AceConfigDialog:SetDefaultSize(appName, width, height)
	local status = AceConfigDialog:GetStatusTable(appName)
	if type(width) == "number" and type(height) == "number" then
		status.width = width
		status.height = height
	end
end

--- Open an option window at the specified path (if any).
-- This function can optionally feed the group into a pre-created container
-- instead of creating a new container frame.
-- @paramsig appName [, container][, ...]
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param container An optional container frame to feed the options into
-- @param ... The path to open after creating the options window (see `:SelectGroup` for details)
function AceConfigDialog:Open(appName, container, ...)
	if not old_CloseSpecialWindows then
		old_CloseSpecialWindows = CloseSpecialWindows
		CloseSpecialWindows = function()
			local found = old_CloseSpecialWindows()
			return self:CloseAll() or found
		end
	end
	local app = reg:GetOptionsTable(appName)
	if not app then
		error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2)
	end
	local options = app("dialog", MAJOR)

	local f

	local path = new()
	local name = GetOptionsMemberValue("name", options, options, path, appName)

	--If an optional path is specified add it to the path table before feeding the options
	--as container is optional as well it may contain the first element of the path
	if type(container) == "string" then
		tinsert(path, container)
		container = nil
	end
	for n = 1, select("#",...) do
		tinsert(path, (select(n, ...)))
	end

	local option = options
	if type(container) == "table" and container.type == "BlizOptionsGroup" and #path > 0 then
		for i = 1, #path do
			option = options.args[path[i]]
		end
		name = format("%s - %s", name, GetOptionsMemberValue("name", option, options, path, appName))
	end

	--if a container is given feed into that
	if container then
		f = container
		f:ReleaseChildren()
		f:SetUserData("appName", appName)
		f:SetUserData("iscustom", true)
		if #path > 0 then
			f:SetUserData("basepath", copy(path))
		end
		local status = AceConfigDialog:GetStatusTable(appName)
		if not status.width then
			status.width =  700
		end
		if not status.height then
			status.height = 500
		end
		if f.SetStatusTable then
			f:SetStatusTable(status)
		end
		if f.SetTitle then
			f:SetTitle(name or "")
		end
	else
		if not self.OpenFrames[appName] then
			f = gui:Create("Frame")
			self.OpenFrames[appName] = f
		else
			f = self.OpenFrames[appName]
		end
		f:ReleaseChildren()
		f:SetCallback("OnClose", FrameOnClose)
		f:SetUserData("appName", appName)
		if #path > 0 then
			f:SetUserData("basepath", copy(path))
		end
		f:SetTitle(name or "")
		local status = AceConfigDialog:GetStatusTable(appName)
		f:SetStatusTable(status)
	end

	self:FeedGroup(appName,options,f,f,path,true)
	if f.Show then
		f:Show()
	end
	del(path)

	if AceConfigDialog.frame.closeAll then
		-- close all is set, but thats not good, since we're just opening here, so force it
		AceConfigDialog.frame.closeAllOverride[appName] = true
	end
end

-- convert pre-39 BlizOptions structure to the new format
if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then
	local old = AceConfigDialog.BlizOptions
	local newOpt = {}
	for key, widget in pairs(old) do
		local appName = widget:GetUserData("appName")
		if not newOpt[appName] then newOpt[appName] = {} end
		newOpt[appName][key] = widget
	end
	AceConfigDialog.BlizOptions = newOpt
else
	AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {}
end

local function FeedToBlizPanel(widget, event)
	local path = widget:GetUserData("path")
	AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(path or emptyTbl))
end

local function ClearBlizPanel(widget, event)
	local appName = widget:GetUserData("appName")
	AceConfigDialog.frame.closing[appName] = true
	AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate)
end

--- Add an option table into the Blizzard Interface Options panel.
-- You can optionally supply a descriptive name to use and a parent frame to use,
-- as well as a path in the options table.\\
-- If no name is specified, the appName will be used instead.
--
-- If you specify a proper `parent` (by name), the interface options will generate a
-- tree layout. Note that only one level of children is supported, so the parent always
-- has to be a head-level note.
--
-- This function returns a reference to the container frame registered with the Interface
-- Options. You can use this reference to open the options with the API function
-- `InterfaceOptionsFrame_OpenToCategory`.
-- @param appName The application name as given to `:RegisterOptionsTable()`
-- @param name A descriptive name to display in the options tree (defaults to appName)
-- @param parent The parent to use in the interface options tree.
-- @param ... The path in the options table to feed into the interface options panel.
-- @return The reference to the frame registered into the Interface Options.
-- @return The category ID to pass to Settings.OpenToCategory (or InterfaceOptionsFrame_OpenToCategory)
function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...)
	local BlizOptions = AceConfigDialog.BlizOptions

	local key = appName
	for n = 1, select("#", ...) do
		key = key.."\001"..select(n, ...)
	end

	if not BlizOptions[appName] then
		BlizOptions[appName] = {}
	end

	if not BlizOptions[appName][key] then
		local group = gui:Create("BlizOptionsGroup")
		BlizOptions[appName][key] = group

		group:SetTitle(name or appName)
		group:SetUserData("appName", appName)
		if select("#", ...) > 0 then
			local path = {}
			for n = 1, select("#",...) do
				tinsert(path, (select(n, ...)))
			end
			group:SetUserData("path", path)
		end
		group:SetCallback("OnShow", FeedToBlizPanel)
		group:SetCallback("OnHide", ClearBlizPanel)
		if Settings and Settings.RegisterCanvasLayoutCategory then
			local categoryName = name or appName
			if parent then
				local category = Settings.GetCategory(parent)
				if not category then
					error(("The parent category '%s' was not found"):format(parent), 2)
				end
				local subcategory = Settings.RegisterCanvasLayoutSubcategory(category, group.frame, categoryName)

				-- force the generated ID to be used for subcategories, as these can have very simple names like "Profiles"
				group:SetName(subcategory.ID, parent)
			else
				local category = Settings.RegisterCanvasLayoutCategory(group.frame, categoryName)
				-- using appName here would be cleaner, but would not be 100% compatible
				-- but for top-level categories it should be fine, as these are typically addon names
				category.ID = categoryName
				group:SetName(categoryName, parent)
				Settings.RegisterAddOnCategory(category)
			end
		else
			group:SetName(name or appName, parent)
			InterfaceOptions_AddCategory(group.frame)
		end
		return group.frame, group.frame.name
	else
		error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2)
	end
end
