annotate State.lua @ 70:2c12e2b1752e

Fixed unbinding a keystate toggle
author Flick <flickerstreak@gmail.com>
date Tue, 03 Jun 2008 23:24:03 +0000
parents a785d6708388
children 3d2cef5dc459
rev   line source
flickerstreak@25 1 --[[
flickerstreak@62 2 ReAction bar state driver interface
flickerstreak@25 3
flickerstreak@25 4 --]]
flickerstreak@25 5
flickerstreak@25 6 -- local imports
flickerstreak@25 7 local ReAction = ReAction
flickerstreak@25 8 local L = ReAction.L
flickerstreak@25 9 local _G = _G
flickerstreak@25 10 local InCombatLockdown = InCombatLockdown
flickerstreak@25 11
flickerstreak@25 12 -- module declaration
flickerstreak@25 13 local moduleID = "State"
flickerstreak@65 14 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
flickerstreak@62 15
flickerstreak@64 16 -- Utility --
flickerstreak@62 17
flickerstreak@62 18 -- traverse a table tree by key list and fetch the result or first nil
flickerstreak@62 19 local function tfetch(t, ...)
flickerstreak@62 20 for i = 1, select('#', ...) do
flickerstreak@62 21 t = t and t[select(i, ...)]
flickerstreak@62 22 end
flickerstreak@62 23 return t
flickerstreak@62 24 end
flickerstreak@62 25
flickerstreak@62 26 -- traverse a table tree by key list and build tree as necessary
flickerstreak@62 27 local function tbuild(t, ...)
flickerstreak@62 28 for i = 1, select('#', ...) do
flickerstreak@62 29 local key = select(i, ...)
flickerstreak@62 30 if not t[key] then t[key] = { } end
flickerstreak@62 31 t = t[key]
flickerstreak@62 32 end
flickerstreak@62 33 return t
flickerstreak@62 34 end
flickerstreak@62 35
flickerstreak@64 36 -- PRIVATE --
flickerstreak@68 37
flickerstreak@68 38 local InitRules, ApplyStates, SetProperty, GetProperty
flickerstreak@64 39 do
flickerstreak@67 40 -- As far as I can tell the macro clauses are NOT locale-specific.
flickerstreak@67 41 local ruleformats = {
flickerstreak@67 42 stealth = "stealth",
flickerstreak@67 43 nostealth = "nostealth",
flickerstreak@67 44 shadowform = "form:1",
flickerstreak@67 45 noshadowform = "noform",
flickerstreak@67 46 pet = "pet",
flickerstreak@67 47 nopet = "nopet",
flickerstreak@67 48 harm = "target=target,harm",
flickerstreak@67 49 help = "target=target,help",
flickerstreak@67 50 notarget = "target=target,noexists",
flickerstreak@67 51 focusharm = "target=focus,harm",
flickerstreak@67 52 focushelp = "target=focus,help",
flickerstreak@67 53 nofocus = "target=focus,noexists",
flickerstreak@67 54 raid = "group:raid",
flickerstreak@67 55 party = "group:party",
flickerstreak@67 56 solo = "nogroup",
flickerstreak@67 57 combat = "combat",
flickerstreak@67 58 nocombat = "nocombat",
flickerstreak@67 59 }
flickerstreak@65 60
flickerstreak@67 61 -- Have to do these shenanigans instead of hardcoding the stances/forms because
flickerstreak@67 62 -- the ordering varies if the character is missing a form. For warriors
flickerstreak@67 63 -- this is rarely a problem (c'mon, who actually skips the level 10 def stance quest?)
flickerstreak@67 64 -- but for druids it can be. Some people never bother to do the aquatic form quest
flickerstreak@67 65 -- until well past when they get cat form, and stance 5 can be flight, tree, or moonkin
flickerstreak@67 66 -- depending on talents.
flickerstreak@65 67 function InitRules()
flickerstreak@65 68 local forms = { }
flickerstreak@67 69 -- sort by icon since it's locale-independent
flickerstreak@65 70 for i = 1, GetNumShapeshiftForms() do
flickerstreak@65 71 local icon = GetShapeshiftFormInfo(i)
flickerstreak@65 72 forms[icon] = i;
flickerstreak@65 73 end
flickerstreak@65 74 -- use 9 if not found since 9 is never a valid stance/form
flickerstreak@65 75 local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9
flickerstreak@65 76 local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
flickerstreak@65 77 local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
flickerstreak@65 78 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
flickerstreak@65 79 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
flickerstreak@65 80 local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
flickerstreak@65 81 local treekin = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
flickerstreak@67 82 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
flickerstreak@65 83
flickerstreak@65 84 ruleformats.battle = "stance:1"
flickerstreak@65 85 ruleformats.defensive = ("stance:%d"):format(defensive)
flickerstreak@65 86 ruleformats.berserker = ("stance:%d"):format(berserker)
flickerstreak@65 87 ruleformats.caster = ("form:0/%d/%d/%d"):format(aquatic, travel, flight)
flickerstreak@65 88 ruleformats.bear = ("form:%d"):format(bear)
flickerstreak@65 89 ruleformats.cat = ("form:%d"):format(cat)
flickerstreak@65 90 ruleformats.treeOrMoonkin = ("form:%d"):format(treekin)
flickerstreak@64 91 end
flickerstreak@62 92
flickerstreak@68 93 -- return a new array of keys of table 't', sorted by comparing
flickerstreak@68 94 -- sub-fields (obtained via tfetch) of the table values
flickerstreak@68 95 local function fieldsort( t, ... )
flickerstreak@68 96 local r = { }
flickerstreak@68 97 for k in pairs(t) do
flickerstreak@68 98 table.insert(r,k)
flickerstreak@68 99 end
flickerstreak@68 100 local dotdotdot = { ... }
flickerstreak@68 101 table.sort(r, function(lhs, rhs)
flickerstreak@68 102 local olhs = tfetch(t[lhs], unpack(dotdotdot)) or 0
flickerstreak@68 103 local orhs = tfetch(t[rhs], unpack(dotdotdot)) or 0
flickerstreak@68 104 return olhs < orhs
flickerstreak@68 105 end)
flickerstreak@68 106 return r
flickerstreak@68 107 end
flickerstreak@68 108
flickerstreak@65 109 local function BuildRuleString(states)
flickerstreak@64 110 local s = ""
flickerstreak@64 111 local default
flickerstreak@68 112 local sorted = fieldsort(states, "rule", "order")
flickerstreak@64 113 for idx, name in ipairs(sorted) do
flickerstreak@64 114 local state = states[name]
flickerstreak@64 115 local semi = #s > 0 and "; " or ""
flickerstreak@64 116 local mode = tfetch(state,"rule","type")
flickerstreak@64 117 if mode == "default" then
flickerstreak@64 118 default = name
flickerstreak@64 119 elseif mode == "custom" then
flickerstreak@64 120 if state.rule.custom then
flickerstreak@64 121 -- strip out all spaces from the custom rule
flickerstreak@64 122 s = ("%s%s%s %s"):format(s, semi, state.rule.custom:gsub("%s",""), name)
flickerstreak@64 123 end
flickerstreak@64 124 elseif mode == "any" then
flickerstreak@64 125 if state.rule.values then
flickerstreak@64 126 local clause = ""
flickerstreak@64 127 for key, value in pairs(state.rule.values) do
flickerstreak@64 128 clause = ("%s[%s]"):format(clause,ruleformats[key])
flickerstreak@64 129 end
flickerstreak@64 130 if #clause > 0 then
flickerstreak@64 131 s = ("%s%s%s %s"):format(s, semi, clause, name)
flickerstreak@64 132 end
flickerstreak@64 133 end
flickerstreak@64 134 elseif mode == "all" then
flickerstreak@64 135 if state.rule.values then
flickerstreak@64 136 local clause = ""
flickerstreak@64 137 for key, value in pairs(state.rule.values) do
flickerstreak@64 138 clause = ("%s%s%s"):format(clause,#clause > 0 and "," or "", ruleformats[key])
flickerstreak@64 139 end
flickerstreak@64 140 if #clause > 0 then
flickerstreak@64 141 s = ("%s%s[%s] %s"):format(s, semi, clause, name)
flickerstreak@64 142 end
flickerstreak@64 143 end
flickerstreak@64 144 end
flickerstreak@64 145 end
flickerstreak@64 146 if default then
flickerstreak@64 147 s = ("%s%s%s"):format(s, #s > 0 and "; " or "", default)
flickerstreak@64 148 end
flickerstreak@68 149 return s, default
flickerstreak@64 150 end
flickerstreak@64 151
flickerstreak@64 152 local drivers = setmetatable({},{__mode="k"})
flickerstreak@68 153 local propertyFuncs = { }
flickerstreak@64 154
flickerstreak@64 155 function ApplyStates( bar )
flickerstreak@64 156 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@64 157 if states then
flickerstreak@64 158 local frame = bar:GetFrame()
flickerstreak@68 159 local string, default = BuildRuleString(states)
flickerstreak@64 160 if string and #string > 0 then
flickerstreak@68 161 drivers[bar] = true
flickerstreak@68 162 -- register a map for each "statemap-reaction-XXX" to set 'state' to 'XXX'
flickerstreak@68 163 -- UNLESS we're in a keybound state AND there's a default state, in which case
flickerstreak@68 164 -- all keybound states go back to themselves.
flickerstreak@68 165 local keybindprefix
flickerstreak@68 166 if default then
flickerstreak@68 167 local tmp = { }
flickerstreak@68 168 for state, config in pairs(states) do
flickerstreak@68 169 if tfetch(config, "rule", "type") == "keybind" then
flickerstreak@68 170 bar:SetStateKeybind(tfetch(config,"rule","keybind"), state, tfetch(config,"rule","keybindreturn") or default or 0)
flickerstreak@68 171 table.insert(tmp, ("%s:%s"):format(state,state))
flickerstreak@68 172 end
flickerstreak@68 173 end
flickerstreak@68 174 if #tmp > 0 then
flickerstreak@68 175 table.insert(tmp,"") -- to get a final ';'
flickerstreak@68 176 end
flickerstreak@68 177 keybindprefix = table.concat(tmp,";")
flickerstreak@68 178 end
flickerstreak@68 179 for state in pairs(states) do
flickerstreak@68 180 frame:SetAttribute(("statemap-reaction-%s"):format(state), ("%s%s"):format(keybindprefix or "",state))
flickerstreak@68 181 end
flickerstreak@64 182 -- register a handler to set the value of attribute "state-reaction"
flickerstreak@64 183 -- in response to events as per the rule string
flickerstreak@64 184 RegisterStateDriver(frame, "reaction", string)
flickerstreak@68 185 SecureStateHeader_Refresh(frame)
flickerstreak@64 186 elseif drivers[bar] then
flickerstreak@64 187 UnregisterStateDriver(frame, "reaction")
flickerstreak@64 188 drivers[bar] = nil
flickerstreak@64 189 end
flickerstreak@68 190 for k, f in pairs(propertyFuncs) do
flickerstreak@68 191 f(bar, states)
flickerstreak@68 192 end
flickerstreak@64 193 end
flickerstreak@64 194 end
flickerstreak@68 195
flickerstreak@68 196 function GetProperty( bar, state, propname )
flickerstreak@68 197 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
flickerstreak@68 198 end
flickerstreak@68 199
flickerstreak@68 200 function SetProperty( bar, state, propname, value )
flickerstreak@68 201 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@68 202 tbuild(states, state)[propname] = value
flickerstreak@68 203 local f = propertyFuncs[propname]
flickerstreak@68 204 if f then
flickerstreak@68 205 f(bar, states)
flickerstreak@68 206 end
flickerstreak@68 207 end
flickerstreak@68 208
flickerstreak@68 209 -- state property functions
flickerstreak@68 210 function propertyFuncs.hide( bar, states )
flickerstreak@68 211 local tmp = { }
flickerstreak@68 212 for state, config in pairs(states) do
flickerstreak@68 213 if config.hide then
flickerstreak@68 214 table.insert(tmp, state)
flickerstreak@68 215 end
flickerstreak@68 216 end
flickerstreak@68 217 local s = table.concat(tmp,",")
flickerstreak@68 218 bar:SetHideStates(s)
flickerstreak@68 219 end
flickerstreak@68 220
flickerstreak@68 221 function propertyFuncs.page( bar, states )
flickerstreak@68 222 local map = { }
flickerstreak@68 223 for state, config in pairs(states) do
flickerstreak@68 224 map[state] = config.page
flickerstreak@68 225 end
flickerstreak@68 226 bar:SetStatePageMap(state, map)
flickerstreak@68 227 end
flickerstreak@68 228
flickerstreak@68 229 function propertyFuncs.keybindstate( bar, states )
flickerstreak@68 230 local map = { }
flickerstreak@68 231 for state, config in pairs(states) do
flickerstreak@68 232 if config.keybindstate then
flickerstreak@68 233 table.insert(map,state)
flickerstreak@68 234 end
flickerstreak@68 235 end
flickerstreak@68 236 bar:SetStateKeybindOverrideMap(map)
flickerstreak@68 237 end
flickerstreak@68 238
flickerstreak@68 239 function propertyFuncs.enableanchor( bar, states )
flickerstreak@68 240
flickerstreak@68 241 end
flickerstreak@68 242
flickerstreak@68 243 function propertyFuncs.anchorPoint( bar, states )
flickerstreak@68 244
flickerstreak@68 245 end
flickerstreak@68 246
flickerstreak@68 247 function propertyFuncs.anchorRelPoint( bar, states )
flickerstreak@68 248
flickerstreak@68 249 end
flickerstreak@68 250
flickerstreak@68 251 function propertyFuncs.anchorX( bar, states )
flickerstreak@68 252
flickerstreak@68 253 end
flickerstreak@68 254
flickerstreak@68 255 function propertyFuncs.anchorY( bar, states )
flickerstreak@68 256
flickerstreak@68 257 end
flickerstreak@68 258
flickerstreak@68 259 function propertyFuncs.enablescale( bar, states )
flickerstreak@68 260
flickerstreak@68 261 end
flickerstreak@68 262
flickerstreak@68 263 function propertyFuncs.scale( bar, states )
flickerstreak@68 264
flickerstreak@68 265 end
flickerstreak@68 266
flickerstreak@64 267 end
flickerstreak@64 268
flickerstreak@64 269
flickerstreak@68 270
flickerstreak@68 271 -- module event handlers --
flickerstreak@68 272
flickerstreak@65 273 function module:OnInitialize()
flickerstreak@65 274 self.db = ReAction.db:RegisterNamespace( moduleID,
flickerstreak@65 275 {
flickerstreak@65 276 profile = {
flickerstreak@65 277 bars = { },
flickerstreak@65 278 }
flickerstreak@65 279 }
flickerstreak@65 280 )
flickerstreak@65 281
flickerstreak@65 282 InitRules()
flickerstreak@65 283 self:RegisterEvent("PLAYER_AURAS_CHANGED")
flickerstreak@65 284
flickerstreak@65 285 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
flickerstreak@65 286
flickerstreak@65 287 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar")
flickerstreak@65 288 ReAction.RegisterCallback(self, "OnRefreshBar")
flickerstreak@65 289 ReAction.RegisterCallback(self, "OnEraseBar")
flickerstreak@65 290 ReAction.RegisterCallback(self, "OnRenameBar")
flickerstreak@65 291 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@65 292 end
flickerstreak@65 293
flickerstreak@65 294 function module:PLAYER_AURAS_CHANGED()
flickerstreak@65 295 self:UnregisterEvent("PLAYER_AURAS_CHANGED")
flickerstreak@66 296 -- on login the number of stances is 0 until this event fires during the init sequence.
flickerstreak@66 297 -- however if you reload just the UI the number of stances is correct immediately
flickerstreak@66 298 -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might
flickerstreak@66 299 -- be in combat.
flickerstreak@66 300 if not InCombatLockdown() then
flickerstreak@66 301 InitRules()
flickerstreak@66 302 for name, bar in ReAction:IterateBars() do
flickerstreak@67 303 self:OnRefreshBar(nil,bar,name)
flickerstreak@66 304 end
flickerstreak@66 305 end
flickerstreak@65 306 end
flickerstreak@65 307
flickerstreak@65 308 function module:OnRefreshBar(event, bar, name)
flickerstreak@65 309 local c = self.db.profile.bars[name]
flickerstreak@65 310 if c then
flickerstreak@68 311 ApplyStates(bar)
flickerstreak@65 312 end
flickerstreak@65 313 end
flickerstreak@65 314
flickerstreak@65 315 function module:OnEraseBar(event, bar, name)
flickerstreak@65 316 self.db.profile.bars[name] = nil
flickerstreak@65 317 end
flickerstreak@65 318
flickerstreak@65 319 function module:OnRenameBar(event, bar, oldname, newname)
flickerstreak@65 320 local b = self.db.profile.bars
flickerstreak@65 321 bars[newname], bars[oldname] = bars[oldname], nil
flickerstreak@65 322 end
flickerstreak@65 323
flickerstreak@65 324 function module:OnConfigModeChanged(event, mode)
flickerstreak@65 325 -- TODO: unregister all state drivers (temporarily) and hidestates
flickerstreak@65 326 end
flickerstreak@65 327
flickerstreak@64 328
flickerstreak@64 329
flickerstreak@64 330 -- Options --
flickerstreak@64 331
flickerstreak@64 332 local CreateBarOptions
flickerstreak@62 333 do
flickerstreak@62 334 local function ClassCheck(...)
flickerstreak@62 335 for i = 1, select('#',...) do
flickerstreak@62 336 local _, c = UnitClass("player")
flickerstreak@62 337 if c == select(i,...) then
flickerstreak@62 338 return false
flickerstreak@62 339 end
flickerstreak@62 340 end
flickerstreak@62 341 return true
flickerstreak@62 342 end
flickerstreak@62 343
flickerstreak@64 344 -- pre-sorted by the order they should appear in
flickerstreak@64 345 local rules = {
flickerstreak@64 346 -- rule hidden fields
flickerstreak@64 347 { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
flickerstreak@64 348 { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {treeOrMoonkin = L["Tree/Moonkin"]} } },
flickerstreak@64 349 { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
flickerstreak@64 350 { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
flickerstreak@64 351 { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
flickerstreak@64 352 { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
flickerstreak@64 353 { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
flickerstreak@64 354 { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
flickerstreak@64 355 { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
flickerstreak@62 356 }
flickerstreak@62 357
flickerstreak@64 358 local ruleSelect = { }
flickerstreak@64 359 local ruleMap = { }
flickerstreak@64 360 local optionMap = setmetatable({},{__mode="k"})
flickerstreak@62 361
flickerstreak@68 362 local pointTable = {
flickerstreak@68 363 NONE = " ",
flickerstreak@68 364 CENTER = L["Center"],
flickerstreak@68 365 LEFT = L["Left"],
flickerstreak@68 366 RIGHT = L["Right"],
flickerstreak@68 367 TOP = L["Top"],
flickerstreak@68 368 BOTTOM = L["Bottom"],
flickerstreak@68 369 TOPLEFT = L["Top Left"],
flickerstreak@68 370 TOPRIGHT = L["Top Right"],
flickerstreak@68 371 BOTTOMLEFT = L["Bottom Left"],
flickerstreak@68 372 BOTTOMRIGHT = L["Bottom Right"],
flickerstreak@68 373 }
flickerstreak@68 374
flickerstreak@64 375 -- unpack rules table into ruleSelect and ruleMap
flickerstreak@64 376 for _, c in ipairs(rules) do
flickerstreak@64 377 local rule, hidden, fields = unpack(c)
flickerstreak@64 378 if not hidden then
flickerstreak@64 379 for _, field in ipairs(fields) do
flickerstreak@64 380 local key, label = next(field)
flickerstreak@64 381 table.insert(ruleSelect, label)
flickerstreak@64 382 table.insert(ruleMap, key)
flickerstreak@62 383 end
flickerstreak@62 384 end
flickerstreak@62 385 end
flickerstreak@62 386
flickerstreak@62 387 local function CreateStateOptions(bar, name)
flickerstreak@62 388 local opts = {
flickerstreak@62 389 type = "group",
flickerstreak@62 390 name = name,
flickerstreak@64 391 childGroups = "tab",
flickerstreak@25 392 }
flickerstreak@62 393
flickerstreak@64 394 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@64 395
flickerstreak@68 396 local function update()
flickerstreak@68 397 ApplyStates(bar)
flickerstreak@68 398 end
flickerstreak@68 399
flickerstreak@68 400 local function setrule( key, value, ... )
flickerstreak@64 401 tbuild(states, opts.name, "rule", ...)[key] = value
flickerstreak@64 402 end
flickerstreak@64 403
flickerstreak@68 404 local function getrule( ... )
flickerstreak@64 405 return tfetch(states, opts.name, "rule", ...)
flickerstreak@64 406 end
flickerstreak@64 407
flickerstreak@68 408 local function setprop(info, value)
flickerstreak@68 409 SetProperty(bar, opts.name, info[#info], value)
flickerstreak@68 410 end
flickerstreak@68 411
flickerstreak@68 412 local function getprop(info)
flickerstreak@68 413 return GetProperty(bar, opts.name, info[#info])
flickerstreak@68 414 end
flickerstreak@68 415
flickerstreak@64 416 local function fixall(setkey)
flickerstreak@64 417 -- if multiple selections in the same group are chosen when 'all' is selected,
flickerstreak@64 418 -- keep only one of them. If changing the mode, the first in the fields list will
flickerstreak@64 419 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
flickerstreak@64 420 -- it will be retained.
flickerstreak@64 421 local notified = false
flickerstreak@64 422 for _, c in ipairs(rules) do
flickerstreak@64 423 local rule, hidden, fields = unpack(c)
flickerstreak@64 424 local found = false
flickerstreak@64 425 for key in ipairs(fields) do
flickerstreak@68 426 if getrule("values",key) then
flickerstreak@64 427 if (found or setkey) and key ~= setkey then
flickerstreak@68 428 setrule(key,false,"values")
flickerstreak@64 429 if not setkey and not notified then
flickerstreak@64 430 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
flickerstreak@64 431 notified = true
flickerstreak@64 432 end
flickerstreak@64 433 end
flickerstreak@64 434 found = true
flickerstreak@64 435 end
flickerstreak@62 436 end
flickerstreak@62 437 end
flickerstreak@62 438 end
flickerstreak@62 439
flickerstreak@64 440 local function getNeighbors()
flickerstreak@64 441 local before, after
flickerstreak@64 442 for k, v in pairs(states) do
flickerstreak@64 443 local o = tonumber(tfetch(v, "rule", "order"))
flickerstreak@64 444 if o and k ~= opts.name then
flickerstreak@64 445 local obefore = tfetch(states,before,"rule","order")
flickerstreak@64 446 local oafter = tfetch(states,after,"rule","order")
flickerstreak@64 447 if o < opts.order and (not obefore or obefore < o) then
flickerstreak@64 448 before = k
flickerstreak@64 449 end
flickerstreak@64 450 if o > opts.order and (not oafter or oafter > o) then
flickerstreak@64 451 after = k
flickerstreak@64 452 end
flickerstreak@64 453 end
flickerstreak@64 454 end
flickerstreak@64 455 return before, after
flickerstreak@64 456 end
flickerstreak@64 457
flickerstreak@64 458 local function swapOrder( a, b )
flickerstreak@64 459 -- do options table
flickerstreak@64 460 local args = optionMap[bar].args
flickerstreak@64 461 args[a].order, args[b].order = args[b].order, args[a].order
flickerstreak@64 462 -- do profile
flickerstreak@64 463 a = tbuild(states, a, "rule")
flickerstreak@64 464 b = tbuild(states, b, "rule")
flickerstreak@64 465 a.order, b.order = b.order, a.order
flickerstreak@64 466 end
flickerstreak@64 467
flickerstreak@68 468 local function anchordisable()
flickerstreak@68 469 return not GetProperty(bar, opts.name, "enableanchor")
flickerstreak@64 470 end
flickerstreak@64 471
flickerstreak@68 472 tbuild(states, name)
flickerstreak@64 473
flickerstreak@68 474 opts.order = getrule("order")
flickerstreak@64 475 if opts.order == nil then
flickerstreak@64 476 -- add after the highest
flickerstreak@64 477 opts.order = 100
flickerstreak@64 478 for _, state in pairs(states) do
flickerstreak@64 479 local x = tonumber(tfetch(state, "rule", "order"))
flickerstreak@64 480 if x and x >= opts.order then
flickerstreak@64 481 opts.order = x + 1
flickerstreak@64 482 end
flickerstreak@64 483 end
flickerstreak@68 484 setrule("order",opts.order)
flickerstreak@64 485 end
flickerstreak@64 486
flickerstreak@64 487 opts.args = {
flickerstreak@68 488 ordering = {
flickerstreak@68 489 name = L["Info"],
flickerstreak@68 490 order = 1,
flickerstreak@64 491 type = "group",
flickerstreak@64 492 args = {
flickerstreak@64 493 delete = {
flickerstreak@68 494 name = L["Delete this State"],
flickerstreak@68 495 order = -1,
flickerstreak@64 496 type = "execute",
flickerstreak@64 497 func = function(info)
flickerstreak@68 498 if states[opts.name] then
flickerstreak@68 499 states[opts.name] = nil
flickerstreak@68 500 ApplyStates(bar)
flickerstreak@68 501 end
flickerstreak@64 502 optionMap[bar].args[opts.name] = nil
flickerstreak@64 503 end,
flickerstreak@64 504 },
flickerstreak@64 505 rename = {
flickerstreak@64 506 name = L["Name"],
flickerstreak@64 507 order = 1,
flickerstreak@68 508 type = "input",
flickerstreak@64 509 get = function() return opts.name end,
flickerstreak@64 510 set = function(info, value)
flickerstreak@64 511 -- check for existing state name
flickerstreak@64 512 if states[value] then
flickerstreak@64 513 L["State named '%s' already exists"]:format(value)
flickerstreak@64 514 end
flickerstreak@64 515 local args = optionMap[bar].args
flickerstreak@64 516 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil
flickerstreak@64 517 opts.name = value
flickerstreak@64 518 update()
flickerstreak@64 519 end,
flickerstreak@64 520 pattern = "^%w*$",
flickerstreak@64 521 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@64 522 },
flickerstreak@64 523 ordering = {
flickerstreak@64 524 name = L["Evaluation Order"],
flickerstreak@64 525 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
flickerstreak@64 526 order = 2,
flickerstreak@68 527 type = "group",
flickerstreak@68 528 inline = true,
flickerstreak@64 529 args = {
flickerstreak@64 530 up = {
flickerstreak@68 531 name = L["Up"],
flickerstreak@68 532 order = 1,
flickerstreak@64 533 type = "execute",
flickerstreak@64 534 width = "half",
flickerstreak@64 535 func = function()
flickerstreak@64 536 local before, after = getNeighbors()
flickerstreak@64 537 if before then
flickerstreak@64 538 swapOrder(before, opts.name)
flickerstreak@64 539 update()
flickerstreak@64 540 end
flickerstreak@64 541 end,
flickerstreak@64 542 },
flickerstreak@64 543 down = {
flickerstreak@68 544 name = L["Down"],
flickerstreak@68 545 order = 2,
flickerstreak@64 546 type = "execute",
flickerstreak@64 547 width = "half",
flickerstreak@64 548 func = function()
flickerstreak@64 549 local before, after = getNeighbors()
flickerstreak@64 550 if after then
flickerstreak@64 551 swapOrder(opts.name, after)
flickerstreak@64 552 update()
flickerstreak@64 553 end
flickerstreak@64 554 end,
flickerstreak@64 555 }
flickerstreak@64 556 }
flickerstreak@68 557 }
flickerstreak@64 558 }
flickerstreak@64 559 },
flickerstreak@68 560 properties = {
flickerstreak@68 561 name = L["Properties"],
flickerstreak@68 562 order = 2,
flickerstreak@68 563 type = "group",
flickerstreak@68 564 args = {
flickerstreak@68 565 desc = {
flickerstreak@68 566 name = L["Set the properties for the bar when in this state"],
flickerstreak@68 567 order = 1,
flickerstreak@68 568 type = "description"
flickerstreak@68 569 },
flickerstreak@68 570 hide = {
flickerstreak@68 571 name = L["Hide Bar"],
flickerstreak@68 572 order = 2,
flickerstreak@68 573 type = "toggle",
flickerstreak@68 574 set = setprop,
flickerstreak@68 575 get = getprop,
flickerstreak@68 576 },
flickerstreak@68 577 page = {
flickerstreak@68 578 name = L["Show Page #"],
flickerstreak@68 579 order = 3,
flickerstreak@68 580 type = "select",
flickerstreak@68 581 disabled = function()
flickerstreak@68 582 return bar:GetNumPages() < 2
flickerstreak@68 583 end,
flickerstreak@68 584 hidden = function()
flickerstreak@68 585 return bar:GetNumPages() < 2
flickerstreak@68 586 end,
flickerstreak@68 587 values = function()
flickerstreak@68 588 local pages = { none = " " }
flickerstreak@68 589 for i = 1, bar:GetNumPages() do
flickerstreak@68 590 pages[i] = i
flickerstreak@68 591 end
flickerstreak@68 592 return pages
flickerstreak@68 593 end,
flickerstreak@68 594 set = function(info, value)
flickerstreak@68 595 if value == "none" then
flickerstreak@68 596 setprop(info, nil)
flickerstreak@68 597 else
flickerstreak@68 598 setprop(info, value)
flickerstreak@68 599 end
flickerstreak@68 600 end,
flickerstreak@68 601 get = function(info)
flickerstreak@68 602 return getprop(info) or "none"
flickerstreak@68 603 end,
flickerstreak@68 604 },
flickerstreak@68 605 keybindstate = {
flickerstreak@68 606 name = L["Override Keybinds"],
flickerstreak@68 607 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
flickerstreak@68 608 order = 4,
flickerstreak@68 609 type = "toggle",
flickerstreak@68 610 set = setprop,
flickerstreak@68 611 get = getprop,
flickerstreak@68 612 },
flickerstreak@68 613 position = {
flickerstreak@68 614 name = L["Position"],
flickerstreak@68 615 order = 5,
flickerstreak@68 616 type = "group",
flickerstreak@68 617 inline = true,
flickerstreak@68 618 args = {
flickerstreak@68 619 enableanchor = {
flickerstreak@68 620 name = L["Set New Position"],
flickerstreak@68 621 order = 1,
flickerstreak@68 622 type = "toggle",
flickerstreak@68 623 set = setprop,
flickerstreak@68 624 get = getprop,
flickerstreak@68 625 },
flickerstreak@68 626 anchorPoint = {
flickerstreak@68 627 name = L["Point"],
flickerstreak@68 628 order = 2,
flickerstreak@68 629 type = "select",
flickerstreak@68 630 values = pointTable,
flickerstreak@68 631 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
flickerstreak@68 632 get = function(info) return getprop(info) or "NONE" end,
flickerstreak@68 633 disabled = anchordisable,
flickerstreak@68 634 hidden = anchordisable,
flickerstreak@68 635 },
flickerstreak@68 636 anchorRelPoint = {
flickerstreak@68 637 name = L["Relative Point"],
flickerstreak@68 638 order = 3,
flickerstreak@68 639 type = "select",
flickerstreak@68 640 values = pointTable,
flickerstreak@68 641 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
flickerstreak@68 642 get = function(info) return getprop(info) or "NONE" end,
flickerstreak@68 643 disabled = anchordisable,
flickerstreak@68 644 hidden = anchordisable,
flickerstreak@68 645 },
flickerstreak@68 646 anchorX = {
flickerstreak@68 647 name = L["X Offset"],
flickerstreak@68 648 order = 4,
flickerstreak@68 649 type = "range",
flickerstreak@68 650 min = -100,
flickerstreak@68 651 max = 100,
flickerstreak@68 652 step = 1,
flickerstreak@68 653 set = setprop,
flickerstreak@68 654 get = getprop,
flickerstreak@68 655 disabled = anchordisable,
flickerstreak@68 656 hidden = anchordisable,
flickerstreak@68 657 },
flickerstreak@68 658 anchorY = {
flickerstreak@68 659 name = L["Y Offset"],
flickerstreak@68 660 order = 5,
flickerstreak@68 661 type = "range",
flickerstreak@68 662 min = -100,
flickerstreak@68 663 max = 100,
flickerstreak@68 664 step = 1,
flickerstreak@68 665 set = setprop,
flickerstreak@68 666 get = getprop,
flickerstreak@68 667 disabled = anchordisable,
flickerstreak@68 668 hidden = anchordisable,
flickerstreak@68 669 },
flickerstreak@68 670 },
flickerstreak@68 671 },
flickerstreak@68 672 scale = {
flickerstreak@68 673 name = L["Scale"],
flickerstreak@68 674 order = 6,
flickerstreak@68 675 type = "group",
flickerstreak@68 676 inline = true,
flickerstreak@68 677 args = {
flickerstreak@68 678 enablescale = {
flickerstreak@68 679 name = L["Set New Scale"],
flickerstreak@68 680 order = 1,
flickerstreak@68 681 type = "toggle",
flickerstreak@68 682 set = setprop,
flickerstreak@68 683 get = getprop,
flickerstreak@68 684 },
flickerstreak@68 685 scale = {
flickerstreak@68 686 name = L["Scale"],
flickerstreak@68 687 order = 2,
flickerstreak@68 688 type = "range",
flickerstreak@68 689 min = 0.1,
flickerstreak@68 690 max = 2.5,
flickerstreak@68 691 step = 0.05,
flickerstreak@68 692 isPercent = true,
flickerstreak@68 693 set = setprop,
flickerstreak@68 694 get = function(info) return getprop(info) or 1 end,
flickerstreak@68 695 disabled = function() return not GetProperty(bar, opts.name, "enablescale") end,
flickerstreak@68 696 hidden = function() return not GetProperty(bar, opts.name, "enablescale") end,
flickerstreak@68 697 },
flickerstreak@68 698 },
flickerstreak@68 699 },
flickerstreak@68 700 },
flickerstreak@68 701 },
flickerstreak@64 702 rules = {
flickerstreak@70 703 name = L["Rule"],
flickerstreak@68 704 order = 3,
flickerstreak@64 705 type = "group",
flickerstreak@64 706 args = {
flickerstreak@64 707 mode = {
flickerstreak@68 708 name = L["Select this state"],
flickerstreak@68 709 order = 2,
flickerstreak@64 710 type = "select",
flickerstreak@64 711 style = "radio",
flickerstreak@64 712 values = {
flickerstreak@64 713 default = L["by default"],
flickerstreak@64 714 any = L["when ANY of these"],
flickerstreak@64 715 all = L["when ALL of these"],
flickerstreak@68 716 custom = L["via custom rule"],
flickerstreak@68 717 keybind = L["via keybinding"],
flickerstreak@64 718 },
flickerstreak@64 719 set = function( info, value )
flickerstreak@68 720 setrule("type", value)
flickerstreak@64 721 fixall()
flickerstreak@64 722 update()
flickerstreak@64 723 end,
flickerstreak@64 724 get = function( info )
flickerstreak@68 725 return getrule("type")
flickerstreak@64 726 end,
flickerstreak@64 727 },
flickerstreak@64 728 clear = {
flickerstreak@68 729 name = L["Clear All"],
flickerstreak@68 730 order = 3,
flickerstreak@64 731 type = "execute",
flickerstreak@68 732 hidden = function()
flickerstreak@68 733 local t = getrule("type")
flickerstreak@68 734 return t ~= "any" and t ~= "all"
flickerstreak@68 735 end,
flickerstreak@68 736 disabled = function()
flickerstreak@68 737 local t = getrule("type")
flickerstreak@68 738 return t ~= "any" and t ~= "all"
flickerstreak@68 739 end,
flickerstreak@64 740 func = function()
flickerstreak@68 741 local type = getrule("type")
flickerstreak@64 742 if type == "custom" then
flickerstreak@68 743 setrule("custom","")
flickerstreak@64 744 elseif type == "any" or type == "all" then
flickerstreak@68 745 setrule("values", {})
flickerstreak@64 746 end
flickerstreak@64 747 update()
flickerstreak@64 748 end,
flickerstreak@64 749 },
flickerstreak@64 750 inputs = {
flickerstreak@68 751 name = L["Conditions"],
flickerstreak@68 752 order = 4,
flickerstreak@64 753 type = "multiselect",
flickerstreak@64 754 hidden = function()
flickerstreak@68 755 local t = getrule("type")
flickerstreak@68 756 return t ~= "any" and t ~= "all"
flickerstreak@64 757 end,
flickerstreak@64 758 disabled = function()
flickerstreak@68 759 local t = getrule("type")
flickerstreak@68 760 return t ~= "any" and t ~= "all"
flickerstreak@64 761 end,
flickerstreak@64 762 values = ruleSelect,
flickerstreak@64 763 set = function(info, key, value )
flickerstreak@68 764 setrule(ruleMap[key], value or nil, "values")
flickerstreak@64 765 if value then
flickerstreak@64 766 fixall(ruleMap[key])
flickerstreak@64 767 end
flickerstreak@64 768 update()
flickerstreak@64 769 end,
flickerstreak@64 770 get = function(info, key)
flickerstreak@68 771 return getrule("values", ruleMap[key]) or false
flickerstreak@64 772 end,
flickerstreak@64 773 },
flickerstreak@64 774 custom = {
flickerstreak@68 775 name = L["Custom Rule"],
flickerstreak@68 776 order = 5,
flickerstreak@64 777 type = "input",
flickerstreak@64 778 multiline = true,
flickerstreak@64 779 hidden = function()
flickerstreak@68 780 return getrule("type") ~= "custom"
flickerstreak@64 781 end,
flickerstreak@64 782 disabled = function()
flickerstreak@68 783 return getrule("type") ~= "custom"
flickerstreak@64 784 end,
flickerstreak@64 785 desc = L["Syntax like macro rules: see preset rules for examples"],
flickerstreak@64 786 set = function(info, value)
flickerstreak@68 787 setrule("custom",value)
flickerstreak@64 788 update()
flickerstreak@64 789 end,
flickerstreak@64 790 get = function(info)
flickerstreak@68 791 return getrule("custom") or ""
flickerstreak@64 792 end,
flickerstreak@64 793 validate = function (info, rule)
flickerstreak@64 794 local s = rule:gsub("%s","") -- remove all spaces
flickerstreak@64 795 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
flickerstreak@64 796 repeat
flickerstreak@64 797 if s == "" then
flickerstreak@64 798 return true
flickerstreak@64 799 end
flickerstreak@64 800 local c, r = s:match("(%b[])(.*)")
flickerstreak@64 801 if c == nil and s and #s > 0 then
flickerstreak@64 802 return L["Invalid custom rule '%s': each clause must appear within [brackets]"]:format(rule)
flickerstreak@64 803 end
flickerstreak@64 804 s = r
flickerstreak@64 805 until c == nil
flickerstreak@64 806 return true
flickerstreak@64 807 end,
flickerstreak@68 808 },
flickerstreak@68 809 keybind = {
flickerstreak@68 810 name = L["Keybinding"],
flickerstreak@68 811 order = 6,
flickerstreak@68 812 inline = true,
flickerstreak@68 813 hidden = function() return getrule("type") ~= "keybind" end,
flickerstreak@68 814 disabled = function() return getrule("type") ~= "keybind" end,
flickerstreak@68 815 type = "group",
flickerstreak@68 816 args = {
flickerstreak@68 817 desc = {
flickerstreak@68 818 name = L["Invoking a state keybind overrides all other transition rules. Toggle the keybind again to remove the override and return to the specified toggle-off state."],
flickerstreak@68 819 order = 1,
flickerstreak@68 820 type = "description",
flickerstreak@68 821 },
flickerstreak@68 822 keybind = {
flickerstreak@68 823 name = L["State Hotkey"],
flickerstreak@68 824 desc = L["Define an override toggle keybind"],
flickerstreak@68 825 order = 2,
flickerstreak@68 826 type = "keybinding",
flickerstreak@68 827 set = function(info, value)
flickerstreak@70 828 if value and #value == 0 then
flickerstreak@70 829 value = nil
flickerstreak@70 830 end
flickerstreak@68 831 setrule("keybind",value)
flickerstreak@68 832 update()
flickerstreak@68 833 end,
flickerstreak@68 834 get = function() return getrule("keybind") end,
flickerstreak@68 835 },
flickerstreak@68 836 default = {
flickerstreak@68 837 name = L["Toggle Off State"],
flickerstreak@68 838 desc = L["Select a state to return to when the keybind override is toggled off"],
flickerstreak@68 839 order = 3,
flickerstreak@68 840 type = "select",
flickerstreak@68 841 values = function()
flickerstreak@68 842 local t = { }
flickerstreak@68 843 for k in pairs(states) do
flickerstreak@68 844 if k ~= opts.name then
flickerstreak@68 845 t[k] = k
flickerstreak@68 846 end
flickerstreak@68 847 end
flickerstreak@68 848 return t
flickerstreak@68 849 end,
flickerstreak@68 850 set = function(info, value)
flickerstreak@68 851 setrule("keybindreturn",value)
flickerstreak@68 852 update()
flickerstreak@68 853 end,
flickerstreak@68 854 get = function() return getrule("keybindreturn") end,
flickerstreak@68 855 },
flickerstreak@68 856 },
flickerstreak@68 857 },
flickerstreak@68 858 },
flickerstreak@68 859 },
flickerstreak@62 860 }
flickerstreak@62 861 return opts
flickerstreak@25 862 end
flickerstreak@62 863
flickerstreak@64 864
flickerstreak@62 865 CreateBarOptions = function(bar)
flickerstreak@62 866 local private = { }
flickerstreak@62 867 local options = {
flickerstreak@62 868 type = "group",
flickerstreak@62 869 name = L["Dynamic State"],
flickerstreak@64 870 childGroups = "tree",
flickerstreak@62 871 disabled = InCombatLockdown,
flickerstreak@62 872 args = {
flickerstreak@64 873 __desc__ = {
flickerstreak@68 874 name = L["States are evaluated in the order they are listed"],
flickerstreak@68 875 order = 1,
flickerstreak@64 876 type = "description",
flickerstreak@64 877 },
flickerstreak@64 878 __new__ = {
flickerstreak@64 879 name = L["New State..."],
flickerstreak@64 880 order = 2,
flickerstreak@68 881 type = "group",
flickerstreak@62 882 args = {
flickerstreak@64 883 name = {
flickerstreak@64 884 name = L["State Name"],
flickerstreak@64 885 desc = L["Set a name for the new state"],
flickerstreak@68 886 order = 1,
flickerstreak@68 887 type = "input",
flickerstreak@64 888 get = function() return private.newstatename or "" end,
flickerstreak@64 889 set = function(info,value) private.newstatename = value end,
flickerstreak@64 890 pattern = "^%w*$",
flickerstreak@64 891 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@64 892 },
flickerstreak@64 893 create = {
flickerstreak@68 894 name = L["Create State"],
flickerstreak@68 895 order = 2,
flickerstreak@64 896 type = "execute",
flickerstreak@64 897 func = function ()
flickerstreak@64 898 local name = private.newstatename
flickerstreak@68 899 if states[name] then
flickerstreak@68 900 ReAction:UserError(L["State named '%s' already exists"]:format(name))
flickerstreak@68 901 else
flickerstreak@68 902 -- TODO: select default state options and pass as final argument
flickerstreak@68 903 states[name] = { }
flickerstreak@68 904 optionMap[bar].args[name] = CreateStateOptions(bar,name)
flickerstreak@68 905 private.newstatename = ""
flickerstreak@68 906 end
flickerstreak@64 907 end,
flickerstreak@64 908 disabled = function()
flickerstreak@64 909 local name = private.newstatename or ""
flickerstreak@64 910 return #name == 0 or name:find("%W")
flickerstreak@64 911 end,
flickerstreak@62 912 }
flickerstreak@62 913 }
flickerstreak@64 914 }
flickerstreak@62 915 }
flickerstreak@62 916 }
flickerstreak@62 917 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@62 918 if states then
flickerstreak@62 919 for name, config in pairs(states) do
flickerstreak@64 920 options.args[name] = CreateStateOptions(bar,name)
flickerstreak@62 921 end
flickerstreak@62 922 end
flickerstreak@64 923 optionMap[bar] = options
flickerstreak@62 924 return options
flickerstreak@62 925 end
flickerstreak@25 926 end
flickerstreak@25 927
flickerstreak@62 928 function module:GetBarOptions(bar)
flickerstreak@64 929 return CreateBarOptions(bar)
flickerstreak@25 930 end