From 7a16831d751a9c596d71b66f19beee759696f868 Mon Sep 17 00:00:00 2001 From: Buds Date: Mon, 14 Aug 2023 02:49:42 +0200 Subject: [PATCH] Support multiple entries for various triggers & load condition Also: * Slightly improve the look of the trigger/load pages by hiding disabled controls and using multiline controls for input boxes that can get full. * Add some titles to the Load options page, making it a bit more structured * Reverted the "Never" load option to the old place, giving people what they want. fixes #4403 --- .luacheckrc | 1 + WeakAuras/BossMods.lua | 6 +- WeakAuras/GenericTrigger.lua | 132 +- WeakAuras/Modernize.lua | 223 +++ WeakAuras/Prototypes.lua | 829 ++++++++--- WeakAuras/WeakAuras.lua | 307 +++-- ...get-WeakAurasMultiLineEditBoxWithEnter.lua | 379 +++++ WeakAurasOptions/LoadOptions.lua | 1219 +++++++++-------- WeakAurasOptions/WeakAurasOptions.toc | 1 + 9 files changed, 2195 insertions(+), 902 deletions(-) create mode 100644 WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasMultiLineEditBoxWithEnter.lua diff --git a/.luacheckrc b/.luacheckrc index b8c7048a5c..449964080f 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -35,6 +35,7 @@ globals = { -- misc custom "AceGUIWeakAurasMultiLineEditBoxInsertLink", + "AceGUIWeakAurasMultiLineEditBoxWithEnterInsertLink", "AceGUIWidgetLSMlists", "CUSTOM_CLASS_COLORS", "LibStub", diff --git a/WeakAuras/BossMods.lua b/WeakAuras/BossMods.lua index 6ab6436308..023ed91eba 100644 --- a/WeakAuras/BossMods.lua +++ b/WeakAuras/BossMods.lua @@ -594,7 +594,7 @@ Private.event_prototypes["DBM Timer"] = { return ret:format( trigger.use_count and trigger.count or "", trigger.use_id and trigger.id or "", - trigger.use_spellId and trigger.spellId or "", + trigger.use_spellId and tostring(trigger.spellId) or "", trigger.use_message and trigger.message or "", trigger.use_message and trigger.message_operator or "", trigger.use_cloneId and "true" or "false", @@ -1220,7 +1220,7 @@ Private.event_prototypes["BigWigs Timer"] = { ]=] return ret:format( trigger.use_count and trigger.count or "", - trigger.use_spellId and trigger.spellId or "", + trigger.use_spellId and tostring(trigger.spellId) or "", trigger.use_text and trigger.text or "", trigger.use_text and trigger.text_operator or "", trigger.use_cloneId and "true" or "false", @@ -1609,7 +1609,7 @@ Private.event_prototypes["Boss Mod Timer"] = { return ret:format( trigger.use_count and trigger.count or "", - trigger.use_spellId and trigger.spellId or "", + trigger.use_spellId and tostring(trigger.spellId) or "", trigger.use_message and trigger.message or "", trigger.use_message and trigger.message_operator or "", trigger.use_cloneId and "true" or "false", diff --git a/WeakAuras/GenericTrigger.lua b/WeakAuras/GenericTrigger.lua index 4e9c163ea4..287b27c013 100644 --- a/WeakAuras/GenericTrigger.lua +++ b/WeakAuras/GenericTrigger.lua @@ -202,47 +202,98 @@ function TestForMultiSelect(trigger, arg) return test; end -function ConstructTest(trigger, arg) +local function singleTest(arg, trigger, name, value, operator, use_exact) + local number = tonumber(value) + if(arg.type == "tristate") then + return TestForTriState(trigger, arg); + elseif(arg.type == "multiselect") then + return TestForMultiSelect(trigger, arg); + elseif(arg.type == "toggle") then + return TestForToggle(trigger, arg); + elseif (arg.type == "spell" or arg.type == "item") then + if arg.test then + if arg.showExactOption then + return "("..arg.test:format(value, tostring(use_exact) or "false") ..")"; + else + return "("..arg.test:format(value)..")"; + end + else + return "(".. name .." and "..name.."==" ..(number or ("\""..(tostring(value) or "").."\""))..")"; + end + elseif(arg.test) then + return "("..arg.test:format(tostring(value) or "")..")"; + elseif(arg.type == "longstring" and operator) then + return TestForLongString(trigger, arg); + elseif (arg.type == "string" or arg.type == "select") then + return "(".. name .." and "..name.."==" ..(number or ("\""..(tostring(value) or "").."\""))..")"; + elseif (arg.type == "number") then + return "(".. name .." and "..name..(operator or "==")..(number or 0) ..")"; + else + -- Should be unused + return "(".. name .." and "..name..(operator or "==")..(number or ("\""..(tostring(value) or 0).."\""))..")"; + end +end + +function ConstructTest(trigger, arg, testGroups, preambleGroups) local test local preamble local name = arg.name; - if(arg.hidden or arg.type == "tristate" or arg.type == "toggle" or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) or ((trigger["use_"..name] or arg.required) and trigger[name])) then - local number = tonumber(trigger[name]); - if(arg.type == "tristate") then - test = TestForTriState(trigger, arg); - elseif(arg.type == "multiselect") then - test = TestForMultiSelect(trigger, arg); - elseif(arg.type == "toggle") then - test = TestForToggle(trigger, arg); - elseif (arg.type == "spell" or arg.type == "item") then - if arg.test then - if arg.showExactOption then - test = "("..arg.test:format(trigger[name], tostring(trigger["use_exact_" .. name]) or "false") ..")"; + + if arg.preamble then + if not arg.preambleGroup or not preambleGroups[arg.preambleGroup] then + preamble = arg.preamble:format(trigger[name] or "") + end + if arg.preambleGroup then + preambleGroups[arg.preambleGroup] = true + end + end + + if arg.hidden + or arg.type == "tristate" + or arg.type == "toggle" + or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) + or ((trigger["use_"..name] or arg.required) and trigger[name]) + then + if arg.multiEntry then + if type(trigger[name]) == "table" and #trigger[name] > 0 then + test = "" + for i, value in ipairs(trigger[name]) do + local operator = name and type(trigger[name.."_operator"]) == "table" and trigger[name.."_operator"][i] + local use_exact = name and type(trigger["use_exact_" .. name]) == "table" and trigger["use_exact_" .. name][i] + + if arg.multiEntry.operator == "preamble" then + preamble = preamble and (preamble .. "\n") or "" + preamble = preamble .. arg.multiEntry.preambleAdd:format(value) + else + local single = singleTest(arg, trigger, name, value, operator, use_exact) + if single then + if test ~= "" then + test = test .. arg.multiEntry.operator + end + test = test .. single + end + end + end + + if arg.multiEntry.operator == "preamble" then + test = arg.test + end + + if test == "" then + test = nil else - test = "("..arg.test:format(trigger[name])..")"; + test = "(" .. test .. ")" end - else - test = "(".. name .." and "..name.."==" ..(number or ("\""..(trigger[name] or "").."\""))..")"; - end - elseif(arg.test) then - test = "("..arg.test:format(tostring(trigger[name]) or "")..")"; - elseif(arg.type == "longstring" and trigger[name.."_operator"]) then - test = TestForLongString(trigger, arg); - elseif (arg.type == "string" or arg.type == "select") then - test = "(".. name .." and "..name.."==" ..(number or ("\""..(trigger[name] or "").."\""))..")"; - elseif (arg.type == "number") then - test = "(".. name .." and "..name..(trigger[name.."_operator"] or "==")..(number or 0) ..")"; + end else - -- Should be unused - test = "(".. name .." and "..name..(trigger[name.."_operator"] or "==")..(number or ("\""..(trigger[name] or 0).."\""))..")"; + local value = trigger[name] + local operator = name and trigger[name.."_operator"] + local use_exact = name and trigger["use_exact_" .. name] + test = singleTest(arg, trigger, name, value, operator, use_exact) end end - if arg.preamble then - preamble = arg.preamble:format(trigger[name] or "") - end - - if (test == "(true)") then + if not test or test == "(true)" or arg.testGroup and testGroups[arg.testGroup] then return nil, preamble end @@ -275,6 +326,9 @@ function ConstructFunction(prototype, trigger) local store = {}; local init; local preambles = "\n" + local orConjunctionGroups = {} + local preambleGroups = {} + local testGroups = {} if(prototype.init) then init = prototype.init(trigger); else @@ -300,12 +354,17 @@ function ConstructFunction(prototype, trigger) if (arg.store) then tinsert(store, name); end - local test, preamble = ConstructTest(trigger, arg); + local test, preamble = ConstructTest(trigger, arg, testGroups, preambleGroups); if (test) then if(arg.required) then tinsert(required, test); else - tinsert(tests, test); + if arg.orConjunctionGroup then + orConjunctionGroups[arg.orConjunctionGroup] = orConjunctionGroups[arg.orConjunctionGroup] or {} + tinsert(orConjunctionGroups[arg.orConjunctionGroup], test) + else + tinsert(tests, test); + end end if(arg.debug) then tinsert(debug, arg.debug:format(trigger[name])); @@ -317,6 +376,11 @@ function ConstructFunction(prototype, trigger) end end end + + for _, orConjunctionGroup in pairs(orConjunctionGroups) do + tinsert(tests, "("..table.concat(orConjunctionGroup, " or ")..")") + end + local ret = preambles .. "return function("..tconcat(input, ", ")..")\n"; ret = ret..(init or ""); diff --git a/WeakAuras/Modernize.lua b/WeakAuras/Modernize.lua index d45f4802b3..079599d39e 100644 --- a/WeakAuras/Modernize.lua +++ b/WeakAuras/Modernize.lua @@ -1734,5 +1734,228 @@ function Private.Modernize(data) end end + if data.internalVersion < 67 then + local function migrateToTable(tab, field) + local value = tab[field] + if value ~= nil and type(value) ~= "table" then + tab[field] = { value } + end + end + do + local trigger_migration = { + ["Cast"] = { + "stage", + "stage_operator", + }, + ["Experience"] = { + "level", + "level_operator", + "currentXP", + "currentXP_operator", + "totalXP", + "totalXP_operator", + "percentXP", + "percentXP_operator", + "restedXP", + "restedXP_operator", + "percentrested", + "percentrested_operator", + }, + ["Health"] = { + "health", + "health_operator", + "percenthealth", + "percenthealth_operator", + "deficit", + "deficit_operator", + "maxhealth", + "maxhealth_operator", + "absorb", + "absorb_operator", + "healabsorb", + "healabsorb_operator", + "healprediction", + "healprediction_operator", + }, + ["Power"] = { + "power", + "power_operator", + "percentpower", + "percentpower_operator", + "deficit", + "deficit_operator", + "maxpower", + "maxpower_operator", + }, + ["Character Stats"] = { + "mainstat", + "mainstat_operator", + "strength", + "strength_operator", + "agility", + "agility_operator", + "intellect", + "intellect_operator", + "spirit", + "spirit_operator", + "stamina", + "stamina_operator", + "criticalrating", + "criticalrating_operator", + "criticalpercent", + "criticalpercent_operator", + "hitrating", + "hitrating_operator", + "hitpercent", + "hitpercent_operator", + "hasterating", + "hasterating_operator", + "hastepercent", + "hastepercent_operator", + "meleehastepercent", + "meleehastepercent_operator", + "expertiserating", + "expertiserating_operator", + "expertisebonus", + "expertisebonus_operator", + "armorpenrating", + "armorpenrating_operator", + "armorpenpercent", + "armorpenpercent_operator", + "resiliencerating", + "resiliencerating_operator", + "resiliencepercent", + "resiliencepercent_operator", + "spellpenpercent", + "spellpenpercent_operator", + "masteryrating", + "masteryrating_operator", + "masterypercent", + "masterypercent_operator", + "versatilityrating", + "versatilityrating_operator", + "versatilitypercent", + "versatilitypercent_operator", + "attackpower", + "attackpower_operator", + "resistanceholy", + "resistanceholy_operator", + "resistancefire", + "resistancefire_operator", + "resistancenature", + "resistancenature_operator", + "resistancefrost", + "resistancefrost_operator", + "resistanceshadow", + "resistanceshadow_operator", + "resistancearcane", + "resistancearcane_operator", + "leechrating", + "leechrating_operator", + "leechpercent", + "leechpercent_operator", + "movespeedrating", + "movespeedrating_operator", + "movespeedpercent", + "movespeedpercent_operator", + "runspeedpercent", + "runspeedpercent_operator", + "avoidancerating", + "avoidancerating_operator", + "avoidancepercent", + "avoidancepercent_operator", + "defense", + "defense_operator", + "dodgerating", + "dodgerating_operator", + "dodgepercent", + "dodgepercent_operator", + "parryrating", + "parryrating_operator", + "parrypercent", + "parrypercent_operator", + "blockpercent", + "blockpercent_operator", + "blocktargetpercent", + "blocktargetpercent_operator", + "blockvalue", + "blockvalue_operator", + "staggerpercent", + "staggerpercent_operator", + "staggertargetpercent", + "staggertargetpercent_operator", + "armorrating", + "armorrating_operator", + "armorpercent", + "armorpercent_operator", + "armortargetpercent", + "armortargetpercent_operator", + }, + ["Threat Situation"] = { + "threatpct", + "threatpct_operator", + "rawthreatpct", + "rawthreatpct_operator", + "threatvalue", + "threatvalue_operator", + }, + ["Unit Characteristics"] = { + "level", + "level_operator", + }, + ["Combat Log"] = { + "spellId", + "spellName", + }, + ["Spell Cast Succeeded"] = { + "spellId" + } + } + for _, triggerData in ipairs(data.triggers) do + local t = triggerData.trigger + local fieldsToMigrate = trigger_migration[t.event] + if fieldsToMigrate then + for _, field in ipairs(fieldsToMigrate) do + migrateToTable(t, field) + end + end + -- cast trigger move data from 'spell' & 'spellId' to 'spellIds' & 'spellNames' + if t.event == "Cast" and t.type == "unit" then + if t.spellId then + if t.useExactSpellId then + t.use_spellIds = t.use_spellId + t.spellIds = t.spellIds or {} + tinsert(t.spellIds, t.spellId) + else + t.use_spellNames = t.use_spellId + t.spellNames = t.spellNames or {} + tinsert(t.spellNames, t.spellId) + end + end + if t.use_spell and t.spell then + t.use_spellNames = true + t.spellNames = t.spellNames or {} + tinsert(t.spellNames, t.spell) + end + t.use_spellId = nil + t.spellId = nil + t.use_spell = nil + t.spell = nil + end + end + end + do + local loadFields = { + "level", "effectiveLevel" + } + + for _, field in ipairs(loadFields) do + migrateToTable(data.load, field) + migrateToTable(data.load, field .. "_operator") + end + end + end + data.internalVersion = max(data.internalVersion or 0, WeakAuras.InternalVersion()) end + diff --git a/WeakAuras/Prototypes.lua b/WeakAuras/Prototypes.lua index 61b25b42d9..8804918504 100644 --- a/WeakAuras/Prototypes.lua +++ b/WeakAuras/Prototypes.lua @@ -894,11 +894,11 @@ function Private.ExecEnv.ParseStringCheck(input) if escaped then escaped = false elseif c == '\\' then - partial = partial .. input:sub(start, i -1) + partial = partial .. input:sub(start, i - 1) start = i + 1 escaped = true elseif c == "," then - matcher:Add(partial .. input:sub(start, i -1):trim()) + matcher:Add(partial .. input:sub(start, i - 1):trim()) start = i + 1 partial = "" end @@ -1209,6 +1209,11 @@ Private.load_prototype = { -- events: the events on which the test must be reevaluated -- optional: whether the test is relevant for the options classification between loaded and unloaded, defaults to false args = { + { + name ="generalTitle", + display = L["General"], + type = "description", + }, { name = "combat", display = L["In Combat"], @@ -1219,13 +1224,11 @@ Private.load_prototype = { events = {"PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED"} }, { - name = "encounter", - display = L["In Encounter"], - type = "tristate", + name = "never", + display = L["Never"], + type = "toggle", width = WeakAuras.normalWidth, - init = "arg", - optional = true, - events = {"ENCOUNTER_START", "ENCOUNTER_END"} + test = "false", }, { name = "alive", @@ -1236,9 +1239,18 @@ Private.load_prototype = { optional = true, events = {"PLAYER_DEAD", "PLAYER_ALIVE", "PLAYER_UNGHOST"} }, + { + name = "encounter", + display = L["In Encounter"], + type = "tristate", + width = WeakAuras.normalWidth, + init = "arg", + optional = true, + events = {"ENCOUNTER_START", "ENCOUNTER_END"} + }, { name = "warmode", - display = L["War Mode Active"], + display = L["In War Mode"], type = "tristate", init = WeakAuras.IsRetail() and "arg" or nil, width = WeakAuras.normalWidth, @@ -1247,13 +1259,6 @@ Private.load_prototype = { hidden = not WeakAuras.IsRetail(), events = {"PLAYER_FLAGS_CHANGED"} }, - { - name = "never", - display = L["Never"], - type = "toggle", - width = WeakAuras.normalWidth, - test = "false", - }, { name = "petbattle", display = L["In Pet Battle"], @@ -1298,41 +1303,9 @@ Private.load_prototype = { events = {"WA_DRAGONRIDING_UPDATE"} }, { - name = "ingroup", - display = L["Group Type"], - type = "multiselect", - width = WeakAuras.normalWidth, - init = "arg", - values = "group_types", - events = {"GROUP_ROSTER_UPDATE"} - }, - { - name = "player", - init = "arg", - enable = false, - hidden = true - }, - { - name = "realm", - init = "arg", - enable = false, - hidden = true - }, - { - name = "namerealm", - display = L["Player Name/Realm"], - type = "string", - test = "nameRealmChecker:Check(player, realm)", - preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", - desc = constants.nameRealmFilterDesc, - }, - { - name = "ignoreNameRealm", - display = L["|cFFFF0000Not|r Player Name/Realm"], - type = "string", - test = "not nameRealmIgnoreChecker:Check(player, realm)", - preamble = "local nameRealmIgnoreChecker = Private.ExecEnv.ParseNameCheck(%q)", - desc = constants.nameRealmFilterDesc, + name ="playerTitle", + display = L["Player"], + type = "description", }, { name = "class", @@ -1625,6 +1598,36 @@ Private.load_prototype = { events = WeakAuras.IsWrathClassic() and {"SPELLS_CHANGED", "UNIT_PET", "PLAYER_TALENT_UPDATE"} or {"SPELLS_CHANGED", "UNIT_PET"}, showExactOption = true }, + { + name = "player", + init = "arg", + enable = false, + hidden = true + }, + { + name = "realm", + init = "arg", + enable = false, + hidden = true + }, + { + name = "namerealm", + display = L["Player Name/Realm"], + type = "string", + multiline = true, + test = "nameRealmChecker:Check(player, realm)", + preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", + desc = constants.nameRealmFilterDesc, + }, + { + name = "ignoreNameRealm", + display = L["|cFFFF0000Not|r Player Name/Realm"], + type = "string", + multiline = true, + test = "not nameRealmIgnoreChecker:Check(player, realm)", + preamble = "local nameRealmIgnoreChecker = Private.ExecEnv.ParseNameCheck(%q)", + desc = constants.nameRealmFilterDesc, + }, { name = "race", display = L["Player Race"], @@ -1644,7 +1647,11 @@ Private.load_prototype = { display = L["Player Level"], type = "number", init = "arg", - events = {"PLAYER_LEVEL_UP"} + events = {"PLAYER_LEVEL_UP"}, + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "effectiveLevel", @@ -1654,12 +1661,71 @@ Private.load_prototype = { desc = L["The effective level differs from the level in e.g. Time Walking dungeons."], enable = WeakAuras.IsRetail(), hidden = not WeakAuras.IsRetail(), - events = {"PLAYER_LEVEL_UP", "UNIT_FLAGS", "ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"} + events = {"PLAYER_LEVEL_UP", "UNIT_FLAGS", "ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, + multiEntry = { + operator = "and", + limit = 2 + }, + }, + { + name = "role", + display = WeakAuras.IsWrathClassic() and L["Assigned Role"] or L["Spec Role"], + type = "multiselect", + values = "role_types", + init = WeakAuras.IsWrathOrRetail() and "arg" or nil, + enable = WeakAuras.IsWrathOrRetail(), + hidden = not WeakAuras.IsWrathOrRetail(), + events = {"PLAYER_ROLES_ASSIGNED", "PLAYER_TALENT_UPDATE"} + }, + { + name = "spec_position", + display = WeakAuras.newFeatureString .. L["Spec Position"], + type = "multiselect", + values = "spec_position_types", + init = WeakAuras.IsRetail() and "arg" or nil, + enable = WeakAuras.IsRetail(), + hidden = not WeakAuras.IsRetail(), + events = {"ACTIVE_TALENT_GROUP_CHANGED"} + }, + { + name = "raid_role", + display = L["Raid Role"], + type = "multiselect", + values = "raid_role_types", + init = WeakAuras.IsClassicEraOrWrath() and "arg" or nil, + enable = WeakAuras.IsClassicEraOrWrath(), + hidden = WeakAuras.IsRetail(), + events = {"PLAYER_ROLES_ASSIGNED"} + }, + { + name = "ingroup", + display = L["Group Type"], + type = "multiselect", + width = WeakAuras.normalWidth, + init = "arg", + values = "group_types", + events = {"GROUP_ROSTER_UPDATE"} + }, + { + name = "group_leader", + display = WeakAuras.newFeatureString .. L["Group Leader/Assist"], + type = "multiselect", + init = "arg", + events = {"PARTY_LEADER_CHANGED", "GROUP_ROSTER_UPDATE"}, + width = WeakAuras.doubleWidth, + values = "group_member_types", + test = "Private.ExecEnv.CheckGroupMemberType(%s, group_leader)" + }, + { + name ="locationTitle", + display = L["Location"], + type = "description", }, { name = "zone", display = L["Zone Name"], type = "string", + multiline = true, init = "arg", preamble = "local checker = Private.ExecEnv.ParseStringCheck(%q)", test = "checker:Check(zone)", @@ -1682,19 +1748,21 @@ Private.load_prototype = { name = "zoneIds", display = L["Zone ID(s)"], type = "string", + multiline = true, events = {"ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA", "VEHICLE_UPDATE"}, desc = get_zoneId_list, preamble = "local zoneChecker = Private.ExecEnv.ParseZoneCheck(%q)", - test = "zoneChecker:Check(zoneId, zonegroupId)" + test = "zoneChecker:Check(zoneId, zonegroupId)", }, { name = "encounterid", display = L["Encounter ID(s)"], type = "string", init = "arg", + multiline = true, desc = Private.get_encounters_list, test = "WeakAuras.CheckNumericIds(%q, encounterid)", - events = {"ENCOUNTER_START", "ENCOUNTER_END"} + events = {"ENCOUNTER_START", "ENCOUNTER_END"}, }, { name = "size", @@ -1726,46 +1794,6 @@ Private.load_prototype = { hidden = WeakAuras.IsClassicEra(), events = {"PLAYER_DIFFICULTY_CHANGED", "ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA"}, }, - { - name = "role", - display = WeakAuras.IsWrathClassic() and L["Assigned Role"] or L["Spec Role"], - type = "multiselect", - values = "role_types", - init = WeakAuras.IsWrathOrRetail() and "arg" or nil, - enable = WeakAuras.IsWrathOrRetail(), - hidden = not WeakAuras.IsWrathOrRetail(), - events = {"PLAYER_ROLES_ASSIGNED", "PLAYER_TALENT_UPDATE"} - }, - { - name = "spec_position", - display = WeakAuras.newFeatureString .. L["Spec Position"], - type = "multiselect", - values = "spec_position_types", - init = WeakAuras.IsRetail() and "arg" or nil, - enable = WeakAuras.IsRetail(), - hidden = not WeakAuras.IsRetail(), - events = {"ACTIVE_TALENT_GROUP_CHANGED"} - }, - { - name = "raid_role", - display = L["Raid Role"], - type = "multiselect", - values = "raid_role_types", - init = WeakAuras.IsClassicEraOrWrath() and "arg" or nil, - enable = WeakAuras.IsClassicEraOrWrath(), - hidden = WeakAuras.IsRetail(), - events = {"PLAYER_ROLES_ASSIGNED"} - }, - { - name = "group_leader", - display = WeakAuras.newFeatureString .. L["Group Leader/Assist"], - type = "multiselect", - init = "arg", - events = {"PARTY_LEADER_CHANGED", "GROUP_ROSTER_UPDATE"}, - width = WeakAuras.doubleWidth, - values = "group_member_types", - test = "Private.ExecEnv.CheckGroupMemberType(%s, group_leader)" - }, { name = "affixes", display = L["Mythic+ Affix"], @@ -1777,6 +1805,11 @@ Private.load_prototype = { hidden = not WeakAuras.IsRetail(), events = {"CHALLENGE_MODE_START", "CHALLENGE_MODE_COMPLETED"}, }, + { + name ="equipmentTitle", + display = L["Equipment"], + type = "description", + }, { name = "itemequiped", display = L["Item Equipped"], @@ -2075,6 +2108,7 @@ Private.event_prototypes = { display = L["Unit Name/Realm"], desc = constants.nameRealmFilterDesc, type = "string", + multiline = true, preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "nameRealmChecker:Check(name, realm)", conditionType = "string", @@ -2212,15 +2246,30 @@ Private.event_prototypes = { type = "number", init = "UnitLevel(unit)", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "npcId", display = L["Npc ID"], type = "string", + multiline = true, store = true, + init = "select(6, strsplit('-', UnitGUID(unit) or ''))", conditionType = "string", - test = "select(6, strsplit('-', UnitGUID(unit) or '')) == %q", + preamble = "local npcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "npcIdChecker:Check(npcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.npcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"] }, { name = "attackable", @@ -2471,6 +2520,10 @@ Private.event_prototypes = { store = true, init = [[UnitLevel("player")]], conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "currentXP", @@ -2479,6 +2532,10 @@ Private.event_prototypes = { store = true, init = [[UnitXP("player")]], conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "totalXP", @@ -2487,6 +2544,10 @@ Private.event_prototypes = { store = true, init = [[UnitXPMax("player")]], conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "value", @@ -2517,7 +2578,11 @@ Private.event_prototypes = { type = "number", init = "total ~= 0 and (value / total) * 100 or nil", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "showRested", @@ -2533,6 +2598,10 @@ Private.event_prototypes = { type = "number", store = true, conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "percentrested", @@ -2541,6 +2610,10 @@ Private.event_prototypes = { type = "number", store = true, conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, }, overlayFuncs = { @@ -2630,7 +2703,11 @@ Private.event_prototypes = { type = "number", init = "UnitHealth(unit)", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "value", @@ -2659,7 +2736,11 @@ Private.event_prototypes = { type = "number", init = "total ~= 0 and (value / total) * 100 or nil", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "deficit", @@ -2667,7 +2748,11 @@ Private.event_prototypes = { type = "number", init = "total - value", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "maxhealth", @@ -2675,7 +2760,11 @@ Private.event_prototypes = { type = "number", init = "total", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "showAbsorb", @@ -2715,13 +2804,6 @@ Private.event_prototypes = { enable = function(trigger) return WeakAuras.IsRetail() and trigger.use_showHealAbsorb end, hidden = not WeakAuras.IsRetail() }, - { - name = "showIncomingHeal", - display = L["Show Incoming Heal"], - type = "toggle", - test = "true", - reloadOptions = true, - }, { name = "absorb", type = "number", @@ -2730,7 +2812,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return WeakAuras.IsRetail() and trigger.use_showAbsorb end, - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "healabsorb", @@ -2740,7 +2826,18 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return WeakAuras.IsRetail() and trigger.use_showHealAbsorb end, - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, + }, + { + name = "showIncomingHeal", + display = L["Show Incoming Heal"], + type = "toggle", + test = "true", + reloadOptions = true, }, { name = "healprediction", @@ -2750,6 +2847,10 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return trigger.use_showIncomingHeal end, + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "name", @@ -2771,6 +2872,7 @@ Private.event_prototypes = { name = "namerealm", display = L["Unit Name/Realm"], type = "string", + multiline = true, preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "nameRealmChecker:Check(name, realm)", conditionType = "string", @@ -2787,9 +2889,20 @@ Private.event_prototypes = { name = "npcId", display = L["Npc ID"], type = "string", + multiline = true, store = true, + init = "select(6, strsplit('-', UnitGUID(unit) or ''))", conditionType = "string", - test = "select(6, strsplit('-', UnitGUID(unit) or '')) == %q", + preamble = "local npcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "npcIdChecker:Check(npcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.npcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"] }, { name = "class", @@ -3214,6 +3327,10 @@ Private.event_prototypes = { or "UnitPower(unit, powerType, powerThirdArg) / WeakAuras.UnitPowerDisplayMod(powerTypeToCheck)", store = true, conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "value", @@ -3250,7 +3367,11 @@ Private.event_prototypes = { type = "number", init = "total ~= 0 and (value / total) * 100 or nil", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "deficit", @@ -3258,7 +3379,11 @@ Private.event_prototypes = { type = "number", init = "total - value", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "maxpower", @@ -3266,7 +3391,11 @@ Private.event_prototypes = { type = "number", init = "total", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "name", @@ -3288,6 +3417,7 @@ Private.event_prototypes = { name = "namerealm", display = L["Unit Name/Realm"], type = "string", + multiline = true, preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "nameRealmChecker:Check(name, realm)", conditionType = "string", @@ -3304,9 +3434,20 @@ Private.event_prototypes = { name = "npcId", display = L["Npc ID"], type = "string", + multiline = true, store = true, + init = "select(6, strsplit('-', UnitGUID(unit) or ''))", conditionType = "string", - test = "select(6, strsplit('-', UnitGUID(unit) or '')) == %q", + preamble = "local npcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "npcIdChecker:Check(npcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.npcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"] }, { name = "class", @@ -3593,6 +3734,7 @@ Private.event_prototypes = { name = "namerealm", display = L["Unit Name/Realm"], type = "string", + multiline = true, preamble = "local nameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "nameRealmChecker:Check(unitname, unitrealm)", conditionType = "string", @@ -3768,15 +3910,32 @@ Private.event_prototypes = { name = "sourceName", display = L["Source Name"], type = "string", + multiline = true, init = "arg", store = true, - conditionType = "string" + conditionType = "string", + preamble = "local sourceNameChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "sourceNameChecker:Check(sourceName)", + desc = L["Supports multiple entries, separated by commas"], }, { name = "sourceNpcId", display = L["Source NPC Id"], type = "string", - test = "select(6, strsplit('-', sourceGUID or '')) == %q", + multiline = true, + init = "select(6, strsplit('-', sourceGUID or ''))", + store = true, + conditionType = "string", + preamble = "local sourceNpcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "sourceNpcIdChecker:Check(sourceNpcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.sourceNpcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"], enable = function(trigger) return not (trigger.subeventPrefix == "ENVIRONMENTAL") end, @@ -3872,18 +4031,35 @@ Private.event_prototypes = { name = "destName", display = L["Destination Name"], type = "string", + multiline = true, init = "arg", + store = true, + conditionType = "string", + preamble = "local destNameChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "destNameChecker:Check(destName)", + desc = L["Supports multiple entries, separated by commas"], enable = function(trigger) return not (trigger.subeventPrefix == "SPELL" and trigger.subeventSuffix == "_CAST_START"); end, - store = true, - conditionType = "string" }, { name = "destNpcId", display = L["Destination NPC Id"], type = "string", - test = "select(6, strsplit('-', destGUID or '')) == %q", + multiline = true, + init = "select(6, strsplit('-', destGUID or ''))", + store = true, + conditionType = "string", + preamble = "local destNpcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "destNpcIdChecker:Check(destNpcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.destNpcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"], enable = function(trigger) return not (trigger.subeventPrefix == "SPELL" and trigger.subeventSuffix == "_CAST_START"); end, @@ -3992,8 +4168,15 @@ Private.event_prototypes = { enable = function(trigger) return trigger.subeventPrefix and (trigger.subeventPrefix:find("SPELL") or trigger.subeventPrefix == "RANGE" or trigger.subeventPrefix:find("DAMAGE")) end, - test = WeakAuras.IsClassicEra() and "GetSpellInfo(%q) == spellName" or nil, store = true, + preambleGroup = "spell", + preamble = "local spellChecker = Private.ExecEnv.CreateSpellChecker()", + multiEntry = { + operator = "preamble", + preambleAdd = WeakAuras.IsClassicEra() and "spellChecker:AddName(%q)" or "spellChecker:AddExact(%q)" + }, + test = WeakAuras.IsClassicEra() and "spellChecker:Check(spellName)" or "spellChecker:Check(spellId)", + testGroup = "spell", conditionType = "number", type = "spell", showExactOption = false, @@ -4007,6 +4190,14 @@ Private.event_prototypes = { return trigger.subeventPrefix and (trigger.subeventPrefix:find("SPELL") or trigger.subeventPrefix == "RANGE" or trigger.subeventPrefix:find("DAMAGE")) end, store = true, + preambleGroup = "spell", + preamble = "local spellChecker = Private.ExecEnv.CreateSpellChecker()", + multiEntry = { + operator = "preamble", + preambleAdd = "spellChecker:AddName(%q)" + }, + test = WeakAuras.IsClassicEra() and "spellChecker:Check(spellName)" or "spellChecker:Check(spellId)", + testGroup = "spell", conditionType = "string" }, { @@ -4835,7 +5026,7 @@ Private.event_prototypes = { type = "spell", init = "arg", showExactOption = true, - test = "Private.ExecEnv.CompareSpellIds(spellName, %s, %s)", + test = "Private.ExecEnv.CompareSpellIds(spellName, %q, %s)", } }, nameFunc = function(trigger) @@ -4896,7 +5087,7 @@ Private.event_prototypes = { type = "spell", init = "arg", showExactOption = true, - test = "Private.ExecEnv.CompareSpellIds(spellName, %s, %s)", + test = "Private.ExecEnv.CompareSpellIds(spellName, %q, %s)", }, { name = "direction", @@ -6833,12 +7024,17 @@ Private.event_prototypes = { { name = "spellId", display = L["Spell Id"], - type = "string", + type = "spell", init = "arg", store = true, + multiEntry = { + operator = "preamble", + preambleAdd = "spellChecker:AddExact(%q)" + }, + preamble = "local spellChecker = Private.ExecEnv.CreateSpellChecker()", + test = "spellChecker:Check(spellId)", conditionType = "number" }, - { name = "icon", hidden = true, @@ -7729,6 +7925,10 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return trigger.unit ~= "none" end, + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "rawthreatpct", @@ -7738,6 +7938,10 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return trigger.unit ~= "none" end, + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "threatvalue", @@ -7747,6 +7951,10 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = function(trigger) return trigger.unit ~= "none" end, + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "value", @@ -8075,22 +8283,50 @@ Private.event_prototypes = { store = true }, { - name = "spellId", - display = L["Spell"], + name = "spellNames", + display = L["Name(s)"], type = "spell", enable = function(trigger) return not trigger.use_inverse end, + preambleGroup = "spell", + preamble = "local spellChecker = Private.ExecEnv.CreateSpellChecker()", + multiEntry = { + operator = "preamble", + preambleAdd = "spellChecker:AddName(%q)" + }, + test = "spellChecker:Check(spellId)", + testGroup = "spell", + noValidation = true, + }, + { + name = "spellIds", + display = L["Exact Spell ID(s)"], + type = "spell", + enable = function(trigger) return not trigger.use_inverse end, + preambleGroup = "spell", + preamble = "local spellChecker = Private.ExecEnv.CreateSpellChecker()", + multiEntry = { + operator = "preamble", + preambleAdd = "spellChecker:AddExact(%q)" + }, + test = "spellChecker:Check(spellId)", + testGroup = "spell", + }, + { + name = "spellId", + display = L["Spell ID"], conditionType = "number", - showExactOption = true, - test = "Private.ExecEnv.CompareSpellIds(spellId, %s, %s)", store = true, + test = "true", + hidden = true }, { name = "spell", - display = L["Legacy Spellname"], + display = L["Spellname"], type = "string", - enable = function(trigger) return not trigger.use_inverse end, conditionType = "string", store = true, + test = "true", + hidden = true }, { name = "castType", @@ -8133,7 +8369,11 @@ Private.event_prototypes = { enable = WeakAuras.IsRetail() and function(trigger) return not trigger.use_inverse end or false, store = true, conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "stageTotal", @@ -8214,12 +8454,23 @@ Private.event_prototypes = { name = "npcId", display = L["Npc ID"], type = "string", + multiline = true, store = true, + init = "select(6, strsplit('-', UnitGUID(unit) or ''))", conditionType = "string", - test = "select(6, strsplit('-', UnitGUID(unit) or '')) == %q", + preamble = "local npcIdChecker = Private.ExecEnv.ParseStringCheck(%q)", + test = "npcIdChecker:Check(npcId)", + conditionPreamble = function(input) + return Private.ExecEnv.ParseStringCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.npcId) + end, + operator_types = "none", + desc = L["Supports multiple entries, separated by commas"], enable = function(trigger) return not trigger.use_inverse - end + end, }, { name = "class", @@ -8346,6 +8597,7 @@ Private.event_prototypes = { name = "sourceNameRealm", display = L["Source Unit Name/Realm"], type = "string", + multiline = true, preamble = "local sourceNameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "sourceNameRealmChecker:Check(sourceName, sourceRealm)", conditionType = "string", @@ -8394,6 +8646,7 @@ Private.event_prototypes = { name = "destNameRealm", display = L["Name/Realm of Caster's Target"], type = "string", + multiline = true, preamble = "local destNameRealmChecker = Private.ExecEnv.ParseNameCheck(%q)", test = "destNameRealmChecker:Check(destName, destRealm)", conditionType = "string", @@ -8560,7 +8813,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "strength", @@ -8570,7 +8827,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "agility", @@ -8580,7 +8841,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "intellect", @@ -8590,7 +8855,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "spirit", @@ -8600,7 +8869,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "stamina", @@ -8608,7 +8881,11 @@ Private.event_prototypes = { type = "number", init = "select(2, UnitStat('player', LE_UNIT_STAT_STAMINA)) * GetUnitMaxHealthModifier('player')", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "criticalrating", @@ -8618,7 +8895,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathOrRetail(), conditionType = "number", - hidden = not WeakAuras.IsWrathOrRetail() + hidden = not WeakAuras.IsWrathOrRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "criticalpercent", @@ -8626,7 +8907,11 @@ Private.event_prototypes = { type = "number", init = "WeakAuras.GetCritChance()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "hitrating", @@ -8636,7 +8921,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "hitpercent", @@ -8646,7 +8935,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = WeakAuras.IsWrathClassic(), - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "hasterating", @@ -8656,7 +8949,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathOrRetail(), conditionType = "number", - hidden = not WeakAuras.IsWrathOrRetail() + hidden = not WeakAuras.IsWrathOrRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "hastepercent", @@ -8664,7 +8961,11 @@ Private.event_prototypes = { type = "number", init = "GetHaste()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "meleehastepercent", @@ -8674,7 +8975,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = WeakAuras.IsWrathClassic(), - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "expertiserating", @@ -8684,7 +8989,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "expertisebonus", @@ -8694,7 +9003,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = WeakAuras.IsWrathClassic(), - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "armorpenrating", @@ -8704,7 +9017,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "armorpenpercent", @@ -8714,7 +9031,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = WeakAuras.IsWrathClassic(), - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resiliencerating", @@ -8724,7 +9045,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resiliencepercent", @@ -8734,7 +9059,11 @@ Private.event_prototypes = { store = true, conditionType = "number", enable = WeakAuras.IsWrathClassic(), - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "spellpenpercent", @@ -8744,7 +9073,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "masteryrating", @@ -8754,7 +9087,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "masterypercent", @@ -8764,7 +9101,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "versatilityrating", @@ -8774,7 +9115,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "versatilitypercent", @@ -8784,7 +9129,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "attackpower", @@ -8792,7 +9141,11 @@ Private.event_prototypes = { type = "number", init = "WeakAuras.GetEffectiveAttackPower()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistanceholy", @@ -8802,7 +9155,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistancefire", @@ -8812,7 +9169,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistancenature", @@ -8822,7 +9183,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistancefrost", @@ -8832,7 +9197,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistanceshadow", @@ -8842,7 +9211,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "resistancearcane", @@ -8852,7 +9225,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsClassicEraOrWrath(), conditionType = "number", - hidden = WeakAuras.IsRetail() + hidden = WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "leechrating", @@ -8862,7 +9239,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "leechpercent", @@ -8872,7 +9253,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "movespeedrating", @@ -8882,7 +9267,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "moveSpeed", @@ -8897,7 +9286,11 @@ Private.event_prototypes = { type = "number", init = "GetUnitSpeed('player') / 7 * 100", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "runspeedpercent", @@ -8905,7 +9298,11 @@ Private.event_prototypes = { type = "number", init = "select(2, GetUnitSpeed('player')) / 7 * 100", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "avoidancerating", @@ -8915,7 +9312,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "avoidancepercent", @@ -8925,7 +9326,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "defense", @@ -8935,7 +9340,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathClassic(), conditionType = "number", - hidden = not WeakAuras.IsWrathClassic() + hidden = not WeakAuras.IsWrathClassic(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "dodgerating", @@ -8945,7 +9354,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathOrRetail(), conditionType = "number", - hidden = not WeakAuras.IsWrathOrRetail() + hidden = not WeakAuras.IsWrathOrRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "dodgepercent", @@ -8953,7 +9366,11 @@ Private.event_prototypes = { type = "number", init = "GetDodgeChance()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "parryrating", @@ -8963,7 +9380,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathOrRetail(), conditionType = "number", - hidden = not WeakAuras.IsWrathOrRetail() + hidden = not WeakAuras.IsWrathOrRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "parrypercent", @@ -8971,7 +9392,11 @@ Private.event_prototypes = { type = "number", init = "GetParryChance()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "blockpercent", @@ -8979,7 +9404,11 @@ Private.event_prototypes = { type = "number", init = "GetBlockChance()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "blocktargetpercent", @@ -8989,7 +9418,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "blockvalue", @@ -8997,7 +9430,11 @@ Private.event_prototypes = { type = "number", init = "GetShieldBlock()", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "staggerpercent", @@ -9007,7 +9444,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "staggertargetpercent", @@ -9017,7 +9458,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "armorrating", @@ -9025,7 +9470,11 @@ Private.event_prototypes = { type = "number", init = "select(2, UnitArmor('player'))", store = true, - conditionType = "number" + conditionType = "number", + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "armorpercent", @@ -9035,7 +9484,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsWrathOrRetail(), conditionType = "number", - hidden = not WeakAuras.IsWrathOrRetail() + hidden = not WeakAuras.IsWrathOrRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, { name = "armortargetpercent", @@ -9045,7 +9498,11 @@ Private.event_prototypes = { store = true, enable = WeakAuras.IsRetail(), conditionType = "number", - hidden = not WeakAuras.IsRetail() + hidden = not WeakAuras.IsRetail(), + multiEntry = { + operator = "and", + limit = 2 + }, }, }, automaticrequired = true diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index 91220fd792..c5502b89bc 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1,7 +1,7 @@ --- @type string, Private local AddonName, Private = ... -local internalVersion = 66 +local internalVersion = 67 -- Lua APIs local insert = table.insert @@ -604,6 +604,128 @@ local function EvalBooleanArg(arg, trigger, default) end end +local function singleTest(arg, trigger, use, name, value, operator, use_exact) + local number = value and tonumber(value) or nil + if(arg.type == "tristate") then + if(use == false) then + return "(not "..name..")"; + elseif(use) then + if(arg.test) then + return "("..arg.test:format(value)..")"; + else + return name; + end + end + elseif(arg.type == "tristatestring") then + if(use == false) then + return "("..name.. "~=".. (number or string.format("%s", Private.QuotedString(value or ""))) .. ")" + elseif(use) then + return "("..name.. "==".. (number or string.format("%s", Private.QuotedString(value or ""))) .. ")" + end + elseif(arg.type == "multiselect") then + if arg.multiNoSingle then + -- convert single to multi + -- this is a lazy migration because multiNoSingle is not set for all game versions + if use == true then + trigger["use_"..name] = false + trigger[name] = trigger[name] or {} + trigger[name].multi = {}; + if trigger[name].single ~= nil then + trigger[name].multi[trigger[name].single] = true; + trigger[name].single = nil + end + end + end + if(use == false) then -- multi selection + local any = false; + if (value and value.multi) then + local test = "("; + for value, positive in pairs(value.multi) do + local arg1 = tonumber(value) or ("[["..value.."]]") + local arg2 + if arg.extraOption then + arg2 = trigger[name .. "_extraOption"] or 0 + elseif arg.multiTristate then + arg2 = positive and 4 or 5 + end + local testEnabled = true + if type(arg.enableTest) == "function" then + testEnabled = arg.enableTest(trigger, arg1, arg2) + end + if testEnabled then + local check + if not arg.test then + check = name.."=="..arg1 + else + check = arg.test:format(arg1, arg2) + end + if arg.multiAll then + test = test..check.." and " + else + test = test..check.." or " + end + any = true; + end + end + if(any) then + test = test:sub(1, -6); + else + test = "(false"; + end + test = test..")" + if arg.inverse then + if type(arg.inverse) == "boolean" then + test = "not " .. test + elseif type(arg.inverse) == "function" then + if arg.inverse(trigger) then + test = "not " .. test + end + end + end + return test + end + elseif(use) then -- single selection + local value = value and value.single or nil; + if not arg.test then + return value and "("..name.."=="..(tonumber(value) or ("[["..value.."]]"))..")"; + else + return value and "("..arg.test:format(tonumber(value) or ("[["..value.."]]"))..")"; + end + end + elseif(arg.type == "toggle") then + if(use) then + if(arg.test) then + return "("..arg.test:format(value)..")"; + else + return name; + end + end + elseif (arg.type == "spell") then + if arg.showExactOption then + return "("..arg.test:format(value, tostring(use_exact) or "false") ..")"; + else + return "("..arg.test:format(value)..")"; + end + elseif(arg.test) then + return "("..arg.test:format(value)..")"; + elseif(arg.type == "longstring" and operator) then + if(operator == "==") then + return "("..name.."==[["..value.."]])"; + else + return "("..name..":"..operator:format(value)..")"; + end + elseif(arg.type == "number") then + if number then + return "("..name..(operator or "==").. number ..")"; + end + else + if(type(value) == "table") then + value = "error"; + end + return "("..name..(operator or "==")..(number or ("[["..(value or "").."]]"))..")"; + end +end + -- Used for the load function, could be simplified a bit -- It used to be also used for the generic trigger system local function ConstructFunction(prototype, trigger, skipOptional) @@ -631,128 +753,43 @@ local function ConstructFunction(prototype, trigger, skipOptional) if(enable) then if (arg.optional and skipOptional) then -- Do nothing - elseif(arg.type == "tristate" or arg.type == "toggle" or arg.type == "tristatestring" - or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) - or ((trigger["use_"..name] or arg.required) and trigger[name])) then - local number = trigger[name] and tonumber(trigger[name]); + elseif arg.type == "tristate" + or arg.type == "toggle" + or arg.type == "tristatestring" + or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) + or ((trigger["use_"..name] or arg.required) and trigger[name]) + then local test; - if(arg.type == "tristate") then - if(trigger["use_"..name] == false) then - test = "(not "..name..")"; - elseif(trigger["use_"..name]) then - if(arg.test) then - test = "("..arg.test:format(trigger[name])..")"; - else - test = name; - end - end - elseif(arg.type == "tristatestring") then - if(trigger["use_"..name] == false) then - test = "("..name.. "~=".. (number or string.format("%s", Private.QuotedString(trigger[name] or ""))) .. ")" - elseif(trigger["use_"..name]) then - test = "("..name.. "==".. (number or string.format("%s", Private.QuotedString(trigger[name] or ""))) .. ")" - end - elseif(arg.type == "multiselect") then - if arg.multiNoSingle then - -- convert single to multi - -- this is a lazy migration because multiNoSingle is not set for all game versions - if trigger["use_"..name] == true then - trigger["use_"..name] = false - trigger[name] = trigger[name] or {} - trigger[name].multi = {}; - if trigger[name].single ~= nil then - trigger[name].multi[trigger[name].single] = true; - trigger[name].single = nil - end - end - end - if(trigger["use_"..name] == false) then -- multi selection - local any = false; - if (trigger[name] and trigger[name].multi) then - test = "("; - for value, positive in pairs(trigger[name].multi) do - local arg1 = tonumber(value) or ("[["..value.."]]") - local arg2 - if arg.extraOption then - arg2 = trigger[name .. "_extraOption"] or 0 - elseif arg.multiTristate then - arg2 = positive and 4 or 5 - end - local testEnabled = true - if type(arg.enableTest) == "function" then - testEnabled = arg.enableTest(trigger, arg1, arg2) - end - if testEnabled then - local check - if not arg.test then - check = name.."=="..arg1 - else - check = arg.test:format(arg1, arg2) - end - if arg.multiAll then - test = test..check.." and " - else - test = test..check.." or " - end - any = true; - end - end - if(any) then - test = test:sub(1, -6); - else - test = "(false"; - end - test = test..")" - if arg.inverse then - if type(arg.inverse) == "boolean" then - test = "not " .. test - elseif type(arg.inverse) == "function" then - if arg.inverse(trigger) then - test = "not " .. test - end + + if arg.multiEntry then + if type(trigger[name]) == "table" and #trigger[name] > 0 then + test = "" + for i, value in ipairs(trigger[name]) do + local operator = name and type(trigger[name.."_operator"]) == "table" and trigger[name.."_operator"][i] + local use_exact = name and type(trigger["use_exact_" .. name]) == "table" and trigger["use_exact_" .. name][i] + local use = name and trigger["use_"..name] + local single = singleTest(arg, trigger, use, name, value, operator, use_exact) + if single then + if test ~= "" then + test = test .. arg.multiEntry.operator end + test = test .. single end end - elseif(trigger["use_"..name]) then -- single selection - local value = trigger[name] and trigger[name].single; - if not arg.test then - test = trigger[name] and trigger[name].single and "("..name.."=="..(tonumber(value) or ("[["..value.."]]"))..")"; + if test == "" then + test = nil else - test = trigger[name] and trigger[name].single and "("..arg.test:format(tonumber(value) or ("[["..value.."]]"))..")"; + test = "(" .. test .. ")" end end - elseif(arg.type == "toggle") then - if(trigger["use_"..name]) then - if(arg.test) then - test = "("..arg.test:format(trigger[name])..")"; - else - test = name; - end - end - elseif (arg.type == "spell") then - if arg.showExactOption then - test = "("..arg.test:format(trigger[name], tostring(trigger["use_exact_" .. name]) or "false") ..")"; - else - test = "("..arg.test:format(trigger[name])..")"; - end - elseif(arg.test) then - test = "("..arg.test:format(trigger[name])..")"; - elseif(arg.type == "longstring" and trigger[name.."_operator"]) then - if(trigger[name.."_operator"] == "==") then - test = "("..name.."==[["..trigger[name].."]])"; - else - test = "("..name..":"..trigger[name.."_operator"]:format(trigger[name])..")"; - end - elseif(arg.type == "number") then - if number then - test = "("..name..(trigger[name.."_operator"] or "==").. number ..")"; - end else - if(type(trigger[name]) == "table") then - trigger[name] = "error"; - end - test = "("..name..(trigger[name.."_operator"] or "==")..(number or ("[["..(trigger[name] or "").."]]"))..")"; + local value = trigger[name] + local operator = name and trigger[name.."_operator"] + local use_exact = name and trigger["use_exact_" .. name] + local use = name and trigger["use_"..name] + test = singleTest(arg, trigger, use, name, value, operator, use_exact) end + if (arg.preamble) then preambles = preambles .. arg.preamble:format(trigger[name]) .. "\n" end @@ -761,9 +798,9 @@ local function ConstructFunction(prototype, trigger, skipOptional) if(arg.required) then tinsert(required, test); elseif test ~= nil then - if arg.orConjunctionGroup then + if arg.orConjunctionGroup then orConjunctionGroups[arg.orConjunctionGroup ] = orConjunctionGroups[arg.orConjunctionGroup ] or {} - tinsert(orConjunctionGroups[arg.orConjunctionGroup ], "("..test..")") + tinsert(orConjunctionGroups[arg.orConjunctionGroup ], test) else tinsert(tests, test); end @@ -1645,14 +1682,14 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) local loadFunc = loadFuncs[id]; local loadOpt = loadFuncsForOptions[id]; if WeakAuras.IsClassicEra() then - shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, inEncounter, alive, vehicle, group, player, realm, class, race, faction, playerLevel, zone, zoneId, zonegroupId, encounter_id, size, raidRole, raidMemberType) - couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, inEncounter, alive, vehicle, group, player, realm, class, race, faction, playerLevel, zone, zoneId, zonegroupId, encounter_id, size, raidRole, raidMemberType) + shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, alive, inEncounter, vehicle, class, player, realm, race, faction, playerLevel, raidRole, group, raidMemberType, zone, zoneId, encounter_id, size) + couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, alive, inEncounter, vehicle, class, player, realm, race, faction, playerLevel, raidRole, group, raidMemberType, zone, zoneId, encounter_id, size) elseif WeakAuras.IsWrathClassic() then - shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, inEncounter, alive, vehicle, vehicleUi, group, player, realm, class, race, faction, playerLevel, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, role, raidRole, raidMemberType) - couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, inEncounter, alive, vehicle, vehicleUi, group, player, realm, class, race, faction, playerLevel, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, role, raidRole, raidMemberType) + shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, alive, inEncounter, vehicle, vehicleUi, class, player, realm, race, faction, playerLevel, role, raidRole, group, raidMemberType, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex) + couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, alive, inEncounter, vehicle, vehicleUi, class, player, realm, race, faction, playerLevel, role, raidRole, group, raidMemberType, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex) elseif WeakAuras.IsRetail() then - shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, inEncounter, alive, warmodeActive, inPetBattle, vehicle, vehicleUi, dragonriding, group, player, realm, specId, race, faction, playerLevel, effectiveLevel, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, role, position, raidMemberType, affixes) - couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, inEncounter, alive, warmodeActive, inPetBattle, vehicle, vehicleUi, dragonriding, group, player, realm, specId, race, faction, playerLevel, effectiveLevel, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, role, position, raidMemberType, affixes) + shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", inCombat, alive, inEncounter, warmodeActive, inPetBattle, vehicle, vehicleUi, dragonriding, specId, player, realm, race, faction, playerLevel, effectiveLevel, role, position, group, raidMemberType, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, affixes) + couldBeLoaded = loadOpt and loadOpt("ScanForLoads_Auras", inCombat, alive, inEncounter, warmodeActive, inPetBattle, vehicle, vehicleUi, dragonriding, specId, player, realm, race, faction, playerLevel, effectiveLevel, role, position, group, raidMemberType, zone, zoneId, zonegroupId, encounter_id, size, difficulty, difficultyIndex, affixes) end if(shouldBeLoaded and not loaded[id]) then @@ -5855,6 +5892,32 @@ function WeakAuras.IsAuraLoaded(id) return Private.loaded[id] end +function Private.ExecEnv.CreateSpellChecker() + local matcher = { + names = {}, + spellIds = {}, + AddName = function(self, name) + local spellId = tonumber(name) + if spellId then + name = GetSpellInfo(spellId) + if name then + self.names[name] = true + end + else + self.names[name] = true + end + end, + AddExact = function(self, spellId) + spellId = tonumber(spellId) + self.spellIds[spellId] = true + end, + Check = function(self, spellId) + return self.spellIds[spellId] or self.names[GetSpellInfo(spellId)] + end + } + return matcher +end + function Private.IconSources(data) local values = { [-1] = L["Dynamic Information"], diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasMultiLineEditBoxWithEnter.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasMultiLineEditBoxWithEnter.lua new file mode 100644 index 0000000000..70879fbe08 --- /dev/null +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasMultiLineEditBoxWithEnter.lua @@ -0,0 +1,379 @@ +if not WeakAuras.IsLibsOK() then return end + +-- based on the AceGUI widget, overwrites the enter handling +local Type, Version = "WeakAuras-MultiLineEditBoxWithEnter", 1 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end + +-- Lua APIs +local pairs = pairs + +-- WoW APIs +local GetCursorInfo, GetSpellInfo, ClearCursor = GetCursorInfo, GetSpellInfo, ClearCursor +local CreateFrame, UIParent = CreateFrame, UIParent +local _G = _G + +--[[----------------------------------------------------------------------------- +Support functions +-------------------------------------------------------------------------------]] + +if not AceGUIWeakAurasMultiLineEditBoxWithEnterInsertLink then + -- upgradeable hook + hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIWeakAurasMultiLineEditBoxWithEnterInsertLink(...) end) +end + +function _G.AceGUIWeakAurasMultiLineEditBoxWithEnterInsertLink(text) + for i = 1, AceGUI:GetWidgetCount(Type) do + local editbox = _G[("MultiLineEditBox%uEdit"):format(i)] + if editbox and editbox:IsVisible() and editbox:HasFocus() then + editbox:Insert(text) + return true + end + end +end + + +local function Layout(self) + self:SetHeight(self.numlines * 14 + (self.disablebutton and 19 or 41) + self.labelHeight) + + if self.labelHeight == 0 then + self.scrollBar:SetPoint("TOP", self.frame, "TOP", 0, -23) + else + self.scrollBar:SetPoint("TOP", self.label, "BOTTOM", 0, -19) + end + + if self.disablebutton then + self.scrollBar:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 21) + self.scrollBG:SetPoint("BOTTOMLEFT", 0, 4) + else + self.scrollBar:SetPoint("BOTTOM", self.button, "TOP", 0, 18) + self.scrollBG:SetPoint("BOTTOMLEFT", self.button, "TOPLEFT") + end +end + +--[[----------------------------------------------------------------------------- +Scripts +-------------------------------------------------------------------------------]] +local function OnEnterPressed(self) -- EditBox + self:HighlightText(0, 0) + self.obj:Fire("OnEnterPressed", self:GetText()) +end + + +local function OnClick(self) -- Button + self = self.obj + self.editBox:ClearFocus() + if not self:Fire("OnEnterPressed", self.editBox:GetText()) then + self.button:Disable() + end +end + +local function OnCursorChanged(self, _, y, _, cursorHeight) -- EditBox + self, y = self.obj.scrollFrame, -y + local offset = self:GetVerticalScroll() + if y < offset then + self:SetVerticalScroll(y) + else + y = y + cursorHeight - self:GetHeight() + if y > offset then + self:SetVerticalScroll(y) + end + end +end + +local function OnEditFocusLost(self) -- EditBox + self:HighlightText(0, 0) + self.obj:Fire("OnEditFocusLost") +end + +local function OnEnter(self) -- EditBox / ScrollFrame + self = self.obj + if not self.entered then + self.entered = true + self:Fire("OnEnter") + end +end + +local function OnLeave(self) -- EditBox / ScrollFrame + self = self.obj + if self.entered then + self.entered = nil + self:Fire("OnLeave") + end +end + +local function OnMouseUp(self) -- ScrollFrame + self = self.obj.editBox + self:SetFocus() + self:SetCursorPosition(self:GetNumLetters()) +end + +local function OnReceiveDrag(self) -- EditBox / ScrollFrame + local type, id, info = GetCursorInfo() + if type == "spell" then + info = GetSpellInfo(id, info) + elseif type ~= "item" then + return + end + ClearCursor() + self = self.obj + local editBox = self.editBox + if not editBox:HasFocus() then + editBox:SetFocus() + editBox:SetCursorPosition(editBox:GetNumLetters()) + end + editBox:Insert(info) + self.button:Enable() +end + +local function OnSizeChanged(self, width, height) -- ScrollFrame + self.obj.editBox:SetWidth(width) +end + +local function OnTextChanged(self, userInput) -- EditBox + if userInput then + self = self.obj + self:Fire("OnTextChanged", self.editBox:GetText()) + self.button:Enable() + end +end + +local function OnTextSet(self) -- EditBox + self:HighlightText(0, 0) + self:SetCursorPosition(self:GetNumLetters()) + self:SetCursorPosition(0) + self.obj.button:Disable() +end + +local function OnVerticalScroll(self, offset) -- ScrollFrame + local editBox = self.obj.editBox + editBox:SetHitRectInsets(0, 0, offset, editBox:GetHeight() - offset - self:GetHeight()) +end + +local function OnScrollRangeChanged(self, xrange, yrange) + if yrange == 0 then + self.obj.editBox:SetHitRectInsets(0, 0, 0, 0) + else + OnVerticalScroll(self, self:GetVerticalScroll()) + end +end + +local function OnShowFocus(frame) + frame.obj.editBox:SetFocus() + frame:SetScript("OnShow", nil) +end + +local function OnEditFocusGained(frame) + AceGUI:SetFocus(frame.obj) + frame.obj:Fire("OnEditFocusGained") +end + +--[[----------------------------------------------------------------------------- +Methods +-------------------------------------------------------------------------------]] +local methods = { + ["OnAcquire"] = function(self) + self.editBox:SetText("") + self:SetDisabled(false) + self:SetWidth(200) + self:DisableButton(false) + self:SetNumLines() + self.entered = nil + self:SetMaxLetters(0) + end, + + ["OnRelease"] = function(self) + self:ClearFocus() + end, + + ["SetDisabled"] = function(self, disabled) + local editBox = self.editBox + if disabled then + editBox:ClearFocus() + editBox:EnableMouse(false) + editBox:SetTextColor(0.5, 0.5, 0.5) + self.label:SetTextColor(0.5, 0.5, 0.5) + self.scrollFrame:EnableMouse(false) + self.button:Disable() + else + editBox:EnableMouse(true) + editBox:SetTextColor(1, 1, 1) + self.label:SetTextColor(1, 0.82, 0) + self.scrollFrame:EnableMouse(true) + end + end, + + ["SetLabel"] = function(self, text) + if text and text ~= "" then + self.label:SetText(text) + if self.labelHeight ~= 10 then + self.labelHeight = 10 + self.label:Show() + end + elseif self.labelHeight ~= 0 then + self.labelHeight = 0 + self.label:Hide() + end + Layout(self) + end, + + ["SetNumLines"] = function(self, value) + if not value or value < 4 then + value = 4 + end + self.numlines = value + Layout(self) + end, + + ["SetText"] = function(self, text) + self.editBox:SetText(text) + end, + + ["GetText"] = function(self) + return self.editBox:GetText() + end, + + ["SetMaxLetters"] = function (self, num) + self.editBox:SetMaxLetters(num or 0) + end, + + ["DisableButton"] = function(self, disabled) + self.disablebutton = disabled + if disabled then + self.button:Hide() + else + self.button:Show() + end + Layout(self) + end, + + ["ClearFocus"] = function(self) + self.editBox:ClearFocus() + self.frame:SetScript("OnShow", nil) + end, + + ["SetFocus"] = function(self) + self.editBox:SetFocus() + if not self.frame:IsShown() then + self.frame:SetScript("OnShow", OnShowFocus) + end + end, + + ["HighlightText"] = function(self, from, to) + self.editBox:HighlightText(from, to) + end, + + ["GetCursorPosition"] = function(self) + return self.editBox:GetCursorPosition() + end, + + ["SetCursorPosition"] = function(self, ...) + return self.editBox:SetCursorPosition(...) + end, +} + +--[[----------------------------------------------------------------------------- +Constructor +-------------------------------------------------------------------------------]] +local backdrop = { + bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], edgeSize = 16, + insets = { left = 4, right = 3, top = 4, bottom = 3 } +} + +local function Constructor() + local frame = CreateFrame("Frame", nil, UIParent) + frame:Hide() + + local widgetNum = AceGUI:GetNextWidgetNum(Type) + + local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -4) + label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -4) + label:SetJustifyH("LEFT") + label:SetText(ACCEPT) + label:SetHeight(10) + + local button = CreateFrame("Button", ("%s%dButton"):format(Type, widgetNum), frame, "UIPanelButtonTemplate") + button:SetPoint("BOTTOMLEFT", 0, 4) + button:SetHeight(22) + button:SetWidth(label:GetStringWidth() + 24) + button:SetText(ACCEPT) + button:SetScript("OnClick", OnClick) + button:Disable() + + local text = button:GetFontString() + text:ClearAllPoints() + text:SetPoint("TOPLEFT", button, "TOPLEFT", 5, -5) + text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -5, 1) + text:SetJustifyV("MIDDLE") + + local scrollBG = CreateFrame("Frame", nil, frame, "BackdropTemplate") + scrollBG:SetBackdrop(backdrop) + scrollBG:SetBackdropColor(0, 0, 0) + scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4) + + local scrollFrame = CreateFrame("ScrollFrame", ("%s%dScrollFrame"):format(Type, widgetNum), frame, "UIPanelScrollFrameTemplate") + + local scrollBar = _G[scrollFrame:GetName() .. "ScrollBar"] + scrollBar:ClearAllPoints() + scrollBar:SetPoint("TOP", label, "BOTTOM", 0, -19) + scrollBar:SetPoint("BOTTOM", button, "TOP", 0, 18) + scrollBar:SetPoint("RIGHT", frame, "RIGHT") + + scrollBG:SetPoint("TOPRIGHT", scrollBar, "TOPLEFT", 0, 19) + scrollBG:SetPoint("BOTTOMLEFT", button, "TOPLEFT") + + scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 5, -6) + scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 4) + scrollFrame:SetScript("OnEnter", OnEnter) + scrollFrame:SetScript("OnLeave", OnLeave) + scrollFrame:SetScript("OnMouseUp", OnMouseUp) + scrollFrame:SetScript("OnReceiveDrag", OnReceiveDrag) + scrollFrame:SetScript("OnSizeChanged", OnSizeChanged) + scrollFrame:HookScript("OnVerticalScroll", OnVerticalScroll) + scrollFrame:HookScript("OnScrollRangeChanged", OnScrollRangeChanged) + + local editBox = CreateFrame("EditBox", ("%s%dEdit"):format(Type, widgetNum), scrollFrame) + editBox:SetAllPoints() + editBox:SetFontObject(ChatFontNormal) + editBox:SetMultiLine(true) + editBox:EnableMouse(true) + editBox:SetAutoFocus(false) + editBox:SetCountInvisibleLetters(false) + editBox:SetScript("OnCursorChanged", OnCursorChanged) + editBox:SetScript("OnEditFocusLost", OnEditFocusLost) + editBox:SetScript("OnEnter", OnEnter) + editBox:SetScript("OnEscapePressed", editBox.ClearFocus) + editBox:SetScript("OnLeave", OnLeave) + editBox:SetScript("OnMouseDown", OnReceiveDrag) + editBox:SetScript("OnReceiveDrag", OnReceiveDrag) + editBox:SetScript("OnTextChanged", OnTextChanged) + editBox:SetScript("OnTextSet", OnTextSet) + editBox:SetScript("OnEditFocusGained", OnEditFocusGained) + editBox:SetScript("OnEnterPressed", OnEnterPressed) + + + scrollFrame:SetScrollChild(editBox) + + local widget = { + button = button, + editBox = editBox, + frame = frame, + label = label, + labelHeight = 10, + numlines = 4, + scrollBar = scrollBar, + scrollBG = scrollBG, + scrollFrame = scrollFrame, + type = Type + } + for method, func in pairs(methods) do + widget[method] = func + end + button.obj, editBox.obj, scrollFrame.obj = widget, widget, widget + + return AceGUI:RegisterAsWidget(widget) +end + +AceGUI:RegisterWidgetType(Type, Constructor, Version) diff --git a/WeakAurasOptions/LoadOptions.lua b/WeakAurasOptions/LoadOptions.lua index 4bd5d37de3..80b3568627 100644 --- a/WeakAurasOptions/LoadOptions.lua +++ b/WeakAurasOptions/LoadOptions.lua @@ -70,6 +70,52 @@ local function CorrectItemName(input) end -- Also used by the GenericTrigger + +local function getValue(trigger, preCheckField, field, multiEntry, entryNumber, tristate) + if preCheckField then + if tristate then + if trigger[preCheckField] ~= nil then + return nil + end + else + if not trigger[preCheckField] then + return nil + end + end + end + if multiEntry then + return type(trigger[field]) == "table" and trigger[field][entryNumber] or nil + else + return trigger[field] or nil + end +end + +local function shiftTable(tbl, pos) + local size = #tbl + for i = pos, size, 1 do + tbl[i] = tbl[i + 1] + end +end + +local function setValue(trigger, field, value, multiEntry, entryNumber) + if multiEntry then + if type(trigger[field]) ~= "table" then + if trigger[field] == nil then + trigger[field] = {} + else + trigger[field] = { trigger[field] } + end + end + if value == "" then + shiftTable(trigger[field], entryNumber) + else + trigger[field][entryNumber] = value + end + else + trigger[field] = value + end +end + function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum, triggertype) local trigger -- For load options only the hidden property counts, but for the generic trigger @@ -115,7 +161,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end local name = arg.name; local validate = arg.validate; - local reloadOptions = arg.reloadOptions; + local reloadOptions = arg.reloadOptions or arg.multiEntry ~= nil if (name and arg.type == "collapse") then options["summary_" .. arg.name] = { type = "execute", @@ -187,9 +233,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end end WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.ClearAndUpdateOptions(data.id) OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); OptionsPrivate.SortDisplayButtons(nil, true); @@ -254,9 +298,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end end WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.ClearAndUpdateOptions(data.id) OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); OptionsPrivate.SortDisplayButtons(nil, true); @@ -301,9 +343,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum set = function(info, v) trigger["use_"..realname] = v; WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.ClearAndUpdateOptions(data.id) OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); OptionsPrivate.SortDisplayButtons(nil, true); @@ -332,566 +372,606 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end end order = order + 1; - if(arg.type == "number") then - if (not arg.noOperator) then - options[name.."_operator"] = { - type = "select", - width = WeakAuras.halfWidth, - name = L["Operator"], - order = order, - hidden = hidden, - values = arg.operator_types == "without_equal" and OptionsPrivate.Private.operator_types_without_equal or OptionsPrivate.Private.operator_types, - disabled = function() return not trigger["use_"..realname]; end, - get = function() return trigger["use_"..realname] and trigger[realname.."_operator"] or nil; end, - set = function(info, v) - trigger[realname.."_operator"] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end - }; - if(arg.required and not triggertype) then - options[name.."_operator"].set = function(info, v) - trigger[realname.."_operator"] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - OptionsPrivate.SortDisplayButtons(nil, true); - end - end - order = order + 1; + + local countEntries = 0 + local multiEntry = arg.multiEntry ~= nil + if multiEntry then + if type(trigger[realname]) == "table" then + countEntries = #trigger[realname] + elseif trigger[realname] ~= nil then + countEntries = 1 end - options[name] = { - type = "input", - width = arg.noOperator and WeakAuras.normalWidth or WeakAuras.halfWidth, - validate = ValidateNumeric, - name = arg.display, - order = order, - hidden = hidden, - desc = arg.desc, - disabled = function() return not trigger["use_"..realname]; end, - get = function() return trigger["use_"..realname] and trigger[realname] or nil; end, - set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); + end + + for entryNumber = 1, countEntries + 1 do + if arg.multiEntry then + if arg.multiEntry.limit and entryNumber > arg.multiEntry.limit then + break end - }; - if(arg.required and not triggertype) then - options[name].set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) + if entryNumber > 1 then + if arg.type == "tristate" or arg.type == "tristatestring" then + if trigger["use_"..realname] == nil then + break + end + else + if not trigger["use_"..realname] then + break + end end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - OptionsPrivate.SortDisplayButtons(nil, true); end end - order = order + 1; - elseif(arg.type == "string" or arg.type == "tristatestring") then - options[name] = { - type = "input", - width = WeakAuras.normalWidth, - name = arg.display, - order = order, - hidden = hidden, - validate = validate, - desc = arg.desc, - set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end - }; - if arg.type == "string" then - options[name].disabled = function() return not trigger["use_"..realname] end - options[name].get = function() return trigger["use_"..realname] and trigger[realname] or nil; end - else - options[name].disabled = function() return trigger["use_"..realname] == nil end - options[name].get = function() return trigger["use_"..realname] ~= nil and trigger[realname] or nil; end + local suffix = multiEntry and entryNumber or "" + if entryNumber > 1 then + options["spacer_"..name..suffix] = { + type = "execute", + name = arg.multiEntry.operator == "and" and L["and"] or L["or"], + image = function() return "", 0, 0 end, + order = order, + hidden = hidden, + } + order = order + 1 end - if(arg.required and not triggertype) then - options[name].set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - OptionsPrivate.SortDisplayButtons(nil, true); + if(arg.type == "number") then + if entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth end - end - order = order + 1; - elseif(arg.type == "longstring") then - options[name.."_operator"] = { - type = "select", - width = WeakAuras.normalWidth, - name = L["Operator"], - order = order, - hidden = hidden, - values = OptionsPrivate.Private.string_operator_types, - disabled = function() return not trigger["use_"..realname]; end, - get = function() return trigger["use_"..realname] and trigger[realname.."_operator"] or nil; end, - set = function(info, v) - trigger[realname.."_operator"] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) + local disabled = not trigger["use_"..realname] + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1 + else + if (not arg.noOperator) then + options[name.."_operator"..suffix] = { + type = "select", + width = WeakAuras.halfWidth, + name = L["Operator"], + order = order, + hidden = hidden, + values = arg.operator_types == "without_equal" and OptionsPrivate.Private.operator_types_without_equal or OptionsPrivate.Private.operator_types, + + get = function() + return getValue(trigger, "use_"..realname, realname.."_operator", multiEntry, entryNumber) + end, + set = function(info, v) + setValue(trigger, realname.."_operator", v, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; + order = order + 1; end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); + options[name..suffix] = { + type = "input", + width = arg.noOperator and WeakAuras.normalWidth or WeakAuras.halfWidth, + validate = ValidateNumeric, + name = arg.display, + order = order, + hidden = hidden, + desc = arg.desc, + get = function() return getValue(trigger, "use_"..realname, realname, multiEntry, entryNumber) end, + set = function(info, v) + setValue(trigger, realname, v, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; + order = order + 1; end - }; - if(arg.required and not triggertype) then - options[name.."_operator"].set = function(info, v) - trigger[realname.."_operator"] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - OptionsPrivate.SortDisplayButtons(nil, true); + elseif(arg.type == "string" or arg.type == "tristatestring") then + if not arg.multiline and entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth end - end - order = order + 1; - options[name] = { - type = "input", - width = WeakAuras.doubleWidth, - name = arg.display, - order = order, - hidden = hidden, - validate = validate, - disabled = function() return not trigger["use_"..realname]; end, - get = function() return trigger["use_"..realname] and trigger[realname] or nil; end, - set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); + + local disabled + if arg.type == "string" then + disabled = not trigger["use_"..realname] + else + disabled = trigger["use_"..realname] == nil end - }; - if(arg.required and not triggertype) then - options[name].set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - OptionsPrivate.SortDisplayButtons(nil, true); + + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1 + else + options[name..suffix] = { + type = "input", + width = arg.multiline and WeakAuras.doubleWidth or WeakAuras.normalWidth, + name = arg.display, + order = order, + hidden = hidden, + validate = validate, + desc = arg.desc, + multiline = arg.multiline, + control = arg.multiline and "WeakAuras-MultiLineEditBoxWithEnter" or nil, + get = function() + return getValue(trigger, "use_"..realname, realname, multiEntry, entryNumber, arg.type == "tristatestring") + end, + set = function(info, v) + setValue(trigger, realname, v, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; + order = order + 1 end - end - order = order + 1; - elseif(arg.type == "spell" or arg.type == "aura" or arg.type == "item") then - if (arg.showExactOption) then - options["exact"..name] = { - type = "toggle", - width = WeakAuras.normalWidth, - name = arg.type == "item" and L["Exact Item Match"] or L["Exact Spell Match"], - order = order, - hidden = hidden, - get = function() - return trigger["use_exact_"..realname]; - end, - set = function(info, v) - trigger["use_exact_"..realname] = v; - WeakAuras.Add(data); - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end, - }; - order = order + 1; - end - options["icon"..name] = { - type = "execute", - width = 0.2, - name = "", - order = order, - hidden = hidden, - image = function() - if(trigger["use_"..realname] and trigger[realname]) then - if(arg.type == "aura") then - local icon = spellCache.GetIcon(trigger[realname]); - return icon and tostring(icon) or "", 18, 18; - elseif(arg.type == "spell") then - if arg.negativeIsEJ and WeakAuras.IsRetail() then - local key = WeakAuras.SafeToNumber(trigger[realname]) - if key and key < 0 then - local tbl = C_EncounterJournal.GetSectionInfo(-key) - if tbl and tbl.abilityIcon then - return tostring(tbl.abilityIcon) or "", 18, 18; - end - end + elseif(arg.type == "longstring") then + if entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth + end + local disabled = not trigger["use_"..realname] + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1; + else + options[name.."_operator"..suffix] = { + type = "select", + width = WeakAuras.normalWidth, + name = L["Operator"], + order = order, + hidden = hidden, + values = OptionsPrivate.Private.string_operator_types, + get = function() return getValue(trigger, "use_"..realname, realname.."_operator", multiEntry, entryNumber) end, + set = function(info, v) + setValue(trigger, realname.."_operator", v, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) end - local _, _, icon = GetSpellInfo(trigger[realname]); - return icon and tostring(icon) or "", 18, 18; - elseif(arg.type == "item") then - local _, _, _, _, _, _, _, _, _, icon = GetItemInfo(trigger[realname]); - return icon and tostring(icon) or "", 18, 18; + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); end - else - return "", 18, 18; + }; + order = order + 1; + options[name..suffix] = { + type = "input", + width = WeakAuras.doubleWidth, + name = arg.display, + order = order, + hidden = hidden, + validate = validate, + get = function() return getValue(trigger, "use_"..realname, realname, multiEntry, entryNumber) end, + set = function(info, v) + setValue(trigger, realname, v, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; + order = order + 1; + end + elseif(arg.type == "spell" or arg.type == "aura" or arg.type == "item") then + if entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth - (arg.showExactOption and 0 or 0.2) + end + local disabled = not trigger["use_"..realname] + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1 + else + if (arg.showExactOption) then + options["exact"..name..suffix] = { + type = "toggle", + width = WeakAuras.normalWidth, + name = arg.type == "item" and L["Exact Item Match"] or L["Exact Spell Match"], + order = order, + hidden = hidden, + get = function() + return getValue(trigger, nil, "use_exact_"..realname, multiEntry, entryNumber) + end, + set = function(info, v) + setValue(trigger, "use_exact_"..realname, v, multiEntry, entryNumber) + WeakAuras.Add(data); + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end, + }; + order = order + 1; end - end, - disabled = function() return not ((arg.type == "aura" and trigger[realname] and spellCache.GetIcon(trigger[realname])) or (arg.type == "spell" and trigger[realname] and GetSpellInfo(trigger[realname])) or (arg.type == "item" and trigger[realname] and GetItemIcon(trigger[realname]))) end - }; - order = order + 1; - options[name] = { - type = "input", - width = (arg.showExactOption and WeakAuras.doubleWidth or WeakAuras.normalWidth) - (arg.showExactOption and 0.2 or 0), - name = arg.display, - order = order, - hidden = hidden, - validate = validate, - disabled = function() return not trigger["use_"..realname]; end, - get = function() - if(arg.type == "item") then - local useExactSpellId = (arg.showExactOption and trigger["use_exact_"..realname]) - if(trigger["use_"..realname] and trigger[realname] and trigger[realname] ~= "") then - if useExactSpellId then - local itemId = tonumber(trigger[realname]) - if itemId and itemId ~= 0 then - return tostring(trigger[realname]) + options["icon"..name..suffix] = { + type = "execute", + width = 0.2, + name = "", + order = order, + hidden = hidden, + image = function() + local value = getValue(trigger, "use_"..realname, realname, multiEntry, entryNumber) + if value then + if(arg.type == "aura") then + local icon = spellCache.GetIcon(value); + return icon and tostring(icon) or "", 18, 18; + elseif(arg.type == "spell") then + if arg.negativeIsEJ and WeakAuras.IsRetail() then + local key = WeakAuras.SafeToNumber(value) + if key and key < 0 then + local tbl = C_EncounterJournal.GetSectionInfo(-key) + if tbl and tbl.abilityIcon then + return tostring(tbl.abilityIcon) or "", 18, 18; + end + end + end + local _, _, icon = GetSpellInfo(value); + return icon and tostring(icon) or "", 18, 18; + elseif(arg.type == "item") then + local _, _, _, _, _, _, _, _, _, icon = GetItemInfo(value); + return icon and tostring(icon) or "", 18, 18; end else - local name = GetItemInfo(trigger[realname]); - if(name) then - return name; - end + return "", 18, 18; end - return (useExactSpellId and L["Invalid Item ID"] or L["Invalid Item Name/ID/Link"]) .. "\0" - else - return nil; + end, + disabled = function() + local value = getValue(trigger, nil, realname, multiEntry, entryNumber) + return not ((arg.type == "aura" and value and spellCache.GetIcon(value)) or (arg.type == "spell" and value and GetSpellInfo(value)) or (arg.type == "item" and value and GetItemIcon(value))) end - elseif(arg.type == "spell") then - local useExactSpellId = (arg.showExactOption and trigger["use_exact_"..realname]) - if(trigger["use_"..realname]) then - if (trigger[realname] and trigger[realname] ~= "") then - local spellID = WeakAuras.SafeToNumber(trigger[realname]) - if spellID then - if arg.negativeIsEJ and WeakAuras.IsRetail() and spellID < 0 then - local tbl = C_EncounterJournal.GetSectionInfo(-spellID) - if tbl and tbl.title then - return ("%s (%s)"):format(spellID, tbl.title) .. "\0" .. value + }; + order = order + 1; + options[name..suffix] = { + type = "input", + width = (arg.showExactOption and WeakAuras.doubleWidth or WeakAuras.normalWidth) - (arg.showExactOption and 0.2 or 0), + name = arg.display, + order = order, + hidden = hidden, + validate = validate, + get = function() + local value = getValue(trigger, "use_"..realname, realname, multiEntry, entryNumber) + if(arg.type == "item") then + local useExactSpellId = (arg.showExactOption and getValue(trigger, nil, "use_exact_"..realname, multiEntry, entryNumber)) + if value and value ~= "" then + if useExactSpellId then + local itemId = tonumber(value) + if itemId and itemId ~= 0 then + return tostring(value) + end + else + local name = GetItemInfo(value); + if name then + return name; end end - local spellName = GetSpellInfo(WeakAuras.SafeToNumber(trigger[realname])) - if spellName then - return ("%s (%s)"):format(spellID, spellName) .. "\0" .. value - end - elseif not useExactSpellId then - local spellName = GetSpellInfo(trigger[realname]) - if spellName then - return spellName + return (useExactSpellId and L["Invalid Item ID"] or L["Invalid Item Name/ID/Link"]) .. "\0" + else + return nil; + end + elseif(arg.type == "spell") then + local useExactSpellId = (arg.showExactOption and getValue(trigger, nil, "use_exact_"..realname, multiEntry, entryNumber)) + if value and value ~= "" then + local spellID = WeakAuras.SafeToNumber(value) + if spellID then + if arg.negativeIsEJ and WeakAuras.IsRetail() and spellID < 0 then + local tbl = C_EncounterJournal.GetSectionInfo(-spellID) + if tbl and tbl.title then + return ("%s (%s)"):format(spellID, tbl.title) .. "\0" .. value + end + end + local spellName = GetSpellInfo(WeakAuras.SafeToNumber(value)) + if spellName then + return ("%s (%s)"):format(spellID, spellName) .. "\0" .. value + end + elseif not useExactSpellId then + local spellName = GetSpellInfo(value) + if spellName then + return spellName + end end end + if arg.noValidation then + return value + end + if value == nil then + return nil + end + return (useExactSpellId and L["Invalid Spell ID"] or L["Invalid Spell Name/ID/Link"]) .. "\0" + else + return value or nil end - if arg.noValidation then - return trigger[realname] + end, + set = function(info, v) + local fixedInput = v; + if not arg.noValidation then + if(arg.type == "aura") then + fixedInput = WeakAuras.spellCache.CorrectAuraName(v); + elseif(arg.type == "spell") then + fixedInput = CorrectSpellName(v); + elseif(arg.type == "item") then + fixedInput = CorrectItemName(v); + end end - return (useExactSpellId and L["Invalid Spell ID"] or L["Invalid Spell Name/ID/Link"]) .. "\0" - else - return nil; - end + setValue(trigger, realname, fixedInput, multiEntry, entryNumber) + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end, + control = "WeakAurasInputFocus", + }; + order = order + 1; + end + elseif(arg.type == "select" or arg.type == "unit") then + if entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth + end + + local disabled = not trigger["use_"..realname] + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1 + else + local values; + if(type(arg.values) == "function") then + values = arg.values(trigger); else - return trigger["use_"..realname] and trigger[realname] or nil; - end - end, - set = function(info, v) - local fixedInput = v; - if not arg.noValidation then - if(arg.type == "aura") then - fixedInput = WeakAuras.spellCache.CorrectAuraName(v); - elseif(arg.type == "spell") then - fixedInput = CorrectSpellName(v); - elseif(arg.type == "item") then - fixedInput = CorrectItemName(v); + if OptionsPrivate.Private[arg.values] then + values = OptionsPrivate.Private[arg.values] + else + values = WeakAuras[arg.values]; end end - trigger[realname] = fixedInput; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end, - control = "WeakAurasInputFocus", - }; - order = order + 1; - elseif(arg.type == "select" or arg.type == "unit") then - local values; - if(type(arg.values) == "function") then - values = arg.values(trigger); - else - if OptionsPrivate.Private[arg.values] then - values = OptionsPrivate.Private[arg.values] - else - values = WeakAuras[arg.values]; - end - end - local sortOrder = arg.sorted and OptionsPrivate.Private.SortOrderForValues(values) or nil - options[name] = { - type = "select", - width = WeakAuras.normalWidth, - name = arg.display, - order = order, - hidden = hidden, - values = values, - sorting = sortOrder, - desc = arg.desc, - disabled = function() return not trigger["use_"..realname]; end, - get = function() - if(arg.type == "unit" and trigger["use_specific_"..realname]) then - return "member"; - end + local sortOrder = arg.sorted and OptionsPrivate.Private.SortOrderForValues(values) or nil + options[name..suffix] = { + type = "select", + width = WeakAuras.normalWidth, + name = arg.display, + order = order, + hidden = hidden, + values = values, + sorting = sortOrder, + desc = arg.desc, - if (not trigger["use_"..realname]) then - return nil; - end + get = function() + if(arg.type == "unit" and trigger["use_specific_"..realname]) then + return "member"; + end - if (arg.default and (not trigger[realname] or not values[trigger[realname]])) then - trigger[realname] = arg.default; - return arg.default; - end + if (not trigger["use_"..realname]) then + return nil; + end - return trigger[realname] or nil; - end, - set = function(info, v) - trigger[realname] = v; - if(arg.type == "unit" and v == "member") then - trigger["use_specific_"..realname] = true; - trigger[realname] = UnitName("player"); - else - trigger["use_specific_"..realname] = nil; + if (arg.default and (not trigger[realname] or not values[trigger[realname]])) then + trigger[realname] = arg.default; + return arg.default; + end + + return trigger[realname] or nil; + end, + set = function(info, v) + setValue(trigger, realname, v, multiEntry, entryNumber) + if(arg.type == "unit" and v == "member") then + trigger["use_specific_"..realname] = true; + trigger[realname] = UnitName("player"); + else + trigger["use_specific_"..realname] = nil; + end + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; + if (arg.control) then + options[name .. suffix].control = arg.control; end - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) + order = order + 1; + if(arg.type == "unit") then + options["use_specific_"..name..suffix] = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Specific Unit"], + order = order, + hidden = function() + return (not trigger["use_specific_"..realname] and trigger[realname] ~= "member") + or (type(hidden) == "function" and hidden(trigger)) + or (type(hidden) ~= "function" and hidden) + end, + get = function() return true end, + set = function(info, v) + trigger["use_specific_"..realname] = nil; + options[name .. suffix].set(info, "player"); + WeakAuras.Add(data) + end + } + order = order + 1; + options["specific_"..name..suffix] = { + type = "input", + width = WeakAuras.normalWidth, + name = L["Specific Unit"], + desc = L["Can be a UID (e.g., party1)."], + order = order, + hidden = function() return (not trigger["use_specific_"..realname] and trigger[realname] ~= "member") or (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) end, + get = function() return trigger[realname] end, + set = function(info, v) + trigger[realname] = v; + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + end + }; + order = order + 1; end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); end - }; - if(arg.required and not triggertype) then - options[name].set = function(info, v) - trigger[realname] = v; - if(arg.type == "unit" and v == "member") then - trigger["use_specific_"..realname] = true; - else - trigger["use_specific_"..realname] = nil; - end - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); + elseif(arg.type == "multiselect") then + if entryNumber > 1 then + options["spacer_"..name..suffix].width = WeakAuras.normalWidth end - end - if (arg.control) then - options[name].control = arg.control; - end - order = order + 1; - if(arg.type == "unit") then - options["use_specific_"..name] = { - type = "toggle", - width = WeakAuras.normalWidth, - name = L["Specific Unit"], - order = order, - hidden = function() return (not trigger["use_specific_"..realname] and trigger[realname] ~= "member") or (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) end, - get = function() return true end, - set = function(info, v) - trigger["use_specific_"..realname] = nil; - options[name].set(info, "player"); - WeakAuras.Add(data) - end - } - order = order + 1; - options["specific_"..name] = { - type = "input", - width = WeakAuras.normalWidth, - name = L["Specific Unit"], - desc = L["Can be a UID (e.g., party1)."], - order = order, - hidden = function() return (not trigger["use_specific_"..realname] and trigger[realname] ~= "member") or (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) end, - get = function() return trigger[realname] end, - set = function(info, v) - trigger[realname] = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - end - }; - order = order + 1; - end - elseif(arg.type == "multiselect") then - local values; - if(type(arg.values) == "function") then - values = arg.values(trigger); - else - if OptionsPrivate.Private[arg.values] then - values = OptionsPrivate.Private[arg.values] + local disabled = trigger["use_"..realname] == nil + if disabled then + options[name..suffix] = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + hidden = hidden, + } + order = order + 1 else - values = WeakAuras[arg.values]; - end - end - local sortOrder = arg.sorted and OptionsPrivate.Private.SortOrderForValues(values) or nil - options[name] = { - type = "select", - width = WeakAuras.normalWidth, - name = arg.display, - order = order, - values = values, - sorting = sortOrder, - control = arg.control, - hidden = function() - return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] == false; - end, - disabled = function() return not trigger["use_"..realname]; end, - get = function() return trigger["use_"..realname] and trigger[realname] and trigger[realname].single or nil; end, - set = function(info, v) - trigger[realname] = trigger[realname] or {}; - trigger[realname].single = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end - }; - if(arg.required and not triggertype) then - options[name].set = function(info, v) - trigger[realname].single = v; - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) + local values; + if(type(arg.values) == "function") then + values = arg.values(trigger); + else + if OptionsPrivate.Private[arg.values] then + values = OptionsPrivate.Private[arg.values] + else + values = WeakAuras[arg.values]; + end end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end - end + local sortOrder = arg.sorted and OptionsPrivate.Private.SortOrderForValues(values) or nil + options[name..suffix] = { + type = "select", + width = WeakAuras.normalWidth, + name = arg.display, + order = order, + values = values, + sorting = sortOrder, + control = arg.control, + hidden = function() + return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] == false; + end, + get = function() return trigger["use_"..realname] and trigger[realname] and trigger[realname].single or nil; end, + set = function(info, v) + trigger[realname] = trigger[realname] or {}; + trigger[realname].single = v; + WeakAuras.Add(data); + if (reloadOptions) then + WeakAuras.ClearAndUpdateOptions(data.id) + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end + }; - if arg.extraOption then - options["multiselect_extraOption_" .. name] = - { - name = arg.extraOption.display, - type = "select", - values = arg.extraOption.values, - order = order, - width = WeakAuras.normalWidth, - hidden = function() return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] ~= false; end, - get = function(info, v) - return trigger[realname .. "_extraOption"] or 0 - end, - set = function(info, v) - trigger[realname .. "_extraOption"] = v - WeakAuras.Add(data) - OptionsPrivate.Private.ScanForLoads({[data.id] = true}) - OptionsPrivate.SortDisplayButtons(nil, true) + if arg.extraOption then + options["multiselect_extraOption_" .. name..suffix] = + { + name = arg.extraOption.display, + type = "select", + values = arg.extraOption.values, + order = order, + width = WeakAuras.normalWidth, + hidden = function() return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] ~= false; end, + get = function(info, v) + return trigger[realname .. "_extraOption"] or 0 + end, + set = function(info, v) + trigger[realname .. "_extraOption"] = v + WeakAuras.Add(data) + OptionsPrivate.Private.ScanForLoads({[data.id] = true}) + OptionsPrivate.SortDisplayButtons(nil, true) + end + } + order = order + 1 end - } - order = order + 1 - end - options["multiselect_"..name] = { - type = "multiselect", - name = arg.display, - width = WeakAuras.doubleWidth, - order = order, - hidden = function() return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] ~= false; end, - values = values, - control = arg.multiUseControlWhenFalse and arg.control, - multiTristate = arg.multiTristate, - get = function(info, v) - if(trigger["use_"..realname] == false and trigger[realname] and trigger[realname].multi) then - if arg.multiConvertKey then - v = arg.multiConvertKey(trigger, v) - end - if v then - return trigger[realname].multi[v]; - end - end - end, - set = function(info, v, calledFromSetAll) - if arg.multiConvertKey then - v = arg.multiConvertKey(trigger, v) - end - if v then - trigger[realname].multi = trigger[realname].multi or {}; - if (calledFromSetAll or arg.multiTristate) then - trigger[realname].multi[v] = calledFromSetAll; - elseif(trigger[realname].multi[v]) then - trigger[realname].multi[v] = nil; - else - trigger[realname].multi[v] = true; - end - WeakAuras.Add(data); - if (reloadOptions) then - -- Hack specifally for dragon flight mini talent - -- That widget needs to be informed before and - -- after a reload - OptionsPrivate.Private.callbacks:Fire("BeforeReload") - WeakAuras.ClearAndUpdateOptions(data.id) - WeakAuras.FillOptions() - OptionsPrivate.Private.callbacks:Fire("AfterReload") + options["multiselect_"..name..suffix] = { + type = "multiselect", + name = arg.display, + width = WeakAuras.doubleWidth, + order = order, + hidden = function() return (type(hidden) == "function" and hidden(trigger)) or (type(hidden) ~= "function" and hidden) or trigger["use_"..realname] ~= false; end, + values = values, + control = arg.multiUseControlWhenFalse and arg.control, + multiTristate = arg.multiTristate, + get = function(info, v) + if(trigger["use_"..realname] == false and trigger[realname] and trigger[realname].multi) then + if arg.multiConvertKey then + v = arg.multiConvertKey(trigger, v) + end + if v then + return trigger[realname].multi[v]; + end + end + end, + set = function(info, v, calledFromSetAll) + if arg.multiConvertKey then + v = arg.multiConvertKey(trigger, v) + end + if v then + trigger[realname].multi = trigger[realname].multi or {}; + if (calledFromSetAll or arg.multiTristate) then + trigger[realname].multi[v] = calledFromSetAll; + elseif(trigger[realname].multi[v]) then + trigger[realname].multi[v] = nil; + else + trigger[realname].multi[v] = true; + end + WeakAuras.Add(data); + if (reloadOptions) then + -- Hack specifally for dragon flight mini talent + -- That widget needs to be informed before and + -- after a reload + OptionsPrivate.Private.callbacks:Fire("BeforeReload") + WeakAuras.ClearAndUpdateOptions(data.id) + WeakAuras.FillOptions() + OptionsPrivate.Private.callbacks:Fire("AfterReload") + end + OptionsPrivate.Private.ScanForLoads({[data.id] = true}); + WeakAuras.UpdateThumbnail(data); + OptionsPrivate.SortDisplayButtons(nil, true); + end end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); - end - end - }; - if(arg.required and not triggertype) then - options[name].set = function(info, v) - if(trigger[realname].multi[v]) then - trigger[realname].multi[v] = nil; - else - trigger[realname].multi[v] = true; - end - WeakAuras.Add(data); - if (reloadOptions) then - WeakAuras.ClearAndUpdateOptions(data.id) - end - OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.UpdateThumbnail(data); - OptionsPrivate.SortDisplayButtons(nil, true); + }; + order = order + 1; end end - - order = order + 1; end end @@ -913,25 +993,37 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum set = function(info, v) trigger.use_count = v WeakAuras.Add(data) + WeakAuras.ClearAndUpdateOptions(data.id) end }; order = order + 1; - options.count = { - type = "input", - width = WeakAuras.normalWidth, - name = L["Count"], - desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3"], - order = order, - disabled = function() return not trigger.use_count end, - get = function() - return trigger.count - end, - set = function(info, v) - trigger.count = v - WeakAuras.Add(data) - end - }; - order = order + 1; + + local disabled = not trigger.use_count + if disabled then + options.count = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + } + order = order + 1 + else + options.count = { + type = "input", + width = WeakAuras.normalWidth, + name = L["Count"], + desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3"], + order = order, + get = function() + return trigger.count + end, + set = function(info, v) + trigger.count = v + WeakAuras.Add(data) + end + }; + order = order + 1; + end end if prototype.delayEvents then options.use_delay = { @@ -945,25 +1037,38 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum set = function(info, v) trigger.use_delay = v WeakAuras.Add(data) + WeakAuras.ClearAndUpdateOptions(data.id) end }; order = order + 1; - options.delay = { - type = "input", - width = WeakAuras.normalWidth, - name = L["Delay"], - order = order, - disabled = function() return not trigger.use_delay end, - validate = WeakAuras.ValidateTime, - get = function() - return OptionsPrivate.Private.tinySecondFormat(trigger.delay) - end, - set = function(info, v) - trigger.delay = WeakAuras.TimeToSeconds(v) - WeakAuras.Add(data) - end - }; - order = order + 1; + + local disabled = not trigger.use_delay + if disabled then + options.delay = { + type = "description", + name = "", + width = WeakAuras.normalWidth, + order = order, + } + order = order + 1 + else + options.delay = { + type = "input", + width = WeakAuras.normalWidth, + name = L["Delay"], + order = order, + + validate = WeakAuras.ValidateTime, + get = function() + return OptionsPrivate.Private.tinySecondFormat(trigger.delay) + end, + set = function(info, v) + trigger.delay = WeakAuras.TimeToSeconds(v) + WeakAuras.Add(data) + end + }; + order = order + 1; + end end if prototype.timedrequired then options.unevent = { diff --git a/WeakAurasOptions/WeakAurasOptions.toc b/WeakAurasOptions/WeakAurasOptions.toc index 5947894f35..9132d6a2c6 100644 --- a/WeakAurasOptions/WeakAurasOptions.toc +++ b/WeakAurasOptions/WeakAurasOptions.toc @@ -82,6 +82,7 @@ AceGUI-Widgets\AceGUIWidget-WeakAurasPendingUpdateButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasTextureButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasIconButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasMultiLineEditBox.lua +AceGUI-Widgets\AceGUIWidget-WeakAurasMultiLineEditBoxWithEnter.lua AceGUI-Widgets\AceGUIWidget-WeakAurasNewButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasImportButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasToolbarButton.lua