annotate SkeletonKey/KeyBinds.lua @ 10:e7977b336bf7

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