annotate ReAction.lua @ 241:09c8e9baa35a

Collect table utility functions
author Flick
date Fri, 25 Mar 2011 16:50:43 -0700
parents 704f4a05a1d7
children b56cff349bd6
rev   line source
flickerstreak@201 1 local addonName, addonTable = ...
flickerstreak@205 2 local pcall = pcall
flickerstreak@205 3 local pairs = pairs
flickerstreak@205 4 local type = type
flickerstreak@205 5 local geterrorhandler = geterrorhandler
flickerstreak@33 6 local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
Flick@230 7 local LKB = LibStub("LibKeyBound-1.0",true)
Flick@230 8 if not LKB then
Flick@230 9 LoadAddOn("LibKeyBound-1.0")
Flick@230 10 LKB = LibStub("LibKeyBound-1.0")
Flick@230 11 end
flickerstreak@33 12
flickerstreak@205 13 ------ Utility ------
Flick@241 14 -- make a deep copy of a table
Flick@241 15 local function tcopy(x)
Flick@241 16 if type(x) ~= "table" then
Flick@241 17 return x
flickerstreak@28 18 end
Flick@241 19 local r = {}
Flick@241 20 for k,v in pairs(x) do
Flick@241 21 r[k] = tcopy(v)
Flick@241 22 end
Flick@241 23 return r
flickerstreak@28 24 end
flickerstreak@28 25
Flick@241 26 -- traverse a table tree by key list and fetch the result or first nil
Flick@241 27 local function tfetch(t, ...)
Flick@241 28 for i = 1, select('#', ...) do
Flick@241 29 t = t and t[select(i, ...)]
Flick@241 30 end
Flick@241 31 return t
Flick@241 32 end
Flick@241 33
Flick@241 34 -- traverse a table tree by key list and build tree as necessary
Flick@241 35 local function tbuild(t, ...)
Flick@241 36 for i = 1, select('#', ...) do
Flick@241 37 local key = select(i, ...)
Flick@241 38 if not t[key] then t[key] = { } end
Flick@241 39 t = t[key]
Flick@241 40 end
Flick@241 41 return t
Flick@241 42 end
Flick@241 43
Flick@241 44 -- return a new array of keys of table 't', sorted by comparing
Flick@241 45 -- sub-fields (obtained via tfetch) of the table values
Flick@241 46 local function fieldsort( t, ... )
Flick@241 47 local r = { }
Flick@241 48 for k in pairs(t) do
Flick@241 49 table.insert(r,k)
Flick@241 50 end
Flick@241 51 local path = { ... }
Flick@241 52 table.sort(r, function(lhs, rhs)
Flick@241 53 local olhs = tfetch(t[lhs], unpack(path)) or 0
Flick@241 54 local orhs = tfetch(t[rhs], unpack(path)) or 0
Flick@241 55 return olhs < orhs
Flick@241 56 end)
Flick@241 57 return r
Flick@241 58 end
Flick@241 59
Flick@241 60 -- store in the addon table
Flick@241 61 addonTable.tcopy = tcopy
Flick@241 62 addonTable.tfetch = tfetch
Flick@241 63 addonTable.tbuild = tbuild
Flick@241 64 addonTable.fieldsort = fieldsort
Flick@241 65
flickerstreak@205 66 ------ Core ------
flickerstreak@205 67 local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction",
flickerstreak@205 68 "AceEvent-3.0"
flickerstreak@205 69 )
flickerstreak@205 70 addonTable.ReAction = ReAction
flickerstreak@205 71 ReAction.version = "1.1"
flickerstreak@205 72 ReAction.L = L
flickerstreak@205 73 ReAction.LKB = LKB
flickerstreak@28 74
flickerstreak@205 75
flickerstreak@223 76 ReAction.barTypes = { }
flickerstreak@223 77
flickerstreak@205 78 ------ Handlers ------
flickerstreak@28 79 function ReAction:OnInitialize()
flickerstreak@28 80 self.db = LibStub("AceDB-3.0"):New("ReAction_DB",
flickerstreak@213 81 self.defaultProfile,
flickerstreak@182 82 true -- use global 'Default' (locale-specific)
flickerstreak@28 83 )
flickerstreak@205 84
flickerstreak@213 85 self:UpgradeProfile()
flickerstreak@211 86
flickerstreak@205 87 self.bars = { }
flickerstreak@205 88
flickerstreak@213 89 self.LBF = LibStub("LibButtonFacade",true)
flickerstreak@213 90 if self.LBF then
flickerstreak@213 91 self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self)
flickerstreak@213 92 end
flickerstreak@213 93
flickerstreak@211 94 -- It's fairly normal to use the Blizzard vehicle bar, and to have
flickerstreak@211 95 -- your regular buttons in the same location. If you do this, and don't
flickerstreak@211 96 -- bother to hide your buttons, they'll obscure some parts of the vehicle bar.
flickerstreak@211 97 VehicleMenuBar:SetFrameLevel(VehicleMenuBar:GetFrameLevel()+3)
flickerstreak@211 98
flickerstreak@205 99 self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
Flick@227 100
flickerstreak@184 101 LKB.RegisterCallback(self,"LIBKEYBOUND_ENABLED")
flickerstreak@184 102 LKB.RegisterCallback(self,"LIBKEYBOUND_DISABLED")
flickerstreak@207 103 LKB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
Flick@227 104
Flick@227 105 -- see Profile.lua for these callback implementations
Flick@227 106 self.db.RegisterCallback(self,"OnProfileChanged")
Flick@227 107 self.db.RegisterCallback(self,"OnProfileCopied","OnProfileChanged")
Flick@227 108 self.db.RegisterCallback(self,"OnNewProfile")
Flick@227 109 self.db.RegisterCallback(self,"OnProfileReset", "OnNewProfile")
Flick@227 110
flickerstreak@182 111 self:RegisterEvent("PLAYER_REGEN_DISABLED")
Flick@227 112
flickerstreak@182 113 self:InitializeOptions()
flickerstreak@28 114 end
flickerstreak@28 115
flickerstreak@28 116 function ReAction:OnEnable()
flickerstreak@205 117 self:InitializeBars()
flickerstreak@28 118 end
flickerstreak@28 119
flickerstreak@28 120 function ReAction:OnDisable()
flickerstreak@205 121 self:TearDownBars()
flickerstreak@28 122 end
flickerstreak@28 123
flickerstreak@33 124 function ReAction:PLAYER_REGEN_DISABLED()
flickerstreak@205 125 if self.configMode == true then
flickerstreak@63 126 self:UserError(L["ReAction config mode disabled during combat."])
flickerstreak@33 127 self:SetConfigMode(false)
flickerstreak@88 128 self:SetKeybindMode(false)
flickerstreak@185 129 self:CloseEditor()
flickerstreak@33 130 end
flickerstreak@33 131 end
flickerstreak@33 132
flickerstreak@88 133 function ReAction:LIBKEYBOUND_ENABLED( evt )
flickerstreak@88 134 self:SetKeybindMode(true)
flickerstreak@88 135 end
flickerstreak@88 136
flickerstreak@88 137 function ReAction:LIBKEYBOUND_DISABLED( evt )
flickerstreak@88 138 return self:SetKeybindMode(false)
flickerstreak@88 139 end
flickerstreak@88 140
flickerstreak@213 141 function ReAction:OnSkinChanged( skinID, gloss, backdrop, group, button, colors )
flickerstreak@213 142 if group == nil then
flickerstreak@213 143 -- don't store global
flickerstreak@213 144 else
flickerstreak@213 145 -- 'group' is the bar-name
flickerstreak@213 146 local bar = self:GetBar(group)
flickerstreak@213 147 if bar then
flickerstreak@213 148 local c = bar:GetConfig().ButtonFacade
flickerstreak@213 149 if c then
flickerstreak@213 150 c.skinID = skinID
flickerstreak@213 151 c.gloss = gloss
flickerstreak@213 152 c.backdrop = backdrop
flickerstreak@213 153 c.colors = colors
flickerstreak@213 154 end
flickerstreak@213 155 end
flickerstreak@213 156 end
flickerstreak@213 157 end
flickerstreak@213 158
flickerstreak@33 159
flickerstreak@205 160 ------ Methods ------
flickerstreak@77 161
flickerstreak@61 162 function ReAction:UserError(msg)
flickerstreak@61 163 UIErrorsFrame:AddMessage(msg)
flickerstreak@61 164 end
flickerstreak@61 165
flickerstreak@205 166 function ReAction:GetBar(arg)
flickerstreak@205 167 if type(arg) == "string" then
flickerstreak@205 168 return self.bars[arg], arg
flickerstreak@205 169 elseif type(arg) == "table" then -- reverse lookup
flickerstreak@205 170 for name, bar in pairs(self.bars) do
flickerstreak@205 171 if arg == bar then
flickerstreak@205 172 return bar, name
flickerstreak@205 173 end
flickerstreak@205 174 end
flickerstreak@205 175 else
flickerstreak@205 176 error("ReAction:GetBar() requires either a name or a bar table arg")
flickerstreak@205 177 end
flickerstreak@184 178 end
flickerstreak@184 179
flickerstreak@205 180 function ReAction:IterateBars()
flickerstreak@205 181 return pairs(self.bars)
flickerstreak@205 182 end
flickerstreak@184 183
flickerstreak@63 184 -- usage:
flickerstreak@91 185 -- (1) ReAction:CreateBar(name, [cfgTable])
flickerstreak@63 186 -- (2) ReAction:CreateBar(name, "barType", [nRows], [nCols], [btnSize], [btnSpacing])
flickerstreak@91 187 function ReAction:CreateBar(name, config, ...)
flickerstreak@91 188 local profile = self.db.profile
flickerstreak@91 189
flickerstreak@217 190 name = tostring(name)
flickerstreak@217 191 if not name or name == "" then
flickerstreak@217 192 error("ReAction:CreateBar() - bar name string required")
flickerstreak@217 193 elseif self.bars[name] then
flickerstreak@217 194 self:UserError(format(L["ReAction: name '%s' already in use"],name))
flickerstreak@217 195 return nil
flickerstreak@91 196 end
flickerstreak@91 197
flickerstreak@223 198 local class
flickerstreak@91 199 if type(config) == "string" then
flickerstreak@223 200 class = self.barTypes[config]
flickerstreak@218 201 if not class then
flickerstreak@218 202 error(("ReAction:CreateBar() - unknown bar type '%s'"):format(config))
flickerstreak@48 203 end
flickerstreak@218 204 config = tcopy(class:GetDefaultBarConfig())
flickerstreak@218 205 config.btnRows = select(1,...) or config.btnRows
flickerstreak@218 206 config.btnColumns = select(2,...) or config.btnColumns
flickerstreak@218 207 config.btnWidth = select(3,...) or config.btnWidth
flickerstreak@218 208 config.btnHeight = select(3,...) or config.btnHeight
flickerstreak@218 209 config.spacing = select(4,...) or config.spacing
flickerstreak@48 210 config.width = config.width or config.btnColumns*(config.btnWidth + config.spacing) + 1
flickerstreak@48 211 config.height = config.height or config.btnRows*(config.btnHeight + config.spacing) + 1
flickerstreak@81 212 config.anchor = config.anchor or "UIParent"
flickerstreak@81 213 config.point = config.point or "BOTTOM"
flickerstreak@81 214 config.relpoint = config.relpoint or "BOTTOM"
flickerstreak@48 215 config.y = config.y or 200
flickerstreak@48 216 config.x = config.x or 0
flickerstreak@223 217 else
flickerstreak@223 218 config = config or profile.bars[name] or { }
flickerstreak@223 219 if not config or not config.type or not self.barTypes[config.type] then
flickerstreak@223 220 error(("ReAction: Unable to construct/fetch config table for bar '%s'"):format(name))
flickerstreak@223 221 end
flickerstreak@223 222 class = self.barTypes[config.type]
flickerstreak@48 223 end
flickerstreak@91 224
flickerstreak@91 225 profile.bars[name] = config
Flick@234 226 local bar = self.Bar:New( name, config, class ) -- ReAction.Bar defined in Bar.lua
flickerstreak@205 227 self.bars[name] = bar
flickerstreak@205 228 self.callbacks:Fire("OnCreateBar", bar, name)
flickerstreak@205 229 if self.configMode then
flickerstreak@33 230 bar:ShowControls(true)
flickerstreak@33 231 end
flickerstreak@33 232
flickerstreak@28 233 return bar
flickerstreak@28 234 end
flickerstreak@28 235
flickerstreak@205 236 function ReAction:DestroyBar(x)
flickerstreak@205 237 local bar, name = self:GetBar(x)
flickerstreak@63 238 if bar and name then
flickerstreak@205 239 self.bars[name] = nil
flickerstreak@205 240 self.callbacks:Fire("OnDestroyBar", bar, name)
flickerstreak@205 241 bar:Destroy()
flickerstreak@28 242 end
flickerstreak@28 243 end
flickerstreak@28 244
flickerstreak@205 245 function ReAction:RefreshBar(x)
flickerstreak@205 246 local bar, name = self:GetBar(x)
flickerstreak@205 247 if bar and name then
flickerstreak@205 248 self.callbacks:Fire("OnRefreshBar", bar, name)
flickerstreak@205 249 end
flickerstreak@63 250 end
flickerstreak@63 251
flickerstreak@205 252 function ReAction:InitializeBars()
flickerstreak@205 253 if not self.barsInitialized then
flickerstreak@211 254 self:ManageBlizzardBars()
flickerstreak@211 255
flickerstreak@205 256 for name, config in pairs(self.db.profile.bars) do
flickerstreak@205 257 if config then
flickerstreak@205 258 self:CreateBar(name, config)
flickerstreak@205 259 end
flickerstreak@205 260 end
flickerstreak@205 261 -- re-anchor and refresh in case anchor order does not match init order
flickerstreak@205 262 for name, bar in pairs(self.bars) do
flickerstreak@205 263 bar:ApplyAnchor()
flickerstreak@205 264 self.callbacks:Fire("OnRefreshBar", bar, name)
flickerstreak@205 265 end
flickerstreak@205 266 self.barsInitialized = true
flickerstreak@205 267 end
flickerstreak@205 268 end
flickerstreak@205 269
flickerstreak@205 270 function ReAction:TearDownBars()
flickerstreak@205 271 for name, bar in pairs(self.bars) do
flickerstreak@205 272 if bar then
flickerstreak@208 273 self.bars[name] = self:DestroyBar(bar)
flickerstreak@205 274 end
flickerstreak@205 275 end
flickerstreak@205 276 self.barsInitialized = false
flickerstreak@205 277 end
flickerstreak@205 278
flickerstreak@205 279 function ReAction:RebuildAll()
flickerstreak@205 280 self:TearDownBars()
flickerstreak@205 281 self:InitializeBars()
flickerstreak@28 282 end
flickerstreak@28 283
flickerstreak@28 284 function ReAction:RenameBar(x, newname)
flickerstreak@205 285 local bar, name = self:GetBar(x)
flickerstreak@63 286 if type(newname) ~= "string" then
flickerstreak@63 287 error("ReAction:RenameBar() - second argument must be a string")
flickerstreak@63 288 end
flickerstreak@63 289 if bar and name and #newname > 0 then
flickerstreak@127 290 if newname == name then
flickerstreak@127 291 return
flickerstreak@127 292 end
flickerstreak@205 293 if self.bars[newname] then
flickerstreak@127 294 self:UserError(format(L["ReAction: name '%s' already in use"],newname))
flickerstreak@47 295 else
flickerstreak@205 296 self.bars[newname], self.bars[name] = self.bars[name], nil
flickerstreak@47 297 bar:SetName(newname or "")
flickerstreak@47 298 local cfg = self.db.profile.bars
flickerstreak@47 299 cfg[newname], cfg[name] = cfg[name], nil
flickerstreak@205 300 self.callbacks:Fire("OnRenameBar", bar, name, newname)
flickerstreak@28 301 end
flickerstreak@28 302 end
flickerstreak@28 303 end
flickerstreak@28 304
flickerstreak@205 305 function ReAction:EraseBar(x)
flickerstreak@205 306 local bar, name = self:GetBar(x)
flickerstreak@63 307 if bar and name then
flickerstreak@205 308 self.callbacks:Fire("OnEraseBar", bar, name)
flickerstreak@208 309 self:DestroyBar(bar)
flickerstreak@205 310 self.db.profile.bars[name] = nil
flickerstreak@63 311 end
flickerstreak@63 312 end
flickerstreak@63 313
flickerstreak@211 314 local blizzFrames = {
flickerstreak@211 315 MainMenuBar,
flickerstreak@211 316 MultiBarLeft,
flickerstreak@211 317 MultiBarRight,
flickerstreak@211 318 MultiBarBottomLeft,
flickerstreak@211 319 MultiBarBottomRight,
flickerstreak@211 320 }
flickerstreak@211 321
flickerstreak@211 322 local hideFrame = CreateFrame("Frame")
flickerstreak@211 323 hideFrame:Hide()
flickerstreak@211 324 local hiddenParents = { }
flickerstreak@211 325 local function ManageBlizzFrame(f, hide)
flickerstreak@211 326 if hide and not hiddenParents[f] then
flickerstreak@211 327 hiddenParents[f] = f:GetParent()
flickerstreak@211 328 f:SetParent(hideFrame)
flickerstreak@211 329 elseif not hide and hiddenParents[f] then
flickerstreak@211 330 f:SetParent(hiddenParents[f])
flickerstreak@211 331 hiddenParents[f] = nil
flickerstreak@211 332 if f:IsShown() then
flickerstreak@211 333 f:Show() -- refresh
flickerstreak@211 334 end
flickerstreak@211 335 end
flickerstreak@211 336 end
flickerstreak@211 337
flickerstreak@211 338 function ReAction:ManageBlizzardBars()
flickerstreak@211 339 for _, f in pairs(blizzFrames) do
flickerstreak@211 340 ManageBlizzFrame(f, self.db.profile.options.hideBlizzardBars)
flickerstreak@211 341 end
flickerstreak@211 342 ManageBlizzFrame(VehicleMenuBar, self.db.profile.options.hideBlizzardVehicleBar)
flickerstreak@211 343 end
flickerstreak@211 344
flickerstreak@218 345 function ReAction:RegisterBarType( class, isDefault )
Flick@231 346 local name = class:GetButtonTypeID()
flickerstreak@218 347 self.barTypes[name] = class
flickerstreak@218 348 if isDefault then
flickerstreak@218 349 self.defaultBarType = name
flickerstreak@48 350 end
flickerstreak@48 351 end
flickerstreak@48 352
flickerstreak@63 353 function ReAction:IterateBarTypes()
flickerstreak@218 354 return pairs(self.barTypes)
flickerstreak@63 355 end
flickerstreak@63 356
flickerstreak@218 357 function ReAction:GetDefaultBarConfig(barType)
flickerstreak@218 358 if barType and self.barTypes[barType] then
flickerstreak@218 359 return self.barTypes[barType]:GetDefaultBarConfig()
flickerstreak@63 360 end
flickerstreak@63 361 end
flickerstreak@63 362
flickerstreak@63 363 function ReAction:GetBarTypeOptions( fill )
flickerstreak@63 364 fill = fill or { }
flickerstreak@63 365 for k in self:IterateBarTypes() do
flickerstreak@63 366 fill[k] = k
flickerstreak@63 367 end
flickerstreak@63 368 return fill
flickerstreak@63 369 end
flickerstreak@63 370
flickerstreak@63 371 function ReAction:GetDefaultBarType()
flickerstreak@218 372 return self.defaultBarType
flickerstreak@63 373 end
flickerstreak@63 374
flickerstreak@33 375 function ReAction:SetConfigMode( mode )
flickerstreak@205 376 if mode ~= self.configMode then
flickerstreak@207 377 if mode then
flickerstreak@207 378 self:SetKeybindMode(false)
flickerstreak@207 379 end
flickerstreak@205 380 self.configMode = mode
flickerstreak@205 381 self.callbacks:Fire("OnConfigModeChanged", mode)
flickerstreak@77 382 end
flickerstreak@63 383 end
flickerstreak@63 384
flickerstreak@63 385 function ReAction:GetConfigMode()
flickerstreak@205 386 return self.configMode
flickerstreak@33 387 end
flickerstreak@38 388
flickerstreak@88 389 function ReAction:SetKeybindMode( mode )
flickerstreak@205 390 if mode ~= self.kbMode then
flickerstreak@88 391 if mode then
flickerstreak@207 392 self:SetConfigMode(false)
flickerstreak@184 393 LKB:Activate()
flickerstreak@88 394 else
flickerstreak@184 395 LKB:Deactivate()
flickerstreak@88 396 end
flickerstreak@207 397 for _, bar in self:IterateBars() do
flickerstreak@207 398 bar:SetKeybindMode(mode)
flickerstreak@207 399 end
flickerstreak@205 400 self.kbMode = LKB:IsShown() or false
flickerstreak@88 401 end
flickerstreak@88 402 end
flickerstreak@88 403
flickerstreak@88 404 function ReAction:GetKeybindMode( mode )
flickerstreak@205 405 return self.kbMode
flickerstreak@88 406 end
Flick@225 407
Flick@225 408
Flick@225 409 -- ConfigMode support
Flick@225 410 CONFIGMODE_CALLBACKS = CONFIGMODE_CALLBACKS or {}
Flick@225 411
Flick@225 412 function CONFIGMODE_CALLBACKS.ReAction( action, mode )
Flick@225 413 if action == "ON" then
Flick@225 414 ReAction:SetConfigMode(true)
Flick@225 415 elseif action == "OFF" then
Flick@225 416 ReAction:SetConfigMode(false)
Flick@225 417 elseif action == "LISTMODES" then
Flick@225 418 -- no modes
Flick@225 419 end
Flick@225 420 end
Flick@225 421