annotate SkeletonKey/KeyBinds.lua @ 6:f6d1c192afc6

Refactored file layout: - frame display logic in UI.lua - player data in Cache.lua - event responses in Events.lua a lot of local tables are now stored members of KeyBinder for that to work
author Nenue
date Thu, 28 Jul 2016 16:45:56 -0400
parents 9ac29fe77455
children a2fc77fa4c73
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@1 19 local db
Nenue@5 20 local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg', ...) end or function() end
Nenue@1 21
Nenue@5 22 --- Caps Lock literals
Nenue@5 23 local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:"
Nenue@6 24 local CLICK_KEYBINDER_KEY = "CLICK KeyBinderKey:"
Nenue@5 25 local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
Nenue@5 26 local BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
Nenue@5 27 local BINDING_FAILED_PROTECTED = '|cFFFF4400Unable to use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400)|r'
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@5 31 local HELP_1 = "Drag and drop spells/items from your inventory, spellbook, or collections panels."
Nenue@5 32 local HELP_2 = "While the cursor is above an icon, up to two key combinations will be bound to that action."
Nenue@5 33 local HELP_3 = "If that key used for a client binding (e.g. game menu), a confirmation popup will appear before making the change."
Nenue@6 34 local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
Nenue@6 35 local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION
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@5 39
Nenue@5 40
Nenue@5 41 --- Caps Lock derivatives
Nenue@5 42 local ACTION_SCRIPT = {
Nenue@5 43 ['mount'] = "/script C_MountJournal.SummonByID(%d)",
Nenue@5 44 ['macro'] = "%s",
Nenue@5 45 ['equipset'] = "/script UseEquipmentSet(%d)",
Nenue@5 46 ['spell'] = "/cast %s",
Nenue@5 47 ['petaction'] = "/cast %s",
Nenue@5 48 ['battlepet'] = SLASH_SUMMON_BATTLE_PET1 .. " %s",
Nenue@5 49 ['item'] = "/use %s"
Nenue@5 50 }
Nenue@5 51
Nenue@5 52 local professionMappings = {
Nenue@5 53 [5] = 3,
Nenue@5 54 [7] = 4,
Nenue@5 55 [9] = 5,
Nenue@5 56 [10] = 6
Nenue@5 57 }
Nenue@5 58
Nenue@6 59 kb.configTitle = {
Nenue@5 60 [BINDING_TYPE_GLOBAL] = 'Global Binds',
Nenue@0 61 [BINDING_TYPE_CHARACTER] = 'Character: %s',
Nenue@5 62 [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s'
Nenue@0 63 }
Nenue@6 64 kb.configDescription = {
Nenue@5 65 [BINDING_TYPE_GLOBAL] = 'The bindings are applied globally.',
Nenue@5 66 [BINDING_TYPE_CHARACTER] = 'Applied when you log onto this character.',
Nenue@5 67 [BINDING_TYPE_SPECIALIZATION] = 'Applied when you log onto this character and are that specialization.',
Nenue@5 68 }
Nenue@5 69
Nenue@0 70
Nenue@0 71
Nenue@6 72 kb.configHeaders = {}
Nenue@6 73 kb.loadedProfiles = {}
Nenue@6 74 kb.orderedProfiles = {}
Nenue@6 75 kb.buttons = {}
Nenue@6 76 kb.macros = {}
Nenue@0 77
Nenue@5 78 local buttons = {}
Nenue@5 79 -- Backlog of changes
Nenue@5 80 local reverts = {}
Nenue@5 81 -- macro buttons used for mounts and other buttonable non-spells
Nenue@5 82 local macros = {}
Nenue@5 83 -- currently active non-blizzard keybinds
Nenue@5 84 local bindings = {}
Nenue@5 85 -- unselected talents
Nenue@5 86 local talentBindings = {}
Nenue@5 87 kb.inactiveTalentBindings = {}
Nenue@5 88 -- placeholder for the StaticPopup used for confirmations
Nenue@5 89 local confirmation
Nenue@0 90
Nenue@0 91 local protected = {
Nenue@0 92 ['OPENCHATSLASH'] = true,
Nenue@0 93 ['OPENCHAT'] = true,
Nenue@0 94 }
Nenue@5 95
Nenue@5 96 --- Used to reflect the current working state
Nenue@5 97 local bindHeader, currentHeader = '', ''
Nenue@5 98 local specID, specGlobalID, specName, specDesc, specTexture, characterHeader = 0, 0, 'SPEC_NAME', 'SPEC_DESCRIPTION', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
Nenue@5 99 local classHeader, className, classID = '', '', 0
Nenue@5 100 local bindsCommitted = true
Nenue@5 101 local forceButtonUpdate = false
Nenue@5 102
Nenue@5 103 --- Control handles
Nenue@0 104 local saveButton, restoreButton, clearButton
Nenue@0 105
Nenue@5 106 --- Cursor "pickup" actuators
Nenue@5 107 local PickupAction = {}
Nenue@5 108 PickupAction.spell = _G.PickupSpell
Nenue@5 109 PickupAction.macro = _G.PickupMacro
Nenue@5 110 PickupAction.item = _G.PickupItem
Nenue@5 111 PickupAction.mount = _G.C_MountJournal.Pickup
Nenue@5 112 local GetPickupValue = {}
Nenue@5 113 GetPickupValue.spell = function(self) return select(7, GetSpellInfo(self.actionID)) end
Nenue@5 114
Nenue@5 115 --- Returns conflicting assignment and binding profiles for use in displaying confirmations
Nenue@6 116 kb.IsCommandBound = function(self, command)
Nenue@6 117 local isAssigned, assignedBy = false, db.bindMode
Nenue@6 118 local isBound, boundBy = false, db.bindMode
Nenue@5 119
Nenue@5 120
Nenue@5 121 command = command or self.command
Nenue@6 122 for i = 1, #kb.orderedProfiles do
Nenue@6 123 local tier = kb.orderedProfiles[i]
Nenue@6 124 if i ~= db.bindMode then
Nenue@5 125
Nenue@5 126 if tier.commands[command] then
Nenue@5 127 isAssigned = true
Nenue@5 128 assignedBy = i
Nenue@5 129 end
Nenue@5 130 if tier.bound[command] then
Nenue@5 131 isBound = true
Nenue@5 132 boundBy = i
Nenue@5 133 end
Nenue@5 134
Nenue@5 135
Nenue@5 136 --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])
Nenue@5 137
Nenue@5 138 if isAssigned and isBound then
Nenue@0 139 break
Nenue@0 140 end
Nenue@0 141 end
Nenue@5 142
Nenue@0 143 end
Nenue@5 144
Nenue@6 145 print('|cFFFFFF00IsCommandBound:|r', command:gsub(CLICK_KEYBINDER_MACRO, ''),'|r [profile:', db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
Nenue@5 146 return isAssigned, isBound, assignedBy, boundBy
Nenue@0 147 end
Nenue@0 148
Nenue@5 149 local talentSpellHardCodes = {
Nenue@5 150 [109248] = 'Binding Shot',
Nenue@5 151 }
Nenue@5 152
Nenue@0 153 --- Returns a value for use with Texture:SetDesaturated()
Nenue@6 154 kb.BindingIsLocked = function(key)
Nenue@0 155 local success = false
Nenue@6 156 for i = 1, db.bindMode-1 do
Nenue@6 157 local tier = kb.orderedProfiles[i]
Nenue@0 158 if tier.bindings[key] then
Nenue@0 159 success = true
Nenue@0 160 break
Nenue@0 161 end
Nenue@0 162 end
Nenue@0 163 return success
Nenue@0 164 end
Nenue@0 165
Nenue@0 166 --- Translates GetBindingKey() results into a printable string.
Nenue@6 167 kb.BindingString = function(...)
Nenue@0 168 local stack = {}
Nenue@0 169 for i = 1, select('#', ...) do
Nenue@0 170 local key = select(i, ...)
Nenue@5 171 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
Nenue@0 172 end
Nenue@0 173
Nenue@0 174 if #stack >= 1 then
Nenue@0 175 return table.concat(stack, ',')
Nenue@0 176 else
Nenue@0 177 return nil
Nenue@0 178 end
Nenue@0 179 end
Nenue@0 180
Nenue@5 181
Nenue@5 182
Nenue@5 183
Nenue@5 184 kb.DropToSlot = function(self)
Nenue@5 185
Nenue@5 186 print(self:GetName(),'|cFF0088FFreceived|r')
Nenue@5 187 local actionType, actionID, subType, subData = GetCursorInfo()
Nenue@5 188 print('GetCursorInfo', GetCursorInfo())
Nenue@5 189
Nenue@5 190
Nenue@5 191 if actionType then
Nenue@5 192
Nenue@5 193 if actionType == 'flyout' then
Nenue@5 194 ClearCursor()
Nenue@5 195 ResetCursor()
Nenue@5 196 return
Nenue@5 197 end
Nenue@5 198
Nenue@5 199
Nenue@5 200 local macroName, macroText
Nenue@5 201 local command, name, icon, _
Nenue@5 202 local pickupID, pickupBook
Nenue@5 203
Nenue@5 204 if actionType == 'spell' then
Nenue@5 205 actionID = subData
Nenue@5 206 name, _, icon = GetSpellInfo(actionID)
Nenue@5 207
Nenue@5 208 elseif actionType == 'macro' then
Nenue@5 209 name, icon = GetMacroInfo(actionID)
Nenue@5 210 actionID = name
Nenue@5 211 elseif actionType == 'petaction' then
Nenue@5 212 if not (CURSOR_SPELLSLOT and CURSOR_BOOKTYPE) then
Nenue@5 213
Nenue@5 214 ClearCursor()
Nenue@5 215 ResetCursor()
Nenue@0 216 end
Nenue@0 217
Nenue@5 218 local bookType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
Nenue@5 219 pickupID = CURSOR_SPELLSLOT
Nenue@5 220 pickupBook = CURSOR_BOOKTYPE
Nenue@5 221 name, _, icon = GetSpellInfo(spellID)
Nenue@5 222 actionID = name
Nenue@5 223
Nenue@5 224 elseif actionType == 'mount' then
Nenue@5 225 if subType == 0 then
Nenue@5 226 name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
Nenue@5 227 actionID = 0
Nenue@5 228 else
Nenue@5 229 name, _, icon = C_MountJournal.GetMountInfoByID(actionID)
Nenue@5 230 end
Nenue@5 231 elseif actionType == 'item' then
Nenue@5 232 name = GetItemInfo(actionID)
Nenue@5 233 icon = GetItemIcon(actionID)
Nenue@5 234 actionID = name
Nenue@5 235 elseif actionType == 'battlepet' then
Nenue@5 236
Nenue@5 237 local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = C_PetJournal.GetPetInfoByPetID(detail);
Nenue@5 238 name = customName or petName
Nenue@5 239 icon = petIcon
Nenue@5 240
Nenue@5 241 end
Nenue@5 242 macroName, macroText, command = kb.RegisterAction(actionType, actionID)
Nenue@5 243
Nenue@5 244
Nenue@6 245 local isAssigned, isBound, assignedBy, boundBy = kb.IsCommandBound(self, command)
Nenue@5 246 if isAssigned then
Nenue@5 247 local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
Nenue@5 248 popup.slot = self
Nenue@5 249 popup.text = "Currently assigned in |cFFFFFF00"..tostring(configHeaders[assignedBy]).."|r. Are you sure?"
Nenue@5 250 popup.oldProfile = assignedBy
Nenue@5 251 popup.args = {command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook }
Nenue@5 252 kb:SetScript('OnMouseWheel', nil) -- disable scrolling
Nenue@5 253 StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
Nenue@5 254 else
Nenue@5 255 kb.SetSlot(self, command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook)
Nenue@5 256 kb.UpdateSlot(self)
Nenue@5 257 self.active = nil
Nenue@5 258 ClearCursor()
Nenue@5 259 ResetCursor()
Nenue@0 260 end
Nenue@0 261 end
Nenue@0 262 end
Nenue@0 263
Nenue@5 264 kb.PickupSlot = function(self)
Nenue@0 265 if not self.command then
Nenue@0 266 return
Nenue@0 267 end
Nenue@5 268 print(self.actionType)
Nenue@5 269 if self.actionType == 'spell' then
Nenue@5 270 -- It can't be picked up if SpellInfo(name) returns void
Nenue@5 271 local dummy = GetSpellInfo(self.actionName)
Nenue@5 272 if not dummy then
Nenue@5 273 return
Nenue@0 274 end
Nenue@5 275 elseif self.actionType == 'petaction' then
Nenue@5 276 PickupSpellBookItem(self.pickupSlot, self.pickupBook)
Nenue@5 277 end
Nenue@5 278 if PickupAction[self.actionType] then
Nenue@5 279 if GetPickupValue[self.actionType] then
Nenue@5 280 PickupAction[self.actionType](GetPickupValue[self.actionType](self))
Nenue@5 281 else
Nenue@5 282 PickupAction[self.actionType](self.actionID)
Nenue@0 283 end
Nenue@5 284 kb.ReleaseSlot(self)
Nenue@5 285 kb.UpdateSlot(self)
Nenue@0 286 end
Nenue@0 287 end
Nenue@0 288
Nenue@5 289
Nenue@6 290 --- Resolve the appropriate command and macroText for the given action parameters
Nenue@5 291 kb.RegisterAction = function(type, id)
Nenue@6 292 local macroText, macroName, command = '', '', ''
Nenue@5 293
Nenue@5 294 if type == 'spell' then
Nenue@6 295 if kb.ProfessionCache[id] then
Nenue@6 296 command = CLICK_KEYBINDER_KEY .. "profession_".. kb.ProfessionCache[id].profOffset .. '_' .. kb.ProfessionCache[id].spellNum
Nenue@6 297 else
Nenue@6 298 command = CLICK_KEYBINDER_KEY ..id
Nenue@6 299 end
Nenue@6 300 else
Nenue@6 301 macroName = type .. ' ' .. id
Nenue@6 302 macroText = ACTION_SCRIPT[type]:format(id)
Nenue@6 303 local baseName, iterative = macroName, 1
Nenue@6 304 while (macros[macroName] and macros[macroName][1] ~= macroText) do
Nenue@6 305 print(' * cannot use|cFF00FF00', macroName, '|r"'.. (macros[macroName][1] or '') .. '"')
Nenue@6 306 macroName = baseName .. '_' .. iterative
Nenue@6 307 iterative = iterative + 1
Nenue@6 308 end
Nenue@6 309 if macroName ~= baseName then
Nenue@6 310 print(' * Creating|cFF00FF00', macroName)
Nenue@6 311 else
Nenue@6 312 print(' * Re-using|cFF00FF00', macroName)
Nenue@6 313 end
Nenue@6 314 command = 'CLICK KeyBinderMacro:'.. macroName
Nenue@6 315 macros[macroName] = {macroText, command }
Nenue@5 316 end
Nenue@5 317
Nenue@6 318 print('RegisterAction', type, id, '->', command , macroText)
Nenue@5 319 return macroName, macroText, command
Nenue@0 320 end
Nenue@0 321
Nenue@0 322 --- Updates the current KeyBinding for the button's command
Nenue@5 323 kb.StoreBinding = function(self, key)
Nenue@0 324
Nenue@0 325 if not self.command then
Nenue@0 326 return
Nenue@0 327 end
Nenue@0 328
Nenue@0 329 if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then
Nenue@0 330 return
Nenue@0 331 end
Nenue@5 332 print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
Nenue@0 333
Nenue@5 334 if key == 'ESCAPE' then
Nenue@5 335 local keys = {GetBindingKey(self.command) }
Nenue@5 336 --print('detected', #keys, 'bindings')
Nenue@5 337 for i, key in pairs(keys) do
Nenue@5 338 --print('clearing', key)
Nenue@5 339 SetBinding(key, nil)
Nenue@5 340 SaveBindings(GetCurrentBindingSet())
Nenue@6 341 if kb.currentProfile.bindings[key] then
Nenue@6 342 kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[db.bindMode]))
Nenue@6 343 kb.currentProfile.bindings[key] = nil
Nenue@5 344 end
Nenue@6 345 if kb.currentProfile.talents[self.actionName] then
Nenue@6 346 kb.currentProfile.talents[self.actionName] = nil
Nenue@5 347 end
Nenue@5 348 bindings[self.actionType][self.actionID] = nil
Nenue@5 349 end
Nenue@6 350 if kb.currentProfile.bound[self.command] then
Nenue@6 351 kb.currentProfile.bound[self.command] = nil
Nenue@6 352 --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[db.bindMode]))
Nenue@5 353 end
Nenue@5 354
Nenue@5 355 bindsCommitted = false
Nenue@5 356 self.active = false
Nenue@5 357 else
Nenue@5 358
Nenue@5 359 local modifier = ''
Nenue@5 360 if IsAltKeyDown() then
Nenue@5 361 modifier = 'ALT-'
Nenue@5 362 end
Nenue@5 363 if IsControlKeyDown() then
Nenue@5 364 modifier = modifier.. 'CTRL-'
Nenue@5 365 end
Nenue@5 366 if IsShiftKeyDown() then
Nenue@5 367 modifier = modifier..'SHIFT-'
Nenue@5 368 end
Nenue@5 369
Nenue@5 370
Nenue@5 371 if self.command then
Nenue@5 372 self.binding = modifier..key
Nenue@5 373
Nenue@5 374 local previousKeys
Nenue@5 375 local previousAction = GetBindingAction(self.binding)
Nenue@5 376 local binding1, binding2, new1, new2
Nenue@5 377 print(type(previousAction), previousAction)
Nenue@5 378 if previousAction ~= "" and previousAction ~= self.command then
Nenue@5 379 if protected[previousAction] then
Nenue@5 380 -- bounce out if trying to use a protected key
Nenue@5 381 kb.statustext:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(previousAction)))
Nenue@5 382 kb.bindingstext:SetText(nil)
Nenue@5 383 return
Nenue@5 384 else
Nenue@5 385 kb:print('Discarding keybind for', previousAction)
Nenue@5 386 -- todo: sort out retcon'd talent spells
Nenue@5 387 end
Nenue@5 388 end
Nenue@5 389
Nenue@5 390 self.pending = true
Nenue@5 391
Nenue@5 392 bindsCommitted = false
Nenue@5 393 SetBinding(self.binding, self.command)
Nenue@5 394 SaveBindings(GetCurrentBindingSet())
Nenue@5 395
Nenue@5 396 local talentInfo
Nenue@5 397 if self.actionType == 'spell' and kb.TalentCache[self.actionID] then
Nenue@5 398 print('conditional binding (talent = "'..self.actionName..'")')
Nenue@5 399 talentInfo = {self.macroName, self.actionName, self.actionType, self.actionID}
Nenue@5 400 local bindings = {GetBindingKey(self.command) }
Nenue@5 401 for i, key in ipairs(bindings) do
Nenue@5 402 tinsert(talentInfo, key)
Nenue@5 403 end
Nenue@5 404 end
Nenue@5 405
Nenue@6 406 for level, profile in ipairs(kb.orderedProfiles) do
Nenue@6 407 if (level == db.bindMode) then
Nenue@6 408 profile.bound[self.command] = true
Nenue@5 409 if talentInfo then
Nenue@6 410 profile.bindings[self.binding] = nil
Nenue@5 411 else
Nenue@6 412 profile.bindings[self.binding] = self.command
Nenue@5 413 end
Nenue@6 414 profile.talents[self.actionName] = talentInfo
Nenue@5 415 else
Nenue@6 416 profile.bindings[self.binding] = nil
Nenue@6 417 profile.bound[self.command] = nil
Nenue@6 418 kb.currentProfile.talents[self.actionName] = nil
Nenue@5 419 end
Nenue@6 420 if kb.currentProfile.talents[self.actionID] then
Nenue@6 421 kb.currentProfile.talents[self.actionID] = nil
Nenue@5 422 end
Nenue@5 423
Nenue@5 424 end
Nenue@5 425
Nenue@5 426
Nenue@5 427
Nenue@6 428 kb:print(BINDING_ASSIGNED:format(self.binding, self.actionName, kb.configHeaders[db.bindMode]))
Nenue@5 429
Nenue@5 430 end
Nenue@0 431 end
Nenue@0 432
Nenue@5 433 kb.UpdateSlot(self, true)
Nenue@5 434 KeyBinderSaveButton:Enable()
Nenue@0 435
Nenue@0 436 end
Nenue@0 437
Nenue@5 438
Nenue@6 439 kb.inactiveTalentBindings = {}
Nenue@5 440 kb.ApplyTalentBinding = function(talentInfo, cache)
Nenue@5 441 for i = 5, #talentInfo do
Nenue@6 442 local command = CLICK_KEYBINDER_KEY.. talentInfo[2]
Nenue@6 443 SetBinding(talentInfo[i], command)
Nenue@6 444 cprint(' **', talentInfo[i], '->', command)
Nenue@5 445 tinsert(cache, talentInfo[i])
Nenue@0 446 end
Nenue@0 447 end
Nenue@5 448 kb.CacheTalentBinding = function(talentInfo, cache)
Nenue@6 449
Nenue@5 450 local spellID = talentInfo[4]
Nenue@5 451 kb.inactiveTalentBindings[spellID] = kb.inactiveTalentBindings[spellID] or {}
Nenue@5 452 kb.inactiveTalentBindings[spellID] = {select(5,unpack(talentInfo)) }
Nenue@6 453 --cprint(spellID, unpack(kb.inactiveTalentBindings[spellID]))
Nenue@0 454 end
Nenue@0 455
Nenue@6 456 kb.LoadBinding = function(command, name, icon, actionType, actionID, macroName, macroText )
Nenue@5 457
Nenue@6 458
Nenue@6 459
Nenue@6 460 if actionType == 'spell' then
Nenue@6 461 KeyBinderKey:SetAttribute("*type-"..name, actionType)
Nenue@6 462 KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)
Nenue@6 463 elseif actionType == 'item' then
Nenue@6 464 KeyBinderKey:SetAttribute("*type-"..name, actionType)
Nenue@6 465 KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)
Nenue@6 466 elseif actionType == 'macro' then
Nenue@5 467 KeyBinderMacro:SetAttribute("*macro-"..macroName, actionID)
Nenue@5 468 else
Nenue@5 469 KeyBinderMacro:SetAttribute("*macrotext-"..macroName, macroText)
Nenue@5 470 end
Nenue@5 471 bindings[actionType] = bindings[actionType] or {}
Nenue@5 472 bindings[actionType][actionID] = bindings[actionType][actionID] or {}
Nenue@5 473 bindings[command] = bindings[actionType][actionID]
Nenue@5 474 return bindings[actionType], actionID
Nenue@5 475 end
Nenue@5 476
Nenue@5 477 kb.ApplyBindings = function (profile)
Nenue@5 478 cprint('binding profile', profile)
Nenue@5 479 for slot, data in pairs(profile.buttons) do
Nenue@6 480 kb.LoadBinding(unpack(data))
Nenue@5 481 end
Nenue@5 482
Nenue@5 483 for key, command in pairs(profile.bindings) do
Nenue@5 484
Nenue@6 485 cprint(' *', key, '->', command)
Nenue@5 486
Nenue@5 487 --_G.print('HotKey','loading', key, command)
Nenue@5 488 SetBinding(key, command)
Nenue@5 489 if bindings[command] and not tContains(bindings[command], key) then
Nenue@5 490 tinsert(bindings[command], key)
Nenue@5 491 end
Nenue@5 492 end
Nenue@5 493
Nenue@5 494 for spellName, talentInfo in pairs(profile.talents) do
Nenue@5 495 local dummy = GetSpellInfo(spellName)
Nenue@5 496 local func = kb.CacheTalentBinding
Nenue@5 497 local dest = kb.inactiveTalentBindings
Nenue@5 498 if dummy then
Nenue@5 499 cprint('|cFFBBFF00Active:|r', dummy)
Nenue@5 500 local macroName, spellName, actionType, actionID = unpack(talentInfo)
Nenue@5 501 bindings[actionType] = bindings[actionType] or {}
Nenue@5 502 bindings[actionType][actionID] = {}
Nenue@5 503 func = kb.ApplyTalentBinding
Nenue@5 504 dest = bindings[actionType][actionID]
Nenue@5 505 else
Nenue@5 506
Nenue@5 507 cprint('|cFFFF4400Inactive:|r', talentInfo[2])
Nenue@5 508 end
Nenue@5 509 func(talentInfo, dest)
Nenue@5 510 end
Nenue@5 511
Nenue@5 512 SaveBindings(GetCurrentBindingSet())
Nenue@5 513 end
Nenue@5 514
Nenue@5 515 kb.ApplyAllBindings =function ()
Nenue@5 516 table.wipe(kb.inactiveTalentBindings)
Nenue@5 517
Nenue@6 518 -- reflect action key settings
Nenue@6 519 if GetCVarBool("ActionButtonUseKeyDown") then
Nenue@6 520 KeyBinderMacro:RegisterForClicks("AnyDown")
Nenue@6 521 KeyBinderKey:RegisterForClicks("AnyDown")
Nenue@6 522 else
Nenue@6 523 KeyBinderMacro:RegisterForClicks("AnyUp")
Nenue@6 524 KeyBinderKey:RegisterForClicks("AnyUp")
Nenue@6 525 end
Nenue@6 526
Nenue@6 527 for i, profile in ipairs(kb.orderedProfiles) do
Nenue@5 528 kb.ApplyBindings(profile)
Nenue@5 529 end
Nenue@5 530 -- do this after to ensure that profession binds are properly overridden
Nenue@5 531 kb.UpdateProfessionInfo()
Nenue@6 532
Nenue@6 533
Nenue@5 534 end
Nenue@5 535
Nenue@5 536 kb.Command = function(args, editor)
Nenue@5 537 if args:match("import") then
Nenue@5 538 kb.ImportCommmit(args)
Nenue@5 539 return
Nenue@5 540 elseif args:match("scan") then
Nenue@5 541 kb.ImportScan(args)
Nenue@5 542 kb.ui()
Nenue@5 543 return
Nenue@5 544 elseif args:match("load") then
Nenue@5 545 kb:ApplyAllBindings()
Nenue@0 546 return
Nenue@0 547 end
Nenue@0 548
Nenue@5 549 if db.showUI then
Nenue@5 550 db.showUI = false
Nenue@5 551 kb:print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
Nenue@5 552 kb:Hide()
Nenue@5 553 else
Nenue@1 554 db.showUI = true
Nenue@5 555 kb:print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
Nenue@5 556 end
Nenue@5 557 kb.ui(true)
Nenue@5 558 end
Nenue@5 559
Nenue@5 560 kb.InitProfile = function(profile, prototype)
Nenue@5 561 if not profile then
Nenue@5 562 profile = {}
Nenue@5 563 end
Nenue@5 564 if prototype then
Nenue@5 565 print('appplying prototype', prototype)
Nenue@5 566 for k,v in pairs(prototype) do
Nenue@5 567 if not profile[k] then
Nenue@5 568 profile[k] = v
Nenue@5 569 end
Nenue@5 570 end
Nenue@0 571 end
Nenue@0 572
Nenue@5 573 profile.bound = profile.bound or {}
Nenue@5 574 profile.buttons = profile.buttons or {}
Nenue@5 575 profile.commands = profile.commands or {}
Nenue@5 576 profile.bindings = profile.bindings or {}
Nenue@5 577 profile.macros = profile.macros or {}
Nenue@5 578 profile.talents = profile.talents or {}
Nenue@5 579 return profile
Nenue@5 580 end
Nenue@5 581
Nenue@5 582 kb.ResetProfile = function(profile, prototype)
Nenue@6 583 if profile == kb.currentProfile then
Nenue@5 584 for i, button in pairs(buttons) do
Nenue@5 585 kb.ReleaseSlot(button)
Nenue@5 586 end
Nenue@5 587 end
Nenue@5 588 table.wipe(profile)
Nenue@5 589 kb.InitProfile(profile, prototype)
Nenue@5 590 end
Nenue@5 591
Nenue@5 592
Nenue@5 593
Nenue@5 594 --- Handles constructing spec profiles as they are selected
Nenue@5 595
Nenue@5 596
Nenue@5 597 --- Obtains profile data or creates the necessary tables
Nenue@5 598 kb.SelectProfileSet = function(name)
Nenue@5 599
Nenue@6 600 local defaultMode
Nenue@5 601 --- General info
Nenue@5 602 classHeader, className, classID = UnitClass('player')
Nenue@5 603 print('|cFF00FF00profile:|r', name)
Nenue@5 604 print('|cFF00FF00class:|r', UnitClass('player'))
Nenue@5 605
Nenue@5 606 --- Global
Nenue@6 607 defaultMode = BINDING_TYPE_GLOBAL
Nenue@5 608 kb.InitProfile(db)
Nenue@6 609 kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
Nenue@5 610
Nenue@5 611 --- Character
Nenue@5 612 if name then
Nenue@5 613 db[name] = kb.InitProfile(db[name],
Nenue@5 614 {classHeader = classHeader, className = className, classID = classID})
Nenue@6 615 kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
Nenue@6 616 defaultMode = BINDING_TYPE_CHARACTER
Nenue@5 617 end
Nenue@5 618
Nenue@5 619 --- Mutable skills data
Nenue@5 620 kb.UpdateSpecInfo()
Nenue@5 621 kb.UpdateTalentInfo()
Nenue@5 622
Nenue@6 623 kb.orderedProfiles = {kb.loadedProfiles[BINDING_TYPE_GLOBAL], kb.loadedProfiles[BINDING_TYPE_CHARACTER], kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION]}
Nenue@6 624 if db.bindMode and (not kb.configTitle[db.bindMode]) then
Nenue@6 625 print('fixing bad bindMode value, was', db.bindMode)
Nenue@6 626 db.bindMode = defaultMode
Nenue@5 627 end
Nenue@5 628
Nenue@5 629
Nenue@5 630 print(BINDING_TYPE_GLOBAL)
Nenue@6 631 kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
Nenue@6 632 kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
Nenue@6 633 kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(specName)
Nenue@5 634
Nenue@5 635
Nenue@6 636 setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
Nenue@6 637 setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
Nenue@6 638 setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
Nenue@5 639
Nenue@6 640 print('|cFF00FF00bindMode:|r', db.bindMode)
Nenue@6 641 kb.currentProfile = kb.loadedProfiles[db.bindMode]
Nenue@5 642 end
Nenue@5 643
Nenue@5 644 local scrollCache = {}
Nenue@5 645 kb.SelectTab = function(self)
Nenue@6 646 scrollCache[db.bindMode] = kb.scrollOffset
Nenue@5 647 db.bindMode = self:GetID()
Nenue@6 648 kb.currentProfile = kb.loadedProfiles[self:GetID()]
Nenue@6 649 kb.scrollOffset = scrollCache[db.bindMode] or 0
Nenue@5 650 kb.ui(true)
Nenue@5 651 end
Nenue@5 652
Nenue@5 653 kb.RevertBindings = function()
Nenue@5 654 -- todo: reversion code
Nenue@5 655 end
Nenue@5 656
Nenue@5 657 kb.ConfirmBindings = function()
Nenue@5 658 SaveBindings(GetCurrentBindingSet())
Nenue@5 659 bindsCommitted = true
Nenue@5 660 for i, button in ipairs(buttons) do
Nenue@5 661 button.pending = false
Nenue@5 662 end
Nenue@5 663 kb.ApplyAllBindings()
Nenue@5 664
Nenue@5 665 kb.ui()
Nenue@5 666 kb:print('Keybinds saved.')
Nenue@5 667 end
Nenue@5 668
Nenue@5 669
Nenue@5 670
Nenue@5 671
Nenue@5 672 --- post ADDON_LOADED
Nenue@5 673 kb.variables = function()
Nenue@5 674 SkeletonKeyDB = SkeletonKeyDB or {spec = {}}
Nenue@5 675 kb.db = SkeletonKeyDB
Nenue@5 676 kb.playerName = UnitName('player')
Nenue@5 677 kb.playerRealm = SelectedRealmName()
Nenue@5 678 kb.profileName = kb.playerRealm .. '_' .. kb.playerName
Nenue@5 679 db = kb.db
Nenue@5 680
Nenue@5 681 kb.SelectProfileSet(kb.profileName)
Nenue@6 682 -- todo: redo import checking
Nenue@6 683
Nenue@6 684
Nenue@6 685
Nenue@5 686 kb.ApplyAllBindings()
Nenue@5 687
Nenue@5 688 kb.ui(true)
Nenue@0 689 end
Nenue@0 690
Nenue@1 691
Nenue@5 692 kb.wrap = function(module)
Nenue@5 693 kb.modules = kb.modules or {}
Nenue@5 694 tinsert(kb.modules, module)
Nenue@0 695 end
Nenue@0 696
Nenue@5 697 -- Volatiles Access
Nenue@5 698 kb.GetBindings = function() return bindings end
Nenue@5 699 kb.GetButtons = function() return buttons end
Nenue@6 700 kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
Nenue@6 701 kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
Nenue@5 702 kb.GetLooseTalents = function() return talentBindings end
Nenue@5 703 kb.GetReverts = function() return reverts end
Nenue@6 704 kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
Nenue@0 705
Nenue@0 706
Nenue@5 707 SLASH_SKB1 = "/skb"
Nenue@5 708 SLASH_SKB2 = "/skeletonkey"
Nenue@5 709 SlashCmdList.SKB = kb.Command
Nenue@0 710
Nenue@5 711 -- This is needed to identify a spells that aren't reflected by GetCursorInfo()
Nenue@5 712 hooksecurefunc("PickupSpellBookItem", function(slot, bookType)
Nenue@5 713 print('|cFFFF4400PickupSpellBookItem(..', tostring(slot),', '..tostring(bookType)..')')
Nenue@5 714 CURSOR_SPELLSLOT = slot
Nenue@5 715 CURSOR_BOOKTYPE = bookType
Nenue@5 716 end)
Nenue@0 717
Nenue@5 718 -- Pet actions
Nenue@5 719 local isPickup
Nenue@5 720 hooksecurefunc("PickupPetAction", function(slot, ...)
Nenue@5 721 isPickup = GetCursorInfo()
Nenue@0 722
Nenue@5 723 CURSOR_PETACTION = isPickup and slot
Nenue@5 724 print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
Nenue@5 725 end)