Mercurial > wow > skeletonkey
comparison SkeletonKey/KeyBinds.lua @ 5:9ac29fe77455
- dynamic profession spell mapping
- dynamic talent spell mapping
- protection of dynamic slots that aren't in use
- plugin abstractors for accessing state data
- a lot of fixes related to the 7.0.3 API
author | Nenue |
---|---|
date | Tue, 26 Jul 2016 19:29:44 -0400 |
parents | 07293831dd7b |
children | f6d1c192afc6 |
comparison
equal
deleted
inserted
replaced
4:a30285f8191e | 5:9ac29fe77455 |
---|---|
1 -------------------------------------------- | 1 -------------------------------------------- |
2 -- KrakTool | 2 -- SkeletonKey |
3 -- Nick | 3 -- Krakyn-Mal'Ganis |
4 -- @project-revision@ @project-hash@ | 4 -- @project-revision@ @project-hash@ |
5 -- @file-revision@ @file-hash@ | 5 -- @file-revision@ @file-hash@ |
6 -- Created: 6/16/2016 3:47 AM | 6 -- Created: 6/16/2016 3:47 AM |
7 -------------------------------------------- | 7 -------------------------------------------- |
8 -- kb | 8 -- kb |
9 -- .bind(button, key) bind current keystroke to command | 9 -- .StoreBinding(button, key) bind current keystroke to command |
10 -- .assign(button, command, name, icon) set button command | 10 -- .GetSlot(index) return display slot |
11 -- .release(button) clear button command | 11 -- .SetSlot(button, command, name, icon) assign display slot |
12 -- .refresh(button) update button contents | 12 -- .ReleaseSlot(button) clear button command |
13 -- .ui() invoke interface | 13 -- .UpdateSlot(button) update button contents |
14 -- .profile(name) set profile character | 14 -- .SelectProfile(name) set profile character |
15 -- .loadbinds(bindings) walk table with SetBinding() | 15 -- .ApplyBindings(bindings) walk table with SetBinding() |
16 | 16 |
17 local KT = LibKT.register(KeyBinder) | 17 local _ |
18 local kb = KeyBinder | 18 local kb, print = LibStub("LibKraken").register(KeyBinder) |
19 local db | 19 local db |
20 | 20 local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg', ...) end or function() end |
21 | |
22 --- Caps Lock literals | |
23 local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:" | |
24 local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).' | |
25 local BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.' | |
26 local BINDING_FAILED_PROTECTED = '|cFFFF4400Unable to use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400)|r' | |
27 local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES" | |
28 local FOOTER_OFFSET | |
29 local HEADER_OFFSET | |
30 local HELP_1 = "Drag and drop spells/items from your inventory, spellbook, or collections panels." | |
31 local HELP_2 = "While the cursor is above an icon, up to two key combinations will be bound to that action." | |
32 local HELP_3 = "If that key used for a client binding (e.g. game menu), a confirmation popup will appear before making the change." | |
33 local BINDS_PER_ROW = 2 | |
34 local BUTTON_HSPACING = 128 | |
35 local BUTTON_SPACING = 4 | |
36 local BUTTON_PADDING = 12 | |
37 local BINDING_TYPE_SPECIALIZATION = 3 | |
38 local BINDING_TYPE_CHARACTER = 2 | |
39 local BINDING_TYPE_GLOBAL = 1 | |
40 local KEY_BUTTON_SIZE = 48 | |
21 local MIN_BIND_SLOTS = 32 | 41 local MIN_BIND_SLOTS = 32 |
22 local BINDS_PER_ROW = 8 | 42 local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544 |
23 local KEY_BUTTON_SIZE = 40 | |
24 local TAB_OFFSET = 12 | 43 local TAB_OFFSET = 12 |
25 local TAB_HEIGHT = 40 | 44 local TAB_HEIGHT = 40 |
26 local TAB_SPACING = 2 | 45 local TAB_SPACING = 2 |
27 local BUTTON_SPACING = 4 | 46 local BORDER_UNASSIGNED = {0.2,0.2,0.2,1 } |
28 local BUTTON_PADDING = 12 | 47 local BORDER_ASSIGNED = {0.5,0.5,0.5,1 } |
29 local HEADER_OFFSET, FOOTER_OFFSET | 48 local BORDER_DYNAMIC = {1,1,0,1} |
30 local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544; | 49 local BORDER_PENDING = {1,0.5,0,1 } |
31 local BINDING_TYPE_SPECIALIZATION = 3 | 50 local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION |
32 local BINDING_TYPE_CHARACTER = 2 | 51 |
33 local BINDING_TYPE_GLOBAL = 1 | 52 |
34 local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).' | 53 --- Caps Lock derivatives |
35 local BINDING_FAILED_PROTECTED = '|cFF00FF00%s|r used by |cFFFFFF00%s|r!' | 54 local ACTION_SCRIPT = { |
55 ['mount'] = "/script C_MountJournal.SummonByID(%d)", | |
56 ['macro'] = "%s", | |
57 ['equipset'] = "/script UseEquipmentSet(%d)", | |
58 ['spell'] = "/cast %s", | |
59 ['petaction'] = "/cast %s", | |
60 ['battlepet'] = SLASH_SUMMON_BATTLE_PET1 .. " %s", | |
61 ['item'] = "/use %s" | |
62 } | |
63 local BUTTON_HEADERS = { | |
64 ['spell'] = SPELLS, | |
65 ['macro'] = MACRO, | |
66 ['petaction'] = PET, | |
67 ['mount'] = MOUNT, | |
68 ['battlepet'] = BATTLEPET, | |
69 | |
70 | |
71 [5] = PROFESSIONS_FIRST_AID, | |
72 [7] = PROFESSIONS_COOKING, | |
73 [9] = PROFESSIONS_FISHING, | |
74 [10] = PROFESSIONS_ARCHAEOLOGY, | |
75 | |
76 } | |
77 | |
78 local professionMappings = { | |
79 [5] = 3, | |
80 [7] = 4, | |
81 [9] = 5, | |
82 [10] = 6 | |
83 } | |
84 | |
36 local BINDING_MODE = { | 85 local BINDING_MODE = { |
37 [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s', | 86 [BINDING_TYPE_GLOBAL] = 'Global Binds', |
38 [BINDING_TYPE_CHARACTER] = 'Character: %s', | 87 [BINDING_TYPE_CHARACTER] = 'Character: %s', |
39 [BINDING_TYPE_GLOBAL] = 'Global Binds' | 88 [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s' |
40 } | 89 } |
90 local BINDING_DESCRIPTION = { | |
91 | |
92 [BINDING_TYPE_GLOBAL] = 'The bindings are applied globally.', | |
93 [BINDING_TYPE_CHARACTER] = 'Applied when you log onto this character.', | |
94 [BINDING_TYPE_SPECIALIZATION] = 'Applied when you log onto this character and are that specialization.', | |
95 } | |
96 | |
41 local BINDING_SCHEME_COLOR = { | 97 local BINDING_SCHEME_COLOR = { |
42 [BINDING_TYPE_SPECIALIZATION] = {0,0,0,0.5}, | 98 [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5}, |
43 [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5}, | 99 [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5}, |
44 [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5} | 100 [BINDING_TYPE_SPECIALIZATION] = {.25,0,0,0.5}, |
45 } | 101 } |
46 local BINDING_SCHEME_VERTEX = { | 102 local BINDING_SCHEME_VERTEX = { |
47 | 103 [BINDING_TYPE_GLOBAL] = {0,.5,1,1}, |
104 [BINDING_TYPE_CHARACTER] = {0,1,0,1}, | |
48 [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1}, | 105 [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1}, |
49 [BINDING_TYPE_CHARACTER] = {0,1,0,1}, | |
50 [BINDING_TYPE_GLOBAL] = {0,.5,1,1} | |
51 } | 106 } |
52 | |
53 local BINDING_SCHEME_TEXT = { | 107 local BINDING_SCHEME_TEXT = { |
54 [BINDING_TYPE_SPECIALIZATION] = {1, 1, 0}, | 108 [BINDING_TYPE_SPECIALIZATION] = {0, 1, 1}, |
55 [BINDING_TYPE_CHARACTER] = {0, 1, 0}, | 109 [BINDING_TYPE_CHARACTER] = {0, 1, 0}, |
56 [BINDING_TYPE_GLOBAL] = {0, 1, 1} | 110 [BINDING_TYPE_GLOBAL] = {0, 1, 1} |
57 } | 111 } |
58 local ACTION_SCRIPT = { | 112 |
59 ['mount'] = "/script C_MountJournal.SummonByID(%d)", | 113 |
60 ['equipset'] = "/script UseEquipmentSet(%d)", | 114 |
61 } | 115 local loadedProfiles = {} |
62 | 116 -- Profiles ordered by precedance |
63 local COMMAND_SPELL = "^SPELL (%S.+)" | |
64 local COMMAND_MACRO = "^MACRO (%S.+)" | |
65 local COMMAND_ITEM = "^ITEM (%S.+)" | |
66 local COMMAND_MOUNT = "^CLICK KeyBinderMacro:mount(%d+)" | |
67 local COMMAND_EQUIPSET = "^CLICK KeyBinderMacro:equipset(%d+)" | |
68 | |
69 local PICKUP_TYPES = { | |
70 [COMMAND_SPELL] = PickupSpell, | |
71 [COMMAND_MACRO] = PickupMacro, | |
72 [COMMAND_ITEM] = PickupItem, | |
73 [COMMAND_MOUNT] = C_MountJournal.Pickup, | |
74 [COMMAND_EQUIPSET] = PickupEquipmentSet | |
75 } | |
76 local PICKUP_VALUES = { | |
77 [COMMAND_SPELL] = function(name) return select(7, GetSpellInfo(name)) end | |
78 } | |
79 local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES" | |
80 local BORDER_UNASSIGNED = {0.2,0.2,0.2,1 } | |
81 local BORDER_ASSIGNED = {0.5,0.5,0.5,1 } | |
82 local BORDER_PENDING = {1,0.5,0,1 } | |
83 | |
84 local bindMode = 3 | |
85 local bindHeader = '' | |
86 local specHeader, specTexture, characterHeader = 'SPEC_NAME', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME' | |
87 local numButtons = BINDS_PER_ROW * 4 | |
88 local bindsCommitted = true | |
89 | |
90 local profile, character, specialization, global | |
91 local priority = {} | 117 local priority = {} |
118 -- Button pointers | |
92 local buttons = {} | 119 local buttons = {} |
120 -- Backlog of changes | |
93 local reverts = {} | 121 local reverts = {} |
94 local KeyButton = {} -- collection of KeyButton template handlers | 122 -- macro buttons used for mounts and other buttonable non-spells |
95 local Action = {} -- collection of special action buttons for special binds | 123 local macros = {} |
124 -- currently active non-blizzard keybinds | |
125 local bindings = {} | |
126 -- unselected talents | |
127 local talentBindings = {} | |
128 kb.inactiveTalentBindings = {} | |
129 -- placeholder for the StaticPopup used for confirmations | |
130 local confirmation | |
131 -- header text | |
132 local configHeaders = {} | |
133 | |
96 local protected = { | 134 local protected = { |
97 ['OPENCHATSLASH'] = true, | 135 ['OPENCHATSLASH'] = true, |
98 ['OPENCHAT'] = true, | 136 ['OPENCHAT'] = true, |
99 } | 137 } |
138 | |
139 --- Used to reflect the current working state | |
140 local bindMode = 3 | |
141 local bindHeader, currentHeader = '', '' | |
142 local configProfile, character, specialization, global, character_specialization | |
143 local specID, specGlobalID, specName, specDesc, specTexture, characterHeader = 0, 0, 'SPEC_NAME', 'SPEC_DESCRIPTION', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME' | |
144 local classHeader, className, classID = '', '', 0 | |
145 local numButtons = BINDS_PER_ROW * 8 | |
146 local bindsCommitted = true | |
147 local forceButtonUpdate = false | |
148 | |
149 --- Control handles | |
100 local saveButton, restoreButton, clearButton | 150 local saveButton, restoreButton, clearButton |
101 | 151 |
102 --- Returns a value for use with Texture:SetDesaturated() | 152 --- Cursor "pickup" actuators |
103 local CommandIsLocked = function(self) | 153 local PickupAction = {} |
104 print('command check: 1-'..(bindMode-1)) | 154 PickupAction.spell = _G.PickupSpell |
105 local desaturated, layer = false, 3 | 155 PickupAction.macro = _G.PickupMacro |
106 for i = 1, bindMode-1 do | 156 PickupAction.item = _G.PickupItem |
157 PickupAction.mount = _G.C_MountJournal.Pickup | |
158 local GetPickupValue = {} | |
159 GetPickupValue.spell = function(self) return select(7, GetSpellInfo(self.actionID)) end | |
160 | |
161 --- Returns conflicting assignment and binding profiles for use in displaying confirmations | |
162 local IsCommandBound = function(self, command) | |
163 local isAssigned, assignedBy = false, bindMode | |
164 local isBound, boundBy = false, bindMode | |
165 | |
166 | |
167 command = command or self.command | |
168 for i = 1, #BINDING_MODE do | |
107 local tier = priority[i] | 169 local tier = priority[i] |
108 local existing = tier.commands[self.command] | 170 if i ~= bindMode then |
109 print(' ', i, tier.commands[self.command]) | 171 |
110 if existing then | 172 if tier.commands[command] then |
111 if self:GetID() ~= existing then | 173 isAssigned = true |
112 -- sanitize bad data | 174 assignedBy = i |
113 tier.commands[self.command] = nil | 175 end |
114 else | 176 if tier.bound[command] then |
115 layer = i | 177 isBound = true |
116 desaturated = true | 178 boundBy = i |
179 end | |
180 | |
181 | |
182 --print(' *', configHeaders[i], tier.commands[command], tier.bound[command]) | |
183 | |
184 if isAssigned and isBound then | |
117 break | 185 break |
118 end | 186 end |
119 end | 187 end |
120 end | 188 |
121 return desaturated, layer | 189 end |
122 end | 190 |
191 print('|cFFFFFF00IsCommandBound:|r', command:gsub(CLICK_KEYBINDER_MACRO, ''),'|r [profile:', bindMode .. ']', isAssigned, isBound, assignedBy, boundBy) | |
192 return isAssigned, isBound, assignedBy, boundBy | |
193 end | |
194 | |
195 local talentSpellHardCodes = { | |
196 [109248] = 'Binding Shot', | |
197 } | |
123 | 198 |
124 --- Returns a value for use with Texture:SetDesaturated() | 199 --- Returns a value for use with Texture:SetDesaturated() |
125 local BindingIsLocked = function(key) | 200 local BindingIsLocked = function(key) |
126 local success = false | 201 local success = false |
127 for i = 1, bindMode-1 do | 202 for i = 1, bindMode-1 do |
137 --- Translates GetBindingKey() results into a printable string. | 212 --- Translates GetBindingKey() results into a printable string. |
138 local BindingString = function(...) | 213 local BindingString = function(...) |
139 local stack = {} | 214 local stack = {} |
140 for i = 1, select('#', ...) do | 215 for i = 1, select('#', ...) do |
141 local key = select(i, ...) | 216 local key = select(i, ...) |
142 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp') | 217 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ') |
143 end | 218 end |
144 | 219 |
145 if #stack >= 1 then | 220 if #stack >= 1 then |
146 return table.concat(stack, ',') | 221 return table.concat(stack, ',') |
147 else | 222 else |
148 return nil | 223 return nil |
149 end | 224 end |
150 end | 225 end |
151 | 226 |
152 --- This keeps our KeyDown handler from getting stuck with game controls | 227 local restingAlpha = 0.7 |
153 KeyButton.OnUpdate = function(self) | 228 local fadeTime, fadeDelay = .30, 0.15 |
229 | |
230 local portraitLayers = {} | |
231 kb.UpdatePortraits = function() | |
232 for i, layeredRegion in ipairs(portraitLayers) do | |
233 SetPortraitTexture(layeredRegion , 'player') | |
234 end | |
235 end | |
236 | |
237 | |
238 | |
239 local KeyButton_OnKeyDown = function(self, key) | |
240 kb.StoreBinding(self, key) | |
241 end | |
242 local KeyButton_OnClick = function(self, click) | |
243 print(self:GetName(), 'OnMouseDown', click) | |
244 if click == 'LeftButton' then | |
245 kb.DropToSlot(self) | |
246 elseif click == 'RightButton' then | |
247 kb.ReleaseSlot(self) | |
248 else | |
249 kb.StoreBinding(self, click:upper()) | |
250 end | |
251 end | |
252 | |
253 local KeyButton_OnDragStart = function(self) | |
254 kb.PickupSlot(self) | |
255 end | |
256 | |
257 local KeyButton_OnReceiveDrag = function(self, ...) | |
258 kb.DropToSlot(self) | |
259 end | |
260 | |
261 | |
262 local KeyBinder_OnUpdate = function(self, elapsed) | |
263 self.elapsed = self.elapsed + elapsed | |
264 self.throttle = self.throttle + elapsed | |
265 | |
266 if (self.throttle >= 0.032) then | |
267 self.throttle = 0 | |
268 else | |
269 return | |
270 end | |
271 | |
272 local progress = 1 | |
273 if self.elapsed > fadeTime then | |
274 self.elapsed = 0 | |
275 self.fadeStep = 0 | |
276 --self.statustext:SetText(nil) | |
277 --self.bindingstext:SetText(nil) | |
278 self:SetScript('OnUpdate', nil) | |
279 else | |
280 if self.elapsed < fadeDelay then | |
281 progress = 0 | |
282 else | |
283 self.fadeStep = self.fadeStep + 1 | |
284 progress = (self.elapsed - fadeDelay) /(fadeTime - fadeDelay) | |
285 end | |
286 --print(self.fadeStep, format('%.02f/%.02f', (self.elapsed - fadeDelay) ,(fadeTime - fadeDelay)) , progress) | |
287 end | |
288 | |
289 local alpha = 1 - progress * (1- restingAlpha) | |
290 self.statustext:SetAlpha(alpha) | |
291 self.bindingstext:SetAlpha(alpha) | |
292 end | |
293 | |
294 local KeyButton_OnUpdate = function(self) | |
154 if not self.command then | 295 if not self.command then |
155 return | 296 return |
156 end | 297 end |
157 | 298 |
158 if self:IsMouseOver() then | 299 if self:IsMouseOver() then |
300 kb.elapsed = 0 | |
159 if not self.active then | 301 if not self.active then |
160 -- only set this handler when the button is activated/mouseOver | 302 -- only set this handler when the button is activated/mouseOver |
161 self.active = true | 303 self.active = true |
162 self:SetScript('OnKeyDown', kb.bind) | 304 self:SetScript('OnKeyDown', KeyButton_OnKeyDown) |
163 | 305 |
164 local bindText = self.command | 306 kb.statustext:SetText(self.statusText .. ': '..self.actionName) |
165 if self.bind:GetText() then | 307 kb.bindingstext:SetText(self.bindingText) |
166 bindText = bindText .. ': |cFF00FF00' .. self.bind:GetText() | 308 kb.fadeStep = 0 |
167 end | 309 kb.throttle = 0 |
168 | 310 kb:SetScript('OnUpdate', KeyBinder_OnUpdate) |
169 kb.bindlist:SetText(bindText) | 311 |
170 GameTooltip:SetOwner(self) | |
171 GameTooltip:SetAnchorType('ANCHOR_BOTTOMRIGHT') | |
172 GameTooltip:SetText(self.actionName) | |
173 GameTooltip:Show() | |
174 end | 312 end |
175 else | 313 else |
176 if self.active then | 314 if self.active then |
177 GameTooltip:Hide() | |
178 self.active = nil | 315 self.active = nil |
179 self:SetScript('OnKeyDown', nil) | 316 self:SetScript('OnKeyDown', nil) |
180 end | 317 end |
181 end | 318 end |
182 end | 319 end |
183 | 320 |
184 --- Cursor pickup handler | 321 local KeyBinder_OnMouseWheel = function(self, delta) |
185 -- Walks through PICKUP_TYPES and runs the function if match(command, key) turns up a result. | 322 print(self, delta, self.scrollOffset, (self.scrollOffset <= 0)) |
186 -- Passes the result through PICKUP_VALUES[pattern]() if defined. | 323 |
187 | 324 |
188 kb.pickup = function(self) | 325 if IsControlKeyDown() then |
189 for pattern, pickup in pairs(PICKUP_TYPES) do | 326 KEY_BUTTON_SIZE = KEY_BUTTON_SIZE - delta |
190 local value = self.command:match(pattern) | 327 else |
191 if value then | 328 |
192 if PICKUP_VALUES[pattern] then | 329 |
193 value = PICKUP_VALUES[pattern](value) | 330 if (delta > 0) and (self.scrollOffset <= 0) then |
194 end | 331 return |
195 | 332 elseif delta < 0 and kb.scrollOffset >= 42 then |
196 pickup(value) | 333 return |
197 kb.release(self) | 334 end |
198 break | 335 kb.scrollOffset = ceil(kb.scrollOffset - (delta * BINDS_PER_ROW)) |
199 end | 336 end |
200 end | 337 |
201 end | 338 kb.ui(true) |
202 | 339 end |
203 --- Setup an action button base on template info | 340 |
204 kb.action = function(type, id) | 341 local KeyBinder_OnHide = function() |
205 | 342 KeyBinderImportLog:Hide() |
206 local macroName = type .. id | 343 end |
207 macroName = macroName:gsub(' ', '') | 344 |
208 | 345 local CloseButton_OnClick = function() |
209 local attribute = '*macrotext-'..macroName | 346 db.showUI = false |
210 local value = ACTION_SCRIPT[type]:format(id) | 347 kb:Hide() |
211 local command = 'CLICK KeyBinderMacro:'.. macroName | 348 end |
212 profile.macros[attribute] = {value, command} | 349 local CancelButton_OnClick = function() |
213 | 350 kb.RevertBindings() |
214 KeyBinderMacro:SetAttribute(attribute, value) | 351 end |
215 return command | 352 local SaveButton_OnClick = function() |
216 end | 353 kb.ConfirmBindings() |
217 | 354 end |
218 KeyButton.OnDragStart = function(self) | 355 |
356 local KeyBinder_Initialize = function() | |
357 for i = 1, GetNumBindings() do | |
358 local command = GetBinding(i) | |
359 bindings[command] = true | |
360 end | |
361 | |
362 kb.scrollOffset = 0 | |
363 kb.tabAnchor = {'TOPLEFT', kb.profilebg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING} | |
364 kb.tabGrowth = {'TOPLEFT', nil,'TOPRIGHT', BUTTON_SPACING, 0} | |
365 kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT } | |
366 kb.UIPanelAnchor = {'TOPLEFT', kb.sourcesbg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING} | |
367 kb.UIPanelGrowth = {'TOPLEFT', nil, 'BOTTOMLEFT', 0, -2 } | |
368 kb.UIPanelSize = {84, 32 } | |
369 kb.UIPanelIcon = {24, 32, 'LEFT', -12, 0} | |
370 kb.controlsAnchor = {'BOTTOMLEFT', kb.footer, BUTTON_PADDING, BUTTON_PADDING } | |
371 kb.controlsGrowth = {'BOTTOMLEFT', nil, 'BOTTOMRIGHT', BUTTON_SPACING, 0} | |
372 | |
373 -- order of these is important | |
374 kb:tab('KeyBinderGlobalTab', | |
375 BINDING_MODE[BINDING_TYPE_GLOBAL] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_GLOBAL], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85}) | |
376 kb:tab('KeyBinderCharacterTab', | |
377 configHeaders[BINDING_TYPE_CHARACTER] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_CHARACTER], nil) | |
378 kb:tab('KeyBinderSpecTab', | |
379 configHeaders[BINDING_TYPE_SPECIALIZATION] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_SPECIALIZATION], specTexture) | |
380 KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85) | |
381 | |
382 | |
383 | |
384 portraitLayers[1] = KeyBinderCharacterTab.icon | |
385 | |
386 saveButton = kb:button('KeyBinderSaveButton', 'Save', 'Commit all changes.', SaveButton_OnClick) | |
387 --restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', CancelButton_OnClick) | |
388 --clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', ResetButton_OnClick) | |
389 | |
390 kb:uibutton( | |
391 'KeyBinderSpellBookButton', 'SpellBook', nil, | |
392 function() ToggleSpellBook(BOOKTYPE_SPELL) end, | |
393 "Interface\\BUTTONS\\UI-MicroButton-Spellbook-Up", {0, 1, .4, 1}) | |
394 kb:uibutton( | |
395 'KeyBinderTalentFrameButton', TALENTS, SPECIALIZATION, | |
396 function() ToggleTalentFrame() end, | |
397 "Interface\\BUTTONS\\UI-MicroButton-Talents-Up", {0, 1, .4, 1}) | |
398 | |
399 kb:uibutton( | |
400 'KeyBinderMacroFrameButton', 'Macros', nil, | |
401 function() if MacroFrame and MacroFrame:IsVisible() then | |
402 HideUIPanel(MacroFrame) | |
403 else | |
404 ShowMacroFrame() end | |
405 end, | |
406 "Interface\\BUTTONS\\UI-MicroButton-Help-Up", {0, 1, .4, 1}) | |
407 | |
408 kb:uibutton( | |
409 'KeyBinderInventoryButton', 'Bags', nil, | |
410 function() OpenAllBags() end, | |
411 "Interface\\BUTTONS\\UI-MicroButtonCharacter-Up", {0, 1, .4, 1}) | |
412 | |
413 | |
414 | |
415 kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING) | |
416 HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING | |
417 + kb.info:GetHeight() | |
418 FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING | |
419 | |
420 kb:SetScript('OnHide', KeyBinder_OnHide) | |
421 kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) | |
422 kb.CloseButton:SetScript('OnClick', CloseButton_OnClick) | |
423 | |
424 kb.UpdatePortraits() | |
425 end | |
426 | |
427 kb.DropToSlot = function(self) | |
428 | |
429 print(self:GetName(),'|cFF0088FFreceived|r') | |
430 local actionType, actionID, subType, subData = GetCursorInfo() | |
431 print('GetCursorInfo', GetCursorInfo()) | |
432 | |
433 | |
434 if actionType then | |
435 | |
436 if actionType == 'flyout' then | |
437 ClearCursor() | |
438 ResetCursor() | |
439 return | |
440 end | |
441 | |
442 | |
443 local macroName, macroText | |
444 local command, name, icon, _ | |
445 local pickupID, pickupBook | |
446 | |
447 if actionType == 'spell' then | |
448 actionID = subData | |
449 name, _, icon = GetSpellInfo(actionID) | |
450 | |
451 elseif actionType == 'macro' then | |
452 name, icon = GetMacroInfo(actionID) | |
453 actionID = name | |
454 elseif actionType == 'petaction' then | |
455 if not (CURSOR_SPELLSLOT and CURSOR_BOOKTYPE) then | |
456 | |
457 ClearCursor() | |
458 ResetCursor() | |
459 end | |
460 | |
461 local bookType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE) | |
462 pickupID = CURSOR_SPELLSLOT | |
463 pickupBook = CURSOR_BOOKTYPE | |
464 name, _, icon = GetSpellInfo(spellID) | |
465 actionID = name | |
466 | |
467 elseif actionType == 'mount' then | |
468 if subType == 0 then | |
469 name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL) | |
470 actionID = 0 | |
471 else | |
472 name, _, icon = C_MountJournal.GetMountInfoByID(actionID) | |
473 end | |
474 elseif actionType == 'item' then | |
475 name = GetItemInfo(actionID) | |
476 icon = GetItemIcon(actionID) | |
477 actionID = name | |
478 elseif actionType == 'battlepet' then | |
479 | |
480 local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = C_PetJournal.GetPetInfoByPetID(detail); | |
481 name = customName or petName | |
482 icon = petIcon | |
483 | |
484 end | |
485 macroName, macroText, command = kb.RegisterAction(actionType, actionID) | |
486 | |
487 | |
488 local isAssigned, isBound, assignedBy, boundBy = IsCommandBound(self, command) | |
489 if isAssigned then | |
490 local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] | |
491 popup.slot = self | |
492 popup.text = "Currently assigned in |cFFFFFF00"..tostring(configHeaders[assignedBy]).."|r. Are you sure?" | |
493 popup.oldProfile = assignedBy | |
494 popup.args = {command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook } | |
495 kb:SetScript('OnMouseWheel', nil) -- disable scrolling | |
496 StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT') | |
497 else | |
498 kb.SetSlot(self, command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook) | |
499 kb.UpdateSlot(self) | |
500 self.active = nil | |
501 KeyButton_OnUpdate(self, 0) | |
502 ClearCursor() | |
503 ResetCursor() | |
504 end | |
505 end | |
506 end | |
507 | |
508 kb.PickupSlot = function(self) | |
219 if not self.command then | 509 if not self.command then |
220 return | 510 return |
221 end | 511 end |
222 kb.pickup(self) | 512 print(self.actionType) |
223 end | 513 if self.actionType == 'spell' then |
224 | 514 -- It can't be picked up if SpellInfo(name) returns void |
225 KeyButton.OnReceiveDrag = function(self, ...) | 515 local dummy = GetSpellInfo(self.actionName) |
226 print(self:GetName(),'|cFF0088FFreceived|r', ...) | 516 if not dummy then |
227 local type, value, subType, subData = GetCursorInfo() | 517 return |
228 print('GetCursorInfo', type, value, subType, subData) | 518 end |
229 if type then | 519 elseif self.actionType == 'petaction' then |
230 if type == 'spell' then | 520 PickupSpellBookItem(self.pickupSlot, self.pickupBook) |
231 value = subData | 521 end |
232 end | 522 if PickupAction[self.actionType] then |
233 | 523 if GetPickupValue[self.actionType] then |
234 local command, name, icon, _ | 524 PickupAction[self.actionType](GetPickupValue[self.actionType](self)) |
235 if type == 'spell' then | 525 else |
236 name, _, icon = GetSpellInfo(value) | 526 PickupAction[self.actionType](self.actionID) |
237 command = 'SPELL ' .. name | 527 end |
238 name = '' | 528 kb.ReleaseSlot(self) |
239 elseif type == 'macro' then | 529 kb.UpdateSlot(self) |
240 name, icon = GetMacroInfo(value) | 530 end |
241 command = 'MACRO ' .. name | 531 end |
242 elseif type == 'mount' then | 532 |
243 if subType == 0 then | 533 |
244 name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL) | 534 --- Resolve the appropriate command and assign the corresponding secure state driver |
245 value= 0 | 535 kb.RegisterAction = function(type, id) |
246 else | 536 |
247 name, _, icon = C_MountJournal.GetMountInfoByID(value) | 537 if type == 'spell' then |
248 end | 538 |
249 command = kb.action(type, value) | 539 id = GetSpellInfo(id) |
250 elseif type == 'item' then | 540 end |
251 name = GetItemInfo(value) | 541 |
252 icon = GetItemIcon(value) | 542 local macroText |
253 command = 'ITEM ' .. name | 543 local macroName = type ..'_' .. id |
254 end | 544 |
255 kb.assign(self, command, name, icon) | 545 if kb.ProfessionCache[id] then |
256 kb.refresh(self) | 546 macroName = "profession_".. kb.ProfessionCache[id].profOffset .. '_' .. kb.ProfessionCache[id].spellNum |
257 ClearCursor() | 547 macroText = "/cast " .. kb.ProfessionCache[id].spellName |
258 end | 548 macros[macroName] = nil |
259 end | |
260 | |
261 KeyButton.OnMouseDown = function(self, click) | |
262 print(self:GetName(), 'OnMouseDown', click) | |
263 if click == 'LeftButton' then | |
264 KeyButton.OnReceiveDrag(self) | |
265 elseif click == 'RightButton' then | |
266 kb.release(self) | |
267 else | 549 else |
268 kb.bind(self) | 550 macroName = macroName:gsub(' ', '') |
269 end | 551 macroText = ACTION_SCRIPT[type]:format(id) |
552 end | |
553 | |
554 local baseName, iterative = macroName, 1 | |
555 while (macros[macroName] and macros[macroName][1] ~= macroText) do | |
556 print(' * cannot use|cFF00FF00', macroName, '|r"'.. (macros[macroName][1] or '') .. '"') | |
557 macroName = baseName .. '_' .. iterative | |
558 iterative = iterative + 1 | |
559 end | |
560 if macroName ~= baseName then | |
561 print(' * Creating|cFF00FF00', macroName) | |
562 else | |
563 print(' * Re-using|cFF00FF00', macroName) | |
564 end | |
565 | |
566 local command = 'CLICK KeyBinderMacro:'.. macroName | |
567 macros[macroName] = {macroText, command } | |
568 print('RegisterAction', command , macroText) | |
569 if type == 'macro' then | |
570 kb.LoadMacro(macroName) | |
571 else | |
572 kb.LoadAction(macroName, macroText, command) | |
573 end | |
574 | |
575 | |
576 return macroName, macroText, command | |
577 end | |
578 | |
579 kb.LoadMacro = function(macroName) | |
580 KeyBinderMacro:SetAttribute('*macro-'..macroName, macros[macroName][1]) | |
581 return true | |
582 end | |
583 | |
584 kb.LoadAction = function(macroName) | |
585 if not macros[macroName] then | |
586 return false | |
587 end | |
588 KeyBinderMacro:SetAttribute('*macrotext-'..macroName, macros[macroName][1]) | |
589 return true | |
590 end | |
591 | |
592 local profressionsCache | |
593 | |
594 kb.AcceptAssignment = function(self, ...) | |
595 local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] | |
596 local source = loadedProfiles[popup.oldProfile] | |
597 kb.SetSlot(popup.slot, unpack(popup.args)) | |
598 kb.UpdateSlot(popup.slot) | |
599 kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) -- re-enable scrolling | |
600 ClearCursor() | |
601 ResetCursor() | |
270 end | 602 end |
271 | 603 |
272 | 604 |
273 --- Updates the current KeyBinding for the button's command | 605 --- Updates the current KeyBinding for the button's command |
274 kb.bind = function(self, key) | 606 kb.StoreBinding = function(self, key) |
275 | 607 |
276 print('|cFFFFFF00bind|cFFFFFF00', self:GetID(), '|cFF00FFFF', key) | |
277 if not self.command then | 608 if not self.command then |
278 return | 609 return |
279 end | 610 end |
280 | 611 |
281 if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then | 612 if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then |
282 return | 613 return |
283 end | 614 end |
284 | 615 print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key) |
285 if protected[GetBindingAction(key)] then | |
286 return | |
287 kb.bindlist:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(key))) | |
288 end | |
289 | 616 |
290 if key == 'ESCAPE' then | 617 if key == 'ESCAPE' then |
291 local key1, key2 = GetBindingKey(self.command) | 618 local keys = {GetBindingKey(self.command) } |
292 if key1 then | 619 --print('detected', #keys, 'bindings') |
293 SetBinding(key1, nil) | 620 for i, key in pairs(keys) do |
294 print('Unbound', key1) | 621 --print('clearing', key) |
295 end | 622 SetBinding(key, nil) |
296 if key2 then | 623 SaveBindings(GetCurrentBindingSet()) |
297 SetBinding(key2, nil) | 624 if configProfile.bindings[key] then |
298 print('Unbound', key2) | 625 kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[bindMode])) |
299 end | 626 configProfile.bindings[key] = nil |
627 end | |
628 if configProfile.talents[self.actionName] then | |
629 configProfile.talents[self.actionName] = nil | |
630 end | |
631 bindings[self.actionType][self.actionID] = nil | |
632 end | |
633 if configProfile.bound[self.command] then | |
634 configProfile.bound[self.command] = nil | |
635 --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[bindMode])) | |
636 end | |
637 | |
638 bindsCommitted = false | |
300 self.active = false | 639 self.active = false |
301 return | 640 else |
302 end | 641 |
303 | 642 local modifier = '' |
304 local modifier = '' | 643 if IsAltKeyDown() then |
305 if IsAltKeyDown() then | 644 modifier = 'ALT-' |
306 modifier = 'ALT-' | 645 end |
307 end | 646 if IsControlKeyDown() then |
308 if IsControlKeyDown() then | 647 modifier = modifier.. 'CTRL-' |
309 modifier = modifier.. 'CTRL-' | 648 end |
310 end | 649 if IsShiftKeyDown() then |
311 if IsShiftKeyDown() then | 650 modifier = modifier..'SHIFT-' |
312 modifier = modifier..'SHIFT-' | 651 end |
313 end | 652 |
314 | 653 |
654 if self.command then | |
655 self.binding = modifier..key | |
656 | |
657 local previousKeys | |
658 local previousAction = GetBindingAction(self.binding) | |
659 local binding1, binding2, new1, new2 | |
660 print(type(previousAction), previousAction) | |
661 if previousAction ~= "" and previousAction ~= self.command then | |
662 if protected[previousAction] then | |
663 -- bounce out if trying to use a protected key | |
664 kb.statustext:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(previousAction))) | |
665 kb.bindingstext:SetText(nil) | |
666 return | |
667 else | |
668 kb:print('Discarding keybind for', previousAction) | |
669 -- todo: sort out retcon'd talent spells | |
670 end | |
671 end | |
672 | |
673 self.pending = true | |
674 | |
675 bindsCommitted = false | |
676 SetBinding(self.binding, self.command) | |
677 SaveBindings(GetCurrentBindingSet()) | |
678 | |
679 local talentInfo | |
680 if self.actionType == 'spell' and kb.TalentCache[self.actionID] then | |
681 print('conditional binding (talent = "'..self.actionName..'")') | |
682 talentInfo = {self.macroName, self.actionName, self.actionType, self.actionID} | |
683 local bindings = {GetBindingKey(self.command) } | |
684 for i, key in ipairs(bindings) do | |
685 tinsert(talentInfo, key) | |
686 end | |
687 end | |
688 | |
689 for level, configProfile in ipairs(priority) do | |
690 if (level == bindMode) then | |
691 configProfile.bound[self.command] = true | |
692 if talentInfo then | |
693 configProfile.bindings[self.binding] = nil | |
694 else | |
695 configProfile.bindings[self.binding] = self.command | |
696 end | |
697 configProfile.talents[self.actionName] = talentInfo | |
698 else | |
699 configProfile.bindings[self.binding] = nil | |
700 configProfile.bound[self.command] = nil | |
701 configProfile.talents[self.actionName] = nil | |
702 end | |
703 if configProfile.talents[self.actionID] then | |
704 configProfile.talents[self.actionID] = nil | |
705 end | |
706 | |
707 end | |
708 | |
709 | |
710 | |
711 kb:print(BINDING_ASSIGNED:format(self.binding, self.actionName, configHeaders[bindMode])) | |
712 | |
713 end | |
714 end | |
715 | |
716 kb.UpdateSlot(self, true) | |
717 KeyBinderSaveButton:Enable() | |
718 | |
719 end | |
720 | |
721 --- Resets button command | |
722 kb.ReleaseSlot = function(self) | |
723 local slot = self:GetID() | |
724 | |
725 | |
726 if configProfile.buttons[slot] then | |
727 configProfile.buttons[slot] = nil | |
728 end | |
315 if self.command then | 729 if self.command then |
316 self.binding = modifier..key | 730 configProfile.commands[self.command] = nil |
317 self.pending = true | 731 end |
318 self.border:SetColorTexture(1,.5,0, 1) | 732 if self.actionType == 'spell' and IsTalentSpell(self.actionName) then |
319 | 733 if configProfile.talents[self.actionID] then |
320 local old = GetBindingAction(self.binding) | 734 configProfile.talents[self.actionID] = nil |
321 local binding1, binding2, new1, new2 | 735 end |
322 if old and old ~= self.command then | 736 end |
323 print('Discarding keybind for', old) | 737 local droppedKeys = {} |
324 local binding1, binding2 = GetBindingKey(old) | 738 |
325 -- need to preserve argument order | 739 -- doing removal in second loop to avoid possible iterator shenanigans |
326 end | 740 for k,v in pairs(configProfile.bindings) do |
327 tinsert(reverts, {old, binding1, binding2, new1, new2}) | 741 if v == self.command then |
328 | 742 tinsert(droppedKeys, k) |
329 | 743 end |
330 bindsCommitted = false | 744 end |
331 SetBinding(self.binding, self.command) | 745 if #droppedKeys >=1 then |
332 for level, profile in ipairs(priority) do | 746 for i, k in ipairs(droppedKeys) do |
333 profile.bindings[self.binding] = (level == bindMode) and self.command or nil | 747 configProfile.bindings[k] = nil |
334 end | 748 end |
335 print(BINDING_ASSIGNED:format(self.binding, self.command, BINDING_MODE[bindMode]:format(bindHeader))) | 749 end |
336 | 750 |
337 kb.refresh(self) | 751 self.isAvailable = nil |
338 end | 752 self.isDynamic = nil |
339 end | 753 self.bindingText = nil |
340 | 754 self.statusText = nil |
341 --- Resets button command | |
342 kb.release = function(self) | |
343 local index = self:GetID() | |
344 self.command = nil | 755 self.command = nil |
756 self.actionType = nil | |
757 self.actionID = nil | |
345 self.actionName = nil | 758 self.actionName = nil |
346 self.macro:SetText(nil) | 759 self.pickupSlot = nil |
760 self.pickupBook = nil | |
761 self.macroName = nil | |
347 self.profile = nil | 762 self.profile = nil |
348 self.bind:SetText(nil) | |
349 self.icon:SetTexture(nil) | 763 self.icon:SetTexture(nil) |
350 self.border:SetColorTexture(unpack(BORDER_UNASSIGNED)) | 764 self.border:SetColorTexture(unpack(BORDER_UNASSIGNED)) |
351 self:EnableKeyboard(false) | 765 self:EnableKeyboard(false) |
352 self:SetScript('OnKeyDown', nil) | 766 self:SetScript('OnKeyDown', nil) |
353 | 767 end |
354 | 768 |
355 if profile.buttons[index] then | 769 kb.SetSlot = function(self, command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook) |
356 profile.buttons[index] = nil | 770 local slot = self:GetID() |
357 end | 771 local isDynamic, isAvailable |
358 end | 772 |
359 | 773 print('|cFFFFFF00SetSlot|r:', self:GetID()) |
360 -- Sets button command | |
361 | |
362 kb.assign = function(self, command, name, icon) | |
363 local index = self:GetID() | |
364 print('|cFF00FFFFassign|cFF0088FF', index, '|cFFFFFF00'.. (command or 'none'), '|cFF00FF00'.. (name or ''), '|cFF00FFFF' .. (icon or '')) | |
365 | |
366 | |
367 if command then | 774 if command then |
368 if command:match(COMMAND_SPELL) then | 775 |
369 name = command:match(COMMAND_SPELL) | 776 if actionType == 'spell' then |
370 end | 777 local professionNum, spellNum = command:match("profession_(%d)_(%d)") |
371 | 778 |
372 | 779 if (professionNum and spellNum) then |
780 isDynamic = 'profession' | |
781 local cacheInfo = kb.ProfessionCache[professionNum..'_'..spellNum] | |
782 if cacheInfo then | |
783 isAvailable = true | |
784 name = cacheInfo.spellName | |
785 icon = cacheInfo.icon | |
786 actionID = cacheInfo.spellID | |
787 self.profIndex = cacheInfo.profIndex | |
788 self.spellOffset = cacheInfo.spellOffset | |
789 end | |
790 print(' Special slot: |cFF00FFFFProfession|r', professionNum, spellNum, isDynamic, isAvailable) | |
791 | |
792 self.professionNum = tonumber(professionNum) | |
793 self.spellNum = tonumber(spellNum) | |
794 | |
795 elseif kb.TalentCache[actionID] then | |
796 | |
797 isDynamic = 'talent' | |
798 isAvailable = GetSpellInfo(name) | |
799 print(' Special slot: |cFFBBFF00talent|r', name, isAvailable) | |
800 end | |
801 if not actionID then | |
802 actionID = select(7, GetSpellInfo(name)) | |
803 end | |
804 elseif actionType == 'macro' then | |
805 if not actionID then | |
806 actionID = GetMacroIndexByName(name) | |
807 end | |
808 else | |
809 --- Journal selections | |
810 -- todo: consider using the deep end of blizzard action bar instead | |
811 if not actionID then | |
812 actionID = command:match("^KeyBinderMacro:(.+)") | |
813 end | |
814 end | |
815 | |
816 if not macroName then | |
817 local previousCommand = command | |
818 macroName, macroText, command = kb.RegisterAction(actionType, actionID) | |
819 | |
820 -- Clean up conflicting command entry for loaded buttons | |
821 if macroName and command ~= previousCommand then | |
822 print(' Repaired corruption in |cFFFFFF00'..currentHeader..'|r button #'.. self:GetID()) | |
823 configProfile.commands[previousCommand] = nil | |
824 configProfile.bound[previousCommand] = nil | |
825 end | |
826 end | |
827 | |
828 if actionType == 'petaction' then | |
829 self.pickupSlot = pickupSlot | |
830 self.pickupBook = pickupBook | |
831 else | |
832 self.pickupSlot = nil | |
833 self.pickupBook = nil | |
834 end | |
835 | |
836 actionID = actionID or 0 | |
373 self:EnableKeyboard(true) | 837 self:EnableKeyboard(true) |
374 print('profile.buttons['..index..'] |cFF00FFFF=|r ', command, name, icon) | 838 print(' |cFF00FF00configProfile.buttons['..slot..'] |cFF00FFFF=|r |cFF00FFFF"'.. command.. '"|r |cFF00FF00"'.. name, '"|r |cFFFFFF00icon:'.. icon .. '|r |cFFFF8800"'.. actionType, '"|r |cFFFF0088id:'.. actionID ..'|r |cFF00FF00"'.. macroName .. '"|r') |
375 profile.buttons[index] = {command, name, icon} | 839 configProfile.buttons[slot] = {command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook} |
376 | 840 |
377 --- Clean up any residual buttons | 841 -- Clean up conflicting entries for loaded button |
378 local previous = profile.commands[command] | 842 local previous = configProfile.commands[command] |
379 if previous ~= index and buttons[previous] then | 843 if previous ~= slot and buttons[previous] then |
380 kb.release(buttons[previous]) | 844 kb.ReleaseSlot(buttons[previous]) |
381 end | 845 end |
382 | 846 configProfile.commands[command] = slot |
383 profile.commands[command] = index | 847 end |
384 end | 848 |
385 | 849 self.isAvailable = isAvailable |
386 self.profile = bindMode | 850 self.isDynamic = isDynamic |
851 | |
852 self.macroText = macroText | |
853 self.macroName = macroName | |
854 self.actionType = actionType | |
855 self.actionID = actionID | |
387 self.actionName = name | 856 self.actionName = name |
388 self.command = command | 857 self.command = command |
389 self.icon:SetTexture(icon) | 858 self.icon:SetTexture(icon) |
859 self.profile = bindMode | |
390 self:RegisterForDrag('LeftButton') | 860 self:RegisterForDrag('LeftButton') |
391 end | 861 end |
392 | 862 |
393 --- Retrieves button at index; creates said button and instates any stored parameters | 863 --- Retrieves button at index; creates said button and instates any stored parameters |
394 kb.keyslot = function(index) | 864 local leftSlot, upSlot |
865 local buttonsDepth = 0 | |
866 kb.GetSlot = function(index) | |
867 | |
868 local slot = index + kb.scrollOffset | |
869 | |
395 if not buttons[index] then | 870 if not buttons[index] then |
396 local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, kb, 'KeyButton') | 871 local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, kb, 'KeyButton') |
397 button:SetScript('OnMouseDown', KeyButton.OnMouseDown) | 872 button:SetScript('OnClick', KeyButton_OnClick) |
398 button:SetScript('OnMouseUp', KeyButton.OnMouseUp) | 873 button:SetScript('OnUpdate', KeyButton_OnUpdate) |
399 button:SetScript('OnUpdate', KeyButton.OnUpdate) | 874 button:SetScript('OnDragStart', KeyButton_OnDragStart) |
400 button:SetScript('OnDragStart', KeyButton.OnDragStart) | 875 button:SetScript('OnReceiveDrag', KeyButton_OnReceiveDrag) |
401 button:SetScript('OnReceiveDrag', KeyButton.OnReceiveDrag) | 876 button:RegisterForClicks('AnyUp') |
402 button:SetID(index) | 877 |
403 | 878 |
404 if profile.buttons[index] and type(profile.buttons[index] ) == 'table' then | 879 local newRow = (mod(index, BINDS_PER_ROW) == 1) |
405 kb.assign(button, unpack(profile.buttons[index] )) | 880 |
881 if index == 1 then | |
882 button:SetPoint('TOPLEFT', kb.bg, 'TOPLEFT', BUTTON_PADDING, - BUTTON_PADDING) | |
883 upSlot = button | |
884 buttonsDepth = KEY_BUTTON_SIZE + BUTTON_PADDING * 2 | |
885 elseif newRow then | |
886 button:SetPoint('TOPLEFT', upSlot, 'BOTTOMLEFT', 0, -BUTTON_SPACING) | |
887 upSlot = button | |
888 buttonsDepth = buttonsDepth + KEY_BUTTON_SIZE + BUTTON_SPACING | |
406 else | 889 else |
407 kb.release(button) | 890 button:SetPoint('TOPLEFT', leftSlot, 'TOPRIGHT', BUTTON_HSPACING, 0) |
408 end | 891 end |
409 | 892 |
410 local x, y = BUTTON_PADDING, - (BUTTON_PADDING + HEADER_OFFSET) | |
411 if index ~= 1 then | |
412 local col = mod(index, BINDS_PER_ROW) | |
413 if col == 0 then | |
414 col = BINDS_PER_ROW - 1 | |
415 else | |
416 col = col - 1 | |
417 end | |
418 x = col * (KEY_BUTTON_SIZE + BUTTON_SPACING) + BUTTON_PADDING | |
419 y = (ceil(index/ BINDS_PER_ROW)-1) * - (KEY_BUTTON_SIZE + BUTTON_SPACING) - BUTTON_PADDING - HEADER_OFFSET | |
420 end | |
421 button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE) | 893 button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE) |
422 button:SetPoint('TOPLEFT', kb, 'TOPLEFT', x, y) | |
423 button:Show() | 894 button:Show() |
424 buttons[index] = button | 895 buttons[index] = button |
896 leftSlot = button | |
425 end | 897 end |
426 return buttons[index] | 898 return buttons[index] |
427 end | 899 end |
428 | 900 |
429 --- Updates profile assignment and button contents | 901 --- Updates profile assignment and button contents |
430 kb.refresh = function(self) | 902 kb.UpdateSlot = function(self, force) |
431 if self.profile ~= bindMode then | 903 local slot = self:GetID() |
432 if profile.buttons[self:GetID()] then | 904 |
433 kb.assign(self, unpack(profile.buttons[self:GetID()])) | 905 if force then |
906 if configProfile.buttons[slot] then | |
907 kb.SetSlot(self, unpack(configProfile.buttons[slot])) | |
434 else | 908 else |
435 kb.release(self) | 909 kb.ReleaseSlot(self) |
436 end | 910 end |
437 end | 911 end |
438 | 912 |
439 if self.command then | 913 if self.command then |
914 print('['..slot..'] =', self.command, GetBindingKey(self.command)) | |
915 | |
440 if self.pending then | 916 if self.pending then |
441 self.border:SetColorTexture(unpack(BORDER_PENDING)) | 917 self.border:SetColorTexture(unpack(BORDER_PENDING)) |
918 elseif self.isDynamic then | |
919 self.border:SetColorTexture(unpack(BORDER_DYNAMIC)) | |
442 else | 920 else |
443 self.border:SetColorTexture(unpack(BORDER_ASSIGNED)) | 921 self.border:SetColorTexture(unpack(BORDER_ASSIGNED)) |
444 end | 922 end |
445 --self.macro:SetText(self.actionName) | 923 |
446 self.bind:SetText(BindingString(GetBindingKey(self.command))) | 924 if self.actionType == 'macro' then |
447 local locked, layer = CommandIsLocked(self) | 925 self.macro:Show() |
448 self.icon:SetDesaturated(locked) | 926 else |
449 self.icon:SetVertexColor(unpack(BINDING_SCHEME_VERTEX[layer])) | 927 self.macro:Hide() |
928 if self.actionType == 'spell' then | |
929 local dummy = GetSpellInfo(self.actionName) | |
930 if not dummy then | |
931 self.icon:SetDesaturated(true) | |
932 else | |
933 self.icon:SetDesaturated(false) | |
934 end | |
935 | |
936 end | |
937 end | |
938 | |
939 if self.isDynamic then | |
940 print('|cFFFFBB00UpdateSlot|r: ', self.isDynamic, self.isAvailable, self.actionID) | |
941 end | |
942 | |
943 if self.isDynamic == 'profession' then | |
944 local profText = (self.spellNum == 1) and TRADE_SKILLS or (BUTTON_HEADERS[self.profIndex] or GetProfessionInfo(self.profIndex)) | |
945 if self.isAvailable then | |
946 print(self.profIndex, 'spnum', type(self.spellNum), (self.spellNum == 1)) | |
947 | |
948 self.statusText = '|cFFFFFF00'..profText..'|r' | |
949 self.bindingText = BindingString(GetBindingKey(self.command)) | |
950 else | |
951 self.statusText = '|cFFFF4400'..profText..'|r' | |
952 self.actionName = '(need to train profession #'..self.profNum..')' | |
953 self.bindingText ='?' | |
954 end | |
955 elseif self.isDynamic == 'talent' then | |
956 | |
957 self.statusText = '|cFF00FFFF'.. TALENT .. '|r' | |
958 if self.isAvailable then | |
959 self.bindingText = BindingString(GetBindingKey(self.command)) | |
960 else | |
961 print(self.actionID, #kb.inactiveTalentBindings[self.actionID]) | |
962 self.bindingText= BindingString(unpack(kb.inactiveTalentBindings[self.actionID])) | |
963 end | |
964 else | |
965 self.statusText = '|cFF00FF00'.. (BUTTON_HEADERS[self.actionType] and BUTTON_HEADERS[self.actionType] or self.actionType) .. '|r' | |
966 self.bindingText = BindingString(GetBindingKey(self.command)) | |
967 end | |
968 | |
969 local locked, layer = IsCommandBound(self) | |
970 if locked then | |
971 self.icon:SetAlpha(0.5) | |
972 else | |
973 self.icon:SetAlpha(1) | |
974 end | |
975 | |
976 if self.actionType == 'spell' then | |
977 self.icon:SetTexture(GetSpellTexture(self.actionID)) | |
978 end | |
979 end | |
980 | |
981 if not self.isAvailable then | |
982 self.bind:SetTextColor(0.7,0.7,0.7,1) | |
450 else | 983 else |
451 self.border:SetColorTexture(unpack(BORDER_UNASSIGNED)) | 984 self.bind:SetTextColor(1,1,1,1) |
452 --self.macro:SetText(nil) | 985 end |
453 self.bind:SetText(nil) | 986 |
454 end | 987 self.header:SetText(self.statusText) |
455 end | 988 self.bind:SetText(self.bindingText) |
456 | 989 self.macro:SetText(self.macroName) |
457 local SetupUI = function() | 990 self.details:SetText(self.actionName) |
458 | 991 end |
459 | 992 |
460 kb.tabAnchor = {'TOPLEFT', kb, 'TOPRIGHT', 2, -TAB_OFFSET} | 993 |
461 kb.tabGrowth = {'TOPLEFT', nil,'BOTTOMLEFT', 0, -TAB_SPACING} | 994 kb.ApplyTalentBinding = function(talentInfo, cache) |
462 kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT } | 995 for i = 5, #talentInfo do |
463 kb.UIPanelAnchor = {'TOPLEFT', kb, 'TOPLEFT', BUTTON_PADDING + 12, -BUTTON_PADDING} | 996 SetBinding(talentInfo[i], "CLICK KeyBinderMacro:".. talentInfo[1]) |
464 kb.UIPanelGrowth = {'TOPLEFT', nil, 'TOPRIGHT', 14, 0 } | 997 tinsert(cache, talentInfo[i]) |
465 kb.controlsAnchor = {'BOTTOMLEFT', kb, BUTTON_PADDING, BUTTON_PADDING } | 998 end |
466 kb.controlsGrowth = {'BOTTOMLEFT', nil, 'BOTTOMRIGHT', BUTTON_SPACING, 0} | 999 end |
467 | 1000 kb.CacheTalentBinding = function(talentInfo, cache) |
468 --tab() frame, name, tooltip, texture, coords | 1001 local spellID = talentInfo[4] |
469 kb:tab('KeyBinderGlobalTab', BINDING_MODE[1], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85}) | 1002 kb.inactiveTalentBindings[spellID] = kb.inactiveTalentBindings[spellID] or {} |
470 kb:tab('KeyBinderCharacterTab', characterHeader, nil) | 1003 kb.inactiveTalentBindings[spellID] = {select(5,unpack(talentInfo)) } |
471 kb:tab('KeyBinderSpecTab', specHeader, specTexture) | 1004 cprint(spellID, unpack(kb.inactiveTalentBindings[spellID])) |
472 SetPortraitTexture(KeyBinderCharacterTab.icon, 'player') | 1005 end |
473 KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85) | 1006 |
474 | 1007 kb.ApplyBinding = function(command, name, icon, actionType, actionID, macroName, macroText ) |
475 saveButton = kb:button('KeyBinderSaveButton', 'Save', 'Commit all changes.', nil, kb.save) | 1008 |
476 restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', nil, kb.restore) | 1009 if actionType == 'macro' then |
477 clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', nil, kb.ResetProfile) | 1010 KeyBinderMacro:SetAttribute("*macro-"..macroName, actionID) |
478 | 1011 else |
479 kb:uibutton( | 1012 KeyBinderMacro:SetAttribute("*macrotext-"..macroName, macroText) |
480 'KeyBinderSpellBookButton', 'SpellBook', nil, | 1013 end |
481 function() ToggleSpellBook(BOOKTYPE_SPELL) end, | 1014 bindings[actionType] = bindings[actionType] or {} |
482 "Interface\\Spellbook\\Spellbook-Icon") | 1015 bindings[actionType][actionID] = bindings[actionType][actionID] or {} |
483 kb:uibutton( | 1016 bindings[command] = bindings[actionType][actionID] |
484 'KeyBinderTalentFrameButton', 'Talents', nil, | 1017 return bindings[actionType], actionID |
485 function() ToggleTalentFrame() end, | 1018 end |
486 "Interface\\TargetingFrame\\UI-Classes-Circles", | 1019 |
487 CLASS_ICON_TCOORDS[strupper(select(2,UnitClass("player")))]) | 1020 kb.ApplyBindings = function (profile) |
488 | 1021 cprint('binding profile', profile) |
489 kb:uibutton( | 1022 for slot, data in pairs(profile.buttons) do |
490 'KeyBinderMacroFrameButton', 'Macros', nil, | 1023 kb.ApplyBinding(unpack(data)) |
491 function() if MacroFrame then HideUIPanel(MacroFrame) else ShowMacroFrame() end end, | 1024 end |
492 "Interface\\MacroFrame\\MacroFrame-Icon") | 1025 |
493 | 1026 for key, command in pairs(profile.bindings) do |
494 kb:uibutton( | 1027 |
495 'KeyBinderInventoryButton', 'Bags', nil, | 1028 cprint('Bindings data registered', command, key) |
496 function() OpenAllBags() end, | 1029 |
497 "Interface\\BUTTONS\\Button-Backpack-Up") | 1030 --_G.print('HotKey','loading', key, command) |
498 | 1031 SetBinding(key, command) |
499 kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING) | 1032 if bindings[command] and not tContains(bindings[command], key) then |
500 HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING | 1033 tinsert(bindings[command], key) |
501 FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING | 1034 end |
502 end | 1035 end |
503 | 1036 |
504 --- Invokes the KeyBinder frame (from the /kb function or some other source) | 1037 for spellName, talentInfo in pairs(profile.talents) do |
505 kb.ui = function() | 1038 local dummy = GetSpellInfo(spellName) |
1039 local func = kb.CacheTalentBinding | |
1040 local dest = kb.inactiveTalentBindings | |
1041 if dummy then | |
1042 cprint('|cFFBBFF00Active:|r', dummy) | |
1043 local macroName, spellName, actionType, actionID = unpack(talentInfo) | |
1044 bindings[actionType] = bindings[actionType] or {} | |
1045 bindings[actionType][actionID] = {} | |
1046 func = kb.ApplyTalentBinding | |
1047 dest = bindings[actionType][actionID] | |
1048 else | |
1049 | |
1050 cprint('|cFFFF4400Inactive:|r', talentInfo[2]) | |
1051 end | |
1052 func(talentInfo, dest) | |
1053 end | |
1054 | |
1055 SaveBindings(GetCurrentBindingSet()) | |
1056 end | |
1057 | |
1058 kb.ApplyAllBindings =function () | |
1059 table.wipe(kb.inactiveTalentBindings) | |
1060 | |
1061 for i, profile in ipairs(priority) do | |
1062 kb.ApplyBindings(profile) | |
1063 end | |
1064 -- do this after to ensure that profession binds are properly overridden | |
1065 kb.UpdateProfessionInfo() | |
1066 end | |
1067 | |
1068 kb.Command = function(args, editor) | |
1069 if args:match("import") then | |
1070 kb.ImportCommmit(args) | |
1071 return | |
1072 elseif args:match("scan") then | |
1073 kb.ImportScan(args) | |
1074 kb.ui() | |
1075 return | |
1076 elseif args:match("load") then | |
1077 kb:ApplyAllBindings() | |
1078 return | |
1079 end | |
1080 | |
1081 if db.showUI then | |
1082 db.showUI = false | |
1083 kb:print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.') | |
1084 kb:Hide() | |
1085 else | |
1086 db.showUI = true | |
1087 kb:print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.') | |
1088 end | |
1089 kb.ui(true) | |
1090 end | |
1091 | |
1092 kb.InitProfile = function(profile, prototype) | |
1093 if not profile then | |
1094 profile = {} | |
1095 end | |
1096 if prototype then | |
1097 print('appplying prototype', prototype) | |
1098 for k,v in pairs(prototype) do | |
1099 if not profile[k] then | |
1100 profile[k] = v | |
1101 end | |
1102 end | |
1103 end | |
1104 | |
1105 profile.bound = profile.bound or {} | |
1106 profile.buttons = profile.buttons or {} | |
1107 profile.commands = profile.commands or {} | |
1108 profile.bindings = profile.bindings or {} | |
1109 profile.macros = profile.macros or {} | |
1110 profile.talents = profile.talents or {} | |
1111 return profile | |
1112 end | |
1113 | |
1114 kb.ResetProfile = function(profile, prototype) | |
1115 if profile == configProfile then | |
1116 for i, button in pairs(buttons) do | |
1117 kb.ReleaseSlot(button) | |
1118 end | |
1119 end | |
1120 table.wipe(profile) | |
1121 kb.InitProfile(profile, prototype) | |
1122 end | |
1123 | |
1124 | |
1125 | |
1126 --- Handles constructing spec profiles as they are selected | |
1127 | |
1128 | |
1129 kb.TalentCache = {} | |
1130 | |
1131 kb.UpdateSpecInfo = function() | |
1132 specID = GetSpecialization() | |
1133 specGlobalID, specName, specDesc , specTexture = GetSpecializationInfo(specID) | |
1134 loadedProfiles[BINDING_TYPE_CHARACTER][specID] = kb.InitProfile(loadedProfiles[BINDING_TYPE_CHARACTER][specID], { | |
1135 specID = specID}) | |
1136 | |
1137 configHeaders[BINDING_TYPE_SPECIALIZATION] = BINDING_MODE[BINDING_TYPE_SPECIALIZATION]:format(specName) | |
1138 loadedProfiles[BINDING_TYPE_SPECIALIZATION] = loadedProfiles[BINDING_TYPE_CHARACTER][specID] | |
1139 configProfile = loadedProfiles[bindMode] | |
1140 print('|cFF00FF00bindMode:|r', bindMode) | |
1141 | |
1142 priority = {loadedProfiles[BINDING_TYPE_GLOBAL], loadedProfiles[BINDING_TYPE_CHARACTER], loadedProfiles[BINDING_TYPE_SPECIALIZATION]} | |
1143 | |
1144 print('|cFF00FF00current spec:|r', specID, 'of', GetNumSpecializations()) | |
1145 end | |
1146 | |
1147 kb.UpdateTalentInfo = function() | |
1148 if kb.talentsPushed then | |
1149 return | |
1150 end | |
1151 | |
1152 | |
1153 table.wipe(kb.TalentCache) | |
1154 | |
1155 for row =1, MAX_TALENT_TIERS do | |
1156 for col = 1, NUM_TALENT_COLUMNS do | |
1157 local talentID, talentName, icon, selected, available, spellID = GetTalentInfo(row, col, 1) | |
1158 local talentInfo = kb.TalentCache[spellID] or {} | |
1159 talentInfo.row = 1 | |
1160 talentInfo.col = col | |
1161 talentInfo.name = talentName | |
1162 talentInfo.talentID = talentID | |
1163 talentInfo.selected = selected | |
1164 talentInfo.available = available | |
1165 talentInfo.spellID = spellID | |
1166 kb.TalentCache[spellID] = talentInfo | |
1167 print('Talent ', row, col, spellID, talentName) | |
1168 end | |
1169 end | |
1170 kb.talentsPushed = true | |
1171 end | |
1172 | |
1173 | |
1174 kb.ProfessionCache = {} | |
1175 kb.UpdateProfessionInfo = function() | |
1176 table.wipe(kb.ProfessionCache) | |
1177 local profs = {GetProfessions() } | |
1178 local primaryNum = 0 | |
1179 for i, index in ipairs(profs) do | |
1180 local profName, texture, rank, maxRank, numSpells, spellOffset = GetProfessionInfo(index) | |
1181 cprint(i, index, profName, numSpells, spellOffset) | |
1182 if not professionMappings[index] then | |
1183 primaryNum = primaryNum + 1 | |
1184 end | |
1185 local profNum = professionMappings[index] or primaryNum | |
1186 | |
1187 | |
1188 kb.ProfessionCache[profNum] = kb.ProfessionCache[i] or {} | |
1189 | |
1190 for j = 1, numSpells do | |
1191 local spellName, _, icon, _, _, _, spellID = GetSpellInfo(spellOffset+j, BOOKTYPE_PROFESSION) | |
1192 | |
1193 local profInfo = { | |
1194 spellName = spellName, | |
1195 spellID = spellID, | |
1196 icon = icon, | |
1197 profOffset = i, | |
1198 profIndex = index, | |
1199 spellOffset = (spellOffset+j), | |
1200 spellNum = j | |
1201 } | |
1202 KeyBinderMacro:SetAttribute("*macrotext-profession_"..i .. '_' ..j, "/cast ".. spellName) | |
1203 | |
1204 kb.ProfessionCache[i .. '_' .. j] = profInfo | |
1205 kb.ProfessionCache[spellName] = profInfo | |
1206 kb.ProfessionCache[spellID] = profInfo | |
1207 cprint(' |cFF0088FF['..i..']|r|cFFFF44BB['..spellOffset+i..']|r', spellName, "*macrotext-profession_"..i .. '_' ..j) | |
1208 end | |
1209 | |
1210 end | |
1211 | |
1212 end | |
1213 | |
1214 --- Obtains profile data or creates the necessary tables | |
1215 kb.SelectProfileSet = function(name) | |
1216 | |
1217 --- General info | |
1218 classHeader, className, classID = UnitClass('player') | |
1219 print('|cFF00FF00profile:|r', name) | |
1220 print('|cFF00FF00class:|r', UnitClass('player')) | |
1221 | |
1222 --- Global | |
1223 bindMode = BINDING_TYPE_GLOBAL | |
1224 kb.InitProfile(db) | |
1225 loadedProfiles[BINDING_TYPE_GLOBAL] = db | |
1226 | |
1227 --- Character | |
1228 if name then | |
1229 db[name] = kb.InitProfile(db[name], | |
1230 {classHeader = classHeader, className = className, classID = classID}) | |
1231 loadedProfiles[BINDING_TYPE_CHARACTER] = db[name] | |
1232 bindMode = BINDING_TYPE_CHARACTER | |
1233 end | |
1234 | |
1235 --- Mutable skills data | |
1236 kb.UpdateSpecInfo() | |
1237 kb.UpdateTalentInfo() | |
1238 | |
1239 priority = {loadedProfiles[BINDING_TYPE_GLOBAL], loadedProfiles[BINDING_TYPE_CHARACTER], loadedProfiles[BINDING_TYPE_SPECIALIZATION]} | |
1240 if db.bindMode and loadedProfiles[db.bindMode] then | |
1241 bindMode = db.bindMode | |
1242 end | |
1243 | |
1244 db.bindMode = bindMode | |
1245 | |
1246 if not BINDING_MODE[bindMode] then | |
1247 bindMode = 3 | |
1248 db.bindMode = 3 | |
1249 print('overriding', bindMode) | |
1250 end | |
1251 | |
1252 print(BINDING_TYPE_GLOBAL) | |
1253 configHeaders[BINDING_TYPE_GLOBAL] = BINDING_MODE[BINDING_TYPE_GLOBAL] | |
1254 configHeaders[BINDING_TYPE_CHARACTER] = BINDING_MODE[BINDING_TYPE_CHARACTER]:format(UnitName('player', true)) | |
1255 configHeaders[BINDING_TYPE_SPECIALIZATION] = BINDING_MODE[BINDING_TYPE_SPECIALIZATION]:format(specName) | |
1256 | |
1257 | |
1258 setmetatable(loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return configHeaders[BINDING_TYPE_GLOBAL] end}) | |
1259 setmetatable(loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return configHeaders[BINDING_TYPE_CHARACTER] end}) | |
1260 setmetatable(loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return configHeaders[BINDING_TYPE_SPECIALIZATION] end}) | |
1261 | |
1262 print('|cFF00FF00bindMode:|r', bindMode) | |
1263 configProfile = loadedProfiles[bindMode] | |
1264 end | |
1265 | |
1266 local scrollCache = {} | |
1267 kb.SelectTab = function(self) | |
1268 scrollCache[bindMode] = kb.scrollOffset | |
1269 bindMode = self:GetID() | |
1270 configProfile = loadedProfiles[self:GetID()] | |
1271 db.bindMode = self:GetID() | |
1272 kb.scrollOffset = scrollCache[bindMode] or 0 | |
1273 kb.ui(true) | |
1274 end | |
1275 | |
1276 kb.RevertBindings = function() | |
1277 -- todo: reversion code | |
1278 end | |
1279 | |
1280 kb.ConfirmBindings = function() | |
1281 SaveBindings(GetCurrentBindingSet()) | |
1282 bindsCommitted = true | |
1283 for i, button in ipairs(buttons) do | |
1284 button.pending = false | |
1285 end | |
1286 kb.ApplyAllBindings() | |
1287 | |
1288 kb.ui() | |
1289 kb:print('Keybinds saved.') | |
1290 end | |
1291 | |
1292 | |
1293 | |
1294 | |
1295 | |
1296 | |
1297 --- push current information into living UI | |
1298 kb.ui = function(force) | |
1299 for i, module in ipairs(kb.modules) do | |
1300 if module.ui then | |
1301 module.ui(force) | |
1302 end | |
1303 end | |
1304 | |
506 if not db.showUI then | 1305 if not db.showUI then |
1306 print('---end of refresh') | |
507 return | 1307 return |
508 end | 1308 end |
509 | |
510 if not kb:IsVisible() then | |
511 kb:Show() | |
512 db.showUI = true | |
513 end | |
514 | |
515 if not kb.loaded then | 1309 if not kb.loaded then |
516 SetupUI() | 1310 KeyBinder_Initialize() |
517 kb.loaded = true | 1311 kb.loaded = true |
518 end | 1312 end |
519 | |
520 for i = 1, numButtons do | 1313 for i = 1, numButtons do |
521 kb.refresh(kb.keyslot(i)) | 1314 local button = kb.GetSlot(i) |
522 end | 1315 button:SetID(i+kb.scrollOffset) |
523 | 1316 kb.UpdateSlot(button, force) |
524 if bindMode == BINDING_TYPE_SPECIALIZATION then | |
525 bindHeader = select(2,GetSpecializationInfo(GetSpecialization())) | |
526 elseif bindMode == BINDING_TYPE_CHARACTER then | |
527 bindHeader = UnitName('player') | |
528 else | |
529 bindHeader = '' | |
530 end | 1317 end |
531 | 1318 |
532 if bindsCommitted then | 1319 if bindsCommitted then |
533 KeyBinderSaveButton:Disable() | 1320 KeyBinderSaveButton:Disable() |
534 KeyBinderRestoreButton:Disable() | 1321 --KeyBinderRestoreButton:Disable() |
535 else | 1322 else |
536 KeyBinderSaveButton:Enable() | 1323 KeyBinderSaveButton:Enable() |
537 KeyBinderRestoreButton:Enable() | 1324 --KeyBinderRestoreButton:Enable() |
538 end | 1325 end |
539 | 1326 |
540 --- panel attributes | 1327 --- Frame Sizing |
1328 kb.profilebg:SetHeight(kb.tabSize[2] + BUTTON_PADDING * 2 + kb.profiletext:GetStringHeight()) | |
1329 | |
1330 kb.bg:SetWidth((KEY_BUTTON_SIZE + BUTTON_HSPACING + BUTTON_SPACING) * BINDS_PER_ROW + BUTTON_PADDING*2 - BUTTON_SPACING) | |
541 local numRows = numButtons/BINDS_PER_ROW | 1331 local numRows = numButtons/BINDS_PER_ROW |
542 kb:SetHeight( numRows * (KEY_BUTTON_SIZE) + (numRows - 1) * BUTTON_SPACING + HEADER_OFFSET + FOOTER_OFFSET + BUTTON_PADDING * 2) | 1332 |
543 kb:SetWidth((BINDS_PER_ROW - 1) * BUTTON_SPACING + BINDS_PER_ROW * KEY_BUTTON_SIZE + BUTTON_PADDING * 2) | 1333 kb.bg:SetHeight((KEY_BUTTON_SIZE + BUTTON_SPACING) * numRows + BUTTON_PADDING*2 - BUTTON_SPACING) |
1334 | |
1335 kb:SetHeight(kb.headerbg:GetHeight() + kb.profilebg:GetHeight() + kb.bg:GetHeight() + kb.footer:GetHeight()) | |
1336 kb:SetWidth((kb.sourcesbg:GetWidth() +(BINDS_PER_ROW * (KEY_BUTTON_SIZE + BUTTON_HSPACING) + (BINDS_PER_ROW - 1) * BUTTON_SPACING + BUTTON_PADDING * 2) )) | |
1337 | |
544 kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[bindMode])) | 1338 kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[bindMode])) |
545 | |
546 | |
547 for i, tab in ipairs(kb.tabButtons) do | 1339 for i, tab in ipairs(kb.tabButtons) do |
548 | 1340 local border = tab:GetNormalTexture() |
549 local n = tab:GetNormalTexture() | |
550 local tabTexture = "Interface\\Buttons\\UI-Quickslot2" | 1341 local tabTexture = "Interface\\Buttons\\UI-Quickslot2" |
551 local left, top, right, bottom = -12, 12, 13, -13 | 1342 local left, top, right, bottom = -12, 12, 13, -13 |
552 if i == bindMode then | 1343 if i == bindMode then |
553 tabTexture = "Interface\\Buttons\\CheckButtonGlow" | 1344 tabTexture = "Interface\\Buttons\\CheckButtonGlow" |
554 left, top, right, bottom = -14, 14, 15, -15 | 1345 left, top, right, bottom = -14, 14, 15, -15 |
555 end | 1346 tab.icon:SetDesaturated(false) |
556 n:SetTexture(tabTexture) | 1347 if tab.icon2 then tab.icon2:SetDesaturated(false) end |
557 n:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top) | 1348 border:SetDesaturated(true) |
558 n:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom) | 1349 border:SetVertexColor(1,1,1, 1) |
559 end | 1350 else |
560 end | 1351 tab.icon:SetDesaturated(true) |
561 | 1352 if tab.icon2 then tab.icon2:SetDesaturated(true) end |
562 kb.loadbinds = function (bindings) | 1353 border:SetDesaturated(false) |
563 for key, command in pairs(bindings) do | 1354 border:SetVertexColor(1,1,1) |
564 -- store for reversion | 1355 end |
565 local oldAction = GetBindingAction(key) | 1356 border:SetTexture(tabTexture) |
566 if oldAction ~= command then | 1357 border:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top) |
567 local bind1, bind2 = GetBindingKey(oldAction) | 1358 border:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom) |
568 if bind1 and not reverts[bind1] then | 1359 end |
569 reverts[bind1] = oldAction | 1360 |
570 end | 1361 KeyBinderSpecTab.icon:SetTexture(specTexture) |
571 if bind2 and not reverts[bind2] then | 1362 |
572 reverts[bind2] = oldAction | 1363 kb.profiletext:SetText(configHeaders[bindMode]) |
573 end | 1364 print(bindMode, configHeaders[bindMode], kb:GetSize()) |
574 end | 1365 print(kb:GetPoint(1)) |
575 SetBindings(key, command) | 1366 |
576 end | 1367 kb:Show() |
577 SaveBindings() | 1368 |
578 end | 1369 -- Reset this so talent cache can be rebuilt |
579 | 1370 kb.talentsPushed = nil |
580 local ACTION_BARS = { | 1371 end |
581 {'ActionButton', 0}, | 1372 |
582 {'MultiBarLeftButton', 24}, | 1373 --- post ADDON_LOADED |
583 {'MultiBarRightButton', 36}, | 1374 kb.variables = function() |
584 {'MultiBarBottomRighttButton', 48}, | 1375 SkeletonKeyDB = SkeletonKeyDB or {spec = {}} |
585 {'MultiBarBottomLeftButton', 60}, | 1376 kb.db = SkeletonKeyDB |
1377 kb.playerName = UnitName('player') | |
1378 kb.playerRealm = SelectedRealmName() | |
1379 kb.profileName = kb.playerRealm .. '_' .. kb.playerName | |
1380 db = kb.db | |
1381 | |
1382 kb.SelectProfileSet(kb.profileName) | |
1383 if not configProfile.imported then | |
1384 kb.ImportScan() | |
1385 end | |
1386 kb.ApplyAllBindings() | |
1387 | |
1388 kb.ui(true) | |
1389 end | |
1390 | |
1391 | |
1392 kb.wrap = function(module) | |
1393 kb.modules = kb.modules or {} | |
1394 tinsert(kb.modules, module) | |
1395 end | |
1396 | |
1397 -- Volatiles Access | |
1398 kb.BindingIsLocked = BindingIsLocked | |
1399 kb.BindingString = BindingString | |
1400 kb.GetBindings = function() return bindings end | |
1401 kb.GetButtons = function() return buttons end | |
1402 kb.GetCharacterProfile = function () return loadedProfiles[BINDING_TYPE_CHARACTER] end | |
1403 kb.GetGlobalProfile = function () return loadedProfiles[BINDING_TYPE_GLOBAL] end | |
1404 kb.GetLooseTalents = function() return talentBindings end | |
1405 kb.GetProfileStack = function() return priority end | |
1406 kb.GetReverts = function() return reverts end | |
1407 kb.GetSpecProfile = function () return loadedProfiles[BINDING_TYPE_SPECIALIZATION] end | |
1408 | |
1409 --- Add to blizzard interfaces | |
1410 StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] = { | |
1411 text = "Confirm moving an assigned command.", | |
1412 button1 = OKAY, | |
1413 button2 = CANCEL, | |
1414 timeout = 0, | |
1415 whileDead = 1, | |
1416 showAlert = 1, | |
1417 OnAccept = kb.AcceptAssignment, | |
1418 OnCancel = function() kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) end | |
586 } | 1419 } |
587 kb.HotKeyText = function (slot) | 1420 |
588 local i, offset = 0, 0 | 1421 SLASH_SKB1 = "/skb" |
589 local actionbar | 1422 SLASH_SKB2 = "/skeletonkey" |
590 | 1423 SlashCmdList.SKB = kb.Command |
591 -- figure out which bar the slot belongs to | 1424 |
592 for i, bar in ipairs(ACTION_BARS) do | 1425 -- This is needed to identify a spells that aren't reflected by GetCursorInfo() |
593 actionbar, offset = unpack(ACTION_BARS[i]) | 1426 hooksecurefunc("PickupSpellBookItem", function(slot, bookType) |
594 if bar[2] > slot then | 1427 print('|cFFFF4400PickupSpellBookItem(..', tostring(slot),', '..tostring(bookType)..')') |
595 break | 1428 CURSOR_SPELLSLOT = slot |
596 end | 1429 CURSOR_BOOKTYPE = bookType |
597 end | 1430 end) |
598 local button = _G[actionbar .. (slot - offset)] | 1431 |
599 | 1432 -- Pet actions |
600 if not button then | 1433 local isPickup |
601 return | 1434 hooksecurefunc("PickupPetAction", function(slot, ...) |
602 end | 1435 isPickup = GetCursorInfo() |
603 | 1436 |
604 local type, id, subType, subID = GetActionInfo(slot) | 1437 CURSOR_PETACTION = isPickup and slot |
605 | 1438 print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION) |
606 if not type then | 1439 end) |
607 return | |
608 end | |
609 | |
610 local bind, command | |
611 if type == 'spell' then | |
612 local name = GetSpellInfo(id) | |
613 command = 'SPELL '..name | |
614 elseif type == 'macro' then | |
615 command = 'MACRO ' .. id | |
616 else | |
617 return | |
618 end | |
619 bind = GetBindingKey(command) | |
620 if bind then | |
621 button.HotKey:SetText(BindingString(bind)) | |
622 button.HotKey:Show() | |
623 end | |
624 end | |
625 | |
626 kb.InitProfile = function(profile) | |
627 profile.buttons = profile.buttons or {} | |
628 profile.commands = profile.commands or {} | |
629 profile.bindings = profile.bindings or {} | |
630 profile.macros = profile.macros or {} | |
631 return profile | |
632 end | |
633 kb.ResetProfile = function() | |
634 | |
635 for i, button in pairs(buttons) do | |
636 kb.release(button) | |
637 end | |
638 | |
639 profile.commands = {} | |
640 profile.bindings = {} | |
641 profile.macros = {} | |
642 end | |
643 | |
644 --- Gives us the profile structure to work with while instating data | |
645 kb.profile = function(name) | |
646 global = kb.InitProfile(db) | |
647 profile = global | |
648 local subtitle | |
649 if name then | |
650 db[name] = db[name] or {} | |
651 db[name] = kb.InitProfile(db[name]) | |
652 character = db[name] | |
653 local spec = GetSpecialization() | |
654 if spec then | |
655 db[name][spec] = db[name][spec] or {} | |
656 profile = kb.InitProfile(db[name][spec]) | |
657 bindMode = BINDING_TYPE_SPECIALIZATION | |
658 subtitle = select(2,GetSpecializationInfo(spec)) | |
659 specialization = db[name][spec] | |
660 else | |
661 profile = kb.InitProfile(db[name]) | |
662 bindMode = BINDING_TYPE_CHARACTER | |
663 subtitle = name | |
664 specialization = character | |
665 end | |
666 end | |
667 priority = {global, character, specialization } | |
668 | |
669 | |
670 | |
671 if not db.bindsPage then | |
672 db.bindsPage = bindMode | |
673 end | |
674 bindMode = db.bindsPage | |
675 | |
676 | |
677 if not BINDING_MODE[bindMode] then | |
678 bindMode = 3 | |
679 db.bindsPage = 3 | |
680 print('overriding', bindMode) | |
681 end | |
682 | |
683 profile = priority[bindMode] | |
684 | |
685 | |
686 local _ | |
687 _, specHeader, _, specTexture = GetSpecializationInfo(GetSpecialization()) | |
688 print(GetSpecializationInfo(GetSpecialization())) | |
689 specHeader = BINDING_MODE[2]:format(specHeader) | |
690 characterHeader = BINDING_MODE[2]:format(UnitName('player')) | |
691 | |
692 print('Using binding profile |cFF00FF88'..BINDING_MODE[bindMode]:format(subtitle)..'|r') | |
693 end | |
694 | |
695 kb.SelectTab = function(self) | |
696 bindMode = self:GetID() | |
697 profile = priority[self:GetID()] | |
698 db.bindsPage = self:GetID() | |
699 kb.ui() | |
700 end | |
701 kb.save = function() | |
702 SaveBindings(GetCurrentBindingSet()) | |
703 bindsCommitted = true | |
704 for i, button in ipairs(buttons) do | |
705 button.pending = false | |
706 end | |
707 | |
708 kb.ui() | |
709 print('Bindings saved.') | |
710 end | |
711 kb.restore = function() | |
712 for i, button in pairs(buttons) do | |
713 button.pending = false | |
714 end | |
715 bindsCommitted = true | |
716 LoadBindings(GetCurrentBindingSet()) | |
717 print('All changes discarded.') | |
718 end | |
719 | |
720 --- Tells all the hud buttons what to do | |
721 kb.init = function() | |
722 KeyBinderMacro:SetAttribute('*type*', 'macro') | |
723 end | |
724 | |
725 --- Get started | |
726 kb.variables = function() | |
727 SkeletonKeyDB = SkeletonKeyDB or {} | |
728 db = SkeletonKeyDB | |
729 kb.profile(GetUnitName('player', true)) | |
730 for i = 1, 3 do | |
731 for attribute, data in pairs(priority[i].macros) do | |
732 KeyBinderMacro:SetAttribute(attribute, data[1]) | |
733 end | |
734 end | |
735 | |
736 kb.UPDATE_BINDINGS() | |
737 kb:RegisterEvent('UPDATE_BINDINGS') | |
738 kb:RegisterEvent('UPDATE_MACROS') | |
739 kb:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED') | |
740 kb:RegisterEvent('PLAYER_EQUIPMENT_CHANGED') | |
741 kb:RegisterEvent('PLAYER_REGEN_DISABLED') | |
742 kb:RegisterEvent('PLAYER_REGEN_ENABLED') | |
743 kb:RegisterEvent('ACTIONBAR_SLOT_CHANGED') | |
744 end | |
745 | |
746 kb.close = function() | |
747 db.showUI = false | |
748 kb:Hide() | |
749 end | |
750 | |
751 kb.PLAYER_REGEN_DISABLED = function() | |
752 if db.showUI then | |
753 kb:Hide() | |
754 end | |
755 end | |
756 | |
757 kb.PLAYER_REGEN_ENABLED = function() | |
758 if db.showUI then | |
759 kb.ui() | |
760 end | |
761 end | |
762 --- Refresh buttons if macros are updated | |
763 kb.UPDATE_BINDINGS = function() | |
764 for i = 1, 120 do | |
765 kb.HotKeyText(i) | |
766 end | |
767 if db.showUI then | |
768 kb.ui() | |
769 end | |
770 end | |
771 | |
772 kb.ACTIONBAR_SLOT_CHANGED = function(self, event, slot) | |
773 kb.HotKeyText(slot) | |
774 return true | |
775 end | |
776 | |
777 kb.UPDATE_MACROS = kb.UPDATE_BINDINGS | |
778 SLASH_KB1 = "/kb" | |
779 SlashCmdList.KB = function(self, input) | |
780 if db.showUI then | |
781 db.showUI = false | |
782 print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.') | |
783 kb:Hide() | |
784 else | |
785 db.showUI = true | |
786 print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.') | |
787 kb.ui() | |
788 end | |
789 end |