flickerstreak@109: --[[ flickerstreak@109: ReAction bar state driver interface flickerstreak@109: flickerstreak@109: --]] flickerstreak@109: flickerstreak@109: -- local imports flickerstreak@175: local addonName, addonTable = ... flickerstreak@175: local ReAction = addonTable.ReAction flickerstreak@109: local L = ReAction.L flickerstreak@109: local _G = _G flickerstreak@109: local format = string.format flickerstreak@109: local InCombatLockdown = InCombatLockdown flickerstreak@109: local RegisterStateDriver = RegisterStateDriver flickerstreak@109: flickerstreak@109: -- module declaration flickerstreak@109: local moduleID = "State" flickerstreak@109: local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) flickerstreak@109: flickerstreak@109: -- Utility -- flickerstreak@109: flickerstreak@109: -- traverse a table tree by key list and fetch the result or first nil flickerstreak@109: local function tfetch(t, ...) flickerstreak@109: for i = 1, select('#', ...) do flickerstreak@109: t = t and t[select(i, ...)] flickerstreak@109: end flickerstreak@109: return t flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- traverse a table tree by key list and build tree as necessary flickerstreak@109: local function tbuild(t, ...) flickerstreak@109: for i = 1, select('#', ...) do flickerstreak@109: local key = select(i, ...) flickerstreak@109: if not t[key] then t[key] = { } end flickerstreak@109: t = t[key] flickerstreak@109: end flickerstreak@109: return t flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- return a new array of keys of table 't', sorted by comparing flickerstreak@109: -- sub-fields (obtained via tfetch) of the table values flickerstreak@109: local function fieldsort( t, ... ) flickerstreak@109: local r = { } flickerstreak@109: for k in pairs(t) do flickerstreak@109: table.insert(r,k) flickerstreak@109: end flickerstreak@109: local path = { ... } flickerstreak@109: table.sort(r, function(lhs, rhs) flickerstreak@109: local olhs = tfetch(t[lhs], unpack(path)) or 0 flickerstreak@109: local orhs = tfetch(t[rhs], unpack(path)) or 0 flickerstreak@109: return olhs < orhs flickerstreak@109: end) flickerstreak@109: return r flickerstreak@109: end flickerstreak@109: flickerstreak@109: flickerstreak@157: local ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty flickerstreak@109: flickerstreak@109: -- PRIVATE -- flickerstreak@109: do flickerstreak@109: function GetProperty( bar, state, propname ) flickerstreak@109: return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function SetProperty( bar, state, propname, value ) flickerstreak@109: local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) flickerstreak@109: s[propname] = value flickerstreak@155: bar:SetSecureStateData(state, propname, value) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function RegisterProperty( propname, snippet ) flickerstreak@109: for _, bar in ReAction:IterateBars() do flickerstreak@155: if type(snippet) == "string" then flickerstreak@155: bar:SetSecureStateExtension(propname,snippet) flickerstreak@109: end flickerstreak@157: ApplyStates(bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function UnregisterProperty( propname ) flickerstreak@109: for _, bar in ReAction:IterateBars() do flickerstreak@155: bar:SetSecureStateExtension(propname,nil) flickerstreak@157: ApplyStates(bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function ApplyStates( bar ) flickerstreak@109: local states = tfetch(module.db.profile.bars, bar:GetName(), "states") flickerstreak@109: if states then flickerstreak@157: bar:SetStateDriver(states) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function CleanupStates( bar ) flickerstreak@155: bar:SetStateDriver(nil) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: flickerstreak@109: flickerstreak@109: -- module event handlers -- flickerstreak@109: flickerstreak@109: function module:OnInitialize() flickerstreak@109: self.db = ReAction.db:RegisterNamespace( moduleID, flickerstreak@109: { flickerstreak@109: profile = { flickerstreak@109: bars = { }, flickerstreak@109: } flickerstreak@109: } flickerstreak@109: ) flickerstreak@109: flickerstreak@109: self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS") flickerstreak@109: flickerstreak@109: ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") flickerstreak@109: flickerstreak@109: ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") flickerstreak@109: ReAction.RegisterCallback(self, "OnDestroyBar") flickerstreak@109: ReAction.RegisterCallback(self, "OnRefreshBar") flickerstreak@109: ReAction.RegisterCallback(self, "OnEraseBar") flickerstreak@109: ReAction.RegisterCallback(self, "OnRenameBar") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:OnEnable() flickerstreak@109: self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:UPDATE_SHAPESHIFT_FORMS() flickerstreak@109: -- Re-parse the rules table according to the new form list. flickerstreak@109: -- This happens both at initial login (after PLAYER_ENTERING_WORLD) flickerstreak@109: -- as well as when gaining new abilities. flickerstreak@157: ReAction.Bar.InitRuleFormats() flickerstreak@155: for _, bar in ReAction:IterateBars() do flickerstreak@155: ApplyStates(bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:OnRefreshBar(event, bar, name) flickerstreak@109: local c = self.db.profile.bars[name] flickerstreak@109: if c then flickerstreak@109: ApplyStates(bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:OnDestroyBar(event, bar, name) flickerstreak@109: CleanupStates(bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:OnEraseBar(event, bar, name) flickerstreak@109: self.db.profile.bars[name] = nil flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:OnRenameBar(event, bar, oldname, newname) flickerstreak@109: local bars = self.db.profile.bars flickerstreak@109: bars[newname], bars[oldname] = bars[oldname], nil flickerstreak@109: end flickerstreak@109: flickerstreak@109: flickerstreak@109: flickerstreak@109: -- Options -- flickerstreak@109: flickerstreak@109: local CreateBarOptions, RegisterPropertyOptions flickerstreak@109: do flickerstreak@109: -- pre-sorted by the order they should appear in flickerstreak@109: local rules = { flickerstreak@118: -- rule fields flickerstreak@118: { "stance", { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, flickerstreak@118: { "form", { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, flickerstreak@118: { "stealth", { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, flickerstreak@118: { "shadow", { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, flickerstreak@118: { "pet", { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, flickerstreak@118: { "target", { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, flickerstreak@118: { "focus", { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, flickerstreak@118: { "possess", { {possess = L["Mind Control"]} } }, flickerstreak@118: { "vehicle", { {vehicle = L["In a Vehicle"]} } }, flickerstreak@118: { "group", { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, flickerstreak@118: { "combat", { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, flickerstreak@109: } flickerstreak@109: flickerstreak@109: local ruleSelect = { } flickerstreak@109: local ruleMap = { } flickerstreak@109: local optionMap = setmetatable({},{__mode="k"}) flickerstreak@109: flickerstreak@109: local pointTable = { flickerstreak@109: NONE = " ", flickerstreak@109: CENTER = L["Center"], flickerstreak@109: LEFT = L["Left"], flickerstreak@109: RIGHT = L["Right"], flickerstreak@109: TOP = L["Top"], flickerstreak@109: BOTTOM = L["Bottom"], flickerstreak@109: TOPLEFT = L["Top Left"], flickerstreak@109: TOPRIGHT = L["Top Right"], flickerstreak@109: BOTTOMLEFT = L["Bottom Left"], flickerstreak@109: BOTTOMRIGHT = L["Bottom Right"], flickerstreak@109: } flickerstreak@109: flickerstreak@109: -- unpack rules table into ruleSelect and ruleMap flickerstreak@109: for _, c in ipairs(rules) do flickerstreak@118: local rule, fields = unpack(c) flickerstreak@118: for _, field in ipairs(fields) do flickerstreak@118: local key, label = next(field) flickerstreak@118: table.insert(ruleSelect, label) flickerstreak@118: table.insert(ruleMap, key) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: local stateOptions = { flickerstreak@109: ordering = { flickerstreak@109: name = L["Info"], flickerstreak@109: order = 1, flickerstreak@109: type = "group", flickerstreak@109: args = { flickerstreak@109: delete = { flickerstreak@109: name = L["Delete this State"], flickerstreak@109: order = -1, flickerstreak@109: type = "execute", flickerstreak@109: func = "DeleteState", flickerstreak@109: }, flickerstreak@109: rename = { flickerstreak@109: name = L["Name"], flickerstreak@109: order = 1, flickerstreak@109: type = "input", flickerstreak@109: get = "GetName", flickerstreak@109: set = "SetStateName", flickerstreak@109: pattern = "^%w*$", flickerstreak@109: usage = L["State names must be alphanumeric without spaces"], flickerstreak@109: }, flickerstreak@109: ordering = { flickerstreak@109: name = L["Evaluation Order"], flickerstreak@109: desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], flickerstreak@109: order = 2, flickerstreak@109: type = "group", flickerstreak@109: inline = true, flickerstreak@109: args = { flickerstreak@109: up = { flickerstreak@109: name = L["Up"], flickerstreak@109: order = 1, flickerstreak@109: type = "execute", flickerstreak@109: width = "half", flickerstreak@109: func = "MoveStateUp", flickerstreak@109: }, flickerstreak@109: down = { flickerstreak@109: name = L["Down"], flickerstreak@109: order = 2, flickerstreak@109: type = "execute", flickerstreak@109: width = "half", flickerstreak@109: func = "MoveStateDown", flickerstreak@109: } flickerstreak@109: } flickerstreak@109: } flickerstreak@109: } flickerstreak@109: }, flickerstreak@109: properties = { flickerstreak@109: name = L["Properties"], flickerstreak@109: order = 2, flickerstreak@109: type = "group", flickerstreak@109: args = { flickerstreak@109: desc = { flickerstreak@109: name = L["Set the properties for the bar when in this state"], flickerstreak@109: order = 1, flickerstreak@109: type = "description" flickerstreak@109: }, flickerstreak@109: hide = { flickerstreak@109: name = L["Hide Bar"], flickerstreak@109: order = 90, flickerstreak@109: type = "toggle", flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: }, flickerstreak@109: --[[ BROKEN flickerstreak@109: keybindState = { flickerstreak@109: name = L["Override Keybinds"], flickerstreak@109: desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], flickerstreak@109: order = 91, flickerstreak@109: type = "toggle", flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: }, ]] flickerstreak@109: position = { flickerstreak@109: name = L["Position"], flickerstreak@109: order = 92, flickerstreak@109: type = "group", flickerstreak@109: inline = true, flickerstreak@109: args = { flickerstreak@109: anchorEnable = { flickerstreak@109: name = L["Reposition"], flickerstreak@109: order = 1, flickerstreak@109: type = "toggle", flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: }, flickerstreak@109: anchorFrame = { flickerstreak@109: name = L["Anchor Frame"], flickerstreak@109: order = 2, flickerstreak@109: type = "select", flickerstreak@109: values = "GetAnchorFrames", flickerstreak@109: set = "SetAnchorFrame", flickerstreak@109: get = "GetAnchorFrame", flickerstreak@109: disabled = "GetAnchorDisabled", flickerstreak@109: hidden = "GetAnchorDisabled", flickerstreak@109: }, flickerstreak@109: anchorPoint = { flickerstreak@109: name = L["Point"], flickerstreak@109: order = 3, flickerstreak@109: type = "select", flickerstreak@109: values = pointTable, flickerstreak@109: set = "SetAnchorPointProp", flickerstreak@109: get = "GetAnchorPointProp", flickerstreak@109: disabled = "GetAnchorDisabled", flickerstreak@109: hidden = "GetAnchorDisabled", flickerstreak@109: }, flickerstreak@109: anchorRelPoint = { flickerstreak@109: name = L["Relative Point"], flickerstreak@109: order = 4, flickerstreak@109: type = "select", flickerstreak@109: values = pointTable, flickerstreak@109: set = "SetAnchorPointProp", flickerstreak@109: get = "GetAnchorPointProp", flickerstreak@109: disabled = "GetAnchorDisabled", flickerstreak@109: hidden = "GetAnchorDisabled", flickerstreak@109: }, flickerstreak@109: anchorX = { flickerstreak@109: name = L["X Offset"], flickerstreak@109: order = 5, flickerstreak@109: type = "range", flickerstreak@109: min = -100, flickerstreak@109: max = 100, flickerstreak@109: step = 1, flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: disabled = "GetAnchorDisabled", flickerstreak@109: hidden = "GetAnchorDisabled", flickerstreak@109: }, flickerstreak@109: anchorY = { flickerstreak@109: name = L["Y Offset"], flickerstreak@109: order = 6, flickerstreak@109: type = "range", flickerstreak@109: min = -100, flickerstreak@109: max = 100, flickerstreak@109: step = 1, flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: disabled = "GetAnchorDisabled", flickerstreak@109: hidden = "GetAnchorDisabled", flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: scale = { flickerstreak@109: name = L["Scale"], flickerstreak@109: order = 93, flickerstreak@109: type = "group", flickerstreak@109: inline = true, flickerstreak@109: args = { flickerstreak@109: enableScale = { flickerstreak@109: name = L["Set New Scale"], flickerstreak@109: order = 1, flickerstreak@109: type = "toggle", flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: }, flickerstreak@109: scale = { flickerstreak@109: name = L["Scale"], flickerstreak@109: order = 2, flickerstreak@109: type = "range", flickerstreak@109: min = 0.25, flickerstreak@109: max = 2.5, flickerstreak@109: step = 0.05, flickerstreak@109: isPercent = true, flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetScale", flickerstreak@109: disabled = "GetScaleDisabled", flickerstreak@109: hidden = "GetScaleDisabled", flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: alpha = { flickerstreak@109: name = L["Transparency"], flickerstreak@109: order = 94, flickerstreak@109: type = "group", flickerstreak@109: inline = true, flickerstreak@109: args = { flickerstreak@109: enableAlpha = { flickerstreak@109: name = L["Set Transparency"], flickerstreak@109: order = 1, flickerstreak@109: type = "toggle", flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetProp", flickerstreak@109: }, flickerstreak@109: alpha = { flickerstreak@109: name = L["Transparency"], flickerstreak@109: order = 2, flickerstreak@109: type = "range", flickerstreak@109: min = 0, flickerstreak@109: max = 1, flickerstreak@109: step = 0.01, flickerstreak@109: bigStep = 0.05, flickerstreak@109: isPercent = true, flickerstreak@109: set = "SetProp", flickerstreak@109: get = "GetAlpha", flickerstreak@109: disabled = "GetAlphaDisabled", flickerstreak@109: hidden = "GetAlphaDisabled", flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: plugins = { } flickerstreak@109: }, flickerstreak@109: rules = { flickerstreak@109: name = L["Rule"], flickerstreak@109: order = 3, flickerstreak@109: type = "group", flickerstreak@109: args = { flickerstreak@109: mode = { flickerstreak@109: name = L["Select this state"], flickerstreak@109: order = 2, flickerstreak@109: type = "select", flickerstreak@109: style = "radio", flickerstreak@109: values = { flickerstreak@109: default = L["by default"], flickerstreak@109: any = L["when ANY of these"], flickerstreak@109: all = L["when ALL of these"], flickerstreak@109: custom = L["via custom rule"], flickerstreak@109: keybind = L["via keybinding"], flickerstreak@109: }, flickerstreak@109: set = "SetType", flickerstreak@109: get = "GetType", flickerstreak@109: }, flickerstreak@109: clear = { flickerstreak@109: name = L["Clear All"], flickerstreak@109: order = 3, flickerstreak@109: type = "execute", flickerstreak@109: hidden = "GetClearAllDisabled", flickerstreak@109: disabled = "GetClearAllDisabled", flickerstreak@109: func = "ClearAllConditions", flickerstreak@109: }, flickerstreak@109: inputs = { flickerstreak@109: name = L["Conditions"], flickerstreak@109: order = 4, flickerstreak@109: type = "multiselect", flickerstreak@109: hidden = "GetConditionsDisabled", flickerstreak@109: disabled = "GetConditionsDisabled", flickerstreak@109: values = ruleSelect, flickerstreak@109: set = "SetCondition", flickerstreak@109: get = "GetCondition", flickerstreak@109: }, flickerstreak@109: custom = { flickerstreak@109: name = L["Custom Rule"], flickerstreak@109: order = 5, flickerstreak@109: type = "input", flickerstreak@109: multiline = true, flickerstreak@109: hidden = "GetCustomDisabled", flickerstreak@109: disabled = "GetCustomDisabled", flickerstreak@109: desc = L["Syntax like macro rules: see preset rules for examples"], flickerstreak@109: set = "SetCustomRule", flickerstreak@109: get = "GetCustomRule", flickerstreak@109: validate = "ValidateCustomRule", flickerstreak@109: }, flickerstreak@109: keybind = { flickerstreak@109: name = L["Keybinding"], flickerstreak@109: order = 6, flickerstreak@109: inline = true, flickerstreak@109: hidden = "GetKeybindDisabled", flickerstreak@109: disabled = "GetKeybindDisabled", flickerstreak@109: type = "group", flickerstreak@109: args = { flickerstreak@109: desc = { flickerstreak@109: name = L["Invoking a state keybind toggles an override of all other transition rules."], flickerstreak@109: order = 1, flickerstreak@109: type = "description", flickerstreak@109: }, flickerstreak@109: keybind = { flickerstreak@109: name = L["State Hotkey"], flickerstreak@109: desc = L["Define an override toggle keybind"], flickerstreak@109: order = 2, flickerstreak@109: type = "keybinding", flickerstreak@109: set = "SetKeybind", flickerstreak@109: get = "GetKeybind", flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: }, flickerstreak@109: } flickerstreak@109: flickerstreak@109: local handlers = { } flickerstreak@109: local meta = { flickerstreak@109: __index = function(self, key) flickerstreak@109: for _, h in pairs(handlers) do flickerstreak@109: if h[key] then flickerstreak@109: return h[key] flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end, flickerstreak@109: } flickerstreak@109: local StateHandler = setmetatable({ }, meta) flickerstreak@109: local proto = { __index = StateHandler } flickerstreak@109: flickerstreak@109: function RegisterPropertyOptions( field, options, handler ) flickerstreak@109: stateOptions.properties.plugins[field] = options flickerstreak@109: handlers[field] = handler flickerstreak@109: end flickerstreak@109: flickerstreak@109: function UnregisterPropertyOptions( field ) flickerstreak@109: stateOptions.properties.plugins[field] = nil flickerstreak@109: handlers[field] = nil flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:New( bar, opts ) flickerstreak@109: local self = setmetatable( flickerstreak@109: { flickerstreak@109: bar = bar flickerstreak@109: }, flickerstreak@109: proto ) flickerstreak@109: flickerstreak@109: function self:GetName() flickerstreak@109: return opts.name flickerstreak@109: end flickerstreak@109: flickerstreak@109: function self:SetName(name) flickerstreak@109: opts.name = name flickerstreak@109: end flickerstreak@109: flickerstreak@109: function self:GetOrder() flickerstreak@109: return opts.order flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- get reference to states table: even if the bar flickerstreak@109: -- name changes the states table ref won't flickerstreak@109: self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") flickerstreak@109: self.state = tbuild(self.states, opts.name) flickerstreak@109: flickerstreak@109: opts.order = self:GetRuleField("order") flickerstreak@109: if opts.order == nil then flickerstreak@109: -- add after the highest flickerstreak@109: opts.order = 100 flickerstreak@109: for _, state in pairs(self.states) do flickerstreak@109: local x = tonumber(tfetch(state, "rule", "order")) flickerstreak@109: if x and x >= opts.order then flickerstreak@109: opts.order = x + 1 flickerstreak@109: end flickerstreak@109: end flickerstreak@109: self:SetRuleField("order",opts.order) flickerstreak@109: end flickerstreak@109: flickerstreak@109: return self flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- helper methods flickerstreak@109: flickerstreak@109: function StateHandler:SetRuleField( key, value, ... ) flickerstreak@109: tbuild(self.state, "rule", ...)[key] = value flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetRuleField( ... ) flickerstreak@109: return tfetch(self.state, "rule", ...) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:FixAll( setkey ) flickerstreak@109: -- if multiple selections in the same group are chosen when 'all' is selected, flickerstreak@109: -- keep only one of them. If changing the mode, the first in the fields list will flickerstreak@109: -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, flickerstreak@109: -- it will be retained. flickerstreak@109: local notified = false flickerstreak@109: if self:GetRuleField("type") == "all" then flickerstreak@109: for _, c in ipairs(rules) do flickerstreak@118: local rule, fields = unpack(c) flickerstreak@109: local once = false flickerstreak@109: if setkey then flickerstreak@109: for idx, field in ipairs(fields) do flickerstreak@109: if next(field) == setkey then flickerstreak@109: once = true flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: for idx, field in ipairs(fields) do flickerstreak@109: local key = next(field) flickerstreak@109: if self:GetRuleField("values",key) then flickerstreak@109: if once and key ~= setkey then flickerstreak@109: self:SetRuleField(key,false,"values") flickerstreak@109: if not setkey and not notified then flickerstreak@109: ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) flickerstreak@109: notified = true flickerstreak@109: end flickerstreak@109: end flickerstreak@109: once = true flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetNeighbors() flickerstreak@109: local before, after flickerstreak@109: for k, v in pairs(self.states) do flickerstreak@109: local o = tonumber(tfetch(v, "rule", "order")) flickerstreak@109: if o and k ~= self:GetName() then flickerstreak@109: local obefore = tfetch(self.states,before,"rule","order") flickerstreak@109: local oafter = tfetch(self.states,after,"rule","order") flickerstreak@109: if o < self:GetOrder() and (not obefore or obefore < o) then flickerstreak@109: before = k flickerstreak@109: end flickerstreak@109: if o > self:GetOrder() and (not oafter or oafter > o) then flickerstreak@109: after = k flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: return before, after flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SwapOrder( a, b ) flickerstreak@109: -- do options table flickerstreak@109: local args = optionMap[self.bar].args flickerstreak@109: args[a].order, args[b].order = args[b].order, args[a].order flickerstreak@109: -- do profile flickerstreak@109: a = tbuild(self.states, a, "rule") flickerstreak@109: b = tbuild(self.states, b, "rule") flickerstreak@109: a.order, b.order = b.order, a.order flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- handler methods flickerstreak@109: flickerstreak@109: function StateHandler:GetProp( info ) flickerstreak@109: -- gets property of the same name as the options arg flickerstreak@109: return GetProperty(self.bar, self:GetName(), info[#info]) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetProp( info, value ) flickerstreak@109: -- sets property of the same name as the options arg flickerstreak@109: SetProperty(self.bar, self:GetName(), info[#info], value) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:DeleteState() flickerstreak@109: if self.states[self:GetName()] then flickerstreak@109: self.states[self:GetName()] = nil flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: optionMap[self.bar].args[self:GetName()] = nil flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetStateName(info, value) flickerstreak@109: -- check for existing state name flickerstreak@109: if self.states[value] then flickerstreak@109: ReAction:UserError(format(L["State named '%s' already exists"],value)) flickerstreak@109: return flickerstreak@109: end flickerstreak@109: local args = optionMap[self.bar].args flickerstreak@109: local name = self:GetName() flickerstreak@109: self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil flickerstreak@109: self:SetName(value) flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: ReAction:ShowEditor(self.bar, moduleID, value) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:MoveStateUp() flickerstreak@109: local before, after = self:GetNeighbors() flickerstreak@109: if before then flickerstreak@109: self:SwapOrder(before, self:GetName()) flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:MoveStateDown() flickerstreak@109: local before, after = self:GetNeighbors() flickerstreak@109: if after then flickerstreak@109: self:SwapOrder(self:GetName(), after) flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAnchorDisabled() flickerstreak@109: return not GetProperty(self.bar, self:GetName(), "anchorEnable") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAnchorFrames(info) flickerstreak@109: self._anchorframes = self._anchorframes or { } flickerstreak@109: table.wipe(self._anchorframes) flickerstreak@109: flickerstreak@109: table.insert(self._anchorframes, "UIParent") flickerstreak@109: for name, bar in ReAction:IterateBars() do flickerstreak@109: table.insert(self._anchorframes, bar:GetFrame():GetName()) flickerstreak@109: end flickerstreak@109: return self._anchorframes flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAnchorFrame(info) flickerstreak@109: local value = self:GetProp(info) flickerstreak@109: for k,v in pairs(self._anchorframes) do flickerstreak@109: if v == value then flickerstreak@109: return k flickerstreak@109: end flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetAnchorFrame(info, value) flickerstreak@109: local f = _G[self._anchorframes[value]] flickerstreak@109: if f then flickerstreak@157: self.bar:SetFrameRef("anchor-"..self:GetName(), f) flickerstreak@109: self:SetProp(info, f:GetName()) flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetAnchorPointProp(info, value) flickerstreak@109: self:SetProp(info, value ~= "NONE" and value or nil) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAnchorPointProp(info) flickerstreak@109: return self:GetProp(info) or "NONE" flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetScale(info) flickerstreak@109: return self:GetProp(info) or 1.0 flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetScaleDisabled() flickerstreak@109: return not GetProperty(self.bar, self:GetName(), "enableScale") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAlpha(info) flickerstreak@109: return self:GetProp(info) or 1.0 flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetAlphaDisabled() flickerstreak@109: return not GetProperty(self.bar, self:GetName(), "enableAlpha") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetType(info, value) flickerstreak@109: self:SetRuleField("type", value) flickerstreak@109: self:FixAll() flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetType() flickerstreak@109: return self:GetRuleField("type") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetClearAllDisabled() flickerstreak@109: local t = self:GetRuleField("type") flickerstreak@109: return not( t == "any" or t == "all" or t == "custom") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:ClearAllConditions() flickerstreak@109: local t = self:GetRuleField("type") flickerstreak@109: if t == "custom" then flickerstreak@109: self:SetRuleField("custom","") flickerstreak@109: elseif t == "any" or t == "all" then flickerstreak@109: self:SetRuleField("values", {}) flickerstreak@109: end flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetConditionsDisabled() flickerstreak@109: local t = self:GetRuleField("type") flickerstreak@109: return not( t == "any" or t == "all") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetCondition(info, key, value) flickerstreak@109: self:SetRuleField(ruleMap[key], value or nil, "values") flickerstreak@109: if value then flickerstreak@109: self:FixAll(ruleMap[key]) flickerstreak@109: end flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetCondition(info, key) flickerstreak@109: return self:GetRuleField("values", ruleMap[key]) or false flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetCustomDisabled() flickerstreak@109: return self:GetRuleField("type") ~= "custom" flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetCustomRule(info, value) flickerstreak@109: self:SetRuleField("custom",value) flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetCustomRule() flickerstreak@109: return self:GetRuleField("custom") or "" flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:ValidateCustomRule(info, value) flickerstreak@109: local s = value:gsub("%s","") -- remove all spaces flickerstreak@109: -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler flickerstreak@109: repeat flickerstreak@109: if s == "" then flickerstreak@109: return true flickerstreak@109: end flickerstreak@109: local c, r = s:match("(%b[])(.*)") flickerstreak@109: if c == nil and s and #s > 0 then flickerstreak@109: return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") flickerstreak@109: end flickerstreak@109: s = r flickerstreak@109: until c == nil flickerstreak@109: return true flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetKeybindDisabled() flickerstreak@109: return self:GetRuleField("type") ~= "keybind" flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:GetKeybind() flickerstreak@109: return self:GetRuleField("keybind") flickerstreak@109: end flickerstreak@109: flickerstreak@109: function StateHandler:SetKeybind(info, value) flickerstreak@109: if value and #value == 0 then flickerstreak@109: value = nil flickerstreak@109: end flickerstreak@109: self:SetRuleField("keybind",value) flickerstreak@109: ApplyStates(self.bar) flickerstreak@109: end flickerstreak@109: flickerstreak@109: local function CreateStateOptions(bar, name) flickerstreak@109: local opts = { flickerstreak@109: type = "group", flickerstreak@109: name = name, flickerstreak@109: childGroups = "tab", flickerstreak@109: args = stateOptions flickerstreak@109: } flickerstreak@109: flickerstreak@109: opts.handler = StateHandler:New(bar,opts) flickerstreak@109: flickerstreak@109: return opts flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:GetBarOptions(bar) flickerstreak@109: local private = { } flickerstreak@109: local states = tbuild(module.db.profile.bars, bar:GetName(), "states") flickerstreak@109: local options = { flickerstreak@109: name = L["Dynamic State"], flickerstreak@109: type = "group", flickerstreak@109: order = -1, flickerstreak@109: childGroups = "tree", flickerstreak@109: disabled = InCombatLockdown, flickerstreak@109: args = { flickerstreak@109: __desc__ = { flickerstreak@109: name = L["States are evaluated in the order they are listed"], flickerstreak@109: order = 1, flickerstreak@109: type = "description", flickerstreak@109: }, flickerstreak@109: __new__ = { flickerstreak@109: name = L["New State..."], flickerstreak@109: order = 2, flickerstreak@109: type = "group", flickerstreak@109: args = { flickerstreak@109: name = { flickerstreak@109: name = L["State Name"], flickerstreak@109: desc = L["Set a name for the new state"], flickerstreak@109: order = 1, flickerstreak@109: type = "input", flickerstreak@109: get = function() return private.newstatename or "" end, flickerstreak@109: set = function(info,value) private.newstatename = value end, flickerstreak@109: pattern = "^%w*$", flickerstreak@109: usage = L["State names must be alphanumeric without spaces"], flickerstreak@109: }, flickerstreak@109: create = { flickerstreak@109: name = L["Create State"], flickerstreak@109: order = 2, flickerstreak@109: type = "execute", flickerstreak@109: func = function () flickerstreak@109: local name = private.newstatename flickerstreak@109: if states[name] then flickerstreak@109: ReAction:UserError(format(L["State named '%s' already exists"],name)) flickerstreak@109: else flickerstreak@109: -- TODO: select default state options and pass as final argument flickerstreak@109: states[name] = { } flickerstreak@109: optionMap[bar].args[name] = CreateStateOptions(bar,name) flickerstreak@109: ReAction:ShowEditor(bar, moduleID, name) flickerstreak@109: private.newstatename = "" flickerstreak@109: end flickerstreak@109: end, flickerstreak@109: disabled = function() flickerstreak@109: local name = private.newstatename or "" flickerstreak@109: return #name == 0 or name:find("%W") flickerstreak@109: end, flickerstreak@109: } flickerstreak@109: } flickerstreak@109: } flickerstreak@109: } flickerstreak@109: } flickerstreak@109: for name, config in pairs(states) do flickerstreak@109: options.args[name] = CreateStateOptions(bar,name) flickerstreak@109: end flickerstreak@109: optionMap[bar] = options flickerstreak@109: return options flickerstreak@109: end flickerstreak@109: end flickerstreak@109: flickerstreak@109: -- Module API -- flickerstreak@109: flickerstreak@109: -- Pass in a property field-name, an implementation secure snippet, a static options table, and an flickerstreak@109: -- optional options handler method-table flickerstreak@109: -- flickerstreak@109: -- The options table is static, i.e. not bar-specific and should only reference handler method flickerstreak@109: -- strings (either existing ones or those added via optHandler). The existing options are ordered flickerstreak@109: -- 90-99. Order #1 is reserved for the heading. flickerstreak@109: -- flickerstreak@109: -- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. flickerstreak@109: -- See above, for existing API. In particular see the properties set up in the New method: self.bar, flickerstreak@109: -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). flickerstreak@109: -- flickerstreak@109: function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) flickerstreak@109: RegisterProperty(field, snippetHandler) flickerstreak@109: RegisterPropertyOptions(field, options, optHandler) flickerstreak@109: end flickerstreak@109: flickerstreak@109: function module:UnregisterStateProperty( field ) flickerstreak@109: UnregisterProperty(field) flickerstreak@109: UnregisterPropertyOptions(field) flickerstreak@109: end flickerstreak@109: flickerstreak@109: flickerstreak@109: -- Export methods to Bar class -- flickerstreak@109: flickerstreak@109: ReAction.Bar.GetStateProperty = GetProperty flickerstreak@109: ReAction.Bar.SetStateProperty = SetProperty