diff KeyButton.lua @ 70:131d9190db6b

Curseforge migration
author Nenue
date Wed, 28 Dec 2016 16:31:15 -0500
parents
children ca3118127e5e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KeyButton.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,710 @@
+-- SkeletonKey
+-- KeyButton.lua
+-- Created: 7/28/2016 11:26 PM
+-- %file-revision%
+-- Deals with display and manipulation of binding slots
+
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('SkeletonKey', ...) end or function() end
+local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end
+local L = kb.L
+local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs
+local _G, unpack, select, tostring = _G, unpack, select, tostring
+local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo
+local GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon = GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon
+local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
+local GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown, IsShiftKeyDown = GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown,IsShiftKeyDown
+local GetBindingKey, GetProfessionInfo = GetBindingKey, GetProfessionInfo
+local GetMountInfoByID, GetPetInfoByPetID = C_MountJournal.GetMountInfoByID, C_PetJournal.GetPetInfoByPetID
+local skb = SkeletonKeyButtonMixin
+local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION, CURSOR_TEXTURE
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
+local BORDER_UNASSIGNED = {0.6,0.6,0.6,1}
+local BORDER_ASSIGNED = {1,1,1,1}
+local BORDER_DYNAMIC = {1,1,0,1}
+local BORDER_PENDING = {1,0.5,0,1 }
+local BUTTON_HEADERS = {
+  ['spell'] = SPELLS,
+  ['macro'] = MACRO,
+  ['petaction'] = PET,
+  ['mount'] = MOUNT,
+  ['battlepet'] = BATTLEPET,
+
+
+  [5] = PROFESSIONS_FIRST_AID,
+  [7] = PROFESSIONS_COOKING,
+  [9] = PROFESSIONS_FISHING,
+  [10] = PROFESSIONS_ARCHAEOLOGY,
+
+}
+
+local PROFESSION_HEADERS = {
+  [1] = 'Profession 1',
+  [2] = 'Profession 2',
+  [3] = 10,
+  [4] = 7,
+  [5] = 9,
+  [6] = 5
+}
+
+
+-- This is needed to identify a spells that aren't reflected by GetCursorInfo()
+kb.OnPickupPetAction = function(slot, ...)
+  local isPickup = GetCursorInfo()
+  print(slot, ...)
+  if kb.PetCache.action[slot] then
+    if isPickup then
+      local key, _, texture = unpack(kb.PetCache.action[slot])
+      local spellName = _G[key] or key
+      if spellName and kb.PetCache.spellslot[spellName] then
+        CURSOR_SPELLSLOT = kb.PetCache.spellslot[spellName][1]
+        CURSOR_BOOKTYPE = BOOKTYPE_PET
+        CURSOR_TEXTURE = _G[texture] or texture
+      end
+    else
+      CURSOR_SPELLSLOT = nil
+      CURSOR_BOOKTYPE = nil
+      CURSOR_TEXTURE = nil
+    end
+    print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
+  end
+
+  local name, subtext, texture, isToken = GetPetActionInfo(slot)
+  if name then
+    kb.PetCache.action[slot] = {name, subtext, texture, isToken}
+  end
+
+
+  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
+
+end
+
+kb.OnPickupSpellBookItem = function(slot, bookType)
+  print('|cFFFF4400PickupSpellBookItem('.. tostring(slot).. ', '..tostring(bookType)..')')
+  CURSOR_SPELLSLOT = slot
+  CURSOR_BOOKTYPE = bookType
+  CURSOR_TEXTURE = GetSpellBookItemTexture(slot, bookType)
+  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
+end
+
+kb.CreateHooks = function()
+  hooksecurefunc("PickupSpellBookItem", kb.OnPickupSpellBookItem)
+  hooksecurefunc("PickupPetAction", kb.OnPickupPetAction)
+end
+
+
+
+function skb:OnLoad()
+  self:EnableKeyboard(false)
+  self:EnableMouse(true)
+  self:RegisterForDrag('LeftButton')
+  self:RegisterForClicks('AnyUp')
+end
+
+function skb:OnEnter()
+  if not self.command then
+    return
+  end
+  if self.statusText then
+    SkeletonKey.statustext:SetText(self.statusText .. ': '..self.actionName)
+    SkeletonKey.bindingstext:SetText(self.bindingText)
+  end
+
+
+  if kb.db.hoverInput and kb.saveTarget ~= self then
+    self:GetParent():ActivateSlot(self)
+    SkeletonKey:Update()
+  end
+end
+
+function skb:OnLeave()
+  if kb.db.hoverInput and kb.saveTarget == self then
+    self:GetParent():DeactivateSlot(self)
+    SkeletonKey:Update()
+  end
+end
+
+function skb:OnUpdate()
+end
+function skb:OnClick(click)
+  print(self:GetName(), 'OnMouseDown', click)
+  local cursorType = GetCursorInfo()
+  if click == 'LeftButton' then
+    if cursorType then
+      self:DropToSlot()
+    else
+      if self.command and self.isAvailable then
+        if IsShiftKeyDown() then
+          kb.db.stickyMode = true
+          KeyBinderStickyMode:SetChecked(true)
+        end
+        self:GetParent():ActivateSlot(self)
+      end
+    end
+  elseif click == 'RightButton' then
+    self:ReleaseSlot()
+  else
+    kb.ProcessInput(strupper(click))
+  end
+  SkeletonKey:Update()
+end
+
+function skb:OnDragStart()
+  self:PickupSlot()
+end
+
+function skb:OnReceiveDrag(...)
+  self:DropToSlot()
+end
+
+function skb:DropToSlot ()
+  print(self:GetName(),'|cFF0088FFreceived|r')
+  local actionType, actionID, subType, subData = GetCursorInfo()
+  print('GetCursorInfo', GetCursorInfo())
+  if actionType then
+
+    if actionType == 'flyout' then
+      ClearCursor()
+      ResetCursor()
+      return
+    end
+
+
+    local name, icon, _
+    local pickupID, pickupBook
+
+    if actionType == 'spell' then
+      actionID = subData
+      name, _, icon = GetSpellInfo(actionID)
+
+    elseif actionType == 'macro' then
+      name, icon = GetMacroInfo(actionID)
+    elseif actionType == 'petaction' then
+      if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then
+
+        local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
+        local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
+        if spellType == 'PETACTION' then
+          name = spellName
+          actionID = spellText
+          icon = CURSOR_TEXTURE
+        else
+          name, _, icon = GetSpellInfo(spellID)
+          actionID = spellID
+        end
+
+        pickupID = CURSOR_SPELLSLOT
+        pickupBook = CURSOR_BOOKTYPE
+      else
+
+
+      end
+
+    elseif actionType == 'mount' then
+      if subType == 0 then
+        name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
+        actionID = 0
+      else
+        name, _, icon = GetMountInfoByID(actionID)
+      end
+    elseif actionType == 'item' then
+      name = GetItemInfo(actionID)
+      icon = GetItemIcon(actionID)
+    elseif actionType == 'battlepet' then
+
+      local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = GetPetInfoByPetID(actionID)
+      name = customName or petName
+      icon = petIcon
+
+    end
+    local macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)
+    local slotInfo = {
+      command = command,
+      actionName = name,
+      iconPath = icon,
+      actionType = actionType,
+      actionID = actionID,
+      macroName = macroName,
+      macroText = macroText,
+      spellbookSlot = pickupID,
+      spellbookType = pickupBook,
+      assignedKeys = {GetBindingKey(command)}
+    }
+
+    local isAssigned, isBound, assignedBy, boundBy = kb.IsCommandBound(self, command)
+    if isAssigned then
+      local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
+      popup.slot = self
+      popup.text = "Currently assigned in |cFFFFFF00"..tostring(kb.configHeaders[assignedBy]).."|r. Are you sure?"
+      popup.oldProfile = assignedBy
+      popup.args = {slotInfo}
+      SkeletonKey:SetScript('OnMouseWheel', nil) -- disable scrolling
+      StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
+    else
+      kb.currentProfile.buttons[self:GetID()] = slotInfo
+      self:SetSlot(slotInfo)
+      self:UpdateSlot()
+      self.active = nil
+      ClearCursor()
+      ResetCursor()
+    end
+  end
+end
+
+
+do
+  local PickupAction = {
+    spell = _G.PickupSpell,
+    petaction =
+      function(...)
+        -- needs to be enclosed to acquire hooksecurefunc effects
+        _G.PickupSpellBookItem(...)
+      end,
+    macro = _G.PickupMacro,
+    item = _G.PickupItem,
+    mount = _G.C_MountJournal.Pickup
+  }
+  local GetPickupValue = {
+    spell = function(self) return select(7, GetSpellInfo(self.actionID)) end,
+    petaction = function(self) return self.pickupSlot, self.pickupBook end,
+  }
+  function skb:PickupSlot ()
+    if not (self.command and self.isAvailable) then
+      return
+    end
+    print(self.actionType)
+    if self.actionType == 'spell' then
+      -- It can't be picked up if SpellInfo(name) returns void
+      local dummy = GetSpellInfo(self.actionName)
+      if not dummy then
+        return
+      end
+    end
+    if PickupAction[self.actionType] then
+      if GetPickupValue[self.actionType] then
+        PickupAction[self.actionType](GetPickupValue[self.actionType](self))
+      else
+        PickupAction[self.actionType](self.actionID)
+      end
+      self:ReleaseSlot()
+      self:UpdateSlot()
+    end
+  end
+end
+
+
+
+
+--- Updates profile assignment and button contents
+function skb:UpdateSlot (force)
+  local slot = self:GetID()
+
+  if force then
+    if kb.currentProfile.buttons[slot] then
+      print('loading in', slot, kb.db.bindMode)
+      self:SetSlot(kb.currentProfile.buttons[slot])
+    else
+      self:ReleaseSlot()
+    end
+  end
+
+  local borderType = BORDER_UNASSIGNED
+
+  if self.command then
+
+    if not self.isAvailable then
+      borderType = BORDER_DYNAMIC
+      self.ignoreTexture:Show()
+    else
+      self.ignoreTexture:Hide()
+
+      if self.pending then
+        borderType = BORDER_PENDING
+      elseif self.dynamicType then
+        borderType = BORDER_DYNAMIC
+      else
+        borderType = BORDER_ASSIGNED
+      end
+    end
+
+
+    if self.actionType == 'macro' then
+      self.macro:Show()
+    else
+      self.macro:Hide()
+      if self.actionType == 'spell' then
+        local dummy = GetSpellInfo(self.actionName)
+        if not dummy then
+          self.icon:SetDesaturated(true)
+        else
+          self.icon:SetDesaturated(false)
+        end
+
+      end
+    end
+
+
+    if self.dynamicType == 'profession'  then
+      if self.isAvailable then
+
+        self.statusText = '|cFFFFFF00Profession|r'
+        self.bindingText = kb.BindingString(GetBindingKey(self.command))
+      else
+
+        self.statusText = '|cFFFF4400'..PROFESSION_HEADERS[self.dynamicIndex]..'|r'
+        self.actionName = '(#'..self.dynamicIndex..')'
+        self.bindingText ='?'
+      end
+    elseif self.dynamicType == 'talent' then
+
+      self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
+      if self.isAvailable then
+        self.bindingText = kb.BindingString(GetBindingKey(self.command))
+      else
+        if kb.TalentBindings[self.actionID] then
+          print(self.actionID, #kb.TalentBindings[self.actionID])
+          self.bindingText= kb.BindingString(unpack(kb.TalentBindings[self.actionID]))
+        end
+
+      end
+    elseif self.dynamicType == 'petaction' then
+      self.bindingText = kb.BindingString(GetBindingKey(self.command))
+    else
+      self.bindingText = kb.BindingString(GetBindingKey(self.command))
+    end
+
+    local locked, layer = kb.IsCommandBound(self)
+    if locked then
+      self.icon:SetAlpha(0.5)
+    else
+      self.icon:SetAlpha(1)
+    end
+
+
+    if self.actionType == 'spell' then
+      self.icon:SetTexture(GetSpellTexture(self.actionID))
+    end
+    print('|cFF00BBFFUpdateSlot|r:', '['..slot..'] =', self.command, self.bindingText, self.dynamicType, self.isAvailable, self.actionID)
+  else
+    if kb.saveTarget == self then
+      kb.DeactivateSlot(self)
+    end
+
+  end
+
+  self.ignoreTexture:SetShown(self.command and not self.isAvailable)
+
+  if not self.isAvailable then
+    self.bind:SetTextColor(0.7,0.7,0.7,1)
+  else
+    self.bind:SetTextColor(1,1,1,1)
+  end
+
+
+  if kb.saveTarget and kb.saveTarget ~= self then
+    self:SetAlpha(0.25)
+  else
+
+    self:SetAlpha(1)
+  end
+
+  --self.alert:SetShown(self.command and not self.isBound)
+
+  self.icon:SetTexture(self.iconPath)
+
+  self.border:SetColorTexture(unpack(borderType))
+  self.header:SetText(self.statusText)
+  self.bind:SetText(self.bindingText)
+  self.details:SetText(self.actionName)
+end
+
+--- Resets button command
+function skb:ReleaseSlot ()
+  local slot = self:GetID()
+
+
+  if kb.currentProfile.buttons[slot] then
+    kb.currentProfile.buttons[slot] = nil
+  end
+  if self.command then
+    kb.currentProfile.commands[self.command] = nil
+  end
+  local talentName = self.actionName
+  if self.actionType == 'macro' then
+    talentName = GetMacroSpell(self.actionID)
+  end
+    -- remove any matching talent data
+  if talentName and kb.currentProfile.talents[talentName] then
+    kb.currentProfile.talents[talentName] = nil
+  end
+  local droppedKeys = {}
+
+  -- doing removal in second loop to avoid possible iterator shenanigans
+  for k,v in pairs(kb.currentProfile.bindings) do
+    if v == self.command then
+      tinsert(droppedKeys, k)
+    end
+  end
+  if #droppedKeys >=1 then
+    for i, k in ipairs(droppedKeys) do
+      kb.currentProfile.bindings[k] = nil
+    end
+  end
+
+  self.isAvailable = nil
+  self.dynamicType = nil
+  self.bindingText = nil
+  self.statusText = nil
+  self.command = nil
+  self.iconPath = nil
+  self.actionType = nil
+  self.actionID = nil
+  self.actionName = nil
+  self.pickupSlot = nil
+  self.pickupBook = nil
+  self.macroName = nil
+  self.profile = nil
+  self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
+  self:EnableKeyboard(false)
+  self:SetScript('OnKeyDown', nil)
+  self.bindingText = nil
+  self.icon:SetTexture(nil)
+  self.ignoreTexture:Hide()
+
+end
+
+local spells = {}
+local SkeletonKey_GetGenericSpell = function(spellName, spellID, icon)
+  if not spells[spellID] then
+    spells[spellID] = {}
+    spells[spellID].actionType = 'spell'
+    spells[spellID].actionID = spellID
+    spells[spellID].actionName = spellName
+    spells[spellID].iconPath = icon
+    spells[spellID].statusText = '|cFFBBBBBBSpell|r'
+    spells[spellID].dynamicType = nil
+  end
+  return spells[spellID]
+end
+
+local tempInfo = {}
+-- tries to resolve spells from talent overrides/profession book/etc
+local dynamicTypes = {['profession'] = 'ProfessionCache', ['talent'] = 'TalentCache', ['petaction'] = 'PetInfoCache'}
+local SkeletonKey_GetSpellDetails = function(self)
+
+  local spellName, spellID, command, icon = self.actionName, self.actionID, self.command, self.iconPath
+
+
+  cprint(' In:', spellName, spellID, command)
+  cprint(GetSpellInfo(spellName or spellID))
+  local internalName, _, internalIcon, _, _, _, _ = GetSpellInfo(spellName or spellID)
+  local isAvailable = internalName and true
+
+  if internalName and (internalName ~= spellName) then
+    -- it's a binding for the originating spell, leave it as is
+    cprint('  |cFFFF4400spell is an override(', internalName, '~=', spellName,') leave the name info alone')
+    self.statusText = '|cFFFFFF00Spell|r'
+    self.isAvailable = true
+    return
+  end
+
+  -- let's us match spells replaced by talents
+  local info = kb.DynamicSpells[internalName or spellName]
+  if not info then
+    local dynamicType, dynamicIndex, dynamicSubIndex = command:match("(%a+)_(%S+)_(%S+)")
+    if kb.DynamicSpells[dynamicType] then
+      cprint('|cFFFF4400resolving dynamic type index:', internalName, spellName, command)
+      dynamicIndex = tonumber(dynamicIndex)
+      dynamicSubIndex = tonumber(dynamicSubIndex)
+      local cache = kb.DynamicSpells[dynamicType]
+      cprint('type:', dynamicType)
+      if dynamicIndex and cache[dynamicIndex] then
+        info = kb.DynamicSpells[dynamicType][dynamicIndex]
+        cprint('index:', dynamicIndex)
+        if dynamicSubIndex and info[dynamicSubIndex] then
+          info = info[dynamicSubIndex]
+          cprint('sub-index:', dynamicSubIndex)
+        end
+        isAvailable = true
+      end
+    end
+    if not info then
+      info = SkeletonKey_GetGenericSpell(spellName, spellID, internalIcon or icon)
+    end
+  end
+  info.isAvailable = isAvailable
+
+  cprint('|cFF00FF88SpellDetails:|r', info.actionName, info.actionID, info.dynamicType)
+  for k,v in pairs(info) do
+    cprint(' ',k,v)
+    self[k] = v
+  end
+
+  return info
+end
+
+--- Assigns the slot via table copy; any manipulations from this point are temporary and
+function skb:SetSlot(slotInfo)
+  print('slot info', self:GetID())
+
+  for k,v in pairs(slotInfo) do
+    print('  -', k, v)
+    self[k] = v
+  end
+  self.dynamicType = slotInfo.dynamicType
+  local command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook
+  = self.command, self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType
+
+
+  local slot = self:GetID()
+  local isBound = false
+  print('|cFFFFFF00SetSlot|r:', self:GetID())
+  if self.command then
+
+    isBound = kb.IsCommandBound(self, self.command)
+    if actionType == 'spell' then
+      local info = SkeletonKey_GetSpellDetails(self)
+      name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook = self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType
+
+    elseif actionType == 'petaction' then
+      self.dynamicType = 'petaction'
+      local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)')
+
+      if kb.PetCache.subtext[specialType] then
+
+        local info = kb.PetCache.subtext[specialType][tonumber(specialNum)]
+        if info then
+          print('***dynamic pet skill', specialType, specialNum)
+          --[[ i, spellName, subText, spellID,  texture, specialNum[subText ]]
+
+          for k,v in pairs(info) do
+            self[k] = v
+          end
+        end
+
+      end
+      self.statusText = 'Pet Action'
+      self.isAvailable = (kb.PetCache.spellslot[name])
+    elseif actionType == 'macro' then
+      if actionID then
+        -- look for corruption
+        local nameByID, _, bodyByID = GetMacroInfo(actionID)
+        local nameByName, _, bodyByName = GetMacroInfo(name)
+        if (nameByID ~= name) or (bodyByID ~= macroText) then
+          local prevIndex = actionID
+          actionID = GetMacroIndexByName(name)
+          local firstName, _, firstBody = GetMacroInfo(actionID)
+          if (firstName ~= name) or (firstBody ~= macroText) then
+            -- go even deeper
+            for i = 1, GetNumMacros() do
+              local searchName, _ , searchBody = GetMacroInfo(i)
+              if (searchName == name) and (searchBody == macroText) then
+                -- complete match
+                actionID = i
+                break
+              elseif (searchName == name) or (searchBody == macroText) then
+                -- partial match, continue the search
+                actionID = i
+              end
+            end
+          end
+          kb:print('Macro index changed: |cFFFFFF00', actionType, '|r', name, '(was '..tostring(prevIndex)..', now '..tostring(actionID)..')')
+        end
+      else
+        actionID = GetMacroIndexByName(name)
+      end
+      self.statusText = 'Macro'
+      self.isAvailable = true
+    else
+      if not actionID then
+        actionID = command:match("^KeyBinderMacro:(.+)")
+      end
+      self.isAvailable = true
+    end
+
+    if self.isAvailable then
+      local oldCommand = command
+      command = kb.LoadBinding(self)
+      if oldCommand ~= command then
+        print('|cFFFF4400fixing command string', actionType, actionID, name)
+        kb.currentProfile.bound[oldCommand] = nil
+        kb.currentProfile.bound[command] = slot
+        for k,v in pairs(kb.currentProfile.bindings) do
+          if v == oldCommand then
+            kb.currentProfile.bindings[k] = command
+          end
+        end
+      end
+    end
+
+
+    actionID = actionID or 0
+    self:EnableKeyboard(true)
+
+    -- this is done to keep legacy key-values from breaking algorithm assumptions
+    local slotInfo = {
+      command = command,
+      actionName = name,
+      iconPath = icon,
+      actionID = actionID,
+      actionType = actionType,
+      macroName = macroName,
+      macroText = macroText,
+      spellbookSlot = pickupSlot,
+      spellbookType = pickupBook,
+      assignedKeys = {GetBindingKey(command)}
+    }
+    kb.currentProfile.buttons[slot] = slotInfo
+
+    -- Clean up conflicting entries for loaded button
+    local previous = kb.currentProfile.commands[command]
+    if previous ~= slot and kb.buttons[previous] then
+      kb.ReleaseSlot(kb.buttons[previous])
+    end
+
+    local binds = {GetBindingKey(command) }
+    if self.isAvailable and (#binds >= 1) then
+      local found
+      for i, key in ipairs(binds) do
+        if not tContains(self.assignedKeys, key) then
+          tinsert(self.assignedKeys, key)
+          kb.currentProfile.bindings[key] = command
+          kb.currentProfile.bound[command] = true
+          found = true
+        end
+      end
+      if found then
+        kb:print('Recovered key binding for', name)
+      end
+    end
+
+    kb.currentProfile.commands[command] = slot
+  end
+
+
+
+  self.isBound = isBound
+  self.pickupSlot = pickupSlot
+  self.pickupBook = pickupBook
+  self.macroText = macroText
+  self.macroName = macroName
+  self.actionType = actionType
+  self.actionID = actionID
+  self.actionName = name
+  self.command = command
+  self.iconPath = icon
+  self.profile = kb.db.bindMode
+  self:RegisterForDrag('LeftButton')
+end
+
+kb.GetCommandAction = function(command)
+  for i, data in ipairs(kb.loadedProfiles) do
+    if data.commands[command] then
+      if data.buttons[data.commands[command]] then
+        local _, _, _, actionType, actionID = unpack(data.buttons[data.commands[command]])
+        return actionType, actionID
+      end
+    end
+  end
+end
+