diff --git a/Clique/AddonCore.lua b/Clique/AddonCore.lua new file mode 100644 index 0000000..2ad67e3 --- /dev/null +++ b/Clique/AddonCore.lua @@ -0,0 +1,207 @@ +--[[------------------------------------------------------------------------- +-- AddonCore.lua +-- +-- This is a very simple, bare-minimum core for addon development. It provide +-- methods to register events, call initialization functions, and sets up the +-- localization table so it can be used elsewhere. This file is designed to be +-- loaded first, as it has no further dependencies. +-- +-- Events registered: +-- * ADDON_LOADED - Watch for saved variables to be loaded, and call the +-- 'Initialize' function in response. +-- * PLAYER_LOGIN - Call the 'Enable' method once the major UI elements +-- have been loaded and initialized. +-------------------------------------------------------------------------]] -- +local addonName, addon = ... + +-- Set global name of addon +_G[addonName] = addon + +-- Extract version information from TOC file +addon.version = GetAddOnMetadata(addonName, "Version") +if addon.version == "@project-version" or addon.version == "wowi:version" then addon.version = "SCM" end + +--[[------------------------------------------------------------------------- +-- Debug support +-------------------------------------------------------------------------]] -- + +local EMERGENCY_DEBUG = false +if EMERGENCY_DEBUG then + local private = {} + for k, v in pairs(addon) do + rawset(private, k, v) + rawset(addon, k, nil) + end + + setmetatable(addon, { + __index = function(t, k) + local value = rawget(private, k) + if type(value) == "function" then print("CALL", addonName .. "." .. tostring(k)) end + return value + end, + __newindex = function(t, k, v) + print(addonName, "NEWINDEX", k, v) + rawset(private, k, v) + end + }) +end + +--[[------------------------------------------------------------------------- +-- Print/Printf support +-------------------------------------------------------------------------]] -- + +local printHeader = "|cFF33FF99%s|r: " + +function addon:Printf(msg, ...) + msg = printHeader .. msg + local success, txt = pcall(string.format, msg, addonName, ...) + if success then + print(txt) + else + error(string.gsub(txt, "'%?'", string.format("'%s'", "Printf")), 3) + end +end + +--[[------------------------------------------------------------------------- +-- Event registration and dispatch +-------------------------------------------------------------------------]] -- + +addon.eventFrame = CreateFrame("Frame", addonName .. "EventFrame", UIParent) +local eventMap = {} + +function addon:RegisterEvent(event, handler) + assert(eventMap[event] == nil, "Attempt to re-register event: " .. tostring(event)) + eventMap[event] = handler and handler or event + addon.eventFrame:RegisterEvent(event) +end + +function addon:UnregisterEvent(event) + assert(type(event) == "string", "Invalid argument to 'UnregisterEvent'") + eventMap[event] = nil + addon.eventFrame:UnregisterEvent(event) +end + +addon.eventFrame:SetScript("OnEvent", function(frame, event, ...) + local handler = eventMap[event] + local handler_t = type(handler) + if handler_t == "function" then + handler(event, ...) + elseif handler_t == "string" and addon[handler] then + addon[handler](addon, event, ...) + end +end) + +--[[------------------------------------------------------------------------- +-- Message support +-------------------------------------------------------------------------]] -- + +local messageMap = {} + +function addon:RegisterMessage(name, handler) + assert(messageMap[name] == nil, "Attempt to re-register message: " .. tostring(name)) + messageMap[name] = handler and handler or name +end + +function addon:UnregisterMessage(name) + assert(type(event) == "string", "Invalid argument to 'UnregisterMessage'") + messageMap[name] = nil +end + +function addon:FireMessage(name, ...) + assert(type(name) == "string", "Invalid argument to 'FireMessage'") + local handler = messageMap[name] + local handler_t = type(handler) + if handler_t == "function" then + handler(name, ...) + elseif handler_t == "string" and addon[handler] then + addon[handler](addon, event, ...) + end +end + +--[[------------------------------------------------------------------------- +-- Setup Initialize/Enable support +-------------------------------------------------------------------------]] -- + +addon:RegisterEvent("PLAYER_LOGIN", "Enable") +addon:RegisterEvent("ADDON_LOADED", function(event, ...) + if ... == addonName then + addon:UnregisterEvent("ADDON_LOADED") + if type(addon["Initialize"]) == "function" then addon["Initialize"](addon) end + + -- If this addon was loaded-on-demand, trigger 'Enable' as well + if IsLoggedIn() and type(addon["Enable"]) == "function" then addon["Enable"](addon) end + end +end) + +--[[------------------------------------------------------------------------- +-- Support for deferred execution (when in-combat) +-------------------------------------------------------------------------]] -- + +local deferframe = CreateFrame("Frame") +deferframe.queue = {} + +local function runDeferred(thing) + local thing_t = type(thing) + if thing_t == "string" and addon[thing] then + addon[thing](addon) + elseif thing_t == "function" then + thing(addon) + end +end + +-- This method will defer the execution of a method or function until the +-- player has exited combat. If they are already out of combat, it will +-- execute the function immediately. +function addon:Defer(...) + for i = 1, select("#", ...) do + local thing = select(i, ...) + local thing_t = type(thing) + if thing_t == "string" or thing_t == "function" then + if InCombatLockdown() then + deferframe.queue[#deferframe.queue + 1] = select(i, ...) + else + runDeferred(thing) + end + else + error("Invalid object passed to 'Defer'") + end + end +end + +deferframe:RegisterEvent("PLAYER_REGEN_ENABLED") +deferframe:SetScript("OnEvent", function(self, event, ...) + for idx, thing in ipairs(deferframe.queue) do runDeferred(thing) end + table.wipe(deferframe.queue) +end) + +--[[------------------------------------------------------------------------- +-- Localization +-------------------------------------------------------------------------]] -- + +addon.L = addon.L or setmetatable({}, { + __index = function(t, k) + rawset(t, k, k) + return k + end, + __newindex = function(t, k, v) + if v == true then + rawset(t, k, k) + else + rawset(t, k, v) + end + end +}) + +function addon:RegisterLocale(locale, tbl) + if locale == "enUS" or locale == GetLocale() then + for k, v in pairs(tbl) do + if v == true then + self.L[k] = k + elseif type(v) == "string" then + self.L[k] = v + else + self.L[k] = k + end + end + end +end diff --git a/Clique/ClickCastTemplate.xml b/Clique/ClickCastTemplate.xml new file mode 100644 index 0000000..3fe483f --- /dev/null +++ b/Clique/ClickCastTemplate.xml @@ -0,0 +1,12 @@ + + + + diff --git a/Clique/Clique.lua b/Clique/Clique.lua index 78ea6b3..e6c8cae 100644 --- a/Clique/Clique.lua +++ b/Clique/Clique.lua @@ -1,887 +1,950 @@ ---[[--------------------------------------------------------------------------------- - Clique by Cladhaire -----------------------------------------------------------------------------------]] +--[[------------------------------------------------------------------- +-- Clique - Copyright 2006-2012 - James N. Whitehead II +-- +-- This is an updated version of the original 'Clique' addon +-- designed to work better with multi-button mice, and those players +-- who want to be able to bind keyboard combinations to enable +-- hover-casting on unit frames. It's a bit of a paradigm shift from +-- the original addon, but should make a much simpler and more +-- powerful addon. +-- +-- * Any keyboard combination can be set as a binding. +-- * Any mouse combination can be set as a binding. +-- * The only types that are allowed are spells and macros. +-- +-- The concept of 'click-sets' has been simplified and extended +-- so that the user can specify their own binding-sets, allowing +-- for different bindings for different sets of frames. By default +-- the following binding-sets are available: +-- +-- * default - These bindings are active on all frames, unless +-- overridden by another binding in a more specific binding-set. +-- * ooc - These bindings will ONLY be active when the player is +-- out of combat. +-- * enemy - These bindings are ONLY active when the unit you are +-- clicking on is an enemy, i.e. a unit that you can attack. +-- * friendly - These bindings are ONLY active when the unit you are +-- clicking on is a friendly unit, i.e. one that you can assist +-- * hovercast - These bindings will be available whenever you are over +-- a unit frame, or a unit in the 3D world. +-- * global - These bindings will be always available. They +-- do not specify a target for the action, so if the action requires +-- a target, you must specify it after performing the binding. +-- +-- The binding-sets layer on each other, with the 'default' binding-set +-- being at the bottom, and any other binding-set being layered on top. +-- Clique will detect any conflicts that you have other than with +-- default bindings, and will warn you of the situation. +-------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L -Clique = { - Locals = {}, -} +function addon:Initialize() + -- Create an AceDB, but it needs to be cleared first + self.db = LibStub("AceDB-3.0"):New("CliqueDB3", self.defaults) + self.db.RegisterCallback(self, "OnNewProfile", "OnNewProfile") + self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged") -assert(DongleStub, string.format("Clique requires DongleStub.")) -DongleStub("Dongle-1.2"):New("Clique", Clique) -Clique.version = GetAddOnMetadata("Clique", "Version") -if Clique.version == "wowi:revision" then - Clique.version = "SVN" + self.settings = self.db.char + self.bindings = self.db.profile.bindings + + self.ccframes = {} + self.hccframes = {} + + -- Registration for group headers (in-combat safe) + self.header = CreateFrame("Frame", addonName .. "HeaderFrame", UIParent, "SecureHandlerBaseTemplate,SecureHandlerAttributeTemplate") + ClickCastHeader = addon.header + + -- This snippet will clear any dangling bindings that might have occurred + -- as a result of frames being shown/hidden. + self.header:SetAttribute("_onattributechanged", [[ + if name == "state-hasunit" then + if value == "false" and danglingButton then + control:RunFor(danglingButton, self:GetAttribute("setup_onleave")) + danglingButton = nil + end + end + ]]) + RegisterStateDriver(self.header, "hasunit", "[@mouseover, exists] true; false") + + -- Create a secure action button that's sole purpose is to cancel a + -- pending spellcast (the targeting hand) + self.stopbutton = CreateFrame("Button", addonName .. "StopButton", nil, "SecureActionButtonTemplate") + self.stopbutton.name = self.stopbutton:GetName() + self.stopbutton:SetAttribute("type", "stop") + + -- Create a secure action button that can be used for 'hovercast' and 'global' + self.globutton = CreateFrame("Button", addonName .. "SABButton", UIParent, "SecureActionButtonTemplate, SecureHandlerBaseTemplate") + + -- Create a table within the addon header to store the frames + -- that are registered for click-casting + self.header:Execute([[ + ccframes = table.new() + ]]) + + -- Create a table within the addon header to store the frame blacklist + self.header:Execute([[ + blacklist = table.new() + ]]) + + -- This snippet is executed from the SecureHandlerEnterLeaveTemplate + -- _onenter and _onleave attributes. The 'self' attribute will contain + -- the unit frame itself. + self.header:SetAttribute("clickcast_onenter", [===[ + local header = self:GetParent():GetFrameRef("clickcast_header") + header:RunFor(self, header:GetAttribute("setup_onenter")) + ]===]) + + -- This snippet is executed from the SecureHandlerEnterLeaveTemplate + -- _onenter and _onleave attributes. The 'self' attribute will contain + -- the unit frame itself. + self.header:SetAttribute("clickcast_onleave", [===[ + local header = self:GetParent():GetFrameRef("clickcast_header") + header:RunFor(self, header:GetAttribute("setup_onleave")) + ]===]) + + local setup, remove = self:GetClickAttributes() + self.header:SetAttribute("setup_clicks", setup) + self.header:SetAttribute("remove_clicks", remove) + + -- This snippet is executed from within the initialConfigFunction secure + -- snippet. The unit frame button is passed in the 'clickcast_button' + -- attribute, which can only be accomplished in a restricted environment. + self.header:SetAttribute("clickcast_register", [===[ + local button = self:GetAttribute("clickcast_button") + + -- Export this frame so we can display it in the insecure environment + self:SetAttribute("export_register", button) + + button:SetAttribute("clickcast_onenter", self:GetAttribute("clickcast_onenter")) + button:SetAttribute("clickcast_onleave", self:GetAttribute("clickcast_onleave")) + ccframes[button] = true + + self:RunFor(button, self:GetAttribute("setup_clicks")) + ]===]) + + -- This snippet is executed from the Clique:UnregisterFrame() function, or + -- possibly from some other restricted environment. The unit frame is passed + -- in the 'clickcast_button' attribute, which can only be accomplished + -- in a restricted environment. + self.header:SetAttribute("clickcast_unregister", [===[ + local button = self:GetAttribute("clickcast_button") + + -- Export this frame so it can be removed from the blacklist editor + self:SetAttribute("export_unregister", button) + + -- Remove any click and binding attributes that have already been set + self:RunFor(button, self:GetAttribute("clickcast_onleave")) + self:RunFor(button, self:GetAttribute("remove_clicks")) + + button:SetAttribute("clickcast_onenter", nil) + button:SetAttribute("clickcast_onleave", nil) + ccframes[button] = nil + ]===]) + + -- We need to track frame registrations so we can display secure frames in + -- the frame blacklist editor. This is done via the 'export_register' and + -- 'export_unregister' attributes. + self.header:HookScript("OnAttributeChanged", function(frame, name, value) + if name == "export_register" and type(value) ~= nil then + -- Convert the userdata object to the global object so we have access + -- to all of the correct methods, such as 'RegisterForClicks'' + local name = value.GetName and value:GetName() + if name then + local button = _G[name] + self.hccframes[name] = button + self:UpdateRegisteredClicks(button) + end + elseif name == "export_unregister" and type(value) ~= nil then + local name = value.GetName and value:GetName() + if name then self.hccframes[name] = nil end + end + end) + + local set, clr = self:GetBindingAttributes() + self.header:SetAttribute("setup_onenter", set) + self.header:SetAttribute("setup_onleave", clr) + + -- Get the override binding attributes for the global click frame + self.globutton.setup, self.globutton.remove = self:GetClickAttributes(true) + self.globutton.setbinds, self.globutton.clearbinds = self:GetBindingAttributes(true) + + self:IntegrateBlizzardFrames() + + self:RegisterEvent("PLAYER_ENTERING_WORLD", "PlayerEnteringWorld") end -local L = Clique.Locals +-- These tables are a queue for frame registration/unregistration +addon.regqueue = {} +addon.unregqueue = {} +addon.regclickqueue = {} -function Clique:Enable() - -- Grab the localisation header - L = Clique.Locals - self.ooc = {} +-- These function may be called during combat. When that is the case, the +-- request must be queued until combat ends, and then we can attempt to +-- register those frames. This is mainly due to integration with the +-- Blizzard raid frames, which we cannot 'register' while in combat. +function addon:RegisterFrame(button) + if InCombatLockdown() then + table.insert(self.regqueue, button) + return + end - self.defaults = { - profile = { - clicksets = { - [L.CLICKSET_DEFAULT] = {}, - [L.CLICKSET_HARMFUL] = {}, - [L.CLICKSET_HELPFUL] = {}, - [L.CLICKSET_OOC] = {} - }, - blacklist = {}, - tooltips = false - }, - char = { - switchSpec = false, - downClick = false - }, - frames = { - PlayerFrame, - PetFrame, - PartyMemberFrame1, - PartyMemberFrame2, - PartyMemberFrame3, - PartyMemberFrame4, - PartyMemberFrame1PetFrame, - PartyMemberFrame2PetFrame, - PartyMemberFrame3PetFrame, - PartyMemberFrame4PetFrame, - TargetFrame, - TargetFrameToT, - FocusFrame, - FocusFrameToT, - Boss1TargetFrame, - Boss2TargetFrame, - Boss3TargetFrame, - Boss4TargetFrame, - } + if self.ccframes[button] ~= nil then return end + + self.ccframes[button] = true + + self:UpdateRegisteredClicks(button) + + -- Wrap the OnEnter/OnLeave scripts in order to handle keybindings + addon.header:WrapScript(button, "OnEnter", addon.header:GetAttribute("setup_onenter")) + addon.header:WrapScript(button, "OnLeave", addon.header:GetAttribute("setup_onleave")) + + -- Set the attributes on the frame + self.header:SetFrameRef("cliquesetup_button", button) + self.header:Execute(self.header:GetAttribute("setup_clicks"), button) +end + +function addon:UnregisterFrame(button) + if InCombatLockdown() then + table.insert(self.unregqueue, button) + return + end + + if self.ccframes[button] ~= nil then + -- Clear any click/bind attributes + self.header:SetFrameRef("cliquesetup_button", button) + self.header:Execute([[ + local button = self:GetFrameRef("cliquesetup_button") + self:RunFor(button, self:GetAttribute("setup_onleave")) + self:RunAttribute("remove_clicks") + ]]) + + self.ccframes[button] = nil + + -- Unwrap the OnEnter/OnLeave scripts, if they were set + addon.header:UnwrapScript(button, "OnEnter") + addon.header:UnwrapScript(button, "OnLeave") + end +end + +function addon:Enable() + -- Make the options window a pushable panel window + UIPanelWindows["CliqueConfig"] = { + area = "left", + pushable = 1, + whileDead = 1 } - self.db = self:InitializeDB("CliqueDB", self.defaults) - self.profile = self.db.profile - self.clicksets = self.profile.clicksets + -- Set the tooltip for the spellbook tab + CliqueSpellTab.tooltip = L["Clique binding configuration"] +end - self.editSet = self.clicksets[L.CLICKSET_DEFAULT] +-- A new profile is being created in the db, called 'profile' +function addon:OnNewProfile(event, db, profile) + table.insert(db.profile.bindings, { + key = "BUTTON1", + type = "target", + unit = "mouseover", + sets = { + default = true + } + }) + table.insert(db.profile.bindings, { + key = "BUTTON2", + type = "menu", + sets = { + default = true + } + }) + self.bindings = db.profile.bindings +end - ClickCastFrames = ClickCastFrames or {} - self.ccframes = ClickCastFrames +function addon:OnProfileChanged(event, db, newProfile) + self.bindings = db.profile.bindings + self:FireMessage("BINDINGS_CHANGED") +end - local newindex = function(t, k, v) - if v == nil then - Clique:UnregisterFrame(k) - rawset(self.ccframes, k, nil) - else - Clique:RegisterFrame(k) - rawset(self.ccframes, k, v) +local function ATTR(indent, prefix, attr, suffix, value) + local fmt = [[%sbutton:SetAttribute("%s%s%s%s%s", %q)]] + return fmt:format(indent, prefix, #prefix > 0 and "-" or "", attr, tonumber(suffix) and "" or "-", suffix, value) +end + +local function REMATTR(prefix, attr, suffix, value) + local fmt = [[button:SetAttribute("%s%s%s%s%s", nil)]] + return fmt:format(prefix, #prefix > 0 and "-" or "", attr, tonumber(suffix) and "" or "-", suffix) +end + +-- A sort function that determines in what order bindings should be applied. +-- This function should be treated with care, it can drastically change behavior +local function ApplicationOrder(a, b) + local acnt, bcnt = 0, 0 + for k, v in pairs(a.sets) do acnt = acnt + 1 end + for k, v in pairs(b.sets) do bcnt = bcnt + 1 end + + -- Force out-of-combat clicks to take the HIGHEST priority + if a.sets.ooc and not b.sets.ooc then + return true + elseif b.sets.ooc and not a.sets.ooc then + return false + elseif a.sets.ooc and b.sets.ooc then + return acnt < bcnt + end + + -- Try to give any 'default' clicks LOWEST priority + if a.sets.default and not b.sets.default then + return true + elseif a.sets.default and b.sets.default then + return acnt < bcnt + end +end + +local function shouldApply(global, entry) + -- If this is the global button and this is a 'global' binding + if global and (entry.sets.hovercast or entry.sets.global) then + return true + elseif not global then + -- Check to see if there's a non-global binding to be set + for k, v in pairs(entry.sets) do if k ~= "global" and k ~= "hovercast" then return true end end + return false + end +end + +local function EntryIsCorrectSpec(entry) + local currentSpec = addon.talentGroup + if (entry.sets.pritalent and currentSpec ~= 1) or (entry.sets.sectalent and currentSpec ~= 2) then return false end + return true +end + +-- This function takes a single argument indicating if the attributes being +-- computed are for the special 'global' button used by Clique. It then +-- computes the set of attributes necessary for the player's bindings to be +-- active on all the appropriate frames. The logic here is quite delicate but +-- also rather well commented. + +function addon:GetClickAttributes(global) + -- In these scripts, 'self' should always be the header + local bits = {"local inCombat = self:GetAttribute('inCombat')", "local setupbutton = self:GetFrameRef('cliquesetup_button')", "local button = setupbutton or self"} + + local rembits = {"local inCombat = self:GetAttribute('inCombat')", "local setupbutton = self:GetFrameRef('cliquesetup_button')", "local button = setupbutton or self"} + + -- Check to see if the frame being setup is blacklisted. Do not perform + -- this check on the global frame. + if not global then + bits[#bits + 1] = "local name = button:GetName()" + bits[#bits + 1] = "if blacklist[name] then return end" + + rembits[#rembits + 1] = "local name = button:GetName()" + rembits[#rembits + 1] = "if blacklist[name] then return end" + end + + -- Sort the bindings so they are applied in order. This sort ensures that + -- any 'ooc' bindings are applied first. + table.sort(self.bindings, ApplicationOrder) + + -- Build a small table of ooc keys that are 'taken' so we can check for + -- masking conflicts with the friend/enemy sets. + local oocKeys = {} + for idx, entry in ipairs(self.bindings) do if shouldApply(global, entry) and entry.sets.ooc and entry.key then oocKeys[entry.key] = true end end + + for idx, entry in ipairs(self.bindings) do + -- Global (i.e. 'hovercast' and 'global') bindings are only applied + -- on the global frame, and not on any others. Additionally, any + -- non-global bindings are only applied on non-global frames. handle + -- this logic here. + + if entry.key and shouldApply(global, entry) and EntryIsCorrectSpec(entry) then + -- Check to see if this is a 'friend' or an 'enemy' binding, and + -- check if it would mask an 'ooc' binding with the same key. If + -- so, we need to add code that prevents this from happening, by + -- stopping the friend/enemy binding from being applied when the + -- player is out of combat. + + local indent = "" + local oocmask = oocKeys[entry.key] + + -- This code needs to set/clear a binding depending on combat + -- state. We do both in this function to ensure that we don't have + -- to run remove_clicks every single time the combat status + -- changes. + + local startbits + if oocmask and not entry.sets.ooc then + -- This means that the binding will mask the 'ooc' binding + -- with the same key, so we must ensure this is only set when + -- we are in combat. + bits[#bits + 1] = "if inCombat then -- non-ooc that is masking" + indent = indent .. " " + elseif entry.sets.ooc then + -- This is a standard 'ooc' binding, so we want to ensure its + -- only applied when out of combat, and cleared otherwise. + bits[#bits + 1] = "if not inCombat then -- ooc binding" + indent = indent .. " " + startbits = #rembits + 1 + end + + local prefix, suffix = addon:GetBindingPrefixSuffix(entry, global) + + -- Set up help/harm bindings. The button value will be either a number, + -- in the case of mouse buttons, otherwise it will be a string of + -- characters. Harmbuttons work alongside modifiers, so we need to include + -- then in the remapping. + if entry.sets.friend then + if global then + -- A modified binding that uses friend/enemy must have the unmodified + -- 'unit' attribute set, in order to do the friend/enemy lookup. Add + -- that here. + -- + -- NOTE: This will not work with useOwnerUnit and usesuffix frames + -- such as pet frames that use the owner's parent. This is a problem + -- with the way the 'mouseover' unit resolves in these cases. + bits[#bits + 1] = ATTR(indent, prefix, "unit", suffix, "mouseover") + rembits[#rembits + 1] = REMATTR(prefix, "unit", suffix) + end + local newbutton = "friend" .. suffix + bits[#bits + 1] = ATTR(indent, prefix, "helpbutton", suffix, newbutton) + rembits[#rembits + 1] = REMATTR(prefix, "helpbutton", suffix) + suffix = newbutton + elseif entry.sets.enemy then + if global then + -- A modified binding that uses friend/enemy must have the unmodified + -- 'unit' attribute set, in order to do the friend/enemy lookup. Add + -- that here. + -- + -- NOTE: This will not work with useOwnerUnit and usesuffix frames + -- such as pet frames that use the owner's parent. This is a problem + -- with the way the 'mouseover' unit resolves in these cases. + bits[#bits + 1] = ATTR(indent, prefix, "unit", suffix, "mouseover") + rembits[#rembits + 1] = REMATTR(prefix, "unit", suffix) + end + local newbutton = "enemy" .. suffix + bits[#bits + 1] = ATTR(indent, prefix, "harmbutton", suffix, newbutton) + rembits[#rembits + 1] = REMATTR(prefix, "harmbutton", suffix) + suffix = newbutton + end + + -- When we're setting up the 'global' button, and the binding is in the + -- 'hovercast' binding set, we need to specify the unit on which to take + -- the action. In this case, that's just mouseover. + if global and entry.sets.hovercast then + bits[#bits + 1] = ATTR(indent, prefix, "unit", suffix, "mouseover") + rembits[#rembits + 1] = REMATTR(prefix, "unit", suffix) + end + + -- Build any needed SetAttribute() calls + if entry.type == "target" or entry.type == "menu" then + bits[#bits + 1] = ATTR(indent, prefix, "type", suffix, entry.type) + rembits[#rembits + 1] = REMATTR(prefix, "type", suffix) + elseif entry.type == "spell" and self.settings.stopcastingfix then + -- Implementation of the 'stop casting' fix + local macrotext + local spellText = addon:SpellTextWithSubName(entry) + if entry.sets.global then + -- Do not include @mouseover + macrotext = string.format("/click %s\n/cast %s", self.stopbutton.name, spellText) + else + macrotext = string.format("/click %s\n/cast [@mouseover] %s", self.stopbutton.name, spellText) + end + bits[#bits + 1] = ATTR(indent, prefix, "type", suffix, "macro") + bits[#bits + 1] = ATTR(indent, prefix, "macrotext", suffix, macrotext) + rembits[#rembits + 1] = REMATTR(prefix, "type", suffix) + rembits[#rembits + 1] = REMATTR(prefix, "macrotext", suffix) + elseif entry.type == "spell" then + local spellText = addon:SpellTextWithSubName(entry) + bits[#bits + 1] = ATTR(indent, prefix, "type", suffix, entry.type) + bits[#bits + 1] = ATTR(indent, prefix, "spell", suffix, spellText) + rembits[#rembits + 1] = REMATTR(prefix, "type", suffix) + rembits[#rembits + 1] = REMATTR(prefix, "spell", suffix) + elseif entry.type == "macro" and self.settings.stopcastingfix then + local macrotext = string.format("/click %s\n%s", self.stopbutton.name, entry.macrotext) + bits[#bits + 1] = ATTR(indent, prefix, "type", suffix, entry.type) + bits[#bits + 1] = ATTR(indent, prefix, "macrotext", suffix, macrotext) + rembits[#rembits + 1] = REMATTR(prefix, "type", suffix) + rembits[#rembits + 1] = REMATTR(prefix, "macrotext", suffix) + elseif entry.type == "macro" then + bits[#bits + 1] = ATTR(indent, prefix, "type", suffix, entry.type) + bits[#bits + 1] = ATTR(indent, prefix, "macrotext", suffix, entry.macrotext) + rembits[#rembits + 1] = REMATTR(prefix, "type", suffix) + rembits[#rembits + 1] = REMATTR(prefix, "macrotext", suffix) + else + error(string.format("Invalid action type: '%s'", entry.type)) + end + + -- Finish the conditional statements started above + if oocmask and not entry.sets.ooc then + -- This means that the binding will mask the 'ooc' binding + -- with the same key, so we must ensure this is only set when + -- we are in combat. + bits[#bits + 1] = "end" + indent = indent:sub(1, -3) + elseif entry.sets.ooc then + -- This is a standard 'ooc' binding, so we want to ensure its + -- only applied when out of combat, and cleared otherwise. + local endbits = #rembits + bits[#bits + 1] = "else -- clear ooc binding" + for i = startbits, endbits, 1 do bits[#bits + 1] = indent .. rembits[i] end + bits[#bits + 1] = "end" + indent = indent:sub(1, -3) + end end end + return table.concat(bits, "\n"), table.concat(rembits, "\n") +end + +local B_SET = [[self:SetBindingClick(true, %q, self, %q);]] +local B_CLR = [[self:ClearBinding(%q);]] + +-- This function takes a single argument, indicating whether the attributes +-- should be built for the special global button or not, and returns an +-- attribute that can set the appropriate attributes, and one that can clear +function addon:GetBindingAttributes(global) + local set, clr + + -- If this is not the global button, include some logic that solves issues + -- when the frame disappears or the frame loses focus without the OnLeave + -- event firing. + -- + -- TODO: In the future, this should be done via OnHide or other ways as well + + if global then + set = {} + clr = {} + else + set = {"local button = self", "local name = button:GetName()", "if danglingButton and control.GetAttribute then control:RunFor(danglingButton, control:GetAttribute('setup_onleave')) end", + "if blacklist[name] then return end", "danglingButton = button"} + clr = {"local button = self", "local name = button:GetName()", "if blacklist[name] then return end", "danglingButton = nil"} + end + + -- This function is greatly simplified in that regardless of whether or + -- not bindings mask one another, they still need to be set as binding + -- clicks on the frame. Simply make a list of the keys that need to be + -- bound, and bind them. + + local unique = {} + + for idx, entry in ipairs(self.bindings) do + if entry.key and shouldApply(global, entry) and EntryIsCorrectSpec(entry) then + if global then + -- Allow for the re-binding of clicks and keys, except for + -- unmodified left/right-click + if entry.key ~= "BUTTON1" and entry.key ~= "BUTTON2" then + local prefix, suffix = addon:GetBindingPrefixSuffix(entry, global) + local key = self:ConvertSpecialKeys(entry) + + local attr = B_SET:format(key, suffix) + if not unique[attr] then + set[#set + 1] = attr + clr[#clr + 1] = B_CLR:format(key) + unique[attr] = true + end + end + else + local buttonNum = entry.key:match("BUTTON(%d+)$") + if not buttonNum then + -- Only apply key-based binding clicks, let the raw + -- attributes handle the others + local prefix, suffix = addon:GetBindingPrefixSuffix(entry, global) + local key = self:ConvertSpecialKeys(entry) + + local attr = B_SET:format(key, suffix) + if not unique[attr] then + set[#set + 1] = attr + clr[#clr + 1] = B_CLR:format(key) + unique[attr] = true + end + end + end + end + end + + return table.concat(set, "\n"), table.concat(clr, "\n") +end + +-- This function adds a binding to the player's current profile. The +-- following options can be included in the click-cast entry: +-- +-- entry = { +-- -- The full prefix and suffix of the key being bound +-- key = "ALT-CTRL-SHIFT-BUTTON1", +-- -- The icon to be used for displaying this entry +-- icon = "Interface\\Icons\\Spell_Nature_HealingTouch", +-- +-- -- Any restricted sets that this click should be applied to +-- sets = {"ooc", "harm", "help", "frames_blizzard"}, +-- +-- -- The type of the click-binding +-- type = "spell", +-- type = "macro", +-- type = "target", +-- type = "menu", +-- +-- -- Any arguments for given click type +-- spell = "Healing Touch", +-- macrotext = "/run Nature's Swiftness\n/cast [target=mouseover] Healing Touch", +-- unit = "mouseover", +-- } + +function addon:AddBinding(entry) + if InCombatLockdown() then return false end + + -- TODO: Check to see if the new binding conflicts with an existing binding + -- TODO: Validate the entry to ensure it has the correct arguments, etc. + + if not entry.sets then + entry.sets = { + default = true + } + end + + table.insert(self.bindings, entry) + self:FireMessage("BINDINGS_CHANGED") + return true +end + +local function bindingeq(a, b) + assert(type(a) == "table", "Error during deletion comparison") + assert(type(b) == "table", "Error during deletion comparison") + + if a.type ~= b.type then + return false + elseif a.type == "target" then + return a.key == b.key + elseif a.type == "menu" then + return a.key == b.key + elseif a.type == "spell" then + return a.spell == b.spell and a.key == b.key and a.spellSubName == b.spellSubName + elseif a.type == "macro" then + return a.macrotext == b.macrotext and a.key == b.key + end + + return false +end + +function addon:DeleteBinding(entry) + if InCombatLockdown() then return false end + + -- Look for an entry that matches the given binding and remove it + for idx, bind in ipairs(self.bindings) do + if bindingeq(entry, bind) then + -- Found the entry that matches, so remove it + table.remove(self.bindings, idx) + break + end + end + + self:FireMessage("BINDINGS_CHANGED") +end + +function addon:ClearAttributes() + self.header:Execute([[ + for button, enabled in pairs(ccframes) do + self:RunFor(button, self:GetAttribute("remove_clicks")) + end + ]]) + + for button, enabled in pairs(self.ccframes) do + -- Perform the setup of click bindings + self.header:SetFrameRef("cliquesetup_button", button) + self.header:Execute(self.header:GetAttribute("remove_clicks"), button) + end + + -- Clear global attributes + local globutton = self.globutton + globutton:Execute(globutton.remove) + globutton:Execute(globutton.clearbinds) +end + +-- Recompute all attributes, so they can later be applied. +function addon:UpdateAttributes() + local setup, remove = self:GetClickAttributes() + self.header:SetAttribute("setup_clicks", setup) + self.header:SetAttribute("remove_clicks", remove) + + local set, clr = self:GetBindingAttributes() + self.header:SetAttribute("setup_onenter", set) + self.header:SetAttribute("setup_onleave", clr) + + local globutton = self.globutton + globutton.setup, globutton.remove = self:GetClickAttributes(true) + globutton.setbinds, globutton.clearbinds = self:GetBindingAttributes(true) +end + +function addon:ApplyAttributes() + -- Handle all of the securely registered frames + self.header:Execute([[ + for button, enabled in pairs(ccframes) do + self:RunFor(button, self:GetAttribute("setup_clicks")) + end + ]]) + + -- Now any compat frames that used the old method + for button, enabled in pairs(self.ccframes) do + -- Unwrap any existing enter/leave scripts + self.header:UnwrapScript(button, "OnEnter") + self.header:UnwrapScript(button, "OnLeave") + self.header:WrapScript(button, "OnEnter", addon.header:GetAttribute("setup_onenter")) + self.header:WrapScript(button, "OnLeave", addon.header:GetAttribute("setup_onleave")) + + -- Perform the setup of click bindings + self.header:SetFrameRef("cliquesetup_button", button) + self.header:Execute(self.header:GetAttribute("setup_clicks"), button) + end + + -- Update the global button attributes + self.globutton:Execute(self.globutton.setup) + self.globutton:Execute(self.globutton.setbinds) +end + +function addon:TalentGroupChanged() + self.talentGroup = SpecializationUtil.GetActiveSpecialization() + + if self.settings.specswap then + local currentProfile = self.db:GetCurrentProfile() + local newProfile + + if self.settings["specswap" .. self.talentGroup] then newProfile = self.settings["specswap" .. self.talentGroup] end + + if newProfile ~= currentProfile and type(newProfile) == "string" then self.db:SetProfile(newProfile) end + end + + self:FireMessage("BINDINGS_CHANGED") +end + +function addon:PlayerEnteringWorld() + -- Compatability with old Clique 1.x registrations + -- Iterate over the frames that were set before we arrived + if ClickCastFrames then for frame, options in pairs(ClickCastFrames) do self:RegisterFrame(frame, options) end end + ClickCastFrames = setmetatable({}, { - __newindex = newindex + __newindex = function(t, k, v) + if v == nil or v == false then + self:UnregisterFrame(k) + else + self:RegisterFrame(k, v) + end + end }) - Clique:OptionsOnLoad() - Clique:EnableFrames() + -- Register for combat events to ensure we can swap between the two states + self:RegisterEvent("PLAYER_REGEN_DISABLED", "EnteringCombat") + self:RegisterEvent("PLAYER_REGEN_ENABLED", "LeavingCombat") - -- Register for dongle events - self:RegisterMessage("DONGLE_PROFILE_CHANGED") - self:RegisterMessage("DONGLE_PROFILE_DELETED") - self:RegisterMessage("DONGLE_PROFILE_RESET") + -- Register for Clique-based messages for settings updates, etc. + self:RegisterMessage("BINDINGS_CHANGED") + self:RegisterMessage("BLACKLIST_CHANGED") - self:RegisterEvent("PLAYER_REGEN_ENABLED") - self:RegisterEvent("PLAYER_REGEN_DISABLED") + -- Handle combat watching so we can change ooc based on party combat status + addon:UpdateCombatWatch() - self:RegisterEvent("LEARNED_SPELL_IN_TAB") - self:RegisterEvent("ASCENSION_CA_SPECIALIZATION_ACTIVE_ID_CHANGED") - self:RegisterEvent("ADDON_LOADED") + -- Support mutliple talent specs on release + self:RegisterEvent("ASCENSION_CA_SPECIALIZATION_ACTIVE_ID_CHANGED", "TalentGroupChanged") + addon:TalentGroupChanged() - self:RegisterEvent("PLAYER_ENTERING_WORLD") - self:RegisterEvent("RAID_ROSTER_UPDATE") - self:RegisterEvent("PARTY_MEMBERS_CHANGED") - self:RegisterEvent("UNIT_PET") - self:RegisterEvent("CVAR_UPDATE") + self:FireMessage("BLACKLIST_CHANGED") + self:FireMessage("BINDINGS_CHANGED") - -- Change to correct profile based on talent spec - if self.db.char.switchSpec then - self.silentProfile = true - self.talentGroup = SpecializationUtil.GetActiveSpecialization() - if self.db.char["spec" .. self.talentGroup .. "Profile"] then - self.db:SetProfile(self.db.char["spec" .. self.talentGroup .. "Profile"]) - end - self.silentProfile = false - end - - self:UpdateClicks() - - -- Register all frames that snuck in before we did =) - for frame in pairs(self.ccframes) do - self:RegisterFrame(frame) - end - - -- Securehook CreateFrame to catch any new raid frames - local raidFunc = function(type, name, parent, template) - if template == "RaidPulloutButtonTemplate" then - ClickCastFrames[getglobal(name .. "ClearButton")] = true - end - end - - local oldotsu = GameTooltip:GetScript("OnTooltipSetUnit") - if oldotsu then - GameTooltip:SetScript("OnTooltipSetUnit", function(...) - Clique:AddTooltipLines() - return oldotsu(...) - end) - else - GameTooltip:SetScript("OnTooltipSetUnit", function(...) - Clique:AddTooltipLines() - end) - end - - hooksecurefunc("CreateFrame", raidFunc) - - -- Create our slash command - self.cmd = self:InitializeSlashCommand("Clique commands", "CLIQUE", "clique") - self.cmd:RegisterSlashHandler("debug - Enables extra messages for debugging purposes", "debug", "ShowAttributes") - self.cmd:InjectDBCommands(self.db, "copy", "delete", "list", "reset", "set") - self.cmd:RegisterSlashHandler("tooltip - Enables binding lists in tooltips.", "tooltip", "ToggleTooltip") - self.cmd:RegisterSlashHandler("showbindings - Shows a window that contains the current bindings", "showbindings", - "ShowBindings") - - -- Place the Clique tab - self:LEARNED_SPELL_IN_TAB() - - -- Register the arena frames, if they're already loaded - if IsAddOnLoaded("Blizzard_ArenaUI") then - self:EnableArenaFrames() - end + self:UnregisterEvent("PLAYER_ENTERING_WORLD") end -function Clique:EnableFrames() - local tbl = self.defaults.frames - - for i, frame in pairs(tbl) do - rawset(self.ccframes, frame, true) - end -end - -function Clique:SpellBookButtonPressed(frame, button) - local texture = getglobal(frame:GetParent():GetName() .. "IconTexture"):GetTexture() - local name = getglobal(frame:GetParent():GetName() .. "SpellName"):GetText() - local rank = getglobal(frame:GetParent():GetName() .. "SubSpellName"):GetText() - - if rank == L.RACIAL_PASSIVE or rank == L.PASSIVE then - StaticPopup_Show("CLIQUE_PASSIVE_SKILL") - return - end - - local type = "spell" - - if self.editSet == self.clicksets[L.CLICKSET_HARMFUL] then - button = string.format("%s%d", "harmbutton", self:GetButtonNumber(button)) - elseif self.editSet == self.clicksets[L.CLICKSET_HELPFUL] then - button = string.format("%s%d", "helpbutton", self:GetButtonNumber(button)) - else - button = self:GetButtonNumber(button) - end - - -- Clear the rank if "Show all spell ranks" is selected - if not GetCVarBool("ShowAllSpellRanks") then - rank = nil - end - - -- Build the structure - local t = { - ["button"] = button, - ["modifier"] = self:GetModifierText(), - ["texture"] = texture, - ["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:ListScrollUpdate() - self:UpdateClicks() - -- We can only make changes when out of combat - self:PLAYER_REGEN_ENABLED() -end - --- Player is LEAVING combat -function Clique:PLAYER_REGEN_ENABLED() - self:ApplyClickSet(L.CLICKSET_DEFAULT) - self:RemoveClickSet(L.CLICKSET_HARMFUL) - self:RemoveClickSet(L.CLICKSET_HELPFUL) - self:ApplyClickSet(self.ooc) -end - --- Player is ENTERING combat -function Clique:PLAYER_REGEN_DISABLED() - self:RemoveClickSet(self.ooc) - self:ApplyClickSet(L.CLICKSET_DEFAULT) - self:ApplyClickSet(L.CLICKSET_HARMFUL) - self:ApplyClickSet(L.CLICKSET_HELPFUL) -end - -function Clique:UpdateClicks() - local ooc = self.clicksets[L.CLICKSET_OOC] - local harm = self.clicksets[L.CLICKSET_HARMFUL] - local help = self.clicksets[L.CLICKSET_HELPFUL] - - -- Since harm/help buttons take priority over any others, we can't - -- - -- just apply the OOC set last. Instead we use the self.ooc pseudo - -- set (which we build here) which contains only those help/harm - -- buttons that don't conflict with those defined in OOC. - - self.ooc = table.wipe(self.ooc or {}) - - -- Create a hash map of the "taken" combinations - local takenBinds = {} - - for name, entry in pairs(ooc) do - local key = string.format("%s:%s", entry.modifier, entry.button) - takenBinds[key] = true - table.insert(self.ooc, entry) - end - - for name, entry in pairs(harm) do - local button = string.gsub(entry.button, "harmbutton", "") - local key = string.format("%s:%s", entry.modifier, button) - if not takenBinds[key] then - table.insert(self.ooc, entry) - end - end - - for name, entry in pairs(help) do - local button = string.gsub(entry.button, "helpbutton", "") - local key = string.format("%s:%s", entry.modifier, button) - if not takenBinds[key] then - table.insert(self.ooc, entry) - end - end - - self:UpdateTooltip() -end - -function Clique:RegisterFrame(frame) - local name = frame:GetName() - - if self.profile.blacklist[name] then - rawset(self.ccframes, frame, false) - return - end - - if not ClickCastFrames[frame] then - rawset(self.ccframes, frame, true) - if CliqueTextListFrame then - Clique:TextListScrollUpdate() - end - end - - -- Register AnyUp or AnyDown on this frame, depending on configuration - self:SetClickType(frame) - - if frame:CanChangeAttribute() or frame:CanChangeProtectedState() then - if InCombatLockdown() then - self:ApplyClickSet(L.CLICKSET_DEFAULT, frame) - self:ApplyClickSet(L.CLICKSET_HELPFUL, frame) - self:ApplyClickSet(L.CLICKSET_HARMFUL, frame) - else - self:ApplyClickSet(L.CLICKSET_DEFAULT, frame) - self:ApplyClickSet(self.ooc, frame) - end - end -end - -function Clique:ApplyClickSet(name, frame) - local set = self.clicksets[name] or name - - if frame then - for modifier, entry in pairs(set) do - self:SetAttribute(entry, frame) +function addon:UpdateCombatWatch() + if self.settings.fastooc then + if not self.registeredUnitFlags then + self:RegisterEvent("UNIT_FLAGS", "CheckPartyCombat") + self.registeredUnitFlags = true end else - for modifier, entry in pairs(set) do - self:SetAction(entry) - end + self:UnregisterEvent("UNIT_FLAGS") + self.registeredUnitFlags = false end end -function Clique:RemoveClickSet(name, frame) - local set = self.clicksets[name] or name +function addon:UpdateBlacklist() + local bits = {"blacklist = table.wipe(blacklist)"} - if frame then - for modifier, entry in pairs(set) do - self:DeleteAttribute(entry, frame) - end - else - for modifier, entry in pairs(set) do - self:DeleteAction(entry) - end + for frame, value in pairs(self.settings.blacklist) do if not not value then bits[#bits + 1] = string.format("blacklist[%q] = true", frame) end end + + addon.header:Execute(table.concat(bits, ";\n")) + addon:UpdateRegisteredClicks() +end + +function addon:EnteringCombat() + -- If there are no 'ooc' bindings, then no need to re-apply + if not self.has_ooc then return end + + -- Check to see if we're already in combat, so we don't re-apply + if not self.header:GetAttribute("inCombat") then + -- Apply attributes, indicating we need the 'combat' set + self.header:SetAttribute("inCombat", true) + self.globutton:SetAttribute("inCombat", true) + addon:ApplyAttributes() end end -function Clique:UnregisterFrame(frame) - assert(not InCombatLockdown(), "An addon attempted to unregister a frame from Clique while in combat.") - for name, set in pairs(self.clicksets) do - for modifier, entry in pairs(set) do - self:DeleteAttribute(entry, frame) - end +function addon:LeavingCombat() + -- Process any frames in the registration queue + for idx, button in ipairs(self.regqueue) do self:RegisterFrame(button) end + if next(self.regqueue) then table.wipe(self.regqueue) end + + -- Process any frames in the unregistration queue + for idx, button in ipairs(self.unregqueue) do self:UnregisterFrame(button) end + if next(self.regqueue) then table.wipe(self.regqueue) end + + -- Process any frames in the clickregister queue + for idx, button in ipairs(self.regclickqueue) do self:UpdateRegisteredClicks(button) end + if next(self.regclickqueue) then table.wipe(self.regclickqueue) end + + -- Only apply attributes if we have an 'ooc' binding set + if self.has_ooc then + if self.partyincombat then self.partyincombat = false end + + -- Clear previously set attributes + self:ClearAttributes() + + -- Apply attributes, indicating we want the 'ooc' set + self.header:SetAttribute("inCombat", false) + self.globutton:SetAttribute("inCombat", false) + self:ApplyAttributes() end end -function Clique:DONGLE_PROFILE_CHANGED(event, db, parent, svname, profileKey) - if db == self.db then - if not self.silentProfile then - self:PrintF(L.PROFILE_CHANGED, profileKey) - end - - for name, set in pairs(self.clicksets) do - self:RemoveClickSet(set) - end - self:RemoveClickSet(self.ooc) - - self.profile = self.db.profile - self.clicksets = self.profile.clicksets - self.editSet = self.clicksets[L.CLICKSET_DEFAULT] - self.profileKey = profileKey - - -- Refresh the profile editor if it exists - self.textlistSelected = nil - self:TextListScrollUpdate() - self:ListScrollUpdate() - self:UpdateClicks() - - self:PLAYER_REGEN_ENABLED() - end -end - -function Clique:DONGLE_PROFILE_RESET(event, db, parent, svname, profileKey) - if db == self.db then - for name, set in pairs(self.clicksets) do - self:RemoveClickSet(set) - end - self:RemoveClickSet(self.ooc) - - self.profile = self.db.profile - self.clicksets = self.profile.clicksets - self.editSet = self.clicksets[L.CLICKSET_DEFAULT] - self.profileKey = profileKey - - -- Refresh the profile editor if it exists - self.textlistSelected = nil - self:TextListScrollUpdate() - self:ListScrollUpdate() - self:UpdateClicks() - - self:PLAYER_REGEN_ENABLED() - self:Print(L.PROFILE_RESET, profileKey) - end -end - -function Clique:DONGLE_PROFILE_DELETED(event, db, parent, svname, profileKey) - if db == self.db then - self:PrintF(L.PROFILE_DELETED, profileKey) - - self.textlistSelected = nil - self:TextListScrollUpdate() - self:ListScrollUpdate() - end -end - -function Clique:SetAttribute(entry, frame) - local name = frame:GetName() - - if self.profile.blacklist and self.profile.blacklist[name] then +function addon:CheckPartyCombat(event, unit) + if InCombatLockdown() or not unit then return end + if not self.has_ooc then + -- No change required if no ooc bindings return end - -- 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) - assert(frame:GetAttribute(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) - if entry.arg2 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg2) - end - elseif entry.type == "pet" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - frame:SetAttribute(entry.modifier .. "action" .. button, entry.arg1) - if entry.arg2 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg2) - end - elseif entry.type == "spell" then - local rank = entry.arg2 - local cast - if rank then - if tonumber(rank) then - -- The rank is a number (pre-2.3) so fill in the format - cast = L.CAST_FORMAT:format(entry.arg1, rank) - else - -- The whole rank string is saved (post-2.3) so use it - cast = string.format("%s(%s)", entry.arg1, rank) - end - else - cast = entry.arg1 - end - - 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) - if entry.arg5 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg5) - end - 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) - if entry.arg4 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg4) - end - elseif entry.type == "macro" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - if entry.arg1 then - frame:SetAttribute(entry.modifier .. "macro" .. button, entry.arg1) - else - local unit = SecureButton_GetModifiedUnit(frame, entry.modifier .. "unit" .. button) - local macro = tostring(entry.arg2) - if unit and macro then - macro = macro:gsub("target%s*=%s*clique", "target=" .. unit) - end - - frame:SetAttribute(entry.modifier .. "macro" .. button, nil) - frame:SetAttribute(entry.modifier .. "macrotext" .. button, macro) - end - 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) - if entry.arg1 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1) - end - elseif entry.type == "focus" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - if entry.arg1 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1) - end - elseif entry.type == "assist" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - if entry.arg1 then - frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1) - end - elseif entry.type == "click" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - frame:SetAttribute(entry.modifier .. "clickbutton" .. button, getglobal(entry.arg1)) - elseif entry.type == "menu" then - frame:SetAttribute(entry.modifier .. "type" .. button, entry.type) - end -end - -function Clique:DeleteAttribute(entry, frame) - local name = frame:GetName() - if self.profile.blacklist and self.profile.blacklist[name] then - return - end - - 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, nil) - button = string.format("-%s%s", type, button) - end - - button = button or entry.button - - entry.delete = true - - frame:SetAttribute(entry.modifier .. "type" .. button, nil) - frame:SetAttribute(entry.modifier .. entry.type .. button, nil) -end - -function Clique:SetAction(entry) - for frame, enabled in pairs(self.ccframes) do - if enabled then - self:SetAttribute(entry, frame) - end - end -end - -function Clique:DeleteAction(entry) - for frame in pairs(self.ccframes) do - self:DeleteAttribute(entry, frame) - end -end - -function Clique:ShowAttributes() - self:Print("Enabled enhanced debugging.") - PlayerFrame:SetScript("OnAttributeChanged", function(...) - self:Print(...) - end) - self:UnregisterFrame(PlayerFrame) - self:RegisterFrame(PlayerFrame) -end - -local tt_ooc = {} -local tt_help = {} -local tt_harm = {} -local tt_default = {} - -function Clique:UpdateTooltip() - local ooc = self.ooc - local default = self.clicksets[L.CLICKSET_DEFAULT] - local harm = self.clicksets[L.CLICKSET_HARMFUL] - local help = self.clicksets[L.CLICKSET_HELPFUL] - - for k, v in pairs(tt_ooc) do - tt_ooc[k] = nil - end - for k, v in pairs(tt_help) do - tt_help[k] = nil - end - for k, v in pairs(tt_harm) do - tt_harm[k] = nil - end - for k, v in pairs(tt_default) do - tt_default[k] = nil - end - - -- Build the ooc lines, which includes both helpful and harmful - for k, v in pairs(ooc) do - local button = self:GetButtonText(v.button) - local mod = string.format("%s%s", v.modifier or "", button) - local action = string.format("%s (%s)", v.arg1 or "", v.type) - table.insert(tt_ooc, { - mod = mod, - action = action - }) - end - - -- Build the default lines - for k, v in pairs(default) do - local button = self:GetButtonText(v.button) - local mod = string.format("%s%s", v.modifier or "", button) - local action = string.format("%s (%s)", v.arg1 or "", v.type) - table.insert(tt_default, { - mod = mod, - action = action - }) - end - - -- Build the harm lines - for k, v in pairs(harm) do - local button = self:GetButtonText(v.button) - local mod = string.format("%s%s", v.modifier or "", button) - local action = string.format("%s (%s)", v.arg1 or "", v.type) - table.insert(tt_harm, { - mod = mod, - action = action - }) - end - - -- Build the help lines - for k, v in pairs(help) do - local button = self:GetButtonText(v.button) - local mod = string.format("%s%s", v.modifier or "", button) - local action = string.format("%s (%s)", v.arg1 or "", v.type) - table.insert(tt_help, { - mod = mod, - action = action - }) - end - - local function sort(a, b) - return a.mod < b.mod - end - - table.sort(tt_ooc, sort) - table.sort(tt_default, sort) - table.sort(tt_harm, sort) - table.sort(tt_help, sort) -end - -function Clique:AddTooltipLines() - if not self.profile.tooltips then - return - end - - local frame = GetMouseFocus() - if not frame then - return - end - if not self.ccframes[frame] then - return - end - - -- Add a buffer line - GameTooltip:AddLine(" ") - if UnitAffectingCombat("player") then - if #tt_default ~= 0 then - GameTooltip:AddLine("Default bindings:") - for k, v in ipairs(tt_default) do - GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - - if #tt_help ~= 0 and not UnitCanAttack("player", "mouseover") then - GameTooltip:AddLine("Helpful bindings:") - for k, v in ipairs(tt_help) do - GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - - if #tt_harm ~= 0 and UnitCanAttack("player", "mouseover") then - GameTooltip:AddLine("Hostile bindings:") - for k, v in ipairs(tt_harm) do - GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - else - if #tt_ooc ~= 0 then - GameTooltip:AddLine("Out of combat bindings:") - for k, v in ipairs(tt_ooc) do - GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) + if self.settings.fastooc then + if UnitInParty(unit) or UnitInRaid(unit) then + if UnitAffectingCombat(unit) == 1 then + -- Trigger pre-combat switch for fastooc + self.partyincombat = true + self.combattrigger = UnitGUID(unit) + self.header:SetAttribute("inCombat", true) + self.globutton:SetAttribute("inCombat", true) + addon:ApplyAttributes() + elseif self.partyincombat then + -- The unit is out of combat, so try to clear our flag + if self.combattrigger == UnitGUID(unit) then + self.partyincombat = false + self.header:SetAttribute("inCombat", false) + self.globutton:SetAttribute("inCombat", false) + addon:ApplyAttributes() + end end end end end -function Clique:ToggleTooltip() - self.profile.tooltips = not self.profile.tooltips - self:PrintF("Listing of bindings in tooltips has been %s", self.profile.tooltips and "Enabled" or "Disabled") +-- This function returns whether or not a frame is blacklisted in the current +-- users settings + +function addon:IsFrameBlacklisted(frame) + local name = frame + if type(frame) == "table" then name = frame.GetName and frame:GetName() end + return self.settings.blacklist[name] end -function Clique:ShowBindings() - if not CliqueTooltip then - CliqueTooltip = CreateFrame("GameTooltip", "CliqueTooltip", UIParent, "GameTooltipTemplate") - CliqueTooltip:SetPoint("CENTER", 0, 0) - CliqueTooltip.close = CreateFrame("Button", nil, CliqueTooltip) - CliqueTooltip.close:SetHeight(32) - CliqueTooltip.close:SetWidth(32) - CliqueTooltip.close:SetPoint("TOPRIGHT", 1, 0) - CliqueTooltip.close:SetScript("OnClick", function() - CliqueTooltip:Hide() - end) - CliqueTooltip.close:SetNormalTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Up") - CliqueTooltip.close:SetPushedTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Down") - CliqueTooltip.close:SetHighlightTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Highlight") - - CliqueTooltip:EnableMouse() - CliqueTooltip:SetMovable() - CliqueTooltip:SetPadding(16) - CliqueTooltip:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b); - CliqueTooltip:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, - TOOLTIP_DEFAULT_BACKGROUND_COLOR.b); - - CliqueTooltip:RegisterForDrag("LeftButton") - CliqueTooltip:SetScript("OnDragStart", function(self) - self:StartMoving() - end) - CliqueTooltip:SetScript("OnDragStop", function(self) - self:StopMovingOrSizing() - ValidateFramePosition(self) - end) - CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE") +-- Update both registered clicks, and ensure that mousewheel events are enabled +-- on the frame. +function addon:UpdateRegisteredClicks(button) + if InCombatLockdown() then + table.insert(self.regclickqueue, button) + return end - if not CliqueTooltip:IsShown() then - CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE") + local direction = self.settings.downclick and "AnyDown" or "AnyUp" + + -- Short version that only updates clicks for one frame + if button and not self:IsFrameBlacklisted(button) then + button:RegisterForClicks(direction) + button:EnableMouseWheel(true) + return end - -- Actually fill it with the bindings - CliqueTooltip:SetText("Clique Bindings") - - if #tt_default > 0 then - CliqueTooltip:AddLine(" ") - CliqueTooltip:AddLine("Default bindings:") - for k, v in ipairs(tt_default) do - CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) + for button in pairs(self.ccframes) do + if not self:IsFrameBlacklisted(button) then + button:RegisterForClicks(direction) + button:EnableMouseWheel(true) end end - if #tt_help > 0 then - CliqueTooltip:AddLine(" ") - CliqueTooltip:AddLine("Helpful bindings:") - for k, v in ipairs(tt_help) do - CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - - if #tt_harm > 0 then - CliqueTooltip:AddLine(" ") - CliqueTooltip:AddLine("Hostile bindings:") - for k, v in ipairs(tt_harm) do - CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - - if #tt_ooc > 0 then - CliqueTooltip:AddLine(" ") - CliqueTooltip:AddLine("Out of combat bindings:") - for k, v in ipairs(tt_ooc) do - CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1) - end - end - - CliqueTooltip:Show() -end - -function Clique:ASCENSION_CA_SPECIALIZATION_ACTIVE_ID_CHANGED(event, newGroup) - if self.db.char.switchSpec then - self:Print("Detected a talent spec change, changing profile") - -- self:Print("Detected "..typeName..", changing profile to "..newGroup) - - if self.db.char["spec" .. newGroup .. "Profile"] then - self.db:SetProfile(self.db.char["spec" .. newGroup .. "Profile"]) - end - - if CliqueFrame then - CliqueFrame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile)); + for name, button in pairs(self.hccframes) do + if not self:IsFrameBlacklisted(button) then + button:RegisterForClicks(direction) + button:EnableMouseWheel(true) end end end -function Clique:SetClickType(frame) - local clickType = Clique.db.char.downClick and "AnyDown" or "AnyUp" - if frame then - frame:RegisterForClicks(clickType) +-- Handler function for message indicating that a change as occurred +-- with the configured bindings. This is the only place that the +-- bindings should be re-computed. If this handler is called during +-- combat than execution should be deferred until the user exits +-- combat. +function addon:BINDINGS_CHANGED() + if InCombatLockdown() then + self:Defer("BINDINGS_CHANGED") + return + end + + -- Clear any existing attributes + self:ClearAttributes() + + -- Very simple optimisation. If the player has no 'ooc' bindings + -- set, then attributes can be applied once and then only updated + -- when the bindings list is changed. + local has_ooc = false + for idx, entry in ipairs(self.bindings) do + if entry.sets.ooc then + has_ooc = true + break + end + end + + self.has_ooc = has_ooc + + -- Update all click/binding attributes + self:UpdateAttributes() + + -- Update the bindings list, if open + CliqueConfig:UpdateList() + + -- Update the actual attributes on all frames + self:ApplyAttributes() +end + +function addon:BLACKLIST_CHANGED() + if InCombatLockdown() then + self:Defer("BLACKLIST_CHANGED") + return + end + + -- Clear attributes on all frames + self:ClearAttributes() + + -- Actually update the blacklist accordingly + local bits = {"blacklist = table.wipe(blacklist)"} + + for frame, value in pairs(self.settings.blacklist) do if not not value then bits[#bits + 1] = string.format("blacklist[%q] = true", frame) end end + + addon.header:Execute(table.concat(bits, ";\n")) + + -- Update the registered clicks, to catch any unblacklisted frames + self:UpdateRegisteredClicks() + + -- Update the options panel + if self.UpdateOptionsPanel then self:UpdateOptionsPanel() end + + -- Update the actual attributes on all frames + self:ApplyAttributes() +end + +SLASH_CLIQUE1 = "/clique" +SlashCmdList["CLIQUE"] = function(msg, editbox) + if AscensionSpellbookFrame:IsVisible() then + CliqueConfig:ShowWithSpellBook() else - for frame, enabled in pairs(self.ccframes) do - if enabled then - frame:RegisterForClicks(clickType) - end - end + ShowUIPanel(CliqueConfig) end end - -function Clique:EnableArenaFrames() - local arenaFrames = {ArenaEnemyFrame1, ArenaEnemyFrame2, ArenaEnemyFrame3, ArenaEnemyFrame4, ArenaEnemyFrame5} - - for idx, frame in ipairs(arenaFrames) do - rawset(self.ccframes, frame, true) - end -end - -function Clique:ADDON_LOADED(event, addon) - if addon == "Blizzard_ArenaUI" then - self:EnableArenaFrames() - end -end - --- Check whether the user is in a raid group or has raid-style party frames (CompactRaidFrames) enabled -function Clique:IsInRaidOrHasRaidFramesEnabled() - return IsInRaid() or GetCVarBool("useCompactPartyFrames") -end - --- Remove stale frames from `self.ccframes` that are no longer valid. --- --- Ensures we only store the default frames and currently active raid frames. -function Clique:CleanStaleCCFrames(frames) - local validFrames = {} - - -- add default frames - for _, f in ipairs(self.defaults.frames) do - validFrames[f] = true - end - - -- add provided frames (e.g. dynamic raid frames) - for _, f in ipairs(frames) do - validFrames[f] = true - end - - -- prune stale frames that are no longer valid - for f in pairs(self.ccframes) do - if not validFrames[f] then - self.ccframes[f] = nil - end - end -end - --- Enumerate and return currently active raid frames --- --- NOTE: CompactRaidFrames, or rather all raid frames in general – for either a --- player or their pets – should be defined in the following format: --- CompactRaidFrame{index} e.g. CompactRaidFrame1. -function Clique:GetActiveRaidFrames() - local frames = {} - - local frame = EnumerateFrames() - while frame do - local name = frame:GetName() - -- include only raid frames starting with `CompactRaidFrame` and ending with - -- a digit while having a valid unit value (ensures the frame belongs to a - -- player) - if name and name:find("^CompactRaidFrame%d+$") and frame.unit then - table.insert(frames, frame) - end - - frame = EnumerateFrames(frame) - end - - return frames -end - --- Collect currently active raid frames, prune stale ones and re-register valid --- ones -function Clique:EnableRaidFrames() - local raidFrames = self:GetActiveRaidFrames() - self:CleanStaleCCFrames(raidFrames) - for _, f in ipairs(raidFrames) do - self:RegisterFrame(f) - end -end - --- Shared event handler for syncing dynamic raid/party frame changes --- --- NOTE: CompactRaidFrames, or rather all raid frames in general, are dynamic and are added to the --- UIParent's "frame tree" on-demand, which requires hooking into multiple --- different events that gets fired when the party or raid group roster changes. --- Pets or CVar -related events need special handling and thus are separated. -local function HandleRaidFrameSync(self, event, unit) - if not self:IsInRaidOrHasRaidFramesEnabled() then return end - - if event == "UNIT_PET" then - -- only refresh if the pet belongs to player/party/raid - if unit == "player" or unit:match("^party%d+$") or unit:match("^raid%d+$") then - self:EnableRaidFrames() - end - elseif event == "CVAR_UPDATE" then - -- sync raid frames when toggling the interface option on/off - if unit == "USE_RAID_STYLE_PARTY_FRAMES" then - self:EnableRaidFrames() - end - else - self:EnableRaidFrames() - end -end - --- Bind handler to events -Clique.PLAYER_ENTERING_WORLD = HandleRaidFrameSync -Clique.RAID_ROSTER_UPDATE = HandleRaidFrameSync -Clique.PARTY_MEMBERS_CHANGED = HandleRaidFrameSync -Clique.UNIT_PET = HandleRaidFrameSync -Clique.CVAR_UPDATE = HandleRaidFrameSync diff --git a/Clique/Clique.toc b/Clique/Clique.toc index cfa9b8e..b378503 100644 --- a/Clique/Clique.toc +++ b/Clique/Clique.toc @@ -1,26 +1,29 @@ -## Interface: 30300 +## Interface: 30300 ## Title: Clique -## Author: Cladhaire, Jobus -## Version: wowi:revision -## Notes: Simply powerful click-casting interface. Tailored for Ascension. -## SavedVariables: CliqueDB -## OptionalDeps: Dongle -## X-Curse-Packaged-Version: r143-release -## X-Curse-Project-Name: Clique -## X-Curse-Project-ID: clique -## X-Curse-Repository-ID: wow/clique/mainline +## Author: Cladhaire, Tsoukie +## Version: 1.6 +## Notes: Simply powerful click-casting interface +## SavedVariables: CliqueDB, CliqueDB3 + +AddonCore.lua +i18n\Localization.enUS.lua +i18n\Localization.ruRU.lua +i18n\Localization.zhCN.lua +i18n\Localization.zhTW.lua + +DatabaseDefaults.lua +ClickCastTemplate.xml -Dongle.lua Clique.xml Clique.lua -Localization.enUS.lua -Localization.esES.lua -Localization.esMX.lua -Localization.frFR.lua -Localization.deDE.lua -Localization.koKR.lua -Localization.ruRU.lua -Localization.zhCN.lua -Localization.zhTW.lua -CliqueOptions.lua -CliqueUtils.lua + +Utils.lua +ImportExport.lua + +config\BindConfig.lua +config\OptionsPanel.lua +config\BlizzardFramesConfig.lua +config\DenylistConfig.lua + +modules\Blizzard_utils.lua +modules\Blizzard_wrath.lua diff --git a/Clique/Clique.xml b/Clique/Clique.xml index 92b29f6..2917409 100644 --- a/Clique/Clique.xml +++ b/Clique/Clique.xml @@ -1,96 +1,535 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - local scrollBar = getglobal(this:GetParent():GetName().."ScrollBar") - this:GetParent():UpdateScrollChildRect(); - local min; - local max; - min, max = scrollBar:GetMinMaxValues(); - if ( max > 0 and (this.max ~= max) ) then - this.max = max; - scrollBar:SetValue(max); - end - - - this:ClearFocus(); - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.arrow:SetSize(21, 53) + self.arrow.arrow = _G[self.arrow:GetName() .. "Arrow"] + self.arrow.glow = _G[self.arrow:GetName() .. "Glow"] + self.arrow.arrow:SetAllPoints(true) + self.arrow.glow:SetAllPoints(true) + -- Rotate 90 degrees + -- left, bottom, right, bottom, left, top, right, top + self.arrow.arrow:SetTexCoord(0.78515625, 0.58789063, 0.99218750, 0.58789063, 0.78515625, 0.54687500, 0.99218750, 0.54687500) + self.arrow.glow:SetTexCoord(0.40625000, 0.82812500, 0.66015625, 0.82812500, 0.40625000, 0.77343750, 0.66015625, 0.77343750) + self.text:SetSpacing(4) + + + + self:SetHeight(self.text:GetHeight() + 42) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + table.insert(UISpecialFrames, "CliqueDialog") + + + + + + + + + + + + + + + + + + + + + + + + + + CliqueConfig:EnableSpellbookButtons() + + + CliqueConfig:EnableSpellbookButtons() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self.text:SetSpacing(4) + + + + + + + CliqueConfigPortrait:SetTexture("Interface\\Addons\\Clique\\images\\icon_circle_128") + CliqueConfig.page1:SetParent(CliqueConfig.Inset) + CliqueConfig.page2:SetParent(CliqueConfig.Inset) + CliqueConfig.page1:SetAllPoints(CliqueConfig.Inset) + CliqueConfig.page2:SetAllPoints(CliqueConfig.Inset) + + + CliqueConfig:OnShow() + + + CliqueConfig:OnHide() + + + diff --git a/Clique/CliqueOptions.lua b/Clique/CliqueOptions.lua deleted file mode 100644 index 3b9741c..0000000 --- a/Clique/CliqueOptions.lua +++ /dev/null @@ -1,1956 +0,0 @@ -local genv = getfenv(0) -local Clique = genv.Clique -local L = Clique.Locals -local StaticPopupDialogs = genv.StaticPopupDialogs -local TEXT = genv.TEXT -local OKAY = genv.OKAY -local CANCEL = genv.CANCEL -local GameTooltip = genv.GameTooltip - -local NUM_ENTRIES = 10 -local ENTRY_SIZE = 35 -local work = {} - -function Clique:OptionsOnLoad() - -- Create a set of buttons to hook the AscensionSpellbookFrame - self.spellbuttons = {} - local onclick = function(frame, button) Clique:SpellBookButtonPressed(frame, button) end - local onleave = function(button) - button.updateTooltip = nil - GameTooltip:Hide() - end - - for i=1,12 do - local parent = getglobal("AscensionSpellbookFrameContentSpellsSpellButton"..i) - local button = CreateFrame("Button", "SpellButtonCliqueCover"..i, parent) - button:SetID(parent:GetID()) - button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") - button:RegisterForClicks("AnyUp") - button:SetAllPoints(parent) - button:SetScript("OnClick", onclick) - button:SetScript("OnEnter", function(self) - local parent = self:GetParent() - if parent:IsEnabled() == 1 then - SpellButton_OnEnter(parent) - else - button:GetHighlightTexture():Hide() - end - end) - button:SetScript("OnLeave", onleave) - - button:Hide() - self.spellbuttons[i] = button - end - - CreateFrame("CheckButton", "CliquePulloutTab", getglobal("AscensionSpellbookFrameContentSpellsSpellButton1"), "SpellBookSkillLineTabTemplate") - CliquePulloutTab:SetNormalTexture("Interface\\AddOns\\Clique\\Images\\CliqueIcon") - CliquePulloutTab:SetScript("OnClick", function() Clique:Toggle() end) - CliquePulloutTab:SetScript("OnEnter", function() local i = 1 end) - CliquePulloutTab:SetScript("OnShow", function() - Clique.inuse = nil - for k,v in pairs(self.clicksets) do - if next(v) then - Clique.inuse = true - end - end - if not Clique.inuse then - CliqueFlashFrame.texture:Show() - CliqueFlashFrame.texture:SetAlpha(1.0) - - local counter, loops, fading = 0, 0, true - CliqueFlashFrame:SetScript("OnUpdate", function(self, elapsed) - counter = counter + elapsed - if counter > 0.5 then - loops = loops + 0.5 - fading = not fading - counter = counter - 0.5 - end - - if loops > 30 then - self.texture:Hide() - self:SetScript("OnUpdate", nil) - return - end - - local texture = self.texture - if fading then - texture:SetAlpha(1.0 - (counter / 0.5)) - else - texture:SetAlpha(counter / 0.5) - end - end) - end - end) - CliquePulloutTab:SetScript("OnEnter", function(self) - GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT") - GameTooltip:SetText("Clique configuration") - GameTooltip:Show() - end) - - local frame = CreateFrame("Frame", "CliqueFlashFrame", CliquePulloutTab) - frame:SetWidth(10) frame:SetHeight(10) - frame:SetPoint("CENTER", 0, 0) - - local texture = frame:CreateTexture(nil, "OVERLAY") - texture:SetTexture("Interface\\Buttons\\CheckButtonGlow") - texture:SetHeight(64) texture:SetWidth(64) - texture:SetPoint("CENTER", 0, 0) - texture:Hide() - CliqueFlashFrame.texture = texture - - CliquePulloutTab:Show() - - -- Hook the container buttons - local containerFunc = function(button) - if IsShiftKeyDown() and CliqueCustomArg1 then - if CliqueCustomArg1:HasFocus() then - CliqueCustomArg1:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID())) - elseif CliqueCustomArg2:HasFocus() then - CliqueCustomArg2:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID())) - elseif CliqueCustomArg3:HasFocus() then - CliqueCustomArg3:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID())) - elseif CliqueCustomArg4:HasFocus() then - CliqueCustomArg4:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID())) - elseif CliqueCustomArg5:HasFocus() then - CliqueCustomArg5:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID())) - end - end - end - - hooksecurefunc("ContainerFrameItemButton_OnModifiedClick", containerFunc) - - -- Hook the bank buttons - local bankFunc = function(button) - if IsShiftKeyDown() and CliqueCustomArg1 then - if CliqueCustomArg1:HasFocus() then - CliqueCustomArg1:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID())) - elseif CliqueCustomArg2:HasFocus() then - CliqueCustomArg2:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID())) - elseif CliqueCustomArg3:HasFocus() then - CliqueCustomArg3:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID())) - elseif CliqueCustomArg4:HasFocus() then - CliqueCustomArg4:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID())) - elseif CliqueCustomArg5:HasFocus() then - CliqueCustomArg5:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID())) - end - end - end - - hooksecurefunc("BankFrameItemButtonGeneric_OnModifiedClick", bankFunc) - - -- Hook the paper doll frame buttons - local dollFunc = function(button) - if IsShiftKeyDown() and CliqueCustomArg1 then - if CliqueCustomArg1:HasFocus() then - CliqueCustomArg1:Insert(GetInventoryItemLink("player", button:GetID())) - elseif CliqueCustomArg2:HasFocus() then - CliqueCustomArg2:Insert(GetInventoryItemLink("player", button:GetID())) - elseif CliqueCustomArg3:HasFocus() then - CliqueCustomArg3:Insert(GetInventoryItemLink("player", button:GetID())) - elseif CliqueCustomArg4:HasFocus() then - CliqueCustomArg4:Insert(GetInventoryItemLink("player", button:GetID())) - elseif CliqueCustomArg5:HasFocus() then - CliqueCustomArg5:Insert(GetInventoryItemLink("player", button:GetID())) - end - end - end - hooksecurefunc("PaperDollItemSlotButton_OnModifiedClick", dollFunc) -end - -function Clique:LEARNED_SPELL_IN_TAB() - local num = GetNumSpellTabs() - CliquePulloutTab:ClearAllPoints() - CliquePulloutTab:SetPoint("BOTTOMLEFT", AscensionSpellbookFrameSideBarTab1,"TOPLEFT",0,10) -end - -function Clique:ToggleSpellBookButtons() - local method = CliqueFrame:IsVisible() and "Show" or "Hide" - local buttons = self.spellbuttons - for i=1,12 do - buttons[i][method](buttons[i]) - end -end - -function Clique:Toggle() - if not CliqueFrame then - Clique:CreateOptionsFrame() - CliqueFrame:Hide() - CliqueFrame:Show() - else - if CliqueFrame:IsVisible() then - CliqueFrame:Hide() - CliquePulloutTab:SetChecked(nil) - else - CliqueFrame:Show() - CliquePulloutTab:SetChecked(true) - end - end - - Clique:ToggleSpellBookButtons() - self:ListScrollUpdate() -end - --- This code is contributed with permission from Beladona -local ondragstart = function(self) - self:GetParent():StartMoving() -end - -local ondragstop = function(self) - self:GetParent():StopMovingOrSizing() - self:GetParent():SetUserPlaced() -end - -function Clique:SkinFrame(frame) - frame:SetBackdrop({ - bgFile = "Interface\\AddOns\\Clique\\images\\backdrop.tga", - edgeFile = "Interface\\AddOns\\Clique\\images\\borders.tga", tile = true, - tileSize = 32, edgeSize = 16, - insets = {left = 16, right = 16, top = 16, bottom = 16} - }); - - frame:EnableMouse() - frame:SetClampedToScreen(true) - - frame.titleBar = CreateFrame("Button", nil, frame) - frame.titleBar:SetHeight(32) - frame.titleBar:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -2) - frame.titleBar:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -2, -2) - frame:SetMovable(true) - frame:SetFrameStrata("MEDIUM") - frame.titleBar:RegisterForDrag("LeftButton") - frame.titleBar:SetScript("OnDragStart", ondragstart) - frame.titleBar:SetScript("OnDragStop", ondragstop) - - frame.headerLeft = frame.titleBar:CreateTexture(nil, "ARTWORK"); - frame.headerLeft:SetTexture("Interface\\AddOns\\Clique\\images\\headCorner.tga"); - frame.headerLeft:SetWidth(32); frame.headerLeft:SetHeight(32); - frame.headerLeft:SetPoint("TOPLEFT", 0, 0); - - frame.headerRight = frame.titleBar:CreateTexture(nil, "ARTWORK"); - frame.headerRight:SetTexture("Interface\\AddOns\\Clique\\images\\headCorner.tga"); - frame.headerRight:SetTexCoord(1,0,0,1); - frame.headerRight:SetWidth(32); frame.headerRight:SetHeight(32); - frame.headerRight:SetPoint("TOPRIGHT", 0, 0); - - frame.header = frame.titleBar:CreateTexture(nil, "ARTWORK"); - frame.header:SetTexture("Interface\\AddOns\\Clique\\images\\header.tga"); - frame.header:SetPoint("TOPLEFT", frame.headerLeft, "TOPRIGHT"); - frame.header:SetPoint("BOTTOMRIGHT", frame.headerRight, "BOTTOMLEFT"); - - frame.title = frame.titleBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall"); - frame.title:SetWidth(200); frame.title:SetHeight(16); - frame.title:SetPoint("TOP", 0, -2); - - frame.footerLeft = frame:CreateTexture(nil, "ARTWORK"); - frame.footerLeft:SetTexture("Interface\\AddOns\\Clique\\images\\footCorner.tga"); - frame.footerLeft:SetWidth(48); frame.footerLeft:SetHeight(48); - frame.footerLeft:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 2, 2); - - frame.footerRight = frame:CreateTexture(nil, "ARTWORK"); - frame.footerRight:SetTexture("Interface\\AddOns\\Clique\\images\\footCorner.tga"); - frame.footerRight:SetTexCoord(1,0,0,1); - frame.footerRight:SetWidth(48); frame.footerRight:SetHeight(48); - frame.footerRight:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 2); - - frame.footer = frame:CreateTexture(nil, "ARTWORK"); - frame.footer:SetTexture("Interface\\AddOns\\Clique\\images\\footer.tga"); - frame.footer:SetPoint("TOPLEFT", frame.footerLeft, "TOPRIGHT"); - frame.footer:SetPoint("BOTTOMRIGHT", frame.footerRight, "BOTTOMLEFT"); -end - -function Clique:CreateOptionsFrame() - local frames = {} - self.frames = frames - - local frame = CreateFrame("Frame", "CliqueFrame", CliquePulloutTab) - frame:SetHeight(415) - frame:SetWidth(400) - frame:SetPoint("LEFT", AscensionSpellbookFrame, "RIGHT", 45, 30) - self:SkinFrame(frame) - frame:SetToplevel(true) - frame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile)); - frame:SetScript("OnShow", function() - frame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile)); - if Clique.inuse then - CliqueHelpText:Hide() - else - CliqueHelpText:Show() - end - end) - - CliqueFrame:SetScript("OnShow", function(self) - if InCombatLockdown() then - Clique:Toggle() - return - end - local parent = self:GetParent() - self:SetFrameLevel(parent:GetFrameLevel() + 5) - Clique:ToggleSpellBookButtons() - end) - - CliqueFrame:SetScript("OnHide", function() Clique:ToggleSpellBookButtons() end) - CliqueFrame:RegisterEvent("PLAYER_REGEN_DISABLED") - CliqueFrame:SetScript("OnEvent", function(self, event, ...) - if self:IsVisible() then - Clique:Toggle() - end - end) - - local frame = CreateFrame("Frame", "CliqueListFrame", CliqueFrame) - frame:SetAllPoints() - - local onclick = function(button) - local offset = FauxScrollFrame_GetOffset(CliqueListScroll) - self.listSelected = offset + button.id - Clique:ListScrollUpdate() - end - - local ondoubleclick = function(button) - onclick(button) - CliqueButtonEdit:Click() - end - - local onenter = function(button) button:SetBackdropBorderColor(1, 1, 1) end - local onleave = function(button) - local selected = FauxScrollFrame_GetOffset(CliqueListScroll) + button.id - if selected == self.listSelected then - button:SetBackdropBorderColor(1, 1, 0) - else - button:SetBackdropBorderColor(0.3, 0.3, 0.3) - end - end - - for i=1,NUM_ENTRIES do - local entry = CreateFrame("Button", "CliqueList"..i, frame) - entry.id = i - entry:SetHeight(ENTRY_SIZE) - entry:SetWidth(390) - entry:SetBackdrop({ - bgFile="Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface/Tooltips/UI-Tooltip-Border", - tile = true, tileSize = 8, edgeSize = 16, - insets = {left = 2, right = 2, top = 2, bottom = 2}}) - - entry:SetBackdropBorderColor(0.3, 0.3, 0.3) - entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3) - entry:SetScript("OnClick", onclick) - entry:SetScript("OnEnter", onenter) - entry:SetScript("OnLeave", onleave) - entry:SetScript("OnDoubleClick", ondoubleclick) - - entry.icon = entry:CreateTexture(nil, "ARTWORK") - entry.icon:SetHeight(24) - entry.icon:SetWidth(24) - entry.icon:SetPoint("LEFT", 5, 0) - - entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - entry.name:SetPoint("LEFT", entry.icon, "RIGHT", 5, 0) - - entry.binding = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - entry.binding:SetPoint("RIGHT", entry, "RIGHT", -5, 0) - frames[i] = entry - end - - frames[1]:SetPoint("TOPLEFT", 5, -55) - for i=2,NUM_ENTRIES do - frames[i]:SetPoint("TOP", frames[i-1], "BOTTOM", 0, 2) - end - - local endButton = getglobal("CliqueList"..NUM_ENTRIES) - CreateFrame("ScrollFrame", "CliqueListScroll", CliqueListFrame, "FauxScrollFrameTemplate") - CliqueListScroll:SetPoint("TOPLEFT", CliqueList1, "TOPLEFT", 0, 0) - CliqueListScroll:SetPoint("BOTTOMRIGHT", endButton, "BOTTOMRIGHT", 0, 0) - - local texture = CliqueListScroll:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueListScroll, "TOPRIGHT", 14, 0) - texture:SetPoint("BOTTOMRIGHT", CliqueListScroll, "BOTTOMRIGHT", 23, 0) - texture:SetGradientAlpha("HORIZONTAL", 0.5, 0.25, 0.05, 0, 0.15, 0.15, 0.15, 1) - - local texture = CliqueListScroll:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueListScroll, "TOPRIGHT", 4, 0) - texture:SetPoint("BOTTOMRIGHT", CliqueListScroll, "BOTTOMRIGHT", 14, 0) - texture:SetGradientAlpha("HORIZONTAL", 0.15, 0.15, 0.15, 0.15, 1, 0.5, 0.25, 0.05, 0) - - local update = function() Clique:ListScrollUpdate() end - - CliqueListScroll:SetScript("OnVerticalScroll", update, function(self, offset) - FauxScrollFrame_OnVerticalScroll(self, offset, ENTRY_SIZE, update) - end) - - CliqueListScroll:SetScript("OnShow", update) - - local frame = CreateFrame("Frame", "CliqueTextListFrame", CliqueFrame) - frame:SetHeight(300) - frame:SetWidth(250) - frame:SetPoint("BOTTOMLEFT", CliqueFrame, "BOTTOMRIGHT", 0, 0) - self:SkinFrame(frame) - frame:SetFrameStrata("HIGH") - - frame:SetScript("OnShow", function(self) - local parent = self:GetParent() - self:SetFrameLevel(parent:GetFrameLevel() + 5) - end) - - local onclick = function(button) - local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll) - if self.textlistSelected == offset + button.id then - self.textlistSelected = nil - else - self.textlistSelected = offset + button.id - end - if self.textlist == "FRAMES" then - local name = button.name:GetText() - local frame = getglobal(name) - if button:GetChecked() then - self.profile.blacklist[name] = nil - self:RegisterFrame(getglobal(name)) - else - self:UnregisterFrame(frame) - self.profile.blacklist[name] = true - end - end - Clique:TextListScrollUpdate() - end - - local onenter = function(button) button:SetBackdropBorderColor(1, 1, 1) end - local onleave = function(button) - local selected = FauxScrollFrame_GetOffset(CliqueTextListScroll) + button.id - button:SetBackdropBorderColor(0.3, 0.3, 0.3) - end - - local frames = {} - - for i=1,12 do - local entry = CreateFrame("CheckButton", "CliqueTextList"..i, frame) - entry.id = i - entry:SetHeight(22) - entry:SetWidth(240) - entry:SetBackdrop({ --- bgFile="Interface\\Tooltips\\UI-Tooltip-Background", --- edgeFile = "Interface/Tooltips/UI-Tooltip-Border", --- tile = true, tileSize = 8, edgeSize = 16, - insets = {left = 2, right = 2, top = 2, bottom = 2}}) - - entry:SetBackdropBorderColor(0.3, 0.3, 0.3) - entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3) - entry:SetScript("OnClick", onclick) - entry:SetScript("OnEnter", onenter) - entry:SetScript("OnLeave", onleave) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - entry:SetNormalTexture(texture) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - texture:SetBlendMode("ADD") - entry:SetHighlightTexture(texture) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - entry:SetCheckedTexture(texture) - - entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - entry.name:SetPoint("LEFT", 25, 0) - entry.name:SetJustifyH("LEFT") - entry.name:SetText("Profile Name") - frames[i] = entry - end - - frames[1]:SetPoint("TOPLEFT", 5, -25) - for i=2,12 do - frames[i]:SetPoint("TOP", frames[i-1], "BOTTOM", 0, 2) - end - - local endButton = CliqueTextList12 - CreateFrame("ScrollFrame", "CliqueTextListScroll", CliqueTextListFrame, "FauxScrollFrameTemplate") - CliqueTextListScroll:SetPoint("TOPLEFT", CliqueTextList1, "TOPLEFT", 0, 0) - CliqueTextListScroll:SetPoint("BOTTOMRIGHT", endButton, "BOTTOMRIGHT", 0, 0) - - local texture = CliqueTextListScroll:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueTextListScroll, "TOPRIGHT", 14, 0) - texture:SetPoint("BOTTOMRIGHT", CliqueTextListScroll, "BOTTOMRIGHT", 23, 0) - texture:SetGradientAlpha("HORIZONTAL", 0.5, 0.25, 0.05, 0, 0.15, 0.15, 0.15, 1) - - local texture = CliqueTextListScroll:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueTextListScroll, "TOPRIGHT", 4, 0) - texture:SetPoint("BOTTOMRIGHT", CliqueTextListScroll, "BOTTOMRIGHT", 14, 0) - texture:SetGradientAlpha("HORIZONTAL", 0.15, 0.15, 0.15, 0.15, 1, 0.5, 0.25, 0.05, 0) - - local update = function() - Clique:TextListScrollUpdate() - end - - CliqueTextListScroll:SetScript("OnVerticalScroll", function(self, offset) - FauxScrollFrame_OnVerticalScroll(self, offset, 22, update) - end) - CliqueTextListFrame:SetScript("OnShow", update) - CliqueTextListFrame:Hide() - - -- Dropdown Frame - CreateFrame("Frame", "CliqueDropDown", CliqueFrame, "UIDropDownMenuTemplate") - CliqueDropDown:SetID(1) - CliqueDropDown:SetPoint("TOPRIGHT", -115, -25) - CliqueDropDown:SetScript("OnShow", function(self) Clique:DropDown_OnShow(self) end) - - CliqueDropDownButton:SetScript("OnEnter", function(self) - GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT") - GameTooltip:SetText("Select a click-set to edit") - GameTooltip:Show() - end) - CliqueDropDownButton:SetScript("OnLeave", function(self) - GameTooltip:Hide() - end) - - local font = CliqueDropDown:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall") - font:SetText("Click Set:") - font:SetPoint("RIGHT", CliqueDropDown, "LEFT", 5, 3) - -- Button Creations - local buttonFunc = function(self) Clique:ButtonOnClick(self) end - - local button = CreateFrame("Button", "CliqueButtonClose", CliqueFrame.titleBar, "UIPanelCloseButton") - button:SetHeight(25) - button:SetWidth(25) - button:SetPoint("TOPRIGHT", -5, 3) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonCustom", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Custom") - button:SetPoint("BOTTOMLEFT", CliqueFrame, "BOTTOMLEFT", 10, 5) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonFrames", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Frames") - button:SetPoint("LEFT", CliqueButtonCustom, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonProfiles", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Profiles") - button:SetPoint("LEFT", CliqueButtonFrames, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonOptions", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Options") - button:SetPoint("LEFT", CliqueButtonProfiles, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonDelete", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Delete") - button:SetPoint("LEFT", CliqueButtonOptions, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonEdit", CliqueFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Edit") - button:SetPoint("LEFT", CliqueButtonDelete, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - -- Buttons for text list scroll frame - - local button = CreateFrame("Button", "CliqueTextButtonClose", CliqueTextListFrame.titleBar, "UIPanelCloseButton") - button:SetHeight(25) - button:SetWidth(25) - button:SetPoint("TOPRIGHT", -5, 3) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonDeleteProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Delete") - button:SetPoint("BOTTOMLEFT", CliqueTextListFrame, "BOTTOMLEFT", 30, 5) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonSetProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("Set") - button:SetPoint("LEFT", CliqueButtonDeleteProfile, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueButtonNewProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(60) - button:SetText("New") - button:SetPoint("LEFT", CliqueButtonSetProfile, "RIGHT", 3, 0) - button:SetScript("OnClick", buttonFunc) - - local frame = CreateFrame("Frame", "CliqueOptionsFrame", CliqueFrame) - frame:SetHeight(500) - frame:SetWidth(300) - frame:SetPoint("CENTER", 0, 0) - self:SkinFrame(frame) - frame:SetFrameStrata("DIALOG") - frame.title:SetText(L["Clique Options"]) - frame:Hide() - self:CreateOptionsWidgets(frame) - - self.customEntry = {} - local frame = CreateFrame("Frame", "CliqueCustomFrame", CliqueFrame) - frame:SetHeight(400) - frame:SetWidth(450) - frame:SetPoint("CENTER", 70, -50) - self:SkinFrame(frame) - frame:SetFrameStrata("DIALOG") - frame.title:SetText("Clique Custom Editor"); - frame:Hide() - - frame:SetScript("OnShow", function(self) - local parent = self:GetParent() - self:SetFrameLevel(parent:GetFrameLevel() + 5) - end) - - -- Help text for Custom screen - - local font = frame:CreateFontString("CliqueCustomHelpText", "OVERLAY", "GameFontHighlight") - font:SetWidth(260) font:SetHeight(100) - font:SetPoint("TOPRIGHT", -10, -25) - font:SetText(L.CUSTOM_HELP) - - local checkFunc = function(self) Clique:CustomRadio(self) end - self.radio = {} - - local buttons = { - {type = "actionbar", name = L.ACTION_ACTIONBAR}, - {type = "action", name = L.ACTION_ACTION}, - {type = "pet", name = L.ACTION_PET}, - {type = "spell", name = L.ACTION_SPELL}, - {type = "item", name = L.ACTION_ITEM}, - {type = "macro", name = L.ACTION_MACRO}, - {type = "stop", name = L.ACTION_STOP}, - {type = "target", name = L.ACTION_TARGET}, - {type = "focus", name = L.ACTION_FOCUS}, - {type = "assist", name = L.ACTION_ASSIST}, - {type = "click", name = L.ACTION_CLICK}, - {type = "menu", name = L.ACTION_MENU}, - } - - for i=1,#buttons do - local entry = buttons[i] - - local name = "CliqueRadioButton"..entry.type - local button = CreateFrame("CheckButton", name, CliqueCustomFrame) - button:SetHeight(20) - button:SetWidth(150) - - local texture = button:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioEmpty") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - button:SetNormalTexture(texture) - - local texture = button:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioChecked") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - texture:SetBlendMode("ADD") - button:SetHighlightTexture(texture) - - local texture = button:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioChecked") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - button:SetCheckedTexture(texture) - - button.name = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - button.name:SetPoint("LEFT", 25, 0) - button.name:SetJustifyH("LEFT") - - local entry = buttons[1] - local name = "CliqueRadioButton"..entry.type - local button = CreateFrame("CheckButton", name, CliqueCustomFrame) - button:SetHeight(22) - button:SetWidth(150) - end - - local entry = buttons[1] - local button = getglobal("CliqueRadioButton"..entry.type) - button.type = entry.type - button.name:SetText(entry.name) - button:SetPoint("TOPLEFT", 5, -30) - button:SetScript("OnClick", checkFunc) - self.radio[button] = true - - local prev = button - - for i=2,#buttons do - local entry = buttons[i] - local name = "CliqueRadioButton"..entry.type - local button = getglobal(name) - - button.type = entry.type - button.name:SetText(entry.name) - button:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, 0) - button:SetScript("OnClick", checkFunc) - self.radio[button] = true - prev = button - end - - -- Button to set the binding - - local button = CreateFrame("Button", "CliqueCustomButtonBinding", CliqueCustomFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(30) - button:SetWidth(175) - button:SetText("Set Click Binding") - button:SetPoint("TOP", CliqueCustomHelpText, "BOTTOM", 40, -10) - button:SetScript("OnClick", function(self) Clique:CustomBinding_OnClick(self) end ) - button:RegisterForClicks("AnyUp") - - -- Button for icon selection - - local button = CreateFrame("Button", "CliqueCustomButtonIcon", CliqueCustomFrame) - button.icon = button:CreateTexture(nil, "BORDER") - button.icon:SetAllPoints() - button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") - button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") - button:GetHighlightTexture():SetBlendMode("ADD") - button:SetHeight(30) - button:SetWidth(30) - button:SetPoint("RIGHT", CliqueCustomButtonBinding, "LEFT", -15, 0) - - local func = function() - GameTooltip:SetOwner(button, "ANCHOR_TOPLEFT") - GameTooltip:SetText("Click here to set icon") - GameTooltip:Show() - end - - button:SetScript("OnEnter", func) - button:SetScript("OnLeave", function() GameTooltip:Hide() end) - button:SetScript("OnClick", function() CliqueIconSelectFrame:Show() end) - - -- Create the editboxes for action arguments - - local edit = CreateFrame("EditBox", "CliqueCustomArg1", CliqueCustomFrame, "InputBoxTemplate") - edit:SetHeight(30) - edit:SetWidth(200) - edit:SetPoint("TOPRIGHT", CliqueCustomFrame, "TOPRIGHT", -10, -190) - edit:SetAutoFocus(nil) - edit:SetScript("OnTabPressed", function() - if CliqueCustomArg2:IsVisible() then - CliqueCustomArg2:SetFocus() - end - end) - edit:SetScript("OnEnterPressed", function() end) - - edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - edit.label:SetText("Spell Name:") - edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0) - edit.label:SetJustifyH("RIGHT") - edit:Hide() - - -- Argument 2 - - local edit = CreateFrame("EditBox", "CliqueCustomArg2", CliqueCustomFrame, "InputBoxTemplate") - edit:SetHeight(30) - edit:SetWidth(200) - edit:SetPoint("TOPRIGHT", CliqueCustomArg1, "BOTTOMRIGHT", 0, 0) - edit:SetAutoFocus(nil) - edit:SetScript("OnTabPressed", function() - if CliqueCustomArg3:IsVisible() then - CliqueCustomArg3:SetFocus() - end - end) - edit:SetScript("OnEnterPressed", function() end) - - edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - edit.label:SetText("Spell Name:") - edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0) - edit.label:SetJustifyH("RIGHT") - edit:Hide() - - -- Multi line edit box - - local edit = CreateFrame("ScrollFrame", "CliqueMulti", CliqueCustomFrame, "CliqueEditTemplate") - edit:SetPoint("TOPRIGHT", CliqueCustomArg1, "BOTTOMRIGHT", -10, -27) - - local name = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - name:SetText("Macro Text:") - name:SetJustifyH("RIGHT") - name:SetPoint("RIGHT", CliqueCustomArg2.label) - - local grabber = CreateFrame("Button", "CliqueFocusGrabber", edit) - grabber:SetPoint("TOPLEFT", 8, -8) - grabber:SetPoint("BOTTOMRIGHT", -8, 8) - grabber:SetScript("OnClick", function() CliqueMultiScrollFrameEditBox:SetFocus() end) - - -- Argument 3 - - local edit = CreateFrame("EditBox", "CliqueCustomArg3", CliqueCustomFrame, "InputBoxTemplate") - edit:SetHeight(30) - edit:SetWidth(200) - edit:SetPoint("TOPRIGHT", CliqueCustomArg2, "BOTTOMRIGHT", 0, 0) - edit:SetAutoFocus(nil) - edit:SetScript("OnTabPressed", function() - if CliqueCustomArg4:IsVisible() then - CliqueCustomArg4:SetFocus() - end - end) - edit:SetScript("OnEnterPressed", function() end) - - edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - edit.label:SetText("Spell Name:") - edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0) - edit.label:SetJustifyH("RIGHT") - edit:Hide() - - -- Argument 4 - - local edit = CreateFrame("EditBox", "CliqueCustomArg4", CliqueCustomFrame, "InputBoxTemplate") - edit:SetHeight(30) - edit:SetWidth(200) - edit:SetPoint("TOPRIGHT", CliqueCustomArg3, "BOTTOMRIGHT", 0, 0) - edit:SetAutoFocus(nil) - edit:SetScript("OnTabPressed", function() - if CliqueCustomArg5:IsVisible() then - CliqueCustomArg5:SetFocus() - end - end) - edit:SetScript("OnEnterPressed", function() end) - - edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - edit.label:SetText("Spell Name:") - edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0) - edit.label:SetJustifyH("RIGHT") - edit:Hide() - - -- Argument 5 - - local edit = CreateFrame("EditBox", "CliqueCustomArg5", CliqueCustomFrame, "InputBoxTemplate") - edit:SetHeight(30) - edit:SetWidth(200) - edit:SetPoint("TOPRIGHT", CliqueCustomArg4, "BOTTOMRIGHT", 0, 0) - edit:SetAutoFocus(nil) - edit:SetScript("OnTabPressed", function() - if CliqueCustomArg1:IsVisible() then - CliqueCustomArg1:SetFocus() - end - end) - edit:SetScript("OnEnterPressed", function() end) - - edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - edit.label:SetText("Spell Name:") - edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0) - edit.label:SetJustifyH("RIGHT") - edit:Hide() - - -- Bottom buttons - - local button = CreateFrame("Button", "CliqueCustomButtonCancel", CliqueCustomFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(70) - button:SetText("Cancel") - button:SetPoint("BOTTOM", 65, 4) - button:SetScript("OnClick", buttonFunc) - - local button = CreateFrame("Button", "CliqueCustomButtonSave", CliqueCustomFrame, "UIPanelButtonGrayTemplate") - button:SetHeight(24) - button:SetWidth(70) - button:SetText("Save") - button:SetPoint("LEFT", CliqueCustomButtonCancel, "RIGHT", 6, 0) - button:SetScript("OnClick", buttonFunc) - - -- Create the macro icon frame - - CreateFrame("Frame", "CliqueIconSelectFrame", CliqueCustomFrame) - CliqueIconSelectFrame:SetWidth(296) - CliqueIconSelectFrame:SetHeight(250) - CliqueIconSelectFrame:SetPoint("CENTER",0,0) - self:SkinFrame(CliqueIconSelectFrame) - CliqueIconSelectFrame:SetFrameStrata("DIALOG") - CliqueIconSelectFrame.title:SetText("Select an icon") - CliqueIconSelectFrame:Hide() - - CreateFrame("CheckButton", "CliqueIcon1", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon1:SetID(1) - CliqueIcon1:SetPoint("TOPLEFT", 25, -35) - - CreateFrame("CheckButton", "CliqueIcon2", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon2:SetID(2) - CliqueIcon2:SetPoint("LEFT", CliqueIcon1, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon3", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon3:SetID(3) - CliqueIcon3:SetPoint("LEFT", CliqueIcon2, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon4", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon4:SetID(4) - CliqueIcon4:SetPoint("LEFT", CliqueIcon3, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon5", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon5:SetID(5) - CliqueIcon5:SetPoint("LEFT", CliqueIcon4, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon6", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon6:SetID(6) - CliqueIcon6:SetPoint("TOPLEFT", CliqueIcon1, "BOTTOMLEFT", 0, -10) - - CreateFrame("CheckButton", "CliqueIcon7", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon7:SetID(7) - CliqueIcon7:SetPoint("LEFT", CliqueIcon6, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon8", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon8:SetID(8) - CliqueIcon8:SetPoint("LEFT", CliqueIcon7, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon9", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon9:SetID(9) - CliqueIcon9:SetPoint("LEFT", CliqueIcon8, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon10", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon10:SetID(10) - CliqueIcon10:SetPoint("LEFT", CliqueIcon9, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon11", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon11:SetID(11) - CliqueIcon11:SetPoint("TOPLEFT", CliqueIcon6, "BOTTOMLEFT", 0, -10) - - CreateFrame("CheckButton", "CliqueIcon12", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon12:SetID(12) - CliqueIcon12:SetPoint("LEFT", CliqueIcon11, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon13", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon13:SetID(13) - CliqueIcon13:SetPoint("LEFT", CliqueIcon12, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon14", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon14:SetID(14) - CliqueIcon14:SetPoint("LEFT", CliqueIcon13, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon15", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon15:SetID(15) - CliqueIcon15:SetPoint("LEFT", CliqueIcon14, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon16", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon16:SetID(16) - CliqueIcon16:SetPoint("TOPLEFT", CliqueIcon11, "BOTTOMLEFT", 0, -10) - - CreateFrame("CheckButton", "CliqueIcon17", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon17:SetID(17) - CliqueIcon17:SetPoint("LEFT", CliqueIcon16, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon18", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon18:SetID(18) - CliqueIcon18:SetPoint("LEFT", CliqueIcon17, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon19", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon19:SetID(19) - CliqueIcon19:SetPoint("LEFT", CliqueIcon18, "RIGHT", 10, 0) - - CreateFrame("CheckButton", "CliqueIcon20", CliqueIconSelectFrame, "CliqueIconTemplate") - CliqueIcon20:SetID(20) - CliqueIcon20:SetPoint("LEFT", CliqueIcon19, "RIGHT", 10, 0) - - CreateFrame("ScrollFrame", "CliqueIconScrollFrame", CliqueIconSelectFrame, "FauxScrollFrameTemplate") - CliqueIconScrollFrame:SetPoint("TOPLEFT", CliqueIcon1, "TOPLEFT", 0, 0) - CliqueIconScrollFrame:SetPoint("BOTTOMRIGHT", CliqueIcon20, "BOTTOMRIGHT", 10, 0) - - local texture = CliqueIconScrollFrame:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueIconScrollFrame, "TOPRIGHT", 14, 0) - texture:SetPoint("BOTTOMRIGHT", 23, 0) - texture:SetVertexColor(0.3, 0.3, 0.3) - - local texture = CliqueIconScrollFrame:CreateTexture(nil, "BACKGROUND") - texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") - texture:SetPoint("TOPLEFT", CliqueIconScrollFrame, "TOPRIGHT", 4, 0) - texture:SetPoint("BOTTOMRIGHT", 14,0) - texture:SetVertexColor(0.3, 0.3, 0.3) - - local function updateicons() - Clique:UpdateIconFrame() - end - - CliqueIconScrollFrame:SetScript("OnVerticalScroll", function(self, offset) - local MACRO_ICON_ROW_HEIGHT = 36 - FauxScrollFrame_OnVerticalScroll(self, offset, MACRO_ICON_ROW_HEIGHT, updateicons) - end) - - CliqueIconSelectFrame:SetScript("OnShow", function(self) - local parent = self:GetParent() - self:SetFrameLevel(parent:GetFrameLevel() + 5) - Clique:UpdateIconFrame() - end) - - -- Create the CliqueHelpText - CliqueFrame:CreateFontString("CliqueHelpText", "OVERLAY", "GameFontHighlight") - CliqueHelpText:SetText(L.HELP_TEXT) - CliqueHelpText:SetPoint("TOPLEFT", 10, -10) - CliqueHelpText:SetPoint("BOTTOMRIGHT", -10, 10) - CliqueHelpText:SetJustifyH("CENTER") - CliqueHelpText:SetJustifyV("CENTER") - CliqueHelpText:SetPoint("CENTER", 0, 0) - - self.sortList = {} - self.listSelected = 0 -end - -function Clique:ListScrollUpdate() - if not CliqueListScroll then return end - - local idx,button - Clique:SortList() - local clickCasts = self.sortList - local offset = FauxScrollFrame_GetOffset(CliqueListScroll) - FauxScrollFrame_Update(CliqueListScroll, table.getn(clickCasts), NUM_ENTRIES, ENTRY_SIZE) - - if not CliqueListScroll:IsShown() then - CliqueFrame:SetWidth(400) - else - CliqueFrame:SetWidth(425) - end - - for i=1,NUM_ENTRIES do - idx = offset + i - button = getglobal("CliqueList"..i) - if idx <= table.getn(clickCasts) then - Clique:FillListEntry(button,idx) - button:Show() - if idx == self.listSelected then - button:SetBackdropBorderColor(1,1,0) - else - button:SetBackdropBorderColor(0.3, 0.3, 0.3) - end - else - button:Hide() - end - end - Clique:ValidateButtons() -end - -local sortFunc = function(a,b) - local numA = tonumber(a.button) or 0 - local numB = tonumber(b.button) or 0 - - if numA == numB then - return a.modifier < b.modifier - else - return numA < numB - end -end - -function Clique:SortList() - self.sortList = {} - for k,v in pairs(self.editSet) do - table.insert(self.sortList, v) - end - table.sort(self.sortList, sortFunc) -end - -function Clique:ValidateButtons() - local entry = self.sortList[self.listSelected] - - if entry then - CliqueButtonDelete:Enable() - CliqueButtonEdit:Enable() - else - CliqueButtonDelete:Disable() - CliqueButtonEdit:Disable() - end - - -- This should always be enabled - CliqueButtonCustom:Enable() - CliqueButtonOptions:Enable() - - -- Disable the help text - Clique.inuse = nil - for k,v in pairs(self.clicksets) do - if next(v) then - Clique.inuse = true - end - end - if Clique.inuse then - CliqueHelpText:Hide() - else - CliqueHelpText:Show() - end -end - -function Clique:FillListEntry(frame, idx) - local entry = self.sortList[idx] - if tonumber(entry.arg2) then - rank = string.format("Rank %d", entry.arg2) - elseif entry.arg2 then - rank = entry.arg2 - end - - local type = string.format("%s%s", string.upper(string.sub(entry.type, 1, 1)), string.sub(entry.type, 2)) - local button = entry.button - - frame.icon:SetTexture(entry.texture or "Interface\\Icons\\INV_Misc_QuestionMark") - frame.binding:SetText(entry.modifier..self:GetButtonText(button)) - - local arg1 = tostring(entry.arg1) - local arg2 = tostring(entry.arg2) - local arg3 = tostring(entry.arg3) - local arg4 = tostring(entry.arg4) - local arg5 = tostring(entry.arg5) - - if entry.type == "action" then - frame.name:SetText(string.format("Action Button %d%s", arg1, entry.arg2 and (" on " .. arg2) or "")) - elseif entry.type == "actionbar" then - frame.name:SetText(string.format("Action Bar: %s", arg1)) - elseif entry.type == "pet" then - local target = "" - if entry.arg2 then - target = " on " .. arg2 - end - frame.name:SetText(string.format("Pet Action %d%s", arg1, target)) - elseif entry.type == "spell" then - if entry.arg2 then - frame.name:SetText(string.format("%s (%s)%s", arg1, rank, - entry.arg5 and (" on " .. arg5) or "")) - else - frame.name:SetText(string.format("%s%s", arg1, entry.arg5 and " on " .. arg5 or "")) - end - elseif entry.type == "menu" then - frame.name:SetText("Show Menu") - elseif entry.type == "stop" then - frame.name:SetText("Cancel Pending Spell") - elseif entry.type == "target" then - frame.name:SetText(string.format("Target Unit: %s", arg1 and entry.arg1 or "")) - elseif entry.type == "focus" then - frame.name:SetText(string.format("Set Focus Unit: %s", arg1 and entry.arg1 or "")) - elseif entry.type == "assist" then - frame.name:SetText(string.format("Assist Unit: %s", arg1 and entry.arg1 or "")) - elseif entry.type == "item" then - if entry.arg1 then - frame.name:SetText(string.format("Item: %d,%d", arg1, arg2)) - elseif entry.arg3 then - frame.name:SetText(string.format("Item: %s", arg3)) - end - elseif entry.type == "macro" then - frame.name:SetText(string.format("Macro: %s", arg1 and entry.arg1 or string.sub(arg2, 1, 20))) - end - - frame:Show() -end - -function Clique:ButtonOnClick(button) - local entry = self.sortList[self.listSelected] - - if button == CliqueButtonDelete then - if InCombatLockdown() then - StaticPopup_Show("CLIQUE_COMBAT_LOCKDOWN") - return - end - - self.editSet[entry.modifier..entry.button] = nil - local len = table.getn(self.sortList) - 1 - - if self.listSelected > len then - self.listSelected = len - end - - self:DeleteAction(entry) - self:UpdateClicks() - self:PLAYER_REGEN_ENABLED() - entry = nil - - self:ListScrollUpdate() - elseif button == CliqueButtonClose then - self:Toggle() - elseif button == CliqueTextButtonClose then - CliqueTextListFrame:Hide() - elseif button == CliqueOptionsButtonClose then - CliqueOptionsFrame:Hide() - elseif button == CliqueButtonOptions then - if CliqueOptionsFrame:IsVisible() then - CliqueOptionsFrame:Hide() - else - CliqueOptionsFrame:Show() - end - elseif button == CliqueButtonCustom then - if CliqueCustomFrame:IsVisible() then - CliqueCustomFrame:Hide() - else - CliqueCustomFrame:Show() - end - elseif button == CliqueButtonFrames then - if CliqueTextListFrame:IsVisible() and self.textlist == "FRAMES" then - CliqueTextListFrame:Hide() - else - CliqueTextListFrame:Show() - end - - self.textlist = "FRAMES" - CliqueButtonDeleteProfile:Hide() - CliqueButtonSetProfile:Hide() - CliqueButtonNewProfile:Hide() - - self:TextListScrollUpdate() - CliqueTextListFrame.title:SetText("Clique Frame Editor") - self.textlistSelected = nil - elseif button == CliqueButtonProfiles then - if CliqueTextListFrame:IsVisible() and self.textlist == "PROFILES" then - CliqueTextListFrame:Hide() - else - CliqueTextListFrame:Show() - end - self.textlist = "PROFILES" - self:TextListScrollUpdate() - CliqueButtonDeleteProfile:Show() - CliqueButtonSetProfile:Show() - CliqueButtonNewProfile:Show() - - --CliqueTextListFrame.title:SetText("Profile: " .. self.db.char.profileKey) - self.textlistSelected = nil - elseif button == CliqueButtonSetProfile then - local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll) - local selected = self.textlistSelected - offset - local button = getglobal("CliqueTextList"..selected) - self.db:SetProfile(button.name:GetText()) - if CliqueFrame then - CliqueFrame.title:SetText("Clique v. " .. self.version .. " - " .. tostring(self.db.keys.profile)); - end - elseif button == CliqueButtonNewProfile then - StaticPopup_Show("CLIQUE_NEW_PROFILE") - elseif button == CliqueButtonDeleteProfile then - local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll) - local selected = self.textlistSelected - offset - local button = getglobal("CliqueTextList"..selected) - self.db:DeleteProfile(button.name:GetText()) - elseif button == CliqueButtonEdit then - -- Make a copy of the entry - self.customEntry = {} - for k,v in pairs(entry) do - self.customEntry[k] = v - end - - CliqueCustomFrame:Show() - - -- Select the right radio button - for k,v in pairs(self.radio) do - if entry.type == k.type then - self:CustomRadio(k) - k:SetChecked(true) - end - end - - self.customEntry.type = entry.type - - CliqueCustomArg1:SetText(entry.arg1 or "") - CliqueCustomArg2:SetText(entry.arg2 or "") - CliqueCustomArg3:SetText(entry.arg3 or "") - CliqueCustomArg4:SetText(entry.arg4 or "") - CliqueCustomArg5:SetText(entry.arg5 or "") - - CliqueMultiScrollFrameEditBox:SetText(entry.arg2 or "") - CliqueCustomButtonIcon.icon:SetTexture(entry.texture or "Interface\\Icons\\INV_Misc_QuestionMark") - - CliqueCustomButtonBinding.modifier = entry.modifier - CliqueCustomButtonBinding.button = self:GetButtonNumber(entry.button) - CliqueCustomButtonBinding:SetText(string.format("%s%s", entry.modifier, self:GetButtonText(entry.button))) - - self.editEntry = entry - - elseif button == CliqueCustomButtonCancel then - CliqueCustomFrame:Hide() - CliqueCustomButtonIcon.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") - CliqueCustomButtonBinding:SetText("Set Click Binding") - self.customEntry = {} - self.editEntry = nil - self:CustomRadio() - - elseif button == CliqueCustomButtonSave then - -- Add custom save logic in here - local entry = self.customEntry - - entry.arg1 = CliqueCustomArg1:GetText() - entry.arg2 = CliqueCustomArg2:GetText() - entry.arg3 = CliqueCustomArg3:GetText() - entry.arg4 = CliqueCustomArg4:GetText() - entry.arg5 = CliqueCustomArg5:GetText() - - if entry.arg1 == "" then entry.arg1 = nil end - if entry.arg2 == "" then entry.arg2 = nil end - if entry.arg3 == "" then entry.arg3 = nil end - if entry.arg4 == "" then entry.arg4 = nil end - if entry.arg5 == "" then entry.arg5 = nil end - - if tonumber(entry.arg1) then entry.arg1 = tonumber(entry.arg1) end - if tonumber(entry.arg2) then entry.arg2 = tonumber(entry.arg2) end - if tonumber(entry.arg3) then entry.arg3 = tonumber(entry.arg3) end - if tonumber(entry.arg4) then entry.arg4 = tonumber(entry.arg4) end - if tonumber(entry.arg5) then entry.arg5 = tonumber(entry.arg5) end - - if entry.type == "macro" then - local text = CliqueMultiScrollFrameEditBox:GetText() - if text ~= "" then - entry.arg2 = text - end - end - - local pattern = "Hitem.+|h%[(.+)%]|h" - if entry.arg1 and string.find(entry.arg1, pattern) then - entry.arg1 = select(3, string.find(entry.arg1, pattern)) - end - if entry.arg2 and string.find(entry.arg2, pattern) then - entry.arg2 = select(3, string.find(entry.arg2, pattern)) - end - if entry.arg3 and string.find(entry.arg3, pattern) then - entry.arg3 = select(3, string.find(entry.arg3, pattern)) - end - if entry.arg4 and string.find(entry.arg4, pattern) then - entry.arg4 = select(3, string.find(entry.arg4, pattern)) - end - if entry.arg5 and string.find(entry.arg5, pattern) then - entry.arg5 = select(3, string.find(entry.arg5, pattern)) - end - - local issue - local arg1 = entry.arg1 and tostring(entry.arg1) - local arg2 = entry.arg2 and tostring(entry.arg2) - - if not entry.type then - issue = "You must select an action type." - elseif not entry.button then - issue = "You must set a click-binding." - elseif entry.type == "action" and not entry.arg1 then - issue = "You must supply an action button number when creating a custom \"action\"." - elseif entry.type == "pet" and not entry.arg1 then - issue = "You must supply a pet action button number when creating a custom action \"pet\"." - elseif entry.type == "spell" and not (entry.arg1 or (entry.arg2 and entry.arg3) or entry.arg4) then - issue = "You must supply either a spell name and optionally an item slot/bag or name to consume when creating a \"spell\" action." - elseif entry.type == "item" and not ((entry.arg1 and entry.arg2) or entry.arg3) then - issue = "You must supply either a bag/slot, or an item name to use." - elseif entry.type == "macro" and arg1 and arg2 then - issue = "You must specify EITHER a macro index, or macro text, not both." - elseif entry.type == "macro" and not arg1 and not arg2 then - issue = "You must supply either a macro index, or macro text" - elseif entry.type == "actionbar" and not arg1 then - issue = "You must supply an action bar to change to." - end - - if issue then - StaticPopupDialogs["CLIQUE_CANT_SAVE"].text = issue - StaticPopup_Show("CLIQUE_CANT_SAVE") - return - end - - -- Delete the one we're editing, if that's the case - if self.editEntry then - local key = self.editEntry.modifier..self.editEntry.button - self.editSet[key] = nil - self:DeleteAction(self.editEntry) - self:UpdateClicks() - self.editEntry = nil - end - - local key = entry.modifier..entry.button - self.editSet[key] = entry - self:UpdateClicks() - self:PLAYER_REGEN_ENABLED() - self:ButtonOnClick(CliqueCustomButtonCancel) - end - - Clique:ValidateButtons() - Clique:ListScrollUpdate() -end - -local click_func = function(self) Clique:DropDown_OnClick(self) end - -function Clique:DropDown_Initialize() - local info = {} - - for k,v in pairs(work) do - info = {} - info.text = v - info.value = self.clicksets[v] - info.func = click_func - UIDropDownMenu_AddButton(info) - end -end - -function Clique:DropDown_OnClick(frame) - UIDropDownMenu_SetSelectedValue(CliqueDropDown, frame.value) - Clique.editSet = frame.value - self.listSelected = 0 - Clique:ListScrollUpdate() -end - -function Clique:DropDown_OnShow(frame) - work = {} - for k,v in pairs(self.clicksets) do - table.insert(work, k) - end - table.sort(work) - - UIDropDownMenu_Initialize(frame, function() Clique:DropDown_Initialize() end); - UIDropDownMenu_SetSelectedValue(CliqueDropDown, self.editSet) - Clique:ListScrollUpdate() -end - -function Clique:CustomBinding_OnClick(frame) - -- This handles the binding click - local mod = self:GetModifierText() - local button = arg1 - - if self.editSet == self.clicksets[L.CLICKSET_HARMFUL] then - button = string.format("%s%d", "harmbutton", self:GetButtonNumber(button)) - elseif self.editSet == self.clicksets[L.CLICKSET_HELPFUL] then - button = string.format("%s%d", "helpbutton", self:GetButtonNumber(button)) - else - button = self:GetButtonNumber(button) - end - - self.customEntry.modifier = mod - self.customEntry.button = button - frame:SetText(string.format("%s%s", mod, arg1)) -end - -local buttonSetup = { - actionbar = { - help = L["BS_ACTIONBAR_HELP"], - arg1 = L["BS_ACTIONBAR_ARG1_LABEL"], - }, - action = { - help = L["BS_ACTION_HELP"], - arg1 = L["BS_ACTION_ARG1_LABEL"], - arg2 = L["BS_ACTION_ARG2_LABEL"], - }, - pet = { - help = L["BS_PET_HELP"], - arg1 = L["BS_PET_ARG1_LABEL"], - arg2 = L["BS_PET_ARG2_LABEL"], - }, - spell = { - help = L["BS_SPELL_HELP"], - arg1 = L["BS_SPELL_ARG1_LABEL"], - arg2 = L["BS_SPELL_ARG2_LABEL"], - arg3 = L["BS_SPELL_ARG3_LABEL"], - arg4 = L["BS_SPELL_ARG4_LABEL"], - arg5 = L["BS_SPELL_ARG5_LABEL"], - }, - item = { - help = L["BS_ITEM_HELP"], - arg1 = L["BS_ITEM_ARG1_LABEL"], - arg2 = L["BS_ITEM_ARG2_LABEL"], - arg3 = L["BS_ITEM_ARG3_LABEL"], - arg4 = L["BS_ITEM_ARG4_LABEL"], - }, - macro = { - help = L["BS_MACRO_HELP"], - arg1 = L["BS_MACRO_ARG1_LABEL"], - arg2 = L["BS_MACRO_ARG2_LABEL"], - }, - stop = { - help = L["BS_STOP_HELP"], - }, - target = { - help = L["BS_TARGET_HELP"], - arg1 = L["BS_TARGET_ARG1_LABEL"], - }, - focus = { - help = L["BS_FOCUS_HELP"], - arg1 = L["BS_FOCUS_ARG1_LABEL"], - }, - assist = { - help = L["BS_ASSIST_HELP"], - arg1 = L["BS_ASSIST_ARG1_LABEL"], - }, - click = { - help = L["BS_CLICK_HELP"], - arg1 = L["BS_CLICK_ARG1_LABEL"], - }, - menu = { - help = L["BS_MENU_HELP"], - }, -} - -function Clique:CustomRadio(button) - local anySelected - for k,v in pairs(self.radio) do - if k ~= button then - k:SetChecked(nil) - end - end - - if not button or not buttonSetup[button.type] then - CliqueCustomHelpText:SetText(L.CUSTOM_HELP) - CliqueCustomArg1:Hide() - CliqueCustomArg2:Hide() - CliqueCustomArg3:Hide() - CliqueCustomArg4:Hide() - CliqueCustomArg5:Hide() - CliqueCustomButtonBinding:SetText("Set Click Binding") - return - end - - local entry = buttonSetup[button.type] - self.customEntry.type = button.type - - if button and button.type then - if not button:GetChecked() then - self.customEntry.type = nil - end - end - - -- Clear any open arguments - CliqueCustomArg1:SetText("") - CliqueCustomArg2:SetText("") - CliqueCustomArg3:SetText("") - CliqueCustomArg4:SetText("") - CliqueCustomArg5:SetText("") - - CliqueCustomHelpText:SetText(entry.help) - CliqueCustomArg1.label:SetText(entry.arg1) - CliqueCustomArg2.label:SetText(entry.arg2) - CliqueCustomArg3.label:SetText(entry.arg3) - CliqueCustomArg4.label:SetText(entry.arg4) - CliqueCustomArg5.label:SetText(entry.arg5) - - if entry.arg1 then CliqueCustomArg1:Show() else CliqueCustomArg1:Hide() end - if entry.arg2 then CliqueCustomArg2:Show() else CliqueCustomArg2:Hide() end - if entry.arg3 then CliqueCustomArg3:Show() else CliqueCustomArg3:Hide() end - if entry.arg4 then CliqueCustomArg4:Show() else CliqueCustomArg4:Hide() end - if entry.arg5 then CliqueCustomArg5:Show() else CliqueCustomArg5:Hide() end - - -- Handle MacroText - if button.type == "macro" then - CliqueCustomArg2:Hide() - CliqueMulti:Show() - CliqueMultiScrollFrameEditBox:SetText("") - else - CliqueMulti:Hide() - end -end - -function Clique:UpdateIconFrame() - local MAX_MACROS = 18; - local NUM_MACRO_ICONS_SHOWN = 20; - local NUM_ICONS_PER_ROW = 5; - local NUM_ICON_ROWS = 4; - local MACRO_ICON_ROW_HEIGHT = 36; - local macroPopupOffset = FauxScrollFrame_GetOffset(CliqueIconScrollFrame); - local numMacroIcons = GetNumMacroIcons(); - local macroPopupIcon,macroPopupButton - - -- Icon list - for i=1, NUM_MACRO_ICONS_SHOWN do - macroPopupIcon = getglobal("CliqueIcon"..i.."Icon"); - macroPopupButton = getglobal("CliqueIcon"..i); - - if not macroPopupButton.icon then - macroPopupButton.icon = macroPopupIcon - end - - local index = (macroPopupOffset * NUM_ICONS_PER_ROW) + i; - if ( index <= numMacroIcons ) then - macroPopupIcon:SetTexture(GetMacroIconInfo(index)); - macroPopupButton:Show(); - else - macroPopupIcon:SetTexture(""); - macroPopupButton:Hide(); - end - macroPopupButton:SetChecked(nil); - end - - FauxScrollFrame_Update(CliqueIconScrollFrame, ceil(numMacroIcons / NUM_ICONS_PER_ROW) , NUM_ICON_ROWS, MACRO_ICON_ROW_HEIGHT ); -end - -function Clique:SetSpellIcon(button) - local texture = button.icon:GetTexture() - self.customEntry.texture = texture - CliqueCustomButtonIcon.icon:SetTexture(texture) - CliqueIconSelectFrame:Hide() -end - -StaticPopupDialogs["CLIQUE_PASSIVE_SKILL"] = { - text = "You can't bind a passive skill.", -button1 = TEXT(OKAY), - OnAccept = function() - end, - timeout = 0, - hideOnEscape = 1 -} - -StaticPopupDialogs["CLIQUE_CANT_SAVE"] = { - text = "", - button1 = TEXT(OKAY), - OnAccept = function() - end, - timeout = 0, - hideOnEscape = 1 -} - -StaticPopupDialogs["CLIQUE_BINDING_PROBLEM"] = { - text = "That combination is already bound. Delete the old one before trying to re-bind.", - button1 = TEXT(OKAY), - OnAccept = function() - end, - timeout = 0, - hideOnEscape = 1 -} - -StaticPopupDialogs["CLIQUE_COMBAT_LOCKDOWN"] = { - text = "You are currently in combat. You cannot make changes to your click casting while in combat..", - button1 = TEXT(OKAY), - OnAccept = function() - end, - timeout = 0, - hideOnEscape = 1 -} - -StaticPopupDialogs["CLIQUE_NEW_PROFILE"] = { - text = TEXT("Enter the name of a new profile you'd like to create"), - button1 = TEXT(OKAY), - button2 = TEXT(CANCEL), - OnAccept = function(self) - local base = self:GetName() - local editbox = getglobal(base .. "EditBox") - Clique.db:SetProfile(editbox:GetText()) - end, - timeout = 0, - whileDead = 1, - exclusive = 1, - showAlert = 1, - hideOnEscape = 1, - hasEditBox = 1, - maxLetters = 32, - OnShow = function(self) - getglobal(self:GetName().."Button1"):Disable(); - getglobal(self:GetName().."EditBox"):SetFocus(); - end, - OnHide = function(self) - if ( ChatFrameEditBox:IsVisible() ) then - ChatFrameEditBox:SetFocus(); - end - getglobal(self:GetName().."EditBox"):SetText(""); - end, - EditBoxOnEnterPressed = function(self) - if ( getglobal(self:GetParent():GetName().."Button1"):IsEnabled() == 1 ) then - Clique.db:SetProfile(self:GetText()) - self:GetParent():Hide(); - end - end, - EditBoxOnTextChanged = function (self) - local editBox = getglobal(self:GetParent():GetName().."EditBox"); - local txt = editBox:GetText() - if #txt > 0 then - getglobal(self:GetParent():GetName().."Button1"):Enable(); - else - getglobal(self:GetParent():GetName().."Button1"):Disable(); - end - end, - EditBoxOnEscapePressed = function(self) - self:GetParent():Hide(); - ClearCursor(); - end -} - -StaticPopupDialogs["CLIQUE_DELETE_PROFILE"] = { - text = TEXT("Enter the name of a profile you'd like to delete"), - button1 = TEXT(OKAY), - button2 = TEXT(CANCEL), - OnAccept = function(self) - Clique.db:DeleteProfile(getglobal(self:GetName().."EditBox"):GetText()) - Clique:DropDownProfile_OnShow() - end, - timeout = 0, - whileDead = 1, - exclusive = 1, - showAlert = 1, - hideOnEscape = 1, - hasEditBox = 1, - maxLetters = 32, - OnShow = function(self) - getglobal(self:GetName().."Button1"):Disable(); - getglobal(self:GetName().."EditBox"):SetFocus(); - end, - OnHide = function(self) - if ( ChatFrameEditBox:IsVisible() ) then - ChatFrameEditBox:SetFocus(); - end - getglobal(self:GetName().."EditBox"):SetText(""); - end, - EditBoxOnEnterPressed = function(self) - if ( getglobal(self:GetParent():GetName().."Button1"):IsEnabled() == 1 ) then - Clique.db:DeleteProfile(self:GetText()) - Clique:DropDownProfile_OnShow() - self:GetParent():Hide(); - end - end, - EditBoxOnTextChanged = function (self) - local editBox = getglobal(self:GetParent():GetName().."EditBox"); - local txt = editBox:GetText() - if Clique.db.profiles[txt] then - getglobal(self:GetParent():GetName().."Button1"):Enable(); - else - getglobal(self:GetParent():GetName().."Button1"):Disable(); - end - end, - EditBoxOnEscapePressed = function(self) - self:GetParent():Hide(); - ClearCursor(); - end -} - -local work = {} - -function Clique:TextListScrollUpdate() - if not CliqueTextListScroll then return end - - local idx,button - for k,v in pairs(work) do work[k] = nil end - - if not self.textlist then self.textlist = "FRAMES" end - - if self.textlist == "PROFILES" then - for k,v in pairs(self.db.profiles) do table.insert(work, k) end - table.sort(work) - CliqueTextListFrame.title:SetText("Profile: " .. self.db.keys.profile) - - elseif self.textlist == "FRAMES" then - for k,v in pairs(self.ccframes) do - local name = k:GetName() - if name then - table.insert(work, name) - end - end - table.sort(work) - end - - local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll) - FauxScrollFrame_Update(CliqueTextListScroll, #work, 12, 22) - - if not CliqueTextListScroll:IsShown() then - CliqueTextListFrame:SetWidth(250) - else - CliqueTextListFrame:SetWidth(275) - end - - for i=1,12 do - idx = offset + i - button = getglobal("CliqueTextList"..i) - if idx <= #work then - button.name:SetText(work[idx]) - button:Show() - -- Change texture - if self.textlist == "PROFILES" then - button:SetNormalTexture("Interface\\AddOns\\Clique\\images\\RadioEmpty") - button:SetCheckedTexture("Interface\\AddOns\\Clique\\images\\RadioChecked") - button:SetHighlightTexture("Interface\\AddOns\\Clique\\images\\RadioChecked") - else - button:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") - button:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") - button:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") - end - - if self.textlistSelected == nil and self.textlist == "PROFILES" then - if work[idx] == self.db.keys.profile then - button:SetChecked(true) - CliqueButtonSetProfile:Disable() - CliqueButtonDeleteProfile:Disable() - else - button:SetChecked(nil) - end - elseif idx == self.textlistSelected and self.textlist == "PROFILES" then - if work[idx] == self.db.keys.profile then - CliqueButtonSetProfile:Disable() - CliqueButtonDeleteProfile:Disable() - else - CliqueButtonSetProfile:Enable() - CliqueButtonDeleteProfile:Enable() - end - button:SetChecked(true) - elseif self.textlist == "FRAMES" then - local name = work[idx] - local frame = getglobal(name) - - if not self.profile.blacklist then - self.profile.blacklist = {} - end - local bl = self.profile.blacklist - - if bl[name] then - button:SetChecked(nil) - else - button:SetChecked(true) - end - else - button:SetBackdropBorderColor(0.3, 0.3, 0.3) - button:SetChecked(nil) - end - else - button:Hide() - end - end -end - -local function makeCheckbox(parent, name, text, width) - local entry = CreateFrame("CheckButton", name, parent) - entry:SetHeight(22) - entry:SetWidth(width) - entry:SetBackdrop({insets = {left = 2, right = 2, top = 2, bottom = 2}}) - - entry:SetBackdropBorderColor(0.3, 0.3, 0.3) - entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3) - entry:SetScript("OnEnter", function(self) - if self.tooltip then - GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT") - GameTooltip:SetText(self.tooltip) - end - end) - entry:SetScript("OnLeave", function(self) - GameTooltip:Hide() - end) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - entry:SetNormalTexture(texture) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - texture:SetBlendMode("ADD") - entry:SetHighlightTexture(texture) - - local texture = entry:CreateTexture("ARTWORK") - texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") - texture:SetPoint("LEFT", 0, 0) - texture:SetHeight(26) - texture:SetWidth(26) - entry:SetCheckedTexture(texture) - - entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - entry.name:SetPoint("LEFT", 25, 0) - entry.name:SetJustifyH("LEFT") - entry.name:SetText(text) - return entry -end - -function Clique:CreateOptionsWidgets(parent) - local button = CreateFrame("Button", "CliqueOptionsButtonClose", parent.titleBar, "UIPanelCloseButton") - button:SetHeight(25) - button:SetWidth(25) - button:SetPoint("TOPRIGHT", -5, 3) - button:SetScript("OnClick", function(self) Clique:ButtonOnClick(self) end) - - local downClick = makeCheckbox(parent, "CliqueOptionsAnyDown", L.DOWNCLICK_LABEL, 300) - downClick:SetPoint("TOPLEFT", 5, -25) - - local switchSpec = makeCheckbox(parent, "CliqueOptionsSpecSwitch", L.SPECSWITCH_LABEL, 300) - switchSpec:SetPoint("TOPLEFT", 5, -45) - - -- Define the DropDown object we will create all the DropDown within - local DD = {} - -- Create the 12 buttons using a loop, since its an identical process for all of them - for i=1,12 do - DD["Spec"..i.."DropDown"] = CreateFrame("Frame", "CliqueSpec"..i.."DropDown", parent, "UIDropDownMenuTemplate") - DD["Spec"..i.."DropDown"]:ClearAllPoints() - if i == 1 then - DD["Spec"..i.."DropDown"]:SetPoint("TOPLEFT", switchSpec, "BOTTOMLEFT", 65, 0) - else - DD["Spec"..i.."DropDown"]:SetPoint("TOPLEFT", DD["Spec"..(i-1).."DropDown"], "BOTTOMLEFT", 65, 0) - end - DD["Spec"..i.."DropDown"]:Show() - DD["Spec"..i.."DropDown"].label = DD["Spec"..i.."DropDown"]:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - DD["Spec"..i.."DropDown"].label:SetText(L["Spec "..i..":"]) - DD["Spec"..i.."DropDown"].label:SetPoint("RIGHT", DD["Spec"..i.."DropDown"], "LEFT", 0, 0) - DD["Spec"..i.."DropDown"].label:SetHeight(16) - - local function initialize(self, level) - local function OnClick(self) - UIDropDownMenu_SetSelectedID(DD["Spec"..i.."DropDown"], self:GetID()) - Clique.db.char["spec"..i.."Profile"] = self.value - Clique:UpdateClicks() - end - - local work = {} - for k,v in pairs(Clique.db.profiles) do - table.insert(work, k) - end - table.sort(work) - - for idx,profile in ipairs(work) do - local info = UIDropDownMenu_CreateInfo() - info.text = profile - info.func = OnClick - info.value = profile - UIDropDownMenu_AddButton(info, level) - end - end - - UIDropDownMenu_Initialize(DD["Spec"..i.."DropDown"], initialize) - UIDropDownMenu_SetWidth(DD["Spec"..i.."DropDown"], 175); - UIDropDownMenu_SetButtonWidth(DD["Spec"..i.."DropDown"], 199) - UIDropDownMenu_JustifyText(DD["Spec"..i.."DropDown"], "LEFT") - if Clique.db.char["spec"..i.."Profile"] then - UIDropDownMenu_SetSelectedValue(DD["Spec"..i.."DropDown"], Clique.db.char["spec"..i.."Profile"]) - else - UIDropDownMenu_SetSelectedValue(DD["Spec"..i.."DropDown"], Clique.db.keys.profile) - end - end - - local function refreshOptions(self) - -- Hide the dropdowns if the spec switch option isn't selected - local switchSpec = Clique.db.char.switchSpec - local downClick = Clique.db.char.downClick - CliqueOptionsSpecSwitch:SetChecked(switchSpec) - CliqueOptionsAnyDown:SetChecked(downClick) - - if switchSpec then - for i=1,12 do - _G["CliqueSpec"..i.."DropDown"]:Show() - if not Clique.db.char["spec"..i.."Profile"] then - Clique.db.char["spec"..i.."Profile"] = Clique.db.keys.profile - end - end - else - for i=1,12 do - _G["CliqueSpec"..i.."DropDown"]:Hide() - end - end - end - - parent:SetScript("OnShow", refreshOptions) - switchSpec:SetScript("OnClick", function(self) - if Clique.db.char.switchSpec then - Clique.db.char.switchSpec = false - else - Clique.db.char.switchSpec = true - end - refreshOptions(parent) - Clique:UpdateClicks() - end) - downClick:SetScript("OnClick", function(self) - if Clique.db.char.downClick then - Clique.db.char.downClick = false - else - Clique.db.char.downClick = true - end - refreshOptions(parent) - Clique:SetClickType() - end) -end diff --git a/Clique/CliqueUtils.lua b/Clique/CliqueUtils.lua deleted file mode 100644 index a2f49a7..0000000 --- a/Clique/CliqueUtils.lua +++ /dev/null @@ -1,47 +0,0 @@ -local buttonMap = setmetatable({ - [1] = "LeftButton", - [2] = "RightButton", - [3] = "MiddleButton", -}, { - __index = function(t, k) - return "Button" .. k - end -}) - -function Clique:GetModifierText() - local modifier = "" - - if IsShiftKeyDown() then - modifier = "Shift-"..modifier - end - if IsControlKeyDown() then - modifier = "Ctrl-"..modifier - end - if IsAltKeyDown() then - modifier = "Alt-"..modifier - end - - return modifier -end - -function Clique:GetButtonNumber(button) - return SecureButton_GetButtonSuffix(button) -end - -function Clique:GetButtonText(num) - if not num then return "" end - if type(num) == "string" then - num = num:gsub("helpbutton", "") - num = num:gsub("harmbutton", "") - end - num = tonumber(num) and tonumber(num) or num - return buttonMap[num] or "" -end - -function Clique:CheckBinding(key) - for k,v in pairs(self.editSet) do - if k == key then - return v - end - end -end diff --git a/Clique/DatabaseDefaults.lua b/Clique/DatabaseDefaults.lua new file mode 100644 index 0000000..ad7abc7 --- /dev/null +++ b/Clique/DatabaseDefaults.lua @@ -0,0 +1,25 @@ +local addonName, addon = ... +local L = addon.L + +addon.defaults = { + char = { + blacklist = {}, + blizzframes = { + PlayerFrame = true, + PetFrame = true, + TargetFrame = true, + TargetFrameToT = true, + FocusFrame = true, + FocusFrameToT = true, + arena = true, + party = true, + compactraid = true, + compactparty = true, + boss = true + }, + stopcastingfix = false + }, + profile = { + bindings = {} + } +} diff --git a/Clique/Dongle.lua b/Clique/Dongle.lua deleted file mode 100644 index c37758b..0000000 --- a/Clique/Dongle.lua +++ /dev/null @@ -1,1434 +0,0 @@ ---[[------------------------------------------------------------------------- - Copyright (c) 2006-2007, Dongle Development Team - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of the Dongle Development Team nor the names of - its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------]] -local major = "DongleStub" -local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1) - -local g = getfenv(0) - -if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then - local lib = setmetatable({}, { - __call = function(t,k) - if type(t.versions) == "table" and t.versions[k] then - return t.versions[k].instance - else - error("Cannot find a library with name '"..tostring(k).."'", 2) - end - end - }) - - function lib:IsNewerVersion(major, minor) - local versionData = self.versions and self.versions[major] - - -- If DongleStub versions have differing major version names - -- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second - -- instance will be loaded, with older logic. This code attempts - -- to compensate for that by matching the major version against - -- "^DongleStub", and handling the version check correctly. - - if major:match("^DongleStub") then - local oldmajor,oldminor = self:GetVersion() - if self.versions and self.versions[oldmajor] then - return minor > oldminor - else - return true - end - end - - if not versionData then return true end - local oldmajor,oldminor = versionData.instance:GetVersion() - return minor > oldminor - end - - local function NilCopyTable(src, dest) - for k,v in pairs(dest) do dest[k] = nil end - for k,v in pairs(src) do dest[k] = v end - end - - function lib:Register(newInstance, activate, deactivate) - assert(type(newInstance.GetVersion) == "function", - "Attempt to register a library with DongleStub that does not have a 'GetVersion' method.") - - local major,minor = newInstance:GetVersion() - assert(type(major) == "string", - "Attempt to register a library with DongleStub that does not have a proper major version.") - assert(type(minor) == "number", - "Attempt to register a library with DongleStub that does not have a proper minor version.") - - -- Generate a log of all library registrations - if not self.log then self.log = {} end - table.insert(self.log, string.format("Register: %s, %s", major, minor)) - - if not self:IsNewerVersion(major, minor) then return false end - if not self.versions then self.versions = {} end - - local versionData = self.versions[major] - if not versionData then - -- New major version - versionData = { - ["instance"] = newInstance, - ["deactivate"] = deactivate, - } - - self.versions[major] = versionData - if type(activate) == "function" then - table.insert(self.log, string.format("Activate: %s, %s", major, minor)) - activate(newInstance) - end - return newInstance - end - - local oldDeactivate = versionData.deactivate - local oldInstance = versionData.instance - - versionData.deactivate = deactivate - - local skipCopy - if type(activate) == "function" then - table.insert(self.log, string.format("Activate: %s, %s", major, minor)) - skipCopy = activate(newInstance, oldInstance) - end - - -- Deactivate the old libary if necessary - if type(oldDeactivate) == "function" then - local major, minor = oldInstance:GetVersion() - table.insert(self.log, string.format("Deactivate: %s, %s", major, minor)) - oldDeactivate(oldInstance, newInstance) - end - - -- Re-use the old table, and discard the new one - if not skipCopy then - NilCopyTable(newInstance, oldInstance) - end - return oldInstance - end - - function lib:GetVersion() return major,minor end - - local function Activate(new, old) - -- This code ensures that we'll move the versions table even - -- if the major version names are different, in the case of - -- DongleStub - if not old then old = g.DongleStub end - - if old then - new.versions = old.versions - new.log = old.log - end - g.DongleStub = new - end - - -- Actually trigger libary activation here - local stub = g.DongleStub or lib - lib = stub:Register(lib, Activate) -end - ---[[------------------------------------------------------------------------- - Begin Library Implementation ----------------------------------------------------------------------------]] - -local major = "Dongle-1.2" -local minor = tonumber(string.match("$Revision: 674 $", "(%d+)") or 1) - -assert(DongleStub, string.format("%s requires DongleStub.", major)) - -if not DongleStub:IsNewerVersion(major, minor) then return end - -local Dongle = {} -local methods = { - "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered", - "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered", - "ScheduleTimer", "ScheduleRepeatingTimer", "CancelTimer", "IsTimerScheduled", - "EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF", - "InitializeDB", - "InitializeSlashCommand", - "NewModule", "HasModule", "IterateModules", -} - -local registry = {} -local lookup = {} -local loadqueue = {} -local loadorder = {} -local events = {} -local databases = {} -local commands = {} -local messages = {} -local timers = {} -local heap = {} - -local frame - ---[[------------------------------------------------------------------------- - Message Localization ----------------------------------------------------------------------------]] - -local L = { - ["ADDMESSAGE_REQUIRED"] = "The frame you specify must have an 'AddMessage' method.", - ["ALREADY_REGISTERED"] = "A Dongle with the name '%s' is already registered.", - ["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)", - ["BAD_ARGUMENT_DB"] = "bad argument #%d to '%s' (DongleDB expected)", - ["CANNOT_DELETE_ACTIVE_PROFILE"] = "You cannot delete your active profile. Change profiles, then attempt to delete.", - ["DELETE_NONEXISTANT_PROFILE"] = "You cannot delete a non-existant profile.", - ["MUST_CALLFROM_DBOBJECT"] = "You must call '%s' from a Dongle database object.", - ["MUST_CALLFROM_REGISTERED"] = "You must call '%s' from a registered Dongle.", - ["MUST_CALLFROM_SLASH"] = "You must call '%s' from a Dongle slash command object.", - ["PROFILE_DOES_NOT_EXIST"] = "Profile '%s' doesn't exist.", - ["REPLACE_DEFAULTS"] = "You are attempting to register defaults with a database that already contains defaults.", - ["SAME_SOURCE_DEST"] = "Source/Destination profile cannot be the same profile.", - ["EVENT_REGISTER_SPECIAL"] = "You cannot register for the '%s' event. Use the '%s' method instead.", - ["Unknown"] = "Unknown", - ["INJECTDB_USAGE"] = "Usage: DongleCmd:InjectDBCommands(db, ['copy', 'delete', 'list', 'reset', 'set'])", - ["DBSLASH_PROFILE_COPY_DESC"] = "profile copy - Copies profile into your current profile.", - ["DBSLASH_PROFILE_COPY_PATTERN"] = "^profile copy (.+)$", - ["DBSLASH_PROFILE_DELETE_DESC"] = "profile delete - Deletes the profile .", - ["DBSLASH_PROFILE_DELETE_PATTERN"] = "^profile delete (.+)$", - ["DBSLASH_PROFILE_LIST_DESC"] = "profile list - Lists all valid profiles.", - ["DBSLASH_PROFILE_LIST_PATTERN"] = "^profile list$", - ["DBSLASH_PROFILE_RESET_DESC"] = "profile reset - Resets the current profile.", - ["DBSLASH_PROFILE_RESET_PATTERN"] = "^profile reset$", - ["DBSLASH_PROFILE_SET_DESC"] = "profile set - Sets the current profile to .", - ["DBSLASH_PROFILE_SET_PATTERN"] = "^profile set (.+)$", - ["DBSLASH_PROFILE_LIST_OUT"] = "Profile List:", -} - ---[[------------------------------------------------------------------------- - Utility functions for Dongle use ----------------------------------------------------------------------------]] - -local function assert(level,condition,message) - if not condition then - error(message,level) - end -end - -local function argcheck(value, num, ...) - if type(num) ~= "number" then - error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1) - end - - for i=1,select("#", ...) do - if type(value) == select(i, ...) then return end - end - - local types = strjoin(", ", ...) - local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") - error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3) -end - -local function safecall(func,...) - local success,err = pcall(func,...) - if not success then - geterrorhandler()(err) - end -end - ---[[------------------------------------------------------------------------- - Dongle constructor, and DongleModule system ----------------------------------------------------------------------------]] - -function Dongle:New(name, obj) - argcheck(name, 2, "string") - argcheck(obj, 3, "table", "nil") - - if not obj then - obj = {} - end - - if registry[name] then - error(string.format(L["ALREADY_REGISTERED"], name)) - end - - local reg = {["obj"] = obj, ["name"] = name} - - registry[name] = reg - lookup[obj] = reg - lookup[name] = reg - - for k,v in pairs(methods) do - obj[v] = self[v] - end - - -- Add this Dongle to the end of the queue - table.insert(loadqueue, obj) - return obj,name -end - -function Dongle:NewModule(name, obj) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "NewModule")) - argcheck(name, 2, "string") - argcheck(obj, 3, "table", "nil") - - obj,name = Dongle:New(name, obj) - - if not reg.modules then reg.modules = {} end - reg.modules[obj] = obj - reg.modules[name] = obj - - return obj,name -end - -function Dongle:HasModule(module) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "HasModule")) - argcheck(module, 2, "string", "table") - - return reg.modules and reg.modules[module] -end - -local function ModuleIterator(t, name) - if not t then return end - local obj - repeat - name,obj = next(t, name) - until type(name) == "string" or not name - - return name,obj -end - -function Dongle:IterateModules() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IterateModules")) - - return ModuleIterator, reg.modules -end - ---[[------------------------------------------------------------------------- - Event registration system ----------------------------------------------------------------------------]] - -local function OnEvent(frame, event, ...) - local eventTbl = events[event] - if eventTbl then - for obj,func in pairs(eventTbl) do - if type(func) == "string" then - if type(obj[func]) == "function" then - safecall(obj[func], obj, event, ...) - end - else - safecall(func, event, ...) - end - end - end -end - -local specialEvents = { - ["PLAYER_LOGIN"] = "Enable", - ["PLAYER_LOGOUT"] = "Disable", -} - -function Dongle:RegisterEvent(event, func) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "RegisterEvent")) - argcheck(event, 2, "string") - argcheck(func, 3, "string", "function", "nil") - - local special = (self ~= Dongle) and specialEvents[event] - if special then - error(string.format(L["EVENT_REGISTER_SPECIAL"], event, special), 3) - end - - -- Name the method the same as the event if necessary - if not func then func = event end - - if not events[event] then - events[event] = {} - frame:RegisterEvent(event) - end - events[event][self] = func -end - -function Dongle:UnregisterEvent(event) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterEvent")) - argcheck(event, 2, "string") - - local tbl = events[event] - if tbl then - tbl[self] = nil - if not next(tbl) then - events[event] = nil - frame:UnregisterEvent(event) - end - end -end - -function Dongle:UnregisterAllEvents() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterAllEvents")) - - for event,tbl in pairs(events) do - tbl[self] = nil - if not next(tbl) then - events[event] = nil - frame:UnregisterEvent(event) - end - end -end - -function Dongle:IsEventRegistered(event) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IsEventRegistered")) - argcheck(event, 2, "string") - - if events[event] and events[event][self] then - return true - else - return false - end -end - ---[[------------------------------------------------------------------------- - Inter-Addon Messaging System ----------------------------------------------------------------------------]] - -function Dongle:RegisterMessage(msg, func) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - argcheck(func, 3, "string", "function", "nil") - - -- Name the method the same as the message if necessary - if not func then func = msg end - - if not messages[msg] then - messages[msg] = {} - end - messages[msg][self] = func -end - -function Dongle:UnregisterMessage(msg) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - - local tbl = messages[msg] - if tbl then - tbl[self] = nil - if not next(tbl) then - messages[msg] = nil - end - end -end - -function Dongle:UnregisterAllMessages() - argcheck(self, 1, "table") - - for msg,tbl in pairs(messages) do - tbl[self] = nil - if not next(tbl) then - messages[msg] = nil - end - end -end - -function Dongle:TriggerMessage(msg, ...) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - local msgTbl = messages[msg] - if not msgTbl then return end - - for obj,func in pairs(msgTbl) do - if type(func) == "string" then - if type(obj[func]) == "function" then - safecall(obj[func], obj, msg, ...) - end - else - safecall(func, msg, ...) - end - end -end - -function Dongle:IsMessageRegistered(msg) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - - local tbl = messages[msg] - return tbl[self] -end - ---[[------------------------------------------------------------------------- - Timer System ----------------------------------------------------------------------------]] - -local function HeapSwap(i1, i2) - heap[i1], heap[i2] = heap[i2], heap[i1] -end - -local function HeapBubbleUp(index) - while index > 1 do - local parentIndex = math.floor(index / 2) - if heap[index].timeToFire < heap[parentIndex].timeToFire then - HeapSwap(index, parentIndex) - index = parentIndex - else - break - end - end -end - -local function HeapBubbleDown(index) - while 2 * index <= heap.lastIndex do - local leftIndex = 2 * index - local rightIndex = leftIndex + 1 - local current = heap[index] - local leftChild = heap[leftIndex] - local rightChild = heap[rightIndex] - - if not rightChild then - if leftChild.timeToFire < current.timeToFire then - HeapSwap(index, leftIndex) - index = leftIndex - else - break - end - else - if leftChild.timeToFire < current.timeToFire or - rightChild.timeToFire < current.timeToFire then - if leftChild.timeToFire < rightChild.timeToFire then - HeapSwap(index, leftIndex) - index = leftIndex - else - HeapSwap(index, rightIndex) - index = rightIndex - end - else - break - end - end - end -end - -local function OnUpdate(frame, elapsed) - local schedule = heap[1] - while schedule and schedule.timeToFire < GetTime() do - if schedule.cancelled then - HeapSwap(1, heap.lastIndex) - heap[heap.lastIndex] = nil - heap.lastIndex = heap.lastIndex - 1 - HeapBubbleDown(1) - else - if schedule.args then - safecall(schedule.func, schedule.name, unpack(schedule.args)) - else - safecall(schedule.func, schedule.name) - end - - if schedule.repeating then - schedule.timeToFire = schedule.timeToFire + schedule.repeating - HeapBubbleDown(1) - else - HeapSwap(1, heap.lastIndex) - heap[heap.lastIndex] = nil - heap.lastIndex = heap.lastIndex - 1 - HeapBubbleDown(1) - timers[schedule.name] = nil - end - end - schedule = heap[1] - end - if not schedule then frame:Hide() end -end - -function Dongle:ScheduleTimer(name, func, delay, ...) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - argcheck(func, 3, "function") - argcheck(delay, 4, "number") - - if Dongle:IsTimerScheduled(name) then - Dongle:CancelTimer(name) - end - - local schedule = {} - timers[name] = schedule - schedule.timeToFire = GetTime() + delay - schedule.func = func - schedule.name = name - if select('#', ...) ~= 0 then - schedule.args = { ... } - end - - if heap.lastIndex then - heap.lastIndex = heap.lastIndex + 1 - else - heap.lastIndex = 1 - end - heap[heap.lastIndex] = schedule - HeapBubbleUp(heap.lastIndex) - if not frame:IsShown() then - frame:Show() - end -end - -function Dongle:ScheduleRepeatingTimer(name, func, delay, ...) - Dongle:ScheduleTimer(name, func, delay, ...) - timers[name].repeating = delay -end - -function Dongle:IsTimerScheduled(name) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - local schedule = timers[name] - if schedule then - return true, schedule.timeToFire - GetTime() - else - return false - end -end - -function Dongle:CancelTimer(name) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - local schedule = timers[name] - if not schedule then return end - schedule.cancelled = true - timers[name] = nil -end - ---[[------------------------------------------------------------------------- - Debug and Print utility functions ----------------------------------------------------------------------------]] - -function Dongle:EnableDebug(level, frame) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) - argcheck(level, 2, "number", "nil") - argcheck(frame, 3, "table", "nil") - - assert(3, type(frame) == "nil" or type(frame.AddMessage) == "function", L["ADDMESSAGE_REQUIRED"]) - reg.debugFrame = frame or ChatFrame1 - reg.debugLevel = level -end - -function Dongle:IsDebugEnabled() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) - - return reg.debugLevel, reg.debugFrame -end - -local function argsToStrings(a1, ...) - if select("#", ...) > 0 then - return tostring(a1), argsToStrings(...) - else - return tostring(a1) - end -end - -local function printHelp(obj, method, header, frame, msg, ...) - local reg = lookup[obj] - assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) - - local name = reg.name - - if header then - msg = "|cFF33FF99"..name.."|r: "..tostring(msg) - end - - if select("#", ...) > 0 then - msg = string.join(", ", msg, argsToStrings(...)) - end - - frame:AddMessage(msg) -end - -local function printFHelp(obj, method, header, frame, msg, ...) - local reg = lookup[obj] - assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) - - local name = reg.name - local success,txt - - if header then - msg = "|cFF33FF99%s|r: " .. msg - success,txt = pcall(string.format, msg, name, ...) - else - success,txt = pcall(string.format, msg, ...) - end - - if success then - frame:AddMessage(txt) - else - error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3) - end -end - -function Dongle:Print(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Print")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printHelp(self, "Print", true, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:PrintF(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "PrintF")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printFHelp(self, "PrintF", true, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:Echo(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Echo")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printHelp(self, "Echo", false, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:EchoF(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EchoF")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printFHelp(self, "EchoF", false, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:Debug(level, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Debug")) - argcheck(level, 2, "number") - - if reg.debugLevel and level <= reg.debugLevel then - printHelp(self, "Debug", true, reg.debugFrame, ...) - end -end - -function Dongle:DebugF(level, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "DebugF")) - argcheck(level, 2, "number") - - if reg.debugLevel and level <= reg.debugLevel then - printFHelp(self, "DebugF", true, reg.debugFrame, ...) - end -end - ---[[------------------------------------------------------------------------- - Database System ----------------------------------------------------------------------------]] - -local dbMethods = { - "RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile", - "GetCurrentProfile", "ResetProfile", "ResetDB", - "RegisterNamespace", -} - -local function copyTable(src) - local dest = {} - for k,v in pairs(src) do - if type(k) == "table" then - k = copyTable(k) - end - if type(v) == "table" then - v = copyTable(v) - end - dest[k] = v - end - return dest -end - -local function copyDefaults(dest, src, force) - for k,v in pairs(src) do - if k == "*" then - if type(v) == "table" then - -- Values are tables, need some magic here - local mt = { - __cache = {}, - __index = function(t,k) - local mt = getmetatable(dest) - local cache = rawget(mt, "__cache") - local tbl = rawget(cache, k) - if not tbl then - local parent = t - local parentkey = k - tbl = copyTable(v) - rawset(cache, k, tbl) - local mt = getmetatable(tbl) - if not mt then - mt = {} - setmetatable(tbl, mt) - end - local newindex = function(t,k,v) - rawset(parent, parentkey, t) - rawset(t, k, v) - end - rawset(mt, "__newindex", newindex) - end - return tbl - end, - } - setmetatable(dest, mt) - -- Now need to set the metatable on any child tables - for dkey,dval in pairs(dest) do - copyDefaults(dval, v) - end - else - -- Values are not tables, so this is just a simple return - local mt = {__index = function() return v end} - setmetatable(dest, mt) - end - elseif type(v) == "table" then - if not dest[k] then dest[k] = {} end - copyDefaults(dest[k], v, force) - else - if (dest[k] == nil) or force then - dest[k] = v - end - end - end -end - -local function removeDefaults(db, defaults) - if not db then return end - for k,v in pairs(defaults) do - if k == "*" and type(v) == "table" then - -- check for any defaults that have been changed - local mt = getmetatable(db) - local cache = rawget(mt, "__cache") - - for cacheKey,cacheValue in pairs(cache) do - removeDefaults(cacheValue, v) - if next(cacheValue) ~= nil then - -- Something's changed - rawset(db, cacheKey, cacheValue) - end - end - -- Now loop through all the actual k,v pairs and remove - for key,value in pairs(db) do - removeDefaults(value, v) - end - elseif type(v) == "table" and db[k] then - removeDefaults(db[k], v) - if not next(db[k]) then - db[k] = nil - end - else - if db[k] == defaults[k] then - db[k] = nil - end - end - end -end - -local function initSection(db, section, svstore, key, defaults) - local sv = rawget(db, "sv") - - local tableCreated - if not sv[svstore] then sv[svstore] = {} end - if not sv[svstore][key] then - sv[svstore][key] = {} - tableCreated = true - end - - local tbl = sv[svstore][key] - - if defaults then - copyDefaults(tbl, defaults) - end - rawset(db, section, tbl) - - return tableCreated, tbl -end - -local dbmt = { - __index = function(t, section) - local keys = rawget(t, "keys") - local key = keys[section] - if key then - local defaultTbl = rawget(t, "defaults") - local defaults = defaultTbl and defaultTbl[section] - - if section == "profile" then - local new = initSection(t, section, "profiles", key, defaults) - if new then - Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", t, rawget(t, "parent"), rawget(t, "sv_name"), key) - end - elseif section == "profiles" then - local sv = rawget(t, "sv") - if not sv.profiles then sv.profiles = {} end - rawset(t, "profiles", sv.profiles) - elseif section == "global" then - local sv = rawget(t, "sv") - if not sv.global then sv.global = {} end - if defaults then - copyDefaults(sv.global, defaults) - end - rawset(t, section, sv.global) - else - initSection(t, section, section, key, defaults) - end - end - - return rawget(t, section) - end -} - -local function initdb(parent, name, defaults, defaultProfile, olddb) - -- This allows us to use an arbitrary table as base instead of saved variable name - local sv - if type(name) == "string" then - sv = getglobal(name) - if not sv then - sv = {} - setglobal(name, sv) - end - elseif type(name) == "table" then - sv = name - end - - -- Generate the database keys for each section - local char = string.format("%s - %s", UnitName("player"), GetRealmName()) - local realm = GetRealmName() - local class = select(2, UnitClass("player")) - local race = select(2, UnitRace("player")) - local faction = UnitFactionGroup("player") - local factionrealm = string.format("%s - %s", faction, realm) - - -- Make a container for profile keys - if not sv.profileKeys then sv.profileKeys = {} end - - -- Try to get the profile selected from the char db - local profileKey = sv.profileKeys[char] or defaultProfile or char - sv.profileKeys[char] = profileKey - - local keyTbl= { - ["char"] = char, - ["realm"] = realm, - ["class"] = class, - ["race"] = race, - ["faction"] = faction, - ["factionrealm"] = factionrealm, - ["global"] = true, - ["profile"] = profileKey, - ["profiles"] = true, -- Don't create until we need - } - - -- If we've been passed an old database, clear it out - if olddb then - for k,v in pairs(olddb) do olddb[k] = nil end - end - - -- Give this database the metatable so it initializes dynamically - local db = setmetatable(olddb or {}, dbmt) - - -- Copy methods locally - for idx,method in pairs(dbMethods) do - db[method] = Dongle[method] - end - - -- Set some properties in the object we're returning - db.profiles = sv.profiles - db.keys = keyTbl - db.sv = sv - db.sv_name = name - db.defaults = defaults - db.parent = parent - - databases[db] = true - - return db -end - -function Dongle:InitializeDB(name, defaults, defaultProfile) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeDB")) - argcheck(name, 2, "string", "table") - argcheck(defaults, 3, "table", "nil") - argcheck(defaultProfile, 4, "string", "nil") - - return initdb(self, name, defaults, defaultProfile) -end - --- This function operates on a Dongle DB object -function Dongle.RegisterDefaults(db, defaults) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterDefaults")) - assert(3, db.defaults == nil, L["REPLACE_DEFAUTS"]) - argcheck(defaults, 2, "table") - - for section,key in pairs(db.keys) do - if defaults[section] and rawget(db, section) then - copyDefaults(db[section], defaults[section]) - end - end - - db.defaults = defaults -end - -function Dongle:ClearDBDefaults() - for db in pairs(databases) do - local defaults = db.defaults - local sv = db.sv - - if db and defaults then - for section,key in pairs(db.keys) do - if defaults[section] and rawget(db, section) then - removeDefaults(db[section], defaults[section]) - end - end - - for section,key in pairs(db.keys) do - local tbl = rawget(db, section) - if tbl and not next(tbl) then - if sv[section] then - if type(key) == "string" then - sv[section][key] = nil - else - sv[section] = nil - end - end - end - end - end - end -end - -function Dongle.SetProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "SetProfile")) - argcheck(name, 2, "string") - - local old = db.profile - local defaults = db.defaults and db.defaults.profile - - if defaults then - -- Remove the defaults from the old profile - removeDefaults(old, defaults) - end - - db.profile = nil - db.keys["profile"] = name - db.sv.profileKeys[db.keys.char] = name - - Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) -end - -function Dongle.GetProfiles(db, tbl) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetProfiles")) - argcheck(tbl, 2, "table", "nil") - - -- Clear the container table - if tbl then - for k,v in pairs(tbl) do tbl[k] = nil end - else - tbl = {} - end - - local i = 0 - for profileKey in pairs(db.profiles) do - i = i + 1 - tbl[i] = profileKey - end - - -- Add the current profile, if it hasn't been created yet - if rawget(db, "profile") == nil then - i = i + 1 - tbl[i] = db.keys.profile - end - - return tbl, i -end - -function Dongle.GetCurrentProfile(db) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile")) - return db.keys.profile -end - -function Dongle.DeleteProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "DeleteProfile")) - argcheck(name, 2, "string") - - if db.keys.profile == name then - error(L["CANNOT_DELETE_ACTIVE_PROFILE"], 2) - end - - assert(type(db.sv.profiles[name]) == "table", L["DELETE_NONEXISTANT_PROFILE"]) - - db.sv.profiles[name] = nil - Dongle:TriggerMessage("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name) -end - -function Dongle.CopyProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "CopyProfile")) - argcheck(name, 2, "string") - - assert(3, db.keys.profile ~= name, L["SAME_SOURCE_DEST"]) - assert(3, type(db.sv.profiles[name]) == "table", string.format(L["PROFILE_DOES_NOT_EXIST"], name)) - - local profile = db.profile - local source = db.sv.profiles[name] - - copyDefaults(profile, source, true) - Dongle:TriggerMessage("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.keys.profile) -end - -function Dongle.ResetProfile(db) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetProfile")) - - local profile = db.profile - - for k,v in pairs(profile) do - profile[k] = nil - end - - local defaults = db.defaults and db.defaults.profile - if defaults then - copyDefaults(profile, defaults) - end - Dongle:TriggerMessage("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.keys.profile) -end - - -function Dongle.ResetDB(db, defaultProfile) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetDB")) - argcheck(defaultProfile, 2, "nil", "string") - - local sv = db.sv - for k,v in pairs(sv) do - sv[k] = nil - end - - local parent = db.parent - - initdb(parent, db.sv_name, db.defaults, defaultProfile, db) - Dongle:TriggerMessage("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.keys.profile) - Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) - return db -end - -function Dongle.RegisterNamespace(db, name, defaults) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterNamespace")) - argcheck(name, 2, "string") - argcheck(defaults, 3, "nil", "table") - - local sv = db.sv - if not sv.namespaces then sv.namespaces = {} end - if not sv.namespaces[name] then - sv.namespaces[name] = {} - end - - local newDB = initdb(db, sv.namespaces[name], defaults, db.keys.profile) - -- Remove the :SetProfile method from newDB - newDB.SetProfile = nil - - if not db.children then db.children = {} end - table.insert(db.children, newDB) - return newDB -end - ---[[------------------------------------------------------------------------- - Slash Command System ----------------------------------------------------------------------------]] - -local slashCmdMethods = { - "InjectDBCommands", - "RegisterSlashHandler", - "PrintUsage", -} - -local function OnSlashCommand(cmd, cmd_line) - if cmd.patterns then - for idx,tbl in pairs(cmd.patterns) do - local pattern = tbl.pattern - if string.match(cmd_line, pattern) then - local handler = tbl.handler - if type(tbl.handler) == "string" then - local obj - -- Look in the command object before we look at the parent object - if cmd[handler] then obj = cmd end - if cmd.parent[handler] then obj = cmd.parent end - if obj then - obj[handler](obj, string.match(cmd_line, pattern)) - end - else - handler(string.match(cmd_line, pattern)) - end - return - end - end - end - cmd:PrintUsage() -end - -function Dongle:InitializeSlashCommand(desc, name, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeSlashCommand")) - argcheck(desc, 2, "string") - argcheck(name, 3, "string") - argcheck(select(1, ...), 4, "string") - for i = 2,select("#", ...) do - argcheck(select(i, ...), i+2, "string") - end - - local cmd = {} - cmd.desc = desc - cmd.name = name - cmd.parent = self - cmd.slashes = { ... } - for idx,method in pairs(slashCmdMethods) do - cmd[method] = Dongle[method] - end - - local genv = getfenv(0) - - for i = 1,select("#", ...) do - genv["SLASH_"..name..tostring(i)] = "/"..select(i, ...) - end - - genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end - - commands[cmd] = true - - return cmd -end - -function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "RegisterSlashHandler")) - - argcheck(desc, 2, "string") - argcheck(pattern, 3, "string") - argcheck(handler, 4, "function", "string") - - if not cmd.patterns then - cmd.patterns = {} - end - - table.insert(cmd.patterns, { - ["desc"] = desc, - ["handler"] = handler, - ["pattern"] = pattern, - }) -end - -function Dongle.PrintUsage(cmd) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "PrintUsage")) - local parent = cmd.parent - - parent:Echo(cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n") - if cmd.patterns then - for idx,tbl in ipairs(cmd.patterns) do - parent:Echo(" - " .. tbl.desc) - end - end -end - -local dbcommands = { - ["copy"] = { - L["DBSLASH_PROFILE_COPY_DESC"], - L["DBSLASH_PROFILE_COPY_PATTERN"], - "CopyProfile", - }, - ["delete"] = { - L["DBSLASH_PROFILE_DELETE_DESC"], - L["DBSLASH_PROFILE_DELETE_PATTERN"], - "DeleteProfile", - }, - ["list"] = { - L["DBSLASH_PROFILE_LIST_DESC"], - L["DBSLASH_PROFILE_LIST_PATTERN"], - }, - ["reset"] = { - L["DBSLASH_PROFILE_RESET_DESC"], - L["DBSLASH_PROFILE_RESET_PATTERN"], - "ResetProfile", - }, - ["set"] = { - L["DBSLASH_PROFILE_SET_DESC"], - L["DBSLASH_PROFILE_SET_PATTERN"], - "SetProfile", - }, -} - -function Dongle.InjectDBCommands(cmd, db, ...) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "InjectDBCommands")) - assert(3, databases[db], string.format(L["BAD_ARGUMENT_DB"], 2, "InjectDBCommands")) - local argc = select("#", ...) - assert(3, argc > 0, L["INJECTDB_USAGE"]) - - for i=1,argc do - local cmdname = string.lower(select(i, ...)) - local entry = dbcommands[cmdname] - assert(entry, L["INJECTDB_USAGE"]) - local func = entry[3] - - local handler - if cmdname == "list" then - handler = function(...) - local profiles = db:GetProfiles() - db.parent:Print(L["DBSLASH_PROFILE_LIST_OUT"] .. "\n" .. strjoin("\n", unpack(profiles))) - end - else - handler = function(...) db[entry[3]](db, ...) end - end - - cmd:RegisterSlashHandler(entry[1], entry[2], handler) - end -end - ---[[------------------------------------------------------------------------- - Internal Message/Event Handlers ----------------------------------------------------------------------------]] - -local function PLAYER_LOGOUT(event) - Dongle:ClearDBDefaults() - for k,v in pairs(registry) do - local obj = v.obj - if type(obj["Disable"]) == "function" then - safecall(obj["Disable"], obj) - end - end -end - -local PLAYER_LOGIN -do - local lockPlayerLogin = false - - function PLAYER_LOGIN() - if lockPlayerLogin then return end - - lockPlayerLogin = true - - local obj = table.remove(loadorder, 1) - while obj do - if type(obj.Enable) == "function" then - safecall(obj.Enable, obj) - end - obj = table.remove(loadorder, 1) - end - - lockPlayerLogin = false - end -end - -local function ADDON_LOADED(event, ...) - local obj = table.remove(loadqueue, 1) - while obj do - table.insert(loadorder, obj) - - if type(obj.Initialize) == "function" then - safecall(obj.Initialize, obj) - end - - obj = table.remove(loadqueue, 1) - end - - if IsLoggedIn() then - PLAYER_LOGIN() - end -end - -local function DONGLE_PROFILE_CHANGED(msg, db, parent, sv_name, profileKey) - local children = db.children - if children then - for i,namespace in ipairs(children) do - local old = namespace.profile - local defaults = namespace.defaults and namespace.defaults.profile - - if defaults then - -- Remove the defaults from the old profile - removeDefaults(old, defaults) - end - - namespace.profile = nil - namespace.keys["profile"] = profileKey - end - end -end - ---[[------------------------------------------------------------------------- - DongleStub required functions and registration ----------------------------------------------------------------------------]] - -function Dongle:GetVersion() return major,minor end - -local function Activate(self, old) - if old then - registry = old.registry or registry - lookup = old.lookup or lookup - loadqueue = old.loadqueue or loadqueue - loadorder = old.loadorder or loadorder - events = old.events or events - databases = old.databases or databases - commands = old.commands or commands - messages = old.messages or messages - frame = old.frame or CreateFrame("Frame") - timers = old.timers or timers - heap = old.heap or heap - else - frame = CreateFrame("Frame") - local reg = {obj = self, name = "Dongle"} - registry[major] = reg - lookup[self] = reg - lookup[major] = reg - end - - self.registry = registry - self.lookup = lookup - self.loadqueue = loadqueue - self.loadorder = loadorder - self.events = events - self.databases = databases - self.commands = commands - self.messages = messages - self.frame = frame - self.timers = timers - self.heap = heap - - frame:SetScript("OnEvent", OnEvent) - frame:SetScript("OnUpdate", OnUpdate) - - -- Lets ensure the lookup table has our entry - -- This fixes an issue with upgrades - lookup[self] = lookup[major] - - -- Register for events using Dongle itself - self:RegisterEvent("ADDON_LOADED", ADDON_LOADED) - self:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN) - self:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT) - self:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED) - - -- Convert all the modules handles - for name,obj in pairs(registry) do - for k,v in ipairs(methods) do - obj[k] = self[v] - end - end - - -- Convert all database methods - for db in pairs(databases) do - for idx,method in ipairs(dbMethods) do - db[method] = self[method] - end - end - - -- Convert all slash command methods - for cmd in pairs(commands) do - for idx,method in ipairs(slashCmdMethods) do - cmd[method] = self[method] - end - end -end - -Dongle = DongleStub:Register(Dongle, Activate) diff --git a/Clique/ImportExport.lua b/Clique/ImportExport.lua new file mode 100644 index 0000000..4455750 --- /dev/null +++ b/Clique/ImportExport.lua @@ -0,0 +1,34 @@ +local addonName, addon = ... + +local LibSerialize = LibStub("LibSerialize") +local LibDeflate = LibStub("LibDeflate") + +function addon:GetExportString() + local data = addon.db.profile.bindings + local serialized = LibSerialize:Serialize(data) + local compressed = LibDeflate:CompressDeflate(serialized) + local encoded = LibDeflate:EncodeForPrint(compressed) + + return string.format("CL01:%s", encoded) +end + +function addon:DecodeExportString(text) + local header = string.sub(text, 1, 5) + if header ~= "CL01:" then return end + + local payload = string.sub(text, 6, string.len(text)) + local decoded = LibDeflate:DecodeForPrint(payload) + if not decoded then return end + local decompressed = LibDeflate:DecompressDeflate(decoded) + if not decompressed then return end + local success, data = LibSerialize:Deserialize(decompressed) + if not success then return end + return data +end + +function addon:ImportBindings(importBindings) + self.db.profile.bindings = importBindings + self.bindings = self.db.profile.bindings + addon:Printf("Importing new bindings into current profile") + self:FireMessage("BINDINGS_CHANGED") +end diff --git a/Clique/Localization.deDE.lua b/Clique/Localization.deDE.lua deleted file mode 100644 index 9d72b38..0000000 --- a/Clique/Localization.deDE.lua +++ /dev/null @@ -1,110 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for deDE. Any commented lines need to be updated -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - -if GetLocale() == "deDE" then - L.RANK = "Rang" - L.RANK_PATTERN = "Rang (%d+)" - L.CAST_FORMAT = "%s(Rang %s)" - - L.RACIAL_PASSIVE = "Volk Passiv" - L.PASSIVE = SPELL_PASSIVE - - L.CLICKSET_DEFAULT = "Standard" - L.CLICKSET_HARMFUL = "Schadhafte Aktionen" - L.CLICKSET_HELPFUL = "Hilfreiche Aktionen" - L.CLICKSET_OOC = "Au\195\159erhalb des Kampfes" - L.CLICKSET_BEARFORM = "B\195\164ren Gestalt" - L.CLICKSET_CATFORM = "Katzen Gestalt" - L.CLICKSET_AQUATICFORM = "Wasser Gestalt" - L.CLICKSET_TRAVELFORM = "Reise Gestalt" - L.CLICKSET_MOONKINFORM = "Moonkin Gestalt" - L.CLICKSET_TREEOFLIFE = "Baum des Lebens Gestalt" - L.CLICKSET_SHADOWFORM = "Schattengestalt" - L.CLICKSET_STEALTHED = "Getarnt" - L.CLICKSET_BATTLESTANCE = "Kampfhaltung" - L.CLICKSET_DEFENSIVESTANCE = "Verteidigungshaltung" - L.CLICKSET_BERSERKERSTANCE = "Berserkerhaltung" - - L.BEAR_FORM = "B\195\164ren Gestalt" - L.DIRE_BEAR_FORM = "Dire B\195\164ren Gestalt" - L.CAT_FORM = "Katzen Gestalt" - L.AQUATIC_FORM = "Wasser Gestalt" - L.TRAVEL_FORM = "Reise Gestalt" - L.TREEOFLIFE = "Baum des Lebens" - L.MOONKIN_FORM = "oonkin Gestalt" - L.STEALTH = "Tarnen" - L.SHADOWFORM = "Schattengestalt" - L.BATTLESTANCE = "Kampfhaltung" - L.DEFENSIVESTANCE = "Verteidigungshaltung" - L.BERSERKERSTANCE = "Berserkerhaltung" - - L.BINDING_NOT_DEFINED = "Belegung nicht definiert." - - L.CANNOT_CHANGE_COMBAT = "\195\132nderungen w\195\164rend des Kampfes nicht m\195\182glich. \195\132nderungen werden bis Kampfende verschoben." - L.APPLY_QUEUE = "Au\195\159erhalb des Kampfes. Ausstehende \195\132nderungen werden durchgef\195\188hrt." - L.PROFILE_CHANGED = "Profil gewechselt zu '%s'." - L.PROFILE_DELETED = "Profile '%s' wurde gel\195\182scht." - - L.ACTION_ACTIONBAR = "Wechsel Aktionsleiste" - L.ACTION_ACTION = "Aktion Taste" - L.ACTION_PET = "Tier Aktion Taste" - L.ACTION_SPELL = "Zauber ausf\195\188hren" - L.ACTION_ITEM = "Benutze Gegenstand" - L.ACTION_MACRO = "Starte eigenes Makro" - L.ACTION_STOP = "Zauber Unterbrechen" - L.ACTION_TARGET = "Ziel ausw\195\164hlen" - L.ACTION_FOCUS = "Setze Fokus" - L.ACTION_ASSIST = "Assistiere Einheit" - L.ACTION_CLICK = "Klicke Taste" - L.ACTION_MENU = "Zeige Men\195\188" - - L.HELP_TEXT = "Willkomen zu Clique. Zuerst suchst du einfach im Zauberbuch einen Spruch aus, welchen du an eine Taste binden m\195\182chtest. Dann klickst du mit der entsprechenden Taste diesen Zauber an. Als Beispiel, gehe zu \"Blitzheilung\" und klicke darauf mit Shift+LinkeMaustaste um diesen Zauber auf Shift+LinkeMaustaste zu legen." - L.CUSTOM_HELP = "Dies ist das Clique konfigurations Fenster. Von hier aus kannst du alle Klick-Kombinationen, die uns die UI erlaubt, konfigurieren. W\195\164hle eine Grundfunktion aus der linken Spalte. Klicke dann auf den unteren Button mit einer Tastenkombination deiner Wahl, und erg\195\164nze (falls ben\195\182tigt) die Parameter." - - L.BS_ACTIONBAR_HELP = "Wechselt die Aktionsleiste. 'increment' Wechselt eine Leiste rauf, 'decrement' mach das Gegenteil. Gibst du eine Zahl ein, wird nur die enstprechende Leiste gezeigt. Die Eingabe von 1,3 w\195\188rde zwischen Leiste 1 und 3 wechseln" - L.BS_ACTIONBAR_ARG1_LABEL = "Aktion:" - - L.BS_ACTION_HELP = "Simuliert einen Klick auf eine Aktions Taste. Gebe die Nummer der Aktions Taste an." - L.BS_ACTION_ARG1_LABEL = "Tasten Nummer:" - L.BS_ACTION_ARG2_LABEL = "(Optional) Unit:" - - L.BS_PET_HELP = "Simuliert einen Klick auf eine Tier Aktions Taste. Gebe die Nummer der Aktions Taste an." - L.BS_PET_ARG1_LABEL = "Tier Tasten Nummer:" - L.BS_PET_ARG2_LABEL = "(Optional) Unit:" - - L.BS_SPELL_HELP = "F\195\188hrt einen Zauber aus dem Zauberbuch aus. M\195\182glich sind Zaubername, und optional Rucksack/Platz, oder Gegenstand-Name als Ziel eines Zaubers.(zB. Tier f\195\188ttern)" - L.BS_SPELL_ARG1_LABEL = "Zauber Name:" - L.BS_SPELL_ARG2_LABEL = "*Rang/Rucksack Nr:" - L.BS_SPELL_ARG3_LABEL = "*Platz Nummer:" - L.BS_SPELL_ARG4_LABEL = "*Gegenstands Name:" - L.BS_SPELL_ARG5_LABEL = "(Optional) Unit:" - - L.BS_ITEM_HELP = "Benutze Gegenstand. Angabe von Rucksack/Platz, oder Gegenstand-Name." - L.BS_ITEM_ARG1_LABEL = "Rucksack Nummer:" - L.BS_ITEM_ARG2_LABEL = "Platz Nummer:" - L.BS_ITEM_ARG3_LABEL = "Gegenstands Name:" - L.BS_ITEM_ARG4_LABEL = "(Optional) Unit:" - - L.BS_MACRO_HELP = "Benutzt eigenes Makro aus vorhandenem index" - L.BS_ARG1_LABEL = "Makro Index:" - L.BS_ARG2_LABEL = "Makro Text:" - - L.BS_STOP_HELP = "Unterbricht den laufenden Zauberspruch" - - L.BS_TARGET_HELP = "Visiert \"unit\" als Ziel an" - L.BS_TARGET_ARG1_LABEL = "(Optional) Unit:" - - L.BS_FOCUS_HELP = "W\195\164hlt \"focus\" Einheit aus." - L.BS_FOCUS_ARG1_LABEL = "(Optional) Unit:" - - L.BS_ASSIST_HELP = "Assistiert der Einheit \"unit\"" - L.BS_ASSIST_ARG1_LABEL = "(Optional) Unit:" - - L.BS_CLICK_HELP = "Simuliert Klick auf eine Aktions Taste" - L.BS_CLICK_ARG1_LABEL = "Tasten Name:" - - L.BS_MENU_HELP = "Zeigt das Einheiten Pop-Up Men\195\188" -end \ No newline at end of file diff --git a/Clique/Localization.enUS.lua b/Clique/Localization.enUS.lua deleted file mode 100644 index ab8dad8..0000000 --- a/Clique/Localization.enUS.lua +++ /dev/null @@ -1,134 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for English -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - --- This is the default locale. -if GetLocale() then - L.RANK = "Rank" - L.RANK_PATTERN = "Rank (%d+)" - L.CAST_FORMAT = "%s(Rank %s)" - - L.RACIAL_PASSIVE = "Racial Passive" - L.PASSIVE = SPELL_PASSIVE - - L.CLICKSET_DEFAULT = "Default" - L.CLICKSET_HARMFUL = "Harmful actions" - L.CLICKSET_HELPFUL = "Helpful actions" - L.CLICKSET_OOC = "Out of Combat" - L.CLICKSET_BEARFORM = "Bear Form" - L.CLICKSET_CATFORM = "Cat Form" - L.CLICKSET_AQUATICFORM = "Aquatic Form" - L.CLICKSET_TRAVELFORM = "Travel Form" - L.CLICKSET_MOONKINFORM = "Moonkin Form" - L.CLICKSET_TREEOFLIFE = "Tree of Life Form" - L.CLICKSET_SHADOWFORM = "Shadowform" - L.CLICKSET_STEALTHED = "Stealthed" - L.CLICKSET_BATTLESTANCE = "Battle Stance" - L.CLICKSET_DEFENSIVESTANCE = "Defensive Stance" - L.CLICKSET_BERSERKERSTANCE = "Berserker Stance" - - L.BEAR_FORM = "Bear Form" - L.DIRE_BEAR_FORM = "Dire Bear Form" - L.CAT_FORM = "Cat Form" - L.AQUATIC_FORM = "Aquatic Form" - L.TRAVEL_FORM = "Travel Form" - L.TREEOFLIFE = "Tree of Life" - L.MOONKIN_FORM = "Moonkin Form" - L.STEALTH = "Stealth" - L.SHADOWFORM = "Shadowform" - L.BATTLESTANCE = "Battle Stance" - L.DEFENSIVESTANCE = "Defensive Stance" - L.BERSERKERSTANCE = "Berserker Stance" - - L.BINDING_NOT_DEFINED = "Binding not defined" - L.CANNOT_CHANGE_COMBAT = "Cannot make changes in combat. These changes will be delayed until you exit combat." - L.APPLY_QUEUE = "Out of combat. Applying all queued changes." - L.PROFILE_CHANGED = "Profile has changed to '%s'." - L.PROFILE_DELETED = "Profile '%s' has been deleted." - L.PROFILE_RESET = "Your profile '%s' has been reset." - - L.ACTION_ACTIONBAR = "Change ActionBar" - L.ACTION_ACTION = "Action Button" - L.ACTION_PET = "Pet Action Button" - L.ACTION_SPELL = "Cast Spell" - L.ACTION_ITEM = "Use Item" - L.ACTION_MACRO = "Run Custom Macro" - L.ACTION_STOP = "Stop Casting" - L.ACTION_TARGET = "Target Unit" - L.ACTION_FOCUS = "Set Focus" - L.ACTION_ASSIST = "Assist Unit" - L.ACTION_CLICK = "Click Button" - L.ACTION_MENU = "Show Menu" - - L.HELP_TEXT = "Welcome to Clique. For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click. Then click on that spell with whatever click-binding you would like. For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick." - L.CUSTOM_HELP = "This is the Clique custom edit screen. From here you can configure any of the combinations that the UI makes available to us in response to clicks. Select a base action from the left column. You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)." - - L.BS_ACTIONBAR_HELP = "Change the actionbar. 'increment' will move it up one page, 'decrement' does the opposite. If you supply a number, the action bar will be turned to that page. You can specify 1,3 to toggle between pages 1 and 3" - L.BS_ACTIONBAR_ARG1_LABEL = "Action:" - - L.BS_ACTION_HELP = "Simulate a click on an action button. Specify the number of the action button." - L.BS_ACTION_ARG1_LABEL = "Button Number:" - L.BS_ACTION_ARG2_LABEL = "(Optional) Unit:" - - L.BS_PET_HELP = "Simulate a click on an pet's action button. Specify the number of the button." - L.BS_PET_ARG1_LABEL = "Pet Button Number:" - L.BS_PET_ARG2_LABEL = "(Optional) Unit:" - - L.BS_SPELL_HELP = "Cast a spell from the spellbook. Takes a spell name, and optionally a bag and slot, or item name to use as the target of the spell (i.e. Feed Pet)" - L.BS_SPELL_ARG1_LABEL = "Spell Name:" - L.BS_SPELL_ARG2_LABEL = "*Rank/Bag Number:" - L.BS_SPELL_ARG3_LABEL = "*Slot Number:" - L.BS_SPELL_ARG4_LABEL = "*Item Name:" - L.BS_SPELL_ARG5_LABEL = "(Optional) Unit:" - - L.BS_ITEM_HELP = "Use an item. Can take either a bag and slot, or an item name." - L.BS_ITEM_ARG1_LABEL = "Bag Number:" - L.BS_ITEM_ARG2_LABEL = "Slot Number:" - L.BS_ITEM_ARG3_LABEL = "Item Name:" - L.BS_ITEM_ARG4_LABEL = "(Optional) Unit:" - - L.BS_MACRO_HELP = "Use a custom macro in a given index" - L.BS_MACRO_ARG1_LABEL = "Macro Index:" - L.BS_MACRO_ARG2_LABEL = "Macro Text:" - - L.BS_STOP_HELP = "Stops casting the current spell" - - L.BS_TARGET_HELP = "Targets the unit" - L.BS_TARGET_ARG1_LABEL = "(Optional) Unit:" - - L.BS_FOCUS_HELP = "Sets your \"focus\" unit" - L.BS_FOCUS_ARG1_LABEL = "(Optional) Unit:" - - L.BS_ASSIST_HELP = "Assists the unit" - L.BS_ASSIST_ARG1_LABEL = "(Optional) Unit:" - - L.BS_CLICK_HELP = "Simulate click on a button" - L.BS_CLICK_ARG1_LABEL = "Button Name:" - - L.BS_MENU_HELP = "Shows the unit pop up menu" - - L.CUSTOM = "Custom" - L.FRAMES = "Frames" - L.PROFILES = "Profiles" - L.DELETE = "Delete" - L.EDIT = "Edit" - - L["Clique Options"] = "Clique Options" - L["Spec 1:"] = "Spec 1:" - L["Spec 2:"] = "Spec 2:" - L["Spec 3:"] = "Spec 3:" - L["Spec 4:"] = "Spec 4:" - L["Spec 5:"] = "Spec 5:" - L["Spec 6:"] = "Spec 6:" - L["Spec 7:"] = "Spec 7:" - L["Spec 8:"] = "Spec 8:" - L["Spec 9:"] = "Spec 9:" - L["Spec 10:"] = "Spec 10:" - L["Spec 11:"] = "Spec 11:" - L["Spec 12:"] = "Spec 12:" - L.DOWNCLICK_LABEL = "Trigger clicks on the 'down' portion of the click" - L.SPECSWITCH_LABEL = "Change profile when switching talent specs" -end - diff --git a/Clique/Localization.esES.lua b/Clique/Localization.esES.lua deleted file mode 100644 index d896357..0000000 --- a/Clique/Localization.esES.lua +++ /dev/null @@ -1,110 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for esES (Spanish) -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - --- This is the default locale. -if GetLocale() == "esES" then - L.RANK = "Rango" - L.RANK_PATTERN = "Rango (%d+)" - L.CAST_FORMAT = "%s(Rango %s)" - L.RACIAL_PASSIVE = "Pasivo racial" - L.PASSIVE = SPELL_PASSIVE - L.CLICKSET_DEFAULT = "Por Defecto" - L.CLICKSET_HARMFUL = "Aciones de Daño" - L.CLICKSET_HELPFUL = "Acciones de Ayuda" - L.CLICKSET_OOC = "Fuera de Combate" - L.CLICKSET_BEARFORM = "Forma de Oso" - L.CLICKSET_CATFORM = "Forma Felina" - L.CLICKSET_AQUATICFORM = "Forma Acuática" - L.CLICKSET_TRAVELFORM = "Forma de Viaje" - L.CLICKSET_MOONKINFORM = "Forma de Moonkin" - L.CLICKSET_TREEOFLIFE = "Forma de Arbol de Vida" - L.CLICKSET_SHADOWFORM = "Forma de las Sombras" - L.CLICKSET_STEALTHED = "En Sigilo" - L.CLICKSET_BATTLESTANCE = "Actitud de Batalla" - L.CLICKSET_DEFENSIVESTANCE = "Actitud Defensiva" - L.CLICKSET_BERSERKERSTANCE = "Actitud Rabiosa" - - L.BEAR_FORM = "Forma de Oso" - L.DIRE_BEAR_FORM = "Forma de Oso Temible" - L.CAT_FORM = "Forma Felina" - L.AQUATIC_FORM = "Forma Acuática" - L.TRAVEL_FORM = "Forma de Viaje" - L.TREEOFLIFE = "Arbol de Vida" - L.MOONKIN_FORM = "Forma de Moonkin" - L.STEALTH = "Sigilo" - L.SHADOWFORM = "Forma de las Sombras" - L.BATTLESTANCE = "Actitud de Batalla" - L.DEFENSIVESTANCE = "Actitud Defensiva" - L.BERSERKERSTANCE = "Actitud Rabiosa" - - L.BINDING_NOT_DEFINED = "Binding no definido" - L.CANNOT_CHANGE_COMBAT = "No puede hacer cambios en combate. Los cambios se realizarán cuando salga de combate." - L.APPLY_QUEUE = "Fuera de combate. Aplicando todos los cambios pendientes." - L.PROFILE_CHANGED = "El perfil ha cambiado a '%s'." - L.PROFILE_DELETED = "El perfil '%s' ha sido borrado." - L.PROFILE_RESET = "Su perfil '%s' ha sido resetiado." - L.ACTION_ACTIONBAR = "Cambiar Barra de Acción" - L.ACTION_ACTION = "Botón de Acción" - L.ACTION_PET = "Botón de Acción Mascota" - L.ACTION_SPELL = "Lanzar Hechizo" - L.ACTION_ITEM = "Usar Item" - L.ACTION_MACRO = "Ejecutar macro por defecto" - L.ACTION_STOP = "Parar Lanzamiento" - L.ACTION_TARGET = "Apuntar Unidad" - L.ACTION_FOCUS = "Fijar Foco" - L.ACTION_ASSIST = "Asistir Unidad" - L.ACTION_CLICK = "Click Botón" - L.ACTION_MENU = "Mostrar Menú" - - L.HELP_TEXT = "Bienvenido a Clique. Para su operación básica, usted puede navegar por el Libro de Hechizos (Spellbook) y determinar que hechizo le gustaría asociar a un click específico. Luego haga click sobre el hechizo, con la combinación de teclas que a usted le guste (click o click + teclado). Por ejemplo, un sacerdote puede buscar en su libro de hechizos la \"Sanación Relámpago\" (Flash Heal) y realizar sobre este un Shift+LeftClick, lo cual asociará dicho hechizo a las combinación de teclas Shift+LeftClick." - L.CUSTOM_HELP = "Esta es la pantalla de edición por defecto de Clique. Desde aquí usted puede configurar cualquiera de las combinaciones que el UI tiene disponible para nosotros en respuesta a los clicks. Seleccione una acción base de la columna izquierda. Usted puede hacer clic en el botón de abajo para asociar las convinaciones de clicks + teclas que desee, y entonces proporcionar los argumentos requeridos (if any)." - - L.BS_ACTIONBAR_HELP = "Cambie la barra de acción. 'increment' lo moverá arriba de una pagina, 'decrement' hará lo contrario. Si usted proporciona un número, la barra de acción será girada a esa página. Usted puede especificar 1,3 para alternar entre las páginas 1 y 3." - - L.BS_ACTIONBAR_ARG1_LABEL = "Acción:" - - L.BS_ACTION_HELP = "Simular un click sobre un botón de acción. Especificar el número del botón de acción." - L.BS_ACTION_ARG1_LABEL = "Número de Botón:" - L.BS_ACTION_ARG2_LABEL = "(Opcional) Unidad:" - - L.BS_PET_HELP = "Simular un click sobre el botón de acción de mascotas. Especificar el número de botón." - L.BS_PET_ARG1_LABEL = "Número de Boton Mascota:" - L.BS_PET_ARG2_LABEL = "(Opcional) Unidad:" - - L.BS_SPELL_HELP = "Lanzar un hechizo desde el libro de hechizos. Toma un nombre de hechizo y opcionalmente una bolsa (bag) y ranura (slot) o un nombre de ítem para usar como objetivo del hechizo (i.e. Alimentar Mascota)" - L.BS_SPELL_ARG1_LABEL = "Nombre del Hechizo:" - L.BS_SPELL_ARG2_LABEL = "*Número de Rango/Bolsa:" - L.BS_SPELL_ARG3_LABEL = "*Número de Ranura (Slot):" - L.BS_SPELL_ARG4_LABEL = "*Nombre del Item:" - L.BS_SPELL_ARG5_LABEL = "(Opcional) Unidad:" - - L.BS_ITEM_HELP = "Use un item. Puede tomar una bolsa y ranura o un nombre de item." - L.BS_ITEM_ARG1_LABEL = "Número de Bolsa:" - L.BS_ITEM_ARG2_LABEL = "Número de Ranura (Slot):" - L.BS_ITEM_ARG3_LABEL = "Nombre del Item:" - L.BS_ITEM_ARG4_LABEL = "(Opcional) Unidad:" - - L.BS_MACRO_HELP = "Use una macro por defecto en un indice en particular" - L.BS_MACRO_ARG1_LABEL = "Indice Macro:" - L.BS_MACRO_ARG2_LABEL = "Texto Macro:" - - L.BS_STOP_HELP = "Parar de lanzar hechizos en curso" - - L.BS_TARGET_HELP = "Apuntar unidad" - L.BS_TARGET_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_FOCUS_HELP = "Fijar su \"foco\" en unidad" - L.BS_FOCUS_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_ASSIST_HELP = "Asistir la unidad" - L.BS_ASSIST_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_CLICK_HELP = "Simular click sobre un botón" - L.BS_CLICK_ARG1_LABEL = "Nombre del Botón:" - - L.BS_MENU_HELP = "Muestra menú emergente de la unidad" -end - diff --git a/Clique/Localization.esMX.lua b/Clique/Localization.esMX.lua deleted file mode 100644 index c14f03c..0000000 --- a/Clique/Localization.esMX.lua +++ /dev/null @@ -1,110 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for esMX (Spanish) -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - --- This is the default locale. -if GetLocale() == "esMX" then - L.RANK = "Rango" - L.RANK_PATTERN = "Rango (%d+)" - L.CAST_FORMAT = "%s(Rango %s)" - L.RACIAL_PASSIVE = "Pasivo racial" - L.PASSIVE = SPELL_PASSIVE - L.CLICKSET_DEFAULT = "Por Defecto" - L.CLICKSET_HARMFUL = "Aciones de Daño" - L.CLICKSET_HELPFUL = "Acciones de Ayuda" - L.CLICKSET_OOC = "Fuera de Combate" - L.CLICKSET_BEARFORM = "Forma de Oso" - L.CLICKSET_CATFORM = "Forma Felina" - L.CLICKSET_AQUATICFORM = "Forma Acuática" - L.CLICKSET_TRAVELFORM = "Forma de Viaje" - L.CLICKSET_MOONKINFORM = "Forma de Moonkin" - L.CLICKSET_TREEOFLIFE = "Forma de Arbol de Vida" - L.CLICKSET_SHADOWFORM = "Forma de las Sombras" - L.CLICKSET_STEALTHED = "En Sigilo" - L.CLICKSET_BATTLESTANCE = "Actitud de Batalla" - L.CLICKSET_DEFENSIVESTANCE = "Actitud Defensiva" - L.CLICKSET_BERSERKERSTANCE = "Actitud Rabiosa" - - L.BEAR_FORM = "Forma de Oso" - L.DIRE_BEAR_FORM = "Forma de Oso Temible" - L.CAT_FORM = "Forma Felina" - L.AQUATIC_FORM = "Forma Acuática" - L.TRAVEL_FORM = "Forma de Viaje" - L.TREEOFLIFE = "Arbol de Vida" - L.MOONKIN_FORM = "Forma de Moonkin" - L.STEALTH = "Sigilo" - L.SHADOWFORM = "Forma de las Sombras" - L.BATTLESTANCE = "Actitud de Batalla" - L.DEFENSIVESTANCE = "Actitud Defensiva" - L.BERSERKERSTANCE = "Actitud Rabiosa" - - L.BINDING_NOT_DEFINED = "Binding no definido" - L.CANNOT_CHANGE_COMBAT = "No puede hacer cambios en combate. Los cambios se realizarán cuando salga de combate." - L.APPLY_QUEUE = "Fuera de combate. Aplicando todos los cambios pendientes." - L.PROFILE_CHANGED = "El perfil ha cambiado a '%s'." - L.PROFILE_DELETED = "El perfil '%s' ha sido borrado." - L.PROFILE_RESET = "Su perfil '%s' ha sido resetiado." - L.ACTION_ACTIONBAR = "Cambiar Barra de Acción" - L.ACTION_ACTION = "Botón de Acción" - L.ACTION_PET = "Botón de Acción Mascota" - L.ACTION_SPELL = "Lanzar Hechizo" - L.ACTION_ITEM = "Usar Item" - L.ACTION_MACRO = "Ejecutar macro por defecto" - L.ACTION_STOP = "Parar Lanzamiento" - L.ACTION_TARGET = "Apuntar Unidad" - L.ACTION_FOCUS = "Fijar Foco" - L.ACTION_ASSIST = "Asistir Unidad" - L.ACTION_CLICK = "Click Botón" - L.ACTION_MENU = "Mostrar Menú" - - L.HELP_TEXT = "Bienvenido a Clique. Para su operación básica, usted puede navegar por el Libro de Hechizos (Spellbook) y determinar que hechizo le gustaría asociar a un click específico. Luego haga click sobre el hechizo, con la combinación de teclas que a usted le guste (click o click + teclado). Por ejemplo, un sacerdote puede buscar en su libro de hechizos la \"Sanación Relámpago\" (Flash Heal) y realizar sobre este un Shift+LeftClick, lo cual asociará dicho hechizo a las combinación de teclas Shift+LeftClick." - L.CUSTOM_HELP = "Esta es la pantalla de edición por defecto de Clique. Desde aquí usted puede configurar cualquiera de las combinaciones que el UI tiene disponible para nosotros en respuesta a los clicks. Seleccione una acción base de la columna izquierda. Usted puede hacer clic en el botón de abajo para asociar las convinaciones de clicks + teclas que desee, y entonces proporcionar los argumentos requeridos (if any)." - - L.BS_ACTIONBAR_HELP = "Cambie la barra de acción. 'increment' lo moverá arriba de una pagina, 'decrement' hará lo contrario. Si usted proporciona un número, la barra de acción será girada a esa página. Usted puede especificar 1,3 para alternar entre las páginas 1 y 3." - - L.BS_ACTIONBAR_ARG1_LABEL = "Acción:" - - L.BS_ACTION_HELP = "Simular un click sobre un botón de acción. Especificar el número del botón de acción." - L.BS_ACTION_ARG1_LABEL = "Número de Botón:" - L.BS_ACTION_ARG2_LABEL = "(Opcional) Unidad:" - - L.BS_PET_HELP = "Simular un click sobre el botón de acción de mascotas. Especificar el número de botón." - L.BS_PET_ARG1_LABEL = "Número de Boton Mascota:" - L.BS_PET_ARG2_LABEL = "(Opcional) Unidad:" - - L.BS_SPELL_HELP = "Lanzar un hechizo desde el libro de hechizos. Toma un nombre de hechizo y opcionalmente una bolsa (bag) y ranura (slot) o un nombre de ítem para usar como objetivo del hechizo (i.e. Alimentar Mascota)" - L.BS_SPELL_ARG1_LABEL = "Nombre del Hechizo:" - L.BS_SPELL_ARG2_LABEL = "*Número de Rango/Bolsa:" - L.BS_SPELL_ARG3_LABEL = "*Número de Ranura (Slot):" - L.BS_SPELL_ARG4_LABEL = "*Nombre del Item:" - L.BS_SPELL_ARG5_LABEL = "(Opcional) Unidad:" - - L.BS_ITEM_HELP = "Use un item. Puede tomar una bolsa y ranura o un nombre de item." - L.BS_ITEM_ARG1_LABEL = "Número de Bolsa:" - L.BS_ITEM_ARG2_LABEL = "Número de Ranura (Slot):" - L.BS_ITEM_ARG3_LABEL = "Nombre del Item:" - L.BS_ITEM_ARG4_LABEL = "(Opcional) Unidad:" - - L.BS_MACRO_HELP = "Use una macro por defecto en un indice en particular" - L.BS_MACRO_ARG1_LABEL = "Indice Macro:" - L.BS_MACRO_ARG2_LABEL = "Texto Macro:" - - L.BS_STOP_HELP = "Parar de lanzar hechizos en curso" - - L.BS_TARGET_HELP = "Apuntar unidad" - L.BS_TARGET_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_FOCUS_HELP = "Fijar su \"foco\" en unidad" - L.BS_FOCUS_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_ASSIST_HELP = "Asistir la unidad" - L.BS_ASSIST_ARG1_LABEL = "(Opcional) Unidad:" - - L.BS_CLICK_HELP = "Simular click sobre un botón" - L.BS_CLICK_ARG1_LABEL = "Nombre del Botón:" - - L.BS_MENU_HELP = "Muestra menú emergente de la unidad" -end - diff --git a/Clique/Localization.frFR.lua b/Clique/Localization.frFR.lua deleted file mode 100644 index 9979c77..0000000 --- a/Clique/Localization.frFR.lua +++ /dev/null @@ -1,23 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for frFR. Any commented lines need to be updated -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - -if GetLocale() == "frFR" then - L.RANK = "Rang" - L.RANK_PATTERN = "Rang (%d+)" - L.CAST_FORMAT = "%s(Rang %d)" - --- L.RACIAL_PASSIVE = "Racial Passive" - --- L.CLICKSET_DEFAULT = "Default" --- L.CLICKSET_HARMFUL = "Harmful actions" --- L.CLICKSET_HELPFUL = "Helpful actions" --- L.CLICKSET_OOC = "Out of Combat" - --- L.BINDING_NOT_DEFINED = "Binding not defined" - --- L.HELP_TEXT = "Welcome to Clique. For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click. Then click on that spell with whatever click-binding you would like. For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick." --- L.CUSTOM_HELP = "This is the Clique custom edit screen. From here you can configure any of the combinations that the UI makes available to us in response to clicks. Select a base action from the left column. You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)." -end diff --git a/Clique/Localization.koKR.lua b/Clique/Localization.koKR.lua deleted file mode 100644 index e8b63ec..0000000 --- a/Clique/Localization.koKR.lua +++ /dev/null @@ -1,23 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for koKR. Any commented lines need to be updated -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - -if GetLocale() == "koKR" then - L.RANK = "레벨" - L.RANK_PATTERN = "(%d+) 레벨" - L.CAST_FORMAT = "%s(%d 레벨)" - --- L.RACIAL_PASSIVE = "Racial Passive" - --- L.CLICKSET_DEFAULT = "Default" --- L.CLICKSET_HARMFUL = "Harmful actions" --- L.CLICKSET_HELPFUL = "Helpful actions" --- L.CLICKSET_OOC = "Out of Combat" - --- L.BINDING_NOT_DEFINED = "Binding not defined" - --- L.HELP_TEXT = "Welcome to Clique. For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click. Then click on that spell with whatever click-binding you would like. For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick." --- L.CUSTOM_HELP = "This is the Clique custom edit screen. From here you can configure any of the combinations that the UI makes available to us in response to clicks. Select a base action from the left column. You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)." -end diff --git a/Clique/Localization.ruRU.lua b/Clique/Localization.ruRU.lua deleted file mode 100644 index 313c4c5..0000000 --- a/Clique/Localization.ruRU.lua +++ /dev/null @@ -1,112 +0,0 @@ ---[[--------------------------------------------------------------------------------- - Localisation for Russian - Перевод на русский от Сини -----------------------------------------------------------------------------------]] - -local L = Clique.Locals - --- This is the default locale. -if GetLocale() == "ruRU" then - L.RANK = "Уровень" - L.RANK_PATTERN = "Уровень (%d+)" - L.CAST_FORMAT = "%s(Уровень %s)" - - L.RACIAL_PASSIVE = "Пассивный расовый навык" - L.PASSIVE = SPELL_PASSIVE - - L.CLICKSET_DEFAULT = "Стандартный" - L.CLICKSET_HARMFUL = "Вредящие действия" - L.CLICKSET_HELPFUL = "Помогающие действия" - L.CLICKSET_OOC = "Вне боя" - L.CLICKSET_BEARFORM = "Облик медведя" - L.CLICKSET_CATFORM = "Облик кошки" - L.CLICKSET_AQUATICFORM = "Водный облик" - L.CLICKSET_TRAVELFORM = "Походный облик" - L.CLICKSET_MOONKINFORM = "Облик лунного совуха" - L.CLICKSET_TREEOFLIFE = "Древо Жизни" - L.CLICKSET_SHADOWFORM = "Облик Тьмы" - L.CLICKSET_STEALTHED = "Незаметность" - L.CLICKSET_BATTLESTANCE = "Боевая стойка" - L.CLICKSET_DEFENSIVESTANCE = "Оборонительная стойка" - L.CLICKSET_BERSERKERSTANCE = "Стойка берсерка" - - L.BEAR_FORM = "Облик медведя" - L.DIRE_BEAR_FORM = "Облик лютого медведя" - L.CAT_FORM = "Облик кошки" - L.AQUATIC_FORM = "Водный облик" - L.TRAVEL_FORM = "Походный облик" - L.TREEOFLIFE = "Древо Жизни" - L.MOONKIN_FORM = "Облик лунного совуха" - L.STEALTH = "Незаметность" - L.SHADOWFORM = "Облик Тьмы" - L.BATTLESTANCE = "Боевая стойка" - L.DEFENSIVESTANCE = "Оборонительная стойка" - L.BERSERKERSTANCE = "Стойка берсерка" - - L.BINDING_NOT_DEFINED = "Действие не определено" - L.CANNOT_CHANGE_COMBAT = "Во время боя применить изменения невозможно. Они будут отложены, пока Вы не выйдете из боя." - L.APPLY_QUEUE = "Бой окончен. Применяю все запланированные изменения." - L.PROFILE_CHANGED = "Профиль сменен на '%s'." - L.PROFILE_DELETED = "Профить '%s' был удален." - L.PROFILE_RESET = "Ваш профиль '%s' был сброшен." - - L.ACTION_ACTIONBAR = "Сменить панель команд" - L.ACTION_ACTION = "Кнопка действия" - L.ACTION_PET = "Кнопка действия питомца" - L.ACTION_SPELL = "Колдовать заклинание" - L.ACTION_ITEM = "Использовать предмет" - L.ACTION_MACRO = "Выполнить макрос" - L.ACTION_STOP = "Прекратить колдование" - L.ACTION_TARGET = "Выбрать цель" - L.ACTION_FOCUS = "Установить фокус" - L.ACTION_ASSIST = "Помочь" - L.ACTION_CLICK = "Щелкнуть кнопкой" - L.ACTION_MENU = "Показать меню" - - L.HELP_TEXT = "Добро пожаловать в Clique. Для начала простого использования, откройте книгу заклинаний и выберите заклинание, которое Вы хотели бы назначить на определенный щелчок мышью. Затем щелкните по заклинанию именно таким щелчком, какой Вы выбрали. Например, зайдите на страницу с заклинанием \"Быстрое исцеление\" и щелкните по нему, удерживая Shift, чтобы закрепить \"Shift+Щелчок\" за этим заклинанием." - L.CUSTOM_HELP = "Это экран ручной настройки Clique. Отсюда Вы можете настроить любую комбинацию, которая предоставляется интерфейсом Warcraft как цель для щелчков мышью. Выберите базовое действие из левой колонки. Затем Вы сможете щелкнуть по кнопке внизу, чтобы установить любое нужно действие, и ввести требуемые аргументы (если необходимо)." - - L.BS_ACTIONBAR_HELP = "Сменить панель команд. 'increment' сдвинет на одну панель вверх, 'decrement' - в обратную сторону. Если Вы введете номер, то будет выбираться соответсвующая панель команд. Вы можете ввести 1,3 чтобы переключаться между 1-ой и 3-ей панелями." - L.BS_ACTIONBAR_ARG1_LABEL = "Действие:" - - L.BS_ACTION_HELP = "Эмулировать щелчок мышью по кнопке действия. Введите номер кнопки действия." - L.BS_ACTION_ARG1_LABEL = "Номер кнопки:" - L.BS_ACTION_ARG2_LABEL = "(необязательно) Цель:" - - L.BS_PET_HELP = "Эмулировать щелчок мышью по кнопке действия питомца. Введите номер кнопки." - L.BS_PET_ARG1_LABEL = "Номер кнопки питомца:" - L.BS_PET_ARG2_LABEL = "(необязательно) Цель:" - - L.BS_SPELL_HELP = "Колдовать заклинание из книги заклинаний. В качестве аргументов принимается название заклинания, необязательно - номер сумки и позиция внутри сумки, или название предмета как цели заклинания (например \"Накормить питомца\")." - L.BS_SPELL_ARG1_LABEL = "Название заклинания:" - L.BS_SPELL_ARG2_LABEL = "*Уровень/Номер сумки:" - L.BS_SPELL_ARG3_LABEL = "*Номер позиции:" - L.BS_SPELL_ARG4_LABEL = "*Название предмета:" - L.BS_SPELL_ARG5_LABEL = "(необязательно) Цель:" - - L.BS_ITEM_HELP = "Использовать предмет. Принимается номер сумки и позиции внутри сумки, или название предмета." - L.BS_ITEM_ARG1_LABEL = "Номер сумки:" - L.BS_ITEM_ARG2_LABEL = "Номер позиции:" - L.BS_ITEM_ARG3_LABEL = "Название предмета:" - L.BS_ITEM_ARG4_LABEL = "(необязательно) Цель:" - - L.BS_MACRO_HELP = "Использовать макрос из указанного номера ячейки." - L.BS_MACRO_ARG1_LABEL = "Номер ячейки макроса:" - L.BS_MACRO_ARG2_LABEL = "Текст макроса:" - - L.BS_STOP_HELP = "Прекратить колдование текущего заклинания." - - L.BS_TARGET_HELP = "Выбрать цель." - L.BS_TARGET_ARG1_LABEL = "(необязательно) Цель:" - - L.BS_FOCUS_HELP = "Установаить \"фокус\" на цели." - L.BS_FOCUS_ARG1_LABEL = "(необязательно) Цель:" - - L.BS_ASSIST_HELP = "Помочь цели." - L.BS_ASSIST_ARG1_LABEL = "(необязательно) Цель:" - - L.BS_CLICK_HELP = "Эмулировать щелчок по кнопке." - L.BS_CLICK_ARG1_LABEL = "Название кнопки:" - - L.BS_MENU_HELP = "Показать всплывающее меню цели." -end diff --git a/Clique/Localization.zhCN.lua b/Clique/Localization.zhCN.lua deleted file mode 100644 index 6e1afe6..0000000 --- a/Clique/Localization.zhCN.lua +++ /dev/null @@ -1,111 +0,0 @@ -------------------------------------------------------------------------------- --- Localization -- --- Simple Chinese Translated by 昏睡墨鱼&Costa -- -------------------------------------------------------------------------------- - -local L = Clique.Locals - -if GetLocale() == "zhCN" then - L.RANK = "等级" - L.RANK_PATTERN = "等级 (%d+)" - L.CAST_FORMAT = "%s(等级 %s)" - - L.RACIAL_PASSIVE = "种族被动技能" - L.PASSIVE = SPELL_PASSIVE - - L.CLICKSET_DEFAULT = "默认" - L.CLICKSET_HARMFUL = "对敌方动作" - L.CLICKSET_HELPFUL = "对友方动作" - L.CLICKSET_OOC = "非战斗中动作" - L.CLICKSET_BEARFORM = "熊形态" - L.CLICKSET_CATFORM = "猎豹形态" - L.CLICKSET_AQUATICFORM = "水栖形态" - L.CLICKSET_TRAVELFORM = "旅行形态" - L.CLICKSET_MOONKINFORM = "枭兽形态" - L.CLICKSET_TREEOFLIFE = "树形" - L.CLICKSET_SHADOWFORM = "暗影形态" - L.CLICKSET_STEALTHED = "潜行状态" - L.CLICKSET_BATTLESTANCE = "战斗姿态" - L.CLICKSET_DEFENSIVESTANCE = "防御姿态" - L.CLICKSET_BERSERKERSTANCE = "狂暴姿态" - - L.BEAR_FORM = "熊形态" - L.DIRE_BEAR_FORM = "巨熊形态" - L.CAT_FORM = "猎豹形态" - L.AQUATIC_FORM = "水栖形态" - L.TRAVEL_FORM = "旅行形态" - L.TREEOFLIFE = "树形" - L.MOONKIN_FORM = "枭兽形态" - L.STEALTH = "潜行状态" - L.SHADOWFORM = "暗影形态" - L.BATTLESTANCE = "战斗姿态" - L.DEFENSIVESTANCE = "防御姿态" - L.BERSERKERSTANCE = "狂暴姿态" - - L.BINDING_NOT_DEFINED = "未定义绑定" - L.CANNOT_CHANGE_COMBAT = "战斗状态中无法改变. 这些改变被推迟到脱离战斗状态后." - L.APPLY_QUEUE = "脱离战斗状态. 进行所有预定了的改变." - L.PROFILE_CHANGED = "已经切换到设置 '%s'." - L.PROFILE_DELETED = "你的设置 '%s' 已经被删除." - L.PROFILE_RESET = "你的设置 '%s' 已经被重值." - - L.ACTION_ACTIONBAR = "切换动作条" - L.ACTION_ACTION = "动作按钮" - L.ACTION_PET = "宠物动作按钮" - L.ACTION_SPELL = "释放法术" - L.ACTION_ITEM = "使用物品" - L.ACTION_MACRO = "运行自定义宏" - L.ACTION_STOP = "终止施法" - L.ACTION_TARGET = "目标单位" - L.ACTION_FOCUS = "指定目标单位" - L.ACTION_ASSIST = "协助某单位" - L.ACTION_CLICK = "点击按钮" - L.ACTION_MENU = "显示菜单" - - L.HELP_TEXT = "欢迎使用Clique. 最基本的操作,你可以浏览法术书然后给喜欢的法术绑定按键.例如,你可以找到\快速治疗\ 然后按住shift点左键,这样shift+左键就被设置为释放快速治疗了." - L.CUSTOM_HELP = "这是Clique自定义窗口.在这里,你可以设置任何点击影射到界面允许的组合按键. 从左侧选择一个基本的动作,然后可以可以点击下边的按钮来映射到任何你希望的操作. " - - L.BS_ACTIONBAR_HELP = "切换动作条. 'increment' 会向后翻一页, 'decrement' 则相反. 如果你提供了一个编号, 动作条就会翻到那一页.你可以定义1,3来在1和3页之间翻动." - L.BS_ACTIONBAR_ARG1_LABEL = "动作条:" - - L.BS_ACTION_HELP = "将一种点击影射到某动作条的按钮上. 请指定动作条的编号." - L.BS_ACTION_ARG1_LABEL = "按钮编号:" - L.BS_ACTION_ARG2_LABEL = "(可选) 单位:" - - L.BS_PET_HELP = "将一种点击影射到宠物动作条的按钮上. 请指定按钮编号." - L.BS_PET_ARG1_LABEL = "宠物按钮编号:" - L.BS_PET_ARG2_LABEL = "(可选) 单位:" - - L.BS_SPELL_HELP = "从法术书上选择施法. 设定法术名称, 以及包裹与物品槽编号或者物品名称(可选), 来对目标进行施法 (例如喂养宠物)" - L.BS_SPELL_ARG1_LABEL = "法术名称:" - L.BS_SPELL_ARG2_LABEL = "*包裹编号:" - L.BS_SPELL_ARG3_LABEL = "*物品槽编号:" - L.BS_SPELL_ARG4_LABEL = "*物品名称:" - L.BS_SPELL_ARG5_LABEL = "(可选) 单位:" - - L.BS_ITEM_HELP = "使用一个物品. 可以通过包裹与物品槽的编号,或者物品名称来指定." - L.BS_ITEM_ARG1_LABEL = "包裹编号:" - L.BS_ITEM_ARG2_LABEL = "物品槽编号:" - L.BS_ITEM_ARG3_LABEL = "物品名称:" - L.BS_ITEM_ARG4_LABEL = "(可选) 单位:" - - L.BS_MACRO_HELP = "使用给定索引的自定义宏" - L.BS_MACRO_ARG1_LABEL = "宏索引:" - L.BS_MACRO_ARG2_LABEL = "宏内容:" - - L.BS_STOP_HELP = "中断当前施法" - - L.BS_TARGET_HELP = "将单位设定为目标" - L.BS_TARGET_ARG1_LABEL = "(可选) 单位:" - - L.BS_FOCUS_HELP = "设定你的 \"目标\" 单位" - L.BS_FOCUS_ARG1_LABEL = "(可选) 单位:" - - L.BS_ASSIST_HELP = "协助某单位" - L.BS_ASSIST_ARG1_LABEL = "(可选) 单位:" - - L.BS_CLICK_HELP = "使用按钮模拟点击" - L.BS_CLICK_ARG1_LABEL = "按钮名称:" - - L.BS_MENU_HELP = "显示单位的弹出菜单" -end \ No newline at end of file diff --git a/Clique/Localization.zhTW.lua b/Clique/Localization.zhTW.lua deleted file mode 100644 index 4485fa1..0000000 --- a/Clique/Localization.zhTW.lua +++ /dev/null @@ -1,111 +0,0 @@ -------------------------------------------------------------------------------- --- Localization -- --- Simple Chinese Translated by 昏睡墨魚&Costa -- -------------------------------------------------------------------------------- - -local L = Clique.Locals - -if GetLocale() == "zhTW" then - L.RANK = "等級" - L.RANK_PATTERN = "等級 (%d+)" - L.CAST_FORMAT = "%s(等級 %s)" - - L.RACIAL_PASSIVE = "種族被動技能" - L.PASSIVE = SPELL_PASSIVE - - L.CLICKSET_DEFAULT = "默認" - L.CLICKSET_HARMFUL = "對敵方動作" - L.CLICKSET_HELPFUL = "對友方動作" - L.CLICKSET_OOC = "非戰鬥中動作" - L.CLICKSET_BEARFORM = "熊形態" - L.CLICKSET_CATFORM = "獵豹形態" - L.CLICKSET_AQUATICFORM = "水棲形態" - L.CLICKSET_TRAVELFORM = "旅行形態" - L.CLICKSET_MOONKINFORM = "梟獸形態" - L.CLICKSET_TREEOFLIFE = "樹形" - L.CLICKSET_SHADOWFORM = "暗影形態" - L.CLICKSET_STEALTHED = "潛行狀態" - L.CLICKSET_BATTLESTANCE = "戰鬥姿態" - L.CLICKSET_DEFENSIVESTANCE = "防禦姿態" - L.CLICKSET_BERSERKERSTANCE = "狂暴姿態" - - L.BEAR_FORM = "熊形態" - L.DIRE_BEAR_FORM = "巨熊形態" - L.CAT_FORM = "獵豹形態" - L.AQUATIC_FORM = "水棲形態" - L.TRAVEL_FORM = "旅行形態" - L.TREEOFLIFE = "樹形" - L.MOONKIN_FORM = "梟獸形態" - L.STEALTH = "潛行狀態" - L.SHADOWFORM = "暗影形態" - L.BATTLESTANCE = "戰鬥姿態" - L.DEFENSIVESTANCE = "防禦姿態" - L.BERSERKERSTANCE = "狂暴姿態" - - L.BINDING_NOT_DEFINED = "未定義綁定" - L.CANNOT_CHANGE_COMBAT = "戰鬥狀態中無法改變. 這些改變被推遲到脫離戰鬥狀態後." - L.APPLY_QUEUE = "脫離戰鬥狀態. 進行所有預定了的改變." - L.PROFILE_CHANGED = "已經切換到設置 '%s'." - L.PROFILE_DELETED = "你的設置 '%s' 已經被刪除." - L.PROFILE_RESET = "你的設置 '%s' 已經被重值." - - L.ACTION_ACTIONBAR = "切換動作條" - L.ACTION_ACTION = "動作按鈕" - L.ACTION_PET = "寵物動作按鈕" - L.ACTION_SPELL = "釋放法術" - L.ACTION_ITEM = "使用物品" - L.ACTION_MACRO = "運行自定義宏" - L.ACTION_STOP = "終止施法" - L.ACTION_TARGET = "目標單位" - L.ACTION_FOCUS = "指定目標單位" - L.ACTION_ASSIST = "協助某單位" - L.ACTION_CLICK = "點擊按鈕" - L.ACTION_MENU = "顯示功能表" - - L.HELP_TEXT = "歡迎使用Clique. 最基本的操作,你可以流覽法術書然後給喜歡的法術綁定按鍵.例如,你可以找到\快速治療\ 然後按住shift點左鍵,這樣shift+左鍵就被設置為釋放快速治療了." - L.CUSTOM_HELP = "這是Clique自定義視窗.在這裏,你可以設置任何點擊影射到介面允許的組合按鍵. 從左側選擇一個基本的動作,然後可以可以點擊下邊的按鈕來映射到任何你希望的操作. " - - L.BS_ACTIONBAR_HELP = "切換動作條. 'increment' 會向後翻一頁, 'decrement' 則相反. 如果你提供了一個編號, 動作條就會翻到那一頁.你可以定義1,3來在1和3頁之間翻動." - L.BS_ACTIONBAR_ARG1_LABEL = "動作條:" - - L.BS_ACTION_HELP = "將一種點擊影射到某動作條的按鈕上. 請指定動作條的編號." - L.BS_ACTION_ARG1_LABEL = "按鈕編號:" - L.BS_ACTION_ARG2_LABEL = "(可選) 單位:" - - L.BS_PET_HELP = "將一種點擊影射到寵物動作條的按鈕上. 請指定按鈕編號." - L.BS_PET_ARG1_LABEL = "寵物按鈕編號:" - L.BS_PET_ARG2_LABEL = "(可選) 單位:" - - L.BS_SPELL_HELP = "從法術書上選擇施法. 設定法術名稱, 以及包裹與物品槽編號或者物品名稱(可選), 來對目標進行施法 (例如餵養寵物)" - L.BS_SPELL_ARG1_LABEL = "法術名稱:" - L.BS_SPELL_ARG2_LABEL = "*包裹編號:" - L.BS_SPELL_ARG3_LABEL = "*物品槽編號:" - L.BS_SPELL_ARG4_LABEL = "*物品名稱:" - L.BS_SPELL_ARG5_LABEL = "(可選) 單位:" - - L.BS_ITEM_HELP = "使用一個物品. 可以通過包裹與物品槽的編號,或者物品名稱來指定." - L.BS_ITEM_ARG1_LABEL = "包裹編號:" - L.BS_ITEM_ARG2_LABEL = "物品槽編號:" - L.BS_ITEM_ARG3_LABEL = "物品名稱:" - L.BS_ITEM_ARG4_LABEL = "(可選) 單位:" - - L.BS_MACRO_HELP = "使用給定索引的自定義巨集" - L.BS_MACRO_ARG1_LABEL = "巨集索引:" - L.BS_MACRO_ARG2_LABEL = "巨集內容:" - - L.BS_STOP_HELP = "中斷當前施法" - - L.BS_TARGET_HELP = "將單位設定為目標" - L.BS_TARGET_ARG1_LABEL = "(可選) 單位:" - - L.BS_FOCUS_HELP = "設定你的 \"目標\" 單位" - L.BS_FOCUS_ARG1_LABEL = "(可選) 單位:" - - L.BS_ASSIST_HELP = "協助某單位" - L.BS_ASSIST_ARG1_LABEL = "(可選) 單位:" - - L.BS_CLICK_HELP = "使用按鈕類比點擊" - L.BS_CLICK_ARG1_LABEL = "按鈕名稱:" - - L.BS_MENU_HELP = "顯示單位的彈出功能表" -end diff --git a/Clique/Utils.lua b/Clique/Utils.lua new file mode 100644 index 0000000..35b35e0 --- /dev/null +++ b/Clique/Utils.lua @@ -0,0 +1,296 @@ +--[[------------------------------------------------------------------------- +-- Utils.lua +-- +-- This file contains a series of general utility functions that could be +-- used throughout the addon, although in practice they are mostly used in +-- the GUI. +-- +-- Events registered: +-- None +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +local strconcat = strconcat +---@diagnostic disable-next-line: undefined-field +local strsplit = string.split + +-- Returns the prefix string for the current keyboard state. +-- +-- Arguments: +-- split - Whether or not to split the modifier keys into left and right components + +function addon:GetPrefixString(split) + local shift, lshift, rshift = IsShiftKeyDown(), IsLeftShiftKeyDown(), IsRightShiftKeyDown() + local ctrl, lctrl, rctrl = IsControlKeyDown(), IsLeftControlKeyDown(), IsRightControlKeyDown() + local alt, lalt, ralt = IsAltKeyDown(), IsLeftAltKeyDown() + IsRightAltKeyDown() + + if not extended then + shift = shift or lshift or rshift + ctrl = ctrl or lctrl or rctrl + alt = alt or lalt or ralt + + lshift, rshift = false, false + lctrl, rctrl = false, false + lalt, ralt = false, false + end + + local prefix = "" + if shift then prefix = ((lshift and "LSHIFT-") or (rshift and "RSHIFT-") or "SHIFT-") .. prefix end + if ctrl then prefix = ((lctrl and "LCTRL-") or (rctrl and "RCTRL-") or "CTRL-") .. prefix end + if alt then prefix = ((lalt and "LALT-") or (ralt and "RALT-") or "ALT-") .. prefix end + + return prefix +end + +-- This function can return a substring of a UTF-8 string, properly handling +-- UTF-8 codepoints. Rather than taking a start index and optionally an end +-- index, it takes the string, the start index and the number of characters +-- to select from the string. +-- +-- UTF-8 Reference: +-- 0xxxxxx - ASCII character +-- 110yyyxx - 2 byte UTF codepoint +-- 1110yyyy - 3 byte UTF codepoint +-- 11110zzz - 4 byte UTF codepoint + +local function utf8sub(str, start, numChars) + local currentIndex = start + while numChars > 0 and currentIndex <= #str do + local char = string.byte(str, currentIndex) + if char >= 240 then + currentIndex = currentIndex + 4 + elseif char >= 225 then + currentIndex = currentIndex + 3 + elseif char >= 192 then + currentIndex = currentIndex + 2 + else + currentIndex = currentIndex + 1 + end + numChars = numChars - 1 + end + return str:sub(start, currentIndex - 1) +end + +local convertMap = setmetatable({ + LSHIFT = L["LShift"], + RSHIFT = L["RShift"], + SHIFT = L["Shift"], + LCTRL = L["LCtrl"], + RCTRL = L["RCtrl"], + CTRL = L["Ctrl"], + LALT = L["LAlt"], + RALT = L["RAlt"], + ALT = L["Alt"], + BUTTON1 = L["LeftButton"], + BUTTON2 = L["RightButton"], + BUTTON3 = L["MiddleButton"], + MOUSEWHEELUP = L["MousewheelUp"], + MOUSEWHEELDOWN = L["MousewheelDown"] +}, { + __index = function(t, k, v) + if k:match("^BUTTON(%d+)$") then + return k:gsub("^BUTTON(%d+)$", "Button%1") + else + if utf8sub(k, 1, 1) ~= k:sub(1, 1) then + -- If the first character is a multi-byte UTF-8 character + return k + else + -- Make the first character upper-case, lower the rest + return tostring(k:sub(1, 1):upper()) .. tostring(k:sub(2, -1):lower()) + end + end + end +}) + +local function convert(item, ...) + if not item then + return "" + else + local mapItem = convertMap[item] + item = mapItem and mapItem or item + + if select("#", ...) > 0 then + return item, "-", convert(...) + else + return item, convert(...) + end + end +end + +function addon:GetBindingIcon(binding) + if type(binding) ~= "table" or not binding.type then return "Interface\\Icons\\INV_Misc_QuestionMark" end + + local btype = binding.type + if btype == "menu" then + return "Interface\\Icons\\ability_hunter_snipershot" + elseif btype == "target" then + return "Interface\\Icons\\ability_vanish" + else + return binding.icon or "Interface\\Icons\\INV_Misc_QuestionMark" + end +end + +function addon:GetBindingKeyComboText(binding) + if type(binding) == "table" and binding.key then + return strconcat(convert(strsplit("-", binding.key))) + elseif type(binding) == "string" then + return strconcat(convert(strsplit("-", binding))) + else + return L["Unknown"] + end +end + +function addon:SpellTextWithSubName(binding) + if binding.spellSubName then + return string.format("%s(%s)", binding.spell, binding.spellSubName) + else + return binding.spell + end +end + +function addon:GetBindingActionText(btype, binding) + if btype == "menu" then + return L["Show unit menu"] + elseif btype == "target" then + return L["Target clicked unit"] + elseif btype == "spell" then + return L["Cast %s"]:format(addon:SpellTextWithSubName(binding)) + elseif btype == "macro" and type(binding) == "table" then + return L["Run macro '%s'"]:format(tostring(binding.macrotext)) + elseif btype == "macro" then + return L["Run macro"]:format(tostring(binding.macrotext)) + else + return L["Unknown binding type '%s'"]:format(tostring(btype)) + end +end + +function addon:GetBindingKey(binding) + if type(binding) ~= "table" or not binding.key then return "UNKNOWN" end + + local key = binding.key:match("[^%-]+$") + return key +end + +local binMap = { + ALT = 1, + LALT = 2, + RALT = 3, + CTRL = 4, + LCTRL = 5, + LCTRL = 6, + SHIFT = 7, + LSHIFT = 8, + RSHIFT = 9 +} + +function addon:GetBinaryBindingKey(binding) + if type(binding) ~= "table" or not binding.key then return "000000000" end + + local ret = {"0", "0", "0", "0", "0", "0", "0", "0", "0"} + local splits = {strsplit("-", binding.key)} + for idx, modifier in ipairs(splits) do + local bit = binMap[modifier] + if bit then + ret[bit] = "1" + else + ret[10] = modifier + end + end + return table.concat(ret) +end + +local invalidKeys = { + ["UNKNOWN"] = true, + ["LSHIFT"] = true, + ["RSHIFT"] = true, + ["LCTRL"] = true, + ["RCTRL"] = true, + ["LALT"] = true, + ["RALT"] = true, + ["ESCAPE"] = true +} + +function addon:GetCapturedKey(key) + -- We can't bind modifiers or invalid keys + if invalidKeys[key] then return end + + -- Remap any mouse buttons + if key == "LeftButton" then + key = "BUTTON1" + elseif key == "RightButton" then + key = "BUTTON2" + elseif key == "MiddleButton" then + key = "BUTTON3" + elseif key == "-" then + key = "DASH" + elseif key == "\\" then + key = "BACKSLASH" + elseif key == "\"" then + key = "DOUBLEQUOTE" + else + local buttonNum = key:match("Button(%d+)") + if buttonNum and tonumber(buttonNum) <= 31 then key = "BUTTON" .. buttonNum end + end + + -- TODO: Support NOT splitting the modifier keys + local prefix = addon:GetPrefixString(true) + return tostring(prefix) .. tostring(key) +end + +function addon:GetBindingInfoText(binding) + if type(binding) ~= "table" or not binding.sets then return L["This binding is invalid, please delete"] end + + local sets = binding.sets + if not sets then + return "" + elseif not next(sets) then + -- No bindings set, so display a message + return L["This binding is DISABLED"] + else + local bits = {} + for k, v in pairs(sets) do table.insert(bits, k) end + table.sort(bits) + return table.concat(bits, ", ") + end +end + +function addon:ConvertSpecialKeys(binding) + if type(binding) ~= "table" or not binding.key then return "UNKNOWN" end + + local mods, key = binding.key:match("^(.-)([^%-]+)$") + if key == "DASH" then + key = "-" + elseif key == "BACKSLASH" then + key = "\\" + elseif key == "DOUBLEQUOTE" then + key = "\"" + end + + return tostring(mods) .. tostring(key) +end + +function addon:GetBindingPrefixSuffix(binding, global) + if type(binding) ~= "table" or not binding.key then return "UNKNOWN", "UNKNOWN" end + + local prefix, suffix = binding.key:match("^(.-)([^%-]+)$") + if prefix:sub(-1, -1) == "-" then prefix = prefix:sub(1, -2) end + + prefix = prefix:lower() + + local prefixKey = prefix:gsub("[%A]", "") + local buttonNum = suffix:match("^BUTTON(%d+)$") + + if buttonNum and global then + suffix = "cliquemouse" .. tostring(prefixKey) .. tostring(buttonNum) + prefix = "" + elseif buttonNum then + suffix = buttonNum + else + suffix = "cliquebutton" .. tostring(prefixKey) .. tostring(suffix) + prefix = "" + end + + return prefix, suffix +end diff --git a/Clique/config/BindConfig.lua b/Clique/config/BindConfig.lua new file mode 100644 index 0000000..fab9cfd --- /dev/null +++ b/Clique/config/BindConfig.lua @@ -0,0 +1,775 @@ +--[[------------------------------------------------------------------------- +-- BindConfig.lua +-- +-- This file contains the definitions of the binding configuration panel. +-- +-- Events registered: +-- None +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +local MAX_ROWS = 12 + +function CliqueConfig:ShowWithSpellBook() + self:ClearAllPoints() + self:SetParent(AscensionSpellbookFrame) + self:SetPoint("LEFT", AscensionSpellbookFrame, "RIGHT", 55, 0) + self:Show() +end + +function CliqueConfig:OnShow() + if not self.initialized then + self:SetupGUI() + self:HijackSpellbook() + self.initialized = true + end + + -- Hide the alertTab if the spellbook isn't shown + if AscensionSpellbookFrameContentSpellsSpellButton1:IsVisible() then + self.bindAlert:Show() + else + self.bindAlert:Hide() + end + + CliqueSpellTab:SetChecked(true) + self:UpdateList() + self:UpdateProfileDisplay() + UIDropDownMenu_Refresh(self.page1.profileDropdown) + self:EnableSpellbookButtons() + self:UpdateBindSpellButtonState() + self:UpdateAlert() +end + +function CliqueConfig:OnHide() + self:ClearAllPoints() + self:SetParent(UIParent) + HideUIPanel(self) + CliqueSpellTab:SetChecked(false) + self:UpdateAlert() +end + +function CliqueConfig:SetupGUI() + self.rows = {} + for i = 1, MAX_ROWS do self.rows[i] = CreateFrame("Button", "CliqueRow" .. i, self.page1, "CliqueRowTemplate") end + + self.rows[1]:ClearAllPoints() + self.rows[1]:SetPoint("TOPLEFT", "CliqueConfigPage1Column1", "BOTTOMLEFT", 0, -3) + self.rows[1]:SetPoint("RIGHT", CliqueConfigPage1Column2, "RIGHT", 0, 0) + + for i = 2, MAX_ROWS do + self.rows[i]:ClearAllPoints() + self.rows[i]:SetPoint("TOPLEFT", self.rows[i - 1], "BOTTOMLEFT") + self.rows[i]:SetPoint("RIGHT", CliqueConfigPage1Column2, "RIGHT", 0, 0) + end + + _G[self:GetName() .. "TitleText"]:SetText(L["Clique Binding Configuration"]) + + self.dialog = _G["CliqueDialog"] + self.dialog.title = _G["CliqueDialogTitleText"] + self.dialog:SetUserPlaced(false) + self.dialog:ClearAllPoints() + self.dialog:SetPoint("CENTER", self, "CENTER", 30, 0) + + self.dialog.title:SetText(L["Set binding"]) + self.dialog.button_accept:SetText(L["Accept"]) + + self.dialog.button_binding:SetText(L["Set binding"]) + local desc = + L["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] + self.dialog.desc:SetText(desc) + + self.alert = _G["CliqueTabAlert"] + + self.bindAlert.text:SetText(L["You are in Clique binding mode"]) + + self.close = _G[self:GetName() .. "CloseButton"] + self.close:SetScript("OnClick", function() HideUIPanel(CliqueConfig) end) + + self.page1.column1:SetText(L["Action"]) + self.page1.column2:SetText(L["Binding"]) + + -- Set columns up to handle sorting + self.page1.column1.sortType = "name" + self.page1.column2.sortType = "key" + self.page1.sortType = self.page1.column2.sortType + + self.page2.button_binding:SetText(L["Set binding"]) + local desc = + L["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] + + self.page2.desc:SetText(desc) + self.page2.editbox = CliqueScrollFrameEditBox + + -- Create profile UI elements programmatically + self:CreateProfileUI() + + -- Create page 2 buttons programmatically + self:CreatePage2Buttons() + + self.page1:Show() +end + +function CliqueConfig:CreateProfileUI() + -- Create profile label with support for long names and multiple lines + self.page1.profileLabel = self.page1:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + self.page1.profileLabel:SetJustifyH("LEFT") + self.page1.profileLabel:SetJustifyV("TOP") + self.page1.profileLabel:SetSize(265, 24) -- Increased height for two lines + self.page1.profileLabel:SetPoint("TOPLEFT", self, "TOPLEFT", 60, -30) + self.page1.profileLabel:SetWordWrap(true) -- Enable word wrapping + self.page1.profileLabel:SetText(L["Current Profile: "]) + + -- Create profile dropdown in bottom left corner + self.page1.profileDropdown = CreateFrame("Frame", "CliqueConfigProfileDropdown", self.page1, "UIDropDownMenuTemplate") + self.page1.profileDropdown:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", -15, -2) + self.page1.profileDropdown:SetFrameLevel(self.page1:GetFrameLevel() + 10) -- Ensure dropdown is on top + UIDropDownMenu_SetWidth(self.page1.profileDropdown, 100) + UIDropDownMenu_Initialize(self.page1.profileDropdown, function(dropdown, level) self:ProfileDropdown_Initialize(dropdown, level) end) + + -- Create buttons programmatically since XML-defined ones weren't showing + -- Bind spell button + self.page1.button_spell = CreateFrame("Button", nil, self.page1, "UIPanelButtonTemplate") + self.page1.button_spell:SetSize(70, 22) + self.page1.button_spell:SetPoint("TOPLEFT", self.page1.profileDropdown, "TOPRIGHT", -15, -2) + self.page1.button_spell:SetText(L["Bind spell"]) + self.page1.button_spell:SetScript("OnClick", function(button) self:Button_OnClick(button) end) + self.page1.button_spell:Show() + + -- Bind other button + self.page1.button_other = CreateFrame("Button", nil, self.page1, "UIPanelButtonTemplate") + self.page1.button_other:SetSize(70, 22) + self.page1.button_other:SetPoint("LEFT", self.page1.button_spell, "RIGHT", 0, 0) + self.page1.button_other:SetText(L["Bind other"]) + self.page1.button_other:SetScript("OnClick", function(button) self:Button_OnClick(button) end) + self.page1.button_other:Show() + + -- Options button + self.page1.button_options = CreateFrame("Button", nil, self.page1, "UIPanelButtonTemplate") + self.page1.button_options:SetSize(70, 22) + self.page1.button_options:SetPoint("LEFT", self.page1.button_other, "RIGHT", 0, 0) + self.page1.button_options:SetText(L["Options"]) + self.page1.button_options:SetScript("OnClick", function(button) self:Button_OnClick(button) end) + self.page1.button_options:Show() +end + +function CliqueConfig:CreatePage2Buttons() + -- Set binding button is working fine from XML, only create Save and Cancel buttons + + -- Create Save button in bottom left corner + self.page2.button_save = CreateFrame("Button", nil, self.page2, "UIPanelButtonTemplate") + self.page2.button_save:SetSize(90, 22) + self.page2.button_save:SetPoint("BOTTOMLEFT", self.page2, "BOTTOMLEFT", 0, -22) + self.page2.button_save:SetText(L["Save"]) + self.page2.button_save:SetScript("OnClick", function(button) self:Button_OnClick(button) end) + self.page2.button_save:SetFrameLevel(self.page2:GetFrameLevel() + 10) + self.page2.button_save:Disable() -- Start disabled + self.page2.button_save:Show() + + -- Create Cancel button in bottom right corner + self.page2.button_cancel = CreateFrame("Button", nil, self.page2, "UIPanelButtonTemplate") + self.page2.button_cancel:SetSize(90, 22) + self.page2.button_cancel:SetPoint("BOTTOMRIGHT", self.page2, "BOTTOMRIGHT", 0, -22) + self.page2.button_cancel:SetText(L["Cancel"]) + self.page2.button_cancel:SetScript("OnClick", function(button) self:Button_OnClick(button) end) + self.page2.button_cancel:SetFrameLevel(self.page2:GetFrameLevel() + 10) + self.page2.button_cancel:Show() +end + +function CliqueConfig:UpdateBindSpellButtonState() + if not self.page1 or not self.page1.button_spell then + return -- Button doesn't exist yet + end + + if AscensionSpellbookFrame:IsVisible() then + self.page1.button_spell:Disable() + else + self.page1.button_spell:Enable() + end +end + +function CliqueConfig:Column_OnClick(frame, button) + self.page1.sortType = frame.sortType + self:UpdateList() +end + +function CliqueConfig:HijackSpellbook() + self.spellbookButtons = {} + + for idx = 1, 12 do + local parent = _G["AscensionSpellbookFrameContentSpellsSpellButton" .. idx] + local button = CreateFrame("Button", "CliqueSpellbookButton" .. idx, parent, "CliqueSpellbookButtonTemplate") + button.spellbutton = parent + button:EnableKeyboard(false) + button:EnableMouseWheel(true) + button:RegisterForClicks("AnyDown") + button:SetID(parent:GetID()) + self.spellbookButtons[idx] = button + end + + AscensionSpellbookFrame:HookScript("OnShow", function(frame) + self:EnableSpellbookButtons() + self:UpdateBindSpellButtonState() + end) + AscensionSpellbookFrame:HookScript("OnHide", function(frame) + self:EnableSpellbookButtons() + self:UpdateBindSpellButtonState() + end) + + self:EnableSpellbookButtons() +end + +function CliqueConfig:EnableSpellbookButtons() + local enabled; + + if self.page1:IsVisible() and AscensionSpellbookFrame:IsVisible() then enabled = true end + + if self.spellbookButtons then + for idx, button in ipairs(self.spellbookButtons) do + if enabled and button.spellbutton:IsEnabled() == 1 then + button:Show() + else + button:Hide() + end + end + end +end + +-- Spellbook button functions +function CliqueConfig:Spellbook_EnableKeyboard(button, motion) button:EnableKeyboard(true) end + +function CliqueConfig:Spellbook_DisableKeyboard(button, motion) button:EnableKeyboard(false) end + +function CliqueConfig:Spellbook_OnBinding(button, key) + if key == "ESCAPE" then + HideUIPanel(CliqueConfig) + return + end + + local name = getglobal(button.spellbutton:GetName() .. "SpellName"):GetText() + local spellSubName = getglobal(button.spellbutton:GetName() .. "SubSpellName"):GetText() + local texture = getglobal(button.spellbutton:GetName() .. "IconTexture"):GetTexture() + -- Only enable spellSubName if ShowAllSpellRanks is checked + if not AscensionSpellbookFrameContentSpellsShowAllSpellRanks:GetChecked() then + spellSubName = nil + elseif spellSubName == "" then + spellSubName = nil + end + + local key = addon:GetCapturedKey(key) + if not key then return end + + local succ, err = addon:AddBinding{ + key = key, + type = "spell", + spell = name, + spellSubName = spellSubName, + icon = texture + } + + CliqueConfig:UpdateList() +end + +function CliqueConfig:UpdateProfileDisplay() + local currentProfile = addon.db:GetCurrentProfile() + local displayText = L["Current Profile: "] .. currentProfile + + -- Check if spec swap is enabled + if addon.settings.specswap then + local currentSpec = addon.talentGroup or 1 + displayText = displayText .. " " .. L["(Spec "] .. currentSpec .. ")" + else + displayText = displayText .. " " .. L["(All specs)"] + end + + self.page1.profileLabel:SetText(displayText) + + -- Update dropdown text + UIDropDownMenu_SetText(self.page1.profileDropdown, currentProfile) +end + +function CliqueConfig:ProfileDropdown_Initialize(dropdown, level) + local profiles = addon.db:GetProfiles() + local currentProfile = addon.db:GetCurrentProfile() + + -- Create sorted list of profiles + local sortedProfiles = {} + for idx, profileName in ipairs(profiles) do table.insert(sortedProfiles, profileName) end + table.sort(sortedProfiles) + + -- Add each profile as a menu item + for _, profileName in ipairs(sortedProfiles) do + local info = UIDropDownMenu_CreateInfo() + info.text = profileName + info.value = profileName + info.func = function() self:ProfileDropdown_OnClick(profileName) end + info.checked = (profileName == currentProfile) + UIDropDownMenu_AddButton(info, level) + end +end + +function CliqueConfig:ProfileDropdown_OnClick(profileName) + if addon.settings.specswap then + -- When spec swap is enabled, change the profile for the current specialization + local currentSpec = addon.talentGroup or 1 + addon.settings["specswap" .. currentSpec] = profileName + -- Also switch to that profile immediately + addon.db:SetProfile(profileName) + else + -- When spec swap is disabled, just change the current profile + addon.db:SetProfile(profileName) + end + + self:UpdateProfileDisplay() + self:UpdateList() + CloseDropDownMenus() +end + +function CliqueConfig:Button_OnClick(button) + -- Click handler for "Bind spell" button + if button == self.page1.button_spell then + ShowUIPanel(AscensionSpellbookFrame) + CliqueConfig:ShowWithSpellBook() + + -- Click handler for "Bind other" button + elseif button == self.page1.button_other then + local config = CliqueConfig + local menu = {{ + text = L["Select a binding type"], + isTitle = true, + notCheckable = true + }, { + text = L["Target clicked unit"], + func = function() self:SetupCaptureDialog("target") end, + notCheckable = true + }, { + text = L["Open unit menu"], + func = function() self:SetupCaptureDialog("menu") end, + notCheckable = true + }, { + text = L["Run custom macro"], + func = function() + config.page1:Hide() + config.page2.bindType = "macro" + -- Clear out the entries + config.page2.bindText:SetText(L["No binding set"]) + config.page2.editbox:SetText("") + config.page2.button_save:Disable() + config.page2:Show() + end, + notCheckable = true + }} + UIDropDownMenu_SetAnchor(self.dropdown, 0, 0, "BOTTOMLEFT", self.page1.button_other, "TOP") + EasyMenu(menu, self.dropdown, nil, 0, 0, "MENU", nil) + + -- Click handler for "Options" button + elseif button == self.page1.button_options then + local menu = {{ + text = L["Select an options category"], + isTitle = true, + notCheckable = true + }, { + text = L["Clique general options"], + func = function() + HideUIPanel(AscensionSpellbookFrame) + HideUIPanel(CliqueConfig) + InterfaceOptionsFrame_OpenToCategory(addon.optpanels["GENERAL"]) + end, + notCheckable = true + }, { + text = L["Frame blacklist"], + func = function() + HideUIPanel(AscensionSpellbookFrame) + HideUIPanel(CliqueConfig) + InterfaceOptionsFrame_OpenToCategory(addon.optpanels["BLACKLIST"]) + end, + notCheckable = true + }, { + text = L["Blizzard frame integration options"], + func = function() + HideUIPanel(AscensionSpellbookFrame) + HideUIPanel(CliqueConfig) + InterfaceOptionsFrame_OpenToCategory(addon.optpanels["BLIZZFRAMES"]) + end, + notCheckable = true + }} + UIDropDownMenu_SetAnchor(self.dropdown, 0, 0, "BOTTOMLEFT", self.page1.button_options, "TOP") + EasyMenu(menu, self.dropdown, nil, 0, 0, "MENU", nil) + elseif button == self.page2.button_save then + -- Check the input + local key = self.page2.key + local macrotext = self.page2.editbox:GetText() + + if self.page2.binding then + self.page2.binding.key = key + self.page2.binding.macrotext = macrotext + self.page2.binding = nil + addon:FireMessage("BINDINGS_CHANGED") + else + local succ, err = addon:AddBinding{ + key = key, + type = "macro", + macrotext = macrotext + } + end + self:UpdateList() + self.page2:Hide() + self.page1:Show() + elseif button == self.page2.button_cancel then + self.page2.binding = nil + self.page2:Hide() + self.page1:Show() + end +end + +local memoizeBindings = setmetatable({}, { + __index = function(t, k, v) + local binbits = addon:GetBinaryBindingKey(k) + rawset(t, k, binbits) + return binbits + end +}) + +local compareFunctions; +compareFunctions = { + name = function(a, b) + local texta = addon:GetBindingActionText(a.type, a) + local textb = addon:GetBindingActionText(b.type, b) + if texta == textb then return compareFunctions.key(a, b) end + return texta < textb + end, + key = function(a, b) + local keya = addon:GetBindingKey(a) + local keyb = addon:GetBindingKey(b) + if keya == keyb then + return memoizeBindings[a] < memoizeBindings[b] + elseif not keya or not keyb then + return false + else + return keya < keyb + end + end, + binding = function(a, b) + local mem = memoizeBindings + if mem[a] == mem[b] then + return compareFunctions.name(a, b) + else + return mem[a] < mem[b] + end + end +} + +-- Mapping between binding entry and index in profile +function CliqueConfig:UpdateList() + local page = self.page1 + local binds = addon.bindings + + -- GUI not created yet + if not self.initialized then + return + elseif not self:IsVisible() then + return + end + + -- Sort the bindings + local sort = {} + for idx, entry in pairs(binds) do sort[#sort + 1] = entry end + + if page.sortType then + table.sort(sort, compareFunctions[page.sortType]) + else + table.sort(sort, compareFunctions.key) + end + + -- Enable or disable the scroll bar + if #sort > MAX_ROWS - 1 then + -- Set up the scrollbar for the item list + page.slider:SetMinMaxValues(0, #sort - MAX_ROWS) + + -- Adjust and show + if not page.slider:IsShown() then + -- Adjust column positions + for idx, row in ipairs(self.rows) do row.bind:SetWidth(90) end + page.slider:SetValue(0) + page.slider:Show() + end + elseif page.slider:IsShown() then + -- Move column positions back and hide the slider + for idx, row in ipairs(self.rows) do row.bind:SetWidth(105) end + page.slider:Hide() + end + + -- Update the rows in the list + local offset = page.slider:GetValue() or 0 + for idx, row in ipairs(self.rows) do + local offsetIndex = offset + idx + if sort[offsetIndex] then + local bind = sort[offsetIndex] + row.icon:SetTexture(addon:GetBindingIcon(bind)) + row.name:SetText(addon:GetBindingActionText(bind.type, bind)) + row.info:SetText(addon:GetBindingInfoText(bind)) + row.bind:SetText(addon:GetBindingKeyComboText(bind)) + row.binding = bind + row:Show() + else + row:Hide() + end + end +end + +function CliqueConfig:ClearEditPage() end + +function CliqueConfig:ShowEditPage() + self:ClearEditPage() + self.page1:Hide() + self.page3:Show() +end + +function CliqueConfig:Save_OnClick(button, down) end + +function CliqueConfig:Cancel_OnClick(button, down) + self:ClearEditPage() + self.page3:Hide() + self.page1:Show() +end + +function CliqueConfig:SetupCaptureDialog(type, binding) + self.dialog.bindType = type + self.dialog.binding = binding + + if not binding then + local actionText = addon:GetBindingActionText(type, binding) + self.dialog.title:SetText(L["Set binding: %s"]:format(actionText)) + else + -- This is a change to an existing binding + local actionText = addon:GetBindingActionText(type, binding) + self.dialog.title:SetText(L["Change binding: %s"]:format(actionText)) + end + + self.dialog.bindText:SetText("") + self.dialog:Show() +end + +function CliqueConfig:BindingButton_OnClick(button, key) + local dialog = CliqueDialog + dialog.key = addon:GetCapturedKey(key) + if dialog.key then CliqueDialog.bindText:SetText(addon:GetBindingKeyComboText(dialog.key)) end +end + +function CliqueConfig:MacroBindingButton_OnClick(button, key) + local key = addon:GetCapturedKey(key) + if key then + self.page2.key = key + self.page2.bindText:SetText(addon:GetBindingKeyComboText(key)) + self.page2.button_save:Enable() + else + self.page2.bindText:SetText(L["No binding set"]) + self.page2.button_save:Disable() + end +end + +function CliqueConfig:AcceptSetBinding() + local dialog = CliqueDialog + local key = dialog.key + + if dialog.binding then + -- This was a CHANGE binding instead of a SET binding + dialog.binding.key = key + dialog.binding = nil + -- Do not forget to update the attributes as well + self:UpdateList() + addon:FireMessage("BINDINGS_CHANGED") + else + local succ, err = addon:AddBinding{ + key = key, + type = dialog.bindType + } + if succ then self:UpdateList() end + end + dialog:Hide() +end + +local function toggleSet(binding, set, ...) + local exclude = {} + for i = 1, select("#", ...) do + local item = select(i, ...) + table.insert(exclude, item) + end + + return function() + if not binding.sets then binding.sets = {} end + if binding.sets[set] then + binding.sets[set] = nil + else + binding.sets[set] = true + end + + for idx, exclset in ipairs(exclude) do binding.sets[exclset] = nil end + + UIDropDownMenu_Refresh(UIDROPDOWNMENU_OPEN_MENU, nil, UIDROPDOWNMENU_MENU_LEVEL) + CliqueConfig:UpdateList() + addon:FireMessage("BINDINGS_CHANGED") + end +end + +function CliqueConfig:Row_OnClick(frame, button) + local binding = frame.binding + local actionText = addon:GetBindingActionText(binding.type, binding) + + local menu = {{ + text = L["Configure binding: '%s'"]:format(actionText:sub(1, 15)), + notCheckable = true, + isTitle = true + }, { + text = L["Change binding"], + func = function() + local binding = frame.binding + self:SetupCaptureDialog(binding.type, binding) + end, + notCheckable = true + }, { + text = L["Delete binding"], + func = function() + addon:DeleteBinding(frame.binding) + self:UpdateList() + end, + notCheckable = true + }} + + if binding.type == "spell" and binding.spellSubName then + -- Enable a 'Remove Rank' option + menu[#menu + 1] = { + text = L["Remove spell rank"], + func = function() + local binding = frame.binding + binding.spellSubName = nil + self:UpdateList() + addon:FireMessage("BINDINGS_CHANGED") + end, + notCheckable = true + } + end + + if binding.type == "macro" then + -- Replace 'Change Binding' with 'Edit macro' + menu[2] = { + text = L["Edit macro"], + func = function() + self.page2.bindType = "macro" + local bindText = addon:GetBindingKeyComboText(binding) + self.page2.bindText:SetText(bindText) + self.page2.binding = binding + self.page2.key = binding.key + self.page2.editbox:SetText(binding.macrotext) + self.page2.button_save:Enable() + self.page1:Hide() + self.page2:Show() + end, + notCheckable = true + } + end + + local submenu = { + text = L["Enable/Disable binding-sets"], + hasArrow = true, + notCheckable = true, + menuList = {} + } + table.insert(menu, submenu) + + table.insert(submenu.menuList, { + text = L["Default"], + checked = function() return binding.sets["default"] end, + func = toggleSet(binding, "default"), + tooltipTitle = L["Clique: 'default' binding-set"], + tooltipText = L["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Friend"], + checked = function() return binding.sets["friend"] end, + func = toggleSet(binding, "friend"), + tooltipTitle = L["Clique: 'friend' binding-set"], + tooltipText = L["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Enemy"], + checked = function() return binding.sets["enemy"] end, + func = toggleSet(binding, "enemy"), + tooltipTitle = L["Clique: 'enemy' binding-set"], + tooltipText = L["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Out-of-combat (ONLY)"], + checked = function() return binding.sets["ooc"] end, + func = toggleSet(binding, "ooc"), + tooltipTitle = L["Clique: 'ooc' binding-set"], + tooltipText = L["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat, regardless of the other binding-sets this binding belongs to. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Primary talent spec (ONLY)"], + checked = function() return binding.sets["pritalent"] end, + func = toggleSet(binding, "pritalent"), + tooltipTitle = L["Clique: 'pritalent' binding-set"], + tooltipText = L["A binding that belongs to the 'pritalent' binding-set is only active when the player is currently using their primary talent spec, regardless of the other binding-sets that this binding belongs to."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Secondary talent spec (ONLY)"], + checked = function() return binding.sets["sectalent"] end, + func = toggleSet(binding, "sectalent"), + tooltipTitle = L["Clique: 'sectalent' binding-set"], + tooltipText = L["A binding that belongs to the 'sectalent' binding-set is only active when the player is currently using their secondary talent spec, regardless of the other binding-sets that this binding belongs to."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Hovercast bindings (target required)"], + checked = function() return binding.sets["hovercast"] end, + func = toggleSet(binding, "hovercast", "global"), + tooltipTitle = L["Clique: 'hovercast' binding-set"], + tooltipText = L["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. THese bindings are also active over unit frames."] + -- keepShownOnClick = true, + }) + + table.insert(submenu.menuList, { + text = L["Global bindings (no target)"], + checked = function() return binding.sets["global"] end, + func = toggleSet(binding, "global", "hovercast"), + tooltipTitle = L["Clique: 'global' binding-set"], + tooltipText = L["A binding that belongs to the 'global' binding-set is always active. If the spell requires a target, you will be given the 'casting hand', otherwise the spell will be cast. If the spell is an AOE spell, then you will be given the ground targeting circle."] + -- keepShownOnClick = true, + }) + + EasyMenu(menu, self.dropdown, "cursor", 0, 0, "MENU", nil) +end + +function CliqueConfig:SpellTab_OnClick(frame) + if self:IsVisible() then + HideUIPanel(CliqueConfig) + elseif AscensionSpellbookFrame:IsVisible() then + self:ShowWithSpellBook() + else + ShowUIPanel(CliqueConfig) + end +end + +function CliqueConfig:UpdateAlert(type) + local alert = CliqueTabAlert + if not addon.settings.alerthidden and AscensionSpellbookFrame:IsVisible() and CliqueConfig:IsVisible() then + alert.type = type + alert.text:SetText( + L["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."]) + alert:Show() + else + alert:Hide() + end +end diff --git a/Clique/config/BlizzardFramesConfig.lua b/Clique/config/BlizzardFramesConfig.lua new file mode 100644 index 0000000..e5c4439 --- /dev/null +++ b/Clique/config/BlizzardFramesConfig.lua @@ -0,0 +1,125 @@ +--[[------------------------------------------------------------------------- +-- BlizzardFrames.lua +-- +-- This file contains the definitions of the blizzard frame integration +-- options. These settings will not apply until the user interface is +-- reloaded. +-- +-- Events registered: +-- * ADDON_LOADED - To watch for loading of the ArenaUI +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +--[[--------------------------------------------------------------------------- +-- Options panel definition +---------------------------------------------------------------------------]] -- + +local panel = CreateFrame("Frame") +panel.name = "Blizzard Frame Options" +panel.parent = addonName + +addon.optpanels["BLIZZFRAMES"] = panel + +panel:SetScript("OnShow", function(self) + if not panel.initialized then + panel:CreateOptions() + panel.refresh() + end +end) + +local function make_checkbox(name, label) + local frame = CreateFrame("CheckButton", "CliqueOptionsBlizzFrame" .. name, panel, "UICheckButtonTemplate") + frame.text = _G[frame:GetName() .. "Text"] + frame.type = "checkbox" + frame.text:SetText(label) + return frame +end + +local function make_label(name, template) + local label = panel:CreateFontString("OVERLAY", "CliqueOptionsBlizzFrame" .. name, template) + label:SetWidth(panel:GetWidth()) + label:SetJustifyH("LEFT") + label.type = "label" + return label +end + +function panel:CreateOptions() + panel.initialized = true + + local bits = {} + self.intro = make_label("Intro", "GameFontHighlightSmall") + self.intro:SetText( + L["These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded."]) + self.intro:SetPoint("RIGHT") + self.intro:SetJustifyV("TOP") + self.intro:SetHeight(40) + + self.PlayerFrame = make_checkbox("PlayerFrame", L["Player frame"]) + self.PetFrame = make_checkbox("PetFrame", L["Player's pet frame"]) + self.TargetFrame = make_checkbox("TargetFrame", L["Player's target frame"]) + self.TargetFrameToT = make_checkbox("TargetFrameToT", L["Target of target frame"]) + self.FocusFrame = make_checkbox("FocusFrame", L["Player's focus frame"]) + self.FocusFrameToT = make_checkbox("FocusFrameToT", L["Target of focus frame"]) + self.arena = make_checkbox("ArenaEnemy", L["Arena enemy frames"]) + self.party = make_checkbox("Party", L["Party member frames"]) + self.compactraid = make_checkbox("CompactRaid", L["Compact raid frames"]) + -- self.compactparty = make_checkbox("CompactParty", L["Compact party frames"]) + self.boss = make_checkbox("BossTarget", L["Boss target frames"]) + + table.insert(bits, self.intro) + table.insert(bits, self.PlayerFrame) + table.insert(bits, self.PetFrame) + table.insert(bits, self.TargetFrame) + table.insert(bits, self.FocusFrame) + table.insert(bits, self.FocusFrameToT) + + -- Group these together + bits[1]:SetPoint("TOPLEFT", 5, -5) + + for i = 2, #bits, 1 do bits[i]:SetPoint("TOPLEFT", bits[i - 1], "BOTTOMLEFT", 0, 0) end + + local last = bits[#bits] + + table.wipe(bits) + table.insert(bits, self.arena) + table.insert(bits, self.party) + table.insert(bits, self.compactraid) + -- table.insert(bits, self.compactparty) + table.insert(bits, self.boss) + + bits[1]:SetPoint("TOPLEFT", last, "BOTTOMLEFT", 0, -15) + + for i = 2, #bits, 1 do bits[i]:SetPoint("TOPLEFT", bits[i - 1], "BOTTOMLEFT", 0, 0) end +end + +function panel.refresh() + local opt = addon.settings.blizzframes + + panel.PlayerFrame:SetChecked(opt.PlayerFrame) + panel.PetFrame:SetChecked(opt.PetFrame) + panel.TargetFrame:SetChecked(opt.TargetFrame) + panel.FocusFrame:SetChecked(opt.FocusFrame) + panel.FocusFrameToT:SetChecked(opt.FocusFrameToT) + panel.arena:SetChecked(opt.arena) + panel.party:SetChecked(opt.party) + panel.compactraid:SetChecked(opt.compactraid) + -- panel.compactparty:SetChecked(opt.compactparty) + panel.boss:SetChecked(opt.boss) +end + +function panel.okay() + local opt = addon.settings.blizzframes + opt.PlayerFrame = not not panel.PlayerFrame:GetChecked() + opt.PetFrame = not not panel.PetFrame:GetChecked() + opt.TargetFrame = not not panel.TargetFrame:GetChecked() + opt.FocusFrame = not not panel.FocusFrame:GetChecked() + opt.FocusFrameToT = not not panel.FocusFrameToT:GetChecked() + opt.arena = not not panel.arena:GetChecked() + opt.party = not not panel.party:GetChecked() + opt.compactraid = not not panel.compactraid:GetChecked() + -- opt.compactparty = not not panel.compactparty:GetChecked() + opt.boss = not not panel.boss:GetChecked() +end + +InterfaceOptions_AddCategory(panel, addon.optpanels.ABOUT) diff --git a/Clique/config/DenylistConfig.lua b/Clique/config/DenylistConfig.lua new file mode 100644 index 0000000..a5af3da --- /dev/null +++ b/Clique/config/DenylistConfig.lua @@ -0,0 +1,169 @@ +--[[------------------------------------------------------------------------- +-- FrameOptionsPanel.lua +-- +-- This file contains the definitions of the frame blacklist options panel. +-- +-- Events registered: +-- None +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +local panel = CreateFrame("Frame") +panel.name = "Frame Blacklist" +panel.parent = addonName + +addon.optpanels["BLACKLIST"] = panel + +panel:SetScript("OnShow", function(self) + if not panel.initialized then + panel:CreateOptions() + panel.refresh() + end + panel.refresh() +end) + +local function make_label(name, template) + local label = panel:CreateFontString("OVERLAY", "CliqueOptionsBlacklist" .. name, template) + label:SetWidth(panel:GetWidth()) + label:SetJustifyH("LEFT") + label:SetJustifyV("TOP") + label.type = "label" + return label +end + +local function make_checkbox(name, parent, label) + local frame = CreateFrame("CheckButton", "CliqueOptionsBlacklist" .. name, parent, "UICheckButtonTemplate") + frame.text = _G[frame:GetName() .. "Text"] + frame.type = "checkbox" + frame.text:SetText(label) + return frame +end + +local state = {} + +function panel:CreateOptions() + panel.initialized = true + + self.intro = make_label("Intro", "GameFontHighlightSmall") + self.intro:SetPoint("TOPLEFT", panel, 5, -5) + self.intro:SetPoint("RIGHT", panel, -5, 0) + self.intro:SetHeight(45) + self.intro:SetText( + L["This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings."]) + + self.scrollframe = CreateFrame("ScrollFrame", "CliqueOptionsBlacklistScrollFrame", self, "FauxScrollFrameTemplate") + self.scrollframe:SetPoint("TOPLEFT", self.intro, "BOTTOMLEFT", 0, -5) + self.scrollframe:SetPoint("RIGHT", self, "RIGHT", -30, 0) + self.scrollframe:SetHeight(320) + self.scrollframe:Show() + + local function row_onclick(row) state[row.frameName] = not not row:GetChecked() end + + self.rows = {} + + -- Create and anchor some items + for idx = 1, 10 do + self.rows[idx] = make_checkbox("Item" .. idx, self.scrollframe, L["Frame name"]) + self.rows[idx]:SetScript("OnClick", row_onclick) + + if idx == 1 then + self.rows[idx]:SetPoint("TOPLEFT", self.scrollframe, "TOPLEFT", 0, 0) + else + self.rows[idx]:SetPoint("TOPLEFT", self.rows[idx - 1], "BOTTOMLEFT", 0, 0) + end + end + + self.rowheight = self.rows[1]:GetHeight() + + -- Number of items? + local function update() self:UpdateScrollFrame() end + + self.scrollframe:SetScript("OnVerticalScroll", function(frame, offset) FauxScrollFrame_OnVerticalScroll(frame, offset, self.rowheight, update) end) + + self.selectall = CreateFrame("Button", "CliqueOptionsBlacklistSelectAll", self, "UIPanelButtonTemplate2") + self.selectall:SetText(L["Select All"]) + self.selectall:SetPoint("BOTTOMLEFT", 10, 10) + self.selectall:SetWidth(100) + self.selectall:SetScript("OnClick", function(button) + for frame in pairs(addon.ccframes) do + local name = frame:GetName() + if name then state[name] = true end + end + + for name, frame in pairs(addon.hccframes) do state[name] = true end + + self:UpdateScrollFrame() + end) + + self.selectnone = CreateFrame("Button", "CliqueOptionsBlacklistSelectNone", self, "UIPanelButtonTemplate2") + self.selectnone:SetText(L["Select None"]) + self.selectnone:SetPoint("BOTTOMLEFT", self.selectall, "BOTTOMRIGHT", 5, 0) + self.selectnone:SetWidth(100) + self.selectnone:SetScript("OnClick", function(button) + for frame in pairs(addon.ccframes) do + local name = frame:GetName() + if name then state[name] = false end + end + + for name, frame in pairs(addon.hccframes) do state[name] = false end + + self:UpdateScrollFrame() + end) +end + +function panel:UpdateScrollFrame() + local sort = {} + for frame in pairs(addon.ccframes) do + local name = frame:GetName() + if name then table.insert(sort, name) end + end + + for name, frame in pairs(addon.hccframes) do table.insert(sort, name) end + + table.sort(sort) + + local offset = FauxScrollFrame_GetOffset(self.scrollframe) + FauxScrollFrame_Update(self.scrollframe, #sort, 10, self.rowheight) + + for i = 1, 10 do + local idx = offset + i + local row = self.rows[i] + if idx <= #sort then + row.frameName = sort[idx] + row.text:SetText(sort[idx]) + row:SetChecked(state[sort[idx]]) + row:Show() + else + row:Hide() + end + end +end + +function panel.okay() + -- Clear the existing blacklist + for frame, value in pairs(state) do + if not not value then + addon.settings.blacklist[frame] = true + else + addon.settings.blacklist[frame] = nil + end + end + + addon:FireMessage("BLACKLIST_CHANGED") +end + +function panel.refresh() + for frame in pairs(addon.ccframes) do + local name = frame:GetName() + if name then state[name] = false end + end + + for name, frame in pairs(addon.hccframes) do state[name] = false end + + for frame, value in pairs(addon.settings.blacklist) do state[frame] = value end + + panel:UpdateScrollFrame() +end + +InterfaceOptions_AddCategory(panel, addon.optpanels.ABOUT) diff --git a/Clique/config/OptionsPanel.lua b/Clique/config/OptionsPanel.lua new file mode 100644 index 0000000..b754841 --- /dev/null +++ b/Clique/config/OptionsPanel.lua @@ -0,0 +1,554 @@ +--[[------------------------------------------------------------------------- +-- OptionsPanel.lua +-- +-- This file contains the definitions of the main interface options panel. +-- Any other options panels are sub-categories of this main panel. +-- +-- Events registered: +-- None +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +--[[------------------------------------------------------------------------- +-- Addon 'About' Dialog for Interface Options +-- +-- Some of this code was taken from/inspired by tekKonfigAboutPanel +--- and it's been moved from AddonCore due to taint issues. +-------------------------------------------------------------------------]] -- + +local about = CreateFrame("Frame", addonName .. "AboutPanel", InterfaceOptionsFramePanelContainer) +about.name = addonName +about:Hide() + +function about.OnShow(frame) + local fields = {"Version", "Author", "Backported"} + local notes = GetAddOnMetadata(addonName, "Notes") + + local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") + + title:SetPoint("TOPLEFT", 16, -16) + title:SetText(addonName) + + local subtitle = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + subtitle:SetHeight(32) + subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8) + subtitle:SetPoint("RIGHT", about, -32, 0) + subtitle:SetNonSpaceWrap(true) + subtitle:SetJustifyH("LEFT") + subtitle:SetJustifyV("TOP") + subtitle:SetText(notes) + + local anchor + for _, field in pairs(fields) do + local val + if (field == "Backported") then + val = "Tsoukie (Based on v3.4.14)" + else + val = GetAddOnMetadata(addonName, field) + end + if val then + local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + title:SetWidth(75) + if not anchor then + title:SetPoint("TOPLEFT", subtitle, "BOTTOMLEFT", -2, -8) + else + title:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -6) + end + title:SetJustifyH("RIGHT") + title:SetText(field) + + local detail = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + detail:SetPoint("LEFT", title, "RIGHT", 4, 0) + detail:SetPoint("RIGHT", -16, 0) + detail:SetJustifyH("LEFT") + detail:SetText(val) + + anchor = title + end + end + + -- Clear the OnShow so it only happens once + frame:SetScript("OnShow", nil) +end + +addon.optpanels = addon.optpanels or {} +addon.optpanels.ABOUT = about + +about:SetScript("OnShow", about.OnShow) +InterfaceOptions_AddCategory(addon.optpanels.ABOUT) + +--[[------------------------------------------------------------------------- +-- End Dialog +-------------------------------------------------------------------------]] -- + +local panel = CreateFrame("Frame") +panel.name = L["General Options"] +panel.parent = addonName + +addon.optpanels.GENERAL = panel + +panel:SetScript("OnShow", function(self) + if not panel.initialized then + panel:CreateOptions() + panel.refresh() + end +end) + +local function make_checkbox(name, parent) + local frame = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate") + frame.text = _G[frame:GetName() .. "Text"] + frame.type = "checkbox" + return frame +end + +local function make_dropdown(name, parent) + local frame = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate") + frame:SetClampedToScreen(true) + frame.type = "dropdown" + return frame +end + +local function make_label(name, parent, template) + local label = parent:CreateFontString("OVERLAY", name, template) + label:SetWidth(120) + label:SetJustifyH("LEFT") + label.type = "label" + return label +end + +local function make_editbox_with_button(editName, buttonName, parent) + local editbox = CreateFrame("EditBox", editName, parent) + editbox:SetHeight(32) + editbox:SetWidth(200) + editbox:SetAutoFocus(false) + editbox:SetFontObject('GameFontHighlightSmall') + editbox.type = "editbox" + + local left = editbox:CreateTexture(nil, "BACKGROUND") + left:SetWidth(8) + left:SetHeight(20) + left:SetPoint("LEFT", -5, 0) + left:SetTexture("Interface\\Common\\Common-Input-Border") + left:SetTexCoord(0, 0.0625, 0, 0.625) + + local right = editbox:CreateTexture(nil, "BACKGROUND") + right:SetWidth(8) + right:SetHeight(20) + right:SetPoint("RIGHT", 0, 0) + right:SetTexture("Interface\\Common\\Common-Input-Border") + right:SetTexCoord(0.9375, 1, 0, 0.625) + + local center = editbox:CreateTexture(nil, "BACKGROUND") + center:SetHeight(20) + center:SetPoint("RIGHT", right, "LEFT", 0, 0) + center:SetPoint("LEFT", left, "RIGHT", 0, 0) + center:SetTexture("Interface\\Common\\Common-Input-Border") + center:SetTexCoord(0.0625, 0.9375, 0, 0.625) + + editbox:SetScript("OnEscapePressed", editbox.ClearFocus) + editbox:SetScript("OnEnterPressed", editbox.ClearFocus) + editbox:SetScript("OnEditFocusGained", function() editbox:HighlightText(0, MAX_HIGHLIGHT_LEN) end) + + local button = CreateFrame("Button", buttonName, editbox, "UIPanelButtonTemplate2") + button:Show() + button:SetHeight(22) + button:SetWidth(75) + button:SetPoint("LEFT", editbox, "RIGHT", 0, 0) + return editbox, button +end + +function panel:CreateOptions() + -- Ensure the panel isn't created twice (thanks haste) + panel.initialized = true + + -- Create the general options panel here: + local bits = {} + + self.updown = make_checkbox("CliqueOptionsUpDownClick", self) + self.updown.text:SetText(L["Trigger bindings on the 'down' portion of the click (experimental)"]) + + self.fastooc = make_checkbox("CliqueOptionsFastOoc", self) + self.fastooc.text:SetText(L["Disable out of combat clicks when party members enter combat"]) + + self.specswap = make_checkbox("CliqueOptionsSpecSwap", self) + self.specswap.text:SetText(L["Swap profiles based on talent spec"]) + self.specswap.EnableDisable = function() + if self.specswap:GetChecked() then + for i = 1, 12 do UIDropDownMenu_EnableDropDown(panel["specswap" .. i]) end + else + for i = 1, 12 do UIDropDownMenu_DisableDropDown(panel["specswap" .. i]) end + end + end + self.specswap:SetScript("PostClick", self.specswap.EnableDisable) + + for i = 1, 12 do + self["specswap" .. i .. "label"] = make_label("CliqueOptionsSpecSwap" .. i .. "Label", self, "GameFontNormalSmall") + self["specswap" .. i .. "label"]:SetText(L["Specialization Slot "] .. i .. L[":"]) + self["specswap" .. i] = make_dropdown("CliqueOptionsSpecSwap" .. i, self) + UIDropDownMenu_SetWidth(self["specswap" .. i], 200) + BlizzardOptionsPanel_SetupDependentControl(self.specswap, self["specswap" .. i]) + end + + self.profilelabel = make_label("CliqueOptionsProfileMgmtLabel", self, "GameFontNormalSmall") + self.profilelabel:SetText(L["Profile Management:"]) + self.profiledd = make_dropdown("CliqueOptionsProfileMgmt", self) + UIDropDownMenu_SetWidth(self.profiledd, 200) + + self.stopcastingfix = make_checkbox("CliqueOptionsStopCastingFix", self) + self.stopcastingfix.text:SetText(L["Attempt to fix the issue introduced in 4.3 with casting on dead targets"]) + + self.exportbindingslabel = make_label("CliqueOptionsExportBindingsLabel", self, "GameFontNormalSmall") + self.exportbindingslabel:SetText(L["Export bindings:"]) + self.exportbindingseditbox, self.exportbindingsbutton = make_editbox_with_button("CliqueOptionsExportBindingsEditbox", "CliqueOptionsExportBindingsEditboxButton", self) + + self.exportbindingsbutton:SetText(L["Generate"]) + self.exportbindingsbutton:SetScript("OnClick", function(self, button) + local payload = addon:GetExportString() + local editbox = self:GetParent() + editbox:SetText(payload) + editbox:SetFocus() + editbox:HighlightText(0, MAX_HIGHLIGHT_LEN) + end) + + self.importbindingslabel = make_label("CliqueOptionsImportBindingsLabel", self, "GameFontNormalSmall") + self.importbindingslabel:SetText(L["Import bindings:"]) + local importEditbox, importButton = make_editbox_with_button("CliqueOptionsImportBindingsEditbox", "CliqueOptionsImportBindingsEditboxButton", self) + self.importbindingseditbox, self.importbindingsbutton = importEditbox, importButton + self.importbindingseditbox:SetScript("OnTextChanged", function(self, userInput) + importButton.validated = false + importButton:SetText(L["Validate"]) + end) + + self.importbindingsbutton.validated = false + self.importbindingsbutton:SetText(L["Validate"]) + self.importbindingsbutton:SetScript("OnClick", function(self, button) + if self.validated then + if not InCombatLockdown() then addon:ImportBindings(self.bindingData) end + self:SetText(L["Success!"]) + importEditbox:SetText("") + else + local editbox = self:GetParent() + local payload = editbox:GetText() + + local bindingData = addon:DecodeExportString(payload) + if bindingData then + self:SetText(L["Import"]) + self.validated = true + self.bindingData = bindingData + else + self:SetText(L["Invalid"]) + self.validated = false + end + end + end) + + -- Collect and anchor the bits together + table.insert(bits, self.updown) + table.insert(bits, self.fastooc) + table.insert(bits, self.stopcastingfix) + table.insert(bits, self.specswap) + table.insert(bits, self.profilelabel) + table.insert(bits, self.profiledd) + table.insert(bits, self.exportbindingslabel) + table.insert(bits, self.exportbindingseditbox) + table.insert(bits, self.importbindingslabel) + table.insert(bits, self.importbindingseditbox) + for i = 1, 12 do + table.insert(bits, self["specswap" .. i .. "label"]) + table.insert(bits, self["specswap" .. i]) + end + + bits[1]:SetPoint("TOPLEFT", 5, -5) + + local bump = 1 + for i = 2, #bits, 1 do + if bits[i].type == "label" then + bits[i]:SetPoint("TOPLEFT", bits[i - bump], "BOTTOMLEFT", 0, -15) + if bump == 1 then bump = 2 end + elseif bits[i].type == "dropdown" then + bits[i]:SetPoint("LEFT", bits[i - 1], "RIGHT", 5, -5) + elseif bits[i].type == "editbox" then + bits[i]:SetPoint("LEFT", bits[i - 1], "RIGHT", 5, 0) + else + bits[i]:SetPoint("TOPLEFT", bits[i - 1], "BOTTOMLEFT", 0, -5) + end + end + + -- Trigger bindings on 'down' clicks instead of 'up' clicks + -- Automatically switch profile based on spec + -- Dropdown to select primary profile + -- Dropdown to select secondary profile + -- Profile managerment + -- * set profile + -- * delete profile + -- * add profile +end + +StaticPopupDialogs["CLIQUE_CONFIRM_PROFILE_DELETE"] = { + button1 = YES, + button2 = NO, + hideOnEscape = 1, + timeout = 0, + whileDead = 1 +} + +StaticPopupDialogs["CLIQUE_NEW_PROFILE"] = { + text = TEXT("Enter the name of a new profile you'd like to create"), + button1 = TEXT(OKAY), + button2 = TEXT(CANCEL), + OnAccept = function(self) + local base = self:GetName() + local editbox = _G[base .. "EditBox"] + local profileName = editbox:GetText() + addon.db:SetProfile(profileName) + end, + timeout = 0, + whileDead = 1, + exclusive = 1, + showAlert = 1, + hideOnEscape = 1, + hasEditBox = 1, + maxLetters = 32, + OnShow = function(self) + _G[self:GetName() .. "Button1"]:Disable(); + _G[self:GetName() .. "EditBox"]:SetFocus(); + end, + EditBoxOnEnterPressed = function(self) + if (_G[self:GetParent():GetName() .. "Button1"]:IsEnabled() == 1) then + local base = self:GetParent():GetName() + local editbox = _G[base .. "EditBox"] + local profileName = editbox:GetText() + addon.db:SetProfile(profileName) + end + self:GetParent():Hide(); + end, + EditBoxOnTextChanged = function(self) + local editBox = _G[self:GetParent():GetName() .. "EditBox"]; + local txt = editBox:GetText() + if #txt > 0 then + _G[self:GetParent():GetName() .. "Button1"]:Enable(); + else + _G[self:GetParent():GetName() .. "Button1"]:Disable(); + end + end, + EditBoxOnEscapePressed = function(self) + self:GetParent():Hide(); + ClearCursor(); + end +} + +local function getsorttbl() + local profiles = addon.db:GetProfiles() + local sort = {} + for idx, profileName in ipairs(profiles) do table.insert(sort, profileName) end + table.sort(sort) + return sort +end + +local function spec_initialize(dropdown, level) + local sort = getsorttbl() + local paged = (#sort >= 15) + + if not level or level == 1 then + if not paged then + -- Display the profiles un-paged + for idx, entry in ipairs(sort) do + local info = UIDropDownMenu_CreateInfo() + info.text = entry + info.value = entry + info.func = function(frame, ...) UIDropDownMenu_SetSelectedValue(dropdown, entry) end + UIDropDownMenu_AddButton(info, level) + end + else + -- Page the results into sub-menus + for idx = 1, #sort, 10 do + -- Make the submenus for each group + local lastidx = (idx + 9 > #sort) and #sort or (idx + 9) + local info = UIDropDownMenu_CreateInfo() + local first = sort[idx] + local last = sort[lastidx] + info.text = first:sub(1, 5):trim() .. ".." .. last:sub(1, 5):trim() + info.value = idx + info.hasArrow = true + info.notCheckable = true + UIDropDownMenu_AddButton(info, level) + end + end + elseif level == 2 then + -- Generate the appropriate submenus depending on need + if paged then + -- Generate the frame submenu + local startIdx = UIDROPDOWNMENU_MENU_VALUE + local lastIdx = (startIdx + 9 > #sort) and #sort or (startIdx + 9) + for idx = startIdx, lastIdx do + local info = UIDropDownMenu_CreateInfo() + info.text = sort[idx] + info.value = sort[idx] + info.func = function(frame, ...) UIDropDownMenu_SetSelectedValue(dropdown, sort[idx]) end + UIDropDownMenu_AddButton(info, level) + end + end + end +end + +local function mgmt_initialize(dropdown, level) + local sort = getsorttbl() + local paged = (#sort >= 15) + local currentProfile = addon.db:GetCurrentProfile() + + if not level or level == 1 then + if not paged then + -- Display the profiles un-paged + for idx, entry in ipairs(sort) do + local info = UIDropDownMenu_CreateInfo() + info.text = entry + info.value = entry + info.notCheckable = true + info.hasArrow = true + UIDropDownMenu_AddButton(info, level) + end + else + -- Page the results into sub-menus + for idx = 1, #sort, 10 do + -- Make the submenus for each group + local lastidx = (idx + 9 > #sort) and #sort or (idx + 9) + local info = UIDropDownMenu_CreateInfo() + local first = sort[idx] + local last = sort[lastidx] + info.text = first:sub(1, 5):trim() .. ".." .. last:sub(1, 5):trim() + info.value = idx + info.notCheckable = true + info.hasArrow = true + UIDropDownMenu_AddButton(info, level) + end + end + + -- Create the 'Add profile' option regardless + local info = UIDropDownMenu_CreateInfo() + info.text = L["Add new profile"] + info.value = "add" + info.notCheckable = true + info.func = function() + HideDropDownMenu(1) + StaticPopup_Show("CLIQUE_NEW_PROFILE") + end + UIDropDownMenu_AddButton(info, level) + elseif level == 2 then + -- Generate the appropriate submenus depending on need + if paged then + -- Generate the frame submenu + local startIdx = UIDROPDOWNMENU_MENU_VALUE + local lastIdx = (startIdx + 9 > #sort) and #sort or (startIdx + 9) + for idx = startIdx, lastIdx do + local info = UIDropDownMenu_CreateInfo() + info.text = sort[idx] + info.value = sort[idx] + info.hasArrow = true + info.notCheckable = true + UIDropDownMenu_AddButton(info, level) + end + else + local info = UIDropDownMenu_CreateInfo() + info.text = L["Select profile: %s"]:format(UIDROPDOWNMENU_MENU_VALUE) + info.value = sort[UIDROPDOWNMENU_MENU_VALUE] + info.notCheckable = true + -- Don't disable this, allow the user to make their own mistakes + -- info.disabled = addon.settings.specswap + info.func = function(frame) + UIDropDownMenu_SetSelectedValue(dropdown, UIDROPDOWNMENU_MENU_VALUE) + UIDropDownMenu_SetText(dropdown, UIDROPDOWNMENU_MENU_VALUE) + addon.db:SetProfile(UIDROPDOWNMENU_MENU_VALUE) + end + UIDropDownMenu_AddButton(info, level) + + info = UIDropDownMenu_CreateInfo() + info.text = L["Delete profile: %s"]:format(UIDROPDOWNMENU_MENU_VALUE) + info.disabled = UIDROPDOWNMENU_MENU_VALUE == currentProfile + info.value = sort[UIDROPDOWNMENU_MENU_VALUE] + info.notCheckable = true + info.func = function(frame) + local dialog = StaticPopupDialogs["CLIQUE_CONFIRM_PROFILE_DELETE"] + dialog.text = L["Delete profile '%s'"]:format(UIDROPDOWNMENU_MENU_VALUE) + dialog.OnAccept = function(self) addon.db:DeleteProfile(UIDROPDOWNMENU_MENU_VALUE) end + HideDropDownMenu(1) + StaticPopup_Show("CLIQUE_CONFIRM_PROFILE_DELETE") + end + UIDropDownMenu_AddButton(info, level) + end + elseif level == 3 then + local info = UIDropDownMenu_CreateInfo() + info.text = L["Select profile: %s"]:format(UIDROPDOWNMENU_MENU_VALUE) + info.value = sort[UIDROPDOWNMENU_MENU_VALUE] + -- info.disabled = addon.settings.specswap + info.disabled = UIDROPDOWNMENU_MENU_VALUE == currentProfile + + info.func = function(frame) + UIDropDownMenu_SetSelectedValue(dropdown, UIDROPDOWNMENU_MENU_VALUE) + UIDropDownMenu_SetText(dropdown, UIDROPDOWNMENU_MENU_VALUE) + addon.db:SetProfile(UIDROPDOWNMENU_MENU_VALUE) + end + UIDropDownMenu_AddButton(info, level) + + info = UIDropDownMenu_CreateInfo() + info.text = L["Delete profile: %s"]:format(UIDROPDOWNMENU_MENU_VALUE) + info.disabled = UIDROPDOWNMENU_MENU_VALUE == currentProfile + info.value = sort[UIDROPDOWNMENU_MENU_VALUE] + info.func = function(frame) + local dialog = StaticPopupDialogs["CLIQUE_CONFIRM_PROFILE_DELETE"] + dialog.text = L["Delete profile '%s'"]:format(UIDROPDOWNMENU_MENU_VALUE) + dialog.OnAccept = function(self) addon.db:DeleteProfile(UIDROPDOWNMENU_MENU_VALUE) end + HideDropDownMenu(1) + StaticPopup_Show("CLIQUE_CONFIRM_PROFILE_DELETE") + end + UIDropDownMenu_AddButton(info, level) + end +end + +-- Update the elements on the panel to the current state +function panel.refresh() + -- Initialize the dropdowns + local settings = addon.settings + local currentProfile = addon.db:GetCurrentProfile() + + UIDropDownMenu_Initialize(panel.profiledd, mgmt_initialize) + UIDropDownMenu_SetSelectedValue(panel.profiledd, currentProfile) + UIDropDownMenu_SetText(panel.profiledd, L["Current: "] .. currentProfile) + + for i = 1, 12 do + UIDropDownMenu_Initialize(panel["specswap" .. i], spec_initialize) + UIDropDownMenu_SetSelectedValue(panel["specswap" .. i], settings["specswap" .. i] or currentProfile) + UIDropDownMenu_SetText(panel["specswap" .. i], settings["specswap" .. i] or currentProfile) + end + + panel.updown:SetChecked(settings.downclick) + panel.fastooc:SetChecked(settings.fastooc) + panel.stopcastingfix:SetChecked(settings.stopcastingfix) + panel.specswap:SetChecked(settings.specswap) + panel.specswap.EnableDisable() +end + +function panel.okay() + local settings = addon.settings + local currentProfile = addon.db:GetCurrentProfile() + + local changed = (not not panel.stopcastingfix:GetChecked()) ~= settings.stopcastingfix + + -- Update the saved variables + settings.downclick = not not panel.updown:GetChecked() + settings.stopcastingfix = not not panel.stopcastingfix:GetChecked() + settings.fastooc = not not panel.fastooc:GetChecked() + settings.specswap = not not panel.specswap:GetChecked() + for i = 1, 12 do settings["specswap" .. i] = UIDropDownMenu_GetSelectedValue(panel["specswap" .. i]) end + + if newProfile ~= currentProfile then addon.db:SetProfile(newProfile) end + addon:UpdateCombatWatch() + + if changed then addon:FireMessage("BINDINGS_CHANGED") end +end + +panel.cancel = panel.refresh + +function addon:UpdateOptionsPanel() if panel:IsVisible() and panel.initialized then panel.refresh() end end + +InterfaceOptions_AddCategory(panel, addon.optpanels.ABOUT) diff --git a/Clique/i18n/Localization.enUS.lua b/Clique/i18n/Localization.enUS.lua new file mode 100644 index 0000000..6e65131 --- /dev/null +++ b/Clique/i18n/Localization.enUS.lua @@ -0,0 +1,107 @@ +local addonName, addon = ... +local baseLocale = { + ["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."] = "A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding.", + ["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen."] = "A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen.", + ["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."] = "A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen.", + ["A binding that belongs to the 'global' binding-set is always active. If the spell requires a target, you will be given the 'casting hand', otherwise the spell will be cast. If the spell is an AOE spell, then you will be given the ground targeting circle."] = "A binding that belongs to the 'global' binding-set is always active. If the spell requires a target, you will be given the 'casting hand', otherwise the spell will be cast. If the spell is an AOE spell, then you will be given the ground targeting circle.", + ["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. These bindings are also active over unit frames."] = "A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. These bindings are also active over unit frames.", + ["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."] = "A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently.", + ["Accept"] = "Accept", + ["Action"] = "Action", + ["Add new profile"] = "Add new profile", + ["Alt"] = "Alt", + ["Arena enemy frames"] = "Arena enemy frames", + ["Bind other"] = "Bind other", + ["Bind spell"] = "Bind spell", + ["Binding"] = "Binding", + ["Blizzard frame integration options"] = "Blizzard frame integration options", + ["Boss target frames"] = "Boss target frames", + ["Cancel"] = "Cancel", + ["Cast %s"] = "Cast %s", + ["Change binding"] = "Change binding", + ["Change binding: %s"] = "Change binding: %s", + ["Clique Binding Configuration"] = "Clique Binding Configuration", + ["Clique binding configuration"] = "Clique binding configuration", + ["Clique general options"] = "Clique general options", + ["Current Profile: "] = "Current Profile: ", + ["(All specs)"] = "(All specs)", + ["(Spec "] = "(Spec ", + ["(Spec-specific)"] = "(Spec-specific)", + ["Clique: 'default' binding-set"] = "Clique: 'default' binding-set", + ["Clique: 'enemy' binding-set"] = "Clique: 'enemy' binding-set", + ["Clique: 'friend' binding-set"] = "Clique: 'friend' binding-set", + ["Clique: 'global' binding-set"] = "Clique: 'global' binding-set", + ["Clique: 'hovercast' binding-set"] = "Clique: 'hovercast' binding-set", + ["Clique: 'ooc' binding-set"] = "Clique: 'ooc' binding-set", + ["Compact party frames"] = "Compact party frames", + ["Compact raid frames"] = "Compact raid frames", + ["Configure binding: '%s'"] = "Configure binding: '%s'", + ["Ctrl"] = "Ctrl", + ["Current: "] = "Current: ", + ["Default"] = "Default", + ["Delete binding"] = "Delete binding", + ["Delete profile '%s'"] = "Delete profile '%s'", + ["Delete profile: %s"] = "Delete profile: %s", + ["Disable out of combat clicks when party members enter combat"] = "Disable out of combat clicks when party members enter combat", + ["Edit macro"] = "Edit macro", + ["Enable/Disable binding-sets"] = "Enable/Disable binding-sets", + ["Enemy"] = "Enemy", + ["Frame blacklist"] = "Frame blacklist", + ["Frame name"] = "Frame name", + ["Friend"] = "Friend", + ["Global bindings (no target)"] = "Global bindings (no target)", + ["Hovercast bindings (target required)"] = "Hovercast bindings (target required)", + ["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] = "In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard.", + ["LAlt"] = "LAlt", + ["LCtrl"] = "LCtrl", + ["LShift"] = "LShift", + ["LeftButton"] = "LeftButton", + ["MiddleButton"] = "MiddleButton", + ["MousewheelDown"] = "MousewheelDown", + ["MousewheelUp"] = "MousewheelUp", + ["No binding set"] = "No binding set", + ["Open unit menu"] = "Open unit menu", + ["Options"] = "Options", + ["Out-of-combat only"] = "Out-of-combat only", + ["Party member frames"] = "Party member frames", + ["Player frame"] = "Player frame", + ["Player's focus frame"] = "Player's focus frame", + ["Player's pet frame"] = "Player's pet frame", + ["Player's target frame"] = "Player's target frame", + ["Profile Management:"] = "Profile Management:", + ["RAlt"] = "RAlt", + ["RCtrl"] = "RCtrl", + ["RShift"] = "RShift", + ["Remove spell rank"] = "Remove spell rank", + ["RightButton"] = "RightButton", + ["Run custom macro"] = "Run custom macro", + ["Run macro"] = "Run macro", + ["Run macro '%s'"] = "Run macro '%s'", + ["Save"] = "Save", + ["Select All"] = "Select All", + ["Select None"] = "Select None", + ["Select a binding type"] = "Select a binding type", + ["Select an options category"] = "Select an options category", + ["Select profile: %s"] = "Select profile: %s", + ["Set binding"] = "Set binding", + ["Set binding: %s"] = "Set binding: %s", + ["Shift"] = "Shift", + ["Show unit menu"] = "Show unit menu", + ["Specialization Slot "] = "Specialization Slot ", + ["Swap profiles based on talent spec"] = "Swap profiles based on talent spec", + ["Target clicked unit"] = "Target clicked unit", + ["Target of focus frame"] = "Target of focus frame", + ["Target of target frame"] = "Target of target frame", + ["These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded."] = "These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded.", + ["This binding is DISABLED"] = "This binding is DISABLED", + ["This binding is invalid, please delete"] = "This binding is invalid, please delete", + ["This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings."] = "This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings.", + ["Trigger bindings on the 'down' portion of the click (experimental)"] = "Trigger bindings on the 'down' portion of the click (experimental)", + ["Unknown"] = "Unknown", + ["Unknown binding type '%s'"] = "Unknown binding type '%s'", + ["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."] = "When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration.", + ["You are in Clique binding mode"] = "You are in Clique binding mode", + ["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] = "You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated." +} + +addon:RegisterLocale('enUS', baseLocale) diff --git a/Clique/i18n/Localization.ruRU.lua b/Clique/i18n/Localization.ruRU.lua new file mode 100644 index 0000000..f3c4b9c --- /dev/null +++ b/Clique/i18n/Localization.ruRU.lua @@ -0,0 +1,109 @@ +-- by StingerSoft +if (GetLocale() ~= 'ruRU') then return end + +local _, addon = ... +local baseLocale = { + ["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."] = "Назначения что относится к набору назначений 'по умолчанию' всегда будут активны на ваших рамках, пока вы не замените его другими назначениями.", + ["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen."] = "Назначения что относится к набору назначений 'враг' всегда будут активны при нажатии на рамку цели которую вы можете атаковать. Если вы кликните на цель которую не можете атаковать, ничего не произойдет.", + ["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."] = "Назначения что относится к набору назначений 'друг' всегда будут активны при нажатии на рамку цели которая отображает дружественных игроков которых вы можете помогать и исцелять их. Если вы кликните на цель которую не можете исцелить или помочь, ничего не произойдет.", + ["A binding that belongs to the 'global' binding-set is always active. If the spell requires a target, you will be given the 'casting hand', otherwise the spell will be cast. If the spell is an AOE spell, then you will be given the ground targeting circle."] = "Назначения что относится к набору назначений 'общие' всегда активна. Если заклинание требует цель, вам будет предоставлена ​​'рука применения', в противном случае заклинание будет наложено. Если заклинание является AOE заклинанием, то вам будет предоставлен ​​круг наведения на цель.", + ["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. These bindings are also active over unit frames."] = "Назначения что относится к набору назначений 'применения при наводе' будут активны при наведении курсора мыши на рамку игрока, или персонажа в 3D мире. Это позволяет использовать 'применения при наводе', где наведении мыши на игрока в мире, и нажатии клавиши для применения на них заклинания. Эти назначения также распространяются на рамки игроков.", + ["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."] = "Назначения что относится к набору назначений 'внб' будут активны только когда игрок вне боя. Как только игрок войдет в бой, эти назначения больше не будут работоспособными, так что будьте осторожны при выборе этого набора назначений для любого часто используемого заклинания.", + ["Accept"] = "Принять", + ["Action"] = "Действие", + ["Add new profile"] = "Добавить новый профиль", + ["Alt"] = "Alt", + ["Arena enemy frames"] = "Рамки портретов на арене", + ["Bind other"] = "Назначить другое", + ["Bind spell"] = "Назначить заклинание", + ["Binding"] = "Назначение клавиш", + ["Blizzard frame integration options"] = "Опции интеграции рамок Blizzard", + ["Boss target frames"] = "Рамка портрета босса", + ["Cancel"] = "Отмена", + ["Cast %s"] = "Применение %s", + ["Change binding"] = "Сменить назначение", + ["Change binding: %s"] = "Сменить назначение: %s", + ["Clique Binding Configuration"] = "Конфигураци назначения Clique", + ["Clique binding configuration"] = "Конфигураци назначения Clique", + ["Clique general options"] = "Общие настройки Clique", + ["Current Profile: "] = "Текущий профиль: ", + ["(All specs)"] = "(Все специализации)", + ["(Spec "] = "(Спек ", + ["(Spec-specific)"] = "(Специфично для спека)", + ["Clique: 'default' binding-set"] = "Clique: набор назначений 'по умолчанию'", + ["Clique: 'enemy' binding-set"] = "Clique: набор назначений 'враг'", + ["Clique: 'friend' binding-set"] = "Clique: набор назначений 'друг'", + ["Clique: 'global' binding-set"] = "Clique: набор назначений 'общие'", + ["Clique: 'hovercast' binding-set"] = "Clique: набор назначений 'применения при наводе'", + ["Clique: 'ooc' binding-set"] = "Clique: набор назначений 'внб'", + ["Compact party frames"] = "Компактные рамки группы", + ["Compact raid frames"] = "Компактные рамки рейда", + ["Configure binding: '%s'"] = "Настройка назначения: '%s'", + ["Ctrl"] = "Ctrl", + ["Current: "] = "Текущее: ", + ["Default"] = "По умолчанию", + ["Delete binding"] = "Удалить назначение", + ["Delete profile '%s'"] = "Удалить профиль: '%s'", + ["Delete profile: %s"] = "Удалить профиль: %s", + ["Disable out of combat clicks when party members enter combat"] = "Отключить клики вне боя когда участники группы вступают в бой", + ["Edit macro"] = "Править макрос", + ["Enable/Disable click-sets"] = "Вкл/выкл наборы-кликов", + ["Enemy"] = "Враг", + ["Frame blacklist"] = "Черный список рамок", + ["Frame name"] = "Название рамки", + ["Friend"] = "Друг", + ["Global bindings (no target)"] = "Общие назначения (без цели)", + ["Hovercast bindings (target required)"] = "Назначения применения при наводе (требуется цель)", -- ????? + ["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] = "Для того, чтобы указать назначение, наведите курсор мыши на кнопку 'Установка назначения' и нажмите клавишу мышки или клавишу на клавиатуре. Вы можете изменить назначение, удерживая сочетание клавиш Alt, Ctrl и Shift.", + ["LAlt"] = 'Л-Alt', + ["LCtrl"] = 'Л-Ctrl', + ["LShift"] = 'Л-Shift', + ["LeftButton"] = "ЛКМ", + ["MiddleButton"] = "СКМ", + ["MousewheelDown"] = "КМВ", + ["MousewheelUp"] = "КМВ", + ["No binding set"] = "Нет назначения клавиш", + ["Open unit menu"] = "Открыть меню объекта", + ["Options"] = "Настройки", + ["Out-of-combat only"] = "Только вне боя", + ["Party member frames"] = "Рамки портретов группы", + ["Player frame"] = "Рамки группы", + ["Player's focus frame"] = "Рамка фокуса игрока", + ["Player's pet frame"] = "Рамка питомца игрока", + ["Player's target frame"] = "Рамка цели игрока", + ["Profile Management:"] = "Управление профилями:", + ["RAlt"] = 'П-Alt', + ["RCtrl"] = 'П-Ctrl', + ["RShift"] = 'П-Shift', + ["RightButton"] = "ПКМ", + ["Run custom macro"] = "Запустить свой макрос", + ["Run macro"] = "Запустить макрос", + ["Run macro '%s'"] = "Запустить макрос '%s'", + ["Save"] = "Сохранить", + ["Select All"] = "Выбрать все", + ["Select None"] = "Отменить выбор", + ["Select a binding type"] = "Выбрать тип назначания", + ["Select an options category"] = "Выбор категории опций", + ["Select profile: %s"] = "Выберите профиль: %s", + ["Set binding"] = "Установка назначения", + ["Set binding: %s"] = "Установка назначения: %s", + ["Shift"] = "Shift", + ["Show unit menu"] = "Показать меню объекта", + ["Specialization Slot "] = "Слот специализации ", + ["Swap profiles based on talent spec"] = "Переключать профиль в зависимости от набора талантов", + ["Target clicked unit"] = "Цель нажатия", + ["Target of focus frame"] = "Рамка цели фокуса", + ["Target of target frame"] = "Рамка цели цели", + ["These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded."] = "Эти опции управляют автоматическим регистрированием Clique определенных создаваемых рамок Blizzard для назначения. Внесенные изменения в эти параметры вступят в силу только после перезагрузки пользовательского интерфейса.", + ["This binding is DISABLED"] = "Это назначение ОТКЛЮЧЕНО", + ["This binding is invalid, please delete"] = "Это назначение является недействительным, пожалуйста удалите", + ["This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings."] = "Данная панель позволяет внести в черный список определенные рамки назначений Clique. Любые рамки, которые включены в этот список не будет регистрироваться. Для возврата их к своему первоначальному назначению, потребуется перезагрузить ваш пользовательский интерфейс.", + ["Trigger bindings on the 'down' portion of the click (requires reload)"] = "Триггер назначения на 'нижнюю' часть клика (требуется перезагрузка)", -- ?? + ["Unknown"] = "Неизвестно", + ["Unknown binding type '%s'"] = "Неизвестный тип назначения '%s'", + ["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."] = "Когда открыты оба окна, книга заклинаний и окно конфигурации назначений, вы можете установить новые назначения просто используя их на иконку заклинания в вашей книге заклинаний. Просто наведите курсор мыши на заклинания, а затем нажмите кнопку мышки или клавишу на клавиатуре вместе с любой комбинацией Alt, Ctrl и Shift. Новые назначения будет добавлен в вашу конфигурацию назначений.", + ["You are in Clique binding mode"] = "Вы находитесь в режиме назначения Clique", + ["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] = "Вы можете использовать эту страницу для создания пользовательских макросов для запуска при нажатии назначение на объекте. При создании этого макроса Вы должны учесть, что вам будет необходимо указать цель любого действия в макросе, используя 'mouseover', на какую цель вы нажимаете. Например, вы можете сделать любой из следующих:\n\n/cast [target=mouseover] Восстановление\n/cast [@mouseover] Восстановление\n/cast [@mouseovertarget] Провокацияn\nНаведите курсор мышки на кнопку ниже 'Установка назначения' и нажате кнопку мышки или клавишу с любым модификаторов. Затем отредактируйте поле ниже, для добавления макроса, который вы хотите запускать при применении этого назначения." +} + +addon:RegisterLocale('ruRU', baseLocale) diff --git a/Clique/i18n/Localization.zhCN.lua b/Clique/i18n/Localization.zhCN.lua new file mode 100644 index 0000000..46ff9df --- /dev/null +++ b/Clique/i18n/Localization.zhCN.lua @@ -0,0 +1,105 @@ +-- by yaroot (yaroot#gmail.com) +if (GetLocale() ~= 'zhCN') then return end + +local _, addon = ... +local baseLocale = { + ["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."] = "默认组别, 设置将对所有框体有效, 除非有其他组别的相同的按键设置", + ["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen."] = "敌对组别, 设置只对敌对的框体有效, 当你对敌对的框体点击施法时, 这个设置将被触发.", + ["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."] = "友善组别, 该组别设置只对友善的框体有效, 当你对友善的框体点击施法时, 该设置将被触发.", + ["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. These bindings are also active over unit frames."] = "全局组别, 该组别按键设置为悬浮式施法, 将鼠标移动到其他玩家/怪物或者头像框体上将可以直接对其施法.", + ["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."] = "非战斗组别, 这个组别的设置只在不战斗时有效.", + ["Accept"] = "接受", + ["Action"] = "动作", + ["Add new profile"] = "添加新配置", + ["Alt"] = "Alt", + ["Arena enemy frames"] = "竞技场敌对框体", + ["Bind other"] = "绑定其他", + ["Bind spell"] = "绑定技能", + ["Binding"] = "绑定", + ["Boss target frames"] = "首领目标框体", + ["Cancel"] = "取消", + ["Cast %s"] = "施放 %s", + ["Change binding"] = "修改绑定", + ["Change binding: %s"] = "修改绑定: %s", + ["Clique Binding Configuration"] = "Clique 设置", + ["Clique binding configuration"] = "Clique 设置", + ["Current Profile: "] = "当前配置: ", + ["(All specs)"] = "(所有专精)", + ["(Spec "] = "(专精 ", + ["(Spec-specific)"] = "(专精特定)", + ["Clique: 'default' binding-set"] = "组别: 默认", + ["Clique: 'enemy' binding-set"] = "组别: 敌对", + ["Clique: 'friend' binding-set"] = "组别: 友善", + ["Clique: 'global' binding-set"] = "组别: 全局", + ["Clique: 'hovercast' binding-set"] = "组别: 悬浮施法", + ["Clique: 'ooc' binding-set"] = "组别: 非战斗", + + ["Compact party frames"] = "小队框体", + ["Compact raid frames"] = "团队框体", + ["Configure binding: '%s'"] = "设置绑定: '%s'", + ["Ctrl"] = "Ctrl", + ["Current: "] = "当前: ", + ["Default"] = "默认", + ["Delete binding"] = "删除绑定", + ["Delete profile '%s'"] = "删除配置: '%s'", + ["Delete profile: %s"] = "Delete profile: %s", + ["Disable out of combat clicks when party members enter combat"] = "当队友进入战斗时禁用 '非战斗' 组别的快捷键", + ["Edit macro"] = "编辑宏", + ["Enable/Disable click-sets"] = "设置组别...", + ["Enemy"] = "敌对", + ["Frame name"] = "框体名", + ["Friend"] = "友善", + ["Global bindings (no target)"] = "全局按键 (无目标)", + ["Hovercast bindings (target required)"] = "悬浮按键 (需要目标)", + ["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] = "设置绑定先要将鼠标移动到 '设置绑定' 按钮上, 然后点击需要设置的快捷键. 你可以使用crtl/shift/alt之类的组合键.", + ["LAlt"] = '左Alt', + ["LCtrl"] = '左Ctrl', + ["LShift"] = '左Shift', + ["LeftButton"] = "左键点击", + ["MiddleButton"] = "中间点击", + ["MousewheelDown"] = "滚轮向下滚动", + ["MousewheelUp"] = "滚轮向上滚动", + ["No binding set"] = "无组别", + ["Open unit menu"] = "打开右键菜单", + ["Options"] = "设置", + ["Out-of-combat only"] = "非战斗", + ["Party member frames"] = "小队框体", + ["Player frame"] = "玩家头像", + ["Player's focus frame"] = "焦点头像", + ["Player's pet frame"] = "宠物头像", + ["Player's target frame"] = "目标头像", + ["Profile Management:"] = "配置管理", + ["RAlt"] = '右Alt', + ["RCtrl"] = '右Ctrl', + ["RShift"] = '右Shift', + ["RightButton"] = "右键点击", + ["Run custom macro"] = "运行自定义宏", + ["Run macro"] = "运行宏", + ["Run macro '%s'"] = "运行宏 '%s'", + ["Save"] = "保存", + ["Select All"] = "选择所有", + ["Select None"] = "清除所有选择", + ["Select a binding type"] = "选择绑定类型", + ["Select profile: %s"] = "选择配置: %s", + ["Set binding"] = "设置绑定", + ["Set binding: %s"] = "设置绑定: %s", + ["Shift"] = true, + ["Show unit menu"] = "打开右键菜单", + ["Specialization Slot "] = "天赋栏 ", + ["Swap profiles based on talent spec"] = "随天赋切换配置", + ["Target clicked unit"] = "选中点击单位", + ["Target of focus frame"] = "焦点的目标", + ["Target of target frame"] = "目标的目标", + ["These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded."] = "这些设置将决定 Clique 是否将原始界面的框体注册按键绑定. 修改该设置将只在重载界面后有效.", + ["This binding is DISABLED"] = "此项设置已被 >禁用<", + ["This binding is invalid, please delete"] = "非法设置, 请删除", + ["This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings."] = "在这页设置里, 你可以勾选框体以将其加入黑名单来防止 Clique 设置其点击施法. 任何你选中的框体都不会被 Clique 注册, 该页设置改动将在重载后生效.", + ["Trigger bindings on the 'down' portion of the click (requires reload)"] = "在'按下'按键时触发(需要重载)", + ["Unknown"] = "未知", + ["Unknown binding type '%s'"] = "未知绑定类型 '%s'", + ["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."] = "当 Clique 设置界面 和 法术书 同时打开时, 你只要用需要设置的快捷键点击法术就可以将其添加到 Clique 设置中. 将鼠标移动到法术图标上, 同时可以按下 ctrl, alt 和 shift 中的一个或多个, 然后点击鼠标的某一个按键, 这项设置就会被添加.", + ["You are in Clique binding mode"] = "你现在处于 Clique 设置模式中", + ["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] = "你可以在这页创建一个用于点击施放的自定义宏. 撰写宏时需要注意, 你必须将施法动作的目标指定为 'mouseover'. 例如, 你可以使用以下内容:\n\n/cast [target=mouseover] 愈合\n/cast [@mouseovertarget] 嘲讽\n\n将鼠标移动到 设置绑定 按钮上然后按下鼠标和ctrl/alt/shift键来创建绑定. 然后撰写你需要运行的宏" +} + +addon:RegisterLocale('zhCN', baseLocale) diff --git a/Clique/i18n/Localization.zhTW.lua b/Clique/i18n/Localization.zhTW.lua new file mode 100644 index 0000000..cdfe483 --- /dev/null +++ b/Clique/i18n/Localization.zhTW.lua @@ -0,0 +1,106 @@ +-- by yaroot (yaroot#gmail.com) + +if(GetLocale() ~= 'zhTW') then return end + +local _, addon = ... +local baseLocale = { + ["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."] = "默認組別, 設置將對所有框體有效, 除非有其他組別的相同的按鍵設置", + ["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, ie those you can attack. If you click on a unit that you cannot attack, nothing will happen." ] = "敵對組別, 設置只對敵對的框體有效, 當你對敵對的框體點擊施法時, 這個設置將被觸發.", + ["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, ie those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."] = "友善組別, 該組別設置只對友善的框體有效, 當你對友善的框體點擊施法時, 該設置將被觸發.", + ["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. These bindings are also active over unit frames."] = "全局組別, 該組別按鍵設置為懸浮式施法, 將鼠標移動到其他玩家/怪物或者頭像框體上將可以直接對其施法.", + ["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."] = "非戰鬥組別, 這個組別的設置只在不戰鬥時有效.", + ["Accept"] = "接受", + ["Action"] = "動作", + ["Add new profile"] = "添加新配置", + ["Alt"] = "Alt", + ["Arena enemy frames"] = "競技場敵對框體", + ["Bind other"] = "綁定其他", + ["Bind spell"] = "綁定技能", + ["Binding"] = "綁定", + ["Boss target frames"] = "首領目標框體", + ["Cancel"] = "取消", + ["Cast %s"] = "施放 %s", + ["Change binding"] = "修改綁定", + ["Change binding: %s"] = "修改綁定: %s", + ["Clique Binding Configuration"] = "Clique 設置", + ["Clique binding configuration"] = "Clique 設置", + ["Current Profile: "] = "當前配置: ", + ["(All specs)"] = "(所有專精)", + ["(Spec "] = "(專精 ", + ["(Spec-specific)"] = "(專精特定)", + ["Clique: 'default' binding-set"] = "組別: 默認", + ["Clique: 'enemy' binding-set"] = "組別: 敵對", + ["Clique: 'friend' binding-set"] = "組別: 友善", + ["Clique: 'global' binding-set"] = "組別: 全局", +["Clique: 'hovercast' binding-set"] = "組別: 懸浮施法", + ["Clique: 'ooc' binding-set"] = "組別: 非戰鬥", + + ["Compact party frames"] = "小隊框體", + ["Compact raid frames"] = "團隊框體", + ["Configure binding: '%s'"] = "設置綁定: '%s'", + ["Ctrl"] = "Ctrl", + ["Current: "] = "當前: ", + ["Default"] = "默認", + ["Delete binding"] = "刪除綁定", + ["Delete profile '%s'"] = "刪除配置: '%s'", +["Delete profile: %s"] = "Delete profile: %s", + ["Disable out of combat clicks when party members enter combat"] = "當隊友進入戰鬥時禁用'非戰鬥' 組別的快捷鍵", +["Edit macro"] = "編輯宏", + ["Enable/Disable click-sets"] = "設置組別...", + ["Enemy"] = "敵對", + ["Frame name"] = "框體名", + ["Friend"] = "友善", +["Global bindings (no target)"] = "全局按鍵(無目標)", +["Hovercast bindings (target required)"] = "懸浮按鍵(需要目標)", + ["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] = "設置綁定先要將鼠標移動到'設置綁定' 按鈕上, 然後點擊需要設置的快捷鍵. 你可以使用crtl/shift/alt之類的組合鍵." , + ["LAlt"] = '左Alt', + ["LCtrl"] = '左Ctrl', + ["LShift"] = '左Shift', + ["LeftButton"] = "左鍵點擊", + ["MiddleButton"] = "中間點擊", + ["MousewheelDown"] = "滾輪向下滾動", + ["MousewheelUp"] = "滾輪向上滾動", + ["No binding set"] = "無組別", + ["Open unit menu"] = "打開右鍵菜單", + ["Options"] = "設置", + ["Out-of-combat only"] = "非戰鬥", + ["Party member frames"] = "小隊框體", + ["Player frame"] = "玩家頭像", + ["Player's focus frame"] = "焦點頭像", + ["Player's pet frame"] = "寵物頭像", + ["Player's target frame"] = "目標頭像", + ["Profile Management:"] = "配置管理", + ["RAlt"] = '右Alt', + ["RCtrl"] = '右Ctrl', + ["RShift"] = '右Shift', + ["RightButton"] = "右鍵點擊", + ["Run custom macro"] = "運行自定義宏", + ["Run macro"] = "運行宏", + ["Run macro '%s'"] = "運行宏 '%s'", + ["Save"] = "保存", + ["Select All"] = "選擇所有", + ["Select None"] = "清除所有選擇", + ["Select a binding type"] = "選擇綁定類型", + ["Select profile: %s"] = "選擇配置: %s", + ["Set binding"] = "設置綁定", + ["Set binding: %s"] = "設置綁定: %s", + ["Shift"] = true, + ["Show unit menu"] = "打開右鍵菜單", + ["Specialization Slot "] = "天賦欄 ", + ["Swap profiles based on talent spec"] = "隨天賦切換配置", + ["Target clicked unit"] = "選中點擊單位", + ["Target of focus frame"] = "焦點的目標", + ["Target of target frame"] = "目標的目標", + ["These options control whether or not Clique automatically registers certain Blizzard-created frames for binding. Changes made to these settings will not take effect until the user interface is reloaded."] = "這些設置將決定Clique 是否將原始界面的框體註冊按鍵綁定. 修改該設置將只在重載界面後有效.", + ["This binding is DISABLED"] = "此項設置已被>禁用<", +["This binding is invalid, please delete"] = "非法設置, 請刪除", + ["This panel allows you to blacklist certain frames from being included for Clique bindings. Any frames that are selected in this list will not be registered, although you may have to reload your user interface to have them return to their original bindings."] = "在這頁設置裡, 你可以勾選框體以將其加入黑名單來防止Clique 設置其點擊施法. 任何你選中的框體都不會被Clique 註冊, 該頁設置改動將在重載後生效.", + ["Trigger bindings on the 'down' portion of the click (requires reload)"] = "在'按下'按鍵時觸發(需要重載)", +["Unknown"] = "未知", + ["Unknown binding type '%s'"] = "未知綁定類型'%s'", + ["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."] = "當Clique 設置界面和法術書同時打開時, 你只要用需要設置的快捷鍵點擊法術就可以將其添加到Clique 設置中. 將鼠標移動到法術圖標上, 同時可以按下ctrl, alt 和shift 中的一個或多個, 然後點擊鼠標的某一個按鍵, 這項設置就會被添加." , +["You are in Clique binding mode"] = "你現在處於Clique 設置模式中", + ["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the ' mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [ @mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] = "你可以在這頁創建一個用於點擊施放的自定義宏. 撰寫宏時需要注意, 你必須將施法動作的目標指定為'mouseover'. 例如, 你可以使用以下內容:\ n\n/cast [target=mouseover] 癒合\n/cast [@mouseovertarget] 嘲諷\n\n將鼠標移動到設置綁定按鈕上然後按下鼠標和ctrl/alt/shift鍵來創建綁定. 然後撰寫你需要運行的宏", +} + +addon:RegisterLocale('zhTW', baseLocale) diff --git a/Clique/images/CliqueIcon.tga b/Clique/images/CliqueIcon.tga deleted file mode 100644 index 0219f56..0000000 Binary files a/Clique/images/CliqueIcon.tga and /dev/null differ diff --git a/Clique/images/RadioChecked.tga b/Clique/images/RadioChecked.tga deleted file mode 100644 index 3b5391a..0000000 Binary files a/Clique/images/RadioChecked.tga and /dev/null differ diff --git a/Clique/images/RadioEmpty.tga b/Clique/images/RadioEmpty.tga deleted file mode 100644 index 6509c74..0000000 Binary files a/Clique/images/RadioEmpty.tga and /dev/null differ diff --git a/Clique/images/backdrop.tga b/Clique/images/backdrop.tga deleted file mode 100644 index f061cda..0000000 Binary files a/Clique/images/backdrop.tga and /dev/null differ diff --git a/Clique/images/borders.tga b/Clique/images/borders.tga deleted file mode 100644 index 42f9e27..0000000 Binary files a/Clique/images/borders.tga and /dev/null differ diff --git a/Clique/images/footCorner.tga b/Clique/images/footCorner.tga deleted file mode 100644 index 3780e58..0000000 Binary files a/Clique/images/footCorner.tga and /dev/null differ diff --git a/Clique/images/footer.tga b/Clique/images/footer.tga deleted file mode 100644 index 0983828..0000000 Binary files a/Clique/images/footer.tga and /dev/null differ diff --git a/Clique/images/headCorner.tga b/Clique/images/headCorner.tga deleted file mode 100644 index 83b28fc..0000000 Binary files a/Clique/images/headCorner.tga and /dev/null differ diff --git a/Clique/images/header.tga b/Clique/images/header.tga deleted file mode 100644 index 7266823..0000000 Binary files a/Clique/images/header.tga and /dev/null differ diff --git a/Clique/images/icon_circle_128.tga b/Clique/images/icon_circle_128.tga new file mode 100644 index 0000000..0e40d0d Binary files /dev/null and b/Clique/images/icon_circle_128.tga differ diff --git a/Clique/images/icon_square_64.tga b/Clique/images/icon_square_64.tga new file mode 100644 index 0000000..362fcb1 Binary files /dev/null and b/Clique/images/icon_square_64.tga differ diff --git a/Clique/images/myborder.tga b/Clique/images/myborder.tga deleted file mode 100644 index 700c1ba..0000000 Binary files a/Clique/images/myborder.tga and /dev/null differ diff --git a/Clique/modules/Blizzard_utils.lua b/Clique/modules/Blizzard_utils.lua new file mode 100644 index 0000000..1fc3196 --- /dev/null +++ b/Clique/modules/Blizzard_utils.lua @@ -0,0 +1,45 @@ +--[[------------------------------------------------------------------------- +-- BlizzardFrames.lua +-- +-- This file contains the definitions of the blizzard frame integration +-- options. These settings will not apply until the user interface is +-- reloaded. +-- +-- Events registered: +-- * ADDON_LOADED - To watch for loading of the ArenaUI +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +-- Register a Blizzard frame for click-casting, with some additional protection +function addon:RegisterBlizzardFrame(frame) + -- Stash the frame in case we later convert it + local frameName = frame + + -- Convert a frame name to the global object + if type(frame) == "string" then + frameName = frame + frame = _G[frameName] + if not frame then + addon:Printf("Error registering frame: %s", tostring(frameName)) + return + end + end + + if not frame then + addon:Printf("Unable to register empty frame: %s", tostring(frameName)) + return + end + + -- Never allow forbidden frames, we can't do anything with those! + local forbidden = frame.IsForbidden and frame:IsForbidden() + if forbidden then return end + + local buttonish = frame and frame.RegisterForClicks + local protected = frame.IsProtected and frame:IsProtected() + local anchorRestricted = frame.IsAnchoringRestricted and frame:IsAnchoringRestricted() + + -- A frame must be a button, and must be protected, and must not be a nameplate, anchor restricted + local valid = buttonish and protected and (not anchorRestricted) + if valid then addon:RegisterFrame(frame) end +end diff --git a/Clique/modules/Blizzard_wrath.lua b/Clique/modules/Blizzard_wrath.lua new file mode 100644 index 0000000..e0c273e --- /dev/null +++ b/Clique/modules/Blizzard_wrath.lua @@ -0,0 +1,77 @@ +--[[------------------------------------------------------------------------- +-- Blizzard_wrath.lua +-- +-- This file contains the definitions of the blizzard frame integration +-- options. These settings will not apply until the user interface is +-- reloaded. +-- +-- Events registered: +-- * ADDON_LOADED - To watch for loading of the ArenaUI +-------------------------------------------------------------------------]] -- +local addonName, addon = ... +local L = addon.L + +-- addon:Printf("Loading Blizzard_wrath integration") + +local waitForAddon + +function addon:IntegrateBlizzardFrames() + self:Wrath_BlizzSelfFrames() + self:Wrath_BlizzPartyFrames() + self:Wrath_BlizzBossFrames() + + -- Check for both standard CompactRaidFrame and Ascension version + if IsAddOnLoaded("CompactRaidFrame") or IsAddOnLoaded("Ascension_CompactRaidFrames") then + self:Wrath_BlizzCompactUnitFrames() + else + waitForAddon = CreateFrame("Frame") + waitForAddon["CompactRaidFrame"] = "Wrath_BlizzCompactUnitFrames" + waitForAddon["Ascension_CompactRaidFrames"] = "Wrath_BlizzCompactUnitFrames" + end + + if waitForAddon then + waitForAddon:RegisterEvent("ADDON_LOADED") + waitForAddon:SetScript("OnEvent", function(frame, event, ...) if waitForAddon[...] then self[waitForAddon[...]](self) end end) + + if not next(waitForAddon) then + waitForAddon:UnregisterEvent("ADDON_LOADED") + waitForAddon:SetScript("OnEvent", nil) + end + end +end + +function addon:Wrath_BlizzCompactUnitFrames() + if not addon.settings.blizzframes.compactraid then return end + + -- For standard CompactRaidFrame addon + if CompactUnitFrame_SetUpFrame then hooksecurefunc("CompactUnitFrame_SetUpFrame", function(frame, ...) addon:RegisterBlizzardFrame(frame) end) end + + -- For Ascension CompactRaidFrames addon + if CompactUnitMixin then hooksecurefunc(CompactUnitMixin, "SetUpFrame", function(frame, ...) addon:RegisterBlizzardFrame(frame) end) end +end + +function addon:Wrath_BlizzSelfFrames() + local frames = {"PlayerFrame", "PetFrame", "TargetFrame", "TargetFrameToT"} + + -- Add focus frames for Wrath + table.insert(frames, "FocusFrame") + table.insert(frames, "FocusFrameToT") + + for idx, frame in ipairs(frames) do if addon.settings.blizzframes[frame] then addon:RegisterBlizzardFrame(frame) end end +end + +function addon:Wrath_BlizzPartyFrames() + if not addon.settings.blizzframes.party then return end + + local frames = {"PartyMemberFrame1", "PartyMemberFrame2", "PartyMemberFrame3", "PartyMemberFrame4", -- "PartyMemberFrame5", + "PartyMemberFrame1PetFrame", "PartyMemberFrame2PetFrame", "PartyMemberFrame3PetFrame", "PartyMemberFrame4PetFrame" -- "PartyMemberFrame5PetFrame", + } + for idx, frame in ipairs(frames) do addon:RegisterBlizzardFrame(frame) end +end + +function addon:Wrath_BlizzBossFrames() + if not addon.settings.blizzframes.boss then return end + + local frames = {"Boss1TargetFrame", "Boss2TargetFrame", "Boss3TargetFrame", "Boss4TargetFrame"} + for idx, frame in ipairs(frames) do addon:RegisterBlizzardFrame(frame) end +end diff --git a/README.md b/README.md index 9ebb3ec..2c2c408 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Clique -This is the repository for Clique. Modified for Ascension.gg. \ No newline at end of file +This is the repository for Clique. Modified for Ascension.gg.