Files
coa-weakauras/WeakAurasOptions/WeakAurasOptions.lua
T
2025-08-06 01:44:29 +02:00

2254 lines
68 KiB
Lua

if not WeakAuras.IsLibsOK() then return end
local AddonName = ...
local OptionsPrivate = select(2, ...)
-- Lua APIs
local tinsert, tremove, wipe = table.insert, table.remove, wipe
local pairs, type = pairs, type
local error = 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 = {};
OptionsPrivate.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;
-- Does not duplicate child auras.
function OptionsPrivate.DuplicateAura(data, newParent, massEdit, targetIndex)
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 targetIndex then
index = targetIndex
elseif newParent then
index = #parentData.controlledChildren + 1
else
index = tIndexOf(parentData.controlledChildren, data.id) + 1
end
if(index) then
tinsert(parentData.controlledChildren, index, 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 = OptionsPrivate.GetDisplayButton(id)
childButton:SetGroup(parentData.id, parentData.regionType == "dynamicgroup")
childButton:SetGroupOrder(index, #parentData.controlledChildren)
end
if not massEdit then
local button = OptionsPrivate.GetDisplayButton(parentData.id)
button.callbacks.UpdateExpandButton()
button:UpdateParentWarning()
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
local function commonParent(controlledChildren)
local allSame = true
local parent = nil
local targetIndex = math.huge
for index, id in ipairs(controlledChildren) do
local childData = WeakAuras.GetData(id);
local childButton = OptionsPrivate.GetDisplayButton(id)
targetIndex = min(targetIndex, childButton:GetGroupOrder() or math.huge)
if (parent == nil) then
parent = childData.parent
elseif not childData.parent then
allSame = false
elseif childData.parent ~= parent then
allSame = false
end
end
if allSame then
return parent, targetIndex
end
end
local function CreateNewGroupFromSelection(regionType, resetChildPositions)
local data = {
id = OptionsPrivate.Private.FindUnusedId(tempGroup.controlledChildren[1].." Group"),
regionType = regionType,
};
WeakAuras.DeepMixin(data, OptionsPrivate.Private.data_stub)
data.internalVersion = WeakAuras.InternalVersion()
OptionsPrivate.Private.validate(data, OptionsPrivate.Private.regionTypes[regionType].default);
local parent, targetIndex = commonParent(tempGroup.controlledChildren)
if (parent) then
local parentData = WeakAuras.GetData(parent)
tinsert(parentData.controlledChildren, targetIndex, data.id)
data.parent = parent
WeakAuras.Add(data);
WeakAuras.Add(parentData);
OptionsPrivate.Private.AddParents(parentData)
WeakAuras.NewDisplayButton(data);
WeakAuras.UpdateGroupOrders(parentData);
OptionsPrivate.ClearOptions(parentData.id);
local parentButton = OptionsPrivate.GetDisplayButton(parent)
parentButton.callbacks.UpdateExpandButton();
parentButton:Expand();
parentButton:ReloadTooltip();
parentButton:UpdateParentWarning();
else
WeakAuras.Add(data);
WeakAuras.NewDisplayButton(data);
end
for index, childId in pairs(tempGroup.controlledChildren) do
local childData = WeakAuras.GetData(childId);
local childButton = OptionsPrivate.GetDisplayButton(childId)
local oldParent = childData.parent
local oldParentData = WeakAuras.GetData(oldParent)
if (oldParent) then
local oldIndex = childButton:GetGroupOrder()
tremove(oldParentData.controlledChildren, oldIndex)
WeakAuras.Add(oldParentData)
OptionsPrivate.Private.AddParents(oldParentData)
WeakAuras.UpdateGroupOrders(oldParentData);
WeakAuras.ClearAndUpdateOptions(oldParent);
local oldParentButton = OptionsPrivate.GetDisplayButton(oldParent)
oldParentButton.callbacks.UpdateExpandButton();
oldParentButton:ReloadTooltip()
oldParentButton:UpdateParentWarning()
end
tinsert(data.controlledChildren, childId);
childData.parent = data.id;
if resetChildPositions then
childData.xOffset = 0;
childData.yOffset = 0;
end
WeakAuras.Add(data);
WeakAuras.Add(childData);
OptionsPrivate.ClearOptions(childData.id)
childButton:SetGroup(data.id, data.regionType == "dynamicgroup");
childButton:SetGroupOrder(index, #data.controlledChildren);
end
local button = OptionsPrivate.GetDisplayButton(data.id);
button.callbacks.UpdateExpandButton();
button:UpdateParentWarning()
OptionsPrivate.SortDisplayButtons();
button:Expand();
if data.parent then
OptionsPrivate.Private.AddParents(data)
end
end
function OptionsPrivate.MultipleDisplayTooltipMenu()
local frame = frame;
local menu = {
{
text = L["Add to new Group"],
notCheckable = 1,
func = function()
CreateNewGroupFromSelection("group")
end
},
{
text = L["Add to new Dynamic Group"],
notCheckable = 1,
func = function()
CreateNewGroupFromSelection("dynamicgroup", true)
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 anyGroup = false;
local allSameParent = true
local commonParent = nil
local first = true
for _, id in pairs(tempGroup.controlledChildren) do
local childData = WeakAuras.GetData(id);
if(childData and childData.controlledChildren) then
anyGroup = true;
end
if (first) then
commonParent = childData.parent
first = false
elseif childData.parent ~= commonParent then
allSameParent = false
end
end
if(anyGroup) then
-- Disable "Add to New Dynamic Group"
menu[2].notClickable = 1;
menu[2].text = "|cFF777777"..menu[2].text;
end
-- Also disable Add to New Dynamic Group/Group if that would create
-- a group inside a dynamic group
if (allSameParent and commonParent) then
local parentData = WeakAuras.GetData(commonParent);
if (parentData and parentData.regionType == "dynamicgroup") then
menu[1].notClickable = 1;
menu[1].text = "|cFF777777"..menu[1].text;
menu[2].notClickable = 1;
menu[2].text = "|cFF777777"..menu[1].text;
end
end
return menu;
end
StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"] = {
text = "",
button1 = L["Delete"],
button2 = L["Cancel"],
OnAccept = function(self)
if self.data then
OptionsPrivate.DeleteAuras(self.data.toDelete, self.data.parents)
end
end,
OnCancel = function(self)
self.data = nil
end,
showAlert = 1,
whileDead = 1,
timeout = 0,
preferredindex = STATICPOPUP_NUMDIALOGS,
}
function OptionsPrivate.IsWagoUpdateIgnored(auraId)
local auraData = WeakAuras.GetData(auraId)
if auraData then
for child in OptionsPrivate.Private.TraverseAll(auraData) do
if child.ignoreWagoUpdate then
return true
end
end
end
return false
end
function OptionsPrivate.HasWagoUrl(auraId)
local auraData = WeakAuras.GetData(auraId)
if auraData then
for child in OptionsPrivate.Private.TraverseAll(auraData) do
if child.url and child.url ~= "" then
return true
end
end
end
return false
end
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
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 OnRename(event, uid, oldid, newid)
local data = OptionsPrivate.Private.GetDataByUID(uid)
OptionsPrivate.displayButtons[newid] = OptionsPrivate.displayButtons[oldid];
OptionsPrivate.displayButtons[newid]:SetData(data)
OptionsPrivate.displayButtons[oldid] = nil;
OptionsPrivate.ClearOptions(oldid)
OptionsPrivate.displayButtons[newid]:SetTitle(newid);
collapsedOptions[newid] = collapsedOptions[oldid]
collapsedOptions[oldid] = nil
if(data.controlledChildren) then
for _, childId in pairs(data.controlledChildren) do
OptionsPrivate.displayButtons[childId]:SetGroup(newid)
end
end
OptionsPrivate.StopGrouping()
OptionsPrivate.SortDisplayButtons(nil, true)
frame:OnRename(uid, oldid, newid)
WeakAuras.PickDisplay(newid)
local parent = data.parent
while parent do
OptionsPrivate.ClearOptions(parent)
local parentData = WeakAuras.GetData(parent)
parent = parentData.parent
end
end
local function OptionsFrame()
if(frame) then
return frame
else
return nil
end
end
if not WeakAuras.ToggleOptions then
function WeakAuras.ToggleOptions(msg, Private)
if not Private then
return
end
if not OptionsPrivate.Private then
OptionsPrivate.Private = Private
Private.OptionsFrame = OptionsFrame
for _, fn in ipairs(OptionsPrivate.registerRegions) do
fn()
end
OptionsPrivate.Private.callbacks: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
local data = Private.GetDataByUID(uid)
if data and data.parent then
local button = OptionsPrivate.GetDisplayButton(data.parent);
if button then
button:UpdateParentWarning()
end
end
end)
OptionsPrivate.Private.callbacks:RegisterCallback("ScanForLoads", AfterScanForLoads)
OptionsPrivate.Private.callbacks:RegisterCallback("AboutToDelete", OnAboutToDelete)
OptionsPrivate.Private.callbacks:RegisterCallback("Rename", OnRename)
OptionsPrivate.Private.OpenUpdate = OptionsPrivate.OpenUpdate
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
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:lower() < b:lower() 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:lower() < b:lower() 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)
if OptionsPrivate.Private.CompanionData.slugs 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 _, id in pairs(unloadedSorted) do
local data = WeakAuras.GetData(id);
if(data) then
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(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);
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
if (WeakAuras.IsOptionsOpen()) then
for id, button in pairs(displayButtons) do
if OptionsPrivate.Private.loaded[id] then
button:PriorityShow(1);
coroutine.yield()
end
end
OptionsPrivate.Private.OptionsFrame().loadedButton:RecheckVisibility()
coroutine.yield()
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
frame:SetLoadProgressVisible(false)
end
local func1 = function()
local num = frame.loadProgressNum or 0;
frame.buttonsScroll:PauseLayout()
for _, id in pairs(loadedSorted) do
local data = WeakAuras.GetData(id);
if(data) then
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(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.Threads:Add("LayoutDisplayButtons2", co2);
end
local co1 = coroutine.create(func1);
OptionsPrivate.Private.Threads:Add("LayoutDisplayButtons1", co1);
end
function OptionsPrivate.DeleteAuras(auras, parents)
local func1 = function()
frame:SetLoadProgressVisible(true)
local num = 0
local total = 0
for _, auraData in pairs(auras) do
total = total +1
end
frame.loadProgress:SetText(L["Deleting auras: "]..num.."/"..total)
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
OptionsPrivate.massDelete = true
for _, auraData in pairs(auras) do
WeakAuras.Delete(auraData)
num = num +1
frame.loadProgress:SetText(L["Deleting auras: "]..num.."/"..total)
coroutine.yield()
end
OptionsPrivate.massDelete = false
if parents then
for id in pairs(parents) do
local parentData = WeakAuras.GetData(id)
local parentButton = OptionsPrivate.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)
parentButton:UpdateParentWarning()
frame.loadProgress:SetText(L["Finishing..."])
coroutine.yield()
end
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
OptionsPrivate.SortDisplayButtons(nil, true)
frame:SetLoadProgressVisible(false)
end
local co1 = coroutine.create(func1)
OptionsPrivate.Private.Threads:Add("Deleting Auras", 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
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
for id, button in pairs(displayButtons) do
button:SyncVisibility()
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
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
local codereview = OptionsPrivate.CodeReview(frame, true)
if codereview then
codereview:Close();
end
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(...)
OptionsPrivate.TextEditor(frame):Open(...);
end
function OptionsPrivate.ExportToString(id)
OptionsPrivate.ImportExport(frame):Open("export", id);
end
function OptionsPrivate.ExportToTable(id)
OptionsPrivate.ImportExport(frame):Open("table", id);
end
function OptionsPrivate.ImportFromString()
OptionsPrivate.ImportExport(frame):Open("import");
end
function OptionsPrivate.OpenDebugLog(text)
OptionsPrivate.DebugLog(frame):Open(text)
end
function OptionsPrivate.OpenUpdate(data, children, target, linkedAuras, sender, callbackFunc)
return OptionsPrivate.UpdateFrame(frame):Open(data, children, target, linkedAuras, sender, callbackFunc)
end
function OptionsPrivate.ConvertDisplay(data, newType)
local id = data.id;
local visibility = displayButtons[id]:GetVisibility();
displayButtons[id]:PriorityHide(2);
if OptionsPrivate.Private.regions[id] and OptionsPrivate.Private.regions[id].region then
OptionsPrivate.Private.regions[id].region:Collapse()
end
OptionsPrivate.Private.CollapseAllClones(id);
OptionsPrivate.Private.Convert(data, newType);
displayButtons[id]:Initialize();
displayButtons[id]:PriorityShow(visibility);
frame:ClearOptions(id)
frame:FillOptions();
WeakAuras.UpdateThumbnail(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.UpdateThumbnail(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 = OptionsPrivate.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 addButton(button, aurasMatchingFilter, visible)
button.frame:Show();
if button.AcquireThumbnail then
button:AcquireThumbnail()
end
tinsert(frame.buttonsScroll.children, button);
visible[button] = true
if button.data.controlledChildren and button:GetExpanded() then
for _, childId in ipairs(button.data.controlledChildren) do
if aurasMatchingFilter[childId] then
addButton(displayButtons[childId], aurasMatchingFilter, visible)
end
end
end
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
if OptionsPrivate.Private.CompanionData.stash then
for id, companionData in pairs(OptionsPrivate.Private.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)
else
if not child.frame:IsShown() then
child.frame:Show()
child:AcquireThumbnail()
end
tinsert(frame.buttonsScroll.children, child)
end
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 OptionsPrivate.Private.CompanionData.slugs 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 = OptionsPrivate.Private.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)
buttonsShown[slug] = true
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);
local aurasMatchingFilter = {}
local useTextFilter = filter ~= ""
local filterTable = OptionsPrivate.Private.splitAtOr(filter)
local topLevelLoadedAuras = {}
local topLevelUnloadedAuras = {}
local visible = {}
for id, child in pairs(displayButtons) do
if child.data.controlledChildren then
local hasLoaded, hasStandBy, hasNotLoaded = 0, 0, 0
for leaf in OptionsPrivate.Private.TraverseLeafs(child.data) do
local id = leaf.id
if OptionsPrivate.Private.loaded[id] == true then
hasLoaded = hasLoaded + 1
elseif OptionsPrivate.Private.loaded[id] == false then
hasStandBy = hasStandBy + 1
else
hasNotLoaded = hasNotLoaded + 1
end
end
if hasLoaded > 0 then
child:SetLoaded(1, "loaded", L["Loaded"], L["%d displays loaded"]:format(hasLoaded))
elseif hasStandBy > 0 then
child:SetLoaded(2, "standby", L["Standby"], L["%d displays on standby"]:format(hasStandBy))
elseif hasNotLoaded > 0 then
child:SetLoaded(3, "unloaded", L["Not Loaded"], L["%d displays not loaded"]:format(hasNotLoaded))
else
child:ClearLoaded()
end
else
if OptionsPrivate.Private.loaded[id] == true then
child:SetLoaded(1, "loaded", L["Loaded"], L["This display is currently loaded"])
elseif OptionsPrivate.Private.loaded[id] == false then
child:SetLoaded(2, "standby", L["Standby"], L["This display is on standby, it will be loaded when needed."])
else
child:SetLoaded(3, "unloaded", L["Not Loaded"], L["This display is not currently loaded"])
end
end
if useTextFilter then
for _, word in ipairs(filterTable) do
if(id:lower():find(word, 1, true)) then
aurasMatchingFilter[id] = true
for parent in OptionsPrivate.Private.TraverseParents(child.data) do
aurasMatchingFilter[parent.id] = true
end
end
end
else
aurasMatchingFilter[id] = true
end
if not child:GetGroup() then
-- Top Level aura
if OptionsPrivate.Private.loaded[id] ~= nil then
tinsert(topLevelLoadedAuras, id)
else
tinsert(topLevelUnloadedAuras, id)
end
end
end
wipe(frame.loadedButton.childButtons)
if frame.loadedButton:GetExpanded() then
table.sort(topLevelLoadedAuras, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(topLevelLoadedAuras) do
if aurasMatchingFilter[id] then
addButton(displayButtons[id], aurasMatchingFilter, visible)
end
end
end
for _, id in ipairs(topLevelLoadedAuras) do
for child in OptionsPrivate.Private.TraverseLeafsOrAura(WeakAuras.GetData(id)) do
tinsert(frame.loadedButton.childButtons, displayButtons[child.id])
end
end
tinsert(frame.buttonsScroll.children, frame.unloadedButton);
wipe(frame.unloadedButton.childButtons)
if frame.unloadedButton:GetExpanded() then
table.sort(topLevelUnloadedAuras, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(topLevelUnloadedAuras) do
if aurasMatchingFilter[id] then
addButton(displayButtons[id], aurasMatchingFilter, visible)
end
end
end
for _, id in ipairs(topLevelUnloadedAuras) do
for child in OptionsPrivate.Private.TraverseLeafsOrAura(WeakAuras.GetData(id)) do
tinsert(frame.unloadedButton.childButtons, displayButtons[child.id])
end
end
for _, 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);
OptionsPrivate.UpdateButtonsScroll()
displayButtons[id].callbacks.OnRenameClick();
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];
if (WeakAuras.GetData(current).controlledChildren) then
-- Skip sub groups
else
table.insert(batchSelection, current);
end
-- 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
if button.type == "WeakAurasDisplayButton" then
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
end
if #batchSelection > 0 then
frame:PickDisplayBatch(batchSelection);
end
end
end
else
WeakAuras.PickDisplay(target);
end
end
function OptionsPrivate.GetDisplayButton(id)
if(id and displayButtons[id]) then
return displayButtons[id];
end
end
function OptionsPrivate.AddDisplayButton(data)
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(data);
frame.buttonsScroll:AddChild(displayButtons[data.id]);
end
function OptionsPrivate.StartGrouping(data)
if not data then
return
end
if not OptionsPrivate.IsDisplayPicked(data) then
WeakAuras.PickDisplay(data.id)
end
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
local children = {};
-- start grouping for selected buttons
for index, childId in ipairs(tempGroup.controlledChildren) do
local button = OptionsPrivate.GetDisplayButton(childId);
button:StartGrouping(tempGroup.controlledChildren, true);
children[childId] = true;
end
-- set grouping for non selected buttons
for _, button in pairs(displayButtons) do
if not children[button.data.id] then
button:StartGrouping(tempGroup.controlledChildren, false);
end
end
else
local children = {};
for child in OptionsPrivate.Private.TraverseAllChildren(data) do
children[child.id] = true
end
for id, button in pairs(displayButtons) do
button:StartGrouping({data.id},
data.id == id,
data.regionType == "dynamicgroup" or data.regionType == "group",
children[id]);
end
end
end
function OptionsPrivate.StopGrouping(data)
for id, button in pairs(displayButtons) do
button:StopGrouping();
end
end
function OptionsPrivate.Ungroup(data)
if not OptionsPrivate.IsDisplayPicked(data.id) then
WeakAuras.PickDisplay(data.id)
end
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
for index, childId in ipairs(tempGroup.controlledChildren) do
local button = OptionsPrivate.GetDisplayButton(childId);
button:Ungroup(data);
end
else
local button = OptionsPrivate.GetDisplayButton(data.id);
button:Ungroup(data);
end
WeakAuras.FillOptions()
end
function OptionsPrivate.DragReset()
for _, button in pairs(displayButtons) do
button:DragReset();
end
OptionsPrivate.UpdateButtonsScroll()
end
local function CompareButtonOrder(a, b)
if (a.data.parent == b.data.parent) then
if (a.data.parent) then
return a:GetGroupOrder() < b:GetGroupOrder()
else
return a.data.id < b.data.id
end
end
-- Different parents, so find common parent by first
-- going up a's hierarchy
local parents = {}
local aNode = a.data.id
local lastAParent = aNode
while(aNode) do
local parent = WeakAuras.GetData(aNode).parent
if (parent) then
parents[parent] = aNode
lastAParent = parent
end
aNode = parent
end
local bNode = b.data.id
local lastBParent = bNode
while(bNode) do
local parent = WeakAuras.GetData(bNode).parent
if parent then
if (parents[parent]) then
-- We have found the common parent, the last node in the chain is
-- Compare the previous nodes GroupOrder
local aButton = OptionsPrivate.GetDisplayButton(parents[parent])
local bButton = OptionsPrivate.GetDisplayButton(bNode)
return aButton:GetGroupOrder() < bButton:GetGroupOrder()
end
lastBParent = parent
end
bNode = parent
end
-- If we are here there was no common parent
local aButton = OptionsPrivate.GetDisplayButton(lastAParent)
local bButton = OptionsPrivate.GetDisplayButton(lastBParent)
return aButton.data.id < bButton.data.id
end
local function CompareButtonOrderReverse(a, b)
return CompareButtonOrder(b, a)
end
function OptionsPrivate.Drop(mainAura, target, action, area)
WeakAuras_DropDownMenu:Hide()
local func1 = function()
frame:SetLoadProgressVisible(true)
local total = 0
local num = 0
for id, button in pairs(displayButtons) do
if button:IsDragging() then
total = total + 1
end
end
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
local mode = ""
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
mode = "MULTI"
elseif mainAura.controlledChildren then
mode = "GROUP"
else
mode = "SINGLE"
end
local buttonsToSort = {}
for id, button in pairs(displayButtons) do
if button:IsDragging() then
tinsert(buttonsToSort, button)
num = num + 1
frame.loadProgress:SetText(L["Preparing auras: "]..num.."/"..total)
else
button:Drop(mode, mainAura, target, action);
end
coroutine.yield()
end
num = 0
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
if mode == "MULTI" then
-- If we are dragging and dropping multiple auras at once, the order in which we drop is important
-- We want to preserve the top-down order
-- Depending on how exactly we find the insert position, we need to use the right order of insertions
if area == "GROUP" then
table.sort(buttonsToSort, CompareButtonOrderReverse)
elseif area == "BEFORE" then
table.sort(buttonsToSort, CompareButtonOrder)
else -- After
table.sort(buttonsToSort, CompareButtonOrderReverse)
end
end
for _, button in ipairs(buttonsToSort) do
button:Drop(mode, mainAura, target, action)
num = num + 1
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
coroutine.yield()
end
-- Update offset, this is a bit wasteful to do for every aura
-- But we also need to update the offset if a parent was dragged
for _, button in pairs(displayButtons) do
button:UpdateOffset();
end
coroutine.yield()
frame:SetLoadProgressVisible(false)
OptionsPrivate.SortDisplayButtons()
OptionsPrivate.UpdateButtonsScroll()
WeakAuras.FillOptions()
end
local co1 = coroutine.create(func1)
OptionsPrivate.Private.Threads:Add("Dropping Auras", co1)
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 = OptionsPrivate.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
-- Group aura
local mode = "GROUP"
local children = {};
for child in OptionsPrivate.Private.TraverseAll(mainAura) do
local button = OptionsPrivate.GetDisplayButton(child.id);
button:DragStart(mode, true, mainAura)
children[child.id] = true
end
-- set dragging for non selected buttons
for _, button in pairs(displayButtons) do
if not children[button.data.id] then
button:DragStart(mode, false, mainAura);
end
end
else
for id, button in pairs(displayButtons) do
button:DragStart("SINGLE", id == mainAura.id, mainAura);
end
end
end
OptionsPrivate.UpdateButtonsScroll()
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.UpdateThumbnail(data)
local id = data.id
local button = displayButtons[id]
if (not button) then
return
end
button:UpdateThumbnail()
end
function OptionsPrivate.OpenTexturePicker(baseObject, paths, properties, textures, SetTextureFunc)
OptionsPrivate.TexturePicker(frame):Open(baseObject, paths, properties, textures, SetTextureFunc)
end
function OptionsPrivate.OpenIconPicker(baseObject, paths, groupIcon)
OptionsPrivate.IconPicker(frame):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(string.format(L["ModelPaths could not be loaded, the addon is %s"], reason));
WeakAuras.ModelPaths = {};
end
OptionsPrivate.ModelPicker(frame).modelTree:SetTree(WeakAuras.ModelPaths)
end
OptionsPrivate.ModelPicker(frame):Open(baseObject, path);
end
function OptionsPrivate.OpenCodeReview(data)
OptionsPrivate.CodeReview(frame):Open(data);
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(string.format(L["Templates could not be loaded, the addon is %s"], 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
OptionsPrivate.currentDynamicTextInput = false;
local BaseDynamicTextCodes = {
trigger = {
{type = "mini", name = "p", desc = L["Progress - The remaining time of a timer, or a non-timer value"]},
{type = "mini", name = "t", desc = L["Total - The maximum duration of a timer, or a maximum non-timer value"]},
{type = "mini", name = "n", desc = L["Name - The name of the display (usually an aura name), or the display's ID if there is no dynamic name"]},
{type = "mini", name = "i", desc = L["Icon - The icon associated with the display"]},
{type = "mini", name = "s", desc = L["Stacks - The number of stacks of an aura (usually)"]},
},
global = {
{type = "mini", name = "c", desc = L["Custom - Allows you to define a custom Lua function that returns a list of string values. %c1 will be replaced by the first value returned, %c2 by the second, etc."]},
{type = "mini", name = "%", desc = L["% - To show a percent sign"]},
}
}
function OptionsPrivate.UpdateTextReplacements(frame, data)
frame.scrollList:ReleaseChildren()
local props = OptionsPrivate.Private.GetAdditionalProperties(data)
local sortedProps = {}
-- Add global header and markers
table.insert(sortedProps, {type = "header", triggerNum = 0, name = "Global Properties"})
for index, icon in ipairs(ICON_LIST) do
table.insert(sortedProps, {type = "marker", triggerNum = 0, name = "{rt"..index.."}", desc = icon..":0|t", widthFraction = #ICON_LIST})
end
-- Add base dynamic text codes
local globalProps = {}
tAppendAll(globalProps, CopyTable(BaseDynamicTextCodes.trigger))
tAppendAll(globalProps, CopyTable(BaseDynamicTextCodes.global))
for _, prop in ipairs(globalProps) do
prop.widthFraction = #globalProps
prop.triggerNum = 0
table.insert(sortedProps, prop)
end
-- Process each trigger's properties
for triggerNum, triggerProps in pairs(props) do
if next(triggerProps) then
-- Create a temporary table for this trigger's properties
local tempProps = {}
-- Add the properties to the temporary table
for name, data in pairs(triggerProps) do
table.insert(tempProps, {triggerNum = triggerNum, name = name, desc = data.display})
end
-- Sort the temporary table by name
table.sort(tempProps, function(a, b)
return a.name < b.name
end)
-- Add a header for the trigger
table.insert(sortedProps, {type = "header", triggerNum = triggerNum, name = OptionsPrivate.GetTriggerTitle(data, triggerNum)})
-- Add the base properties for the trigger
for _, v in ipairs(BaseDynamicTextCodes.trigger) do
local prop = CopyTable(v)
prop.widthFraction = #BaseDynamicTextCodes.trigger
prop.triggerNum = triggerNum
table.insert(sortedProps, prop)
end
-- Add the sorted properties to the sortedProps table
for _, prop in ipairs(tempProps) do
table.insert(sortedProps, prop)
end
end
end
-- Create a modified WeakAurasSnippetButton for each property and add it to ScrollList
local lastType, miniGroup
for i, prop in ipairs(sortedProps) do
if prop.type == "header" then
local heading = AceGUI:Create("Heading")
heading:SetText(prop.name)
heading:SetRelativeWidth(1)
heading.label:SetFontObject(GameFontNormalSmall)
frame.scrollList:AddChild(heading)
else
if ((prop.type == "mini" or prop.type == "marker") and prop.type ~= lastType)
then
miniGroup = AceGUI:Create("SimpleGroup")
miniGroup:SetLayout("Flow")
miniGroup:SetAutoAdjustHeight(true)
miniGroup:SetRelativeWidth(1)
frame.scrollList:AddChild(miniGroup)
end
local button = AceGUI:Create("WeakAurasSnippetButton")
local propIndex = prop.triggerNum > 0 and ("%s"):format(prop.triggerNum) or ""
local propPrefix = prop.triggerNum > 0 and ("%%%s."):format(propIndex) or "%"
if prop.type == "marker" then
button:SetTitle(prop.desc)
else
button:SetTitle(string.format("|cFFFFCC00%s|r%s", propPrefix, prop.name))
end
if prop.type == "mini" or prop.type == "marker" then
button:SetRelativeWidth((1/prop.widthFraction) - 1e-10)
else
button:SetRelativeWidth(1)
end
button.title:SetFontObject(GameFontNormal)
button.frame:SetHeight(28)
button:SetDynamicTextStyle()
-- Set Tooltip
if prop.type ~= "marker" then
button.frame:SetScript("OnEnter", function(frame)
local tooltip = GameTooltip
tooltip:SetWidth(300)
tooltip:SetOwner(frame, "ANCHOR_RIGHT")
tooltip:ClearLines()
tooltip:AddLine(("%s%s"):format(propPrefix, prop.name))
tooltip:AddLine(prop.desc, 1, 1, 1, true)
if prop.name ~= "c" and prop.name ~= "%" then
tooltip:AddLine("\n")
tooltip:AddLine(
prop.triggerNum > 0
and L["The trigger number is optional. When no trigger number is specified, the trigger selected via dynamic information will be used."]
or L["By default this shows the information from the trigger selected via dynamic information. The information from a specific trigger can be shown via e.g. %2.p."],
0.8, 0.8, 0.8,
true)
end
tooltip:Show()
frame.obj:Fire("OnEnter")
end)
else
button.frame:SetScript("OnEnter", nil)
end
-- Insert dynamic text property on click
button:SetCallback("OnClick", function()
local insertProp
if prop.type == "marker" then
insertProp = prop.name
else
if IsShiftKeyDown() then
insertProp = prop.name == "%" and "%%" or ("%%{%s}"):format(prop.name)
if prop.triggerNum > 0 then
insertProp = string.format("%%{%d.%s}", propIndex, prop.name)
end
else
insertProp = prop.name == "%" and "%%" or ("%%%s"):format(prop.name)
if prop.triggerNum > 0 then
insertProp = string.format("%%%d.%s", propIndex, prop.name)
end
end
end
OptionsPrivate.currentDynamicTextInput.editbox:Insert(insertProp)
OptionsPrivate.currentDynamicTextInput.editbox:SetFocus()
end)
if prop.type == "mini" or prop.type == "marker" then
miniGroup:AddChild(button)
else
frame.scrollList:AddChild(button)
end
end
lastType = prop.type
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)
OptionsPrivate.Private.EnsureRegion(id)
if OptionsPrivate.Private.regions[id].region.toShow then
frame.moversizer:SetToRegion(OptionsPrivate.Private.regions[id].region, db.displays[id])
else
if OptionsPrivate.Private.clones[id] then
local _, clone = next(OptionsPrivate.Private.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 = OptionsPrivate.Private.FindUnusedId("New")
local data = {id = new_id, regionType = regionType, uid = WeakAuras.GenerateUniqueID()}
WeakAuras.DeepMixin(data, OptionsPrivate.Private.data_stub);
if (sourceData) then
WeakAuras.DeepMixin(data, sourceData);
end
data.internalVersion = WeakAuras.InternalVersion();
OptionsPrivate.Private.validate(data, OptionsPrivate.Private.regionTypes[regionType].default);
AddDefaultSubRegions(data)
if targetId then
local target = OptionsPrivate.GetDisplayButton(targetId);
local group
if (target) then
if (target:IsGroup()) then
group = target;
else
group = OptionsPrivate.GetDisplayButton(target.data.parent);
end
if (group) then
-- Sanity check so that we don't create a group/dynamic group in a group
if (regionType == "group" or regionType == "dynamicgroup") and group.data.regionType == "dynamicgroup" then
return
end
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);
group.callbacks.UpdateExpandButton();
group:UpdateParentWarning();
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(string.format("Calling 'WeakAuras.NewAura' with invalid groupId %s. Reload your UI to fix the display list.", targetId))
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,
withoutColor, 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 = {}
local parseFn = function(symbol)
if not seenSymbols[symbol] then
local _, sym = string.match(symbol, "(.+)%.(.+)")
sym = sym or symbol
if 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, withoutColor)
end
seenSymbols[symbol] = true
end
end
end
if type(input) == "table" then
for _, txt in ipairs(input) do
OptionsPrivate.Private.ParseTextStr(txt, parseFn)
end
else
OptionsPrivate.Private.ParseTextStr(input, parseFn)
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