Quantcast
--[[
	PowerAurasButtons

	Module: Buttons
--]]
-- Create module frames.
local CoreFrame        = PowerAurasButtons;
local ModuleFrame      = CoreFrame:RegisterModule("Buttons", { "Auras" });
local Modules          = CoreFrame.Modules;
--[[
----------------------------------------------------------------------------------------------------
Variables
	Buttons            Stores all registered buttons in a table.
	ButtonsBySlot      All button objects by their action/slot ID.
	ButtonData         Stores the switches for each button - whether it should glow, etc.
	ButtonQueue        Stores a queue of buttons to be updated.
	ButtonsQueued      Boolean value set to true when buttons are queued.
	ButtonQueueAll     Updates all buttons next time.
	ThrottleTimer      Stores the current throttle timer for mass updates.
----------------------------------------------------------------------------------------------------
--]]
local Buttons          = {};
local ButtonsBySlot    = {};
local ButtonData       = {};
local ButtonQueue      = {};
local ButtonsQueued    = false;
local ButtonQueueAll   = false;
local ThrottleTimer    = 0;
-- Upvalues.
local unpack, setmetatable, ActionButton_ShowOverlayGlow, ActionButton_HideOverlayGlow, wipe, type, GetActionInfo,
	GetMacroSpell, GetMacroItem, pairs, IsSpellOVerlayed, hooksecurefunc = unpack, setmetatable,
	ActionButton_ShowOverlayGlow, ActionButton_HideOverlayGlow, wipe, type, GetActionInfo, GetMacroSpell, GetMacroItem,
	pairs, IsSpellOVerlayed, hooksecurefunc;
-- Caches.
local spellcache = setmetatable({}, {__index=function(t,v) local a = {GetSpellInfo(v)} if GetSpellInfo(v) then t[v] = a end return a end});
local function GetSpellInfo(a)
    return unpack(spellcache[a]);
end
local itemcache = setmetatable({}, {__index=function(t,v) local a = {GetItemInfo(v)} if GetItemInfo(v) then t[v] = a end return a end});
local function GetItemInfo(a)
    return unpack(spellcache[a]);
end
--[[
----------------------------------------------------------------------------------------------------
GetButtons

Returns all the buttons!
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:GetButtons()
	return Buttons;
end
--[[
----------------------------------------------------------------------------------------------------
OnButtonUpdate

Event handler for button updates. Updates glows depending on assigned auras, etc.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:OnButtonUpdate(button)
	-- Only bother updating if we can see it.
	if(not button or not button:IsShown()) then return; end
	-- Test the button for glowability.
	ModuleFrame:ProcessButtonActions(button);
	-- Fire button update event.
	CoreFrame:FireModuleEvent("OnButtonUpdate", button:GetName());
	-- So, does the glow need showing or hiding?
	if(ModuleFrame:GetButtonData(button:GetName())["glow"]) then
		-- Show the glow.
		ActionButton_ShowOverlayGlow(button);
	else
		-- Hide the glow.
		ActionButton_HideOverlayGlow(button);
	end
end
--[[
----------------------------------------------------------------------------------------------------
GetButtonData

Retrieves the button data table for the given button ID. Returns nil on failure.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:GetButtonData(buttonID)
	-- Go.
	return ButtonData[buttonID] or nil;
end
--[[
----------------------------------------------------------------------------------------------------
ProcessButtonActions

Processes all of the assigned actions on a button. This will determine whether a button should
be glowing, showing displays, etc.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:ProcessButtonActions(button)
	-- Few locals.
	local buttonID = button:GetName();
	-- Get the button data table if it exists. Otherwise, make a new one. We recycle the old one
	-- so the memory size won't fluctuate.
	local buttonData = ButtonData[buttonID] or {};
	-- Wipe the data.
	wipe(buttonData);
	-- Fire button processing event.
	CoreFrame:FireModuleEvent("OnButtonProcess", buttonID);
	-- Get the non blizzard auras.
	local CustomAuras, BlizzAuras = Modules.Auras:GetAuras();
	-- More locals.
	local buttonAction, buttonActionType, buttonActionID, buttonMacro, displayCount;
	-- Get the button action ID.
	buttonAction = button._state_action or button.action;
	-- Action needs to be integer.
	if(not buttonAction or type(buttonAction) ~= "number") then
		-- Action isn't valid.
		ButtonData[buttonID] = buttonData;
		return;
	end
	-- Make sure button is cached by slot.
	ButtonsBySlot[buttonAction] = button;
	-- Get the button action data.
	buttonActionType, buttonActionID = GetActionInfo(buttonAction);
	-- Get macro names if needed.
	if(buttonActionType == "macro") then
		buttonMacro = GetMacroSpell(buttonActionID) or GetMacroItem(buttonActionID);
	end
	-- Right, first off we need to go over all the auras see if they're linked to this one.
	for auraID, _ in pairs(CustomAuras) do
		-- Aura needs to be active.
		if(Modules.Auras:IsAuraShown(auraID)) then
			-- And go over the actions.
			for auraActionID, auraActionData in pairs(Modules.Auras:GetAuraActions(auraID)) do
				-- Action needs to be a valid ID (> 0)
				if(auraActionData["id"] and auraActionData["id"] > 0) then
					-- If the type/data keys match, or this is a macro/spell combo then continue.
					if(buttonActionType == auraActionData["type"]
					or (buttonActionType == "macro" and auraActionData["type"] == "spell")
					or (buttonActionType == "macro" and auraActionData["type"] == "item")) then
						-- Compare ID's. If they match, we're golden. If they don't, do macro
						-- comparisons.
						if((buttonActionID == auraActionData["id"]
						and buttonActionType == auraActionData["type"])
						or buttonMacro and (auraActionData["type"] == "spell"
						and GetSpellInfo(auraActionData["id"]) == buttonMacro
						or auraActionData["type"] == "item"
						and GetItemInfo(auraActionData["id"]) == buttonMacro)) then
							-- Enable glows if the action says so.
							Modules.Auras:MergeAuraAction(buttonData, auraActionData);
							-- Fire the OnAuraDisplay event.
							CoreFrame:FireModuleEvent("OnButtonDisplayAura", buttonID, auraID,
								auraActionData, auraActionID);
						end
					end
				end
			end
		end
	end
	-- Blizzard auras need checking if glow isn't on, and if enabled.
	if(CoreFrame:GetModuleSetting("Buttons", "ShowBlizzardGlows")) then
		if(not buttonData["glow"] and buttonActionType == "spell"
		and IsSpellOverlayed(buttonActionID)) then
			-- It needs to glow.
			buttonData["glow"] = true;
		elseif(not buttonData["glow"] and buttonActionType == "macro") then
			-- Macros should glow too.
			buttonMacro = GetMacroSpell(buttonActionID) or GetMacroItem(buttonActionID);
			-- Loop over active Blizzard auras.
			for blizzAuraID, _ in pairs(BlizzAuras) do
				-- Check ID.
				if(not buttonData["glow"] and buttonMacro
				and (buttonMacro == GetSpellInfo(blizzAuraID)
				or GetItemInfo(blizzAuraID) == buttonMacro)) then
					-- Yeah, it's a match. Timers/Stacks aren't on for blizz ones.
					buttonData["glow"] = true;
					break; -- Break early, it doesn't matter if any others are glowing or not.
				end
			end
		end
	end
	-- Update.
	ButtonData[buttonID] = buttonData;
end
--[[
----------------------------------------------------------------------------------------------------
UpdateAllButtons

Fired when OnAuraShow/OnAuraHide are called. Performs a mass button update.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:UpdateAllButtons()
	-- Queue all buttons for an update.
	ButtonQueueAll = true;
	ButtonsQueued = true;
end
--[[
----------------------------------------------------------------------------------------------------
UpdateButton

Registers a single button for an update.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:UpdateButton(button)
	-- Register button for update.
	if(not ButtonQueueAll and Buttons[button:GetName()]) then
		ButtonQueue[button:GetName()] = button;
	end
	-- Flag queue.
	ButtonsQueued = true;
end
--[[
----------------------------------------------------------------------------------------------------
OnUpdate

Acts as our function for throttling update requests. It's called OnUpdate but is only present
while we're throttling - we unregister it after.
----------------------------------------------------------------------------------------------------
--]]
do
local throttle = 0;

function ModuleFrame:OnUpdate(elapsed)
	throttle = throttle+elapsed;
	if(throttle >= 1) then

		throttle = throttle-1;
	end
	-- Update time elapsed.
	ThrottleTimer = ThrottleTimer + (ButtonsQueued and elapsed or 0);
	-- Time up?
	if(ThrottleTimer < CoreFrame:GetModuleSetting("Buttons", "Throttle")) then return; end
	-- Process queue.
	for buttonID, state in pairs((ButtonQueueAll and Buttons or ButtonQueue)) do
		-- Increment counter.
		if(state) then
			-- Remove from queue...
			ButtonQueue[buttonID] = false;
			-- This one is weird, since the ButtonQueue stores a state boolean but ButtonQueueAll iterates over the
			-- Buttons table directly.
			local button = (ButtonQueueAll and state or Buttons[buttonID]);
			-- If button is true, resolve it to an actual button.
			if(button == true) then
				Buttons[buttonID] = _G[buttonID] or true;
				button = Buttons[buttonID];
			end
			-- Does button exist now?
			if(button and button ~= true) then
				ModuleFrame:OnButtonUpdate(button);
			end
		end
	end
	-- Clear booleans.
	ButtonsQueued = false;
	ButtonQueueAll = false;
	-- Reset throttle.
	ThrottleTimer = ThrottleTimer - CoreFrame:GetModuleSetting("Buttons", "Throttle");
end

end
--[[
----------------------------------------------------------------------------------------------------
RegisterButtons

Registers buttons into our button array for glow activation purposes.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:RegisterButtons(key, count)
	-- Register, nils included (it's a Dominos thing)
	local button = nil;
	for i=1,(count or 12) do
		-- Register it.
		if(not CoreFrame:GetModuleSetting("Buttons", "IgnoredButtons")[key .. i]) then
			Buttons[key .. i] = _G[key .. i] or true;
		end
	end
end
--[[
----------------------------------------------------------------------------------------------------
OnActionCreate

Fired when an action is created. Used to set defaults in the newly made action ID.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:OnActionCreate(auraID, actionID)
	-- Get action.
	local actionData = Modules.Auras:GetAuraAction(auraID, actionID);
	-- Write.
	actionData["glow"] = true;
	-- Save.
	Modules.Auras:SetAuraAction(auraID, actionID, actionData);
end
--[[
----------------------------------------------------------------------------------------------------
IsEnabled

Checks to see if the module is enabled.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:IsEnabled()
	return true;
end
--[[
----------------------------------------------------------------------------------------------------
FixSettings

Fixes all saved variables and migrates older ones across.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:FixSettings(force)
	-- Do the module settings exist?
	if(not CoreFrame:GetSetting("Buttons") or force) then
		-- We'd best fix that then.
		PowerAurasButtons_SettingsDB["Buttons"] = {
			["Throttle"] = 0.05,
			["RegisterBlizzardButtons"] = true,
			["ShowBlizzardGlows"] = true,
			["IgnoredButtons"] = {},
		};
	end
	-- Compatibility.
	if(PowerAurasButtons_SettingsDB["Buttons"]["IgnoredButtons"] == nil) then
		PowerAurasButtons_SettingsDB["Buttons"]["IgnoredButtons"] = {};
	end
end
--[[
----------------------------------------------------------------------------------------------------
OnInitialize

Fired by the module handler. Put all the loading code into here.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:OnInitialize()
	-- Fix settings first.
	ModuleFrame:FixSettings();
	-- Register the needed buttons.
	if(Dominos) then
		-- Dominos reuses the Blizzard AB's and creates 60 of its own.
		CoreFrame:Debug("Dominos detected");
		ModuleFrame:RegisterButtons("DominosActionButton", 60);
	elseif(RazerNaga) then
		-- Register additional buttons. Dominos style.
		ModuleFrame:RegisterButtons("RazerNagaActionButton", 60);
	elseif(LibStub) then
		-- Bartender4 is a tad more tricky. It uses LAB which makes buttons as needed.
		-- So we need to check for LAB (and LibStub), then scan all loaded buttons and make
		-- sure future ones are added.
		local LAB = LibStub("LibActionButton-1.0", true);
		if(LAB) then
			CoreFrame:Debug("Bartender4/LibActionButton detected");
			-- LibActionButton found. Go over all of the buttons.
			for button in pairs(LAB:GetAllButtons()) do
				Buttons[button:GetName()] = button;
				-- Store by slot too.
				if(button._state_action) then
					ButtonsBySlot[button._state_action] = button;
				end
			end
			-- In addition, make sure this applies to future buttons.
			LAB:RegisterCallback("OnButtonCreated", function(_, button)
				Buttons[button:GetName()] = button;
				-- Store by slot too.
				if(button._state_action) then
					ButtonsBySlot[button._state_action] = button;
				end
			end);
			-- Add a button update hook.
			LAB:RegisterCallback("OnButtonUpdate", function(_, button)
				if(not button:IsShown()) then return; end
				ModuleFrame:UpdateButton(button);
			end);
		end
	end
	-- Odds are you're using the default buttons if you're not using Dominos/BT.
	-- Register them if not told otherwise.
	if(CoreFrame:GetModuleSetting("Buttons", "RegisterBlizzardButtons") or Dominos or RazerNaga) then
		CoreFrame:Debug("Registering Blizzard buttons");
		ModuleFrame:RegisterButtons("ActionButton");
		ModuleFrame:RegisterButtons("BonusActionButton");
		ModuleFrame:RegisterButtons("MultiBarRightButton");
		ModuleFrame:RegisterButtons("MultiBarLeftButton");
		ModuleFrame:RegisterButtons("MultiBarBottomRightButton");
		ModuleFrame:RegisterButtons("MultiBarBottomLeftButton");
	end
	-- If you use Dominos or have the Blizzard buttons on, you need this.
	if(Dominos or RazerNaga or CoreFrame:GetModuleSetting("Buttons", "RegisterBlizzardButtons")) then
		-- Hook for button updates.
		hooksecurefunc("ActionButton_Update", function(button)
			if(not button:IsShown()) then return; end
			ModuleFrame:UpdateButton(button);
		end);
	end
--	-- Update only if slot data changes.
--	CoreFrame:RegisterBlizzEventListener("ACTIONBAR_SLOT_CHANGED", ModuleFrame, function(self, id)
--		if(id == 0 or not ButtonsBySlot[id]) then
--			ModuleFrame:UpdateAllButtons();
--		else
--			ModuleFrame:UpdateButton(ButtonsBySlot[id]);
--		end
--	end);
	-- Create some events for modules to hook on to.
	CoreFrame:RegisterModuleEvent("OnButtonUpdate");
	CoreFrame:RegisterModuleEvent("OnButtonProcess");
	CoreFrame:RegisterModuleEvent("OnButtonDisplayAura");
	-- Register OnAuraShow/OnAuraHide.
	CoreFrame:RegisterModuleEventListener("OnAuraShow", ModuleFrame, ModuleFrame.UpdateAllButtons);
	CoreFrame:RegisterModuleEventListener("OnAuraHide", ModuleFrame, ModuleFrame.UpdateAllButtons);
	CoreFrame:RegisterModuleEventListener("OnActionCreate", ModuleFrame);
	-- Updates are throttled and processed in an update loop.
	ModuleFrame:SetScript("OnUpdate", ModuleFrame.OnUpdate);
	-- Done.
	return true;
end