annotate classes/Bar.lua @ 170:0030201b5fc2

use secure HasAction() (drycoded)
author Flick <flickerstreak@gmail.com>
date Tue, 19 Oct 2010 20:07:55 +0000
parents 8cc187143acd
children fe0c7be6f6ef
rev   line source
flickerstreak@122 1 local ReAction = ReAction
flickerstreak@122 2 local L = ReAction.L
flickerstreak@122 3 local _G = _G
flickerstreak@122 4 local CreateFrame = CreateFrame
flickerstreak@122 5 local floor = math.floor
flickerstreak@122 6 local fmod = math.fmod
flickerstreak@122 7 local format = string.format
flickerstreak@122 8
flickerstreak@147 9 local KB = LibStub("LibKeyBound-1.0")
flickerstreak@147 10
flickerstreak@155 11 ---- Secure snippets ----
flickerstreak@155 12 local _reaction_init =
flickerstreak@155 13 [[
flickerstreak@155 14 anchorKeys = newtable("point","relPoint","x","y")
flickerstreak@155 15
flickerstreak@155 16 state = nil
flickerstreak@155 17 set_state = nil
flickerstreak@155 18 state_override = nil
flickerstreak@156 19 unit_exists = nil
flickerstreak@155 20
flickerstreak@155 21 showAll = false
flickerstreak@155 22 hidden = false
flickerstreak@155 23
flickerstreak@155 24 defaultAlpha = 1.0
flickerstreak@155 25 defaultScale = 1.0
flickerstreak@155 26 defaultAnchor = newtable()
flickerstreak@155 27
flickerstreak@155 28 activeStates = newtable()
flickerstreak@155 29 settings = newtable()
flickerstreak@155 30 extensions = newtable()
flickerstreak@155 31 ]]
flickerstreak@155 32
flickerstreak@155 33 local _reaction_refresh =
flickerstreak@155 34 [[
flickerstreak@155 35 local oldState = state
flickerstreak@155 36 state = state_override or set_state or state
flickerstreak@155 37
flickerstreak@156 38 local hide = nil
flickerstreak@155 39 if state then
flickerstreak@155 40 local settings = settings[state]
flickerstreak@155 41 if settings then
flickerstreak@155 42 -- show/hide
flickerstreak@156 43 hide = settings.hide
flickerstreak@155 44 -- re-anchor
flickerstreak@155 45 local old_anchor = activeStates.anchor
flickerstreak@155 46 activeStates.anchor = settings.anchorEnable and state
flickerstreak@155 47 if old_anchor ~= activeStates.anchor or not set_state then
flickerstreak@155 48 if activeStates.anchor then
flickerstreak@155 49 if settings.anchorPoint then
flickerstreak@155 50 self:ClearAllPoints()
flickerstreak@155 51 local f = self:GetAttribute("frameref-anchor-"..state)
flickerstreak@155 52 if f then
flickerstreak@155 53 self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY)
flickerstreak@155 54 end
flickerstreak@155 55 end
flickerstreak@155 56 elseif defaultAnchor.point then
flickerstreak@155 57 self:ClearAllPoints()
flickerstreak@155 58 self:SetPoint(defaultAnchor.point, defaultAnchor.frame,
flickerstreak@155 59 defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
flickerstreak@155 60 end
flickerstreak@155 61 end
flickerstreak@155 62 -- re-scale
flickerstreak@155 63 local old_scale = activeStates.scale
flickerstreak@155 64 activeStates.scale = settings.enableScale and state
flickerstreak@155 65 if old_scale ~= activeStates.scale or not set_state then
flickerstreak@155 66 self:SetScale(activeStates.scale and settings.scale or defaultScale)
flickerstreak@155 67 end
flickerstreak@155 68 -- alpha
flickerstreak@155 69 local old_alpha = activeStates.alpha
flickerstreak@155 70 activeStates.alpha = settings.enableAlpha and state
flickerstreak@155 71 if old_alpha ~= activeStates.alpha or not set_state then
flickerstreak@155 72 self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha)
flickerstreak@155 73 end
flickerstreak@155 74 end
flickerstreak@155 75 end
flickerstreak@155 76
flickerstreak@156 77 -- hide if state or unit_exists says to
flickerstreak@156 78 hide = not showAll and (hide or unithide)
flickerstreak@156 79 if hide ~= hidden then
flickerstreak@156 80 hidden = hide
flickerstreak@156 81 if hide then
flickerstreak@156 82 self:Hide()
flickerstreak@156 83 else
flickerstreak@156 84 self:Show()
flickerstreak@156 85 end
flickerstreak@156 86 end
flickerstreak@156 87
flickerstreak@155 88 for _, attr in pairs(extensions) do
flickerstreak@155 89 control:RunAttribute(attr)
flickerstreak@155 90 end
flickerstreak@155 91
flickerstreak@155 92 control:ChildUpdate()
flickerstreak@155 93
flickerstreak@155 94 if showAll then
flickerstreak@155 95 control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide)
flickerstreak@155 96 end
flickerstreak@155 97
flickerstreak@155 98 if oldState ~= state then
flickerstreak@155 99 control:CallMethod("StateRefresh", state)
flickerstreak@155 100 end
flickerstreak@155 101 ]]
flickerstreak@155 102
flickerstreak@155 103 local _onstate_reaction = -- function( self, stateid, newstate )
flickerstreak@155 104 [[
flickerstreak@155 105 set_state = newstate
flickerstreak@155 106 ]] .. _reaction_refresh
flickerstreak@155 107
flickerstreak@155 108 local _onstate_showgrid = -- function( self, stateid, newstate )
flickerstreak@155 109 [[
flickerstreak@155 110 control:ChildUpdate(stateid,newstate)
flickerstreak@155 111 control:CallMethod("UpdateShowGrid")
flickerstreak@155 112 ]]
flickerstreak@155 113
flickerstreak@155 114 local _onstate_unitexists = -- function( self, stateid, newstate )
flickerstreak@155 115 [[
flickerstreak@159 116 unithide = not newstate or newstate == "hide"
flickerstreak@155 117 ]] .. _reaction_refresh
flickerstreak@155 118
flickerstreak@155 119 local _onclick = -- function( self, button, down )
flickerstreak@155 120 [[
flickerstreak@155 121 if state_override == button then
flickerstreak@155 122 state_override = nil -- toggle
flickerstreak@155 123 else
flickerstreak@155 124 state_override = button
flickerstreak@155 125 end
flickerstreak@155 126 ]] .. _reaction_refresh
flickerstreak@155 127
flickerstreak@157 128 -- For reference
flickerstreak@157 129 -- the option field names must match the field names of the options table, below
flickerstreak@157 130 local stateProperties = {
flickerstreak@157 131 hide = true,
flickerstreak@157 132 --keybindState = true, TODO: broken
flickerstreak@157 133 anchorEnable = true,
flickerstreak@157 134 anchorFrame = true,
flickerstreak@157 135 anchorPoint = true,
flickerstreak@157 136 anchorRelPoint = true,
flickerstreak@157 137 anchorX = true,
flickerstreak@157 138 anchorY = true,
flickerstreak@157 139 enableScale = true,
flickerstreak@157 140 scale = true,
flickerstreak@157 141 enableAlpha = true,
flickerstreak@157 142 alpha = true,
flickerstreak@157 143 }
flickerstreak@157 144
flickerstreak@157 145
flickerstreak@157 146 ---- Utility functions ----
flickerstreak@157 147
flickerstreak@157 148 -- traverse a table tree by key list and fetch the result or first nil
flickerstreak@157 149 local function tfetch(t, ...)
flickerstreak@157 150 for i = 1, select('#', ...) do
flickerstreak@157 151 t = t and t[select(i, ...)]
flickerstreak@157 152 end
flickerstreak@157 153 return t
flickerstreak@157 154 end
flickerstreak@157 155
flickerstreak@157 156 -- traverse a table tree by key list and build tree as necessary
flickerstreak@157 157 local function tbuild(t, ...)
flickerstreak@157 158 for i = 1, select('#', ...) do
flickerstreak@157 159 local key = select(i, ...)
flickerstreak@157 160 if not t[key] then t[key] = { } end
flickerstreak@157 161 t = t[key]
flickerstreak@157 162 end
flickerstreak@157 163 return t
flickerstreak@157 164 end
flickerstreak@157 165
flickerstreak@157 166 -- return a new array of keys of table 't', sorted by comparing
flickerstreak@157 167 -- sub-fields (obtained via tfetch) of the table values
flickerstreak@157 168 local function fieldsort( t, ... )
flickerstreak@157 169 local r = { }
flickerstreak@157 170 for k in pairs(t) do
flickerstreak@157 171 table.insert(r,k)
flickerstreak@157 172 end
flickerstreak@157 173 local path = { ... }
flickerstreak@157 174 table.sort(r, function(lhs, rhs)
flickerstreak@157 175 local olhs = tfetch(t[lhs], unpack(path)) or 0
flickerstreak@157 176 local orhs = tfetch(t[rhs], unpack(path)) or 0
flickerstreak@157 177 return olhs < orhs
flickerstreak@157 178 end)
flickerstreak@157 179 return r
flickerstreak@157 180 end
flickerstreak@157 181
flickerstreak@155 182
flickerstreak@147 183 ---- Bar class ----
flickerstreak@122 184 local Bar = { }
flickerstreak@122 185 local weak = { __mode = "k" }
flickerstreak@147 186 local frameList = { }
flickerstreak@122 187
flickerstreak@122 188 ReAction.Bar = Bar -- export to ReAction
flickerstreak@122 189
flickerstreak@122 190 function Bar:New( name, config )
flickerstreak@122 191 if type(config) ~= "table" then
flickerstreak@122 192 error("ReAction.Bar: config table required")
flickerstreak@122 193 end
flickerstreak@122 194
flickerstreak@122 195 -- create new self
flickerstreak@122 196 self = setmetatable(
flickerstreak@122 197 {
flickerstreak@122 198 config = config,
flickerstreak@122 199 name = name,
flickerstreak@122 200 buttons = setmetatable( { }, weak ),
flickerstreak@122 201 width = config.width or 480,
flickerstreak@147 202 height = config.height or 40,
flickerstreak@122 203 },
flickerstreak@147 204 {__index = self} )
flickerstreak@122 205
flickerstreak@122 206 -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
flickerstreak@122 207 -- not mouse-clickable by the user.
flickerstreak@122 208 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
flickerstreak@147 209 name = name and "ReAction-"..name
flickerstreak@147 210 local f = name and frameList[name]
flickerstreak@147 211 if not f then
flickerstreak@147 212 f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
flickerstreak@147 213 if name then
flickerstreak@147 214 frameList[name] = f
flickerstreak@147 215 end
flickerstreak@147 216 end
flickerstreak@122 217 f:SetFrameStrata("MEDIUM")
flickerstreak@122 218 f:SetWidth(self.width)
flickerstreak@146 219 f:SetHeight(self.height)
flickerstreak@122 220 f:SetAlpha(config.alpha or 1.0)
flickerstreak@122 221 f:Show()
flickerstreak@122 222 f:EnableMouse(false)
flickerstreak@122 223 f:SetClampedToScreen(true)
flickerstreak@155 224 ReAction.gridProxy:AddFrame(f)
flickerstreak@122 225
flickerstreak@155 226 -- secure handlers
flickerstreak@155 227 f:Execute(_reaction_init)
flickerstreak@155 228 f:SetAttribute("_onstate-reaction", _onstate_reaction)
flickerstreak@155 229 f:SetAttribute("_onstate-showgrid", _onstate_showgrid)
flickerstreak@155 230 f:SetAttribute("_onstate-unitexists", _onstate_unitexists)
flickerstreak@155 231 f:SetAttribute("_onclick", _onclick)
flickerstreak@155 232
flickerstreak@155 233 -- secure handler CallMethod()s
flickerstreak@155 234 f.UpdateShowGrid = function() self:UpdateShowGrid() end
flickerstreak@155 235 f.StateRefresh = function() self:RefreshControls() end
flickerstreak@155 236 f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end
flickerstreak@122 237
flickerstreak@122 238 -- Override the default frame accessor to provide strict read-only access
flickerstreak@122 239 function self:GetFrame()
flickerstreak@122 240 return f
flickerstreak@122 241 end
flickerstreak@122 242
flickerstreak@122 243 self:ApplyAnchor()
flickerstreak@147 244 self:SetConfigMode(ReAction:GetConfigMode())
flickerstreak@147 245 self:SetKeybindMode(ReAction:GetKeybindMode())
flickerstreak@147 246
flickerstreak@122 247 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@147 248 KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
flickerstreak@147 249 KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
flickerstreak@147 250 KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
flickerstreak@122 251
flickerstreak@122 252 return self
flickerstreak@122 253 end
flickerstreak@122 254
flickerstreak@122 255 function Bar:Destroy()
flickerstreak@122 256 local f = self:GetFrame()
flickerstreak@122 257 f:UnregisterAllEvents()
flickerstreak@125 258 self:ShowControls(false)
flickerstreak@122 259 ReAction.UnregisterAllCallbacks(self)
flickerstreak@147 260 KB.UnregisterAllCallbacks(self)
flickerstreak@122 261 ReAction.gridProxy:RemoveFrame(f)
flickerstreak@122 262 f:SetParent(UIParent)
flickerstreak@122 263 f:ClearAllPoints()
flickerstreak@147 264 f:Hide()
flickerstreak@122 265 end
flickerstreak@122 266
flickerstreak@147 267 --
flickerstreak@147 268 -- Events
flickerstreak@147 269 --
flickerstreak@147 270
flickerstreak@147 271 function Bar:OnConfigModeChanged(event, mode)
flickerstreak@147 272 self:SetConfigMode(mode)
flickerstreak@123 273 end
flickerstreak@123 274
flickerstreak@147 275 function Bar:LIBKEYBOUND_ENABLED(evt)
flickerstreak@147 276 self:SetKeybindMode(true)
flickerstreak@147 277 end
flickerstreak@147 278
flickerstreak@147 279 function Bar:LIBKEYBOUND_DISABLED(evt)
flickerstreak@147 280 self:SetKeybindMode(false)
flickerstreak@122 281 end
flickerstreak@122 282
flickerstreak@153 283 --
flickerstreak@153 284 -- Accessors
flickerstreak@153 285 --
flickerstreak@153 286
flickerstreak@153 287 function Bar:GetName()
flickerstreak@153 288 return self.name
flickerstreak@153 289 end
flickerstreak@153 290
flickerstreak@153 291 -- only ReAction:RenameBar() should call this function. Calling from any other
flickerstreak@153 292 -- context will desync the bar list in the ReAction class.
flickerstreak@153 293 function Bar:SetName(name)
flickerstreak@153 294 self.name = name
flickerstreak@154 295 if self.overlay then
flickerstreak@154 296 self.overlay:SetLabel(self.name)
flickerstreak@154 297 end
flickerstreak@153 298 end
flickerstreak@153 299
flickerstreak@153 300 function Bar:GetFrame()
flickerstreak@153 301 -- this method is included for documentation purposes. It is overridden
flickerstreak@153 302 -- for each object in the :New() method.
flickerstreak@153 303 error("Invalid Bar object: used without initialization")
flickerstreak@153 304 end
flickerstreak@153 305
flickerstreak@153 306 function Bar:GetConfig()
flickerstreak@153 307 return self.config
flickerstreak@153 308 end
flickerstreak@153 309
flickerstreak@153 310 function Bar:GetAnchor()
flickerstreak@122 311 local c = self.config
flickerstreak@153 312 return (c.point or "CENTER"),
flickerstreak@153 313 (c.anchor or self:GetFrame():GetParent():GetName()),
flickerstreak@153 314 (c.relpoint or c.point or "CENTER"),
flickerstreak@153 315 (c.x or 0),
flickerstreak@153 316 (c.y or 0)
flickerstreak@122 317 end
flickerstreak@122 318
flickerstreak@122 319 function Bar:SetAnchor(point, frame, relativePoint, x, y)
flickerstreak@122 320 local c = self.config
flickerstreak@122 321 c.point = point or c.point
flickerstreak@122 322 c.anchor = frame or c.anchor
flickerstreak@122 323 c.relpoint = relativePoint or c.relpoint
flickerstreak@122 324 c.x = x or c.x
flickerstreak@122 325 c.y = y or c.y
flickerstreak@122 326 self:ApplyAnchor()
flickerstreak@122 327 ReAction:RefreshBar(self)
flickerstreak@122 328 end
flickerstreak@122 329
flickerstreak@122 330 function Bar:GetSize()
flickerstreak@122 331 local f = self:GetFrame()
flickerstreak@122 332 return f:GetWidth(), f:GetHeight()
flickerstreak@122 333 end
flickerstreak@122 334
flickerstreak@122 335 function Bar:SetSize(w,h)
flickerstreak@122 336 local f = self:GetFrame()
flickerstreak@122 337 self.config.width = w
flickerstreak@122 338 self.config.height = h
flickerstreak@122 339 f:SetWidth(w)
flickerstreak@122 340 f:SetHeight(h)
flickerstreak@122 341 end
flickerstreak@122 342
flickerstreak@122 343 function Bar:GetButtonSize()
flickerstreak@122 344 local w = self.config.btnWidth or 32
flickerstreak@122 345 local h = self.config.btnHeight or 32
flickerstreak@122 346 -- TODO: get from modules?
flickerstreak@122 347 return w,h
flickerstreak@122 348 end
flickerstreak@122 349
flickerstreak@122 350 function Bar:SetButtonSize(w,h)
flickerstreak@122 351 if w > 0 and h > 0 then
flickerstreak@122 352 self.config.btnWidth = w
flickerstreak@122 353 self.config.btnHeight = h
flickerstreak@122 354 end
flickerstreak@122 355 ReAction:RefreshBar(self)
flickerstreak@122 356 end
flickerstreak@122 357
flickerstreak@153 358 function Bar:GetNumButtons()
flickerstreak@153 359 local r,c = self:GetButtonGrid()
flickerstreak@153 360 return r*c
flickerstreak@153 361 end
flickerstreak@153 362
flickerstreak@122 363 function Bar:GetButtonGrid()
flickerstreak@122 364 local cfg = self.config
flickerstreak@122 365 local r = cfg.btnRows or 1
flickerstreak@122 366 local c = cfg.btnColumns or 1
flickerstreak@122 367 local s = cfg.spacing or 4
flickerstreak@122 368 return r,c,s
flickerstreak@122 369 end
flickerstreak@122 370
flickerstreak@122 371 function Bar:SetButtonGrid(r,c,s)
flickerstreak@122 372 if r > 0 and c > 0 and s > 0 then
flickerstreak@122 373 local cfg = self.config
flickerstreak@122 374 cfg.btnRows = r
flickerstreak@122 375 cfg.btnColumns = c
flickerstreak@122 376 cfg.spacing = s
flickerstreak@122 377 end
flickerstreak@122 378 ReAction:RefreshBar(self)
flickerstreak@122 379 end
flickerstreak@122 380
flickerstreak@122 381 function Bar:GetAlpha()
flickerstreak@122 382 return self.config.alpha or 1.0
flickerstreak@122 383 end
flickerstreak@122 384
flickerstreak@122 385 function Bar:SetAlpha(value)
flickerstreak@122 386 self.config.alpha = value
flickerstreak@122 387 self:GetFrame():SetAlpha(value or 1.0)
flickerstreak@155 388 self:UpdateDefaultStateAlpha()
flickerstreak@122 389 ReAction:RefreshBar(self)
flickerstreak@122 390 end
flickerstreak@122 391
flickerstreak@122 392 function Bar:IterateButtons()
flickerstreak@155 393 -- iterator returns button, idx and does NOT iterate in index order
flickerstreak@122 394 return pairs(self.buttons)
flickerstreak@122 395 end
flickerstreak@122 396
flickerstreak@153 397 --
flickerstreak@153 398 -- Methods
flickerstreak@153 399 --
flickerstreak@153 400
flickerstreak@147 401 function Bar:SetConfigMode(mode)
flickerstreak@155 402 self:SetSecureData("showAll",mode)
flickerstreak@147 403 self:ShowControls(mode)
flickerstreak@147 404 for b in self:IterateButtons() do
flickerstreak@147 405 b:ShowGridTemp(mode)
flickerstreak@147 406 b:UpdateActionIDLabel(mode)
flickerstreak@147 407 end
flickerstreak@147 408 end
flickerstreak@147 409
flickerstreak@147 410 function Bar:SetKeybindMode(mode)
flickerstreak@155 411 self:SetSecureData("showAll",mode)
flickerstreak@147 412 for b in self:IterateButtons() do
flickerstreak@147 413 b:SetKeybindMode(mode)
flickerstreak@147 414 end
flickerstreak@147 415 end
flickerstreak@147 416
flickerstreak@153 417 function Bar:ApplyAnchor()
flickerstreak@153 418 local f = self:GetFrame()
flickerstreak@153 419 local c = self.config
flickerstreak@153 420 local p = c.point
flickerstreak@153 421
flickerstreak@153 422 f:SetWidth(c.width)
flickerstreak@153 423 f:SetHeight(c.height)
flickerstreak@153 424 f:ClearAllPoints()
flickerstreak@153 425
flickerstreak@153 426 if p then
flickerstreak@153 427 local a = f:GetParent()
flickerstreak@153 428 if c.anchor then
flickerstreak@153 429 local bar = ReAction:GetBar(c.anchor)
flickerstreak@153 430 if bar then
flickerstreak@153 431 a = bar:GetFrame()
flickerstreak@153 432 else
flickerstreak@153 433 a = _G[c.anchor]
flickerstreak@153 434 end
flickerstreak@153 435 end
flickerstreak@153 436 local fr = a or f:GetParent()
flickerstreak@153 437 f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
flickerstreak@153 438 else
flickerstreak@153 439 f:SetPoint("CENTER")
flickerstreak@153 440 end
flickerstreak@155 441
flickerstreak@155 442 self:UpdateDefaultStateAnchor()
flickerstreak@153 443 end
flickerstreak@153 444
flickerstreak@153 445 function Bar:ClipNButtons( n )
flickerstreak@153 446 local cfg = self.config
flickerstreak@153 447 local r = cfg.btnRows or 1
flickerstreak@153 448 local c = cfg.btnColumns or 1
flickerstreak@153 449
flickerstreak@153 450 cfg.btnRows = ceil(n/c)
flickerstreak@153 451 cfg.btnColumns = min(n,c)
flickerstreak@153 452 end
flickerstreak@153 453
flickerstreak@153 454 function Bar:AddButton(idx, button)
flickerstreak@153 455 local f = self:GetFrame()
flickerstreak@153 456
flickerstreak@153 457 -- store in a weak reverse-index array
flickerstreak@153 458 self.buttons[button] = idx
flickerstreak@153 459
flickerstreak@153 460 -- Store a properly wrapped reference to the child frame as an attribute
flickerstreak@153 461 -- (accessible via "frameref-btn#")
flickerstreak@153 462 f:SetFrameRef(format("btn%d",idx), button:GetFrame())
flickerstreak@153 463 end
flickerstreak@153 464
flickerstreak@153 465 function Bar:RemoveButton(button)
flickerstreak@153 466 local idx = self.buttons[button]
flickerstreak@153 467 if idx then
flickerstreak@153 468 self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
flickerstreak@153 469 self.buttons[button] = nil
flickerstreak@153 470 end
flickerstreak@153 471 end
flickerstreak@153 472
flickerstreak@122 473 function Bar:PlaceButton(button, baseW, baseH)
flickerstreak@122 474 local idx = self.buttons[button]
flickerstreak@122 475 if idx then
flickerstreak@122 476 local r, c, s = self:GetButtonGrid()
flickerstreak@122 477 local bh, bw = self:GetButtonSize()
flickerstreak@122 478 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
flickerstreak@122 479 local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
flickerstreak@122 480 local scale = bw/baseW
flickerstreak@122 481 local b = button:GetFrame()
flickerstreak@122 482
flickerstreak@122 483 b:ClearAllPoints()
flickerstreak@122 484 b:SetPoint("TOPLEFT",x/scale,y/scale)
flickerstreak@122 485 b:SetScale(scale)
flickerstreak@122 486 end
flickerstreak@122 487 end
flickerstreak@122 488
flickerstreak@122 489 function Bar:SkinButton()
flickerstreak@122 490 -- does nothing by default
flickerstreak@147 491 end
flickerstreak@148 492
flickerstreak@155 493 function Bar:UpdateShowGrid()
flickerstreak@155 494 for button in self:IterateButtons() do
flickerstreak@155 495 button:UpdateShowGrid()
flickerstreak@155 496 end
flickerstreak@155 497 end
flickerstreak@155 498
flickerstreak@154 499 function Bar:ShowControls(show)
flickerstreak@154 500 if show then
flickerstreak@155 501 if not self.overlay then
flickerstreak@155 502 self.overlay = Bar.Overlay:New(self) -- see Overlay.lua
flickerstreak@154 503 end
flickerstreak@155 504 self.overlay:Show()
flickerstreak@155 505 self:RefreshSecureState()
flickerstreak@155 506 elseif self.overlay then
flickerstreak@155 507 self.overlay:Hide()
flickerstreak@154 508 end
flickerstreak@154 509 end
flickerstreak@154 510
flickerstreak@154 511 function Bar:RefreshControls()
flickerstreak@154 512 if self.overlay and self.overlay:IsShown() then
flickerstreak@154 513 self.overlay:RefreshControls()
flickerstreak@154 514 end
flickerstreak@154 515 end
flickerstreak@154 516
flickerstreak@154 517 function Bar:SetLabelSubtext(text)
flickerstreak@154 518 if self.overlay then
flickerstreak@154 519 self.overlay:SetLabelSubtext(text)
flickerstreak@154 520 end
flickerstreak@154 521 end
flickerstreak@154 522
flickerstreak@154 523 --
flickerstreak@154 524 -- Secure state functions
flickerstreak@154 525 --
flickerstreak@154 526
flickerstreak@155 527 function Bar:GetSecureState()
flickerstreak@155 528 local env = GetManagedEnvironment(self:GetFrame())
flickerstreak@155 529 return env and env.state
flickerstreak@155 530 end
flickerstreak@155 531
flickerstreak@155 532 function Bar:GetStateProperty(state, propname)
flickerstreak@155 533 -- override in modules/State.lua for now
flickerstreak@155 534 end
flickerstreak@155 535
flickerstreak@155 536 function Bar:SetStateProperty(state, propname, value)
flickerstreak@155 537 -- override in modules/State.lua for now
flickerstreak@155 538 end
flickerstreak@155 539
flickerstreak@155 540 function Bar:RefreshSecureState()
flickerstreak@155 541 self:GetFrame():Execute(_reaction_refresh)
flickerstreak@155 542 end
flickerstreak@155 543
flickerstreak@155 544 -- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value)
flickerstreak@155 545 function Bar:SetSecureData( ... )
flickerstreak@155 546 local n = select('#',...)
flickerstreak@155 547 if n < 2 then
flickerstreak@155 548 error("ReAction.Bar:SetSecureData() requires at least 2 arguments")
flickerstreak@155 549 end
flickerstreak@155 550 local f = self:GetFrame()
flickerstreak@155 551 f:SetAttribute("data-depth",n-1)
flickerstreak@155 552 f:SetAttribute("data-value",select(n,...))
flickerstreak@155 553 for i = 1, n-1 do
flickerstreak@155 554 local key = select(i,...)
flickerstreak@155 555 if key == nil then
flickerstreak@155 556 error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")")
flickerstreak@155 557 end
flickerstreak@155 558 f:SetAttribute("data-key-"..i, key)
flickerstreak@155 559 end
flickerstreak@155 560 f:Execute(
flickerstreak@155 561 [[
flickerstreak@155 562 local n = self:GetAttribute("data-depth")
flickerstreak@155 563 if n > 0 then
flickerstreak@155 564 local value = self:GetAttribute("data-value")
flickerstreak@155 565 local t = _G
flickerstreak@155 566 for i = 1, n do
flickerstreak@155 567 local key = self:GetAttribute("data-key-"..i)
flickerstreak@155 568 if not key then return end
flickerstreak@155 569 if not t[key] then
flickerstreak@155 570 t[key] = newtable()
flickerstreak@155 571 end
flickerstreak@155 572 if i == n then
flickerstreak@155 573 t[key] = value
flickerstreak@155 574 else
flickerstreak@155 575 t = t[key]
flickerstreak@155 576 end
flickerstreak@155 577 end
flickerstreak@155 578 end
flickerstreak@155 579 ]])
flickerstreak@155 580 self:RefreshSecureState()
flickerstreak@155 581 end
flickerstreak@155 582
flickerstreak@155 583 function Bar:SetSecureStateData( state, key, value )
flickerstreak@155 584 self:SetSecureData("settings",state,key,value)
flickerstreak@155 585 end
flickerstreak@155 586
flickerstreak@155 587 -- sets a snippet to be run as an extension to _onstate-reaction
flickerstreak@155 588 function Bar:SetSecureStateExtension( id, snippet )
flickerstreak@155 589 if id == nil then
flickerstreak@155 590 error("ReAction.Bar:SetSecureStateExtension() requires an id")
flickerstreak@155 591 end
flickerstreak@155 592 local f = self:GetFrame()
flickerstreak@155 593 f:SetAttribute("input-secure-ext-id",id)
flickerstreak@155 594 f:SetAttribute("secure-ext-"..id,snippet)
flickerstreak@155 595 f:Execute(
flickerstreak@155 596 [[
flickerstreak@155 597 local id = self:GetAttribute("input-secure-ext-id")
flickerstreak@155 598 if id then
flickerstreak@155 599 extensions[id] = self:GetAttribute("secure-ext-"..id) or nil
flickerstreak@155 600 end
flickerstreak@155 601 ]])
flickerstreak@155 602 self:RefreshSecureState()
flickerstreak@155 603 end
flickerstreak@155 604
flickerstreak@155 605 function Bar:SetFrameRef( name, refFrame )
flickerstreak@155 606 if refFrame then
flickerstreak@155 607 local _, explicit = refFrame:IsProtected()
flickerstreak@155 608 if not explicit then
flickerstreak@155 609 refFrame = nil
flickerstreak@155 610 end
flickerstreak@155 611 end
flickerstreak@155 612 if refFrame then
flickerstreak@155 613 self:GetFrame():SetFrameRef(name,refFrame)
flickerstreak@155 614 else
flickerstreak@155 615 self:GetFrame():SetAttribute("frameref-"..name,nil)
flickerstreak@155 616 end
flickerstreak@155 617 end
flickerstreak@155 618
flickerstreak@157 619 function Bar:SetStateDriver( states )
flickerstreak@157 620 if states then
flickerstreak@157 621 for state, props in pairs(states) do
flickerstreak@157 622 self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state
flickerstreak@157 623 for propname, value in pairs(props) do
flickerstreak@157 624 if propname == "anchorFrame" then
flickerstreak@157 625 self:SetFrameRef("anchor-"..state, _G[value])
flickerstreak@157 626 elseif propname == "rule" then
flickerstreak@157 627 -- do nothing
flickerstreak@157 628 else
flickerstreak@157 629 self:SetSecureStateData(state, propname, value)
flickerstreak@157 630 end
flickerstreak@157 631 end
flickerstreak@157 632 end
flickerstreak@157 633 end
flickerstreak@157 634 local rule = states and self:BuildStateRule(states)
flickerstreak@155 635 if rule then
flickerstreak@155 636 RegisterStateDriver(self:GetFrame(),"reaction",rule)
flickerstreak@155 637 elseif self.statedriver then
flickerstreak@155 638 UnregisterStateDriver(self:GetFrame(),"reaction")
flickerstreak@155 639 end
flickerstreak@155 640 self.statedriver = rule
flickerstreak@157 641 self:BuildStateKeybinds(states)
flickerstreak@155 642 self:RefreshSecureState()
flickerstreak@155 643 end
flickerstreak@155 644
flickerstreak@148 645 -- pass unit=nil to set up the unit elsewhere, if you want something more complex
flickerstreak@148 646 function Bar:RegisterUnitWatch( unit, enable )
flickerstreak@148 647 local f = self:GetFrame()
flickerstreak@148 648 if unit then
flickerstreak@148 649 f:SetAttribute("unit",unit)
flickerstreak@148 650 end
flickerstreak@156 651 if enable then
flickerstreak@156 652 RegisterUnitWatch(self:GetFrame(),true)
flickerstreak@156 653 elseif self.unitwatch then
flickerstreak@156 654 UnregisterUnitWatch(self:GetFrame())
flickerstreak@156 655 end
flickerstreak@148 656 self.unitwatch = enable
flickerstreak@156 657 self:RefreshSecureState()
flickerstreak@148 658 end
flickerstreak@148 659
flickerstreak@155 660 function Bar:SetStateKeybind( key, state )
flickerstreak@155 661 local f = self:GetFrame()
flickerstreak@155 662 local binds = self.statebinds
flickerstreak@155 663 if not binds then
flickerstreak@155 664 binds = { }
flickerstreak@155 665 self.statebinds = binds
flickerstreak@155 666 end
flickerstreak@155 667
flickerstreak@155 668 -- clear the old binding, if any
flickerstreak@155 669 if binds[state] then
flickerstreak@155 670 SetOverrideBinding(f, false, binds[state], nil)
flickerstreak@155 671 end
flickerstreak@155 672
flickerstreak@155 673 if key then
flickerstreak@157 674 SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button
flickerstreak@155 675 end
flickerstreak@155 676 binds[state] = key
flickerstreak@155 677 end
flickerstreak@155 678
flickerstreak@155 679 function Bar:GetStateKeybind( state )
flickerstreak@155 680 if self.statebinds and state then
flickerstreak@155 681 return self.statebinds[state]
flickerstreak@155 682 end
flickerstreak@155 683 end
flickerstreak@155 684
flickerstreak@155 685 function Bar:UpdateDefaultStateAnchor()
flickerstreak@155 686 local point, frame, relPoint, x, y = self:GetAnchor()
flickerstreak@155 687 local f = self:GetFrame()
flickerstreak@155 688 f:SetAttribute("defaultAnchor-point",point)
flickerstreak@155 689 f:SetAttribute("defaultAnchor-relPoint",relPoint)
flickerstreak@155 690 f:SetAttribute("defaultAnchor-x",x)
flickerstreak@155 691 f:SetAttribute("defaultAnchor-y",y)
flickerstreak@155 692 self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"])
flickerstreak@155 693 f:Execute([[
flickerstreak@155 694 for _, k in pairs(anchorKeys) do
flickerstreak@155 695 defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k)
flickerstreak@155 696 end
flickerstreak@155 697 defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
flickerstreak@155 698 ]])
flickerstreak@155 699 end
flickerstreak@155 700
flickerstreak@155 701 function Bar:UpdateDefaultStateAlpha()
flickerstreak@155 702 local f = self:GetFrame()
flickerstreak@155 703 f:SetAttribute("defaultAlpha",self:GetAlpha())
flickerstreak@155 704 f:Execute([[
flickerstreak@155 705 defaultAlpha = self:GetAttribute("defaultAlpha")
flickerstreak@155 706 ]])
flickerstreak@155 707 end
flickerstreak@157 708
flickerstreak@157 709 ---- secure state driver rules ----
flickerstreak@157 710
flickerstreak@157 711 local playerClass = select(2, UnitClass("player"))
flickerstreak@157 712 local function ClassFilter(...)
flickerstreak@157 713 for i = 1, select('#',...) do
flickerstreak@157 714 if playerClass == select(i,...) then
flickerstreak@157 715 return false
flickerstreak@157 716 end
flickerstreak@157 717 end
flickerstreak@157 718 return true
flickerstreak@157 719 end
flickerstreak@157 720
flickerstreak@157 721 local ruleformats = {
flickerstreak@157 722 stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@157 723 nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@157 724 shadowdance = { format = "bonusbar:2", filter = ClassFilter("ROGUE") },
flickerstreak@157 725 shadowform = { format = "form:1", filter = ClassFilter("PRIEST") },
flickerstreak@157 726 noshadowform = { format = "noform", filter = ClassFilter("PRIEST") },
flickerstreak@157 727 battle = { format = "stance:1", filter = ClassFilter("WARRIOR") },
flickerstreak@157 728 defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") },
flickerstreak@157 729 berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") },
flickerstreak@157 730 caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
flickerstreak@157 731 bear = { format = "form:1", filter = ClassFilter("DRUID") },
flickerstreak@157 732 cat = { format = "form:3", filter = ClassFilter("DRUID") },
flickerstreak@157 733 tree = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@157 734 moonkin = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@157 735 pet = { format = "pet" },
flickerstreak@157 736 nopet = { format = "nopet" },
flickerstreak@157 737 harm = { format = "target=target,harm" },
flickerstreak@157 738 help = { format = "target=target,help" },
flickerstreak@157 739 notarget = { format = "target=target,noexists" },
flickerstreak@157 740 focusharm = { format = "target=focus,harm" },
flickerstreak@157 741 focushelp = { format = "target=focus,help" },
flickerstreak@157 742 nofocus = { format = "target=focus,noexists" },
flickerstreak@157 743 raid = { format = "group:raid" },
flickerstreak@157 744 party = { format = "group:party" },
flickerstreak@157 745 solo = { format = "nogroup" },
flickerstreak@157 746 combat = { format = "combat" },
flickerstreak@157 747 nocombat = { format = "nocombat" },
flickerstreak@157 748 possess = { format = "target=vehicle,noexists,bonusbar:5" },
flickerstreak@157 749 vehicle = { format = "target=vehicle,exists,bonusbar:5" },
flickerstreak@157 750 }
flickerstreak@157 751
flickerstreak@157 752 function Bar.InitRuleFormats()
flickerstreak@157 753 local forms = { }
flickerstreak@157 754 for i = 1, GetNumShapeshiftForms() do
flickerstreak@157 755 local _, name = GetShapeshiftFormInfo(i)
flickerstreak@157 756 forms[name] = i;
flickerstreak@157 757 end
flickerstreak@157 758 -- use 9 if not found since 9 is never a valid stance/form
flickerstreak@157 759 local defensive = forms[GetSpellInfo(71)] or 9
flickerstreak@157 760 local berserker = forms[GetSpellInfo(2458)] or 9
flickerstreak@157 761 local bear = forms[GetSpellInfo(9634)] or forms[GetSpellInfo(5487)] or 9
flickerstreak@157 762 local aquatic = forms[GetSpellInfo(1066)] or 9
flickerstreak@157 763 local cat = forms[GetSpellInfo(768)] or 9
flickerstreak@157 764 local travel = forms[GetSpellInfo(783)] or 9
flickerstreak@157 765 local tree = forms[GetSpellInfo(33891)] or 9
flickerstreak@157 766 local moonkin = forms[GetSpellInfo(24858)] or 9
flickerstreak@157 767 local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9
flickerstreak@157 768
flickerstreak@157 769 ruleformats.defensive.format = "stance:"..defensive
flickerstreak@157 770 ruleformats.berserker.format = "stance:"..berserker
flickerstreak@157 771 ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight)
flickerstreak@157 772 ruleformats.bear.format = "form:"..bear
flickerstreak@157 773 ruleformats.cat.format = "form:"..cat
flickerstreak@157 774 ruleformats.tree.format = "form:"..tree
flickerstreak@157 775 ruleformats.moonkin.format = "form:"..moonkin
flickerstreak@157 776 end
flickerstreak@157 777
flickerstreak@157 778 function Bar:BuildStateRule(states)
flickerstreak@157 779 -- states is a table :
flickerstreak@157 780 -- states[statename].rule = {
flickerstreak@157 781 -- order = #,
flickerstreak@157 782 -- type = "default"/"custom"/"any"/"all",
flickerstreak@157 783 -- values = { ... }, -- keys of ruleformats[]
flickerstreak@157 784 -- custom = "...",
flickerstreak@157 785 -- }
flickerstreak@157 786 local rules = { }
flickerstreak@157 787 local default
flickerstreak@157 788
flickerstreak@157 789 for idx, state in ipairs(fieldsort(states, "rule", "order")) do
flickerstreak@157 790 local c = states[state].rule
flickerstreak@157 791 local type = c.type
flickerstreak@157 792 if type == "default" then
flickerstreak@157 793 default = default or state
flickerstreak@157 794 elseif type == "custom" then
flickerstreak@157 795 if c.custom then
flickerstreak@157 796 -- strip out all spaces from the custom rule
flickerstreak@157 797 table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
flickerstreak@157 798 end
flickerstreak@157 799 elseif type == "any" or type == "all" then
flickerstreak@157 800 if c.values then
flickerstreak@157 801 local clauses = { }
flickerstreak@157 802 for key, value in pairs(c.values) do
flickerstreak@157 803 if ruleformats[key] and not ruleformats[key].filter then
flickerstreak@157 804 table.insert(clauses, ruleformats[key].format)
flickerstreak@157 805 end
flickerstreak@157 806 end
flickerstreak@157 807 if #clauses > 0 then
flickerstreak@157 808 local sep = (type == "any") and "][" or ","
flickerstreak@157 809 table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
flickerstreak@157 810 end
flickerstreak@157 811 end
flickerstreak@157 812 end
flickerstreak@157 813 end
flickerstreak@157 814 -- make sure that the default, if any, is last
flickerstreak@157 815 if default then
flickerstreak@157 816 table.insert(rules, default)
flickerstreak@157 817 end
flickerstreak@157 818 return table.concat(rules,";")
flickerstreak@157 819 end
flickerstreak@157 820
flickerstreak@157 821 function Bar:BuildStateKeybinds( states )
flickerstreak@157 822 if states then
flickerstreak@157 823 for name, state in pairs(states) do
flickerstreak@157 824 local rule = tfetch(state, "rule")
flickerstreak@157 825 if rule and rule.type == "keybind" then
flickerstreak@157 826 self:SetStateKeybind(rule.keybind, name)
flickerstreak@157 827 else
flickerstreak@157 828 self:SetStateKeybind(nil, name) -- this clears an existing keybind
flickerstreak@157 829 end
flickerstreak@157 830 end
flickerstreak@157 831 end
flickerstreak@157 832 end
flickerstreak@157 833