diff SkeletonKey/KeyBinds.lua @ 0:69e828f4238a

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