view modules/ReAction_PossessBar/ReAction_PossessBar.lua @ 63:768be7eb22a0

Converted several ReAction APIs to event-driven model instead of 'call-method-on-all-modules' model. Cleaned up a number of other architectural issues.
author Flick <flickerstreak@gmail.com>
date Thu, 22 May 2008 22:02:08 +0000
parents 44649a10378d
children
line wrap: on
line source
--[[
  ReAction Possess Bar (Mind Control/etc) button module.

  Wraps the standard Action buttons 121-132 with an automatic show/hide
  when mind control (etc) is active

--]]

-- local imports
local ReAction = ReAction
local L = ReAction.L
local _G = _G
local CreateFrame = CreateFrame

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

-- module methods
function module:OnInitialize()
  self.db = ReAction.db:RegisterNamespace( moduleID,
    { 
      profile = {
        buttons = { }
      }
    }
  )
  self.buttons = { }

  ReAction:RegisterOptions(self, {
      [moduleID] = {
        type = "group",
        name = L["Possess Bar"],
        args = {
          hideEmptyPossess = {
            type = "toggle",
            name = L["Hide Empty Possess Bar Buttons"],
            get  = function() return self.db.profile.hideEmptyButtons end,
            set  = function(info, val) module:SetHideEmptyButtons(val) end,
          }
        }
      }
    })

  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["Possess Bar"], 
    { 
      type = moduleID,
      defaultButtonSize = 36,
      defaultBarRows = 1,
      defaultBarCols = 12,
      defaultBarSpacing = 3
    })
end

function module:OnDisable()
  ReAction:UnregisterBarType(L["Possess Bar"])
end

function module:OnCreateBar(event, bar, name)
  if bar.config.type == moduleID then
    bar:GetFrame():SetParent(PossessBarFrame)
    bar.config.parent = "PossessBarFrame"
    self:CreatePossessControlButtons(bar)
  end
  self:OnRefreshBar(event, bar, name)
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 ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
        if ok and b then
          btns[i] = b
        end
      else
        btns[i]:Refresh(bar,i)
      end
    end
    for i = n+1, #btns do
      if btns[i] then
        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:SetHideEmptyButtons(hide)
  if hide ~= self.db.profile.hideEmptyButtons then
    for _, bar in pairs(self.buttons) do
      for _, b in pairs(bar) do
        if hide then
          ActionButton_HideGrid(b.frame)
        else
          ActionButton_ShowGrid(b.frame)
        end
      end
    end
    self.db.profile.hideEmptyButtons = hide
  end
end

function module:OnConfigModeChanged(event, mode)
  for _, bar in ReAction:IterateBars() do
    if bar and self.buttons[bar] then
      for _, b in pairs(self.buttons[bar]) do
        if b then
          if mode then
            ActionButton_ShowGrid(b.frame)
            self:showActionIDLabel(b)
          else
            ActionButton_HideGrid(b.frame)
            self:hideActionIDLabel(b)
          end
        end
      end
      local f = bar:GetFrame()
      if mode then
        f:SetParent(UIParent)
        f:Show()
      else
        f:SetParent(PossessBarFrame)
      end
    end
  end
end

function module:showActionIDLabel(button)
  if not button.actionIDLabel and button:GetActionID() then
    local label = button:GetFrame():CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
    label:SetAllPoints()
    label:SetJustifyH("CENTER")
    label:SetShadowColor(0,0,0,1)
    label:SetShadowOffset(2,-2)
    label:SetText(tostring(button:GetActionID()))
    button.actionIDLabel = label
  end
  button.actionIDLabel:Show()
end

function module:hideActionIDLabel(button)
  if button.actionIDLabel then
    button.actionIDLabel:Hide()
  end
end


-- possess-bar control buttons (shows buff, cancel buff)
function module:CreatePossessControlButton(bar,id,name)
  -- guard against taint by reusing global variable frames
  -- instead of nilling them out (e.g. create bar, delete bar, create bar with same name)
  name = name or ("ReAction_%s_PossessCtrlButton%d"):format(bar:GetName(),id)
  local b = name and _G[name]
  if b then 
    b:SetParent(bar:GetFrame())
  else
    b = CreateFrame("CheckButton", name, bar:GetFrame(), "PossessButtonTemplate")
  end
  b:SetID(id)

  b:RegisterEvent("PLAYER_AURAS_CHANGED");

  local icon = _G[("%sIcon"):format(name)]
  local cooldown = _G[("%sCooldown"):format(name)]
  local nTex = _G[("%sNormalTexture"):format(name)]
  nTex:SetWidth(54)
  nTex:SetHeight(54)

  local function update()
    local texture = GetPossessInfo(id);
    icon:SetTexture(texture);
    icon:Show()
    cooldown:Hide();
    b:SetChecked(0);
    icon:SetVertexColor(1.0, 1.0, 1.0);
  end
  update()

  b:HookScript("OnClick", function() b:SetChecked(0) end)
  b:SetScript("OnEvent", update)
  b:SetScript("OnShow", update)

  return b
end

function module:CreatePossessControlButtons(bar)
  if not bar.possessButtons then
    bar.possessButtons = { }
    bar.config.possessFrameNames = bar.config.possessFrameNames or { }
    local previous
    local n = NUM_POSSESS_SLOTS
    for i = 1, n do
      local name = bar.config.possessFrameNames[i]
      local f = self:CreatePossessControlButton(bar,i,name)
      bar.possessButtons[i] = f
      bar.config.possessFrameNames[i] = f:GetName()

      local r, c, s = bar:GetButtonGrid()
      local w, h = bar:GetButtonSize()

      local scale = ((h - (n-1)*s)/n)/30
      f:SetScale(scale)

      if previous then
        f:SetPoint("TOP", previous, "BOTTOM", 0, -s/scale)
      else
        f:SetPoint("TOPRIGHT", bar:GetFrame(), "TOPLEFT", -s/scale, 0)
      end
      f:Show()
      previous = f
    end
  end
end

---- Options ----
function module:GetBarOptions(bar)
  return {
    type = "group",
    name = L["Possess Buttons"],
    hidden = function() return bar.config.type ~= moduleID end,
    args = {
    }
  }
end


-- use-count of action IDs
local minActionID = 121
local maxActionID = 132
local ActionIDList = setmetatable( {}, {
  __index = function(self, idx)
    if idx == nil then
      for i = minActionID, maxActionID do
        if rawget(self,i) == nil then
          rawset(self,i,1)
          return i
        end
      end
      error("ran out of 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
})




------ Button class ------
local Button = { }

local function Constructor( self, bar, idx, config )
  self.bar, self.idx, self.config = bar, idx, config

  local barFrame = bar:GetFrame()

  config.name = config.name or ("ReAction_%s_Possess_%d"):format(bar:GetName(),idx)
  self.name = config.name
  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
  
  local f = CreateFrame("CheckButton", self.name, barFrame, "BonusActionButtonTemplate")

  -- TODO: re-implement ActionButton event handlers that don't do secure stuff

  -- this will probably cause taint, using right now for display/debugging purposes
  f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
  f:SetAttribute("action", config.actionID)

  barFrame:SetAttribute("addchild",f)

  self.frame = f
  self:Refresh(bar,idx)

  if not module.db.profile.hideEmptyButtons then
    ActionButton_ShowGrid(self.frame)
  end

  if ReAction.configMode then
    ActionButton_ShowGrid(self.frame)
    module:showActionIDLabel(self)
  end
end

function Button:Destroy()
  local f = self.frame
  f:UnregisterAllEvents()
  f:Hide()
  f:SetParent(UIParent)
  f:ClearAllPoints()
  if self.name then
    _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(bar,idx)
  bar:PlaceButton(self.frame, idx, 36, 36)
end

function Button:GetFrame()
  return self.frame
end

function Button:GetName()
  return self.name
end

function Button:GetActionID()
  return self.config.actionID
end


-- export as a class-factory to module
module.BtnClass = {
  new = function(self, ...)
    local x = { }
    for k,v in pairs(Button) do
      x[k] = v
    end
    Constructor(x, ...)
    return x
  end
}