if not WeakAuras.IsCorrectVersion() then return end local AddonName, OptionsPrivate = ... -- Lua APIs local tinsert, tremove, wipe = table.insert, table.remove, wipe local pairs, type, unpack = pairs, type, unpack local loadstring, error = loadstring, error local coroutine = coroutine local _G = _G -- WoW APIs local InCombatLockdown = InCombatLockdown local CreateFrame, IsAddOnLoaded, LoadAddOn = CreateFrame, IsAddOnLoaded, LoadAddOn local AceGUI = LibStub("AceGUI-3.0") local WeakAuras = WeakAuras local L = WeakAuras.L local ADDON_NAME = "WeakAurasOptions"; local displayButtons = {}; WeakAuras.displayButtons = displayButtons; local spellCache = WeakAuras.spellCache; local savedVars = {}; OptionsPrivate.savedVars = savedVars; OptionsPrivate.expanderAnchors = {} OptionsPrivate.expanderButtons = {} local collapsedOptions = {} local collapsed = {} -- magic value local tempGroup = { id = {"tempGroup"}, regionType = "group", controlledChildren = {}, load = {}, triggers = {{}}, config = {}, authorOptions = {}, anchorPoint = "CENTER", anchorFrameType = "SCREEN", xOffset = 0, yOffset = 0 }; OptionsPrivate.tempGroup = tempGroup; function OptionsPrivate.DuplicateAura(data, newParent, massEdit) local base_id = data.id .. " " local num = 2 -- if the old id ends with a number increment the number local matchName, matchNumber = string.match(data.id, "^(.-)(%d*)$") matchNumber = tonumber(matchNumber) if (matchName ~= "" and matchNumber ~= nil) then base_id = matchName num = matchNumber + 1 end local new_id = base_id .. num while(WeakAuras.GetData(new_id)) do new_id = base_id .. num num = num + 1 end local newData = CopyTable(data) newData.id = new_id newData.parent = nil newData.uid = WeakAuras.GenerateUniqueID() if newData.controlledChildren then newData.controlledChildren = {} end WeakAuras.Add(newData) WeakAuras.NewDisplayButton(newData, massEdit) if(newParent or data.parent) then local parentId = newParent or data.parent local parentData = WeakAuras.GetData(parentId) local index if newParent then index = #parentData.controlledChildren else index = tIndexOf(parentData.controlledChildren, data.id) end if(index) then tinsert(parentData.controlledChildren, index + 1, newData.id) newData.parent = parentId WeakAuras.Add(newData) WeakAuras.Add(parentData) OptionsPrivate.Private.AddParents(parentData) for index, id in pairs(parentData.controlledChildren) do local childButton = WeakAuras.GetDisplayButton(id) childButton:SetGroup(parentData.id, parentData.regionType == "dynamicgroup") childButton:SetGroupOrder(index, #parentData.controlledChildren) end if not massEdit then local button = WeakAuras.GetDisplayButton(parentData.id) button.callbacks.UpdateExpandButton() WeakAuras.UpdateDisplayButton(parentData) end OptionsPrivate.ClearOptions(parentData.id) end end return newData end AceGUI:RegisterLayout("AbsoluteList", function(content, children) local yOffset = 0; for i = 1, #children do local child = children[i] local frame = child.frame; frame:ClearAllPoints(); frame:Show(); frame:SetPoint("LEFT", content); frame:SetPoint("RIGHT", content); frame:SetPoint("TOP", content, "TOP", 0, yOffset) if child.DoLayout then child:DoLayout() end yOffset = yOffset - ((frame.height or frame:GetHeight() or 0) + 2); end if(content.obj.LayoutFinished) then content.obj:LayoutFinished(nil, yOffset * -1); end end); AceGUI:RegisterLayout("ButtonsScrollLayout", function(content, children, skipLayoutFinished) local yOffset = 0 local scrollTop, scrollBottom = content.obj:GetScrollPos() for i = 1, #children do local child = children[i] local frame = child.frame; if not child.dragging then local frameHeight = (frame.height or frame:GetHeight() or 0); frame:ClearAllPoints(); if (-yOffset + frameHeight > scrollTop and -yOffset - frameHeight < scrollBottom) then frame:Show(); frame:SetPoint("LEFT", content); frame:SetPoint("RIGHT", content); frame:SetPoint("TOP", content, "TOP", 0, yOffset) else frame:Hide(); frame.yOffset = yOffset end yOffset = yOffset - (frameHeight + 2); end if child.DoLayout then child:DoLayout() end end if(content.obj.LayoutFinished and not skipLayoutFinished) then content.obj:LayoutFinished(nil, yOffset * -1) end end) function OptionsPrivate.MultipleDisplayTooltipDesc() local desc = {{L["Multiple Displays"], L["Temporary Group"]}}; for index, id in pairs(tempGroup.controlledChildren) do desc[index + 1] = {" ", id}; end desc[2][1] = L["Children:"] tinsert(desc, " "); tinsert(desc, {" ", "|cFF00FFFF"..L["Right-click for more options"]}); tinsert(desc, {" ", "|cFF00FFFF"..L["Drag to move"]}); return desc; end local frame; local db; local odb; local reopenAfterCombat = false; local loadedFrame = CreateFrame("FRAME"); loadedFrame:RegisterEvent("ADDON_LOADED"); loadedFrame:RegisterEvent("PLAYER_REGEN_ENABLED"); loadedFrame:RegisterEvent("PLAYER_REGEN_DISABLED"); loadedFrame:SetScript("OnEvent", function(self, event, addon) if (event == "ADDON_LOADED") then if(addon == ADDON_NAME) then db = WeakAurasSaved; WeakAurasOptionsSaved = WeakAurasOptionsSaved or {}; odb = WeakAurasOptionsSaved; -- Remove icon and id cache (replaced with spellCache) if (odb.iconCache) then odb.iconCache = nil; end if (odb.idCache) then odb.idCache = nil; end odb.spellCache = odb.spellCache or {}; spellCache.Load(odb); if odb.magnetAlign == nil then odb.magnetAlign = true end if db.import_disabled then db.import_disabled = nil end savedVars.db = db; savedVars.odb = odb; end elseif (event == "PLAYER_REGEN_DISABLED") then if(frame and frame:IsVisible()) then reopenAfterCombat = true; WeakAuras.HideOptions(); end elseif (event == "PLAYER_REGEN_ENABLED") then if (reopenAfterCombat) then reopenAfterCombat = nil; WeakAuras.ShowOptions() end end end); local function addParents(hash, data) local parent = data.parent if parent then hash[parent] = true local parentData = WeakAuras.GetData(parent) if parentData then addParents(hash, parentData) end end end function OptionsPrivate.MultipleDisplayTooltipMenu() local frame = frame; local menu = { { text = L["Add to new Group"], notCheckable = 1, func = function() local data = { id = WeakAuras.FindUnusedId(tempGroup.controlledChildren[1].." Group"), regionType = "group", }; WeakAuras.DeepMixin(data, WeakAuras.data_stub) data.internalVersion = WeakAuras.InternalVersion() WeakAuras.Add(data); WeakAuras.NewDisplayButton(data); for index, childId in pairs(tempGroup.controlledChildren) do local childData = WeakAuras.GetData(childId); tinsert(data.controlledChildren, childId); childData.parent = data.id; WeakAuras.Add(data); WeakAuras.Add(childData); OptionsPrivate.ClearOptions(childData.id) end for index, id in pairs(data.controlledChildren) do local childButton = WeakAuras.GetDisplayButton(id); childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); childButton:SetGroupOrder(index, #data.controlledChildren); end local button = WeakAuras.GetDisplayButton(data.id); button.callbacks.UpdateExpandButton(); WeakAuras.UpdateDisplayButton(data); OptionsPrivate.SortDisplayButtons(); button:Expand(); end }, { text = L["Add to new Dynamic Group"], notCheckable = 1, func = function() local data = { id = WeakAuras.FindUnusedId(tempGroup.controlledChildren[1].." Group"), regionType = "dynamicgroup", }; WeakAuras.DeepMixin(data, WeakAuras.data_stub) data.internalVersion = WeakAuras.InternalVersion() WeakAuras.Add(data); WeakAuras.NewDisplayButton(data); for index, childId in pairs(tempGroup.controlledChildren) do local childData = WeakAuras.GetData(childId); tinsert(data.controlledChildren, childId); childData.parent = data.id; childData.xOffset = 0; childData.yOffset = 0; WeakAuras.Add(data); WeakAuras.Add(childData); end for index, id in pairs(data.controlledChildren) do local childButton = WeakAuras.GetDisplayButton(id); childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); childButton:SetGroupOrder(index, #data.controlledChildren); end local button = WeakAuras.GetDisplayButton(data.id); button.callbacks.UpdateExpandButton(); WeakAuras.UpdateDisplayButton(data); OptionsPrivate.SortDisplayButtons(); button:Expand(); WeakAuras.PickDisplay(data.id); end }, { text = L["Duplicate All"], notCheckable = 1, func = function() local duplicated = {}; for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do local newData = OptionsPrivate.DuplicateAura(child) tinsert(duplicated, newData.id); end OptionsPrivate.ClearPicks(); frame:PickDisplayBatch(duplicated); end }, { text = " ", notCheckable = 1, notClickable = 1 }, { text = L["Delete all"], notCheckable = 1, func = function() local toDelete = {}; local parents = {}; for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do tinsert(toDelete, child) addParents(parents, child) end OptionsPrivate.ConfirmDelete(toDelete, parents) end }, { text = " ", notClickable = 1, notCheckable = 1, }, { text = L["Close"], notCheckable = 1, func = function() WeakAuras_DropDownMenu:Hide() end } }; local anyGrouped = false; for index, id in pairs(tempGroup.controlledChildren) do local childData = WeakAuras.GetData(id); if(childData and childData.parent) then anyGrouped = true; break; end end if(anyGrouped) then menu[1].notClickable = 1; menu[1].text = "|cFF777777"..menu[1].text; menu[2].notClickable = 1; menu[2].text = "|cFF777777"..menu[2].text; end return menu; end StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"] = { text = "", button1 = L["Delete"], button2 = L["Cancel"], OnAccept = function(self) if self.data then OptionsPrivate.Private.PauseAllDynamicGroups() OptionsPrivate.massDelete = true for _, auraData in pairs(self.data.toDelete) do WeakAuras.Delete(auraData) end OptionsPrivate.massDelete = false if self.data.parents then for id in pairs(self.data.parents) do local parentData = WeakAuras.GetData(id) local parentButton = WeakAuras.GetDisplayButton(id) WeakAuras.UpdateGroupOrders(parentData) if(#parentData.controlledChildren == 0) then parentButton:DisableExpand() else parentButton:EnableExpand() end parentButton:SetNormalTooltip() WeakAuras.Add(parentData) WeakAuras.ClearAndUpdateOptions(parentData.id) WeakAuras.UpdateDisplayButton(parentData) end end OptionsPrivate.Private.ResumeAllDynamicGroups() OptionsPrivate.SortDisplayButtons(nil, true) end end, OnCancel = function(self) if self.data.parents then for id in pairs(self.data.parents) do local parentRegion = WeakAuras.GetRegion(id) if parentRegion.Resume then parentRegion:Resume() end end end self.data = nil end, showAlert = true, whileDead = true, preferredindex = STATICPOPUP_NUMDIALOGS, } StaticPopupDialogs["WEAKAURAS_CONFIRM_IGNORE_UPDATES"] = { text = L["Do you want to ignore all future updates for this aura"], button1 = L["Yes"], button2 = L["Cancel"], OnAccept = function(self) if self.data then local auraData = WeakAuras.GetData(self.data) if auraData then for child in OptionsPrivate.Private.TraverseAll(auraData) do child.ignoreWagoUpdate = true end end OptionsPrivate.SortDisplayButtons(nil, true) end end, OnCancel = function(self) end, whileDead = true, preferredindex = STATICPOPUP_NUMDIALOGS, } function OptionsPrivate.ConfirmDelete(toDelete, parents) if toDelete then local warningForm = L["You are about to delete %d aura(s). |cFFFF0000This cannot be undone!|r Would you like to continue?"] StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"].text = warningForm:format(#toDelete) StaticPopup_Show("WEAKAURAS_CONFIRM_DELETE", "", "", {toDelete = toDelete, parents = parents}) end end function WeakAuras.OptionsFrame() if(frame) then return frame; else return nil; end end local function AfterScanForLoads() if(frame) then if (frame:IsVisible()) then OptionsPrivate.SortDisplayButtons(nil, true); else frame.needsSort = true; end end end local function OnAboutToDelete(event, uid, id, parentUid, parentId) local data = OptionsPrivate.Private.GetDataByUID(uid) if(data.controlledChildren) then for index, childId in pairs(data.controlledChildren) do local childButton = displayButtons[childId]; if(childButton) then childButton:SetGroup(); end local childData = db.displays[childId]; if(childData) then childData.parent = nil; end end end OptionsPrivate.Private.CollapseAllClones(id); OptionsPrivate.ClearOptions(id) frame:ClearPicks(); if(displayButtons[id])then frame.buttonsScroll:DeleteChild(displayButtons[id]); displayButtons[id] = nil; end collapsedOptions[id] = nil end local function OnDelete(event, uid, id, parentUid, parentId) local parentData = OptionsPrivate.Private.GetDataByUID(parentUid) if(parentData and parentData.controlledChildren and not OptionsPrivate.massDelete) then for index, childId in pairs(parentData.controlledChildren) do local childButton = displayButtons[childId]; if(childButton) then childButton:SetGroupOrder(index, #parentData.controlledChildren) end end WeakAuras.ClearAndUpdateOptions(parentData.id) WeakAuras.UpdateDisplayButton(parentData) end end local function OnRename(event, uid, oldid, newid) local data = OptionsPrivate.Private.GetDataByUID(uid) WeakAuras.displayButtons[newid] = WeakAuras.displayButtons[oldid]; WeakAuras.displayButtons[newid]:SetData(data) WeakAuras.displayButtons[oldid] = nil; OptionsPrivate.ClearOptions(oldid) WeakAuras.displayButtons[newid]:SetTitle(newid); collapsedOptions[newid] = collapsedOptions[oldid] collapsedOptions[oldid] = nil if(data.controlledChildren) then for index, childId in pairs(data.controlledChildren) do WeakAuras.displayButtons[childId]:SetGroup(newid) end end OptionsPrivate.SetGrouping() OptionsPrivate.SortDisplayButtons() WeakAuras.PickDisplay(newid) local parent = data.parent while parent do OptionsPrivate.ClearOptions(parent) local parentData = WeakAuras.GetData(parent) parent = parentData.parent end end function WeakAuras.ToggleOptions(msg, Private) if not Private then return end if not OptionsPrivate.Private then OptionsPrivate.Private = Private OptionsPrivate.Private:RegisterCallback("AuraWarningsUpdated", function(event, uid) local id = OptionsPrivate.Private.UIDtoID(uid) if displayButtons[id] then -- The button does not yet exists if a new aura is created displayButtons[id]:UpdateWarning() end end) OptionsPrivate.Private:RegisterCallback("ScanForLoads", AfterScanForLoads) OptionsPrivate.Private:RegisterCallback("AboutToDelete", OnAboutToDelete) OptionsPrivate.Private:RegisterCallback("Rename", OnRename) end if(frame and frame:IsVisible()) then WeakAuras.HideOptions(); elseif (InCombatLockdown()) then WeakAuras.prettyPrint(L["Options will open after combat ends."]) reopenAfterCombat = true; else WeakAuras.ShowOptions(msg); end end function WeakAuras.HideOptions() if(frame) then frame:Hide() end end function WeakAuras.IsOptionsOpen() if(frame and frame:IsVisible()) then return true; else return false; end end local function EnsureDisplayButton(data) local id = data.id; if not(displayButtons[id]) then displayButtons[id] = AceGUI:Create("WeakAurasDisplayButton"); if(displayButtons[id]) then displayButtons[id]:SetData(data); displayButtons[id]:Initialize(); displayButtons[id]:UpdateWarning() else print("|cFF8800FFWeakAuras|r: Error creating button for", id); end end end local function GetSortedOptionsLists() local loadedSorted, unloadedSorted = {}, {}; local to_sort = {}; for id, data in pairs(db.displays) do if(data.parent) then -- Do nothing; children will be added later elseif(OptionsPrivate.Private.loaded[id]) then tinsert(to_sort, id); end end table.sort(to_sort, function(a, b) return a < b end); for _, id in ipairs(to_sort) do local data = WeakAuras.GetData(id); for child in OptionsPrivate.Private.TraverseAll(data) do tinsert(loadedSorted, child.id) end end wipe(to_sort); for id, data in pairs(db.displays) do if(data.parent) then -- Do nothing; children will be added later elseif not(OptionsPrivate.Private.loaded[id]) then tinsert(to_sort, id); end end table.sort(to_sort, function(a, b) return a < b end); for _, id in ipairs(to_sort) do local data = WeakAuras.GetData(id); for child in OptionsPrivate.Private.TraverseAll(data) do tinsert(unloadedSorted, child.id) end end return loadedSorted, unloadedSorted; end local function LayoutDisplayButtons(msg) local total = 0; for _,_ in pairs(db.displays) do total = total + 1; end local loadedSorted, unloadedSorted = GetSortedOptionsLists(); frame:SetLoadProgressVisible(true) --frame.buttonsScroll:AddChild(frame.newButton); --if(frame.addonsButton) then -- frame.buttonsScroll:AddChild(frame.addonsButton); --end if WeakAurasCompanion then frame.buttonsScroll:AddChild(frame.pendingInstallButton); frame.buttonsScroll:AddChild(frame.pendingUpdateButton); end frame.buttonsScroll:AddChild(frame.loadedButton); frame.buttonsScroll:AddChild(frame.unloadedButton); local func2 = function() local num = frame.loadProgressNum or 0; for index, id in pairs(unloadedSorted) do local data = WeakAuras.GetData(id); if(data) then EnsureDisplayButton(data); WeakAuras.UpdateDisplayButton(data); frame.buttonsScroll:AddChild(displayButtons[data.id]); if (num % 50 == 0) then frame.buttonsScroll:ResumeLayout() frame.buttonsScroll:PerformLayout() frame.buttonsScroll:PauseLayout() end num = num + 1; end frame.loadProgress:SetText(L["Creating buttons: "]..num.."/"..total); frame.loadProgressNum = num; coroutine.yield(); end frame.buttonsScroll:ResumeLayout() frame.buttonsScroll:PerformLayout() OptionsPrivate.SortDisplayButtons(msg); OptionsPrivate.Private.PauseAllDynamicGroups(); if (WeakAuras.IsOptionsOpen()) then for id, button in pairs(displayButtons) do if(OptionsPrivate.Private.loaded[id] ~= nil) then button:PriorityShow(1); end end end OptionsPrivate.Private.ResumeAllDynamicGroups(); frame:SetLoadProgressVisible(false) end local func1 = function() local num = frame.loadProgressNum or 0; frame.buttonsScroll:PauseLayout() for index, id in pairs(loadedSorted) do local data = WeakAuras.GetData(id); if(data) then EnsureDisplayButton(data); WeakAuras.UpdateDisplayButton(data); local button = displayButtons[data.id] frame.buttonsScroll:AddChild(button); num = num + 1; end if (num % 50 == 0) then frame.buttonsScroll:ResumeLayout() frame.buttonsScroll:PerformLayout() frame.buttonsScroll:PauseLayout() end frame.loadProgress:SetText(L["Creating buttons: "]..num.."/"..total); frame.loadProgressNum = num; coroutine.yield(); end local co2 = coroutine.create(func2); OptionsPrivate.Private.dynFrame:AddAction("LayoutDisplayButtons2", co2); end local co1 = coroutine.create(func1); OptionsPrivate.Private.dynFrame:AddAction("LayoutDisplayButtons1", co1); end function WeakAuras.ShowOptions(msg) local firstLoad = not(frame); OptionsPrivate.Private.Pause(); OptionsPrivate.Private.SetFakeStates() WeakAuras.spellCache.Build() if (firstLoad) then frame = OptionsPrivate.CreateFrame(); frame.buttonsScroll.frame:Show(); LayoutDisplayButtons(msg); end if (frame:GetWidth() > GetScreenWidth()) then frame:SetWidth(GetScreenWidth()) end if (frame:GetHeight() > GetScreenHeight() - 50) then frame:SetHeight(GetScreenHeight() - 50) end frame.buttonsScroll.frame:Show(); if (frame.needsSort) then OptionsPrivate.SortDisplayButtons(); frame.needsSort = nil; end frame:Show(); if (OptionsPrivate.Private.mouseFrame) then OptionsPrivate.Private.mouseFrame:OptionsOpened(); end if (OptionsPrivate.Private.personalRessourceDisplayFrame) then OptionsPrivate.Private.personalRessourceDisplayFrame:OptionsOpened(); end if not(firstLoad) then -- Show what was last shown OptionsPrivate.Private.PauseAllDynamicGroups(); for id, button in pairs(displayButtons) do if (button:GetVisibility() > 0) then button:PriorityShow(button:GetVisibility()); end end OptionsPrivate.Private.ResumeAllDynamicGroups(); end if (frame.pickedDisplay) then if (OptionsPrivate.IsPickedMultiple()) then local children = {} for k,v in pairs(tempGroup.controlledChildren) do children[k] = v end frame:PickDisplayBatch(children); else WeakAuras.PickDisplay(frame.pickedDisplay); end else frame:NewAura(); end if (frame.window == "codereview") then frame.codereview:Close(); end if firstLoad then frame:ShowTip() end end function OptionsPrivate.UpdateOptions() frame:UpdateOptions() end function WeakAuras.ClearAndUpdateOptions(id, clearChildren) frame:ClearAndUpdateOptions(id, clearChildren) end function OptionsPrivate.ClearOptions(id) frame:ClearOptions(id) end function WeakAuras.FillOptions() frame:FillOptions() end function OptionsPrivate.EnsureOptions(data, subOption) return frame:EnsureOptions(data, subOption) end function OptionsPrivate.GetPickedDisplay() return frame:GetPickedDisplay() end function OptionsPrivate.OpenTextEditor(...) frame.texteditor:Open(...); end function OptionsPrivate.ExportToString(id) frame.importexport:Open("export", id); end function OptionsPrivate.ExportToTable(id) frame.importexport:Open("table", id); end function OptionsPrivate.ImportFromString() frame.importexport:Open("import"); end function WeakAuras.CloseImportExport() frame.codereview:Close(); frame.importexport:Close(); end function OptionsPrivate.ConvertDisplay(data, newType) local id = data.id; local visibility = displayButtons[id]:GetVisibility(); displayButtons[id]:PriorityHide(0); WeakAuras.regions[id].region:Collapse(); OptionsPrivate.Private.CollapseAllClones(id); OptionsPrivate.Private.Convert(data, newType); displayButtons[id]:SetViewRegion(WeakAuras.regions[id].region); displayButtons[id]:Initialize(); displayButtons[id]:PriorityShow(visibility); frame:ClearOptions(id) frame:FillOptions(); WeakAuras.UpdateDisplayButton(data); WeakAuras.SetMoverSizer(id) OptionsPrivate.ResetMoverSizer(); OptionsPrivate.SortDisplayButtons() end function WeakAuras.NewDisplayButton(data, massEdit) local id = data.id; OptionsPrivate.Private.ScanForLoads({[id] = true}); EnsureDisplayButton(db.displays[id]); WeakAuras.UpdateDisplayButton(db.displays[id]); frame.buttonsScroll:AddChild(displayButtons[id]); if not massEdit then OptionsPrivate.SortDisplayButtons() end end function WeakAuras.UpdateGroupOrders(data) if(data.controlledChildren) then local total = #data.controlledChildren; for index, id in pairs(data.controlledChildren) do local button = WeakAuras.GetDisplayButton(id); button:SetGroupOrder(index, total); end end end function OptionsPrivate.UpdateButtonsScroll() if OptionsPrivate.Private.IsOptionsProcessingPaused() then return end frame.buttonsScroll:DoLayout() end local function addChildButtons(button, children, visible) local controlledChildren = children[button:GetTitle()]; if(controlledChildren) then table.sort(controlledChildren, function(a, b) return displayButtons[a]:GetGroupOrder() < displayButtons[b]:GetGroupOrder(); end); for _, groupchild in ipairs(controlledChildren) do if(button:GetExpanded()) then displayButtons[groupchild].frame:Show(); if displayButtons[groupchild].AcquireThumbnail then displayButtons[groupchild]:AcquireThumbnail() end tinsert(frame.buttonsScroll.children, displayButtons[groupchild]); visible[displayButtons[groupchild]] = true addChildButtons(displayButtons[groupchild], children, visible) end end end end local function anyParentLoaded(parentId) if(parentId) then if(OptionsPrivate.Private.loaded[parentId]) then return true end local parentData = WeakAuras.GetData(parentId) if parentData then return anyParentLoaded(parentData.parent) end end return false end local previousFilter; local pendingUpdateButtons = {} local pendingInstallButtons = {} function OptionsPrivate.SortDisplayButtons(filter, overrideReset, id) if (OptionsPrivate.Private.IsOptionsProcessingPaused()) then return; end local recenter = false; filter = filter or (overrideReset and previousFilter or ""); if(frame.filterInput:GetText() ~= filter) then frame.filterInput:SetText(filter); end if(previousFilter and previousFilter ~= "" and (filter == "" or not filter)) then recenter = true; end previousFilter = filter; filter = filter:lower(); wipe(frame.buttonsScroll.children); local pendingInstallButtonShown = false local CompanionData = WeakAurasCompanion and WeakAurasCompanion.WeakAuras or WeakAurasCompanion if CompanionData and CompanionData.stash then for id, companionData in pairs(CompanionData.stash) do if not pendingInstallButtonShown then tinsert(frame.buttonsScroll.children, frame.pendingInstallButton) pendingInstallButtonShown = true end local child = pendingInstallButtons[id] if frame.pendingInstallButton:GetExpanded() then if not child then child = AceGUI:Create("WeakAurasPendingInstallButton") pendingInstallButtons[id] = child child:Initialize(id, companionData) if companionData.logo then child:SetLogo(companionData.logo) end if companionData.refreshLogo then child:SetRefreshLogo(companionData.refreshLogo) end child.frame:Show() child:AcquireThumbnail() frame.buttonsScroll:AddChild(child) end if not child.frame:IsShown() then child.frame:Show() child:AcquireThumbnail() end tinsert(frame.buttonsScroll.children, child) elseif child then child.frame:Hide() if child.ReleaseThumbnail then child:ReleaseThumbnail() end end end end if not pendingInstallButtonShown and frame.pendingInstallButton then frame.pendingInstallButton.frame:Hide() end local pendingUpdateButtonShown = false if CompanionData then local buttonsShown = {} for _, button in pairs(pendingUpdateButtons) do button:ResetLinkedAuras() end for id, aura in pairs(WeakAurasSaved.displays) do if not aura.ignoreWagoUpdate and aura.url and aura.url ~= "" then local slug, version = aura.url:match("wago.io/([^/]+)/([0-9]+)") if not slug and not version then slug = aura.url:match("wago.io/([^/]+)$") version = 1 end if slug and version then local auraData = CompanionData.slugs and CompanionData.slugs[slug] if auraData and auraData.wagoVersion then if tonumber(auraData.wagoVersion) > tonumber(version) then -- there is an update for this aura if not pendingUpdateButtonShown then tinsert(frame.buttonsScroll.children, frame.pendingUpdateButton) pendingUpdateButtonShown = true end if frame.pendingUpdateButton:GetExpanded() then local child = pendingUpdateButtons[slug] if not child then child = AceGUI:Create("WeakAurasPendingUpdateButton") pendingUpdateButtons[slug] = child child:Initialize(slug, auraData) if auraData.logo then child:SetLogo(auraData.logo) end if auraData.refreshLogo then child:SetRefreshLogo(auraData.refreshLogo) end child.frame:Show() child:AcquireThumbnail() frame.buttonsScroll:AddChild(child) end if not child.frame:IsShown() then child.frame:Show() child:AcquireThumbnail() end if not buttonsShown[slug] then tinsert(frame.buttonsScroll.children, child) buttonsShown[slug] = true end child:MarkLinkedAura(id) for childData in OptionsPrivate.Private.TraverseAllChildren(aura) do child:MarkLinkedChildren(childData.id) end end end end end end end -- hide all buttons not marked as shown for slug, button in pairs(pendingUpdateButtons) do if not buttonsShown[slug] then if button and button.frame:IsShown() then button.frame:Hide() if button.ReleaseThumbnail then button:ReleaseThumbnail() end end end end end if not pendingUpdateButtonShown and frame.pendingUpdateButton then frame.pendingUpdateButton.frame:Hide() end tinsert(frame.buttonsScroll.children, frame.loadedButton); -- First handle loaded auras local numLoaded = 0; local to_sort = {}; local children = {}; local containsFilter = false; local visible = {} for id, child in pairs(displayButtons) do containsFilter = not filter or filter == ""; local data = WeakAuras.GetData(id); if not(data) then print("|cFF8800FFWeakAuras|r: No data for", id); else if(not containsFilter and data.controlledChildren) then for child in OptionsPrivate.Private.TraverseAllChildren(data) do if(child.id:lower():find(filter, 1, true)) then containsFilter = true; break; end end end if( frame.loadedButton:GetExpanded() and (not filter or id:lower():find(filter, 1, true) or containsFilter) ) then local group = child:GetGroup(); if(group) then -- In a Group if anyParentLoaded(group) then if(OptionsPrivate.Private.loaded[id]) then child:EnableLoaded(); else child:DisableLoaded(); end children[group] = children[group] or {}; tinsert(children[group], id); end else -- Top Level if(OptionsPrivate.Private.loaded[id] ~= nil) then if(OptionsPrivate.Private.loaded[id]) then child:EnableLoaded(); else child:DisableLoaded(); end tinsert(to_sort, child); end end end end end table.sort(to_sort, function(a, b) return a:GetTitle() < b:GetTitle() end); for _, child in ipairs(to_sort) do child.frame:Show(); if child.AcquireThumbnail then child:AcquireThumbnail() end tinsert(frame.buttonsScroll.children, child); visible[child] = true addChildButtons(child, children, visible) end -- Now handle unloaded auras tinsert(frame.buttonsScroll.children, frame.unloadedButton); local numUnloaded = 0; wipe(to_sort); wipe(children); for id, child in pairs(displayButtons) do containsFilter = not filter or filter == ""; local data = WeakAuras.GetData(id); if(not containsFilter and data.controlledChildren) then for child in OptionsPrivate.Private.TraverseAllChildren(data) do if(child.id:lower():find(filter, 1, true)) then containsFilter = true; break; end end end if( frame.unloadedButton:GetExpanded() and (not filter or id:lower():find(filter, 1, true) or containsFilter) ) then local group = child:GetGroup(); if(group) then if not anyParentLoaded(group) then if(OptionsPrivate.Private.loaded[id]) then child:EnableLoaded(); else child:DisableLoaded(); end children[group] = children[group] or {}; tinsert(children[group], id); end else if(OptionsPrivate.Private.loaded[id] == nil) then child:DisableLoaded(); tinsert(to_sort, child); end end end end table.sort(to_sort, function(a, b) return a:GetTitle() < b:GetTitle() end); for _, child in ipairs(to_sort) do child.frame:Show(); if child.AcquireThumbnail then child:AcquireThumbnail() end tinsert(frame.buttonsScroll.children, child); visible[child] = true addChildButtons(child, children, visible) end -- Hiding the other buttons for id, child in pairs(displayButtons) do if(not visible[child]) then child.frame:Hide(); if child.ReleaseThumbnail then child:ReleaseThumbnail() end end end frame.buttonsScroll:DoLayout(); if(recenter) then frame:CenterOnPicked(); end end function OptionsPrivate.IsPickedMultiple() if(frame.pickedDisplay == tempGroup) then return true; else return false; end end function OptionsPrivate.IsDisplayPicked(id) if(frame.pickedDisplay == tempGroup) then for child in OptionsPrivate.Private.TraverseLeafs(tempGroup) do if(id == child.id) then return true; end end return false; else return frame.pickedDisplay == id; end end function WeakAuras.PickDisplay(id, tab, noHide) frame:PickDisplay(id, tab, noHide) OptionsPrivate.UpdateButtonsScroll() end function OptionsPrivate.PickAndEditDisplay(id) frame:PickDisplay(id); displayButtons[id].callbacks.OnRenameClick(); OptionsPrivate.UpdateButtonsScroll() end function OptionsPrivate.ClearPick(id) frame:ClearPick(id); end function OptionsPrivate.ClearPicks() frame:ClearPicks(); end function OptionsPrivate.PickDisplayMultiple(id) frame:PickDisplayMultiple(id); end function OptionsPrivate.PickDisplayMultipleShift(target) if (frame.pickedDisplay) then -- get first aura selected local first; if (OptionsPrivate.IsPickedMultiple()) then first = tempGroup.controlledChildren[#tempGroup.controlledChildren]; else first = frame.pickedDisplay; end if (first and first ~= target) then -- check if target and first are in same group and are not a group local firstData = WeakAuras.GetData(first); local targetData = WeakAuras.GetData(target); if (firstData.parent == targetData.parent and not targetData.controlledChildren and not firstData.controlledChildren) then local batchSelection = {}; -- in a group if (firstData.parent) then local group = WeakAuras.GetData(targetData.parent); for index, child in ipairs(group.controlledChildren) do -- 1st button if (child == target or child == first) then table.insert(batchSelection, child); for i = index + 1, #group.controlledChildren do local current = group.controlledChildren[i]; table.insert(batchSelection, current); -- last button: stop selection if (current == target or current == first) then break; end end break; end end elseif (firstData.parent == nil and targetData.parent == nil) then -- top-level for index, button in ipairs(frame.buttonsScroll.children) do local data = button.data; -- 1st button if (data and (data.id == target or data.id == first)) then table.insert(batchSelection, data.id); for i = index + 1, #frame.buttonsScroll.children do local current = frame.buttonsScroll.children[i]; local currentData = current.data; if currentData and not currentData.parent and not currentData.controlledChildren then table.insert(batchSelection, currentData.id); -- last button: stop selection if (currentData.id == target or currentData.id == first) then break; end end end break; end end end if #batchSelection > 0 then frame:PickDisplayBatch(batchSelection); end end end else WeakAuras.PickDisplay(target); end end function WeakAuras.GetDisplayButton(id) if(id and displayButtons[id]) then return displayButtons[id]; end end function OptionsPrivate.AddDisplayButton(data) EnsureDisplayButton(data); WeakAuras.UpdateDisplayButton(data); frame.buttonsScroll:AddChild(displayButtons[data.id]); if(WeakAuras.regions[data.id] and WeakAuras.regions[data.id].region.SetStacks) then WeakAuras.regions[data.id].region:SetStacks(1); end end function OptionsPrivate.SetGrouping(data) if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0 and data) then local children = {}; -- set grouping for selected buttons for index, childId in ipairs(tempGroup.controlledChildren) do local button = WeakAuras.GetDisplayButton(childId); button:SetGrouping(tempGroup.controlledChildren, true); children[childId] = true; end -- set grouping for non selected buttons for id, button in pairs(displayButtons) do if not children[button.data.id] then button:SetGrouping(tempGroup.controlledChildren); end end else for id, button in pairs(displayButtons) do button:SetGrouping(data); end end end function OptionsPrivate.Ungroup(data) if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then for index, childId in ipairs(tempGroup.controlledChildren) do local button = WeakAuras.GetDisplayButton(childId); button:Ungroup(data); end else local button = WeakAuras.GetDisplayButton(data.id); button:Ungroup(data); end WeakAuras.FillOptions() end function OptionsPrivate.DragReset() for id, button in pairs(displayButtons) do button:DragReset(); end OptionsPrivate.UpdateButtonsScroll() end function OptionsPrivate.Drop(mainAura, target, action) WeakAuras_DropDownMenu:Hide() local mode = "" if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then mode = "MULTI" elseif mainAura.controlledChildren then mode = "GROUP" else mode = "SINGLE" end for id, button in pairs(displayButtons) do button:Drop(mode, mainAura, target, action); end OptionsPrivate.SortDisplayButtons() OptionsPrivate.UpdateButtonsScroll() end function OptionsPrivate.StartDrag(mainAura) WeakAuras_DropDownMenu:Hide() if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then -- Multi selection local children = {}; local size = #tempGroup.controlledChildren; -- set dragging for selected buttons in reverse for ordering for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do local button = WeakAuras.GetDisplayButton(child.id); button:DragStart("MULTI", true, mainAura, size) children[child.id] = true end -- set dragging for non selected buttons for id, button in pairs(displayButtons) do if not children[button.data.id] then button:DragStart("MULTI", false, mainAura); end end else if mainAura.controlledChildren then else for id, button in pairs(displayButtons) do button:DragStart("SINGLE", id == mainAura.id, mainAura); end end end end function OptionsPrivate.DropIndicator() local indicator = frame.dropIndicator if not indicator then indicator = CreateFrame("Frame", "WeakAuras_DropIndicator") indicator:SetHeight(4) indicator:SetFrameStrata("FULLSCREEN") local groupTexture = indicator:CreateTexture(nil, "ARTWORK") groupTexture:SetBlendMode("ADD") groupTexture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_FullWhite") local lineTexture = indicator:CreateTexture(nil, "ARTWORK") lineTexture:SetBlendMode("ADD") lineTexture:SetAllPoints(indicator) lineTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") indicator.lineTexture = lineTexture indicator.groupTexture = groupTexture frame.dropIndicator = indicator indicator:Hide() function indicator:ShowAction(target, action) self:Show() self:ClearAllPoints() if action == "GROUP" then self.groupTexture:ClearAllPoints() self.groupTexture:SetVertexColor(0.4, 0.7, 1, 0.7) self.groupTexture:Show() self.groupTexture:SetPoint("TOPLEFT", target.icon, "TOPRIGHT", 2, -1) self.groupTexture:SetPoint("BOTTOMRIGHT", target.frame, "BOTTOMRIGHT", 0, 1) else self.groupTexture:Hide() end -- Position line texture, if needed if action == "BEFORE" then self.lineTexture:Show() self:SetPoint("BOTTOMLEFT", target.frame, "TOPLEFT", 0, -1) self:SetPoint("BOTTOMRIGHT", target.frame, "TOPRIGHT", 0, -1) self:SetHeight(4) elseif action == "AFTER" then self.lineTexture:Show() self:SetPoint("TOPLEFT", target.frame, "BOTTOMLEFT", 0, 1) self:SetPoint("TOPRIGHT", target.frame, "BOTTOMRIGHT", 0, 1) self:SetHeight(4) else self.lineTexture:Hide() end end end return indicator end function WeakAuras.UpdateDisplayButton(data) local id = data.id; local button = displayButtons[id]; if (button) then button:UpdateThumbnail() end end function WeakAuras.UpdateThumbnail(data) local id = data.id local button = displayButtons[id] if (not button) then return end button:UpdateThumbnail() end function OptionsPrivate.OpenTexturePicker(baseObject, path, properties, textures, SetTextureFunc) frame.texturePicker:Open(baseObject, path, properties, textures, SetTextureFunc) end function OptionsPrivate.OpenIconPicker(baseObject, paths, groupIcon) frame.iconPicker:Open(baseObject, paths, groupIcon) end function OptionsPrivate.OpenModelPicker(baseObject, path) if not(IsAddOnLoaded("WeakAurasModelPaths")) then local loaded, reason = LoadAddOn("WeakAurasModelPaths"); if not(loaded) then reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") WeakAuras.prettyPrint("ModelPaths could not be loaded, the addon is " .. reason); WeakAuras.ModelPaths = {}; end frame.modelPicker.modelTree:SetTree(WeakAuras.ModelPaths); end frame.modelPicker:Open(baseObject, path); end function WeakAuras.OpenCodeReview(data) frame.codereview:Open(data); end function WeakAuras.CloseCodeReview(data) frame.codereview:Close(); end function OptionsPrivate.OpenTriggerTemplate(data, targetId) if not(IsAddOnLoaded("WeakAurasTemplates")) then local loaded, reason = LoadAddOn("WeakAurasTemplates"); if not(loaded) then reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") WeakAuras.prettyPrint("Templates could not be loaded, the addon is " .. reason); return; end frame.newView = WeakAuras.CreateTemplateView(OptionsPrivate.Private, frame); end -- This is called multiple times if a group is selected if frame.window ~= "newView" then frame.newView:Open(data, targetId); end end function OptionsPrivate.ResetMoverSizer() if(frame and frame.mover and frame.moversizer and frame.mover.moving.region and frame.mover.moving.data) then frame.moversizer:SetToRegion(frame.mover.moving.region, frame.mover.moving.data); end end function WeakAuras.SetMoverSizer(id) if WeakAuras.regions[id].region.toShow then frame.moversizer:SetToRegion(WeakAuras.regions[id].region, db.displays[id]) else if WeakAuras.clones[id] then local cloneId, clone = next(WeakAuras.clones[id]) if clone then frame.moversizer:SetToRegion(clone, db.displays[id]) end end end end function WeakAuras.GetMoverSizerId() return frame.moversizer:GetCurrentId() end local function AddDefaultSubRegions(data) data.subRegions = data.subRegions or {} for type, subRegionData in pairs(OptionsPrivate.Private.subRegionTypes) do if subRegionData.addDefaultsForNewAura then subRegionData.addDefaultsForNewAura(data) end end end function WeakAuras.NewAura(sourceData, regionType, targetId) local function ensure(t, k, v) return t and k and v and t[k] == v end local new_id = WeakAuras.FindUnusedId("New") local data = {id = new_id, regionType = regionType, uid = WeakAuras.GenerateUniqueID()} WeakAuras.DeepMixin(data, WeakAuras.data_stub); if (sourceData) then WeakAuras.DeepMixin(data, sourceData); end data.internalVersion = WeakAuras.InternalVersion(); WeakAuras.validate(data, WeakAuras.regionTypes[regionType].default); AddDefaultSubRegions(data) if (data.regionType ~= "group" and data.regionType ~= "dynamicgroup" and targetId) then local target = WeakAuras.GetDisplayButton(targetId); local group if (target) then if (target:IsGroup()) then group = target; else group = WeakAuras.GetDisplayButton(target.data.parent); end if (group) then local children = group.data.controlledChildren; local index = target:GetGroupOrder(); if (ensure(children, index, target.data.id)) then -- account for insert position index = index + 1; tinsert(children, index, data.id); else -- move source into group as the first child tinsert(children, 1, data.id); end data.parent = group.data.id; WeakAuras.Add(data); WeakAuras.Add(group.data); OptionsPrivate.Private.AddParents(group.data) WeakAuras.NewDisplayButton(data); WeakAuras.UpdateGroupOrders(group.data); OptionsPrivate.ClearOptions(group.data.id); WeakAuras.UpdateDisplayButton(group.data); group.callbacks.UpdateExpandButton(); group:Expand(); group:ReloadTooltip(); OptionsPrivate.PickAndEditDisplay(data.id); else -- move source into the top-level list WeakAuras.Add(data); WeakAuras.NewDisplayButton(data); OptionsPrivate.PickAndEditDisplay(data.id); end else error("Calling 'WeakAuras.NewAura' with invalid groupId. Reload your UI to fix the display list.") end else -- move source into the top-level list WeakAuras.Add(data); WeakAuras.NewDisplayButton(data); OptionsPrivate.PickAndEditDisplay(data.id); end end function OptionsPrivate.ResetCollapsed(id, namespace) if id then if namespace and collapsedOptions[id] then collapsedOptions[id][namespace] = nil else collapsedOptions[id] = nil end end end function OptionsPrivate.IsCollapsed(id, namespace, path, default) local tmp = collapsedOptions[id] if tmp == nil then return default end tmp = tmp[namespace] if tmp == nil then return default end if type(path) ~= "table" then tmp = tmp[path] else for _, key in ipairs(path) do tmp = tmp[key] if tmp == nil or tmp[collapsed] then break end end end if tmp == nil or tmp[collapsed] == nil then return default else return tmp[collapsed] end end function OptionsPrivate.SetCollapsed(id, namespace, path, v) collapsedOptions[id] = collapsedOptions[id] or {} collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {} if type(path) ~= "table" then collapsedOptions[id][namespace][path] = collapsedOptions[id][namespace][path] or {} collapsedOptions[id][namespace][path][collapsed] = v else local tmp = collapsedOptions[id][namespace] or {} for _, key in ipairs(path) do tmp[key] = tmp[key] or {} tmp = tmp[key] end tmp[collapsed] = v end end function OptionsPrivate.MoveCollapseDataUp(id, namespace, path) collapsedOptions[id] = collapsedOptions[id] or {} collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {} if type(path) ~= "table" then collapsedOptions[id][namespace][path], collapsedOptions[id][namespace][path - 1] = collapsedOptions[id][namespace][path - 1], collapsedOptions[id][namespace][path] else local tmp = collapsedOptions[id][namespace] local lastKey = tremove(path) for _, key in ipairs(path) do tmp[key] = tmp[key] or {} tmp = tmp[key] end tmp[lastKey], tmp[lastKey - 1] = tmp[lastKey - 1], tmp[lastKey] end end function OptionsPrivate.MoveCollapseDataDown(id, namespace, path) collapsedOptions[id] = collapsedOptions[id] or {} collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {} if type(path) ~= "table" then collapsedOptions[id][namespace][path], collapsedOptions[id][namespace][path + 1] = collapsedOptions[id][namespace][path + 1], collapsedOptions[id][namespace][path] else local tmp = collapsedOptions[id][namespace] local lastKey = tremove(path) for _, key in ipairs(path) do tmp[key] = tmp[key] or {} tmp = tmp[key] end tmp[lastKey], tmp[lastKey + 1] = tmp[lastKey + 1], tmp[lastKey] end end function OptionsPrivate.RemoveCollapsed(id, namespace, path) local data = collapsedOptions[id] and collapsedOptions[id][namespace] if not data then return end local index local maxIndex = 0 if type(path) ~= "table" then index = path else index = path[#path] for i = 1, #path - 1 do data = data[path[i]] if not data then return end end end for k in pairs(data) do if k ~= collapsed then maxIndex = max(maxIndex, k) end end while index <= maxIndex do data[index] = data[index + 1] index = index + 1 end end function OptionsPrivate.InsertCollapsed(id, namespace, path, value) local data = collapsedOptions[id] and collapsedOptions[id][namespace] if not data then return end local insertPoint local maxIndex if type(path) ~= "table" then insertPoint = path else insertPoint = path[#path] for i = 1, #path - 1 do data = data[path[i]] if not data then return end end end for k in pairs(data) do if k ~= collapsed and k >= insertPoint then if not maxIndex or k > maxIndex then maxIndex = k end end end if maxIndex then -- may be nil if insertPoint is greater than the max of anything else for i = maxIndex, insertPoint, -1 do data[i + 1] = data[i] end end data[insertPoint] = {[collapsed] = value} end function OptionsPrivate.DuplicateCollapseData(id, namespace, path) collapsedOptions[id] = collapsedOptions[id] or {} collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {} if type(path) ~= "table" then if (collapsedOptions[id][namespace][path]) then tinsert(collapsedOptions[id][namespace], path + 1, CopyTable(collapsedOptions[id][namespace][path])) end else local tmp = collapsedOptions[id][namespace] local lastKey = tremove(path) for _, key in ipairs(path) do tmp[key] = tmp[key] or {} tmp = tmp[key] end if (tmp[lastKey]) then tinsert(tmp, lastKey + 1, CopyTable(tmp[lastKey])) end end end function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, hidden, setHidden, index, total) local headerOption if withHeader and (not index or index == 1) then headerOption = { type = "execute", control = "WeakAurasExpandSmall", name = L["|cFFffcc00Format Options|r"], width = WeakAuras.doubleWidth, func = function(info, button) setHidden(not hidden()) end, image = function() return hidden() and "collapsed" or "expanded" end, imageWidth = 15, imageHeight = 15, arg = { expanderName = tostring(addOption) } } addOption("header", headerOption) else hidden = false end local seenSymbols = {} OptionsPrivate.Private.ParseTextStr(input, function(symbol) if not seenSymbols[symbol] then local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") sym = sym or symbol if sym == "c" or sym == "i" then -- No special options for these else addOption(symbol .. "desc", { type = "description", name = L["Format for %s"]:format("%" .. symbol), width = WeakAuras.normalWidth, hidden = hidden }) addOption(symbol .. "_format", { type = "select", name = L["Format"], width = WeakAuras.normalWidth, values = OptionsPrivate.Private.format_types_display, hidden = hidden, reloadOptions = true }) local selectedFormat = get(symbol .. "_format") if (OptionsPrivate.Private.format_types[selectedFormat]) then OptionsPrivate.Private.format_types[selectedFormat].AddOptions(symbol, hidden, addOption, get) end seenSymbols[symbol] = true end end end) if withHeader and (not index or index == total) then addOption("header_anchor", { type = "description", name = "", control = "WeakAurasExpandAnchor", arg = { expanderName = tostring(addOption) } } ) end if not next(seenSymbols) and headerOption and not index then headerOption.hidden = true end return next(seenSymbols) ~= nil end