Quantcast
--[[---------------------------------------------------------------------------------
  Clique by Cladhaire <cladhaire@gmail.com>
----------------------------------------------------------------------------------]]

Clique = {Locals = {}}

DongleStub("Dongle"):New(Clique, "Clique")

local L = Clique.Locals
-- Create a frame for event registration
local eventFrame = CreateFrame("Frame")
local elapsed = 0
eventFrame:SetScript("OnUpdate", function()
	elapsed = elapsed + arg1
	if elapsed >= 2.0 then
		elapsed = 0
		Clique:ClearQueue()
	end
end)
eventFrame:Hide()

if not InCombatLockdown then
    function InCombatLockdown()
	return UnitAffectingCombat("player")
    end
end

function Clique:Enable()
	-- Grab the localisation header
	L = Clique.Locals

	self.defaults = {
		profile = {
			[L.CLICKSET_DEFAULT] = {},
			[L.CLICKSET_HARMFUL] = {},
			[L.CLICKSET_HELPFUL] = {},
			[L.CLICKSET_OOC] = {},
		}
	}

	self.db = self:InitializeDB("CliqueDB", self.defaults)
	self.profile = self.db.profile

    self.editSet = self.profile[L.CLICKSET_DEFAULT]

    local newindex = function(t,k,v)
		Clique:RegisterFrame(k)
		rawset(t,k,v)
    end

	ClickCastFrames = ClickCastFrames or {}
    setmetatable(ClickCastFrames, {__newindex=newindex})

    -- Register all frames that snuck in before we did =)
    for frame in pairs(ClickCastFrames) do
		self:RegisterFrame(frame)
    end

    Clique:OptionsOnLoad()
    Clique:EnableFrames()

	-- Run the OOC script if we need to
	Clique:CombatUnlock()

    -- Securehook the RaidFrame_LoadUI
    local raidFunc = function(type, name, parent, template)
		if template == "RaidPulloutButtonTemplate" then
			ClickCastFrames[getglobal(name.."ClearButton")] = true
		end
	end

    hooksecurefunc("CreateFrame", raidFunc)
end

function Clique:EnableFrames()
    local tbl = {
		PlayerFrame,
		PetFrame,
		PartyMemberFrame1,
		PartyMemberFrame2,
		PartyMemberFrame3,
		PartyMemberFrame4,
		PartyMemberFrame1PetFrame,
		PartyMemberFrame2PetFrame,
		PartyMemberFrame3PetFrame,
		PartyMemberFrame4PetFrame,
		TargetFrame,
		TargetofTargetFrame,
    }

    for i,frame in pairs(tbl) do
		ClickCastFrames[frame] = true
    end
end

function Clique:SpellBookButtonPressed()
    local id = SpellBook_GetSpellID(this:GetParent():GetID());
    local texture = GetSpellTexture(id, SpellBookFrame.bookType)
    local name, rank = GetSpellName(id, SpellBookFrame.bookType)

    if rank == L.RACIAL_PASSIVE or rank == L.PASSIVE then
		StaticPopup_Show("CLIQUE_PASSIVE_SKILL")
		return
    else
		rank = select(3, string.find(rank, L.RANK_PATTERN))
		if rank then rank = tonumber(rank) end
    end

    local type = "spell"
	local button

	if self.editSet == self.profile[L.CLICKSET_HARMFUL] then
		button = string.format("%s%d", "harmbutton", self:GetButtonNumber())
	elseif self.editSet == self.profile[L.CLICKSET_HELPFUL] then
		button = string.format("%s%d", "helpbutton", self:GetButtonNumber())
	else
		button = self:GetButtonNumber()
	end

    -- Build the SVN/live structure
    local t = {
		["button"] = button,
		["modifier"] = self:GetModifierText(),
		["texture"] = GetSpellTexture(id, SpellBookFrame.bookType),
		["type"] = type,
		["arg1"] = name,
		["arg2"] = rank,
    }

    local key = t.modifier .. t.button

    if self:CheckBinding(key) then
		StaticPopup_Show("CLIQUE_BINDING_PROBLEM")
	return
    end

    self.editSet[key] = t

    self:SetAction(t)
    self:ListScrollUpdate()
end

function Clique:CombatLockdown()
	self:Debug(1, "Going into combat mode")
	-- Remove all OOC clicks
	for k,v in pairs(self.profile[L.CLICKSET_OOC]) do
		self:DeleteAction(v)
		self:Debug(1, "Removing %s, %s", v.type, tostring(v.arg1))
	end

	-- Just bluntly force our clicks back onto the frames
    for frame in pairs(ClickCastFrames) do
		self:RegisterFrame(frame)
    end
end

function Clique:CombatUnlock()
	self:Debug(1, "Setting any out of combat clicks")
    for frame in pairs(ClickCastFrames) do
		for k,v in pairs(self.profile[L.CLICKSET_OOC]) do
			self:SetAttribute(v,frame)
		end
	end
end

local queue = {}

function Clique:CombatDelay(tbl)
	if InCombatLockdown() then
		if #queue == 0 then
			self:Print("Cannot make changes in combat.  These changes will be delayed until you exit combat.")
		end
		table.insert(queue, tbl)
		eventFrame:Show()
		return true
	end
end

function Clique:ClearQueue()
	if InCombatLockdown() then return end

	eventFrame:Hide()
	self:Print("Out of combat.  Applying all queued changes.")
	for k,v in ipairs(queue) do
	    if v.GetAttribute then
			self:RegisterFrame(v)
	    else
			if v.delete then
				self:DeleteAction(v)
			else
				self:SetAction(v)
			end
	    end
	end
	queue = {}
end

function Clique:RegisterFrame(frame)
	if self:CombatDelay(frame) then return end
	-- Ensure we have all the buttons registered
	frame:RegisterForClicks("LeftButtonUp", "MiddleButtonUp", "RightButtonUp", "Button4Up", "Button5Up")

	for name,set in pairs(self.profile) do
		if name ~= L.CLICKSET_OOC then
			for modifier,entry in pairs(set) do
				self:SetAttribute(entry, frame)
			end
		end
	end
end

function Clique:ProfileChanged(new)

	for name,set in pairs(self.profile) do
	    for modifier,entry in pairs(set) do
			self:DeleteAction(entry)
	    end
	end

	self.profile = self.db.profile
    self.editSet = self.profile[L.CLICKSET_DEFAULT]
	self.profileKey = new

	-- refresh the dropdown if its active
	CliqueDropDownProfile:Hide()
	CliqueDropDownProfile:Show()

	self:Print("Profile changed to '%s'", new)

    for frame in pairs(ClickCastFrames) do
		self:RegisterFrame(frame)
    end
end

function Clique:SetAttribute(entry, frame)
	-- Set up any special attributes
	local type,button,value

	if not tonumber(entry.button) then
		type,button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
		frame:SetAttribute(entry.modifier..entry.button, type..button)
		button = string.format("-%s%s", type, button)
	end

	button = button or entry.button

	if entry.type == "actionbar" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
	elseif entry.type == "action" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg2)
	elseif entry.type == "pet" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg2)
	elseif entry.type == "spell" then
		local rank = tonumber(entry.arg2)
		local cast = string.format(rank and L.CAST_FORMAT or "%s", entry.arg1, rank)
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."spell"..button, cast)

		frame:SetAttribute(entry.modifier.."bag"..button, entry.arg2)
		frame:SetAttribute(entry.modifier.."slot"..button, entry.arg3)
		frame:SetAttribute(entry.modifier.."item"..button, entry.arg4)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg5)
	elseif entry.type == "item" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."bag"..button, entry.arg1)
		frame:SetAttribute(entry.modifier.."slot"..button, entry.arg2)
		frame:SetAttribute(entry.modifier.."item"..button, entry.arg3)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg4)
	elseif entry.type == "macro" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."macro"..button, entry.arg1)
	elseif entry.type == "stop" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
	elseif entry.type == "target" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
	elseif entry.type == "focus" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
	elseif entry.type == "assist" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
	elseif entry.type == "click" then
		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
		frame:SetAttribute(entry.modifier.."delegate"..button, getglobal(entry.arg1))
	end
end

function Clique:SetAction(entry)
	if self:CombatDelay(entry) then return end
	for frame in pairs(ClickCastFrames) do
		self:SetAttribute(entry, frame)
	end
end

function Clique:DeleteAction(entry)
	local type,button,value

	if not tonumber(entry.button) then
		type,button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
		for frame in pairs(ClickCastFrames) do
			frame:SetAttribute(entry.modifier..entry.button, nil)
		end
		button = string.format("-%s%s", type, button)
	end

	button = button or entry.button

	entry.delete = true

	if self:CombatDelay(entry) then return end
	for frame in pairs(ClickCastFrames) do
		frame:SetAttribute(entry.modifier.."type"..button, nil)
		frame:SetAttribute(entry.modifier..entry.type..button, nil)
	end
end

function Clique:Print(msg, ...)
	if string.find(msg, "%%") then
		-- This is a string format, so lets format
		msg = string.format(msg, ...)
	end
	ChatFrame1:AddMessage("|cFF33FF99Clique|r: " .. tostring(msg))
end