diff modules/PetAction.lua @ 109:410d036c43b2

- reorganize modularity file structure (part 1)
author Flick <flickerstreak@gmail.com>
date Thu, 08 Jan 2009 00:57:27 +0000
parents modules/ReAction_PetAction/ReAction_PetAction.lua@b2fb8f7dc780
children fb48811a8736
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/PetAction.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,637 @@
+--[[
+  ReAction Pet Action button module
+
+  The button module implements standard action button functionality by wrapping Blizzard's 
+  PetActionButton frame and associated functions.
+
+--]]
+
+-- local imports
+local ReAction = ReAction
+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 = "PetAction"
+local module = ReAction:NewModule( moduleID )
+
+-- Button class declaration
+local Button = { }
+
+-- private
+local function UpdateButtonLock(bar)
+  local f = bar:GetFrame()
+  f:SetAttribute("lockbuttons",bar.config.lockButtons)
+  f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat)
+  f:Execute(
+    [[
+      lockButtons = self:GetAttribute("lockbuttons")
+      lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
+    ]])
+end
+
+-- module methods
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    { 
+      profile = {
+        buttons = { }
+      }
+    }
+  )
+  self.buttons = { }
+
+  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
+
+  ReAction.RegisterCallback(self, "OnCreateBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  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()
+  ReAction:RegisterBarType(L["Pet Action Bar"], 
+    { 
+      type = moduleID ,
+      defaultButtonSize = 30,
+      defaultBarRows = 1,
+      defaultBarCols = 10,
+      defaultBarSpacing = 8
+    })
+end
+
+function module:OnDisable()
+  ReAction:UnregisterBarType(L["Pet Action Bar"])
+end
+
+function module:OnCreateBar(event, bar, name)
+  if bar.config.type == moduleID then
+    -- auto show/hide when pet exists
+    bar:GetFrame():SetAttribute("unit","pet")
+    if not ReAction:GetConfigMode() then
+      RegisterUnitWatch(bar:GetFrame())
+    end
+    self:OnRefreshBar(event, bar, name)
+  end
+end
+
+function module:OnRefreshBar(event, bar, name)
+  if bar.config.type == moduleID then
+    if self.buttons[bar] == nil then
+      self.buttons[bar] = { }
+    end
+    local btns = self.buttons[bar]
+    local profile = self.db.profile
+    if profile.buttons[name] == nil then
+      profile.buttons[name] = {}
+    end
+    local btnCfg = profile.buttons[name]
+
+    local r, c = bar:GetButtonGrid()
+    local n = r*c
+    for i = 1, n do
+      if btnCfg[i] == nil then
+        btnCfg[i] = {}
+      end
+      if btns[i] == nil then
+        local success, r = pcall(Button.New,Button,bar,i,btnCfg[i])
+        if success and r then
+          btns[i] = r
+          bar:AddButton(i,r)
+        else
+          n = i - 1
+          bar:ClipNButtons(n)
+          break
+        end
+      end
+      btns[i]:Refresh()
+    end
+    for i = n+1, #btns do
+      if btns[i] then
+        bar:RemoveButton(btns[i])
+        btns[i] = btns[i]:Destroy()
+        if btnCfg[i] then
+          btnCfg[i] = nil
+        end
+      end
+    end
+    UpdateButtonLock(bar)
+  end
+end
+
+function module:OnDestroyBar(event, bar, name)
+  if self.buttons[bar] then
+    local btns = self.buttons[bar]
+    for _,b in pairs(btns) do
+      if b then
+        b:Destroy()
+      end
+    end
+    self.buttons[bar] = nil
+  end
+end
+
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.buttons[name] = nil
+end
+
+function module:OnRenameBar(event, bar, oldname, newname)
+  local b = self.db.profile.buttons
+  b[newname], b[oldname] = b[oldname], nil
+end
+
+
+function module:OnConfigModeChanged(event, mode)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:ShowActionIDLabel(mode)
+    end
+  end
+  for _, bar in ReAction:IterateBars() do
+    if bar and self.buttons[bar] then
+      local f = bar:GetFrame()
+      if mode then
+        UnregisterUnitWatch(f)
+        f:Show()
+      else
+        RegisterUnitWatch(f)
+      end
+    end
+  end
+end
+
+function module:LIBKEYBOUND_ENABLED(evt)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:SetKeybindMode(true)
+    end
+  end
+end
+
+function module:LIBKEYBOUND_DISABLED(evt)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:SetKeybindMode(false)
+    end
+  end
+end
+
+
+---- Options ----
+local Handler = { }
+local meta = { __index = Handler }
+
+function Handler:New(bar)
+  return setmetatable(
+    {
+      bar = bar,
+      config = bar.config
+    }, meta)
+end
+
+function Handler:GetLockButtons()
+  return LOCK_ACTIONBAR == "1" or self.config.lockButtons
+end
+
+function Handler:SetLockButtons(info, value)
+  self.config.lockButtons = value
+  UpdateButtonLock(self.bar)
+end
+
+function Handler:LockButtonsDisabled()
+  return LOCK_ACTIONBAR == "1"
+end
+
+function Handler:GetLockButtonsCombat()
+  return self.config.lockButtonsCombat
+end
+
+function Handler:SetLockButtonsCombat(info, value)
+  self.config.lockButtonsCombat = value
+  UpdateButtonLock(self.bar)
+end
+
+function Handler:LockButtonsCombatDisabled()
+  return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
+end
+
+
+function module:GetBarOptions(bar)
+  if bar.config.type == moduleID then
+    return {
+      type = "group",
+      name = L["Pet Buttons"],
+      handler = Handler:New(bar),
+      args = {
+        lockButtons = {
+          name = L["Lock Buttons"],
+          desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
+          order = 2,
+          type = "toggle",
+          disabled = "LockButtonsDisabled",
+          get = "GetLockButtons",
+          set = "SetLockButtons",
+        },
+        lockOnlyCombat = {
+          name = L["Only in Combat"],
+          desc = L["Only lock the buttons when in combat"],
+          order = 3,
+          type = "toggle",
+          disabled = "LockButtonsCombatDisabled",
+          get = "GetLockButtonsCombat",
+          set = "SetLockButtonsCombat",
+        },
+      }
+    }
+  end
+end
+
+
+
+------ Button class ------
+
+-- use-count of action IDs
+local nActionIDs = NUM_PET_ACTION_SLOTS
+local ActionIDList = setmetatable( {}, {
+  __index = function(self, idx)
+    if idx == nil then
+      for i = 1, nActionIDs do
+        if rawget(self,i) == nil then
+          rawset(self,i,1)
+          return i
+        end
+      end
+      error("ran out of pet action IDs")
+    else
+      local c = rawget(self,idx) or 0
+      rawset(self,idx,c+1)
+      return idx
+    end
+  end,
+  __newindex = function(self,idx,value)
+    if value == nil then
+      value = rawget(self,idx)
+      if value == 1 then
+        value = nil
+      elseif value then
+        value = value - 1
+      end
+    end
+    rawset(self,idx,value)
+  end
+})
+
+local frameRecycler = {}
+local trash = CreateFrame("Frame")
+local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave
+do
+  local buttonLookup = setmetatable({},{__mode="kv"})
+
+  -- 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)
+    if ReAction:GetKeybindMode() then
+      button.border:SetVertexColor(KB:GetColorKeyBoundMode())
+      button.border:Show()
+    end
+  end
+
+  function OnEnter( self )
+    if not self.tooltipName then
+      return;
+    end
+    local uber = GetCVar("UberTooltips")
+    if self.isToken or (uber == "0") then
+      if uber == "0" then
+        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+      else
+        GameTooltip_SetDefaultAnchor(GameTooltip, self)
+      end
+      local tooltip = self.tooltipName
+      local k = GetBindings(self)
+      if k then
+        tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE)
+      end
+      GameTooltip:SetText(tooltip)
+      if self.tooltipSubtext then
+        GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5)
+      end
+      GameTooltip:Show()
+    else
+      GameTooltip_SetDefaultAnchor(GameTooltip, self)
+      GameTooltip:SetPetAction(self:GetID())
+    end
+  end
+
+  function OnLeave()
+    GameTooltip:Hide()
+  end
+
+end
+
+local meta = { __index = Button }
+
+function Button:New( bar, idx, config )
+  -- create new self
+  self = setmetatable( 
+    { 
+      bar = bar,
+      idx = idx,
+      config = config,
+    }, meta )
+
+  local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
+  config.name = name
+  self.name = name
+  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
+  
+  -- have to recycle frames with the same name:
+  -- otherwise you either get references to old textures because named CreateFrame()
+  -- doesn't overwrite existing globals. Can't set them to nil in the global table, 
+  -- as it causes taint.
+  local parent = bar:GetFrame()
+  local f = frameRecycler[name]
+  if f then
+    f:SetParent(parent)
+  else
+    f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate")
+    -- 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:HookScript("OnDragStart", function() self:Update() end)
+    f:HookScript("OnReceiveDrag", function() self:Update() end)
+    f:SetScript("OnEnter", OnEnter)
+    f:SetScript("OnLeave", OnLeave)
+  end
+  if config.actionID then
+    f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton
+  end
+  f:SetFrameStrata("MEDIUM")
+  self.frame    = f
+  self.icon     = _G[("%sIcon"):format(name)]
+  self.acTex    = _G[("%sAutoCastable"):format(name)]
+  self.acModel  = _G[("%sShine"):format(name)]
+  self.cooldown = _G[("%sCooldown"):format(name)]
+  self.hotkey   = f.hotkey
+  self.border   = _G[("%sBorder"):format(name)]
+
+
+  f:RegisterEvent("PLAYER_CONTROL_LOST");
+	f:RegisterEvent("PLAYER_CONTROL_GAINED");
+	f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED");
+	f:RegisterEvent("UNIT_PET");
+	f:RegisterEvent("UNIT_FLAGS");
+	f:RegisterEvent("UNIT_AURA");
+	f:RegisterEvent("PET_BAR_UPDATE");
+	f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN");
+
+  f:SetScript("OnEvent",
+    function(event,arg1)
+      if event =="PET_BAR_UPDATE_COOLDOWN" then
+        self:UpdateCooldown()
+      elseif event == "UPDATE_BINDINGS" then
+        self:UpdateHotkey()
+      else
+        self:Update()
+      end
+    end)
+
+  -- install drag wrappers to lock buttons 
+  bar:GetFrame():WrapScript(f, "OnDragStart",
+    -- OnDragStart(self, button, kind, value, ...)
+    [[
+      if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
+        return "clear"
+      end
+    ]])
+
+  KBAttach(self)
+
+  -- attach to skinner
+  bar:SkinButton(self,
+    {
+      HotKey = self.hotkey,
+    }
+  )
+
+  self:Refresh()
+  self:SetKeybindMode(ReAction:GetKeybindMode())
+
+  return self
+end
+
+function Button:Destroy()
+  local f = self.frame
+  f:UnregisterAllEvents()
+  f:Hide()
+  f:SetParent(UIParent)
+  f:ClearAllPoints()
+  if self.name then
+    frameRecycler[self.name] = f
+    _G[self.name] = nil
+  end
+  if self.config.actionID then
+    ActionIDList[self.config.actionID] = nil
+  end
+  self.frame = nil
+  self.config = nil
+  self.bar = nil
+end
+
+function Button:Refresh()
+  self.bar:PlaceButton(self, 30, 30)
+  self:Update()
+  self:UpdateHotkey()
+  self.frame:Show()
+end
+
+function Button:GetFrame()
+  return self.frame
+end
+
+function Button:GetName()
+  return self.name
+end
+
+function Button:GetConfig()
+  return self.config
+end
+
+function Button:GetActionID()
+  return self.config.actionID
+end
+
+function Button:Update()
+  local id = self.frame:GetID()
+  local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id);
+  local f = self.frame
+  --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled)))
+
+  if isToken then
+    self.icon:SetTexture(_G[texture]);
+    f.tooltipName = _G[name];
+  else
+    self.icon:SetTexture(texture);
+    f.tooltipName = name;
+  end
+
+  f.isToken = isToken;
+	f.tooltipSubtext = subtext;
+  f:SetChecked( isActive and 1 or 0);
+
+  if autoCastAllowed then
+    self.acTex:Show();
+  else
+    self.acTex:Hide();
+  end
+
+  if autoCastEnabled then
+    AutoCastShine_AutoCastStart(self.acModel)
+  else
+    AutoCastShine_AutoCastStop(self.acModel)
+  end
+
+  if texture then
+    if GetPetActionSlotUsable(id) then
+      SetDesaturation(self.icon,nil)
+    else
+      SetDesaturation(self.icon,1)
+    end
+    self.icon:Show();
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
+  else
+    self.icon:Hide();
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot");
+  end
+
+  self:UpdateCooldown()
+end
+
+function Button:UpdateCooldown()
+	local start, duration, enable = GetPetActionCooldown(self.frame:GetID());
+  CooldownFrame_SetTimer(self.cooldown, start, duration, enable);
+end
+
+function Button:UpdateHotkey()
+  self:DisplayHotkey(GetHotkey(self.frame))
+end
+
+function Button:ShowActionIDLabel(show)
+  if show then
+    -- store the action ID label in the frame due to frame recycling
+    if not self.actionIDLabel and self:GetActionID() then
+      local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
+      label:SetAllPoints()
+      label:SetJustifyH("CENTER")
+      label:SetShadowColor(0,0,0,1)
+      label:SetShadowOffset(2,-2)
+      label:SetText(tostring(self:GetActionID()))
+      self.actionIDLabel = label
+    end
+    self.actionIDLabel:Show()
+  elseif self.actionIDLabel then
+    self.actionIDLabel:Hide()
+  end
+end
+
+
+function Button:SetKeybindMode(mode)
+  if mode then
+    self.border:SetVertexColor(KB:GetColorKeyBoundMode())
+    self.border:Show()
+  else
+    self.border:Hide()
+  end
+end
+
+function Button:DisplayHotkey( key )
+  self.hotkey:SetText(key or "")
+end