# HG changeset patch
# User Nenue
# Date 1469575784 14400
# Node ID 9ac29fe7745547834816cd63a00cca95801db509
# Parent a30285f8191edae5c03281aafb9b0384fab287d6
- 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
diff -r a30285f8191e -r 9ac29fe77455 LibKraken/LibKraken-1.0.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LibKraken/LibKraken-1.0.lua Tue Jul 26 19:29:44 2016 -0400
@@ -0,0 +1,357 @@
+--[[
+-- KrakynTools
+-- AddOn prototyping library.
+--
+-- Implements
+-- KT.register(frame) to hook the following (all optional):
+-- frame:init() run immediately after KT sets itself up
+-- frame:profile("Name-TruncatedRealm") called the first time SavedVars data becomes available
+-- frame:variables() called upon variables being available
+-- frame:event(event, ...) replaces the event callback
+-- frame:ui() called by /ui when activating
+--
+-- frame:tab(name, tooltip, texture, coord)
+-- produces a serial button that changes display tabs
+--
+-- frame:button(name, text, tooltip, onClick)
+-- produces a button with OnClick script
+--
+-- frame:uibutton(name, text, tooltip, onClick, texture, coord)
+-- produces a header button with desired onClick
+--
+]]--
+
+local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 1
+local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR)
+
+--GLOBALS: LibKT, KrakTool, KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
+local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
+local print, max, unpack, tinsert = print, max, unpack, tinsert
+local ipairs, xpcall, next, safecall = ipairs, xpcall, next, safecall
+local UI_TOGGLE = false
+local db
+
+
+KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
+KT.addons = {}
+KT.initStack = {}
+KT.varsStack = {}
+local print = DEVIAN_WORKSPACE and function(...) print('LKT', ...) end or function() end
+local registeredHandles = {}
+
+--- /rl
+-- ReloadUI shortcut
+SLASH_RL1 = "/rl"
+SlashCmdList.RL = function ()
+ ReloadUI()
+end
+
+--- /kv addon ...
+-- Dumps table values from "addon", using the arguments as index values.
+-- Numerics are converted, and names that end with "()" will be called as such
+SLASH_KV1 = "/kv"
+SlashCmdList.KV = function (editbox, input)
+
+end
+
+
+--- /ui
+-- Run any addon:ui() methods
+SLASH_UI1 = "/ui"
+SlashCmdList.UI = function ()
+ if UI_TOGGLE then
+ UI_TOGGLE = false
+ else
+ UI_TOGGLE = true
+ end
+ for i, frame in pairs(KT.frames) do
+ if UI_TOGGLE then
+ if frame.close then
+ frame.close()
+ else
+ frame:Hide()
+ end
+ else
+ if frame.ui then
+ frame.ui()
+ end
+ frame:Show()
+ end
+ end
+end
+
+LibKTError = function(msg)
+ local dstack = debugstack(2)
+ :gsub("Interface\\AddOns\\",'')
+ :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)
+
+
+
+ KTErrorFrame.errmsg:SetText(msg)
+ KTErrorFrame.debugstack:SetText(dstack)
+ KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
+ KTErrorFrame:Show()
+end
+
+
+local pending = {}
+local processing = false
+local isHandled = false
+local nodebug = false
+function KT.OnEvent (addon, event, ...)
+ if processing then
+ local args = {...}
+ C_Timer.After(0, function() KT.OnEvent(addon, event, unpack(args)) end)
+ return
+ else
+
+ end
+ --- reset state
+ processing = true
+ isHandled = false
+ nodebug = false
+
+
+ if addon.event then
+ nodebug = addon.event(addon, event, ...)
+ end
+
+ if addon[event] then
+ nodebug = addon[event](addon, event, ...) or nodebug
+ addon.missed = 0
+ addon.handled = addon.handled + 1
+ isHandled = true
+ else
+ addon.firstEvent = false
+ addon.unhandled = addon.unhandled + 1
+ addon.missed = addon.missed + 1
+ end
+
+ for i, module in ipairs(addon.modules) do
+ --print(i, module)
+ if module[event] then
+ nodebug = module[event](addon, event, ...) or nodebug
+ addon.missed = 0
+ addon.handled = addon.handled + 1
+ isHandled = true
+ else
+ addon.firstEvent = false
+ addon.unhandled = addon.unhandled + 1
+ addon.missed = addon.missed + 1
+ end
+
+ end
+ if nodebug then
+ processing = false
+ return
+ else
+ KT.UpdateEventStatus(addon, event, ...)
+ processing = false
+ end
+
+end
+
+KT.UpdateEventStatus = function(addon, event, ...)
+ print(addon:GetName(), event, ...)
+
+ -- debug outputs
+ if addon.status then
+ addon.status:SetText(event .. '\n|cFF00FF00' .. addon.handled .. '|r |cFFFF8800' .. addon.missed .. '|r |cFFFF4400' .. addon.unhandled .. '|r')
+ if isHandled then
+ addon.status:SetTextColor(0,1,0)
+ if addon.log then
+ local logtext = event
+ for i = 1, select('#',...) do
+ logtext = logtext .. '\n' .. i .. ':' .. tostring(select(i,...))
+ end
+ addon.log:SetText('|cFFFFFF00last|r\n' .. logtext)
+ local newWidth = addon.log:GetStringWidth()
+
+ if addon.logfirst then
+ if not addon.firstEvent then
+ addon.firstEvent = event
+ addon.logfirst:SetText('|cFF00FF88first|r\n' .. logtext)
+ end
+
+ newWidth = newWidth + addon.logfirst:GetStringWidth()
+ end
+ if addon.logdiff then
+ if not event ~= addon.firstEvent then
+ addon.firstEvent = event
+ addon.logdiff:SetText('|cFF0088FFdiff|r\n' .. logtext)
+ end
+ newWidth = newWidth + addon.logdiff:GetStringWidth()
+ end
+ --addon:SetWidth(newWidth)
+ end
+ else
+ addon.status:SetTextColor(1,0,0)
+ end
+ end
+end
+
+KT.register = function(addon, name, noGUI)
+ if registeredHandles[addon] then
+ name = name or debugstack(2,1,0):gsub("\\n.+", ""):gsub("^Interface\\AddOns\\", ""):gsub("%s+$", "")
+ else
+ if not name then
+ assert(type(addon) == 'table', 'Need a valid table.')
+ if addon.GetName then
+ name = addon:GetName()
+ else
+ name = debugstack(2,1,0):gsub("\\n.+", ""):gsub("^Interface\\AddOns\\", ""):gsub("%s+$", "")
+ end
+ assert(type(name) == 'string', 'Unable to resolve a valid stub name.')
+ end
+ -- if calling again, assume name is a file handle
+
+ registeredHandles[addon] = name
+ KT.addons[name] = addon
+ if addon.SetScript then
+ addon:SetScript('OnEvent', KT.OnEvent)
+ end
+ addon.unhandled = 0
+ addon.missed = 0
+ addon.handled = 0
+ addon.firstEvent = false
+ addon.modules = {}
+ tinsert(KT.initStack, addon)
+ tinsert(KT.varsStack, addon)
+
+ if addon.GetName and (not noGUI) then
+ addon.UIPanelAnchor = {'TOPLEFT', addon, 'TOPLEFT', 12, -12 }
+ addon.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
+ addon.button = KT.button
+ addon.uibutton = KT.uibutton
+ addon.tab = KT.tab
+ addon.print = KT.print
+ end
+ end
+
+ return addon, (DEVIAN_WORKSPACE and function(...) _G.print(name, ...) end or function() end)
+end
+
+
+
+local onEvent = function(self, event, arg1)
+ if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
+ -- run any init blocks left in the queue
+ while #KT.initStack >= 1 do
+ local addon = tremove(KT.initStack, 1)
+ print('KT', addon:GetName(), 'init')
+ if addon.init then
+ xpcall(addon.init, LibKTError)
+ for i, module in ipairs(addon.modules) do
+ if module.init then
+ xpcall(module.init, LibKTError)
+ end
+ end
+ end
+ end
+
+ -- run any variables blocks if player variables are ready
+ if IsLoggedIn() and #KT.varsStack >= 1 then
+ while #KT.varsStack >= 1 do
+ local addon = tremove(KT.varsStack, 1)
+ print(addon:GetName())
+ if addon.variables then
+ xpcall(addon.variables, LibKTError)
+ for i, module in ipairs(addon.modules) do
+ if module.variables then
+ xpcall(module.variables, LibKTError)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+KT.print = function(module, ...)
+ local msg = '|cFF00FFFF'..module:GetName()..'|r:'
+ for i = 1, select('#', ...) do
+ msg = msg .. ' ' .. tostring(select(i, ...))
+ end
+ DEFAULT_CHAT_FRAME:AddMessage(msg)
+end
+
+--- Button generators
+
+local GetButtonTemplate = function(name, parent, template, onClick)
+ if _G[name] then
+ return _G[name]
+ end
+
+ local button = CreateFrame('Button', name, parent, template)
+ button:RegisterForClicks('AnyUp')
+ button:SetScript('OnClick', onClick)
+ return button
+end
+
+local SetButtonAnchor = function(self, collector, anchor, growth)
+ if self:GetID() == 0 then
+ self:SetID(#collector)
+ print('registered TabButton #', self:GetID())
+ end
+
+ if self:GetID() == 1 then
+ self:SetPoint(unpack(anchor))
+ else
+ growth[2] = collector[self:GetID()-1]
+ self:SetPoint(unpack(growth))
+ end
+end
+
+KT.tab = function(self, name, tooltip, texture, coords)
+ local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab)
+ button.icon:SetTexture(texture)
+ button.tooltip = tooltip
+ button:SetSize(unpack(self.tabSize))
+ if coords then
+ button.icon:SetTexCoord(unpack(coords))
+ end
+ SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
+ return button
+end
+
+KT.button = function(self, name, text, tooltip, onClick)
+ local button = GetButtonTemplate(name, self, 'KTButton', onClick)
+
+ button.tooltip = tooltip
+ button:SetText(text)
+ button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
+
+ SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
+ return button
+end
+
+KT.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
+ local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
+
+ button.tooltip = tooltip
+ button:SetText(text)
+
+ if self.UIPanelIcon then
+ local w, h, anchor, x, y = unpack(self.UIPanelIcon)
+ button.icon:SetTexture(texture)
+ button.icon:SetSize(w, h)
+ button.icon:ClearAllPoints()
+ button.icon:SetPoint(anchor, button, anchor, x, y)
+ end
+
+ if not self.UIPanelSize then
+ button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
+ else
+ button:SetSize(unpack(self.UIPanelSize))
+ end
+ if coords then
+ button.icon:SetTexCoord(unpack(coords))
+ end
+ SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
+ return button
+end
+
+
+KT.handler:RegisterEvent('ADDON_LOADED')
+KT.handler:RegisterEvent('PLAYER_LOGIN')
+KT.handler:SetScript('OnEvent', onEvent)
\ No newline at end of file
diff -r a30285f8191e -r 9ac29fe77455 LibKraken/LibKraken-1.0.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LibKraken/LibKraken-1.0.xml Tue Jul 26 19:29:44 2016 -0400
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ self:RegisterForDrag('LeftButton')
+
+
+ self:StartMoving()
+
+
+ self:StopMovingOrSizing()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r a30285f8191e -r 9ac29fe77455 LibKraken/LibKraken.iml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LibKraken/LibKraken.iml Tue Jul 26 19:29:44 2016 -0400
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r a30285f8191e -r 9ac29fe77455 LibKraken/LibKraken.toc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LibKraken/LibKraken.toc Tue Jul 26 19:29:44 2016 -0400
@@ -0,0 +1,12 @@
+## Interface: 60200
+## Title: !LibKraken
+## Notes: Addon framework for dinosaurs
+## Author: Krakyn
+## Version: 1.0-@project-revision@
+## X-Category: Interface Enhancements
+## DefaultState: Enabled
+## LoadOnDemand: 0
+## OptionalDeps: Ace3, LibStub
+
+LibStub\LibStub.lua
+LibKraken-1.0.xml
diff -r a30285f8191e -r 9ac29fe77455 LibKraken/LibStub/LibStub.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LibKraken/LibStub/LibStub.lua Tue Jul 26 19:29:44 2016 -0400
@@ -0,0 +1,30 @@
+-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+ LibStub = LibStub or {libs = {}, minors = {} }
+ _G[LIBSTUB_MAJOR] = LibStub
+ LibStub.minor = LIBSTUB_MINOR
+
+ function LibStub:NewLibrary(major, minor)
+ assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+ minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+ local oldminor = self.minors[major]
+ if oldminor and oldminor >= minor then return nil end
+ self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+ return self.libs[major], oldminor
+ end
+
+ function LibStub:GetLibrary(major, silent)
+ if not self.libs[major] and not silent then
+ error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
+ end
+ return self.libs[major], self.minors[major]
+ end
+
+ function LibStub:IterateLibraries() return pairs(self.libs) end
+ setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff -r a30285f8191e -r 9ac29fe77455 SkeletonKey/KeyBinds.lua
--- a/SkeletonKey/KeyBinds.lua Tue Jun 21 11:56:14 2016 -0400
+++ b/SkeletonKey/KeyBinds.lua Tue Jul 26 19:29:44 2016 -0400
@@ -1,126 +1,201 @@
--------------------------------------------
--- KrakTool
--- Nick
+-- SkeletonKey
+-- Krakyn-Mal'Ganis
-- @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()
+-- .StoreBinding(button, key) bind current keystroke to command
+-- .GetSlot(index) return display slot
+-- .SetSlot(button, command, name, icon) assign display slot
+-- .ReleaseSlot(button) clear button command
+-- .UpdateSlot(button) update button contents
+-- .SelectProfile(name) set profile character
+-- .ApplyBindings(bindings) walk table with SetBinding()
-local KT = LibKT.register(KeyBinder)
-local kb = KeyBinder
+local _
+local kb, print = LibStub("LibKraken").register(KeyBinder)
local db
+local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg', ...) end or function() end
+--- Caps Lock literals
+local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:"
+local BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
+local BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
+local BINDING_FAILED_PROTECTED = '|cFFFF4400Unable to use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400)|r'
+local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"
+local FOOTER_OFFSET
+local HEADER_OFFSET
+local HELP_1 = "Drag and drop spells/items from your inventory, spellbook, or collections panels."
+local HELP_2 = "While the cursor is above an icon, up to two key combinations will be bound to that action."
+local HELP_3 = "If that key used for a client binding (e.g. game menu), a confirmation popup will appear before making the change."
+local BINDS_PER_ROW = 2
+local BUTTON_HSPACING = 128
+local BUTTON_SPACING = 4
+local BUTTON_PADDING = 12
+local BINDING_TYPE_SPECIALIZATION = 3
+local BINDING_TYPE_CHARACTER = 2
+local BINDING_TYPE_GLOBAL = 1
+local KEY_BUTTON_SIZE = 48
local MIN_BIND_SLOTS = 32
-local BINDS_PER_ROW = 8
-local KEY_BUTTON_SIZE = 40
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
local TAB_OFFSET = 12
local TAB_HEIGHT = 40
local TAB_SPACING = 2
-local BUTTON_SPACING = 4
-local BUTTON_PADDING = 12
-local HEADER_OFFSET, 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 BORDER_UNASSIGNED = {0.2,0.2,0.2,1 }
+local BORDER_ASSIGNED = {0.5,0.5,0.5,1 }
+local BORDER_DYNAMIC = {1,1,0,1}
+local BORDER_PENDING = {1,0.5,0,1 }
+local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION
+
+
+--- Caps Lock derivatives
+local ACTION_SCRIPT = {
+ ['mount'] = "/script C_MountJournal.SummonByID(%d)",
+ ['macro'] = "%s",
+ ['equipset'] = "/script UseEquipmentSet(%d)",
+ ['spell'] = "/cast %s",
+ ['petaction'] = "/cast %s",
+ ['battlepet'] = SLASH_SUMMON_BATTLE_PET1 .. " %s",
+ ['item'] = "/use %s"
+}
+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 professionMappings = {
+ [5] = 3,
+ [7] = 4,
+ [9] = 5,
+ [10] = 6
+}
+
local BINDING_MODE = {
- [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s',
+ [BINDING_TYPE_GLOBAL] = 'Global Binds',
[BINDING_TYPE_CHARACTER] = 'Character: %s',
- [BINDING_TYPE_GLOBAL] = 'Global Binds'
+ [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s'
}
+local BINDING_DESCRIPTION = {
+
+ [BINDING_TYPE_GLOBAL] = 'The bindings are applied globally.',
+ [BINDING_TYPE_CHARACTER] = 'Applied when you log onto this character.',
+ [BINDING_TYPE_SPECIALIZATION] = 'Applied when you log onto this character and are that specialization.',
+}
+
local BINDING_SCHEME_COLOR = {
- [BINDING_TYPE_SPECIALIZATION] = {0,0,0,0.5},
+ [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5},
[BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5},
- [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5}
+ [BINDING_TYPE_SPECIALIZATION] = {.25,0,0,0.5},
}
local BINDING_SCHEME_VERTEX = {
-
+ [BINDING_TYPE_GLOBAL] = {0,.5,1,1},
+ [BINDING_TYPE_CHARACTER] = {0,1,0,1},
[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_SPECIALIZATION] = {0, 1, 1},
[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
-local bindHeader = ''
-local specHeader, specTexture, characterHeader = 'SPEC_NAME', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
-local numButtons = BINDS_PER_ROW * 4
-local bindsCommitted = true
+local loadedProfiles = {}
+-- Profiles ordered by precedance
+local priority = {}
+-- Button pointers
+local buttons = {}
+-- Backlog of changes
+local reverts = {}
+-- macro buttons used for mounts and other buttonable non-spells
+local macros = {}
+-- currently active non-blizzard keybinds
+local bindings = {}
+-- unselected talents
+local talentBindings = {}
+kb.inactiveTalentBindings = {}
+-- placeholder for the StaticPopup used for confirmations
+local confirmation
+-- header text
+local configHeaders = {}
-local profile, character, specialization, global
-local priority = {}
-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,
}
+
+--- Used to reflect the current working state
+local bindMode = 3
+local bindHeader, currentHeader = '', ''
+local configProfile, character, specialization, global, character_specialization
+local specID, specGlobalID, specName, specDesc, specTexture, characterHeader = 0, 0, 'SPEC_NAME', 'SPEC_DESCRIPTION', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
+local classHeader, className, classID = '', '', 0
+local numButtons = BINDS_PER_ROW * 8
+local bindsCommitted = true
+local forceButtonUpdate = false
+
+--- Control handles
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
+--- Cursor "pickup" actuators
+local PickupAction = {}
+PickupAction.spell = _G.PickupSpell
+PickupAction.macro = _G.PickupMacro
+PickupAction.item = _G.PickupItem
+PickupAction.mount = _G.C_MountJournal.Pickup
+local GetPickupValue = {}
+GetPickupValue.spell = function(self) return select(7, GetSpellInfo(self.actionID)) end
+
+--- Returns conflicting assignment and binding profiles for use in displaying confirmations
+local IsCommandBound = function(self, command)
+ local isAssigned, assignedBy = false, bindMode
+ local isBound, boundBy = false, bindMode
+
+
+ command = command or self.command
+ for i = 1, #BINDING_MODE 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
+ if i ~= bindMode then
+
+ if tier.commands[command] then
+ isAssigned = true
+ assignedBy = i
+ end
+ if tier.bound[command] then
+ isBound = true
+ boundBy = i
+ end
+
+
+ --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])
+
+ if isAssigned and isBound then
break
end
end
+
end
- return desaturated, layer
+
+ print('|cFFFFFF00IsCommandBound:|r', command:gsub(CLICK_KEYBINDER_MACRO, ''),'|r [profile:', bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
+ return isAssigned, isBound, assignedBy, boundBy
end
+local talentSpellHardCodes = {
+ [109248] = 'Binding Shot',
+}
+
--- Returns a value for use with Texture:SetDesaturated()
local BindingIsLocked = function(key)
local success = false
@@ -139,7 +214,7 @@
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')
+ stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
end
if #stack >= 1 then
@@ -149,131 +224,387 @@
end
end
---- This keeps our KeyDown handler from getting stuck with game controls
-KeyButton.OnUpdate = function(self)
+local restingAlpha = 0.7
+local fadeTime, fadeDelay = .30, 0.15
+
+local portraitLayers = {}
+kb.UpdatePortraits = function()
+ for i, layeredRegion in ipairs(portraitLayers) do
+ SetPortraitTexture(layeredRegion , 'player')
+ end
+end
+
+
+
+local KeyButton_OnKeyDown = function(self, key)
+ kb.StoreBinding(self, key)
+end
+local KeyButton_OnClick = function(self, click)
+ print(self:GetName(), 'OnMouseDown', click)
+ if click == 'LeftButton' then
+ kb.DropToSlot(self)
+ elseif click == 'RightButton' then
+ kb.ReleaseSlot(self)
+ else
+ kb.StoreBinding(self, click:upper())
+ end
+end
+
+local KeyButton_OnDragStart = function(self)
+ kb.PickupSlot(self)
+end
+
+local KeyButton_OnReceiveDrag = function(self, ...)
+ kb.DropToSlot(self)
+end
+
+
+local KeyBinder_OnUpdate = function(self, elapsed)
+ self.elapsed = self.elapsed + elapsed
+ self.throttle = self.throttle + elapsed
+
+ if (self.throttle >= 0.032) then
+ self.throttle = 0
+ else
+ return
+ end
+
+ local progress = 1
+ if self.elapsed > fadeTime then
+ self.elapsed = 0
+ self.fadeStep = 0
+ --self.statustext:SetText(nil)
+ --self.bindingstext:SetText(nil)
+ self:SetScript('OnUpdate', nil)
+ else
+ if self.elapsed < fadeDelay then
+ progress = 0
+ else
+ self.fadeStep = self.fadeStep + 1
+ progress = (self.elapsed - fadeDelay) /(fadeTime - fadeDelay)
+ end
+ --print(self.fadeStep, format('%.02f/%.02f', (self.elapsed - fadeDelay) ,(fadeTime - fadeDelay)) , progress)
+ end
+
+ local alpha = 1 - progress * (1- restingAlpha)
+ self.statustext:SetAlpha(alpha)
+ self.bindingstext:SetAlpha(alpha)
+end
+
+local KeyButton_OnUpdate = function(self)
if not self.command then
return
end
if self:IsMouseOver() then
+ kb.elapsed = 0
if not self.active then
-- only set this handler when the button is activated/mouseOver
self.active = true
- self:SetScript('OnKeyDown', kb.bind)
+ self:SetScript('OnKeyDown', KeyButton_OnKeyDown)
- local bindText = self.command
- if self.bind:GetText() then
- bindText = bindText .. ': |cFF00FF00' .. self.bind:GetText()
- end
+ kb.statustext:SetText(self.statusText .. ': '..self.actionName)
+ kb.bindingstext:SetText(self.bindingText)
+ kb.fadeStep = 0
+ kb.throttle = 0
+ kb:SetScript('OnUpdate', KeyBinder_OnUpdate)
- 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.
+local KeyBinder_OnMouseWheel = function(self, delta)
+ print(self, delta, self.scrollOffset, (self.scrollOffset <= 0))
-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)
+
+ if IsControlKeyDown() then
+ KEY_BUTTON_SIZE = KEY_BUTTON_SIZE - delta
+ else
+
+
+ if (delta > 0) and (self.scrollOffset <= 0) then
+ return
+ elseif delta < 0 and kb.scrollOffset >= 42 then
+ return
+ end
+ kb.scrollOffset = ceil(kb.scrollOffset - (delta * BINDS_PER_ROW))
+ end
+
+ kb.ui(true)
+end
+
+local KeyBinder_OnHide = function()
+ KeyBinderImportLog:Hide()
+end
+
+local CloseButton_OnClick = function()
+ db.showUI = false
+ kb:Hide()
+end
+local CancelButton_OnClick = function()
+ kb.RevertBindings()
+end
+local SaveButton_OnClick = function()
+ kb.ConfirmBindings()
+end
+
+local KeyBinder_Initialize = function()
+ for i = 1, GetNumBindings() do
+ local command = GetBinding(i)
+ bindings[command] = true
+ end
+
+ kb.scrollOffset = 0
+ kb.tabAnchor = {'TOPLEFT', kb.profilebg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING}
+ kb.tabGrowth = {'TOPLEFT', nil,'TOPRIGHT', BUTTON_SPACING, 0}
+ kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT }
+ kb.UIPanelAnchor = {'TOPLEFT', kb.sourcesbg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING}
+ kb.UIPanelGrowth = {'TOPLEFT', nil, 'BOTTOMLEFT', 0, -2 }
+ kb.UIPanelSize = {84, 32 }
+ kb.UIPanelIcon = {24, 32, 'LEFT', -12, 0}
+ kb.controlsAnchor = {'BOTTOMLEFT', kb.footer, BUTTON_PADDING, BUTTON_PADDING }
+ kb.controlsGrowth = {'BOTTOMLEFT', nil, 'BOTTOMRIGHT', BUTTON_SPACING, 0}
+
+ -- order of these is important
+ kb:tab('KeyBinderGlobalTab',
+ BINDING_MODE[BINDING_TYPE_GLOBAL] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_GLOBAL], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85})
+ kb:tab('KeyBinderCharacterTab',
+ configHeaders[BINDING_TYPE_CHARACTER] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_CHARACTER], nil)
+ kb:tab('KeyBinderSpecTab',
+ configHeaders[BINDING_TYPE_SPECIALIZATION] .. '\n' .. BINDING_DESCRIPTION[BINDING_TYPE_SPECIALIZATION], specTexture)
+ KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85)
+
+
+
+ portraitLayers[1] = KeyBinderCharacterTab.icon
+
+ saveButton = kb:button('KeyBinderSaveButton', 'Save', 'Commit all changes.', SaveButton_OnClick)
+ --restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', CancelButton_OnClick)
+ --clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', ResetButton_OnClick)
+
+ kb:uibutton(
+ 'KeyBinderSpellBookButton', 'SpellBook', nil,
+ function() ToggleSpellBook(BOOKTYPE_SPELL) end,
+ "Interface\\BUTTONS\\UI-MicroButton-Spellbook-Up", {0, 1, .4, 1})
+ kb:uibutton(
+ 'KeyBinderTalentFrameButton', TALENTS, SPECIALIZATION,
+ function() ToggleTalentFrame() end,
+ "Interface\\BUTTONS\\UI-MicroButton-Talents-Up", {0, 1, .4, 1})
+
+ kb:uibutton(
+ 'KeyBinderMacroFrameButton', 'Macros', nil,
+ function() if MacroFrame and MacroFrame:IsVisible() then
+ HideUIPanel(MacroFrame)
+ else
+ ShowMacroFrame() end
+ end,
+ "Interface\\BUTTONS\\UI-MicroButton-Help-Up", {0, 1, .4, 1})
+
+ kb:uibutton(
+ 'KeyBinderInventoryButton', 'Bags', nil,
+ function() OpenAllBags() end,
+ "Interface\\BUTTONS\\UI-MicroButtonCharacter-Up", {0, 1, .4, 1})
+
+
+
+ kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING)
+ HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING
+ + kb.info:GetHeight()
+ FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING
+
+ kb:SetScript('OnHide', KeyBinder_OnHide)
+ kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel)
+ kb.CloseButton:SetScript('OnClick', CloseButton_OnClick)
+
+ kb.UpdatePortraits()
+end
+
+kb.DropToSlot = function(self)
+
+ 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 macroName, macroText
+ local command, name, icon, _
+ local pickupID, pickupBook
+
+ if actionType == 'spell' then
+ actionID = subData
+ name, _, icon = GetSpellInfo(actionID)
+
+ elseif actionType == 'macro' then
+ name, icon = GetMacroInfo(actionID)
+ actionID = name
+ elseif actionType == 'petaction' then
+ if not (CURSOR_SPELLSLOT and CURSOR_BOOKTYPE) then
+
+ ClearCursor()
+ ResetCursor()
end
- pickup(value)
- kb.release(self)
- break
+ local bookType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
+ pickupID = CURSOR_SPELLSLOT
+ pickupBook = CURSOR_BOOKTYPE
+ name, _, icon = GetSpellInfo(spellID)
+ actionID = name
+
+ elseif actionType == 'mount' then
+ if subType == 0 then
+ name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
+ actionID = 0
+ else
+ name, _, icon = C_MountJournal.GetMountInfoByID(actionID)
+ end
+ elseif actionType == 'item' then
+ name = GetItemInfo(actionID)
+ icon = GetItemIcon(actionID)
+ actionID = name
+ elseif actionType == 'battlepet' then
+
+ local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = C_PetJournal.GetPetInfoByPetID(detail);
+ name = customName or petName
+ icon = petIcon
+
+ end
+ macroName, macroText, command = kb.RegisterAction(actionType, actionID)
+
+
+ local isAssigned, isBound, assignedBy, boundBy = IsCommandBound(self, command)
+ if isAssigned then
+ local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
+ popup.slot = self
+ popup.text = "Currently assigned in |cFFFFFF00"..tostring(configHeaders[assignedBy]).."|r. Are you sure?"
+ popup.oldProfile = assignedBy
+ popup.args = {command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook }
+ kb:SetScript('OnMouseWheel', nil) -- disable scrolling
+ StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
+ else
+ kb.SetSlot(self, command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook)
+ kb.UpdateSlot(self)
+ self.active = nil
+ KeyButton_OnUpdate(self, 0)
+ ClearCursor()
+ ResetCursor()
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)
+kb.PickupSlot = 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
+ 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
-
- 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
+ elseif self.actionType == 'petaction' then
+ PickupSpellBookItem(self.pickupSlot, self.pickupBook)
+ 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
- kb.assign(self, command, name, icon)
- kb.refresh(self)
- ClearCursor()
+ kb.ReleaseSlot(self)
+ kb.UpdateSlot(self)
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)
+
+--- Resolve the appropriate command and assign the corresponding secure state driver
+kb.RegisterAction = function(type, id)
+
+ if type == 'spell' then
+
+ id = GetSpellInfo(id)
+ end
+
+ local macroText
+ local macroName = type ..'_' .. id
+
+ if kb.ProfessionCache[id] then
+ macroName = "profession_".. kb.ProfessionCache[id].profOffset .. '_' .. kb.ProfessionCache[id].spellNum
+ macroText = "/cast " .. kb.ProfessionCache[id].spellName
+ macros[macroName] = nil
else
- kb.bind(self)
+ macroName = macroName:gsub(' ', '')
+ macroText = ACTION_SCRIPT[type]:format(id)
end
+
+ local baseName, iterative = macroName, 1
+ while (macros[macroName] and macros[macroName][1] ~= macroText) do
+ print(' * cannot use|cFF00FF00', macroName, '|r"'.. (macros[macroName][1] or '') .. '"')
+ macroName = baseName .. '_' .. iterative
+ iterative = iterative + 1
+ end
+ if macroName ~= baseName then
+ print(' * Creating|cFF00FF00', macroName)
+ else
+ print(' * Re-using|cFF00FF00', macroName)
+ end
+
+ local command = 'CLICK KeyBinderMacro:'.. macroName
+ macros[macroName] = {macroText, command }
+ print('RegisterAction', command , macroText)
+ if type == 'macro' then
+ kb.LoadMacro(macroName)
+ else
+ kb.LoadAction(macroName, macroText, command)
+ end
+
+
+ return macroName, macroText, command
end
+kb.LoadMacro = function(macroName)
+ KeyBinderMacro:SetAttribute('*macro-'..macroName, macros[macroName][1])
+ return true
+end
+
+kb.LoadAction = function(macroName)
+ if not macros[macroName] then
+ return false
+ end
+ KeyBinderMacro:SetAttribute('*macrotext-'..macroName, macros[macroName][1])
+ return true
+end
+
+local profressionsCache
+
+kb.AcceptAssignment = function(self, ...)
+ local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
+ local source = loadedProfiles[popup.oldProfile]
+ kb.SetSlot(popup.slot, unpack(popup.args))
+ kb.UpdateSlot(popup.slot)
+ kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) -- re-enable scrolling
+ ClearCursor()
+ ResetCursor()
+end
+
--- Updates the current KeyBinding for the button's command
-kb.bind = function(self, key)
+kb.StoreBinding = function(self, key)
- print('|cFFFFFF00bind|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
if not self.command then
return
end
@@ -281,509 +612,828 @@
if key:match('[RL]SHIFT') or key:match('[RL]ALT') or key:match('[RL]CTRL') then
return
end
+ print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
- if protected[GetBindingAction(key)] then
- return
- kb.bindlist:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(key)))
+ if key == 'ESCAPE' then
+ local keys = {GetBindingKey(self.command) }
+ --print('detected', #keys, 'bindings')
+ for i, key in pairs(keys) do
+ --print('clearing', key)
+ SetBinding(key, nil)
+ SaveBindings(GetCurrentBindingSet())
+ if configProfile.bindings[key] then
+ kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[bindMode]))
+ configProfile.bindings[key] = nil
+ end
+ if configProfile.talents[self.actionName] then
+ configProfile.talents[self.actionName] = nil
+ end
+ bindings[self.actionType][self.actionID] = nil
+ end
+ if configProfile.bound[self.command] then
+ configProfile.bound[self.command] = nil
+ --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[bindMode]))
+ end
+
+ bindsCommitted = false
+ self.active = false
+ else
+
+ 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
+
+ local previousKeys
+ local previousAction = GetBindingAction(self.binding)
+ local binding1, binding2, new1, new2
+ print(type(previousAction), previousAction)
+ if previousAction ~= "" and previousAction ~= self.command then
+ if protected[previousAction] then
+ -- bounce out if trying to use a protected key
+ kb.statustext:SetText(BINDING_FAILED_PROTECTED:format(key, GetBindingAction(previousAction)))
+ kb.bindingstext:SetText(nil)
+ return
+ else
+ kb:print('Discarding keybind for', previousAction)
+ -- todo: sort out retcon'd talent spells
+ end
+ end
+
+ self.pending = true
+
+ bindsCommitted = false
+ SetBinding(self.binding, self.command)
+ SaveBindings(GetCurrentBindingSet())
+
+ local talentInfo
+ if self.actionType == 'spell' and kb.TalentCache[self.actionID] then
+ print('conditional binding (talent = "'..self.actionName..'")')
+ talentInfo = {self.macroName, self.actionName, self.actionType, self.actionID}
+ local bindings = {GetBindingKey(self.command) }
+ for i, key in ipairs(bindings) do
+ tinsert(talentInfo, key)
+ end
+ end
+
+ for level, configProfile in ipairs(priority) do
+ if (level == bindMode) then
+ configProfile.bound[self.command] = true
+ if talentInfo then
+ configProfile.bindings[self.binding] = nil
+ else
+ configProfile.bindings[self.binding] = self.command
+ end
+ configProfile.talents[self.actionName] = talentInfo
+ else
+ configProfile.bindings[self.binding] = nil
+ configProfile.bound[self.command] = nil
+ configProfile.talents[self.actionName] = nil
+ end
+ if configProfile.talents[self.actionID] then
+ configProfile.talents[self.actionID] = nil
+ end
+
+ end
+
+
+
+ kb:print(BINDING_ASSIGNED:format(self.binding, self.actionName, configHeaders[bindMode]))
+
+ end
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
+ kb.UpdateSlot(self, true)
+ KeyBinderSaveButton:Enable()
- 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()
+kb.ReleaseSlot = function(self)
+ local slot = self:GetID()
+
+
+ if configProfile.buttons[slot] then
+ configProfile.buttons[slot] = nil
+ end
+ if self.command then
+ configProfile.commands[self.command] = nil
+ end
+ if self.actionType == 'spell' and IsTalentSpell(self.actionName) then
+ if configProfile.talents[self.actionID] then
+ configProfile.talents[self.actionID] = nil
+ end
+ end
+ local droppedKeys = {}
+
+ -- doing removal in second loop to avoid possible iterator shenanigans
+ for k,v in pairs(configProfile.bindings) do
+ if v == self.command then
+ tinsert(droppedKeys, k)
+ end
+ end
+ if #droppedKeys >=1 then
+ for i, k in ipairs(droppedKeys) do
+ configProfile.bindings[k] = nil
+ end
+ end
+
+ self.isAvailable = nil
+ self.isDynamic = nil
+ self.bindingText = nil
+ self.statusText = nil
self.command = nil
+ self.actionType = nil
+ self.actionID = nil
self.actionName = nil
- self.macro:SetText(nil)
+ self.pickupSlot = nil
+ self.pickupBook = nil
+ self.macroName = 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.SetSlot = function(self, command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook)
+ local slot = self:GetID()
+ local isDynamic, isAvailable
-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 ''))
+ print('|cFFFFFF00SetSlot|r:', self:GetID())
+ if command then
+ if actionType == 'spell' then
+ local professionNum, spellNum = command:match("profession_(%d)_(%d)")
- if command then
- if command:match(COMMAND_SPELL) then
- name = command:match(COMMAND_SPELL)
+ if (professionNum and spellNum) then
+ isDynamic = 'profession'
+ local cacheInfo = kb.ProfessionCache[professionNum..'_'..spellNum]
+ if cacheInfo then
+ isAvailable = true
+ name = cacheInfo.spellName
+ icon = cacheInfo.icon
+ actionID = cacheInfo.spellID
+ self.profIndex = cacheInfo.profIndex
+ self.spellOffset = cacheInfo.spellOffset
+ end
+ print(' Special slot: |cFF00FFFFProfession|r', professionNum, spellNum, isDynamic, isAvailable)
+
+ self.professionNum = tonumber(professionNum)
+ self.spellNum = tonumber(spellNum)
+
+ elseif kb.TalentCache[actionID] then
+
+ isDynamic = 'talent'
+ isAvailable = GetSpellInfo(name)
+ print(' Special slot: |cFFBBFF00talent|r', name, isAvailable)
+ end
+ if not actionID then
+ actionID = select(7, GetSpellInfo(name))
+ end
+ elseif actionType == 'macro' then
+ if not actionID then
+ actionID = GetMacroIndexByName(name)
+ end
+ else
+ --- Journal selections
+ -- todo: consider using the deep end of blizzard action bar instead
+ if not actionID then
+ actionID = command:match("^KeyBinderMacro:(.+)")
+ end
end
+ if not macroName then
+ local previousCommand = command
+ macroName, macroText, command = kb.RegisterAction(actionType, actionID)
- 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])
+ -- Clean up conflicting command entry for loaded buttons
+ if macroName and command ~= previousCommand then
+ print(' Repaired corruption in |cFFFFFF00'..currentHeader..'|r button #'.. self:GetID())
+ configProfile.commands[previousCommand] = nil
+ configProfile.bound[previousCommand] = nil
+ end
end
- profile.commands[command] = index
+ if actionType == 'petaction' then
+ self.pickupSlot = pickupSlot
+ self.pickupBook = pickupBook
+ else
+ self.pickupSlot = nil
+ self.pickupBook = nil
+ end
+
+ actionID = actionID or 0
+ self:EnableKeyboard(true)
+ print(' |cFF00FF00configProfile.buttons['..slot..'] |cFF00FFFF=|r |cFF00FFFF"'.. command.. '"|r |cFF00FF00"'.. name, '"|r |cFFFFFF00icon:'.. icon .. '|r |cFFFF8800"'.. actionType, '"|r |cFFFF0088id:'.. actionID ..'|r |cFF00FF00"'.. macroName .. '"|r')
+ configProfile.buttons[slot] = {command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook}
+
+ -- Clean up conflicting entries for loaded button
+ local previous = configProfile.commands[command]
+ if previous ~= slot and buttons[previous] then
+ kb.ReleaseSlot(buttons[previous])
+ end
+ configProfile.commands[command] = slot
end
- self.profile = bindMode
+ self.isAvailable = isAvailable
+ self.isDynamic = isDynamic
+
+ self.macroText = macroText
+ self.macroName = macroName
+ self.actionType = actionType
+ self.actionID = actionID
self.actionName = name
self.command = command
self.icon:SetTexture(icon)
+ self.profile = bindMode
self:RegisterForDrag('LeftButton')
end
--- Retrieves button at index; creates said button and instates any stored parameters
-kb.keyslot = function(index)
+local leftSlot, upSlot
+local buttonsDepth = 0
+kb.GetSlot = function(index)
+
+ local slot = index + kb.scrollOffset
+
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)
+ button:SetScript('OnClick', KeyButton_OnClick)
+ button:SetScript('OnUpdate', KeyButton_OnUpdate)
+ button:SetScript('OnDragStart', KeyButton_OnDragStart)
+ button:SetScript('OnReceiveDrag', KeyButton_OnReceiveDrag)
+ button:RegisterForClicks('AnyUp')
- if profile.buttons[index] and type(profile.buttons[index] ) == 'table' then
- kb.assign(button, unpack(profile.buttons[index] ))
+
+ local newRow = (mod(index, BINDS_PER_ROW) == 1)
+
+ if index == 1 then
+ button:SetPoint('TOPLEFT', kb.bg, 'TOPLEFT', BUTTON_PADDING, - BUTTON_PADDING)
+ upSlot = button
+ buttonsDepth = KEY_BUTTON_SIZE + BUTTON_PADDING * 2
+ elseif newRow then
+ button:SetPoint('TOPLEFT', upSlot, 'BOTTOMLEFT', 0, -BUTTON_SPACING)
+ upSlot = button
+ buttonsDepth = buttonsDepth + KEY_BUTTON_SIZE + BUTTON_SPACING
else
- kb.release(button)
+ button:SetPoint('TOPLEFT', leftSlot, 'TOPRIGHT', BUTTON_HSPACING, 0)
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
+ leftSlot = 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()]))
+kb.UpdateSlot = function(self, force)
+ local slot = self:GetID()
+
+ if force then
+ if configProfile.buttons[slot] then
+ kb.SetSlot(self, unpack(configProfile.buttons[slot]))
else
- kb.release(self)
+ kb.ReleaseSlot(self)
end
end
if self.command then
+ print('['..slot..'] =', self.command, GetBindingKey(self.command))
+
if self.pending then
self.border:SetColorTexture(unpack(BORDER_PENDING))
+ elseif self.isDynamic then
+ self.border:SetColorTexture(unpack(BORDER_DYNAMIC))
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]))
+
+ 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.isDynamic then
+ print('|cFFFFBB00UpdateSlot|r: ', self.isDynamic, self.isAvailable, self.actionID)
+ end
+
+ if self.isDynamic == 'profession' then
+ local profText = (self.spellNum == 1) and TRADE_SKILLS or (BUTTON_HEADERS[self.profIndex] or GetProfessionInfo(self.profIndex))
+ if self.isAvailable then
+ print(self.profIndex, 'spnum', type(self.spellNum), (self.spellNum == 1))
+
+ self.statusText = '|cFFFFFF00'..profText..'|r'
+ self.bindingText = BindingString(GetBindingKey(self.command))
+ else
+ self.statusText = '|cFFFF4400'..profText..'|r'
+ self.actionName = '(need to train profession #'..self.profNum..')'
+ self.bindingText ='?'
+ end
+ elseif self.isDynamic == 'talent' then
+
+ self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
+ if self.isAvailable then
+ self.bindingText = BindingString(GetBindingKey(self.command))
+ else
+ print(self.actionID, #kb.inactiveTalentBindings[self.actionID])
+ self.bindingText= BindingString(unpack(kb.inactiveTalentBindings[self.actionID]))
+ end
+ else
+ self.statusText = '|cFF00FF00'.. (BUTTON_HEADERS[self.actionType] and BUTTON_HEADERS[self.actionType] or self.actionType) .. '|r'
+ self.bindingText = BindingString(GetBindingKey(self.command))
+ end
+
+ local locked, layer = 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
+ end
+
+ if not self.isAvailable then
+ self.bind:SetTextColor(0.7,0.7,0.7,1)
else
- self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
- --self.macro:SetText(nil)
- self.bind:SetText(nil)
+ self.bind:SetTextColor(1,1,1,1)
+ end
+
+ self.header:SetText(self.statusText)
+ self.bind:SetText(self.bindingText)
+ self.macro:SetText(self.macroName)
+ self.details:SetText(self.actionName)
+end
+
+
+kb.ApplyTalentBinding = function(talentInfo, cache)
+ for i = 5, #talentInfo do
+ SetBinding(talentInfo[i], "CLICK KeyBinderMacro:".. talentInfo[1])
+ tinsert(cache, talentInfo[i])
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
+kb.CacheTalentBinding = function(talentInfo, cache)
+ local spellID = talentInfo[4]
+ kb.inactiveTalentBindings[spellID] = kb.inactiveTalentBindings[spellID] or {}
+ kb.inactiveTalentBindings[spellID] = {select(5,unpack(talentInfo)) }
+ cprint(spellID, unpack(kb.inactiveTalentBindings[spellID]))
end
---- Invokes the KeyBinder frame (from the /kb function or some other source)
-kb.ui = function()
- if not db.showUI then
+kb.ApplyBinding = function(command, name, icon, actionType, actionID, macroName, macroText )
+
+ if actionType == 'macro' then
+ KeyBinderMacro:SetAttribute("*macro-"..macroName, actionID)
+ else
+ KeyBinderMacro:SetAttribute("*macrotext-"..macroName, macroText)
+ end
+ bindings[actionType] = bindings[actionType] or {}
+ bindings[actionType][actionID] = bindings[actionType][actionID] or {}
+ bindings[command] = bindings[actionType][actionID]
+ return bindings[actionType], actionID
+end
+
+kb.ApplyBindings = function (profile)
+ cprint('binding profile', profile)
+ for slot, data in pairs(profile.buttons) do
+ kb.ApplyBinding(unpack(data))
+ end
+
+ for key, command in pairs(profile.bindings) do
+
+ cprint('Bindings data registered', command, key)
+
+ --_G.print('HotKey','loading', key, command)
+ SetBinding(key, command)
+ if bindings[command] and not tContains(bindings[command], key) then
+ tinsert(bindings[command], key)
+ end
+ end
+
+ for spellName, talentInfo in pairs(profile.talents) do
+ local dummy = GetSpellInfo(spellName)
+ local func = kb.CacheTalentBinding
+ local dest = kb.inactiveTalentBindings
+ if dummy then
+ cprint('|cFFBBFF00Active:|r', dummy)
+ local macroName, spellName, actionType, actionID = unpack(talentInfo)
+ bindings[actionType] = bindings[actionType] or {}
+ bindings[actionType][actionID] = {}
+ func = kb.ApplyTalentBinding
+ dest = bindings[actionType][actionID]
+ else
+
+ cprint('|cFFFF4400Inactive:|r', talentInfo[2])
+ end
+ func(talentInfo, dest)
+ end
+
+ SaveBindings(GetCurrentBindingSet())
+end
+
+kb.ApplyAllBindings =function ()
+ table.wipe(kb.inactiveTalentBindings)
+
+ for i, profile in ipairs(priority) do
+ kb.ApplyBindings(profile)
+ end
+ -- do this after to ensure that profession binds are properly overridden
+ kb.UpdateProfessionInfo()
+end
+
+kb.Command = function(args, editor)
+ if args:match("import") then
+ kb.ImportCommmit(args)
+ return
+ elseif args:match("scan") then
+ kb.ImportScan(args)
+ kb.ui()
+ return
+ elseif args:match("load") then
+ kb:ApplyAllBindings()
return
end
- if not kb:IsVisible() then
- kb:Show()
+ if db.showUI then
+ db.showUI = false
+ kb:print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
+ kb:Hide()
+ else
db.showUI = true
+ kb:print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
+ end
+ kb.ui(true)
+end
+
+kb.InitProfile = function(profile, prototype)
+ if not profile then
+ profile = {}
+ end
+ if prototype then
+ print('appplying prototype', prototype)
+ for k,v in pairs(prototype) do
+ if not profile[k] then
+ profile[k] = v
+ end
+ end
end
+ profile.bound = profile.bound or {}
+ profile.buttons = profile.buttons or {}
+ profile.commands = profile.commands or {}
+ profile.bindings = profile.bindings or {}
+ profile.macros = profile.macros or {}
+ profile.talents = profile.talents or {}
+ return profile
+end
+
+kb.ResetProfile = function(profile, prototype)
+ if profile == configProfile then
+ for i, button in pairs(buttons) do
+ kb.ReleaseSlot(button)
+ end
+ end
+ table.wipe(profile)
+ kb.InitProfile(profile, prototype)
+end
+
+
+
+--- Handles constructing spec profiles as they are selected
+
+
+kb.TalentCache = {}
+
+kb.UpdateSpecInfo = function()
+ specID = GetSpecialization()
+ specGlobalID, specName, specDesc , specTexture = GetSpecializationInfo(specID)
+ loadedProfiles[BINDING_TYPE_CHARACTER][specID] = kb.InitProfile(loadedProfiles[BINDING_TYPE_CHARACTER][specID], {
+ specID = specID})
+
+ configHeaders[BINDING_TYPE_SPECIALIZATION] = BINDING_MODE[BINDING_TYPE_SPECIALIZATION]:format(specName)
+ loadedProfiles[BINDING_TYPE_SPECIALIZATION] = loadedProfiles[BINDING_TYPE_CHARACTER][specID]
+ configProfile = loadedProfiles[bindMode]
+ print('|cFF00FF00bindMode:|r', bindMode)
+
+ priority = {loadedProfiles[BINDING_TYPE_GLOBAL], loadedProfiles[BINDING_TYPE_CHARACTER], loadedProfiles[BINDING_TYPE_SPECIALIZATION]}
+
+ print('|cFF00FF00current spec:|r', specID, 'of', GetNumSpecializations())
+end
+
+kb.UpdateTalentInfo = function()
+ if kb.talentsPushed then
+ return
+ end
+
+
+ table.wipe(kb.TalentCache)
+
+ for row =1, MAX_TALENT_TIERS do
+ for col = 1, NUM_TALENT_COLUMNS do
+ local talentID, talentName, icon, selected, available, spellID = GetTalentInfo(row, col, 1)
+ local talentInfo = kb.TalentCache[spellID] or {}
+ talentInfo.row = 1
+ talentInfo.col = col
+ talentInfo.name = talentName
+ talentInfo.talentID = talentID
+ talentInfo.selected = selected
+ talentInfo.available = available
+ talentInfo.spellID = spellID
+ kb.TalentCache[spellID] = talentInfo
+ print('Talent ', row, col, spellID, talentName)
+ end
+ end
+ kb.talentsPushed = true
+end
+
+
+kb.ProfessionCache = {}
+kb.UpdateProfessionInfo = function()
+ table.wipe(kb.ProfessionCache)
+ local profs = {GetProfessions() }
+ local primaryNum = 0
+ for i, index in ipairs(profs) do
+ local profName, texture, rank, maxRank, numSpells, spellOffset = GetProfessionInfo(index)
+ cprint(i, index, profName, numSpells, spellOffset)
+ if not professionMappings[index] then
+ primaryNum = primaryNum + 1
+ end
+ local profNum = professionMappings[index] or primaryNum
+
+
+ kb.ProfessionCache[profNum] = kb.ProfessionCache[i] or {}
+
+ for j = 1, numSpells do
+ local spellName, _, icon, _, _, _, spellID = GetSpellInfo(spellOffset+j, BOOKTYPE_PROFESSION)
+
+ local profInfo = {
+ spellName = spellName,
+ spellID = spellID,
+ icon = icon,
+ profOffset = i,
+ profIndex = index,
+ spellOffset = (spellOffset+j),
+ spellNum = j
+ }
+ KeyBinderMacro:SetAttribute("*macrotext-profession_"..i .. '_' ..j, "/cast ".. spellName)
+
+ kb.ProfessionCache[i .. '_' .. j] = profInfo
+ kb.ProfessionCache[spellName] = profInfo
+ kb.ProfessionCache[spellID] = profInfo
+ cprint(' |cFF0088FF['..i..']|r|cFFFF44BB['..spellOffset+i..']|r', spellName, "*macrotext-profession_"..i .. '_' ..j)
+ end
+
+ end
+
+end
+
+--- Obtains profile data or creates the necessary tables
+kb.SelectProfileSet = function(name)
+
+ --- General info
+ classHeader, className, classID = UnitClass('player')
+ print('|cFF00FF00profile:|r', name)
+ print('|cFF00FF00class:|r', UnitClass('player'))
+
+ --- Global
+ bindMode = BINDING_TYPE_GLOBAL
+ kb.InitProfile(db)
+ loadedProfiles[BINDING_TYPE_GLOBAL] = db
+
+ --- Character
+ if name then
+ db[name] = kb.InitProfile(db[name],
+ {classHeader = classHeader, className = className, classID = classID})
+ loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
+ bindMode = BINDING_TYPE_CHARACTER
+ end
+
+ --- Mutable skills data
+ kb.UpdateSpecInfo()
+ kb.UpdateTalentInfo()
+
+ priority = {loadedProfiles[BINDING_TYPE_GLOBAL], loadedProfiles[BINDING_TYPE_CHARACTER], loadedProfiles[BINDING_TYPE_SPECIALIZATION]}
+ if db.bindMode and loadedProfiles[db.bindMode] then
+ bindMode = db.bindMode
+ end
+
+ db.bindMode = bindMode
+
+ if not BINDING_MODE[bindMode] then
+ bindMode = 3
+ db.bindMode = 3
+ print('overriding', bindMode)
+ end
+
+ print(BINDING_TYPE_GLOBAL)
+ configHeaders[BINDING_TYPE_GLOBAL] = BINDING_MODE[BINDING_TYPE_GLOBAL]
+ configHeaders[BINDING_TYPE_CHARACTER] = BINDING_MODE[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
+ configHeaders[BINDING_TYPE_SPECIALIZATION] = BINDING_MODE[BINDING_TYPE_SPECIALIZATION]:format(specName)
+
+
+ setmetatable(loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return configHeaders[BINDING_TYPE_GLOBAL] end})
+ setmetatable(loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return configHeaders[BINDING_TYPE_CHARACTER] end})
+ setmetatable(loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return configHeaders[BINDING_TYPE_SPECIALIZATION] end})
+
+ print('|cFF00FF00bindMode:|r', bindMode)
+ configProfile = loadedProfiles[bindMode]
+end
+
+local scrollCache = {}
+kb.SelectTab = function(self)
+ scrollCache[bindMode] = kb.scrollOffset
+ bindMode = self:GetID()
+ configProfile = loadedProfiles[self:GetID()]
+ db.bindMode = self:GetID()
+ kb.scrollOffset = scrollCache[bindMode] or 0
+ kb.ui(true)
+end
+
+kb.RevertBindings = function()
+ -- todo: reversion code
+end
+
+kb.ConfirmBindings = function()
+ SaveBindings(GetCurrentBindingSet())
+ bindsCommitted = true
+ for i, button in ipairs(buttons) do
+ button.pending = false
+ end
+ kb.ApplyAllBindings()
+
+ kb.ui()
+ kb:print('Keybinds saved.')
+end
+
+
+
+
+
+
+--- push current information into living UI
+kb.ui = function(force)
+ for i, module in ipairs(kb.modules) do
+ if module.ui then
+ module.ui(force)
+ end
+ end
+
+ if not db.showUI then
+ print('---end of refresh')
+ return
+ end
if not kb.loaded then
- SetupUI()
+ KeyBinder_Initialize()
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 = ''
+ local button = kb.GetSlot(i)
+ button:SetID(i+kb.scrollOffset)
+ kb.UpdateSlot(button, force)
end
if bindsCommitted then
KeyBinderSaveButton:Disable()
- KeyBinderRestoreButton:Disable()
+ --KeyBinderRestoreButton:Disable()
else
KeyBinderSaveButton:Enable()
- KeyBinderRestoreButton:Enable()
+ --KeyBinderRestoreButton:Enable()
end
- --- panel attributes
+ --- Frame Sizing
+ kb.profilebg:SetHeight(kb.tabSize[2] + BUTTON_PADDING * 2 + kb.profiletext:GetStringHeight())
+
+ kb.bg:SetWidth((KEY_BUTTON_SIZE + BUTTON_HSPACING + BUTTON_SPACING) * BINDS_PER_ROW + BUTTON_PADDING*2 - BUTTON_SPACING)
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:SetHeight((KEY_BUTTON_SIZE + BUTTON_SPACING) * numRows + BUTTON_PADDING*2 - BUTTON_SPACING)
+
+ kb:SetHeight(kb.headerbg:GetHeight() + kb.profilebg:GetHeight() + kb.bg:GetHeight() + kb.footer:GetHeight())
+ kb:SetWidth((kb.sourcesbg:GetWidth() +(BINDS_PER_ROW * (KEY_BUTTON_SIZE + BUTTON_HSPACING) + (BINDS_PER_ROW - 1) * BUTTON_SPACING + BUTTON_PADDING * 2) ))
+
kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[bindMode]))
-
-
for i, tab in ipairs(kb.tabButtons) do
-
- local n = tab:GetNormalTexture()
+ local border = 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
+ tab.icon:SetDesaturated(false)
+ if tab.icon2 then tab.icon2:SetDesaturated(false) end
+ border:SetDesaturated(true)
+ border:SetVertexColor(1,1,1, 1)
+ else
+ tab.icon:SetDesaturated(true)
+ if tab.icon2 then tab.icon2:SetDesaturated(true) end
+ border:SetDesaturated(false)
+ border:SetVertexColor(1,1,1)
end
- n:SetTexture(tabTexture)
- n:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top)
- n:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom)
+ border:SetTexture(tabTexture)
+ border:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top)
+ border:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom)
end
+
+ KeyBinderSpecTab.icon:SetTexture(specTexture)
+
+ kb.profiletext:SetText(configHeaders[bindMode])
+ print(bindMode, configHeaders[bindMode], kb:GetSize())
+ print(kb:GetPoint(1))
+
+ kb:Show()
+
+ -- Reset this so talent cache can be rebuilt
+ kb.talentsPushed = nil
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)
+--- post ADDON_LOADED
+kb.variables = function()
+ SkeletonKeyDB = SkeletonKeyDB or {spec = {}}
+ kb.db = SkeletonKeyDB
+ kb.playerName = UnitName('player')
+ kb.playerRealm = SelectedRealmName()
+ kb.profileName = kb.playerRealm .. '_' .. kb.playerName
+ db = kb.db
+
+ kb.SelectProfileSet(kb.profileName)
+ if not configProfile.imported then
+ kb.ImportScan()
end
- SaveBindings()
+ kb.ApplyAllBindings()
+
+ kb.ui(true)
end
-local ACTION_BARS = {
- {'ActionButton', 0},
- {'MultiBarLeftButton', 24},
- {'MultiBarRightButton', 36},
- {'MultiBarBottomRighttButton', 48},
- {'MultiBarBottomLeftButton', 60},
-}
-kb.HotKeyText = function (slot)
- local i, offset = 0, 0
- local actionbar
- -- figure out which bar the slot belongs to
- for i, bar in ipairs(ACTION_BARS) do
- actionbar, offset = unpack(ACTION_BARS[i])
- if bar[2] > slot then
- break
- end
- end
- local button = _G[actionbar .. (slot - offset)]
-
- if not button then
- return
- end
-
- local type, id, subType, subID = GetActionInfo(slot)
-
- if not type then
- return
- end
-
- local bind, command
- if type == 'spell' then
- local name = GetSpellInfo(id)
- command = 'SPELL '..name
- elseif type == 'macro' then
- command = 'MACRO ' .. id
- else
- return
- end
- bind = GetBindingKey(command)
- if bind then
- button.HotKey:SetText(BindingString(bind))
- button.HotKey:Show()
- end
+kb.wrap = function(module)
+ kb.modules = kb.modules or {}
+ tinsert(kb.modules, module)
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()
+-- Volatiles Access
+kb.BindingIsLocked = BindingIsLocked
+kb.BindingString = BindingString
+kb.GetBindings = function() return bindings end
+kb.GetButtons = function() return buttons end
+kb.GetCharacterProfile = function () return loadedProfiles[BINDING_TYPE_CHARACTER] end
+kb.GetGlobalProfile = function () return loadedProfiles[BINDING_TYPE_GLOBAL] end
+kb.GetLooseTalents = function() return talentBindings end
+kb.GetProfileStack = function() return priority end
+kb.GetReverts = function() return reverts end
+kb.GetSpecProfile = function () return loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
- for i, button in pairs(buttons) do
- kb.release(button)
- end
+--- Add to blizzard interfaces
+StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] = {
+ text = "Confirm moving an assigned command.",
+ button1 = OKAY,
+ button2 = CANCEL,
+ timeout = 0,
+ whileDead = 1,
+ showAlert = 1,
+ OnAccept = kb.AcceptAssignment,
+ OnCancel = function() kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) end
+}
- profile.commands = {}
- profile.bindings = {}
- profile.macros = {}
-end
+SLASH_SKB1 = "/skb"
+SLASH_SKB2 = "/skeletonkey"
+SlashCmdList.SKB = kb.Command
---- Gives us the profile structure to work with while instating data
-kb.profile = function(name)
- global = kb.InitProfile(db)
- profile = global
- local subtitle
- if name then
- db[name] = db[name] or {}
- db[name] = kb.InitProfile(db[name])
- character = db[name]
- local spec = GetSpecialization()
- if spec then
- db[name][spec] = db[name][spec] or {}
- profile = kb.InitProfile(db[name][spec])
- bindMode = BINDING_TYPE_SPECIALIZATION
- subtitle = select(2,GetSpecializationInfo(spec))
- specialization = db[name][spec]
- else
- profile = kb.InitProfile(db[name])
- bindMode = BINDING_TYPE_CHARACTER
- subtitle = name
- specialization = character
- end
- end
- priority = {global, character, specialization }
+-- This is needed to identify a spells that aren't reflected by GetCursorInfo()
+hooksecurefunc("PickupSpellBookItem", function(slot, bookType)
+ print('|cFFFF4400PickupSpellBookItem(..', tostring(slot),', '..tostring(bookType)..')')
+ CURSOR_SPELLSLOT = slot
+ CURSOR_BOOKTYPE = bookType
+end)
+-- Pet actions
+local isPickup
+hooksecurefunc("PickupPetAction", function(slot, ...)
+ isPickup = GetCursorInfo()
-
- if not db.bindsPage then
- db.bindsPage = bindMode
- end
- bindMode = db.bindsPage
-
-
- if not BINDING_MODE[bindMode] then
- bindMode = 3
- 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()]
- 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()
- SkeletonKeyDB = SkeletonKeyDB or {}
- db = SkeletonKeyDB
- 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()
- db.showUI = false
- kb:Hide()
-end
-
-kb.PLAYER_REGEN_DISABLED = function()
- if db.showUI then
- kb:Hide()
- end
-end
-
-kb.PLAYER_REGEN_ENABLED = function()
- if db.showUI then
- kb.ui()
- end
-end
---- Refresh buttons if macros are updated
-kb.UPDATE_BINDINGS = function()
- for i = 1, 120 do
- kb.HotKeyText(i)
- end
- if db.showUI then
- kb.ui()
- end
-end
-
-kb.ACTIONBAR_SLOT_CHANGED = function(self, event, slot)
- kb.HotKeyText(slot)
- return true
-end
-
-kb.UPDATE_MACROS = kb.UPDATE_BINDINGS
-SLASH_KB1 = "/kb"
-SlashCmdList.KB = function(self, input)
- if db.showUI then
- db.showUI = false
- print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
- kb:Hide()
- else
- db.showUI = true
- print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
- kb.ui()
- end
-end
+ CURSOR_PETACTION = isPickup and slot
+ print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
+end)
\ No newline at end of file
diff -r a30285f8191e -r 9ac29fe77455 SkeletonKey/KeyBinds.xml
--- a/SkeletonKey/KeyBinds.xml Tue Jun 21 11:56:14 2016 -0400
+++ b/SkeletonKey/KeyBinds.xml Tue Jul 26 19:29:44 2016 -0400
@@ -1,6 +1,12 @@
-
+
@@ -21,24 +27,38 @@
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
@@ -51,27 +71,32 @@
-
+
+
+
+
+
self:RegisterForDrag('LeftButton')
+
+
self:StartMoving()
self:StopMovingOrSizing()
+
+ self:OnMouseWheel(delta)
+
+
+ self:OnHide()
+
-
-
-
-
-
-
-
@@ -80,41 +105,147 @@
-
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+ self:AddMessage('SkeletonKey import tool')
+
+
+ if delta >= 0 then
+ if IsControlKeyDown() then
+ -- extremely janky but avoids having one line at the bottom
+ for i =1, self:GetMaxLines() do
+ self:ScrollUp()
+ end
+ end
+
+ return self:ScrollUp()
+ else
+
+ if IsControlKeyDown() then
+ return self:ScrollToBottom()
+ end
+
+ self:ScrollDown()
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r a30285f8191e -r 9ac29fe77455 SkeletonKey/SkeletonKey.toc
--- a/SkeletonKey/SkeletonKey.toc Tue Jun 21 11:56:14 2016 -0400
+++ b/SkeletonKey/SkeletonKey.toc Tue Jul 26 19:29:44 2016 -0400
@@ -1,4 +1,4 @@
-## Interface: 70000
+## Interface: 60200
## Title: SkeletonKey
## Notes: Key Bindings for dinosaurs
## Author: Krakyn
@@ -7,8 +7,12 @@
## X-Category: Interface Enhancements
## DefaultState: Enabled
## LoadOnDemand: 0
-## OptionalDeps: libKT
+## OptionalDeps: LibStub, libKT
+LibStub\LibStub.lua
libKT-1.0\libKT-1.0.xml
KeyBinds.xml
-KeyBinds.lua
\ No newline at end of file
+KeyBinds.lua
+Import.lua
+Events.lua
+HotKey.lua
\ No newline at end of file