changeset 88:fc83b3f5b322

Added keybindings using LibKeyBound-1.0, with modifications for Override bindings instead of standard bindings.
author Flick <flickerstreak@gmail.com>
date Sun, 31 Aug 2008 06:02:18 +0000
parents 3499ac7c3a9b
children 491a6ffe7260
files Bindings.xml ReAction.lua lib/LibKeyBound-1.0/LibKeyBound-1.0.lua lib/LibKeyBound-1.0/Locale-deDE.lua lib/LibKeyBound-1.0/Locale-enUS.lua lib/LibKeyBound-1.0/Locale-esES.lua lib/LibKeyBound-1.0/Locale-esMX.lua lib/LibKeyBound-1.0/Locale-frFR.lua lib/LibKeyBound-1.0/Locale-koKR.lua lib/LibKeyBound-1.0/Locale-ruRU.lua lib/LibKeyBound-1.0/Locale-zhCN.lua lib/LibKeyBound-1.0/Locale-zhTW.lua lib/LibKeyBound-1.0/lib.xml lib/embeds.xml locale/enUS.lua modules/ReAction_Action/ReAction_Action.lua modules/ReAction_ConfigUI/ReAction_ConfigUI.lua modules/ReAction_PetAction/ReAction_PetAction.lua
diffstat 18 files changed, 1559 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/Bindings.xml	Sat Jun 28 00:54:21 2008 +0000
+++ b/Bindings.xml	Sun Aug 31 06:02:18 2008 +0000
@@ -1,11 +1,9 @@
 <Bindings>
   <!-- general keybindings -->
   <Binding name="REACTION_TOGGLELOCK" header="REACTION">
-
+    ReAction:SetConfigMode(not ReAction:GetConfigMode())
   </Binding>
   <Binding name="REACTION_TOGGLEKEYBIND">
-
+    ReAction:SetKeybindMode(not ReAction:GetKeybindMode())
   </Binding>
-
-  <!-- dummy keybinds -->
 </Bindings>
--- a/ReAction.lua	Sat Jun 28 00:54:21 2008 +0000
+++ b/ReAction.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -51,11 +51,16 @@
 
 ------ LIBRARIES ------
 local callbacks = LibStub("CallbackHandler-1.0"):New(ReAction)
+local KB = LibStub("LibKeyBound-1.0")
 local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
 ReAction.L = L
 
 ------ PRIVATE ------
-local private = { }
+local weak = {__mode="k"}
+local private = {
+  allKB = setmetatable({}, weak),
+  kbHooked = setmetatable({}, weak),
+}
 local bars = {}
 local defaultBarConfig = {}
 local barOptionGenerators = { }
@@ -198,12 +203,15 @@
       self:SetConfigMode(true)
     elseif option == "lock" then
       self:SetConfigMode(false)
+    elseif option == "kb" then
+      self:SetKeybindMode(true)
     else
       self:Print(("%3.1f.%d"):format(version,self.revision))
       self:Print("/rxn config")
       self:Print("/rxn edit")
       self:Print("/rxn lock")
       self:Print("/rxn unlock")
+      self:Print("/rxn kb")
     end
   end
 end
@@ -223,6 +231,9 @@
   self.db.RegisterCallback(self,"OnProfileChanged")
   self.db.RegisterCallback(self,"OnProfileReset","OnProfileChanged")
 
+  KB.RegisterCallback(self,"LIBKEYBOUND_ENABLED")
+  KB.RegisterCallback(self,"LIBKEYBOUND_DISABLED")
+
   options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
 
   self:RegisterChatCommand("reaction", SlashHandler)
@@ -247,9 +258,18 @@
   if private.configMode == true then
     self:UserError(L["ReAction config mode disabled during combat."])
     self:SetConfigMode(false)
+    self:SetKeybindMode(false)
   end
 end
 
+function ReAction:LIBKEYBOUND_ENABLED( evt )
+  self:SetKeybindMode(true)
+end
+
+function ReAction:LIBKEYBOUND_DISABLED( evt )
+  return self:SetKeybindMode(false)
+end
+
 
 
 ------ API ------
@@ -465,3 +485,58 @@
 function ReAction:ShowEditor(bar, ...)
   CallModuleMethod("ConfigUI","LaunchBarEditor",bar, ...)
 end
+
+
+local kb_onEnter
+do
+  function kb_onEnter( self )
+    if ReAction:GetKeybindMode() then
+      KB:Set(self)
+    end
+  end
+end
+
+function ReAction:SetKeybindMode( mode )
+  if mode ~= private.kbMode then
+    if mode then
+      for f in pairs(private.allKB) do
+        if not private.kbHooked[f] then
+          -- avoid taint, particularly with SecureAnchorEnterTemplate
+          -- don't hook scripts multiple times, there isn't any unhook!
+          f:HookScript("OnEnter",kb_onEnter)
+          private.kbHooked[f] = true
+        end
+      end
+      KB:Activate()
+    else
+      KB:Deactivate()
+    end
+    private.kbMode = KB:IsShown() or false
+  end
+end
+
+function ReAction:GetKeybindMode( mode )
+  return private.kbMode
+end
+
+function ReAction:RegisterKeybindFrame( f )
+  private.allKB[f] = true
+end
+
+function ReAction:FreeOverrideHotkey( key )
+  for f in pairs(private.allKB) do
+    if f.GetBindings then
+      for i = 1, select('#', f:GetBindings()) do
+        if select(i, f:GetBindings()) == key then
+          if f.FreeKey then
+            return f:FreeKey(key)
+          else
+            local action = f.GetActionName and f:GetActionName() or f:GetName()
+            SetOverrideBinding(f, false, key, nil)
+            return action
+          end
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/LibKeyBound-1.0.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,659 @@
+--[[
+Name: LibKeyBound-1.0
+Revision: $Rev: 76300 $
+Author(s): Gello, Maul, Toadkiller, Tuller
+Website: http://www.wowace.com/wiki/LibKeyBound-1.0
+Documentation: http://www.wowace.com/wiki/LibKeyBound-1.0
+SVN: http://svn.wowace.com/wowace/trunk/LibKeyBound-1.0
+Description: An intuitive keybindings system: mouseover frame, click keys or buttons.
+Dependencies: CallbackHandler-1.0
+--]]
+
+local MAJOR = "LibKeyBound-1.0"
+local MINOR = "$Revision: 76300 $"
+
+--[[
+	LibKeyBound-1.0
+		ClickBinder by Gello and TrinityBinder by Maul -> keyBound by Tuller -> LibKeyBound library by Toadkiller
+
+		Functions needed to implement
+			button:GetHotkey() - returns the current hotkey assigned to the given button
+
+		Functions to implement if using a custom keybindings system:
+			button:SetKey(key) - binds the given key to the given button
+			button:FreeKey(key) - unbinds the given key from all other buttons
+			button:ClearBindings() - removes all keys bound to the given button
+			button:GetBindings() - returns a string listing all bindings of the given button
+			button:GetActionName() - what we're binding to, used for printing
+--]]
+
+local LibKeyBound, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not LibKeyBound then return end -- no upgrade needed
+
+local _G = _G
+
+-- CallbackHandler
+LibKeyBound.events = LibKeyBound.events or _G.LibStub("CallbackHandler-1.0"):New(LibKeyBound)
+
+local L = LibKeyBoundLocale10
+LibKeyBound.L = L
+-- ToDo delete global LibKeyBoundLocale10 at some point
+LibKeyBound.Binder = LibKeyBound.Binder or {}
+
+-- #NODOC
+function LibKeyBound:Initialize()
+	do
+		local f = CreateFrame("Frame", "KeyboundDialog", UIParent)
+		f:SetFrameStrata("DIALOG")
+		f:SetToplevel(true); f:EnableMouse(true)
+		f:SetWidth(360); f:SetHeight(140)
+		f:SetBackdrop{
+			bgFile="Interface\\DialogFrame\\UI-DialogBox-Background" ,
+			edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border",
+			tile = true,
+			insets = {left = 11, right = 12, top = 12, bottom = 11},
+			tileSize = 32,
+			edgeSize = 32,
+		}
+		f:SetPoint("TOP", 0, -24)
+		f:Hide()
+
+		local tr = f:CreateTitleRegion()
+		tr:SetAllPoints(f)
+		f:SetClampedToScreen(true)
+
+		local text = f:CreateFontString("ARTWORK")
+		text:SetFontObject("GameFontHighlight")
+		text:SetPoint("TOP", 0, -16)
+		text:SetWidth(252); text:SetHeight(0)
+		text:SetText(format(L.BindingsHelp, GetBindingText("ESCAPE", "KEY_")))
+
+		-- Per character bindings checkbox
+		local perChar = CreateFrame("CheckButton", "KeyboundDialogCheck", f, "OptionsCheckButtonTemplate")
+		getglobal(perChar:GetName() .. "Text"):SetText(CHARACTER_SPECIFIC_KEYBINDINGS)
+
+		perChar:SetScript("OnShow", function(self)
+			self:SetChecked(GetCurrentBindingSet() == 2)
+		end)
+
+		local current
+		perChar:SetScript("OnClick", function(self)
+			current = (perChar:GetChecked() and 2) or 1
+			LoadBindings(current)
+		end)
+
+--		local added
+		f:SetScript("OnShow", function(self)
+--			if (not added) then
+--				UISpecialFrames[#UISpecialFrames + 1] = self:GetName()
+--				added = true
+--			end
+--			PlaySound("igCharacterInfoOpen")
+		end)
+		f:SetScript("OnHide", function(self)
+--			PlaySound("igCharacterInfoClose")
+		end)
+
+		-- Okay bindings checkbox
+		local okayBindings = CreateFrame("CheckButton", "KeyboundDialogOkay", f, "OptionsButtonTemplate")
+		getglobal(okayBindings:GetName() .. "Text"):SetText(OKAY)
+		okayBindings:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12, 14)
+
+		okayBindings:SetScript("OnClick", function(self)
+			current = (perChar:GetChecked() and 2) or 1
+			if InCombatLockdown() then
+				self:RegisterEvent("PLAYER_REGEN_ENABLED")
+			else
+--				DEFAULT_CHAT_FRAME:AddMessage("okayBindings " .. tostring(current))
+				SaveBindings(current)
+				LibKeyBound:Deactivate()
+			end
+		end)
+
+		okayBindings:SetScript("OnHide", function(self)
+			current = (perChar:GetChecked() and 2) or 1
+			if InCombatLockdown() then
+				self:RegisterEvent("PLAYER_REGEN_ENABLED")
+			else
+				SaveBindings(current)
+			end
+		end)
+
+		okayBindings:SetScript("OnEvent", function(self, event)
+--			DEFAULT_CHAT_FRAME:AddMessage("okayBindings delayed " .. tostring(current))
+			SaveBindings(current)
+			self:UnregisterEvent(event)
+			LibKeyBound:Deactivate()
+		end)
+
+		-- Cancel bindings checkbox
+		local cancelBindings = CreateFrame("CheckButton", "KeyboundDialogCancel", f, "OptionsButtonTemplate")
+		getglobal(cancelBindings:GetName() .. "Text"):SetText(CANCEL)
+		cancelBindings:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 12, 14)
+
+		cancelBindings:SetScript("OnClick", function(self)
+			if InCombatLockdown() then
+				self:RegisterEvent("PLAYER_REGEN_ENABLED")
+			else
+--				DEFAULT_CHAT_FRAME:AddMessage("cancelBindings ")
+				LoadBindings(GetCurrentBindingSet())
+				LibKeyBound:Deactivate()
+			end
+		end)
+
+		cancelBindings:SetScript("OnEvent", function(self, event)
+--			DEFAULT_CHAT_FRAME:AddMessage("cancelBindings delayed ")
+			LoadBindings(GetCurrentBindingSet())
+			self:UnregisterEvent(event)
+			LibKeyBound:Deactivate()
+		end)
+
+		perChar:SetPoint("BOTTOMLEFT", cancelBindings, "TOPLEFT", 0, 4)
+
+		self.dialog = f
+	end
+
+	SlashCmdList["LibKeyBoundSlashCOMMAND"] = function() self:Toggle() end
+	SLASH_LibKeyBoundSlashCOMMAND1 = "/libkeybound"
+	SLASH_LibKeyBoundSlashCOMMAND2 = "/kb"
+	SLASH_LibKeyBoundSlashCOMMAND3 = "/lkb"
+
+	LibKeyBound.initialized = true
+end
+
+
+-- Default color to indicate bindable frames in your mod.
+LibKeyBound.colorKeyBoundMode = LibKeyBound.colorKeyBoundMode or { 0, 1, 1, 0.5 }
+
+--[[
+LibKeyBound:SetColorKeyBoundMode([r][, g][, b][, a])
+--]]
+--[[
+Arguments:
+	number - red, default 0
+	number - green, default 0
+	number - blue, default 0
+	number - alpha, default 1
+
+Example:
+	if (MyMod.keyBoundMode) then
+		overlayFrame:SetBackdropColor(LibKeyBound:GetColorKeyBoundMode())
+	end
+	...
+	local r, g, b, a = LibKeyBound:GetColorKeyBoundMode()
+
+Notes:
+	* Returns the color to use on your participating buttons during KeyBound Mode
+	* Values are unpacked and ready to use as color arguments
+--]]
+function LibKeyBound:SetColorKeyBoundMode(r, g, b, a)
+	r, g, b, a = r or 0, g or 0, b or 0, a or 1
+	LibKeyBound.colorKeyBoundMode[1] = r
+	LibKeyBound.colorKeyBoundMode[2] = g
+	LibKeyBound.colorKeyBoundMode[3] = b
+	LibKeyBound.colorKeyBoundMode[4] = a
+	LibKeyBound.events:Fire("LIBKEYBOUND_MODE_COLOR_CHANGED")
+end
+
+--[[
+Returns:
+	* number - red
+	* number - green
+	* number - blue
+	* number - alpha
+
+Example:
+	if (MyMod.keyBoundMode) then
+		overlayFrame:SetBackdropColor(LibKeyBound:GetColorKeyBoundMode())
+	end
+	...
+	local r, g, b, a = LibKeyBound:GetColorKeyBoundMode()
+
+Notes:
+	* Returns the color to use on your participating buttons during KeyBound Mode
+	* Values are unpacked and ready to use as color arguments
+--]]
+function LibKeyBound:GetColorKeyBoundMode()
+	return unpack(LibKeyBound.colorKeyBoundMode)
+end
+
+
+function LibKeyBound:PLAYER_REGEN_ENABLED()
+	if self.enabled then
+		UIErrorsFrame:AddMessage(L.CombatBindingsEnabled, 1, 0.3, 0.3, 1, UIERRORS_HOLD_TIME)
+		self.dialog:Hide()
+	end
+end
+
+function LibKeyBound:PLAYER_REGEN_DISABLED()
+	if self.enabled then
+		self:Set(nil)
+		UIErrorsFrame:AddMessage(L.CombatBindingsDisabled, 1, 0.3, 0.3, 1, UIERRORS_HOLD_TIME)
+		self.dialog:Show()
+	end
+end
+
+
+--[[
+Notes:
+	* Switches KeyBound Mode between on and off
+
+Example:
+	local LibKeyBound = LibStub("LibKeyBound-1.0")
+ 	LibKeyBound:Toggle()
+--]]
+function LibKeyBound:Toggle()
+	if (LibKeyBound:IsShown()) then
+		LibKeyBound:Deactivate()
+	else
+		LibKeyBound:Activate()
+	end
+end
+
+
+--[[
+Notes:
+	* Switches KeyBound Mode to on
+
+Example:
+	local LibKeyBound = LibStub("LibKeyBound-1.0")
+ 	LibKeyBound:Activate()
+--]]
+function LibKeyBound:Activate()
+	if not self:IsShown() then
+		if InCombatLockdown() then
+			UIErrorsFrame:AddMessage(L.CannotBindInCombat, 1, 0.3, 0.3, 1, UIERRORS_HOLD_TIME)
+		else
+			self.enabled = true
+			if not self.frame then
+				self.frame = LibKeyBound.Binder:Create()
+			end
+			self:Set(nil)
+			self.dialog:Show()
+			self.events:Fire("LIBKEYBOUND_ENABLED")
+		end
+	end
+end
+
+
+--[[
+Notes:
+	* Switches KeyBound Mode to off
+
+Example:
+	local LibKeyBound = LibStub("LibKeyBound-1.0")
+ 	LibKeyBound:Deactivate()
+--]]
+function LibKeyBound:Deactivate()
+	if self:IsShown() then
+		self.enabled = nil
+		self:Set(nil)
+		self.dialog:Hide()
+
+		self.events:Fire("LIBKEYBOUND_DISABLED")
+	end
+end
+
+
+--[[
+Returns:
+	boolean - true if KeyBound Mode is currently on
+
+Example:
+	local LibKeyBound = LibStub("LibKeyBound-1.0")
+ 	local isKeyBoundMode = LibKeyBound:IsShown()
+ 	if (isKeyBoundMode) then
+ 		-- Do something
+ 	else
+ 		-- Do another thing
+ 	end
+
+Notes:
+	* Is KeyBound Mode currently on
+--]]
+function LibKeyBound:IsShown()
+	return self.enabled
+end
+
+
+--[[
+Arguments:
+	table - the button frame
+
+Example:
+		local button = this
+		LibKeyBound:Set(button)
+
+Notes:
+	 * Sets up button for keybinding
+	 * Call this in your OnEnter script for the button
+	 * Current bindings are shown in the tooltip
+	 * Primary binding is shown in green in the button text
+--]]
+function LibKeyBound:Set(button)
+	local bindFrame = self.frame
+
+	if button and self:IsShown() and not InCombatLockdown() then
+		bindFrame.button = button
+		bindFrame:SetAllPoints(button)
+
+		bindFrame.text:SetFontObject("GameFontNormalLarge")
+		bindFrame.text:SetText(button:GetHotkey())
+		if bindFrame.text:GetStringWidth() > bindFrame:GetWidth() then
+			bindFrame.text:SetFontObject("GameFontNormal")
+		end
+		bindFrame:Show()
+		bindFrame:OnEnter()
+	elseif bindFrame then
+		bindFrame.button = nil
+		bindFrame:ClearAllPoints()
+		bindFrame:Hide()
+	end
+end
+
+
+--[[
+Arguments:
+	string - the keyString to shorten
+
+Returns:
+	string - the shortened displayString
+
+Example:
+	local key1 = GetBindingKey(button:GetName())
+	local displayKey = LibKeyBound:ToShortKey(key1)
+	return displayKey
+
+Notes:
+	* Shortens the key text (returned from GetBindingKey etc.)
+	* Result is suitable for display on a button
+	* Can be used for your button:GetHotkey() return value
+--]]
+function LibKeyBound:ToShortKey(key)
+	if key then
+		key = key:upper()
+		key = key:gsub(" ", "")
+		key = key:gsub("ALT%-", L["Alt"])
+		key = key:gsub("CTRL%-", L["Ctrl"])
+		key = key:gsub("SHIFT%-", L["Shift"])
+		key = key:gsub("NUMPAD", L["NumPad"])
+
+		key = key:gsub("PLUS", "%+")
+		key = key:gsub("MINUS", "%-")
+		key = key:gsub("MULTIPLY", "%*")
+		key = key:gsub("DIVIDE", "%/")
+
+		key = key:gsub("BACKSPACE", L["Backspace"])
+		key = key:gsub("BUTTON3", L["Button3"])
+		key = key:gsub("BUTTON4", L["Button4"])
+		key = key:gsub("BUTTON5", L["Button5"])
+		key = key:gsub("CAPSLOCK", L["Capslock"])
+		key = key:gsub("CLEAR", L["Clear"])
+		key = key:gsub("DELETE", L["Delete"])
+		key = key:gsub("END", L["End"])
+		key = key:gsub("HOME", L["Home"])
+		key = key:gsub("INSERT", L["Insert"])
+		key = key:gsub("MOUSEWHEELDOWN", L["Mouse Wheel Down"])
+		key = key:gsub("MOUSEWHEELUP", L["Mouse Wheel Up"])
+		key = key:gsub("NUMLOCK", L["Num Lock"])
+		key = key:gsub("PAGEDOWN", L["Page Down"])
+		key = key:gsub("PAGEUP", L["Page Up"])
+		key = key:gsub("SCROLLLOCK", L["Scroll Lock"])
+		key = key:gsub("SPACEBAR", L["Spacebar"])
+		key = key:gsub("TAB", L["Tab"])
+
+		key = key:gsub("DOWNARROW", L["Down Arrow"])
+		key = key:gsub("LEFTARROW", L["Left Arrow"])
+		key = key:gsub("RIGHTARROW", L["Right Arrow"])
+		key = key:gsub("UPARROW", L["Up Arrow"])
+
+		return key
+	end
+end
+
+
+--[[ Binder Widget ]]--
+
+function LibKeyBound.Binder:Create()
+	local binder = CreateFrame("Button")
+	binder:RegisterForClicks("anyUp")
+	binder:SetFrameStrata("DIALOG")
+	binder:EnableKeyboard(true)
+	binder:EnableMouseWheel(true)
+
+	for k,v in pairs(self) do
+		binder[k] = v
+	end
+
+	local bg = binder:CreateTexture()
+	bg:SetTexture(0, 0, 0, 0.5)
+	bg:SetAllPoints(binder)
+
+	local text = binder:CreateFontString("OVERLAY")
+	text:SetFontObject("GameFontNormalLarge")
+	text:SetTextColor(0, 1, 0)
+	text:SetAllPoints(binder)
+	binder.text = text
+
+	binder:SetScript("OnClick", self.OnKeyDown)
+	binder:SetScript("OnKeyDown", self.OnKeyDown)
+	binder:SetScript("OnMouseWheel", self.OnMouseWheel)
+	binder:SetScript("OnEnter", self.OnEnter)
+	binder:SetScript("OnLeave", self.OnLeave)
+	binder:SetScript("OnHide", self.OnHide)
+	binder:Hide()
+
+	return binder
+end
+
+function LibKeyBound.Binder:OnHide()
+	LibKeyBound:Set(nil)
+end
+
+function LibKeyBound.Binder:OnKeyDown(key)
+	local button = self.button
+	if not button then return end
+
+	if (key == "UNKNOWN" or key == "LSHIFT" or key == "RSHIFT" or
+		key == "LCTRL" or key == "RCTRL" or key == "LALT" or key == "RALT" or
+		key == "LeftButton" or key == "RightButton") then
+		return
+	end
+
+	local screenshotKey = GetBindingKey("SCREENSHOT")
+	if screenshotKey and key == screenshotKey then
+		Screenshot()
+		return
+	end
+
+	local openChatKey = GetBindingKey("OPENCHAT")
+	if openChatKey and key == openChatKey then
+		ChatFrameEditBox:Show()
+		return
+	end
+
+	if key == "MiddleButton" then
+		key = "BUTTON3"
+	elseif key == "Button4" then
+		key = "BUTTON4"
+	elseif key == "Button5" then
+		key = "BUTTON5"
+	end
+
+	if key == "ESCAPE" then
+		self:ClearBindings(button)
+		LibKeyBound:Set(button)
+		return
+	end
+
+	if IsShiftKeyDown() then
+		key = "SHIFT-" .. key
+	end
+	if IsControlKeyDown() then
+		key = "CTRL-" .. key
+	end
+	if IsAltKeyDown() then
+		key = "ALT-" .. key
+	end
+
+	if MouseIsOver(button) then
+		self:SetKey(button, key)
+		LibKeyBound:Set(button)
+	end
+end
+
+function LibKeyBound.Binder:OnMouseWheel(arg1)
+	if arg1 > 0 then
+		self:OnKeyDown("MOUSEWHEELUP")
+	else
+		self:OnKeyDown("MOUSEWHEELDOWN")
+	end
+end
+
+function LibKeyBound.Binder:OnEnter()
+	local button = self.button
+	if button and not InCombatLockdown() then
+		if self:GetRight() >= (GetScreenWidth() / 2) then
+			GameTooltip:SetOwner(self, "ANCHOR_LEFT")
+		else
+			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+		end
+
+		if button.GetActionName then
+			GameTooltip:SetText(button:GetActionName(), 1, 1, 1)
+		else
+			GameTooltip:SetText(button:GetName(), 1, 1, 1)
+		end
+
+		local bindings = self:GetBindings(button)
+		if bindings then
+			GameTooltip:AddLine(bindings, 0, 1, 0)
+			GameTooltip:AddLine(L.ClearTip)
+		else
+			GameTooltip:AddLine(L.NoKeysBoundTip, 0, 1, 0)
+		end
+		GameTooltip:Show()
+	else
+		GameTooltip:Hide()
+	end
+end
+
+function LibKeyBound.Binder:OnLeave()
+	LibKeyBound:Set(nil)
+	GameTooltip:Hide()
+end
+
+
+--[[ Update Functions ]]--
+
+function LibKeyBound.Binder:ToBinding(button)
+	return format("CLICK %s:LeftButton", button:GetName())
+end
+
+function LibKeyBound.Binder:FreeKey(button, key)
+	local msg
+	if button.FreeKey then
+		local action = button:FreeKey(key)
+		if button:FreeKey(key) then
+			msg = format(L.UnboundKey, GetBindingText(key, "KEY_"), action)
+		end
+	else
+		local action = GetBindingAction(key)
+		if action and action ~= "" and action ~= self:ToBinding(button) then
+			msg = format(L.UnboundKey, GetBindingText(key, "KEY_"), action)
+		end
+	end
+
+	if msg then
+		UIErrorsFrame:AddMessage(msg, 1, 0.82, 0, 1, UIERRORS_HOLD_TIME)
+	end
+end
+
+function LibKeyBound.Binder:SetKey(button, key)
+	if InCombatLockdown() then
+		UIErrorsFrame:AddMessage(L.CannotBindInCombat, 1, 0.3, 0.3, 1, UIERRORS_HOLD_TIME)
+	else
+		self:FreeKey(button, key)
+
+		if button.SetKey then
+			button:SetKey(key)
+		else
+			SetBindingClick(key, button:GetName(), "LeftButton")
+		end
+
+		local msg
+		if button.GetActionName then
+			msg = format(L.BoundKey, GetBindingText(key, "KEY_"), button:GetActionName())
+		else
+			msg = format(L.BoundKey, GetBindingText(key, "KEY_"), button:GetName())
+		end
+		UIErrorsFrame:AddMessage(msg, 1, 1, 1, 1, UIERRORS_HOLD_TIME)
+	end
+end
+
+function LibKeyBound.Binder:ClearBindings(button)
+	if InCombatLockdown() then
+		UIErrorsFrame:AddMessage(L.CannotBindInCombat, 1, 0.3, 0.3, 1, UIERRORS_HOLD_TIME)
+	else
+		if button.ClearBindings then
+			button:ClearBindings()
+		else
+			local binding = self:ToBinding(button)
+			while (GetBindingKey(binding)) do
+				SetBinding(GetBindingKey(binding), nil)
+			end
+		end
+
+		local msg
+		if button.GetActionName then
+			msg = format(L.ClearedBindings, button:GetActionName())
+		else
+			msg = format(L.ClearedBindings, button:GetName())
+		end
+		UIErrorsFrame:AddMessage(msg, 1, 1, 1, 1, UIERRORS_HOLD_TIME)
+	end
+end
+
+function LibKeyBound.Binder:GetBindings(button)
+	if button.GetBindings then
+		return button:GetBindings()
+	end
+
+	local keys
+	local binding = self:ToBinding(button)
+	for i = 1, select("#", GetBindingKey(binding)) do
+		local hotKey = select(i, GetBindingKey(binding))
+		if keys then
+			keys = keys .. ", " .. GetBindingText(hotKey, "KEY_")
+		else
+			keys = GetBindingText(hotKey, "KEY_")
+		end
+	end
+
+	return keys
+end
+
+LibKeyBound.EventButton = LibKeyBound.EventButton or CreateFrame("Frame")
+do
+	local EventButton = LibKeyBound.EventButton
+	EventButton:UnregisterAllEvents()
+	EventButton:SetScript("OnEvent", function(self, event, addon)
+		if (event == "PLAYER_REGEN_DISABLED") then
+			LibKeyBound:PLAYER_REGEN_DISABLED()
+		elseif (event == "PLAYER_REGEN_ENABLED") then
+			LibKeyBound:PLAYER_REGEN_ENABLED()
+		elseif (event == "PLAYER_LOGIN" and not LibKeyBound.initialized) then
+			LibKeyBound:Initialize()
+			EventButton:UnregisterEvent("PLAYER_LOGIN")
+		end
+	end)
+
+	if IsLoggedIn() and not LibKeyBound.initialized then
+		LibKeyBound:Initialize()
+	elseif not LibKeyBound.initialized then
+		EventButton:RegisterEvent("PLAYER_LOGIN")
+	end
+	EventButton:RegisterEvent("PLAYER_REGEN_ENABLED")
+	EventButton:RegisterEvent("PLAYER_REGEN_DISABLED")
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-deDE.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	LibKeyBound-1.0 localization file
+		Deutch by Gamefaq
+--]]
+
+if (GetLocale() ~= "deDE") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = "Tastenzuweisung Modus aktiviert";
+	Disabled = "Tastenzuweisung Modus deaktiviert";
+	ClearTip = format("Drücke %s um alle Tastenzuweisungen zu löschen", GetBindingText("ESCAPE", "KEY_"));
+	NoKeysBoundTip = "Keine Tasten zugewiesen";
+	ClearedBindings = "Entferne alle Zuweisungen von %s";
+	BoundKey = "Setze %s zu %s";
+	UnboundKey = "Entferne %s von %s";
+	CannotBindInCombat = "Kann Tasten nicht im Kampf zuweisen";
+	CombatBindingsEnabled = "Verlasse Kampf, Tastenzuweisung Modus aktiviert";
+	CombatBindingsDisabled = "Beginne Kampf, Tastenzuweisung Modus deaktiviert";
+	BindingsHelp = "Schwebe mit der Maus über einem Schalter. Drück dann eine Taste um sie zuzuweisen. Um die Belegung der Taste wieder zu löschen drück %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "S",
+	["Shift"] = "U",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "DA",
+	["Left Arrow"] = "LA",
+	["Right Arrow"] = "RA",
+	["Up Arrow"] = "UA",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-enUS.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,130 @@
+--[[
+	KeyBound localization file
+		English
+--]]
+
+local REVISION = tonumber(("$Revision: 77847 $"):match("%d+"))
+if (LibKeyBoundBaseLocale10 and REVISION <= LibKeyBoundBaseLocale10.BASEREVISION) then
+	return
+end
+
+LibKeyBoundBaseLocale10 = {
+	REVISION = 0;
+	BASEREVISION = REVISION;
+	Enabled = 'Bindings mode enabled';
+	Disabled = 'Bindings mode disabled';
+	ClearTip = format('Press %s to clear all bindings', GetBindingText('ESCAPE', 'KEY_'));
+	NoKeysBoundTip = 'No current bindings';
+	ClearedBindings = 'Removed all bindings from %s';
+	BoundKey = 'Set %s to %s';
+	UnboundKey = 'Unbound %s from %s';
+	CannotBindInCombat = 'Cannot bind keys in combat';
+	CombatBindingsEnabled = 'Exiting combat, keybinding mode enabled';
+	CombatBindingsDisabled = 'Entering combat, keybinding mode disabled';
+	BindingsHelp = "Hover over a button, then press a key to set its binding.  To clear a button's current keybinding, press %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "Dn",
+	["Left Arrow"] = "Lf",
+	["Right Arrow"] = "Rt",
+	["Up Arrow"] = "Up",
+}
+if not LibKeyBoundLocale10 then
+	LibKeyBoundLocale10 = LibKeyBoundBaseLocale10
+else
+	setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
+end
+--[[
+World of Warcraft/Blizzard Interface Data (enUS)/FrameXML/GlobalStrings.lua:
+
+KEY_APOSTROPHE = "'";
+KEY_BACKSLASH = "\\";
+KEY_BACKSPACE = "Backspace";
+KEY_BACKSPACE_MAC = "Delete";
+KEY_BINDING = "Key Binding";
+KEY_BINDINGS = "Key Bindings"; -- Title for the key bindings frame
+KEY_BINDINGS_MAC = "Bindings";
+KEY_BOUND = "Key Bound Successfully";
+KEY_BUTTON1 = "Left Mouse Button";
+KEY_BUTTON2 = "Right Mouse Button";
+KEY_BUTTON3 = "Middle Mouse";
+KEY_BUTTON4 = "Mouse Button 4";
+KEY_BUTTON5 = "Mouse Button 5";
+KEY_COMMA = ",";
+KEY_DELETE = "Delete";
+KEY_DELETE_MAC = "Del";
+KEY_DOWN = "Down Arrow";
+KEY_END = "End";
+KEY_HOME = "Home";
+KEY_INSERT = "Insert";
+KEY_INSERT_MAC = "Help";
+KEY_LEFT = "Left Arrow";
+KEY_LEFTBRACKET = "[";
+KEY_MINUS = "-";
+KEY_MOUSEWHEELDOWN = "Mouse Wheel Down";
+KEY_MOUSEWHEELUP = "Mouse Wheel Up";
+KEY_NUMLOCK = "Num Lock";
+KEY_NUMLOCK_MAC = "Clear";
+KEY_NUMPAD0 = "Num Pad 0";
+KEY_NUMPAD1 = "Num Pad 1";
+KEY_NUMPAD2 = "Num Pad 2";
+KEY_NUMPAD3 = "Num Pad 3";
+KEY_NUMPAD4 = "Num Pad 4";
+KEY_NUMPAD5 = "Num Pad 5";
+KEY_NUMPAD6 = "Num Pad 6";
+KEY_NUMPAD7 = "Num Pad 7";
+KEY_NUMPAD8 = "Num Pad 8";
+KEY_NUMPAD9 = "Num Pad 9";
+KEY_NUMPADDECIMAL = "Num Pad .";
+KEY_NUMPADDIVIDE = "Num Pad /";
+KEY_NUMPADMINUS = "Num Pad -";
+KEY_NUMPADMULTIPLY = "Num Pad *";
+KEY_NUMPADPLUS = "Num Pad +";
+KEY_PAGEDOWN = "Page Down";
+KEY_PAGEUP = "Page Up";
+KEY_PAUSE = "Pause";
+KEY_PAUSE_MAC = "F15";
+KEY_PERIOD = ".";
+KEY_PLUS = "+";
+KEY_PRINTSCREEN = "Print Screen";
+KEY_PRINTSCREEN_MAC = "F13";
+KEY_RIGHT = "Right Arrow";
+KEY_RIGHTBRACKET = "]";
+KEY_SCROLLLOCK = "Scroll Lock";
+KEY_SCROLLLOCK_MAC = "F14";
+KEY_SEMICOLON = ";";
+KEY_SLASH = "/";
+KEY_SPACE = "Spacebar"; -- Spacebar
+KEY_TAB = "Tab";
+KEY_TILDE = "~";
+KEY_UNBOUND_ERROR = "|cffff0000%s Function is Now Unbound!|r";
+KEY_UP = "Up Arrow";
+
+KEY_ESCAPE = "Escape";
+--]]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-esES.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	LibKeyBound-1.0 localization file
+		Spanish by StiviS
+--]]
+
+if (GetLocale() ~= "esES") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = 'Modo Atajos activado';
+	Disabled = 'Modo Atajos desactivado';
+	ClearTip = format('Pulsa %s para limpiar todos los atajos', GetBindingText('ESCAPE', 'KEY_'));
+	NoKeysBoundTip = 'No existen atajos';
+	ClearedBindings = 'Eliminados todos los atajos de %s';
+	BoundKey = 'Establecer %s a %s';
+	UnboundKey = 'Quitado atajo %s de %s';
+	CannotBindInCombat = 'No se pueden atajar teclas en combate';
+	CombatBindingsEnabled = 'Saliendo de combate, modo de Atajos de Teclado activado';
+	CombatBindingsDisabled = 'Entrando en combate, modo de Atajos de Teclado desactivado';
+	BindingsHelp = "Sitúese en un botón, entonces pulse una tecla para establecer su atajo.  Para limpiar el Atajo del botón actual, pulse %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "Fin",
+	["Home"] = "Ini",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "AW",
+	["Mouse Wheel Up"] = "RW",
+	["Num Lock"] = "NL",
+	["Page Down"] = "AP",
+	["Page Up"] = "RP",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "Ar",
+	["Left Arrow"] = "Ab",
+	["Right Arrow"] = "Iz",
+	["Up Arrow"] = "De",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-esMX.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	LibKeyBound-1.0 localization file
+		Latin America Spanish by ?
+--]]
+
+if (GetLocale() ~= "esMX") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = 'Modo Atajos activado';
+	Disabled = 'Modo Atajos desactivado';
+	ClearTip = format('Pulsa %s para limpiar todos los atajos', GetBindingText('ESCAPE', 'KEY_'));
+	NoKeysBoundTip = 'No existen atajos';
+	ClearedBindings = 'Eliminados todos los atajos de %s';
+	BoundKey = 'Establecer %s a %s';
+	UnboundKey = 'Quitado atajo %s de %s';
+	CannotBindInCombat = 'No se pueden atajar teclas en combate';
+	CombatBindingsEnabled = 'Saliendo de combate, modo de Atajos de Teclado activado';
+	CombatBindingsDisabled = 'Entrando en combate, modo de Atajos de Teclado desactivado';
+	BindingsHelp = "Sitese en un botn, entonces pulse una tecla para establecer su atajo.  Para limpiar el Atajo del botn actual, pulse %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "Fin",
+	["Home"] = "Ini",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "AW",
+	["Mouse Wheel Up"] = "RW",
+	["Num Lock"] = "NL",
+	["Page Down"] = "AP",
+	["Page Up"] = "RP",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "Ar",
+	["Left Arrow"] = "Ab",
+	["Right Arrow"] = "Iz",
+	["Up Arrow"] = "De",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-frFR.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	LibKeyBound-1.0 localization file
+		French by ?
+--]]
+
+if (GetLocale() ~= "frFR") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = "Mode Raccourcis activé";
+	Disabled = "Mode Raccourcis désactivé";
+	ClearTip = format("Appuyez sur %s pour effacer tous les raccourcis", GetBindingText("ESCAPE", "KEY_"));
+	NoKeysBoundTip = "Aucun raccourci";
+	ClearedBindings = "Suppression de tous les raccourcis de %s";
+	BoundKey = "%s associé à %s";
+	UnboundKey = "%s n'est plus associé à %s";
+	CannotBindInCombat = "Impossible de faire des raccourcis en combat";
+	CombatBindingsEnabled = "Sortie de combat, mode Raccourcis activé";
+	CombatBindingsDisabled = "Entrée en combat, mode Raccourcis désactivé";
+	BindingsHelp = "Survolez un bouton, puis appuyez sur une touche pour définir son raccourci.  Pour effacer le raccourci actuel d'un bouton, appuyez sur %s";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "S1",
+	["Button2"] = "S2",
+	["Button3"] = "S3",
+	["Button4"] = "S4",
+	["Button5"] = "S5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "BA",
+	["Left Arrow"] = "GA",
+	["Right Arrow"] = "DA",
+	["Up Arrow"] = "HA",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-koKR.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	KeyBound localization file
+		Korean by damjau
+--]]
+
+if (GetLocale() ~= "koKR") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = '단축키 설정 기능 사용 가능';
+	Disabled = '단축키 설정 기능 사용 불가';
+	ClearTip = format('%s키를 누르면 모든 단축키가 초기화됩니다', GetBindingText('ESCAPE', 'KEY_'));
+	NoKeysBoundTip = '현재 단축키 없음';
+	ClearedBindings = '%s의 모든 단축키가 초기화 되었습니다';
+	BoundKey = '%2$s의 단축키로 %1$s|1을;를; 설정합니다.';
+	UnboundKey = '%2$s에서 %1$s의 단축키가 삭제되었습니다';
+	CannotBindInCombat = '전투 중에는 단축키를 지정할 수 없습니다';
+	CombatBindingsEnabled = '전투 종료. 단축키 설정이 가능해집니다';
+	CombatBindingsDisabled = '전투 시작. 단축키 설정이 불가능합니다';
+	BindingsHelp = "버튼 위에 마우스를 올려 놓고 지정할 키를 누르세요.  버튼의 현재 단축키를 삭제하시려면 %s|1을;를; 누르세요.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "DA",
+	["Left Arrow"] = "LA",
+	["Right Arrow"] = "RA",
+	["Up Arrow"] = "UA",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-ruRU.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	KeyBound localization file
+		Russian by ?
+--]]
+
+if (GetLocale() ~= "ruRU") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 78684 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = 'Режим назначения клавиш включен';
+	Disabled = 'Режим назначения клавиш отключен';
+	ClearTip = format('Нажмите %s для сброса всех назначений', GetBindingText('ESCAPE', 'KEY_'));
+	NoKeysBoundTip = 'Нет текущих назначений';
+	ClearedBindings = 'Удалить все назначения с %s';
+	BoundKey = 'Установить %s на %s';
+	UnboundKey = 'Снять назначение %s с %s';
+	CannotBindInCombat = 'Невозможно назначить клавишу в бою';
+	CombatBindingsEnabled = 'Выход из боя, режим назначения клавиш включен';
+	CombatBindingsDisabled = 'Начало боя, режим назначения клавиш отключен';
+	BindingsHelp = "Зависните над кнопкой, и тогда нажмите клавишу для установки назначения.  Для очистки текущих назначений клавиш, нажмите %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "Ц",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "КМВХ",
+	["Mouse Wheel Up"] = "КМВЗ",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Прбл",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "Dn",
+	["Left Arrow"] = "Lf",
+	["Right Arrow"] = "Rt",
+	["Up Arrow"] = "Up",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-zhCN.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	KeyBound localization file
+		Chinese Simplified by ondh - http://www.ondh.cn
+--]]
+
+if (GetLocale() ~= "zhCN") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = "按键绑定模式已启用";
+	Disabled = "按键绑定模式已禁用";
+	ClearTip = format("按 %s 清除所有绑定", GetBindingText("ESCAPE", "KEY_"));
+	NoKeysBoundTip = "当前没有绑定按键";
+	ClearedBindings = "从 %s 移除按键绑定";
+	BoundKey = "设置 %s 到 %s";
+	UnboundKey = "取消绑定 %s 从 %s";
+	CannotBindInCombat = "不能在战斗状态绑定按键";
+	CombatBindingsEnabled = "离开战斗状态, 按键绑定模式已启用";
+	CombatBindingsDisabled = "进入战斗状态, 按键绑定模式已禁用";
+	BindingsHelp = "将鼠标停留在按钮上, 然后按下欲指定快捷键之后就能绑定。  要清除目前绑定的按钮请按";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "B1",
+	["Button2"] = "B2",
+	["Button3"] = "B3",
+	["Button4"] = "B4",
+	["Button5"] = "B5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "DA",
+	["Left Arrow"] = "LA",
+	["Right Arrow"] = "RA",
+	["Up Arrow"] = "UA",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/Locale-zhTW.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,61 @@
+--[[
+	KeyBound localization file
+		Traditional Chinese by ?
+--]]
+
+if (GetLocale() ~= "zhTW") then
+	return
+end
+
+local REVISION = tonumber(("$Revision: 77823 $"):match("%d+"))
+if (LibKeyBoundLocale10 and REVISION <= LibKeyBoundLocale10.REVISION) then
+	return
+end
+
+LibKeyBoundLocale10 = {
+	REVISION = REVISION;
+	Enabled = "按鍵綁定模式已啟用";
+	Disabled = "按鍵綁定模式已停用";
+	ClearTip = format("按 %s 清除所有綁定", GetBindingText("ESCAPE", "KEY_"));
+	NoKeysBoundTip = "目前没有綁定按鍵";
+	ClearedBindings = "從 %s 移除按鍵綁定";
+	BoundKey = "設置 %s 到 %s";
+	UnboundKey = "取消綁定 %s 從 %s";
+	CannotBindInCombat = "無法在戰鬥狀態綁定按鍵";
+	CombatBindingsEnabled = "離開戰鬥狀態, 按鍵綁定模式已啟用";
+	CombatBindingsDisabled = "進入戰鬥狀態, 按鍵綁定模式已停用";
+	BindingsHelp = "將滑鼠停留在按鈕上, 然後按下欲指定快捷鍵之後就能綁定。  要清除目前綁定的按鈕請按 %s.";
+
+	-- This is the short display version you see on the Button
+	["Alt"] = "A",
+	["Ctrl"] = "C",
+	["Shift"] = "S",
+	["NumPad"] = "N",
+
+	["Backspace"] = "BS",
+	["Button1"] = "鼠1",
+	["Button2"] = "鼠2",
+	["Button3"] = "鼠3",
+	["Button4"] = "鼠4",
+	["Button5"] = "鼠5",
+	["Capslock"] = "Cp",
+	["Clear"] = "Cl",
+	["Delete"] = "Del",
+	["End"] = "En",
+	["Home"] = "HM",
+	["Insert"] = "Ins",
+	["Mouse Wheel Down"] = "WD",
+	["Mouse Wheel Up"] = "WU",
+	["Num Lock"] = "NL",
+	["Page Down"] = "PD",
+	["Page Up"] = "PU",
+	["Scroll Lock"] = "SL",
+	["Spacebar"] = "Sp",
+	["Tab"] = "Tb",
+
+	["Down Arrow"] = "下",
+	["Left Arrow"] = "左",
+	["Right Arrow"] = "右",
+	["Up Arrow"] = "上",
+}
+setmetatable(LibKeyBoundLocale10, {__index = LibKeyBoundBaseLocale10})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/LibKeyBound-1.0/lib.xml	Sun Aug 31 06:02:18 2008 +0000
@@ -0,0 +1,13 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="Locale-deDE.lua"/>
+	<Script file="Locale-enUS.lua"/>
+	<Script file="Locale-esES.lua"/>
+	<Script file="Locale-esMX.lua"/>
+	<Script file="Locale-frFR.lua"/>
+	<Script file="Locale-koKR.lua"/>
+	<Script file="Locale-zhCN.lua"/>
+	<Script file="Locale-zhTW.lua"/>
+	<Script file="Locale-ruRU.lua"/>
+	<Script file="LibKeyBound-1.0.lua"/>
+</Ui>
\ No newline at end of file
--- a/lib/embeds.xml	Sat Jun 28 00:54:21 2008 +0000
+++ b/lib/embeds.xml	Sun Aug 31 06:02:18 2008 +0000
@@ -10,6 +10,7 @@
   <Include file="AceDBOptions-3.0\AceDBOptions-3.0.xml"/>
   <Include file="AceConsole-3.0\AceConsole-3.0.xml"/>
   <Include file="AceEvent-3.0\AceEvent-3.0.xml"/>
+  <Include file="LibKeyBound-1.0\lib.xml"/>
 
   <!-- for now -->
   <Script file="AceLibrary\AceLibrary.lua"/>
--- a/locale/enUS.lua	Sat Jun 28 00:54:21 2008 +0000
+++ b/locale/enUS.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -167,6 +167,8 @@
 "Show the ReAction Bar Editor dialogue",
 "Close on Launch",
 "Close the Interface Options window when launching the ReAction Bar Editor",
+"Key Bindings",
+"Show the keybinding dialogue",
 "Bar Editor",
 "Global Config",
 "Opens ReAction global configuration settings panel",
--- a/modules/ReAction_Action/ReAction_Action.lua	Sat Jun 28 00:54:21 2008 +0000
+++ b/modules/ReAction_Action/ReAction_Action.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -15,9 +15,13 @@
 local L = ReAction.L
 local _G = _G
 local CreateFrame = CreateFrame
+local format = string.format
 
 ReAction:UpdateRevision("$Revision$")
 
+-- libraries
+local KB = LibStub("LibKeyBound-1.0")
+
 -- module declaration
 local moduleID = "Action"
 local module = ReAction:NewModule( moduleID )
@@ -61,6 +65,9 @@
   ReAction.RegisterCallback(self, "OnRenameBar")
   ReAction.RegisterCallback(self, "OnConfigModeChanged")
 
+  KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
 end
 
 function module:OnEnable()
@@ -155,6 +162,25 @@
   end
 end
 
+function module:LIBKEYBOUND_ENABLED(evt)
+  -- set the border for all buttons to the keybind-enable color
+  local r,g,b,a = KB:GetColorKeyBoundMode()
+  for _, bar in pairs(self.buttons) do
+    for _, b in pairs(bar) do
+    	b.border:SetVertexColor(r,g,b,a)
+      b.border:Show()
+    end
+  end
+end
+
+function module:LIBKEYBOUND_DISABLED(evt)
+  for _, bar in pairs(self.buttons) do
+    for _, b in pairs(bar) do
+      b.border:Hide()
+    end
+  end
+end
+
 
 ---- Options ----
 do
@@ -652,9 +678,127 @@
   end
 end
 
+------ Button class ------
+
 local frameRecycler = { }
+local trash = CreateFrame("Frame")
+local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings
+do
+  local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
+  local IsActionInRange = IsActionInRange
 
------- Button class ------
+  local buttonLookup = setmetatable({},{__mode="kv"})
+
+  function OnUpdate(frame, elapsed)
+    -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these
+    --       are only read by ActionButton_OnUpdate (which this function replaces). In
+    --       all other places they're just written, so it doesn't taint any secure code.
+    if frame.flashing == 1 then
+      frame.flashtime = frame.flashtime - elapsed
+      if frame.flashtime <= 0 then
+        local overtime = -frame.flashtime
+        if overtime >= ATTACK_BUTTON_FLASH_TIME then
+          overtime = 0
+        end
+        frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
+
+        local flashTexture = frame.flash
+        if flashTexture:IsShown() then
+          flashTexture:Hide()
+        else
+          flashTexture:Show()
+        end
+      end
+    end
+    
+    if frame.rangeTimer then
+      frame.rangeTimer = frame.rangeTimer - elapsed;
+
+      if frame.rangeTimer <= 0 then
+        if IsActionInRange(frame.action) == 0 then
+          frame.icon:SetVertexColor(1.0,0.1,0.1)
+        else
+          ActionButton_UpdateUsable()
+        end
+        frame.rangeTimer = 0.1
+      end
+    end
+  end
+
+  -- Use KeyBound-1.0 for binding, but use Override bindings instead of
+  -- regular bindings to support multiple profile use. This is a little
+  -- weird with the KeyBound dialog box (which has per-char selector as well
+  -- as an OK/Cancel box) but it's the least amount of effort to implement.
+  function GetActionName(f)
+    local b = buttonLookup[f]
+    if b then
+      return format("%s:%s", b.bar:GetName(), b.idx)
+    end
+  end
+
+  function GetHotkey(f)
+    local b = buttonLookup[f]
+    if b then
+      return KB:ToShortKey(b:GetConfig().hotkey)
+    end
+  end
+
+  function SetKey(f, key)
+    local b = buttonLookup[f]
+    if b then
+      local c = b:GetConfig()
+      if c.hotkey then
+        SetOverrideBinding(f, false, c.hotkey, nil)
+      end
+      if key then
+        SetOverrideBindingClick(f, false, key, f:GetName(), nil)
+      end
+      c.hotkey = key
+      b:DisplayHotkey(GetHotkey(f))
+    end
+  end
+
+  function FreeKey(f, key)
+    local b = buttonLookup[f]
+    if b then
+      local c = b:GetConfig()
+      if c.hotkey == key then
+        local action = f:GetActionName()
+        SetOverrideBinding(f, false, c.hotkey, nil)
+        c.hotkey = nil
+        b:DisplayHotkey(nil)
+        return action
+      end
+    end
+    return ReAction:FreeOverrideHotkey(key)
+  end
+
+  function ClearBindings(f)
+    SetKey(f, nil)
+  end
+
+  function GetBindings(f)
+    local b = buttonLookup[f]
+    if b then
+      return b:GetConfig().hotkey
+    end
+  end
+
+  function KBAttach( button )
+    local f = button:GetFrame()
+    f.GetActionName = GetActionName
+    f.GetHotkey     = GetHotkey
+    f.SetKey        = SetKey
+    f.FreeKey       = FreeKey
+    f.ClearBindings = ClearBindings
+    f.GetBindings   = GetBindings
+    buttonLookup[f] = button
+    f:SetKey(button:GetConfig().hotkey)
+    ReAction:RegisterKeybindFrame(f)
+  end
+end
+
+
 function Button:New( bar, idx, config, barConfig )
   -- create new self
   self = setmetatable( { }, {__index = Button} )
@@ -676,8 +820,25 @@
     f:SetParent(parent)
   else
     f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
+    -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
+    -- standard binding. We use override bindings.
+    local hotkey = _G[name.."HotKey"]
+    hotkey:SetParent(trash)
+    hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
+    hotkey:SetWidth(36)
+    hotkey:SetHeight(18)
+    hotkey:SetJustifyH("RIGHT")
+    hotkey:SetJustifyV("TOP")
+    hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
+    f.hotkey = hotkey
+    f.icon = _G[name.."Icon"]
+    f.flash = _G[name.."Flash"]
+    f:SetScript("OnUpdate",OnUpdate)
   end
 
+  self.hotkey = f.hotkey
+  self.border = _G[name.."Border"]
+
   f:SetAttribute("action", config.actionID)
   -- install mind control action support for all buttons here just for simplicity
   if self.idx <= 12 then
@@ -697,6 +858,9 @@
   -- show the ID label if applicable
   self:ShowActionIDLabel(ReAction:GetConfigMode())
 
+  -- attach the keybinder
+  KBAttach(self)
+
   self:Refresh()
   return self
 end
@@ -740,6 +904,10 @@
   return self.name
 end
 
+function Button:GetConfig()
+  return self.config
+end
+
 function Button:GetActionID(page)
   if page == nil then
     -- get the effective ID
@@ -856,3 +1024,6 @@
   end
 end
 
+function Button:DisplayHotkey( key )
+  self.hotkey:SetText(key or "")
+end
--- a/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Sat Jun 28 00:54:21 2008 +0000
+++ b/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -80,6 +80,16 @@
         set  = function(info, val) self.db.profile.closeOnLaunch = val end,
         order = 3,
       },
+      _keybind = {
+        type = "execute",
+        handler = self,
+        name = L["Key Bindings"],
+        desc = L["Show the keybinding dialogue"],
+        func = function()
+            ReAction:SetKeybindMode(true)
+          end,
+        order = 4,
+      },
     }, true) -- global
 
   AceConfigReg:RegisterOptionsTable(configName,ReAction.options)
--- a/modules/ReAction_PetAction/ReAction_PetAction.lua	Sat Jun 28 00:54:21 2008 +0000
+++ b/modules/ReAction_PetAction/ReAction_PetAction.lua	Sun Aug 31 06:02:18 2008 +0000
@@ -249,6 +249,8 @@
       end
     end)
 
+  self.binder = ReAction:AttachBinder(self)
+
   self:Refresh()
   return self
 end
@@ -285,6 +287,10 @@
   return self.name
 end
 
+function Button:GetConfig()
+  return self.config
+end
+
 function Button:GetActionID()
   return self.config.actionID
 end