annotate SkeletonKey/SkeletonKey.lua @ 16:cdd387d39137

filename refactor
author Nenue
date Fri, 29 Jul 2016 21:18:15 -0400
parents 32d64e42ec9b
children 500f9b2bd9ac
rev   line source
Nenue@0 1 --------------------------------------------
Nenue@5 2 -- SkeletonKey
Nenue@5 3 -- Krakyn-Mal'Ganis
Nenue@0 4 -- @project-revision@ @project-hash@
Nenue@0 5 -- @file-revision@ @file-hash@
Nenue@0 6 -- Created: 6/16/2016 3:47 AM
Nenue@0 7 --------------------------------------------
Nenue@0 8 -- kb
Nenue@5 9 -- .StoreBinding(button, key) bind current keystroke to command
Nenue@5 10 -- .GetSlot(index) return display slot
Nenue@5 11 -- .SetSlot(button, command, name, icon) assign display slot
Nenue@5 12 -- .ReleaseSlot(button) clear button command
Nenue@5 13 -- .UpdateSlot(button) update button contents
Nenue@5 14 -- .SelectProfile(name) set profile character
Nenue@5 15 -- .ApplyBindings(bindings) walk table with SetBinding()
Nenue@0 16
Nenue@5 17 local _
Nenue@5 18 local kb, print = LibStub("LibKraken").register(KeyBinder)
Nenue@5 19 local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg', ...) end or function() end
Nenue@14 20 kb.L = setmetatable({}, {
Nenue@14 21 __call = function(t, k, ...) return format(t[k] or '', ...) end
Nenue@14 22 })
Nenue@14 23 local L = kb.L
Nenue@7 24
Nenue@5 25 --- Caps Lock literals
Nenue@5 26 local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:"
Nenue@6 27 local CLICK_KEYBINDER_KEY = "CLICK KeyBinderKey:"
Nenue@5 28 local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"
Nenue@5 29 local FOOTER_OFFSET
Nenue@5 30 local HEADER_OFFSET
Nenue@14 31 L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
Nenue@14 32 L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
Nenue@14 33 L.BINDING_FAILED_PROTECTED = '|cFFFF4400Unable to use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400)|r'
Nenue@14 34
Nenue@14 35
Nenue@5 36 local BINDING_TYPE_SPECIALIZATION = 3
Nenue@5 37 local BINDING_TYPE_CHARACTER = 2
Nenue@5 38 local BINDING_TYPE_GLOBAL = 1
Nenue@6 39 kb.configTitle = {
Nenue@5 40 [BINDING_TYPE_GLOBAL] = 'Global Binds',
Nenue@0 41 [BINDING_TYPE_CHARACTER] = 'Character: %s',
Nenue@5 42 [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s'
Nenue@0 43 }
Nenue@6 44 kb.configDescription = {
Nenue@5 45 [BINDING_TYPE_GLOBAL] = 'The bindings are applied globally.',
Nenue@5 46 [BINDING_TYPE_CHARACTER] = 'Applied when you log onto this character.',
Nenue@5 47 [BINDING_TYPE_SPECIALIZATION] = 'Applied when you log onto this character and are that specialization.',
Nenue@5 48 }
Nenue@5 49
Nenue@0 50
Nenue@0 51
Nenue@14 52 kb.inactiveTalentBindings = {}
Nenue@6 53 kb.configHeaders = {}
Nenue@6 54 kb.loadedProfiles = {}
Nenue@6 55 kb.orderedProfiles = {}
Nenue@6 56 kb.buttons = {}
Nenue@6 57 kb.macros = {}
Nenue@15 58 kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed
Nenue@15 59 kb.talentFrames = {}
Nenue@15 60 kb.professionFrames = {}
Nenue@0 61
Nenue@14 62 -- these are sent to plugin
Nenue@14 63
Nenue@14 64 local bindings = {}
Nenue@5 65 local macros = {}
Nenue@5 66 local talentBindings = {}
Nenue@0 67
Nenue@0 68 local protected = {
Nenue@0 69 ['OPENCHATSLASH'] = true,
Nenue@0 70 ['OPENCHAT'] = true,
Nenue@0 71 }
Nenue@5 72
Nenue@14 73
Nenue@14 74 local db
Nenue@5 75 local bindHeader, currentHeader = '', ''
Nenue@5 76 local specID, specGlobalID, specName, specDesc, specTexture, characterHeader = 0, 0, 'SPEC_NAME', 'SPEC_DESCRIPTION', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
Nenue@5 77 local classHeader, className, classID = '', '', 0
Nenue@5 78 local bindsCommitted = true
Nenue@5 79 local forceButtonUpdate = false
Nenue@5 80
Nenue@5 81 --- Control handles
Nenue@0 82 local saveButton, restoreButton, clearButton
Nenue@0 83
Nenue@5 84 --- Returns conflicting assignment and binding profiles for use in displaying confirmations
Nenue@6 85 kb.IsCommandBound = function(self, command)
Nenue@6 86 local isAssigned, assignedBy = false, db.bindMode
Nenue@6 87 local isBound, boundBy = false, db.bindMode
Nenue@5 88
Nenue@5 89
Nenue@5 90 command = command or self.command
Nenue@6 91 for i = 1, #kb.orderedProfiles do
Nenue@6 92 local tier = kb.orderedProfiles[i]
Nenue@6 93 if i ~= db.bindMode then
Nenue@5 94
Nenue@5 95 if tier.commands[command] then
Nenue@5 96 isAssigned = true
Nenue@5 97 assignedBy = i
Nenue@5 98 end
Nenue@5 99 if tier.bound[command] then
Nenue@5 100 isBound = true
Nenue@5 101 boundBy = i
Nenue@5 102 end
Nenue@5 103
Nenue@5 104
Nenue@5 105 --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])
Nenue@5 106
Nenue@5 107 if isAssigned and isBound then
Nenue@0 108 break
Nenue@0 109 end
Nenue@0 110 end
Nenue@5 111
Nenue@0 112 end
Nenue@5 113
Nenue@6 114 print('|cFFFFFF00IsCommandBound:|r', command:gsub(CLICK_KEYBINDER_MACRO, ''),'|r [profile:', db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
Nenue@5 115 return isAssigned, isBound, assignedBy, boundBy
Nenue@0 116 end
Nenue@0 117
Nenue@5 118 local talentSpellHardCodes = {
Nenue@5 119 [109248] = 'Binding Shot',
Nenue@5 120 }
Nenue@5 121
Nenue@0 122 --- Returns a value for use with Texture:SetDesaturated()
Nenue@6 123 kb.BindingIsLocked = function(key)
Nenue@0 124 local success = false
Nenue@6 125 for i = 1, db.bindMode-1 do
Nenue@6 126 local tier = kb.orderedProfiles[i]
Nenue@0 127 if tier.bindings[key] then
Nenue@0 128 success = true
Nenue@0 129 break
Nenue@0 130 end
Nenue@0 131 end
Nenue@0 132 return success
Nenue@0 133 end
Nenue@0 134
Nenue@0 135 --- Translates GetBindingKey() results into a printable string.
Nenue@6 136 kb.BindingString = function(...)
Nenue@0 137 local stack = {}
Nenue@0 138 for i = 1, select('#', ...) do
Nenue@0 139 local key = select(i, ...)
Nenue@5 140 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
Nenue@0 141 end
Nenue@0 142
Nenue@0 143 if #stack >= 1 then
Nenue@0 144 return table.concat(stack, ',')
Nenue@0 145 else
Nenue@0 146 return nil
Nenue@0 147 end
Nenue@0 148 end
Nenue@0 149
Nenue@5 150
Nenue@5 151
Nenue@5 152
Nenue@0 153
Nenue@5 154
Nenue@6 155 kb.inactiveTalentBindings = {}
Nenue@5 156 kb.ApplyTalentBinding = function(talentInfo, cache)
Nenue@5 157 for i = 5, #talentInfo do
Nenue@6 158 local command = CLICK_KEYBINDER_KEY.. talentInfo[2]
Nenue@6 159 SetBinding(talentInfo[i], command)
Nenue@6 160 cprint(' **', talentInfo[i], '->', command)
Nenue@5 161 tinsert(cache, talentInfo[i])
Nenue@0 162 end
Nenue@0 163 end
Nenue@5 164 kb.CacheTalentBinding = function(talentInfo, cache)
Nenue@6 165
Nenue@5 166 local spellID = talentInfo[4]
Nenue@5 167 kb.inactiveTalentBindings[spellID] = kb.inactiveTalentBindings[spellID] or {}
Nenue@5 168 kb.inactiveTalentBindings[spellID] = {select(5,unpack(talentInfo)) }
Nenue@6 169 --cprint(spellID, unpack(kb.inactiveTalentBindings[spellID]))
Nenue@0 170 end
Nenue@0 171
Nenue@6 172 kb.LoadBinding = function(command, name, icon, actionType, actionID, macroName, macroText )
Nenue@5 173
Nenue@6 174 if actionType == 'spell' then
Nenue@6 175 KeyBinderKey:SetAttribute("*type-"..name, actionType)
Nenue@6 176 KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)
Nenue@13 177
Nenue@6 178 elseif actionType == 'item' then
Nenue@6 179 KeyBinderKey:SetAttribute("*type-"..name, actionType)
Nenue@6 180 KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)
Nenue@6 181 elseif actionType == 'macro' then
Nenue@5 182 KeyBinderMacro:SetAttribute("*macro-"..macroName, actionID)
Nenue@5 183 else
Nenue@5 184 KeyBinderMacro:SetAttribute("*macrotext-"..macroName, macroText)
Nenue@5 185 end
Nenue@13 186
Nenue@13 187 cprint('Loading binding', actionType, actionID)
Nenue@5 188 bindings[actionType] = bindings[actionType] or {}
Nenue@5 189 bindings[actionType][actionID] = bindings[actionType][actionID] or {}
Nenue@5 190 bindings[command] = bindings[actionType][actionID]
Nenue@5 191 return bindings[actionType], actionID
Nenue@5 192 end
Nenue@5 193
Nenue@5 194 kb.ApplyBindings = function (profile)
Nenue@5 195 cprint('binding profile', profile)
Nenue@5 196 for slot, data in pairs(profile.buttons) do
Nenue@6 197 kb.LoadBinding(unpack(data))
Nenue@5 198 end
Nenue@5 199
Nenue@5 200 for key, command in pairs(profile.bindings) do
Nenue@5 201
Nenue@6 202 cprint(' *', key, '->', command)
Nenue@5 203
Nenue@5 204 --_G.print('HotKey','loading', key, command)
Nenue@5 205 SetBinding(key, command)
Nenue@5 206 if bindings[command] and not tContains(bindings[command], key) then
Nenue@5 207 tinsert(bindings[command], key)
Nenue@5 208 end
Nenue@5 209 end
Nenue@5 210
Nenue@5 211 for spellName, talentInfo in pairs(profile.talents) do
Nenue@5 212 local dummy = GetSpellInfo(spellName)
Nenue@5 213 local func = kb.CacheTalentBinding
Nenue@5 214 local dest = kb.inactiveTalentBindings
Nenue@5 215 if dummy then
Nenue@5 216 cprint('|cFFBBFF00Active:|r', dummy)
Nenue@5 217 local macroName, spellName, actionType, actionID = unpack(talentInfo)
Nenue@5 218 bindings[actionType] = bindings[actionType] or {}
Nenue@5 219 bindings[actionType][actionID] = {}
Nenue@5 220 func = kb.ApplyTalentBinding
Nenue@5 221 dest = bindings[actionType][actionID]
Nenue@5 222 else
Nenue@5 223
Nenue@5 224 cprint('|cFFFF4400Inactive:|r', talentInfo[2])
Nenue@5 225 end
Nenue@5 226 func(talentInfo, dest)
Nenue@5 227 end
Nenue@5 228
Nenue@5 229 SaveBindings(GetCurrentBindingSet())
Nenue@5 230 end
Nenue@5 231
Nenue@5 232 kb.ApplyAllBindings =function ()
Nenue@5 233 table.wipe(kb.inactiveTalentBindings)
Nenue@5 234
Nenue@15 235
Nenue@6 236 -- reflect action key settings
Nenue@6 237 if GetCVarBool("ActionButtonUseKeyDown") then
Nenue@6 238 KeyBinderMacro:RegisterForClicks("AnyDown")
Nenue@6 239 KeyBinderKey:RegisterForClicks("AnyDown")
Nenue@6 240 else
Nenue@6 241 KeyBinderMacro:RegisterForClicks("AnyUp")
Nenue@6 242 KeyBinderKey:RegisterForClicks("AnyUp")
Nenue@6 243 end
Nenue@6 244
Nenue@6 245 for i, profile in ipairs(kb.orderedProfiles) do
Nenue@5 246 kb.ApplyBindings(profile)
Nenue@5 247 end
Nenue@5 248 -- do this after to ensure that profession binds are properly overridden
Nenue@5 249 kb.UpdateProfessionInfo()
Nenue@6 250
Nenue@6 251
Nenue@5 252 end
Nenue@5 253
Nenue@5 254 kb.Command = function(args, editor)
Nenue@5 255 if args:match("import") then
Nenue@5 256 kb.ImportCommmit(args)
Nenue@5 257 return
Nenue@5 258 elseif args:match("scan") then
Nenue@5 259 kb.ImportScan(args)
Nenue@5 260 kb.ui()
Nenue@5 261 return
Nenue@5 262 elseif args:match("load") then
Nenue@5 263 kb:ApplyAllBindings()
Nenue@0 264 return
Nenue@0 265 end
Nenue@0 266
Nenue@5 267 if db.showUI then
Nenue@5 268 db.showUI = false
Nenue@5 269 kb:print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
Nenue@5 270 kb:Hide()
Nenue@5 271 else
Nenue@1 272 db.showUI = true
Nenue@5 273 kb:print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
Nenue@5 274 end
Nenue@5 275 kb.ui(true)
Nenue@5 276 end
Nenue@5 277
Nenue@5 278 kb.InitProfile = function(profile, prototype)
Nenue@5 279 if not profile then
Nenue@5 280 profile = {}
Nenue@5 281 end
Nenue@5 282 if prototype then
Nenue@5 283 print('appplying prototype', prototype)
Nenue@5 284 for k,v in pairs(prototype) do
Nenue@5 285 if not profile[k] then
Nenue@5 286 profile[k] = v
Nenue@5 287 end
Nenue@5 288 end
Nenue@0 289 end
Nenue@0 290
Nenue@5 291 profile.bound = profile.bound or {}
Nenue@5 292 profile.buttons = profile.buttons or {}
Nenue@5 293 profile.commands = profile.commands or {}
Nenue@5 294 profile.bindings = profile.bindings or {}
Nenue@5 295 profile.macros = profile.macros or {}
Nenue@5 296 profile.talents = profile.talents or {}
Nenue@5 297 return profile
Nenue@5 298 end
Nenue@5 299
Nenue@5 300 kb.ResetProfile = function(profile, prototype)
Nenue@6 301 if profile == kb.currentProfile then
Nenue@5 302 for i, button in pairs(buttons) do
Nenue@5 303 kb.ReleaseSlot(button)
Nenue@5 304 end
Nenue@5 305 end
Nenue@5 306 table.wipe(profile)
Nenue@5 307 kb.InitProfile(profile, prototype)
Nenue@5 308 end
Nenue@5 309
Nenue@5 310
Nenue@5 311
Nenue@5 312 --- Handles constructing spec profiles as they are selected
Nenue@5 313
Nenue@5 314
Nenue@5 315 --- Obtains profile data or creates the necessary tables
Nenue@5 316 kb.SelectProfileSet = function(name)
Nenue@5 317
Nenue@6 318 local defaultMode
Nenue@5 319 --- General info
Nenue@5 320 classHeader, className, classID = UnitClass('player')
Nenue@5 321 print('|cFF00FF00profile:|r', name)
Nenue@5 322 print('|cFF00FF00class:|r', UnitClass('player'))
Nenue@5 323
Nenue@5 324 --- Global
Nenue@6 325 defaultMode = BINDING_TYPE_GLOBAL
Nenue@5 326 kb.InitProfile(db)
Nenue@6 327 kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
Nenue@5 328
Nenue@5 329 --- Character
Nenue@5 330 if name then
Nenue@5 331 db[name] = kb.InitProfile(db[name],
Nenue@5 332 {classHeader = classHeader, className = className, classID = classID})
Nenue@6 333 kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
Nenue@6 334 defaultMode = BINDING_TYPE_CHARACTER
Nenue@5 335 end
Nenue@5 336
Nenue@5 337 --- Mutable skills data
Nenue@5 338 kb.UpdateSpecInfo()
Nenue@5 339 kb.UpdateTalentInfo()
Nenue@5 340
Nenue@6 341 kb.orderedProfiles = {kb.loadedProfiles[BINDING_TYPE_GLOBAL], kb.loadedProfiles[BINDING_TYPE_CHARACTER], kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION]}
Nenue@15 342 if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then
Nenue@6 343 print('fixing bad bindMode value, was', db.bindMode)
Nenue@6 344 db.bindMode = defaultMode
Nenue@5 345 end
Nenue@5 346
Nenue@5 347
Nenue@5 348 print(BINDING_TYPE_GLOBAL)
Nenue@6 349 kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
Nenue@6 350 kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
Nenue@10 351 kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name)
Nenue@5 352
Nenue@5 353
Nenue@6 354 setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
Nenue@6 355 setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
Nenue@6 356 setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
Nenue@5 357
Nenue@6 358 print('|cFF00FF00bindMode:|r', db.bindMode)
Nenue@6 359 kb.currentProfile = kb.loadedProfiles[db.bindMode]
Nenue@5 360 end
Nenue@5 361
Nenue@5 362 local scrollCache = {}
Nenue@5 363 kb.SelectTab = function(self)
Nenue@6 364 scrollCache[db.bindMode] = kb.scrollOffset
Nenue@5 365 db.bindMode = self:GetID()
Nenue@6 366 kb.currentProfile = kb.loadedProfiles[self:GetID()]
Nenue@6 367 kb.scrollOffset = scrollCache[db.bindMode] or 0
Nenue@5 368 kb.ui(true)
Nenue@5 369 end
Nenue@5 370
Nenue@5 371 kb.RevertBindings = function()
Nenue@5 372 -- todo: reversion code
Nenue@5 373 end
Nenue@5 374
Nenue@5 375 kb.ConfirmBindings = function()
Nenue@5 376 kb.ApplyAllBindings()
Nenue@5 377 kb.ui()
Nenue@5 378 end
Nenue@5 379
Nenue@5 380
Nenue@5 381
Nenue@5 382
Nenue@5 383 --- post ADDON_LOADED
Nenue@5 384 kb.variables = function()
Nenue@5 385 SkeletonKeyDB = SkeletonKeyDB or {spec = {}}
Nenue@5 386 kb.db = SkeletonKeyDB
Nenue@5 387 kb.playerName = UnitName('player')
Nenue@5 388 kb.playerRealm = SelectedRealmName()
Nenue@5 389 kb.profileName = kb.playerRealm .. '_' .. kb.playerName
Nenue@5 390 db = kb.db
Nenue@5 391
Nenue@5 392 kb.SelectProfileSet(kb.profileName)
Nenue@6 393 -- todo: redo import checking
Nenue@6 394
Nenue@15 395 kb.UpdateSystemBinds()
Nenue@5 396 kb.ApplyAllBindings()
Nenue@5 397
Nenue@5 398 kb.ui(true)
Nenue@0 399 end
Nenue@0 400
Nenue@1 401
Nenue@5 402 kb.wrap = function(module)
Nenue@5 403 kb.modules = kb.modules or {}
Nenue@5 404 tinsert(kb.modules, module)
Nenue@0 405 end
Nenue@0 406
Nenue@5 407 -- Volatiles Access
Nenue@5 408 kb.GetBindings = function() return bindings end
Nenue@5 409 kb.GetButtons = function() return buttons end
Nenue@6 410 kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
Nenue@6 411 kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
Nenue@5 412 kb.GetLooseTalents = function() return talentBindings end
Nenue@5 413 kb.GetReverts = function() return reverts end
Nenue@6 414 kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
Nenue@0 415
Nenue@0 416
Nenue@5 417 SLASH_SKB1 = "/skb"
Nenue@5 418 SLASH_SKB2 = "/skeletonkey"
Nenue@5 419 SlashCmdList.SKB = kb.Command