/branches
--ARE YOU A PRIEST?-- |
if select(2, UnitClass("player")) ~= "PRIEST" then |
return |
end |
--DEFINE MY LOCALS-- |
--local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local player = UnitName("player") |
--local spell, spell2 |
local resurrectionSpellID = { [2006] = true, --rank 1 resurrection |
[2010] = true, --rank 2 resurrection |
[10880] = true, --rank 3 resurrection |
[10881] = true, --rank 4 resurrection |
[20770] = true, --rank 5 resurrection |
[25435] = true --rank 6 resurrection |
} |
-------------------------------------- |
--VOODOO MAGIC STUFF-- |
-------------------------------------- |
--function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, eID, eName) |
function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, _, eventType, _, srcName, _, _, _, dstFlags, spellID, spellName, spellSchool, eID, eName) |
--IS THE ADDON IN STANDBY?-- |
if not Broadcaster.db.profile.standby then |
--FILTER THE COMBAT LOG-- |
-- local isDestEnemy = (bit.band(dstFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) |
-- local isDestTarget = (bit.band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET) |
-- local isDestFocus = (bit.band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS) |
--AM I CASTING?-- |
if eventType == "SPELL_CAST_START" and srcName == player then |
--AND AM I CASTING RESURRECTION?-- |
if resurrectionSpellID[spellID] then |
local max = #BroadcasterPriestMsgs.Resurrection |
local random = math.random(max) |
SendChatMessage(BroadcasterPriestMsgs.Resurrection[random] , Broadcaster.db.profile.channel) |
end |
end |
end |
end |
--ARE YOU A DRUID?-- |
if select(2, UnitClass("player")) ~= "DRUID" then |
return |
end |
--DEFINE MY LOCALS-- |
--local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local player = UnitName("player") |
--local spell, spell2 |
local rebirthSpellID = { [20484] = true, --rank 1 rebirth |
[20739] = true, --rank 2 rebirth |
[20742] = true, --rank 3 rebirth |
[20747] = true, --rank 4 rebirth |
[20748] = true, --rank 5 rebirth |
[26994] = true --rank 6 rebirth |
} |
-------------------------------------- |
--VOODOO MAGIC STUFF-- |
-------------------------------------- |
--function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, eID, eName) |
function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, _, eventType, _, srcName, _, _, _, dstFlags, spellID, spellName, spellSchool, eID, eName) |
--IS THE ADDON IN STANDBY?-- |
if not Broadcaster.db.profile.standby then |
--FILTER THE COMBAT LOG-- |
-- local isDestEnemy = (bit.band(dstFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) |
-- local isDestTarget = (bit.band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET) |
-- local isDestFocus = (bit.band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS) |
--AM I CASTING?-- |
if eventType == "SPELL_CAST_START" and srcName == player then |
--AND AM I CASTING REBIRTH?-- |
if rebirthSpellID[spellID] then |
local max = #BroadcasterDruidMsgs.Rebirth |
local random = math.random(max) |
SendChatMessage(BroadcasterDruidMsgs.Rebirth[random] , Broadcaster.db.profile.channel) |
end |
end |
end |
end |
--ARE YOU A SHAMAN?-- |
if select(2, UnitClass("player")) ~= "SHAMAN" then |
return |
end |
--DEFINE MY LOCALS-- |
--local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local player = UnitName("player") |
--local spell, spell2 |
local ancSpiritSpellID = { [2008] = true, --rank 1 ancSpirit |
[20609] = true, --rank 2 ancSpirit |
[20610] = true, --rank 3 ancSpirit |
[20776] = true, --rank 4 ancSpirit |
[20777] = true, --rank 5 ancSpirit |
} |
-------------------------------------- |
--VOODOO MAGIC STUFF-- |
-------------------------------------- |
--function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, eID, eName) |
function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, _, eventType, _, srcName, _, _, _, dstFlags, spellID, spellName, spellSchool, eID, eName) |
--IS THE ADDON IN STANDBY?-- |
if not Broadcaster.db.profile.standby then |
--FILTER THE COMBAT LOG-- |
-- local isDestEnemy = (bit.band(dstFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) |
-- local isDestTarget = (bit.band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET) |
-- local isDestFocus = (bit.band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS) |
--AM I CASTING?-- |
if eventType == "SPELL_CAST_START" and srcName == player then |
--AND AM I CASTING ANCESTRALSPIRIT?-- |
if ancSpiritSpellID[spellID] then |
local max = #BroadcasterShamanMsgs.AncestralSpirit |
local random = math.random(max) |
SendChatMessage(BroadcasterShamanMsgs.AncestralSpirit[random] , Broadcaster.db.profile.channel) |
end |
end |
end |
end |
--ARE YOU A PALADIN?-- |
if select(2, UnitClass("player")) ~= "PALADIN" then |
return |
end |
--DEFINE MY LOCALS-- |
--local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local player = UnitName("player") |
--local spell, spell2 |
local redemptionSpellID = { [7328] = true, --rank 1 redemption |
[10322] = true, --rank 2 redemption |
[10324] = true, --rank 3 redemption |
[20772] = true, --rank 4 redemption |
[20773] = true, --rank 5 redemption |
} |
-------------------------------------- |
--VOODOO MAGIC STUFF-- |
-------------------------------------- |
--function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, eID, eName) |
function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, _, eventType, _, srcName, _, _, _, dstFlags, spellID, spellName, spellSchool, eID, eName) |
--IS THE ADDON IN STANDBY?-- |
if not Broadcaster.db.profile.standby then |
--FILTER THE COMBAT LOG-- |
-- local isDestEnemy = (bit.band(dstFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) |
-- local isDestTarget = (bit.band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET) |
-- local isDestFocus = (bit.band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS) |
--AM I CASTING?-- |
if eventType == "SPELL_CAST_START" and srcName == player then |
--AND AM I CASTING REDEMPTION?-- |
if redemptionSpellID[spellID] then |
local max = #BroadcasterPaladinMsgs.Redemption |
local random = math.random(max) |
SendChatMessage(BroadcasterPaladinMsgs.Redemption[random] , Broadcaster.db.profile.channel) |
end |
end |
end |
end |
--ARE YOU A MAGE?-- |
if select(2, UnitClass("player")) ~= "MAGE" then |
return |
end |
--DEFINE MY LOCALS-- |
local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local player = UnitName("player") |
local spell, spell2 |
local polySpellID = { [118] = true, --rank 1 sheep |
[12824] = true, --rank 2 sheep |
[12825] = true, --rank 3 sheep |
[12826] = true, --rank 4 sheep |
[28271] = true, --rank 1 turtle |
[28272] = true --rank 1 pig |
} |
-------------------------------------- |
--VOODOO MAGIC STUFF-- |
-------------------------------------- |
--function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, eID, eName) |
function Broadcaster:COMBAT_LOG_EVENT_UNFILTERED(event, _, eventType, _, srcName, _, _, _, dstFlags, spellID, spellName, spellSchool, eID, eName) |
--IS THE ADDON IN STANDBY?-- |
if not Broadcaster.db.profile.standby then |
--FILTER THE COMBAT LOG-- |
-- local isDestEnemy = (bit.band(dstFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE) |
-- local isDestTarget = (bit.band(dstFlags, COMBATLOG_OBJECT_TARGET) == COMBATLOG_OBJECT_TARGET) |
local isDestFocus = (bit.band(dstFlags, COMBATLOG_OBJECT_FOCUS) == COMBATLOG_OBJECT_FOCUS) |
if eventType == "SPELL_CAST_SUCCESS" and srcName == player then --removed "and isDestFocus" to try to remedy Zidomo's problem |
--CHECK FOR PoM POLY-- |
if polySpellID[spellID] then |
spell = spellID |
end |
--CHECK FOR RITUAL OF REFRESHMENT-- |
if spellID == 43987 and BroadcasterChannel then |
local max = #BroadcasterMageMsgs.Table |
local random = math.random(max) |
SendChatMessage(BroadcasterMageMsgs.Table[random] , Broadcaster.db.profile.channel) |
end |
end |
--AM I CASTING?-- |
if eventType == "SPELL_CAST_START" and srcName == player then |
--AND AM I CASTING POLYMORPH?-- |
if polySpellID[spellID] then |
spell = spellID |
end |
end |
--DID MY FOCUS GET POLYMORPHED?-- |
if eventType == "SPELL_AURA_APPLIED" and isDestFocus then |
if polySpellID[spellID] then |
spell2 = spellID |
--WAS IT CAST BY ME?-- |
if spell == spell2 then --and BroadcasterChannel then |
--IF PIG, THEN PIGGIE MESSAGE-- |
if spell2 == 28272 then |
local max = #BroadcasterMageMsgs.PolyPig |
local random = math.random(max) |
SendChatMessage(BroadcasterMageMsgs.PolyPig[random] , Broadcaster.db.profile.channel) |
--IF TURTLE, THEN TURTLE MESSAGE-- |
elseif spell2 == 28271 then |
local max = #BroadcasterMageMsgs.PolyTurtle |
local random = math.random(max) |
SendChatMessage(BroadcasterMageMsgs.PolyTurtle[random] , Broadcaster.db.profile.channel) |
--IF SHEEP, THEN SHEEP MESSAGE-- |
else |
local max = #BroadcasterMageMsgs.PolySheep |
local random = math.random(max) |
SendChatMessage(BroadcasterMageMsgs.PolySheep[random] , Broadcaster.db.profile.channel) |
end |
--CLEAR SPELL IDs JUST IN CASE ANOTHER MAGE RESHEEPS MY FOCUS-- |
spell, spell2 = nil |
end |
end |
end |
end |
end |
--PORTAL MESSAGES-- |
function Broadcaster:UNIT_SPELLCAST_START() |
--IS THE ADDON IN STANDBY AND AM I IN A GROUP?-- |
if not Broadcaster.db.profile.standby and Broadcaster.db.profile.channel then |
--LOOK FOR PORTAL SPELL CAST BY ME-- |
if arg1 == "player" and string.match(arg2, L["Portal:"]) then |
--TRIM SPELL NAME TO GET DESTINATION-- |
local dest = strtrim(select(2, strsplit(":", arg2))) |
--GET RANDOM MESSAGE AND REPLACE %d WITH DEST-- |
local max = #BroadcasterMageMsgs.Portals |
local random = math.random(max) |
local msg,_ = string.gsub(BroadcasterMageMsgs.Portals[random], "%%d", dest) |
SendChatMessage(msg , Broadcaster.db.profile.channel) |
end |
end |
end |
----------------------------------- |
--MEAT AND POTATOES-- |
----------------------------------- |
Broadcaster = LibStub("AceAddon-3.0"):NewAddon("Broadcaster", "AceConsole-3.0", "AceEvent-3.0") |
local L = LibStub("AceLocale-3.0"):GetLocale("Broadcaster") |
local chan = {"RAID", "PARTY", "Self"} |
--OPTIONS-- |
local options = { |
name = "Broadcaster", |
handler = Broadcaster, |
type = "group", |
args = { |
standby = { |
name = L["Standby"], |
desc = L["Puts the addon into standby mode, so no messages are sent."], |
type = "toggle", |
get = function() return Broadcaster.db.profile.standby end, |
set = function() Broadcaster.db.profile.standby = not Broadcaster.db.profile.standby end |
}, |
config = { |
name = L["Config"], |
desc = L["Opens the options window."], |
type = "execute", |
func = function() |
LibStub("AceConfigDialog-3.0"):Open("Broadcaster") |
end |
}, |
channel = { |
name = "Channel", |
desc = "The channel to broadcast your messages to.", |
type = "select", |
values = {"RAID", "PARTY", "Self"}, |
get = function() |
for k, v in pairs(chan) do |
if Broadcaster.db.profile.channel == v then |
return k |
end |
end |
end, |
set = function(_, channel) |
Broadcaster.db.profile.channel = chan[channel] |
end, |
}, |
}, |
} |
function Broadcaster:OnInitialize() |
self.db = LibStub("AceDB-3.0"):New("BroadcasterDB", defaults, "Default") |
LibStub("AceConfig-3.0"):RegisterOptionsTable("Broadcaster", options, {"broadcaster", "bc"}) |
self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Broadcaster", "Broadcaster") |
end |
function Broadcaster:OnEnable() |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
--[[ self:RegisterEvent("PARTY_MEMBERS_CHANGED") |
self:RegisterEvent("RAID_ROSTER_UPDATE") |
-- self:RegisterEvent("UNIT_SPELLCAST_START") |
--CHECK IF I'M ALREADY IN A PARTY OR RAID (FROM A RELOAD UI OR DISCONNECT)-- |
if GetNumPartyMembers() > 0 then |
BroadcasterChannel = "PARTY" |
elseif GetNumRaidMembers() > 0 then |
BroadcasterChannel = "RAID" |
end]]-- |
end |
--[[ |
--AM I IN A PARTY?-- |
function Broadcaster:PARTY_MEMBERS_CHANGED() |
if GetNumPartyMembers() > 0 then |
BroadcasterChannel = "PARTY" |
end |
end |
--AM I IN A RAID?-- |
function Broadcaster:RAID_ROSTER_UPDATE() |
if GetNumRaidMembers() > 0 then |
BroadcasterChannel = "RAID" |
end |
end |
]]-- |
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info |
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke |
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! |
local LibStub = _G[LIBSTUB_MAJOR] |
if not LibStub or LibStub.minor < LIBSTUB_MINOR then |
LibStub = LibStub or {libs = {}, minors = {} } |
_G[LIBSTUB_MAJOR] = LibStub |
LibStub.minor = LIBSTUB_MINOR |
function LibStub:NewLibrary(major, minor) |
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") |
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") |
local oldminor = self.minors[major] |
if oldminor and oldminor >= minor then return nil end |
self.minors[major], self.libs[major] = minor, self.libs[major] or {} |
return self.libs[major], oldminor |
end |
function LibStub:GetLibrary(major, silent) |
if not self.libs[major] and not silent then |
error(("Cannot find a library instance of %q."):format(tostring(major)), 2) |
end |
return self.libs[major], self.minors[major] |
end |
function LibStub:IterateLibraries() return pairs(self.libs) end |
setmetatable(LibStub, { __call = LibStub.GetLibrary }) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceLocale-3.0.lua"/> |
</Ui> |
--[[ $Id: AceLocale-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]] |
local MAJOR,MINOR = "AceLocale-3.0", 1 |
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceLocale then return end -- no upgrade needed |
local gameLocale = GetLocale() |
if gameLocale == "enGB" then |
gameLocale = "enUS" |
end |
AceLocale.apps = AceLocale.apps or {} -- array of ["AppName"]=localetableref |
AceLocale.appnames = AceLocale.appnames or {} -- array of [localetableref]="AppName" |
-- This metatable is used on all tables returned from GetLocale |
local readmeta = { |
__index = function(self, key) -- requesting totally unknown entries: fire off a nonbreaking error and return key |
geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'") |
rawset(self, key, key) -- only need to see the warning once, really |
return key |
end |
} |
-- Remember the locale table being registered right now (it gets set by :NewLocale()) |
local registering |
-- local assert false function |
local assertfalse = function() assert(false) end |
-- This metatable proxy is used when registering nondefault locales |
local writeproxy = setmetatable({}, { |
__newindex = function(self, key, value) |
rawset(registering, key, value == true and key or value) -- assigning values: replace 'true' with key string |
end, |
__index = assertfalse |
}) |
-- This metatable proxy is used when registering the default locale. |
-- It refuses to overwrite existing values |
-- Reason 1: Allows loading locales in any order |
-- Reason 2: If 2 modules have the same string, but only the first one to be |
-- loaded has a translation for the current locale, the translation |
-- doesn't get overwritten. |
-- |
local writedefaultproxy = setmetatable({}, { |
__newindex = function(self, key, value) |
if not rawget(registering, key) then |
rawset(registering, key, value == true and key or value) |
end |
end, |
__index = assertfalse |
}) |
-- AceLocale:NewLocale(application, locale, isDefault) |
-- |
-- application (string) - unique name of addon / module |
-- locale (string) - name of locale to register, e.g. "enUS", "deDE", etc... |
-- isDefault (string) - if this is the default locale being registered |
-- |
-- Returns a table where localizations can be filled out, or nil if the locale is not needed |
function AceLocale:NewLocale(application, locale, isDefault) |
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed |
-- Ammo: I still think this is a bad idea, for instance an addon that checks for some ingame string will fail, just because some other addon |
-- gives the user the illusion that they can run in a different locale? Ditch this whole thing or allow a setting per 'application'. I'm of the |
-- opinion to remove this. |
local gameLocale = GAME_LOCALE or gameLocale |
if locale ~= gameLocale and not isDefault then |
return -- nop, we don't need these translations |
end |
local app = AceLocale.apps[application] |
if not app then |
app = setmetatable({}, readmeta) |
AceLocale.apps[application] = app |
AceLocale.appnames[app] = application |
end |
registering = app -- remember globally for writeproxy and writedefaultproxy |
if isDefault then |
return writedefaultproxy |
end |
return writeproxy |
end |
-- AceLocale:GetLocale(application [, silent]) |
-- |
-- application (string) - unique name of addon |
-- silent (boolean) - if true, the locale is optional, silently return nil if it's not found |
-- |
-- Returns localizations for the current locale (or default locale if translations are missing) |
-- Errors if nothing is registered (spank developer, not just a missing translation) |
function AceLocale:GetLocale(application, silent) |
if not silent and not AceLocale.apps[application] then |
error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2) |
end |
return AceLocale.apps[application] |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="CallbackHandler-1.0.lua"/> |
</Ui> |
--[[ $Id: CallbackHandler-1.0.lua 60697 2008-02-09 16:51:20Z nevcairiel $ ]] |
local MAJOR, MINOR = "CallbackHandler-1.0", 3 |
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) |
if not CallbackHandler then return end -- No upgrade needed |
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} |
local type = type |
local pcall = pcall |
local pairs = pairs |
local assert = assert |
local concat = table.concat |
local loadstring = loadstring |
local next = next |
local select = select |
local type = type |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local next, xpcall, eh = ... |
local method, ARGS |
local function call() method(ARGS) end |
local function dispatch(handlers, ...) |
local index |
index, method = next(handlers) |
if not method then return end |
local OLD_ARGS = ARGS |
ARGS = ... |
repeat |
xpcall(call, eh) |
index, method = next(handlers, index) |
until not method |
ARGS = OLD_ARGS |
end |
return dispatch |
]] |
local ARGS, OLD_ARGS = {}, {} |
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end |
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
-------------------------------------------------------------------------- |
-- CallbackHandler:New |
-- |
-- target - target object to embed public APIs in |
-- RegisterName - name of the callback registration API, default "RegisterCallback" |
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" |
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. |
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused) |
-- TODO: Remove this after beta has gone out |
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused") |
RegisterName = RegisterName or "RegisterCallback" |
UnregisterName = UnregisterName or "UnregisterCallback" |
if UnregisterAllName==nil then -- false is used to indicate "don't want this method" |
UnregisterAllName = "UnregisterAllCallbacks" |
end |
-- we declare all objects and exported APIs inside this closure to quickly gain access |
-- to e.g. function names, the "target" parameter, etc |
-- Create the registry object |
local events = setmetatable({}, meta) |
local registry = { recurse=0, events=events } |
-- registry:Fire() - fires the given event/message into the registry |
function registry:Fire(eventname, ...) |
if not rawget(events, eventname) or not next(events[eventname]) then return end |
local oldrecurse = registry.recurse |
registry.recurse = oldrecurse + 1 |
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) |
registry.recurse = oldrecurse |
if registry.insertQueue and oldrecurse==0 then |
-- Something in one of our callbacks wanted to register more callbacks; they got queued |
for eventname,callbacks in pairs(registry.insertQueue) do |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
for self,func in pairs(callbacks) do |
events[eventname][self] = func |
-- fire OnUsed callback? |
if first and registry.OnUsed then |
registry.OnUsed(registry, target, eventname) |
first = nil |
end |
end |
end |
registry.insertQueue = nil |
end |
end |
-- Registration of a callback, handles: |
-- self["method"], leads to self["method"](self, ...) |
-- self with function ref, leads to functionref(...) |
-- "addonId" (instead of self) with function ref, leads to functionref(...) |
-- all with an optional arg, which, if present, gets passed as first argument (after self if present) |
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) |
if type(eventname) ~= "string" then |
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) |
end |
method = method or eventname |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
if type(method) ~= "string" and type(method) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) |
end |
local regfunc |
if type(method) == "string" then |
-- self["method"] calling style |
if type(self) ~= "table" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) |
elseif self==target then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) |
elseif type(self[method]) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) self[method](self,arg,...) end |
else |
regfunc = function(...) self[method](self,...) end |
end |
else |
-- function ref with self=object or self="addonId" |
if type(self)~="table" and type(self)~="string" then |
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) method(arg,...) end |
else |
regfunc = method |
end |
end |
if events[eventname][self] or registry.recurse<1 then |
-- if registry.recurse<1 then |
-- we're overwriting an existing entry, or not currently recursing. just set it. |
events[eventname][self] = regfunc |
-- fire OnUsed callback? |
if registry.OnUsed and first then |
registry.OnUsed(registry, target, eventname) |
end |
else |
-- we're currently processing a callback in this registry, so delay the registration of this new entry! |
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency |
registry.insertQueue = registry.insertQueue or setmetatable({},meta) |
registry.insertQueue[eventname][self] = regfunc |
end |
end |
-- Unregister a callback |
target[UnregisterName] = function(self, eventname) |
if not self or self==target then |
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) |
end |
if type(eventname) ~= "string" then |
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) |
end |
if rawget(events, eventname) and events[eventname][self] then |
events[eventname][self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(events[eventname]) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then |
registry.insertQueue[eventname][self] = nil |
end |
end |
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds |
if UnregisterAllName then |
target[UnregisterAllName] = function(...) |
if select("#",...)<1 then |
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) |
end |
if select("#",...)==1 and ...==target then |
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) |
end |
for i=1,select("#",...) do |
local self = select(i,...) |
if registry.insertQueue then |
for eventname, callbacks in pairs(registry.insertQueue) do |
if callbacks[self] then |
callbacks[self] = nil |
end |
end |
end |
for eventname, callbacks in pairs(events) do |
if callbacks[self] then |
callbacks[self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(callbacks) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
end |
end |
end |
end |
return registry |
end |
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it |
-- try to upgrade old implicit embeds since the system is selfcontained and |
-- relies on closures to work. |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceEvent-3.0.lua"/> |
</Ui> |
--[[ $Id: AceEvent-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]] |
local MAJOR, MINOR = "AceEvent-3.0", 3 |
local AceEvent = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceEvent then return end |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame |
AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib |
-- APIs and registry for blizzard events, using CallbackHandler lib |
if not AceEvent.events then |
AceEvent.events = CallbackHandler:New(AceEvent, |
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents") |
end |
function AceEvent.events:OnUsed(target, eventname) |
AceEvent.frame:RegisterEvent(eventname) |
end |
function AceEvent.events:OnUnused(target, eventname) |
AceEvent.frame:UnregisterEvent(eventname) |
end |
-- APIs and registry for IPC messages, using CallbackHandler lib |
if not AceEvent.messages then |
AceEvent.messages = CallbackHandler:New(AceEvent, |
"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages" |
) |
AceEvent.SendMessage = AceEvent.messages.Fire |
end |
--- embedding and embed handling |
local mixins = { |
"RegisterEvent", "UnregisterEvent", |
"RegisterMessage", "UnregisterMessage", |
"SendMessage", |
"UnregisterAllEvents", "UnregisterAllMessages", |
} |
-- AceEvent:Embed( target ) |
-- target (object) - target object to embed AceEvent in |
-- |
-- Embeds AceEvent into the target object making the functions from the mixins list available on target:.. |
function AceEvent:Embed(target) |
for k, v in pairs(mixins) do |
target[v] = self[v] |
end |
self.embeds[target] = true |
return target |
end |
-- AceEvent:OnEmbedDisable( target ) |
-- target (object) - target object that is being disabled |
-- |
-- Unregister all events messages etc when the target disables. |
-- this method should be called by the target manually or by an addon framework |
function AceEvent:OnEmbedDisable(target) |
target:UnregisterAllEvents() |
target:UnregisterAllMessages() |
end |
-- Script to fire blizzard events into the event listeners |
local events = AceEvent.events |
AceEvent.frame:SetScript("OnEvent", function(this, event, ...) |
events:Fire(event, ...) |
end) |
--- Finally: upgrade our old embeds |
for target, v in pairs(AceEvent.embeds) do |
AceEvent:Embed(target) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConsole-3.0.lua"/> |
</Ui> |
--[[ $Id: AceConsole-3.0.lua 63886 2008-03-08 12:43:31Z nevcairiel $ ]] |
local MAJOR,MINOR = "AceConsole-3.0", 4 |
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceConsole then return end -- No upgrade needed |
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in. |
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered |
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable |
-- local upvalues |
local _G = _G |
local pairs = pairs |
local select = select |
local type = type |
local tostring = tostring |
local strfind = string.find |
local strsub = string.sub |
local max = math.max |
-- AceConsole:Print( [chatframe,] ... ) |
-- |
-- Print to DEFAULT_CHAT_FRAME or given chatframe (anything with an .AddMessage member) |
function AceConsole:Print(...) |
local text = "" |
if self ~= AceConsole then |
text = "|cff33ff99"..tostring( self ).."|r: " |
end |
local frame = select(1, ...) |
if not ( type(frame) == "table" and frame.AddMessage ) then -- Is first argument something with an .AddMessage member? |
frame=nil |
end |
for i=(frame and 2 or 1), select("#", ...) do |
text = text .. tostring( select( i, ...) ) .." " |
end |
(frame or DEFAULT_CHAT_FRAME):AddMessage( text ) |
end |
-- AceConsole:RegisterChatCommand(. command, func, persist ) |
-- |
-- command (string) - chat command to be registered WITHOUT leading "/" |
-- func (function|membername) - function to call, or self[membername](self, ...) call |
-- persist (boolean) - false: the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true) |
-- silent (boolean) - don't whine if command already exists, silently fail |
-- |
-- Register a simple chat command |
function AceConsole:RegisterChatCommand( command, func, persist, silent ) |
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist[, silent] ]): 'command' - expected a string]], 2) end |
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk |
local name = "ACECONSOLE_"..command:upper() |
if SlashCmdList[name] then |
if not silent then |
geterrorhandler()(tostring(self)..": Chat Command '"..command.."' already exists, will not overwrite.") |
end |
return |
end |
if type( func ) == "string" then |
SlashCmdList[name] = function(input) |
self[func](self, input) |
end |
else |
SlashCmdList[name] = func |
end |
_G["SLASH_"..name.."1"] = "/"..command:lower() |
AceConsole.commands[command] = name |
-- non-persisting commands are registered for enabling disabling |
if not persist then |
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end |
AceConsole.weakcommands[self][command] = func |
end |
return true |
end |
-- AceConsole:UnregisterChatCommand( command ) |
-- |
-- Unregister a chatcommand |
function AceConsole:UnregisterChatCommand( command ) |
local name = AceConsole.commands[command] |
if name then |
SlashCmdList[name] = nil |
_G["SLASH_" .. name .. "1"] = nil |
hash_SlashCmdList["/" .. command:upper()] = nil |
AceConsole.commands[command] = nil |
end |
end |
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end |
local function nils(n, ...) |
if n>1 then |
return nil, nils(n-1, ...) |
elseif n==1 then |
return nil, ... |
else |
return ... |
end |
end |
-- AceConsole:GetArgs(string, numargs, startpos) |
-- |
-- Retreive one or more space-separated arguments from a string. |
-- Treats quoted strings and itemlinks as non-spaced. |
-- |
-- string - The raw argument string |
-- numargs - How many arguments to get (default 1) |
-- startpos - Where in the string to start scanning (default 1) |
-- |
-- Returns arg1, arg2, ..., nextposition |
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string. |
function AceConsole:GetArgs(str, numargs, startpos) |
numargs = numargs or 1 |
startpos = max(startpos or 1, 1) |
local pos=startpos |
-- find start of new arg |
pos = strfind(str, "[^ ]", pos) |
if not pos then -- whoops, end of string |
return nils(numargs, 1e9) |
end |
if numargs<1 then |
return pos |
end |
-- quoted or space separated? find out which pattern to use |
local delim_or_pipe |
local ch = strsub(str, pos, pos) |
if ch=='"' then |
pos = pos + 1 |
delim_or_pipe='([|"])' |
elseif ch=="'" then |
pos = pos + 1 |
delim_or_pipe="([|'])" |
else |
delim_or_pipe="([| ])" |
end |
startpos = pos |
while true do |
-- find delimiter or hyperlink |
local ch,_ |
pos,_,ch = strfind(str, delim_or_pipe, pos) |
if not pos then break end |
if ch=="|" then |
-- some kind of escape |
if strsub(str,pos,pos+1)=="|H" then |
-- It's a |H....|hhyper link!|h |
pos=strfind(str, "|h", pos+2) -- first |h |
if not pos then break end |
pos=strfind(str, "|h", pos+2) -- second |h |
if not pos then break end |
elseif strsub(str,pos, pos+1) == "|T" then |
-- It's a |T....|t texture |
pos=strfind(str, "|t", pos+2) |
if not pos then break end |
end |
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink) |
else |
-- found delimiter, done with this arg |
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1) |
end |
end |
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink) |
return strsub(str, startpos), nils(numargs-1, 1e9) |
end |
--- embedding and embed handling |
local mixins = { |
"Print", |
"RegisterChatCommand", |
"UnregisterChatCommand", |
"GetArgs", |
} |
-- AceConsole:Embed( target ) |
-- target (object) - target object to embed AceBucket in |
-- |
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:.. |
function AceConsole:Embed( target ) |
for k, v in pairs( mixins ) do |
target[v] = self[v] |
end |
self.embeds[target] = true |
return target |
end |
function AceConsole:OnEmbedEnable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry |
end |
end |
end |
function AceConsole:OnEmbedDisable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care? |
end |
end |
end |
for addon in pairs(AceConsole.embeds) do |
AceConsole:Embed(addon) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Heading -- |
-------------------------- |
do |
local Type = "Heading" |
local Version = 2 |
local function Acquire(self) |
self:SetText("") |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
if (text or "") == "" then |
self.left:SetPoint("RIGHT",self.frame,"RIGHT",-3,0) |
self.right:Hide() |
else |
self.left:SetPoint("RIGHT",self.label,"LEFT",-5,0) |
self.right:Show() |
end |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetText = SetText |
self.frame = frame |
frame.obj = self |
frame:SetHeight(18) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontNormal") |
label:SetPoint("TOP",frame,"TOP",0,0) |
label:SetPoint("BOTTOM",frame,"BOTTOM",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(18) |
self.label = label |
local left = frame:CreateTexture(nil, "BACKGROUND") |
self.left = left |
left:SetHeight(8) |
left:SetPoint("LEFT",frame,"LEFT",3,0) |
left:SetPoint("RIGHT",label,"LEFT",-5,0) |
left:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
left:SetTexCoord(0.81, 0.94, 0.5, 1) |
local right = frame:CreateTexture(nil, "BACKGROUND") |
self.right = right |
right:SetHeight(8) |
right:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
right:SetPoint("LEFT",label,"RIGHT",5,0) |
right:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
right:SetTexCoord(0.81, 0.94, 0.5, 1) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Simple Group -- |
-------------------------- |
--[[ |
This is a simple grouping container, no selection, no borders |
It will resize automatically to the height of the controls added to it |
]] |
do |
local Type = "SimpleGroup" |
local Version = 3 |
local function Acquire(self) |
self:SetWidth(300) |
self:SetHeight(100) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function LayoutFinished(self, width, height) |
self:SetHeight(height or 0) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
content:SetWidth(width) |
content.width = width |
end |
local function OnHeightSet(self, height) |
local content = self.content |
content:SetHeight(height) |
content.height = height |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.frame = frame |
self.LayoutFinished = LayoutFinished |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
--[[ |
Selection Group controls all have an interface to select a group for thier contents |
None of them will auto size to thier contents, and should usually be used with a scrollframe |
unless you know that the controls will fit inside |
]] |
-------------------------- |
-- Dropdown Group -- |
-------------------------- |
--[[ |
Events : |
OnGroupSelected |
]] |
do |
local Type = "DropdownGroup" |
local Version = 5 |
local function Acquire(self) |
self.dropdown:SetText("") |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.dropdown.list = nil |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function SelectedGroup(self,event,value) |
local group = self.parentgroup |
local status = group.status or group.localstatus |
status.selectedgroup = value |
self.parentgroup:Fire("OnGroupSelected", value) |
end |
local function SetGroupList(self,list) |
self.dropdown.list = list |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
end |
local function SetGroup(self,group) |
self.dropdown:SetValue(group) |
local status = self.status or self.localstatus |
status.selectedgroup = group |
self:Fire("OnGroupSelected", group) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 63 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 26 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame") |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetTitle = SetTitle |
self.SetGroupList = SetGroupList |
self.SetGroup = SetGroup |
self.SetStatusTable = SetStatusTable |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local dropdown = AceGUI:Create("Dropdown") |
self.dropdown = dropdown |
dropdown.frame:SetParent(frame) |
dropdown.parentgroup = self |
dropdown:SetCallback("OnValueChanged",SelectedGroup) |
dropdown.frame:SetPoint("TOPLEFT",titletext,"BOTTOMLEFT",-7,3) |
dropdown.frame:Show() |
dropdown:SetLabel("") |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-40) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Scroll Frame -- |
-------------------------- |
do |
local Type = "ScrollFrame" |
local Version = 2 |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
local function SetScroll(self, value) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local viewheight = frame:GetHeight() |
local height = child:GetHeight() |
local offset |
if viewheight > height then |
offset = 0 |
else |
offset = floor((height - viewheight) / 1000.0 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
status.offset = offset |
status.scrollvalue = value |
end |
local function MoveScroll(self, value) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
if height > viewheight then |
self.scrollbar:Hide() |
else |
self.scrollbar:Show() |
local diff = height - viewheight |
local delta = 1 |
if value < 0 then |
delta = -1 |
end |
self.scrollbar:SetValue(math.min(math.max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000)) |
end |
end |
local function FixScroll(self) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local offset = status.offset |
if not offset then |
offset = 0 |
end |
local curvalue = self.scrollbar:GetValue() |
if viewheight < height then |
self.scrollbar:Hide() |
self.scrollbar:SetValue(0) |
--self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0) |
else |
self.scrollbar:Show() |
--self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-16,0) |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.scrollbar:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
status.offset = offset |
end |
end |
end |
local function OnMouseWheel(this,value) |
this.obj:MoveScroll(value) |
end |
local function OnScrollValueChanged(this, value) |
this.obj:SetScroll(value) |
end |
local function FixScrollOnUpdate(this) |
this:SetScript("OnUpdate", nil) |
this.obj:FixScroll() |
end |
local function OnSizeChanged(this) |
--this:SetScript("OnUpdate", FixScrollOnUpdate) |
this.obj:FixScroll() |
end |
local function LayoutFinished(self,width,height) |
self.content:SetHeight(height or 0 + 20) |
self:FixScroll() |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
if not status.scrollvalue then |
status.scrollvalue = 0 |
end |
end |
local createdcount = 0 |
local function OnWidthSet(self, width) |
local content = self.content |
content.width = width |
end |
local function OnHeightSet(self, height) |
local content = self.content |
content.height = height |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.MoveScroll = MoveScroll |
self.FixScroll = FixScroll |
self.SetScroll = SetScroll |
self.LayoutFinished = LayoutFinished |
self.SetStatusTable = SetStatusTable |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
--Container Support |
local scrollframe = CreateFrame("ScrollFrame",nil,frame) |
local content = CreateFrame("Frame",nil,scrollframe) |
createdcount = createdcount + 1 |
local scrollbar = CreateFrame("Slider",("AceConfigDialogScrollFrame%dScrollBar"):format(createdcount),scrollframe,"UIPanelScrollBarTemplate") |
local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND") |
scrollbg:SetAllPoints(scrollbar) |
scrollbg:SetTexture(0,0,0,0.4) |
self.scrollframe = scrollframe |
self.content = content |
self.scrollbar = scrollbar |
scrollbar.obj = self |
scrollframe.obj = self |
content.obj = self |
scrollframe:SetScrollChild(content) |
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-16,0) |
scrollframe:EnableMouseWheel(true) |
scrollframe:SetScript("OnMouseWheel", OnMouseWheel) |
scrollframe:SetScript("OnSizeChanged", OnSizeChanged) |
content:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) |
content:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0) |
content:SetHeight(400) |
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) |
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) |
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged) |
scrollbar:SetMinMaxValues(0,1000) |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
self.localstatus.scrollvalue = 0 |
self:FixScroll() |
AceGUI:RegisterAsContainer(self) |
--AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Button -- |
-------------------------- |
do |
local Type = "Button" |
local Version = 5 |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
local function Button_OnClick(this) |
this.obj:Fire("OnClick") |
end |
local function Button_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Button_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function SetText(self, text) |
self.text:SetText(text or "") |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.frame:Disable() |
else |
self.frame:Enable() |
end |
end |
local count = 0 |
local function Constructor() |
count = count + 1 |
local frame = CreateFrame("Button","AceGUI30Button"..count,UIParent,"UIPanelButtonTemplate2") |
local self = {} |
self.type = Type |
self.frame = frame |
local text = frame:GetFontString() |
self.text = text |
text:SetPoint("LEFT",frame,"LEFT",15,0) |
text:SetPoint("RIGHT",frame,"RIGHT",-15,0) |
frame:SetScript("OnClick",Button_OnClick) |
frame:SetScript("OnEnter",Button_OnEnter) |
frame:SetScript("OnLeave",Button_OnLeave) |
self.SetText = SetText |
self.SetDisabled = SetDisabled |
frame:EnableMouse(true) |
frame:SetHeight(24) |
frame:SetWidth(200) |
self.Release = Release |
self.Acquire = Acquire |
self.frame = frame |
frame.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- ColorPicker -- |
-------------------------- |
do |
local Type = "ColorPicker" |
local Version = 8 |
local function Acquire(self) |
self.HasAlpha = false |
self:SetColor(0,0,0,1) |
end |
local function SetLabel(self, text) |
self.text:SetText(text) |
end |
local function SetColor(self,r,g,b,a) |
self.r = r |
self.g = g |
self.b = b |
self.a = a or 1 |
self.colorSwatch:SetVertexColor(r,g,b,a) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function SetHasAlpha(self, HasAlpha) |
self.HasAlpha = HasAlpha |
end |
local function ColorCallback(self,r,g,b,a,isAlpha) |
if not self.HasAlpha then |
a = 1 |
end |
self:SetColor(r,g,b,a) |
if ColorPickerFrame:IsVisible() then |
--colorpicker is still open |
self:Fire("OnValueChanged",r,g,b,a) |
else |
--colorpicker is closed, color callback is first, ignore it, |
--alpha callback is the final call after it closes so confirm now |
if isAlpha then |
self:Fire("OnValueConfirmed",r,g,b,a) |
end |
end |
end |
local function ColorSwatch_OnClick(this) |
HideUIPanel(ColorPickerFrame) |
local self = this.obj |
if not self.disabled then |
ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG") |
ColorPickerFrame.func = function() |
local r,g,b = ColorPickerFrame:GetColorRGB() |
local a = 1 - OpacitySliderFrame:GetValue() |
ColorCallback(self,r,g,b,a) |
end |
ColorPickerFrame.hasOpacity = self.HasAlpha |
ColorPickerFrame.opacityFunc = function() |
local r,g,b = ColorPickerFrame:GetColorRGB() |
local a = 1 - OpacitySliderFrame:GetValue() |
ColorCallback(self,r,g,b,a,true) |
end |
local r, g, b, a = self.r, self.g, self.b, self.a |
if self.HasAlpha then |
ColorPickerFrame.opacity = 1 - (a or 0) |
end |
ColorPickerFrame:SetColorRGB(r, g, b) |
ColorPickerFrame.cancelFunc = function() |
ColorCallback(self,r,g,b,a,true) |
end |
ShowUIPanel(ColorPickerFrame) |
end |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if self.disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
else |
self.text:SetTextColor(1,1,1) |
end |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetLabel = SetLabel |
self.SetColor = SetColor |
self.SetDisabled = SetDisabled |
self.SetHasAlpha = SetHasAlpha |
self.frame = frame |
frame.obj = self |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
self.text = text |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(200) |
text:SetHeight(24) |
frame:SetScript("OnClick", ColorSwatch_OnClick) |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
local colorSwatch = frame:CreateTexture(nil, "OVERLAY") |
self.colorSwatch = colorSwatch |
colorSwatch:SetWidth(19) |
colorSwatch:SetHeight(19) |
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") |
local texture = frame:CreateTexture(nil, "BACKGROUND") |
colorSwatch.texture = texture |
texture:SetWidth(16) |
texture:SetHeight(16) |
texture:SetTexture(1,1,1) |
texture:Show() |
local checkers = frame:CreateTexture(nil, "BACKGROUND") |
colorSwatch.checkers = checkers |
checkers:SetTexture("Tileset\\Generic\\Checkers") |
checkers:SetDesaturated(true) |
checkers:SetVertexColor(1,1,1,0.75) |
checkers:SetTexCoord(.25,0,0.5,.25) |
checkers:SetWidth(14) |
checkers:SetHeight(14) |
checkers:Show() |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(frame) |
highlight:Hide() |
texture:SetPoint("CENTER", colorSwatch, "CENTER") |
checkers:SetPoint("CENTER", colorSwatch, "CENTER") |
colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0) |
text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0) |
text:SetPoint("RIGHT",frame,"RIGHT") |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-- Recycling functions |
local new, del |
do |
local pool = setmetatable({},{__mode='k'}) |
function new() |
local t = next(pool) |
if t then |
pool[t] = nil |
return t |
else |
return {} |
end |
end |
function del(t) |
for k in pairs(t) do |
t[k] = nil |
end |
pool[t] = true |
end |
end |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------- |
-- TreeView -- |
-------------- |
do |
local Type = "TreeGroup" |
local Version = 6 |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k, v in pairs(self.localstatus) do |
if k == "groups" then |
for k2 in pairs(v) do |
v[k2] = nil |
end |
else |
self.localstatus[k] = nil |
end |
end |
self.localstatus.scrollvalue = 0 |
end |
local function GetButtonParents(line) |
local parent = line.parent |
if parent and parent.value then |
return parent.value, GetButtonParents(parent) |
end |
end |
local function GetButtonUniqueValue(line) |
local parent = line.parent |
if parent and parent.value then |
return GetButtonUniqueValue(parent).."\001"..line.value |
else |
return line.value |
end |
end |
local function ButtonOnClick(this) |
local self = this.obj |
self:Fire("OnClick",this.uniquevalue, this.selected) |
if not this.selected then |
self:SetSelected(this.uniquevalue) |
this.selected = true |
this:LockHighlight() |
self:RefreshTree() |
end |
end |
local function ExpandOnClick(this) |
local button = this.button |
local self = button.obj |
local status = (self.status or self.localstatus).groups |
status[button.uniquevalue] = not status[button.uniquevalue] |
self:RefreshTree() |
end |
local function ButtonOnDoubleClick(button) |
local self = button.obj |
local status = self.status or self.localstatus |
local status = (self.status or self.localstatus).groups |
status[button.uniquevalue] = not status[button.uniquevalue] |
self:RefreshTree() |
end |
local buttoncount = 1 |
local function CreateButton(self) |
local button = CreateFrame("Button",("AceGUI30TreeButton%d"):format(buttoncount),self.treeframe, "InterfaceOptionsButtonTemplate") |
buttoncount = buttoncount + 1 |
button.obj = self |
button:SetScript("OnClick",ButtonOnClick) |
button:SetScript("OnDoubleClick", ButtonOnDoubleClick) |
button.toggle.button = button |
button.toggle:SetScript("OnClick",ExpandOnClick) |
return button |
end |
local function UpdateButton(button, treeline, selected, canExpand, isExpanded) |
local self = button.obj |
local toggle = button.toggle |
local frame = self.frame |
local text = treeline.text or "" |
local level = treeline.level |
local value = treeline.value |
local uniquevalue = treeline.uniquevalue |
local disabled = treeline.disabled |
button.treeline = treeline |
button.value = value |
button.uniquevalue = uniquevalue |
if selected then |
button:LockHighlight() |
button.selected = true |
else |
button:UnlockHighlight() |
button.selected = false |
end |
local normalText = button.text |
local normalTexture = button:GetNormalTexture() |
local line = button.line |
button.level = level |
if ( level == 1 ) then |
button:SetTextFontObject("GameFontNormal") |
button:SetHighlightFontObject("GameFontHighlight") |
button.text:SetPoint("LEFT", 8, 2) |
else |
button:SetTextFontObject("GameFontHighlightSmall") |
button:SetHighlightFontObject("GameFontHighlightSmall") |
button.text:SetPoint("LEFT", 8 * level, 2) |
end |
if disabled then |
button:EnableMouse(false) |
button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE) |
else |
button.text:SetText(text) |
button:EnableMouse(true) |
end |
if canExpand then |
if not isExpanded then |
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP") |
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN") |
else |
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP") |
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN") |
end |
toggle:Show() |
else |
toggle:Hide() |
end |
end |
local function OnScrollValueChanged(this, value) |
if this.obj.noupdate then return end |
local self = this.obj |
local status = self.status or self.localstatus |
status.scrollvalue = value |
self:RefreshTree() |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
if not status.groups then |
status.groups = {} |
end |
if not status.scrollvalue then |
status.scrollvalue = 0 |
end |
self:RefreshTree() |
end |
--sets the tree to be displayed |
--[[ |
example tree |
Alpha |
Bravo |
-Charlie |
-Delta |
-Echo |
Foxtrot |
tree = { |
{ |
value = "A", |
text = "Alpha" |
}, |
{ |
value = "B", |
text = "Bravo", |
children = { |
{ |
value = "C", |
text = "Charlie" |
}, |
{ |
value = "D", |
text = "Delta" |
children = { |
{ |
value = "E", |
text = "Echo" |
} |
} |
} |
} |
}, |
{ |
value = "F", |
text = "Foxtrot" |
}, |
} |
]] |
local function SetTree(self, tree) |
assert(type(tree) == "table") |
self.tree = tree |
self:RefreshTree() |
end |
local function BuildLevel(self, tree, level, parent) |
local lines = self.lines |
local status = (self.status or self.localstatus) |
local groups = status.groups |
local hasChildren = self.hasChildren |
for i, v in ipairs(tree) do |
local line = new() |
lines[#lines+1] = line |
line.value = v.value |
line.text = v.text |
line.disabled = v.disabled |
line.tree = tree |
line.level = level |
line.parent = parent |
line.uniquevalue = GetButtonUniqueValue(line) |
if v.children then |
line.hasChildren = true |
else |
line.hasChildren = nil |
end |
if v.children then |
if groups[line.uniquevalue] then |
self:BuildLevel(v.children, level+1, line) |
end |
end |
end |
end |
--fire an update after one frame to catch the treeframes height |
local function FirstFrameUpdate(this) |
local self = this.obj |
this:SetScript("OnUpdate",nil) |
self:RefreshTree() |
end |
local function ResizeUpdate(this) |
this.obj:RefreshTree() |
end |
local function RefreshTree(self) |
if not self.tree then return end |
--Build the list of visible entries from the tree and status tables |
local status = self.status or self.localstatus |
local groupstatus = status.groups |
local tree = self.tree |
local lines = self.lines |
local buttons = self.buttons |
local treeframe = self.treeframe |
while lines[1] do |
local t = tremove(lines) |
for k in pairs(t) do |
t[k] = nil |
end |
del(t) |
end |
self:BuildLevel(tree, 1) |
for i, v in ipairs(buttons) do |
v:Hide() |
end |
local numlines = #lines |
local maxlines = (math.floor(((self.treeframe:GetHeight()or 0) - 20 ) / 20)) |
local first, last |
if numlines <= maxlines then |
--the whole tree fits in the frame |
status.scrollvalue = 0 |
self:ShowScroll(false) |
first, last = 1, numlines |
else |
self:ShowScroll(true) |
--scrolling will be needed |
self.noupdate = true |
self.scrollbar:SetMinMaxValues(0, numlines - maxlines) |
--check if we are scrolled down too far |
if numlines - status.scrollvalue < maxlines then |
status.scrollvalue = numlines - maxlines |
self.scrollbar:SetValue(status.scrollvalue) |
end |
self.noupdate = nil |
first, last = status.scrollvalue+1, status.scrollvalue + maxlines |
end |
local buttonnum = 1 |
for i = first, last do |
local line = lines[i] |
local button = buttons[buttonnum] |
if not button then |
button = self:CreateButton() |
buttons[buttonnum] = button |
button:SetParent(treeframe) |
button:SetFrameLevel(treeframe:GetFrameLevel()+1) |
button:ClearAllPoints() |
if i == 1 then |
if self.showscroll then |
button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10) |
button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10) |
else |
button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10) |
button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10) |
end |
else |
button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0) |
button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0) |
end |
end |
UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] ) |
button:Show() |
buttonnum = buttonnum + 1 |
end |
end |
local function SetSelected(self, value) |
local status = self.status or self.localstatus |
if status.selected ~= value then |
status.selected = value |
self:Fire("OnGroupSelected", value) |
end |
end |
local function BuildUniqueValue(...) |
local n = select('#', ...) |
if n == 1 then |
return ... |
else |
return (...).."\001"..BuildUniqueValue(select(2,...)) |
end |
end |
local function Select(self, uniquevalue, ...) |
local status = self.status or self.localstatus |
local groups = status.groups |
for i = 1, select('#', ...) do |
groups[BuildUniqueValue(select(i, ...))] = true |
end |
status.selected = uniquevalue |
self:RefreshTree() |
self:Fire("OnGroupSelected", uniquevalue) |
end |
local function SelectByPath(self, ...) |
self:Select(BuildUniqueValue(...), ...) |
end |
--Selects a tree node by UniqueValue |
local function SelectByValue(self, uniquevalue) |
self:Select(uniquevalue,string.split("\001", uniquevalue)) |
end |
local function ShowScroll(self, show) |
self.showscroll = show |
if show then |
self.scrollbar:Show() |
if self.buttons[1] then |
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10) |
end |
else |
self.scrollbar:Hide() |
if self.buttons[1] then |
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10) |
end |
end |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 199 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 20 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function TreeOnMouseWheel(this, delta) |
local self = this.obj |
if self.showscroll then |
local scrollbar = self.scrollbar |
local min, max = scrollbar:GetMinMaxValues() |
local value = scrollbar:GetValue() |
local newvalue = math.min(max,math.max(min,value - delta)) |
if value ~= newvalue then |
scrollbar:SetValue(newvalue) |
end |
end |
end |
local createdcount = 0 |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.lines = {} |
self.levels = {} |
self.buttons = {} |
self.hasChildren = {} |
self.localstatus = {} |
self.localstatus.groups = {} |
local treeframe = CreateFrame("Frame",nil,frame) |
treeframe.obj = self |
treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) |
treeframe:SetWidth(183) |
treeframe:SetScript("OnUpdate",FirstFrameUpdate) |
treeframe:SetScript("OnSizeChanged",ResizeUpdate) |
treeframe:EnableMouseWheel(true) |
treeframe:SetScript("OnMouseWheel", TreeOnMouseWheel) |
treeframe:SetBackdrop(PaneBackdrop) |
treeframe:SetBackdropColor(0.1,0.1,0.1,0.5) |
treeframe:SetBackdropBorderColor(0.4,0.4,0.4) |
self.treeframe = treeframe |
self.Release = Release |
self.Acquire = Acquire |
self.SetTree = SetTree |
self.RefreshTree = RefreshTree |
self.SetStatusTable = SetStatusTable |
self.BuildLevel = BuildLevel |
self.CreateButton = CreateButton |
self.SetSelected = SetSelected |
self.ShowScroll = ShowScroll |
self.SetStatusTable = SetStatusTable |
self.Select = Select |
self.SelectByValue = SelectByValue |
self.SelectByPath = SelectByPath |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.frame = frame |
frame.obj = self |
createdcount = createdcount + 1 |
local scrollbar = CreateFrame("Slider",("AceConfigDialogTreeGroup%dScrollBar"):format(createdcount),treeframe,"UIPanelScrollBarTemplate") |
self.scrollbar = scrollbar |
local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND") |
scrollbg:SetAllPoints(scrollbar) |
scrollbg:SetTexture(0,0,0,0.4) |
scrollbar.obj = self |
self.noupdate = true |
scrollbar:SetPoint("TOPRIGHT",treeframe,"TOPRIGHT",-10,-26) |
scrollbar:SetPoint("BOTTOMRIGHT",treeframe,"BOTTOMRIGHT",-10,26) |
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged) |
scrollbar:SetMinMaxValues(0,0) |
self.localstatus.scrollvalue = 0 |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
self.noupdate = nil |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",179,0) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
--AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Label -- |
-------------------------- |
do |
local Type = "Label" |
local Version = 6 |
local function Acquire(self) |
self:SetText("") |
self:SetImage(nil) |
self:SetColor() |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function UpdateImageAnchor(self) |
local width = self.frame.width or self.frame:GetWidth() or 0 |
local image = self.image |
local label = self.label |
local frame = self.frame |
local height |
label:ClearAllPoints() |
image:ClearAllPoints() |
if self.imageshown then |
local imagewidth = image:GetWidth() |
if (width - imagewidth) < 200 or (label:GetText() or "") == "" then |
--image goes on top centered when less than 200 width for the text, or if there is no text |
image:SetPoint("TOP",frame,"TOP",0,0) |
label:SetPoint("TOP",image,"BOTTOM",0,0) |
label:SetPoint("LEFT",frame,"LEFT",0,0) |
label:SetWidth(width) |
height = image:GetHeight() + label:GetHeight() |
else |
--image on the left |
image:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPLEFT",image,"TOPRIGHT",0,0) |
label:SetWidth(width - imagewidth) |
height = math.max(image:GetHeight(), label:GetHeight()) |
end |
else |
--no image shown |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetWidth(width) |
height = self.label:GetHeight() |
end |
frame.resizing = true |
self.frame:SetHeight(height) |
self.frame.height = height |
frame.resizing = nil |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
UpdateImageAnchor(self) |
end |
local function SetColor(self, r, g, b) |
if not (r and g and b) then |
r, g, b = 1, 1, 1 |
end |
self.label:SetVertexColor(r, g, b) |
end |
local function OnWidthSet(self, width) |
UpdateImageAnchor(self) |
end |
local function OnFrameResize(this) |
if this.resizing then return end |
local self = this.obj |
OnWidthSet(self, this:GetWidth()) |
end |
local function SetImage(self, path, ...) |
local image = self.image |
image:SetTexture(path) |
if image:GetTexture() then |
self.imageshown = true |
local n = select('#', ...) |
if n == 4 or n == 8 then |
image:SetTexCoord(...) |
end |
else |
self.imageshown = nil |
end |
UpdateImageAnchor(self) |
end |
local function SetImageSize(self, width, height) |
self.image:SetWidth(width) |
self.image:SetHeight(height) |
UpdateImageAnchor(self) |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetText = SetText |
self.SetColor = SetColor |
self.frame = frame |
self.OnWidthSet = OnWidthSet |
self.SetImage = SetImage |
self.SetImageSize = SetImageSize |
frame.obj = self |
frame:SetHeight(18) |
frame:SetWidth(200) |
frame:SetScript("OnSizeChanged", OnFrameResize) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontHighlightSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetWidth(200) |
label:SetJustifyH("LEFT") |
label:SetJustifyV("TOP") |
self.label = label |
local image = frame:CreateTexture(nil,"BACKGROUND") |
self.image = image |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
--[[ |
--Multiline Editbox Widget, Originally by bam |
--]] |
local assert, error, ipairs, next, pairs, select, tonumber, tostring, type, unpack, pcall, xpcall = |
assert, error, ipairs, next, pairs, select, tonumber, tostring, type, unpack, pcall, xpcall |
local getmetatable, setmetatable, rawequal, rawget, rawset, getfenv, setfenv, loadstring, debugstack = |
getmetatable, setmetatable, rawequal, rawget, rawset, getfenv, setfenv, loadstring, debugstack |
local math, string, table = math, string, table |
local find, format, gmatch, gsub, tolower, match, toupper, join, split, trim = |
string.find, string.format, string.gmatch, string.gsub, string.lower, string.match, string.upper, string.join, string.split, string.trim |
local concat, insert, maxn, remove, sort = table.concat, table.insert, table.maxn, table.remove, table.sort |
local max, min, abs, ceil, floor = math.max, math.min, math.abs, math.ceil, math.floor |
local LibStub = assert(LibStub) |
local ChatFontNormal = ChatFontNormal |
local ClearCursor = ClearCursor |
local CreateFrame = CreateFrame |
local GetCursorInfo = GetCursorInfo |
local GetSpellName = GetSpellName |
local UIParent = UIParent |
local UISpecialFrames = UISpecialFrames |
-- No global variables after this! |
local _G = getfenv() |
local AceGUI = LibStub("AceGUI-3.0") |
local Version = 4 |
--------------------- |
-- Common Elements -- |
--------------------- |
local FrameBackdrop = { |
bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", |
tile = true, tileSize = 32, edgeSize = 32, |
insets = { left = 8, right = 8, top = 8, bottom = 8 } |
} |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local ControlBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 3, bottom = 3 } |
} |
-------------------------- |
-- Edit box -- |
-------------------------- |
--[[ |
Events : |
OnTextChanged |
OnEnterPressed |
]] |
do |
local Type = "MultiLineEditBox" |
local MultiLineEditBox = {} |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
local cancel = self:Fire("OnEnterPressed",value) |
if not cancel then |
self.button:Disable() |
end |
end |
local function Button_OnClick(this) |
local editbox = this.obj.editbox |
editbox:ClearFocus() |
EditBox_OnEnterPressed(editbox) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if type == "item" then |
self:SetText(info) |
self:Fire("OnEnterPressed",info) |
ClearCursor() |
elseif type == "spell" then |
local name, rank = GetSpellName(id, info) |
if rank and rank:match("%d") then |
name = name.."("..rank..")" |
end |
self:SetText(name) |
self:Fire("OnEnterPressed",name) |
ClearCursor() |
end |
self.button:Disable() |
end |
function MultiLineEditBox:Acquire() |
self:SetDisabled(false) |
self:ShowButton(true) |
end |
function MultiLineEditBox:Release() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
function MultiLineEditBox:SetDisabled(disabled) |
self.disabled = disabled |
if disabled then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5, 0.5, 0.5) |
else |
self.editbox:EnableMouse(true) |
self.editbox:SetTextColor(1, 1, 1) |
end |
end |
function MultiLineEditBox:SetText(text) |
text = text or "" |
local editbox = self.editbox |
local oldText = editbox:GetText() |
local dummy = format(" %s", text) |
self.lasttext = dummy -- prevents OnTextChanged from firing |
editbox:SetText(dummy) |
editbox:HighlightText(0, 1) |
self.lasttext = oldText |
editbox:Insert("") |
end |
function MultiLineEditBox:SetLabel(text) |
if (text or "") == "" then |
self.backdrop:SetPoint("TOPLEFT",self.frame,"TOPLEFT",0,0) |
self.label:Hide() |
self.label:SetText("") |
else |
self.backdrop:SetPoint("TOPLEFT",self.frame,"TOPLEFT",0,-20) |
self.label:Show() |
self.label:SetText(text) |
end |
end |
function MultiLineEditBox:GetText() |
return self.editbox:GetText() |
end |
function MultiLineEditBox:ShowButton(show) |
if show then |
self.backdrop:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,22) |
self.button:Show() |
else |
self.backdrop:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0) |
self.button:Hide() |
end |
end |
local function Constructor() |
local frame = CreateFrame("Frame", nil, UIParent) |
local backdrop = CreateFrame("Frame", nil, frame) |
local self = {} |
for k, v in pairs(MultiLineEditBox) do self[k] = v end |
self.type = Type |
self.frame = frame |
self.backdrop = backdrop |
frame.obj = self |
backdrop:SetBackdrop(ControlBackdrop) |
backdrop:SetBackdropColor(0, 0, 0) |
backdrop:SetBackdropBorderColor(0.4, 0.4, 0.4) |
backdrop:SetPoint("TOPLEFT",frame,"TOPLEFT",0, -20) |
backdrop:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,22) |
local scrollframe = CreateFrame("ScrollFrame", format("%s@%s@%s", Type, "ScrollFrame", tostring(self)), backdrop, "UIPanelScrollFrameTemplate") |
scrollframe:SetPoint("TOPLEFT", 5, -6) |
scrollframe:SetPoint("BOTTOMRIGHT", -28, 6) |
scrollframe.obj = self |
local scrollchild = CreateFrame("Frame", nil, scrollframe) |
scrollframe:SetScrollChild(scrollchild) |
scrollchild:SetHeight(2) |
scrollchild:SetWidth(2) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local editbox = CreateFrame("EditBox", nil, scrollchild) |
self.editbox = editbox |
editbox.obj = self |
editbox:SetPoint("TOPLEFT") |
editbox:SetHeight(50) |
editbox:SetWidth(50) |
editbox:SetMultiLine(true) |
-- editbox:SetMaxLetters(7500) |
editbox:SetTextInsets(5, 5, 3, 3) |
editbox:EnableMouse(true) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(ChatFontNormal) |
local button = CreateFrame("Button",nil,scrollframe,"UIPanelButtonTemplate") |
button:SetWidth(80) |
button:SetHeight(20) |
button:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,2) |
button:SetText("Apply") |
button:SetScript("OnClick", Button_OnClick) |
button:Disable() |
button:Hide() |
self.button = button |
button.obj = self |
scrollframe:EnableMouse(true) |
scrollframe:SetScript("OnMouseUp", function() editbox:SetFocus() end) |
scrollframe:SetScript("OnEnter", function(this) this.obj:Fire("OnEnter") end) |
scrollframe:SetScript("OnLeave", function(this) this.obj:Fire("OnLeave") end) |
editbox:SetScript("OnEnter", function(this) this.obj:Fire("OnEnter") end) |
editbox:SetScript("OnLeave", function(this) this.obj:Fire("OnLeave") end) |
local function FixSize() |
scrollchild:SetHeight(scrollframe:GetHeight()) |
scrollchild:SetWidth(scrollframe:GetWidth()) |
editbox:SetWidth(scrollframe:GetWidth()) |
end |
scrollframe:SetScript("OnShow", FixSize) |
scrollframe:SetScript("OnSizeChanged", FixSize) |
editbox:SetScript("OnEscapePressed", editbox.ClearFocus) |
editbox:SetScript("OnTextChanged", function(_, ...) |
scrollframe:UpdateScrollChildRect() |
local value = editbox:GetText() |
if value ~= self.lasttext then |
self:Fire("OnTextChanged", value) |
self.lasttext = value |
self.button:Enable() |
end |
end) |
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
do |
local cursorOffset, cursorHeight |
local idleTime |
local function FixScroll(_, elapsed) |
if cursorOffset and cursorHeight then |
idleTime = 0 |
local height = scrollframe:GetHeight() |
local range = scrollframe:GetVerticalScrollRange() |
local scroll = scrollframe:GetVerticalScroll() |
local size = height + range |
cursorOffset = -cursorOffset |
while cursorOffset < scroll do |
scroll = scroll - (height / 2) |
if scroll < 0 then scroll = 0 end |
scrollframe:SetVerticalScroll(scroll) |
end |
while cursorOffset + cursorHeight > scroll + height and scroll < range do |
scroll = scroll + (height / 2) |
if scroll > range then scroll = range end |
scrollframe:SetVerticalScroll(scroll) |
end |
elseif not idleTime or idleTime > 2 then |
frame:SetScript("OnUpdate", nil) |
idleTime = nil |
else |
idleTime = idleTime + elapsed |
end |
cursorOffset = nil |
end |
editbox:SetScript("OnCursorChanged", function(_, x, y, w, h) |
cursorOffset, cursorHeight = y, h |
if not idleTime then |
frame:SetScript("OnUpdate", FixScroll) |
end |
end) |
end |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Slider -- |
-------------------------- |
do |
local Type = "Slider" |
local Version = 3 |
local function Acquire(self) |
self:SetDisabled(false) |
self:SetSliderValues(0,100,1) |
self:SetIsPercent(nil) |
self:SetValue(0) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.slider:EnableMouseWheel(false) |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function UpdateText(self) |
if self.ispercent then |
self.editbox:SetText((math.floor(self.value*1000+0.5)/10)..'%') |
else |
self.editbox:SetText(math.floor(self.value*100+0.5)/100) |
end |
end |
local function Slider_OnValueChanged(this) |
local self = this.obj |
if not this.setup then |
local newvalue |
newvalue = this:GetValue() |
if newvalue ~= self.value and not self.disabled then |
self.value = newvalue |
self:Fire("OnValueChanged", newvalue) |
end |
if self.value then |
local value = self.value |
UpdateText(self) |
end |
end |
end |
local function Slider_OnMouseUp(this) |
local self = this.obj |
self:Fire("OnMouseUp",this:GetValue()) |
end |
local function Slider_OnMouseWheel(this, v) |
local self = this.obj |
if not self.disabled then |
local value = self.value |
if v > 0 then |
value = math.min(value + (self.step or 1),self.max) |
else |
value = math.max(value - (self.step or 1), self.min) |
end |
self.slider:SetValue(value) |
end |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.slider:EnableMouse(false) |
self.label:SetTextColor(.5,.5,.5) |
self.hightext:SetTextColor(.5,.5,.5) |
self.lowtext:SetTextColor(.5,.5,.5) |
--self.valuetext:SetTextColor(.5,.5,.5) |
self.editbox:SetTextColor(.5,.5,.5) |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
else |
self.slider:EnableMouse(true) |
self.label:SetTextColor(1,.82,0) |
self.hightext:SetTextColor(1,1,1) |
self.lowtext:SetTextColor(1,1,1) |
--self.valuetext:SetTextColor(1,1,1) |
self.editbox:SetTextColor(1,1,1) |
self.editbox:EnableMouse(true) |
end |
end |
local function SetValue(self, value) |
self.slider.setup = true |
self.slider:SetValue(value) |
self.value = value |
UpdateText(self) |
self.slider.setup = nil |
end |
local function SetLabel(self, text) |
self.label:SetText(text) |
end |
local function SetSliderValues(self,min, max, step) |
local frame = self.slider |
frame.setup = true |
self.min = min |
self.max = max |
self.step = step |
frame:SetMinMaxValues(min or 0,max or 100) |
self.lowtext:SetText(min or 0) |
self.hightext:SetText(max or 100) |
frame:SetValueStep(step or 1) |
frame.setup = nil |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
if self.ispercent then |
value = value:gsub('%%','') |
value = tonumber(value) / 100 |
else |
value = tonumber(value) |
end |
if value then |
self:Fire("OnMouseUp",value) |
end |
end |
local function SetIsPercent(self, value) |
self.ispercent = value |
end |
local function FrameOnMouseDown(this) |
this.obj.slider:EnableMouseWheel(true) |
end |
local SliderBackdrop = { |
bgFile = "Interface\\Buttons\\UI-SliderBar-Background", |
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", |
tile = true, tileSize = 8, edgeSize = 8, |
insets = { left = 3, right = 3, top = 6, bottom = 6 } |
} |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.frame = frame |
frame.obj = self |
self.SetDisabled = SetDisabled |
self.SetValue = SetValue |
self.SetSliderValues = SetSliderValues |
self.SetLabel = SetLabel |
self.SetIsPercent = SetIsPercent |
self.alignoffset = 25 |
frame:EnableMouse(true) |
frame:SetScript("OnMouseDown",FrameOnMouseDown) |
self.slider = CreateFrame("Slider",nil,frame) |
local slider = self.slider |
slider:SetScript("OnEnter",Control_OnEnter) |
slider:SetScript("OnLeave",Control_OnLeave) |
slider:SetScript("OnMouseUp", Slider_OnMouseUp) |
slider.obj = self |
slider:SetOrientation("HORIZONTAL") |
slider:SetHeight(15) |
slider:SetHitRectInsets(0,0,-10,0) |
slider:SetBackdrop(SliderBackdrop) |
--slider:EnableMouseWheel(true) |
slider:SetScript("OnMouseWheel", Slider_OnMouseWheel) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(15) |
self.label = label |
self.lowtext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") |
self.lowtext:SetPoint("TOPLEFT",slider,"BOTTOMLEFT",2,3) |
self.hightext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") |
self.hightext:SetPoint("TOPRIGHT",slider,"BOTTOMRIGHT",-2,3) |
local editbox = CreateFrame("EditBox",nil,frame) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(GameFontHighlightSmall) |
editbox:SetPoint("TOP",slider,"BOTTOM",0,0) |
editbox:SetHeight(14) |
editbox:SetWidth(100) |
editbox:SetJustifyH("CENTER") |
editbox:EnableMouse(true) |
editbox:SetScript("OnEscapePressed",EditBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed",EditBox_OnEnterPressed) |
self.editbox = editbox |
editbox.obj = self |
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") |
frame:SetWidth(200) |
frame:SetHeight(44) |
slider:SetPoint("TOP",label,"BOTTOM",0,0) |
slider:SetPoint("LEFT",frame,"LEFT",3,0) |
slider:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
slider:SetValue(self.value or 0) |
slider:SetScript("OnValueChanged",Slider_OnValueChanged) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Tab Group -- |
-------------------------- |
do |
local Type = "TabGroup" |
local Version = 4 |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
local function Tab_FixWidth(self) |
self:SetScript("OnUpdate",nil) |
self:SetWidth(self.text:GetWidth()+20) |
end |
local function Tab_SetText(self, text) |
self.text:SetText(text) |
self:SetScript("OnUpdate",Tab_FixWidth) |
end |
local function UpdateTabLook(self) |
if self.selected then |
self.left:SetAlpha(1) |
self.right:SetAlpha(1) |
self.middle:SetAlpha(1) |
self.text:SetTextColor(1,1,1) |
self:GetHighlightTexture():Hide() |
else |
self.left:SetAlpha(0.5) |
self.right:SetAlpha(0.5) |
self.middle:SetAlpha(0.5) |
self.text:SetTextColor(1,0.82,0) |
self:GetHighlightTexture():Show() |
end |
if self.disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
self:GetHighlightTexture():Hide() |
end |
end |
local function Tab_SetSelected(self, selected) |
self.selected = selected |
UpdateTabLook(self) |
end |
local function Tab_OnClick(self) |
if not (self.selected or self.disabled) then |
self.obj:SelectTab(self.value) |
end |
end |
local function Tab_SetDisabled(self, disabled) |
self.disabled = disabled |
UpdateTabLook(self) |
end |
local function CreateTab(self, id) |
local tab = CreateFrame("Button",nil,self.border) |
tab.obj = self |
tab.id = id |
tab:SetWidth(64) |
tab:SetHeight(32) |
tab:SetScript("OnClick",Tab_OnClick) |
tab:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") |
tab:GetHighlightTexture():SetBlendMode("ADD") |
tab:GetHighlightTexture():SetPoint("TOPLEFT",tab,"TOPLEFT",2,-7) |
tab:GetHighlightTexture():SetPoint("BOTTOMRIGHT",tab,"BOTTOMRIGHT",-2,-3) |
local left = tab:CreateTexture(nil,"BACKGROUND") |
local middle = tab:CreateTexture(nil,"BACKGROUND") |
local right = tab:CreateTexture(nil,"BACKGROUND") |
local text = tab:CreateFontString(nil,"BACKGROUND","GameFontNormalSmall") |
tab.text = text |
tab.left = left |
tab.right = right |
tab.middle = middle |
tab.SetText = Tab_SetText |
tab.SetSelected = Tab_SetSelected |
tab.SetDisabled = Tab_SetDisabled |
text:SetPoint("LEFT",tab,"LEFT",5,-4) |
text:SetPoint("RIGHT",tab,"RIGHT",-5,-4) |
text:SetHeight(18) |
text:SetText("") |
left:SetTexture("Interface\\ChatFrame\\ChatFrameTab") |
middle:SetTexture("Interface\\ChatFrame\\ChatFrameTab") |
right:SetTexture("Interface\\ChatFrame\\ChatFrameTab") |
left:SetWidth(16) |
left:SetHeight(32) |
middle:SetWidth(44) |
middle:SetHeight(32) |
right:SetWidth(16) |
right:SetHeight(32) |
left:SetTexCoord(0,0.25,0,1) |
middle:SetTexCoord(0.25,0.75,0,1) |
right:SetTexCoord(0.75,1,0,1) |
left:SetPoint("TOPLEFT",tab,"TOPLEFT",0,0) |
right:SetPoint("TOPRIGHT",tab,"TOPRIGHT",0,0) |
middle:SetPoint("LEFT",left,"RIGHT",0,0) |
middle:SetPoint("RIGHT",right,"LEFT",0,0) |
return tab |
end |
local function SetTitle(self, text) |
self.titletext:SetText(text or "") |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
end |
local function SelectTab(self, value) |
local status = self.status or self.localstatus |
local found |
for i, v in ipairs(self.tabs) do |
if v.value == value then |
v:SetSelected(true) |
found = true |
else |
v:SetSelected(false) |
end |
end |
status.selected = value |
if found then |
self:Fire("OnGroupSelected",value) |
end |
end |
local function SetTabs(self, tabs) |
self.tablist = tabs |
self:BuildTabs() |
end |
local function BuildTabs(self) |
local status = self.status or self.localstatus |
local tablist = self.tablist |
local tabs = self.tabs |
for i, v in ipairs(tabs) do |
v:Hide() |
end |
for i, v in ipairs(tablist) do |
local tab = tabs[i] |
if not tab then |
tab = self:CreateTab(i) |
tabs[i] = tab |
if i == 1 then |
tab:SetPoint("BOTTOMLEFT",self.border,"TOPLEFT",0,-3) |
else |
tab:SetPoint("LEFT",tabs[i-1],"RIGHT",-3,0) |
end |
end |
tab:Show() |
tab:SetText(v.text) |
tab:SetDisabled(v.disabled) |
tab.value = v.value |
end |
if #tablist > 1 then |
self:SelectTab(status.selected or tablist[1].value) |
end |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 60 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 26 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.localstatus = {} |
self.Release = Release |
self.Acquire = Acquire |
self.SetTitle = SetTitle |
self.CreateTab = CreateTab |
self.SelectTab = SelectTab |
self.BuildTabs = BuildTabs |
self.SetStatusTable = SetStatusTable |
self.SetTabs = SetTabs |
self.frame = frame |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-37) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
self.tabs = {} |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Keybinding -- |
-------------------------- |
do |
local Type = "Keybinding" |
local Version = 6 |
local ControlBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 3, bottom = 3 } |
} |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function keybindingMsgFixWidth(this) |
this:SetWidth(this.msg:GetWidth()+10) |
this:SetScript("OnUpdate",nil) |
end |
local function Keybinding_OnClick(this, button) |
if button == "LeftButton" or button == "RightButton" then |
local self = this.obj |
if self.waitingForKey then |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
else |
this:EnableKeyboard(true) |
self.msgframe:Show() |
this:LockHighlight() |
self.waitingForKey = true |
end |
end |
end |
local ignoreKeys = nil |
local function Keybinding_OnKeyDown(this, key) |
local self = this.obj |
if self.waitingForKey then |
local keyPressed = key |
if keyPressed == "ESCAPE" then |
keyPressed = "" |
else |
if not ignoreKeys then |
ignoreKeys = { |
["BUTTON1"] = true, ["BUTTON2"] = true, |
["UNKNOWN"] = true, |
["LSHIFT"] = true, ["LCTRL"] = true, ["LALT"] = true, |
["RSHIFT"] = true, ["RCTRL"] = true, ["RALT"] = true, |
} |
end |
if ignoreKeys[keyPressed] then return end |
if IsShiftKeyDown() then |
keyPressed = "SHIFT-"..keyPressed |
end |
if IsControlKeyDown() then |
keyPressed = "CTRL-"..keyPressed |
end |
if IsAltKeyDown() then |
keyPressed = "ALT-"..keyPressed |
end |
end |
if not self.disabled then |
self:Fire("OnKeyChanged",keyPressed) |
end |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
end |
end |
local function Keybinding_OnMouseDown(this, button) |
if button == "LeftButton" or button == "RightButton" then |
return |
elseif button == "MiddleButton" then |
button = "BUTTON3" |
elseif button == "Button4" then |
button = "BUTTON4" |
elseif button == "Button5" then |
button = "BUTTON5" |
end |
Keybinding_OnKeyDown(this, button) |
end |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.waitingForKey = nil |
self.msgframe:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.button:Disable() |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.button:Enable() |
self.label:SetTextColor(1,1,1) |
end |
end |
local function SetKey(self, key) |
self.button:SetText(key or "") |
end |
local function SetLabel(self, label) |
self.label:SetText(label or "") |
end |
local count = 0 |
local function Constructor() |
count = count + 1 |
local frame = CreateFrame("Frame",nil,UIParent) |
local button = CreateFrame("Button","AceGUI-3.0 KeybindingButton"..count,frame,"UIPanelButtonTemplate2") |
local self = {} |
self.type = Type |
local text = button:GetFontString() |
text:SetPoint("LEFT",button,"LEFT",7,0) |
text:SetPoint("RIGHT",button,"RIGHT",-7,0) |
button:SetScript("OnClick",Keybinding_OnClick) |
button:SetScript("OnKeyDown",Keybinding_OnKeyDown) |
button:SetScript("OnEnter",Control_OnEnter) |
button:SetScript("OnLeave",Control_OnLeave) |
button:SetScript("OnMouseDown",Keybinding_OnMouseDown) |
button:RegisterForClicks("AnyDown") |
button:EnableMouse() |
button:SetHeight(24) |
button:SetWidth(200) |
button:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT",0,0) |
button:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
frame:SetWidth(200) |
frame:SetHeight(44) |
self.alignoffset = 30 |
self.button = button |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(18) |
self.label = label |
local msgframe = CreateFrame("Frame",nil,UIParent) |
msgframe:SetHeight(30) |
msgframe:SetBackdrop(ControlBackdrop) |
msgframe:SetBackdropColor(0,0,0) |
msgframe:SetFrameStrata("FULLSCREEN_DIALOG") |
msgframe:SetFrameLevel(1000) |
self.msgframe = msgframe |
local msg = msgframe:CreateFontString(nil,"OVERLAY","GameFontNormal") |
msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel") |
msgframe.msg = msg |
msg:SetPoint("TOPLEFT",msgframe,"TOPLEFT",5,-5) |
msgframe:SetScript("OnUpdate", keybindingMsgFixWidth) |
msgframe:SetPoint("BOTTOM",button,"TOP",0,0) |
msgframe:Hide() |
self.Release = Release |
self.Acquire = Acquire |
self.SetLabel = SetLabel |
self.SetDisabled = SetDisabled |
self.SetKey = SetKey |
self.frame = frame |
frame.obj = self |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Check Box -- |
-------------------------- |
--[[ |
Events : |
OnValueChanged |
]] |
do |
local Type = "CheckBox" |
local Version = 3 |
local function Acquire(self) |
self:SetValue(false) |
self.tristate = nil |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.check:Hide() |
self.highlight:Hide() |
self.down = nil |
self.checked = nil |
self:SetType() |
self:SetDisabled(false) |
end |
local function CheckBox_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.highlight:Show() |
end |
self:Fire("OnEnter") |
end |
local function CheckBox_OnLeave(this) |
local self = this.obj |
if not self.down then |
self.highlight:Hide() |
end |
self:Fire("OnLeave") |
end |
local function CheckBox_OnMouseUp(this) |
local self = this.obj |
if not self.disabled then |
self:ToggleChecked() |
self:Fire("OnValueChanged",self.checked) |
self.text:SetPoint("LEFT",self.check,"RIGHT",0,0) |
end |
self.down = nil |
end |
local function CheckBox_OnMouseDown(this) |
local self = this.obj |
if not self.disabled then |
self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1) |
self.down = true |
end |
end |
local function SetDisabled(self,disabled) |
self.disabled = disabled |
if disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
SetDesaturation(self.check, true) |
else |
self.text:SetTextColor(1,1,1) |
if self.tristate and self.checked == nil then |
SetDesaturation(self.check, true) |
else |
SetDesaturation(self.check, false) |
end |
end |
end |
local function SetValue(self,value) |
local check = self.check |
self.checked = value |
if value then |
SetDesaturation(self.check, false) |
check:SetWidth(24) |
check:SetHeight(24) |
self.check:Show() |
else |
--Nil is the unknown tristate value |
if self.tristate and value == nil then |
SetDesaturation(self.check, true) |
check:SetWidth(20) |
check:SetHeight(20) |
self.check:Show() |
else |
SetDesaturation(self.check, false) |
check:SetWidth(24) |
check:SetHeight(24) |
self.check:Hide() |
end |
end |
end |
local function SetTriState(self, enabled) |
self.tristate = enabled |
self:SetValue(self:GetValue()) |
end |
local function GetValue(self) |
return self.checked |
end |
local function SetType(self, type) |
local checkbg = self.checkbg |
local check = self.check |
local highlight = self.highlight |
if type == "radio" then |
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton") |
checkbg:SetTexCoord(0,0.25,0,1) |
check:SetTexture("Interface\\Buttons\\UI-RadioButton") |
check:SetTexCoord(0.5,0.75,0,1) |
check:SetBlendMode("ADD") |
highlight:SetTexture("Interface\\Buttons\\UI-RadioButton") |
highlight:SetTexCoord(0.5,0.75,0,1) |
else |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
checkbg:SetTexCoord(0,1,0,1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
check:SetTexCoord(0,1,0,1) |
check:SetBlendMode("BLEND") |
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") |
highlight:SetTexCoord(0,1,0,1) |
end |
end |
local function ToggleChecked(self) |
local value = self:GetValue() |
if self.tristate then |
--cycle in true, nil, false order |
if value then |
self:SetValue(nil) |
elseif value == nil then |
self:SetValue(false) |
else |
self:SetValue(true) |
end |
else |
self:SetValue(not self:GetValue()) |
end |
end |
local function SetLabel(self, label) |
self.text:SetText(label) |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetValue = SetValue |
self.GetValue = GetValue |
self.SetDisabled = SetDisabled |
self.SetType = SetType |
self.ToggleChecked = ToggleChecked |
self.SetLabel = SetLabel |
self.SetTriState = SetTriState |
self.frame = frame |
frame.obj = self |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
self.text = text |
frame:SetScript("OnEnter",CheckBox_OnEnter) |
frame:SetScript("OnLeave",CheckBox_OnLeave) |
frame:SetScript("OnMouseUp",CheckBox_OnMouseUp) |
frame:SetScript("OnMouseDown",CheckBox_OnMouseDown) |
frame:EnableMouse() |
local checkbg = frame:CreateTexture(nil,"ARTWORK") |
self.checkbg = checkbg |
checkbg:SetWidth(24) |
checkbg:SetHeight(24) |
checkbg:SetPoint("LEFT",frame,"LEFT",0,0) |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
local check = frame:CreateTexture(nil,"OVERLAY") |
self.check = check |
check:SetWidth(24) |
check:SetHeight(24) |
check:SetPoint("CENTER",checkbg,"CENTER",0,0) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(checkbg) |
highlight:Hide() |
text:SetJustifyH("LEFT") |
frame:SetHeight(24) |
frame:SetWidth(200) |
text:SetHeight(18) |
text:SetPoint("LEFT",check,"RIGHT",0,0) |
text:SetPoint("RIGHT",frame,"RIGHT",0,0) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Label -- |
-------------------------- |
do |
local Type = "Icon" |
local Version = 3 |
local function Acquire(self) |
self:SetText("") |
self:SetImage(nil) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
end |
local function SetImage(self, path, ...) |
local image = self.image |
image:SetTexture(path) |
if image:GetTexture() then |
self.imageshown = true |
local n = select('#', ...) |
if n == 4 or n == 8 then |
image:SetTexCoord(...) |
end |
else |
self.imageshown = nil |
end |
end |
local function OnClick(this) |
this.obj:Fire("OnClick") |
end |
local function OnEnter(this) |
this.obj.highlight:Show() |
end |
local function OnLeave(this) |
this.obj.highlight:Hide() |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetText = SetText |
self.frame = frame |
self.SetImage = SetImage |
frame.obj = self |
frame:SetHeight(110) |
frame:SetWidth(110) |
frame:EnableMouse(true) |
frame:SetScript("OnClick", OnClick) |
frame:SetScript("OnLeave", OnLeave) |
frame:SetScript("OnEnter", OnEnter) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontHighlight") |
label:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,10) |
label:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,10) |
label:SetJustifyH("CENTER") |
label:SetJustifyV("TOP") |
label:SetHeight(18) |
self.label = label |
local image = frame:CreateTexture(nil,"BACKGROUND") |
self.image = image |
image:SetWidth(64) |
image:SetHeight(64) |
image:SetPoint("TOP",frame,"TOP",0,-10) |
local highlight = frame:CreateTexture(nil,"OVERLAY") |
self.highlight = highlight |
highlight:SetAllPoints(image) |
highlight:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") |
highlight:SetTexCoord(0,1,0.23,0.77) |
highlight:SetBlendMode("ADD") |
highlight:Hide() |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
---------------------------------- |
-- Blizzard Options Group -- |
---------------------------------- |
--[[ |
Group Designed to be added to the bliz interface options panel |
]] |
do |
local Type = "BlizOptionsGroup" |
local Version = 5 |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetName() |
end |
local function okay(this) |
this.obj:Fire("okay") |
end |
local function cancel(this) |
this.obj:Fire("cancel") |
end |
local function defaults(this) |
this.obj:Fire("defaults") |
end |
local function SetName(self, name, parent) |
self.frame.name = name |
self.frame.parent = parent |
end |
local function OnShow(this) |
this.obj:Fire("OnShow") |
end |
local function OnHide(this) |
this.obj:Fire("OnHide") |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 63 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 26 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function SetTitle(self, title) |
local content = self.content |
content:ClearAllPoints() |
if not title or title == "" then |
content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",15,-10) |
self.label:SetText("") |
else |
content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",15,-40) |
self.label:SetText(title) |
end |
content:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-10,10) |
end |
local function Constructor() |
local frame = CreateFrame("Frame") |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.frame = frame |
self.SetName = SetName |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.SetTitle = SetTitle |
frame.obj = self |
frame.okay = okay |
frame.cancel = cancel |
frame.defaults = defaults |
frame:Hide() |
frame:SetScript("OnHide",OnHide) |
frame:SetScript("OnShow",OnShow) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") |
self.label = label |
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -15) |
label:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45) |
label:SetJustifyH("LEFT") |
label:SetJustifyV("TOP") |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",15,-10) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
---------------- |
-- Main Frame -- |
---------------- |
--[[ |
Events : |
OnClose |
]] |
do |
local Type = "Frame" |
local Version = 3 |
local FrameBackdrop = { |
bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", |
tile = true, tileSize = 32, edgeSize = 32, |
insets = { left = 8, right = 8, top = 8, bottom = 8 } |
} |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function frameOnClose(this) |
this.obj:Fire("OnClose") |
end |
local function frameOnSizeChanged(this) |
local self = this.obj |
local status = self.status or self.localstatus |
status.width = this:GetWidth() |
status.height = this:GetHeight() |
status.top = this:GetTop() |
status.left = this:GetLeft() |
end |
local function closeOnClick(this) |
this.obj:Hide() |
end |
local function frameOnMouseDown(this) |
this:GetParent():StartMoving() |
end |
local function frameOnMouseUp(this) |
local frame = this:GetParent() |
frame:StopMovingOrSizing() |
local self = frame.obj |
local status = self.status or self.localstatus |
status.width = frame:GetWidth() |
status.height = frame:GetHeight() |
status.top = frame:GetTop() |
status.left = frame:GetLeft() |
end |
local function sizerseOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOMRIGHT") |
end |
local function sizersOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOM") |
end |
local function sizereOnMouseDown(this) |
this:GetParent():StartSizing("RIGHT") |
end |
local function sizerOnMouseUp(this) |
this:GetParent():StopMovingOrSizing() |
end |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function SetStatusText(self,text) |
self.statustext:SetText(text) |
end |
local function Hide(self) |
self.frame:Hide() |
end |
local function Show(self) |
self.frame:Show() |
end |
local function Acquire(self) |
self.frame:SetParent(UIParent) |
self.frame:SetFrameStrata("FULLSCREEN_DIALOG") |
self:ApplyStatus() |
end |
local function Release(self) |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
self:ApplyStatus() |
end |
local function ApplyStatus(self) |
local status = self.status or self.localstatus |
local frame = self.frame |
frame:SetWidth(status.width or 700) |
frame:SetHeight(status.height or 500) |
if status.top and status.left then |
frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top) |
frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0) |
else |
frame:SetPoint("CENTER",UIParent,"CENTER") |
end |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 44 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 57 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = "Frame" |
self.Hide = Hide |
self.Show = Show |
self.SetTitle = SetTitle |
self.Release = Release |
self.Acquire = Acquire |
self.SetStatusText = SetStatusText |
self.SetStatusTable = SetStatusTable |
self.ApplyStatus = ApplyStatus |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
frame:SetWidth(700) |
frame:SetHeight(500) |
frame:SetPoint("CENTER",UIParent,"CENTER",0,0) |
frame:EnableMouse() |
frame:SetMovable(true) |
frame:SetResizable(true) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
frame:SetBackdrop(FrameBackdrop) |
frame:SetBackdropColor(0,0,0,1) |
frame:SetScript("OnHide",frameOnClose) |
frame:SetMinResize(400,200) |
frame:SetScript("OnSizeChanged", frameOnSizeChanged) |
frame:SetToplevel(true) |
local closebutton = CreateFrame("Button",nil,frame,"UIPanelButtonTemplate") |
closebutton:SetScript("OnClick", closeOnClick) |
closebutton:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-27,17) |
closebutton:SetHeight(20) |
closebutton:SetWidth(100) |
closebutton:SetText("Close") |
self.closebutton = closebutton |
closebutton.obj = self |
local statusbg = CreateFrame("Frame",nil,frame) |
statusbg:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",15,15) |
statusbg:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-132,15) |
statusbg:SetHeight(24) |
statusbg:SetBackdrop(PaneBackdrop) |
statusbg:SetBackdropColor(0.1,0.1,0.1) |
statusbg:SetBackdropBorderColor(0.4,0.4,0.4) |
self.statusbg = statusbg |
local statustext = statusbg:CreateFontString(nil,"OVERLAY","GameFontNormal") |
self.statustext = statustext |
statustext:SetPoint("TOPLEFT",statusbg,"TOPLEFT",7,-2) |
statustext:SetPoint("BOTTOMRIGHT",statusbg,"BOTTOMRIGHT",-7,2) |
statustext:SetHeight(20) |
statustext:SetJustifyH("LEFT") |
statustext:SetText("") |
local title = CreateFrame("Frame",nil,frame) |
self.title = title |
title:EnableMouse() |
title:SetScript("OnMouseDown",frameOnMouseDown) |
title:SetScript("OnMouseUp", frameOnMouseUp) |
local titlebg = frame:CreateTexture(nil,"OVERLAY") |
titlebg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg:SetTexCoord(0.31,0.67,0,0.63) |
titlebg:SetPoint("TOP",frame,"TOP",0,12) |
titlebg:SetWidth(100) |
titlebg:SetHeight(40) |
local titlebg_l = frame:CreateTexture(nil,"OVERLAY") |
titlebg_l:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg_l:SetTexCoord(0.21,0.31,0,0.63) |
titlebg_l:SetPoint("RIGHT",titlebg,"LEFT",0,0) |
titlebg_l:SetWidth(30) |
titlebg_l:SetHeight(40) |
local titlebg_right = frame:CreateTexture(nil,"OVERLAY") |
titlebg_right:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg_right:SetTexCoord(0.67,0.77,0,0.63) |
titlebg_right:SetPoint("LEFT",titlebg,"RIGHT",0,0) |
titlebg_right:SetWidth(30) |
titlebg_right:SetHeight(40) |
title:SetAllPoints(titlebg) |
local titletext = title:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOP",titlebg,"TOP",0,-14) |
self.titletext = titletext |
local sizer_se = CreateFrame("Frame",nil,frame) |
sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
sizer_se:SetWidth(25) |
sizer_se:SetHeight(25) |
sizer_se:EnableMouse() |
sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown) |
sizer_se:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_se = sizer_se |
local line1 = sizer_se:CreateTexture(nil, "BACKGROUND") |
self.line1 = line1 |
line1:SetWidth(14) |
line1:SetHeight(14) |
line1:SetPoint("BOTTOMRIGHT", -8, 8) |
line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
local x = 0.1 * 14/17 |
line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5) |
local line2 = sizer_se:CreateTexture(nil, "BACKGROUND") |
self.line2 = line2 |
line2:SetWidth(8) |
line2:SetHeight(8) |
line2:SetPoint("BOTTOMRIGHT", -8, 8) |
line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
local x = 0.1 * 8/17 |
line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5) |
local sizer_s = CreateFrame("Frame",nil,frame) |
sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0) |
sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) |
sizer_s:SetHeight(25) |
sizer_s:EnableMouse() |
sizer_s:SetScript("OnMouseDown",sizersOnMouseDown) |
sizer_s:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_s = sizer_s |
local sizer_e = CreateFrame("Frame",nil,frame) |
sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25) |
sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
sizer_e:SetWidth(25) |
sizer_e:EnableMouse() |
sizer_e:SetScript("OnMouseDown",sizereOnMouseDown) |
sizer_e:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_e = sizer_e |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",17,-27) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-17,40) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Inline Group -- |
-------------------------- |
--[[ |
This is a simple grouping container, no selection |
It will resize automatically to the height of the controls added to it |
]] |
do |
local Type = "InlineGroup" |
local Version = 3 |
local function Acquire(self) |
self:SetWidth(300) |
self:SetHeight(100) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function LayoutFinished(self, width, height) |
self:SetHeight((height or 0) + 40) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 20 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 20 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetTitle = SetTitle |
self.frame = frame |
self.LayoutFinished = LayoutFinished |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-17) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Dropdown -- |
-------------------------- |
--[[ |
Events : |
OnValueChanged |
]] |
do |
local Type = "Dropdown" |
local Version = 10 |
local ControlBackdrop = { |
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", |
edgeSize = 32, |
tileSize = 32, |
tile = true, |
insets = { left = 11, right = 12, top = 12, bottom = 11 }, |
} |
local function Acquire(self) |
self:SetLabel("") |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetLabel(nil) |
self.list = nil |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function SetText(self, text) |
self.text:SetText(text or "") |
end |
local function SetValue(self, value) |
if self.list then |
self.text:SetText(self.list[value] or "") |
end |
self.text.value = value |
end |
local function SetList(self, list) |
self.list = list |
end |
local function AddItem(self, value, text) |
if self.list then |
self.list[value] = text |
end |
end |
local function Dropdown_OnEnterPressed(this) |
local self = this.obj |
if not self.disabled then |
local ret = this.value or this:GetText() |
self:Fire("OnValueChanged",ret) |
end |
end |
local function Dropdown_TogglePullout(this) |
local self = this.obj |
if self.open then |
self.open = nil |
self.pullout:Hide() |
else |
self.open = true |
self:BuildPullout() |
if self.lines[1] and self.lines[1]:IsShown() then |
self.pullout:Show() |
end |
end |
end |
local function Dropdown_OnHide(this) |
this.obj.pullout:Hide() |
end |
local function Dropdown_LineClicked(this) |
local self = this.obj |
self.open = false |
self.pullout:Hide() |
self.text:SetText(this.text:GetText()) |
self.text.value = this.value |
Dropdown_OnEnterPressed(self.text) |
end |
local function Dropdown_LineEnter(this) |
this.highlight:Show() |
end |
local function Dropdown_LineLeave(this) |
this.highlight:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
self.button:Disable() |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.button:Enable() |
self.label:SetTextColor(1,.82,0) |
self.text:SetTextColor(1,1,1) |
end |
end |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local ddsort = {} |
local function BuildPullout(self) |
local list = self.list |
local lines = self.lines |
local totalheight = 22 |
self:ClearPullout() |
self.pullout:SetFrameLevel(self.frame:GetFrameLevel()+1000) |
if type(list) == "table" then |
for k, v in pairs(list) do |
tinsert(ddsort,k) |
end |
table.sort(ddsort) |
for i, value in pairs(ddsort) do |
local text = list[value] |
if not lines[i] then |
lines[i] = self:CreateLine() |
if i == 1 then |
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-10) |
else |
lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,1) |
end |
end |
lines[i].text:SetText(text) |
lines[i]:SetFrameLevel(self.frame:GetFrameLevel()+1001) |
lines[i].value = value |
if lines[i].value == self.text.value then |
lines[i].check:Show() |
else |
lines[i].check:Hide() |
end |
lines[i]:Show() |
totalheight = totalheight + 16 |
i = i + 1 |
end |
for k in pairs(ddsort) do |
ddsort[k] = nil |
end |
end |
self.pullout:SetHeight(totalheight) |
fixlevels(self.pullout,self.pullout:GetChildren()) |
end |
local function ClearPullout(self) |
if self.lines then |
for i, line in ipairs(self.lines) do |
line.text:SetText("") |
line:Hide() |
end |
end |
self.pullout:SetHeight(10) |
self.pullout:SetWidth(200) |
end |
local function SetLabel(self, text) |
if text and text ~= "" then |
self.label:SetText(text) |
self.label:Show() |
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-18) |
self.frame:SetHeight(44) |
else |
self.label:SetText("") |
self.label:Hide() |
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0) |
self.frame:SetHeight(26) |
end |
end |
local function CreateLine(self, row, column) |
local frame = CreateFrame("Button",nil,self.pullout) |
frame.text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
frame.text:SetTextColor(1,1,1) |
frame.text:SetJustifyH("LEFT") |
frame:SetHeight(17) |
frame:SetPoint("LEFT",self.pullout,"LEFT",6,0) |
frame:SetPoint("RIGHT",self.pullout,"RIGHT",-6,0) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
frame.obj = self |
local highlight = frame:CreateTexture(nil, "OVERLAY") |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetHeight(14) |
highlight:ClearAllPoints() |
highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
highlight:SetPoint("LEFT",frame,"LEFT",5,0) |
highlight:Hide() |
frame.highlight = highlight |
local check = frame:CreateTexture("OVERLAY") |
frame.check = check |
check:SetWidth(16) |
check:SetHeight(16) |
check:SetPoint("LEFT",frame,"LEFT",3,-1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
frame.text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0) |
frame.text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0) |
frame:SetScript("OnClick",Dropdown_LineClicked) |
frame:SetScript("OnEnter",Dropdown_LineEnter) |
frame:SetScript("OnLeave",Dropdown_LineLeave) |
return frame |
end |
local count = 0 |
local function Constructor() |
count = count + 1 |
local self = {} |
local frame = CreateFrame("Frame",nil,UIParent) |
local dropdown = CreateFrame("Frame","AceGUI30DropDown" .. count,frame, "UIDropDownMenuTemplate") |
self.dropdown = dropdown |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.CreateLine = CreateLine |
self.ClearPullout = ClearPullout |
self.BuildPullout = BuildPullout |
self.SetText = SetText |
self.SetValue = SetValue |
self.SetList = SetList |
self.AddItem = AddItem |
self.SetLabel = SetLabel |
self.SetDisabled = SetDisabled |
self.frame = frame |
frame.obj = self |
self.alignoffset = 30 |
frame:SetHeight(44) |
frame:SetWidth(200) |
frame:SetScript("OnHide",Dropdown_OnHide) |
dropdown:ClearAllPoints() |
dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0) |
dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0) |
dropdown:SetScript("OnHide", nil) |
-- fix anchoring of the dropdown |
local left = _G[dropdown:GetName() .. "Left"] |
local middle = _G[dropdown:GetName() .. "Middle"] |
local right = _G[dropdown:GetName() .. "Right"] |
middle:ClearAllPoints() |
right:ClearAllPoints() |
middle:SetPoint("LEFT", left, "RIGHT", 0, 0) |
middle:SetPoint("RIGHT", right, "LEFT", 0, 0) |
right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17) |
local button = _G[dropdown:GetName() .. "Button"] |
self.button = button |
button.obj = self |
button:SetScript("OnEnter",Control_OnEnter) |
button:SetScript("OnLeave",Control_OnLeave) |
button:SetScript("OnClick",Dropdown_TogglePullout) |
local text = _G[dropdown:GetName() .. "Text"] |
self.text = text |
text.obj = self |
text:ClearAllPoints() |
text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2) |
text:SetPoint("LEFT", left, "LEFT", 25, 2) |
local pullout = CreateFrame("Frame",nil,UIParent) |
self.pullout = pullout |
frame:EnableMouse() |
pullout:SetBackdrop(ControlBackdrop) |
pullout:SetBackdropColor(0,0,0) |
pullout:SetFrameStrata("FULLSCREEN_DIALOG") |
pullout:SetPoint("TOPLEFT",frame,"BOTTOMLEFT",0,0) |
pullout:SetPoint("TOPRIGHT",frame,"BOTTOMRIGHT",0,0) |
pullout:SetClampedToScreen(true) |
pullout:Hide() |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
label:Hide() |
self.label = label |
self.lines = {} |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Edit box -- |
-------------------------- |
--[[ |
Events : |
OnTextChanged |
OnEnterPressed |
]] |
do |
local Type = "EditBox" |
local Version = 5 |
local function Acquire(self) |
self:SetDisabled(false) |
self.showbutton = true |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function ShowButton(self) |
if self.showbutton then |
self.button:Show() |
self.editbox:SetTextInsets(0,20,3,3) |
end |
end |
local function HideButton(self) |
self.button:Hide() |
self.editbox:SetTextInsets(0,0,3,3) |
end |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
local cancel = self:Fire("OnEnterPressed",value) |
if not cancel then |
HideButton(self) |
end |
end |
local function Button_OnClick(this) |
local editbox = this.obj.editbox |
editbox:ClearFocus() |
EditBox_OnEnterPressed(editbox) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if type == "item" then |
self:SetText(info) |
self:Fire("OnEnterPressed",info) |
ClearCursor() |
elseif type == "spell" then |
local name, rank = GetSpellName(id, info) |
if rank and rank:match("%d") then |
name = name.."("..rank..")" |
end |
self:SetText(name) |
self:Fire("OnEnterPressed",name) |
ClearCursor() |
end |
HideButton(self) |
end |
local function EditBox_OnTextChanged(this) |
local self = this.obj |
local value = this:GetText() |
if value ~= self.lasttext then |
self:Fire("OnTextChanged",value) |
self.lasttext = value |
ShowButton(self) |
end |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5,0.5,0.5) |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.editbox:EnableMouse(true) |
self.editbox:SetTextColor(1,1,1) |
self.label:SetTextColor(1,.82,0) |
end |
end |
local function SetText(self, text) |
self.lasttext = text or "" |
self.editbox:SetText(text or "") |
self.editbox:SetCursorPosition(0) |
HideButton(self) |
end |
local function SetWidth(self, width) |
self.frame:SetWidth(width) |
end |
local function SetLabel(self, text) |
if text and text ~= "" then |
self.label:SetText(text) |
self.label:Show() |
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18) |
self.frame:SetHeight(44) |
else |
self.label:SetText("") |
self.label:Hide() |
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0) |
self.frame:SetHeight(26) |
end |
end |
local count = 0 |
local function Constructor() |
count = count + 1 |
local frame = CreateFrame("Frame",nil,UIParent) |
local editbox = CreateFrame("EditBox","AceGUI-3.0EditBox"..count,frame,"InputBoxTemplate") |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.SetDisabled = SetDisabled |
self.SetText = SetText |
self.SetWidth = SetWidth |
self.SetLabel = SetLabel |
self.frame = frame |
frame.obj = self |
self.editbox = editbox |
editbox.obj = self |
self.alignoffset = 30 |
frame:SetHeight(44) |
frame:SetWidth(200) |
editbox:SetScript("OnEnter",Control_OnEnter) |
editbox:SetScript("OnLeave",Control_OnLeave) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(ChatFontNormal) |
editbox:SetScript("OnEscapePressed",EditBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed",EditBox_OnEnterPressed) |
editbox:SetScript("OnTextChanged",EditBox_OnTextChanged) |
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
editbox:SetTextInsets(0,0,3,3) |
editbox:SetMaxLetters(256) |
editbox:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",6,0) |
editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
editbox:SetHeight(19) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-2) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-2) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local button = CreateFrame("Button",nil,editbox,"UIPanelButtonTemplate") |
button:SetWidth(20) |
button:SetHeight(20) |
button:SetPoint("RIGHT",editbox,"RIGHT",-2,0) |
button:SetText("OK") |
button:SetScript("OnClick", Button_OnClick) |
button:Hide() |
self.button = button |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceGUI-3.0.lua"/> |
<Script file="widgets\AceGUIWidget-Button.lua"/> |
<Script file="widgets\AceGUIWidget-CheckBox.lua"/> |
<Script file="widgets\AceGUIWidget-ColorPicker.lua"/> |
<Script file="widgets\AceGUIWidget-DropDownGroup.lua"/> |
<Script file="widgets\AceGUIWidget-DropDown.lua"/> |
<Script file="widgets\AceGUIWidget-EditBox.lua"/> |
<Script file="widgets\AceGUIWidget-Frame.lua"/> |
<Script file="widgets\AceGUIWidget-Heading.lua"/> |
<Script file="widgets\AceGUIWidget-InlineGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Keybinding.lua"/> |
<Script file="widgets\AceGUIWidget-ScrollFrame.lua"/> |
<Script file="widgets\AceGUIWidget-SimpleGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Slider.lua"/> |
<Script file="widgets\AceGUIWidget-TabGroup.lua"/> |
<Script file="widgets\AceGUIWidget-TreeGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Label.lua"/> |
<Script file="widgets\AceGUIWidget-MultiLineEditBox.lua"/> |
<Script file="widgets\AceGUIWidget-BlizOptionsGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Icon.lua"/> |
</Ui> |
--[[ $Id: AceGUI-3.0.lua 65669 2008-03-25 08:59:22Z nevcairiel $ ]] |
local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 7 |
local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR) |
if not AceGUI then return end -- No upgrade needed |
local con = LibStub("AceConsole-3.0",true) |
AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {} |
AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {} |
AceGUI.WidgetBase = AceGUI.WidgetBase or {} |
AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {} |
AceGUI.WidgetVersions = AceGUI.WidgetVersions or {} |
-- local upvalues |
local WidgetRegistry = AceGUI.WidgetRegistry |
local LayoutRegistry = AceGUI.LayoutRegistry |
local WidgetVersions = AceGUI.WidgetVersions |
local pcall = pcall |
local select = select |
local pairs = pairs |
local ipairs = ipairs |
local type = type |
local assert = assert |
local tinsert = tinsert |
local tremove = tremove |
local CreateFrame = CreateFrame |
local UIParent = UIParent |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", table.concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
return Dispatchers[select('#', ...)](func, ...) |
end |
-- Recycling functions |
local new, del |
do |
AceGUI.objPools = AceGUI.objPools or {} |
local objPools = AceGUI.objPools |
--Returns a new instance, if none are available either returns a new table or calls the given contructor |
function new(type,constructor,...) |
if not type then |
type = "table" |
end |
if not objPools[type] then |
objPools[type] = {} |
end |
local newObj = tremove(objPools[type]) |
if not newObj then |
newObj = constructor and constructor(...) or {} |
end |
return newObj |
end |
-- Releases an instance to the Pool |
function del(obj,type) |
if not type then |
type = "table" |
end |
if not objPools[type] then |
objPools[type] = {} |
end |
tinsert(objPools[type],obj) |
end |
end |
------------------- |
-- API Functions -- |
------------------- |
-- Gets a widget Object |
local warned = {} |
function AceGUI:Create(type) |
local reg = WidgetRegistry |
if reg[type] then |
local widget = new(type,reg[type]) |
if widget.Acquire then |
widget:Acquire() |
elseif widget.Aquire then |
if not warned[type] then |
DEFAULT_CHAT_FRAME:AddMessage(("AceGUI: Warning, Widget type %s uses the deprecated Aquire, this should be updated to Acquire"):format(type)) |
warned[type] = true |
end |
widget.Acquire = widget.Aquire |
widget:Acquire() |
else |
error(("Widget type %s doesn't supply an Acquire Function"):format(type)) |
end |
safecall(widget.ResumeLayout, widget) |
return widget |
end |
end |
-- Releases a widget Object |
function AceGUI:Release(widget) |
safecall( widget.PauseLayout, widget ) |
widget:Fire("OnRelease") |
safecall( widget.ReleaseChildren, widget ) |
for k in pairs(widget.userdata) do |
widget.userdata[k] = nil |
end |
for k in pairs(widget.events) do |
widget.events[k] = nil |
end |
widget.width = nil |
widget:Release() |
--widget.frame:SetParent(nil) |
widget.frame:ClearAllPoints() |
widget.frame:Hide() |
widget.frame:SetParent(nil) |
if widget.content then |
widget.content.width = nil |
widget.content.height = nil |
end |
del(widget,widget.type) |
end |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is acquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Widget Base Template -- |
-------------------------- |
do |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local WidgetBase = AceGUI.WidgetBase |
WidgetBase.SetParent = function(self, parent) |
local frame = self.frame |
frame:SetParent(nil) |
frame:SetParent(parent.content) |
self.parent = parent |
fixlevels(parent.frame,parent.frame:GetChildren()) |
end |
WidgetBase.SetCallback = function(self, name, func) |
if type(func) == "function" then |
self.events[name] = func |
end |
end |
WidgetBase.Fire = function(self, name, ...) |
if self.events[name] then |
local success, ret = safecall(self.events[name], self, name, ...) |
if success then |
return ret |
end |
end |
end |
WidgetBase.SetWidth = function(self, width) |
self.frame:SetWidth(width) |
self.frame.width = width |
if self.OnWidthSet then |
self:OnWidthSet(width) |
end |
end |
WidgetBase.SetHeight = function(self, height) |
self.frame:SetHeight(height) |
self.frame.height = height |
if self.OnHeightSet then |
self:OnHeightSet(height) |
end |
end |
-- local function LayoutOnUpdate(this) |
-- this:SetScript("OnUpdate",nil) |
-- this.obj:PerformLayout() |
-- end |
local WidgetContainerBase = AceGUI.WidgetContainerBase |
WidgetContainerBase.PauseLayout = function(self) |
self.LayoutPaused = true |
end |
WidgetContainerBase.ResumeLayout = function(self) |
self.LayoutPaused = nil |
end |
WidgetContainerBase.PerformLayout = function(self) |
if self.LayoutPaused then |
return |
end |
safecall(self.LayoutFunc,self.content, self.children) |
end |
--call this function to layout, makes sure layed out objects get a frame to get sizes etc |
WidgetContainerBase.DoLayout = function(self) |
self:PerformLayout() |
-- if not self.parent then |
-- self.frame:SetScript("OnUpdate", LayoutOnUpdate) |
-- end |
end |
WidgetContainerBase.AddChild = function(self, child) |
tinsert(self.children,child) |
child:SetParent(self) |
child.frame:Show() |
self:DoLayout() |
end |
WidgetContainerBase.ReleaseChildren = function(self) |
local children = self.children |
for i in ipairs(children) do |
AceGUI:Release(children[i]) |
children[i] = nil |
end |
end |
WidgetContainerBase.SetLayout = function(self, Layout) |
self.LayoutFunc = AceGUI:GetLayout(Layout) |
end |
local function ContentResize(this) |
if this.lastwidth ~= this:GetWidth() then |
this.width = this:GetWidth() |
this.height = this:GetHeight() |
this.obj:DoLayout() |
end |
end |
setmetatable(WidgetContainerBase,{__index=WidgetBase}) |
--One of these function should be called on each Widget Instance as part of its creation process |
function AceGUI:RegisterAsContainer(widget) |
widget.children = {} |
widget.userdata = {} |
widget.events = {} |
widget.base = WidgetContainerBase |
widget.content:SetScript("OnSizeChanged",ContentResize) |
setmetatable(widget,{__index=WidgetContainerBase}) |
widget:SetLayout("List") |
end |
function AceGUI:RegisterAsWidget(widget) |
widget.userdata = {} |
widget.events = {} |
widget.base = WidgetBase |
setmetatable(widget,{__index=WidgetBase}) |
end |
end |
------------------ |
-- Widget API -- |
------------------ |
-- Registers a widget Constructor, this function returns a new instance of the Widget |
function AceGUI:RegisterWidgetType(Name, Constructor, Version) |
assert(type(Constructor) == "function") |
assert(type(Version) == "number") |
local oldVersion = WidgetVersions[Name] |
if oldVersion and oldVersion >= Version then return end |
WidgetVersions[Name] = Version |
WidgetRegistry[Name] = Constructor |
end |
-- Registers a Layout Function |
function AceGUI:RegisterLayout(Name, LayoutFunc) |
assert(type(LayoutFunc) == "function") |
if type(Name) == "string" then |
Name = Name:upper() |
end |
LayoutRegistry[Name] = LayoutFunc |
end |
function AceGUI:GetLayout(Name) |
if type(Name) == "string" then |
Name = Name:upper() |
end |
return LayoutRegistry[Name] |
end |
--[[ Widget Template |
-------------------------- |
-- Widget Name -- |
-------------------------- |
do |
local Type = "Type" |
local function Acquire(self) |
end |
local function Release(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.Release = Release |
self.Acquire = Acquire |
self.frame = frame |
frame.obj = self |
--Container Support |
--local content = CreateFrame("Frame",nil,frame) |
--self.content = content |
--AceGUI:RegisterAsContainer(self) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor) |
end |
]] |
------------- |
-- Layouts -- |
------------- |
--[[ |
A Layout is a func that takes 2 parameters |
content - the frame that widgets will be placed inside |
children - a table containing the widgets to layout |
]] |
-- Very simple Layout, Children are stacked on top of each other down the left side |
AceGUI:RegisterLayout("List", |
function(content, children) |
local height = 0 |
for i, child in ipairs(children) do |
local frame = child.frame |
frame:ClearAllPoints() |
frame:Show() |
if i == 1 then |
frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0) |
else |
frame:SetPoint("TOPLEFT",children[i-1].frame,"BOTTOMLEFT",0,0) |
end |
if child.width == "fill" then |
frame:SetPoint("RIGHT",content,"RIGHT") |
if child.OnWidthSet then |
child:OnWidthSet(content.width or content:GetWidth()) |
end |
if child.DoLayout then |
child:DoLayout() |
end |
end |
height = height + (frame.height or frame:GetHeight() or 0) |
end |
safecall( content.obj.LayoutFinished, content.obj, nil, height ) |
end |
) |
-- A single control fills the whole content area |
AceGUI:RegisterLayout("Fill", |
function(content, children) |
if children[1] then |
children[1].frame:SetAllPoints(content) |
children[1].frame:Show() |
safecall( content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight() ) |
end |
end |
) |
AceGUI:RegisterLayout("Flow", |
function(content, children) |
--used height so far |
local height = 0 |
--width used in the current row |
local usedwidth = 0 |
--height of the current row |
local rowheight = 0 |
local rowoffset = 0 |
local lastrowoffset |
local width = content.width or content:GetWidth() or 0 |
--control at the start of the row |
local rowstart |
local rowstartoffset |
local lastrowstart |
local isfullheight |
local frameoffset |
local lastframeoffset |
for i, child in ipairs(children) do |
local frame = child.frame |
local frameheight = frame.height or frame:GetHeight() or 0 |
local framewidth = frame.width or frame:GetWidth() or 0 |
lastframeoffset = frameoffset |
frameoffset = child.alignoffset or (frameheight / 2) |
frame:Show() |
frame:ClearAllPoints() |
if i == 1 then |
-- anchor the first control to the top left |
--frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0) |
rowheight = frameheight |
rowoffset = frameoffset |
rowstart = frame |
rowstartoffset = frameoffset |
usedwidth = framewidth |
else |
-- if there isn't available width for the control start a new row |
-- if a control is "fill" it will be on a row of its own full width |
if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then |
--anchor the previous row, we will now know its height and offset |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3)) |
height = height + rowheight + 3 |
--save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it |
rowstart = frame |
rowstartoffset = frameoffset |
rowheight = frameheight |
rowoffset = frameoffset |
usedwidth = frame.width or frame:GetWidth() |
-- put the control on the current row, adding it to the width and checking if the height needs to be increased |
else |
--handles cases where the new height is higher than either control because of the offsets |
--math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset) |
--offset is always the larger of the two offsets |
rowoffset = math.max(rowoffset, frameoffset) |
rowheight = math.max(rowheight,rowoffset+(frameheight/2)) |
frame:SetPoint("TOPLEFT",children[i-1].frame,"TOPRIGHT",0,frameoffset-lastframeoffset) |
usedwidth = framewidth + usedwidth |
end |
end |
if child.width == "fill" then |
child:SetWidth(width) |
frame:SetPoint("RIGHT",content,"RIGHT",0,0) |
usedwidth = 0 |
rowstart = frame |
rowstartoffset = frameoffset |
if child.DoLayout then |
child:DoLayout() |
end |
rowheight = frame.height or frame:GetHeight() or 0 |
rowoffset = child.alignoffset or (rowheight / 2) |
rowstartoffset = rowoffset |
end |
if child.height == "fill" then |
frame:SetPoint("BOTTOM",content,"BOTTOM") |
isfullheight = true |
break |
end |
end |
--anchor the last row, if its full height needs a special case since its height has just been changed by the anchor |
if isfullheight then |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-height) |
elseif rowstart then |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3)) |
end |
height = height + rowheight + 3 |
safecall( content.obj.LayoutFinished, content.obj, nil, height ) |
end |
) |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceAddon-3.0.lua"/> |
</Ui> |
--[[ $Id: AceAddon-3.0.lua 66331 2008-03-27 09:41:53Z nevcairiel $ ]] |
local MAJOR, MINOR = "AceAddon-3.0", 4 |
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceAddon then return end -- No Upgrade needed. |
AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame |
AceAddon.addons = AceAddon.addons or {} -- addons in general |
AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon. |
AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized |
AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled |
AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon |
local tinsert, tconcat = table.insert, table.concat |
local fmt = string.format |
local pairs, next, type = pairs, next, type |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", tconcat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
-- we check to see if the func is passed is actually a function here and don't error when it isn't |
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not |
-- present execution should continue without hinderance |
if type(func) == "function" then |
return Dispatchers[select('#', ...)](func, ...) |
end |
end |
-- local functions that will be implemented further down |
local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype |
-- used in the addon metatable |
local function addontostring( self ) return self.name end |
-- AceAddon:NewAddon( name, [lib, lib, lib, ...] ) |
-- name (string) - unique addon object name |
-- [lib] (string) - optional libs to embed in the addon object |
-- |
-- returns the addon object when succesful |
function AceAddon:NewAddon(name, ...) |
if type(name) ~= "string" then error(("Usage: NewAddon(name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end |
if self.addons[name] then error(("Usage: NewAddon(name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2) end |
local addon = setmetatable( {name = name}, { __tostring = addontostring } ) |
self.addons[name] = addon |
addon.modules = {} |
addon.defaultModuleLibraries = {} |
Embed( addon ) -- embed NewModule, GetModule methods |
self:EmbedLibraries(addon, ...) |
-- add to queue of addons to be initialized upon ADDON_LOADED |
tinsert(self.initializequeue, addon) |
return addon |
end |
-- AceAddon:GetAddon( name, [silent]) |
-- name (string) - unique addon object name |
-- silent (boolean) - if true, addon is optional, silently return nil if its not found |
-- |
-- throws an error if the addon object can not be found (except silent is set) |
-- returns the addon object if found |
function AceAddon:GetAddon(name, silent) |
if not silent and not self.addons[name] then |
error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2) |
end |
return self.addons[name] |
end |
-- AceAddon:EmbedLibraries( addon, [lib, lib, lib, ...] ) |
-- addon (object) - addon to embed the libs in |
-- [lib] (string) - optional libs to embed |
function AceAddon:EmbedLibraries(addon, ...) |
for i=1,select("#", ... ) do |
local libname = select(i, ...) |
self:EmbedLibrary(addon, libname, false, 4) |
end |
end |
-- AceAddon:EmbedLibrary( addon, libname, silent, offset ) |
-- addon (object) - addon to embed the libs in |
-- libname (string) - lib to embed |
-- [silent] (boolean) - optional, marks an embed to fail silently if the library doesn't exist. |
-- [offset] (number) - will push the error messages back to said offset defaults to 2 |
function AceAddon:EmbedLibrary(addon, libname, silent, offset) |
local lib = LibStub:GetLibrary(libname, true) |
if not lib and not silent then |
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2) |
elseif lib and type(lib.Embed) == "function" then |
lib:Embed(addon) |
tinsert(self.embeds[addon], libname) |
return true |
elseif lib then |
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2) |
end |
end |
-- addon:GetModule( name, [silent]) |
-- name (string) - unique module object name |
-- silent (boolean) - if true, module is optional, silently return nil if its not found |
-- |
-- throws an error if the addon object can not be found (except silent is set) |
-- returns the module object if found |
function GetModule(self, name, silent) |
if not self.modules[name] and not silent then |
error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2) |
end |
return self.modules[name] |
end |
local function IsModuleTrue(self) return true end |
-- addon:NewModule( name, [prototype, [lib, lib, lib, ...] ) |
-- name (string) - unique module object name for this addon |
-- prototype (object) - object to derive this module from, methods and values from this table will be mixed into the module, if a string is passed a lib is assumed |
-- [lib] (string) - optional libs to embed in the addon object |
-- |
-- returns the addon object when succesful |
function NewModule(self, name, prototype, ...) |
if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end |
if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end |
if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end |
-- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well. |
-- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is. |
local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name)) |
module.IsModule = IsModuleTrue |
module:SetEnabledState(self.defaultModuleState) |
module.moduleName = name |
if type(prototype) == "string" then |
AceAddon:EmbedLibraries(module, prototype, ...) |
else |
AceAddon:EmbedLibraries(module, ...) |
end |
AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries)) |
if not prototype or type(prototype) == "string" then |
prototype = self.defaultModulePrototype or nil |
end |
if type(prototype) == "table" then |
local mt = getmetatable(module) |
mt.__index = prototype |
setmetatable(module, mt) -- More of a Base class type feel. |
end |
safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy. |
self.modules[name] = module |
return module |
end |
--addon:GetName() |
-- Returns the real name of the addon or module, without any prefix |
function GetName(self) |
return self.moduleName or self.name |
end |
--addon:Enable() |
-- Enables the Addon if possible, return true or false depending on success |
function Enable(self) |
self:SetEnabledState(true) |
return AceAddon:EnableAddon(self) |
end |
--addon:Disable() |
-- Disables the Addon if possible, return true or false depending on success |
function Disable(self) |
self:SetEnabledState(false) |
return AceAddon:DisableAddon(self) |
end |
-- addon:EnableModule( name ) |
-- name (string) - unique module object name |
-- |
-- Enables the Module if possible, return true or false depending on success |
function EnableModule(self, name) |
local module = self:GetModule( name ) |
return module:Enable() |
end |
-- addon:DisableModule( name ) |
-- name (string) - unique module object name |
-- |
-- Disables the Module if possible, return true or false depending on success |
function DisableModule(self, name) |
local module = self:GetModule( name ) |
return module:Disable() |
end |
-- addon:SetDefaultModuleLibraries( [lib, lib, lib, ...] ) |
-- [lib] (string) - libs to embed in every module |
function SetDefaultModuleLibraries(self, ...) |
if next(self.modules) then |
error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2) |
end |
self.defaultModuleLibraries = {...} |
end |
-- addon:SetDefaultModuleState( state ) |
-- state (boolean) - default state for new modules (enabled=true, disabled=false) |
function SetDefaultModuleState(self, state) |
if next(self.modules) then |
error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2) |
end |
self.defaultModuleState = state |
end |
-- addon:SetDefaultModulePrototype( prototype ) |
-- prototype (string or table) - the default prototype to use if none is specified on module creation |
function SetDefaultModulePrototype(self, prototype) |
if next(self.modules) then |
error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2) |
end |
if type(prototype) ~= "table" then |
error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2) |
end |
self.defaultModulePrototype = prototype |
end |
-- addon:SetEnabledState ( state ) |
-- state ( boolean ) - set the state of an addon or module (enabled=true, disabled=false) |
-- |
-- should only be called before any Enabling actually happend, aka in OnInitialize |
function SetEnabledState(self, state) |
self.enabledState = state |
end |
local function IterateModules(self) return pairs(self.modules) end |
local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end |
local function IsEnabled(self) return self.enabledState end |
local mixins = { |
NewModule = NewModule, |
GetModule = GetModule, |
Enable = Enable, |
Disable = Disable, |
EnableModule = EnableModule, |
DisableModule = DisableModule, |
IsEnabled = IsEnabled, |
SetDefaultModuleLibraries = SetDefaultModuleLibraries, |
SetDefaultModuleState = SetDefaultModuleState, |
SetDefaultModulePrototype = SetDefaultModulePrototype, |
SetEnabledState = SetEnabledState, |
IterateModules = IterateModules, |
IterateEmbeds = IterateEmbeds, |
GetName = GetName, |
} |
local function IsModule(self) return false end |
local pmixins = { |
defaultModuleState = true, |
enabledState = true, |
IsModule = IsModule, |
} |
-- Embed( target ) |
-- target (object) - target object to embed aceaddon in |
-- |
-- this is a local function specifically since it's meant to be only called internally |
function Embed(target) |
for k, v in pairs(mixins) do |
target[k] = v |
end |
for k, v in pairs(pmixins) do |
target[k] = target[k] or v |
end |
end |
-- AceAddon:IntializeAddon( addon ) |
-- addon (object) - addon to intialize |
-- |
-- calls OnInitialize on the addon object if available |
-- calls OnEmbedInitialize on embedded libs in the addon object if available |
function AceAddon:InitializeAddon(addon) |
safecall(addon.OnInitialize, addon) |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedInitialize, lib, addon) end |
end |
-- we don't call InitializeAddon on modules specifically, this is handled |
-- from the event handler and only done _once_ |
end |
-- AceAddon:EnableAddon( addon ) |
-- addon (object) - addon to enable |
-- |
-- calls OnEnable on the addon object if available |
-- calls OnEmbedEnable on embedded libs in the addon object if available |
function AceAddon:EnableAddon(addon) |
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end |
if self.statuses[addon.name] or not addon.enabledState then return false end |
-- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable. |
self.statuses[addon.name] = true |
-- TODO: handle 'first'? Or let addons do it on their own? |
safecall(addon.OnEnable, addon) |
-- make sure we're still enabled before continueing |
if self.statuses[addon.name] then |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedEnable, lib, addon) end |
end |
-- enable possible modules. |
for name, module in pairs(addon.modules) do |
self:EnableAddon(module) |
end |
end |
return self.statuses[addon.name] -- return true if we're disabled |
end |
-- AceAddon:DisableAddon( addon ) |
-- addon (object|string) - addon to disable |
-- |
-- calls OnDisable on the addon object if available |
-- calls OnEmbedDisable on embedded libs in the addon object if available |
function AceAddon:DisableAddon(addon) |
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end |
if not self.statuses[addon.name] then return false end |
-- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable. |
self.statuses[addon.name] = false |
safecall( addon.OnDisable, addon ) |
-- make sure we're still disabling... |
if not self.statuses[addon.name] then |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedDisable, lib, addon) end |
end |
-- disable possible modules. |
for name, module in pairs(addon.modules) do |
self:DisableAddon(module) |
end |
end |
return not self.statuses[addon.name] -- return true if we're disabled |
end |
--The next few funcs are just because no one should be reaching into the internal registries |
--Thoughts? |
function AceAddon:IterateAddons() return pairs(self.addons) end |
function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end |
function AceAddon:IterateAddonStatus() return pairs(self.statuses) end |
function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end |
-- Event Handling |
local function onEvent(this, event, arg1) |
if event == "ADDON_LOADED" or event == "PLAYER_LOGIN" then |
-- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration |
while(#AceAddon.initializequeue > 0) do |
local addon = tremove(AceAddon.initializequeue, 1) |
-- this might be an issue with recursion - TODO: validate |
if event == "ADDON_LOADED" then addon.baseName = arg1 end |
AceAddon:InitializeAddon(addon) |
tinsert(AceAddon.enablequeue, addon) |
end |
if IsLoggedIn() then |
while(#AceAddon.enablequeue > 0) do |
local addon = tremove(AceAddon.enablequeue, 1) |
AceAddon:EnableAddon(addon) |
end |
end |
end |
end |
AceAddon.frame:RegisterEvent("ADDON_LOADED") |
AceAddon.frame:RegisterEvent("PLAYER_LOGIN") |
AceAddon.frame:SetScript("OnEvent", onEvent) |
-- upgrade embeded |
for name, addon in pairs(AceAddon.addons) do |
Embed(addon) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigCmd-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigCmd-3.0 |
Handles commandline optionstable access |
REQUIRES: AceConsole-3.0 for command registration (loaded on demand) |
]] |
-- TODO: handle disabled / hidden |
-- TODO: implement handlers for all types |
-- TODO: plugin args |
local MAJOR, MINOR = "AceConfigCmd-3.0", 4 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.commands = lib.commands or {} |
local commands = lib.commands |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local AceConsole -- LoD |
local AceConsoleName = "AceConsole-3.0" |
local L = setmetatable({}, { -- TODO: replace with proper locale |
__index = function(self,k) return k end |
}) |
local function print(msg) |
(SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg) |
end |
-- pickfirstset() - picks the first non-nil value and returns it |
local function pickfirstset(...) |
for i=1,select("#",...) do |
if select(i,...)~=nil then |
return select(i,...) |
end |
end |
end |
-- err() - produce real error() regarding malformed options tables etc |
local function err(info,inputpos,msg ) |
local cmdstr=" "..strsub(info.input, 1, inputpos-1) |
error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2) |
end |
-- usererr() - produce chatframe message regarding bad slash syntax etc |
local function usererr(info,inputpos,msg ) |
local cmdstr=strsub(info.input, 1, inputpos-1); |
print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table")) |
end |
-- callmethod() - call a given named method (e.g. "get", "set") with given arguments |
local function callmethod(info, inputpos, tab, methodtype, ...) |
local method = info[methodtype] |
if not method then |
err(info, inputpos, "'"..methodtype.."': not set") |
end |
info.arg = tab.arg |
info.option = tab |
info.type = tab.type |
if type(method)=="function" then |
return method(info, ...) |
elseif type(method)=="string" then |
if type(info.handler[method])~="function" then |
err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler)) |
end |
return info.handler[method](info.handler, info, ...) |
else |
assert(false) -- type should have already been checked on read |
end |
end |
-- callfunction() - call a given named function (e.g. "name", "desc") with given arguments |
local function callfunction(info, tab, methodtype, ...) |
local method = tab[methodtype] |
info.arg = tab.arg |
info.option = tab |
info.type = tab.type |
if type(method)=="function" then |
return method(info, ...) |
else |
assert(false) -- type should have already been checked on read |
end |
end |
-- do_final() - do the final step (set/execute) along with validation and confirmation |
local function do_final(info, inputpos, tab, methodtype, ...) |
if info.validate then |
local res = callmethod(info,inputpos,tab,"validate",...) |
if type(res)=="string" then |
usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res) |
return |
end |
end |
-- console ignores .confirm |
callmethod(info,inputpos,tab,methodtype, ...) |
end |
-- getparam() - used by handle() to retreive and store "handler", "get", "set", etc |
local function getparam(info, inputpos, tab, depth, paramname, types, errormsg) |
local old,oldat = info[paramname], info[paramname.."_at"] |
local val=tab[paramname] |
if val~=nil then |
if val==false then |
val=nil |
elseif not types[type(val)] then |
err(info, inputpos, "'" .. paramname.. "' - "..errormsg) |
end |
info[paramname] = val |
info[paramname.."_at"] = depth |
end |
return old,oldat |
end |
-- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.* |
local dummytable={} |
local function iterateargs(tab) |
if not tab.plugins then |
return pairs(tab.args) |
end |
local argtabkey,argtab=next(tab.plugins) |
local v |
return function(_, k) |
while argtab do |
k,v = next(argtab, k) |
if k then return k,v end |
if argtab==tab.args then |
argtab=nil |
else |
argtabkey,argtab = next(tab.plugins, argtabkey) |
if not argtabkey then |
argtab=tab.args |
end |
end |
end |
end |
end |
local function showhelp(info, inputpos, tab, noHead) |
if not noHead then |
print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":") |
end |
local sortTbl = {} -- [1..n]=name |
local refTbl = {} -- [name]=tableref |
for k,v in iterateargs(tab) do |
if not refTbl[k] then -- a plugin overriding something in .args |
table.insert(sortTbl, k) |
refTbl[k] = v |
end |
end |
table.sort(sortTbl, function(one, two) |
local o1 = refTbl[one].order or 100 |
local o2 = refTbl[two].order or 100 |
if type(o1) == "function" or type(o1) == "string" then |
info.order = o1 |
info[#info+1] = one |
o1 = callmethod(info, inputpos, refTbl[one], "order") |
info[#info] = nil |
info.order = nil |
end |
if type(o2) == "function" or type(o1) == "string" then |
info.order = o2 |
info[#info+1] = two |
o2 = callmethod(info, inputpos, refTbl[two], "order") |
info[#info] = nil |
info.order = nil |
end |
if o1<0 and o2<0 then return o1<o2 end |
if o2<0 then return true end |
if o1<0 then return false end |
if o1==o2 then return tostring(one)<tostring(two) end -- compare names |
return o1<o2 |
end) |
for _,k in ipairs(sortTbl) do |
local v = refTbl[k] |
if not pickfirstset(v.cmdHidden, v.hidden, false) then |
-- recursively show all inline groups |
local name, desc = v.name, v.desc |
if type(name) == "function" then |
name = callfunction(info, v, 'name') |
end |
if type(desc) == "function" then |
desc = callfunction(info, v, 'desc') |
end |
if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then |
print(" "..(desc or name)..":") |
showhelp(info, inputpos, v, true) |
else |
print(" |cffffff78"..k.."|r - "..(desc or name or "")) |
end |
end |
end |
end |
local function keybindingValidateFunc(text) |
if text == nil or text == "NONE" then |
return nil |
end |
text = text:upper() |
local shift, ctrl, alt |
local modifier |
while true do |
if text == "-" then |
break |
end |
modifier, text = strsplit('-', text, 2) |
if text then |
if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then |
return false |
end |
if modifier == "SHIFT" then |
if shift then |
return false |
end |
shift = true |
end |
if modifier == "CTRL" then |
if ctrl then |
return false |
end |
ctrl = true |
end |
if modifier == "ALT" then |
if alt then |
return false |
end |
alt = true |
end |
else |
text = modifier |
break |
end |
end |
if text == "" then |
return false |
end |
if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then |
return false |
end |
local s = text |
if shift then |
s = "SHIFT-" .. s |
end |
if ctrl then |
s = "CTRL-" .. s |
end |
if alt then |
s = "ALT-" .. s |
end |
return s |
end |
-- constants used by getparam() calls below |
local handlertypes = {["table"]=true} |
local handlermsg = "expected a table" |
local functypes = {["function"]=true, ["string"]=true} |
local funcmsg = "expected function or member name" |
-- handle() - selfrecursing function that processes input->optiontable |
-- - depth - starts at 0 |
-- - retfalse - return false rather than produce error if a match is not found (used by inlined groups) |
local function handle(info, inputpos, tab, depth, retfalse) |
if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end |
------------------------------------------------------------------- |
-- Grab hold of handler,set,get,func,etc if set (and remember old ones) |
-- Note that we do NOT validate if method names are correct at this stage, |
-- the handler may change before they're actually used! |
local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg) |
local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg) |
local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg) |
local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg) |
local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg) |
local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg) |
------------------------------------------------------------------- |
-- Act according to .type of this table |
if tab.type=="group" then |
------------ group -------------------------------------------- |
if type(tab.args)~="table" then err(info, inputpos) end |
if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end |
-- grab next arg from input |
local _,nextpos,arg = string.find(info.input, " *([^ ]+) *", inputpos) |
if not arg then |
showhelp(info, inputpos, tab) |
return |
end |
nextpos=nextpos+1 |
-- loop .args and try to find a key with a matching name |
for k,v in iterateargs(tab) do |
if not(type(k)=="string" and type(v)=="table" and type(v.type)=="string") then err(info,inputpos, "options table child '"..tostring(k).."' is malformed") end |
-- is this child an inline group? if so, traverse into it |
if v.type=="group" and pickfirstset(v.cmdInline, v.inline, false) then |
info[depth+1] = k |
if handle(info, inputpos, v, depth+1, true)==false then |
info[depth+1] = nil |
-- wasn't found in there, but that's ok, we just keep looking down here |
else |
return -- done, name was found in inline group |
end |
-- matching name and not a inline group |
elseif strlower(arg)==strlower(k) then |
info[depth+1] = k |
return handle(info,nextpos,v,depth+1) |
end |
end |
-- no match |
if retfalse then |
-- restore old infotable members and return false to indicate failure |
info.handler,info.handler_at = oldhandler,oldhandler_at |
info.set,info.set_at = oldset,oldset_at |
info.get,info.get_at = oldget,oldget_at |
info.func,info.func_at = oldfunc,oldfunc_at |
info.validate,info.validate_at = oldvalidate,oldvalidate_at |
info.confirm,info.confirm_at = oldconfirm,oldconfirm_at |
return false |
end |
-- couldn't find the command, display error |
usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"]) |
return |
end |
local str = strsub(info.input,inputpos); |
if tab.type=="execute" then |
------------ execute -------------------------------------------- |
do_final(info, inputpos, tab, "func") |
elseif tab.type=="input" then |
------------ input -------------------------------------------- |
local res = true |
if tab.pattern then |
if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end |
if not strmatch(str, tab.pattern) then |
usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"]) |
return |
end |
end |
do_final(info, inputpos, tab, "set", str) |
elseif tab.type=="toggle" then |
------------ toggle -------------------------------------------- |
local b |
local str = strtrim(strlower(str)) |
if str=="" then |
b = callmethod(info, inputpos, tab, "get") |
if tab.tristate then |
--cycle in true, nil, false order |
if b then |
b = nil |
elseif b == nil then |
b = false |
else |
b = true |
end |
else |
b = not b |
end |
elseif str==L["on"] then |
b = true |
elseif str==L["off"] then |
b = false |
elseif tab.tristate and str==L["default"] then |
b = nil |
else |
if tab.tristate then |
usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str)) |
else |
usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str)) |
end |
return |
end |
do_final(info, inputpos, tab, "set", b) |
elseif tab.type=="range" then |
------------ range -------------------------------------------- |
local val = tonumber(str) |
if not val then |
usererr(info, inputpos, "'"..str.."' - "..L["expected number"]) |
return |
end |
if type(info.step)=="number" then |
val = val- (val % info.step) |
end |
if type(info.min)=="number" and val<info.min then |
usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(info.min)) ) |
return |
end |
if type(info.max)=="number" and val>info.max then |
usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(info.max)) ) |
return |
end |
do_final(info, inputpos, tab, "set", val) |
elseif tab.type=="select" then |
------------ select ------------------------------------ |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current selection and possible values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
info.values = values |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
local ok |
for k,v in pairs(values) do |
if strlower(k)==str then |
str = k -- overwrite with key (in case of case mismatches) |
ok = true |
break |
end |
end |
if not ok then |
usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"]) |
return |
end |
do_final(info, inputpos, tab, "set", str) |
elseif tab.type=="multiselect" then |
------------ multiselect ------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
info.values = values |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
--build a table of the selections, checking that they exist |
--parse for =on =off =default in the process |
--table will be key = true for options that should toggle, key = [on|off|default] for options to be set |
local sels = {} |
for v in string.gmatch(str, "[^ ]+") do |
--parse option=on etc |
local opt, val = string.match(v,'(.+)=(.+)') |
--get option if toggling |
if not opt then |
opt = v |
end |
--check that the opt is valid |
local ok |
for k,v in pairs(values) do |
if strlower(k)==opt then |
opt = k -- overwrite with key (in case of case mismatches) |
ok = true |
break |
end |
end |
if not ok then |
usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"]) |
return |
end |
--check that if val was supplied it is valid |
if val then |
if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then |
--val is valid insert it |
sels[opt] = val |
else |
if tab.tristate then |
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val)) |
else |
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val)) |
end |
return |
end |
else |
-- no val supplied, toggle |
sels[opt] = true |
end |
end |
for opt, val in pairs(sels) do |
local newval |
if (val == true) then |
--toggle the option |
local b = callmethod(info, inputpos, tab, "get", opt) |
if tab.tristate then |
--cycle in true, nil, false order |
if b then |
b = nil |
elseif b == nil then |
b = false |
else |
b = true |
end |
else |
b = not b |
end |
newval = b |
else |
--set the option as specified |
if val==L["on"] then |
newval = true |
elseif val==L["off"] then |
newval = false |
elseif val==L["default"] then |
newval = nil |
end |
end |
do_final(info, inputpos, tab, "set", opt, newval) |
end |
elseif tab.type=="color" then |
------------ color -------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current value |
return |
end |
local r, g, b, a |
if tab.hasAlpha then |
if str:len() == 8 and str:find("^%x*$") then |
--parse a hex string |
r,g,b,a = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255, tonumber(str:sub(7, 8), 16) / 255 |
else |
--parse seperate values |
r,g,b,a = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$") |
r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a) |
end |
if not (r and g and b and a) then |
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str)) |
return |
end |
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 and a >= 0.0 and a <= 1.0 then |
--values are valid |
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 and a >= 0 and a <= 255 then |
--values are valid 0..255, convert to 0..1 |
r = r / 255 |
g = g / 255 |
b = b / 255 |
a = a / 255 |
else |
--values are invalid |
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str)) |
end |
else |
a = 1.0 |
if str:len() == 6 and str:find("^%x*$") then |
--parse a hex string |
r,g,b = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255 |
else |
--parse seperate values |
r,g,b = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+)$") |
r,g,b = tonumber(r), tonumber(g), tonumber(b) |
end |
if not (r and g and b) then |
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str)) |
return |
end |
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 then |
--values are valid |
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then |
--values are valid 0..255, convert to 0..1 |
r = r / 255 |
g = g / 255 |
b = b / 255 |
else |
--values are invalid |
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str)) |
end |
end |
do_final(info, inputpos, tab, "set", r,g,b,a) |
elseif tab.type=="keybinding" then |
------------ keybinding -------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current value |
return |
end |
local value = keybindingValidateFunc(str:upper()) |
if value == false then |
usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str)) |
return |
end |
do_final(info, inputpos, tab, "set", value) |
else |
err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'") |
end |
end |
----------------------------------------------------------------------- |
-- HandleCommand(slashcmd, appName, input) |
-- |
-- Call this from a chat command handler to parse the command input as operations on an aceoptions table |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
-- input (string) -- the commandline input (as given by the WoW handler, i.e. without the command itself) |
function lib:HandleCommand(slashcmd, appName, input) |
local optgetter = cfgreg:GetOptionsTable(appName) |
if not optgetter then |
error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2) |
end |
local options = assert( optgetter("cmd", MAJOR) ) |
local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot |
[0] = slashcmd, |
appName = appName, |
options = options, |
input = input, |
self = self, |
handler = self, |
uiType = "cmd", |
uiName = MAJOR, |
} |
handle(info, 1, options, 0) -- (info, inputpos, table, depth) |
end |
----------------------------------------------------------------------- |
-- CreateChatCommand(slashcmd, appName) |
-- |
-- Utility function to create a slash command handler. |
-- Also registers tab completion with AceTab |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
function lib:CreateChatCommand(slashcmd, appName) |
if not AceConsole then |
AceConsole = LibStub(AceConsoleName) |
end |
if AceConsole.RegisterChatCommand(self, slashcmd, function(input) |
lib.HandleCommand(self, slashcmd, appName, input) -- upgradable |
end, |
true) then -- succesfully registered so lets get the command -> app table in |
commands[slashcmd] = appName |
end |
end |
-- GetChatCommandOptions(slashcmd) |
-- |
-- Utility function that returns the options table that belongs to a slashcommand |
-- mainly used by AceTab |
function lib:GetChatCommandOptions(slashcmd) |
return commands[slashcmd] |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigRegistry-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigRegistry-3.0: |
Handle central registration of options tables in use by addons and modules. Do nothing else. |
Options tables can be registered as raw tables, or as function refs that return a table. |
These functions receive two arguments: "uiType" and "uiName". |
- Valid "uiTypes": "cmd", "dropdown", "dialog". This is verified by the library at call time. |
- The "uiName" field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time. |
:IterateOptionsTables() and :GetOptionsTable() always return a function reference that the requesting config handling addon must call with the above arguments. |
]] |
local MAJOR, MINOR = "AceConfigRegistry-3.0", 5 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.tables = lib.tables or {} |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
if not lib.callbacks then |
lib.callbacks = CallbackHandler:New(lib) |
end |
----------------------------------------------------------------------- |
-- Validating options table consistency: |
lib.validated = { |
-- list of options table names ran through :ValidateOptionsTable automatically. |
-- CLEARED ON PURPOSE, since newer versions may have newer validators |
cmd = {}, |
dropdown = {}, |
dialog = {}, |
} |
local function err(msg, errlvl, ...) |
local t = {} |
for i=select("#",...),1,-1 do |
tinsert(t, (select(i, ...))) |
end |
error(MAJOR..":ValidateOptionsTable(): "..table.concat(t,".")..msg, errlvl+2) |
end |
local isstring={["string"]=true, _="string"} |
local isstringfunc={["string"]=true,["function"]=true, _="string or funcref"} |
local istable={["table"]=true, _="table"} |
local ismethodtable={["table"]=true,["string"]=true,["function"]=true, _="methodname, funcref or table"} |
local optstring={["nil"]=true,["string"]=true, _="string"} |
local optstringfunc={["nil"]=true,["string"]=true,["function"]=true, _="string or funcref"} |
local optnumber={["nil"]=true,["number"]=true, _="number"} |
local optmethod={["nil"]=true,["string"]=true,["function"]=true, _="methodname or funcref"} |
local optmethodfalse={["nil"]=true,["string"]=true,["function"]=true,["boolean"]={[false]=true}, _="methodname, funcref or false"} |
local optmethodnumber={["nil"]=true,["string"]=true,["function"]=true,["number"]=true, _="methodname, funcref or number"} |
local optmethodtable={["nil"]=true,["string"]=true,["function"]=true,["table"]=true, _="methodname, funcref or table"} |
local optmethodbool={["nil"]=true,["string"]=true,["function"]=true,["boolean"]=true, _="methodname, funcref or boolean"} |
local opttable={["nil"]=true,["table"]=true, _="table"} |
local optbool={["nil"]=true,["boolean"]=true, _="boolean"} |
local optboolnumber={["nil"]=true,["boolean"]=true,["number"]=true, _="boolean or number"} |
local basekeys={ |
type=isstring, |
name=isstringfunc, |
desc=optstringfunc, |
order=optmethodnumber, |
validate=optmethodfalse, |
confirm=optmethodbool, |
confirmText=optstring, |
disabled=optmethodbool, |
hidden=optmethodbool, |
guiHidden=optmethodbool, |
dialogHidden=optmethodbool, |
dropdownHidden=optmethodbool, |
cmdHidden=optmethodbool, |
icon=optstringfunc, |
iconCoords=optmethodtable, |
handler=opttable, |
get=optmethodfalse, |
set=optmethodfalse, |
func=optmethodfalse, |
arg={["*"]=true}, |
width=optstring, |
} |
local typedkeys={ |
header={}, |
description={ |
image=optstringfunc, |
imageCoords=optmethodtable, |
imageHeight=optnumber, |
imageWidth=optnumber, |
}, |
group={ |
args=istable, |
plugins=opttable, |
inline=optbool, |
cmdInline=optbool, |
guiInline=optbool, |
dropdownInline=optbool, |
dialogInline=optbool, |
childGroups=optstring, |
}, |
execute={ |
-- func={ |
-- ["function"]=true, |
-- ["string"]=true, |
-- _="methodname or funcref" |
-- }, |
}, |
input={ |
pattern=optstring, |
usage=optstring, |
control=optstring, |
dialogControl=optstring, |
dropdownControl=optstring, |
multiline=optboolnumber, |
}, |
toggle={ |
tristate=optbool, |
}, |
tristate={ |
}, |
range={ |
min=optnumber, |
max=optnumber, |
step=optnumber, |
bigStep=optnumber, |
isPercent=optbool, |
}, |
select={ |
values=ismethodtable, |
style={ |
["nil"]=true, |
["string"]={dropdown=true,radio=true}, |
_="string: 'dropdown' or 'radio'" |
}, |
control=optstring, |
dialogControl=optstring, |
dropdownControl=optstring, |
}, |
multiselect={ |
values=ismethodtable, |
style=optstring, |
tristate=optbool, |
}, |
color={ |
hasAlpha=optbool, |
}, |
keybinding={ |
-- TODO |
}, |
} |
local function validateKey(k,errlvl,...) |
errlvl=(errlvl or 0)+1 |
if type(k)~="string" then |
err("["..tostring(k).."] - key is not a string", errlvl,...) |
end |
if strfind(k, "[%c \127]") then |
err("["..tostring(k).."] - key name contained spaces (or control characters)", errlvl,...) |
end |
end |
local function validateVal(v, oktypes, errlvl,...) |
errlvl=(errlvl or 0)+1 |
local isok=oktypes[type(v)] or oktypes["*"] |
if not isok then |
err(": expected a "..oktypes._..", got '"..tostring(v).."'", errlvl,...) |
end |
if type(isok)=="table" then -- isok was a table containing specific values to be tested for! |
if not isok[v] then |
err(": did not expect "..type(v).." value '"..tostring(v).."'", errlvl,...) |
end |
end |
end |
local function validate(options,errlvl,...) |
errlvl=(errlvl or 0)+1 |
-- basic consistency |
if type(options)~="table" then |
err(": expected a table, got a "..type(options), errlvl,...) |
end |
if type(options.type)~="string" then |
err(".type: expected a string, got a "..type(options.type), errlvl,...) |
end |
-- get type and 'typedkeys' member |
local tk = typedkeys[options.type] |
if not tk then |
err(".type: unknown type '"..options.type.."'", errlvl,...) |
end |
-- make sure that all options[] are known parameters |
for k,v in pairs(options) do |
if not (tk[k] or basekeys[k]) then |
err(": unknown parameter", errlvl,tostring(k),...) |
end |
end |
-- verify that required params are there, and that everything is the right type |
for k,oktypes in pairs(basekeys) do |
validateVal(options[k], oktypes, errlvl,k,...) |
end |
for k,oktypes in pairs(tk) do |
validateVal(options[k], oktypes, errlvl,k,...) |
end |
-- extra logic for groups |
if options.type=="group" then |
for k,v in pairs(options.args) do |
validateKey(k,errlvl,"args",...) |
validate(v, errlvl,k,"args",...) |
end |
if options.plugins then |
for plugname,plugin in pairs(options.plugins) do |
if type(plugin)~="table" then |
err(": expected a table, got '"..tostring(plugin).."'", errlvl,tostring(plugname),"plugins",...) |
end |
for k,v in pairs(plugin) do |
validateKey(k,errlvl,tostring(plugname),"plugins",...) |
validate(v, errlvl,k,tostring(plugname),"plugins",...) |
end |
end |
end |
end |
end |
--------------------------------------------------------------------- |
-- :ValidateOptionsTable(options,name,errlvl) |
-- - options - the table |
-- - name - (string) name of table, used in error reports |
-- - errlvl - (optional number) error level offset, default 0 |
-- |
-- Validates basic structure and integrity of an options table |
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth |
function lib:ValidateOptionsTable(options,name,errlvl) |
errlvl=(errlvl or 0)+1 |
name = name or "Optionstable" |
if not options.name then |
options.name=name -- bit of a hack, the root level doesn't really need a .name :-/ |
end |
validate(options,errlvl,name) |
end |
------------------------------ |
-- :NotifyChange(appName) |
-- - appName - string identifying the addon |
-- |
-- Fires a ConfigTableChange callback for those listening in on it, allowing config GUIs to refresh |
------------------------------ |
function lib:NotifyChange(appName) |
if not lib.tables[appName] then return end |
lib.callbacks:Fire("ConfigTableChange", appName) |
end |
--------------------------------------------------------------------- |
-- Registering and retreiving options tables: |
-- validateGetterArgs: helper function for :GetOptionsTable (or, rather, the getter functions returned by it) |
local function validateGetterArgs(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+2 |
if uiType~="cmd" and uiType~="dropdown" and uiType~="dialog" then |
error(MAJOR..": Requesting options table: 'uiType' - invalid configuration UI type, expected 'cmd', 'dropdown' or 'dialog'", errlvl) |
end |
if not strmatch(uiName, "[A-Za-z]%-[0-9]") then -- Expecting e.g. "MyLib-1.2" |
error(MAJOR..": Requesting options table: 'uiName' - badly formatted or missing version number. Expected e.g. 'MyLib-1.2'", errlvl) |
end |
end |
--------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options) |
-- - appName - string identifying the addon |
-- - options - table or function reference |
function lib:RegisterOptionsTable(appName, options) |
if type(options)=="table" then |
if options.type~="group" then -- quick sanity checker |
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - missing type='group' member in root group", 2) |
end |
lib.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(options, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
end |
return options |
end |
elseif type(options)=="function" then |
lib.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
local tab = assert(options(uiType, uiName)) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(tab, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
end |
return tab |
end |
else |
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2) |
end |
end |
--------------------------------------------------------------------- |
-- :IterateOptionsTables() |
-- |
-- Returns an iterator of ["appName"]=funcref pairs |
function lib:IterateOptionsTables() |
return pairs(lib.tables) |
end |
--------------------------------------------------------------------- |
-- :GetOptionsTable(appName) |
-- - appName - which addon to retreive the options table of |
-- Optional: |
-- - uiType - "cmd", "dropdown", "dialog" |
-- - uiName - e.g. "MyLib-1.0" |
-- |
-- If only appName is given, a function is returned which you |
-- can call with (uiType,uiName) to get the table. |
-- If uiType&uiName are given, the table is returned. |
function lib:GetOptionsTable(appName, uiType, uiName) |
local f = lib.tables[appName] |
if not f then |
return nil |
end |
if uiType then |
return f(uiType,uiName,1) -- get the table for us |
else |
return f -- return the function |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Include file="AceConfigRegistry-3.0\AceConfigRegistry-3.0.xml"/> |
<Include file="AceConfigCmd-3.0\AceConfigCmd-3.0.xml"/> |
<Include file="AceConfigDialog-3.0\AceConfigDialog-3.0.xml"/> |
<!--<Include file="AceConfigDropdown-3.0\AceConfigDropdown-3.0.xml"/>--> |
<Script file="AceConfig-3.0.lua"/> |
</Ui> |
--[[ $Id: AceConfig-3.0.lua 60131 2008-02-03 13:03:56Z nevcairiel $ ]] |
--[[ |
AceConfig-3.0 |
Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole. |
Also automatically adds "config", "enable" and "disable" commands to options table as appropriate. |
]] |
local MAJOR, MINOR = "AceConfig-3.0", 2 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local cfgcmd = LibStub("AceConfigCmd-3.0") |
local cfgdlg = LibStub("AceConfigDialog-3.0") |
--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0") |
--------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options, slashcmd, persist) |
-- |
-- - appName - (string) application name |
-- - options - table or function ref, see AceConfigRegistry |
-- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command |
function lib:RegisterOptionsTable(appName, options, slashcmd) |
local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options) |
if not ok then error(msg, 2) end |
if slashcmd then |
if type(slashcmd) == "table" then |
for _,cmd in pairs(slashcmd) do |
cfgcmd:CreateChatCommand(cmd, appName) |
end |
else |
cfgcmd:CreateChatCommand(slashcmd, appName) |
end |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigDialog-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigDialog-3.0 |
]] |
local LibStub = LibStub |
local MAJOR, MINOR = "AceConfigDialog-3.0", 17 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.OpenFrames = lib.OpenFrames or {} |
lib.Status = lib.Status or {} |
lib.frame = lib.frame or CreateFrame("Frame") |
local gui = LibStub("AceGUI-3.0") |
local reg = LibStub("AceConfigRegistry-3.0") |
local select = select |
local pairs = pairs |
local type = type |
local assert = assert |
local tinsert = tinsert |
local tremove = tremove |
local error = error |
local table = table |
local unpack = unpack |
local string = string |
local next = next |
local math = math |
local _ |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", table.concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
return Dispatchers[select('#', ...)](func, ...) |
end |
local width_multiplier = 170 |
--[[ |
Group Types |
Tree - All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree |
- Descendant Groups with inline=true and thier children will not become nodes |
Tab - Direct Child Groups will become tabs, direct child options will appear above the tab control |
- Grandchild groups will default to inline unless specified otherwise |
Select- Same as Tab but with entries in a dropdown rather than tabs |
Inline Groups |
- Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border |
- If declared on a direct child of a root node of a select group, they will appear above the group container control |
- When a group is displayed inline, all descendants will also be inline members of the group |
]] |
-- Recycling functions |
local new, del, copy |
--newcount, delcount,createdcount,cached = 0,0,0 |
do |
local pool = setmetatable({},{__mode='k'}) |
function new() |
--newcount = newcount + 1 |
local t = next(pool) |
if t then |
pool[t] = nil |
return t |
else |
--createdcount = createdcount + 1 |
return {} |
end |
end |
function copy(t) |
local c = new() |
for k, v in pairs(t) do |
c[k] = v |
end |
return c |
end |
function del(t) |
--delcount = delcount + 1 |
for k in pairs(t) do |
t[k] = nil |
end |
pool[t] = true |
end |
-- function cached() |
-- local n = 0 |
-- for k in pairs(pool) do |
-- n = n + 1 |
-- end |
-- return n |
-- end |
end |
-- picks the first non-nil value and returns it |
local function pickfirstset(...) |
for i=1,select("#",...) do |
if select(i,...)~=nil then |
return select(i,...) |
end |
end |
end |
--gets an option from a given group, checking plugins |
local function GetSubOption(group, key) |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
if t[key] then |
return t[key] |
end |
end |
end |
return group.args[key] |
end |
--Option member type definitions, used to decide how to access it |
--Is the member Inherited from parent options |
local isInherited = { |
set = true, |
get = true, |
func = true, |
confirm = true, |
validate = true, |
disabled = true, |
hidden = true |
} |
--Does a string type mean a literal value, instead of the default of a method of the handler |
local stringIsLiteral = { |
name = true, |
desc = true, |
icon = true, |
usage = true, |
width = true, |
image = true, |
} |
--Is Never a function or method |
local allIsLiteral = { |
type = true, |
imageWidth = true, |
imageHeight = true, |
} |
--gets the value for a member that could be a function |
--function refs are called with an info arg |
--every other type is returned |
local function GetOptionsMemberValue(membername, option, options, path, appName, ...) |
--get definition for the member |
local inherits = isInherited[membername] |
--get the member of the option, traversing the tree if it can be inherited |
local member |
if inherits then |
local group = options |
if group[membername] ~= nil then |
member = group[membername] |
end |
for i = 1, #path do |
group = GetSubOption(group, path[i]) |
if group[membername] ~= nil then |
member = group[membername] |
end |
end |
else |
member = option[membername] |
end |
--check if we need to call a functon, or if we have a literal value |
if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then |
--We have a function to call |
local info = new() |
--traverse the options table, picking up the handler and filling the info with the path |
local handler |
local group = options |
handler = group.handler or handler |
for i = 1, #path do |
group = GetSubOption(group, path[i]) |
info[i] = path[i] |
handler = group.handler or handler |
end |
info.options = options |
info[0] = appName |
info.arg = option.arg |
info.handler = handler |
info.option = option |
info.type = option.type |
info.uiType = 'dialog' |
info.uiName = MAJOR |
local a, b, c ,d |
--using 4 returns for the get of a color type, increase if a type needs more |
if type(member) == "function" then |
--Call the function |
a,b,c,d = member(info, ...) |
else |
--Call the method |
if handler and handler[member] then |
a,b,c,d = handler[member](handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type %s", member, membername)) |
end |
end |
del(info) |
return a,b,c,d |
else |
--The value isnt a function to call, return it |
return member |
end |
end |
--[[calls an options function that could be inherited, method name or function ref |
local function CallOptionsFunction(funcname ,option, options, path, appName, ...) |
local info = new() |
local func |
local group = options |
local handler |
--build the info table containing the path |
-- pick up functions while traversing the tree |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
for i, v in ipairs(path) do |
group = GetSubOption(group, v) |
info[i] = v |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
end |
info.options = options |
info[0] = appName |
info.arg = option.arg |
local a, b, c ,d |
if type(func) == "string" then |
if handler and handler[func] then |
a,b,c,d = handler[func](handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
a,b,c,d = func(info, ...) |
end |
del(info) |
return a,b,c,d |
end |
--]] |
--tables to hold orders and names for options being sorted, will be created with new() |
--prevents needing to call functions repeatedly while sorting |
local tempOrders |
local tempNames |
local function compareOptions(a,b) |
if not a then |
return true |
end |
if not b then |
return false |
end |
local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100 |
if OrderA == OrderB then |
local NameA = (type(tempNames[a] == "string") and tempNames[a]) or "" |
local NameB = (type(tempNames[b] == "string") and tempNames[b]) or "" |
return NameA:upper() < NameB:upper() |
end |
if OrderA < 0 then |
if OrderB > 0 then |
return false |
end |
else |
if OrderB < 0 then |
return true |
end |
end |
return OrderA < OrderB |
end |
--builds 2 tables out of an options group |
-- keySort, sorted keys |
-- opts, combined options from .plugins and args |
local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
tempOrders = new() |
tempNames = new() |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
for k, v in pairs(t) do |
if not opts[k] then |
tinsert(keySort, k) |
opts[k] = v |
path[#path+1] = k |
tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) |
tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) |
path[#path] = nil |
end |
end |
end |
end |
for k, v in pairs(group.args) do |
if not opts[k] then |
tinsert(keySort, k) |
opts[k] = v |
path[#path+1] = k |
tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) |
tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) |
path[#path] = nil |
end |
end |
table.sort(keySort, compareOptions) |
del(tempOrders) |
del(tempNames) |
end |
local function DelTree(tree) |
if tree.children then |
local childs = tree.children |
for i = 1, #childs do |
DelTree(childs[i]) |
del(childs[i]) |
end |
del(childs) |
end |
end |
local function CleanUserData(widget, event) |
local user = widget.userdata |
if user.path then |
del(user.path) |
end |
if widget.type == "TreeGroup" then |
local tree = widget.tree |
if tree then |
for i = 1, #tree do |
DelTree(tree[i]) |
del(tree[i]) |
end |
del(tree) |
widget.tree = nil |
end |
end |
if widget.type == "TabGroup" then |
del(widget.tablist) |
widget.tablist = nil |
end |
if widget.type == "DropdownGroup" then |
if widget.dropdown.list then |
del(widget.dropdown.list) |
widget.dropdown.list = nil |
end |
end |
end |
--[[ |
Gets a status table for the given appname and options path |
]] |
function lib:GetStatusTable(appName, path) |
local status = self.Status |
if not status[appName] then |
status[appName] = {} |
status[appName].status = {} |
status[appName].children = {} |
end |
status = status[appName] |
if path then |
for i = 1, #path do |
local v = path[i] |
if not status.children[v] then |
status.children[v] = {} |
status.children[v].status = {} |
status.children[v].children = {} |
end |
status = status.children[v] |
end |
end |
return status.status |
end |
local function OptionOnMouseOver(widget, event) |
--show a tooltip/set the status bar to the desc text |
local user = widget.userdata |
local opt = user.option |
local options = user.options |
local path = user.path |
local appName = user.appName |
GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT") |
local name = GetOptionsMemberValue("name", opt, options, path, appName) |
local desc = GetOptionsMemberValue("desc", opt, options, path, appName) |
local usage = GetOptionsMemberValue("usage", opt, options, path, appName) |
GameTooltip:SetText(name, 1, .82, 0, 1) |
if opt.type == 'multiselect' then |
GameTooltip:AddLine(user.text,0.5, 0.5, 0.8, 1) |
end |
if type(desc) == "string" then |
GameTooltip:AddLine(desc, 1, 1, 1, 1) |
end |
if type(usage) == "string" then |
GameTooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) |
end |
GameTooltip:Show() |
end |
local function OptionOnMouseLeave(widget, event) |
GameTooltip:Hide() |
end |
local function GetFuncName(option) |
local type = option.type |
if type == 'execute' then |
return 'func' |
else |
return 'set' |
end |
end |
local function confirmPopup(appName, rootframe, info, message, func, ...) |
if not StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] then |
StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] = {} |
end |
local t = StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] |
for k in pairs(t) do |
t[k] = nil |
end |
t.text = message |
t.button1 = ACCEPT |
t.button2 = CANCEL |
local dialog, oldstrata |
t.OnAccept = function() |
safecall(func, unpack(t)) |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
lib:Open(appName, rootframe) |
del(info) |
end |
t.OnCancel = function() |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
del(info) |
end |
for i = 1, select('#', ...) do |
t[i] = select(i, ...) or false |
end |
t.timeout = 0 |
t.whileDead = 1 |
t.hideOnEscape = 1 |
dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") |
if dialog then |
oldstrata = dialog:GetFrameStrata() |
dialog:SetFrameStrata("TOOLTIP") |
end |
end |
local function ActivateControl(widget, event, ...) |
--This function will call the set / execute handler for the widget |
--widget.userdata contains the needed info |
local user = widget.userdata |
local option = user.option |
local options = user.options |
local path = user.path |
local info = new() |
local func |
local group = options |
local funcname = GetFuncName(option) |
local handler |
local confirm |
local validate |
--build the info table containing the path |
-- pick up functions while traversing the tree |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
confirm = group.confirm |
validate = group.validate |
for i = 1, #path do |
local v = path[i] |
group = GetSubOption(group, v) |
info[i] = v |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
if group.confirm ~= nil then |
confirm = group.confirm |
end |
if group.validate ~= nil then |
validate = group.validate |
end |
end |
info.options = options |
info[0] = user.appName |
info.arg = option.arg |
info.handler = handler |
info.option = option |
info.type = option.type |
info.uiType = 'dialog' |
info.uiName = MAJOR |
local name |
if type(option.name) == "function" then |
name = option.name(info) |
elseif type(option.name) == "string" then |
name = option.name |
else |
name = "" |
end |
local usage = option.usage |
local pattern = option.pattern |
local validated = true |
if option.type == "input" then |
if type(pattern)=="string" then |
if not strmatch(..., pattern) then |
validated = false |
end |
end |
end |
local success |
if validated and option.type ~= "execute" then |
if type(validate) == "string" then |
if handler and handler[validate] then |
success, validated = safecall(handler[validate], handler, info, ...) |
if not success then validated = false end |
else |
error(string.format("Method %s doesn't exist in handler for type execute", validate)) |
end |
elseif type(validate) == "function" then |
success, validated = safecall(validate, info, ...) |
if not success then validated = false end |
end |
end |
local rootframe = user.rootframe |
if type(validated) == "string" then |
--validate function returned a message to display |
if rootframe.SetStatusText then |
rootframe:SetStatusText(validated) |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
return true |
elseif not validated then |
--validate returned false |
if rootframe.SetStatusText then |
if usage then |
rootframe:SetStatusText(name..": "..usage) |
else |
if pattern then |
rootframe:SetStatusText(name..": Expected "..pattern) |
else |
rootframe:SetStatusText(name..": Invalid Value") |
end |
end |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
return true |
else |
local confirmText = option.confirmText |
--call confirm func/method |
if type(confirm) == "string" then |
if handler and handler[confirm] then |
success, confirm = safecall(handler[confirm],handler, info) |
if success and type(confirm) == "string" then |
confirmText = confirm |
confirm = true |
elseif not success then |
confirm = false |
end |
else |
error(string.format("Method %s doesn't exist in handler for type confirm", confirm)) |
end |
elseif type(confirm) == "function" then |
success, confirm = safecall(confirm,info) |
if success and type(confirm) == "string" then |
confirmText = confirm |
confirm = true |
elseif not success then |
confirm = false |
end |
end |
--confirm if needed |
if type(confirm) == "boolean" then |
if confirm then |
if not confirmText then |
local name, desc = option.name, option.desc |
if type(name) == "function" then |
name = name(info) |
end |
if type(desc) == "function" then |
desc = desc(info) |
end |
confirmText = name |
if desc then |
confirmText = confirmText.." - "..desc |
end |
end |
local iscustom = user.rootframe.userdata.iscustom |
local rootframe |
if iscustom then |
rootframe = user.rootframe |
end |
if type(func) == "string" then |
if handler and handler[func] then |
confirmPopup(user.appName, rootframe, info, confirmText, handler[func], handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
confirmPopup(user.appName, rootframe, info, confirmText, func, info, ...) |
end |
--func will be called and info deleted when the confirm dialog is responded to |
return |
end |
end |
--call the function |
if type(func) == "string" then |
if handler and handler[func] then |
safecall(handler[func],handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
safecall(func,info, ...) |
end |
local iscustom = user.rootframe.userdata.iscustom |
--full refresh of the frame, some controls dont cause this on all events |
if option.type == "color" then |
if event == "OnValueConfirmed" then |
if iscustom then |
lib:Open(user.appName, user.rootframe) |
else |
lib:Open(user.appName) |
end |
end |
elseif option.type == "range" then |
if event == "OnMouseUp" then |
if iscustom then |
lib:Open(user.appName, user.rootframe) |
else |
lib:Open(user.appName) |
end |
end |
else |
if iscustom then |
lib:Open(user.appName, user.rootframe) |
else |
lib:Open(user.appName) |
end |
end |
end |
del(info) |
end |
local function ActivateSlider(widget, event, value) |
local option = widget.userdata.option |
local min, max, step = option.min or 0, option.max or 100, option.step |
if step then |
value = math.floor((value - min) / step + 0.5) * step + min |
else |
value = math.max(math.min(value,max),min) |
end |
ActivateControl(widget,event,value) |
end |
local function ActivateMultiControl(widget, event, ...) |
ActivateControl(widget, event, widget.userdata.value, ...) |
end |
local function FrameOnClose(widget, event) |
local appName = widget.userdata.appName |
lib.OpenFrames[appName] = nil |
gui:Release(widget) |
end |
local function CheckOptionHidden(option, options, path, appName) |
--check for a specific boolean option |
local hidden = pickfirstset(option.dialogHidden,option.guiHidden) |
if hidden ~= nil then |
return hidden |
end |
return GetOptionsMemberValue("hidden", option, options, path, appName) |
end |
local function CheckOptionDisabled(option, options, path, appName) |
--check for a specific boolean option |
local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) |
if disabled ~= nil then |
return disabled |
end |
return GetOptionsMemberValue("disabled", option, options, path, appName) |
end |
--[[ |
local function BuildTabs(group, options, path, appName) |
local tabs = new() |
local text = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
tinsert(tabs, k) |
text[k] = GetOptionsMemberValue("name", v, options, path, appName) |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return tabs, text |
end |
]] |
local function BuildSelect(group, options, path, appName) |
local groups = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
groups[k] = GetOptionsMemberValue("name", v, options, path, appName) |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return groups |
end |
local function BuildSubGroups(group, tree, options, path, appName) |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
if not tree.children then tree.children = new() end |
tinsert(tree.children,entry) |
if (v.childGroups or "tree") == "tree" then |
BuildSubGroups(v,entry, options, path, appName) |
end |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
end |
local function BuildGroups(group, options, path, appName, recurse) |
local tree = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
tinsert(tree,entry) |
if recurse and (v.childGroups or "tree") == "tree" then |
BuildSubGroups(v,entry, options, path, appName) |
end |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return tree |
end |
local function InjectInfo(control, options, option, path, rootframe, appName) |
local user = control.userdata |
for i = 1, #path do |
user[i] = path[i] |
end |
user.rootframe = rootframe |
user.option = option |
user.options = options |
user.path = copy(path) |
user.appName = appName |
control:SetCallback("OnRelease", CleanUserData) |
control:SetCallback("OnLeave", OptionOnMouseLeave) |
control:SetCallback("OnEnter", OptionOnMouseOver) |
end |
--[[ |
options - root of the options table being fed |
container - widget that controls will be placed in |
rootframe - Frame object the options are in |
path - table with the keys to get to the group being fed |
--]] |
local function FeedOptions(appName, options,container,rootframe,path,group,inline) |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
tinsert(path, k) |
local hidden = CheckOptionHidden(v, options, path, appName) |
local name = GetOptionsMemberValue("name", v, options, path, appName) |
if not hidden then |
if v.type == "group" then |
if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
--Inline group |
local GroupContainer |
if name and name ~= "" then |
GroupContainer = gui:Create("InlineGroup") |
GroupContainer:SetTitle(name or "") |
else |
GroupContainer = gui:Create("SimpleGroup") |
end |
GroupContainer.width = "fill" |
GroupContainer:SetLayout("flow") |
container:AddChild(GroupContainer) |
FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) |
end |
else |
--Control to feed |
local control |
local name = GetOptionsMemberValue("name", v, options, path, appName) |
if v.type == "execute" then |
control = gui:Create("Button") |
control:SetText(name) |
control:SetCallback("OnClick",ActivateControl) |
elseif v.type == "input" then |
local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
if v.multiline then |
local lines = 4 |
if type(v.multiline) == "number" then |
lines = v.multiline |
end |
control:SetHeight(60 + (14*lines)) |
end |
control:SetLabel(name) |
control:SetCallback("OnEnterPressed",ActivateControl) |
local text = GetOptionsMemberValue("get",v, options, path, appName) |
if type(text) ~= "string" then |
text = "" |
end |
control:SetText(text) |
elseif v.type == "toggle" then |
control = gui:Create("CheckBox") |
control:SetLabel(name) |
control:SetTriState(v.tristate) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateControl) |
elseif v.type == "range" then |
control = gui:Create("Slider") |
control:SetLabel(name) |
control:SetSliderValues(v.min or 0,v.max or 100, v.bigStep or v.step or 0) |
control:SetIsPercent(v.isPercent) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
if type(value) ~= "number" then |
value = 0 |
end |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateSlider) |
control:SetCallback("OnMouseUp",ActivateSlider) |
elseif v.type == "select" then |
local values = GetOptionsMemberValue("values", v, options, path, appName) |
local controlType = v.dialogControl or v.control or "Dropdown" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
control:SetLabel(name) |
control:SetList(values) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
if not values[value] then |
value = nil |
end |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateControl) |
elseif v.type == "multiselect" then |
local values = GetOptionsMemberValue("values", v, options, path, appName) |
control = gui:Create("InlineGroup") |
control:SetLayout("Flow") |
control:SetTitle(name) |
control.width = "fill" |
local valuesort = new() |
local disabled = CheckOptionDisabled(v, options, path, appName) |
if values then |
for value, text in pairs(values) do |
tinsert(valuesort, value) |
end |
table.sort(valuesort) |
control:PauseLayout() |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
for i = 1, #valuesort do |
local value = valuesort[i] |
local text = values[value] |
local check = gui:Create("CheckBox") |
check:SetLabel(text) |
check.userdata.value = value |
check.userdata.text = text |
check:SetDisabled(disabled) |
check:SetTriState(v.tristate) |
check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) |
check:SetCallback("OnValueChanged",ActivateMultiControl) |
InjectInfo(check, options, v, path, rootframe, appName) |
control:AddChild(check) |
if width == "double" then |
check:SetWidth(width_multiplier * 2) |
elseif width == "half" then |
check:SetWidth(width_multiplier / 2) |
elseif width == "full" then |
check.width = "fill" |
else |
check:SetWidth(width_multiplier) |
end |
end |
control:ResumeLayout() |
control:DoLayout() |
end |
del(valuesort) |
elseif v.type == "color" then |
control = gui:Create("ColorPicker") |
control:SetLabel(name) |
control:SetHasAlpha(v.hasAlpha) |
control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) |
control:SetCallback("OnValueChanged",ActivateControl) |
control:SetCallback("OnValueConfirmed",ActivateControl) |
elseif v.type == "keybinding" then |
control = gui:Create("Keybinding") |
control:SetLabel(name) |
control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) |
control:SetCallback("OnKeyChanged",ActivateControl) |
elseif v.type == "header" then |
control = gui:Create("Heading") |
control:SetText(name) |
control.width = "fill" |
elseif v.type == "description" then |
control = gui:Create("Label") |
control:SetText(name) |
local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) |
local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) |
if type(image) == 'string' then |
if not width then |
width = GetOptionsMemberValue("imageWidth",v, options, path, appName) |
end |
if not height then |
height = GetOptionsMemberValue("imageHeight",v, options, path, appName) |
end |
if type(imageCoords) == 'table' then |
control:SetImage(image, unpack(imageCoords)) |
else |
control:SetImage(image) |
end |
if type(width) ~= "number" then |
width = 32 |
end |
if type(height) ~= "number" then |
height = 32 |
end |
control:SetImageSize(width, height) |
end |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
control.width = not width and "fill" |
end |
--Common Init |
if control then |
if control.width ~= "fill" then |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
if width == "double" then |
control:SetWidth(width_multiplier * 2) |
elseif width == "half" then |
control:SetWidth(width_multiplier / 2) |
elseif width == "full" then |
control.width = "fill" |
else |
control:SetWidth(width_multiplier) |
end |
end |
if control.SetDisabled then |
local disabled = CheckOptionDisabled(v, options, path, appName) |
control:SetDisabled(disabled) |
end |
InjectInfo(control, options, v, path, rootframe, appName) |
container:AddChild(control) |
end |
end |
end |
tremove(path) |
end |
container:ResumeLayout() |
container:DoLayout() |
del(keySort) |
del(opts) |
end |
local function BuildPath(path, ...) |
for i = 1, select('#',...) do |
tinsert(path, (select(i,...))) |
end |
end |
local function GroupExists(appName, options, path, uniquevalue) |
if not uniquevalue then return false end |
local feedpath = new() |
local temppath = new() |
for i = 1, #path do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
local v = feedpath[i] |
temppath[i] = v |
group = GetSubOption(group, v) |
if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then |
del(feedpath) |
del(temppath) |
return false |
end |
end |
del(feedpath) |
del(temppath) |
return true |
end |
local function GroupSelected(widget, event, uniquevalue) |
local user = widget.userdata |
local options = user.options |
local option = user.option |
local path = user.path |
local rootframe = user.rootframe |
local feedpath = new() |
for i = 1, #path do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
group = GetSubOption(group, feedpath[i]) |
end |
widget:ReleaseChildren() |
lib:FeedGroup(user.appName,options,widget,rootframe,feedpath,group) |
del(feedpath) |
end |
--[[ |
This function will feed one group, and any inline child groups into the given container |
Select Groups will only have the selection control (tree, tabs, dropdown) fed in |
and have a group selected, this event will trigger the feeding of child groups |
Rules: |
If the group is Inline, FeedOptions |
If the group has no child groups, FeedOptions |
If the group is a tab or select group, FeedOptions then add the Group Control |
If the group is a tree group FeedOptions then |
its parent isnt a tree group: then add the tree control containing this and all child tree groups |
if its parent is a tree group, its already a node on a tree |
--]] |
function lib:FeedGroup(appName,options,container,rootframe,path) |
local group = options |
--follow the path to get to the curent group |
local inline |
local grouptype, parenttype = options.childGroups, "none" |
--temp path table to pass to callbacks as we traverse the tree |
local temppath = new() |
for i = 1, #path do |
local v = path[i] |
temppath[i] = v |
group = GetSubOption(group, v) |
inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
parenttype = grouptype |
grouptype = group.childGroups |
end |
del(temppath) |
if not parenttype then |
parenttype = "tree" |
end |
--check if the group has child groups |
local hasChildGroups |
for k, v in pairs(group.args) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
hasChildGroups = true |
end |
end |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
for k, v in pairs(t) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
hasChildGroups = true |
end |
end |
end |
end |
container:SetLayout("flow") |
local scroll |
--Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on |
if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and parenttype == "tree") then |
if container.type ~= "InlineGroup" then |
scroll = gui:Create("ScrollFrame") |
scroll:SetLayout("flow") |
scroll.width = "fill" |
scroll.height = "fill" |
container:SetLayout("fill") |
container:AddChild(scroll) |
container = scroll |
end |
end |
FeedOptions(appName,options,container,rootframe,path,group,nil) |
if scroll then |
container:PerformLayout() |
local status = self:GetStatusTable(appName, path) |
if not status.scroll then |
status.scroll = {} |
end |
scroll:SetStatusTable(status.scroll) |
end |
if hasChildGroups and not inline then |
if grouptype == "tab" then |
local tab = gui:Create("TabGroup") |
InjectInfo(tab, options, group, path, rootframe, appName) |
tab:SetCallback("OnGroupSelected", GroupSelected) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
tab:SetStatusTable(status.groups) |
tab.width = "fill" |
tab.height = "fill" |
local tabs = BuildGroups(group, options, path, appName) |
tab:SetTabs(tabs) |
for i = 1, #tabs do |
local entry = tabs[i] |
if not entry.disabled then |
tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) |
break |
end |
end |
container:AddChild(tab) |
elseif grouptype == "select" then |
local select = gui:Create("DropdownGroup") |
InjectInfo(select, options, group, path, rootframe, appName) |
select:SetCallback("OnGroupSelected", GroupSelected) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
select:SetStatusTable(status.groups) |
local grouplist = BuildSelect(group, options, path, appName) |
select:SetGroupList(grouplist) |
local firstgroup |
for k, v in pairs(grouplist) do |
if not firstgroup or k < firstgroup then |
firstgroup = k |
end |
end |
if firstgroup then |
select:SetGroup( (GroupExists(appName, options, path,status.groups.selectedgroup) and status.groups.selectedgroup) or firstgroup) |
end |
select.width = "fill" |
select.height = "fill" |
container:AddChild(select) |
--assume tree group by default |
--if parenttype is tree then this group is already a node on that tree |
elseif parenttype ~= "tree" then |
local tree = gui:Create("TreeGroup") |
InjectInfo(tree, options, group, path, rootframe, appName) |
tree.width = "fill" |
tree.height = "fill" |
tree:SetCallback("OnGroupSelected", GroupSelected) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
local treedefinition = BuildGroups(group, options, path, appName, true) |
tree:SetStatusTable(status.groups) |
tree:SetTree(treedefinition) |
for i = 1, #treedefinition do |
local entry = treedefinition[i] |
if not entry.disabled then |
tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) |
break |
end |
end |
container:AddChild(tree) |
end |
end |
end |
local old_CloseSpecialWindows |
function lib:CloseAll() |
local closed |
for k, v in pairs(self.OpenFrames) do |
v:Hide() |
closed = true |
end |
return closed |
end |
function lib:Close(appName) |
if self.OpenFrames[appName] then |
self.OpenFrames[appName]:Hide() |
return true |
end |
end |
local function RefreshOnUpdate(this) |
for appName in pairs(this.apps) do |
if lib.OpenFrames[appName] then |
lib:Open(appName) |
end |
if lib.BlizOptions and lib.BlizOptions[appName] then |
local widget = lib.BlizOptions[appName] |
if widget.frame:IsVisible() then |
lib:Open(widget.userdata.appName, widget) |
end |
end |
this.apps[appName] = nil |
end |
this:SetScript("OnUpdate", nil) |
end |
function lib:ConfigTableChanged(event, appName) |
if not lib.frame.apps then |
lib.frame.apps = {} |
end |
lib.frame.apps[appName] = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
end |
reg.RegisterCallback(lib, "ConfigTableChange", "ConfigTableChanged") |
function lib:SetDefaultSize(appName, width, height) |
local status = lib:GetStatusTable(appName) |
if type(width) == "number" and type(height) == "number" then |
status.width = width |
status.height = height |
end |
end |
function lib:Open(appName, container) |
if not old_CloseSpecialWindows then |
old_CloseSpecialWindows = CloseSpecialWindows |
CloseSpecialWindows = function() |
local found = old_CloseSpecialWindows() |
return self:CloseAll() or found |
end |
end |
local app = reg:GetOptionsTable(appName) |
if not app then |
error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) |
end |
local options = app("dialog", MAJOR) |
local f |
local path = new() |
local name = GetOptionsMemberValue("name", options, options, path, appName) |
--if a container is given feed into that |
if container then |
f = container |
f:ReleaseChildren() |
f.userdata.appName = appName |
f.userdata.iscustom = true |
if f.SetTitle then |
f:SetTitle(name or "") |
end |
local status = lib:GetStatusTable(appName) |
if not status.width then |
status.width = 700 |
end |
if not status.height then |
status.height = 500 |
end |
if f.SetStatusTable then |
f:SetStatusTable(status) |
end |
else |
if not self.OpenFrames[appName] then |
f = gui:Create("Frame") |
self.OpenFrames[appName] = f |
else |
f = self.OpenFrames[appName] |
end |
f:ReleaseChildren() |
f:SetCallback("OnClose", FrameOnClose) |
f.userdata.appName = appName |
f:SetTitle(name or "") |
local status = lib:GetStatusTable(appName) |
f:SetStatusTable(status) |
end |
self:FeedGroup(appName,options,f,f,path) |
if f.Show then |
f:Show() |
end |
del(path) |
end |
lib.BlizOptions = lib.BlizOptions or {} |
local function FeedToBlizPanel(widget, event) |
lib:Open(widget.userdata.appName, widget) |
end |
local function ClearBlizPanel(widget, event) |
widget:ReleaseChildren() |
end |
function lib:AddToBlizOptions(appName, name, parent) |
local BlizOptions = lib.BlizOptions |
if not BlizOptions[appName] then |
local group = gui:Create("BlizOptionsGroup") |
BlizOptions[appName] = group |
group:SetName(name or appName, parent) |
group:SetTitle(name or appName) |
group.userdata.appName = appName |
group:SetCallback("OnShow", FeedToBlizPanel) |
group:SetCallback("OnHide", ClearBlizPanel) |
InterfaceOptions_AddCategory(group.frame) |
return group.frame |
else |
error(("%s has already been added to the Blizzard Options Window"):format(appName), 2) |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceDB-3.0.lua"/> |
</Ui> |
--[[ $Id: AceDB-3.0.lua 69511 2008-04-13 10:10:53Z nevcairiel $ ]] |
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 7 |
local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR) |
if not AceDB then return end -- No upgrade needed |
local type = type |
local pairs, next = pairs, next |
local rawget, rawset = rawget, rawset |
local setmetatable = setmetatable |
AceDB.db_registry = AceDB.db_registry or {} |
AceDB.frame = AceDB.frame or CreateFrame("Frame") |
local CallbackHandler |
local CallbackDummy = { Fire = function() end } |
local DBObjectLib = {} |
--[[------------------------------------------------------------------------- |
AceDB Utility Functions |
---------------------------------------------------------------------------]] |
-- Simple shallow copy for copying defaults |
local function copyTable(src, dest) |
if type(dest) ~= "table" then dest = {} end |
if type(src) == "table" then |
for k,v in pairs(src) do |
if type(v) == "table" then |
-- try to index the key first so that the metatable creates the defaults, if set, and use that table |
v = copyTable(v, dest[k]) |
end |
dest[k] = v |
end |
end |
return dest |
end |
-- Called to add defaults to a section of the database |
-- |
-- When a ["*"] default section is indexed with a new key, a table is returned |
-- and set in the host table. These tables must be cleaned up by removeDefaults |
-- in order to ensure we don't write empty default tables. |
local function copyDefaults(dest, src) |
-- this happens if some value in the SV overwrites our default value with a non-table |
--if type(dest) ~= "table" then return end |
for k, v in pairs(src) do |
if k == "*" or k == "**" then |
if type(v) == "table" then |
-- This is a metatable used for table defaults |
local mt = { |
-- This handles the lookup and creation of new subtables |
__index = function(t,k) |
if k == nil then return nil end |
local tbl = {} |
copyDefaults(tbl, v) |
rawset(t, k, tbl) |
return tbl |
end, |
} |
setmetatable(dest, mt) |
-- handle already existing tables in the SV |
for dk, dv in pairs(dest) do |
if not rawget(src, dk) and type(dv) == "table" then |
copyDefaults(dv, v) |
end |
end |
else |
-- Values are not tables, so this is just a simple return |
local mt = {__index = function(t,k) return k~=nil and v or nil end} |
setmetatable(dest, mt) |
end |
elseif type(v) == "table" then |
if not rawget(dest, k) then rawset(dest, k, {}) end |
if type(dest[k]) == "table" then |
copyDefaults(dest[k], v) |
if src['**'] then |
copyDefaults(dest[k], src['**']) |
end |
end |
else |
if rawget(dest, k) == nil then |
rawset(dest, k, v) |
end |
end |
end |
end |
-- Called to remove all defaults in the default table from the database |
local function removeDefaults(db, defaults, blocker) |
for k,v in pairs(defaults) do |
if k == "*" or k == "**" then |
if type(v) == "table" then |
-- Loop through all the actual k,v pairs and remove |
for key, value in pairs(db) do |
if type(value) == "table" then |
-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables |
if defaults[key] == nil then |
removeDefaults(value, v) |
-- if the table is empty afterwards, remove it |
if not next(value) then |
db[key] = nil |
end |
-- if it was specified, only strip ** content, but block values which were set in the key table |
elseif k == "**" then |
removeDefaults(value, v, defaults[key]) |
end |
end |
end |
elseif k == "*" then |
-- check for non-table default |
for key, value in pairs(db) do |
if defaults[key] == nil and v == value then |
db[key] = nil |
end |
end |
end |
elseif type(v) == "table" and type(db[k]) == "table" then |
-- if a blocker was set, dive into it, to allow multi-level defaults |
removeDefaults(db[k], v, blocker and blocker[k]) |
if not next(db[k]) then |
db[k] = nil |
end |
else |
-- check if the current value matches the default, and that its not blocked by another defaults table |
if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then |
db[k] = nil |
end |
end |
end |
-- remove all metatables from the db |
setmetatable(db, nil) |
end |
-- This is called when a table section is first accessed, to set up the defaults |
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 |
-- Metatable to handle the dynamic creation of sections and copying of sections. |
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 |
-- Callback: OnNewProfile, database, newProfileKey |
t.callbacks:Fire("OnNewProfile", t, 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 validateDefaults(defaults, keyTbl, offset) |
if not defaults then return end |
offset = offset or 0 |
for k in pairs(defaults) do |
if not keyTbl[k] or k == "profiles" then |
error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset) |
end |
end |
end |
local preserve_keys = { |
["callbacks"] = true, |
["RegisterCallback"] = true, |
["UnregisterCallback"] = true, |
["UnregisterAllCallbacks"] = true, |
["children"] = true, |
} |
local realmKey = GetRealmName() |
local charKey = UnitName("player") .. " - " .. realmKey |
local _, classKey = UnitClass("player") |
local _, raceKey = UnitRace("player") |
local factionKey = UnitFactionGroup("player") |
local factionrealmKey = factionKey .. " - " .. realmKey |
-- Actual database initialization function |
local function initdb(sv, defaults, defaultProfile, olddb, parent) |
-- Generate the database keys for each section |
-- 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[charKey] or defaultProfile or charKey |
sv.profileKeys[charKey] = profileKey |
-- This table contains keys that enable the dynamic creation |
-- of each section of the table. The 'global' and 'profiles' |
-- have a key of true, since they are handled in a special case |
local keyTbl= { |
["char"] = charKey, |
["realm"] = realmKey, |
["class"] = classKey, |
["race"] = raceKey, |
["faction"] = factionKey, |
["factionrealm"] = factionrealmKey, |
["profile"] = profileKey, |
["global"] = true, |
["profiles"] = true, |
} |
validateDefaults(defaults, keyTbl, 1) |
-- This allows us to use this function to reset an entire database |
-- Clear out the old database |
if olddb then |
for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end |
end |
-- Give this database the metatable so it initializes dynamically |
local db = setmetatable(olddb or {}, dbmt) |
if not rawget(db, "callbacks") then |
-- try to load CallbackHandler-1.0 if it loaded after our library |
if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end |
db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy |
end |
-- Copy methods locally into the database object, to avoid hitting |
-- the metatable when calling methods |
if not parent then |
for name, func in pairs(DBObjectLib) do |
db[name] = func |
end |
else |
-- hack this one in |
db.RegisterDefaults = DBObjectLib.RegisterDefaults |
db.ResetProfile = DBObjectLib.ResetProfile |
end |
-- Set some properties in the database object |
db.profiles = sv.profiles |
db.keys = keyTbl |
db.sv = sv |
--db.sv_name = name |
db.defaults = defaults |
db.parent = parent |
-- store the DB in the registry |
AceDB.db_registry[db] = true |
return db |
end |
-- handle PLAYER_LOGOUT |
-- strip all defaults from all databases |
local function logoutHandler(frame, event) |
if event == "PLAYER_LOGOUT" then |
for db in pairs(AceDB.db_registry) do |
db.callbacks:Fire("OnDatabaseShutdown", db) |
for section, key in pairs(db.keys) do |
if db.defaults and db.defaults[section] and rawget(db, section) then |
removeDefaults(db[section], db.defaults[section]) |
end |
end |
end |
end |
end |
AceDB.frame:RegisterEvent("PLAYER_LOGOUT") |
AceDB.frame:SetScript("OnEvent", logoutHandler) |
--[[------------------------------------------------------------------------- |
AceDB Object Method Definitions |
---------------------------------------------------------------------------]] |
-- DBObject:RegisterDefaults(defaults) |
-- defaults (table) - A table of defaults for this database |
-- |
-- Sets the defaults table for the given database object by clearing any |
-- that are currently set, and then setting the new defaults. |
function DBObjectLib:RegisterDefaults(defaults) |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2) |
end |
validateDefaults(defaults, self.keys) |
-- Remove any currently set defaults |
if self.defaults then |
for section,key in pairs(self.keys) do |
if self.defaults[section] and rawget(self, section) then |
removeDefaults(self[section], self.defaults[section]) |
end |
end |
end |
-- Set the DBObject.defaults table |
self.defaults = defaults |
-- Copy in any defaults, only touching those sections already created |
if defaults then |
for section,key in pairs(self.keys) do |
if defaults[section] and rawget(self, section) then |
copyDefaults(self[section], defaults[section]) |
end |
end |
end |
end |
-- DBObject:SetProfile(name) |
-- name (string) - The name of the profile to set as the current profile |
-- |
-- Changes the profile of the database and all of it's namespaces to the |
-- supplied named profile |
function DBObjectLib:SetProfile(name) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2) |
end |
-- changing to the same profile, dont do anything |
if name == self.keys.profile then return end |
local oldProfile = self.profile |
local defaults = self.defaults and self.defaults.profile |
if oldProfile and defaults then |
-- Remove the defaults from the old profile |
removeDefaults(oldProfile, defaults) |
end |
self.profile = nil |
self.keys["profile"] = name |
self.sv.profileKeys[charKey] = name |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.SetProfile(db, name) |
end |
end |
-- Callback: OnProfileChanged, database, newProfileKey |
self.callbacks:Fire("OnProfileChanged", self, name) |
end |
-- DBObject:GetProfiles(tbl) |
-- tbl (table) - A table to store the profile names in (optional) |
-- |
-- Returns a table with the names of the existing profiles in the database. |
-- You can optionally supply a table to re-use for this purpose. |
function DBObjectLib:GetProfiles(tbl) |
if tbl and type(tbl) ~= "table" then |
error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2) |
end |
-- Clear the container table |
if tbl then |
for k,v in pairs(tbl) do tbl[k] = nil end |
else |
tbl = {} |
end |
local curProfile = self.keys.profile |
local i = 0 |
for profileKey in pairs(self.profiles) do |
i = i + 1 |
tbl[i] = profileKey |
if curProfile and profileKey == curProfile then curProfile = nil end |
end |
-- Add the current profile, if it hasn't been created yet |
if curProfile then |
i = i + 1 |
tbl[i] = curProfile |
end |
return tbl, i |
end |
-- DBObject:GetCurrentProfile() |
-- |
-- Returns the current profile name used by the database |
function DBObjectLib:GetCurrentProfile() |
return self.keys.profile |
end |
-- DBObject:DeleteProfile(name) |
-- name (string) - The name of the profile to be deleted |
-- |
-- Deletes a named profile. This profile must not be the active profile. |
function DBObjectLib:DeleteProfile(name, silent) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2) |
end |
if self.keys.profile == name then |
error("Cannot delete the active profile in an AceDBObject.", 2) |
end |
if not rawget(self.sv.profiles, name) and not silent then |
error("Cannot delete profile '" .. name .. "'. It does not exist.", 2) |
end |
self.sv.profiles[name] = nil |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.DeleteProfile(db, name, true) |
end |
end |
-- Callback: OnProfileDeleted, database, profileKey |
self.callbacks:Fire("OnProfileDeleted", self, name) |
end |
-- DBObject:CopyProfile(name) |
-- name (string) - The name of the profile to be copied into the current profile |
-- |
-- Copies a named profile into the current profile, overwriting any conflicting |
-- settings. |
function DBObjectLib:CopyProfile(name, silent) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2) |
end |
if name == self.keys.profile then |
error("Cannot have the same source and destination profiles.", 2) |
end |
if not rawget(self.sv.profiles, name) and not silent then |
error("Cannot copy profile '" .. name .. "'. It does not exist.", 2) |
end |
-- Reset the profile before copying |
DBObjectLib.ResetProfile(self) |
local profile = self.profile |
local source = self.sv.profiles[name] |
copyTable(source, profile) |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.CopyProfile(db, name, true) |
end |
end |
-- Callback: OnProfileCopied, database, sourceProfileKey |
self.callbacks:Fire("OnProfileCopied", self, name) |
end |
-- DBObject:ResetProfile() |
-- noChildren (boolean) - if set to true, the reset will not be populated to the child namespaces of this DB object |
-- |
-- Resets the current profile |
function DBObjectLib:ResetProfile(noChildren) |
local profile = self.profile |
for k,v in pairs(profile) do |
profile[k] = nil |
end |
local defaults = self.defaults and self.defaults.profile |
if defaults then |
copyDefaults(profile, defaults) |
end |
-- populate to child namespaces |
if self.children and not noChildren then |
for _, db in pairs(self.children) do |
DBObjectLib.ResetProfile(db) |
end |
end |
-- Callback: OnProfileReset, database |
self.callbacks:Fire("OnProfileReset", self) |
end |
-- DBObject:ResetDB(defaultProfile) |
-- defaultProfile (string) - The profile name to use as the default |
-- |
-- Resets the entire database, using the string defaultProfile as the default |
-- profile. |
function DBObjectLib:ResetDB(defaultProfile) |
if defaultProfile and type(defaultProfile) ~= "string" then |
error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2) |
end |
local sv = self.sv |
for k,v in pairs(sv) do |
sv[k] = nil |
end |
local parent = self.parent |
initdb(sv, self.defaults, defaultProfile, self) |
-- fix the child namespaces |
if self.children then |
if not sv.namespaces then sv.namespaces = {} end |
for name, db in pairs(self.children) do |
if not sv.namespaces[name] then sv.namespaces[name] = {} end |
initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self) |
end |
end |
-- Callback: OnDatabaseReset, database |
self.callbacks:Fire("OnDatabaseReset", self) |
-- Callback: OnProfileChanged, database, profileKey |
self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"]) |
return self |
end |
-- DBObject:RegisterNamespace(name [, defaults]) |
-- name (string) - The name of the new namespace |
-- defaults (table) - A table of values to use as defaults |
-- |
-- Creates a new database namespace, directly tied to the database. This |
-- is a full scale database in it's own rights other than the fact that |
-- it cannot control its profile individually |
function DBObjectLib:RegisterNamespace(name, defaults) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2) |
end |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2) |
end |
if self.children and self.children[name] then |
error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2) |
end |
local sv = self.sv |
if not sv.namespaces then sv.namespaces = {} end |
if not sv.namespaces[name] then |
sv.namespaces[name] = {} |
end |
local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self) |
if not self.children then self.children = {} end |
self.children[name] = newDB |
return newDB |
end |
--[[------------------------------------------------------------------------- |
AceDB Exposed Methods |
---------------------------------------------------------------------------]] |
-- AceDB:New(name, defaults, defaultProfile) |
-- name (table or string) - The name of variable, or table to use for the database |
-- defaults (table) - A table of database defaults |
-- defaultProfile (string) - The name of the default profile |
-- |
-- Creates a new database object that can be used to handle database settings |
-- and profiles. |
function AceDB:New(tbl, defaults, defaultProfile) |
if type(tbl) == "string" then |
local name = tbl |
tbl = getglobal(name) |
if not tbl then |
tbl = {} |
setglobal(name, tbl) |
end |
end |
if type(tbl) ~= "table" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2) |
end |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2) |
end |
if defaultProfile and type(defaultProfile) ~= "string" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string expected.", 2) |
end |
return initdb(tbl, defaults, defaultProfile) |
end |
-- upgrade existing databases |
for db in pairs(AceDB.db_registry) do |
if not db.parent then |
for name,func in pairs(DBObjectLib) do |
db[name] = func |
end |
else |
db.RegisterDefaults = DBObjectLib.RegisterDefaults |
end |
end |
====================== |
| BROADCASTER README | |
====================== |
Broadcaster is my first "real" addon, and is a result of myself wanting a mod to announce random messages for polymorph and other spells. |
I would like to thank people on irc who helped me learn how to do things, and just listened when I was frustrated. The messages in this addon were either taken from other addons (such as Cryolysis), the WoW forums, or were made up my myself and my guildies/friends. |
Broadcaster reads directly from the combat log. For polymorph, it won't announce the message unless your spell is successfully cast on your focus. (Yes, it requires you to set your poly target as focus to work.) |
How to use Broadcaster: |
======================= |
* Install Broadcaster like any other addon |
* If you're a mage and you don't have one yet, set up a macro for polymorph so that it sets your poly target as focus. You may also have it set to resheep your focus so that you don't have to change targets. |
* Join a party or raid and use a supported spell |
* If you or your group wishes you to silence Broadcaster, type /broadcaster standby or go to Interface Options to put it into standby mode. |
============ |
| TO DO... | |
============ |
Future Supported Spells: |
======================== |
Hunter: |
Ice Trap |
Warlock: |
Ritual of Summoning |
Ritual of Souls |
Soulstone |
Banish |
Future Options: |
=============== |
Broadcast to... |
Raid or Party |
Party Only |
Self Only |
Disabled (Standby) |
Broadcasting enabled for the following spells... |
Broadcast in battlegrounds? |
Future Code ToDo: |
================= |
============= |
| CHANGELOG | |
============= |
1.1a (5/20/08) |
Added support for Pally's Redemption, Priest's Resurrection, Druid's Rebirth, and Shaman's Ancestral Spirit spells. |
These currently have the same rez messages set to them. |
Was not tested - let me know if it all works. :) |
Also, I hope that the %t in the messages is good enough for you to get target's info. Blame the Blizz combat log, etc for that. -.- |
1.0e (5/18/08) |
first cut at splitting into modules per class |
localization added - translators needed! Look in enUS.lua and change "true" to what your translation would be. I also need translations for the strings in the MageMessages.lua file |
still only Mage support. Other classes coming. ;) |
1.0d (5/12/08) |
attempt to fix Zidomo's issue that was reported in wowi comments |
added support for mage's Ritual of Refreshment. Manna biscuits, anyone? |
1.0c (5/11/08) |
added support for mage portals! Let me know if you have any issues. :) |
1.0b (5/6/08) |
fixed a typo in one message, added a new turtle message. |
1.0a (5/5/08) |
Initial release |
## Interface: 30000 |
## Title: Broadcaster |
## Notes: Announces polymorphs to party and raid chat |
## Author: Seerah |
## Version: 1.1a |
## SavedVariables: BroadcasterDB |
## OptionalDeps: Ace3 |
## X-Embeds: Ace3 |
## X-Category: Combat |
embeds.xml |
Locales\enUS.lua |
Locales\MageMessages.lua |
Locales\DruidMessages.lua |
Locales\PriestMessages.lua |
Locales\PaladinMessages.lua |
Locales\ShamanMessages.lua |
Broadcaster.lua |
Modules\Mage.lua |
Modules\Druid.lua |
Modules\Priest.lua |
Modules\Paladin.lua |
Modules\Shaman.lua |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="Libs\LibStub\LibStub.lua"/> |
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/> |
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/> |
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/> |
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/> |
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/> |
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/> |
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/> |
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/> |
</Ui> |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
Be sure to include the locale of your translation in the subject. |
------------------------------------------------------------------------------]]-- |
--ARE YOU A PRIEST?-- |
if select(2, UnitClass("player")) ~= "PRIEST" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterPriestMsgs = {} |
elseif loc == deDE then |
BroadcasterPriestMsgs = {} |
elseif loc == esES then |
BroadcasterPriestMsgs = {} |
elseif loc == esMX then |
BroadcasterPriestMsgs = {} |
elseif loc == frFR then |
BroadcasterPriestMsgs = {} |
elseif loc == koKR then |
BroadcasterPriestMsgs = {} |
elseif loc == zhCN then |
BroadcasterPriestMsgs = {} |
elseif loc == zhTW then |
BroadcasterPriestMsgs = {} |
else]]-- |
BroadcasterPriestMsgs = { |
["Resurrection"] = { |
"%t, get up, you're making me look bad!", -- 1 |
"%t, if you stop dying I'll stop using my lame rez macros!", -- 2 |
"%t, your soul has been disconnected. Please stand by while I reconnect you.", -- 3 |
"Arise my champion!", -- 4 |
"Boy, I really knocked 'em dead! Get it?", -- 5 |
"Don't rush me, %t. You rush a miracle worker and you get lousy miracles.", -- 6 |
"Hmm, blue clamp is positive, red clamp is ... Hey, %t, which nipple is positive?", -- 7 |
"Hope this works, %t; last guy I tried this on couldn't stop eating everyone's brains.", --8 |
"If you're dead and you know it, click Accept! *click* *click*", -- 9 |
"L2StayAlive, %t.", -- 10 |
"Oops... Rezzing %t...", -- 11 |
"Run towards the light %t!!... The LIGHT!!", -- 12 |
"This better be worth a cooldown %t...", -- 13 |
"Um, yeah, %t we're going to need you to get off the ground now, and um, yeah, we're going to need you to start fighting again, that would be great, thanks!", -- 14 |
"We can rebuild %t. We can make them stronger, better, faster.", -- 15 |
"Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo ... that's the best ambulance impression I can do, %t." -- 16 |
} |
} |
--end |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
Be sure to include the locale of your translation in the subject. |
------------------------------------------------------------------------------]]-- |
--ARE YOU A DRUID?-- |
if select(2, UnitClass("player")) ~= "DRUID" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterDruidMsgs = {} |
elseif loc == deDE then |
BroadcasterDruidMsgs = {} |
elseif loc == esES then |
BroadcasterDruidMsgs = {} |
elseif loc == esMX then |
BroadcasterDruidMsgs = {} |
elseif loc == frFR then |
BroadcasterDruidMsgs = {} |
elseif loc == koKR then |
BroadcasterDruidMsgs = {} |
elseif loc == zhCN then |
BroadcasterDruidMsgs = {} |
elseif loc == zhTW then |
BroadcasterDruidMsgs = {} |
else]]-- |
BroadcasterDruidMsgs = { |
["Rebirth"] = { |
"%t, get up, you're making me look bad!", -- 1 |
"%t, if you stop dying I'll stop using my lame rez macros!", -- 2 |
"%t, your soul has been disconnected. Please stand by while I reconnect you.", -- 3 |
"Arise my champion!", -- 4 |
"Boy, I really knocked 'em dead! Get it?", -- 5 |
"Don't rush me, %t. You rush a miracle worker and you get lousy miracles.", -- 6 |
"Hmm, blue clamp is positive, red clamp is ... Hey, %t, which nipple is positive?", -- 7 |
"Hope this works, %t; last guy I tried this on couldn't stop eating everyone's brains.", --8 |
"If you're dead and you know it, click Accept! *click* *click*", -- 9 |
"L2StayAlive, %t.", -- 10 |
"Oops... Rezzing %t...", -- 11 |
"Run towards the light %t!!... The LIGHT!!", -- 12 |
"This better be worth a cooldown %t...", -- 13 |
"Um, yeah, %t we're going to need you to get off the ground now, and um, yeah, we're going to need you to start fighting again, that would be great, thanks!", -- 14 |
"We can rebuild %t. We can make them stronger, better, faster.", -- 15 |
"Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo ... that's the best ambulance impression I can do, %t." -- 16 |
} |
} |
--end |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
Be sure to include the locale of your translation in the subject. |
------------------------------------------------------------------------------]]-- |
--ARE YOU A SHAMAN?-- |
if select(2, UnitClass("player")) ~= "SHAMAN" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterShamanMsgs = {} |
elseif loc == deDE then |
BroadcasterShamanMsgs = {} |
elseif loc == esES then |
BroadcasterShamanMsgs = {} |
elseif loc == esMX then |
BroadcasterShamanMsgs = {} |
elseif loc == frFR then |
BroadcasterShamanMsgs = {} |
elseif loc == koKR then |
BroadcasterShamanMsgs = {} |
elseif loc == zhCN then |
BroadcasterShamanMsgs = {} |
elseif loc == zhTW then |
BroadcasterShamanMsgs = {} |
else]]-- |
BroadcasterShamanMsgs = { |
["AncestralSpirit"] = { |
"%t, get up, you're making me look bad!", -- 1 |
"%t, if you stop dying I'll stop using my lame rez macros!", -- 2 |
"%t, your soul has been disconnected. Please stand by while I reconnect you.", -- 3 |
"Arise my champion!", -- 4 |
"Boy, I really knocked 'em dead! Get it?", -- 5 |
"Don't rush me, %t. You rush a miracle worker and you get lousy miracles.", -- 6 |
"Hmm, blue clamp is positive, red clamp is ... Hey, %t, which nipple is positive?", -- 7 |
"Hope this works, %t; last guy I tried this on couldn't stop eating everyone's brains.", --8 |
"If you're dead and you know it, click Accept! *click* *click*", -- 9 |
"L2StayAlive, %t.", -- 10 |
"Oops... Rezzing %t...", -- 11 |
"Run towards the light %t!!... The LIGHT!!", -- 12 |
"This better be worth a cooldown %t...", -- 13 |
"Um, yeah, %t we're going to need you to get off the ground now, and um, yeah, we're going to need you to start fighting again, that would be great, thanks!", -- 14 |
"We can rebuild %t. We can make them stronger, better, faster.", -- 15 |
"Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo ... that's the best ambulance impression I can do, %t." -- 16 |
} |
} |
--end |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
Be sure to include the locale of your translation in the subject. |
------------------------------------------------------------------------------]]-- |
--ARE YOU A PALADIN?-- |
if select(2, UnitClass("player")) ~= "PALADIN" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterPaladinMsgs = {} |
elseif loc == deDE then |
BroadcasterPaladinMsgs = {} |
elseif loc == esES then |
BroadcasterPaladinMsgs = {} |
elseif loc == esMX then |
BroadcasterPaladinMsgs = {} |
elseif loc == frFR then |
BroadcasterPaladinMsgs = {} |
elseif loc == koKR then |
BroadcasterPaladinMsgs = {} |
elseif loc == zhCN then |
BroadcasterPaladinMsgs = {} |
elseif loc == zhTW then |
BroadcasterPaladinMsgs = {} |
else]]-- |
BroadcasterPaladinMsgs = { |
["Redemption"] = { |
"%t, get up, you're making me look bad!", -- 1 |
"%t, if you stop dying I'll stop using my lame rez macros!", -- 2 |
"%t, your soul has been disconnected. Please stand by while I reconnect you.", -- 3 |
"Arise my champion!", -- 4 |
"Boy, I really knocked 'em dead! Get it?", -- 5 |
"Don't rush me, %t. You rush a miracle worker and you get lousy miracles.", -- 6 |
"Hmm, blue clamp is positive, red clamp is ... Hey, %t, which nipple is positive?", -- 7 |
"Hope this works, %t; last guy I tried this on couldn't stop eating everyone's brains.", --8 |
"If you're dead and you know it, click Accept! *click* *click*", -- 9 |
"L2StayAlive, %t.", -- 10 |
"Oops... Rezzing %t...", -- 11 |
"Run towards the light %t!!... The LIGHT!!", -- 12 |
"This better be worth a cooldown %t...", -- 13 |
"Um, yeah, %t we're going to need you to get off the ground now, and um, yeah, we're going to need you to start fighting again, that would be great, thanks!", -- 14 |
"We can rebuild %t. We can make them stronger, better, faster.", -- 15 |
"Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo! Wee-ooo ... that's the best ambulance impression I can do, %t." -- 16 |
} |
} |
--end |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
%d is the destination of your portal. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
------------------------------------------------------------------------------]]-- |
--ARE YOU A MAGE?-- |
if select(2, UnitClass("player")) ~= "MAGE" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterMageMsgs = {} |
elseif loc == deDE then |
BroadcasterMageMsgs = {} |
elseif loc == esES then |
BroadcasterMageMsgs = {} |
elseif loc == esMX then |
BroadcasterMageMsgs = {} |
elseif loc == frFR then |
BroadcasterMageMsgs = {} |
elseif loc == koKR then |
BroadcasterMageMsgs = {} |
elseif loc == zhCN then |
BroadcasterMageMsgs = {} |
elseif loc == zhTW then |
BroadcasterMageMsgs = {} |
else]]-- |
BroadcasterMageMsgs = { |
["PolySheep"] = { |
"%f has been baaaaaad!", --1 |
"%f is my sheep. There are many like him, but this one is mine.", --2 |
"%f is now poly'd! Weapons down! Step away from the sheep!", --3 |
"%f is now safe to pet; no poking the animals!", --4 |
"%f's feeling a little bit... sheepish.", --5 |
"And now for my next trick, I'll replace %f with a harmless sheep!", --6 |
"Beep Beep %f's a sheep!", --7 |
"How's my sheeping? Call 1-800-BAH-RAM-U", --8 |
"I ain't no shepherd, nor Little-Bo-Peep, yet poor old %f still be me sheep!", --9 |
"I'm Little Bo Peep; don't touch the sheep!", --10 |
"Okay, %f, repeat after me: Baaaa!", --11 |
"Piggies go OINK, cows go MOO, sheep go BAA and now %f does too!", --12 |
"Poly on %f - NO TOUCHIE!!!", --13 |
"Poly on %f. Every time you break a sheep, God kills a kitten.", --14 |
"Poly on %f... you spank it, you tank it.", --15 |
"Polymorph on *yawn* %f. So many sheep... zzzz", --16 |
"Sheeping %f; repeated poking may cause explosions!", --17 |
"Skies above and oceans deep, %f is now a sheep!", --18 |
"Stay away from %f, I still need mats for [Wool Socks].", --19 |
"What's white and fluffy and covered in wool? %f of course!", --20 |
"You are in my power, %f; now you shall become a chicken! ... Oh darnit, this NEVER works right!" --21 |
}, |
["PolyPig"] = { |
"%f is my piggy. There are many like him, but this one is mine.", --1 |
"%f's a piggy; don't steal the bacon.", --2 |
"%f: the other white meat.", --3 |
"%f? That little piggy just got an extra 50 seconds to live...", --4 |
"%f? That little piggy stayed home.", --5 |
"%f? That little piggy went to the market.", --6 |
"Dogs can't tell that %f's not bacon.", --7 |
"I smell bacon, I smell pork; look out %f, I've got a fork!", --8 |
"In space, no one can hear you squeal.", --9 |
"PORKCHOP SANDWICHES, %f!", --10 |
"Pigging %f. Do not molest the pig.", --11 |
"Poly on %f!! NO TOUCHIE!!", --12 |
"Porking %f; there's your darn conjured food.", --13 |
"Sheep go BAA, cows go MOO, pigs go OINK and now %f does too!", --14 |
"Sooooo Weeeeee! Here Piggy %f. Dont Steal the Bacon!", --15 |
"%f's a little piglet short and stout, he has a tail and he has a snout; if you dare hit my pig then I shall yell, and then send you an abusive tell.", --16 |
"That'll do, %f... that'll do.", --17 |
"The %f be piggified; poke it not!", --18 |
"You are in my power, %f; now you shall become a chicken! ... Oh darnit, this NEVER works right!" --19 |
}, |
["PolyTurtle"] = { |
"Poly on %f - NO TOUCHIE!!!", --1 |
"Poly on %f... you spank it, you tank it.", --2 |
"You are in my power, %f; now you shall become a chicken! ... Oh darnit, this NEVER works right!", --3 |
"Mmm... Turtle Bisque...", --4 |
"Slow and steady won't win the race for %f this time", --5 |
"%f is a hero in a half-shell!", --6 |
"Slow and steady wins the race... but not for you, %f!", --7 |
"Cowabunga, %f!", --8 |
"Turtle soup anyone?", --9 |
"What does the world rest on? Why, it's turtles all the way down!" --10 |
}, |
["Portals"] = { |
"Azerothian Air service is proud to be servicing your one-way trip to %d. Please remember to close your eyes for the duration because it can be scary as hell.", --1 |
"Chortle chortle, it's a portal!", --2 |
"DISCLAIMER: I RECEIVED MY PORTAL LICENSE OFF THE AH. CLICK AT YOUR OWN RISK.", --3 |
"Eeny-meeny-miny-mo, pick a portal, off you go!", --4 |
"Entering through this dimensional gateway will get you to %d.", --5 |
"Here's your damn portal. What? Well how the hell should *I* know where it goes?! I just open the damn things...", --6 |
"Now opening a portal to %d. Or was it Orgrimmar? Maybe Stormwind or Thunder bluff? I guess we'll just have to find out!", --7 |
"Okay, focus. They want to go to %d, not the middle of the Maelstrom. I can do this.. %d, not ocean.", --8 |
"Opening a portal to %d. Hey, you guys see that episode of Star Trek where the guy got turned into an amorphous blob of flesh by the transporter? Heh, I *love* that episode...", --9 |
"This is the final boarding call for passengers travelling to %d. Please keep your arms and legs inside the portal at all times.", --10 |
"This is the final boarding call for passengers travelling to %d. Please note that Azerothian Airlines cannot be held responsible for missing body parts, luggage or you being turned into a small pink Elekk.", --11 |
"This is the final boarding call for passengers travelling to %d. Thankyou for flying Azerothian Airlines.", --12 |
"Thish is the final boarding call for pashengersh travelling to %d. Ash your captain, I'd just like to thank you for flying with ush today. *hic*", --13 |
"WARNING: There is a fork in this portal. Down one side is %d. Home. And down the other... a ROOM with a MOOSE!", --14 |
"Whatever you do, DON'T TOUCH THE PORTAL!", --15 |
"Why go to %d when we can bring %d here? Please click the portal to help me summon the city!" --16 |
}, |
["Table"] = { |
"Please click the shiny bubble!", --1 |
"Now summoning Manna Biscuits!", --2 |
"Clicking this portal will get you to the heavenly bakery - can't you just smell it?!?", --3 |
"Mmm... Manna Biscuits...", --4 |
"One stack for you, two for me. One for you, three for me.", --5 |
"I hope no one's on a low-carb diet...", --6 |
"Manna Biscuits are the best thing since sliced bread!", --6 |
"Picnic is served - get it before the ants do!", --7 |
"Click the glowing circle. I promise I won't port you to the Maelstrom. Maybe." --8 |
} |
} |
--end |
--[[------------------------------------------------------------------------------ |
These messages have been taken from all over. Some are from |
Cryolysis, some from other addons, some from the WoW forums, |
some my guild and I made up ourselves. |
%t refers to your target, and %f refers to your focus target. |
-------------------------- |
To TRANSLATORS: |
-------------------------- |
Please translate each string into your locale, leaving the numbers |
that are commented out next to them. This way, we'll know what |
goes with what, and it will be easy to tell if there is a new string |
that needs localizing. |
You may email me translations at umsinger@yahoo.com |
Be sure to include the locale of your translation in the subject. |
------------------------------------------------------------------------------]]-- |
--ARE YOU A WARLOCK?-- |
if select(2, UnitClass("player")) ~= "WARLOCK" then |
return |
end |
--local loc = GetLocale() |
--[[if loc == enGB then |
BroadcasterWarlockMsgs = {} |
elseif loc == deDE then |
BroadcasterWarlockMsgs = {} |
elseif loc == esES then |
BroadcasterWarlockMsgs = {} |
elseif loc == esMX then |
BroadcasterWarlockMsgs = {} |
elseif loc == frFR then |
BroadcasterWarlockMsgs = {} |
elseif loc == koKR then |
BroadcasterWarlockMsgs = {} |
elseif loc == zhCN then |
BroadcasterWarlockMsgs = {} |
elseif loc == zhTW then |
BroadcasterWarlockMsgs = {} |
else]]-- |
BroadcasterWarlockMsgs = { |
["Summon"] = { |
"Arcanum Taxi Cab! I am summoning %t, please click on the portal.", --1 |
"Welcome aboard, %t, and thank you for flying ~Succubus Air Lines~... Air Hostesses and their lashes are at your service during your trip!", --2 |
"If you click on the portal, someone named %t will appear and do your job for you!", --3 |
"If you do not want a sprawling, phlegm-looking, asthmatic creature to come from this portal, click on it to help %t find a path through Hell as quickly as possible!", --4 |
"Arcane Taxi Cab for %t, please click the portal for the slacker please.", --5 |
"**Summoning %t, please click on the portal**", --6 |
"Please click the shiny bubble to summon %t!", --7 |
}, |
["Soulwell"] = { |
}, |
["Soulstone"] = { |
"If you cherish the idea of a mass suicide, %t can now self-resurrect, so all should be fine. Go ahead.", --1 |
"%t can go afk to drink a cup of coffee or something, soulstone is in place to allow for the wipe...", --2 |
"Hmmm... %t is soulstoned... full of confidence tonight aren't we!!", --3 |
"%t is Stoned... duuuude heavy!", --4 |
"--> %t is soulstoned for 30 minutes <--" --5 |
}, |
["Banish"] = { |
} |
} |
local L = LibStub("AceLocale-3.0"):NewLocale("Broadcaster", "enUS", true) |
--OPTIONS-- |
L["Standby"] = true |
L["Puts the addon into standby mode, so no messages are sent."] = true |
L["Config"] = true |
L["Opens the options window."] = true |
--MAGE MODULE-- |
L["Portal:"] = true |