view main.lua @ 8:c05fd3e18b4f

Version 0.31
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:33:59 +0000
parents f920db5fc6b1
children f3a7bfebc283
line wrap: on
line source
-- main.lua
-- 
-- Top-level file for the ReAction Action Bar add-on
--
-- implemented in terms of the Ace 2 development framework library: http://www.wowace.com
--

-- Ace Library local object initialization
local L       = AceLibrary("AceLocale-2.2"):new("ReAction")
local dewdrop = AceLibrary("Dewdrop-2.0")
local tablet  = AceLibrary("Tablet-2.0")

-- private functions
local function tcopy(t)
  local r = { }
  for k, v in pairs(t) do
    r[k] = (type(v) == "table" and tcopy(v) or v)
  end
  return r
end

-- private constants
local EMPTY_BAR_SLOT = -1

-- key binding label constants
BINDING_HEADER_REACTION                = L["ReAction"]
BINDING_NAME_REACTION_TOGGLELOCK       = L["Toggle ReAction Bar Lock"]
BINDING_NAME_REBOUND_TOGGLEBINDINGMODE = L["Toggle ReBound Keybinding Mode"]


-- main object
local main = AceLibrary("AceAddon-2.0"):new(
  "AceConsole-2.0",
  "AceEvent-2.0",
  "AceDB-2.0",
  "FuBarPlugin-2.0"
)

-- initial non-persistent state
main.locked = true

-- set a global variable for Bindings.xml
ReActionAddOn = main


-- FuBar plugin setup
-- Even if FuBar isn't installed, this gives us a nice minimap-button interface.
main.hasIcon = "Interface\\Icons\\INV_Qiraj_JewelEncased"
main.hasNoColor = true
main.hideMenuTitle = true
main.defaultPosition = "LEFT"
main.defaultMinimapPosition = 240 -- degrees
main.OnMenuRequest = tcopy(ReActionGlobalMenuOptions) -- use a copy, or bar menus will have FuBar inserted items
main.independentProfile = true

-- set the handler for the global bar menu options
-- have to do this after tcopy() above, otherwise it will try to copy the handler object (bad idea)
ReActionGlobalMenuOptions.handler = main




-- Event handling
function main:OnInitialize()
  self:RegisterChatCommand( {L["/reaction"], L["/rxn"]}, ReActionConsoleOptions, "REACTION" )
  self:RegisterDB("ReActionDB","ReActionDBPC")
  self:RegisterDefaults("profile", ReAction_DefaultProfile)
  self:RegisterEvent("PLAYER_REGEN_DISABLED","CombatLockdown")
  self:RegisterEvent("PLAYER_ENTERING_WORLD","HideDefaultBars")
  self:RegisterEvent("EVENT_REBOUND_KEYBINDING_MODE")
end

function main:OnEnable()
  -- this gets called at startup and when the profile is changed
  if self.db.profile.firstRunDone ~= true then
    -- Do some "first-run" setup
    self:StealKeyBindings()
    self.db.profile.firstRunDone = true
    self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars)
  end
  self:DestroyAllBars()
  self:SetupBars()
end

function main:OnDisable()
  self:Lock()
end

function main:OnProfileEnable()
  -- for profile switching
  self:OnEnable()
end

function main:CombatLockdown()
  if not self:IsLocked() then
    self:Lock()
    ReBound:Disable()
    UIErrorsFrame:AddMessage(L["ReAction bars locked when in combat"])
  end
end

function main:EVENT_REBOUND_KEYBINDING_MODE(enabled)
  for _, bar in pairs(self.bars) do
    for __, button in pairs(bar.buttons) do
      if button and button ~= EMPTY_BAR_SLOT then
        button:TempShow(enabled)
      end
    end
  end
end


-- FuBar plugin methods
function main:OnTooltipUpdate()
	local c = tablet:AddCategory("columns", 2)
	c:AddLine("text", L["Bar lock"], "text2", self.locked and ("|cffff0000"..L["Locked"].."|r") or ("|cffffcc00"..L["Unlocked"].."|r"))
  c:AddLine("text", L["Button lock"], "text2", LOCK_ACTIONBAR == "1" and ("|cffcc0000"..L["Locked"].."|r") or ("|cff00cc00"..L["Unlocked"].."|r"))
  c:AddLine("text", L["Kebinding mode"], "text2", ReBound:IsEnabled() and ("|cff33ff33"..L["On"].."|r") or ("|cffffcc00"..L["Off"].."|r"))
	tablet:SetHint(L["|cffffcc00Shift-Click for bar lock|n|cff33ff33Alt-Click|r for keybindings|nRight-click for menu"])
end

function main:OnClick(button)
	if IsShiftKeyDown() then
	  self:ToggleLocked()
    self:UpdateDisplay()
	elseif IsAltKeyDown() then
    ReBound:ToggleEnabled()
    self:UpdateDisplay()
  end
end


-- lock/unlock bars
function main:SetLocked( lock )
  if lock ~= self.locked then
    if not lock and InCombatLockdown() then
      UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT)
    else
      self.locked = lock and true or false -- force data integrity
      for _, bar in pairs(self.bars) do
        if bar ~= EMPTY_BAR_SLOT then
          if self.locked then 
            bar:HideControls()
            -- close any dewdrop menu owned by the bar
            if bar:GetControlFrame() == dewdrop:GetOpenedParent() then
              dewdrop:Close()
            end
          else
            bar:ShowControls() 
          end
        end
      end
    end
  end
end

function main:IsLocked()
  return self.locked
end

function main:Lock()
  self:SetLocked(true)
end

function main:Unlock()
  self:SetLocked(false)
end

function main:ToggleLocked()
  main:SetLocked( not(self.locked) )
end



-- Hide the default Blizzard main bar artwork
function main:HideArt()
  if self.db.profile.hideArt then
    -- the pet bar is a child of MainMenuBar, and can't be hidden. Need to reparent it
    PetActionBarFrame:SetParent(UIParent)
    MainMenuBar:Hide() -- this also hides the bags, xp bar, lag meter, and micro menu buttons.
    -- these two are the pet bar background
    -- unfortunately UIParent_ManageFramePositions() shows and hides these too
    -- so they get reparented to MainMenuBar
    SlidingActionBarTexture0:SetParent(MainMenuBar)
    SlidingActionBarTexture1:SetParent(MainMenuBar)
  else
    SlidingActionBarTexture0:SetParent(PetActionBarFrame)
    SlidingActionBarTexture1:SetParent(PetActionBarFrame)
    MainMenuBar:Show()
  end
end

function main:IsArtHidden()
  return self.db.profile.hideArt
end

function main:SetHideArt( hide )
  if InCombatLockdown() then
    UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT)
  else
    self.db.profile.hideArt = hide and true or false -- force data integrity
    self:HideArt()
  end
end

function main:ToggleHideArt()
  self:SetHideArt( not self:IsArtHidden() )
end



-- Hide default Blizzard bars
local blizzDefaultBars = {
  ActionButton1,
  ActionButton2,
  ActionButton3,
  ActionButton4,
  ActionButton5,
  ActionButton6,
  ActionButton7,
  ActionButton8,
  ActionButton9,
  ActionButton10,
  ActionButton11,
  ActionButton12,
  PetActionButton1,
  PetActionButton2,
  PetActionButton3,
  PetActionButton4,
  PetActionButton5,
  PetActionButton6,
  PetActionButton7,
  PetActionButton8,
  PetActionButton9,
  PetActionButton10,
  -- NOT the PetActionBarFrame, though - we need that to auto-hide/show our pet action bars
  MainMenuBarPageNumber,
  ActionBarUpButton,
  ActionBarDownButton,
  BonusActionBarFrame,
  ShapeshiftBarFrame,
  MultiBarLeft,
  MultiBarRight,
  MultiBarBottomLeft,
  MultiBarBottomRight,
}

function main:StealKeyBindings()
  -- steal the keybindings of the main action bar and assign them to rebar 1, buttons 1-12
  for i = 1, 12 do
    -- TODO: when we convert to override bindings
  end
end

local function disableUIOptions()
  -- disable the buttons to hide/show the blizzard multiaction bars
  -- see UIOptionsFrame.lua and .xml
  -- This is called every time the options panel is shown, after it is set up
  for _, idx in pairs( { 33, 34, 35, 36, 37, 40 } ) do
    local f = getglobal("UIOptionsFrameCheckButton"..idx)
    f.disabled = true
    OptionsFrame_DisableCheckBox(f)
    f:SetChecked(false)
  end
end

function main:HideDefaultBars()
  for _, f in pairs(blizzDefaultBars) do
    f:Hide()
    f:ClearAllPoints()
    f:SetParent(ReAction.recycler)
    f:SetPoint("TOPLEFT")
  end

  MainMenuBar:SetFrameStrata("LOW") -- otherwise it appears on top of bars, if it isn't hidden
  hooksecurefunc("UIOptionsFrame_Load",disableUIOptions)
end


-- Reset bars to blizzard defaults
function main:ResetBars()
  if InCombatLockdown() then
    UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT)
  else
    self:DestroyAllBars()
    self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars)
    self:SetupBars()
  end
end


-- re-sync action IDs
function main:ResyncActionIDs()
  -- TODO
end



-- Bar manipulation
main.bars    = { }

function main:DestroyAllBars()
  -- destroy any existing bars
  for id = 1, table.maxn(self.bars) do
    self:DestroyBar(id)
  end
end


function main:SetupBars()
  -- hide the default Blizzard art, if configued
  self:HideArt()

  -- set up the bars from the profile
  -- note the use of table.maxn rather than # or ipairs: 
  -- our array of bars can in fact contain holes
  for id = 1, table.maxn(self.db.profile.bars) do
    local config = self.db.profile.bars[id]
    if config then
      self:CreateBar(config, id)
    end
  end
  
  -- anchor the bars, have to do this in a second pass because
  -- they might be anchored to each other in a non-ordered way
  for _, bar in pairs(self.bars) do
    if bar ~= EMPTY_BAR_SLOT then
      bar:ApplyAnchor()
    end
  end
end

function main:CreateBar( config, id )
  local bar = ReBar:new(config, id)
  local buttonType = ReAction:GetButtonType(config.btnConfig.subtype)

  if buttonType then
    self.bars[id] = bar
    self.db.profile.bars[id] = config

    -- initialize dewdrop menu
    local cf = bar:GetControlFrame()
    dewdrop:Register(cf, 
      'children', 
      function()
        dewdrop:FeedAceOptionsTable(ReActionGlobalMenuOptions)
        dewdrop:FeedAceOptionsTable(GenerateReActionBarOptions(bar,self))
        dewdrop:FeedAceOptionsTable(buttonType:GenerateOptionsTable(config.btnConfig, function() return bar:GetButtonList() end))
      end,
      'cursorX', true, 
      'cursorY', true
    )

    bar:GetControlFrame():SetScript("OnClick", 
      function(btn)
        if btn == "RightButton" then
          dewdrop:Open(cf)
        end
      end
    )

    if not self.locked then
      bar:ShowControls()
    end
    return bar
  else
    if bar then
      bar:Destroy()
    end
    error(L["Tried to create a button of unknown type"])
  end
end

function main:DestroyBar( id )
  local bar = self.bars[id]
  if bar and bar ~= EMPTY_BAR_SLOT then 
    local cf = bar:GetControlFrame()
    if cf == dewdrop:GetOpenedParent() then
      dewdrop:Close()
      dewdrop:Unregister(cf)
    end
    bar:Destroy()
    -- we can't do tremove because each bar ID is encoded into the
    -- frame names as they're created. Need a blank entry in the table.
    -- The nice thing is that table.insert in NewBar() will automatically
    -- find the lowest numbered nil slot.
    self.bars[id] = EMPTY_BAR_SLOT
  end
end

function main:NewBar( type )
  if InCombatLockdown() then
    UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT)
  else
    local t = ReAction:GetButtonType(type)
    if t then
      local c = tcopy(ReAction_DefaultBarConfig["ReAction"][type])
      local id = nil
      for i = 1, table.maxn(self.bars) + 1 do  -- there may be holes, so #self.bars won't work
        if self.bars[i] == nil or self.bars[i] == EMPTY_BAR_SLOT then
          id = i
          break
        end
      end
      local bar = self:CreateBar(c, id)
      bar:ApplyAnchor()
      self:Unlock()
    end
  end
end


function main:DeleteBar(id)
  if InCombatLockdown() then
    UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT)
  else
    if self.bars[id] then
      self:DestroyBar(id)
      self.db.profile.bars[id] = nil
    end
  end
end

function main:ToggleIds()
  if self.showIds then
    ReAction:HideAllIds()
  else
    ReAction:ShowAllIds()
  end
  self.showIds = not self.showIds
end

function main:AreIdsVisible()
  return self.showIds
end