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.
	ButtonData         Stores the switches for each button - whether it should glow, etc.
	ThrottleActive     Stores the current throttle state.
	ThrottlePending    Stores the status of any pending mass updates.
	ThrottleTimer      Stores the current throttle timer for mass updates.
----------------------------------------------------------------------------------------------------
--]]
local Buttons          = {};
local ButtonData       = {};
local ThrottleActive   = nil;
local ThrottlePending  = nil;
local ThrottleTimer    = 0;
--[[
----------------------------------------------------------------------------------------------------
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 button data related locals.
	local buttonID = button:GetName();
	if(not ButtonData[buttonID]) then ButtonData[buttonID] = {}; end
	wipe(ButtonData[buttonID]);
	-- Fire button processing event.
	CoreFrame:FireModuleEvent("OnButtonProcess", buttonID);
	-- Determine the action key from the button.
	local buttonAction = button._state_action or button.action;
	-- Action must be a number.
	if(not buttonAction or type(buttonAction) ~= "number") then return; end
	local buttonActionType, buttonActionID = GetActionInfo(buttonAction);
	-- Make sure we got the stuff.
	if(not buttonActionType or not buttonActionID) then return; end
	-- Key it.
	local key = strupper(strsub(buttonActionType, 0, 1)) .. buttonActionID;
	-- Blizzard auras need checking first off, but only if enabled.
	if(CoreFrame:GetModuleSetting("Buttons", "ShowBlizzardGlows")) then
		if(buttonActionType == "spell" and IsSpellOverlayed(buttonActionID)) then
			-- It needs to glow.
			ButtonData[buttonID]["glow"] = true;
		elseif(buttonActionType == "macro") then
			-- Macros should glow too.
			local buttonMacro = GetMacroSpell(buttonActionID);
			-- Loop over active Blizzard auras.
			local _, BlizzAuras = Modules.Auras:GetAuras();
			for blizzAuraID, _ in pairs(BlizzAuras) do
				-- Check ID.
				if(not ButtonData[buttonID]["glow"] and buttonMacro
				and buttonMacro == GetSpellInfo(blizzAuraID)) then
					-- Yeah, it's a match. Timers/Stacks aren't on for blizz ones.
					ButtonData[buttonID]["glow"] = true;
				end
			end
		end
	end
	-- Does this key exist anywhere?
	local auras = Modules.Auras:GetActionTable(key);
	if(not auras) then
		-- It's not necessarily the end, is this a macro?
		if(buttonActionType == "macro") then
			-- In that case, get the spell or item name the button represents.
			local buttonMacro = GetMacroSpell(buttonActionID) or GetMacroItem(buttonActionID);
			local actionData;
			-- Go over all of the actions. Slow, I know, but decent enough for now...
			for actionKey, auras in pairs(Modules.Auras:GetActionTable()) do
				-- And for this one, figure out what the hell spell or item name it represents.
				local actionType = strsub(actionKey, 0, 1);
				actionData = (actionType == "S"
					and GetSpellInfo(tonumber(strsub(actionKey, 2))))
					or (actionType == "I"
					and GetSpellInfo(tonumber(strsub(actionKey, 2))))
					or nil;
				if(actionData and actionData == buttonMacro) then
					-- Right, it's a match. A complicated match. Go over the auras.
					for _, auraID in pairs(auras) do
						-- I hate duplicating code but I don't want to create tables.
						local auraActionID = tonumber(strsub(auraID, 4, 5));
						auraID = tonumber(strsub(auraID, 0, 3));
						-- Is the aura active?
						if(Modules.Auras:IsAuraShown(auraID)) then
							-- Get the action data.
							local actionData = Modules.Auras:GetAuraAction(auraID, auraActionID);
							-- One difference for macros is we check the type.
							if(actionType == strupper(strsub(actionData["type"], 0, 1))) then
								-- Enable displays for this aura.
								Modules.Auras:MergeAuraAction(ButtonData[buttonID], actionData);
								-- Fire aura display event.
								CoreFrame:FireModuleEvent("OnButtonDisplayAura", buttonID, auraID,
									actionData, auraActionID);
							end
						end
					end
				end
			end
			-- Done.
			return;
		else
			-- Not a macro, end it.
			return;
		end
	end
	-- Go over the auras this key has.
	for _, auraID in pairs(auras) do
		-- The action ID is actually the auraID padded to 3 digits + the action ID appended.
		-- So split it up.
		local auraActionID = tonumber(strsub(auraID, 4, 5));
		auraID = tonumber(strsub(auraID, 0, 3));
		-- Is the aura active?
		if(Modules.Auras:IsAuraShown(auraID)) then
			-- Get the action data.
			local actions = Modules.Auras:GetAuraAction(auraID, auraActionID);
			-- Enable displays for this aura.
			Modules.Auras:MergeAuraAction(ButtonData[buttonID], actions);
			-- Fire aura display event.
			CoreFrame:FireModuleEvent("OnButtonDisplayAura", buttonID, auraID, actions,
				auraActionID);
		end
	end
end
--[[
----------------------------------------------------------------------------------------------------
OnUpdateTrigger

Fired when OnAuraShow/OnAuraHide are called. Performs a mass button update.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:OnUpdateTrigger()
	-- Check to see if update throttling is enabled. If it is, we'll queue this update.
	if(CoreFrame:GetModuleSetting("Buttons", "Throttle") > 0 and ThrottleActive) then
		-- We're throttled.
		ThrottlePending = true;
	else
		-- Throttle further updates (won't do anything if disabled!)
		ModuleFrame:ThrottleUpdates();
		-- Iterate over the active buttons and go to town on it.
		for buttonID, button in pairs(Buttons) do
			-- Buttons that should be made but aren't registered yet are recorded as TRUE values.
			-- On each mass update we try to replace these with actual buttons, this fixes issues
			-- with Dominos.
			if(button and button == true) then
				Buttons[buttonID] = _G[buttonID] or true;
			end
			-- Pass to UpdateButton.
			if(button and button ~= true) then
				ModuleFrame:OnButtonUpdate(Buttons[buttonID]);
			end
		end
	end
end
--[[
----------------------------------------------------------------------------------------------------
ThrottleUpdates

Throttles further updates if the feature is enabled.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:ThrottleUpdates()
	-- Make sure it's enabled.
	if(CoreFrame:GetModuleSetting("Buttons", "Throttle") == 0) then return; end
	-- Stop further updates.
	ThrottleActive = true;
	-- Reset our throttle timer.
	ThrottleTimer = 0;
	-- Register update script.
	ModuleFrame:SetScript("OnUpdate", ModuleFrame.OnUpdate);
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.
----------------------------------------------------------------------------------------------------
--]]
function ModuleFrame:OnUpdate(elapsed)
	-- Update time elapsed.
	ThrottleTimer = ThrottleTimer + elapsed;
	-- Time up?
	if(ThrottleTimer < CoreFrame:GetModuleSetting("Buttons", "Throttle")) then return; end
	-- Remove update script.
	ModuleFrame:SetScript("OnUpdate", nil);
	-- Time up! Rip this off.
	ThrottleActive = nil;
	-- Any updates queued?
	if(ThrottlePending) then
		-- Trigger update, remove the var.
		ThrottlePending = nil;
		-- This will trigger re-throttling if another request comes along while processing this
		-- one. It's intended.
		ModuleFrame:OnUpdateTrigger();
	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.
		Buttons[key .. i] = _G[key .. i] or true;
	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
		};
	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(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;
			end
			-- In addition, make sure this applies to future buttons.
			LAB:RegisterCallback("OnButtonCreated", function(_, button)
				Buttons[button:GetName()] = button;
			end);
			-- Add a button update hook.
			LAB:RegisterCallback("OnButtonUpdate", function(_, button)
				ModuleFrame:OnButtonUpdate(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) 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 CoreFrame:GetModuleSetting("Buttons", "RegisterBlizzardButtons")) then
		-- Hook for button updates.
		hooksecurefunc("ActionButton_Update", function(button)
			ModuleFrame:OnButtonUpdate(button);
		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.OnUpdateTrigger);
	CoreFrame:RegisterModuleEventListener("OnAuraHide", ModuleFrame, ModuleFrame.OnUpdateTrigger);
	CoreFrame:RegisterModuleEventListener("OnActionCreate", ModuleFrame);
	-- Done.
	return true;
end