view libs/ReBound-1.0/ReBound-1.0.lua @ 10:f3a7bfebc283

Version 0.33
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:37:38 +0000
parents
children 2735edcf9ab7
line wrap: on
line source
--[[
Name: ReBound-1.0
Revision: $Rev: 1 $
Author: Flick
Website: 
Documentation: 
SVN: 
Description: Library for point-and-click key binding interfaces
License: MIT
Dependencies: AceLibrary, AceEvent-2.0, AceLocale-2.2
]]


local version_major, version_minor = "ReBound-1.0", "$Rev: 1 $"

if not AceLibrary then error(version_major .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(version_major, version_minor) then return end
if not AceLibrary:HasInstance("AceEvent-2.0") then error(version_major .. " requires AceEvent-2.0.") end
if not AceLibrary:HasInstance("AceLocale-2.2") then error(version_major .. " requires AceLocale-2.2.") end

local L = AceLibrary("AceLocale-2.2"):new("ReBound")

local colorGreen = "|cff00ff00"
local colorOff = "|r"

local mouseButtonConvert = {
  MiddleButton = "BUTTON3",
  Button4 = "BUTTON4",
  Button5 = "BUTTON5"
}

-- localization
L:RegisterTranslations( "enUS", function()
  return {
    ["none"] = true,
    ["Right-click"] = true,
    ["Click to select for binding"] = true,
    ["Shift-click to clear binding"] = true,
    ["Press a key to assign binding"] = true,
    ["is now unbound"] = true,
  }
end )




local ReBound = { }


--[[
  Arguments:
  key: A string representation of a key, suitable for passing to SetBinding.
  target: The frame with an OnClick handler to attach a click-binding to
  [button]: The mouse button to emulate. Default is "LeftButton".

  Returns:
  nothing.

  Notes:
  This does not save the bindings.
]]
function ReBound:SetBinding( key, target, button )
  if not key then self:error("ReBound:SetBinding() requires a key argument.") end
  if not target then self:error("ReBound:SetBinding() requires a binding target argument") end
  button = button or "LeftButton"

  -- prevent setting a binding that's already set
  local current = { self:GetBinding(target,button) }
  for _, b in pairs(current) do
    if b == key then
      return
    end
  end

  -- clear the old binding for the key. This isn't strictly necessary, but it allows us to collect
  -- notification of the unbinding in one place (ClearBinding).
  self:ClearBinding( key )

  -- clear the old binding for the target and button (silently)
  self:ClearBinding( nil, target, button, true )

  -- set the new binding
  SetBindingClick(key, target:GetName(), button)

  -- notify listeners, e.g. for storing the setting
  self.event:TriggerEvent("REBOUND_BIND", key, target:GetName(), button)
end


--[[
  Arguments:
  [key]: A string representation of a key, suitable for passing to SetBinding. This can be nil if target is specified.
  [target]: The frame with a click keybinding to search for a key. 
  [button]: The mouse button to emulate. Default is "LeftButton". Only used with [target].
  [silent]: if true, omits printout.

  Returns:
  nothing.

  Notes:
  If key is provided, then the binding for that key is cleared. If key is not provided and target is provided, then
  all the bindings attached to the click-binding for that target are cleared.

  This does NOT save the bindings. Call SaveBindings() to commit the bindings to disk.
]]
function ReBound:ClearBinding( key, target, button, silent ) 
  if not target and not key then self:error("ReBound:ClearBinding() requires a key or click-binding target argument") end
  button = button or "LeftButton"

  local keys = key and { key } or { self:GetBinding(target,button) }
  for _, k in ipairs(keys) do
    -- Print a notification message
    if k and not silent then
      local action = GetBindingAction(k)
      if action then
        local name = GetBindingText(action,"BINDING_NAME_")
        local keyTxt = GetBindingText(k,"KEY_")
        -- make click-bindings look prettier
        local f, b = name:match("CLICK (.+)\:(.+)")
        if f then
          name = f
          if b ~= "LeftButton" then
            if b == "RightButton" then b = "Right Click" end
            name = f .."-"..b
          end
        end
        if name and #name > 0 then
          UIErrorsFrame:AddMessage(name.." ("..colorGreen..keyTxt..colorOff..") "..L["is now unbound"].."!")
        end
      end
    end
    SetBinding(k,nil)
    self.event:TriggerEvent("REBOUND_UNBIND", k)
  end
end


--[[
  Gets the keys bound to clicking a frame.

  Arguments:
  target: target frame to query
  [button]: mouse button to emulate ("LeftButton", "RightButton")

  Returns:
  key1, key2, key3, etc, as strings.
]]
function ReBound:GetBinding( target, button )
  if not target then self:error("ReBound:GetBinding() requires a target frame argument") end
  button = button or "LeftButton"
  return GetBindingKey("CLICK "..target:GetName()..":"..button)
end


--[[
  Registers a target frame by creating a click-binding frame and putting that frame in the list of 
  registered frames, which can then be all shown/hidden as one unit.

  Arguments:
  target = the frame whose OnClick handler should be the target of keybinding

  Returns:
  A clickbinder frame.
]]
function ReBound:Register( target )
  local f = self:CreateClickBindingFrame(target)
  self.frames[target] = f
  return f
end


--[[ 
  Unregisters a target frame by removing it from the internal list. Does nothing to the clickbinding frame.

  Arguments:
  target = the frame whose OnClick handler should no longer be the target of keybinding

  Returns:
  nothing.
]]
function ReBound:Unregister( target )
  self.frames[target] = nil
end


--[[
  Shows all the registered click binding frames.
]]
function ReBound:ShowAll()
  if InCombatLockdown() then
    -- can't set bindings while in combat, so don't bother showing them
    UIErrorsFrame:AddMessage(ERR_NOT_IN_COMBAT)
  else
    for _, f in pairs(self.frames) do
      f:Show()
    end
  end
end


--[[
  Hides all the registered click binding frames.
]]
function ReBound:HideAll()
  for _, f in pairs(self.frames) do
    f:Hide()
  end
end

-- click binding frame implementation functions
local function ShowTooltip1( self )
  local target = self:GetParent()

  GameTooltip:ClearLines()
  GameTooltip:SetOwner(self,"ANCHOR_TOPRIGHT")
  -- line 1: button name and current binding
  GameTooltip:AddDoubleLine(target:GetName(), colorGreen.."("..(self.ReBound:GetBinding(target,"LeftButton") or L["none"])..")"..colorOff)
  -- line 2: current right-click binding (if any)
  local binding2 = self.ReBound:GetBinding(target,"RightButton")
  if binding2 then
    GameTooltip:AddDoubleLine(L["Right-click"]..":", colorGreen.."("..binding2..")"..colorOff)
  end
  -- line 3: instructions
  GameTooltip:AddLine(L["Click to select for binding"])
  GameTooltip:AddLine(L["Shift-click to clear binding"])
  GameTooltip:Show()
end

local function ShowTooltip2( self )
  if GameTooltip:IsOwned(self) then
    local target = self:GetParent()
    GameTooltip:ClearLines()
    GameTooltip:SetOwner(self)
    local clickSuffix = self.selectedButton == "RightButton" and (" ("..L["Right-click"]..")") or ""
    -- line 1: button name and binding to be set
    GameTooltip:AddDoubleLine(target:GetName()..clickSuffix, colorGreen.."("..(self.ReBound:GetBinding(target,self.selectedButton) or L["none"])..")"..colorOff)
    -- line 2: instructions
    GameTooltip:AddLine(colorGreen..L["Press a key to assign binding"]..colorOff)
    GameTooltip:Show()
  end
end

local function OnClick( self, button )
  if button == "LeftButton" or button == "RightButton" then
    if IsShiftKeyDown() then
      self.ReBound:ClearBinding( nil, self:GetParent(), button )
      self.selectedButton = nil
      self:EnableKeyboard(false)
      ShowTooltip1(self)
    else
      self.selectedButton = button
      self:EnableKeyboard(true)
      ShowTooltip2(self)
    end
  elseif self.selectedButton then
    self.ReBound:SetBinding( mouseButtonConvert[button], self:GetParent(), self.selectedButton )
    self.selectedButton = nil
    self:EnableKeyboard(false)
    ShowTooltip1(self)
  end
end

local function OnEnter( self )
  -- clear current binding button
  self.selectedButton = nil
  -- show tooltip 1
  ShowTooltip1(self)
end

local function OnLeave( self )
  -- disable keyboard input, if it was enabled
  self:EnableKeyboard(false)
  -- hide tooltip
  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end

local function OnKeyDown( self, key )
  if key == nil or key == "UNKNOWN" or key == "SHIFT" or key == "CTRL" or key == "ALT" then 
    return
  end
  if IsShiftKeyDown()   then key = "SHIFT-"..key end
  if IsControlKeyDown() then key = "CTRL-"..key end
  if IsAltKeyDown()     then key = "ALT-"..key end

  if key ~= "ESCAPE" then
    self.ReBound:SetBinding( key, self:GetParent(), self.selectedButton )
  end

  self:EnableKeyboard(false)
  self.selectedButton = nil
  ShowTooltip1(self)
end

--[[
  Creates a click-binding frame attached to the target frame, which can be used for point-and-click keybind assignments. The
  frame is initially hidden by default. It is not registered with ReBound for automatic show/hide: use Register() for that.

  Arguments:
  target - the frame whose OnClick handler should be the target of keybinding

  Returns:
  A clickbinder frame.
]]
function ReBound:CreateClickBindingFrame( target )
  local f = CreateFrame("Button", nil, target)
  f.ReBound = self
  f:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
  f:SetToplevel(1)
  f:SetFrameStrata("DIALOG")
  f:RegisterForClicks("AnyUp")
  f:SetScript("OnClick",   OnClick)
  f:SetScript("OnEnter",   OnEnter)
  f:SetScript("OnLeave",   OnLeave)
  f:SetScript("OnKeyDown", OnKeyDown)
  f:SetAllPoints(target)
  f:Hide()
  return f
end



-- library setup

local function activate( self, oldLib, oldDeactivate )
  -- set initial values
  self.mode = oldLib and oldLib.mode or "char"
  self.frames = { }

  self.event = AceLibrary("AceOO-2.0").Class("AceEvent-2.0"):new()
  self.event:RegisterEvent("PLAYER_REGEN_DISABLED", function() self:HideAll() end)

  if oldDeactivate then
    oldDeactivate(oldLib)
  end
end

local function deactivate( self )
  self.event:UnregisterEvent("PLAYER_REGEN_DISABLED")
end

AceLibrary:Register(ReBound, version_major, version_minor, activate, deactivate)
ReBound = nil