You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2262 lines
70 KiB
2262 lines
70 KiB
if not WeakAuras.IsLibsOK() then return end
|
|
---@type string
|
|
local AddonName = ...
|
|
---@class OptionsPrivate
|
|
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 = CreateFrame
|
|
|
|
local AceGUI = LibStub("AceGUI-3.0")
|
|
|
|
---@class WeakAuras
|
|
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;
|
|
--- @type boolean?
|
|
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 = true,
|
|
whileDead = true,
|
|
preferredindex = 4,
|
|
}
|
|
|
|
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
|
|
---@type fun(msg: string, Private: Private)
|
|
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 frame.moversizer then
|
|
frame.moversizer: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
|
|
---@class Frame
|
|
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, adjustSize)
|
|
OptionsPrivate.TexturePicker(frame):Open(baseObject, paths, properties, textures, SetTextureFunc, adjustSize)
|
|
end
|
|
|
|
function OptionsPrivate.OpenIconPicker(baseObject, paths, groupIcon)
|
|
OptionsPrivate.IconPicker(frame):Open(baseObject, paths, groupIcon)
|
|
end
|
|
|
|
function OptionsPrivate.OpenModelPicker(baseObject, path)
|
|
if not(C_AddOns.IsAddOnLoaded("WeakAurasModelPaths")) then
|
|
local loaded, reason = C_AddOns.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(C_AddOns.IsAddOnLoaded("WeakAurasTemplates")) then
|
|
local loaded, reason = C_AddOns.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(GameFontNormalSmall2)
|
|
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
|
|
|