view ReAction.lua @ 53:7e09c02ae620

Pet Action support
author Flick <flickerstreak@gmail.com>
date Fri, 25 Apr 2008 20:35:55 +0000
parents c3c64e2def50
children 20003239af0b
line wrap: on
line source
-- ReAction.lua
-- See modules/ReAction_ModuleTemplate for Module API listing
-- See Bar.lua for Bar object listing

------ CORE ------
local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction",
  "AceConsole-3.0",
  "AceEvent-3.0"
)
ReAction.version = GetAddOnMetadata("ReAction","Version")
ReAction.revision = tonumber(("$Revision$"):match("%d+"))

------ GLOBALS ------
_G["ReAction"] = ReAction

------ DEBUGGING ------
ReAction.debug = true
local dbprint
if ReAction.debug then
  dbprint = function(msg)
    DEFAULT_CHAT_FRAME:AddMessage(msg)
  end
  --seterrorhandler(dbprint)
else
  dbprint = function() end
end
ReAction.dbprint = dbprint

------ LIBRARIES ------
local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
ReAction.L = L

------ PRIVATE ------
local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod, SlashHandler
do
  local pcall = pcall
  local geterrorhandler = geterrorhandler

  SelectBar = function(x)
    local bar, name
    if type(x) == "string" then
      name = x
      bar = ReAction:GetBar(name)
    else
      for k,v in pairs(ReAction.bars) do
        if v == x then
          name = k
          bar = x
        end
      end
    end
    return bar, name
  end

  DestroyBar = function(x)
    local bar, name = SelectBar(x)
    if name and bar then
      ReAction.bars[name] = nil
      ReAction:CallMethodOnAllModules("RemoveFromBar", bar)
      bar:Destroy()
    end
  end

  InitializeBars = function ()
    if not(ReAction.inited) then
      for name, config in pairs(ReAction.db.profile.bars) do
        if config then
          ReAction:CreateBar(name, config)
        end
      end
      ReAction:CallMethodOnAllBars("ApplyAnchor") -- re-anchor in the case of oddball ordering
      ReAction.inited = true
    end
  end

  TearDownBars = function()
    for name, bar in pairs(ReAction.bars) do
      if bar then
        ReAction.bars[name] = DestroyBar(bar)
      end
    end
    ReAction.inited = false
  end

  DeepCopy = function(x)
    if type(x) ~= "table" then
      return x
    end
    local r = {}
    for k,v in pairs(x) do
      r[k] = DeepCopy(v)
    end
    return r
  end

  SafeCall = function(f, ...)
    if f then
      local success, err = pcall(f,...)
      if not success then
        geterrorhandler()(err)
      end
    end
  end

  CheckMethod = function(m)
    if type(m) == "function" then
      return m
    end
    if type(m) ~= "string" then
      error("Invalid method")
    end
  end

  SlashHandler = function(option)
    if option == "config" then
      ReAction:ShowConfig()
    elseif option == "layout" then
      ReAction:ShowLayout()
    elseif option == "unlock" then
      ReAction:SetConfigMode(true)
    elseif option == "lock" then
      ReAction:SetConfigMode(false)
    else
      ReAction:Print(("%3.1f.%d"):format(ReAction.version,ReAction.revision))
      ReAction:Print("/reaction config")
      ReAction:Print("/reaction layout")
      ReAction:Print("/reaction lock")
      ReAction:Print("/reaction unlock")
    end
  end
end


------ HANDLERS ------
function ReAction:OnInitialize()
  self.db = LibStub("AceDB-3.0"):New("ReAction_DB", 
    { 
      profile = {
        bars = { },
        defaultBar = { }
      }
    }
    -- default profile is character-specific
  )
  self.db.RegisterCallback(self,"OnProfileChanged")
  self.db.RegisterCallback(self,"OnProfileReset","OnProfileChanged")
  self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
  self:RegisterChatCommand("reaction", SlashHandler)
  self:RegisterChatCommand("rxn", SlashHandler)
  self:RegisterEvent("PLAYER_REGEN_DISABLED")

  self.bars = {}
  self.options = {}
  self.defaultBarConfig = {}

  self:RegisterOptions("global", self, {
    unlock = {
      type     = "toggle",
      handler  = module,
      name     = L["Unlock Bars"],
      desc     = L["Unlock bars for dragging and resizing with the mouse"],
      get      = function() return self.configMode end,
      set      = function(info, value) self:SetConfigMode(value) end,
      disabled = InCombatLockdown,
      order    = 1
    },
  })

end

function ReAction:OnEnable()
  InitializeBars()
end

function ReAction:OnDisable()
  TearDownBars()
end

function ReAction:OnProfileChanged()
  TearDownBars()
  InitializeBars()
end

function ReAction:OnModuleEnable(module)
  if module.ApplyToBar then
    for _, b in pairs(bars) do
      if b then
        module:ApplyToBar(b)
      end
    end
  end
end

function ReAction:OnModuleDisable(module)
  if module.RemoveFromBar then
    for _, b in pairs(bars) do
      if b then
        module:RemoveFromBar(b)
      end
    end
  end
end

function ReAction:PLAYER_REGEN_DISABLED()
  if self.configMode == true then
    UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."])
    self:SetConfigMode(false)
  end
end



------ API ------
function ReAction:CallMethodOnAllModules(method, ...)
  local m = CheckMethod(method)
  for _, x in self:IterateModules() do
    if x then
      SafeCall(m or x[method], x, ...)
    end
  end
end

function ReAction:CallMethodOnAllBars(method,...)
  local m = CheckMethod(method)
  for _, x in pairs(self.bars) do
    if x then
      SafeCall(m or x[method], x, ...)
    end
  end
end

function ReAction:CallModuleMethod(modulename, method, ...)
  local m = self:GetModule(modulename,true)
  if not m then
    LoadAddOn(("ReAction_%s"):format(modulename))
    m = self:GetModule(modulename,true)
    if m then
      dbprint(("succesfully loaded LOD module: %s"):format(modulename))
    end
  end
  if m then
    if type(m) == "table" and type(m[method]) == "function" then
      m[method](m,...)
    else
      dbprint(("Bad call '%s' to %s module"):format(tostring(method),modulename));
    end
  else
    self:Print(("Module '%s' not found"):format(tostring(modulename)))
  end
end


function ReAction:CreateBar(name, ...)
  local config = select(1,...)
  if config and type(config) ~= "table" then
    bartype = select(1,...)
    if type(bartype) ~= "string" then
      error("ReAction:CreateBar() - first argument must be a config table or a default config type string")
    end
    config = self.defaultBarConfig[bartype]
    if not config then
      error(("ReAction:CreateBar() - unknown bar type '%s'"):format(bartype))
    end
    config = DeepCopy(config)
    config.btnRows    = select(2,...) or config.btnRows    or 1
    config.btnColumns = select(3,...) or config.btnColumns or 12
    config.btnWidth   = select(4,...) or config.btnWidth   or 36
    config.btnHeight  = select(4,...) or config.btnHeight  or 36
    config.spacing    = select(5,...) or config.spacing    or 3
    config.width      = config.width or config.btnColumns*(config.btnWidth + config.spacing) + 1
    config.height     = config.height or config.btnRows*(config.btnHeight + config.spacing) + 1
    config.anchor     = config.anchor or "BOTTOM"
    config.anchorTo   = config.anchorTo or "UIParent"
    config.relativePoint = config.relativePoint or "BOTTOM"
    config.y          = config.y or 200
    config.x          = config.x or 0
  end
  local profile = self.db.profile
  config = config or DeepCopy(profile.defaultBar)
  prefix = prefix or L["Bar "]
  if not name then
    i = 1
    repeat
      name = prefix..i
      i = i + 1
    until self.bars[name] == nil
  end
  profile.bars[name] = profile.bars[name] or config
  local bar = self.Bar:new( name, profile.bars[name] )  -- ReAction.Bar defined in Bar.lua
  self:CallMethodOnAllModules("ApplyToBar", bar)
  self.bars[name] = bar
  self.callbacks:Fire("OnCreateBar", bar)
  if self.configMode then
    bar:ShowControls(true)
  end

  return bar
end

function ReAction:EraseBar(x)
  local bar, name = SelectBar(x)
  if name and bar then
    DestroyBar(bar)
    self.db.profile.bars[name] = nil
    self:CallMethodOnAllModules("EraseBarConfig", name)
    self.callbacks:Fire("OnEraseBar",name)
  end
end

function ReAction:GetBar(name)
  return self.bars[name]
end

function ReAction:RenameBar(x, newname)
  local bar, name = SelectBar(x)
  if bar and name and newname then
    if self.bars[newname] then
      UIErrorsFrame:AddMessage(("%s ('%s')"):format(L["ReAction: name already in use"],newname))
    else
      self.bars[newname] = self.bars[name]
      self.bars[name] = nil
      bar:SetName(newname or "")
      local cfg = self.db.profile.bars
      cfg[newname], cfg[name] = cfg[name], nil
      self:CallMethodOnAllModules("RenameBarConfig", name, newname)
      self.callbacks:Fire("OnRenameBar", name, newname)
    end
  end
end

function ReAction:RegisterBarType( name, config, isDefaultChoice )
  self.defaultBarConfig[name] = config
  if isDefaultChoice then
    self.defaultBarConfigChoice = name
  end
  self:RefreshOptions()
end

function ReAction:UnregisterBarType( name )
  self.defaultBarConfig[name] = nil
  if self.defaultBarConfigChoice == name then
    self.defaultBarConfigChoice = nil
  end
  self:RefreshOptions()
end

-- See modules/ReAction_ConfigUI for valid options contexts.
function ReAction:RegisterOptions(context, module, opts)
  if module == nil or context == nil then
    error("ReAction:RegisterOptions requires a module object and context ID")
  end
  if not self.options[context] then
    self.options[context] = {}
  end
  self.options[context][module] = opts
  self.callbacks:Fire("OnOptionsRegistered", context, module, opts)
end

function ReAction:GetOptions(context)
  if context then
    if not self.options[context] then
      self.options[context] = { }
    end
    return self.options[context]
  end
end

function ReAction:RefreshOptions()
  self.callbacks:Fire("OnOptionsRefreshed")
end

function ReAction:SetConfigMode( mode )
  self:CallMethodOnAllBars("ShowControls",mode)
  self:CallMethodOnAllModules("ApplyConfigMode",mode,self.bars)
  self.configMode = mode
end

function ReAction:ShowConfig()
  self:CallModuleMethod("ConfigUI","OpenConfig")
end

function ReAction:ShowLayout()
  self:CallModuleMethod("ConfigUI","LaunchLayoutEditor")
end