annotate SkeletonKey/KeyBinds.lua @ 13:eeec4a600064

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