view modules/ReAction_PetAction/ReAction_PetAction.lua @ 91:c2504a8b996c

Bug fixes - action buttons resetting to 6 pixels - stray prints - config panels for action buttons not showing all panels - fixed multi-ID typo - fixed autocast model on pet buttons
author Flick <flickerstreak@gmail.com>
date Fri, 17 Oct 2008 03:59:55 +0000
parents 7cabc8ac6c16
children 567a885cdfad
line wrap: on
line source
--[[
  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

ReAction:UpdateRevision("$Revision$")

-- module declaration
local moduleID = "PetAction"
local module = ReAction:NewModule( moduleID )

-- Button class declaration
local Button = { }

-- 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")
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 b = Button:New(bar,i,btnCfg[i])
        btns[i] = b
        bar:AddButton(i,b)
      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
  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


---- Options ----
function module:GetBarOptions(bar)
  if bar.config.type == moduleID then
    return {
      type = "group",
      name = L["Pet Buttons"],
      args = {
      }
    }
  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 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")
  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 = _G[("%sHotKey"):format(name)]

  f:HookScript("OnDragStart", function() self:Update() end)
  f:HookScript("OnReceiveDrag", function() self:Update() end)

  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)

  --self.binder = ReAction:AttachBinder(self)

  self:Refresh()
  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()
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()

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