| tercio@0 | 1 --[[----------------------------------------------------------------------------- | 
| tercio@0 | 2 TreeGroup Container | 
| tercio@0 | 3 Container that uses a tree control to switch between groups. | 
| tercio@0 | 4 -------------------------------------------------------------------------------]] | 
| Tercio@11 | 5 local Type, Version = "TreeGroup", 40 | 
| tercio@0 | 6 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) | 
| tercio@0 | 7 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end | 
| tercio@0 | 8 | 
| Tercio@11 | 9 local IsLegion = select(4, GetBuildInfo()) >= 70000 | 
| Tercio@11 | 10 | 
| tercio@0 | 11 -- Lua APIs | 
| tercio@0 | 12 local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type | 
| tercio@0 | 13 local math_min, math_max, floor = math.min, math.max, floor | 
| tercio@0 | 14 local select, tremove, unpack, tconcat = select, table.remove, unpack, table.concat | 
| tercio@0 | 15 | 
| tercio@0 | 16 -- WoW APIs | 
| tercio@0 | 17 local CreateFrame, UIParent = CreateFrame, UIParent | 
| tercio@0 | 18 | 
| tercio@0 | 19 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded | 
| tercio@0 | 20 -- List them here for Mikk's FindGlobals script | 
| tercio@0 | 21 -- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE | 
| tercio@0 | 22 | 
| tercio@0 | 23 -- Recycling functions | 
| tercio@0 | 24 local new, del | 
| tercio@0 | 25 do | 
| tercio@0 | 26 	local pool = setmetatable({},{__mode='k'}) | 
| tercio@0 | 27 	function new() | 
| tercio@0 | 28 		local t = next(pool) | 
| tercio@0 | 29 		if t then | 
| tercio@0 | 30 			pool[t] = nil | 
| tercio@0 | 31 			return t | 
| tercio@0 | 32 		else | 
| tercio@0 | 33 			return {} | 
| tercio@0 | 34 		end | 
| tercio@0 | 35 	end | 
| tercio@0 | 36 	function del(t) | 
| tercio@0 | 37 		for k in pairs(t) do | 
| tercio@0 | 38 			t[k] = nil | 
| tercio@0 | 39 		end | 
| tercio@0 | 40 		pool[t] = true | 
| tercio@0 | 41 	end | 
| tercio@0 | 42 end | 
| tercio@0 | 43 | 
| tercio@0 | 44 local DEFAULT_TREE_WIDTH = 175 | 
| tercio@0 | 45 local DEFAULT_TREE_SIZABLE = true | 
| tercio@0 | 46 | 
| tercio@0 | 47 --[[----------------------------------------------------------------------------- | 
| tercio@0 | 48 Support functions | 
| tercio@0 | 49 -------------------------------------------------------------------------------]] | 
| tercio@0 | 50 local function GetButtonUniqueValue(line) | 
| tercio@0 | 51 	local parent = line.parent | 
| tercio@0 | 52 	if parent and parent.value then | 
| tercio@0 | 53 		return GetButtonUniqueValue(parent).."\001"..line.value | 
| tercio@0 | 54 	else | 
| tercio@0 | 55 		return line.value | 
| tercio@0 | 56 	end | 
| tercio@0 | 57 end | 
| tercio@0 | 58 | 
| tercio@0 | 59 local function UpdateButton(button, treeline, selected, canExpand, isExpanded) | 
| tercio@0 | 60 	local self = button.obj | 
| tercio@0 | 61 	local toggle = button.toggle | 
| tercio@0 | 62 	local frame = self.frame | 
| tercio@0 | 63 	local text = treeline.text or "" | 
| tercio@0 | 64 	local icon = treeline.icon | 
| tercio@0 | 65 	local iconCoords = treeline.iconCoords | 
| tercio@0 | 66 	local level = treeline.level | 
| tercio@0 | 67 	local value = treeline.value | 
| tercio@0 | 68 	local uniquevalue = treeline.uniquevalue | 
| tercio@0 | 69 	local disabled = treeline.disabled | 
| tercio@0 | 70 | 
| tercio@0 | 71 	button.treeline = treeline | 
| tercio@0 | 72 	button.value = value | 
| tercio@0 | 73 	button.uniquevalue = uniquevalue | 
| tercio@0 | 74 	if selected then | 
| tercio@0 | 75 		button:LockHighlight() | 
| tercio@0 | 76 		button.selected = true | 
| tercio@0 | 77 	else | 
| tercio@0 | 78 		button:UnlockHighlight() | 
| tercio@0 | 79 		button.selected = false | 
| tercio@0 | 80 	end | 
| tercio@0 | 81 	local normalTexture = button:GetNormalTexture() | 
| tercio@0 | 82 	local line = button.line | 
| tercio@0 | 83 	button.level = level | 
| tercio@0 | 84 	if ( level == 1 ) then | 
| tercio@0 | 85 		button:SetNormalFontObject("GameFontNormal") | 
| tercio@0 | 86 		button:SetHighlightFontObject("GameFontHighlight") | 
| tercio@0 | 87 		button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2) | 
| tercio@0 | 88 	else | 
| tercio@0 | 89 		button:SetNormalFontObject("GameFontHighlightSmall") | 
| tercio@0 | 90 		button:SetHighlightFontObject("GameFontHighlightSmall") | 
| tercio@0 | 91 		button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2) | 
| tercio@0 | 92 	end | 
| tercio@0 | 93 | 
| tercio@0 | 94 	if disabled then | 
| tercio@0 | 95 		button:EnableMouse(false) | 
| tercio@0 | 96 		button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE) | 
| tercio@0 | 97 	else | 
| tercio@0 | 98 		button.text:SetText(text) | 
| tercio@0 | 99 		button:EnableMouse(true) | 
| tercio@0 | 100 	end | 
| tercio@0 | 101 | 
| tercio@0 | 102 	if icon then | 
| tercio@0 | 103 		button.icon:SetTexture(icon) | 
| tercio@0 | 104 		button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1) | 
| tercio@0 | 105 	else | 
| tercio@0 | 106 		button.icon:SetTexture(nil) | 
| tercio@0 | 107 	end | 
| tercio@0 | 108 | 
| tercio@0 | 109 	if iconCoords then | 
| tercio@0 | 110 		button.icon:SetTexCoord(unpack(iconCoords)) | 
| tercio@0 | 111 	else | 
| tercio@0 | 112 		button.icon:SetTexCoord(0, 1, 0, 1) | 
| tercio@0 | 113 	end | 
| tercio@0 | 114 | 
| tercio@0 | 115 	if canExpand then | 
| tercio@0 | 116 		if not isExpanded then | 
| tercio@0 | 117 			toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP") | 
| tercio@0 | 118 			toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN") | 
| tercio@0 | 119 		else | 
| tercio@0 | 120 			toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP") | 
| tercio@0 | 121 			toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN") | 
| tercio@0 | 122 		end | 
| tercio@0 | 123 		toggle:Show() | 
| tercio@0 | 124 	else | 
| tercio@0 | 125 		toggle:Hide() | 
| tercio@0 | 126 	end | 
| tercio@0 | 127 end | 
| tercio@0 | 128 | 
| tercio@0 | 129 local function ShouldDisplayLevel(tree) | 
| tercio@0 | 130 	local result = false | 
| tercio@0 | 131 	for k, v in ipairs(tree) do | 
| tercio@0 | 132 		if v.children == nil and v.visible ~= false then | 
| tercio@0 | 133 			result = true | 
| tercio@0 | 134 		elseif v.children then | 
| tercio@0 | 135 			result = result or ShouldDisplayLevel(v.children) | 
| tercio@0 | 136 		end | 
| tercio@0 | 137 		if result then return result end | 
| tercio@0 | 138 	end | 
| tercio@0 | 139 	return false | 
| tercio@0 | 140 end | 
| tercio@0 | 141 | 
| tercio@0 | 142 local function addLine(self, v, tree, level, parent) | 
| tercio@0 | 143 	local line = new() | 
| tercio@0 | 144 	line.value = v.value | 
| tercio@0 | 145 	line.text = v.text | 
| tercio@0 | 146 	line.icon = v.icon | 
| tercio@0 | 147 	line.iconCoords = v.iconCoords | 
| tercio@0 | 148 	line.disabled = v.disabled | 
| tercio@0 | 149 	line.tree = tree | 
| tercio@0 | 150 	line.level = level | 
| tercio@0 | 151 	line.parent = parent | 
| tercio@0 | 152 	line.visible = v.visible | 
| tercio@0 | 153 	line.uniquevalue = GetButtonUniqueValue(line) | 
| tercio@0 | 154 	if v.children then | 
| tercio@0 | 155 		line.hasChildren = true | 
| tercio@0 | 156 	else | 
| tercio@0 | 157 		line.hasChildren = nil | 
| tercio@0 | 158 	end | 
| tercio@0 | 159 	self.lines[#self.lines+1] = line | 
| tercio@0 | 160 	return line | 
| tercio@0 | 161 end | 
| tercio@0 | 162 | 
| tercio@0 | 163 --fire an update after one frame to catch the treeframes height | 
| tercio@0 | 164 local function FirstFrameUpdate(frame) | 
| tercio@0 | 165 	local self = frame.obj | 
| tercio@0 | 166 	frame:SetScript("OnUpdate", nil) | 
| tercio@0 | 167 	self:RefreshTree() | 
| tercio@0 | 168 end | 
| tercio@0 | 169 | 
| tercio@0 | 170 local function BuildUniqueValue(...) | 
| tercio@0 | 171 	local n = select('#', ...) | 
| tercio@0 | 172 	if n == 1 then | 
| tercio@0 | 173 		return ... | 
| tercio@0 | 174 	else | 
| tercio@0 | 175 		return (...).."\001"..BuildUniqueValue(select(2,...)) | 
| tercio@0 | 176 	end | 
| tercio@0 | 177 end | 
| tercio@0 | 178 | 
| tercio@0 | 179 --[[----------------------------------------------------------------------------- | 
| tercio@0 | 180 Scripts | 
| tercio@0 | 181 -------------------------------------------------------------------------------]] | 
| tercio@0 | 182 local function Expand_OnClick(frame) | 
| tercio@0 | 183 	local button = frame.button | 
| tercio@0 | 184 	local self = button.obj | 
| tercio@0 | 185 	local status = (self.status or self.localstatus).groups | 
| tercio@0 | 186 	status[button.uniquevalue] = not status[button.uniquevalue] | 
| tercio@0 | 187 	self:RefreshTree() | 
| tercio@0 | 188 end | 
| tercio@0 | 189 | 
| tercio@0 | 190 local function Button_OnClick(frame) | 
| tercio@0 | 191 	local self = frame.obj | 
| tercio@0 | 192 	self:Fire("OnClick", frame.uniquevalue, frame.selected) | 
| tercio@0 | 193 	if not frame.selected then | 
| tercio@0 | 194 		self:SetSelected(frame.uniquevalue) | 
| tercio@0 | 195 		frame.selected = true | 
| tercio@0 | 196 		frame:LockHighlight() | 
| tercio@0 | 197 		self:RefreshTree() | 
| tercio@0 | 198 	end | 
| tercio@0 | 199 	AceGUI:ClearFocus() | 
| tercio@0 | 200 end | 
| tercio@0 | 201 | 
| tercio@0 | 202 local function Button_OnDoubleClick(button) | 
| tercio@0 | 203 	local self = button.obj | 
| tercio@0 | 204 	local status = self.status or self.localstatus | 
| tercio@0 | 205 	local status = (self.status or self.localstatus).groups | 
| tercio@0 | 206 	status[button.uniquevalue] = not status[button.uniquevalue] | 
| tercio@0 | 207 	self:RefreshTree() | 
| tercio@0 | 208 end | 
| tercio@0 | 209 | 
| tercio@0 | 210 local function Button_OnEnter(frame) | 
| tercio@0 | 211 	local self = frame.obj | 
| tercio@0 | 212 	self:Fire("OnButtonEnter", frame.uniquevalue, frame) | 
| tercio@0 | 213 | 
| tercio@0 | 214 	if self.enabletooltips then | 
| tercio@0 | 215 		GameTooltip:SetOwner(frame, "ANCHOR_NONE") | 
| tercio@0 | 216 		GameTooltip:SetPoint("LEFT",frame,"RIGHT") | 
| tercio@5 | 217 		GameTooltip:SetText(frame.text:GetText() or "", 1, .82, 0, true) | 
| tercio@0 | 218 | 
| tercio@0 | 219 		GameTooltip:Show() | 
| tercio@0 | 220 	end | 
| tercio@0 | 221 end | 
| tercio@0 | 222 | 
| tercio@0 | 223 local function Button_OnLeave(frame) | 
| tercio@0 | 224 	local self = frame.obj | 
| tercio@0 | 225 	self:Fire("OnButtonLeave", frame.uniquevalue, frame) | 
| tercio@0 | 226 | 
| tercio@0 | 227 	if self.enabletooltips then | 
| tercio@0 | 228 		GameTooltip:Hide() | 
| tercio@0 | 229 	end | 
| tercio@0 | 230 end | 
| tercio@0 | 231 | 
| tercio@0 | 232 local function OnScrollValueChanged(frame, value) | 
| tercio@0 | 233 	if frame.obj.noupdate then return end | 
| tercio@0 | 234 	local self = frame.obj | 
| tercio@0 | 235 	local status = self.status or self.localstatus | 
| tercio@0 | 236 	status.scrollvalue = floor(value + 0.5) | 
| tercio@0 | 237 	self:RefreshTree() | 
| tercio@0 | 238 	AceGUI:ClearFocus() | 
| tercio@0 | 239 end | 
| tercio@0 | 240 | 
| tercio@0 | 241 local function Tree_OnSizeChanged(frame) | 
| tercio@0 | 242 	frame.obj:RefreshTree() | 
| tercio@0 | 243 end | 
| tercio@0 | 244 | 
| tercio@0 | 245 local function Tree_OnMouseWheel(frame, delta) | 
| tercio@0 | 246 	local self = frame.obj | 
| tercio@0 | 247 	if self.showscroll then | 
| tercio@0 | 248 		local scrollbar = self.scrollbar | 
| tercio@0 | 249 		local min, max = scrollbar:GetMinMaxValues() | 
| tercio@0 | 250 		local value = scrollbar:GetValue() | 
| tercio@0 | 251 		local newvalue = math_min(max,math_max(min,value - delta)) | 
| tercio@0 | 252 		if value ~= newvalue then | 
| tercio@0 | 253 			scrollbar:SetValue(newvalue) | 
| tercio@0 | 254 		end | 
| tercio@0 | 255 	end | 
| tercio@0 | 256 end | 
| tercio@0 | 257 | 
| tercio@0 | 258 local function Dragger_OnLeave(frame) | 
| tercio@0 | 259 	frame:SetBackdropColor(1, 1, 1, 0) | 
| tercio@0 | 260 end | 
| tercio@0 | 261 | 
| tercio@0 | 262 local function Dragger_OnEnter(frame) | 
| tercio@0 | 263 	frame:SetBackdropColor(1, 1, 1, 0.8) | 
| tercio@0 | 264 end | 
| tercio@0 | 265 | 
| tercio@0 | 266 local function Dragger_OnMouseDown(frame) | 
| tercio@0 | 267 	local treeframe = frame:GetParent() | 
| tercio@0 | 268 	treeframe:StartSizing("RIGHT") | 
| tercio@0 | 269 end | 
| tercio@0 | 270 | 
| tercio@0 | 271 local function Dragger_OnMouseUp(frame) | 
| tercio@0 | 272 	local treeframe = frame:GetParent() | 
| tercio@0 | 273 	local self = treeframe.obj | 
| tercio@0 | 274 	local frame = treeframe:GetParent() | 
| tercio@0 | 275 	treeframe:StopMovingOrSizing() | 
| tercio@0 | 276 	--treeframe:SetScript("OnUpdate", nil) | 
| tercio@0 | 277 	treeframe:SetUserPlaced(false) | 
| tercio@0 | 278 	--Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize | 
| tercio@0 | 279 	treeframe:SetHeight(0) | 
| tercio@0 | 280 	treeframe:SetPoint("TOPLEFT", frame, "TOPLEFT",0,0) | 
| tercio@0 | 281 	treeframe:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT",0,0) | 
| tercio@0 | 282 | 
| tercio@0 | 283 	local status = self.status or self.localstatus | 
| tercio@0 | 284 	status.treewidth = treeframe:GetWidth() | 
| tercio@0 | 285 | 
| tercio@0 | 286 	treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth()) | 
| tercio@0 | 287 	-- recalculate the content width | 
| tercio@0 | 288 	treeframe.obj:OnWidthSet(status.fullwidth) | 
| tercio@0 | 289 	-- update the layout of the content | 
| tercio@0 | 290 	treeframe.obj:DoLayout() | 
| tercio@0 | 291 end | 
| tercio@0 | 292 | 
| tercio@0 | 293 --[[----------------------------------------------------------------------------- | 
| tercio@0 | 294 Methods | 
| tercio@0 | 295 -------------------------------------------------------------------------------]] | 
| tercio@0 | 296 local methods = { | 
| tercio@0 | 297 	["OnAcquire"] = function(self) | 
| tercio@0 | 298 		self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE) | 
| tercio@0 | 299 		self:EnableButtonTooltips(true) | 
| Tercio@11 | 300 		self.frame:SetScript("OnUpdate", FirstFrameUpdate) | 
| tercio@0 | 301 	end, | 
| tercio@0 | 302 | 
| tercio@0 | 303 	["OnRelease"] = function(self) | 
| tercio@0 | 304 		self.status = nil | 
| tercio@0 | 305 		for k, v in pairs(self.localstatus) do | 
| tercio@0 | 306 			if k == "groups" then | 
| tercio@0 | 307 				for k2 in pairs(v) do | 
| tercio@0 | 308 					v[k2] = nil | 
| tercio@0 | 309 				end | 
| tercio@0 | 310 			else | 
| tercio@0 | 311 				self.localstatus[k] = nil | 
| tercio@0 | 312 			end | 
| tercio@0 | 313 		end | 
| tercio@0 | 314 		self.localstatus.scrollvalue = 0 | 
| tercio@0 | 315 		self.localstatus.treewidth = DEFAULT_TREE_WIDTH | 
| tercio@0 | 316 		self.localstatus.treesizable = DEFAULT_TREE_SIZABLE | 
| tercio@0 | 317 	end, | 
| tercio@0 | 318 | 
| tercio@0 | 319 	["EnableButtonTooltips"] = function(self, enable) | 
| tercio@0 | 320 		self.enabletooltips = enable | 
| tercio@0 | 321 	end, | 
| tercio@0 | 322 | 
| tercio@0 | 323 	["CreateButton"] = function(self) | 
| tercio@0 | 324 		local num = AceGUI:GetNextWidgetNum("TreeGroupButton") | 
| tercio@0 | 325 		local button = CreateFrame("Button", ("AceGUI30TreeButton%d"):format(num), self.treeframe, "OptionsListButtonTemplate") | 
| tercio@0 | 326 		button.obj = self | 
| tercio@0 | 327 | 
| tercio@0 | 328 		local icon = button:CreateTexture(nil, "OVERLAY") | 
| tercio@0 | 329 		icon:SetWidth(14) | 
| tercio@0 | 330 		icon:SetHeight(14) | 
| tercio@0 | 331 		button.icon = icon | 
| tercio@0 | 332 | 
| tercio@0 | 333 		button:SetScript("OnClick",Button_OnClick) | 
| tercio@0 | 334 		button:SetScript("OnDoubleClick", Button_OnDoubleClick) | 
| tercio@0 | 335 		button:SetScript("OnEnter",Button_OnEnter) | 
| tercio@0 | 336 		button:SetScript("OnLeave",Button_OnLeave) | 
| tercio@0 | 337 | 
| tercio@0 | 338 		button.toggle.button = button | 
| tercio@0 | 339 		button.toggle:SetScript("OnClick",Expand_OnClick) | 
| tercio@0 | 340 | 
| Tercio@11 | 341 		button.text:SetHeight(14) -- Prevents text wrapping | 
| Tercio@11 | 342 | 
| tercio@0 | 343 		return button | 
| tercio@0 | 344 	end, | 
| tercio@0 | 345 | 
| tercio@0 | 346 	["SetStatusTable"] = function(self, status) | 
| tercio@0 | 347 		assert(type(status) == "table") | 
| tercio@0 | 348 		self.status = status | 
| tercio@0 | 349 		if not status.groups then | 
| tercio@0 | 350 			status.groups = {} | 
| tercio@0 | 351 		end | 
| tercio@0 | 352 		if not status.scrollvalue then | 
| tercio@0 | 353 			status.scrollvalue = 0 | 
| tercio@0 | 354 		end | 
| tercio@0 | 355 		if not status.treewidth then | 
| tercio@0 | 356 			status.treewidth = DEFAULT_TREE_WIDTH | 
| tercio@0 | 357 		end | 
| tercio@0 | 358 		if status.treesizable == nil then | 
| tercio@0 | 359 			status.treesizable = DEFAULT_TREE_SIZABLE | 
| tercio@0 | 360 		end | 
| tercio@0 | 361 		self:SetTreeWidth(status.treewidth,status.treesizable) | 
| tercio@0 | 362 		self:RefreshTree() | 
| tercio@0 | 363 	end, | 
| tercio@0 | 364 | 
| tercio@0 | 365 	--sets the tree to be displayed | 
| tercio@0 | 366 	["SetTree"] = function(self, tree, filter) | 
| tercio@0 | 367 		self.filter = filter | 
| tercio@0 | 368 		if tree then | 
| tercio@0 | 369 			assert(type(tree) == "table") | 
| tercio@0 | 370 		end | 
| tercio@0 | 371 		self.tree = tree | 
| tercio@0 | 372 		self:RefreshTree() | 
| tercio@0 | 373 	end, | 
| tercio@0 | 374 | 
| tercio@0 | 375 	["BuildLevel"] = function(self, tree, level, parent) | 
| tercio@0 | 376 		local groups = (self.status or self.localstatus).groups | 
| tercio@0 | 377 		local hasChildren = self.hasChildren | 
| tercio@0 | 378 | 
| tercio@0 | 379 		for i, v in ipairs(tree) do | 
| tercio@0 | 380 			if v.children then | 
| tercio@0 | 381 				if not self.filter or ShouldDisplayLevel(v.children) then | 
| tercio@0 | 382 					local line = addLine(self, v, tree, level, parent) | 
| tercio@0 | 383 					if groups[line.uniquevalue] then | 
| tercio@0 | 384 						self:BuildLevel(v.children, level+1, line) | 
| tercio@0 | 385 					end | 
| tercio@0 | 386 				end | 
| tercio@0 | 387 			elseif v.visible ~= false or not self.filter then | 
| tercio@0 | 388 				addLine(self, v, tree, level, parent) | 
| tercio@0 | 389 			end | 
| tercio@0 | 390 		end | 
| tercio@0 | 391 	end, | 
| tercio@0 | 392 | 
| tercio@0 | 393 	["RefreshTree"] = function(self,scrollToSelection) | 
| tercio@0 | 394 		local buttons = self.buttons | 
| tercio@0 | 395 		local lines = self.lines | 
| tercio@0 | 396 | 
| tercio@0 | 397 		for i, v in ipairs(buttons) do | 
| tercio@0 | 398 			v:Hide() | 
| tercio@0 | 399 		end | 
| tercio@0 | 400 		while lines[1] do | 
| tercio@0 | 401 			local t = tremove(lines) | 
| tercio@0 | 402 			for k in pairs(t) do | 
| tercio@0 | 403 				t[k] = nil | 
| tercio@0 | 404 			end | 
| tercio@0 | 405 			del(t) | 
| tercio@0 | 406 		end | 
| tercio@0 | 407 | 
| tercio@0 | 408 		if not self.tree then return end | 
| tercio@0 | 409 		--Build the list of visible entries from the tree and status tables | 
| tercio@0 | 410 		local status = self.status or self.localstatus | 
| tercio@0 | 411 		local groupstatus = status.groups | 
| tercio@0 | 412 		local tree = self.tree | 
| tercio@0 | 413 | 
| tercio@0 | 414 		local treeframe = self.treeframe | 
| tercio@0 | 415 | 
| tercio@0 | 416 		status.scrollToSelection = status.scrollToSelection or scrollToSelection	-- needs to be cached in case the control hasn't been drawn yet (code bails out below) | 
| tercio@0 | 417 | 
| tercio@0 | 418 		self:BuildLevel(tree, 1) | 
| tercio@0 | 419 | 
| tercio@0 | 420 		local numlines = #lines | 
| tercio@0 | 421 | 
| tercio@0 | 422 		local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18)) | 
| tercio@0 | 423 		if maxlines <= 0 then return end | 
| tercio@0 | 424 | 
| tercio@0 | 425 		local first, last | 
| tercio@0 | 426 | 
| tercio@0 | 427 		scrollToSelection = status.scrollToSelection | 
| tercio@0 | 428 		status.scrollToSelection = nil | 
| tercio@0 | 429 | 
| tercio@0 | 430 		if numlines <= maxlines then | 
| tercio@0 | 431 			--the whole tree fits in the frame | 
| tercio@0 | 432 			status.scrollvalue = 0 | 
| tercio@0 | 433 			self:ShowScroll(false) | 
| tercio@0 | 434 			first, last = 1, numlines | 
| tercio@0 | 435 		else | 
| tercio@0 | 436 			self:ShowScroll(true) | 
| tercio@0 | 437 			--scrolling will be needed | 
| tercio@0 | 438 			self.noupdate = true | 
| tercio@0 | 439 			self.scrollbar:SetMinMaxValues(0, numlines - maxlines) | 
| tercio@0 | 440 			--check if we are scrolled down too far | 
| tercio@0 | 441 			if numlines - status.scrollvalue < maxlines then | 
| tercio@0 | 442 				status.scrollvalue = numlines - maxlines | 
| tercio@0 | 443 			end | 
| tercio@0 | 444 			self.noupdate = nil | 
| tercio@0 | 445 			first, last = status.scrollvalue+1, status.scrollvalue + maxlines | 
| tercio@0 | 446 			--show selection? | 
| tercio@0 | 447 			if scrollToSelection and status.selected then | 
| tercio@0 | 448 				local show | 
| tercio@0 | 449 				for i,line in ipairs(lines) do	-- find the line number | 
| tercio@0 | 450 					if line.uniquevalue==status.selected then | 
| tercio@0 | 451 						show=i | 
| tercio@0 | 452 					end | 
| tercio@0 | 453 				end | 
| tercio@0 | 454 				if not show then | 
| tercio@0 | 455 					-- selection was deleted or something? | 
| tercio@0 | 456 				elseif show>=first and show<=last then | 
| tercio@0 | 457 					-- all good | 
| tercio@0 | 458 				else | 
| tercio@0 | 459 					-- scrolling needed! | 
| tercio@0 | 460 					if show<first then | 
| tercio@0 | 461 						status.scrollvalue = show-1 | 
| tercio@0 | 462 					else | 
| tercio@0 | 463 						status.scrollvalue = show-maxlines | 
| tercio@0 | 464 					end | 
| tercio@0 | 465 					first, last = status.scrollvalue+1, status.scrollvalue + maxlines | 
| tercio@0 | 466 				end | 
| tercio@0 | 467 			end | 
| tercio@0 | 468 			if self.scrollbar:GetValue() ~= status.scrollvalue then | 
| tercio@0 | 469 				self.scrollbar:SetValue(status.scrollvalue) | 
| tercio@0 | 470 			end | 
| tercio@0 | 471 		end | 
| tercio@0 | 472 | 
| tercio@0 | 473 		local buttonnum = 1 | 
| tercio@0 | 474 		for i = first, last do | 
| tercio@0 | 475 			local line = lines[i] | 
| tercio@0 | 476 			local button = buttons[buttonnum] | 
| tercio@0 | 477 			if not button then | 
| tercio@0 | 478 				button = self:CreateButton() | 
| tercio@0 | 479 | 
| tercio@0 | 480 				buttons[buttonnum] = button | 
| tercio@0 | 481 				button:SetParent(treeframe) | 
| tercio@0 | 482 				button:SetFrameLevel(treeframe:GetFrameLevel()+1) | 
| tercio@0 | 483 				button:ClearAllPoints() | 
| tercio@0 | 484 				if buttonnum == 1 then | 
| tercio@0 | 485 					if self.showscroll then | 
| tercio@0 | 486 						button:SetPoint("TOPRIGHT", -22, -10) | 
| tercio@0 | 487 						button:SetPoint("TOPLEFT", 0, -10) | 
| tercio@0 | 488 					else | 
| tercio@0 | 489 						button:SetPoint("TOPRIGHT", 0, -10) | 
| tercio@0 | 490 						button:SetPoint("TOPLEFT", 0, -10) | 
| tercio@0 | 491 					end | 
| tercio@0 | 492 				else | 
| tercio@0 | 493 					button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0) | 
| tercio@0 | 494 					button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0) | 
| tercio@0 | 495 				end | 
| tercio@0 | 496 			end | 
| tercio@0 | 497 | 
| tercio@0 | 498 			UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] ) | 
| tercio@0 | 499 			button:Show() | 
| tercio@0 | 500 			buttonnum = buttonnum + 1 | 
| tercio@0 | 501 		end | 
| tercio@0 | 502 | 
| tercio@0 | 503 	end, | 
| tercio@0 | 504 | 
| tercio@0 | 505 	["SetSelected"] = function(self, value) | 
| tercio@0 | 506 		local status = self.status or self.localstatus | 
| tercio@0 | 507 		if status.selected ~= value then | 
| tercio@0 | 508 			status.selected = value | 
| tercio@0 | 509 			self:Fire("OnGroupSelected", value) | 
| tercio@0 | 510 		end | 
| tercio@0 | 511 	end, | 
| tercio@0 | 512 | 
| tercio@0 | 513 	["Select"] = function(self, uniquevalue, ...) | 
| tercio@0 | 514 		self.filter = false | 
| tercio@0 | 515 		local status = self.status or self.localstatus | 
| tercio@0 | 516 		local groups = status.groups | 
| tercio@0 | 517 		local path = {...} | 
| tercio@0 | 518 		for i = 1, #path do | 
| tercio@0 | 519 			groups[tconcat(path, "\001", 1, i)] = true | 
| tercio@0 | 520 		end | 
| tercio@0 | 521 		status.selected = uniquevalue | 
| tercio@0 | 522 		self:RefreshTree(true) | 
| tercio@0 | 523 		self:Fire("OnGroupSelected", uniquevalue) | 
| tercio@0 | 524 	end, | 
| tercio@0 | 525 | 
| tercio@0 | 526 	["SelectByPath"] = function(self, ...) | 
| tercio@0 | 527 		self:Select(BuildUniqueValue(...), ...) | 
| tercio@0 | 528 	end, | 
| tercio@0 | 529 | 
| tercio@0 | 530 	["SelectByValue"] = function(self, uniquevalue) | 
| tercio@0 | 531 		self:Select(uniquevalue, ("\001"):split(uniquevalue)) | 
| tercio@0 | 532 	end, | 
| tercio@0 | 533 | 
| tercio@0 | 534 	["ShowScroll"] = function(self, show) | 
| tercio@0 | 535 		self.showscroll = show | 
| tercio@0 | 536 		if show then | 
| tercio@0 | 537 			self.scrollbar:Show() | 
| tercio@0 | 538 			if self.buttons[1] then | 
| tercio@0 | 539 				self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10) | 
| tercio@0 | 540 			end | 
| tercio@0 | 541 		else | 
| tercio@0 | 542 			self.scrollbar:Hide() | 
| tercio@0 | 543 			if self.buttons[1] then | 
| tercio@0 | 544 				self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10) | 
| tercio@0 | 545 			end | 
| tercio@0 | 546 		end | 
| tercio@0 | 547 	end, | 
| tercio@0 | 548 | 
| tercio@0 | 549 	["OnWidthSet"] = function(self, width) | 
| tercio@0 | 550 		local content = self.content | 
| tercio@0 | 551 		local treeframe = self.treeframe | 
| tercio@0 | 552 		local status = self.status or self.localstatus | 
| tercio@0 | 553 		status.fullwidth = width | 
| tercio@0 | 554 | 
| tercio@0 | 555 		local contentwidth = width - status.treewidth - 20 | 
| tercio@0 | 556 		if contentwidth < 0 then | 
| tercio@0 | 557 			contentwidth = 0 | 
| tercio@0 | 558 		end | 
| tercio@0 | 559 		content:SetWidth(contentwidth) | 
| tercio@0 | 560 		content.width = contentwidth | 
| tercio@0 | 561 | 
| tercio@0 | 562 		local maxtreewidth = math_min(400, width - 50) | 
| tercio@0 | 563 | 
| tercio@0 | 564 		if maxtreewidth > 100 and status.treewidth > maxtreewidth then | 
| tercio@0 | 565 			self:SetTreeWidth(maxtreewidth, status.treesizable) | 
| tercio@0 | 566 		end | 
| tercio@0 | 567 		treeframe:SetMaxResize(maxtreewidth, 1600) | 
| tercio@0 | 568 	end, | 
| tercio@0 | 569 | 
| tercio@0 | 570 	["OnHeightSet"] = function(self, height) | 
| tercio@0 | 571 		local content = self.content | 
| tercio@0 | 572 		local contentheight = height - 20 | 
| tercio@0 | 573 		if contentheight < 0 then | 
| tercio@0 | 574 			contentheight = 0 | 
| tercio@0 | 575 		end | 
| tercio@0 | 576 		content:SetHeight(contentheight) | 
| tercio@0 | 577 		content.height = contentheight | 
| tercio@0 | 578 	end, | 
| tercio@0 | 579 | 
| tercio@0 | 580 	["SetTreeWidth"] = function(self, treewidth, resizable) | 
| tercio@0 | 581 		if not resizable then | 
| tercio@0 | 582 			if type(treewidth) == 'number' then | 
| tercio@0 | 583 				resizable = false | 
| tercio@0 | 584 			elseif type(treewidth) == 'boolean' then | 
| tercio@0 | 585 				resizable = treewidth | 
| tercio@0 | 586 				treewidth = DEFAULT_TREE_WIDTH | 
| tercio@0 | 587 			else | 
| tercio@0 | 588 				resizable = false | 
| tercio@0 | 589 				treewidth = DEFAULT_TREE_WIDTH | 
| tercio@0 | 590 			end | 
| tercio@0 | 591 		end | 
| tercio@0 | 592 		self.treeframe:SetWidth(treewidth) | 
| tercio@0 | 593 		self.dragger:EnableMouse(resizable) | 
| tercio@0 | 594 | 
| tercio@0 | 595 		local status = self.status or self.localstatus | 
| tercio@0 | 596 		status.treewidth = treewidth | 
| tercio@0 | 597 		status.treesizable = resizable | 
| tercio@0 | 598 | 
| tercio@0 | 599 		-- recalculate the content width | 
| tercio@0 | 600 		if status.fullwidth then | 
| tercio@0 | 601 			self:OnWidthSet(status.fullwidth) | 
| tercio@0 | 602 		end | 
| tercio@0 | 603 	end, | 
| tercio@0 | 604 | 
| tercio@0 | 605 	["GetTreeWidth"] = function(self) | 
| tercio@0 | 606 		local status = self.status or self.localstatus | 
| tercio@0 | 607 		return status.treewidth or DEFAULT_TREE_WIDTH | 
| tercio@0 | 608 	end, | 
| tercio@0 | 609 | 
| tercio@0 | 610 	["LayoutFinished"] = function(self, width, height) | 
| tercio@0 | 611 		if self.noAutoHeight then return end | 
| tercio@0 | 612 		self:SetHeight((height or 0) + 20) | 
| tercio@0 | 613 	end | 
| tercio@0 | 614 } | 
| tercio@0 | 615 | 
| tercio@0 | 616 --[[----------------------------------------------------------------------------- | 
| tercio@0 | 617 Constructor | 
| tercio@0 | 618 -------------------------------------------------------------------------------]] | 
| tercio@0 | 619 local PaneBackdrop  = { | 
| tercio@0 | 620 	bgFile = "Interface\\ChatFrame\\ChatFrameBackground", | 
| tercio@0 | 621 	edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", | 
| tercio@0 | 622 	tile = true, tileSize = 16, edgeSize = 16, | 
| tercio@0 | 623 	insets = { left = 3, right = 3, top = 5, bottom = 3 } | 
| tercio@0 | 624 } | 
| tercio@0 | 625 | 
| tercio@0 | 626 local DraggerBackdrop  = { | 
| tercio@0 | 627 	bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", | 
| tercio@0 | 628 	edgeFile = nil, | 
| tercio@0 | 629 	tile = true, tileSize = 16, edgeSize = 0, | 
| tercio@0 | 630 	insets = { left = 3, right = 3, top = 7, bottom = 7 } | 
| tercio@0 | 631 } | 
| tercio@0 | 632 | 
| tercio@0 | 633 local function Constructor() | 
| tercio@0 | 634 	local num = AceGUI:GetNextWidgetNum(Type) | 
| tercio@0 | 635 	local frame = CreateFrame("Frame", nil, UIParent) | 
| tercio@0 | 636 | 
| tercio@0 | 637 	local treeframe = CreateFrame("Frame", nil, frame) | 
| tercio@0 | 638 	treeframe:SetPoint("TOPLEFT") | 
| tercio@0 | 639 	treeframe:SetPoint("BOTTOMLEFT") | 
| tercio@0 | 640 	treeframe:SetWidth(DEFAULT_TREE_WIDTH) | 
| tercio@0 | 641 	treeframe:EnableMouseWheel(true) | 
| tercio@0 | 642 	treeframe:SetBackdrop(PaneBackdrop) | 
| tercio@0 | 643 	treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5) | 
| tercio@0 | 644 	treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4) | 
| tercio@0 | 645 	treeframe:SetResizable(true) | 
| tercio@0 | 646 	treeframe:SetMinResize(100, 1) | 
| tercio@0 | 647 	treeframe:SetMaxResize(400, 1600) | 
| tercio@0 | 648 	treeframe:SetScript("OnUpdate", FirstFrameUpdate) | 
| tercio@0 | 649 	treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged) | 
| tercio@0 | 650 	treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel) | 
| tercio@0 | 651 | 
| tercio@0 | 652 	local dragger = CreateFrame("Frame", nil, treeframe) | 
| tercio@0 | 653 	dragger:SetWidth(8) | 
| tercio@0 | 654 	dragger:SetPoint("TOP", treeframe, "TOPRIGHT") | 
| tercio@0 | 655 	dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT") | 
| tercio@0 | 656 	dragger:SetBackdrop(DraggerBackdrop) | 
| tercio@0 | 657 	dragger:SetBackdropColor(1, 1, 1, 0) | 
| tercio@0 | 658 	dragger:SetScript("OnEnter", Dragger_OnEnter) | 
| tercio@0 | 659 	dragger:SetScript("OnLeave", Dragger_OnLeave) | 
| tercio@0 | 660 	dragger:SetScript("OnMouseDown", Dragger_OnMouseDown) | 
| tercio@0 | 661 	dragger:SetScript("OnMouseUp", Dragger_OnMouseUp) | 
| tercio@0 | 662 | 
| tercio@0 | 663 	local scrollbar = CreateFrame("Slider", ("AceConfigDialogTreeGroup%dScrollBar"):format(num), treeframe, "UIPanelScrollBarTemplate") | 
| tercio@0 | 664 	scrollbar:SetScript("OnValueChanged", nil) | 
| tercio@0 | 665 	scrollbar:SetPoint("TOPRIGHT", -10, -26) | 
| tercio@0 | 666 	scrollbar:SetPoint("BOTTOMRIGHT", -10, 26) | 
| tercio@0 | 667 	scrollbar:SetMinMaxValues(0,0) | 
| tercio@0 | 668 	scrollbar:SetValueStep(1) | 
| tercio@0 | 669 	scrollbar:SetValue(0) | 
| tercio@0 | 670 	scrollbar:SetWidth(16) | 
| tercio@0 | 671 	scrollbar:SetScript("OnValueChanged", OnScrollValueChanged) | 
| tercio@0 | 672 | 
| tercio@0 | 673 	local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND") | 
| tercio@0 | 674 	scrollbg:SetAllPoints(scrollbar) | 
| Tercio@11 | 675 | 
| Tercio@11 | 676 	if IsLegion then | 
| Tercio@11 | 677 		scrollbg:SetColorTexture(0,0,0,0.4) | 
| Tercio@11 | 678 	else | 
| Tercio@11 | 679 		scrollbg:SetTexture(0,0,0,0.4) | 
| Tercio@11 | 680 	end | 
| tercio@0 | 681 | 
| tercio@0 | 682 	local border = CreateFrame("Frame",nil,frame) | 
| tercio@0 | 683 	border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT") | 
| tercio@0 | 684 	border:SetPoint("BOTTOMRIGHT") | 
| tercio@0 | 685 	border:SetBackdrop(PaneBackdrop) | 
| tercio@0 | 686 	border:SetBackdropColor(0.1, 0.1, 0.1, 0.5) | 
| tercio@0 | 687 	border:SetBackdropBorderColor(0.4, 0.4, 0.4) | 
| tercio@0 | 688 | 
| tercio@0 | 689 	--Container Support | 
| tercio@0 | 690 	local content = CreateFrame("Frame", nil, border) | 
| tercio@0 | 691 	content:SetPoint("TOPLEFT", 10, -10) | 
| tercio@0 | 692 	content:SetPoint("BOTTOMRIGHT", -10, 10) | 
| tercio@0 | 693 | 
| tercio@0 | 694 	local widget = { | 
| tercio@0 | 695 		frame        = frame, | 
| tercio@0 | 696 		lines        = {}, | 
| tercio@0 | 697 		levels       = {}, | 
| tercio@0 | 698 		buttons      = {}, | 
| tercio@0 | 699 		hasChildren  = {}, | 
| tercio@0 | 700 		localstatus  = { groups = {}, scrollvalue = 0 }, | 
| tercio@0 | 701 		filter       = false, | 
| tercio@0 | 702 		treeframe    = treeframe, | 
| tercio@0 | 703 		dragger      = dragger, | 
| tercio@0 | 704 		scrollbar    = scrollbar, | 
| tercio@0 | 705 		border       = border, | 
| tercio@0 | 706 		content      = content, | 
| tercio@0 | 707 		type         = Type | 
| tercio@0 | 708 	} | 
| tercio@0 | 709 	for method, func in pairs(methods) do | 
| tercio@0 | 710 		widget[method] = func | 
| tercio@0 | 711 	end | 
| tercio@0 | 712 	treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget | 
| tercio@0 | 713 | 
| tercio@0 | 714 	return AceGUI:RegisterAsContainer(widget) | 
| tercio@0 | 715 end | 
| tercio@0 | 716 | 
| tercio@0 | 717 AceGUI:RegisterWidgetType(Type, Constructor, Version) |