annotate modules/State.lua @ 145:42cade25d40d

Fixed display bugs with vehicle support
author Flick <flickerstreak@gmail.com>
date Mon, 27 Apr 2009 18:10:04 +0000
parents d186e041ca14
children 806a61b331a0
rev   line source
flickerstreak@109 1 --[[
flickerstreak@109 2 ReAction bar state driver interface
flickerstreak@109 3
flickerstreak@109 4 --]]
flickerstreak@109 5
flickerstreak@109 6 -- local imports
flickerstreak@109 7 local ReAction = ReAction
flickerstreak@109 8 local L = ReAction.L
flickerstreak@109 9 local _G = _G
flickerstreak@109 10 local format = string.format
flickerstreak@109 11 local InCombatLockdown = InCombatLockdown
flickerstreak@109 12 local RegisterStateDriver = RegisterStateDriver
flickerstreak@109 13
flickerstreak@109 14 ReAction:UpdateRevision("$Revision$")
flickerstreak@109 15
flickerstreak@109 16 -- module declaration
flickerstreak@109 17 local moduleID = "State"
flickerstreak@109 18 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
flickerstreak@109 19
flickerstreak@109 20 -- Utility --
flickerstreak@109 21
flickerstreak@109 22 -- traverse a table tree by key list and fetch the result or first nil
flickerstreak@109 23 local function tfetch(t, ...)
flickerstreak@109 24 for i = 1, select('#', ...) do
flickerstreak@109 25 t = t and t[select(i, ...)]
flickerstreak@109 26 end
flickerstreak@109 27 return t
flickerstreak@109 28 end
flickerstreak@109 29
flickerstreak@109 30 -- traverse a table tree by key list and build tree as necessary
flickerstreak@109 31 local function tbuild(t, ...)
flickerstreak@109 32 for i = 1, select('#', ...) do
flickerstreak@109 33 local key = select(i, ...)
flickerstreak@109 34 if not t[key] then t[key] = { } end
flickerstreak@109 35 t = t[key]
flickerstreak@109 36 end
flickerstreak@109 37 return t
flickerstreak@109 38 end
flickerstreak@109 39
flickerstreak@109 40 -- return a new array of keys of table 't', sorted by comparing
flickerstreak@109 41 -- sub-fields (obtained via tfetch) of the table values
flickerstreak@109 42 local function fieldsort( t, ... )
flickerstreak@109 43 local r = { }
flickerstreak@109 44 for k in pairs(t) do
flickerstreak@109 45 table.insert(r,k)
flickerstreak@109 46 end
flickerstreak@109 47 local path = { ... }
flickerstreak@109 48 table.sort(r, function(lhs, rhs)
flickerstreak@109 49 local olhs = tfetch(t[lhs], unpack(path)) or 0
flickerstreak@109 50 local orhs = tfetch(t[rhs], unpack(path)) or 0
flickerstreak@109 51 return olhs < orhs
flickerstreak@109 52 end)
flickerstreak@109 53 return r
flickerstreak@109 54 end
flickerstreak@109 55
flickerstreak@109 56 -- set a frame-ref, if the frame is valid, or set nil to the
flickerstreak@109 57 -- corresponding attribute
flickerstreak@109 58 local function SetFrameRef(frame, name, refFrame)
flickerstreak@109 59 if refFrame then
flickerstreak@109 60 local _, explicit = refFrame:IsProtected()
flickerstreak@109 61 if not explicit then
flickerstreak@109 62 refFrame = nil
flickerstreak@109 63 end
flickerstreak@109 64 end
flickerstreak@109 65 if refFrame then
flickerstreak@109 66 frame:SetFrameRef(name,refFrame)
flickerstreak@109 67 else
flickerstreak@109 68 frame:SetAttribute("frameref-"..name,nil)
flickerstreak@109 69 end
flickerstreak@109 70 end
flickerstreak@109 71
flickerstreak@109 72
flickerstreak@109 73 local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll
flickerstreak@109 74
flickerstreak@109 75 -- PRIVATE --
flickerstreak@109 76 do
flickerstreak@109 77
flickerstreak@109 78 -- the field names must match the field names of the options table, below
flickerstreak@109 79 -- the field values are secure snippets or 'true' to skip the snippet for that property.
flickerstreak@109 80 local properties = {
flickerstreak@109 81 hide =
flickerstreak@109 82 [[
flickerstreak@109 83 local h = hide and hide[state] and not showAll
flickerstreak@109 84 if h ~= hidden then
flickerstreak@109 85 if h then
flickerstreak@109 86 self:Hide()
flickerstreak@109 87 else
flickerstreak@109 88 self:Show()
flickerstreak@109 89 end
flickerstreak@109 90 hidden = h
flickerstreak@109 91 end
flickerstreak@109 92 if showAll then
flickerstreak@109 93 control:CallMethod("UpdateHiddenLabel", hide and hide[state])
flickerstreak@109 94 end
flickerstreak@109 95 ]],
flickerstreak@109 96
flickerstreak@109 97 --keybindState TODO: broken
flickerstreak@109 98
flickerstreak@109 99 anchorEnable =
flickerstreak@109 100 [[
flickerstreak@109 101 local old_anchor = anchorstate
flickerstreak@109 102 anchorstate = (anchorEnable and anchorEnable[state]) and state
flickerstreak@109 103 if old_anchor ~= anchorstate or not set_state then
flickerstreak@109 104 if anchorstate and anchorPoint then
flickerstreak@109 105 if anchorPoint[state] then
flickerstreak@109 106 self:ClearAllPoints()
flickerstreak@109 107 local f = self:GetAttribute("frameref-anchor-"..anchorstate)
flickerstreak@109 108 if f then
flickerstreak@109 109 self:SetPoint(anchorPoint[state], f, anchorRelPoint[state], anchorX[state], anchorY[state])
flickerstreak@109 110 end
flickerstreak@109 111 end
flickerstreak@109 112 elseif defaultAnchor and defaultAnchor.point then
flickerstreak@109 113 self:ClearAllPoints()
flickerstreak@109 114 self:SetPoint(defaultAnchor.point, defaultAnchor.frame,
flickerstreak@109 115 defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
flickerstreak@109 116 end
flickerstreak@109 117 end
flickerstreak@109 118 ]],
flickerstreak@109 119 -- anchorEnable handles all the other bits
flickerstreak@109 120 anchorFrame = true,
flickerstreak@109 121 anchorPoint = true,
flickerstreak@109 122 anchorRelPoint = true,
flickerstreak@109 123 anchorX = true,
flickerstreak@109 124 anchorY = true,
flickerstreak@109 125
flickerstreak@109 126
flickerstreak@109 127 enableScale =
flickerstreak@109 128 [[
flickerstreak@109 129 local old_scale = scalestate
flickerstreak@109 130 scalestate = (enableScale and enableScale[state]) and state
flickerstreak@109 131 if old_scale ~= scalestate or not set_state then
flickerstreak@109 132 if scalestate and scale then
flickerstreak@109 133 if scale[state] then
flickerstreak@109 134 self:SetScale(scale[state])
flickerstreak@109 135 end
flickerstreak@109 136 else
flickerstreak@109 137 self:SetScale(1.0)
flickerstreak@109 138 end
flickerstreak@109 139 end
flickerstreak@109 140 ]],
flickerstreak@109 141 -- enableScale handles scale
flickerstreak@109 142 scale = true,
flickerstreak@109 143
flickerstreak@109 144 enableAlpha =
flickerstreak@109 145 [[
flickerstreak@109 146 local old_alpha = alphastate
flickerstreak@109 147 alphastate = (enableAlpha and enableAlpha[state]) and state
flickerstreak@109 148 if old_alpha ~= alphastate or not set_state then
flickerstreak@109 149 control:CallMethod("UpdateAlpha", alphastate and alpha[state] or defaultAlpha)
flickerstreak@109 150 end
flickerstreak@109 151 ]],
flickerstreak@109 152 -- enableAlpha handles alpha
flickerstreak@109 153 alpha = true,
flickerstreak@109 154 }
flickerstreak@109 155
flickerstreak@109 156 local weak = { __mode = "k" }
flickerstreak@109 157 local statedrivers = setmetatable( { }, weak )
flickerstreak@109 158 local keybinds = setmetatable( { }, weak )
flickerstreak@109 159
flickerstreak@109 160 --
flickerstreak@109 161 -- Secure Handler Snippets
flickerstreak@109 162 --
flickerstreak@109 163 local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState
flickerstreak@109 164 do
flickerstreak@109 165 local stateHandler_propInit =
flickerstreak@109 166 [[
flickerstreak@109 167 propfuncs = table.new()
flickerstreak@109 168 local proplist = self:GetAttribute("prop-func-list")
flickerstreak@109 169 for s in string.gmatch(proplist, "(%w+)") do
flickerstreak@109 170 table.insert(propfuncs, s)
flickerstreak@109 171 end
flickerstreak@109 172 ]]
flickerstreak@109 173
flickerstreak@109 174 local onStateHandler =
flickerstreak@109 175 -- function _onstate-reaction( self, stateid, newstate )
flickerstreak@109 176 [[
flickerstreak@109 177 set_state = newstate
flickerstreak@109 178
flickerstreak@109 179 local oldState = state
flickerstreak@109 180 state = state_override or set_state or state
flickerstreak@109 181 for i = 1, #propfuncs do
flickerstreak@109 182 control:RunAttribute("func-"..propfuncs[i])
flickerstreak@109 183 end
flickerstreak@109 184
flickerstreak@109 185 control:ChildUpdate()
flickerstreak@109 186
flickerstreak@109 187 if oldState ~= state then
flickerstreak@109 188 control:CallMethod("StateRefresh", state)
flickerstreak@109 189 end
flickerstreak@109 190 ]]
flickerstreak@109 191
flickerstreak@109 192 local onClickHandler =
flickerstreak@109 193 -- function OnClick( self, button, down )
flickerstreak@109 194 [[
flickerstreak@109 195 if state_override == button then
flickerstreak@109 196 state_override = nil -- toggle
flickerstreak@109 197 else
flickerstreak@109 198 state_override = button
flickerstreak@109 199 end
flickerstreak@109 200 ]] .. onStateHandler
flickerstreak@109 201
flickerstreak@109 202 local function UpdateAlpha( frame, alpha )
flickerstreak@109 203 if alpha then
flickerstreak@109 204 frame:SetAlpha(alpha)
flickerstreak@109 205 end
flickerstreak@109 206 end
flickerstreak@109 207
flickerstreak@109 208 -- Construct a lua assignment as a code string and execute it within the header
flickerstreak@109 209 -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called
flickerstreak@109 210 -- with four arguments, then it treats 'varname' as an existing global table and
flickerstreak@109 211 -- sets a key-value pair. For a slight efficiency boost, pass the values in as
flickerstreak@109 212 -- attributes and fetch them as attributes from the snippet code, to leverage snippet
flickerstreak@109 213 -- caching.
flickerstreak@109 214 function SetHandlerData( bar, varname, value, key )
flickerstreak@109 215 local f = bar:GetFrame()
flickerstreak@109 216 f:SetAttribute("data-varname",varname)
flickerstreak@109 217 f:SetAttribute("data-value", value)
flickerstreak@109 218 f:SetAttribute("data-key", key)
flickerstreak@109 219 f:Execute(
flickerstreak@109 220 [[
flickerstreak@109 221 local name = self:GetAttribute("data-varname")
flickerstreak@109 222 local value = self:GetAttribute("data-value")
flickerstreak@109 223 local key = self:GetAttribute("data-key")
flickerstreak@109 224 if name then
flickerstreak@109 225 if key then
flickerstreak@109 226 if not _G[name] then
flickerstreak@109 227 _G[name] = table.new()
flickerstreak@109 228 end
flickerstreak@109 229 _G[name][key] = value
flickerstreak@109 230 else
flickerstreak@109 231 _G[name] = value
flickerstreak@109 232 end
flickerstreak@109 233 end
flickerstreak@109 234 ]])
flickerstreak@109 235 end
flickerstreak@109 236
flickerstreak@109 237 function SetDefaultAnchor( bar )
flickerstreak@109 238 local point, frame, relPoint, x, y = bar:GetAnchor()
flickerstreak@109 239 SetHandlerData(bar, "defaultAnchor", point, "point")
flickerstreak@109 240 SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint")
flickerstreak@109 241 SetHandlerData(bar, "defaultAnchor", x, "x")
flickerstreak@109 242 SetHandlerData(bar, "defaultAnchor", y, "y")
flickerstreak@109 243 SetHandlerData(bar, "defaultAlpha", bar:GetAlpha())
flickerstreak@109 244
flickerstreak@109 245 local f = bar:GetFrame()
flickerstreak@109 246 f.UpdateAlpha = UpdateAlpha
flickerstreak@109 247 SetFrameRef(f, "defaultAnchor", _G[frame or "UIParent"])
flickerstreak@109 248 f:Execute(
flickerstreak@109 249 [[
flickerstreak@109 250 defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
flickerstreak@109 251 ]])
flickerstreak@109 252 end
flickerstreak@109 253
flickerstreak@109 254 function RefreshState( bar )
flickerstreak@109 255 SetDefaultAnchor(bar)
flickerstreak@109 256 bar:GetFrame():Execute(
flickerstreak@109 257 [[
flickerstreak@109 258 if self:GetAttribute("reaction-refresh") then
flickerstreak@109 259 control:RunAttribute("reaction-refresh")
flickerstreak@109 260 end
flickerstreak@109 261 ]])
flickerstreak@109 262 end
flickerstreak@109 263
flickerstreak@109 264 function SetStateDriver( bar, rule )
flickerstreak@109 265 local f = bar:GetFrame()
flickerstreak@109 266
flickerstreak@109 267 if not f.UpdateHiddenLabel then
flickerstreak@109 268 function f:UpdateHiddenLabel(hide)
flickerstreak@109 269 bar:SetLabelSubtext( hide and L["Hidden"] )
flickerstreak@109 270 end
flickerstreak@109 271 end
flickerstreak@109 272
flickerstreak@109 273 function f:StateRefresh( state )
flickerstreak@109 274 bar:RefreshControls()
flickerstreak@109 275 end
flickerstreak@109 276
flickerstreak@109 277 local props = { }
flickerstreak@109 278 for p, h in pairs(properties) do
flickerstreak@109 279 if type(h) == "string" then
flickerstreak@109 280 table.insert(props,p)
flickerstreak@109 281 f:SetAttribute("func-"..p, h)
flickerstreak@109 282 end
flickerstreak@109 283 end
flickerstreak@109 284 f:SetAttribute("prop-func-list", table.concat(props," "))
flickerstreak@109 285 f:Execute(stateHandler_propInit)
flickerstreak@109 286 f:SetAttribute("reaction-refresh", onStateHandler)
flickerstreak@109 287
flickerstreak@109 288 if rule and #rule > 0 then
flickerstreak@109 289 f:SetAttribute( "_onstate-reaction", onStateHandler )
flickerstreak@109 290 RegisterStateDriver(f, "reaction", rule)
flickerstreak@109 291 statedrivers[bar] = rule
flickerstreak@109 292 elseif statedrivers[bar] then
flickerstreak@109 293 UnregisterStateDriver(f, "reaction")
flickerstreak@109 294 f:SetAttribute( "_onstate-reaction", nil )
flickerstreak@109 295 statedrivers[bar] = nil
flickerstreak@109 296 end
flickerstreak@109 297 end
flickerstreak@109 298
flickerstreak@109 299 function SetStateKeybind( bar, key, state )
flickerstreak@109 300 local f = bar:GetFrame()
flickerstreak@109 301
flickerstreak@109 302 local kb = keybinds[bar]
flickerstreak@109 303 if kb == nil then
flickerstreak@109 304 if key == nil then
flickerstreak@109 305 -- nothing to do
flickerstreak@109 306 return
flickerstreak@109 307 end
flickerstreak@109 308 kb = { }
flickerstreak@109 309 keybinds[bar] = kb
flickerstreak@109 310 end
flickerstreak@109 311
flickerstreak@109 312 -- clear the old binding, if any
flickerstreak@109 313 if kb[state] then
flickerstreak@109 314 SetOverrideBinding(f, false, kb[state], nil)
flickerstreak@109 315 end
flickerstreak@109 316 kb[state] = key
flickerstreak@109 317
flickerstreak@109 318 if key then
flickerstreak@109 319 f:SetAttribute("_onclick", onClickHandler)
flickerstreak@109 320 SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button
flickerstreak@109 321 end
flickerstreak@109 322 end
flickerstreak@109 323 end
flickerstreak@109 324
flickerstreak@118 325 local playerClass = select(2, UnitClass("player"))
flickerstreak@118 326 local function ClassFilter(...)
flickerstreak@118 327 for i = 1, select('#',...) do
flickerstreak@118 328 if playerClass == select(i,...) then
flickerstreak@118 329 return false
flickerstreak@118 330 end
flickerstreak@118 331 end
flickerstreak@118 332 return true
flickerstreak@118 333 end
flickerstreak@118 334
flickerstreak@109 335 -- As far as I can tell the macro clauses are NOT locale-specific.
flickerstreak@118 336 -- 'filter' specifies whether rules should be omitted from execution.
flickerstreak@118 337 -- 'true' indicates they should be filtered out.
flickerstreak@109 338 local ruleformats = {
flickerstreak@118 339 stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@118 340 nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@118 341 shadowform = { format = "form:1", filter = ClassFilter("PRIEST") },
flickerstreak@118 342 noshadowform = { format = "noform", filter = ClassFilter("PRIEST") },
flickerstreak@118 343 battle = { format = "stance:1", filter = ClassFilter("WARRIOR") },
flickerstreak@118 344 defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") },
flickerstreak@118 345 berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") },
flickerstreak@118 346 caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
flickerstreak@118 347 bear = { format = "form:1", filter = ClassFilter("DRUID") },
flickerstreak@118 348 cat = { format = "form:1", filter = ClassFilter("DRUID") },
flickerstreak@118 349 tree = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@118 350 moonkin = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@118 351 pet = { format = "pet" },
flickerstreak@118 352 nopet = { format = "nopet" },
flickerstreak@118 353 harm = { format = "target=target,harm" },
flickerstreak@118 354 help = { format = "target=target,help" },
flickerstreak@118 355 notarget = { format = "target=target,noexists" },
flickerstreak@118 356 focusharm = { format = "target=focus,harm" },
flickerstreak@118 357 focushelp = { format = "target=focus,help" },
flickerstreak@118 358 nofocus = { format = "target=focus,noexists" },
flickerstreak@118 359 raid = { format = "group:raid" },
flickerstreak@118 360 party = { format = "group:party" },
flickerstreak@118 361 solo = { format = "nogroup" },
flickerstreak@118 362 combat = { format = "combat" },
flickerstreak@118 363 nocombat = { format = "nocombat" },
flickerstreak@118 364 possess = { format = "bonusbar:5" },
flickerstreak@145 365 vehicle = { format = "target=vehicle,exists,bonusbar:5" },
flickerstreak@109 366 }
flickerstreak@109 367
flickerstreak@109 368 -- Have to do these shenanigans instead of hardcoding the stances/forms because the
flickerstreak@109 369 -- ordering varies if the character is missing a form. For warriors this is rarely
flickerstreak@109 370 -- a problem (c'mon, who actually skips the level 10 def stance quest?) but for druids
flickerstreak@109 371 -- it can be. Some people never bother to do the aquatic form quest until well past
flickerstreak@109 372 -- when they get cat form, and stance 5/6 can be flight, tree, or moonkin depending
flickerstreak@109 373 -- on talents.
flickerstreak@109 374 function InitRules()
flickerstreak@109 375 local forms = { }
flickerstreak@109 376 -- sort by icon since it's locale-independent
flickerstreak@109 377 for i = 1, GetNumShapeshiftForms() do
flickerstreak@109 378 local icon, name, active = GetShapeshiftFormInfo(i)
flickerstreak@134 379 -- if it's the current form, the icon is wrong (Spell_Nature_WispSplode)
flickerstreak@109 380 -- so capture it from the spell info directly
flickerstreak@109 381 if active then
flickerstreak@109 382 local _1, _2
flickerstreak@109 383 _1, _2, icon = GetSpellInfo(name)
flickerstreak@109 384 end
flickerstreak@109 385 forms[icon] = i;
flickerstreak@109 386 end
flickerstreak@109 387 -- use 9 if not found since 9 is never a valid stance/form
flickerstreak@109 388 local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9
flickerstreak@109 389 local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
flickerstreak@109 390 local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
flickerstreak@109 391 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
flickerstreak@109 392 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
flickerstreak@109 393 local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
flickerstreak@109 394 local tree = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9
flickerstreak@109 395 local moonkin = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
flickerstreak@109 396 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
flickerstreak@109 397
flickerstreak@118 398 ruleformats.defensive.format = format("stance:%d",defensive)
flickerstreak@118 399 ruleformats.berserker.format = format("stance:%d",berserker)
flickerstreak@118 400 ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight)
flickerstreak@118 401 ruleformats.bear.format = format("form:%d",bear)
flickerstreak@118 402 ruleformats.cat.format = format("form:%d",cat)
flickerstreak@118 403 ruleformats.tree.format = format("form:%d",tree)
flickerstreak@118 404 ruleformats.moonkin.format = format("form:%d",moonkin)
flickerstreak@109 405 end
flickerstreak@109 406
flickerstreak@109 407 local function BuildRule(states)
flickerstreak@109 408 local rules = { }
flickerstreak@109 409 local default
flickerstreak@109 410
flickerstreak@109 411 for idx, state in ipairs(fieldsort(states, "rule", "order")) do
flickerstreak@109 412 local c = states[state].rule
flickerstreak@109 413 local type = c.type
flickerstreak@109 414 if type == "default" then
flickerstreak@109 415 default = default or state
flickerstreak@109 416 elseif type == "custom" then
flickerstreak@109 417 if c.custom then
flickerstreak@109 418 -- strip out all spaces from the custom rule
flickerstreak@109 419 table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
flickerstreak@109 420 end
flickerstreak@109 421 elseif type == "any" or type == "all" then
flickerstreak@109 422 if c.values then
flickerstreak@109 423 local clauses = { }
flickerstreak@109 424 for key, value in pairs(c.values) do
flickerstreak@118 425 if ruleformats[key] and not ruleformats[key].filter then
flickerstreak@118 426 table.insert(clauses, ruleformats[key].format)
flickerstreak@118 427 end
flickerstreak@109 428 end
flickerstreak@109 429 if #clauses > 0 then
flickerstreak@109 430 local sep = (type == "any") and "][" or ","
flickerstreak@109 431 table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
flickerstreak@109 432 end
flickerstreak@109 433 end
flickerstreak@109 434 end
flickerstreak@109 435 end
flickerstreak@109 436 -- make sure that the default, if any, is last
flickerstreak@109 437 if default then
flickerstreak@109 438 table.insert(rules, default)
flickerstreak@109 439 end
flickerstreak@109 440 return table.concat(rules,";")
flickerstreak@109 441 end
flickerstreak@109 442
flickerstreak@109 443 local function BuildKeybinds( bar, states )
flickerstreak@109 444 for name, state in pairs(states) do
flickerstreak@109 445 local type = tfetch(state, "rule", "type")
flickerstreak@109 446 if type == "keybind" then
flickerstreak@109 447 local key = tfetch(state, "rule", "keybind")
flickerstreak@109 448 SetStateKeybind(bar, key, name)
flickerstreak@109 449 else
flickerstreak@109 450 SetStateKeybind(bar, nil, name) -- this clears an existing keybind
flickerstreak@109 451 end
flickerstreak@109 452 end
flickerstreak@109 453 end
flickerstreak@109 454
flickerstreak@109 455 function GetProperty( bar, state, propname )
flickerstreak@109 456 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
flickerstreak@109 457 end
flickerstreak@109 458
flickerstreak@109 459 function SetProperty( bar, state, propname, value )
flickerstreak@109 460 local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state)
flickerstreak@109 461 s[propname] = value
flickerstreak@109 462 SetHandlerData(bar, propname, value, state)
flickerstreak@109 463 RefreshState(bar)
flickerstreak@109 464 end
flickerstreak@109 465
flickerstreak@109 466 function RegisterProperty( propname, snippet )
flickerstreak@109 467 properties[propname] = snippet or true
flickerstreak@109 468 for _, bar in ReAction:IterateBars() do
flickerstreak@109 469 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@109 470 if states then
flickerstreak@109 471 for name, s in pairs(states) do
flickerstreak@109 472 SetHandlerData(bar, propname, s[propname], name)
flickerstreak@109 473 end
flickerstreak@109 474 SetStateDriver(bar, BuildRule(states))
flickerstreak@109 475 RefreshState(bar)
flickerstreak@109 476 end
flickerstreak@109 477 end
flickerstreak@109 478 end
flickerstreak@109 479
flickerstreak@109 480 function UnregisterProperty( propname )
flickerstreak@109 481 properties[propname] = nil
flickerstreak@109 482 for _, bar in ReAction:IterateBars() do
flickerstreak@109 483 SetHandlerData(bar, propname, nil)
flickerstreak@109 484 SetStateDriver(bar, BuildRule(states))
flickerstreak@109 485 RefreshState(bar)
flickerstreak@109 486 end
flickerstreak@109 487 end
flickerstreak@109 488
flickerstreak@109 489 function ApplyStates( bar )
flickerstreak@109 490 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@109 491 if states then
flickerstreak@109 492 for propname in pairs(properties) do
flickerstreak@109 493 for name, s in pairs(states) do
flickerstreak@109 494 if propname == "anchorFrame" then
flickerstreak@109 495 SetFrameRef(bar:GetFrame(), "anchor-"..name, _G[s.anchorFrame])
flickerstreak@109 496 else
flickerstreak@109 497 SetHandlerData(bar, propname, s[propname], name)
flickerstreak@109 498 end
flickerstreak@109 499 end
flickerstreak@109 500 end
flickerstreak@109 501 BuildKeybinds(bar, states)
flickerstreak@109 502 SetHandlerData(bar, "showAll", ReAction:GetConfigMode())
flickerstreak@109 503 SetStateDriver(bar, BuildRule(states))
flickerstreak@109 504 RefreshState(bar)
flickerstreak@109 505 end
flickerstreak@109 506 end
flickerstreak@109 507
flickerstreak@109 508 function CleanupStates( bar )
flickerstreak@109 509 SetStateDriver(bar, nil)
flickerstreak@109 510 end
flickerstreak@109 511
flickerstreak@109 512 function ShowAll( bar, show )
flickerstreak@109 513 if statedrivers[bar] then
flickerstreak@109 514 SetHandlerData(bar, "showAll", show)
flickerstreak@109 515 RefreshState(bar)
flickerstreak@109 516 end
flickerstreak@109 517 end
flickerstreak@109 518 end
flickerstreak@109 519
flickerstreak@109 520
flickerstreak@109 521
flickerstreak@109 522 -- module event handlers --
flickerstreak@109 523
flickerstreak@109 524 function module:OnInitialize()
flickerstreak@109 525 self.db = ReAction.db:RegisterNamespace( moduleID,
flickerstreak@109 526 {
flickerstreak@109 527 profile = {
flickerstreak@109 528 bars = { },
flickerstreak@109 529 }
flickerstreak@109 530 }
flickerstreak@109 531 )
flickerstreak@109 532
flickerstreak@109 533 self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
flickerstreak@109 534
flickerstreak@109 535 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
flickerstreak@109 536
flickerstreak@109 537 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar")
flickerstreak@109 538 ReAction.RegisterCallback(self, "OnDestroyBar")
flickerstreak@109 539 ReAction.RegisterCallback(self, "OnRefreshBar")
flickerstreak@109 540 ReAction.RegisterCallback(self, "OnEraseBar")
flickerstreak@109 541 ReAction.RegisterCallback(self, "OnRenameBar")
flickerstreak@109 542 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@109 543 end
flickerstreak@109 544
flickerstreak@109 545 function module:OnEnable()
flickerstreak@109 546 self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui
flickerstreak@109 547 end
flickerstreak@109 548
flickerstreak@109 549 function module:UPDATE_SHAPESHIFT_FORMS()
flickerstreak@109 550 -- Re-parse the rules table according to the new form list.
flickerstreak@109 551 -- This happens both at initial login (after PLAYER_ENTERING_WORLD)
flickerstreak@109 552 -- as well as when gaining new abilities.
flickerstreak@109 553 InitRules()
flickerstreak@109 554 for name, bar in ReAction:IterateBars() do
flickerstreak@109 555 self:OnRefreshBar(nil,bar,name)
flickerstreak@109 556 end
flickerstreak@109 557 end
flickerstreak@109 558
flickerstreak@109 559 function module:OnRefreshBar(event, bar, name)
flickerstreak@109 560 local c = self.db.profile.bars[name]
flickerstreak@109 561 if c then
flickerstreak@109 562 ApplyStates(bar)
flickerstreak@109 563 end
flickerstreak@109 564 end
flickerstreak@109 565
flickerstreak@109 566 function module:OnDestroyBar(event, bar, name)
flickerstreak@109 567 CleanupStates(bar)
flickerstreak@109 568 end
flickerstreak@109 569
flickerstreak@109 570 function module:OnEraseBar(event, bar, name)
flickerstreak@109 571 self.db.profile.bars[name] = nil
flickerstreak@109 572 end
flickerstreak@109 573
flickerstreak@109 574 function module:OnRenameBar(event, bar, oldname, newname)
flickerstreak@109 575 local bars = self.db.profile.bars
flickerstreak@109 576 bars[newname], bars[oldname] = bars[oldname], nil
flickerstreak@109 577 end
flickerstreak@109 578
flickerstreak@109 579 function module:OnConfigModeChanged(event, mode)
flickerstreak@109 580 for name, bar in ReAction:IterateBars() do
flickerstreak@109 581 if self.db.profile.bars[name] then
flickerstreak@109 582 ShowAll(bar, mode)
flickerstreak@109 583 end
flickerstreak@109 584 end
flickerstreak@109 585 end
flickerstreak@109 586
flickerstreak@109 587
flickerstreak@109 588
flickerstreak@109 589 -- Options --
flickerstreak@109 590
flickerstreak@109 591 local CreateBarOptions, RegisterPropertyOptions
flickerstreak@109 592 do
flickerstreak@109 593 -- pre-sorted by the order they should appear in
flickerstreak@109 594 local rules = {
flickerstreak@118 595 -- rule fields
flickerstreak@118 596 { "stance", { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
flickerstreak@118 597 { "form", { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } },
flickerstreak@118 598 { "stealth", { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
flickerstreak@118 599 { "shadow", { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
flickerstreak@118 600 { "pet", { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
flickerstreak@118 601 { "target", { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
flickerstreak@118 602 { "focus", { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
flickerstreak@118 603 { "possess", { {possess = L["Mind Control"]} } },
flickerstreak@118 604 { "vehicle", { {vehicle = L["In a Vehicle"]} } },
flickerstreak@118 605 { "group", { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
flickerstreak@118 606 { "combat", { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
flickerstreak@109 607 }
flickerstreak@109 608
flickerstreak@109 609 local ruleSelect = { }
flickerstreak@109 610 local ruleMap = { }
flickerstreak@109 611 local optionMap = setmetatable({},{__mode="k"})
flickerstreak@109 612
flickerstreak@109 613 local pointTable = {
flickerstreak@109 614 NONE = " ",
flickerstreak@109 615 CENTER = L["Center"],
flickerstreak@109 616 LEFT = L["Left"],
flickerstreak@109 617 RIGHT = L["Right"],
flickerstreak@109 618 TOP = L["Top"],
flickerstreak@109 619 BOTTOM = L["Bottom"],
flickerstreak@109 620 TOPLEFT = L["Top Left"],
flickerstreak@109 621 TOPRIGHT = L["Top Right"],
flickerstreak@109 622 BOTTOMLEFT = L["Bottom Left"],
flickerstreak@109 623 BOTTOMRIGHT = L["Bottom Right"],
flickerstreak@109 624 }
flickerstreak@109 625
flickerstreak@109 626 -- unpack rules table into ruleSelect and ruleMap
flickerstreak@109 627 for _, c in ipairs(rules) do
flickerstreak@118 628 local rule, fields = unpack(c)
flickerstreak@118 629 for _, field in ipairs(fields) do
flickerstreak@118 630 local key, label = next(field)
flickerstreak@118 631 table.insert(ruleSelect, label)
flickerstreak@118 632 table.insert(ruleMap, key)
flickerstreak@109 633 end
flickerstreak@109 634 end
flickerstreak@109 635
flickerstreak@109 636 local stateOptions = {
flickerstreak@109 637 ordering = {
flickerstreak@109 638 name = L["Info"],
flickerstreak@109 639 order = 1,
flickerstreak@109 640 type = "group",
flickerstreak@109 641 args = {
flickerstreak@109 642 delete = {
flickerstreak@109 643 name = L["Delete this State"],
flickerstreak@109 644 order = -1,
flickerstreak@109 645 type = "execute",
flickerstreak@109 646 func = "DeleteState",
flickerstreak@109 647 },
flickerstreak@109 648 rename = {
flickerstreak@109 649 name = L["Name"],
flickerstreak@109 650 order = 1,
flickerstreak@109 651 type = "input",
flickerstreak@109 652 get = "GetName",
flickerstreak@109 653 set = "SetStateName",
flickerstreak@109 654 pattern = "^%w*$",
flickerstreak@109 655 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@109 656 },
flickerstreak@109 657 ordering = {
flickerstreak@109 658 name = L["Evaluation Order"],
flickerstreak@109 659 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
flickerstreak@109 660 order = 2,
flickerstreak@109 661 type = "group",
flickerstreak@109 662 inline = true,
flickerstreak@109 663 args = {
flickerstreak@109 664 up = {
flickerstreak@109 665 name = L["Up"],
flickerstreak@109 666 order = 1,
flickerstreak@109 667 type = "execute",
flickerstreak@109 668 width = "half",
flickerstreak@109 669 func = "MoveStateUp",
flickerstreak@109 670 },
flickerstreak@109 671 down = {
flickerstreak@109 672 name = L["Down"],
flickerstreak@109 673 order = 2,
flickerstreak@109 674 type = "execute",
flickerstreak@109 675 width = "half",
flickerstreak@109 676 func = "MoveStateDown",
flickerstreak@109 677 }
flickerstreak@109 678 }
flickerstreak@109 679 }
flickerstreak@109 680 }
flickerstreak@109 681 },
flickerstreak@109 682 properties = {
flickerstreak@109 683 name = L["Properties"],
flickerstreak@109 684 order = 2,
flickerstreak@109 685 type = "group",
flickerstreak@109 686 args = {
flickerstreak@109 687 desc = {
flickerstreak@109 688 name = L["Set the properties for the bar when in this state"],
flickerstreak@109 689 order = 1,
flickerstreak@109 690 type = "description"
flickerstreak@109 691 },
flickerstreak@109 692 hide = {
flickerstreak@109 693 name = L["Hide Bar"],
flickerstreak@109 694 order = 90,
flickerstreak@109 695 type = "toggle",
flickerstreak@109 696 set = "SetProp",
flickerstreak@109 697 get = "GetProp",
flickerstreak@109 698 },
flickerstreak@109 699 --[[ BROKEN
flickerstreak@109 700 keybindState = {
flickerstreak@109 701 name = L["Override Keybinds"],
flickerstreak@109 702 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
flickerstreak@109 703 order = 91,
flickerstreak@109 704 type = "toggle",
flickerstreak@109 705 set = "SetProp",
flickerstreak@109 706 get = "GetProp",
flickerstreak@109 707 }, ]]
flickerstreak@109 708 position = {
flickerstreak@109 709 name = L["Position"],
flickerstreak@109 710 order = 92,
flickerstreak@109 711 type = "group",
flickerstreak@109 712 inline = true,
flickerstreak@109 713 args = {
flickerstreak@109 714 anchorEnable = {
flickerstreak@109 715 name = L["Reposition"],
flickerstreak@109 716 order = 1,
flickerstreak@109 717 type = "toggle",
flickerstreak@109 718 set = "SetProp",
flickerstreak@109 719 get = "GetProp",
flickerstreak@109 720 },
flickerstreak@109 721 anchorFrame = {
flickerstreak@109 722 name = L["Anchor Frame"],
flickerstreak@109 723 order = 2,
flickerstreak@109 724 type = "select",
flickerstreak@109 725 values = "GetAnchorFrames",
flickerstreak@109 726 set = "SetAnchorFrame",
flickerstreak@109 727 get = "GetAnchorFrame",
flickerstreak@109 728 disabled = "GetAnchorDisabled",
flickerstreak@109 729 hidden = "GetAnchorDisabled",
flickerstreak@109 730 },
flickerstreak@109 731 anchorPoint = {
flickerstreak@109 732 name = L["Point"],
flickerstreak@109 733 order = 3,
flickerstreak@109 734 type = "select",
flickerstreak@109 735 values = pointTable,
flickerstreak@109 736 set = "SetAnchorPointProp",
flickerstreak@109 737 get = "GetAnchorPointProp",
flickerstreak@109 738 disabled = "GetAnchorDisabled",
flickerstreak@109 739 hidden = "GetAnchorDisabled",
flickerstreak@109 740 },
flickerstreak@109 741 anchorRelPoint = {
flickerstreak@109 742 name = L["Relative Point"],
flickerstreak@109 743 order = 4,
flickerstreak@109 744 type = "select",
flickerstreak@109 745 values = pointTable,
flickerstreak@109 746 set = "SetAnchorPointProp",
flickerstreak@109 747 get = "GetAnchorPointProp",
flickerstreak@109 748 disabled = "GetAnchorDisabled",
flickerstreak@109 749 hidden = "GetAnchorDisabled",
flickerstreak@109 750 },
flickerstreak@109 751 anchorX = {
flickerstreak@109 752 name = L["X Offset"],
flickerstreak@109 753 order = 5,
flickerstreak@109 754 type = "range",
flickerstreak@109 755 min = -100,
flickerstreak@109 756 max = 100,
flickerstreak@109 757 step = 1,
flickerstreak@109 758 set = "SetProp",
flickerstreak@109 759 get = "GetProp",
flickerstreak@109 760 disabled = "GetAnchorDisabled",
flickerstreak@109 761 hidden = "GetAnchorDisabled",
flickerstreak@109 762 },
flickerstreak@109 763 anchorY = {
flickerstreak@109 764 name = L["Y Offset"],
flickerstreak@109 765 order = 6,
flickerstreak@109 766 type = "range",
flickerstreak@109 767 min = -100,
flickerstreak@109 768 max = 100,
flickerstreak@109 769 step = 1,
flickerstreak@109 770 set = "SetProp",
flickerstreak@109 771 get = "GetProp",
flickerstreak@109 772 disabled = "GetAnchorDisabled",
flickerstreak@109 773 hidden = "GetAnchorDisabled",
flickerstreak@109 774 },
flickerstreak@109 775 },
flickerstreak@109 776 },
flickerstreak@109 777 scale = {
flickerstreak@109 778 name = L["Scale"],
flickerstreak@109 779 order = 93,
flickerstreak@109 780 type = "group",
flickerstreak@109 781 inline = true,
flickerstreak@109 782 args = {
flickerstreak@109 783 enableScale = {
flickerstreak@109 784 name = L["Set New Scale"],
flickerstreak@109 785 order = 1,
flickerstreak@109 786 type = "toggle",
flickerstreak@109 787 set = "SetProp",
flickerstreak@109 788 get = "GetProp",
flickerstreak@109 789 },
flickerstreak@109 790 scale = {
flickerstreak@109 791 name = L["Scale"],
flickerstreak@109 792 order = 2,
flickerstreak@109 793 type = "range",
flickerstreak@109 794 min = 0.25,
flickerstreak@109 795 max = 2.5,
flickerstreak@109 796 step = 0.05,
flickerstreak@109 797 isPercent = true,
flickerstreak@109 798 set = "SetProp",
flickerstreak@109 799 get = "GetScale",
flickerstreak@109 800 disabled = "GetScaleDisabled",
flickerstreak@109 801 hidden = "GetScaleDisabled",
flickerstreak@109 802 },
flickerstreak@109 803 },
flickerstreak@109 804 },
flickerstreak@109 805 alpha = {
flickerstreak@109 806 name = L["Transparency"],
flickerstreak@109 807 order = 94,
flickerstreak@109 808 type = "group",
flickerstreak@109 809 inline = true,
flickerstreak@109 810 args = {
flickerstreak@109 811 enableAlpha = {
flickerstreak@109 812 name = L["Set Transparency"],
flickerstreak@109 813 order = 1,
flickerstreak@109 814 type = "toggle",
flickerstreak@109 815 set = "SetProp",
flickerstreak@109 816 get = "GetProp",
flickerstreak@109 817 },
flickerstreak@109 818 alpha = {
flickerstreak@109 819 name = L["Transparency"],
flickerstreak@109 820 order = 2,
flickerstreak@109 821 type = "range",
flickerstreak@109 822 min = 0,
flickerstreak@109 823 max = 1,
flickerstreak@109 824 step = 0.01,
flickerstreak@109 825 bigStep = 0.05,
flickerstreak@109 826 isPercent = true,
flickerstreak@109 827 set = "SetProp",
flickerstreak@109 828 get = "GetAlpha",
flickerstreak@109 829 disabled = "GetAlphaDisabled",
flickerstreak@109 830 hidden = "GetAlphaDisabled",
flickerstreak@109 831 },
flickerstreak@109 832 },
flickerstreak@109 833 },
flickerstreak@109 834 },
flickerstreak@109 835 plugins = { }
flickerstreak@109 836 },
flickerstreak@109 837 rules = {
flickerstreak@109 838 name = L["Rule"],
flickerstreak@109 839 order = 3,
flickerstreak@109 840 type = "group",
flickerstreak@109 841 args = {
flickerstreak@109 842 mode = {
flickerstreak@109 843 name = L["Select this state"],
flickerstreak@109 844 order = 2,
flickerstreak@109 845 type = "select",
flickerstreak@109 846 style = "radio",
flickerstreak@109 847 values = {
flickerstreak@109 848 default = L["by default"],
flickerstreak@109 849 any = L["when ANY of these"],
flickerstreak@109 850 all = L["when ALL of these"],
flickerstreak@109 851 custom = L["via custom rule"],
flickerstreak@109 852 keybind = L["via keybinding"],
flickerstreak@109 853 },
flickerstreak@109 854 set = "SetType",
flickerstreak@109 855 get = "GetType",
flickerstreak@109 856 },
flickerstreak@109 857 clear = {
flickerstreak@109 858 name = L["Clear All"],
flickerstreak@109 859 order = 3,
flickerstreak@109 860 type = "execute",
flickerstreak@109 861 hidden = "GetClearAllDisabled",
flickerstreak@109 862 disabled = "GetClearAllDisabled",
flickerstreak@109 863 func = "ClearAllConditions",
flickerstreak@109 864 },
flickerstreak@109 865 inputs = {
flickerstreak@109 866 name = L["Conditions"],
flickerstreak@109 867 order = 4,
flickerstreak@109 868 type = "multiselect",
flickerstreak@109 869 hidden = "GetConditionsDisabled",
flickerstreak@109 870 disabled = "GetConditionsDisabled",
flickerstreak@109 871 values = ruleSelect,
flickerstreak@109 872 set = "SetCondition",
flickerstreak@109 873 get = "GetCondition",
flickerstreak@109 874 },
flickerstreak@109 875 custom = {
flickerstreak@109 876 name = L["Custom Rule"],
flickerstreak@109 877 order = 5,
flickerstreak@109 878 type = "input",
flickerstreak@109 879 multiline = true,
flickerstreak@109 880 hidden = "GetCustomDisabled",
flickerstreak@109 881 disabled = "GetCustomDisabled",
flickerstreak@109 882 desc = L["Syntax like macro rules: see preset rules for examples"],
flickerstreak@109 883 set = "SetCustomRule",
flickerstreak@109 884 get = "GetCustomRule",
flickerstreak@109 885 validate = "ValidateCustomRule",
flickerstreak@109 886 },
flickerstreak@109 887 keybind = {
flickerstreak@109 888 name = L["Keybinding"],
flickerstreak@109 889 order = 6,
flickerstreak@109 890 inline = true,
flickerstreak@109 891 hidden = "GetKeybindDisabled",
flickerstreak@109 892 disabled = "GetKeybindDisabled",
flickerstreak@109 893 type = "group",
flickerstreak@109 894 args = {
flickerstreak@109 895 desc = {
flickerstreak@109 896 name = L["Invoking a state keybind toggles an override of all other transition rules."],
flickerstreak@109 897 order = 1,
flickerstreak@109 898 type = "description",
flickerstreak@109 899 },
flickerstreak@109 900 keybind = {
flickerstreak@109 901 name = L["State Hotkey"],
flickerstreak@109 902 desc = L["Define an override toggle keybind"],
flickerstreak@109 903 order = 2,
flickerstreak@109 904 type = "keybinding",
flickerstreak@109 905 set = "SetKeybind",
flickerstreak@109 906 get = "GetKeybind",
flickerstreak@109 907 },
flickerstreak@109 908 },
flickerstreak@109 909 },
flickerstreak@109 910 },
flickerstreak@109 911 },
flickerstreak@109 912 }
flickerstreak@109 913
flickerstreak@109 914 local handlers = { }
flickerstreak@109 915 local meta = {
flickerstreak@109 916 __index = function(self, key)
flickerstreak@109 917 for _, h in pairs(handlers) do
flickerstreak@109 918 if h[key] then
flickerstreak@109 919 return h[key]
flickerstreak@109 920 end
flickerstreak@109 921 end
flickerstreak@109 922 end,
flickerstreak@109 923 }
flickerstreak@109 924 local StateHandler = setmetatable({ }, meta)
flickerstreak@109 925 local proto = { __index = StateHandler }
flickerstreak@109 926
flickerstreak@109 927 function RegisterPropertyOptions( field, options, handler )
flickerstreak@109 928 stateOptions.properties.plugins[field] = options
flickerstreak@109 929 handlers[field] = handler
flickerstreak@109 930 end
flickerstreak@109 931
flickerstreak@109 932 function UnregisterPropertyOptions( field )
flickerstreak@109 933 stateOptions.properties.plugins[field] = nil
flickerstreak@109 934 handlers[field] = nil
flickerstreak@109 935 end
flickerstreak@109 936
flickerstreak@109 937 function StateHandler:New( bar, opts )
flickerstreak@109 938 local self = setmetatable(
flickerstreak@109 939 {
flickerstreak@109 940 bar = bar
flickerstreak@109 941 },
flickerstreak@109 942 proto )
flickerstreak@109 943
flickerstreak@109 944 function self:GetName()
flickerstreak@109 945 return opts.name
flickerstreak@109 946 end
flickerstreak@109 947
flickerstreak@109 948 function self:SetName(name)
flickerstreak@109 949 opts.name = name
flickerstreak@109 950 end
flickerstreak@109 951
flickerstreak@109 952 function self:GetOrder()
flickerstreak@109 953 return opts.order
flickerstreak@109 954 end
flickerstreak@109 955
flickerstreak@109 956 -- get reference to states table: even if the bar
flickerstreak@109 957 -- name changes the states table ref won't
flickerstreak@109 958 self.states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@109 959 self.state = tbuild(self.states, opts.name)
flickerstreak@109 960
flickerstreak@109 961 opts.order = self:GetRuleField("order")
flickerstreak@109 962 if opts.order == nil then
flickerstreak@109 963 -- add after the highest
flickerstreak@109 964 opts.order = 100
flickerstreak@109 965 for _, state in pairs(self.states) do
flickerstreak@109 966 local x = tonumber(tfetch(state, "rule", "order"))
flickerstreak@109 967 if x and x >= opts.order then
flickerstreak@109 968 opts.order = x + 1
flickerstreak@109 969 end
flickerstreak@109 970 end
flickerstreak@109 971 self:SetRuleField("order",opts.order)
flickerstreak@109 972 end
flickerstreak@109 973
flickerstreak@109 974 return self
flickerstreak@109 975 end
flickerstreak@109 976
flickerstreak@109 977 -- helper methods
flickerstreak@109 978
flickerstreak@109 979 function StateHandler:SetRuleField( key, value, ... )
flickerstreak@109 980 tbuild(self.state, "rule", ...)[key] = value
flickerstreak@109 981 end
flickerstreak@109 982
flickerstreak@109 983 function StateHandler:GetRuleField( ... )
flickerstreak@109 984 return tfetch(self.state, "rule", ...)
flickerstreak@109 985 end
flickerstreak@109 986
flickerstreak@109 987 function StateHandler:FixAll( setkey )
flickerstreak@109 988 -- if multiple selections in the same group are chosen when 'all' is selected,
flickerstreak@109 989 -- keep only one of them. If changing the mode, the first in the fields list will
flickerstreak@109 990 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
flickerstreak@109 991 -- it will be retained.
flickerstreak@109 992 local notified = false
flickerstreak@109 993 if self:GetRuleField("type") == "all" then
flickerstreak@109 994 for _, c in ipairs(rules) do
flickerstreak@118 995 local rule, fields = unpack(c)
flickerstreak@109 996 local once = false
flickerstreak@109 997 if setkey then
flickerstreak@109 998 for idx, field in ipairs(fields) do
flickerstreak@109 999 if next(field) == setkey then
flickerstreak@109 1000 once = true
flickerstreak@109 1001 end
flickerstreak@109 1002 end
flickerstreak@109 1003 end
flickerstreak@109 1004 for idx, field in ipairs(fields) do
flickerstreak@109 1005 local key = next(field)
flickerstreak@109 1006 if self:GetRuleField("values",key) then
flickerstreak@109 1007 if once and key ~= setkey then
flickerstreak@109 1008 self:SetRuleField(key,false,"values")
flickerstreak@109 1009 if not setkey and not notified then
flickerstreak@109 1010 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
flickerstreak@109 1011 notified = true
flickerstreak@109 1012 end
flickerstreak@109 1013 end
flickerstreak@109 1014 once = true
flickerstreak@109 1015 end
flickerstreak@109 1016 end
flickerstreak@109 1017 end
flickerstreak@109 1018 end
flickerstreak@109 1019 end
flickerstreak@109 1020
flickerstreak@109 1021 function StateHandler:GetNeighbors()
flickerstreak@109 1022 local before, after
flickerstreak@109 1023 for k, v in pairs(self.states) do
flickerstreak@109 1024 local o = tonumber(tfetch(v, "rule", "order"))
flickerstreak@109 1025 if o and k ~= self:GetName() then
flickerstreak@109 1026 local obefore = tfetch(self.states,before,"rule","order")
flickerstreak@109 1027 local oafter = tfetch(self.states,after,"rule","order")
flickerstreak@109 1028 if o < self:GetOrder() and (not obefore or obefore < o) then
flickerstreak@109 1029 before = k
flickerstreak@109 1030 end
flickerstreak@109 1031 if o > self:GetOrder() and (not oafter or oafter > o) then
flickerstreak@109 1032 after = k
flickerstreak@109 1033 end
flickerstreak@109 1034 end
flickerstreak@109 1035 end
flickerstreak@109 1036 return before, after
flickerstreak@109 1037 end
flickerstreak@109 1038
flickerstreak@109 1039 function StateHandler:SwapOrder( a, b )
flickerstreak@109 1040 -- do options table
flickerstreak@109 1041 local args = optionMap[self.bar].args
flickerstreak@109 1042 args[a].order, args[b].order = args[b].order, args[a].order
flickerstreak@109 1043 -- do profile
flickerstreak@109 1044 a = tbuild(self.states, a, "rule")
flickerstreak@109 1045 b = tbuild(self.states, b, "rule")
flickerstreak@109 1046 a.order, b.order = b.order, a.order
flickerstreak@109 1047 end
flickerstreak@109 1048
flickerstreak@109 1049 -- handler methods
flickerstreak@109 1050
flickerstreak@109 1051 function StateHandler:GetProp( info )
flickerstreak@109 1052 -- gets property of the same name as the options arg
flickerstreak@109 1053 return GetProperty(self.bar, self:GetName(), info[#info])
flickerstreak@109 1054 end
flickerstreak@109 1055
flickerstreak@109 1056 function StateHandler:SetProp( info, value )
flickerstreak@109 1057 -- sets property of the same name as the options arg
flickerstreak@109 1058 SetProperty(self.bar, self:GetName(), info[#info], value)
flickerstreak@109 1059 end
flickerstreak@109 1060
flickerstreak@109 1061 function StateHandler:DeleteState()
flickerstreak@109 1062 if self.states[self:GetName()] then
flickerstreak@109 1063 self.states[self:GetName()] = nil
flickerstreak@109 1064 ApplyStates(self.bar)
flickerstreak@109 1065 end
flickerstreak@109 1066 optionMap[self.bar].args[self:GetName()] = nil
flickerstreak@109 1067 end
flickerstreak@109 1068
flickerstreak@109 1069 function StateHandler:SetStateName(info, value)
flickerstreak@109 1070 -- check for existing state name
flickerstreak@109 1071 if self.states[value] then
flickerstreak@109 1072 ReAction:UserError(format(L["State named '%s' already exists"],value))
flickerstreak@109 1073 return
flickerstreak@109 1074 end
flickerstreak@109 1075 local args = optionMap[self.bar].args
flickerstreak@109 1076 local name = self:GetName()
flickerstreak@109 1077 self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil
flickerstreak@109 1078 self:SetName(value)
flickerstreak@109 1079 ApplyStates(self.bar)
flickerstreak@109 1080 ReAction:ShowEditor(self.bar, moduleID, value)
flickerstreak@109 1081 end
flickerstreak@109 1082
flickerstreak@109 1083 function StateHandler:MoveStateUp()
flickerstreak@109 1084 local before, after = self:GetNeighbors()
flickerstreak@109 1085 if before then
flickerstreak@109 1086 self:SwapOrder(before, self:GetName())
flickerstreak@109 1087 ApplyStates(self.bar)
flickerstreak@109 1088 end
flickerstreak@109 1089 end
flickerstreak@109 1090
flickerstreak@109 1091 function StateHandler:MoveStateDown()
flickerstreak@109 1092 local before, after = self:GetNeighbors()
flickerstreak@109 1093 if after then
flickerstreak@109 1094 self:SwapOrder(self:GetName(), after)
flickerstreak@109 1095 ApplyStates(self.bar)
flickerstreak@109 1096 end
flickerstreak@109 1097 end
flickerstreak@109 1098
flickerstreak@109 1099 function StateHandler:GetAnchorDisabled()
flickerstreak@109 1100 return not GetProperty(self.bar, self:GetName(), "anchorEnable")
flickerstreak@109 1101 end
flickerstreak@109 1102
flickerstreak@109 1103 function StateHandler:GetAnchorFrames(info)
flickerstreak@109 1104 self._anchorframes = self._anchorframes or { }
flickerstreak@109 1105 table.wipe(self._anchorframes)
flickerstreak@109 1106
flickerstreak@109 1107 table.insert(self._anchorframes, "UIParent")
flickerstreak@109 1108 for name, bar in ReAction:IterateBars() do
flickerstreak@109 1109 table.insert(self._anchorframes, bar:GetFrame():GetName())
flickerstreak@109 1110 end
flickerstreak@109 1111 return self._anchorframes
flickerstreak@109 1112 end
flickerstreak@109 1113
flickerstreak@109 1114 function StateHandler:GetAnchorFrame(info)
flickerstreak@109 1115 local value = self:GetProp(info)
flickerstreak@109 1116 for k,v in pairs(self._anchorframes) do
flickerstreak@109 1117 if v == value then
flickerstreak@109 1118 return k
flickerstreak@109 1119 end
flickerstreak@109 1120 end
flickerstreak@109 1121 end
flickerstreak@109 1122
flickerstreak@109 1123 function StateHandler:SetAnchorFrame(info, value)
flickerstreak@109 1124 local f = _G[self._anchorframes[value]]
flickerstreak@109 1125 if f then
flickerstreak@109 1126 SetFrameRef(self.bar:GetFrame(), "anchor-"..self:GetName(), f)
flickerstreak@109 1127 self:SetProp(info, f:GetName())
flickerstreak@109 1128 end
flickerstreak@109 1129 end
flickerstreak@109 1130
flickerstreak@109 1131 function StateHandler:SetAnchorPointProp(info, value)
flickerstreak@109 1132 self:SetProp(info, value ~= "NONE" and value or nil)
flickerstreak@109 1133 end
flickerstreak@109 1134
flickerstreak@109 1135 function StateHandler:GetAnchorPointProp(info)
flickerstreak@109 1136 return self:GetProp(info) or "NONE"
flickerstreak@109 1137 end
flickerstreak@109 1138
flickerstreak@109 1139 function StateHandler:GetScale(info)
flickerstreak@109 1140 return self:GetProp(info) or 1.0
flickerstreak@109 1141 end
flickerstreak@109 1142
flickerstreak@109 1143 function StateHandler:GetScaleDisabled()
flickerstreak@109 1144 return not GetProperty(self.bar, self:GetName(), "enableScale")
flickerstreak@109 1145 end
flickerstreak@109 1146
flickerstreak@109 1147 function StateHandler:GetAlpha(info)
flickerstreak@109 1148 return self:GetProp(info) or 1.0
flickerstreak@109 1149 end
flickerstreak@109 1150
flickerstreak@109 1151 function StateHandler:GetAlphaDisabled()
flickerstreak@109 1152 return not GetProperty(self.bar, self:GetName(), "enableAlpha")
flickerstreak@109 1153 end
flickerstreak@109 1154
flickerstreak@109 1155 function StateHandler:SetType(info, value)
flickerstreak@109 1156 self:SetRuleField("type", value)
flickerstreak@109 1157 self:FixAll()
flickerstreak@109 1158 ApplyStates(self.bar)
flickerstreak@109 1159 end
flickerstreak@109 1160
flickerstreak@109 1161 function StateHandler:GetType()
flickerstreak@109 1162 return self:GetRuleField("type")
flickerstreak@109 1163 end
flickerstreak@109 1164
flickerstreak@109 1165 function StateHandler:GetClearAllDisabled()
flickerstreak@109 1166 local t = self:GetRuleField("type")
flickerstreak@109 1167 return not( t == "any" or t == "all" or t == "custom")
flickerstreak@109 1168 end
flickerstreak@109 1169
flickerstreak@109 1170 function StateHandler:ClearAllConditions()
flickerstreak@109 1171 local t = self:GetRuleField("type")
flickerstreak@109 1172 if t == "custom" then
flickerstreak@109 1173 self:SetRuleField("custom","")
flickerstreak@109 1174 elseif t == "any" or t == "all" then
flickerstreak@109 1175 self:SetRuleField("values", {})
flickerstreak@109 1176 end
flickerstreak@109 1177 ApplyStates(self.bar)
flickerstreak@109 1178 end
flickerstreak@109 1179
flickerstreak@109 1180 function StateHandler:GetConditionsDisabled()
flickerstreak@109 1181 local t = self:GetRuleField("type")
flickerstreak@109 1182 return not( t == "any" or t == "all")
flickerstreak@109 1183 end
flickerstreak@109 1184
flickerstreak@109 1185 function StateHandler:SetCondition(info, key, value)
flickerstreak@109 1186 self:SetRuleField(ruleMap[key], value or nil, "values")
flickerstreak@109 1187 if value then
flickerstreak@109 1188 self:FixAll(ruleMap[key])
flickerstreak@109 1189 end
flickerstreak@109 1190 ApplyStates(self.bar)
flickerstreak@109 1191 end
flickerstreak@109 1192
flickerstreak@109 1193 function StateHandler:GetCondition(info, key)
flickerstreak@109 1194 return self:GetRuleField("values", ruleMap[key]) or false
flickerstreak@109 1195 end
flickerstreak@109 1196
flickerstreak@109 1197 function StateHandler:GetCustomDisabled()
flickerstreak@109 1198 return self:GetRuleField("type") ~= "custom"
flickerstreak@109 1199 end
flickerstreak@109 1200
flickerstreak@109 1201 function StateHandler:SetCustomRule(info, value)
flickerstreak@109 1202 self:SetRuleField("custom",value)
flickerstreak@109 1203 ApplyStates(self.bar)
flickerstreak@109 1204 end
flickerstreak@109 1205
flickerstreak@109 1206 function StateHandler:GetCustomRule()
flickerstreak@109 1207 return self:GetRuleField("custom") or ""
flickerstreak@109 1208 end
flickerstreak@109 1209
flickerstreak@109 1210 function StateHandler:ValidateCustomRule(info, value)
flickerstreak@109 1211 local s = value:gsub("%s","") -- remove all spaces
flickerstreak@109 1212 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
flickerstreak@109 1213 repeat
flickerstreak@109 1214 if s == "" then
flickerstreak@109 1215 return true
flickerstreak@109 1216 end
flickerstreak@109 1217 local c, r = s:match("(%b[])(.*)")
flickerstreak@109 1218 if c == nil and s and #s > 0 then
flickerstreak@109 1219 return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "")
flickerstreak@109 1220 end
flickerstreak@109 1221 s = r
flickerstreak@109 1222 until c == nil
flickerstreak@109 1223 return true
flickerstreak@109 1224 end
flickerstreak@109 1225
flickerstreak@109 1226 function StateHandler:GetKeybindDisabled()
flickerstreak@109 1227 return self:GetRuleField("type") ~= "keybind"
flickerstreak@109 1228 end
flickerstreak@109 1229
flickerstreak@109 1230 function StateHandler:GetKeybind()
flickerstreak@109 1231 return self:GetRuleField("keybind")
flickerstreak@109 1232 end
flickerstreak@109 1233
flickerstreak@109 1234 function StateHandler:SetKeybind(info, value)
flickerstreak@109 1235 if value and #value == 0 then
flickerstreak@109 1236 value = nil
flickerstreak@109 1237 end
flickerstreak@109 1238 self:SetRuleField("keybind",value)
flickerstreak@109 1239 ApplyStates(self.bar)
flickerstreak@109 1240 end
flickerstreak@109 1241
flickerstreak@109 1242 local function CreateStateOptions(bar, name)
flickerstreak@109 1243 local opts = {
flickerstreak@109 1244 type = "group",
flickerstreak@109 1245 name = name,
flickerstreak@109 1246 childGroups = "tab",
flickerstreak@109 1247 args = stateOptions
flickerstreak@109 1248 }
flickerstreak@109 1249
flickerstreak@109 1250 opts.handler = StateHandler:New(bar,opts)
flickerstreak@109 1251
flickerstreak@109 1252 return opts
flickerstreak@109 1253 end
flickerstreak@109 1254
flickerstreak@109 1255 function module:GetBarOptions(bar)
flickerstreak@109 1256 local private = { }
flickerstreak@109 1257 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@109 1258 local options = {
flickerstreak@109 1259 name = L["Dynamic State"],
flickerstreak@109 1260 type = "group",
flickerstreak@109 1261 order = -1,
flickerstreak@109 1262 childGroups = "tree",
flickerstreak@109 1263 disabled = InCombatLockdown,
flickerstreak@109 1264 args = {
flickerstreak@109 1265 __desc__ = {
flickerstreak@109 1266 name = L["States are evaluated in the order they are listed"],
flickerstreak@109 1267 order = 1,
flickerstreak@109 1268 type = "description",
flickerstreak@109 1269 },
flickerstreak@109 1270 __new__ = {
flickerstreak@109 1271 name = L["New State..."],
flickerstreak@109 1272 order = 2,
flickerstreak@109 1273 type = "group",
flickerstreak@109 1274 args = {
flickerstreak@109 1275 name = {
flickerstreak@109 1276 name = L["State Name"],
flickerstreak@109 1277 desc = L["Set a name for the new state"],
flickerstreak@109 1278 order = 1,
flickerstreak@109 1279 type = "input",
flickerstreak@109 1280 get = function() return private.newstatename or "" end,
flickerstreak@109 1281 set = function(info,value) private.newstatename = value end,
flickerstreak@109 1282 pattern = "^%w*$",
flickerstreak@109 1283 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@109 1284 },
flickerstreak@109 1285 create = {
flickerstreak@109 1286 name = L["Create State"],
flickerstreak@109 1287 order = 2,
flickerstreak@109 1288 type = "execute",
flickerstreak@109 1289 func = function ()
flickerstreak@109 1290 local name = private.newstatename
flickerstreak@109 1291 if states[name] then
flickerstreak@109 1292 ReAction:UserError(format(L["State named '%s' already exists"],name))
flickerstreak@109 1293 else
flickerstreak@109 1294 -- TODO: select default state options and pass as final argument
flickerstreak@109 1295 states[name] = { }
flickerstreak@109 1296 optionMap[bar].args[name] = CreateStateOptions(bar,name)
flickerstreak@109 1297 ReAction:ShowEditor(bar, moduleID, name)
flickerstreak@109 1298 private.newstatename = ""
flickerstreak@109 1299 end
flickerstreak@109 1300 end,
flickerstreak@109 1301 disabled = function()
flickerstreak@109 1302 local name = private.newstatename or ""
flickerstreak@109 1303 return #name == 0 or name:find("%W")
flickerstreak@109 1304 end,
flickerstreak@109 1305 }
flickerstreak@109 1306 }
flickerstreak@109 1307 }
flickerstreak@109 1308 }
flickerstreak@109 1309 }
flickerstreak@109 1310 for name, config in pairs(states) do
flickerstreak@109 1311 options.args[name] = CreateStateOptions(bar,name)
flickerstreak@109 1312 end
flickerstreak@109 1313 optionMap[bar] = options
flickerstreak@109 1314 return options
flickerstreak@109 1315 end
flickerstreak@109 1316 end
flickerstreak@109 1317
flickerstreak@109 1318 -- Module API --
flickerstreak@109 1319
flickerstreak@109 1320 -- Pass in a property field-name, an implementation secure snippet, a static options table, and an
flickerstreak@109 1321 -- optional options handler method-table
flickerstreak@109 1322 --
flickerstreak@109 1323 -- The options table is static, i.e. not bar-specific and should only reference handler method
flickerstreak@109 1324 -- strings (either existing ones or those added via optHandler). The existing options are ordered
flickerstreak@109 1325 -- 90-99. Order #1 is reserved for the heading.
flickerstreak@109 1326 --
flickerstreak@109 1327 -- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable.
flickerstreak@109 1328 -- See above, for existing API. In particular see the properties set up in the New method: self.bar,
flickerstreak@109 1329 -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp().
flickerstreak@109 1330 --
flickerstreak@109 1331 function module:RegisterStateProperty( field, snippetHandler, options, optHandler )
flickerstreak@109 1332 RegisterProperty(field, snippetHandler)
flickerstreak@109 1333 RegisterPropertyOptions(field, options, optHandler)
flickerstreak@109 1334 end
flickerstreak@109 1335
flickerstreak@109 1336 function module:UnregisterStateProperty( field )
flickerstreak@109 1337 UnregisterProperty(field)
flickerstreak@109 1338 UnregisterPropertyOptions(field)
flickerstreak@109 1339 end
flickerstreak@109 1340
flickerstreak@109 1341
flickerstreak@109 1342 -- Export methods to Bar class --
flickerstreak@109 1343
flickerstreak@109 1344 function ReAction.Bar:GetState()
flickerstreak@134 1345 local env = GetManagedEnvironment(self:GetFrame())
flickerstreak@134 1346 return env and env.state
flickerstreak@109 1347 end
flickerstreak@109 1348
flickerstreak@109 1349 ReAction.Bar.GetStateProperty = GetProperty
flickerstreak@109 1350 ReAction.Bar.SetStateProperty = SetProperty