Nenue@0: -------------------------------------------- Nenue@0: -- KrakTool Nenue@0: -- Nick Nenue@0: -- @project-revision@ @project-hash@ Nenue@0: -- @file-revision@ @file-hash@ Nenue@0: -- Created: 6/16/2016 3:47 AM Nenue@0: -------------------------------------------- Nenue@0: -- kb Nenue@0: -- .bind(button, key) bind current keystroke to command Nenue@0: -- .assign(button, command, name, icon) set button command Nenue@0: -- .release(button) clear button command Nenue@0: -- .refresh(button) update button contents Nenue@0: -- .ui() invoke interface Nenue@0: -- .profile(name) set profile character Nenue@0: -- .loadbinds(bindings) walk table with SetBinding() Nenue@0: Nenue@0: local kb = KeyBinder Nenue@0: local KT = select(2,...) Nenue@0: KT.register(KeyBinder) Nenue@0: local MIN_BIND_SLOTS = 32 Nenue@0: local BINDS_PER_ROW = 8 Nenue@0: local KEY_BUTTON_SIZE = 40 Nenue@0: local TAB_OFFSET = 12 Nenue@0: local TAB_HEIGHT = 40 Nenue@0: local TAB_SPACING = 2 Nenue@0: local BUTTON_SPACING = 4 Nenue@0: local BUTTON_PADDING = 12 Nenue@0: local HEADER_OFFSET Nenue@0: local FOOTER_OFFSET Nenue@0: local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544; Nenue@0: local BINDING_TYPE_SPECIALIZATION = 3 Nenue@0: local BINDING_TYPE_CHARACTER = 2 Nenue@0: local BINDING_TYPE_GLOBAL = 1 Nenue@0: local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).' Nenue@0: local BINDING_FAILED_PROTECTED = '|cFF00FF00%s|r used by |cFFFFFF00%s|r!' Nenue@0: local BINDING_MODE = { Nenue@0: [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s', Nenue@0: [BINDING_TYPE_CHARACTER] = 'Character: %s', Nenue@0: [BINDING_TYPE_GLOBAL] = 'Global Binds' Nenue@0: } Nenue@0: local BINDING_SCHEME_COLOR = { Nenue@0: [BINDING_TYPE_SPECIALIZATION] = {0,0,0,0.5}, Nenue@0: [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5}, Nenue@0: [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5} Nenue@0: } Nenue@0: local BINDING_SCHEME_VERTEX = { Nenue@0: Nenue@0: [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1}, Nenue@0: [BINDING_TYPE_CHARACTER] = {0,1,0,1}, Nenue@0: [BINDING_TYPE_GLOBAL] = {0,.5,1,1} Nenue@0: } Nenue@0: Nenue@0: local BINDING_SCHEME_TEXT = { Nenue@0: [BINDING_TYPE_SPECIALIZATION] = {1, 1, 0}, Nenue@0: [BINDING_TYPE_CHARACTER] = {0, 1, 0}, Nenue@0: [BINDING_TYPE_GLOBAL] = {0, 1, 1} Nenue@0: } Nenue@0: local ACTION_SCRIPT = { Nenue@0: ['mount'] = "/script C_MountJournal.SummonByID(%d)", Nenue@0: ['equipset'] = "/script UseEquipmentSet(%d)", Nenue@0: } Nenue@0: Nenue@0: local COMMAND_SPELL = "^SPELL (%S.+)" Nenue@0: local COMMAND_MACRO = "^MACRO (%S.+)" Nenue@0: local COMMAND_ITEM = "^ITEM (%S.+)" Nenue@0: local COMMAND_MOUNT = "^CLICK KeyBinderMacro:mount(%d+)" Nenue@0: local COMMAND_EQUIPSET = "^CLICK KeyBinderMacro:equipset(%d+)" Nenue@0: Nenue@0: local PICKUP_TYPES = { Nenue@0: [COMMAND_SPELL] = PickupSpell, Nenue@0: [COMMAND_MACRO] = PickupMacro, Nenue@0: [COMMAND_ITEM] = PickupItem, Nenue@0: [COMMAND_MOUNT] = C_MountJournal.Pickup, Nenue@0: [COMMAND_EQUIPSET] = PickupEquipmentSet Nenue@0: } Nenue@0: local PICKUP_VALUES = { Nenue@0: [COMMAND_SPELL] = function(name) return select(7, GetSpellInfo(name)) end Nenue@0: } Nenue@0: local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES" Nenue@0: local BORDER_UNASSIGNED = {0.2,0.2,0.2,1 } Nenue@0: local BORDER_ASSIGNED = {0.5,0.5,0.5,1 } Nenue@0: local BORDER_PENDING = {1,0.5,0,1 } Nenue@0: Nenue@0: Nenue@0: Nenue@0: local bindMode = 3 -- not to be confused with db.bindMode which is boolean Nenue@0: local bindHeader = '' Nenue@0: local specHeader, specTexture, characterHeader = 'SPEC_NAME', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME' Nenue@0: local bindsCommitted = true Nenue@0: Nenue@0: local profile Nenue@0: local character Nenue@0: local specialization Nenue@0: local global Nenue@0: local priority = {} Nenue@0: local numButtons = BINDS_PER_ROW * 4 Nenue@0: local buttons = {} Nenue@0: local reverts = {} Nenue@0: local KeyButton = {} -- collection of KeyButton template handlers Nenue@0: local Action = {} -- collection of special action buttons for special binds Nenue@0: local protected = { Nenue@0: ['OPENCHATSLASH'] = true, Nenue@0: ['OPENCHAT'] = true, Nenue@0: } Nenue@0: local saveButton, restoreButton, clearButton Nenue@0: Nenue@0: --- Returns a value for use with Texture:SetDesaturated() Nenue@0: local CommandIsLocked = function(self) Nenue@0: print('command check: 1-'..(bindMode-1)) Nenue@0: local desaturated, layer = false, 3 Nenue@0: for i = 1, bindMode-1 do Nenue@0: local tier = priority[i] Nenue@0: local existing = tier.commands[self.command] Nenue@0: print(' ', i, tier.commands[self.command]) Nenue@0: if existing then Nenue@0: if self:GetID() ~= existing then Nenue@0: -- sanitize bad data Nenue@0: tier.commands[self.command] = nil Nenue@0: else Nenue@0: layer = i Nenue@0: desaturated = true Nenue@0: break Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: return desaturated, layer Nenue@0: end Nenue@0: Nenue@0: --- Returns a value for use with Texture:SetDesaturated() Nenue@0: local BindingIsLocked = function(key) Nenue@0: local success = false Nenue@0: for i = 1, bindMode-1 do Nenue@0: local tier = priority[i] Nenue@0: if tier.bindings[key] then Nenue@0: success = true Nenue@0: break Nenue@0: end Nenue@0: end Nenue@0: return success Nenue@0: end Nenue@0: Nenue@0: --- Translates GetBindingKey() results into a printable string. Nenue@0: local BindingString = function(...) Nenue@0: local stack = {} Nenue@0: for i = 1, select('#', ...) do Nenue@0: local key = select(i, ...) Nenue@0: stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp') Nenue@0: end Nenue@0: Nenue@0: if #stack >= 1 then Nenue@0: return table.concat(stack, ',') Nenue@0: else Nenue@0: return nil Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: --- This keeps our KeyDown handler from getting stuck with game controls Nenue@0: KeyButton.OnUpdate = function(self) Nenue@0: if not self.command then Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: if self:IsMouseOver() then Nenue@0: if not self.active then Nenue@0: -- only set this handler when the button is activated/mouseOver Nenue@0: self.active = true Nenue@0: self:SetScript('OnKeyDown', kb.bind) Nenue@0: Nenue@0: local bindText = self.command Nenue@0: if self.bind:GetText() then Nenue@0: bindText = bindText .. ': |cFF00FF00' .. self.bind:GetText() Nenue@0: end Nenue@0: Nenue@0: kb.bindlist:SetText(bindText) Nenue@0: GameTooltip:SetOwner(self) Nenue@0: GameTooltip:SetAnchorType('ANCHOR_BOTTOMRIGHT') Nenue@0: GameTooltip:SetText(self.actionName) Nenue@0: GameTooltip:Show() Nenue@0: end Nenue@0: else Nenue@0: if self.active then Nenue@0: GameTooltip:Hide() Nenue@0: self.active = nil Nenue@0: self:SetScript('OnKeyDown', nil) Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: --- Cursor pickup handler Nenue@0: -- Walks through PICKUP_TYPES and runs the function if match(command, key) turns up a result. Nenue@0: -- Passes the result through PICKUP_VALUES[pattern]() if defined. Nenue@0: Nenue@0: kb.pickup = function(self) Nenue@0: for pattern, pickup in pairs(PICKUP_TYPES) do Nenue@0: local value = self.command:match(pattern) Nenue@0: if value then Nenue@0: if PICKUP_VALUES[pattern] then Nenue@0: value = PICKUP_VALUES[pattern](value) Nenue@0: end Nenue@0: Nenue@0: pickup(value) Nenue@0: kb.release(self) Nenue@0: break Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: --- Setup an action button base on template info Nenue@0: kb.action = function(type, id) Nenue@0: Nenue@0: local macroName = type .. id Nenue@0: macroName = macroName:gsub(' ', '') Nenue@0: Nenue@0: local attribute = '*macrotext-'..macroName Nenue@0: local value = ACTION_SCRIPT[type]:format(id) Nenue@0: local command = 'CLICK KeyBinderMacro:'.. macroName Nenue@0: profile.macros[attribute] = {value, command} Nenue@0: Nenue@0: KeyBinderMacro:SetAttribute(attribute, value) Nenue@0: return command Nenue@0: end Nenue@0: Nenue@0: KeyButton.OnDragStart = function(self) Nenue@0: if not self.command then Nenue@0: return Nenue@0: end Nenue@0: kb.pickup(self) Nenue@0: end Nenue@0: Nenue@0: KeyButton.OnReceiveDrag = function(self, ...) Nenue@0: print(self:GetName(),'|cFF0088FFreceived|r', ...) Nenue@0: local type, value, subType, subData = GetCursorInfo() Nenue@0: print('GetCursorInfo', type, value, subType, subData) Nenue@0: if type then Nenue@0: if type == 'spell' then Nenue@0: value = subData Nenue@0: end Nenue@0: Nenue@0: local command, name, icon, _ Nenue@0: if type == 'spell' then Nenue@0: name, _, icon = GetSpellInfo(value) Nenue@0: command = 'SPELL ' .. name Nenue@0: name = '' Nenue@0: elseif type == 'macro' then Nenue@0: name, icon = GetMacroInfo(value) Nenue@0: command = 'MACRO ' .. name Nenue@0: elseif type == 'mount' then Nenue@0: if subType == 0 then Nenue@0: name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL) Nenue@0: value= 0 Nenue@0: else Nenue@0: name, _, icon = C_MountJournal.GetMountInfoByID(value) Nenue@0: end Nenue@0: command = kb.action(type, value) Nenue@0: elseif type == 'item' then Nenue@0: name = GetItemInfo(value) Nenue@0: icon = GetItemIcon(value) Nenue@0: command = 'ITEM ' .. name Nenue@0: end Nenue@0: kb.assign(self, command, name, icon) Nenue@0: kb.refresh(self) Nenue@0: ClearCursor() Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: KeyButton.OnMouseDown = function(self, click) Nenue@0: print(self:GetName(), 'OnMouseDown', click) Nenue@0: if click == 'LeftButton' then Nenue@0: KeyButton.OnReceiveDrag(self) Nenue@0: elseif click == 'RightButton' then Nenue@0: kb.release(self) Nenue@0: else Nenue@0: kb.bind(self) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: Nenue@0: --- Updates the current KeyBinding for the button's command Nenue@0: kb.bind = function(self, key) Nenue@0: Nenue@0: print('|cFFFFFF00bind|cFFFFFF00', self:GetID(), '|cFF00FFFF', key) Nenue@0: if not self.command then Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: if protected[GetBindingAction(key)] then Nenue@0: return Nenue@0: kb.bindlist:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(key))) Nenue@0: end Nenue@0: Nenue@0: if key == 'ESCAPE' then Nenue@0: local key1, key2 = GetBindingKey(self.command) Nenue@0: if key1 then Nenue@0: SetBinding(key1, nil) Nenue@0: print('Unbound', key1) Nenue@0: end Nenue@0: if key2 then Nenue@0: SetBinding(key2, nil) Nenue@0: print('Unbound', key2) Nenue@0: end Nenue@0: self.active = false Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: local modifier = '' Nenue@0: if IsAltKeyDown() then Nenue@0: modifier = 'ALT-' Nenue@0: end Nenue@0: if IsControlKeyDown() then Nenue@0: modifier = modifier.. 'CTRL-' Nenue@0: end Nenue@0: if IsShiftKeyDown() then Nenue@0: modifier = modifier..'SHIFT-' Nenue@0: end Nenue@0: Nenue@0: if self.command then Nenue@0: self.binding = modifier..key Nenue@0: self.pending = true Nenue@0: self.border:SetColorTexture(1,.5,0, 1) Nenue@0: Nenue@0: local old = GetBindingAction(self.binding) Nenue@0: local binding1, binding2, new1, new2 Nenue@0: if old and old ~= self.command then Nenue@0: print('Discarding keybind for', old) Nenue@0: local binding1, binding2 = GetBindingKey(old) Nenue@0: -- need to preserve argument order Nenue@0: end Nenue@0: tinsert(reverts, {old, binding1, binding2, new1, new2}) Nenue@0: Nenue@0: Nenue@0: bindsCommitted = false Nenue@0: SetBinding(self.binding, self.command) Nenue@0: for level, profile in ipairs(priority) do Nenue@0: profile.bindings[self.binding] = (level == bindMode) and self.command or nil Nenue@0: end Nenue@0: print(BINDING_ASSIGNED:format(self.binding, self.command, BINDING_MODE[bindMode]:format(bindHeader))) Nenue@0: Nenue@0: kb.refresh(self) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: --- Resets button command Nenue@0: kb.release = function(self) Nenue@0: local index = self:GetID() Nenue@0: self.command = nil Nenue@0: self.actionName = nil Nenue@0: self.macro:SetText(nil) Nenue@0: self.profile = nil Nenue@0: self.bind:SetText(nil) Nenue@0: self.icon:SetTexture(nil) Nenue@0: self.border:SetColorTexture(unpack(BORDER_UNASSIGNED)) Nenue@0: self:EnableKeyboard(false) Nenue@0: self:SetScript('OnKeyDown', nil) Nenue@0: Nenue@0: Nenue@0: if profile.buttons[index] then Nenue@0: profile.buttons[index] = nil Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: -- Sets button command Nenue@0: Nenue@0: kb.assign = function(self, command, name, icon) Nenue@0: local index = self:GetID() Nenue@0: print('|cFF00FFFFassign|cFF0088FF', index, '|cFFFFFF00'.. (command or 'none'), '|cFF00FF00'.. (name or ''), '|cFF00FFFF' .. (icon or '')) Nenue@0: Nenue@0: Nenue@0: if command then Nenue@0: if command:match(COMMAND_SPELL) then Nenue@0: name = command:match(COMMAND_SPELL) Nenue@0: end Nenue@0: Nenue@0: Nenue@0: self:EnableKeyboard(true) Nenue@0: print('profile.buttons['..index..'] |cFF00FFFF=|r ', command, name, icon) Nenue@0: profile.buttons[index] = {command, name, icon} Nenue@0: Nenue@0: --- Clean up any residual buttons Nenue@0: local previous = profile.commands[command] Nenue@0: if previous ~= index and buttons[previous] then Nenue@0: kb.release(buttons[previous]) Nenue@0: end Nenue@0: Nenue@0: profile.commands[command] = index Nenue@0: end Nenue@0: Nenue@0: self.profile = bindMode Nenue@0: self.actionName = name Nenue@0: self.command = command Nenue@0: self.icon:SetTexture(icon) Nenue@0: self:RegisterForDrag('LeftButton') Nenue@0: end Nenue@0: Nenue@0: --- Retrieves button at index; creates said button and instates any stored parameters Nenue@0: kb.keyslot = function(index) Nenue@0: if not buttons[index] then Nenue@0: local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, kb, 'KeyButton') Nenue@0: button:SetScript('OnMouseDown', KeyButton.OnMouseDown) Nenue@0: button:SetScript('OnMouseUp', KeyButton.OnMouseUp) Nenue@0: button:SetScript('OnUpdate', KeyButton.OnUpdate) Nenue@0: button:SetScript('OnDragStart', KeyButton.OnDragStart) Nenue@0: button:SetScript('OnReceiveDrag', KeyButton.OnReceiveDrag) Nenue@0: button:SetID(index) Nenue@0: Nenue@0: if profile.buttons[index] and type(profile.buttons[index] ) == 'table' then Nenue@0: kb.assign(button, unpack(profile.buttons[index] )) Nenue@0: else Nenue@0: kb.release(button) Nenue@0: end Nenue@0: Nenue@0: local x, y = BUTTON_PADDING, - (BUTTON_PADDING + HEADER_OFFSET) Nenue@0: if index ~= 1 then Nenue@0: local col = mod(index, BINDS_PER_ROW) Nenue@0: if col == 0 then Nenue@0: col = BINDS_PER_ROW - 1 Nenue@0: else Nenue@0: col = col - 1 Nenue@0: end Nenue@0: x = col * (KEY_BUTTON_SIZE + BUTTON_SPACING) + BUTTON_PADDING Nenue@0: y = (ceil(index/ BINDS_PER_ROW)-1) * - (KEY_BUTTON_SIZE + BUTTON_SPACING) - BUTTON_PADDING - HEADER_OFFSET Nenue@0: end Nenue@0: button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE) Nenue@0: button:SetPoint('TOPLEFT', kb, 'TOPLEFT', x, y) Nenue@0: button:Show() Nenue@0: buttons[index] = button Nenue@0: end Nenue@0: return buttons[index] Nenue@0: end Nenue@0: Nenue@0: --- Updates profile assignment and button contents Nenue@0: kb.refresh = function(self) Nenue@0: if self.profile ~= bindMode then Nenue@0: if profile.buttons[self:GetID()] then Nenue@0: kb.assign(self, unpack(profile.buttons[self:GetID()])) Nenue@0: else Nenue@0: kb.release(self) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: if self.command then Nenue@0: if self.pending then Nenue@0: self.border:SetColorTexture(unpack(BORDER_PENDING)) Nenue@0: else Nenue@0: self.border:SetColorTexture(unpack(BORDER_ASSIGNED)) Nenue@0: end Nenue@0: --self.macro:SetText(self.actionName) Nenue@0: self.bind:SetText(BindingString(GetBindingKey(self.command))) Nenue@0: local locked, layer = CommandIsLocked(self) Nenue@0: self.icon:SetDesaturated(locked) Nenue@0: self.icon:SetVertexColor(unpack(BINDING_SCHEME_VERTEX[layer])) Nenue@0: else Nenue@0: self.border:SetColorTexture(unpack(BORDER_UNASSIGNED)) Nenue@0: --self.macro:SetText(nil) Nenue@0: self.bind:SetText(nil) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: local SetupUI = function() Nenue@0: Nenue@0: Nenue@0: kb.tabAnchor = {'TOPLEFT', kb, 'TOPRIGHT', 2, -TAB_OFFSET} Nenue@0: kb.tabGrowth = {'TOPLEFT', nil,'BOTTOMLEFT', 0, -TAB_SPACING} Nenue@0: kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT } Nenue@0: kb.UIPanelAnchor = {'TOPLEFT', kb, 'TOPLEFT', BUTTON_PADDING + 12, -BUTTON_PADDING} Nenue@0: kb.UIPanelGrowth = {'TOPLEFT', nil, 'TOPRIGHT', 14, 0 } Nenue@0: kb.controlsAnchor = {'BOTTOMLEFT', kb, BUTTON_PADDING, BUTTON_PADDING } Nenue@0: kb.controlsGrowth = {'BOTTOMLEFT', nil, 'BOTTOMRIGHT', BUTTON_SPACING, 0} Nenue@0: Nenue@0: --tab() frame, name, tooltip, texture, coords Nenue@0: kb:tab('KeyBinderGlobalTab', BINDING_MODE[1], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85}) Nenue@0: kb:tab('KeyBinderCharacterTab', characterHeader, nil) Nenue@0: kb:tab('KeyBinderSpecTab', specHeader, specTexture) Nenue@0: SetPortraitTexture(KeyBinderCharacterTab.icon, 'player') Nenue@0: KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85) Nenue@0: Nenue@0: saveButton = kb:button('KeyBinderSaveButton', 'Save', 'Commit all changes.', nil, kb.save) Nenue@0: restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', nil, kb.restore) Nenue@0: clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', nil, kb.ResetProfile) Nenue@0: Nenue@0: kb:uibutton( Nenue@0: 'KeyBinderSpellBookButton', 'SpellBook', nil, Nenue@0: function() ToggleSpellBook(BOOKTYPE_SPELL) end, Nenue@0: "Interface\\Spellbook\\Spellbook-Icon") Nenue@0: kb:uibutton( Nenue@0: 'KeyBinderTalentFrameButton', 'Talents', nil, Nenue@0: function() ToggleTalentFrame() end, Nenue@0: "Interface\\TargetingFrame\\UI-Classes-Circles", Nenue@0: CLASS_ICON_TCOORDS[strupper(select(2,UnitClass("player")))]) Nenue@0: Nenue@0: kb:uibutton( Nenue@0: 'KeyBinderMacroFrameButton', 'Macros', nil, Nenue@0: function() if MacroFrame then HideUIPanel(MacroFrame) else ShowMacroFrame() end end, Nenue@0: "Interface\\MacroFrame\\MacroFrame-Icon") Nenue@0: Nenue@0: kb:uibutton( Nenue@0: 'KeyBinderInventoryButton', 'Bags', nil, Nenue@0: function() OpenAllBags() end, Nenue@0: "Interface\\BUTTONS\\Button-Backpack-Up") Nenue@0: Nenue@0: kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING) Nenue@0: HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING Nenue@0: FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING Nenue@0: end Nenue@0: Nenue@0: --- Invokes the KeyBinder frame (from the /kb function or some other source) Nenue@0: kb.ui = function() Nenue@0: if not KT.db.bindMode then Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: if not kb:IsVisible() then Nenue@0: kb:Show() Nenue@0: KT.db.bindMode = true Nenue@0: end Nenue@0: Nenue@0: if not kb.loaded then Nenue@0: SetupUI() Nenue@0: kb.loaded = true Nenue@0: end Nenue@0: Nenue@0: for i = 1, numButtons do Nenue@0: kb.refresh(kb.keyslot(i)) Nenue@0: end Nenue@0: Nenue@0: if bindMode == BINDING_TYPE_SPECIALIZATION then Nenue@0: bindHeader = select(2,GetSpecializationInfo(GetSpecialization())) Nenue@0: elseif bindMode == BINDING_TYPE_CHARACTER then Nenue@0: bindHeader = UnitName('player') Nenue@0: else Nenue@0: bindHeader = '' Nenue@0: end Nenue@0: Nenue@0: if bindsCommitted then Nenue@0: KeyBinderSaveButton:Disable() Nenue@0: KeyBinderRestoreButton:Disable() Nenue@0: else Nenue@0: KeyBinderSaveButton:Enable() Nenue@0: KeyBinderRestoreButton:Enable() Nenue@0: end Nenue@0: Nenue@0: --- panel attributes Nenue@0: local numRows = numButtons/BINDS_PER_ROW Nenue@0: kb:SetHeight( numRows * (KEY_BUTTON_SIZE) + (numRows - 1) * BUTTON_SPACING + HEADER_OFFSET + FOOTER_OFFSET + BUTTON_PADDING * 2) Nenue@0: kb:SetWidth((BINDS_PER_ROW - 1) * BUTTON_SPACING + BINDS_PER_ROW * KEY_BUTTON_SIZE + BUTTON_PADDING * 2) Nenue@0: kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[bindMode])) Nenue@0: Nenue@0: Nenue@0: for i, tab in ipairs(kb.tabButtons) do Nenue@0: Nenue@0: local n = tab:GetNormalTexture() Nenue@0: local tabTexture = "Interface\\Buttons\\UI-Quickslot2" Nenue@0: local left, top, right, bottom = -12, 12, 13, -13 Nenue@0: if i == bindMode then Nenue@0: tabTexture = "Interface\\Buttons\\CheckButtonGlow" Nenue@0: left, top, right, bottom = -14, 14, 15, -15 Nenue@0: end Nenue@0: n:SetTexture(tabTexture) Nenue@0: n:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top) Nenue@0: n:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: kb.loadbinds = function (bindings) Nenue@0: for key, command in pairs(bindings) do Nenue@0: -- store for reversion Nenue@0: local oldAction = GetBindingAction(key) Nenue@0: if oldAction ~= command then Nenue@0: local bind1, bind2 = GetBindingKey(oldAction) Nenue@0: if bind1 and not reverts[bind1] then Nenue@0: reverts[bind1] = oldAction Nenue@0: end Nenue@0: if bind2 and not reverts[bind2] then Nenue@0: reverts[bind2] = oldAction Nenue@0: end Nenue@0: end Nenue@0: SetBindings(key, command) Nenue@0: end Nenue@0: SaveBindings() Nenue@0: end Nenue@0: Nenue@0: local ACTION_BARS = { Nenue@0: 'MultiBarBottomLeftButton', Nenue@0: 'MultiBarBottomRighttButton', Nenue@0: 'MultiBarLeftButton', Nenue@0: 'MultiBarRightButton', Nenue@0: 'ActionButton' Nenue@0: } Nenue@0: kb.HotKeyText = function () Nenue@0: for _, prefix in ipairs(ACTION_BARS) do Nenue@0: for i = 1,12 do Nenue@0: local button = _G[prefix .. i] Nenue@0: if button and button.action then Nenue@0: local type, id, subType, subID = GetActionInfo(button.action) Nenue@0: if type == 'spell' then Nenue@0: local name = GetSpellInfo(id) Nenue@0: local bind, bind2 = GetBindingKey('SPELL '..name) Nenue@0: if bind or bind2 then Nenue@0: --print('SPELL '..name, GetBindingKey('SPELL '..name)) Nenue@0: button.HotKey:SetText(BindingString(bind)) Nenue@0: button.HotKey:Show() Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: kb.InitProfile = function(profile) Nenue@0: profile.buttons = profile.buttons or {} Nenue@0: profile.commands = profile.commands or {} Nenue@0: profile.bindings = profile.bindings or {} Nenue@0: profile.macros = profile.macros or {} Nenue@0: return profile Nenue@0: end Nenue@0: kb.ResetProfile = function() Nenue@0: Nenue@0: for i, button in pairs(buttons) do Nenue@0: kb.release(button) Nenue@0: end Nenue@0: Nenue@0: profile.commands = {} Nenue@0: profile.bindings = {} Nenue@0: profile.macros = {} Nenue@0: end Nenue@0: Nenue@0: --- Gives us the profile structure to work with while instating data Nenue@0: kb.profile = function(name) Nenue@0: KT.db = KT.db or {} Nenue@0: global = kb.InitProfile(KT.db) Nenue@0: profile = global Nenue@0: local subtitle Nenue@0: if name then Nenue@0: KT.db[name] = KT.db[name] or {} Nenue@0: KT.db[name] = kb.InitProfile(KT.db[name]) Nenue@0: character = KT.db[name] Nenue@0: local spec = GetSpecialization() Nenue@0: if spec then Nenue@0: KT.db[name][spec] = KT.db[name][spec] or {} Nenue@0: profile = kb.InitProfile(KT.db[name][spec]) Nenue@0: bindMode = BINDING_TYPE_SPECIALIZATION Nenue@0: subtitle = select(2,GetSpecializationInfo(spec)) Nenue@0: specialization = KT.db[name][spec] Nenue@0: else Nenue@0: profile = kb.InitProfile(KT.db[name]) Nenue@0: bindMode = BINDING_TYPE_CHARACTER Nenue@0: subtitle = name Nenue@0: specialization = character Nenue@0: end Nenue@0: end Nenue@0: priority = {global, character, specialization } Nenue@0: Nenue@0: Nenue@0: Nenue@0: if not KT.db.bindsPage then Nenue@0: KT.db.bindsPage = bindMode Nenue@0: end Nenue@0: bindMode = KT.db.bindsPage Nenue@0: Nenue@0: Nenue@0: if not BINDING_MODE[bindMode] then Nenue@0: bindMode = 3 Nenue@0: KT.db.bindsPage = 3 Nenue@0: print('overriding', bindMode) Nenue@0: end Nenue@0: Nenue@0: profile = priority[bindMode] Nenue@0: Nenue@0: Nenue@0: local _ Nenue@0: _, specHeader, _, specTexture = GetSpecializationInfo(GetSpecialization()) Nenue@0: print(GetSpecializationInfo(GetSpecialization())) Nenue@0: specHeader = BINDING_MODE[2]:format(specHeader) Nenue@0: characterHeader = BINDING_MODE[2]:format(UnitName('player')) Nenue@0: Nenue@0: print('Using binding profile |cFF00FF88'..BINDING_MODE[bindMode]:format(subtitle)..'|r') Nenue@0: end Nenue@0: Nenue@0: kb.SelectTab = function(self) Nenue@0: bindMode = self:GetID() Nenue@0: profile = priority[self:GetID()] Nenue@0: KT.db.bindsPage = self:GetID() Nenue@0: kb.ui() Nenue@0: end Nenue@0: kb.save = function() Nenue@0: SaveBindings(GetCurrentBindingSet()) Nenue@0: bindsCommitted = true Nenue@0: for i, button in ipairs(buttons) do Nenue@0: button.pending = false Nenue@0: end Nenue@0: Nenue@0: kb.ui() Nenue@0: print('Bindings saved.') Nenue@0: end Nenue@0: kb.restore = function() Nenue@0: for i, button in pairs(buttons) do Nenue@0: button.pending = false Nenue@0: end Nenue@0: bindsCommitted = true Nenue@0: LoadBindings(GetCurrentBindingSet()) Nenue@0: print('All changes discarded.') Nenue@0: end Nenue@0: Nenue@0: --- Tells all the hud buttons what to do Nenue@0: kb.init = function() Nenue@0: KeyBinderMacro:SetAttribute('*type*', 'macro') Nenue@0: end Nenue@0: Nenue@0: --- Get started Nenue@0: kb.variables = function() Nenue@0: kb.profile(GetUnitName('player', true)) Nenue@0: for i = 1, 3 do Nenue@0: for attribute, data in pairs(priority[i].macros) do Nenue@0: KeyBinderMacro:SetAttribute(attribute, data[1]) Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: kb.UPDATE_BINDINGS() Nenue@0: kb:RegisterEvent('UPDATE_BINDINGS') Nenue@0: kb:RegisterEvent('UPDATE_MACROS') Nenue@0: kb:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED') Nenue@0: kb:RegisterEvent('PLAYER_EQUIPMENT_CHANGED') Nenue@0: kb:RegisterEvent('PLAYER_REGEN_DISABLED') Nenue@0: kb:RegisterEvent('PLAYER_REGEN_ENABLED') Nenue@0: kb:RegisterEvent('ACTIONBAR_SLOT_CHANGED') Nenue@0: end Nenue@0: Nenue@0: kb.close = function() Nenue@0: KT.db.bindMode = false Nenue@0: kb:Hide() Nenue@0: end Nenue@0: Nenue@0: kb.PLAYER_REGEN_DISABLED = function() Nenue@0: if KT.db.bindMode then Nenue@0: Nenue@0: kb:Hide() Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: kb.PLAYER_REGEN_ENABLED = function() Nenue@0: if KT.db.bindMode then Nenue@0: kb.ui() Nenue@0: end Nenue@0: end Nenue@0: --- Refresh buttons if macros are updated Nenue@0: kb.UPDATE_BINDINGS = function() Nenue@0: if KT.db.bindMode then Nenue@0: kb.ui() Nenue@0: end Nenue@0: kb.HotKeyText() Nenue@0: end Nenue@0: Nenue@0: kb.ACTIONBAR_SLOT_CHANGED = function() Nenue@0: kb.HotKeyText() Nenue@0: end Nenue@0: Nenue@0: Nenue@0: Nenue@0: kb.UPDATE_MACROS = kb.UPDATE_BINDINGS Nenue@0: Nenue@0: Nenue@0: SLASH_KB1 = "/kb" Nenue@0: SlashCmdList.KB = function() Nenue@0: if KT.db.bindMode then Nenue@0: KT.db.bindMode = false Nenue@0: print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.') Nenue@0: kb:Hide() Nenue@0: else Nenue@0: KT.db.bindMode = true Nenue@0: print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.') Nenue@0: kb.ui() Nenue@0: end Nenue@0: end