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) |